From 5f2b799cfaae89cafb316a445002b85921634ed4 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 25 Oct 2017 20:02:51 +0100 Subject: [PATCH] as: Z80 assembler from Coherent 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. --- Applications/MWC/cmd/asz80/Makefile.68000 | 44 ++ Applications/MWC/cmd/asz80/Makefile.6809 | 40 ++ Applications/MWC/cmd/asz80/Makefile.z80 | 34 ++ Applications/MWC/cmd/asz80/TODO | 49 ++ Applications/MWC/cmd/asz80/as.h | 195 +++++++ Applications/MWC/cmd/asz80/as0.c | 123 ++++ Applications/MWC/cmd/asz80/as1.c | 676 ++++++++++++++++++++++ Applications/MWC/cmd/asz80/as2.c | 233 ++++++++ Applications/MWC/cmd/asz80/as3.c | 254 ++++++++ Applications/MWC/cmd/asz80/as4.c | 121 ++++ Applications/MWC/cmd/asz80/as5.c | 75 +++ Applications/MWC/cmd/asz80/as6.c | 137 +++++ 12 files changed, 1981 insertions(+) create mode 100644 Applications/MWC/cmd/asz80/Makefile.68000 create mode 100644 Applications/MWC/cmd/asz80/Makefile.6809 create mode 100644 Applications/MWC/cmd/asz80/Makefile.z80 create mode 100644 Applications/MWC/cmd/asz80/TODO create mode 100644 Applications/MWC/cmd/asz80/as.h create mode 100644 Applications/MWC/cmd/asz80/as0.c create mode 100644 Applications/MWC/cmd/asz80/as1.c create mode 100644 Applications/MWC/cmd/asz80/as2.c create mode 100644 Applications/MWC/cmd/asz80/as3.c create mode 100644 Applications/MWC/cmd/asz80/as4.c create mode 100644 Applications/MWC/cmd/asz80/as5.c create mode 100644 Applications/MWC/cmd/asz80/as6.c diff --git a/Applications/MWC/cmd/asz80/Makefile.68000 b/Applications/MWC/cmd/asz80/Makefile.68000 new file mode 100644 index 00000000..07b360bd --- /dev/null +++ b/Applications/MWC/cmd/asz80/Makefile.68000 @@ -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 index 00000000..2379f785 --- /dev/null +++ b/Applications/MWC/cmd/asz80/Makefile.6809 @@ -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 index 00000000..d420648e --- /dev/null +++ b/Applications/MWC/cmd/asz80/Makefile.z80 @@ -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 index 00000000..644cbaf3 --- /dev/null +++ b/Applications/MWC/cmd/asz80/TODO @@ -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 index 00000000..d69c3efe --- /dev/null +++ b/Applications/MWC/cmd/asz80/as.h @@ -0,0 +1,195 @@ +/* + * Z-80 assembler. + * Header file, used by all + * parts of the assembler. + */ +#include +#include +#include +#include +#include + +/* + * 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 index 00000000..4fe954fb --- /dev/null +++ b/Applications/MWC/cmd/asz80/as0.c @@ -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; is_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 index 00000000..a7272a1c --- /dev/null +++ b/Applications/MWC/cmd/asz80/as2.c @@ -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 index 00000000..bacbc447 --- /dev/null +++ b/Applications/MWC/cmd/asz80/as3.c @@ -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 index 00000000..d9a0853b --- /dev/null +++ b/Applications/MWC/cmd/asz80/as4.c @@ -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 index 00000000..8e78f624 --- /dev/null +++ b/Applications/MWC/cmd/asz80/as5.c @@ -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; is_id); + sp->s_fp = phash[hash]; + phash[hash] = sp; + ++sp; + } +} -- 2.34.1