From a6d47b5b53ca4fcd814136c2d7eec475b03ef5d6 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Sat, 12 Nov 2022 17:30:12 -0500 Subject: Make dla2csv more like a real C program instead of "portable assembly". --- dla2csv.c | 390 ++++++++++++++++++++++++++++++++---------------------------- dla2csv.xex | Bin 11014 -> 11150 bytes 2 files changed, 211 insertions(+), 179 deletions(-) diff --git a/dla2csv.c b/dla2csv.c index 5c2142b..4910f7e 100644 --- a/dla2csv.c +++ b/dla2csv.c @@ -10,6 +10,12 @@ #include #include +#ifdef __CC65__ + #ifndef __ATARI__ + #error Unsupported cc65 machine type: only "-t atari" is supported. + #endif +#endif + #include "dlaver.h" #define HEIGHT 170 @@ -22,90 +28,76 @@ #define STRINGBUF_SIZE 256 -/* cc65 doesn't "localize" \b to the Atari backspace character, so: */ -#ifdef __ATARI__ - #define BS '\x7e' -#else - #define BS '\b' -#endif +#define PRINT_ID printf("DLA to CSV converter v" VERSION ".\n") -char inbuf[INBUF_SIZE]; -char stringbuf[STRINGBUF_SIZE]; -FILE *inf, *outf; +#ifdef __ATARI__ + #include -/* We can't use string literal syntax, cc65 "helpfully" turns "\x0a" - into an Atari 0x9b EOL character. Numeric constants are left alone. */ -char a8eol[] = { 0x9b, 0x00, 0x00 }; -char uxeol[] = { 0x0a, 0x00, 0x00 }; -char mseol[] = { 0x0d, 0x0a, 0x00 }; -char *eoltypes[][2] = { - { "Atari ($9B)", a8eol }, - { "Unix (\\n)", uxeol }, - { "MS (\\r\\n)", mseol }, - { NULL, NULL } -}; +/* cc65 doesn't "localize" \b to the Atari backspace character, so: */ + #define BS CH_DEL /* On the Atari, we should show Atari-style error messages, including the familiar error number. Another reason to do this: on the Atari, perror() says "Bad file number" instead of "No space left on device", if the disk fills up due to fprintf(). - On modern platforms, just use perror(). - */ -#ifdef __ATARI__ + On modern platforms, just use perror(). */ #define PERROR(x) atari_perror(x) void atari_perror(char *msg) { printf("%s: Error %d: %s\n", msg, _oserror, _stroserror(_oserror)); } -#else - #define PERROR(x) perror(x) -#endif /* cc65 doesn't play nice with the Break key on the Atari. It works as expected during disk I/O, but if you press it while printing to stdout or reading from stdin, the program freezes. so call disable_break() at startup, then wrap disk I/O in enable_break() and disable_break(). */ -#ifdef __ATARI__ -char get_pokmsk(void) { - return *((char *)0x10); +void enable_break() { + OS.pokmsk = POKEY_WRITE.irqen = POKEY_READ.irqst | 0x80; } -void set_pokmsk(char value) { - *((char *)0x10) = *((char *)0xd20e) = value; +void disable_break() { + OS.pokmsk = POKEY_WRITE.irqen = POKEY_READ.irqst & 0x7f; } -void enable_break() { - set_pokmsk(get_pokmsk() | 0x80); -} +void init_console(void) { + /* cc65's startup code turns off caps lock. turn it back on, + since we're typing DOS filenames. in BASIC this would be: + POKE 702,64 */ + OS.shflok = 0x40; -void disable_break() { - set_pokmsk(get_pokmsk() & 0x7f); + /* also, cc65 sets APPMHI to $bc1f (last byte before the GR.0 + display list). which causes the atari to lock up when Reset + is pressed. I can't believe this is useful behaviour... */ + OS.appmhi = 0; + + /* clear the screen */ + putchar(CH_CLR); } -#else -void enable_break() { } /* no-op */ -void disable_break() { } /* no-op */ -#endif -/* Read a string from stdin (E: on the Atari). - Exit on EOF (or Break key on the Atari, except we disable it). - On Linux, EOF is the ^D character, and this works. - On the Atari, EOF is ^3... On DOS 2.0S, we end up back at the DUP - menu... but SpartaDOS 3.2d locks up with a black screen. So don't - document ctrl-3 as a way to exit the program (too bad), and just - hope the user never presses it. - */ -void readstring(void) { - char *result; - memset(stringbuf, 0, STRINGBUF_SIZE); - result = fgets(stringbuf, STRINGBUF_SIZE - 1, stdin); - if(result == NULL) { - enable_break(); - exit(1); +void print_banner(void) { + int i; + + PRINT_ID; + + printf("\n" + "At any filename prompt, you may:\n" ); + printf( + "- Press Return, to exit this program.\n"); + printf( + "- Enter a number, to get a directory of\n" + " that drive."); + + /* be nice to emulator users. */ + for(i = 0; i < 12; i++) { + if(OS.hatabs[i].id == 'H') { + printf(" Use 0 for \"H:\"."); + break; + } } + printf("\n"); } -#ifdef __ATARI__ /* Show disk directory, in a 3-column layout. drive is an ASCII digit, e.g. 0x31 is drive 1. */ void show_dir(char drive) { @@ -139,8 +131,54 @@ void show_dir(char drive) { closedir(dir); if(column) putchar('\n'); } +#else + /* non-Atari is assumed to be POSIX (and not something like + Commodore or Apple II). */ + #define BS '\b' + #define PERROR(x) perror(x) + #define enable_break() noop() + #define disable_break() noop() + #define init_console() noop() +void noop(void) { } +void print_banner(void) { + PRINT_ID; +} #endif +char inbuf[INBUF_SIZE]; +char stringbuf[STRINGBUF_SIZE]; +FILE *inf, *outf; + +/* We can't use string literal syntax, cc65 "helpfully" turns "\x0a" + into an Atari 0x9b EOL character. Numeric constants are left alone. */ +char a8eol[] = { 0x9b, 0x00, 0x00 }; +char uxeol[] = { 0x0a, 0x00, 0x00 }; +char mseol[] = { 0x0d, 0x0a, 0x00 }; +char *eoltypes[][2] = { + { "Atari ($9B)", a8eol }, + { "Unix (\\n)", uxeol }, + { "MS (\\r\\n)", mseol }, + { NULL, NULL } +}; + +/* Read a string from stdin (E: on the Atari). + Exit on EOF (or Break key on the Atari, except we disable it). + On Linux, EOF is the ^D character, and this works. + On the Atari, EOF is ^3... On DOS 2.0S, we end up back at the DUP + menu... but SpartaDOS 3.2d locks up with a black screen. So don't + document ctrl-3 as a way to exit the program (too bad), and just + hope the user never presses it. + */ +void readstring(void) { + char *result; + memset(stringbuf, 0, STRINGBUF_SIZE); + result = fgets(stringbuf, STRINGBUF_SIZE - 1, stdin); + if(result == NULL) { + enable_break(); + exit(1); + } +} + int prompt_yn(char *prompt, int default_y) { char *yn = "y/N"; if(default_y) yn = "Y/n"; @@ -236,89 +274,131 @@ void backspace3(void) { putchar(BS); } -int main(int argc, char **argv) { - char *inp, *eol; - int i, bytes = 0, x, y, xmask, err; +/* check_dla() returns 1 if all is well, 0 if there's a problem. */ +int check_dla(int bytes) { + int i, ok = 1; + + /* warn if the file size is wrong... */ + if(bytes < INBUF_SIZE) { + printf("Warning: File too short!\n"); + ok = 0; + } else if(fgetc(inf) != EOF) { + printf("Warning: File too long!\n"); + ok = 0; + } else { + printf("File size is OK.\n"); + } + fclose(inf); -#ifdef __ATARI__ - /* cc65's startup code turns off caps lock. turn it back on, - since we're typing DOS filenames. in BASIC this would be: - POKE 702,64 */ - *((char *)0x02be) = 0x40; + /* a DLA file never has non-zero bytes at the beginning. */ + for(i = 0; i < 32; i++) { + if(inbuf[i]) { + printf("Warning: File doesn't look like a DLA!\n"); + ok = 0; + break; + } + } - /* also, cc65 sets APPMHI to $bc1f (last byte before the GR.0 - display list). which causes the atari to lock up when Reset - is pressed. I can't believe this is useful behaviour... */ - *((int *)0x0e) = 0; + return ok; +} - /* clear the screen */ - putchar(0x7d); -#endif +int read_file(void) { + int bytes; - printf("DLA to CSV converter v" VERSION ".\n"); + /* in case of short read on 2nd or later conversion: */ + memset(inbuf, 0, INBUF_SIZE); -#ifdef __ATARI__ - printf("\n" - "At any filename prompt, you may:\n" ); - printf( - "- Press Return, to exit this program.\n"); - printf( - "- Enter a number, to get a directory of\n" - " that drive."); - /* be nice to emulator users. 0x31a is HATABS. */ - for(i = 0x31a; i < 0x340; i += 3) { - if(*((char *)i) == 'H') { - printf(" Use 0 for \"H:\"."); - break; - } + /* read whole input file into memory */ + inf = prompt_filename("Input DLA", "rb"); + printf("Reading..."); + fflush(stdout); + enable_break(); + bytes = fread(inbuf, 1, INBUF_SIZE, inf); + disable_break(); + if(bytes <= 0) { + PERROR(stringbuf); } - printf("\n"); + + return bytes; +} + +/* convert() returns the number of particles on success, 0 on failure. */ +int convert(char *eol) { + char *inp; + int x, y, xmask, particles; + + outf = prompt_filename("Output CSV", "wb"); + +#ifdef __ATARI__ + printf("Press Ctrl-C to abort conversion.\n"); #endif + printf("\nConverting... "); + fflush(stdout); - while(1) { - disable_break(); - err = 0; + /* CSV file header row (column names) */ + fprintf(outf, "x,y%s", eol); - /* in case of short read on 2nd or later conversion: */ - memset(inbuf, 0, INBUF_SIZE); + inp = inbuf; + xmask = 0x80; + particles = 0; - /* read whole input file into memory */ - inf = prompt_filename("Input DLA", "rb"); - printf("Reading..."); - fflush(stdout); - enable_break(); - bytes = fread(inbuf, 1, INBUF_SIZE, inf); - disable_break(); - if(bytes <= 0) { - PERROR(stringbuf); - continue; - } - printf("Read %d bytes.\n", bytes); - - /* warn if the file size is wrong... */ - if(bytes < INBUF_SIZE) { - printf("Warning: File too short!\n"); - err = 1; - } else if(fgetc(inf) != EOF) { - printf("Warning: File too long!\n"); - err = 1; - } else { - printf("File size is OK.\n"); + /* write output file one line at a time */ + /* loop over all the pixels, by row, then column. this + loop is rather slow. */ + for(y = 0; y < HEIGHT; y++) { +#ifdef __ATARI__ + /* check for ^C */ + if(OS.ch == KEY_C | KEY_CTRL) { + printf("\nUser abort!\n"); + fclose(outf); + remove(stringbuf); + OS.ch = KEY_NONE; + return 0; } - fclose(inf); - - /* a DLA file never has non-zero bytes at the beginning. */ - for(i = 0; i < 32; i++) { - if(inbuf[i]) { - printf("Warning: File doesn't look like a DLA!\n"); - err = 1; - break; +#endif + backspace3(); + printf("%02d%%", y * 100 / HEIGHT); /* percentage */ + fflush(stdout); + for(x = 0; x < WIDTH; x++) { + if(*inp & xmask) { + if(fprintf(outf, "%d,%d%s", x, y, eol) < 0) { + putchar('\n'); + PERROR(stringbuf); + fclose(outf); + remove(stringbuf); + return 0; + } + particles++; + } + xmask >>= 1; + if(!xmask) { + xmask = 0x80; + inp++; } } + } + fclose(outf); + + return particles; +} + +int main(int argc, char **argv) { + char *eol; + int done, result = 0; + + init_console(); + print_banner(); + + while(1) { + disable_break(); + + result = read_file(); + if(result <= 0) continue; + printf("Read %d bytes.\n", result); /* if anything looks wrong, we shouldn't proceed... but the user is boss, so give him the choice to shoot himself in the foot. */ - if(err) { + if(!check_dla(result)) { if(!prompt_yn("Try to convert anyway", 0)) continue; } @@ -327,64 +407,16 @@ int main(int argc, char **argv) { /* convert in a loop. in case of write error, this allows retry without re-reading the input file. */ - i = err = 0; - while(!i) { - outf = prompt_filename("Output CSV", "wb"); - -#ifdef __ATARI__ - printf("Press Ctrl-C to abort conversion.\n"); -#endif - printf("\nConverting... "); - fflush(stdout); - - /* CSV file header row (column names) */ - fprintf(outf, "x,y%s", eol); - - /* write output file one line at a time */ - inp = inbuf; - xmask = 0x80; - bytes = 0; - for(y = 0; y < HEIGHT; y++) { -#ifdef __ATARI__ - /* check for ^C */ - if( *((char *)0x2fc) == 0x92 ) { - printf("User abort!\n"); - fclose(outf); - remove(stringbuf); - err = 1; - *((char *)0x2fc) = 0xff; - break; - } -#endif + done = 0; + while(!done) { + result = convert(eol); + if(result) { + done = 1; backspace3(); - printf("%02d%%", y * 100 / HEIGHT); /* percentage */ - fflush(stdout); - for(x = 0; x < WIDTH; x++) { - if(*inp & xmask) { - if(fprintf(outf, "%d,%d%s", x, y, eol) < 0) { - putchar('\n'); - PERROR(stringbuf); - err = 1; - break; - } - bytes++; - } - xmask >>= 1; - if(!xmask) { - xmask = 0x80; - inp++; - } - } - if(err) break; - } - fclose(outf); - if(err) { - if(!prompt_yn("Conversion failed, try again", 1)) - i = 1; + printf("100%%\n%d particles.\n", result); } else { - i = 1; - backspace3(); - printf("100%%\n%d particles.\n", bytes); + if(!prompt_yn("Conversion failed, try again", 1)) + done = 1; } } diff --git a/dla2csv.xex b/dla2csv.xex index 8ed97ad..3d03634 100644 Binary files a/dla2csv.xex and b/dla2csv.xex differ -- cgit v1.2.3