From 17df87f3ddc4a5a040539f22f819d0be4cdce8d4 Mon Sep 17 00:00:00 2001 From: waltje Date: Thu, 13 Apr 2023 23:05:36 -0400 Subject: [PATCH 1/2] Updated to implement the original wishes in the README (escaping and such), added support for the BASIC V3.5 and V7.0 keywords. Moved platform stuff into separate folders. --- README.md | 53 +++++--- src/bas2prg.c | 174 ++++++++++++++++++------- src/prg2bas.c | 145 +++++++++++++++------ src/tokens.c | 224 ++++++++++++++++++++++++--------- src/tokens.h | 18 ++- src/{ => unix}/Makefile.GCC | 5 +- src/version.h | 4 +- src/{ => win32}/Makefile.MinGW | 9 +- src/{ => win32}/Makefile.VC | 13 +- tests/calc.bas | 101 +++++++++++++++ tests/calc.prg | Bin 0 -> 3750 bytes 11 files changed, 579 insertions(+), 167 deletions(-) rename src/{ => unix}/Makefile.GCC (85%) rename src/{ => win32}/Makefile.MinGW (78%) rename src/{ => win32}/Makefile.VC (62%) create mode 100644 tests/calc.bas create mode 100644 tests/calc.prg diff --git a/README.md b/README.md index 2380095..ecae10f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,12 @@ -Commodore 64 BASIC/PRG tools -============================ +Commodore 64/128 BASIC/PRG tools +================================ This is a pair of quick-and-dirty programs I wrote originally in 2011 to -convert between a PRG file and a BAS (text) file. +convert between a PRG (tokenized) file and a BAS (text) file. Although +originally only handling the BASIC V2 keywords (Commodore 64), support +for the V3.5 (Plus/4 ?) and V7 (Commodore 128) was added. This can be +turned off by NOT defining the USE_EXTEND macro in the Makefiles. + Usage ----- @@ -10,19 +14,38 @@ Usage Convert a BAS file to a PRG file: bas2prg program.prg +or + bas2prg -o program.prg program.bas Convert a PRG file to a BAS file: prg2bas program.bas +or + prg2bas -o program.bas program.prg + +whichever format feels better. + +Each program takes its own commandline options; you can see these by calling +them with the -? argument: + + prg2bas -? +or + bas2prg -? -These programs do not take any arguments. How to build ------------ Run the following command: - make + make -f unix/Makefile.GCC +or + make -f win32/Makefile.MinGW +or + make -f win32/Makefile.VC + +depending on your operating system and development tools. + Layout of a BASIC PRG file -------------------------- @@ -35,19 +58,21 @@ Layout of a BASIC PRG file * nul byte (1 byte) * value 0000 (2 bytes) (end of PRG) + Future directions ----------------- These two programs have scratched my itch, so I don't plan to do anything else -with them. +with them: -It would be nice to expose the internal flags in bas2prg as command-line -arguments. It's trivial enough to edit the source code and recompile, so I -haven't put in the effort to do so. +- It would be nice to expose the internal flags in bas2prg as command-line + arguments. It's trivial enough to edit the source code and recompile, so I + haven't put in the effort to do so. -It would be nice if prg2bas would escape unprintable characters (inside -strings, for example) as hexadecimal codes inside braces (such as "{d0}"). This -would minimize problems related to differences in character sets (PETSCII vs -ASCII). It would be doubly nice if bas2prg recognized and converted these -escaped sequences properly. +- It would be nice if prg2bas would escape unprintable characters (inside + strings, for example) as hexadecimal codes inside braces (such as "{d0}"). + This would minimize problems related to differences in character sets + (PETSCII vs ASCII). It would be doubly nice if bas2prg recognized and + converted these escaped sequences properly. +That said, done :) diff --git a/src/bas2prg.c b/src/bas2prg.c index 7aa7e6c..e43c168 100644 --- a/src/bas2prg.c +++ b/src/bas2prg.c @@ -1,6 +1,8 @@ /* - * bas2prg.c, convert a C64 BASIC text file to a PRG file. + * bas2prg.c, convert a C64/128 BASIC text file to a PRG file. + * * Copyright 2011, 2012 Christopher Williams + * Copyright 2023 Fred N. van Kempen, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,40 +32,61 @@ #define MAXLINELEN 1024 -#define TOKEN_REM 0x8f -#ifdef _DEBUG - debug = 0; // debug level -#endif -int invertcase, // rough ASCII to PETSCII conversion +int debug, // debug level + verbose, // output noise level + invertcase, // rough ASCII to PETSCII conversion autonumber, // add line numbers if no line number found startaddr, // load address trimspaces, // remove spaces from beginning/end of line collapsespaces; // remove free spaces inside line +/* Read a hex string, similar to strtoul. Hex digits are in uppercase. */ +static unsigned long +hexstrtoul(const char *str, char **endptr) +{ + unsigned long num = 0; + + while (*str != '\0') { + if (*str >= '0' && *str <= '9') { + num <<= 4; + num += *str++ - '0'; + } else if (*str >= 'A' && *str <= 'F') { + num <<= 4; + num += (*str++ - 'A') + 10; + } else + break; + } + + if (endptr != NULL) + *endptr = (char *)str; + + return num; +} + + /* * find a token in *src and increment *src past the token if one is found * return -1 if no token is found */ static int -gettoken(const char **src) +gettoken(const char **src, const char **table, int table_size) { const char **tp; int len; - int t; - - for (tp = tokens; tp < &tokens[128]; ++tp) { + + for (tp = tokens; tp < table + table_size; tp++) { len = strlen(*tp); if (! strncmp(*tp, *src, len)) { #ifdef _DEBUG if (debug) fprintf(stderr, "found token: %s\n", *tp); #endif - t = tp-tokens + 128; *src += len; - return t; + + return tp-tokens; } } @@ -80,23 +103,72 @@ tokenize(unsigned char *dest, const char *src) int rem = 0; int token; - for (sp = src, dp = dest; *sp;) { + for (sp = src, dp = dest; *sp != '\0'; ) { if (collapsespaces && !(rem || quoted)) { while (*sp && isspace(*sp)) - ++sp; + sp++; } if (*sp == '"') quoted = !quoted; if (!rem && !quoted) { - token = gettoken(&sp); + /* Try the standard tokens. */ + token = gettoken(&sp, tokens, tokens_size); if (token != -1) { + token += TOKEN_BASE; + if (token == TOKEN_REM) rem = 1; + + *dp++ = (unsigned char)token; + continue; + } + +#ifdef USE_EXTEND + /* Try the extended CE-prefixed tokens. */ + token = gettoken(&sp, tokens_ce, tokens_ce_size); + if (token != -1) { + *dp++ = (unsigned char)PREFIX_1; *dp++ = (unsigned char)token; continue; } + + /* Try the extended FE-prefixed tokens. */ + token = gettoken(&sp, tokens_fe, tokens_fe_size); + if (token != -1) { + *dp++ = (unsigned char)PREFIX_2; + *dp++ = (unsigned char)token; + continue; + } +#endif + } + + if ((*sp == '{') && (*(sp+3) == '}')) { + /* + * Escaped non-printable character. + * + * For now, we escape such characters simply by + * converting them to a hexadecimal number, and + * then enclose them between { and } characters. + * + * This works fine, but maybe we should use the + * naming as mentioned in + * + * https://www.c64-wiki.com/wiki/PETSCII_Codes_in_Listings + * + * or such? + */ + token = (int)hexstrtoul(sp+1, (char **)&sp); + if (*sp != '}') { + fprintf(stderr, "Invalid format in escape!\n"); + return 0; + } + sp++; + + *dp++ = (unsigned char)token; + + continue; } #ifdef _DEBUG @@ -105,7 +177,7 @@ tokenize(unsigned char *dest, const char *src) *sp, *sp, rem?" (rem)":"", quoted?" (quoted)":""); #endif - if (*src != '\r') + if (*sp != '\r') *dp++ = (unsigned char)*sp++; } @@ -116,8 +188,8 @@ tokenize(unsigned char *dest, const char *src) *dp++ = 0; #ifdef _DEBUG - if (debug) - fprintf(stderr, "\n"); + if (debug) + fprintf(stderr, "\n"); #endif return dp - dest; @@ -138,28 +210,26 @@ main(int argc, char **argv) unsigned char tokline[MAXLINELEN]; // tokenized line unsigned char *tp; // pointer in tokenized line char line[MAXLINELEN]; // source line - char *cp; + char *cp, *ep; long linenum; int lastlinenum = -1; int toklinelen; - int c; FILE *fi, *fo; char *out_name; + int c; /* Set defaults. */ autonumber = 0; collapsespaces = 0; -#ifdef _DEBUG - debug = 0; -#endif + debug = verbose = 0; invertcase = 0; - startaddr = 0x0801; + startaddr = 0x0801; // $1C01 for C128/BASICv7 trimspaces = 0; out_name = NULL; /* Process commandline arguments. */ opterr = 0; - while ((c = getopt(argc, argv, "acdio:s:t")) != EOF) switch (c) { + while ((c = getopt(argc, argv, "acdio:s:tv")) != EOF) switch (c) { case 'a': // auto-number autonumber ^= 1; break; @@ -169,11 +239,7 @@ main(int argc, char **argv) break; case 'd': // debug-level -#ifdef _DEBUG debug++; -#else - fprintf(stderr, "Debugging not compiled in.\n"); -#endif break; case 'i': // invert-case @@ -185,19 +251,26 @@ main(int argc, char **argv) break; case 's': // start-address - (void)sscanf(optarg, "0x%x", &startaddr); - (void)sscanf(optarg, "$%x", &startaddr); - (void)sscanf(optarg, "$%X", &startaddr); + if (*optarg == '0' && *(optarg+1) == 'x') + optarg += 2; + else if (*optarg == '$') + optarg++; + startaddr = hexstrtoul(optarg, NULL); + printf("Start address is $%04X\n", startaddr); break; case 't': // trim-spaces trimspaces ^= 1; break; + case 'v': // output noise level + verbose++; + break; + default: usage: fprintf(stderr, - "Usage: bas2prg [-acdit] [-s addr] [-o outfile] filename\n"); + "Usage: bas2prg [-acditv] [-s addr] [-o outfile] filename\n"); exit(1); } @@ -236,8 +309,10 @@ main(int argc, char **argv) if (optind != argc) goto usage; - /* load address */ - fprintf(stderr, "Load address: $%04X\n", startaddr); + /* Show the load address. */ + if (verbose) + fprintf(stderr, "Load address: $%04X\n", startaddr); + tp = tokline; putword(startaddr, &tp); fwrite(tokline, 1, 2, fo); @@ -245,22 +320,25 @@ main(int argc, char **argv) for (;;) { if (! fgets(line, MAXLINELEN, fi)) break; - - /* trim off the newline */ + + /* Trim off the newline. */ cp = &line[strlen(line)-1]; if (*cp == '\n') *cp = '\0'; + cp = &line[strlen(line)-1]; + if (*cp == '\r') + *cp = '\0'; if (invertcase) { for (cp = line; *cp; ++cp) { - int c = *cp; + c = *cp; if (isupper(c)) *cp = tolower(c); else if (islower(c)) *cp = toupper(c); } } - + linenum = strtol(line, &cp, 10); /* @@ -292,24 +370,28 @@ main(int argc, char **argv) lastlinenum = linenum; - /* trim extraneous whitespace at the beginning and end */ + /* Trim extraneous whitespace at the beginning and end. */ if (trimspaces) { - char *ep = &line[strlen(line)-1]; + ep = &line[strlen(line)-1]; + while (ep >= line && isspace(*ep)) - --ep; + ep--; ep[1] = '\0'; while (*cp && isspace(*cp)) - ++cp; + cp++; } - + tp = &tokline[2]; /* skip first word for now */ putword(linenum, &tp); - + toklinelen = tokenize(tp, cp); #ifdef _DEBUG if (debug) fprintf(stderr, "line length: %i\n", toklinelen); #endif + if (toklinelen == 0) + break; + startaddr += toklinelen + 4; tp = tokline; putword(startaddr, &tp); @@ -320,6 +402,8 @@ main(int argc, char **argv) putword(0, &tp); fwrite(tokline, 1, 2, fo); + fflush(stdout); + if (fo != stdout) fclose(fo); diff --git a/src/prg2bas.c b/src/prg2bas.c index 1580d4a..cb8aedc 100644 --- a/src/prg2bas.c +++ b/src/prg2bas.c @@ -1,6 +1,8 @@ /* - * prg2bas.c, convert a C64 PRG file to a BASIC text file. + * prg2bas.c, convert a C64/128 PRG file to a BASIC text file. + * * Copyright 2011, 2012 Christopher Williams + * Copyright 2023 Fred N. van Kempen, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,14 +24,14 @@ # include # include #endif +#include #include #include "tokens.h" #include "version.h" -#ifdef _DEBUG -static int debug = 0; // debug level -#endif +static int debug; // debug level +static int verbose; // output noise level /* @@ -42,12 +44,12 @@ getword(FILE *fp) unsigned int x; int n; - n = getc(fp); - if (n < 0) + if (feof(fp) || ferror(fp) || ((n = getc(fp)) == EOF)) return -1; + x = n; - n = getc(fp); - if (n < 0) + + if (feof(fp) || ferror(fp) || ((n = getc(fp)) == EOF)) return -1; return x | (n << 8); @@ -58,41 +60,38 @@ int main(int argc, char **argv) { long addr, line; - int quoted; - int c; - FILE *fi, *fo; char *out_name; + FILE *fi, *fo; + int c, quoted; /* Set defaults. */ -#ifdef _DEBUG - debug = 0; -#endif + debug = verbose = 0; out_name = NULL; /* Process commandline arguments. */ opterr = 0; - while ((c = getopt(argc, argv, "do:")) != EOF) switch (c) { - case 'd': // debug-level -#ifdef _DEBUG + while ((c = getopt(argc, argv, "do:v")) != EOF) switch (c) { + case 'd': // debug level debug++; -#else - fprintf(stderr, "Debugging not compiled in.\n"); -#endif break; case 'o': // output-file out_name = optarg; break; + case 'v': // output level + verbose++; + break; + default: usage: - fprintf(stderr, "Usage: prg2bas [-d] [-o outfile] filename\n"); + fprintf(stderr, "Usage: prg2bas [-dv] [-o outfile] filename\n"); exit(1); } /* If we have an output filename, open it. */ if (out_name != NULL) { - fo = fopen(out_name, "wb"); + fo = fopen(out_name, "w"); if (fo == NULL) { fprintf(stderr, "Unable to create output '%s'\n", out_name); return(2); @@ -102,7 +101,7 @@ main(int argc, char **argv) /* If we have a filename, use it. */ if (optind < argc) { - fi = fopen(argv[optind], "r"); + fi = fopen(argv[optind], "rb"); if (fi == NULL) { fprintf(stderr, "Unable to open input '%s'\n", argv[optind]); if (fo != stdout) { @@ -127,48 +126,120 @@ main(int argc, char **argv) /* Get load address. */ addr = getword(fi); - - fprintf(stderr, "Load address: 0x%04lx\n", addr); + if (verbose) + fprintf(stderr, "Load address: $%04lX\n", addr); for (;;) { /* Get next line address. */ addr = getword(fi); if (addr <= 0) break; + if (debug) + fprintf(stderr, "$%04lX ", addr); /* Get line number. */ line = getword(fi); + if (debug) + fprintf(stderr, "%li ", line); fprintf(fo, "%li", line); + /* + * I have seen malformed PRG files with a next-address word, + * but then no more line number or line data. So, we check. + */ + if (line <= 0) + break; + quoted = 0; for (;;) { - c = fgetc(fi); - if (c == 0) - break; - - if (c < 0) + /* Get a byte from the input file. */ + if (feof(fi) || ferror(fi) || ((c = fgetc(fi)) == EOF)) { + if (debug) + fprintf(stderr, "*** END OF FILE\n"); goto end; + } + + /* End of line? */ + if (c == 0x00) + break; + /* Handle quotes. */ if (c == '"') quoted = !quoted; - if (!quoted && c >= 0x80) { - fprintf(fo, "%s", tokens[c - 0x80]); -#ifdef _DEBUG - if (debug) - fprintf(stderr, "TOKEN{0x%02x}", c); + if (quoted) { + /* Inside a string, try to escape special characters. */ + if (isprint(c)) + fputc(c, fo); + else + fprintf(fo, "{%02X}", c); + + continue; + } + + if (debug > 1) + fprintf(stderr, "Token $%02X", c); + + if (c == PREFIX_1) { + /* Special token: prefix to alternate table 1. */ + if (verbose) + fprintf(stderr, "Prefix %02X found!\n", c); + + /* Get next byte. */ + if (feof(fi) || ferror(fi) || ((c = fgetc(fi)) == EOF)) + goto end; + + if (debug > 1) + fprintf(stderr, "Extended token $%02X", c); +#ifdef USE_EXTEND + if (c < tokens_ce_size) + fprintf(fo, "%s", tokens_ce[c]); + else { +#endif + fprintf(stderr, "*** unknown extended token $%02X $%02X found!\n", PREFIX_1, c); + fprintf(fo, "{%02X}{%02X}", PREFIX_1, c); +#ifdef USE_EXTEND + } +#endif + } else if (c == PREFIX_2) { + /* Special token: prefix to alternate table 2. */ + if (verbose) + fprintf(stderr, "Prefix %02X found!\n", c); + + /* Get next byte. */ + if (feof(fi) || ferror(fi) || ((c = fgetc(fi)) == EOF)) + goto end; + + if (debug > 1) + fprintf(stderr, "Extended token $%02X", c); +#ifdef USE_EXTEND + if (c < tokens_fe_size) + fprintf(fo, "%s", tokens_fe[c]); + else { +#endif + fprintf(stderr, "*** unknown extended token $%02X $%02X found!\n", PREFIX_2, c); + fprintf(fo, "{%02X}{%02X}", PREFIX_2, c); +#ifdef USE_EXTEND + } #endif } else { - fputc(c, fo); + if (c >= TOKEN_BASE) + fprintf(fo, "%s", tokens[c - TOKEN_BASE]); + else if (c != '\r') + fputc(c, fo); } } + if (debug) + fputc('\n', stderr); fputc('\n', fo); } +end: + fflush(fo); + if (fo != stdout) fclose(fo); -end: return 0; } diff --git a/src/tokens.c b/src/tokens.c index 2955938..f6036d9 100644 --- a/src/tokens.c +++ b/src/tokens.c @@ -19,7 +19,9 @@ #include "tokens.h" -const char *tokens[128] = { +/* Standard CBM tokens, for all versions of BASIC. */ +const char *tokens[] = { + // Standard, block $80 "END", // 80 "FOR", // 81 "NEXT", // 82 @@ -29,6 +31,7 @@ const char *tokens[128] = { "DIM", // 86 "READ", // 87 + // Standard, block $88 "LET", // 88 "GOTO", // 89 "RUN", // 8a @@ -38,6 +41,7 @@ const char *tokens[128] = { "RETURN", // 8e "REM", // 8f + // Standard, block $90 "STOP", // 90 "ON", // 91 "WAIT", // 92 @@ -47,6 +51,7 @@ const char *tokens[128] = { "DEF", // 96 "POKE", // 97 + // Standard, block $98 "PRINT#", // 98 "PRINT", // 99 "CONT", // 9a @@ -56,6 +61,7 @@ const char *tokens[128] = { "SYS", // 9e "OPEN", // 9f + // Standard, block $A0 "CLOSE", // a0 "GET", // a1 "NEW", // a2 @@ -65,6 +71,7 @@ const char *tokens[128] = { "SPC(", // a6 "THEN", // a7 + // Standard, block $A8 "NOT", // a8 "STEP", // a9 "+", // aa @@ -74,6 +81,7 @@ const char *tokens[128] = { "^", // ae "AND", // af + // Standard, block $B0 "OR", // b0 ">", // b1 "=", // b2 @@ -83,6 +91,7 @@ const char *tokens[128] = { "ABS", // b6 "USR", // b7 + // Standard, block $B8 "FRE", // b8 "POS", // b9 "SQR", // ba @@ -92,6 +101,7 @@ const char *tokens[128] = { "COS", // be "SIN", // bf + // Standard, block $C0 "TAN", // c0 "ATN", // c1 "PEEK", // c2 @@ -101,66 +111,162 @@ const char *tokens[128] = { "ASC", // c6 "CHR$", // c7 + // Standard, block $C8 "LEFT$", // c8 "RIGHT$", // c9 "MID$", // ca "GO", // cb - "{cc}", - "{cd}", - "{ce}", - "{cf}", - - "{d0}", - "{d1}", - "{d2}", - "{d3}", - "{d4}", - "{d5}", - "{d6}", - "{d7}", - - "{d8}", - "{d9}", - "{da}", - "{db}", - "{dc}", - "{dd}", - "{de}", - "{df}", - - "{e0}", - "{e1}", - "{e2}", - "{e3}", - "{e4}", - "{e5}", - "{e6}", - "{e7}", - - "{e8}", - "{e9}", - "{ea}", - "{eb}", - "{ec}", - "{ed}", - "{ee}", - "{ef}", - - "{f0}", - "{f1}", - "{f2}", - "{f3}", - "{f4}", - "{f5}", - "{f6}", - "{f7}", - - "{f8}", - "{f9}", - "{fa}", - "{fb}", - "{fc}", - "{fd}", - "{fe}", - "{pi}" // ff + +#ifdef USE_EXTEND + // Extended, for V3.5 and V7.0 + "RGR", // V3.5/7.0 + "RCLR", // V3.5/7.0 + "RLNUM", // V3.5; prefix CE for V7.0 + "JOY", // V3.5/7.0 + + // Extended, for V3.5 and V7.0 + "RDOT", + "DEC", + "HEX$", + "ERR$", + "INSTR", + "ELSE", + "RESUME", + "TRAP", + + // Extended, for V3.5 and V7.0 + "TRON", + "TROFF", + "SOUND", + "VOL", + "AUTO", + "PUDEF", + "GRAPHIC", + "PAINT", + + // Extended, for V3.5 and V7.0 + "CHAR", + "BOX", + "CIRCLE", + "GSHAPE", + "SSHAPE", + "DRAW", + "LOCATE", + "COLOR", + + // Extended, for V3.5 and V7.0 + "SCNCLR", + "SCALE", + "HELP", + "DO", + "LOOP", + "EXIT", + "DIRECTORY", + "DSAVE", + + // Extended, for V3.5 and V7.0 + "DLOAD", + "HEADER", + "SCRATCH", + "COLLECT", + "COPY", + "RENAME", + "BACKUP", + "DELETE", + + // Extended, for V3.5 and V7.0 + "RENUMBER", + "xKEY", + "MONITOR", + "USING", + "UNTIL", + "WHILE", + "{fe}", // prefix FE for V7.0 + "{pi}" +#endif }; + +#ifdef USE_EXTEND +// Extended keywords, for V3.5 and V7.0, using the $CE prefix. +const char *tokens_ce[] = { + "{00}", + "{01}", + "POT", + "BUMP", + "PEN", + "RSPPOS", + "RSPRITE", + "RSPCOLOR", + "XOR", + "RWINDOW", + "POINTER" +}; + +// Extended keywords, for V7.0, using the $FE prefix. +const char *tokens_fe[] = { + "${00}", + "${01}", + "BANK", + "FILTER", + "PLAY", + "TEMPO", + "MOVSPR", + "SPRITE", + "SPRCOLOR", + "RREG", + "ENVELOPE", + "SLEEP", + "CATALOG", + "DOPEN", + "APPEND", + "DCLOSE", + "BSAVE", + "BLOAD", + "RECORD", + "CONCAT", + "DVERIFY", + "DCLEAR", + "SPRSAV", + "COLLISION", + "BEGIN", + "BEND", + "WINDOW", + "BOOT", + "WIDTH", + "SPRDEF", + "QUIT", + "STASH", + "{20}", + "FETCH", + "{22}", + "SWAP", + "OFF", + "FAST", + "SLOW" + + /* Extensions for V7.1. */ + "CWIND", + "SSCRN", + "LSCRN", + "HIDE", + "SHOW", + "SFONT", + "LFONT", + "VIEW", + "FCOPY", + "ESAVE", + "SEND", + "CHECK", + "ESC", + "OLD", + "FIND", + "DUMP", + "MERGE" +}; +#endif + +int tokens_size = sizeof(tokens) / sizeof(const char *); +#ifdef USE_EXTEND +int tokens_ce_size = sizeof(tokens_ce) / sizeof(const char *); +int tokens_fe_size = sizeof(tokens_fe) / sizeof(const char *); +#endif diff --git a/src/tokens.h b/src/tokens.h index f0f7a46..fa1ee73 100644 --- a/src/tokens.h +++ b/src/tokens.h @@ -20,7 +20,23 @@ # define _TOKENS_H_ -extern const char *tokens[128]; +#define TOKEN_BASE 0x80 +# define TOKEN_REM 0x8f + +#define PREFIX_1 0xce // prefix for V3.5 and V7.0 +#define PREFIX_2 0xfe // prefix for V7.0 and V7.1 + + +extern const char *tokens[]; +extern int tokens_size; + +#ifdef USE_EXTEND +extern const char *tokens_ce[]; +extern int tokens_ce_size; + +extern const char *tokens_fe[]; +extern int tokens_fe_size; +#endif #endif /*_TOKENS_H*/ diff --git a/src/Makefile.GCC b/src/unix/Makefile.GCC similarity index 85% rename from src/Makefile.GCC rename to src/unix/Makefile.GCC index 1169cd2..7a1e8c6 100644 --- a/src/Makefile.GCC +++ b/src/unix/Makefile.GCC @@ -2,10 +2,13 @@ ARCH = +DEFS = -DUSE_EXTEND + + CC = gcc LINK = gcc -CFLAGS = $(ARCH) -Wall -O3 +CFLAGS = $(ARCH) -Wall -O3 $(DEFS) LDFLAGS = $(ARCH) -s diff --git a/src/version.h b/src/version.h index 6ed4a3b..4cd9e4e 100644 --- a/src/version.h +++ b/src/version.h @@ -3,7 +3,7 @@ * * Define application version and build info. * - * Version: @(#)version.h 1.0.1 2023/04/10 + * Version: @(#)version.h 1.0.3 2023/04/13 * * Author: Fred N. van Kempen, * @@ -40,7 +40,7 @@ /* Version info. */ #define APP_VER_MAJOR 1 #define APP_VER_MINOR 0 -#define APP_VER_REV 1 +#define APP_VER_REV 3 #define APP_VER_PATCH 0 diff --git a/src/Makefile.MinGW b/src/win32/Makefile.MinGW similarity index 78% rename from src/Makefile.MinGW rename to src/win32/Makefile.MinGW index 356bfe8..50e7174 100644 --- a/src/Makefile.MinGW +++ b/src/win32/Makefile.MinGW @@ -2,11 +2,14 @@ ARCH = +DEFS = -DUSE_EXTEND + + CC = gcc LINK = gcc RC = windres -CFLAGS = $(ARCH) -Wall -O3 -Iwin32 +CFLAGS = $(ARCH) -Wall -O3 -Iwin32 $(DEFS) LDFLAGS = $(ARCH) -s RCOPTS = -O coff @@ -31,11 +34,11 @@ all: $(PROGS) prg2bas.exe: prg2bas.o tokens.o prg2bas.res @echo Linking $@ .. - @$(LINK) $(LFLAGS) -o $@ $< tokens.o prg2bas.res + @$(LINK) $(LDFLAGS) -o $@ $< tokens.o prg2bas.res bas2prg.exe: bas2prg.o tokens.o bas2prg.res @echo Linking $@ .. - @$(LINK) $(LFLAGS) -o $@ $< tokens.o bas2prg.res + @$(LINK) $(LDFLAGS) -o $@ $< tokens.o bas2prg.res .PHONY: clean diff --git a/src/Makefile.VC b/src/win32/Makefile.VC similarity index 62% rename from src/Makefile.VC rename to src/win32/Makefile.VC index 2483ebe..1cc30dc 100644 --- a/src/Makefile.VC +++ b/src/win32/Makefile.VC @@ -1,10 +1,13 @@ # Makefile for Windows systems using Microsoft Visual Studio. +DEFS = -DUSE_EXTEND + + CC = cl /nologo LINK = link /nologo RC = rc /nologo -CFLAGS = -O1 -Iwin32 +CFLAGS = -O1 -Iwin32 $(DEFS) LDFLAGS = /SUBSYSTEM:console /DEBUG:NONE RCOPTS = /r @@ -25,13 +28,13 @@ VPATH = win32 . all: prg2bas.exe bas2prg.exe -prg2bas.exe: prg2bas.obj tokens.obj getopt.obj prg2bas.res +prg2bas.exe: prg2bas.res prg2bas.obj tokens.obj getopt.obj @echo Linking $@ - @$(LINK) /OUT:$@ $(LDFLAGS) prg2bas tokens getopt prg2bas.res + @$(LINK) /OUT:$@ $(LDFLAGS) prg2bas.res prg2bas tokens getopt -bas2prg.exe: bas2prg.obj tokens.obj getopt.obj bas2prg.res +bas2prg.exe: bas2prg.res bas2prg.obj tokens.obj getopt.obj @echo Linking $@ - @$(LINK) /OUT:$@ $(LDFLAGS) bas2prg tokens getopt bas2prg.res + @$(LINK) /OUT:$@ $(LDFLAGS) bas2prg.res bas2prg tokens getopt .PHONY: clean diff --git a/tests/calc.bas b/tests/calc.bas new file mode 100644 index 0000000..16d23f2 --- /dev/null +++ b/tests/calc.bas @@ -0,0 +1,101 @@ +10REM: CALC-128...WDM...02/15/86 +20: +30: +40: +50FORX=1TO8:KEYX,CHR$(132+X):NEXT:REM:KEY8,"LIST4500-4599"+CHR$(13) +60SCNCLR:COLOR4,12:COLOR1,1 +70PRINT"{12}{1E} {99} {92}" +80PRINT"{12}{1E} {12} {99} {92} {12} {92}" +90PRINT"{12}{1E} {9E} {1E} KEY FUNCTION {99} {92} {12} {92}" +100PRINT"{12}{1E} {9E} {1E} {A3}{A3}{A3} {A3}{A3}{A3}{A3}{A3}{A3}{A3}{A3} {99} {92} {12} {92}" +110PRINT"{12}{1E} {9E} {1E} + ADDITION {99} {92} {12} {92}" +120PRINT"{12}{1E} {9E} {1E} {99} {92} {12} {92}" +130PRINT"{12}{1E} - SUBTRACT {99} {92} {12} {92}" +140PRINT"{12}{1E} {9E} {1E} {99} {92} {12} {92}" +150PRINT"{12}{1E} {9E} {1E} {9E} {1E} F1 MULTIPLY {99} {92} {12} {92}" +160PRINT"{12}{1E} {9E} {1E} {99} {92} {12} {92}" +170PRINT"{12}{1E} {9E} {1E} {9E} {1E} F3 DIVISION {99} {92} {12} {92}" +180PRINT"{12}{1E} {99} {92} {12} {92}" +190PRINT"{12}{1E} {9E} {1E} F5 CL ENTRY {99} {92} {12} {92}" +200PRINT"{12}{1E} {9E} {1E} {99} {92} {12} {92}" +210PRINT"{12}{1E} {9E} {1E} F7 SUBTOTAL {99} {92} {12} {92}" +220PRINT"{12}{1E} {9E} {1E} {99} {92} {12} {92}" +230PRINT"{12}{1E} F2 PERCENT% {99} {92} {12} {92}" +240PRINT"{12}{1E} {9E} {1E} {99} {92} {12} {92}" +250PRINT"{12}{1E} {9E} {1E} F4 $ OR FLT {99} {92} {12} {92}" +260PRINT"{12}{1E} {9E} {1E} {99} {92} {12} {92}" +270PRINT"{12}{1E} {9E} {1E} F6 CLR SCRN {99} {92} {12} {92}" +280PRINT"{12}{1E} {99} {92} {12} {92}" +290PRINT"{12}{1E} {9E}128{1E} F8 GRANDTTL {99} {12} {92}" +300PRINT"{12}{1E} {99} {92} {12} {92}" +310PRINT"{12}{1E} ESC TWICE TO END {99} {12} {92}";:LI$=" {C0}{C0}{C0}{C0}{C0}{C0}{C0}{C0}{C0}{C0}{C0}{C0}{C0}{C0}{C0}" +320WINDOW21,1,37,21,1:CT=1967:POKE56027,13:DT=1:PU$=" ###,###,###.##" +330GETKEYA$ +340IFASC(A$)=27THEN8998 +350IFASC(A$)=43THEN1000 +360IFASC(A$)=45THEN1500 +370IFASC(A$)=133THEN2000 +380IFASC(A$)=135THEN2500 +390IFASC(A$)=137THENGOSUB3000:GOTO330 +400IFASC(A$)=139THEN3500 +410IFASC(A$)=134THEN4000 +420IFASC(A$)=136THEN4500 +430IFASC(A$)=138THEN5000 +440IFASC(A$)=140THEN5500 +450IFASC(A$)=46THEN6000 +460IFASC(A$)<48ORASC(A$)>57THEN330:ELSEGOTO6500 +1000REM:**** ADDITION **** +1010NE=VAL(NE$):SB=SB+NE:GT=SB:PO=1 +1020PRINTUSINGPU$;NE;:PRINT" +";:CT=1967:GOTO330 +1500REM:**** SUBTRACT **** +1510NE=VAL(NE$):SB=SB-NE:GT=SB:PO=2 +1520PRINTUSINGPU$;NE;:PRINT" -";:CT=1967:GOTO330 +2000REM:**** MULTIPLY **** +2010NE=VAL(NE$):IFSB=0THENSB=NE:GOTO2020:ELSESB=SB*NE +2020PRINTUSINGPU$;NE;:PRINT" *";:CT=1967:PO=3:GOTO330 +2500REM:**** DIVISION **** +2510NE=VAL(NE$):IFSB=0THENSB=NE:GOTO2520:ELSESB=SB/NE +2520PRINTUSINGPU$;NE;:PRINT" /";:CT=1967:PO=4:GOTO330 +3000REM:**** CL ENTRY **** +3010FORX=1967TO1978:POKEX,32:NEXT:RETURN +3500REM:**** SUBTOTAL **** +3510IFPO<3THENPRINTLI$:PRINTUSINGPU$;SB;:GOTO3590 +3520IFPO=3THENBEGIN:NE=VAL(NE$):SB=SB*NE:PRINTUSINGPU$;NE; +3521BEND:PRINT" _";:PRINTLI$:PRINTUSINGPU$;SB;:SB=0:GOTO3590 +3530IFPO=4THENBEGIN:NE=VAL(NE$):SB=SB/NE:PRINTUSINGPU$;NE; +3531BEND:PRINT" _";:PRINTLI$:PRINTUSINGPU$;SB;:SB=0:GOTO3590 +3590PRINT" S":PRINT:CT=1967:PO=0:NE=0:NE$="":GOSUB3000:GOTO330 +4000REM:**** PERCENT% **** +4005IFPO<3THEN330 +4010NE=VAL(NE$):PC=NE/100:IFPO=3THENSB=SB*PC:GOTO4020:ELSESB=SB/PC +4020PRINTUSINGPU$;NE;:PRINT" %";:PRINTLI$:CT=1967 +4030PRINTUSINGPU$;SB;:PRINT" S":PRINT:SB=0:NE=0:NE$="":GOSUB3000:GOTO330 +4500REM:**** $ OR FLT **** +4510IFDT=1THENPOKE56027,5:POKE56032,13:POKE56033,13:POKE56034,13:DT=0:GOTO4530 +4520IFDT=0THENPOKE56032,5:POKE56033,5:POKE56034,5:POKE56027,13:DT=1 +4530IFDT=1THENPU$=" ###,###,###.##":GOTO4550 +4540IFDT=0THENPU$=" ###,###.######" +4550GOTO330 +5000REM:**** CLR SCRN **** +5010SCNCLR:GT=0:SB=0:NE=0:NE$="":GOTO330 +5500REM:**** GRANDTTL **** +5510IFPO<3THENPRINTLI$:PRINTUSINGPU$;GT;:GOTO5590 +5520IFPO=3THENBEGIN:NE=VAL(NE$):GT=SB*NE:PRINTUSINGPU$;NE; +5521BEND:PRINT" _";:PRINTLI$:PRINTUSINGPU$;GT;:GOTO5590 +5530IFPO=4THENBEGIN:NE=VAL(NE$):GT=SB/NE:PRINTUSINGPU$;NE; +5531BEND:PRINT" _";:PRINTLI$:PRINTUSINGPU$;GT;:GOTO5590 +5590PRINT" G";:CT=1967:PO=0:NE=0:NE$="":SB=0:GT=0 +5591PRINT" ===============":PRINT:PRINT:GOSUB3000:GOTO330 +6000REM:**** DECPOINT **** +6005IFCT=1967THENGOSUB3000:NE$="":NE=0 +6010DP=0:FORX=1967TOCT:IFPEEK(X)=46THENDP=1:NEXT:ELSENEXT +6020IFDP=1THEN330:ELSEPOKECT,46:CT=CT+1:NE$=NE$+A$:GOTO330 +6500REM:**** NUMBERS. **** +6505IFCT=1967THENGOSUB3000:NE$="":NE=0 +6510DP=0:FORX=1967TOCT:IFPEEK(X)=46THENDP=1:NEXT:ELSENEXT +6520IFCT=1979THEN330 +6530IFCT=1976ANDDP=0THEN330:ELSEPOKECT,ASC(A$):CT=CT+1:NE$=NE$+A$:GOTO330 +8997GOTO330 +8998GETKEYA$:IFASC(A$)=27THEN8999:ELSEGOTO330 +8999PRINTCHR$(19);CHR$(19):SCNCLR:END +9000SCRATCH"CALC-128":DSAVE"CALC-128":END diff --git a/tests/calc.prg b/tests/calc.prg new file mode 100644 index 0000000000000000000000000000000000000000..acadb140a3e10daf0be935e365b0a3d2b169b07c GIT binary patch literal 3750 zcmbVPYitx%6uwObf`m2&n@X~6r!%upwq;)Xy4A$(>~2|Ewwv9lZ8gC^m`FlM(U=fJ z6dFOBs#Ibm3rPbhX0(EjEh0Y%!K##~F@^**$WH$!HRe^jyI%-2E(%fQI@6#gLnk*BbIoW&1FlP5)p(>C6Y+6Yt{8d9wr6Ri03IZ zhp#JPQNq{7u!z3)WKfx1)dnHVyUOz{46~m_H$4S6$SS7H?4O5PMNn+(^9E`Ho&z1k z$uODzysBmU^6s#q*3A}-#yB}S$uKa`%N;h}y3GM&)&grXmC7#8Hru+xg3%zIskr8L zw{^sT8S^{gtJL446_cvw9-`d#?xS#RZTrZAwJ{TBHumSVY#}$~&Ntb1*aALG zZdcl-ESSY7179lpT(-36$~Cp0Fn~r;9HVO`*SDXs*#MG>z@X-sbY3eilG~f@=N&Ky znR`i2wqJ0-GBE&>mFv@zId|Bf?N=<=!&H)Gt{E^IT}FbhLb|9zYF>A)A?Eg@aE&v6 zTQIt9G77NqOka`7~}iss^;NXq+{J>#F_BG*~cNkW3U%YOctX)Z!w0Tkd$$ zfYoqVx2{}UI#`nn)RJ_693Z{LWIm;7WXm!?udvKn=f$E<_L;hYZ2sne1gaaBV`1`*Rjnc>qA`3R5*4Ia zSd?)JEV$55d~h5WHa}e)$D%mNxj>Q+R-~97PbA`K6T8VWDY7mK zf`GQOFIP|Waz)8CU}A|91x0nM`G_LGDz zli7c;2W^|8E5z$9_NZ-(>f|s&_1x#SEv|z}KrP&h+W-OZ$)uUuuj{Dg( zO)K#)Y3g4^Vmd^GFOEhD=p}Bhejg6OWeYv|h2G}w)aTQc9|HL_AH=1eN~vcmpT@l! z+~7iAMMR%+ck9dlK-IhR>26$RnYC!Ql&Hu98jVV%PIu1;i`QMj2nASO&6oct6{?rPSsVd);slhw5g1o#y z+aDDZu{b_37?ve?b$K88y~kG86q$-J#bmrESB@3>re(bh+Y1}?rJin}iX;T|s`o5; zT#@yjbZ<6~?L@5lm=IW9bjbVd%AQ?hkza(s@AF40Y&t#f{gFCV7Iq4ysBwDR`_ltC zWqeB;fVsrta<#u8aDjwO2H5s2uGwq}-{D5HBGsdUR;1VWaidWYvM1m0O*Ps{7gXqw zpf{I5KyJ?OWRCL&PqS3LGUaKOeE};v)Gv*9ZZ7XtGC6|bA`aWzjBd#7d zG-AJhqG^n%n{J9-gsPq_5!E9mS+3r}>nT-2SPLZr`Mu!(gklAX1&E22tsBL>u{9L! z_E${41=W;zjtJ<8|Fr4ZF$?sTTr8UMpKGE5@SXpi)zyAB4>p?DRsa6gb3{x8A}!99 zq{w^zoy{h)dQ4HIC$P79URU>O(A5#r)!D!}dAvYZQ{{qPU(Vb`nxP|lp=YdPY+ftVijdl%rr>X6_S4Rb?1OS`!-s;yEoQM& zX|>Ro&1(!TRyO!r%b3Yy9!KVOA&rQJgRi%w3Q&GaT2u{>%>)L66*xfPfd~R~aRf~S z-)b2nKaGRqfU1R+C<$NHrbN2`!0A+yH#F><;N~@EF!}zCJ?Ubp%LwLDaLbxSg1Hym zw&nqXX$b8wy~N-gg*NXEjaY{07sP=bB|Y$511*HS9PPE?kdHH75wPR+AOVLYVb~M$ hb7lCum$5zpaWXfhPw?y%M$P!`{+x!;8l`VWmn(t-d0 literal 0 HcmV?d00001 From 04fc8ffbd9cfa266e17e0fe95ad31764a011983b Mon Sep 17 00:00:00 2001 From: waltje Date: Thu, 13 Apr 2023 23:13:12 -0400 Subject: [PATCH 2/2] Updated formatting of README.md. --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index ecae10f..d734f1e 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,17 @@ Usage Convert a BAS file to a PRG file: bas2prg program.prg + or + bas2prg -o program.prg program.bas Convert a PRG file to a BAS file: prg2bas program.bas + or + prg2bas -o program.bas program.prg whichever format feels better. @@ -29,7 +33,9 @@ Each program takes its own commandline options; you can see these by calling them with the -? argument: prg2bas -? + or + bas2prg -? @@ -39,9 +45,13 @@ How to build Run the following command: make -f unix/Makefile.GCC + or + make -f win32/Makefile.MinGW + or + make -f win32/Makefile.VC depending on your operating system and development tools.