as: Z80 assembler from Coherent
authorAlan Cox <alan@linux.intel.com>
Wed, 25 Oct 2017 19:02:51 +0000 (20:02 +0100)
committerAlan Cox <alan@linux.intel.com>
Wed, 25 Oct 2017 19:02:51 +0000 (20:02 +0100)
This one doesn't do relocations or external symbol resolution so will need a
bit of tweaking to generate object files not binaries. It is however very
tidy and extremely small.

12 files changed:
Applications/MWC/cmd/asz80/Makefile.68000 [new file with mode: 0644]
Applications/MWC/cmd/asz80/Makefile.6809 [new file with mode: 0644]
Applications/MWC/cmd/asz80/Makefile.z80 [new file with mode: 0644]
Applications/MWC/cmd/asz80/TODO [new file with mode: 0644]
Applications/MWC/cmd/asz80/as.h [new file with mode: 0644]
Applications/MWC/cmd/asz80/as0.c [new file with mode: 0644]
Applications/MWC/cmd/asz80/as1.c [new file with mode: 0644]
Applications/MWC/cmd/asz80/as2.c [new file with mode: 0644]
Applications/MWC/cmd/asz80/as3.c [new file with mode: 0644]
Applications/MWC/cmd/asz80/as4.c [new file with mode: 0644]
Applications/MWC/cmd/asz80/as5.c [new file with mode: 0644]
Applications/MWC/cmd/asz80/as6.c [new file with mode: 0644]

