diff --git a/2048/2048.ASM b/2048/2048.ASM index b2770f6..1b3a4e3 100644 --- a/2048/2048.ASM +++ b/2048/2048.ASM @@ -13,12 +13,15 @@ ; Compile: ; ; TASM -80 -b 2048.ASM 2048.COM +; -or- +; uz80as 2048.ASM 2048.COM ; ; Credits: ; ; Based on 2048 created by Gabriele Cirulli. ; Based on the console version for GNU/Linux by Maurits van der Schee ; Ported to Z80 and CP/M by Marco Maccaferri +; Slight VT100 compatibility changes by acn128 CTRL_A .EQU 1 @@ -945,7 +948,8 @@ DIV16: LD HL,0 LD B,16 -DL1 SLL C ; Undoc. 0CBH, 31H +DL1 SCF + RL C RLA ADC HL,HL SBC HL,DE @@ -1041,7 +1045,7 @@ POINTS .DW 0000H .DW 4096H .DW 8192H -INIT: .DB 12, 1BH, "[?25l", EOS +INIT: .DB 1BH, "[?25l", 1BH, "[2J", EOS TERM: .DB 1BH, "[?25h" RESET: .DB 1BH, "[0m", EOS @@ -1061,16 +1065,16 @@ BTMBORDER .DB 0DFH .DB 0DFH, 0DFH, 0DFH, 0DFH, 0DFH, 0DFH, 0DFH .DB 0DFH, EOS -MSG1: .DB 1BH, "[0m", - .DB " ", 11H, ",", 1EH, ",", 10H, ",", 1FH, " or q " +MSG1: .DB 1BH, "[0m" + .DB " <,>,^,v or q " .DB EOS MSG2: .DB 1BH, "[0m" - .DB " QUIT? (y/n) ", + .DB " QUIT? (y/n) " .DB EOS -MSG3: .DB 1BH, "[0m", +MSG3: .DB 1BH, "[0m" .DB " GAME OVER! " .DB BEL, CR, LF, EOS -MSG4: .DB 1BH, "[0m", +MSG4: .DB 1BH, "[0m" .DB "2048 pts" .DB EOS diff --git a/2048/2048.COM b/2048/2048.COM index e287e34..589dba7 100644 Binary files a/2048/2048.COM and b/2048/2048.COM differ diff --git a/2048/README.md b/2048/README.md index b4c63dc..2150a11 100644 --- a/2048/README.md +++ b/2048/README.md @@ -19,7 +19,15 @@ When two tiles with the same number touch, they merge into one. ## Compile: -TASM -80 -b 2048.ASM 2048.COM +If using TASM, the command is: ``TASM -80 -b 2048.ASM 2048.COM`` + +The "Micro Z80 Assembler" was used to assemble ``2048.COM`` on Linux. +It is provided in the directory ``uz80as``, just compile it using ``make``. + +The command for assembling 2048 is: ``uz80as 2048.ASM 2048.COM``. + +uz80as can be found here: https://github.com/jorgicor/uz80as +The uz80as directory here is taken from the RomWBW source tree. ## License: diff --git a/2048/uz80as/Makefile b/2048/uz80as/Makefile new file mode 100644 index 0000000..cc5e4da --- /dev/null +++ b/2048/uz80as/Makefile @@ -0,0 +1,67 @@ +# =========================================================================== +# uz80as, an assembler for the Zilog Z80 and several other microprocessors. +# =========================================================================== + +CC = gcc +CFLAGS = -g + +OBJECTS = ngetopt.o main.o options.o \ + utils.o err.o incl.o sym.o \ + expr.o exprint.o pp.o list.o \ + prtable.o uz80as.o targets.o \ + z80.o gbcpu.o \ + dp2200.o i4004.o \ + i8008.o i8048.o \ + i8051.o i8080.o \ + mos6502.o mc6800.o + +SOURCES = \ + config.h \ + ngetopt.c ngetopt.h \ + main.c \ + options.c options.h \ + utils.c utils.h \ + err.c err.h \ + incl.c incl.h \ + sym.c sym.h \ + expr.c expr.h \ + exprint.c exprint.h \ + pp.c pp.h \ + list.c list.h \ + prtable.c prtable.h \ + uz80as.c uz80as.h \ + targets.c targets.h \ + z80.c \ + gbcpu.c \ + dp2200.c \ + i4004.c \ + i8008.c \ + i8048.c \ + i8051.c \ + i8080.c \ + mos6502.c \ + mc6800.c + +all: uz80as + +clobber: clean + -rm -f uz80as uz80as + +clean: + -rm -f $(OBJECTS) + +uz80as: $(OBJECTS) + $(CC) $(CFLAGS) -o uz80as $(OBJECTS) + +TESTS=test*.asm + +test: uz80as + echo "running tests" + for i in $(TESTS) ; do \ + echo "====== $$i ======" ; \ + ./uz80as $$i ; \ + cat $$(basename $$i .asm).lst ; \ + done + +.c.o: + $(CC) $(CFLAGS) -I. -c $< -o $@ diff --git a/2048/uz80as/config.h b/2048/uz80as/config.h new file mode 100644 index 0000000..cca14cb --- /dev/null +++ b/2048/uz80as/config.h @@ -0,0 +1,29 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Years of copyright */ +#define COPYRIGHT_YEARS "2018" + +/* Name of package */ +#define PACKAGE "uz80as" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "jorge.giner@hotmail.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "uz80as" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "uz80as 1.10" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "uz80as" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "https://jorgicor.niobe.org/uz80as" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.10" + +/* Version number of package */ +#define VERSION "1.10" diff --git a/2048/uz80as/dp2200.c b/2048/uz80as/dp2200.c new file mode 100644 index 0000000..c659924 --- /dev/null +++ b/2048/uz80as/dp2200.c @@ -0,0 +1,208 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Datapoint 2200. + * =========================================================================== + */ + +/* + * Datapoint 2200 Version I, 2K to 8K mem (program counter 13 bits). + * Datapoint 2200 Version II, 2K to 16K mem (protram counter 14 bits). + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: ADA,ADB,ADC,ADD,ADH,ADL,ADM, + * ACA,ACB,ACC,ACD,ACH,ACL,ACM, + * SUA,SUB,SUC,SUD,SUH,SUL,SUM, + * SBA,SBB,SBC,SBD,SBH,SBL,SBM, + * NDA,NDB,NDC,NDD,NDH,NDL,NDM, + * XRA,XRB,XRC,XRD,XRH,XRL,XRM, + * ORA,ORB,ORC,ORD,ORH,ORL,ORM, + * CPA,CPB,CPC,CPD,CPH,CPL,CPM + * c: NOP,LAB,LAC,LAD,LAE,LAH,LAL,LAM, + * LBA,LBC,LBD,LBE,LBH,LBL,LBM, + * LCA,LCB,LCD,LCE,LCH,LCL,LCM, + * LDA,LDB,LDC,LDE,LDH,LDL,LDM, + * LEA,LEB,LEC,LED,LEH,LEL,LEM, + * LHA,LHB,LHC,LHD,LHE,LHL,LHM, + * LLA,LLB,LLC,LLD,LLE,LLH,LLM, + * LMA,LMB,LMC,LMD,LME,LMH,LML,HALT + * d: ADR,STATUS,DATA,WRITE,COM1,COM2,COM3,COM4 + * BEEP,CLICK,DECK1,DECK2, + * RBK,WBK,BSP,SF,SB,REWND,TSTOP + * e: RFC,RFS,RTC,RTS,RFZ,RFP,RTZ,RTP + * f: JFC,JFZ,JFS,JFP,JTC,JTZ,JTS,JTP + * g: AD,SU,ND,OR,AC,SB,XR,CP + * h: LA,LB,LC,LD,LE,LH,LL + * i: CFC,CFZ,CFS,CFP,CTC,CTZ,CTS,CTP + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: (op << 1) + lastbyte + * g: (op << 4) | lastbyte + */ + +const struct matchtab s_matchtab_dp2200[] = { + { "SLC", "02.", 3, 0 }, + { "SRC", "0A.", 3, 0 }, + { "RETURN", "07.", 3, 0 }, + { "INPUT", "41.", 3, 0 }, + { "b", "80c0.", 3, 0 }, + { "c", "C0c0.", 3, 0 }, + { "EX d", "51f0.", 3, 0 }, + { "e", "03b0.", 3, 0 }, + { "g a", "04b0.d1.", 3, 0, "e8" }, + { "h a", "06b0.d1.", 3, 0, "e8"}, + { "f a", "40b0.e1", 3, 0 }, + { "i a", "42b0.e1", 3, 0 }, + { "JMP a", "44.e0", 3, 0 }, + { "CALL a", "46.e0", 3, 0 }, + /* version II */ + { "BETA", "10.", 2, 0 }, + { "DI", "20.", 2, 0 }, + { "POP", "30.", 2, 0 }, + { "ALPHA", "18.", 2, 0 }, + { "EI", "28.", 2, 0 }, + { "PUSH", "38.", 2, 0 }, + { NULL, NULL }, +}; + +static const char *const bval[] = { +"ADA", "ADB", "ADC", "ADD", "ADE", "ADH", "ADL", "ADM", +"ACA", "ACB", "ACC", "ACD", "ACE", "ACH", "ACL", "ACM", +"SUA", "SUB", "SUC", "SUD", "SUE", "SUH", "SUL", "SUM", +"SBA", "SBB", "SBC", "SBD", "SBE", "SBH", "SBL", "SBM", +"NDA", "NDB", "NDC", "NDD", "NDE", "NDH", "NDL", "NDM", +"XRA", "XRB", "XRC", "XRD", "XRE", "XRH", "XRL", "XRM", +"ORA", "ORB", "ORC", "ORD", "ORE", "ORH", "ORL", "ORM", +"CPA", "CPB", "CPC", "CPD", "CPE", "CPH", "CPL", "CPM", +NULL }; + +static const char *const cval[] = { +"NOP", "LAB", "LAC", "LAD", "LAE", "LAH", "LAL", "LAM", +"LBA", "", "LBC", "LBD", "LBE", "LBH", "LBL", "LBM", +"LCA", "LCB", "", "LCD", "LCE", "LCH", "LCL", "LCM", +"LDA", "LDB", "LDC", "", "LDE", "LDH", "LDL", "LDM", +"LEA", "LEB", "LEC", "LED", "", "LEH", "LEL", "LEM", +"LHA", "LHB", "LHC", "LHD", "LHE", "", "LHL", "LHM", +"LLA", "LLB", "LLC", "LLD", "LLE", "LLH", "", "LLM", +"LMA", "LMB", "LMC", "LMD", "LME", "LMH", "LML", "HALT", +NULL }; + +static const char *const dval[] = { +"ADR", "STATUS", "DATA", "WRITE", "COM1", "COM2", "COM3", "COM4", +"", "", "", "", "BEEP", "CLICK", "DECK1", "DECK2", +"RBK", "WBK", "", "BSP", "SF", "SB", "REWND", "TSTOP", +NULL }; + +static const char *const eval[] = { "RFC", "RFZ", "RFS", "RFP", + "RTC", "RTZ", "RTS", "RTP", + NULL }; + +static const char *const fval[] = { "JFC", "JFZ", "JFS", "JFP", + "JTC", "JTZ", "JTS", "JTP", + NULL }; + +static const char *const gval[] = { "AD", "AC", "SU", "SB", + "ND", "XR", "OR", "CP", + NULL }; + +static const char *const hval[] = { "LA", "LB", "LC", "LD", + "LE", "LH", "LL", + NULL }; + +static const char *const ival[] = { "CFC", "CFZ", "CFS", "CFP", + "CTC", "CTZ", "CTS", "CTP", + NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval, + gval, hval, ival +}; + +static int match_dp2200(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'i') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_dp2200(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': b += (vs[i] << 1); break; + case 'g': b |= (vs[i] << 4); break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_dp2200(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_dp2200(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'n') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_dp2200 = { + .id = "dp2200", + .descr = "Datapoint 2200 Version I", + .matcht = s_matchtab_dp2200, + .matchf = match_dp2200, + .genf = gen_dp2200, + .pat_char_rewind = pat_char_rewind_dp2200, + .pat_next_str = pat_next_str_dp2200, + .mask = 1 +}; + +const struct target s_target_dp2200ii = { + .id = "dp2200ii", + .descr = "Datapoint 2200 Version II", + .matcht = s_matchtab_dp2200, + .matchf = match_dp2200, + .genf = gen_dp2200, + .pat_char_rewind = pat_char_rewind_dp2200, + .pat_next_str = pat_next_str_dp2200, + .mask = 2 +}; diff --git a/2048/uz80as/err.c b/2048/uz80as/err.c new file mode 100644 index 0000000..b6745ae --- /dev/null +++ b/2048/uz80as/err.c @@ -0,0 +1,196 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Error reporting. + * =========================================================================== + */ + +#include "config.h" +#include "err.h" +#include "incl.h" + +#ifndef ASSERT_H +#include +#endif + +#ifndef CTYPE_H +#include +#endif + +#ifndef STDARG_H +#include +#endif + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +#ifndef STRING_H +#include +#endif + +/* Max number of errors before halt. */ +#define MAXERR 64 + +int s_nerrors; + +static void eprfl(void) +{ + fprintf(stderr, "%s:%d: ", curfile()->name, curfile()->linenum); +} + +static void eprwarn(void) +{ + fputs(_("warning:"), stderr); + fputc(' ', stderr); +} + +/* Print the characters in [p, q[ to stderr. */ +void echars(const char *p, const char *q) +{ + while (*p != '\0' && p != q) { + fputc(*p, stderr); + p++; + } +} + +/* + * Print a space, an opening parenthesis, the characters in [p, q[, + * and a closing parenthesis to stderr. + */ +void epchars(const char *p, const char *q) +{ + fputs(" (", stderr); + echars(p, q); + fputs(")", stderr); +} + +/* + * Increments the number of errors, and exit with failure if + * maximum number of errors allowed is reached. + */ +void newerr(void) +{ + s_nerrors++; + if (s_nerrors >= MAXERR) { + eprogname(); + fprintf(stderr, _("exiting: too many errors")); + enl(); + exit(EXIT_FAILURE); + } +} + +static void evprint(int warn, const char *ecode, va_list args) +{ + if (nfiles() > 0) + eprfl(); + else + eprogname(); + + if (warn) + eprwarn(); + + assert(ecode != NULL); + vfprintf(stderr, ecode, args); +} + +/* Prints only the printable characters, the rst as space. */ +static void eprint_printable(const char *p) +{ + for (; *p != '\0'; p++) { + if (isprint(*p)) + putc(*p, stderr); + else + putc(' ', stderr); + } +} + +/* Prints the line and a marker pointing to the charcater q inside line. */ +void eprcol(const char *line, const char *q) +{ + putc(' ', stderr); + eprint_printable(line); + fputs("\n ", stderr); + while (line != q) { + putc(' ', stderr); + line++; + } + fputs("^\n", stderr); +} + +/* + * Like fprintf but prints to stderr. + * If we are parsing any file (incl.c), print first the file and the line. + * If not, print first the program name. + */ +void eprint(const char *ecode, ...) +{ + va_list args; + + va_start(args, ecode); + evprint(0, ecode, args); + va_end(args); +} + +/* Same as eprint, but print "warning: " before ecode str. */ +void wprint(const char *ecode, ...) +{ + va_list args; + + va_start(args, ecode); + evprint(1, ecode, args); + va_end(args); +} + +/* Print \n on stderr. */ +void enl(void) +{ + fputc('\n', stderr); +} + +/* Print the program name on stderr. */ +void eprogname(void) +{ + fprintf(stderr, PACKAGE": "); +} + +/* Call malloc, but if no memory, print that error and exit with failure. */ +void *emalloc(size_t n) +{ + void *p; + + if ((p = malloc(n)) == NULL) { + eprint(_("malloc fail\n")); + exit(EXIT_FAILURE); + } + // printf("emalloc: %d = %x\n", n, p); + return p; +} + +/* Call realloc, but if no memory, print that error and exit with failure. */ +void *erealloc(void *p, size_t n) +{ + // void *q = p; + if ((p = realloc(p, n)) == NULL) { + eprint(_("realloc fail\n")); + exit(EXIT_FAILURE); + } + // printf("erealloc: %x %d = %x\n", q, n, p); + return p; +} + +/* Call fopen, but if any error, print it and exit with failure. */ +FILE *efopen(const char *fname, const char *ops) +{ + FILE *fp; + + if ((fp = fopen(fname, ops)) == NULL) { + eprint(_("cannot open file %s\n"), fname); + exit(EXIT_FAILURE); + } + return fp; +} diff --git a/2048/uz80as/err.h b/2048/uz80as/err.h new file mode 100644 index 0000000..86d8e1d --- /dev/null +++ b/2048/uz80as/err.h @@ -0,0 +1,32 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Error reporting. + * =========================================================================== + */ + +#ifndef ERR_H +#define ERR_H + +#ifndef STDIO_H +#define STDIO_H +#include +#endif + +#define _(str) (str) + +extern int s_nerrors; + +void newerr(void); +void eprogname(void); +void echars(const char *p, const char *q); +void epchars(const char *p, const char *q); +void eprint(const char *ecode, ...); +void wprint(const char *ecode, ...); +void eprcol(const char *line, const char *q); +void enl(void); +void *emalloc(size_t n); +void *erealloc(void *p, size_t n); +FILE *efopen(const char *fname, const char *ops); + +#endif diff --git a/2048/uz80as/expr.c b/2048/uz80as/expr.c new file mode 100644 index 0000000..445e36f --- /dev/null +++ b/2048/uz80as/expr.c @@ -0,0 +1,459 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Expression parsing. + * =========================================================================== + */ + +#include "config.h" +#include "expr.h" +#include "utils.h" +#include "err.h" +#include "sym.h" + +#ifndef ASSERT_H +#include +#endif + +#ifndef CTYPE_H +#include +#endif + +#ifndef LIMITS_H +#include +#endif + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +/* Max nested expressions. */ +#define ESTKSZ 16 +#define ESTKSZ2 (ESTKSZ*2) + +/* Return -1 on syntax error. + * *p must be a digit already. + * *q points to one past the end of the number without suffix. + */ +static int takenum(const char *p, const char *q, int radix) +{ + int k, n; + + n = 0; + while (p != q) { + k = hexval(*p); + p++; + if (k >= 0 && k < radix) + n = n * radix + k; + else + return -1; + } + return n; +} + +/* Go to the end of a number (advance all digits or letters). */ +static const char *goendnum(const char *p) +{ + const char *q; + + for (q = p; isalnum(*q); q++) + ; + return q; +} + +/* + * Returns NULL on error. + * '*p' must be a digit already. + */ +static const char *getnum(const char *p, int *v) +{ + int n; + char c; + const char *q; + + assert(isdigit(*p)); + + n = 0; + q = goendnum(p) - 1; + if (isalpha(*q)) { + c = toupper(*q); + if (c == 'H') { + n = takenum(p, q, 16); + } else if (c == 'D') { + n = takenum(p, q, 10); + } else if (c == 'O') { + n = takenum(p, q, 8); + } else if (c == 'B') { + n = takenum(p, q, 2); + } else { + return NULL; + } + } else { + n = takenum(p, q + 1, 10); + } + + if (n < 0) + return NULL; + + *v = n; + return q + 1; +} + +/* + * Gets a number that was prefixed. + * Returns NULL on error. + */ +static const char *getpnum(const char *p, int radix, int *v) +{ + const char *q; + int n; + + q = goendnum(p); + n = takenum(p, q, radix); + if (n < 0) + return NULL; + *v = n; + return q; +} + +/* Left shift */ +static int shl(int r, int n) +{ + n &= int_precission(); + return r << n; +} + + +/* Portable arithmetic right shift. */ +static int ashr(int r, int n) +{ + n &= int_precission(); + if (r & INT_MIN) { + return ~(~r >> n); + } else { + return r >> n; + } +} + +/* Parses expression pointed by 'p'. + * If success, returns pointer to the end of parsed expression, and + * 'v' contains the calculated value of the expression. + * Returns NULL if a syntactic error has occurred. + * Operators are evaluated left to right. + * To allow precedence use parenthesis. + * 'linepc' is the program counter to consider when we find the $ current + * pointer location symbol ($). + * 'allowfr' stands for 'allow forward references'. We will issue an error + * if we find a label that is not defined. + * 'ecode' will be valid if NULL is returned. NULL can be passed as ecode. + * 'ep' is the pointer to the position where the error ocurred. NULL can be + * passed as ep. + */ +const char *expr(const char *p, int *v, int linepc, int allowfr, + enum expr_ecode *ecode, const char **ep) +{ + int si, usi, usl; + const char *q; + char last; + int stack[ESTKSZ2]; + int uopstk[ESTKSZ]; + int r, n; + struct sym *sym; + int err; + enum expr_ecode ec; + + ec = EXPR_E_NO_EXPR; + err = 0; + usi = 0; + si = 0; + r = 0; + last = 'V'; /* first void */ + usl = 0; +loop: + p = skipws(p); + if (*p == '(') { + if (last == 'n') { + goto end; + } else { + if (si >= ESTKSZ2) { + eprint(_("expression too complex\n")); + exit(EXIT_FAILURE); + } + stack[si++] = last; + stack[si++] = r; + stack[si++] = usl; + usl = usi; + p++; + r = 0; + last = 'v'; /* void */ + } + } else if (*p == ')') { + if (last != 'n') { + ec = EXPR_E_CPAR; + goto esyntax; + } else if (si == 0) { + goto end; + } else { + p++; + n = r; + usl = stack[--si]; + r = stack[--si]; + last = (char) stack[--si]; + goto oper; + } + } else if (*p == '+') { + p++; + if (last == 'n') + last = '+'; + } else if (*p == '-') { + if (last == 'n') { + p++; + last = '-'; + } else { + goto uoper; + } + } else if (*p == '~') { + goto uoper; + } else if (*p == '!') { + if (*(p + 1) == '=') { + if (last != 'n') { + ec = EXPR_E_OPER; + goto esyntax; + } else { + p += 2; + last = 'N'; + } + } else { + goto uoper; + } + } else if (*p == '*') { + if (last == 'n') { + last = *p++; + } else { + p++; + n = linepc; + goto oper; + } + } else if (*p == '/' || *p == '&' || *p == '|' + || *p == '^') + { + if (last != 'n') { + ec = EXPR_E_OPER; + goto esyntax; + } else { + last = *p++; + } + } else if (*p == '>') { + if (last != 'n') { + ec = EXPR_E_OPER; + goto esyntax; + } + p++; + if (*p == '=') { + last = 'G'; + p++; + } else if (*p == '>') { + last = 'R'; + p++; + } else { + last = '>'; + } + } else if (*p == '<') { + if (last != 'n') { + ec = EXPR_E_OPER; + goto esyntax; + } + p++; + if (*p == '=') { + last = 'S'; + p++; + } else if (*p == '<') { + last = 'L'; + p++; + } else { + last = '<'; + } + } else if (*p == '=') { + if (last != 'n') { + ec = EXPR_E_OPER; + goto esyntax; + } + p++; + if (*p == '=') + p++; + last = '='; + } else if (*p == '\'') { + if (last == 'n') + goto end; + p++; + n = *p++; + if (*p != '\'') { + ec = EXPR_E_CHAR; + goto esyntax; + } + p++; + goto oper; + } else if (*p == '$') { + if (last == 'n') + goto end; + p++; + if (hexval(*p) < 0) { + n = linepc; + goto oper; + } + q = getpnum(p, 16, &n); + if (q == NULL) { + p--; + ec = EXPR_E_HEX; + goto esyntax; + } + p = q; + goto oper; + } else if (*p == '@') { + if (last == 'n') + goto end; + p++; + q = getpnum(p, 8, &n); + if (q == NULL) { + p--; + ec = EXPR_E_OCTAL; + goto esyntax; + } + p = q; + goto oper; + } else if (*p == '%') { + if (last == 'n') { + last = *p; + p++; + } else { + p++; + q = getpnum(p, 2, &n); + if (q == NULL) { + ec = EXPR_E_BIN; + goto esyntax; + } + p = q; + goto oper; + } + } else if ((p[0] == '0') && (p[1] == 'x')) { + p+=2; + q = getpnum(p, 16, &n); + if (q == NULL) { + p--; + ec = EXPR_E_HEX; + goto esyntax; + } + p = q; + goto oper; + } else if (isdigit(*p)) { + if (last == 'n') + goto end; + q = getnum(p, &n); + if (q == NULL) { + ec = EXPR_E_DEC; + goto esyntax; + } + p = q; + goto oper; + } else if (isidc0(*p)) { + if (last == 'n') + goto end; + q = p; + while (isidc(*p)) + p++; + sym = lookup(q, p, 0, 0); + if (sym == NULL) { + n = 0; + if (!allowfr) { + err = 1; + eprint(_("undefined label")); + epchars(q, p); + enl(); + newerr(); + } + } else { + n = sym->val; + } + goto oper; + } else if (last == 'V') { + goto esyntax; + } else if (last != 'n') { + ec = EXPR_E_SYNTAX; + goto esyntax; + } else { +end: if (v != NULL) + *v = r; + return p; + } + goto loop; +uoper: + if (last == 'n') + goto end; + if (usi >= ESTKSZ) { + eprint(_("expression too complex\n")); + exit(EXIT_FAILURE); + } + uopstk[usi++] = *p++; + goto loop; +oper: + while (usi > usl) { + usi--; + switch (uopstk[usi]) { + case '~': n = ~n; break; + case '-': n = -n; break; + case '!': n = !n; break; + } + } + switch (last) { + case 'V': r = n; break; + case 'v': r = n; break; + case '+': r += n; break; + case '-': r -= n; break; + case '*': r *= n; break; + case '&': r &= n; break; + case '|': r |= n; break; + case '^': r ^= n; break; + case '=': r = r == n; break; + case '<': r = r < n; break; + case '>': r = r > n ; break; + case 'G': r = r >= n; break; + case 'S': r = r <= n; break; + case 'N': r = r != n; break; + /* This would be logical right shift: + * case 'R': r = (unsigned int) r >> n; break; + */ + case 'R': r = ashr(r, n); break; + case 'L': r = shl(r, n); break; + case '~': r = ~n; break; + case '%': + if (n != 0) { + r %= n; + } else if (!err && !allowfr) { + err = 1; + eprint(_("modulo by zero\n")); + exit(EXIT_FAILURE); + } + break; + case '/': + if (n != 0) { + r /= n; + } else if (!err && !allowfr) { + err = 1; + eprint(_("division by zero\n")); + exit(EXIT_FAILURE); + } + break; + } + last = 'n'; + goto loop; +esyntax: + if (ecode != NULL) + *ecode = ec; + if (ep != NULL) + *ep = p; + return NULL; +} diff --git a/2048/uz80as/expr.h b/2048/uz80as/expr.h new file mode 100644 index 0000000..e1e55fc --- /dev/null +++ b/2048/uz80as/expr.h @@ -0,0 +1,26 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Expression parsing. + * =========================================================================== + */ + +#ifndef EXPR_H +#define EXPR_H + +enum expr_ecode { + EXPR_E_NO_EXPR, /* There was no expression parsed. */ + EXPR_E_SYNTAX, /* Syntax error. */ + EXPR_E_CPAR, + EXPR_E_OPER, + EXPR_E_CHAR, + EXPR_E_HEX, + EXPR_E_OCTAL, + EXPR_E_BIN, + EXPR_E_DEC, +}; + +const char *expr(const char *p, int *v, int linepc, int allowfr, + enum expr_ecode *ecode, const char **ep); + +#endif diff --git a/2048/uz80as/exprint.c b/2048/uz80as/exprint.c new file mode 100644 index 0000000..6769963 --- /dev/null +++ b/2048/uz80as/exprint.c @@ -0,0 +1,32 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Expression error reporting. + * =========================================================================== + */ + +#include "config.h" +#include "exprint.h" +#include "err.h" + +static const char *expr_get_error_str(enum expr_ecode ecode) +{ + switch (ecode) { + case EXPR_E_NO_EXPR: return _("expression expected\n"); + case EXPR_E_SYNTAX: return _("syntax error in expression\n"); + case EXPR_E_CPAR: return _("unexpected ')'\n"); + case EXPR_E_OPER: return _("misplaced operator\n"); + case EXPR_E_CHAR: return _("invalid character code\n"); + case EXPR_E_HEX: return _("invalid hexadecimal constant\n"); + case EXPR_E_OCTAL: return _("invalid octal constant\n"); + case EXPR_E_BIN: return _("invalid binary constant\n"); + case EXPR_E_DEC: return _("invalid decimal constant\n"); + default: return "\n"; + } +} + +void exprint(enum expr_ecode ecode, const char *pline, const char *ep) +{ + eprint(expr_get_error_str(ecode)); + eprcol(pline, ep); +} diff --git a/2048/uz80as/exprint.h b/2048/uz80as/exprint.h new file mode 100644 index 0000000..00b43c2 --- /dev/null +++ b/2048/uz80as/exprint.h @@ -0,0 +1,17 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Expression error reporting. + * =========================================================================== + */ + +#ifndef EXPRINT_H +#define EXPRINT_H + +#ifndef EXPR_H +#include "expr.h" +#endif + +void exprint(enum expr_ecode ecode, const char *pline, const char *ep); + +#endif diff --git a/2048/uz80as/gbcpu.c b/2048/uz80as/gbcpu.c new file mode 100644 index 0000000..d6b347f --- /dev/null +++ b/2048/uz80as/gbcpu.c @@ -0,0 +1,301 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Sharp LR35902 (Nintendo Gameboy CPU). + * =========================================================================== + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +#if 0 +Opcode Z80 GMB + --------------------------------------- + 08 EX AF,AF LD (nn),SP + 10 DJNZ PC+dd STOP + 22 LD (nn),HL LDI (HL),A + 2A LD HL,(nn) LDI A,(HL) + 32 LD (nn),A LDD (HL),A + 3A LD A,(nn) LDD A,(HL) + D3 OUT (n),A - + D9 EXX RETI + DB IN A,(n) - + DD - + E0 RET PO LD (FF00+n),A + E2 JP PO,nn LD (FF00+C),A + E3 EX (SP),HL - + E4 CALL PO,nn - + E8 RET PE ADD SP,dd + EA JP PE,nn LD (nn),A + EB EX DE,HL - + EC CALL PE,nn - + ED - + F0 RET P LD A,(FF00+n) + F2 JP P,nn LD A,(FF00+C) + F4 CALL P,nn - + F8 RET M LD HL,SP+dd + FA JP M,nn LD A,(nn) + FC CALL M,nn - + FD - + CB3X SLL r/(HL) SWAP r/(HL) +#endif + +/* pat: + * a: expr + * b: B,C,D,E,H,L,A + * c: * + * d: BC,DE,HL,SP + * e: * + * f: BC,DE,HL,AF + * g: ADD,ADC,SUB,SBC,AND,XOR,OR,CP + * h: INC,DEC + * i: * + * j: * + * k: * + * l: BIT,RES,SET + * m: * + * n: NZ,Z,NC,C + * o: RLC,RRC,RL,RR,SLA,SRA,SWAP,SRL + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: (op << 4) | lastbyte + * g: (op << 6) | lastbyte + * h: if op >= FF00 output last byte and then op as 8 bit value; + * else output (lastbyte | 0x0A) and output op as word + * (no '.' should follow) + * i: relative jump to op + * j: possible value to RST + * k: possible value to IM + * l: * + * m: check arithmetic used with A register + * n: check arithmetic used without A register + */ + +const struct matchtab s_matchtab_gbcpu[] = { + { "LD b,b", "40b0c1.", 1, 0 }, + { "LD b,(HL)", "46b0.", 1, 0 }, + { "LD A,(C)", "F2.", 1, 0 }, // * LD A,(FF00+C) + { "LD A,(BC)", "0A.", 1, 0 }, + { "LD A,(DE)", "1A.", 1, 0 }, + { "LD A,(HLI)", "2A.", 1, 0 }, // * + { "LD A,(HLD)", "3A.", 1, 0 }, // * + { "LD A,(a)", "F0h0", 1, 0 }, // * LD A,(nn) & LD A,(FF00+n) + { "LD b,a", "06b0.d1.", 1, 0, "e8" }, + { "LD SP,HL", "F9.", 1, 0 }, + { "LDHL SP,a", "F8.d0.", 1, 0, "e8" }, // * LD HL,SP+n + { "LD d,a", "01f0.e1", 1, 0 }, + { "LD (C),A", "E2.", 1, 0 }, // * LD (FF00+C),A + { "LD (HL),b", "70c0.", 1, 0 }, + { "LD (HL),a", "36.d0.", 1, 0, "e8" }, + { "LD (HLI),A", "22.", 1, 0 }, // * + { "LD (HLD),A", "32.", 1, 0 }, // * + { "LD (BC),A", "02.", 1, 0 }, + { "LD (DE),A", "12.", 1, 0 }, + { "LD (a),A", "E0h0", 1, 0 }, // * LD (nn),A & LD (FF00+n),A + { "LD (a),SP", "08.e0", 1, 0 }, // * + { "LDH A,(a)", "F0.d0.", 1, 0, "e8" }, // * LD A,(FF00+n) + { "LDH (a),A", "E0.d0.", 1, 0, "e8" }, // * LD (FF00+n),A + { "PUSH f", "C5f0.", 1, 0 }, + { "POP f", "C1f0.", 1, 0 }, + { "ADD HL,d", "09f0.", 1, 0 }, + { "ADD SP,a", "E8.d0.", 1, 0, "e8" }, // * + { "g A,b", "m080b0c1.", 1, 0 }, + { "g A,(HL)", "m086b0.", 1, 0 }, + { "g A,a", "m0C6b0.d1.", 1, 0, "e8" }, + { "g b", "n080b0c1.", 1, 0 }, + { "g (HL)", "n086b0.", 1, 0 }, + { "g a", "n0C6b0.d1.", 1, 0, "e8" }, + { "h b", "04b1c0.", 1, 0 }, + { "h (HL)", "34c0.", 1, 0 }, + { "INC d", "03f0.", 1, 0 }, + { "DEC d", "0Bf0.", 1, 0 }, + { "DAA", "27.", 1, 0 }, + { "CPL", "2F.", 1, 0 }, + { "CCF", "3F.", 1, 0 }, + { "SCF", "37.", 1, 0 }, + { "NOP", "00.", 1, 0 }, + { "HALT", "76.", 1, 0 }, + { "DI", "F3.", 1, 0 }, + { "EI", "FB.", 1, 0 }, + { "RLCA", "07.", 1, 0 }, + { "RLA", "17.", 1, 0 }, + { "RRCA", "0F.", 1, 0 }, + { "RRA", "1F.", 1, 0 }, + { "o b", "CB.00b0c1.", 1, 0 }, + { "o (HL)", "CB.06b0.", 1, 0 }, + { "l a,b", "CB.00g0b1c2.", 1, 0, "b3" }, + { "l a,(HL)", "CB.06g0b1.", 1, 0, "b3" }, + { "JP (HL)", "E9.", 1, 0 }, + { "JP n,a", "C2b0.e1", 1, 0 }, // * + { "JP a", "C3.e0", 1, 0 }, + { "JR n,a", "20b0.i1.", 1, 0, "r8" }, + { "JR a", "18.i0.", 1, 0, "r8" }, + { "STOP", "10.00.", 1, 0 }, // * + { "CALL n,a", "C4b0.e1", 1, 0 }, // * + { "CALL a", "CD.e0", 1, 0 }, + { "RETI", "D9.", 1, 0 }, // * + { "RET n", "C0b0.", 1, 0 }, + { "RET", "C9.", 1, 0 }, + { "RST a", "C7j0.", 1, 0, "ss" }, + { NULL, NULL }, +}; + +static const char *const bval[] = { "B", "C", "D", "E", + "H", "L", "", "A", NULL }; +static const char *const dval[] = { "BC", "DE", "HL", "SP", NULL }; +static const char *const fval[] = { "BC", "DE", "HL", "AF", NULL }; +static const char *const gval[] = { "ADD", "ADC", "SUB", "SBC", + "AND", "XOR", "OR", "CP", NULL }; +static const char *const hval[] = { "INC", "DEC", NULL }; +static const char *const lval[] = { "", "BIT", "RES", "SET", NULL }; +static const char *const nval[] = { "NZ", "Z", "NC", "C", NULL }; +static const char *const oval[] = { "RLC", "RRC", "RL", "RR", + "SLA", "SRA", "SWAP", "SRL", NULL }; +static const char *const nullv[] = { NULL }; + +static const char *const *const valtab[] = { + bval, nullv, dval, nullv, fval, + gval, hval, nullv, nullv, nullv, + lval, nullv, nval, oval +}; + +static int match_gbcpu(char c, const char *p, const char **q) +{ + int v; + + switch (c) { + case 'b': + case 'd': + case 'f': + case 'g': + case 'h': + case 'l': + case 'n': + case 'o': + v = mreg(p, valtab[(int) (c - 'b')], q); + break; + default: + v = -1; + } + + return v; +} + +static int gen_gbcpu(int *eb, char p, const int *vs, int i, int savepc) +{ + int w, b; + + b = *eb; + switch (p) { + case 'f': b |= (vs[i] << 4); break; + case 'g': b |= (vs[i] << 6); break; + case 'h': w = vs[i] & 0xffff; + if (w >= 0xff00) { + genb(b, s_pline_ep); + b = 0; + genb(w & 0xff, s_pline_ep); + } else { + b |= 0x0A; + genb(b, s_pline_ep); + b = 0; + genb(w & 0xff, s_pline_ep); + genb(w >> 8, s_pline_ep); + } + break; + case 'i': b = (vs[i] - savepc - 2); break; + case 'j': if (s_pass > 0 && (vs[i] & ~56) != 0) { + eprint(_("invalid RST argument (%d)\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= vs[i]; + break; + case 'k': if (s_pass > 0 && (vs[i] < 0 || vs[i] > 2)) { + eprint(_("invalid IM argument (%d)\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b = 0x46; + if (vs[i] == 1) + b = 0x56; + else if (vs[i] == 2) + b = 0x5E; + break; + case 'm': if (s_pass == 0 && !s_extended_op) { + if (vs[i] != 0 && vs[i] != 1 && vs[i] != 3) { + eprint(_("unofficial syntax\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } + } + break; + case 'n': if (s_pass == 0 && !s_extended_op) { + if (vs[i] == 0 || vs[i] == 1 || vs[i] == 3) { + eprint(_("unofficial syntax\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } + } + break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_gbcpu(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_gbcpu(void) +{ + const char *s; + + switch (s_pat_char) { + case 'b': + case 'd': + case 'f': + case 'g': + case 'h': + case 'l': + case 'n': + case 'o': + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + break; + default: + s = NULL; + } + + return s; +}; + +const struct target s_target_gbcpu = { + .id = "gbcpu", + .descr = "Sharp LR35902 (Nintendo Gameboy CPU)", + .matcht = s_matchtab_gbcpu, + .matchf = match_gbcpu, + .genf = gen_gbcpu, + .pat_char_rewind = pat_char_rewind_gbcpu, + .pat_next_str = pat_next_str_gbcpu, + .mask = 1 +}; diff --git a/2048/uz80as/i4004.c b/2048/uz80as/i4004.c new file mode 100644 index 0000000..98649ab --- /dev/null +++ b/2048/uz80as/i4004.c @@ -0,0 +1,189 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Intel 4004. + * Intel 4040. + * =========================================================================== + */ + +/* Intel 4004. Max. memory 4K (12 bit addresses). + * Intel 4040. Max. memory 8K (13 bit addresses). + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: ADD,SUB,LD,XCH,BBL,LDM + * c: WRM,WMP,WRR,WPM,WR0,WR1,WR2,WR3 + * SBM,RDM,RDR,ADM,RD0,RD1,RD2,RD3 + * d: CLB,CLC,IAC,CMC,CMA,RAL,RAR,TCC, + * DAC,TCS,STC,DAA,KBP,DCL, + * e: HLT,BBS,LCR,OR4,OR5,AN6,AN7 + * DB0,DB1,SB0,SB1,EIN,DIN,RPM + * f: 0P,1P,2P,3P,4P,5P,6P,7P + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: op | lastbyte, op in [0-15] + * g: op | lastbyte, op in [0,2,4,6,8,10,12,14] + * h: output (op & 0xff0000 >> 8) | lastbyte; + * then ouput op as 8 bit value + * i: (op << 4) | lastbyte + * j: (op << 1) | lastbyte + */ + +static const struct matchtab s_matchtab_i4004[] = { + { "NOP", "00.", 1, 0 }, + { "JCN a,a", "10f0.d1.", 1, 0, "b4e8" }, + { "FIM f,a", "20j0.d1.", 1, 0, "e8" }, + { "FIM a,a", "20g0.d1.", 1, 0, "ppe8" }, + { "SRC f", "21j0.", 1, 0 }, + { "SRC a", "21g0.", 1, 0, "pp" }, + { "FIN f", "30j0.", 1, 0 }, + { "FIN a", "30g0.", 1, 0, "pp" }, + { "JIN f", "31j0.", 1, 0 }, + { "JIN a", "31g0.", 1, 0, "pp" }, + { "JUN a", "40h0", 1, 0 }, + { "JMS a", "50h0", 1, 0 }, + { "INC a", "60f0.", 1, 0, "b4" }, + { "ISZ a,a", "70f0.d1.", 1, 0, "b4e8" }, + { "b a", "80i0f1.", 1, 0, "b4" }, + { "c", "E0c0.", 1, 0 }, + { "d", "F0c0.", 1, 0 }, + { "e", "00c0.", 2, 0 }, + { NULL, NULL }, +}; + +static const char *const bval[] = { +"ADD", "SUB", "LD", "XCH", "BBL", "LDM", +NULL }; + +static const char *const cval[] = { +"WRM", "WMP", "WRR", "WPM", "WR0", "WR1", "WR2", "WR3", +"SBM", "RDM", "RDR", "ADM", "RD0", "RD1", "RD2", "RD3", +NULL }; + +static const char *const dval[] = { +"CLB", "CLC", "IAC", "CMC", "CMA", "RAL", "RAR", "TCC", +"DAC", "TCS", "STC", "DAA", "KBP", "DCL", +NULL }; + +static const char *const eval[] = { +"", "HLT", "BBS", "LCR", "OR4", "OR5", "AN6", "AN7", +"DB0", "DB1", "SB0", "SB1", "EIN", "DIN", "RPM", +NULL }; + +static const char *const fval[] = { +"0P", "1P", "2P", "3P", "4P", "5P", "6P", "7P", +NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval +}; + +static int match_i4004(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'f') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_i4004(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': if (s_pass > 0 && (vs[i] < 0 || vs[i] > 15)) { + eprint(_("argument (%d) must be in range [0-15]\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= vs[i]; + break; + case 'g': if (s_pass > 0 && (vs[i] < 0 || vs[i] > 14 || (vs[i] & 1))) { + eprint( + _("argument (%d) must be an even number in range [0-14]\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= vs[i]; + break; + case 'h': b |= ((vs[i] >> 8) & 0x0f); + genb(b, s_pline_ep); + genb(vs[i], s_pline_ep); + break; + case 'i': b |= (vs[i] << 4); break; + case 'j': b |= (vs[i] << 1); break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_i4004(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_i4004(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'f') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_i4004 = { + .id = "i4004", + .descr = "Intel 4004", + .matcht = s_matchtab_i4004, + .matchf = match_i4004, + .genf = gen_i4004, + .pat_char_rewind = pat_char_rewind_i4004, + .pat_next_str = pat_next_str_i4004, + .mask = 1 +}; + +const struct target s_target_i4040 = { + .id = "i4040", + .descr = "Intel 4040", + .matcht = s_matchtab_i4004, + .matchf = match_i4004, + .genf = gen_i4004, + .pat_char_rewind = pat_char_rewind_i4004, + .pat_next_str = pat_next_str_i4004, + .mask = 3 +}; + diff --git a/2048/uz80as/i8008.c b/2048/uz80as/i8008.c new file mode 100644 index 0000000..a7fd9f5 --- /dev/null +++ b/2048/uz80as/i8008.c @@ -0,0 +1,220 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Intel 8008. + * =========================================================================== + */ + +/* Max. memory 16K (14 bits addresses). */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: ADA,ADB,ADC,ADD,ADH,ADL,ADM, + * ACA,ACB,ACC,ACD,ACH,ACL,ACM, + * SUA,SUB,SUC,SUD,SUH,SUL,SUM, + * SBA,SBB,SBC,SBD,SBH,SBL,SBM, + * NDA,NDB,NDC,NDD,NDH,NDL,NDM, + * XRA,XRB,XRC,XRD,XRH,XRL,XRM, + * ORA,ORB,ORC,ORD,ORH,ORL,ORM, + * CPA,CPB,CPC,CPD,CPH,CPL,CPM + * c: NOP,LAB,LAC,LAD,LAE,LAH,LAL,LAM, + * LBA,LBB,LBC,LBD,LBE,LBH,LBL,LBM, + * LCA,LCB,LCC,LCD,LCE,LCH,LCL,LCM, + * LDA,LDB,LDC,LDD,LDE,LDH,LDL,LDM, + * LEA,LEB,LEC,LED,LEE,LEH,LEL,LEM, + * LHA,LHB,LHC,LHD,LHE,LHH,LHL,LHM, + * LLA,LLB,LLC,LLD,LLE,LLH,LLL,LLM, + * LMA,LMB,LMC,LMD,LME,LMH,LML,HLT + * d: JFC,CFC,JMP,CAL,JFZ,CFZ, + * JFS,CFS,JFP,CFP, + * JTC,CTC,JTZ,CTZ, + * JTS,CTS,JTP,CTP + * e: INB,INC,IND,INE,INH,INL + * f: DCB,DCC,DCD,DCE,DCH,DCL + * g: ADI,ACI,SUI,SBI,NDI,XRI,ORI,CPI + * h: LAI,LBI,LCI,LDI,LEI,LHI,LLI,LMI + * i: RFC,RFS,RTC,RTS,RFZ,RFP,RTZ,RTP + * j: RLC,RRC,RAL,RAR + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: (op << 1) | lastbyte, op in [0-7] + * g: (op << 1) | lastbyte, op in [8-31] + * h: (op << 4) | lastbyte + * i: (op << 1) | lastbyte + * j: (op << 3) | lastbyte, op in [0-7] + */ + +static const struct matchtab s_matchtab_i8008[] = { + { "RET", "07.", 1, 0 }, + { "j", "02b0.", 1, 0 }, + { "i", "03b0.", 1, 0 }, + { "h a", "06b0.d1.", 1, 0, "e8" }, + { "g a", "04b0.d1.", 1, 0, "e8" }, + { "e", "00b0.", 1, 0 }, + { "f", "01b0.", 1, 0 }, + { "RST a", "05j0.", 1, 0, "b3" }, + { "d a", "40i0.e1", 1, 0 }, + { "INP a", "41f0.", 1, 0, "b3" }, + { "OUT a", "41g0.", 1, 0, "kk" }, + { "b", "80c0.", 1, 0 }, + { "c", "C0c0.", 1, 0 }, + { NULL, NULL }, +}; + +static const char *const bval[] = { +"ADA", "ADB", "ADC", "ADD", "ADE", "ADH", "ADL", "ADM", +"ACA", "ACB", "ACC", "ACD", "ACE", "ACH", "ACL", "ACM", +"SUA", "SUB", "SUC", "SUD", "SUE", "SUH", "SUL", "SUM", +"SBA", "SBB", "SBC", "SBD", "SBE", "SBH", "SBL", "SBM", +"NDA", "NDB", "NDC", "NDD", "NDE", "NDH", "NDL", "NDM", +"XRA", "XRB", "XRC", "XRD", "XRE", "XRH", "XRL", "XRM", +"ORA", "ORB", "ORC", "ORD", "ORE", "ORH", "ORL", "ORM", +"CPA", "CPB", "CPC", "CPD", "CPE", "CPH", "CPL", "CPM", +NULL }; + +static const char *const cval[] = { +"NOP", "LAB", "LAC", "LAD", "LAE", "LAH", "LAL", "LAM", +"LBA", "LBB", "LBC", "LBD", "LBE", "LBH", "LBL", "LBM", +"LCA", "LCB", "LCC", "LCD", "LCE", "LCH", "LCL", "LCM", +"LDA", "LDB", "LDC", "LDD", "LDE", "LDH", "LDL", "LDM", +"LEA", "LEB", "LEC", "LED", "LEE", "LEH", "LEL", "LEM", +"LHA", "LHB", "LHC", "LHD", "LHE", "LHH", "LHL", "LHM", +"LLA", "LLB", "LLC", "LLD", "LLE", "LLH", "LLL", "LLM", +"LMA", "LMB", "LMC", "LMD", "LME", "LMH", "LML", "HLT", +NULL }; + +static const char *const dval[] = { +"JFC", "CFC", "JMP", "CAL", "JFZ", "CFZ", "", "", +"JFS", "CFS", "", "", "JFP", "CFP", "", "", +"JTC", "CTC", "", "", "JTZ", "CTZ", "", "", +"JTS", "CTS", "", "", "JTP", "CTP", +NULL }; + +static const char *const eval[] = { "", "INB", "INC", "IND", + "INE", "INH", "INL", + NULL }; + +static const char *const fval[] = { "", "DCB", "DCC", "DCD", + "DCE", "DCH", "DCL", + NULL }; + +static const char *const gval[] = { "ADI", "ACI", "SUI", "SBI", + "NDI", "XRI", "ORI", "CPI", + NULL }; + +static const char *const hval[] = { "LAI", "LBI", "LCI", "LDI", + "LEI", "LHI", "LLI", "LMI", + NULL }; + +static const char *const ival[] = { "RFC", "RFZ", "RFS", "RFP", + "RTC", "RTZ", "RTS", "RTP", + NULL }; + +static const char *const jval[] = { "RLC", "RRC", "RAL", "RAR", + NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval, + gval, hval, ival, jval +}; + +static int match_i8008(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'j') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_i8008(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': if (s_pass > 0 && (vs[i] < 0 || vs[i] > 7)) { + eprint(_("argument (%d) must be in range [0-7]\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= (vs[i] << 1); + break; + case 'g': if (s_pass > 0 && (vs[i] < 8 || vs[i] > 31)) { + eprint(_("argument (%d) must be in range [8-31]\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= (vs[i] << 1); + break; + case 'h': b |= (vs[i] << 4); break; + case 'i': b |= (vs[i] << 1); break; + case 'j': if (s_pass > 0 && (vs[i] < 0 || vs[i] > 7)) { + eprint(_("argument (%d) must be in range [0-7]\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= (vs[i] << 3); + break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_i8008(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_i8008(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'j') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_i8008 = { + .id = "i8008", + .descr = "Intel 8008", + .matcht = s_matchtab_i8008, + .matchf = match_i8008, + .genf = gen_i8008, + .pat_char_rewind = pat_char_rewind_i8008, + .pat_next_str = pat_next_str_i8008, + .mask = 1 +}; + diff --git a/2048/uz80as/i8048.c b/2048/uz80as/i8048.c new file mode 100644 index 0000000..1526d25 --- /dev/null +++ b/2048/uz80as/i8048.c @@ -0,0 +1,276 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Intel 8021. + * Intel 8022. + * Intel 8041. + * Intel 8048. + * =========================================================================== + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: R0,R1,R2,R3,R4,R5,R6,R7 + * c: R0,R1 + * d: P1,P2 + * e: P4,P5,P6,P7 + * f: JB0,JB1,JB2,JB3,JB4,JB5,JB6,JB7 + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: output lastbyte | ((op & 0x700) >> 3) + * output op as 8 bit value + * (no '.' should follow) + * g: (op << 5) | lastbyte + */ + +static const struct matchtab s_matchtab_i8048[] = { + { "NOP", "00.", 1, 0 }, + { "ADD A,b", "68c0.", 1, 0 }, + { "ADD A,@c", "60c0.", 1, 0 }, + { "ADD A,#a", "03.d0.", 1, 0, "e8" }, + { "ADDC A,b", "78c0.", 1, 0 }, + { "ADDC A,@c", "70c0.", 1, 0 }, + { "ADDC A,#a", "13.d0.", 1, 0, "e8" }, + { "ANL A,b", "58c0.", 1, 0 }, + { "ANL A,@c", "50c0.", 1, 0 }, + { "ANL A,#a", "53.d0.", 1, 0, "e8" }, + { "ANL BUS,#a", "98.d0.", 32, 0, "e8" }, + { "ANL d,#a", "98c0.d1.", 4, 0, "e8" }, + { "ANLD e,A", "9Cc0.", 1, 0 }, + { "CALL a", "14f0", 1, 0 }, + { "CLR A", "27.", 1, 0 }, + { "CLR C", "97.", 1, 0 }, + { "CLR F1", "A5.", 4, 0 }, + { "CLR F0", "85.", 4, 0 }, + { "CPL A", "37.", 1, 0 }, + { "CPL C", "A7.", 1, 0 }, + { "CPL F0", "95.", 4, 0 }, + { "CPL F1", "B5.", 4, 0 }, + { "DA A", "57.", 1, 0 }, + { "DEC A", "07.", 1, 0 }, + { "DEC b", "C8c0.", 4, 0 }, + { "DIS I", "15.", 2, 0 }, + { "DIS TCNTI", "35.", 2, 0 }, + { "DJNZ b,a", "E8c0.d1.", 1, 0, "e8" }, + { "EN DMA", "E5.", 64, 0 }, + { "EN FLAGS", "F5.", 64, 0 }, + { "EN I", "05.", 2, 0 }, + { "EN TCNTI", "25.", 2, 0 }, + { "ENT0 CLK", "75.", 32, 0 }, + { "IN A,DBB", "22.", 64, 0 }, + { "IN A,P0", "08.", 8, 0 }, + { "IN A,d", "08c0.", 1, 0 }, + { "INC A", "17.", 1, 0 }, + { "INC b", "18c0.", 1, 0 }, + { "INC @c", "10c0.", 1, 0 }, + { "INS A,BUS", "08.", 32, 0 }, + { "f a", "12g0.d1.", 4, 0, "e8" }, + { "JC a", "F6.d0.", 1, 0, "e8" }, + { "JF0 a", "B6.d0.", 4, 0, "e8" }, + { "JF1 a", "76.d0.", 4, 0, "e8" }, + { "JMP a", "04f0", 1, 0, "e11" }, + { "JMPP @A", "B3.", 1, 0 }, + { "JNC a", "E6.d0.", 1, 0, "e8" }, + { "JNI a", "86.d0.", 32, 0, "e8" }, + { "JNIBF a", "D6.d0.", 64, 0, "e8" }, + { "JNT0 a", "26.d0.", 2, 0, "e8" }, + { "JNT1 a", "46.d0.", 1, 0, "e8" }, + { "JNZ a", "96.d0.", 1, 0, "e8" }, + { "JOBF a", "86.d0.", 64, 0, "e8" }, + { "JTF a", "16.d0.", 1, 0, "e8" }, + { "JT0 a", "36.d0.", 2, 0, "e8" }, + { "JT1 a", "56.d0.", 1, 0, "e8" }, + { "JZ a", "C6.d0.", 1, 0, "e8" }, + { "MOV A,#a", "23.d0.", 1, 0, "e8" }, + { "MOV A,PSW", "C7.", 4, 0 }, + { "MOV A,b", "F8c0.", 1, 0 }, + { "MOV A,@c", "F0c0.", 1, 0 }, + { "MOV A,T", "42.", 1, 0 }, + { "MOV PSW,A", "D7.", 4, 0 }, + { "MOV b,A", "A8c0.", 1, 0 }, + { "MOV b,#a", "B8c0.d1.", 1, 0, "e8" }, + { "MOV @c,A", "A0c0.", 1, 0 }, + { "MOV @c,#a", "B0c0.d1.", 1, 0, "e8" }, + { "MOV STS,A", "90.", 64, 0 }, + { "MOV T,A", "62.", 1, 0 }, + { "MOVD A,e", "0Cc0.", 1, 0 }, + { "MOVD e,A", "3Cc0.", 1, 0 }, + { "MOVP A,@A", "A3.", 1, 0 }, + { "MOVP3 A,@A", "E3.", 4, 0 }, + { "MOVX A,@c", "80c0.", 32, 0 }, + { "MOVX @c,A", "90c0.", 32, 0 }, + { "NOP", "00.", 1, 0 }, + { "ORL A,b", "48c0.", 1, 0 }, + { "ORL A,@c", "40c0.", 1, 0 }, + { "ORL A,#a", "43.d0.", 1, 0, "e8" }, + { "ORL BUS,#a", "88.d0.", 32, 0, "e8" }, + { "ORL d,#a", "88c0.d1.", 4, 0, "e8" }, + { "ORLD e,A", "8Cc0.", 1, 0 }, + { "OUT DBB,A", "02.", 64, 0 }, + { "OUTL BUS,A", "02.", 32, 0 }, + { "OUTL P0,A", "90.", 8, 0 }, + { "OUTL d,A", "38c0.", 1, 0 }, + { "RAD", "80.", 16, 0 }, + { "RET", "83.", 1, 0 }, + { "RETR", "93.", 4, 0 }, + { "RETI", "93.", 16, 0 }, + { "RL A", "E7.", 1, 0 }, + { "RLC A", "F7.", 1, 0 }, + { "RR A", "77.", 1, 0 }, + { "RRC A", "67.", 1, 0 }, + { "SEL AN0", "85.", 16, 0 }, + { "SEL AN1", "95.", 16, 0 }, + { "SEL MB0", "E5.", 32, 0 }, + { "SEL MB1", "F5.", 32, 0 }, + { "SEL RB0", "C5.", 4, 0 }, + { "SEL RB1", "D5.", 4, 0 }, + { "STOP TCNT", "65.", 1, 0 }, + { "STRT CNT", "45.", 1, 0 }, + { "STRT T", "55.", 1, 0 }, + { "SWAP A", "47.", 1, 0 }, + { "XCH A,b", "28c0.", 1, 0 }, + { "XCH A,@c", "20c0.", 1, 0 }, + { "XCHD A,@c", "30c0.", 1, 0 }, + { "XRL A,b", "D8c0.", 1, 0 }, + { "XRL A,@c", "D0c0.", 1, 0 }, + { "XRL A,#a", "D3.d0.", 1, 0, "e8" }, + { NULL, NULL }, +}; + +static const char *const bval[] = { +"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", +NULL }; + +static const char *const cval[] = { +"R0", "R1", +NULL }; + +static const char *const dval[] = { +"", "P1", "P2", +NULL }; + +static const char *const eval[] = { +"P4", "P5", "P6", "P7", +NULL }; + +static const char *const fval[] = { +"JB0", "JB1", "JB2", "JB3", "JB4", "JB5", "JB6", "JB7", +NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval +}; + +static int match_i8048(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'f') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_i8048(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': b |= ((vs[i] & 0x700) >> 3); + genb(b, s_pline_ep); + genb(vs[i], s_pline_ep); + break; + case 'g': b |= (vs[i] << 5); + break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_i8048(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_i8048(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'f') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_i8041 = { + .id = "i8041", + .descr = "Intel 8041", + .matcht = s_matchtab_i8048, + .matchf = match_i8048, + .genf = gen_i8048, + .pat_char_rewind = pat_char_rewind_i8048, + .pat_next_str = pat_next_str_i8048, + .mask = 71 +}; + +const struct target s_target_i8048 = { + .id = "i8048", + .descr = "Intel 8048", + .matcht = s_matchtab_i8048, + .matchf = match_i8048, + .genf = gen_i8048, + .pat_char_rewind = pat_char_rewind_i8048, + .pat_next_str = pat_next_str_i8048, + .mask = 39 +}; + +const struct target s_target_i8021 = { + .id = "i8021", + .descr = "Intel 8021", + .matcht = s_matchtab_i8048, + .matchf = match_i8048, + .genf = gen_i8048, + .pat_char_rewind = pat_char_rewind_i8048, + .pat_next_str = pat_next_str_i8048, + .mask = 9 +}; + +const struct target s_target_i8022 = { + .id = "i8022", + .descr = "Intel 8022", + .matcht = s_matchtab_i8048, + .matchf = match_i8048, + .genf = gen_i8048, + .pat_char_rewind = pat_char_rewind_i8048, + .pat_next_str = pat_next_str_i8048, + .mask = 27 +}; + diff --git a/2048/uz80as/i8051.c b/2048/uz80as/i8051.c new file mode 100644 index 0000000..034e129 --- /dev/null +++ b/2048/uz80as/i8051.c @@ -0,0 +1,244 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Intel 8051. + * =========================================================================== + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: R0,R1,R2,R3,R4,R5,R6,R7 + * c: R0,R1 + * d: ADD,ADDC,ORL,ANL,XRL,SUBB,XCH,MOV + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: output lastbyte | ((op & 0x700) >> 3) + * output op as 8 bit value + * (no '.' should follow) + * g: relative jump -2 + * h: relative jump -3 + * i: ouput op as big endian word + * j: b + (op << 4) + */ + +static const struct matchtab s_matchtab_i8051[] = { + { "ACALL a", "11f0", 1, 0, "e11" }, + { "d A,b", "08j0c1.", 1, 0 }, + { "d A,@c", "06j0c1.", 1, 0 }, + { "ADD A,#a", "24.d0.", 1, 0, "e8" }, + { "ADDC A,#a", "34.d0.", 1, 0, "e8" }, + { "ORL A,#a", "44.d0.", 1, 0, "e8" }, + { "ANL A,#a", "54.d0.", 1, 0, "e8" }, + { "XRL A,#a", "64.d0.", 1, 0, "e8" }, + { "SUBB A,#a", "94.d0.", 1, 0, "e8" }, + { "MOV A,#a", "74.d0.", 1, 0, "e8" }, + { "d A,a", "05j0.d1.", 1, 0, "e8" }, + // { "ADD A,b", "28c0.", 1, 0 }, + // { "ADD A,@c", "26c0.", 1, 0 }, + // { "ADD A,a", "25.d0.", 1, 0, "e8" }, + // { "ADDC A,b", "38c0.", 1, 0 }, + // { "ADDC A,@c", "36c0.", 1, 0 }, + // { "ADDC A,a", "35.d0.", 1, 0, "e8" }, + { "AJMP a", "01f0", 1, 0, "e11" }, + // { "ANL A,b", "58c0.", 1, 0 }, + // { "ANL A,@c", "56c0.", 1, 0 }, + // { "ANL A,a", "55.d0.", 1, 0, "e8" }, + { "ANL C,/a", "B0.d0.", 1, 0, "e8" }, + { "ANL C,a", "82.d0.", 1, 0, "e8" }, + { "ANL a,A", "52.d0.", 1, 0 }, + { "ANL a,#a", "53.d0.d1.", 1, 0, "e8e8" }, + { "CJNE A,#a,a", "B4.d0.h1.", 1, 0, "e8r8" }, + { "CJNE b,#a,a", "B8c0.d1.h2.", 1, 0, "e8r8" }, + { "CJNE @c,#a,a", "B6c0.d1.h2.", 1, 0, "e8r8" }, + { "CJNE A,a,a", "B5.d0.h1.", 1, 0, "e8r8" }, + { "CLR A", "E4.", 1, 0 }, + { "CLR C", "C3.", 1, 0 }, + { "CLR a", "C2.d0.", 1, 0, "e8" }, + { "CPL A", "F4.", 1, 0 }, + { "CPL C", "B3.", 1, 0 }, + { "CPL a", "B2.d0.", 1, 0, "e8" }, + { "DA A", "D4.", 1, 0 }, + { "DEC A", "14.", 1, 0 }, + { "DEC b", "18c0.", 1, 0 }, + { "DEC @c", "16c0.", 1, 0 }, + { "DEC a", "15.d0.", 1, 0, "e8" }, + { "DIV AB", "84.", 1, 0 }, + { "DJNZ b,a", "D8c0.g1.", 1, 0, "r8" }, + { "DJNZ a,a", "D5.d0.h1.", 1, 0, "e8r8" }, + { "INC DPTR", "A3.", 1, 0 }, + { "INC A", "04.", 1, 0 }, + { "INC b", "08c0.", 1, 0 }, + { "INC @c", "06c0.", 1, 0 }, + { "INC a", "05.d0.", 1, 0, "e8" }, + { "JB a,a", "20.d0.h1.", 1, 0, "e8r8" }, + { "JBC a,a", "10.d0.h1.", 1, 0, "e8r8" }, + { "JC a", "40.g0.", 1, 0, "r8" }, + { "JMP @A+DPTR", "73.", 1, 0 }, + { "JNB a,a", "30.d0.h1.", 1, 0, "e8r8" }, + { "JNC a", "50.g0.", 1, 0, "r8" }, + { "JNZ a", "70.g0.", 1, 0, "r8" }, + { "JZ a", "60.g0.", 1, 0, "r8" }, + { "LCALL a", "12.i0", 1, 0 }, + { "LJMP a", "02.i0", 1, 0 }, + // { "MOV A,b", "E8c0.", 1, 0 }, + // { "MOV A,@c", "E6c0.", 1, 0 }, + // { "MOV A,a", "E5.d0.", 1, 0, "e8" }, // MOV A,ACC not valid? + { "MOV b,A", "F8c0.", 1, 0 }, + { "MOV b,#a", "78c0.d1.", 1, 0, "e8" }, + { "MOV b,a", "A8c0.d1.", 1, 0, "e8" }, + { "MOV @c,A", "F6c0.", 1, 0 }, + { "MOV @c,#a", "76c0.d1.", 1, 0, "e8" }, + { "MOV @c,a", "A6c0.d1.", 1, 0, "e8" }, + { "MOV C,a", "A2.d0.", 1, 0, "e8" }, + { "MOV DPTR,#a", "90.i0", 1, 0, "e8" }, + { "MOV a,A", "F5.d0.", 1, 0, "e8" }, + { "MOV a,C", "92.d0.", 1, 0, "e8" }, + { "MOV a,b", "88c1.d0.", 1, 0, "e8" }, + { "MOV a,@c", "86c1.d0.", 1, 0, "e8" }, + { "MOV a,#a", "75.d0.d1.", 1, 0, "e8e8" }, + { "MOV a,a", "85.d1.d0.", 1, 0, "e8e8" }, + { "MOVC A,@A+DPTR", "93.", 1, 0 }, + { "MOVC A,@A+PC", "83.", 1, 0 }, + { "MOVX A,@c", "E2c0.", 1, 0 }, + { "MOVX A,@DPTR", "E0.", 1, 0 }, + { "MOVX @c,A", "F2c0.", 1, 0 }, + { "MOVX @DPTR,A", "F0.", 1, 0 }, + { "MUL AB", "A4.", 1, 0 }, + { "NOP", "00.", 1, 0 }, + // { "ORL A,b", "48c0.", 1, 0 }, + // { "ORL A,@c", "46c0.", 1, 0 }, + // { "ORL A,a", "45.d0.", 1, 0, "e8" }, + { "ORL C,/a", "A0.d0.", 1, 0, "e8" }, + { "ORL C,a", "72.d0.", 1, 0, "e8" }, + { "ORL a,A", "42.d0.", 1, 0, "e8" }, + { "ORL a,#a", "43.d0.d1.", 1, 0, "e8e8" }, + { "POP a", "D0.d0.", 1, 0, "e8" }, + { "PUSH a", "C0.d0.", 1, 0, "e8" }, + { "RET", "22.", 1, 0 }, + { "RETI", "32.", 1, 0 }, + { "RL A", "23.", 1, 0 }, + { "RLC A", "33.", 1, 0 }, + { "RR A", "03.", 1, 0 }, + { "RRC A", "13.", 1, 0 }, + { "SETB C", "D3.", 1, 0 }, + { "SETB a", "D2.d0.", 1, 0, "e8" }, + { "SJMP a", "80.g0.", 1, 0, "r8" }, + // { "SUBB A,b", "98c0.", 1, 0 }, + // { "SUBB A,@c", "96c0.", 1, 0 }, + // { "SUBB A,a", "95.d0.", 1, 0, "e8" }, + { "SWAP A", "C4.", 1, 0 }, + // { "XCH A,b", "C8c0.", 1, 0 }, + // { "XCH A,@c", "C6c0.", 1, 0 }, + // { "XCH A,a", "C5.d0.", 1, 0, "e8" }, + { "XCHD A,@c", "D6c0.", 1, 0 }, + // { "XRL A,b", "68c0.", 1, 0 }, + // { "XRL A,@c", "66c0.", 1, 0 }, + // { "XRL A,a", "65.d0.", 1, 0, "e8" }, + { "XRL a,A", "62.d0.", 1, 0, "e8" }, + { "XRL a,#a", "63.d0.d1.", 1, 0, "e8e8" }, + { NULL, NULL }, +}; + +static const char *const bval[] = { +"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", +NULL }; + +static const char *const cval[] = { +"R0", "R1", +NULL }; + +static const char *const dval[] = { +"", "", "ADD", "ADDC", "ORL", "ANL", "XRL", "", +"", "SUBB", "", "", "XCH", "", "MOV", +NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval +}; + +static int match_i8051(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'd') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_i8051(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': b |= ((vs[i] & 0x700) >> 3); + genb(b, s_pline_ep); + genb(vs[i], s_pline_ep); + break; + case 'g': b = (vs[i] - savepc - 2); break; + break; + case 'h': b = (vs[i] - savepc - 3); break; + break; + case 'i': genb(vs[i] >> 8, s_pline_ep); + genb(vs[i], s_pline_ep); + break; + case 'j': b += (vs[i] << 4); break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_i8051(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_i8051(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'd') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_i8051 = { + .id = "i8051", + .descr = "Intel 8051", + .matcht = s_matchtab_i8051, + .matchf = match_i8051, + .genf = gen_i8051, + .pat_char_rewind = pat_char_rewind_i8051, + .pat_next_str = pat_next_str_i8051, + .mask = 1 +}; + diff --git a/2048/uz80as/i8080.c b/2048/uz80as/i8080.c new file mode 100644 index 0000000..e0f7f51 --- /dev/null +++ b/2048/uz80as/i8080.c @@ -0,0 +1,214 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Intel 8080. + * =========================================================================== + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: B,C,D,E,H,L,M,A + * c: B,D,H,SP + * d: B,D + * e: B,D,H,PSW + * f: JNZ,JZ,JNC,JC,JPO,JPE,JP,JM + * g: CNZ,CZ,CNC,CC,CPO,CPE,CP,CM + * h: RNZ,RZ,RNC,RC,RPO,RPE,RP,RM + * i: B,C,D,E,H,L,A + * j: ADD,ADC,SUB,SBB,ANA,XRA,ORA,CMP + * k: RLC,RRC,RAL,RAR + * l: ADI,ACI,SUI,SBI,ANI,XRI,ORI,CPI + * m: SHLD,LHLD,STA,LDA + * n: DI,EI + * o: OUT,IN + * p: STC,CMC + * q: POP,PUSH + * r: STAX,LDAX + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: (op << 4) | lastbyte + * g: possible value to RST + * h: (op << 2) | lastbyte + */ + +static const struct matchtab s_matchtab_i8080[] = { + { "MOV M,i", "70c0.", 3, 0 }, + { "MOV i,M", "46b0.", 3, 0 }, + { "MOV i,i", "40b0c1.", 3, 0 }, + { "MVI b,a", "06b0.d1.", 3, 0, "e8" }, + { "LXI c,a", "01f0.e1", 3, 0 }, + { "m a", "22b0.e1", 3, 0 }, + { "r d", "02b0f1.", 3, 0 }, + { "XCHG", "EB.", 3, 0 }, + { "j b", "80b0c1.", 3, 0 }, + { "l a", "C6b0.d1.", 3, 0, "e8" }, + { "INR b", "04b0.", 3, 0 }, + { "DCR b", "05b0.", 3, 0 }, + { "INX c", "03f0.", 3, 0 }, + { "DCX c", "0Bf0.", 3, 0 }, + { "DAD c", "09f0.", 3, 0 }, + { "DAA", "27.", 3, 0 }, + { "k", "07b0.", 3, 0 }, + { "CMA", "2F.", 3, 0 }, + { "p", "37b0.", 3, 0 }, + { "JMP a", "C3.e0", 3, 0 }, + { "f a", "C2b0.e1", 3, 0 }, + { "CALL a", "CD.e0", 3, 0 }, + { "g a", "C4b0.e1", 3, 0 }, + { "RET", "C9.", 3, 0 }, + { "h", "C0b0.", 3, 0 }, + { "RST a", "C7g0.", 3, 0, "b3" }, + { "PCHL", "E9.", 3, 0 }, + { "q e", "C1h0f1.", 3, 0 }, + { "XTHL", "E3.", 3, 0 }, + { "SPHL", "F9.", 3, 0 }, + { "o a", "D3b0.d1.", 3, 0, "e8" }, + { "n", "F3b0.", 3, 0 }, + { "HLT", "76.", 3, 0 }, + { "NOP", "00.", 3, 0 }, + /* 8085 added instructions */ + { "RIM", "20.", 2, 0 }, + { "SIM", "30.", 2, 0 }, + { "ARHL", "10.", 2, 2 }, + { "DSUB", "08.", 2, 2 }, + { "RDEL", "18.", 2, 2 }, + { "LDHI a", "28.d0.", 2, 2, "e8" }, + { "LDSI a", "38.d0.", 2, 2, "e8" }, + { "RSTV", "CB.", 2, 2 }, + { "SHLX", "D9.", 2, 2 }, + { "LHLX", "ED.", 2, 2 }, + { "JNK a", "DD.e0", 2, 2 }, + { "JNX5 a", "DD.e0", 2, 2 }, + { "JNUI a", "DD.e0", 2, 2 }, + { "JK a", "FD.e0", 2, 2 }, + { "JX5 a", "FD.e0", 2, 2 }, + { "JUI a", "FD.e0", 2, 2 }, + { NULL, NULL }, +}; + +static const char *const bval[] = { "B", "C", "D", "E", + "H", "L", "M", "A", NULL }; +static const char *const cval[] = { "B", "D", "H", "SP", NULL }; +static const char *const dval[] = { "B", "D", NULL }; +static const char *const eval[] = { "B", "D", "H", "PSW", NULL }; +static const char *const fval[] = { "JNZ", "JZ", "JNC", "JC", + "JPO", "JPE", "JP", "JM", NULL }; +static const char *const gval[] = { "CNZ", "CZ", "CNC", "CC", + "CPO", "CPE", "CP", "CM", NULL }; +static const char *const hval[] = { "RNZ", "RZ", "RNC", "RC", + "RPO", "RPE", "RP", "RM", NULL }; +static const char *const ival[] = { "B", "C", "D", "E", + "H", "L", "", "A", NULL }; +static const char *const jval[] = { "ADD", "ADC", "SUB", "SBB", + "ANA", "XRA", "ORA", "CMP", NULL }; +static const char *const kval[] = { "RLC", "RRC", "RAL", "RAR", NULL }; +static const char *const lval[] = { "ADI", "ACI", "SUI", "SBI", + "ANI", "XRI", "ORI", "CPI", NULL }; +static const char *const mval[] = { "SHLD", "LHLD", "STA", "LDA", NULL }; +static const char *const nval[] = { "DI", "EI", NULL }; +static const char *const oval[] = { "OUT", "IN", NULL }; +static const char *const pval[] = { "STC", "CMC", NULL }; +static const char *const qval[] = { "POP", "PUSH", NULL }; +static const char *const rval[] = { "STAX", "LDAX", NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval, + gval, hval, ival, jval, kval, + lval, mval, nval, oval, pval, + qval, rval +}; + +static int match_i8080(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'r') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_i8080(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': b |= (vs[i] << 4); break; + case 'g': if (s_pass > 0 && (vs[i] & ~7) != 0) { + eprint(_("invalid RST argument (%d)\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= (vs[i] << 3); + break; + case 'h': b |= (vs[i] << 2); break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_i8080(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_i8080(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'r') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_i8080 = { + .id = "i8080", + .descr = "Intel 8080", + .matcht = s_matchtab_i8080, + .matchf = match_i8080, + .genf = gen_i8080, + .pat_char_rewind = pat_char_rewind_i8080, + .pat_next_str = pat_next_str_i8080, + .mask = 1 +}; + +const struct target s_target_i8085 = { + .id = "i8085", + .descr = "Intel 8085", + .matcht = s_matchtab_i8080, + .matchf = match_i8080, + .genf = gen_i8080, + .pat_char_rewind = pat_char_rewind_i8080, + .pat_next_str = pat_next_str_i8080, + .mask = 2 +}; diff --git a/2048/uz80as/incl.c b/2048/uz80as/incl.c new file mode 100644 index 0000000..e11d467 --- /dev/null +++ b/2048/uz80as/incl.c @@ -0,0 +1,81 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Include file stack. + * =========================================================================== + */ + +#include "config.h" +#include "incl.h" +#include "utils.h" +#include "err.h" + +#ifndef ASSERT_H +#include +#endif + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +/* Max number of nested included files. */ +#define NFILES 128 + +/* Number of nested files. */ +static int s_nfiles; + +/* Current file. */ +static struct incfile *s_curfile; + +/* Get the current file. Never returns NULL. */ +struct incfile *curfile(void) +{ + assert(s_curfile != NULL); + return s_curfile; +} + +/* The number of nested files. 0 means no file loaded. */ +int nfiles(void) +{ + return s_nfiles; +} + +/* Leave the current included file. */ +void popfile(void) +{ + struct incfile *ifile; + + assert(s_curfile != NULL); + fclose(s_curfile->fin); + ifile = s_curfile; + s_curfile = ifile->prev; + free(ifile); + s_nfiles--; +} + +/* Include a file whose name is [p, q[. */ +void pushfile(const char *p, const char *q) +{ + struct incfile *ifile; + + if (s_nfiles == NFILES) { + eprint(_("maximum number of nested includes exceeded (%d)\n"), + NFILES); + exit(EXIT_FAILURE); + } + + // printf("pushfile: %s\n", p); + ifile = emalloc((sizeof *ifile) + (q - p) + 1); + ifile->name = (char *) ((unsigned char *) ifile + sizeof *ifile); + copychars(ifile->name, p, q); + + ifile->fin = efopen(ifile->name, "r"); + ifile->linenum = 0; + ifile->prev = s_curfile; + s_curfile = ifile; + s_nfiles++; +} diff --git a/2048/uz80as/incl.h b/2048/uz80as/incl.h new file mode 100644 index 0000000..1da62f6 --- /dev/null +++ b/2048/uz80as/incl.h @@ -0,0 +1,28 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Include file stack. + * =========================================================================== + */ + +#ifndef INCL_H +#define INCL_H + +#ifndef STDIO_H +#define STDIO_H +#include +#endif + +struct incfile { + struct incfile *prev; + FILE *fin; + int linenum; + char *name; +}; + +void pushfile(const char *p, const char *q); +void popfile(void); +struct incfile *curfile(void); +int nfiles(void); + +#endif diff --git a/2048/uz80as/list.c b/2048/uz80as/list.c new file mode 100644 index 0000000..f340e30 --- /dev/null +++ b/2048/uz80as/list.c @@ -0,0 +1,164 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Assembly listing generation. + * =========================================================================== + */ + +#include "config.h" +#include "list.h" +#include "err.h" + +#ifndef STDIO_H +#include +#endif + +#ifndef STLIB_H +#include +#endif + +#ifndef STRING_H +#include +#endif + +static FILE *s_list_file; + +/* N characters in current line. */ +static int s_nchars; + +/* Generated data bytes in this line. */ +static int s_nbytes; + +/* Line text. */ +static const char *s_line; + +/* Line number. */ +static int s_linenum; + +/* Program counter. */ +static int s_pc; + +/* Number of current nested files. */ +static int s_nfiles; + +/* If listing line number, etc are generated or just the line. */ +int s_codes = 1; + +/* If listing is enabled or not. */ +int s_list_on = 1; + +/* If we are skipping lines. */ +static int s_skip_on = 0; + +void list_open(const char *fname) +{ + s_list_file = fopen(fname, "w"); + if (s_list_file == NULL) { + eprint(_("cannot open file %s\n"), fname); + } +} + +void list_close(void) +{ + if (s_list_file != NULL) + fclose(s_list_file); +} + +static void prhead(void) +{ + int i, j, n; + + s_nchars = fprintf(s_list_file, "%-.4d", s_linenum); + + n = 7 - s_nchars; + if (n <= 0) + n = 1; + j = 0; + if (s_nfiles > 0) + j = s_nfiles - 1; + if (j > n) + j = n; + for (i = 0; i < j; i++) + fputc('+', s_list_file); + j = n - j; + while (j--) + fputc(' ', s_list_file); + s_nchars += n; + + s_nchars += fprintf(s_list_file, "%.4X", s_pc); + if (s_skip_on) + fputc('~', s_list_file); + else + fputc(' ', s_list_file); + s_nchars += 1; +} + +static void prline(void) +{ + if (s_line == NULL) { + fputs("\n", s_list_file); + } else { + if (s_codes) { + while (s_nchars < 24) { + s_nchars++; + fputc(' ', s_list_file); + } + } + fprintf(s_list_file, "%s\n", s_line); + s_line = NULL; + } + s_nchars = 0; + s_nbytes = 0; +} + +void list_startln(const char *line, int linenum, int pc, int nested_files) +{ + if (s_list_file == NULL) + return; + s_linenum = linenum; + s_pc = pc; + s_line = line; + s_nchars = 0; + s_nbytes = 0; + s_nfiles = nested_files; +} + +void list_setpc(int pc) +{ + s_pc = pc; +} + +void list_skip(int on) +{ + s_skip_on = on; +} + +void list_eject(void) +{ + if (s_list_file == NULL || !s_list_on) + return; +} + +void list_genb(int b) +{ + if (s_list_file == NULL || !s_codes || !s_list_on) + return; + if (s_nchars == 0) + prhead(); + if (s_nbytes >= 4) { + prline(); + prhead(); + } + s_nchars += fprintf(s_list_file, "%2.2X ", (b & 0xff)); + s_nbytes++; + s_pc++; +} + +void list_endln(void) +{ + if (s_list_file == NULL || !s_list_on) + return; + if (s_codes && s_nchars == 0) + prhead(); + prline(); +} diff --git a/2048/uz80as/list.h b/2048/uz80as/list.h new file mode 100644 index 0000000..7868cd2 --- /dev/null +++ b/2048/uz80as/list.h @@ -0,0 +1,23 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Assembly listing generation. + * =========================================================================== + */ + +#ifndef LIST_H +#define LIST_H + +extern int s_codes; +extern int s_list_on; + +void list_open(const char *fname); +void list_close(void); +void list_startln(const char *line, int linenum, int pc, int nested_files); +void list_setpc(int pc); +void list_skip(int on); +void list_eject(void); +void list_genb(int b); +void list_endln(void); + +#endif diff --git a/2048/uz80as/main.c b/2048/uz80as/main.c new file mode 100644 index 0000000..e3e3ff1 --- /dev/null +++ b/2048/uz80as/main.c @@ -0,0 +1,303 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * main(), handling of command line options. + * =========================================================================== + */ + +#include "config.h" +#include "ngetopt.h" +#include "options.h" +#include "utils.h" +#include "err.h" +#include "uz80as.h" +#include "prtable.h" +#include "targets.h" + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +#ifndef STRING_H +#include +#endif + +#ifndef CTYPE_H +#include +#endif + +void print_copyright(FILE *f) +{ + static const char *copyright = +"Copyright (C) " COPYRIGHT_YEARS " Jorge Giner Cordero.\n"; + + fputs(copyright, f); +} + +static void print_license(FILE *f) +{ + static const char *license[] = { +"Permission is hereby granted, free of charge, to any person obtaining", +"a copy of this software and associated documentation files (the", +"\"Software\"), to deal in the Software without restriction, including", +"without limitation the rights to use, copy, modify, merge, publish,", +"distribute, sublicense, and/or sell copies of the Software, and to", +"permit persons to whom the Software is furnished to do so, subject to", +"the following conditions:", +"", +"The above copyright notice and this permission notice shall be included", +"in all copies or substantial portions of the Software.", +"", +"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,", +"EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", +"MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.", +"IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY", +"CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", +"TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE", +"SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + }; + + int i; + + for (i = 0; i < NELEMS(license); i++) { + fprintf(f, "%s\n", license[i]); + } +} + +static void print_version(FILE *f) +{ + const struct target *p; + + fputs(PACKAGE_STRING, f); + fputs("\n", f); + fputs("Targets:", f); + for (p = first_target(); p != NULL; p = next_target()) { + fprintf(f, " %s", p->id); + } + fputs("\n", f); +} + +static void print_help(const char *argv0) +{ + static const char *help = +"Usage: %s [OPTION]... ASM_FILE [OBJ_FILE [LST_FILE]]\n" +"\n" +"Assemble ASM_FILE into OBJ_FILE and generate the listing LST_FILE.\n" +"If not specified, OBJ_FILE is ASM_FILE with the extension changed to .obj.\n" +"If not specified, LST_FILE is ASM_FILE with the extension changed to .lst.\n" +"\n" +"Options:\n" +" -h, --help Display this help and exit.\n" +" -V, --verbose be chatty.\n" +" -v, --version Output version information and exit.\n" +" -l, --license Display the license text and exit.\n" +" -d, --define=MACRO Define a macro.\n" +" -f, --fill=n Fill memory with value n.\n" +" -q, --quiet Do not generate the listing file.\n" +" -x, --extended Enable extended instruction syntax.\n" +" -u, --undocumented Enable undocumented instructions.\n" +" -t, --target=NAME Select the target micro. z80 is the default.\n" +" -e, --list-targets List the targets supported.\n" +"\n" +"Examples:\n" +" " PACKAGE " p.asm Assemble p.asm into p.obj\n" +" " PACKAGE " p.asm p.bin Assemble p.asm into p.bin\n" +" " PACKAGE " -d\"MUL(a,b) (a*b)\" p.asm Define the macro MUL and assemble p.asm\n" +"\n" +"Report bugs to: <" PACKAGE_BUGREPORT ">.\n" +"Home page: <" PACKAGE_URL ">.\n"; + + printf(help, argv0); +} + +/* + * Get the filename part of fname (that is, for ../../fname.abc, get + * fname.abc). + * Then substitute the extension .abd by .ext or append .ext. + */ +static char *mkfname(const char *fname, const char *ext) +{ + size_t alen, elen; + const char *p, *q; + char *s; + + alen = strlen(fname); + elen = strlen(ext); + + /* Find start of filename in path string */ + p = fname + alen; + while (p > fname && *p != '/' && *p != '\\') + p--; + + if (*p == '/' || *p == '\\') + p++; + + /* Find the extension */ + q = fname + alen; + while (q > p && *q != '.') + q--; + + if (*q != '.') + q = fname + alen; + + s = emalloc((q - p) + 1 + elen + 1); + if (q > p) + memmove(s, p, (q - p)); + s[q - p] = '\0'; + strcat(s, "."); + strcat(s, ext); + return s; +} + +static void parse_fill_byte(const char *optarg) +{ + int hi, lo; + + if (strlen(optarg) != 2) + goto error; + + if ((hi = hexval(optarg[0])) < 0) + goto error; + if ((lo = hexval(optarg[1])) < 0) + goto error; + + s_mem_fillval = hi * 16 + lo; + return; + +error: eprogname(); + fprintf(stderr, _("invalid command line fill value (%s)\n"), optarg); + eprogname(); + fprintf(stderr, " "); + fprintf(stderr, _("Please, use two hexadecimal digits.\n")); + exit(EXIT_FAILURE); +} + +static void parse_target_id(const char *optarg) +{ + const struct target *p; + + p = find_target(optarg); + if (p == NULL) { + eprogname(); + fprintf(stderr, _("invalid target '%s'\n"), optarg); + exit(EXIT_FAILURE); + } else { + s_target_id = p->id; + } +} + +static void list_targets(FILE *f) +{ + const struct target *p; + + for (p = first_target(); p != NULL; p = next_target()) { + fprintf(f, "%-14s%s\n", p->id, p->descr); + } +} + +int main(int argc, char *argv[]) +{ + int c; + struct ngetopt ngo; + + static struct ngetopt_opt ops[] = { + { "define", 1, 'd' }, + { "extended", 0, 'x' }, + { "list-targets", 0, 'e' }, + { "fill", 1, 'f' }, + { "help", 0, 'h' }, + { "license", 0, 'l' }, + { "quiet", 0, 'q' }, + { "target", 1, 't' }, + { "undocumented", 0, 'u' }, + { "version", 0, 'v' }, + { "print-table", 1, 0 }, + { "print-delta", 1, 0 }, + { "verbose", 0, 'V' }, + { NULL, 0, 0 }, + }; + + ngetopt_init(&ngo, argc, argv, ops); + do { + c = ngetopt_next(&ngo); + switch (c) { + case 'V': + verbose++; + break; + case 'v': + print_version(stdout); + exit(EXIT_SUCCESS); + case 'h': + print_help(argv[0]); + exit(EXIT_SUCCESS); + case 'l': + print_copyright(stdout); + fputs("\n", stdout); + print_license(stdout); + exit(EXIT_SUCCESS); + case 't': + parse_target_id(ngo.optarg); + break; + case 'e': + list_targets(stdout); + exit(EXIT_SUCCESS); + break; + case 'd': + predefine(ngo.optarg); + break; + case 'f': + parse_fill_byte(ngo.optarg); + break; + case 'q': + s_listing = 0; + break; + case 'x': + s_extended_op = 1; + break; + case 'u': + s_undocumented_op = 1; + break; + case '?': + eprint(_("unrecognized option %s\n"), + ngo.optarg); + exit(EXIT_FAILURE); + case ':': + eprint(_("%s needs an argument\n"), + ngo.optarg); + exit(EXIT_FAILURE); + case ';': + eprint(_("%s does not allow for arguments\n"), + ngo.optarg); + exit(EXIT_FAILURE); + case 0: + if (strcmp(ngo.optstr, "print-table") == 0) { + print_table(stdout, ngo.optarg); + exit(EXIT_SUCCESS); + } + } + } while (c != -1); + + if (argc == ngo.optind) { + eprint(_("wrong number of arguments\n")); + exit(EXIT_FAILURE); + } + + s_asmfname = argv[ngo.optind]; + + if (argc - ngo.optind > 1) + s_objfname = argv[ngo.optind + 1]; + else + s_objfname = mkfname(s_asmfname, "obj"); + + if (argc - ngo.optind > 2) + s_lstfname = argv[ngo.optind + 2]; + else + s_lstfname = mkfname(s_asmfname, "lst"); + + uz80as(); + return 0; +} diff --git a/2048/uz80as/mc6800.c b/2048/uz80as/mc6800.c new file mode 100644 index 0000000..4ff80be --- /dev/null +++ b/2048/uz80as/mc6800.c @@ -0,0 +1,338 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Motorola 6800, 6801. + * =========================================================================== + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: NEGA,COMA,LSRA,RORA,ASRA, + * ROLA,DECA,INCA,TSTA,CLRA + * c: NEGB,COMB,LSRB,RORB,ASRB, + * ROLB,DECB,INCB,TSTB,CLRB + * d: NEG,COM,LSR,ROR,ASR, + * ROL,DEC,INC,TST,JMP,CLR + * e: SUBA,CMPA,SBCA,ANDA,BITA,LDAA, + * EORA,ADCA,ORAA,ADDA + * f: SUBB,CMPB,SBCB,ANDB,BITB,LDAB, + * EORB,ADCB,ORAB,ADDB + * g: INX,DEX,CLV,SEV,CLC,SEC,CLI,SEI + * h: BRA,BHI,BLS,BCC,BCS,BNE,BEQ, + * BVC,BVS,BPL,BMI,BGE,BLT,BGT,BLE + * i: TSX,INS,PULA,PULB,DES,TXS,PSHA, + * PSHB,RTS,RTI,WAI,SWI + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: ouput op as big endian word (no '.' should follow) + * g: if op<=$ff output lastbyte and output op as byte + * else output (lastbyte | 0x20) and output op as big endian word + * (no '.' should follow) + * h: relative - 2 + * i: relative - 4 + * i: relative - 5 + */ + +static const struct matchtab s_matchtab_mc6800[] = { + { "NOP", "01.", 1, 0 }, + { "TAP", "06.", 1, 0 }, + { "TPA", "07.", 1, 0 }, + { "g", "08c0.", 1, 0 }, + { "SBA", "10.", 1, 0 }, + { "CBA", "11.", 1, 0 }, + { "TAB", "16.", 1, 0 }, + { "TBA", "17.", 1, 0 }, + { "DAA", "19.", 1, 0 }, + { "ABA", "1B.", 1, 0 }, + { "i", "30c0.", 1, 0 }, + { "h a", "20c0.h1.", 1, 0, "r8" }, + { "b", "40c0.", 1, 0 }, + { "c", "50c0.", 1, 0 }, + { "d a,X", "60c0.d1.", 1, 0, "e8" }, + { "d a,Y", "18.60c0.d1.", 8, 0, "e8" }, + { "d a", "70c0.f1", 1, 0, "e16" }, + { "e #a", "80c0.d1.", 1, 0, "e8" }, + { "f #a", "C0c0.d1.", 1, 0, "e8" }, + { "e >a", "B0c0.f1", 1, 0, "e16" }, + { "f >a", "F0c0.f1", 1, 0, "e16" }, + { "e a,X", "A0c0.d1.", 1, 0, "e8" }, + { "f a,X", "E0c0.d1.", 1, 0, "e8" }, + { "e a,Y", "18.A0c0.d1.", 8, 0, "e8" }, + { "f a,Y", "18.E0c0.d1.", 8, 0, "e8" }, + { "e a", "90c0g1", 1, 0 }, + { "f a", "D0c0g1", 1, 0 }, + { "STAA >a", "B7.f0", 1, 0, "e16" }, + { "STAA a,X", "A7.d0.", 1, 0, "e8" }, + { "STAA a,Y", "18.A7.d0.", 8, 0, "e8" }, + { "STAA a", "97g0", 1, 0 }, + { "STAB >a", "F7.f0", 1, 0, "e16" }, + { "STAB a,X", "E7.d0.", 1, 0, "e8" }, + { "STAB a,Y", "18.E7.d0.", 8, 0, "e8" }, + { "STAB a", "D7g0", 1, 0 }, + { "CPX #a", "8C.f0", 1, 0, "e16" }, + { "CPX >a", "BC.f0", 1, 0, "e16" }, + { "CPX a,X", "AC.d0.", 1, 0, "e8" }, + { "CPX a,Y", "CD.AC.d0.", 8, 0, "e8" }, + { "CPX a", "9Cg0", 1, 0 }, + { "LDS #a", "8E.f0", 1, 0, "e16" }, + { "LDS >a", "BE.f0", 1, 0, "e16" }, + { "LDS a,X", "AE.d0.", 1, 0, "e8" }, + { "LDS a,Y", "18.AE.d0.", 8, 0, "e8" }, + { "LDS a", "9Eg0", 1, 0 }, + { "STS >a", "BF.f0", 1, 0, "e16" }, + { "STS a,X", "AF.d0.", 1, 0, "e8" }, + { "STS a,Y", "18.AF.d0.", 8, 0, "e8" }, + { "STS a", "9Fg0", 1, 0 }, + { "LDX #a", "CE.f0", 1, 0, "e16" }, + { "LDX >a", "FE.f0", 1, 0, "e16" }, + { "LDX a,X", "EE.d0.", 1, 0, "e8" }, + { "LDX a,Y", "CD.EE.d0.", 8, 0, "e8" }, + { "LDX a", "DEg0", 1, 0 }, + { "STX >a", "FF.f0", 1, 0, "e16" }, + { "STX a,X", "EF.d0.", 1, 0, "e8" }, + { "STX a,Y", "CD.EF.d0.", 8, 0, "e8" }, + { "STX a", "DFg0", 1, 0 }, + { "BSR a", "8D.h0.", 1, 0, "r8" }, + { "JSR >a", "BD.f0", 4, 0, "e16" }, + { "JSR a,X", "AD.d0.", 1, 0, "e8" }, + { "JSR a,Y", "18.AD.d0.", 8, 0, "e8" }, + { "JSR a", "BD.f0", 2, 0, "e16" }, + { "JSR a", "9Dg0", 4, 0 }, + { "ABX", "3A.", 4, 0 }, + { "ADDD #a", "C3.f0", 4, 0, "e16" }, + { "ADDD >a", "F3.f0", 4, 0, "e16" }, + { "ADDD a,X", "E3.d0.", 4, 0, "e8" }, + { "ADDD a,Y", "18.E3.d0.", 8, 0, "e8" }, + { "ADDD a", "D3g0", 4, 0 }, + { "ASLD", "05.", 4, 0 }, + { "LSLD", "05.", 4, 0 }, + { "BHS a", "24.h0.", 4, 0, "r8" }, + { "BLO a", "25.h0.", 4, 0, "r8" }, + { "BRN a", "21.h0.", 4, 0, "r8" }, + { "LDD #a", "CC.f0", 4, 0, "e16" }, + { "LDD >a", "FC.f0", 4, 0, "e16" }, + { "LDD a,X", "EC.d0.", 4, 0, "e8" }, + { "LDD a,Y", "18.EC.d0.", 8, 0, "e8" }, + { "LDD a", "DCg0", 4, 0 }, + { "LSL a,X", "68.d0.", 4, 0, "e8" }, + { "LSL a,Y", "18.68.d0.", 8, 0, "e8" }, + { "LSL a", "78.f0", 4, 0, "e16" }, + { "LSRD", "04.", 4, 0 }, + { "MUL", "3D.", 4, 0 }, + { "PSHX", "3C.", 4, 0 }, + { "PSHY", "18.3C.", 8, 0 }, + { "PULX", "38.", 4, 0 }, + { "PULY", "18.38.", 8, 0 }, + { "STD >a", "FD.f0", 4, 0, "e16" }, + { "STD a,X", "ED.d0.", 4, 0, "e8" }, + { "STD a,Y", "18.ED.d0.", 8, 0, "e8" }, + { "STD a", "DDg0", 4, 0 }, + { "SUBD #a", "83.f0", 4, 0, "e16" }, + { "SUBD >a", "B3.f0", 4, 0, "e16" }, + { "SUBD a,X", "A3.d0.", 4, 0, "e8" }, + { "SUBD a,Y", "18.A3.d0.", 8, 0, "e8" }, + { "SUBD a", "93g0", 4, 0 }, + { "TEST", "00.", 8, 0 }, + { "IDIV", "02.", 8, 0 }, + { "FDIV", "03.", 8, 0 }, + { "BRSET a,X,a,a", "1E.d0.d1.i2.", 8, 0, "e8e8r8" }, + { "BRSET a,Y,a,a", "18.1E.d0.d1.j2.", 8, 0, "e8e8r8" }, + { "BRSET a,a,a", "12.d0.d1.i2.", 8, 0, "e8e8r8" }, + { "BRCLR a,X,a,a", "1F.d0.d1.i2.", 8, 0, "e8e8r8" }, + { "BRCLR a,Y,a,a", "18.1F.d0.d1.j2.", 8, 0, "e8e8r8" }, + { "BRCLR a,a,a", "13.d0.d1.i2.", 8, 0, "e8e8r8" }, + { "BSET a,X,a", "1C.d0.d1.", 8, 0, "e8e8" }, + { "BSET a,Y,a", "18.1C.d0.d1.", 8, 0, "e8e8" }, + { "BSET a,a", "14.d0.d1.", 8, 0, "e8e8" }, + { "BCLR a,X,a", "1D.d0.d1.", 8, 0, "e8e8" }, + { "BCLR a,Y,a", "18.1D.d0.d1.", 8, 0, "e8e8" }, + { "BCLR a,a", "15.d0.d1.", 8, 0, "e8e8" }, + { "LSLA", "48.", 8, 0 }, + { "LSLB", "58.", 8, 0 }, + { "XGDX", "8F.", 8, 0 }, + { "STOP", "CF.", 8, 0 }, + { "ABY", "18.3A.", 8, 0 }, + { "CPY #a", "18.8C.f0", 8, 0, "e16" }, + { "CPY >a", "18.BC.f0", 8, 0, "e16" }, + { "CPY a,X", "1A.AC.d0.", 8, 0, "e8" }, + { "CPY a,Y", "18.AC.d0.", 8, 0, "e8" }, + { "CPY a", "18.9Cg0", 8, 0 }, + { "DEY", "18.09.", 8, 0 }, + { "INY", "18.08.", 8, 0 }, + { "LDY #a", "18.CE.f0", 8, 0, "e16" }, + { "LDY >a", "18.FE.f0", 8, 0, "e16" }, + { "LDY a,X", "1A.EE.d0.", 8, 0, "e8" }, + { "LDY a,Y", "18.EE.d0.", 8, 0, "e8" }, + { "LDY a", "18.DEg0", 8, 0 }, + { "STY >a", "18.FF.f0", 8, 0, "e16" }, + { "STY a,X", "1A.EF.d0.", 8, 0, "e8" }, + { "STY a,Y", "18.EF.d0.", 8, 0, "e8" }, + { "STY a", "18.DFg0", 8, 0 }, + { "TSY", "18.30.", 8, 0 }, + { "TYS", "18.35.", 8, 0 }, + { "XGDY", "18.8F.", 8, 0 }, + { "CPD #a", "1A.83.f0", 8, 0, "e16" }, + { "CPD >a", "1A.B3.f0", 8, 0, "e16" }, + { "CPD a,X", "1A.A3.d0.", 8, 0, "e8" }, + { "CPD a,Y", "CD.A3.d0.", 8, 0, "e8" }, + { "CPD a", "1A.93g0", 8, 0 }, + { NULL, NULL }, +}; + +static const char *const bval[] = { +"NEGA", "", "", "COMA", "LSRA", "", "RORA", "ASRA", +"ASLA", "ROLA", "DECA", "", "INCA", "TSTA", "", "CLRA", +NULL }; + +static const char *const cval[] = { +"NEGB", "", "", "COMB", "LSRB", "", "RORB", "ASRB", +"ASLB", "ROLB", "DECB", "", "INCB", "TSTB", "", "CLRB", +NULL }; + +static const char *const dval[] = { +"NEG", "", "", "COM", "LSR", "", "ROR", "ASR", +"ASL", "ROL", "DEC", "", "INC", "TST", "JMP", "CLR", +NULL }; + +static const char *const eval[] = { +"SUBA", "CMPA", "SBCA", "", "ANDA", "BITA", "LDAA", "", +"EORA", "ADCA", "ORAA", "ADDA", +NULL }; + +static const char *const fval[] = { +"SUBB", "CMPB", "SBCB", "", "ANDB", "BITB", "LDAB", "", +"EORB", "ADCB", "ORAB", "ADDB", +NULL }; + +static const char *const gval[] = { +"INX", "DEX", "CLV", "SEV", "CLC", "SEC", "CLI", "SEI", +NULL }; + +static const char *const hval[] = { +"BRA", "", "BHI", "BLS", "BCC", "BCS", "BNE", "BEQ", +"BVC", "BVS", "BPL", "BMI", "BGE", "BLT", "BGT", "BLE", +NULL }; + +static const char *const ival[] = { +"TSX", "INS", "PULA", "PULB", "DES", "TXS", "PSHA", +"PSHB", "", "RTS", "", "RTI", "", "", "WAI", "SWI", +NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval, + gval, hval, ival +}; + +static int match_mc6800(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'i') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_mc6800(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': genb(vs[i] >> 8, s_pline_ep); + genb(vs[i], s_pline_ep); + break; + case 'g': if (vs[i] <= 255) { + genb(b, s_pline_ep); + genb(vs[i], s_pline_ep); + } else { + genb(b | 0x20, s_pline_ep); + genb(vs[i] >> 8, s_pline_ep); + genb(vs[i], s_pline_ep); + } + break; + case 'h': b = (vs[i] - savepc - 2); + break; + case 'i': b = (vs[i] - savepc - 4); + break; + case 'j': b = (vs[i] - savepc - 5); + break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_mc6800(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_mc6800(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'i') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_mc6800 = { + .id = "mc6800", + .descr = "Motorola 6800", + .matcht = s_matchtab_mc6800, + .matchf = match_mc6800, + .genf = gen_mc6800, + .pat_char_rewind = pat_char_rewind_mc6800, + .pat_next_str = pat_next_str_mc6800, + .mask = 3 +}; + +const struct target s_target_mc6801 = { + .id = "mc6801", + .descr = "Motorola 6801", + .matcht = s_matchtab_mc6800, + .matchf = match_mc6800, + .genf = gen_mc6800, + .pat_char_rewind = pat_char_rewind_mc6800, + .pat_next_str = pat_next_str_mc6800, + .mask = 5 +}; + +const struct target s_target_m68hc11 = { + .id = "m68hc11", + .descr = "Motorola 68HC11", + .matcht = s_matchtab_mc6800, + .matchf = match_mc6800, + .genf = gen_mc6800, + .pat_char_rewind = pat_char_rewind_mc6800, + .pat_next_str = pat_next_str_mc6800, + .mask = 13 +}; diff --git a/2048/uz80as/mos6502.c b/2048/uz80as/mos6502.c new file mode 100644 index 0000000..f1041b2 --- /dev/null +++ b/2048/uz80as/mos6502.c @@ -0,0 +1,385 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * MOS Technology 6502. + * Rockwell R6501. + * California Micro Devices G65SC02. + * Rockwell R65C02. + * Rockwell R65C29. + * Western Design Center W65C02S. + * =========================================================================== + */ + +/* mos6502, the original + * + * g65sc02 California Micro Devices, adds to mos6502: + * - zp ADC,AND,CMP,EOR,LDA,ORA,SBC,STA + * - DEC A, INC A + * - JMP (abs,X) + * - BRA + * - PHX,PHY,PLX,PLY + * - STZ + * - TRB + * - TSB + * - More addressing modes for BIT, etc + * + * r6501 Rockwell, adds to mos6502: + * - BBR, BBS + * - RMB, SMB + * + * r65c02 Rockwell, adds the instructions of the g65sc02 and r6501 + * + * r65c29 Rockwell, adds to r65c02: + * - MUL + * + * w65c02s Western Design Center, adds to r65c02: + * - STP,WAI + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: ORA,AND,EOR,ADC,STA,LDA,CMP,SBC + * c: ORA,AND,EOR,ADC,LDA,CMP,SBC + * d: PHP,CLC,PLP,SEC,PHA,CLI,PLA,SEI, + * DEY,TYA,TAY,CLV,INY,CLD,INX,SED + * e: ASL,ROL,LSR,ROR + * f: DEC, INC + * g: BPL,BMI,BVC,BVS,BCC,BCS,BNE,BEQ + * h: TXA,TXS,TAX,TSX,DEX,NOP + * i: CPY,CPX + * j: TSB,TRB + * k: BBR0,BBR1,BBR2,BBR3,BBR4,BBR5,BBR6,BBR6, + * BBS0,BBS1,BBS2,BBS3,BBS4,BBS5,BBS6,BBS7 + * l: RMB0,RMB1,RMB2,RMB3,RMB4,RMB5,EMB6,RMB7, + * SMB0,SMB1,SMB2,SMB3,SMB4,SMB5,SMB6,SMB7 + * m: PHY,PLY + * n: PHX,PLX + * o: INC, DEC + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: (op << 5) | lastbyte + * g: if op <= $FF output last byte and then op as 8 bit value; + * else output (lastbyte | 0x08) and output op as word + * (no '.' should follow) + * h: (op << 4) | lastbyte + * i: relative jump to op (-2) + * j: if op <= $FF output $64 and op as 8 bit + * else output $9C and op as word + * (no '.' should follow) + * k: if op <= $FF ouput $74 and op as 8 bit + * else output $9E and op as word + * (no '.' should follow) + * l: relative jump to op (-3) + */ + +static const struct matchtab s_matchtab_mos6502[] = { + { "BRK", "00.", 1, 0 }, + { "JSR a", "20.e0", 1, 0 }, + { "RTI", "40.", 1, 0 }, + { "RTS", "60.", 1, 0 }, + { "h", "8Ah0.", 1, 0 }, + { "d", "08h0.", 1, 0 }, + { "c #a", "09f0.d1.", 1, 0, "e8" }, + { "b (a,X)", "01f0.d1.", 1, 0, "e8" }, + { "b (a),Y", "11f0.d1.", 1, 0, "e8" }, + { "b (a)", "12f0.d1.", 2, 0, "e8" }, + { "b a", "05f0g1", 1, 0 }, + { "b a,X", "15f0g1", 1, 0 }, + { "b a,Y", "19f0.e1", 1, 0 }, + { "e A", "0Af0.", 1, 0 }, + { "e a", "06f0g1", 1, 0 }, + { "e a,X", "16f0g1", 1, 0 }, + { "STX a", "86g0", 1, 0 }, + { "STX a,Y", "96.d0.", 1, 0, "e8" }, + { "LDX #a", "A2.d0.", 1, 0, "e8" }, + { "LDX a", "A6g0", 1, 0 }, + { "LDX a,Y", "B6g0", 1, 0 }, + { "o A", "1Af0.", 2, 0 }, + { "f a", "C6f0g1", 1, 0 }, + { "f a,X", "D6f0g1", 1, 0 }, + { "g a", "10f0.i1.", 1, 0, "r8" }, + { "BIT #a", "89.d0.", 2, 0, "e8" }, + { "BIT a", "24g0", 1, 0 }, + { "BIT a,X", "34g0", 2, 0 }, + { "JMP (a)", "6C.e0", 1, 0 }, + { "JMP (a,X)", "7C.e0", 2, 0 }, + { "JMP a", "4C.e0", 1, 0 }, + { "STY a", "84g0", 1, 0 }, + { "STY a,X", "94.d0.", 1, 0, "e8" }, + { "LDY #a", "A0.d0.", 1, 0, "e8" }, + { "LDY a", "A4g0", 1, 0 }, + { "LDY a,X", "B4g0", 1, 0 }, + { "i #a", "C0f0.d1.", 1, 0, "e8" }, + { "i a", "C4f0g1", 1, 0 }, + { "j a", "04h0g1", 2, 0 }, + { "k a,a", "0Fh0.d1.l2.", 4, 0, "e8r8" }, + { "l a", "07h0.d1.", 4, 0, "e8" }, + { "m", "5Af0.", 2, 0 }, + { "n", "DAf0.", 2, 0 }, + { "BRA a", "80.i0.", 2, 0, "r8" }, + { "STZ a,X", "k1", 2, 0 }, + { "STZ a", "j1", 2, 0 }, + { "MUL", "02.", 8, 0 }, + { "WAI", "CB.", 16, 0 }, + { "STP", "DB.", 16, 0 }, + { NULL, NULL }, +}; + +static const char *const bval[] = { + "ORA", "AND", "EOR", "ADC", + "STA", "LDA", "CMP", "SBC", + NULL +}; + +static const char *const cval[] = { + "ORA", "AND", "EOR", "ADC", + "", "LDA", "CMP", "SBC", NULL +}; + +static const char *const dval[] = { + "PHP", "CLC", "PLP", "SEC", + "PHA", "CLI", "PLA", "SEI", + "DEY", "TYA", "TAY", "CLV", + "INY", "CLD", "INX", "SED", + NULL +}; + + +static const char *const eval[] = { + "ASL", "ROL", "LSR", "ROR", + NULL +}; + +static const char *const fval[] = { + "DEC", "INC", + NULL +}; + +static const char *const gval[] = { + "BPL", "BMI", "BVC", "BVS", + "BCC", "BCS", "BNE", "BEQ", + NULL +}; + +static const char *const hval[] = { + "TXA", "TXS", "TAX", "TSX", + "DEX", "", "NOP", + NULL +}; + +static const char *const ival[] = { + "CPY", "CPX", + NULL +}; + +static const char *const jval[] = { + "TSB", "TRB", + NULL +}; + +static const char *const kval[] = { + "BBR0", "BBR1", "BBR2", "BBR3", + "BBR4", "BBR5", "BBR6", "BBR7", + "BBS0", "BBS1", "BBS2", "BBS3", + "BBS4", "BBS5", "BBS6", "BBS7", + NULL +}; + +static const char *const lval[] = { + "RMB0", "RMB1", "RMB2", "RMB3", + "RMB4", "RMB5", "RMB6", "RMB7", + "SMB0", "SMB1", "SMB2", "SMB3", + "SMB4", "SMB5", "SMB6", "SMB7", + NULL +}; + +static const char *const mval[] = { + "PHY", "PLY", + NULL +}; + +static const char *const nval[] = { + "PHX", "PLX", + NULL +}; + +static const char *const oval[] = { + "INC", "DEC", + NULL +}; + +static const char *const *const valtab[] = { + bval, cval, dval, eval, fval, + gval, hval, ival, jval, kval, + lval, mval, nval, oval +}; + +static int match_mos6502(char c, const char *p, const char **q) +{ + int v; + + if (c <= 'o') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_mos6502(int *eb, char p, const int *vs, int i, int savepc) +{ + int b, w; + + b = *eb; + switch (p) { + case 'f': b |= (vs[i] << 5); break; + case 'g': w = vs[i] & 0xffff; + if (w <= 0xff) { + genb(b, s_pline_ep); + b = 0; + genb(w, s_pline_ep); + } else { + b |= 0x08; + genb(b, s_pline_ep); + b = 0; + genb(w, s_pline_ep); + genb(w >> 8, s_pline_ep); + } + break; + case 'h': b |= (vs[i] << 4); break; + case 'i': b = (vs[i] - savepc - 2); break; + case 'j': w = vs[i] & 0xffff; + if (w <= 0xff) { + genb(0x64, s_pline_ep); + b = 0; + genb(w, s_pline_ep); + } else { + genb(0x9C, s_pline_ep); + b = 0; + genb(w, s_pline_ep); + genb(w >> 8, s_pline_ep); + } + break; + case 'k': w = vs[i] & 0xffff; + if (w <= 0xff) { + genb(0x74, s_pline_ep); + b = 0; + genb(w, s_pline_ep); + } else { + genb(0x9E, s_pline_ep); + b = 0; + genb(w, s_pline_ep); + genb(w >> 8, s_pline_ep); + } + break; + case 'l': b = (vs[i] - savepc - 3); break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_mos6502(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_mos6502(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'o') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_mos6502 = { + .id = "mos6502", + .descr = "MOS Technology 6502", + .matcht = s_matchtab_mos6502, + .matchf = match_mos6502, + .genf = gen_mos6502, + .pat_char_rewind = pat_char_rewind_mos6502, + .pat_next_str = pat_next_str_mos6502, + .mask = 1 +}; + +const struct target s_target_r6501 = { + .id = "r6501", + .descr = "Rockwell R6501", + .matcht = s_matchtab_mos6502, + .matchf = match_mos6502, + .genf = gen_mos6502, + .pat_char_rewind = pat_char_rewind_mos6502, + .pat_next_str = pat_next_str_mos6502, + .mask = 5 +}; + +const struct target s_target_g65sc02 = { + .id = "g65sc02", + .descr = "California Micro Devices G65SC02", + .matcht = s_matchtab_mos6502, + .matchf = match_mos6502, + .genf = gen_mos6502, + .pat_char_rewind = pat_char_rewind_mos6502, + .pat_next_str = pat_next_str_mos6502, + .mask = 3 +}; + +const struct target s_target_r65c02 = { + .id = "r65c02", + .descr = "Rockwell R65C02", + .matcht = s_matchtab_mos6502, + .matchf = match_mos6502, + .genf = gen_mos6502, + .pat_char_rewind = pat_char_rewind_mos6502, + .pat_next_str = pat_next_str_mos6502, + .mask = 7 +}; + +const struct target s_target_r65c29 = { + .id = "r65c29", + .descr = "Rockwell R65C29, R65C00/21", + .matcht = s_matchtab_mos6502, + .matchf = match_mos6502, + .genf = gen_mos6502, + .pat_char_rewind = pat_char_rewind_mos6502, + .pat_next_str = pat_next_str_mos6502, + .mask = 15 +}; + +const struct target s_target_w65c02s = { + .id = "w65c02s", + .descr = "Western Design Center W65C02S", + .matcht = s_matchtab_mos6502, + .matchf = match_mos6502, + .genf = gen_mos6502, + .pat_char_rewind = pat_char_rewind_mos6502, + .pat_next_str = pat_next_str_mos6502, + .mask = 027 +}; diff --git a/2048/uz80as/ngetopt.c b/2048/uz80as/ngetopt.c new file mode 100644 index 0000000..c040401 --- /dev/null +++ b/2048/uz80as/ngetopt.c @@ -0,0 +1,237 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Handling of command line options, similar to getopt. + * =========================================================================== + */ + +/* + * Changes: + * + * - Jul 22 2018: long options without short option character recognized. + * + */ + +#include "ngetopt.h" + +#ifndef STRING_H +#include +#endif + +static int find_short_opt(int val, struct ngetopt_opt *ops) +{ + int i; + + i = 0; + while (ops[i].name != NULL) { + if (ops[i].val > 0 && ops[i].val == val) + return i; + i++; + } + + return -1; +} + +static int find_long_opt(char *str, struct ngetopt_opt *ops) +{ + int i; + const char *p, *q; + + i = 0; + while (ops[i].name != NULL) { + p = ops[i].name; + q = str; + while (*p != '\0' && *p == *q) { + p++; + q++; + } + if (*p == '\0' && (*q == '\0' || *q == '=')) { + return i; + } + i++; + } + + return -1; +} + +void ngetopt_init(struct ngetopt *p, int argc, char *const *argv, + struct ngetopt_opt *ops) +{ + p->argc = argc; + p->argv = argv; + p->ops = ops; + p->optind = 1; + p->subind = 0; + strcpy(p->str, "-X"); +} + +static int get_short_opt(struct ngetopt *p) +{ + int i; + char *opt; + + opt = p->argv[p->optind]; + i = find_short_opt(opt[p->subind], p->ops); + if (i < 0) { + /* unrecognized option */ + p->str[1] = (char) opt[p->subind]; + p->optarg = p->str; + p->subind++; + return '?'; + } + + if (!p->ops[i].has_arg) { + /* it's ok */ + p->subind++; + return p->ops[i].val; + } + + /* needs an argument */ + if (opt[p->subind + 1] != '\0') { + /* the argument is the suffix */ + p->optarg = &opt[p->subind + 1]; + p->subind = 0; + p->optind++; + return p->ops[i].val; + } + + /* the argument is the next token */ + p->optind++; + p->subind = 0; + if (p->optind < p->argc) { + p->optarg = p->argv[p->optind]; + p->optind++; + return p->ops[i].val; + } + + /* ups, argument missing */ + p->str[1] = (char) p->ops[i].val; + p->optarg = p->str; + return ':'; +} + +static int get_opt(struct ngetopt *p) +{ + int i; + char *opt, *optnext; + + /* all arguments consumed */ + if (p->optind >= p->argc) + return -1; + + opt = p->argv[p->optind]; + if (opt[0] != '-') { + /* non option */ + return -1; + } + + /* - */ + if (opt[1] == '\0') { + /* stdin */ + return -1; + } + + if (opt[1] != '-') { + /* -xxxxx */ + p->subind = 1; + return get_short_opt(p); + } + + /* -- */ + if (opt[2] == '\0') { + /* found "--" */ + p->optind++; + return -1; + } + + /* long option */ + i = find_long_opt(&opt[2], p->ops); + if (i < 0) { + /* not found */ + p->optind++; + p->optarg = opt; + while (*opt != '\0' && *opt != '=') { + opt++; + } + *opt = '\0'; + return '?'; + } + + /* found, go to end of option */ + optnext = opt + 2 + strlen(p->ops[i].name); + + if (*optnext == '\0' && !p->ops[i].has_arg) { + /* doesn't need arguments */ + p->optind++; + p->optstr = opt + 2; + return p->ops[i].val; + } + + if (*optnext == '=' && !p->ops[i].has_arg) { + /* does not need arguments but argument supplied */ + *optnext = '\0'; + p->optarg = opt; + return ';'; + } + + /* the argument is the next token */ + if (*optnext == '\0') { + p->optind++; + if (p->optind < p->argc) { + p->optstr = opt + 2; + p->optarg = p->argv[p->optind]; + p->optind++; + return p->ops[i].val; + } + + /* ups, argument missing */ + p->optarg = opt; + p->optind++; + return ':'; + } + + /* *optnext == '=' */ + *optnext = '\0'; + p->optstr = opt + 2; + p->optarg = optnext + 1; + p->optind++; + return p->ops[i].val; +} + +/* + * If ok: + * + * - For a long option with a zero value single character option, 0 is + * returned, optstr is the string of the long option (without '-' or '--') + * and optarg is the option argument or NULL. + * + * - For anything else the single option character is returned and optarg + * is the option argument or NULL. + * + * If the option is not recognized, '?' is returned, and optarg is the + * literal string of the option not recognized (already with '-' or '--' + * prefixed). + * + * If the option is recognized but the argument is missing, ':' is + * returned and optarg is the option as supplied (with '-' or '--' prefixed). + * + * If the option is recognized and it is a long option followed by '=', but the + * option does not take arguments, ';' is returned and optarg is the option + * (with '-' or '--' prefixed). + * + * -1 is returned if no more options. + */ +int ngetopt_next(struct ngetopt *p) +{ + if (p->subind == 0) + return get_opt(p); + + /* p->subind > 0 */ + if (p->argv[p->optind][p->subind] != '\0') + return get_short_opt(p); + + /* no more options in this list of short options */ + p->subind = 0; + p->optind++; + return get_opt(p); +} diff --git a/2048/uz80as/ngetopt.h b/2048/uz80as/ngetopt.h new file mode 100644 index 0000000..31ff8a6 --- /dev/null +++ b/2048/uz80as/ngetopt.h @@ -0,0 +1,40 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Handling of command line options, similar to getopt. + * =========================================================================== + */ + +#ifndef NGETOPT_H +#define NGETOPT_H + +/* + * Changelog: + * + * - Jul 21 2018: long options without short option character recognized. + * + */ + +struct ngetopt_opt { + const char *name; + int has_arg; + int val; +}; + +struct ngetopt { + char *optstr; + char *optarg; + /* private */ + int optind; + int argc; + char *const *argv; + struct ngetopt_opt *ops; + int subind; + char str[3]; +}; + +void ngetopt_init(struct ngetopt *p, int argc, char *const *argv, + struct ngetopt_opt *ops); +int ngetopt_next(struct ngetopt *p); + +#endif diff --git a/2048/uz80as/options.c b/2048/uz80as/options.c new file mode 100644 index 0000000..c3c24cd --- /dev/null +++ b/2048/uz80as/options.c @@ -0,0 +1,33 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Global options, normally coming from the command line. + * =========================================================================== + */ + +#include "config.h" +#include "options.h" +#include "err.h" + +const char *s_asmfname; /* Name of source file. */ +const char *s_objfname; /* Name of generated binary file. */ +const char *s_lstfname; /* Name of listing file. */ +const char *s_target_id = "z80"; /* ID of target */ +int s_listing = 1; /* If we generate the listing file or not. */ +int s_extended_op = 0; /* Allow extended instruction syntax. */ +int s_undocumented_op = 0; /* Allow undocumented instructions. */ +int s_mem_fillval = 0; /* Default value to fill the 64K memory. */ + +/* Command line macro definitions. */ +struct predef *s_predefs; + +/* Predefine a macro in the command line that must persist between passes. */ +void predefine(const char *text) +{ + struct predef *pdef; + + pdef = emalloc(sizeof(*pdef)); + pdef->name = text; + pdef->next = s_predefs; + s_predefs = pdef; +} diff --git a/2048/uz80as/options.h b/2048/uz80as/options.h new file mode 100644 index 0000000..c23ed91 --- /dev/null +++ b/2048/uz80as/options.h @@ -0,0 +1,29 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Global options, normally coming from the command line. + * =========================================================================== + */ + +#ifndef OPTIONS_H +#define OPTIONS_H + +/* Predefined macro at the command line. */ +struct predef { + struct predef *next; + const char *name; +}; + +extern const char *s_asmfname; +extern const char *s_objfname; +extern const char *s_lstfname; +extern const char *s_target_id; +extern int s_listing; +extern int s_extended_op; +extern int s_undocumented_op; +extern int s_mem_fillval; +extern struct predef *s_predefs; + +void predefine(const char *name); + +#endif diff --git a/2048/uz80as/pp.c b/2048/uz80as/pp.c new file mode 100644 index 0000000..b3b69f2 --- /dev/null +++ b/2048/uz80as/pp.c @@ -0,0 +1,745 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Preprocessor. + * =========================================================================== + */ + +#include "config.h" +#include "pp.h" +#include "utils.h" +#include "err.h" +#include "incl.h" +#include "expr.h" +#include "exprint.h" + +#ifndef CTYPE_H +#include +#endif + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +#ifndef STRING_H +#include +#endif + +/* Max number of macros. */ +#define NMACROS 1000 + +/* Closest prime to NMACROS / 4. */ +#define MACTABSZ 241 + +/* Max number of macro arguments. */ +#define NPARAMS 20 + +#define DEFINESTR "DEFINE" +#define DEFCONTSTR "DEFCONT" +#define INCLUDESTR "INCLUDE" +#define IFSTR "IF" +#define IFDEFSTR "IFDEF" +#define IFNDEFSTR "IFNDEF" +#define ENDIFSTR "ENDIF" +#define ELSESTR "ELSE" + +/* + * Macro. + * + * For example, the macro: + * + * #define SUM(a,b) (a+b) + * + * is: + * + * name = SUM + * pars = a\0b\0 + * ppars[0] points to &pars[0], that is to "a" + * ppars[1] points to &pars[2], that is to "b" + * npars = 2 + * text is "(a+b)" + */ +struct macro { + struct macro *next; /* Next in hash chain. */ + char *name; /* Identifier. */ + char *pars; /* String with params separated by '\0'. */ + char *text; /* Text to expand. */ + char *ppars[NPARAMS]; /* Pointers to the beginning of each param. */ + int npars; /* Valid number of params in ppars. */ +}; + +/* Hash table of preprocessor symbols. */ +static struct macro *s_mactab[MACTABSZ]; + +/* Preprocessing line buffers. */ +static char s_ppbuf[2][LINESZ]; + +/* If we are discarding lines; if not 0, level of if. */ +int s_skipon; + +/* Number of nested #if or #ifdef or #ifndef. */ +static int s_nifs; + +/* Last defined macro. */ +static struct macro *s_lastmac; + +/* Number of macros in table. */ +static int s_nmacs; + +/* The preprocessed line, points to one of s_ppbuf. */ +char *s_pline; + +/* Current program counter. */ +int s_pc; + +/* Current pass. */ +int s_pass; + + +/* Only valid while in the call to pp_line(). */ +static const char *s_line; /* original line */ +static const char *s_line_ep; /* pointer inside s_line for error reporting */ + +/* + * Copy [p, q[ to [dp, dq[. + */ +static char *copypp(char *dp, char *dq, const char *p, const char *q) +{ + while (dp < dq && p < q) + *dp++ = *p++; + return dp; +} + +/* + * Find the 'argnum' argument in 'args' and return a pointer to it. + * + * 'args' is a list of arguments "([id [,id]*). + * 'argnum' is the argument number to find. + * + * Return not found. + */ +static const char *findarg(const char *args, int argnum) +{ + if (*args == '(') { + do { + args++; + if (argnum == 0) + return args; + argnum--; + while (*args != '\0' && *args != ',' + && *args != ')') + { + args++; + } + } while (*args == ','); + } + return NULL; +} + +/* + * Find the 'argnum' argument in 'args' and copy it to [dp, dq[. + * + * 'args' points to a list of arguments "([id [,id]*). + * 'argnum' is the argument number to copy. + * + * Return the new 'dp' after copying. + */ +static char *copyarg(char *dp, char *dq, const char *args, int argnum) +{ + const char *p; + + p = findarg(args, argnum); + if (p == NULL) + return dp; + + while (dp < dq && *p != '\0' && *p != ',' && *p != ')') + *dp++ = *p++; + return dp; +} + +/* + * Sees if [idp, idq[ is a parameter of the macro 'pps'. + * If it is, return the number of parameter. + * Else return -1. + */ +static int findparam(const char *idp, const char *idq, struct macro *pps) +{ + int i; + const char *p, *r; + + for (i = 0; i < pps->npars; i++) { + p = pps->ppars[i]; + r = idp; + while (*p != '\0' && r < idq && *p == *r) { + p++; + r++; + } + if (*p == '\0' && r == idq) + return i; + } + return -1; +} + +/* + * Lookup the string in [p, q[ in 's_mactab'. + * Return the symbol or NULL if it is not in the table. + */ +static struct macro *pplookup(const char *p, const char *q) +{ + int h; + struct macro *nod; + + h = hash(p, q, MACTABSZ); + for (nod = s_mactab[h]; nod != NULL; nod = nod->next) + if (scmp(p, q, nod->name) == 0) + return nod; + + return nod; +} + +/* + * Expand macro in [dp, dq[. + * + * 'pps' is the macro to expand. + * 'args' points to the start of the arguments to substitute, if any. + * + * Return new dp. + */ +static char *expandid(char *dp, char *dq, struct macro *pps, const char *args) +{ + const char *p, *q; + int validid, argnum; + + validid = 1; + p = pps->text; + while (*p != '\0' && dp < dq) { + if (isidc0(*p)) { + for (q = p; isidc(*q); q++) + ; + if (validid) { + argnum = findparam(p, q, pps); + if (argnum >= 0) + dp = copyarg(dp, dq, args, argnum); + else + dp = copypp(dp, dq, p, q); + } else { + dp = copypp(dp, dq, p, q); + } + p = q; + validid = 1; + } else { + validid = !isidc(*p); + *dp++ = *p++; + } + } + return dp; +} + +/* + * If 'p' points the the start of an argument list, that is, '(', + * point to one character past the first ')' after 'p'. + * Else return 'p'. + */ +static const char *skipargs(const char *p) +{ + if (*p == '(') { + while (*p != '\0' && *p != ')') + p++; + if (*p == ')') + p++; + } + return p; +} + +/* + * Expand macros found in 'p' (null terminated) into [dp, dq[. + * dq must be writable to put a final '\0'. + */ +static int expand_line(char *dp, char *dq, const char *p) +{ + char *op; + int expanded, validid; + const char *s; + struct macro *nod; + + validid = 1; + expanded = 0; + while (dp < dq && *p != '\0' && *p != ';') { + if (*p == '\'' && *(p + 1) != '\0' && *(p + 2) == '\'') { + /* characters */ + dp = copypp(dp, dq, p, p + 3); + p += 3; + validid = 1; + } else if (*p == '\"') { + /* strings */ + s = p; + p++; + /* skip over the string literal */ + while (*p != '\0' && *p != '\"') { + if (p[0] == '\\' && p[1] == '\"') + p++; + p++; + } + if (*p == '\"') + p++; + dp = copypp(dp, dq, s, p); + validid = 1; + } else if (isidc0(*p)) { + s = p; + while (isidc(*p)) + p++; + if (validid) { + nod = pplookup(s, p); + if (nod != NULL) { + op = dp; + dp = expandid(dp, dq, nod, p); + expanded = dp != op; + p = skipargs(p); + } else { + dp = copypp(dp, dq, s, p); + } + } else { + dp = copypp(dp, dq, s, p); + } + validid = 1; + } else { + validid = *p != '.' && !isalnum(*p); + *dp++ = *p++; + } + } + *dp = '\0'; + return expanded; +} + +/* + * Expand macros found in 'p' (null terminated). + * Return a pointer to an internal preprocessed line (null terminated). + */ +static char *expand_line0(const char *p) +{ + int iter, expanded; + char *np, *nq, *op; + + iter = 0; + np = &s_ppbuf[iter & 1][0]; + nq = &s_ppbuf[iter & 1][LINESZ - 1]; + expanded = expand_line(np, nq, p); + /* TODO: recursive macro expansion limit */ + while (expanded && iter < 5) { + op = np; + iter++; + np = &s_ppbuf[iter & 1][0]; + nq = &s_ppbuf[iter & 1][LINESZ - 1]; + expanded = expand_line(np, nq, op); + } + return np; +} + +/* + * Check if 'p' starts with the preprocessor directive 'ucq', that must be in + * upper case. + * 'p' can have any case. + * After the preprocessor directive must be a space or '\0'. + * Return 1 if all the above is true. 0 otherwise. + */ +static int isppid(const char *p, const char *ucq) +{ + while (*p != '\0' && *ucq != '\0' && toupper(*p) == *ucq) { + p++; + ucq++; + } + return (*ucq == '\0') && (*p == '\0' || isspace(*p)); +} + +/* + * Define a macro. + * + * [idp, idq[ is the macro id. + * [ap, aq[ is the macro argument list. If ap == aq there are no arguments. + * [tp, tq[ is the macro text. + */ +static void define(const char *idp, const char *idq, + const char *tp, const char *tq, + const char *ap, const char *aq) +{ + int h; + char *p; + struct macro *nod; + + h = hash(idp, idq, MACTABSZ); + for (nod = s_mactab[h]; nod != NULL; nod = nod->next) { + if (scmp(idp, idq, nod->name) == 0) { + /* Already defined. */ + return; + } + } + + s_nmacs++; + if (s_nmacs >= NMACROS) { + eprint(_("maximum number of macros exceeded (%d)\n"), NMACROS); + exit(EXIT_FAILURE); + } + + nod = emalloc((sizeof *nod) + (idq - idp) + (aq - ap) + 2); + nod->text = emalloc(tq - tp + 1); + nod->name = (char *) ((unsigned char *) nod + (sizeof *nod)); + nod->pars = nod->name + (idq - idp + 1); + + copychars(nod->name, idp, idq); + copychars(nod->text, tp, tq); + copychars(nod->pars, ap, aq); + + // printf("DEF %s(%s) %s\n", nod->name, nod->pars, nod->text); + + /* We don't check whether the arguments are different. */ + + /* + * Make ppars point to each argument and null terminate each one. + * Count the number of arguments. + */ + nod->npars = 0; + p = nod->pars; + while (*p != '\0') { + nod->ppars[nod->npars++] = p; + while (*p != '\0' && *p != ',') + p++; + if (*p == ',') + *p++ = '\0'; + } + + nod->next = s_mactab[h]; + s_mactab[h] = nod; + s_lastmac = nod; +} + +/* Add the text [p, q[ to the last macro text. */ +static void defcont(const char *p, const char *q) +{ + char *nt; + size_t len; + + len = strlen(s_lastmac->text); + nt = erealloc(s_lastmac->text, (q - p) + len + 1); + copychars(nt + len, p, q); + s_lastmac->text = nt; +} + +/* + * If 'p' points to a valid identifier start, go to the end of the identifier. + * Else return 'p'. + */ +static const char *getid(const char *p) +{ + if (isidc0(*p)) { + while (isidc(*p)) + p++; + } + return p; +} + +/* Issues error in a macro definition. */ +static void macdeferr(int cmdline, const char *estr, const char *ep) +{ + if (cmdline) { + eprint(_("error in command line macro definition\n")); + } + eprint(estr); + eprcol(s_line, ep); + if (cmdline) { + exit(EXIT_FAILURE); + } else { + newerr(); + } +} + +/* Parse macro definition. */ +static void pmacdef(const char *p, int cmdline) +{ + const char *q, *ap, *aq, *idp, *idq; + + idp = p; + idq = getid(idp); + if (idq == idp) { + macdeferr(cmdline, _("identifier excepted\n"), p); + return; + } + p = idq; + ap = aq = p; + if (*p == '(') { + p++; + ap = p; + while (isidc0(*p)) { + p = getid(p); + if (*p != ',') + break; + p++; + } + if (*p != ')') { + macdeferr(cmdline, _("')' expected\n"), p); + return; + } + aq = p; + p++; + } + if (*p != '\0' && !isspace(*p)) { + macdeferr(cmdline, _("space expected\n"), p); + return; + } + p = skipws(p); + /* go to the end */ + for (q = p; *q != '\0'; q++) + ; + /* go to the first non white from the end */ + while (q > p && isspace(*(q - 1))) + q--; + define(idp, idq, p, q, ap, aq); +} + +/* Parse #define. */ +static void pdefine(const char *p) +{ + p = skipws(p + sizeof(DEFINESTR) - 1); + pmacdef(p, 0); +} + +/* Parse #defcont. */ +static void pdefcont(const char *p) +{ + const char *q; + + p = skipws(p + sizeof(DEFCONTSTR) - 1); + + /* go to the end */ + for (q = p; *q != '\0'; q++) + ; + + /* go to the first non white from the end */ + while (q > p && isspace(*(q - 1))) + q--; + + if (p == q) { + /* nothing to add */ + return; + } + + if (s_lastmac == NULL) { + eprint(_("#DEFCONT without a previous #DEFINE\n")); + eprcol(s_line, s_line_ep); + newerr(); + return; + } + + defcont(p, q); +} + +/* Parse #include. */ +static void pinclude(const char *p) +{ + const char *q; + + p = skipws(p + sizeof(INCLUDESTR) - 1); + if (*p != '\"') { + eprint(_("#INCLUDE expects a filename between quotes\n")); + eprcol(s_line, p); + newerr(); + return; + } + q = ++p; + while (*q != '\0' && *q != '\"') + q++; + if (*q != '\"') { + wprint(_("no terminating quote\n")); + eprcol(s_line, q); + } + pushfile(p, q); +} + +/* + * Parse #ifdef or #ifndef. + * 'idsz' is the length of the string 'ifdef' or 'ifndef', plus '\0'. + * 'ifdef' must be 1 if we are #ifdef, 0 if #ifndef. + */ +static void pifdef(const char *p, size_t idsz, int ifdef) +{ + const char *q; + struct macro *nod; + + s_nifs++; + if (s_skipon) + return; + + p = skipws(p + idsz - 1); + if (!isidc0(*p)) { + s_skipon = s_nifs; + eprint(_("identifier expected\n")); + eprcol(s_line, p); + newerr(); + return; + } + q = p; + while (isidc(*q)) + q++; + nod = pplookup(p, q); + if (ifdef == (nod != NULL)) + s_skipon = 0; + else + s_skipon = s_nifs; +} + +/* Parse #else. */ +static void pelse(const char *p) +{ + if (s_nifs == 0) { + eprint(_("unbalanced #ELSE\n")); + eprcol(s_line, s_line_ep); + newerr(); + return; + } + + if (s_skipon && s_nifs == s_skipon) + s_skipon = 0; + else if (!s_skipon) + s_skipon = s_nifs; +} + +/* Parse #endif. */ +static void pendif(const char *p) +{ + if (s_nifs == 0) { + eprint(_("unbalanced #ENDIF\n")); + eprcol(s_line, s_line_ep); + newerr(); + return; + } + + if (s_skipon && s_nifs == s_skipon) + s_skipon = 0; + s_nifs--; +} + +/* + * Parse #if. + */ +static void pif(const char *p) +{ + int v; + enum expr_ecode ex_ec; + const char *ep; + + s_nifs++; + if (s_skipon) + return; + + p = skipws(p + sizeof(IFSTR) - 1); + if (!expr(p, &v, s_pc, 1, &ex_ec, &ep)) { + s_skipon = 1; + exprint(ex_ec, s_line, ep); + newerr(); + return; + } + + if (v == 0) + s_skipon = s_nifs; + else + s_skipon = 0; +} + +/* + * Parse a preprocessor line. + * 'p' points to the next character after the '#'. + */ +static int +parse_line(const char *p) +{ + if (isppid(p, IFDEFSTR)) { + pifdef(p, sizeof IFDEFSTR, 1); + } else if (isppid(p, IFNDEFSTR)) { + pifdef(p, sizeof IFNDEFSTR, 0); + } else if (isppid(p, IFSTR)) { + pif(p); + } else if (isppid(p, ELSESTR)) { + pelse(p); + } else if (isppid(p, ENDIFSTR)) { + pendif(p); + } else if (s_skipon) { + ; + } else if (isppid(p, INCLUDESTR)) { + pinclude(p); + } else if (isppid(p, DEFINESTR)) { + pdefine(p); + } else if (isppid(p, DEFCONTSTR)) { + pdefcont(p); + } else { + return 0; +/* + eprint(_("unknown preprocessor directive\n")); + eprcol(s_line, s_line_ep); + newerr(); +*/ + } + return 1; +} + +/* + * Preprocess 'line' in 's_pline'. + * In this module, while we are preprocessing: + * s_line is the original line. + * s_line_ep is a pointer inside line that we keep for error reporting. + */ +void pp_line(const char *line) +{ + const char *p; + + s_line = line; + s_line_ep = line; + + p = skipws(line); + if ((*p == '#') || (*p == '.')) { + s_line_ep = p; + if (parse_line(p + 1)) { + s_ppbuf[0][0] = '\0'; + s_pline = &s_ppbuf[0][0]; + return; + } + } + if (s_skipon) { + s_ppbuf[0][0] = '\0'; + s_pline = &s_ppbuf[0][0]; + return; + } + s_pline = expand_line0(line); +} + +/* Reset the module for other passes. */ +void pp_reset(void) +{ + int i; + struct macro *nod, *cur; + + s_nmacs = 0; + s_nifs = 0; + s_skipon = 0; + s_lastmac = NULL; + for (i = 0; i < MACTABSZ; i++) { + nod = s_mactab[i]; + while (nod != NULL) { + cur = nod; + nod = nod->next; + free(cur->text); + free(cur); + } + } + memset(s_mactab, 0, MACTABSZ * sizeof(s_mactab[0])); +} + +void pp_define(const char *mactext) +{ + s_line = mactext; + s_line_ep = mactext; + pmacdef(mactext, 1); + s_lastmac = NULL; +} diff --git a/2048/uz80as/pp.h b/2048/uz80as/pp.h new file mode 100644 index 0000000..74a6d6f --- /dev/null +++ b/2048/uz80as/pp.h @@ -0,0 +1,23 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Preprocessor. + * =========================================================================== + */ + +#ifndef PP_H +#define PP_H + +/* Max line length after macro expansion + '\0'. */ +#define LINESZ 512 + +extern char *s_pline; +extern int s_pc; +extern int s_pass; +extern int s_skipon; + +void pp_line(const char *line); +void pp_reset(void); +void pp_define(const char *name); + +#endif diff --git a/2048/uz80as/prtable.c b/2048/uz80as/prtable.c new file mode 100644 index 0000000..1c3be97 --- /dev/null +++ b/2048/uz80as/prtable.c @@ -0,0 +1,343 @@ +#include "prtable.h" +#include "err.h" +#include "targets.h" +#include "uz80as.h" + +#ifndef STDLIB_H +#include +#endif + +#ifndef CTYPE_H +#include +#endif + +#ifndef STRING_H +#include +#endif + +enum { STRSZ = 32 }; + +struct itext { + struct itext *next; + int undoc; + char str[STRSZ]; +}; + +struct ilist { + struct itext *head; + int nelems; +}; + +struct itable { + struct itext **table; + int nelems; +}; + +static char s_buf[STRSZ]; + +static void nomem(const char *str) +{ + eprogname(); + fprintf(stderr, _("not enough memory (%s)\n"), str); + exit(EXIT_FAILURE); +} + +static int compare(const void *pa, const void *pb) +{ + const struct itext * const *ia; + const struct itext * const *ib; + + ia = (const struct itext * const *) pa; + ib = (const struct itext * const *) pb; + return strcmp((*ia)->str, (*ib)->str); +} + +/* + * Returns a new allocated itable of pointers that point to each element in the + * list ilist, alphabetically sorted. + */ +static struct itable *sort_list(struct ilist *ilist) +{ + int n; + struct itable *itable; + struct itext *p; + + if ((itable = calloc(1, sizeof(*itable))) == NULL) { + return NULL; + } + itable->nelems = 0; + + if (ilist->nelems == 0) { + return itable; + } + + itable->table = malloc(ilist->nelems * sizeof(*itable->table)); + if (itable->table == NULL) { + free(itable); + return NULL; + } + + for (n = 0, p = ilist->head; + p != NULL && n < ilist->nelems; + p = p->next, n++) + { + itable->table[n] = p; + } + itable->nelems = n; + + qsort(itable->table, itable->nelems, sizeof(*itable->table), compare); + return itable; +} + +static void print_itable(struct itable *itable, FILE *f) +{ + int i, col; + struct itext *p; + + if (itable == NULL) { + return; + } + + fputs("@multitable @columnfractions .25 .25 .25 .25\n", f); + col = 0; + for (i = 0; i < itable->nelems; i++) { + p = itable->table[i]; + if (col == 0) { + fputs("@item ", f); + } else { + fputs("@tab ", f); + } + if (p->undoc) { + fputs("* ", f); + } + fprintf(f, "%s\n", p->str); + col++; + if (col >= 4) { + col = 0; + } + } + fputs("@end multitable\n", f); +} + +#if 0 +static void print_ilist(struct ilist *ilist, FILE *f) +{ + int col; + struct itext *p; + + if (ilist == NULL) { + return; + } + + fputs("@multitable @columnfractions .25 .25 .25 .25\n", f); + col = 0; + for (p = ilist->head; p != NULL; p = p->next) { + if (col == 0) { + fputs("@item ", f); + } else { + fputs("@tab ", f); + } + if (p->undoc) { + fputs("* ", f); + } + fprintf(f, "%s\n", p->str); + col++; + if (col >= 4) { + col = 0; + } + } + fputs("@end multitable\n", f); +} +#endif + +static void bufset(int i, char c) +{ + if (i >= STRSZ) { + eprogname(); + fputs(_("prtable: please, increase s_buf size\n"), stderr); + exit(EXIT_FAILURE); + } else { + s_buf[i] = c; + } +} + +static void gen_inst2(struct ilist *ilist, char *instr, int undoc) +{ + struct itext *p; + + if ((p = malloc(sizeof(*p))) == NULL) { + nomem("gen_inst2"); + } + + snprintf(p->str, STRSZ, "%s", instr); + p->undoc = undoc; + p->next = ilist->head; + ilist->head = p; + ilist->nelems++; +} + +static void gen_inst(struct ilist *ilist, const struct target *t, + unsigned char undoc, const char *p, size_t bufi, + const char *pr) +{ + size_t bufk; + const char *s; + + while (*p) { + if (!islower(*p)) { + if (*p == '@') { + bufset(bufi++, '@'); + } + bufset(bufi++, *p); + p++; + } else if (*p == 'a') { + if (pr == NULL) { + bufset(bufi++, 'e'); + } else if (pr[0] && pr[1]) { + if (pr[0] == pr[1]) { + bufset(bufi++, pr[0]); + pr += 2; + } else if (isdigit(pr[1])) { + bufset(bufi++, *pr); + pr++; + while (isdigit(*pr)) { + bufset(bufi++, *pr); + pr++; + } + } else { + bufset(bufi++, pr[0]); + bufset(bufi++, pr[1]); + pr += 2; + } + } else { + bufset(bufi++, 'e'); + } + p++; + } else { + break; + } + } + + if (*p == '\0') { + bufset(bufi, '\0'); + gen_inst2(ilist, s_buf, t->mask & undoc); + } else { + t->pat_char_rewind(*p); + while ((s = t->pat_next_str()) != NULL) { + if (s[0] != '\0') { + bufset(bufi, '\0'); + bufk = bufi; + while (*s != '\0') { + bufset(bufk++, *s); + s++; + } + bufset(bufk, '\0'); + gen_inst(ilist, t, undoc, p + 1, bufk, pr); + } + } + } +} + +/* Generate a list of instructions. */ +static struct ilist *gen_list(const struct target *t, unsigned char mask2, + int delta) +{ + int i, pr; + const struct matchtab *mt; + struct ilist *ilist; + + if ((ilist = calloc(1, sizeof(*ilist))) == NULL) { + return NULL; + } + + i = 0; + mt = t->matcht; + while (mt[i].pat != NULL) { + pr = 0; + if (t->mask == 1 && (mt[i].mask & 1)) { + pr = 1; + } else if (delta) { + if ((mt[i].mask & t->mask) && + !(mt[i].mask & mask2)) + { + pr = 1; + } + } else if (t->mask & mt[i].mask) { + pr = 1; + } + if (pr) { + gen_inst(ilist, t, mt[i].undoc, mt[i].pat, + 0, mt[i].pr); + } + i++; + } + + return ilist; +} + +/* + * Prints the instruction set of a target or if target_id is "target2,target1" + * prints the instructions in target2 not in target1. + */ +void print_table(FILE *f, const char *target_id) +{ + struct ilist *ilist; + struct itable *itable; + const struct target *t, *t2; + char target1[STRSZ]; + const char *target2; + unsigned char mask2; + int delta; + + /* check if we have "target" or "target,target" as arguments */ + if ((target2 = strchr(target_id, ',')) != NULL) { + delta = 1; + snprintf(target1, sizeof(target1), "%s", target_id); + target1[target2 - target_id] = '\0'; + target2++; + } else { + delta = 0; + snprintf(target1, sizeof(target1), "%s", target_id); + target2 = NULL; + } + + t = find_target(target1); + if (t == NULL) { + eprogname(); + fprintf(stderr, _("invalid target '%s'\n"), target1); + exit(EXIT_FAILURE); + } + + if (target2) { + t2 = find_target(target2); + if (t2 == NULL) { + eprogname(); + fprintf(stderr, _("invalid target '%s'\n"), target2); + exit(EXIT_FAILURE); + } + if (t->matcht != t2->matcht) { + eprogname(); + fprintf(stderr, _("unrelated targets %s,%s\n"), + target1, target2); + exit(EXIT_FAILURE); + } + mask2 = t2->mask; + } else { + mask2 = 1; + } + + if ((ilist = gen_list(t, mask2, delta)) == NULL) { + nomem("gen_list"); + } + + if ((itable = sort_list(ilist)) == NULL) { + nomem("sort_list"); + } + + print_itable(itable, f); + + /* We don't free ilist nor itable for now, since this is called + * from main and then the program terminated. + */ +} + diff --git a/2048/uz80as/prtable.h b/2048/uz80as/prtable.h new file mode 100644 index 0000000..41eb646 --- /dev/null +++ b/2048/uz80as/prtable.h @@ -0,0 +1,11 @@ +#ifndef PRTABLE_H +#define PRTABLE_H + +#ifndef STDIO_H +#define STDIO_H +#include +#endif + +void print_table(FILE *f, const char *target_id); + +#endif diff --git a/2048/uz80as/sym.c b/2048/uz80as/sym.c new file mode 100644 index 0000000..53fc262 --- /dev/null +++ b/2048/uz80as/sym.c @@ -0,0 +1,103 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Symbol table for labels. + * =========================================================================== + */ + +#include "config.h" +#include "sym.h" +#include "utils.h" +#include "err.h" + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +/* + * Maximum number of symbols (labels) allowed. + * Must not be more than 64K. + */ +#define NSYMS 15000 + +/* Closest prime to NSYMS / 4. */ +#define SYMTABSZ 3739 + +/* + * Nodes for the s_symtab hash table. + * The symbol at index 0 is never used. + */ +static struct sym s_symlist[NSYMS]; + +/* + * Hash table of indexes into s_symlist. + * 0 means that the bucket is empty. + */ +static unsigned short s_symtab[SYMTABSZ]; + +/* Next free symbol in s_symlist. Note: 0 not used. */ +static int s_nsyms = 1; + +/* + * Lookups the string in [p, q[ in s_symtab. + * If !insert, returns the symbol or NULL if it is not in the table. + * If insert, inserts the symbol in the table if it is not there, and + * sets its .val to 'pc'. + */ +struct sym *lookup(const char *p, const char *q, int insert, int pc) +{ + int h, k; + struct sym *nod; + + if (q - p > SYMLEN - 1) { + /* Label too long, don't add. */ + eprint(_("label too long")); + epchars(p, q); + enl(); + newerr(); + /* + * This would truncate: + * q = p + (SYMLEN - 1); + */ + return NULL; + } + + h = hash(p, q, SYMTABSZ); + for (k = s_symtab[h]; k != 0; k = s_symlist[k].next) { + if (scmp(p, q, s_symlist[k].name) == 0) { + if (insert) { + if (!s_symlist[k].isequ) { + wprint("duplicate label (%s)\n", + s_symlist[k].name); + } + } + return &s_symlist[k]; + } + } + + if (insert) { + if (s_nsyms == NSYMS) { + eprint(_("maximum number of labels exceeded (%d)\n"), + NSYMS); + exit(EXIT_FAILURE); + } + + nod = &s_symlist[s_nsyms]; + nod->next = s_symtab[h]; + s_symtab[h] = (unsigned short) s_nsyms; + s_nsyms++; + + k = 0; + while (p != q && k < SYMLEN - 1) + nod->name[k++] = *p++; + nod->name[k] = '\0'; + nod->val = pc; + return nod; + } + + return NULL; +} diff --git a/2048/uz80as/sym.h b/2048/uz80as/sym.h new file mode 100644 index 0000000..43108c6 --- /dev/null +++ b/2048/uz80as/sym.h @@ -0,0 +1,23 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Symbol table for labels. + * =========================================================================== + */ + +#ifndef SYM_H +#define SYM_H + +/* Max symbol length + '\0'. */ +#define SYMLEN 32 + +struct sym { + char name[SYMLEN]; /* null terminated string */ + int val; /* value of symbol */ + unsigned short next; /* index into symlist; 0 is no next */ + unsigned char isequ; /* if val comes from EQU */ +}; + +struct sym *lookup(const char *p, const char *q, int insert, int pc); + +#endif diff --git a/2048/uz80as/targets.c b/2048/uz80as/targets.c new file mode 100644 index 0000000..742ef67 --- /dev/null +++ b/2048/uz80as/targets.c @@ -0,0 +1,96 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Target list. + * =========================================================================== + */ + +#include "targets.h" +#include "uz80as.h" + +#ifndef STRING_H +#include +#endif + +extern const struct target s_target_z80; +extern const struct target s_target_hd64180; +extern const struct target s_target_gbcpu; +extern const struct target s_target_dp2200; +extern const struct target s_target_dp2200ii; +extern const struct target s_target_i4004; +extern const struct target s_target_i4040; +extern const struct target s_target_i8008; +extern const struct target s_target_i8021; +extern const struct target s_target_i8022; +extern const struct target s_target_i8041; +extern const struct target s_target_i8048; +extern const struct target s_target_i8051; +extern const struct target s_target_i8080; +extern const struct target s_target_i8085; +extern const struct target s_target_mos6502; +extern const struct target s_target_r6501; +extern const struct target s_target_g65sc02; +extern const struct target s_target_r65c02; +extern const struct target s_target_r65c29; +extern const struct target s_target_w65c02s; +extern const struct target s_target_mc6800; +extern const struct target s_target_mc6801; +extern const struct target s_target_m68hc11; + +static const struct target *s_targets[] = { + &s_target_z80, + &s_target_hd64180, + &s_target_gbcpu, + &s_target_dp2200, + &s_target_dp2200ii, + &s_target_i4004, + &s_target_i4040, + &s_target_i8008, + &s_target_i8021, + &s_target_i8022, + &s_target_i8041, + &s_target_i8048, + &s_target_i8051, + &s_target_i8080, + &s_target_i8085, + &s_target_mos6502, + &s_target_r6501, + &s_target_g65sc02, + &s_target_r65c02, + &s_target_r65c29, + &s_target_w65c02s, + &s_target_mc6800, + &s_target_mc6801, + &s_target_m68hc11, + NULL, +}; + +static int s_index; + +const struct target *find_target(const char *id) +{ + const struct target **p; + + for (p = s_targets; *p != NULL; p++) { + if (strcmp(id, (*p)->id) == 0) { + return *p; + } + } + + return NULL; +} + +const struct target *first_target(void) +{ + s_index = 0; + return next_target(); +} + +const struct target *next_target(void) +{ + if (s_targets[s_index] != NULL) { + return s_targets[s_index++]; + } else { + return NULL; + } +} diff --git a/2048/uz80as/targets.h b/2048/uz80as/targets.h new file mode 100644 index 0000000..12b6f64 --- /dev/null +++ b/2048/uz80as/targets.h @@ -0,0 +1,18 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Target list. + * =========================================================================== + */ + +#ifndef TARGETS_H +#define TARGETS_H + +struct target; + +const struct target *find_target(const char *id); + +const struct target *first_target(void); +const struct target *next_target(void); + +#endif diff --git a/2048/uz80as/test.asm b/2048/uz80as/test.asm new file mode 100644 index 0000000..28fdff3 --- /dev/null +++ b/2048/uz80as/test.asm @@ -0,0 +1,8 @@ + .org 0x100 +beep: .text "\"\"\"" ; comment + .align 4 + .text "foo" + .align 16 + .text "bar" + .db $45,0x67,'7' + .end diff --git a/2048/uz80as/test1.asm b/2048/uz80as/test1.asm new file mode 100644 index 0000000..1133b01 --- /dev/null +++ b/2048/uz80as/test1.asm @@ -0,0 +1,3 @@ + .org $100 +beep: .text "x" ; comment + .end diff --git a/2048/uz80as/utils.c b/2048/uz80as/utils.c new file mode 100644 index 0000000..a361f2f --- /dev/null +++ b/2048/uz80as/utils.c @@ -0,0 +1,137 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Generic functions. + * =========================================================================== + */ + +#include "config.h" +#include "utils.h" + +#ifndef CTYPE_H +#include +#endif + +#ifndef LIMITS_H +#include +#endif + +/* + * Copy [p, q[ to dst and null terminate dst. + */ +void copychars(char *dst, const char *p, const char *q) +{ +// int i = 0; +// printf("copychars %x->%x to %x \'", p, q, dst); + while (p != q) { +// printf("%c", *p); + *dst++ = *p++; +// i++; + } + *dst = '\0'; +// printf("\' %d %x %d\n", *dst, dst, i); +} + +/* Skip space. */ +const char *skipws(const char *p) +{ + while (isspace(*p)) + p++; + return p; +} + +/* Return 1 if *p is a valid start character for an identifier. */ +int isidc0(char c) +{ + return (c == '_') || isalpha(c); +} + +/* + * Return 1 if *p is a valid character for an identifier. + * Don't use for the first character. + */ +int isidc(char c) +{ + return (c == '_') || (c == '.') || isalnum(c); +} + +/* Hash the string in [p, q[ to give a bucket in symtab. */ +int hash(const char *p, const char *q, unsigned int tabsz) +{ + unsigned int h; + + h = 0; + while (p != q) { + h = 31 * h + (unsigned char) *p; + p++; + } + + return h % tabsz; +} + +/* + * Compare the string in [p, q[ with the null-terminated string s. + * Return 0 if equal. + */ +int scmp(const char *p, const char *q, const char *s) +{ + while (p < q) { + if (*p == *s) { + p++; + s++; + } else if (*s == '\0') { + return 1; + } else if (*p < *s) { + return -1; + } else { + return 1; + } + } + + if (*s == '\0') + return 0; + else + return -1; +} +/* + * Given a hexadecimal character (in upper case), returns its integer value. + * Returns -1 if c is not a hexadecimal character. + */ +int hexvalu(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'F') + return (c - 'A') + 10; + else + return -1; +} + +/* + * Given a hexadecimal character, returns its integer value. + * Returns -1 if c is not a hexadecimal character. + */ +int hexval(char c) +{ + if (c >= 'a' && c <= 'f') + return (c - 'a') + 10; + else + return hexvalu(c); +} + +int int_precission(void) +{ + static int bits = 0; + unsigned int i; + + if (bits > 0) + return bits; + + i = INT_MAX; + bits = 0; + while (i) { + bits++; + i >>= 1; + } + return bits; +} diff --git a/2048/uz80as/utils.h b/2048/uz80as/utils.h new file mode 100644 index 0000000..2d22db6 --- /dev/null +++ b/2048/uz80as/utils.h @@ -0,0 +1,26 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Generic functions. + * =========================================================================== + */ + +#ifndef UTILS_H +#define UTILS_H + +#define NELEMS(a) (sizeof(a)/sizeof(a[0])) + +#define XSTR(n) STR(n) +#define STR(n) #n + +void copychars(char *dst, const char *p, const char *q); +int hash(const char *p, const char *q, unsigned int tabsz); +int isidc0(char c); +int isidc(char c); +int scmp(const char *p, const char *q, const char *s); +const char *skipws(const char *p); +int hexvalu(char c); +int hexval(char c); +int int_precission(void); + +#endif diff --git a/2048/uz80as/uz80as.c b/2048/uz80as/uz80as.c new file mode 100644 index 0000000..35c13ac --- /dev/null +++ b/2048/uz80as/uz80as.c @@ -0,0 +1,1240 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Assembler. + * =========================================================================== + */ + +#include "config.h" +#include "uz80as.h" +#include "options.h" +#include "utils.h" +#include "err.h" +#include "incl.h" +#include "sym.h" +#include "expr.h" +#include "exprint.h" +#include "pp.h" +#include "list.h" +#include "targets.h" + +#ifndef ASSERT_H +#include +#endif + +#ifndef CTYPE_H +#include +#endif + +#ifndef STDIO_H +#include +#endif + +#ifndef STDLIB_H +#include +#endif + +#ifndef STRING_H +#include +#endif + +static void output(); + +static const char *d_align(const char *); +static const char *d_null(const char *); +static const char *d_block(const char *); +static const char *d_byte(const char *); +static const char *d_chk(const char *); +static const char *d_codes(const char *); +static const char *d_echo(const char *); +static const char *d_eject(const char *); +static const char *d_export(const char *); +static const char *d_end(const char *); +static const char *d_equ(const char *); +static const char *d_fill(const char *); +static const char *d_ds(const char *); +static const char *d_list(const char *); +static const char *d_lsfirst(const char *); +static const char *d_module(const char *); +static const char *d_msfirst(const char *); +static const char *d_nocodes(const char *); +static const char *d_nolist(const char *); +static const char *d_org(const char *); +static const char *d_set(const char *); +static const char *d_text(const char *); +static const char *d_title(const char *); +static const char *d_word(const char *); + +/* + * Directives. + * This table must be sorted, to allow for binary search. + */ +static struct direc { + const char *name; + const char *(*fun)(const char *); +} s_directab[] = { + { "ALIGN", d_align }, + { "BLOCK", d_block }, + { "BYTE", d_byte }, + { "CHK", d_chk }, + { "CODES", d_codes }, + { "DB", d_byte }, + { "DS", d_ds }, + { "DW", d_word }, + { "ECHO", d_echo }, + { "EJECT", d_eject }, + { "END", d_end }, + { "EQU", d_equ }, + { "EXPORT", d_export }, + { "FILL", d_fill }, + { "GLOBAL", d_export }, + { "LIST", d_list }, + { "LSFIRST", d_lsfirst }, + { "MODULE", d_module }, + { "MSFIRST", d_msfirst }, + { "NOCODES", d_nocodes }, + { "NOLIST", d_nolist }, + { "NOPAGE", d_null }, + { "ORG", d_org }, + { "PAGE", d_null }, + { "SECTION", d_null }, + { "SET", d_set }, + { "TEXT", d_text }, + { "TITLE", d_title }, + { "WORD", d_word }, +}; + +/* binary output file */ +FILE *fout; + +/* output in source order */ +int b_flag = 1; + +/* The target. */ +const struct target *s_target; + +/* The z80 addressable memory. The object code. */ +static unsigned char s_mem[64 * 1024]; + +/* Program counter min and max ([s_minpc, s_maxpc[). */ +static int s_minpc, s_maxpc; + +/* Original input line. */ +static char s_line[LINESZ]; + +/* Label defined on this line. */ +static struct sym *s_lastsym; + +/* Output words the most significant byte first */ +static int s_msbword; + +/* If we have seen the .END directive. */ +static int s_end_seen; + +/* We have issued the error of generating things after an .END. */ +static int s_gen_after_end; + +/* The empty line, to pass to listing, for compatibility with TASM. */ +static const char *s_empty_line = ""; + +/* Pointer in s_pline for error reporting. */ +const char *s_pline_ep; + +/* We skip characters until endline or backslash or comment. */ +static const char *sync(const char *p) +{ + while (*p != '\0' && *p != '\\' && *p != ';') + p++; + return p; +} + +/* the written bitmap */ +unsigned char membit[65536 / 8]; + +void +setbit(int pc) +{ + membit[pc / 8] |= (1 << (pc % 8)); +} + +int +isset(int pc) +{ + return membit[pc / 8] & (1 << (pc % 8)); +} + +void +open_output() +{ + fout = efopen(s_objfname, "wb"); +} + +void +close_output() +{ + if (fclose(fout) == EOF) { + eprint(_("cannot close file %s\n"), s_objfname); + } +} + +/* + * Generates a byte to the output and updates s_pc, s_minpc and s_maxpc. + * Will issue a fatal error if we write beyong 64k. + */ +void genb(int b, const char *ep) +{ + if (s_pass == 0 && s_end_seen && !s_gen_after_end) { + s_gen_after_end = 1; + eprint(_("generating code after .END\n")); + eprcol(s_pline, ep); + newerr(); + } + if (s_minpc < 0) + s_minpc = s_pc; + if (s_pc >= 65536) { + eprint(_("generating code beyond address 65535\n")); + eprcol(s_pline, ep); + exit(EXIT_FAILURE); + } + s_mem[s_pc] = (unsigned char) b; + setbit(s_pc); + + if (s_pass == 1) { + list_genb(b); + if (b_flag) { + fwrite(&s_mem[s_pc], 1, 1, fout); + } + } + + if (s_pc < s_minpc) + s_minpc = s_pc; + s_pc++; + if (s_pc > s_maxpc) + s_maxpc = s_pc; +} + +/* + * Generate 'n' as a 16 bit word, little endian or big endian depending on + * s_msbword. + */ +static void genw(int n, const char *ep) +{ + if (s_msbword) + genb(n >> 8, ep); + genb(n, ep); + if (!s_msbword) + genb(n >> 8, ep); +} + +/* + * We have matched an instruction in the table. + * Generate the machine code for the instruction using the generation + * pattern 'p. 'vs are the arguments generated during the matching process. + */ +static void gen(const char *p, const int *vs) +{ + // int w, b, i, savepc; + int b, i, savepc; + const char *p_orig; + + savepc = s_pc; + p_orig = p; + b = 0; +loop: + i = hexvalu(*p); + if (i >= 0) { + p++; + b = (i << 4) | hexval(*p); + } else if (*p == '.') { + genb(b, s_pline_ep); + b = 0; + } else if (*p == '\0') { + return; + } else { + i = *(p + 1) - '0'; + switch (*p) { + case 'b': b |= (vs[i] << 3); break; + case 'c': b |= vs[i]; break; + case 'd': b = vs[i]; break; + case 'e': genb(vs[i] & 0xff, s_pline_ep); + genb(vs[i] >> 8, s_pline_ep); + break; + default: + if (s_target->genf(&b, *p, vs, i, savepc) == -1) { + eprogname(); + fprintf(stderr, + _("fatal: bad pattern %s ('%c')"), + p_orig, *p); + enl(); + exit(EXIT_FAILURE); + } + } + p++; + } + p++; + goto loop; +} + +/* + * Tries to match *p with any of the strings in list. + * If matched, returns the index in list and r points to the position + * in p past the matched string. + */ +int mreg(const char *p, const char *const list[], const char **r) +{ + const char *s; + const char *q; + int i; + + i = 0; + while ((s = list[i++]) != NULL) { + if (*s == '\0') + continue; + q = p; + while (toupper(*q++) == *s++) { + if (*s == '\0') { + if (!isalnum(*q)) { + *r = q; + return i - 1; + } else { + break; + } + } + } + } + return -1; +} + +static int isoctal(int c) +{ + return c >= '0' && c <= '7'; +} + +/* + * Read an octal of 3 digits, being the maximum value 377 (255 decimal); + * Return -1 if there is an error in the syntax. + */ +static int readoctal(const char *p) +{ + int n; + const char *q; + + if (*p >= '0' && *p <= '3' && isoctal(*(p + 1)) && isoctal(*(p + 2))) { + n = 0; + q = p + 3; + while (p < q) { + n *= 8; + n += (*p - '0'); + p++; + } + return n; + } + + return -1; +} + +enum strmode { + STRMODE_ECHO, + STRMODE_NULL, + STRMODE_BYTE, + STRMODE_WORD +}; + +/* + * Generate the string bytes until double quote or null char. + * Return a pointer to the ending double quote character or '\0'. + * 'p must point to the starting double quote. + * If mode: + * STRMODE_ECHO only echo to stderr the characters. + * STRMODE_NULL only parses the string. + * STRMODE_BYTE generate the characters in the binary file as bytes. + * STRMODE_WORD generate the characters in the binary file as words. + */ +static const char *genstr(const char *p, enum strmode mode) +{ + int c; + + for (p = p + 1; *p != '\0' && *p != '\"'; p++) { + c = *p; + if (c == '\\') { + p++; + switch (*p) { + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 'b': c = '\b'; break; + case 't': c = '\t'; break; + case 'f': c = '\f'; break; + case '\\': c = '\\'; break; + case '\"': c = '\"'; break; + default: + c = readoctal(p); + if (c < 0) { + eprint(_("bad character escape " + "sequence\n")); + eprcol(s_pline, p - 1); + newerr(); + p--; + } else { + p += 2; + } + } + } + switch (mode) { + case STRMODE_ECHO: fputc(c, stderr); break; + case STRMODE_NULL: break; + case STRMODE_BYTE: genb(c, p); break; + case STRMODE_WORD: genw(c, p); break; + } + } + + return p; +} + +/* Match an instruction. + * If no match returns NULL; else returns one past end of match. + * p should point to no whitespace. + */ +static const char *match(const char *p) +{ + const struct matchtab *mtab; + const char *s, *pp, *q; + int v, n, vi, linepc; + int vs[4]; + + assert(!isspace(*p)); + + mtab = s_target->matcht; + linepc = s_pc; + pp = p; + n = -1; +next: + n++; + s = mtab[n].pat; + if (s == NULL) { + return NULL; + } else if ((s_target->mask & mtab[n].mask) == 0) { + goto next; + } else if (!s_undocumented_op && (s_target->mask & mtab[n].undoc)) { + goto next; + } + p = pp; + vi = 0; +loop: + if (*s == '\0') { + p = skipws(p); + if (*p != ';' && *p != '\0' && *p != '\\') + goto next; + else + goto found; + } else if (*s == ' ') { + if (!isspace(*p)) + goto next; + p = skipws(p); + } else if ((*s == ',' || *s == '(' || *s == ')') && isspace(*p)) { + p = skipws(p); + if (*s != *p) + goto next; + p = skipws(p + 1); + } else if (*s == 'a') { + p = expr(p, &v, linepc, s_pass == 0, NULL, NULL); + if (p == NULL) + return NULL; + vs[vi++] = v; + } else if (*s >= 'b' && *s <= 'z') { + v = s_target->matchf(*s, p, &q); + goto reg; + } else if (*p == *s && *p == ',') { + p = skipws(p + 1); + } else if (toupper(*p) == *s) { + p++; + } else { + goto next; + } +freg: + s++; + goto loop; +reg: + if (v < 0) { + goto next; + } else { + assert(vi < sizeof(vs)); + vs[vi++] = v; + p = q; + } + goto freg; +found: + // printf("%s\n", s_matchtab[n].pat); + gen(mtab[n].gen, vs); + return p; +} + +static const char * +d_null(const char *p) +{ + p = sync(p); + while (*p != '\0' && *p != '\\') { + if (!isspace(*p)) { + wprint(_("invalid characters after directive\n")); + eprcol(s_pline, p); + return sync(p); + } else { + p++; + } + } + return p; +} + +static const char *d_end(const char *p) +{ + enum expr_ecode ecode; + const char *q; + const char *ep; + + if (s_pass == 0) { + if (s_end_seen) { + eprint(_("duplicate .END\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } else { + s_end_seen = 1; + } + } + + q = expr(p, NULL, s_pc, s_pass == 0, &ecode, &ep); + if (q == NULL && ecode == EXPR_E_NO_EXPR) { + return p; + } else if (q == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } else { + return q; + } +} + +static const char *d_codes(const char *p) +{ + s_codes = 1; + return p; +} + +static const char *d_module(const char *p) +{ + p = sync(p); + while (*p != '\0' && *p != '\\') { + if (!isspace(*p)) { + wprint(_("invalid characters after directive\n")); + eprcol(s_pline, p); + return sync(p); + } else { + p++; + } + } + return p; +} + +static const char *d_nocodes(const char *p) +{ + s_codes = 0; + return p; +} + +static const char *d_list(const char *p) +{ + s_list_on = 1; + return p; +} + +static const char *d_nolist(const char *p) +{ + s_list_on = 0; + return p; +} + +static const char *d_eject(const char *p) +{ + list_eject(); + return p; +} + +static const char *d_echo(const char *p) +{ + int n; + int mode; + enum expr_ecode ecode; + const char *ep; + + mode = (s_pass == 0) ? STRMODE_NULL : STRMODE_ECHO; + if (*p == '\"') { + p = genstr(p, mode); + if (*p == '\"') { + p++; + } else if (s_pass == 0) { + wprint(_("no terminating quote\n")); + eprcol(s_pline, p); + } + } else if (*p != '\0') { + p = expr(p, &n, s_pc, s_pass == 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + if (mode == STRMODE_ECHO) { + fprintf(stderr, "%d", n); + } + } + return p; +} + +static const char *d_equ(const char *p) +{ + int n; + enum expr_ecode ecode; + const char *ep; + + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + if (s_lastsym == NULL) { + eprint(_(".EQU without label\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } else { + /* TODO: check label misalign? */ + s_lastsym->val = n; + s_lastsym->isequ = 1; + } + return p; +} + +static const char *d_set(const char *p) +{ + int n; + enum expr_ecode ecode; + const char *ep; + + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + if (s_lastsym == NULL) { + eprint(_(".EQU without label\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } else { + /* TODO: check label misalign? */ + s_lastsym->val = n; + s_lastsym->isequ = 1; + } + return p; +} + +static const char *d_export(const char *p) +{ + /* TODO */ + return NULL; +} + +static const char *d_fill(const char *p) +{ + int n, v, er; + const char *q; + enum expr_ecode ecode; + const char *ep, *eps; + + eps = p; + er = 0; + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + if (n < 0) { + eprint(_("number of positions to fill is negative (%d)\n"), n); + eprcol(s_pline, eps); + exit(EXIT_FAILURE); + } + + v = 255; + p = skipws(p); + if (*p == ',') { + p = skipws(p + 1); + q = expr(p, &v, s_pc, s_pass == 0, &ecode, &ep); + if (q == NULL) { + er = 1; + exprint(ecode, s_pline, ep); + newerr(); + } else { + p = q; + } + } + + while (n--) + genb(v, eps); + + if (er) + return NULL; + else + return p; +} + +static const char *d_ds(const char *p) +{ + int n, v, er; + const char *q; + enum expr_ecode ecode; + const char *ep, *eps; + + eps = p; + er = 0; + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + if (n < 0) { + eprint(_("number of positions to space over is negative (%d)\n"), n); + eprcol(s_pline, eps); + exit(EXIT_FAILURE); + } + + v = 255; + p = skipws(p); + if (*p == ',') { + p = skipws(p + 1); + q = expr(p, &v, s_pc, s_pass == 0, &ecode, &ep); + if (q == NULL) { + er = 1; + exprint(ecode, s_pline, ep); + newerr(); + } else { + p = q; + } + } + + s_pc += n; + + if (er) + return NULL; + else + return p; +} + +static const char *d_lsfirst(const char *p) +{ + s_msbword = 0; + return p; +} + +static const char *d_msfirst(const char *p) +{ + s_msbword = 1; + return p; +} + +static const char *d_org(const char *p) +{ + int n; + enum expr_ecode ecode; + const char *ep, *eps; + + eps = p; + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + if (n < 0 || n > 65536) { + eprint(_(".ORG address (%d) is not in range [0, 65536]\n"), n); + eprcol(s_pline, eps); + exit(EXIT_FAILURE); + } + + s_pc = n; + + /* Change the listing PC so in orgs we print the changed PC. */ + if (s_pass > 0) + list_setpc(s_pc); + + if (s_lastsym != NULL) { + /* TODO: check label misalign? */ + s_lastsym->val = s_pc; + s_lastsym->isequ = 1; + } + + return p; +} + +static const char *d_lst(const char *p, int w) +{ + enum strmode mode; + int n, linepc; + enum expr_ecode ecode; + const char *ep, *eps; + + if (w) + mode = STRMODE_WORD; + else + mode = STRMODE_BYTE; + + linepc = s_pc; +dnlst: + if (*p == '\"') { + p = genstr(p, mode); + if (*p == '\"') { + p++; + } else { + wprint(_("no terminating quote\n")); + eprcol(s_pline, p); + } + } else { + eps = p; + p = expr(p, &n, linepc, s_pass == 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + if (w) + genw(n, eps); + else + genb(n, eps); + } + p = skipws(p); + if (*p == ',') { + p++; + p = skipws(p); + goto dnlst; + } + return p; +} + +static const char *d_align(const char *p) +{ + int n, v, er; + const char *q; + enum expr_ecode ecode; + const char *ep, *eps; + + eps = p; + er = 0; + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + if (n < 0) { + eprint(_("align is negative (%d)\n"), n); + eprcol(s_pline, eps); + exit(EXIT_FAILURE); + } + + while (s_pc % n) { + genb(0, eps); + } + + if (er) + return NULL; + else + return p; +} + +static const char *d_byte(const char *p) +{ + return d_lst(p, 0); +} + +static const char *d_word(const char *p) +{ + return d_lst(p, 1); +} + +static const char *d_text(const char *p) +{ + if (*p == '\"') { + p = genstr(p, STRMODE_BYTE); + if (*p == '\"') { + p++; + } else { + wprint(_("no terminating quote\n")); + eprcol(s_pline, p); + } + return p; + } else { + eprint(_(".TEXT directive needs a quoted string argument\n")); + eprcol(s_pline, p); + newerr(); + return NULL; + } +} + +static const char *d_title(const char *p) +{ + return NULL; +} + +static const char *d_block(const char *p) +{ + int n; + enum expr_ecode ecode; + const char *ep, *eps; + + eps = p; + p = expr(p, &n, s_pc, 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + return NULL; + } + + s_pc += n; + if (s_pc < 0 || s_pc > 65536) { + eprint(_("address (%d) set by .BLOCK is not in range " + "[0, 65536]\n"), s_pc); + eprcol(s_pline, eps); + exit(EXIT_FAILURE); + } + + return p; +} + +/* a must be < b. */ +static int checksum(int a, int b) +{ + int n; + + assert(a < b); + + n = 0; + while (a < b) + n += s_mem[a++]; + + return n; +} + +static const char *d_chk(const char *p) +{ + int n; + enum expr_ecode ecode; + const char *ep, *eps; + + eps = p; + p = expr(p, &n, s_pc, s_pass == 0, &ecode, &ep); + if (p == NULL) { + exprint(ecode, s_pline, ep); + newerr(); + genb(0, eps); + return NULL; + } + + if (s_pass == 0) { + genb(0, s_pline_ep); + } else if (n < 0 || n >= s_pc) { + eprint(_(".CHK address (%d) is not in range [0, %d[\n"), n, + s_pc); + eprcol(s_pline, eps); + newerr(); + genb(0, eps); + } else { + genb(checksum(n, s_pc), eps); + } + + return p; +} + +/* Parses an internal directive (those that start with '.'). + * Returns NULL on error; + * If no error returns position past the parsed directive and arguments. */ +static const char *parse_direc(const char *cp) +{ + const char *cq, *p; + int a, b, m = 0; + + a = 0; + b = NELEMS(s_directab) - 1; + while (a <= b) { + m = (a + b) / 2; + cq = cp; + p = s_directab[m].name; + while (*p != '\0' && toupper(*cq) == *p) { + p++; + cq++; + } + if (*p == '\0' && (*cq == '\0' || isspace(*cq))) + break; + else if (toupper(*cq) < *p) + b = m - 1; + else + a = m + 1; + } + + if (a <= b) { + cq = skipws(cq); + return s_directab[m].fun(cq); + } else { + eprint(_("unrecognized directive\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + return NULL; + } +} + +static void parselin(const char *cp) +{ + int col0, alloweq; + const char *q; + + s_pline_ep = cp; +start: s_lastsym = NULL; + alloweq = 0; + col0 = 1; +loop: + if (*cp == '\0' || *cp == ';') { + return; + } else if (*cp == '\\') { + if (s_pass == 1) { + list_endln(); + list_startln(s_empty_line, curfile()->linenum, s_pc, + nfiles()); + } + cp++; + goto start; + } else if (*cp == '.') { + s_pline_ep = cp; + cp++; + q = parse_direc(cp); + if (q == NULL) { + cp = sync(cp); + } else { + cp = d_null(q); + } + } else if ((*cp == '$' || *cp == '*') && cp[1] == '=') { + /* Alternative form of .ORG: *= or $= */ + cp += 2; + q = d_org(cp); + if (q == NULL) { + cp = sync(cp); + } else { + cp = d_null(q); + } + } else if (*cp == '=' && alloweq) { + /* equ */ + s_pline_ep = cp; + cp++; + q = d_equ(cp); + if (q == NULL) { + cp = sync(cp); + } else { + cp = d_null(q); + } + } else if (isidc0(*cp)) { + if (col0 && *cp != '.') { + /* take label */ + s_pline_ep = cp; + q = cp; + col0 = 0; + while (isidc(*cp)) + cp++; + s_lastsym = lookup(q, cp, s_pass == 0, s_pc); + if (*cp == ':' || isspace(*cp)) { + alloweq = 1; + cp++; + } else if (*cp == '=') { + alloweq = 1; + } + if (s_pass == 1 && !s_lastsym->isequ && + s_lastsym->val != s_pc) + { + eprint(_("misaligned label %s\n"), + s_lastsym->name); + fprintf(stderr, _(" Previous value was %XH, " + "new value %XH."), s_lastsym->val, + s_pc); + eprcol(s_pline, s_pline_ep); + newerr(); + } + } else { + cp = skipws(cp); + s_pline_ep = cp; + q = match(cp); + if (q == NULL) { + eprint(_("syntax error\n")); + newerr(); + cp = sync(cp); + } else { + cp = d_null(q); + } + } + } else if (isspace(*cp)) { + col0 = 0; + while (isspace(*cp)) + cp++; + } else { + eprint(_("unexpected character (%c)\n"), *cp); + eprcol(s_pline, cp); + newerr(); + cp = sync(cp + 1); + } + goto loop; +} + +/* + * Gets a new line into 's_line from 'fin. + * Terminates the line with '\0'. + * Does not read more than LINESZ - 1 characters. + * Does not add a '\n' character, thus a line of length 0 it's possible. + * Always advances to the next line. + * Returns -1 for EOF or the line length. + */ +static int getlin(FILE *fin) +{ + int i, c; + + c = EOF; + i = 0; + while (i < LINESZ - 1) { + c = getc(fin); + if (c == EOF || c == '\n') + break; + s_line[i++] = (char) c; + } + if (c != EOF && c != '\n') { + wprint(_("line too long, truncated to %d characters\n"), + LINESZ); + } + while (c != EOF && c != '\n') + c = getc(fin); + if (i == 0 && c == EOF) + return -1; + s_line[i] = '\0'; + return i; +} + +/* Preinstall the macros defined in the command line. */ +static void install_predefs(void) +{ + struct predef *pdef; + + for (pdef = s_predefs; pdef != NULL; pdef = pdef->next) + pp_define(pdef->name); +} + +/* Do a pass through the source. */ +static void dopass(const char *fname) +{ + /* Fill memory with default value. */ + if ((s_pass == 0 && s_mem_fillval != 0) || s_pass > 0) { + memset(s_mem, s_mem_fillval, sizeof(s_mem)); + } + + if (s_pass > 0) { + pp_reset(); + list_open(s_lstfname); + s_codes = 1; + s_list_on = 1; + } + + + install_predefs(); + s_minpc = -1; + s_maxpc = -1; + s_pc = 0; + s_lastsym = NULL; + s_msbword = 0; + + pushfile(fname, fname + strlen(fname)); + while (nfiles() > 0) { + curfile()->linenum++; + if (getlin(curfile()->fin) >= 0) { + if (s_pass == 1) { + list_startln(s_line, curfile()->linenum, s_pc, + nfiles()); + } + pp_line(s_line); + if (s_pass == 1) + list_skip(s_skipon); + parselin(s_pline); + if (s_pass == 1) + list_endln(); + } else { + popfile(); + } + } + + if (s_pass > 0) { + list_close(); + } +} + +/* + * Write the object file in memory order + */ +static void output() +{ + int i; + + // fprintf(stderr, "output: min: %x max: %x\n", s_minpc, s_maxpc); + + if (s_minpc < 0) + s_minpc = 0; + if (s_maxpc < 0) + s_maxpc = 0; + + for (i = s_minpc; i < s_maxpc; i++) { + if (isset(i)) { + fwrite(&s_mem[i], 1, 1, fout); + } + } + if (ferror(fout)) { + eprint(_("cannot write to file %s\n"), s_objfname); + clearerr(fout); + } +} + +/* Start the assembly using the config in options.c. */ +void uz80as(void) +{ + s_target = find_target(s_target_id); + if (s_target == NULL) { + eprint(_("target '%s' not supported\n"), s_target_id); + exit(EXIT_FAILURE); + } + + for (s_pass = 0; s_nerrors == 0 && s_pass < 2; s_pass++) { + if ((s_pass > 0) && (s_nerrors == 0)) { + open_output(); + } + dopass(s_asmfname); + if (s_pass == 0 && !s_end_seen) { + wprint(_("no .END statement in the source\n")); + } + if (s_nerrors == 0) { + if (verbose) printf("Pass %d completed.\n", s_pass + 1); + } + } + + if (s_nerrors > 0) { + exit(EXIT_FAILURE); + } + + if (!b_flag) { + output(); + } + close_output(); +} diff --git a/2048/uz80as/uz80as.h b/2048/uz80as/uz80as.h new file mode 100644 index 0000000..511da08 --- /dev/null +++ b/2048/uz80as/uz80as.h @@ -0,0 +1,63 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Assembler. + * =========================================================================== + */ + +#ifndef UZ80AS_H +#define UZ80AS_H + +int verbose; + +/* matchtab.flags */ +enum { + MATCH_F_UNDOC = 1, + MATCH_F_EXTEN = 2, +}; + +/* pat: + * a: expr + * b - z: used by target + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f - z: used by target + * + * pr: + * 8: e8 + * f: e16 + * r: relative jump + */ + +struct matchtab { + const char *pat; + const char *gen; + unsigned char mask; + unsigned char undoc; + const char *pr; +}; + +struct target { + const char *id; + const char *descr; + const struct matchtab *matcht; + int (*matchf)(char c, const char *p, const char **q); + int (*genf)(int *eb, char p, const int *vs, int i, int savepc); + void (*pat_char_rewind)(int c); + const char * (*pat_next_str)(void); + unsigned char mask; +}; + +extern const char *s_pline_ep; + +void genb(int b, const char *ep); +int mreg(const char *p, const char *const list[], const char **r); + +void uz80as(void); + +#endif diff --git a/2048/uz80as/z80.c b/2048/uz80as/z80.c new file mode 100644 index 0000000..4e2db51 --- /dev/null +++ b/2048/uz80as/z80.c @@ -0,0 +1,362 @@ +/* =========================================================================== + * uz80as, an assembler for the Zilog Z80 and several other microprocessors. + * + * Zilog Z80 CPU. + * =========================================================================== + */ + +#include "pp.h" +#include "err.h" +#include "options.h" +#include "uz80as.h" +#include + +/* pat: + * a: expr + * b: B,C,D,E,H,L,A + * c: IX,IY (must be followed by + or -) + * d: BC,DE,HL,SP + * e: IX,IY + * f: BC,DE,HL,AF + * g: ADD,ADC,SUB,SBC,AND,XOR,OR,CP + * h: INC,DEC + * i: BC,DE,IX,SP + * j: BC,DE,IY,SP + * k: RLC,RRC,RL,RR,SLA,SRA,SRL + * l: BIT,RES,SET + * m: NZ,Z,NC,C,PO,PE,P,M + * n: NZ,Z,NC,C + * o: * + * p: B,C,D,E,IXH,IXL,A + * q: B,C,D,E,IYH,IYL,A + * + * gen: + * .: output lastbyte + * b: (op << 3) | lastbyte + * c: op | lastbyte + * d: lastbyte = op as 8 bit value + * e: output op as word (no '.' should follow) + * f: (op << 4) | lastbyte + * g: (op << 6) | lastbyte + * h: * + * i: relative jump to op + * j: possible value to RST + * k: possible value to IM + * m: check arithmetic used with A register + * n: check arithmetic used without A register + */ + +static const struct matchtab s_matchtab_z80[] = { + { "LD b,b", "40b0c1.", 3, 0 }, + { "LD p,p", "DD.40b0c1.", 1, 1 }, + { "LD q,q", "FD.40b0c1.", 1, 1 }, + { "LD b,(HL)", "46b0.", 3, 0 }, + { "LD b,(e)", "d1.46b0.00.", 3, 0, "ii" }, + { "LD b,(ca)", "d1.46b0.d2.", 3, 0, "ii" }, + { "LD A,I", "ED.57.", 3, 0 }, + { "LD A,R", "ED.5F.", 3, 0 }, + { "LD A,(BC)", "0A.", 3, 0 }, + { "LD A,(DE)", "1A.", 3, 0 }, + { "LD A,(a)", "3A.e0", 3, 0 }, + { "LD b,a", "06b0.d1.", 3, 0, "e8" }, + { "LD p,a", "DD.06b0.d1.", 1, 1, "e8" }, + { "LD q,a", "FD.06b0.d1.", 1, 1, "e8" }, + { "LD I,A", "ED.47.", 3, 0 }, + { "LD R,A", "ED.4F.", 3, 0 }, + { "LD SP,HL", "F9.", 3, 0 }, + { "LD SP,e", "d0.F9.", 3, 0 }, + { "LD HL,(a)", "2A.e0", 3, 0 }, + { "LD d,(a)", "ED.4Bf0.e1", 3, 0 }, + { "LD d,a", "01f0.e1", 3, 0 }, + { "LD e,(a)", "d0.2A.e1", 3, 0 }, + { "LD e,a", "d0.21.e1", 3, 0 }, + { "LD (HL),b", "70c0.", 3, 0 }, + { "LD (HL),a", "36.d0.", 3, 0, "e8" }, + { "LD (BC),A", "02.", 3, 0 }, + { "LD (DE),A", "12.", 3, 0 }, + { "LD (e),b", "d0.70c1.00.", 3, 0, "ii" }, + { "LD (ca),b", "d0.70c2.d1.", 3, 0, "ii" }, + { "LD (e),a", "d0.36.00.d1.", 3, 0, "iie8" }, + { "LD (ca),a", "d0.36.d1.d2.", 3, 0, "iie8" }, + { "LD (a),A", "32.e0", 3, 0 }, + { "LD (a),HL", "22.e0", 3, 0 }, + { "LD (a),d", "ED.43f1.e0", 3, 0 }, + { "LD (a),e", "d1.22.e0", 3, 0 }, + { "PUSH f", "C5f0.", 3, 0 }, + { "PUSH e", "d0.E5.", 3, 0 }, + { "POP f", "C1f0.", 3, 0 }, + { "POP e", "d0.E1.", 3, 0 }, + { "EX DE,HL", "EB.", 3, 0 }, + { "EX AF,AF'", "08.", 3, 0 }, + { "EX (SP),HL", "E3.", 3, 0 }, + { "EX (SP),e", "d0.E3.", 3, 0 }, + { "EXX", "D9.", 3, 0 }, + { "LDI", "ED.A0.", 3, 0 }, + { "LDIR", "ED.B0.", 3, 0 }, + { "LDD", "ED.A8.", 3, 0 }, + { "LDDR", "ED.B8.", 3, 0 }, + { "CPI", "ED.A1.", 3, 0 }, + { "CPIR", "ED.B1.", 3, 0 }, + { "CPD", "ED.A9.", 3, 0 }, + { "CPDR", "ED.B9.", 3, 0 }, + { "ADD HL,d", "09f0.", 3, 0 }, + { "ADD IX,i", "DD.09f0.", 3, 0 }, + { "ADD IY,j", "FD.09f0.", 3, 0 }, + { "ADC HL,d", "ED.4Af0.", 3, 0 }, + { "SBC HL,d", "ED.42f0.", 3, 0 }, + { "g A,b", "m080b0c1.", 3, 0 }, + { "g A,p", "DD.m080b0c1.", 1, 1 }, + { "g A,q", "FD.m080b0c1.", 1, 1 }, + { "g A,(HL)", "m086b0.", 3, 0 }, + { "g A,(ca)", "m0d1.86b0.d2.", 3, 0, "ii" }, + { "g A,a", "m0C6b0.d1.", 3, 0, "e8" }, + { "g b", "n080b0c1.", 3, 0 }, + { "g p", "DD.n080b0c1.", 1, 1 }, + { "g q", "FD.n080b0c1.", 1, 1 }, + { "g (HL)", "n086b0.", 3, 0 }, + { "g (ca)", "n0d1.86b0.d2.", 3, 0, "ii" }, + { "g a", "n0C6b0.d1.", 3, 0, "e8" }, + { "h b", "04b1c0.", 3, 0 }, + { "h p", "DD.04b1c0.", 1, 1 }, + { "h q", "FD.04b1c0.", 1, 1 }, + { "h (HL)", "34c0.", 3, 0 }, + { "h (ca)", "d1.34c0.d2.", 3, 0, "ii" }, + { "h (e)", "d1.34c0.00.", 3, 0, "ii" }, + { "INC d", "03f0.", 3, 0 }, + { "INC e", "d0.23.", 3, 0 }, + { "DEC d", "0Bf0.", 3, 0 }, + { "DEC e", "d0.2B.", 3, 0 }, + { "DAA", "27.", 3, 0 }, + { "CPL", "2F.", 3, 0 }, + { "NEG", "ED.44.", 3, 0 }, + { "CCF", "3F.", 3, 0 }, + { "SCF", "37.", 3, 0 }, + { "NOP", "00.", 3, 0 }, + { "HALT", "76.", 3, 0 }, + { "DI", "F3.", 3, 0 }, + { "EI", "FB.", 3, 0 }, + { "IM a", "ED.k0.", 3, 0, "tt" }, + { "RLCA", "07.", 3, 0 }, + { "RLA", "17.", 3, 0 }, + { "RRCA", "0F.", 3, 0 }, + { "RRA", "1F.", 3, 0 }, + { "SLL b", "CB.30c0.", 1, 1 }, + { "SLL (HL)", "CB.36.", 1, 1 }, + { "SLL (ca)", "d0.CB.d1.36.", 1, 1, "ii" }, + { "SLL (ca),b", "d0.CB.d1.30c2.", 1, 1, "ii" }, + { "k b", "CB.00b0c1.", 3, 0 }, + { "k (HL)", "CB.06b0.", 3, 0 }, + { "k (ca)", "d1.CB.d2.06b0.", 3, 0, "ii" }, + { "k (ca),b", "d1.CB.d2.00b0c3.", 1, 1, "ii" }, + { "RLD", "ED.6F.", 3, 0 }, + { "RRD", "ED.67.", 3, 0 }, + { "l a,b", "CB.00g0b1c2.", 3, 0, "b3" }, + { "l a,(HL)", "CB.06g0b1.", 3, 0, "b3" }, + { "l a,(ca)", "d2.CB.d3.06g0b1.", 3, 0, "b3ii" }, + { "RES a,(ca),b", "d1.CB.d2.80b0c3.", 1, 1, "b3ii" }, + { "SET a,(ca),b", "d1.CB.d2.C0b0c3.", 1, 1, "b3ii" }, + { "JP (HL)", "E9.", 3, 0 }, + { "JP (e)", "d0.E9.", 3, 0 }, + { "JP m,a", "C2b0.e1", 3, 0 }, + { "JP a", "C3.e0", 3, 0 }, + { "JR n,a", "20b0.i1.", 3, 0, "r8" }, + { "JR a", "18.i0.", 3, 0, "r8" }, + { "DJNZ a", "10.i0.", 3, 0, "r8" }, + { "CALL m,a", "C4b0.e1", 3, 0 }, + { "CALL a", "CD.e0", 3, 0 }, + { "RETI", "ED.4D.", 3, 0 }, + { "RETN", "ED.45.", 3, 0 }, + { "RET m", "C0b0.", 3, 0 }, + { "RET", "C9.", 3, 0 }, + { "RST a", "C7j0.", 3, 0, "ss" }, + { "IN b,(C)", "ED.40b0.", 3, 0 }, + { "IN A,(a)", "DB.d0.", 3, 0, "e8" }, + { "IN F,(a)", "ED.70.", 3, 0 }, + { "IN (C)", "ED.70.", 1, 1 }, + { "INI", "ED.A2.", 3, 0 }, + { "INIR", "ED.B2.", 3, 0 }, + { "IND", "ED.AA.", 3, 0 }, + { "INDR", "ED.BA.", 3, 0 }, + { "OUT (C),0", "ED.71.", 1, 1 }, + { "OUT (C),b", "ED.41b0.", 3, 0 }, + { "OUT (a),A", "D3.d0.", 3, 0, "e8" }, + { "OUTI", "ED.A3.", 3, 0 }, + { "OTIR", "ED.B3.", 3, 0 }, + { "OUTD", "ED.AB.", 3, 0 }, + { "OTDR", "ED.BB.", 3, 0 }, + /* hd64180 added instructions */ + { "IN0 b,(a)", "ED.00b0.d1.", 2, 0, "e8" }, + { "OUT0 (a),b", "ED.01b1.d0.", 2, 0, "e8" }, + { "OTDM", "ED.8B.", 2, 0 }, + { "OTDMR", "ED.9B.", 2, 0 }, + { "OTIM", "ED.83.", 2, 0 }, + { "OTIMR", "ED.93.", 2, 0 }, + { "MLT d", "ED.4Cf0.", 2, 0 }, + { "SLP", "ED.76.", 2, 0 }, + { "TST b", "ED.04b0.", 2, 0 }, + { "TST (HL)", "ED.34.", 2, 0 }, + { "TST a", "ED.64.d0.", 2, 0, "e8" }, + { "TSTIO a", "ED.74.d0.", 2, 0, "e8" }, + { NULL, NULL }, +}; + +static const char *const bval[] = { "B", "C", "D", "E", + "H", "L", "", "A", NULL }; +static const char *const cval[] = { "IX", "IY", NULL }; +static const char *const dval[] = { "BC", "DE", "HL", "SP", NULL }; +static const char *const fval[] = { "BC", "DE", "HL", "AF", NULL }; +static const char *const gval[] = { "ADD", "ADC", "SUB", "SBC", + "AND", "XOR", "OR", "CP", NULL }; +static const char *const hval[] = { "INC", "DEC", NULL }; +static const char *const ival[] = { "BC", "DE", "IX", "SP", NULL }; +static const char *const jval[] = { "BC", "DE", "IY", "SP", NULL }; +static const char *const kval[] = { "RLC", "RRC", "RL", "RR", + "SLA", "SRA", "", "SRL", NULL }; +static const char *const lval[] = { "", "BIT", "RES", "SET", NULL }; +static const char *const mval[] = { "NZ", "Z", "NC", "C", + "PO", "PE", "P", "M", NULL }; +static const char *const nval[] = { "NZ", "Z", "NC", "C", NULL }; +static const char *const pval[] = { "B", "C", "D", "E", + "IXH", "IXL", "", "A", NULL }; +static const char *const qval[] = { "B", "C", "D", "E", + "IYH", "IYL", "", "A", NULL }; +static const char *const nullv[] = { NULL }; + +static const char *const *const valtab[] = { + bval, cval, dval, dval, fval, + gval, hval, ival, jval, kval, + lval, mval, nval, nullv, pval, + qval +}; + +static int indval(const char *p, int disp, const char **q) +{ + int v; + const char *r; + + v = mreg(p, cval, &r); + if (v >= 0) { + v = (v == 0) ? 0xDD : 0xFD; + while (*r == ' ') r++; + if (!disp || *r == '+' || *r == '-') { + *q = r; + return v; + } + } + return -1; +} + +static int match_z80(char c, const char *p, const char **q) +{ + int v; + + if (c == 'c' || c == 'e') { + v = indval(p, c == 'c', q); + } else if (c <= 'q') { + v = mreg(p, valtab[(int) (c - 'b')], q); + } else { + v = -1; + } + + return v; +} + +static int gen_z80(int *eb, char p, const int *vs, int i, int savepc) +{ + int b; + + b = *eb; + switch (p) { + case 'f': b |= (vs[i] << 4); break; + case 'g': b |= (vs[i] << 6); break; + case 'i': b = (vs[i] - savepc - 2); break; + case 'j': if (s_pass > 0 && (vs[i] & ~56) != 0) { + eprint(_("invalid RST argument (%d)\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b |= vs[i]; + break; + case 'k': if (s_pass > 0 && (vs[i] < 0 || vs[i] > 2)) { + eprint(_("invalid IM argument (%d)\n"), + vs[i]); + eprcol(s_pline, s_pline_ep); + newerr(); + } + b = 0x46; + if (vs[i] == 1) + b = 0x56; + else if (vs[i] == 2) + b = 0x5E; + break; + case 'm': if (s_pass == 0 && !s_extended_op) { + if (vs[i] != 0 && vs[i] != 1 && vs[i] != 3) { + eprint(_("unofficial syntax\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } + } + break; + case 'n': if (s_pass == 0 && !s_extended_op) { + if (vs[i] == 0 || vs[i] == 1 || vs[i] == 3) { + eprint(_("unofficial syntax\n")); + eprcol(s_pline, s_pline_ep); + newerr(); + } + } + break; + default: + return -1; + } + + *eb = b; + return 0; +} + +static int s_pat_char = 'b'; +static int s_pat_index; + +static void pat_char_rewind_z80(int c) +{ + s_pat_char = c; + s_pat_index = 0; +}; + +static const char *pat_next_str_z80(void) +{ + const char *s; + + if (s_pat_char >= 'b' && s_pat_char <= 'q') { + s = valtab[(int) (s_pat_char - 'b')][s_pat_index]; + if (s != NULL) { + s_pat_index++; + } + } else { + s = NULL; + } + + return s; +}; + +const struct target s_target_z80 = { + .id = "z80", + .descr = "Zilog Z80", + .matcht = s_matchtab_z80, + .matchf = match_z80, + .genf = gen_z80, + .pat_char_rewind = pat_char_rewind_z80, + .pat_next_str = pat_next_str_z80, + .mask = 1 +}; + +const struct target s_target_hd64180 = { + .id = "hd64180", + .descr = "Hitachi HD64180", + .matcht = s_matchtab_z80, + .matchf = match_z80, + .genf = gen_z80, + .pat_char_rewind = pat_char_rewind_z80, + .pat_next_str = pat_next_str_z80, + .mask = 2 +};