diff --git a/Applications/MWC/cmd/asz80/Makefile.68000 b/Applications/MWC/cmd/asz80/Makefile.68000
new file mode 100644 (file)
index 0000000..07b360b
--- /dev/null
@@ -0,0 +1,44 @@
+PLATFORM = 68000
+CC = m68k-uclinux-gcc
+ASM = m68k-uclinux-as
+AR = m68k-uclinux-ar
+LINKER = m68k-uclinux-ld
+CFLAGS = -Os -fno-strict-aliasing -fomit-frame-pointer -fno-builtin -Wall -m68000 -I../../../../Library/include -I../../../../Library/include/68000
+LINKER_OPT = -L../../../../Library/libs -lc68000
+LIBGCCDIR = $(dir $(shell $(CC) -print-libgcc-file-name))
+LINKER_OPT += --emit-relocs -L$(LIBGCCDIR) -lgcc -T ../../../../Library/elf2flt.ld
+CRT0 = ../../../../Library/libs/crt0_68000.o
+CRT0NS = ../../../../Library/libs/crt0nostdio_68000.o
+# For now while we get going. Really we want to use some kind of elf2zmagic
+# with relocs.
+ELF2FUZIX = elf2flt
+.SUFFIXES: .c .o
+
+SRCS  = as0.c as1.c as2.c as3.c as4.c as5.c as6.c
+
+INCS  = as.h
+
+OBJS = $(SRCS:.c=.o)
+
+all: as
+
+$(OBJS): $(INCS)
+
+OBJS = $(SRCS:.c=.o)
+
+$(OBJS): %.o : %.c
+       $(CC) -c $(CFLAGS) $(COPT) $<
+
+as: $(CRT0) $(OBJS)
+       $(LINKER) $^ -o $@.bin $(LINKER_OPT)
+       $(ELF2FUZIX) -o $@ $@.bin
+
+size.report: $(APPS)
+       ls -l $^ > $@
+
+clean:
+       rm -f $(OBJS) $(APPS) $(SRCS:.c=) core *~ *.asm *.lst *.sym *.map *.noi *.lk *.ihx *.tmp *.bin size.report *.o
+
+rmbak:
+       rm -f *~ core
+
diff --git a/Applications/MWC/cmd/asz80/Makefile.6809 b/Applications/MWC/cmd/asz80/Makefile.6809
new file mode 100644 (file)
index 0000000..2379f78
--- /dev/null
@@ -0,0 +1,40 @@
+PLATFORM = 6809
+CC = m6809-unknown-gcc
+ASM = m6809-unknown-as
+AR = m6809-unknown-ar
+LINKER = m6809-unknown-ld
+CFLAGS =  -I../../../../Library/include -I../../../../Library/include/6809 -Wall -pedantic -fno-strict-aliasing
+# Workaround for gcc6809 bug - register copy propagation issue
+CFLAGS += -fno-cprop-registers
+COPT = -Os
+LINKER_OPT = --oformat=raw -L../../../../Library/libs -lc6809
+LIBGCCDIR = $(dir $(shell $(CC) -print-libgcc-file-name))
+LINKER_OPT += -L$(LIBGCCDIR) -lgcc -Map=sh.map
+LINKER_OPT += --script=../../../util/$(TARGET).link
+ASM_OPT = -o
+CRT0 = ../../../../Library/libs/crt0_6809.o
+
+.SUFFIXES: .c .o
+
+
+SRCS  = as0.c as1.c as2.c as3.c as4.c as5.c as6.c
+
+INCS  = as.h
+
+OBJS = $(SRCS:.c=.o)
+
+all: as
+
+$(OBJS): $(INCS)
+
+$(OBJS): %.o : %.c
+       $(CC) -c $(CFLAGS) $(COPT) $<
+
+as: $(OBJS) $(CRT0)
+       $(LINKER) -o $@ $(LINKER_OPT) $^
+
+clean:
+       rm -f $(OBJS) as $(SRCS:.c=) core *~
+
+rmbak:
+       rm -f *~ core
diff --git a/Applications/MWC/cmd/asz80/Makefile.z80 b/Applications/MWC/cmd/asz80/Makefile.z80
new file mode 100644 (file)
index 0000000..d420648
--- /dev/null
@@ -0,0 +1,34 @@
+LINKER = sdcc
+FCC = ../../../../Library/tools/fcc -O2
+PLATFORM =
+#PLATFORM = -tzx128
+
+.SUFFIXES: .c .rel
+
+SRCS  = as0.c as1.c as2.c as3.c as4.c as5.c as6.c
+
+INCS  = as.h
+
+OBJS = $(SRCS:.c=.rel)
+
+LIBS = ../../../../Library/libs/syslib.lib
+
+all: as
+
+as: $(OBJS)
+       $(FCC) $(PLATFORM) $(OBJS) -o $@
+
+$(OBJS): $(INCS)
+
+.c.rel:
+       $(FCC) $(PLATFORM) -c $<
+
+%: %.rel
+       $(FCC) $(PLATFORM) $< -o $@
+
+clean:
+       rm -f $(OBJS) as $(SRCS:.c=) core *~ *.asm *.lst *.sym *.map *.noi *.lk *.ihx *.tmp *.bin
+
+rmbak:
+       rm -f *~ core
+
diff --git a/Applications/MWC/cmd/asz80/TODO b/Applications/MWC/cmd/asz80/TODO
new file mode 100644 (file)
index 0000000..644cbaf
--- /dev/null
@@ -0,0 +1,49 @@
+Add outraw outrab and use them so that we can generate relocations
+
+Change the output format to some kind of binary folow
+
+Header
+Code
+Data
+BSS
+Symbols
+DebugSymbols (optional copy of local only symbols)
+
+Code/Data is packed as follows (BSS is 0 anyway but you can have a sym in
+BSS)
+
+0      END
+1-223  copy this many bytes
+
+224-239 reserved
+241    relocate next byte low
+242    relocate next byte high
+243    relocate next word (native order)
+       followed by symnum.w
+       followed by data byte/word
+
+Special symnums for versus our own base (for simple relocs) or a different
+byte code (eg 240-247 / 248-255 where latter don't give sym ?)
+
+Future
+
+244    relocate ZP ref (6502)
+245    24bit
+246    32bit
+
+etc
+
+Symbol Table
+type.b seg.b {addr} sym\0
+
+type
+
+bits 0-2 -> size of addr       1-4
+bits 3-6 -> free
+bit 7
+       0x00    import
+       0x80    export
+
+
+Write a matching nlist(3) and nm to test the basics
+
diff --git a/Applications/MWC/cmd/asz80/as.h b/Applications/MWC/cmd/asz80/as.h
new file mode 100644 (file)
index 0000000..d69c3ef
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Z-80 assembler.
+ * Header file, used by all
+ * parts of the assembler.
+ */
+#include       <stdio.h>
+#include       <string.h>
+#include       <stdlib.h>
+#include       <ctype.h>
+#include       <setjmp.h>
+
+/*
+ * Table sizes, etc.
+ */
+#define        NCPS    8                       /* # of characters in symbol */
+#define        NHASH   64                      /* # of hash buckets */
+#define        HMASK   077                     /* Mask for above */
+#define        NFNAME  32                      /* # of characters in filename */
+#define        NERR    10                      /* Size of error buffer */
+#define        NCODE   128                     /* # of characters in code buffer */
+#define        NINPUT  128                     /* # of characters in input line */
+#define        NLPP    60                      /* # of lines on a page */
+#define        XXXX    0                       /* Unused value */
+
+/*
+ * Exit codes.
+ */
+#define        GOOD    0
+#define        BAD     1
+
+/*
+ * Listing modes.
+ */
+#define        NLIST   0                       /* No list */
+#define        ALIST   1                       /* Address only */
+#define        BLIST   2                       /* Byte format */
+#define        WLIST   3                       /* Word format */
+#define        SLIST   4                       /* Source text only */
+
+/*
+ * Types. These are used
+ * in both symbols and in address
+ * descriptions. Observe the way the
+ * symbol flags hide in the register
+ * field of the address.
+ */
+#define        TMREG   0x00FF                  /* Register code */
+#define        TMMDF   0x0001                  /* Multidef */
+#define        TMASG   0x0002                  /* Defined by "=" */
+#define        TMMODE  0xFF00                  /* Mode */
+#define        TMINDIR 0x8000                  /* Indirect flag in mode */
+
+#define        TNEW    0x0000                  /* Virgin */
+#define        TUSER   0x0100                  /* User name */
+#define        TBR     0x0200                  /* Byte register */
+#define        TWR     0x0300                  /* Word register */
+#define        TSR     0x0400                  /* Special register (I, R) */
+#define        TDEFB   0x0500                  /* defb */
+#define        TDEFW   0x0600                  /* defw */
+#define        TDEFS   0x0700                  /* defs */
+#define        TDEFM   0x0800                  /* defm */
+#define        TORG    0x0900                  /* org */
+#define        TEQU    0x0A00                  /* equ */
+#define        TCOND   0x0B00                  /* conditional */
+#define        TENDC   0x0C00                  /* end conditional */
+#define        TNOP    0x0F00                  /* nop */
+#define        TRST    0x1000                  /* restarts */
+#define        TREL    0x1100                  /* djnz, jr */
+#define        TRET    0x1200                  /* ret */
+#define        TJMP    0x1300                  /* call, jp */
+#define        TPUSH   0x1400                  /* push, pop */
+#define        TIM     0x1500                  /* im */
+#define        TIO     0x1600                  /* in, out */
+#define        TBIT    0x1700                  /* set, res, bit */
+#define        TSHR    0x1800                  /* sl, sr et al */
+#define        TINC    0x1900                  /* inc, dec */
+#define        TEX     0x1A00                  /* ex */
+#define        TADD    0x1B00                  /* add, adc, sbc */
+#define        TLD     0x1C00                  /* ld */
+#define        TCC     0x1D00                  /* condition code */
+#define        TSUB    0x1E00                  /* sub et al */
+
+/*
+ * Registers.
+ */
+#define        B       0
+#define        C       1
+#define        D       2
+#define        E       3
+#define        H       4
+#define        L       5
+#define        M       6
+#define        A       7
+#define        IX      8
+#define        IY      9
+
+#define        BC      0
+#define        DE      1
+#define        HL      2
+#define        SP      3
+#define        AF      4
+#define        AFPRIME 5
+
+#define        I       0
+#define        R       1
+
+/*
+ * Condition codes.
+ */
+#define        CNZ     0
+#define        CZ      1
+#define        CNC     2
+#define        CC      3
+#define        CPO     4
+#define        CPE     5
+#define        CP      6
+#define        CM      7
+
+typedef        unsigned int    VALUE;          /* For symbol values */
+
+/*
+ * Address description.
+ */
+typedef        struct  ADDR    {
+       int     a_type;                 /* Type */
+       VALUE   a_value;                /* Index offset, etc */
+}      ADDR;
+
+/*
+ * Symbol.
+ */
+typedef        struct  SYM     {
+       struct  SYM *s_fp;              /* Link in hash */
+       char    s_id[NCPS];             /* Name */
+       int     s_type;                 /* Type */
+       VALUE   s_value;                /* Value */
+}      SYM;
+
+/*
+ * External variables.
+ */
+extern char    *cp;
+extern char    *ep;
+extern char    *ip;
+extern char    cb[];
+extern char    eb[];
+extern char    ib[];
+extern FILE    *ifp;
+extern FILE    *ofp;
+extern FILE    *lfp;
+extern int     line;
+extern int     lmode;
+extern VALUE   laddr;
+extern SYM     sym[];
+extern int     pass;
+extern SYM     *phash[];
+extern SYM     *uhash[];
+extern int     lflag;
+extern jmp_buf env;
+extern VALUE   dot;
+
+extern void asmline(void);
+extern void asmld(void);
+extern ADDR *getldaddr(ADDR *, int *, int *, ADDR *);
+extern void outop(int, ADDR *);
+extern void comma(void);
+extern void istuser(ADDR *);
+extern int ccfetch(ADDR *);
+extern int symhash(char *);
+extern void err(char);
+extern void uerr(char *);
+extern void aerr(void);
+extern void qerr(void);
+extern void storerror(int);
+extern void getid(char *, int);
+extern SYM *lookup(char *, SYM *[], int);
+extern int symeq(char *, char *);
+extern void symcopy(char *, char *);
+extern int get(void);
+extern int getnb(void);
+extern void unget(int);
+extern void getaddr(ADDR *);
+extern void expr1(ADDR *, int, int);
+extern void expr2(ADDR *);
+extern void expr3(ADDR *, int);
+extern void isokaors(ADDR *, int);
+extern void outaw(int);
+extern void outab(int);
+extern void outeof(void);
+extern void outbyte(int);
+extern void outflush(void);
+extern void outhex(int);
+extern void list(void);
+extern void list1(char *, int, int);
+extern void syminit(void);
diff --git a/Applications/MWC/cmd/asz80/as0.c b/Applications/MWC/cmd/asz80/as0.c
new file mode 100644 (file)
index 0000000..4fe954f
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Z-80 assembler.
+ * Command line processing
+ * and main driver.
+ */
+#include       "as.h"
+
+FILE   *ifp;
+FILE   *ofp;
+FILE   *lfp;
+char   cb[NCODE];
+char   eb[NERR];
+char   ib[NINPUT];
+char   *cp;
+char   *ep;
+char   *ip;
+int    lflag;
+VALUE  laddr;
+int    lmode;
+VALUE  dot;
+SYM    *phash[NHASH];
+SYM    *uhash[NHASH];
+int    pass;
+int    line;
+jmp_buf        env;
+
+
+/*
+ * Make up a file name.
+ * The "sfn" is the source file
+ * name and "dft" is the desired file
+ * type. The finished name is copied
+ * into the "dfn" buffer.
+ */
+void mkname(char *dfn, char *sfn, char *dft)
+{
+       char *p1;
+       char *p2;
+       int c;
+
+       p1 = sfn;
+       while (*p1 != 0)
+               ++p1;
+       while (p1!=sfn && p1[-1]!='/')
+               --p1;
+       p2 = dfn;
+       while ((c = *p1++)!=0 && c!='.')
+               *p2++ = c;
+       *p2++ = '.';
+       p1 = dft;
+       while ((*p2++ = *p1++) != 0);
+}
+
+int main(int argc, char *argv[])
+{
+       char *ifn;
+       char *p;
+       int i;
+       int c;
+       char fn[NFNAME];
+
+       ifn = NULL;
+       for (i=1; i<argc; ++i) {
+               p = argv[i];
+               if (*p == '-') {
+                       while ((c = *++p) != 0) {
+                               switch (c) {
+                               case 'l':
+                                       ++lflag;
+                                       break;
+
+                               default:
+                                       fprintf(stderr, "Bad option %c\n", c);
+                                       exit(BAD);
+                               }
+                       }
+               } else if (ifn == NULL)
+                       ifn = p;
+               else {
+                       fprintf(stderr, "Too many source files\n");
+                       exit(BAD);
+               }
+       }
+       if (ifn == NULL) {
+               fprintf(stderr, "No source file\n");
+               exit(BAD);
+       }
+       if ((ifp=fopen(ifn, "r")) == NULL) {
+               fprintf(stderr, "%s: cannot open\n", ifn);
+               exit(BAD);
+       }
+       mkname(fn, ifn, "hex");
+       if ((ofp=fopen(fn, "w")) == NULL) {
+               fprintf(stderr, "%s: cannot create\n", fn);
+               exit(BAD);
+       }
+       if (lflag != 0) {
+               mkname(fn, ifn, "lis");
+               if ((lfp=fopen(fn, "w")) == NULL) {
+                       fprintf(stderr, "%s: cannot create\n", fn);
+                       exit(BAD);
+               }
+       }
+       syminit();
+       for (pass=0; pass<2; ++pass) {
+               line = 0;
+               dot  = 0;
+               fseek(ifp, 0L, 0);
+               while (fgets(ib, NINPUT, ifp) != NULL) {
+                       ++line;
+                       cp = &cb[0];
+                       ep = &eb[0];
+                       ip = &ib[0];
+                       if (setjmp(env) == 0)
+                               asmline();
+                       if (pass != 0)
+                               list();
+               }
+       }
+       outeof();
+       exit(GOOD);
+}
+
diff --git a/Applications/MWC/cmd/asz80/as1.c b/Applications/MWC/cmd/asz80/as1.c
new file mode 100644 (file)
index 0000000..336028f
--- /dev/null
@@ -0,0 +1,676 @@
+/*
+ * Z-80 assembler.
+ * Assemble one line of input.
+ * Knows all the dirt.
+ */
+#include       "as.h"
+
+#define        OPDJNZ  0x10                    /* Opcode: djnz */
+#define        OPADD   0x80                    /* Opcode: add */
+#define        OPDAD   0x09                    /* Opcode: dad */
+#define        OPADC   0x88                    /* Opcode: adc */
+#define        OPADCW  0x4A                    /* Opcode: adc hl */
+#define        OPSBCW  0x42                    /* Opcode: sbc hl */
+#define        OPSUBI  0xC6                    /* Opcode: make immediate */
+#define        OPXCHG  0xEB                    /* Opcode: xchg */
+#define        OPXTHL  0xE3                    /* Opcode: xthl */
+#define        OPEXAF  0x08                    /* Opcode: ex af,af' */
+#define        OPRST   0xC7                    /* Opcode: rst 0 */
+#define        OPINCRP 0x03                    /* Opcode: inc rp */
+#define        OPDECRP 0x0B                    /* Opcode: dec rp */
+#define        OPINC   0x04                    /* Opcode: inc */
+#define        OPIIN   0x40                    /* Opcode: indirect in */
+#define        OPIOUT  0x41                    /* Opcode: indirect out */
+#define        OPIN    0xDB                    /* Opcode: in */
+#define        OPIM    0x46                    /* Opcode: im */
+#define        OPPCHL  0xE9                    /* Opcode: jp (hl) */
+#define        OPJP    0xC3                    /* Opcode: jp cc base */
+#define        OPJR    0x20                    /* Opcode: jr cc base */
+#define        OPRET   0xC0                    /* Opcode: ret cc base */
+
+/*
+ * Assemble one line.
+ * The line in in "ib", the "ip"
+ * scans along it. The code is written
+ * right out, and also stashed in the
+ * "cb" for the listing.
+ */
+void asmline(void)
+{
+       SYM *sp;
+       int c;
+       int opcode;
+       int disp;
+       int reg;
+       int srcreg;
+       int cc;
+       VALUE value;
+       int delim;
+       SYM *sp1;
+       char id[NCPS];
+       char id1[NCPS];
+       ADDR a1;
+       ADDR a2;
+
+       laddr = dot;
+       lmode = SLIST;
+loop:
+       if ((c=getnb())=='\n' || c==';')
+               return;
+       if (isalpha(c) == 0)
+               qerr();
+       getid(id, c);
+       if ((c=getnb()) == ':') {
+               sp = lookup(id, uhash, 1);
+               if (pass == 0) {
+                       if ((sp->s_type&TMMODE) != TNEW
+                       &&  (sp->s_type&TMASG) == 0)
+                               sp->s_type |= TMMDF;
+                       sp->s_type &= ~TMMODE;
+                       sp->s_type |= TUSER;
+                       sp->s_value = dot;
+               } else {
+                       if ((sp->s_type&TMMDF) != 0)
+                               err('m');
+                       if (sp->s_value != dot)
+                               err('p');
+               }
+               lmode = ALIST;
+               goto loop;
+       }
+       /*
+        * If the first token is an
+        * id and not an operation code,
+        * assume that it is the name in front
+        * of an "equ" assembler directive.
+        */
+       if ((sp=lookup(id, phash, 0)) == NULL) {
+               getid(id1, c);
+               if ((sp1=lookup(id1, phash, 0)) == NULL
+               ||  (sp1->s_type&TMMODE) != TEQU) {
+                       err('o');
+                       return;
+               }
+               getaddr(&a1);
+               istuser(&a1);
+               sp = lookup(id, uhash, 1);
+               if ((sp->s_type&TMMODE) != TNEW
+               &&  (sp->s_type&TMASG) == 0)
+                       err('m');
+               sp->s_type &= ~TMMODE;
+               sp->s_type |= TUSER|TMASG;
+               sp->s_value = a1.a_value;
+               laddr = a1.a_value;
+               lmode = ALIST;
+               goto loop;
+       }
+       unget(c);
+       lmode = BLIST;
+       opcode = sp->s_value;
+       switch (sp->s_type&TMMODE) {
+       case TORG:
+               getaddr(&a1);
+               istuser(&a1);
+               lmode = ALIST;
+               laddr = dot = a1.a_value;
+               break;
+
+       case TDEFB:
+               do {
+                       getaddr(&a1);
+                       istuser(&a1);
+                       outab(a1.a_value);
+               } while ((c=getnb()) == ',');
+               unget(c);
+               break;
+
+       case TDEFW:
+               lmode = WLIST;
+               do {
+                       getaddr(&a1);
+                       istuser(&a1);
+                       outaw(a1.a_value);
+               } while ((c=getnb()) == ',');
+               unget(c);
+               break;
+
+       case TDEFM:
+               if ((delim=getnb()) == '\n')
+                       qerr();
+               while ((c=get()) != delim) {
+                       if (c == '\n')
+                               qerr();
+                       outab(c);
+               }
+               break;
+
+       case TDEFS:
+               laddr = dot;
+               lmode = ALIST;
+               getaddr(&a1);
+               istuser(&a1);
+               dot += a1.a_value;
+               break;
+
+       case TNOP:
+               if ((opcode&0xFF00) != 0)
+                       outab(opcode >> 8);
+               outab(opcode);
+               break;
+
+       case TRST:
+               getaddr(&a1);
+               istuser(&a1);
+               if (a1.a_value < 8) {
+                       outab(OPRST|(a1.a_value<<3));
+                       break;  
+               }
+               aerr();
+               break;
+
+       case TREL:
+               getaddr(&a1);
+               if ((cc=ccfetch(&a1)) >= 0) {
+                       if (opcode==OPDJNZ || cc>=CPO)
+                               aerr();
+                       opcode = OPJR | (cc<<3);
+                       comma();
+                       getaddr(&a1);
+               }
+               istuser(&a1);
+               disp = a1.a_value-dot-2;
+               if (disp<-128 || disp>127)
+                       aerr();
+               outab(opcode);
+               outab(disp);
+               break;
+
+       case TRET:
+               unget(c = getnb());
+               if (c!='\n' && c!=';') {
+                       getaddr(&a1);
+                       if ((cc=ccfetch(&a1)) < 0)
+                               aerr();
+                       opcode = OPRET | (cc<<3);
+               }
+               outab(opcode);
+               break;
+
+       case TJMP:
+               getaddr(&a1);
+               if ((cc=ccfetch(&a1)) >= 0) {
+                       opcode = (opcode&0x00C6) | (cc<<3);
+                       comma();
+                       getaddr(&a1);
+               }
+               if ((a1.a_type&TMMODE) == TBR) {
+                       reg = a1.a_type&TMREG;
+                       if (reg==M || reg==IX || reg==IY) {
+                               if (opcode != OPJP)
+                                       aerr();
+                               outop(OPPCHL, &a1);
+                               break;
+                       }
+               }
+               istuser(&a1);
+               outab(opcode);
+               outaw(a1.a_value);
+               break;
+
+       case TPUSH:
+               getaddr(&a1);
+               if ((a1.a_type&TMMODE) == TWR) {
+                       reg = a1.a_type&TMREG;
+                       switch (reg) {
+                       case IX:
+                               outab(0xDD);
+                               reg = HL;
+                               break;
+                       case IY:
+                               outab(0xFD);
+                               reg = HL;
+                               break;
+                       case AF:
+                               reg = SP;
+                               break;
+                       case SP:
+                       case AFPRIME:
+                               aerr();
+                       }
+                       outab(opcode|(reg<<4));
+                       break;
+               }
+               aerr();
+               break;
+
+       case TIM:
+               getaddr(&a1);
+               istuser(&a1);
+               if ((value=a1.a_value) > 2)
+                       aerr();
+               else if (value != 0)
+                       ++value;
+               outab(0xED);
+               outab(OPIM|(value<<3));
+               break;
+
+       case TIO:
+               getaddr(opcode==OPIN ? &a1 : &a2);
+               comma();
+               getaddr(opcode==OPIN ? &a2 : &a1);
+               if (a1.a_type==(TBR|A) && a2.a_type==(TUSER|TMINDIR)) {
+                       outab(opcode);
+                       outab(a2.a_value);
+                       break;
+               }
+               if ((a1.a_type&TMMODE)==TBR && a2.a_type==(TBR|TMINDIR|C)) {
+                       reg = a1.a_type&TMREG;
+                       if (reg==M || reg==IX || reg==IY)
+                               aerr();
+                       outab(0xED);
+                       if (opcode == OPIN)
+                               opcode = OPIIN; else
+                               opcode = OPIOUT;
+                       outab(opcode|(reg<<3));
+                       break;
+               }
+               aerr();
+               break;
+
+       case TBIT:
+               getaddr(&a1);
+               comma();
+               getaddr(&a2);
+               if ((a1.a_type&TMMODE) == TUSER
+               &&   a1.a_value < 8
+               &&  (a2.a_type&TMMODE) == TBR) {
+                       if ((reg=a2.a_type&TMREG)==IX || reg==IY)
+                               reg = M;
+                       outop(opcode|(a1.a_value<<3)|reg, &a2);
+                       break;
+               }
+               aerr();
+               break;
+
+       case TSHR:
+               getaddr(&a1);
+               if ((a1.a_type&TMMODE) == TBR) {
+                       if ((reg=a1.a_type&TMREG)==IX || reg==IY)
+                               reg = M;
+                       outop(opcode|reg, &a1);
+                       break;
+               }
+               aerr();
+
+       case TINC:
+               getaddr(&a1);
+               if ((a1.a_type&TMMODE) == TWR) {
+                       reg = a1.a_type&TMREG;
+                       switch (reg) {
+                       case IX:
+                               outab(0xDD);
+                               reg = HL;
+                               break;
+                       case IY:
+                               outab(0xFD);
+                               reg = HL;
+                               break;
+                       case AF:
+                       case AFPRIME:
+                               aerr();
+                       }
+                       if (opcode == OPINC)
+                               opcode = OPINCRP; else
+                               opcode = OPDECRP;
+                       outab(opcode|(reg<<4));
+                       break;
+               }
+               if ((a1.a_type&TMMODE) == TBR) {
+                       if ((reg=a1.a_type&TMREG)==IX || reg==IY)
+                               reg = M;
+                       outop(opcode|(reg<<3), &a1);
+                       break;
+               }
+               aerr();
+               break;
+
+       case TEX:
+               getaddr(&a1);
+               comma();
+               getaddr(&a2);
+               if (a1.a_type==(TWR|AF) && a2.a_type==(TWR|AFPRIME)) {
+                       outab(OPEXAF);
+                       break;
+               }
+               if (a1.a_type == (TWR|DE))
+                       opcode = OPXCHG;
+               else if (a1.a_type == (TWR|TMINDIR|SP))
+                       opcode = OPXTHL;
+               else
+                       aerr();
+               if (a2.a_type == (TWR|HL))
+                       outab(opcode);
+               else if (a2.a_type == (TWR|IX)) {
+                       outab(0xDD);
+                       outab(opcode);
+               } else if (a2.a_type == (TWR|IY)) {
+                       outab(0xFD);
+                       outab(opcode);
+               } else
+                       aerr();
+               break;
+
+       case TSUB:
+               getaddr(&a1);
+               if (a1.a_type == TUSER) {
+                       outab(opcode | OPSUBI);
+                       outab(a1.a_value);
+                       break;
+               }
+               if ((a1.a_type&TMMODE) == TBR) {
+                       if ((reg=a1.a_type&TMREG)==IX || reg==IY)
+                               reg = M;
+                       outop(opcode|reg, &a1);
+                       break;
+               }
+               aerr();
+               break;
+
+       case TADD:
+               getaddr(&a1);
+               comma();
+               getaddr(&a2);
+               if (a1.a_type == (TBR|A)) {
+                       if (a2.a_type == TUSER) {
+                               outab(opcode | OPSUBI);
+                               outab(a2.a_value);
+                               break;
+                       }
+                       if ((a2.a_type&TMMODE) == TBR) {
+                               if ((reg=a2.a_type&TMREG)==IX || reg==IY)
+                                       reg = M;
+                               outop(opcode|reg, &a1);
+                               break;
+                       }
+               }
+               if ((a1.a_type&TMMODE) == TWR) {
+                       switch(reg = a1.a_type&TMREG) {
+                       case IX:
+                               if (opcode != OPADD)
+                                       aerr();
+                               outab(0xDD);
+                               opcode = OPDAD;
+                               srcreg = IX;
+                               break;
+                       case IY:
+                               if (opcode != OPADD)
+                                       aerr();
+                               outab(0xFD);
+                               opcode = OPDAD;
+                               srcreg = IY;
+                               break;
+                       case HL:
+                               if (opcode == OPADD)
+                                       opcode = OPDAD;
+                               else {
+                                       outab(0xED);
+                                       if (opcode == OPADC)
+                                               opcode = OPADCW;
+                                       else
+                                               opcode = OPSBCW;
+                               }
+                               srcreg = HL;
+                               break;
+                       default:
+                               aerr();
+                       }
+                       if ((a2.a_type&TMMODE) == TWR) {
+                               reg = a2.a_type&TMREG;
+                               if (reg==BC || reg==DE || reg==SP) {
+                                       outab(opcode|(reg<<4));
+                                       break;
+                               }
+                               if (reg == srcreg) {
+                                       outab(opcode|(HL<<4));
+                                       break;
+                               }
+                       }
+               }
+               aerr();
+               break;
+
+       case TLD:
+               asmld();
+               break;
+
+       default:
+               err('o');
+       }
+       goto loop;
+}
+
+/*
+ * Handle the dreaded "ld" instruction,
+ * with its dozens of forms, formats and special
+ * encodings. The "getldaddr" routine performs most
+ * of the special stuff for index registers and for
+ * indexing. This layer just screens out the many
+ * cases, and emits the correct bytes.
+ */
+void asmld(void)
+{
+       int mdst;
+       int rdst;
+       int msrc;
+       int rsrc;
+       ADDR *indexap;
+       ADDR dst;
+       ADDR src;
+
+       indexap = NULL;
+       indexap = getldaddr(&dst, &mdst, &rdst, indexap);
+       comma();
+       indexap = getldaddr(&src, &msrc, &rsrc, indexap);
+       if (dst.a_type == (TBR|A)) {
+               if (msrc == TSR) {
+                       if (rsrc == I)
+                               outaw(0x57ED);          /* ld a,i */
+                       else
+                               outaw(0x5FED);          /* ld a,r */
+                       return;
+               }
+               if (msrc == (TMINDIR|TUSER)) {
+                       outab(0x3A);                    /* lda */
+                       outaw(src.a_value);
+                       return;
+               }
+               if (msrc == (TMINDIR|TWR)) {
+                       if (rsrc==BC || rsrc==DE) {
+                               outab(0x0A|(rsrc<<4));  /* ldax */
+                               return;
+                       }
+               }
+       }
+       if (src.a_type == (TBR|A)) {
+               if (mdst == TSR) {
+                       if (rdst == I)
+                               outaw(0x47ED);          /* ld i,a */
+                       else
+                               outaw(0x4FED);          /* ld r,a */
+                       return;
+               }
+               if (mdst == (TMINDIR|TUSER)) {
+                       outab(0x32);                    /* sta */
+                       outaw(dst.a_value);
+                       return;
+               }
+               if (mdst == (TMINDIR|TWR)) {
+                       if (rdst==BC || rdst==DE) {
+                               outab(0x02|(rdst<<4));  /* stax */
+                               return;
+                       }
+               }
+       }
+       if (dst.a_type==(TWR|SP) && msrc==TWR) {
+               if (rsrc == HL) {
+                       outab(0xF9);                    /* sphl */
+                       return;
+               }
+       }
+       if (msrc == TUSER) {
+               if (mdst == TBR) {
+                       outab(0x06|(rdst<<3));          /* mvi */
+                       if (indexap != NULL)
+                               outab(indexap->a_value);
+                       outab(src.a_value);
+                       return;
+               }
+               if (mdst == TWR) {
+                       outab(0x01|(rdst<<4));          /* lxi */
+                       outaw(src.a_value);
+                       return;
+               }
+       }
+       if (mdst==TWR && msrc==(TMINDIR|TUSER)) {
+               if (rdst == HL)
+                       outab(0x2A);                    /* lhld */
+               else
+                       outaw(0x4BED|(rdst<<12));       /* ld rp,(ppqq) */
+               outaw(src.a_value);
+               return;
+       }
+       if (mdst==(TMINDIR|TUSER) && msrc==TWR) {
+               if (rsrc == HL)
+                       outab(0x22);                    /* shld */
+               else
+                       outaw(0x43ED|(rsrc<<12));       /* ld (ppqq),rp */
+               outaw(dst.a_value);
+               return;
+       }
+       if (mdst==TBR && msrc==TBR && (rdst!=M || rsrc!=M)) {
+               outab(0x40|(rdst<<3)|rsrc);
+               if (indexap != NULL)
+                       outab(indexap->a_value);
+               return;
+       }
+       aerr();
+}
+
+/*
+ * Read in addresses for "ld"
+ * instructions. Split off the mode
+ * and the register name. Adjust the register
+ * name to correctly deal with the index registers
+ * and for indexed addressing modes. Return the address
+ * pointer "ap" if indexing is required, otherwise just
+ * pass the "iap" through.
+ */
+ADDR   *getldaddr(ADDR *ap, int *modep, int *regp, ADDR *iap)
+{
+       int mode;
+       int reg;
+
+       getaddr(ap);
+       mode = ap->a_type&TMMODE;
+       reg  = ap->a_type&TMREG;
+       switch (ap->a_type) {
+       case TBR|IX:
+               outab(0xDD);
+               reg = M;
+               iap = ap;
+               break;
+
+       case TBR|IY:
+               outab(0xFD);
+               reg = M;
+               iap = ap;
+               break;
+
+       case TWR|IX:
+               outab(0xDD);
+               reg = HL;
+               break;
+
+       case TWR|IY:
+               outab(0xFD);
+               reg = HL;
+               break;
+
+       case TWR|AF:
+       case TWR|AFPRIME:
+               aerr();
+               reg = HL;
+       }
+       *modep = mode;
+       *regp  = reg;
+       return (iap);
+}
+
+/*
+ * Output an opcode, surrounded
+ * by the index prefix bytes and the
+ * index displacement byte. Look at
+ * the address mode to see if the bytes
+ * are needed.
+ */
+void outop(int op, ADDR *ap)
+{
+       int needisp;
+
+       needisp = 0;
+       if (ap->a_type == (TBR|IX)) {
+               outab(0xDD);
+               needisp = 1;
+       } else if (ap->a_type == (TBR|IY)) {
+               outab(0xFD);
+               needisp = 1;
+       }
+       if ((op&0xFF00) != 0) {
+               outab(op>>8);
+               if (needisp != 0) {
+                       outab(ap->a_value);
+                       needisp = 0;
+               }
+       }
+       outab(op);
+       if (needisp != 0)
+               outab(ap->a_value);
+}
+
+/*
+ * The next character
+ * in the input must be a comma
+ * or it is a fatal error.
+ */
+void comma(void)
+{
+       if (getnb() != ',')
+               qerr();
+}
+
+/*
+ * Check if the mode of
+ * an ADDR is TUSER. If not, give
+ * an error.
+ */
+void istuser(ADDR *ap)
+{
+       if ((ap->a_type&TMMODE) != TUSER)
+               aerr();
+}
+
+/*
+ * Try to interpret an "ADDR"
+ * as a condition code name. Return
+ * the condition, or "-1" if it cannot
+ * be interpreted as a condition. The
+ * "c" condition is a pain.
+ */
+int ccfetch(ADDR *ap)
+{
+       if (ap->a_type == (TBR|C))
+               return (CC);
+       if ((ap->a_type&TMMODE) == TCC)
+               return (ap->a_type&TMREG);
+       return (-1);
+}
diff --git a/Applications/MWC/cmd/asz80/as2.c b/Applications/MWC/cmd/asz80/as2.c
new file mode 100644 (file)
index 0000000..a7272a1
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Z-80 assembler.
+ * Symbol table routines
+ * and error handling.
+ */
+#include       "as.h"
+
+/*
+ * Given a pointer to a
+ * sumbol, compute the hash bucket
+ * number. The "add all the characters
+ * and take the result modulo the table
+ * size" algorithm is used.
+ */
+int symhash(char *id)
+{
+       int hash;
+       int n;
+
+       hash = 0;
+       n = NCPS;
+       do {
+               hash += *id++;
+       } while (--n);
+       return (hash&HMASK);
+}
+
+/*
+ * Handle an error.
+ * If no listing file, write out
+ * the error directly. Otherwise save
+ * the error in the error buffer.
+ */
+void err(char c)
+{
+       if (pass != 0) {
+               if (lflag != 0)
+                       storerror(c);
+               else
+                       printf("%04d %c\n", line, c);
+       }
+       if (c == 'q')
+               longjmp(env, 1);
+}
+
+/*
+ * This routine is like
+ * "err", but it has the "u"
+ * code screwed into it, and it
+ * prints the name of the identifier
+ * "id" in the message.
+ */
+void uerr(char *id)
+{
+       if (pass != 0) {
+               if (lflag != 0)
+                       storerror('u');
+               else
+                       printf("%04d u %.*s\n", line, NCPS, id);
+       }
+}
+
+/*
+ * The "a" error is common.
+ */
+void aerr(void)
+{
+       err('a');
+}
+
+/*
+ * Ditto the "q" error.
+ */
+void qerr(void)
+{
+       err('q');
+}
+
+/*
+ * Put the error code
+ * "c" into the error buffer.
+ * Check that it is not already
+ * there.
+ */
+
+void storerror(int c)
+{
+       char *p;
+
+       p = &eb[0];
+       while (p < ep)
+               if (*p++ == c)
+                       return;
+       if (p < &eb[NERR]) {
+               *p++ = c;
+               ep = p;
+       }
+}
+
+/*
+ * Read identifier.
+ * The character "c" is the first
+ * character of the name.
+ */
+void getid(char *id, int c)
+{
+       char *p;
+
+       if (c < 0) {
+               c = getnb();
+               if (isalpha(c) == 0)
+                       qerr();
+       }
+       p = &id[0];
+       do {
+               if (p < &id[NCPS]) {
+                       if (isupper(c))
+                               c = tolower(c);
+                       *p++ = c;
+               }
+               if ((c = *ip) != '\n')
+                       ++ip;
+       } while (c=='\'' || isalnum(c)!=0);
+       if (c != '\n')
+               --ip;
+       while (p < &id[NCPS])
+               *p++ = 0;
+}
+
+/*
+ * Lookup symbol in
+ * hash table "htable".
+ * If not there, and "cf" is
+ * true, create it.
+ */
+SYM    *lookup(char *id, SYM *htable[], int cf)
+{
+       SYM *sp;
+       int hash;
+
+       hash = symhash(id);
+       sp  = htable[hash];
+       while (sp != NULL) {
+               if (symeq(id, sp->s_id))
+                       return (sp);
+               sp = sp->s_fp;
+       }
+       if (cf != 0) {
+               if ((sp=(SYM *)malloc(sizeof(SYM))) == NULL) {
+                       fprintf(stderr, "No memory\n");
+                       exit(BAD);
+               }
+               sp->s_fp = htable[hash];
+               htable[hash] = sp;
+               sp->s_type = TNEW;
+               sp->s_value = 0;
+               symcopy(sp->s_id, id);
+       }
+       return (sp);
+}
+
+/*
+ * Compare two names.
+ * Each are blocks of "NCPS"
+ * bytes. True return if the names
+ * are exactly equal.
+ */
+int symeq(char *p1, char *p2)
+{
+       int n;
+
+       n = NCPS;
+       do {
+               if (*p1++ != *p2++)
+                       return (0);
+       } while (--n);
+       return (1);
+}
+
+/*
+ * Copy the characters
+ * that make up the name of a
+ * symbol.
+ */
+void symcopy(char *p1, char *p2)
+{
+       int n;
+
+       n = NCPS;
+       do {
+               *p1++ = *p2++;
+       } while (--n);
+}
+
+/*
+ * Get the next character
+ * from the input buffer. Do not
+ * step past the newline.
+ */
+int get(void)
+{
+       int c;
+
+       if ((c = *ip) != '\n')
+               ++ip;
+       return (c);
+}
+
+/*
+ * Get the next non
+ * whitespace character from 
+ * the input buffer. Do not step
+ * past the newline.
+ */
+int getnb(void)
+{
+       int c;
+
+       while ((c = *ip)==' ' || c=='\t')
+               ++ip;
+       if (c != '\n')
+               ++ip;
+       return (c);
+}
+
+/*
+ * Put a character back.
+ */
+void unget(int c)
+{
+       if (c != '\n')
+               --ip;
+}
diff --git a/Applications/MWC/cmd/asz80/as3.c b/Applications/MWC/cmd/asz80/as3.c
new file mode 100644 (file)
index 0000000..bacbc44
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Z-80 assembler.
+ * Read in expressions in
+ * address fields.
+ */
+#include       "as.h"
+
+#define        LOPRI   0
+#define        ADDPRI  1
+#define        MULPRI  2
+#define        HIPRI   3
+
+/*
+ * Read in an address
+ * descriptor, and fill in
+ * the supplied "ADDR" structure
+ * with the mode and value.
+ * Exits directly to "qerr" if
+ * there is no address field or
+ * if the syntax is bad.
+ */
+void getaddr(ADDR *ap)
+{
+       int reg;
+       int c;
+
+       if ((c=getnb()) != '(') {
+               unget(c);
+               expr1(ap, LOPRI, 0);
+               return;
+       }
+       expr1(ap, LOPRI, 1);
+       if (getnb() != ')')
+               qerr();
+       reg = ap->a_type&TMREG;
+       switch (ap->a_type&TMMODE) {
+       case TBR:
+               if (reg != C)
+                       aerr();
+               ap->a_type |= TMINDIR;
+               break;
+       case TSR:
+       case TCC:
+               aerr();
+               break;
+       case TUSER:
+               ap->a_type |= TMINDIR;
+               break;
+       case TWR:
+               if (reg == HL)
+                       ap->a_type = TBR|M;
+               else if (reg==IX || reg==IY)
+                       ap->a_type = TBR|reg;
+               else if (reg==AF || reg==AFPRIME)
+                       aerr();
+               else
+                       ap->a_type |= TMINDIR;
+       }
+}
+
+/*
+ * Expression reader,
+ * real work, part I. Read
+ * operators and resolve types.
+ * The "lpri" is the firewall operator
+ * priority, which stops the scan.
+ * The "paren" argument is true if
+ * the expression is in parentheses.
+ */
+void expr1(ADDR *ap, int lpri, int paren)
+{
+       int c;
+       int opri;
+       ADDR right;
+
+       expr2(ap);
+       while ((c=getnb())=='+' || c=='-' || c=='*' || c=='/') {
+               opri = ADDPRI;
+               if (c=='*' || c=='/')
+                       opri = MULPRI;
+               if (opri <= lpri)
+                       break;
+               expr1(&right, opri, paren);
+               switch (c) {
+               case '+':
+                       if ((ap->a_type&TMMODE) != TUSER)
+                               istuser(&right);
+                       else
+                               ap->a_type = right.a_type;
+                       isokaors(ap, paren);
+                       ap->a_value += right.a_value;
+                       break;
+               case '-':
+                       istuser(&right);
+                       isokaors(ap, paren);
+                       ap->a_value -= right.a_value;
+                       break;
+               case '*':
+                       istuser(ap);
+                       istuser(&right);
+                       ap->a_value *= right.a_value;
+                       break;
+               case '/':
+                       istuser(ap);
+                       istuser(&right);
+                       ap->a_value /= right.a_value;
+               }
+       }
+       unget(c);
+}
+
+/*
+ * Expression reader,
+ * real work, part II. Read
+ * in terminals.
+ */
+void expr2(ADDR *ap)
+{
+       int c;
+       SYM *sp;
+       int mode;
+       char id[NCPS];
+
+       c = getnb();
+       if (c == '[') {
+               expr1(ap, LOPRI, 0);
+               if (getnb() != ']')
+                       qerr();
+               return;
+       }
+       if (c == '-') {
+               expr1(ap, HIPRI, 0);
+               istuser(ap);
+               ap->a_value = -ap->a_value;
+               return;
+       }
+       if (c == '~') {
+               expr1(ap, HIPRI, 0);
+               istuser(ap);
+               ap->a_value = ~ap->a_value;
+               return;
+       }
+       if (c == '\'') {
+               ap->a_type  = TUSER;
+               ap->a_value = get();
+               while ((c=get()) != '\'') {
+                       if (c == '\n')
+                               qerr();
+                       ap->a_value = (ap->a_value<<8) + c;
+               }
+               return;
+       }
+       if (c>='0' && c<='9') {
+               expr3(ap, c);
+               return;
+       }
+       if (isalpha(c)) {
+               getid(id, c);
+               if ((sp=lookup(id, uhash, 0)) == NULL
+               &&  (sp=lookup(id, phash, 0)) == NULL)
+                       sp = lookup(id, uhash, 1);
+               mode = sp->s_type&TMMODE;
+               if (mode==TBR || mode==TWR || mode==TSR || mode==TCC) {
+                       ap->a_type  = mode|sp->s_value;
+                       ap->a_value = 0;
+                       return;
+               }
+               if (mode == TNEW)
+                       uerr(id);
+               ap->a_type  = TUSER;
+               ap->a_value = sp->s_value;
+               return;
+       }
+       qerr();
+}
+
+/*
+ * Read in a constant. The argument
+ * "c" is the first character of the constant,
+ * and has already been validated. The number is
+ * gathered up (stopping on non alphanumeric).
+ * The radix is determined, and the number is
+ * converted to binary.
+ */
+void expr3(ADDR *ap, int c)
+{
+       char *np1;
+       char *np2;
+       int radix;
+       VALUE value;
+       char num[40];
+
+       np1 = &num[0];
+       do {
+               if (isupper(c))
+                       c = tolower(c);
+               *np1++ = c;
+               c = *ip++;
+       } while (isalnum(c));
+       --ip;
+       switch (*--np1) {
+       case 'h':
+               radix = 16;
+               break;
+       case 'o':
+       case 'q':
+               radix = 8;
+               break;
+       case 'b':
+               radix = 2;
+               break;
+       default:
+               radix = 10;
+               ++np1;
+       }
+       np2 = &num[0];
+       value = 0;
+       while (np2 < np1) {
+               if ((c = *np2++)>='0' && c<='9')
+                       c -= '0';
+               else if (c>='a' && c<='f')
+                       c -= 'a'-10;
+               else
+                       err('n');
+               if (c >= radix)
+                       err('n');
+               value = radix*value + c;
+       }
+       ap->a_type  = TUSER;
+       ap->a_value = value;
+}
+
+/*
+ * Make sure that the
+ * mode and register fields of
+ * the type of the "ADDR" pointed to
+ * by "ap" can participate in an addition
+ * or a subtraction.
+ */
+void isokaors(ADDR *ap, int paren)
+{
+       int mode;
+       int reg;
+
+       mode = ap->a_type&TMMODE;
+       if (mode == TUSER)
+               return;
+       if (mode==TWR && paren!=0) {
+               reg = ap->a_type&TMREG;
+               if (reg==IX || reg==IY)
+                       return;
+       }
+       aerr();
+}
diff --git a/Applications/MWC/cmd/asz80/as4.c b/Applications/MWC/cmd/asz80/as4.c
new file mode 100644 (file)
index 0000000..d9a0853
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Z-80 assembler.
+ * Output Intel compatable
+ * hex files.
+ */
+#include       "as.h"
+
+#define        NHEX    32                      /* Longest record */
+
+VALUE  hexla;
+VALUE  hexpc;
+char   hexb[NHEX];
+char   *hexp   = &hexb[0];
+
+/*
+ * Output a word. Use the
+ * standard Z-80 ordering (low
+ * byte then high byte).
+ */
+void outaw(int w)
+{
+       outab(w);
+       outab(w >> 8);
+}
+
+/*
+ * Output an absolute
+ * byte to the code and listing
+ * streams.
+ */
+void outab(int b)
+{
+       if (pass != 0) {
+               if (cp < &cb[NCODE])
+                       *cp++ = b;
+               outbyte(b);
+       }
+       ++dot;
+}
+
+/*
+ * Put out the end of file
+ * hex item at the very end of 
+ * the object file.
+ */
+void outeof(void)
+{
+       outflush();
+       fprintf(ofp, ":00000001FF\n");
+}
+
+/*
+ * Output a hex byte. Flush
+ * the buffer if no room. Store the
+ * byte in the buffer, for future
+ * checksumming. Remember the load
+ * address for flushing.
+ */
+void outbyte(int b)
+{
+       if (hexp>=&hexb[NHEX] || hexpc!=dot) {
+               outflush();
+               hexp = &hexb[0];
+       }
+       if (hexp == &hexb[0]) {
+               hexla = dot;
+               hexpc = dot;
+       }
+       *hexp++ = b;
+       ++hexpc;
+}
+
+/*
+ * Flush out a block of
+ * code to the hex file. Figure
+ * out the length word and the
+ * checksum byte.
+ */
+void outflush(void)
+{
+       char *p;
+       int b;
+       int c;
+
+       if ((b = hexp-&hexb[0]) != 0) {
+               putc(':', ofp);
+               outhex(b);
+               outhex(hexla >> 8);
+               outhex(hexla);
+               outhex(0);
+               c = b + (hexla>>8) + hexla;
+               p = &hexb[0];
+               while (p < hexp) {
+                       b = *p++;
+                       outhex(b);
+                       c += b;
+               }
+               outhex(-c);
+               putc('\n', ofp);
+       }
+}
+
+/*
+ * Put out "b", as a 
+ * two character hex value.
+ * We cannot use printf because
+ * of case problems on VMS.
+ * Upper case ascii.
+ */
+void outhex(int b)
+{
+       static  const char hex[] = {
+               '0',    '1',    '2',    '3',
+               '4',    '5',    '6',    '7',
+               '8',    '9',    'A',    'B',
+               'C',    'D',    'E',    'F'
+       };
+
+       putc(hex[(b>>4)&0x0F], ofp);
+       putc(hex[b&0x0F], ofp);
+}
diff --git a/Applications/MWC/cmd/asz80/as5.c b/Applications/MWC/cmd/asz80/as5.c
new file mode 100644 (file)
index 0000000..8e78f62
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Z-80 assembler.
+ * Build up lines for the
+ * listing file.
+ */
+#include       "as.h"
+
+/*
+ * Copy the data in the listing
+ * code buffer to the listing file.
+ * Produce no file if "lfp" is NULL.
+ * Honour the listing mode stored
+ * in the "lmode".
+ */
+void list(void)
+{
+       char *wp;
+       int nb;
+
+       if (lfp==NULL || lmode==NLIST)
+               return;
+       while (ep < &eb[NERR])
+               *ep++ = ' ';
+       fprintf(lfp, "%.10s", eb);
+       if (lmode == SLIST) {
+               fprintf(lfp, "%31s %5d %s", "", line, ib);
+               return;
+       }
+       fprintf(lfp, "   %04x", laddr);
+       if (lmode == ALIST) {
+               fprintf(lfp, "%24s %5d %s", "", line, ib);
+               return;
+       }
+       wp = cb;
+       nb = cp - cb;
+       list1(wp, nb, 1);
+       fprintf(lfp, " %5d %s", line, ib);
+       while ((nb -= 8) > 0) {
+               wp += 8;
+               fprintf(lfp, "%17s", "");
+               list1(wp, nb, 0);
+               fprintf(lfp, "\n");
+       }
+}
+
+/*
+ * Copy out a partial line
+ * to the listing. Used for the first
+ * and the extra lines in BLIST and
+ * WLIST mode.
+ */
+void list1(char *wp, int nb, int f)
+{
+       int d;
+       int i;
+
+       if (nb > 8)
+               nb = 8;
+       for (i=0; i<nb; ++i) {
+               d = (*wp++) & 0xFF;
+               if (lmode == BLIST)
+                       fprintf(lfp, " %02x", d);
+               else {
+                       d |= *wp++ << 8;
+                       fprintf(lfp, "  %04x", d);
+                       ++i;
+               }
+       }
+       if (f != 0) {
+               while (i < 8) {
+                       fprintf(lfp, "   ");
+                       ++i;
+               }
+       }
+}
diff --git a/Applications/MWC/cmd/asz80/as6.c b/Applications/MWC/cmd/asz80/as6.c
new file mode 100644 (file)
index 0000000..1e6b977
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Z-80 assembler.
+ * Basic symbol tables.
+ * Contain all of the instructions
+ * and register names.
+ */
+#include       "as.h"
+
+/*
+ * This array of symbol nodes
+ * make up the basic symbol table.
+ * The "syminit" routine links these
+ * nodes into the builtin symbol hash
+ * table at start-up time.
+ */
+SYM    sym[] = {
+       {       0,      "b",            TBR,            B       },
+       {       0,      "c",            TBR,            C       },
+       {       0,      "d",            TBR,            D       },
+       {       0,      "e",            TBR,            E       },
+       {       0,      "h",            TBR,            H       },
+       {       0,      "l",            TBR,            L       },
+       {       0,      "a",            TBR,            A       },
+       {       0,      "bc",           TWR,            BC      },
+       {       0,      "de",           TWR,            DE      },
+       {       0,      "hl",           TWR,            HL      },
+       {       0,      "sp",           TWR,            SP      },
+       {       0,      "af",           TWR,            AF      },
+       {       0,      "af'",          TWR,            AFPRIME },
+       {       0,      "ix",           TWR,            IX      },
+       {       0,      "iy",           TWR,            IY      },
+       {       0,      "i",            TSR,            I       },
+       {       0,      "r",            TSR,            R       },
+       {       0,      "nz",           TCC,            CNZ     },
+       {       0,      "z",            TCC,            CZ      },
+       {       0,      "nc",           TCC,            CNC     },
+       {       0,      "po",           TCC,            CPO     },
+       {       0,      "pe",           TCC,            CPE     },
+       {       0,      "p",            TCC,            CP      },
+       {       0,      "m",            TCC,            CM      },
+       {       0,      "defb",         TDEFB,          XXXX    },
+       {       0,      "defw",         TDEFW,          XXXX    },
+       {       0,      "defs",         TDEFS,          XXXX    },
+       {       0,      "defm",         TDEFM,          XXXX    },
+       {       0,      "org",          TORG,           XXXX    },
+       {       0,      "equ",          TEQU,           XXXX    },
+       {       0,      "cond",         TCOND,          XXXX    },
+       {       0,      "endc",         TENDC,          XXXX    },
+       {       0,      "nop",          TNOP,           0x0000  },
+       {       0,      "rlca",         TNOP,           0x0007  },
+       {       0,      "rrca",         TNOP,           0x000F  },
+       {       0,      "rla",          TNOP,           0x0017  },
+       {       0,      "rra",          TNOP,           0x001F  },
+       {       0,      "daa",          TNOP,           0x0027  },
+       {       0,      "cpl",          TNOP,           0x002F  },
+       {       0,      "scf",          TNOP,           0x0037  },
+       {       0,      "ccf",          TNOP,           0x003F  },
+       {       0,      "halt",         TNOP,           0x0076  },
+       {       0,      "exx",          TNOP,           0x00D9  },
+       {       0,      "di",           TNOP,           0x00F3  },
+       {       0,      "ei",           TNOP,           0x00FB  },
+       {       0,      "neg",          TNOP,           0xED44  },
+       {       0,      "retn",         TNOP,           0xED45  },
+       {       0,      "reti",         TNOP,           0xED4D  },
+       {       0,      "rrd",          TNOP,           0xED67  },
+       {       0,      "rld",          TNOP,           0xED6F  },
+       {       0,      "ldi",          TNOP,           0xEDA0  },
+       {       0,      "cpi",          TNOP,           0xEDA1  },
+       {       0,      "ini",          TNOP,           0xEDA2  },
+       {       0,      "outi",         TNOP,           0xEDA3  },
+       {       0,      "ldd",          TNOP,           0xEDA8  },
+       {       0,      "cpd",          TNOP,           0xEDA9  },
+       {       0,      "ind",          TNOP,           0xEDAA  },
+       {       0,      "outd",         TNOP,           0xEDAB  },
+       {       0,      "ldir",         TNOP,           0xEDB0  },
+       {       0,      "cpir",         TNOP,           0xEDB1  },
+       {       0,      "inir",         TNOP,           0xEDB2  },
+       {       0,      "otir",         TNOP,           0xEDB3  },
+       {       0,      "lddr",         TNOP,           0xEDB8  },
+       {       0,      "cpdr",         TNOP,           0xEDB9  },
+       {       0,      "indr",         TNOP,           0xEDBA  },
+       {       0,      "otdr",         TNOP,           0xEDBB  },
+       {       0,      "rst",          TRST,           XXXX    },
+       {       0,      "djnz",         TREL,           0x0010  },
+       {       0,      "jr",           TREL,           0x0018  },
+       {       0,      "ret",          TRET,           0x00C9  },
+       {       0,      "call",         TJMP,           0x00CD  },
+       {       0,      "jp",           TJMP,           0x00C3  },
+       {       0,      "push",         TPUSH,          0x00C5  },
+       {       0,      "pop",          TPUSH,          0x00C1  },
+       {       0,      "im",           TIM,            XXXX    },
+       {       0,      "in",           TIO,            0x00DB  },
+       {       0,      "out",          TIO,            0x00D3  },
+       {       0,      "bit",          TBIT,           0xCB40  },
+       {       0,      "res",          TBIT,           0xCB80  },
+       {       0,      "set",          TBIT,           0xCBC0  },
+       {       0,      "rlc",          TSHR,           0xCB00  },
+       {       0,      "rrc",          TSHR,           0xCB08  },
+       {       0,      "rl",           TSHR,           0xCB10  },
+       {       0,      "rr",           TSHR,           0xCB18  },
+       {       0,      "sla",          TSHR,           0xCB20  },
+       {       0,      "sra",          TSHR,           0xCB28  },
+       {       0,      "srl",          TSHR,           0xCB38  },
+       {       0,      "inc",          TINC,           0x0004  },
+       {       0,      "dec",          TINC,           0x0005  },
+       {       0,      "ex",           TEX,            XXXX    },
+       {       0,      "add",          TADD,           0x0080  },
+       {       0,      "adc",          TADD,           0x0088  },
+       {       0,      "sbc",          TADD,           0x0098  },
+       {       0,      "sub",          TSUB,           0x0090  },
+       {       0,      "and",          TSUB,           0x00A0  },
+       {       0,      "xor",          TSUB,           0x00A8  },
+       {       0,      "or",           TSUB,           0x00B0  },
+       {       0,      "cp",           TSUB,           0x00B8  },
+       {       0,      "ld",           TLD,            XXXX    }
+};
+
+/*
+ * Set up the symbol table.
+ * Sweep through the initializations
+ * of the "phash", and link them into the
+ * buckets. Because it is here, a
+ * "sizeof" works.
+ */
+void syminit(void)
+{
+       SYM *sp;
+       int hash;
+
+       sp = &sym[0];
+       while (sp < &sym[sizeof(sym)/sizeof(SYM)]) {
+               hash = symhash(sp->s_id);
+               sp->s_fp = phash[hash];
+               phash[hash] = sp;
+               ++sp;
+       }
+}