From 5715297aad9bfad257d9366c141494c093effad9 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 30 Oct 2017 23:44:05 +0000 Subject: [PATCH] as: lots of stuff here - Rework types for cpu / flags - Add 6502 types - Teach ld that there can be a ZP segment - Restructure the assembler to put arch specific code in less files - Fix various minor bugs (segment checking etc) - Fix a bug where a_sym was not reliably set to NULL for constants - Add initial test 6502 support to prove can re-target easily --- Applications/MWC/cmd/asz80/Makefile.z80 | 2 +- Applications/MWC/cmd/asz80/as.h | 152 +++++++++++++++++++++--- Applications/MWC/cmd/asz80/as0.c | 2 +- Applications/MWC/cmd/asz80/as1.c | 86 +++++++++----- Applications/MWC/cmd/asz80/as2.c | 35 ++---- Applications/MWC/cmd/asz80/as3.c | 76 ++---------- Applications/MWC/cmd/asz80/as4.c | 22 ++-- Applications/MWC/cmd/asz80/as6.c | 44 +++++++ Applications/MWC/cmd/asz80/ld.c | 8 +- Applications/MWC/cmd/asz80/obj.h | 11 ++ 10 files changed, 290 insertions(+), 148 deletions(-) diff --git a/Applications/MWC/cmd/asz80/Makefile.z80 b/Applications/MWC/cmd/asz80/Makefile.z80 index f583c6f5..1b46e27f 100644 --- a/Applications/MWC/cmd/asz80/Makefile.z80 +++ b/Applications/MWC/cmd/asz80/Makefile.z80 @@ -1,5 +1,5 @@ LINKER = sdcc -FCC = ../../../../Library/tools/fcc -O2 +FCC = ../../../../Library/tools/fcc -O2 -DTARGET_Z80 PLATFORM = #PLATFORM = -tzx128 diff --git a/Applications/MWC/cmd/asz80/as.h b/Applications/MWC/cmd/asz80/as.h index 7285af2e..ad053e43 100644 --- a/Applications/MWC/cmd/asz80/as.h +++ b/Applications/MWC/cmd/asz80/as.h @@ -21,7 +21,6 @@ #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 NSEGMENT 4 /* # of segments */ #define XXXX 0 /* Unused value */ /* @@ -30,6 +29,15 @@ #define GOOD 0 #define BAD 1 +#ifdef TARGET_Z80 + +typedef uint16_t VALUE; /* For symbol values */ + +#define NSEGMENT 4 /* # of segments */ + +#define ARCH OA_8080 +#define ARCH_FLAGS OA_8080_Z80 + /* * Types. These are used * in both symbols and in address @@ -57,6 +65,8 @@ #define TEQU 0x0A00 /* equ */ #define TCOND 0x0B00 /* conditional */ #define TENDC 0x0C00 /* end conditional */ +#define TSEGMENT 0x0D00 /* segments by number */ +#define TEXPORT 0x0E00 /* symbol export */ #define TNOP 0x0F00 /* nop */ #define TRST 0x1000 /* restarts */ #define TREL 0x1100 /* djnz, jr */ @@ -73,8 +83,6 @@ #define TLD 0x1C00 /* ld */ #define TCC 0x1D00 /* condition code */ #define TSUB 0x1E00 /* sub et al */ -#define TSEGMENT 0x1F00 /* segments by number */ -#define TEXPORT 0x2000 /* symbol export */ #define TNOP180 0x2100 /* Z180 immediate */ #define TTST180 0x2200 /* TST m/g/(hl) */ #define TIMMED8 0x2300 /* TSTIO m */ @@ -118,13 +126,104 @@ #define CM 7 /* - * Segments + * Error message numbers: FIXME - sort general first */ -#define UNKNOWN -1 -#define ABSOLUTE 0 -#define CODE 1 -#define DATA 2 -#define BSS 3 + +#define BRACKET_EXPECTED 1 +#define MISSING_COMMA 2 +#define SQUARE_EXPECTED 3 +#define PERCENT_EXPECTED 4 +#define UNEXPECTED_CHR 10 +#define PHASE_ERROR 11 +#define MULTIPLE_DEFS 12 +#define SYNTAX_ERROR 13 +#define MUST_BE_ABSOLUTE 14 +#define MISSING_DELIMITER 15 +#define INVALID_CONST 16 +#define BRA_RANGE 17 +#define CONDCODE_ONLY 18 +#define INVALID_REG 19 +#define ADDR_REQUIRED 20 +#define INVALID_ID 21 +#define REG_MUST_BE_C 22 +#define DIVIDE_BY_ZERO 23 +#define CONSTANT_RANGE 24 +#define DATA_IN_BSS 25 +#define SEGMENT_OVERFLOW 26 +#define DATA_IN_ZP 27 +#define REQUIRE_Z180 28 + + + +#elif TARGET_6502 + +typedef uint16_t VALUE; /* For symbol values */ + +#define NSEGMENT 5 /* # of segments */ + +#define ARCH OA_6502 +#define ARCH_FLAGS OA_6502_BCD /* For now until CPU type properly settable */ + + +/* + * 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 0x000F /* Register code */ +#define TMMDF 0x0001 /* Multidef */ +#define TMASG 0x0002 /* Defined by "=" */ +#define TMMODE 0xFF00 /* Mode */ +#define TMINDIR 0x8000 /* Indirect flag in mode */ +#define TPUBLIC 0x0080 /* Exported symbol */ +#define TMADDR 0x00F0 /* Addressing mode bits */ + +#define TZP 0x0010 /* 0000 is TUSER */ +#define TACCUM 0x0020 +#define TZPX 0x0030 +#define TZPY 0x0040 +#define TABSX 0x0050 +#define TABSY 0x0060 +#define TZPX_IND 0x0070 +#define TZPY_IND 0x0080 +#define TZP_IND 0x0090 + + +#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 TSEGMENT 0x0D00 /* segments by number */ +#define TEXPORT 0x0E00 /* symbol export */ +#define TCC 0x0F00 +/* CPU specific codes */ +#define TCLASS0 0x1000 /* xxxyyy00 instructions */ +#define TCLASS1 0x1100 /* xxxyyy01 instructions */ +#define TCLASS2 0x1200 /* xxxyyy10 instructions */ +#define TCLASS2Y 0x1300 /* ditto but taking Y */ +#define TJMP 0x1400 /* JMP */ +#define TREL8 0x1500 /* Bcc */ +#define TIMPL 0x1600 /* Implicit */ +#define TBRK 0x1700 /* BRK */ +#define TJSR 0x1800 /* JSR */ + +/* + * Registers. + */ +#define A 0 +#define X 1 +#define Y 2 /* * Error message numbers @@ -141,19 +240,40 @@ #define MUST_BE_ABSOLUTE 14 #define MISSING_DELIMITER 15 #define INVALID_CONST 16 -#define JR_RANGE 17 +#define BRA_RANGE 17 #define CONDCODE_ONLY 18 #define INVALID_REG 19 #define ADDR_REQUIRED 20 #define INVALID_ID 21 -#define REG_MUST_BE_C 22 +#define BADMODE 22 #define DIVIDE_BY_ZERO 23 #define CONSTANT_RANGE 24 #define DATA_IN_BSS 25 #define SEGMENT_OVERFLOW 26 -#define REQUIRE_Z180 27 +#define DATA_IN_ZP 27 -typedef uint16_t VALUE; /* For symbol values */ + +#else +#error "Unknown target" +#endif + +/* + * Segments + */ +#define UNKNOWN -1 +#define ABSOLUTE 0 +#define CODE 1 +#define DATA 2 +#define BSS 3 + +/* + * Expression priority + */ + +#define LOPRI 0 +#define ADDPRI 1 +#define MULPRI 2 +#define HIPRI 3 /* * Address description. @@ -204,12 +324,8 @@ extern int noobj; extern int cpu_flags; 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, uint8_t); extern void uerr(char *); @@ -241,4 +357,6 @@ extern void outbyte(uint8_t); extern void outflush(void); extern void syminit(void); +extern char *etext[]; + #include "obj.h" diff --git a/Applications/MWC/cmd/asz80/as0.c b/Applications/MWC/cmd/asz80/as0.c index d2295554..28ba0fdd 100644 --- a/Applications/MWC/cmd/asz80/as0.c +++ b/Applications/MWC/cmd/asz80/as0.c @@ -23,7 +23,7 @@ int line; jmp_buf env; int debug_write = 1 ; int noobj; -int cpu_flags = OA_8080_Z80; +int cpu_flags = ARCH_FLAGS; /* * Make up a file name. diff --git a/Applications/MWC/cmd/asz80/as1.c b/Applications/MWC/cmd/asz80/as1.c index d7645051..b2592bb4 100644 --- a/Applications/MWC/cmd/asz80/as1.c +++ b/Applications/MWC/cmd/asz80/as1.c @@ -28,6 +28,11 @@ #define OPJR 0x20 /* Opcode: jr cc base */ #define OPRET 0xC0 /* Opcode: ret cc base */ +static void asmld(void); +static ADDR *getldaddr(ADDR *ap, int *modep, int *regp, ADDR *iap); +static void outop(int op, ADDR *ap); +static int ccfetch(ADDR *ap); + static void require_z180(void) { if (!(cpu_flags & OA_8080_Z180)) { @@ -36,6 +41,55 @@ static void require_z180(void) } } +/* + * 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(BRACKET_EXPECTED); + reg = ap->a_type&TMREG; + switch (ap->a_type&TMMODE) { + case TBR: + if (reg != C) + aerr(REG_MUST_BE_C); + ap->a_type |= TMINDIR; + break; + case TSR: + case TCC: + aerr(ADDR_REQUIRED); + 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(INVALID_REG); + else + ap->a_type |= TMINDIR; + } +} + + /* * Assemble one line. * The line in in "ib", the "ip" @@ -205,7 +259,7 @@ loop: istuser(&a1); disp = a1.a_value-dot[segment]-2; if (disp<-128 || disp>127 || a1.a_segment != segment) - aerr(JR_RANGE); + aerr(BRA_RANGE); outab(opcode); outab(disp); break; @@ -525,7 +579,7 @@ loop: * indexing. This layer just screens out the many * cases, and emits the correct bytes. */ -void asmld(void) +static void asmld(void) { int mdst; int rdst; @@ -633,7 +687,7 @@ void asmld(void) * pointer "ap" if indexing is required, otherwise just * pass the "iap" through. */ -ADDR *getldaddr(ADDR *ap, int *modep, int *regp, ADDR *iap) +static ADDR *getldaddr(ADDR *ap, int *modep, int *regp, ADDR *iap) { int mode; int reg; @@ -681,7 +735,7 @@ ADDR *getldaddr(ADDR *ap, int *modep, int *regp, ADDR *iap) * the address mode to see if the bytes * are needed. */ -void outop(int op, ADDR *ap) +static void outop(int op, ADDR *ap) { int needisp; @@ -705,28 +759,6 @@ void outop(int op, ADDR *ap) outrab(ap); } -/* - * The next character - * in the input must be a comma - * or it is a fatal error. - */ -void comma(void) -{ - if (getnb() != ',') - qerr(MISSING_COMMA); -} - -/* - * 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(ADDR_REQUIRED); -} - /* * Try to interpret an "ADDR" * as a condition code name. Return @@ -734,7 +766,7 @@ void istuser(ADDR *ap) * be interpreted as a condition. The * "c" condition is a pain. */ -int ccfetch(ADDR *ap) +static int ccfetch(ADDR *ap) { if (ap->a_type == (TBR|C)) return (CC); diff --git a/Applications/MWC/cmd/asz80/as2.c b/Applications/MWC/cmd/asz80/as2.c index f6975692..9093df79 100644 --- a/Applications/MWC/cmd/asz80/as2.c +++ b/Applications/MWC/cmd/asz80/as2.c @@ -25,30 +25,6 @@ int symhash(char *id) return (hash&HMASK); } -/* We may want to move this out into a a helper app at the end to dump - errors without using assembler space */ - -static char *etext[] = { - "unexpected character", - "phase error", - "multiple definitions", - "syntax error", - "must be absolute", - "missing delimiter", - "invalid constant", - "JR out of range", - "condition required", - "invalid register for operation", - "address required", - "invalid id", - "must be C", - "divide by 0", - "constant out of range", - "data in BSS", - "segment overflow", - "Z180 instruction" -}; - static void errstr(uint8_t code) { if (code < 10) { @@ -101,6 +77,17 @@ void qerr(uint8_t code) err('q', code); } +/* + * The next character + * in the input must be a comma + * or it is a fatal error. + */ +void comma(void) +{ + if (getnb() != ',') + qerr(MISSING_COMMA); +} + /* * Read identifier. * The character "c" is the first diff --git a/Applications/MWC/cmd/asz80/as3.c b/Applications/MWC/cmd/asz80/as3.c index dfbf3674..6585d97c 100644 --- a/Applications/MWC/cmd/asz80/as3.c +++ b/Applications/MWC/cmd/asz80/as3.c @@ -5,59 +5,20 @@ */ #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. + * Check if the mode of + * an ADDR is TUSER. If not, give + * an error. */ -void getaddr(ADDR *ap) +void istuser(ADDR *ap) { - int reg; - int c; - - if ((c=getnb()) != '(') { - unget(c); - expr1(ap, LOPRI, 0); - return; - } - expr1(ap, LOPRI, 1); - if (getnb() != ')') - qerr(BRACKET_EXPECTED); - reg = ap->a_type&TMREG; - switch (ap->a_type&TMMODE) { - case TBR: - if (reg != C) - aerr(REG_MUST_BE_C); - ap->a_type |= TMINDIR; - break; - case TSR: - case TCC: + if ((ap->a_type&TMMODE) != TUSER) aerr(ADDR_REQUIRED); - 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(INVALID_REG); - else - ap->a_type |= TMINDIR; - } } + static void chkabsolute(ADDR *a) { /* Not symbols, doesn't matter */ @@ -313,27 +274,6 @@ void expr3(ADDR *ap, int c) ap->a_type = TUSER; ap->a_value = value; ap->a_segment = ABSOLUTE; + ap->a_sym = NULL; } -/* - * 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(ADDR_REQUIRED); -} diff --git a/Applications/MWC/cmd/asz80/as4.c b/Applications/MWC/cmd/asz80/as4.c index dfa473e5..1205d557 100644 --- a/Applications/MWC/cmd/asz80/as4.c +++ b/Applications/MWC/cmd/asz80/as4.c @@ -22,14 +22,14 @@ void outpass(void) /* Lay the file out */ for (i = 1; i < NSEGMENT; i++) { segbase[i] = base; - if (i != BSS) { + if (i != BSS && i != ZP) { obh.o_segbase[i] = base; base += segsize[i] + 2; /* 2 for the EOF mark */ } obh.o_size[i] = truesize[i]; } obh.o_magic = 0; - obh.o_arch = OA_8080; + obh.o_arch = ARCH; obh.o_flags = 0; obh.o_cpuflags = cpu_flags; obh.o_symbase = base; @@ -69,11 +69,18 @@ void outaw(uint16_t w) outab(w >> 8); } +static void check_store_allowed(uint8_t segment, uint16_t value) +{ + if (segment == BSS) + err('b', DATA_IN_BSS); + if (segment == ZP) + err('z', DATA_IN_ZP); +} + void outraw(ADDR *a) { if (a->a_segment != ABSOLUTE) { - if (segment == BSS) - err('b', DATA_IN_BSS); + check_store_allowed(segment, a->a_value); if (a->a_sym == NULL) { outbyte(REL_ESC); outbyte((1 << 4) | REL_SIMPLE | a->a_segment); @@ -95,8 +102,8 @@ void outraw(ADDR *a) void outab(uint8_t b) { /* Not allowed to put data in the BSS except zero */ - if (segment == BSS && b) - err('b', DATA_IN_BSS); + check_store_allowed(segment, b); + /* FIXME: wrong error ?? */ if (segment == ABSOLUTE) err('A', MUST_BE_ABSOLUTE); outbyte(b); @@ -119,8 +126,7 @@ void outrab(ADDR *a) { /* FIXME: handle symbols */ if (a->a_segment != ABSOLUTE) { - if (segment == BSS) - err('b', DATA_IN_BSS); + check_store_allowed(segment, a->a_value); if (a->a_sym == NULL) { outbyte(REL_ESC); outbyte((0 << 4) | REL_SIMPLE | a->a_segment); diff --git a/Applications/MWC/cmd/asz80/as6.c b/Applications/MWC/cmd/asz80/as6.c index 9cd6c52f..acb4dcbf 100644 --- a/Applications/MWC/cmd/asz80/as6.c +++ b/Applications/MWC/cmd/asz80/as6.c @@ -161,3 +161,47 @@ void syminit(void) ++sp; } } + +char *etext[] = { + "unexpected character", + "phase error", + "multiple definitions", + "syntax error", + "must be absolute", + "missing delimiter", + "invalid constant", + "JR out of range", + "condition required", + "invalid register for operation", + "address required", + "invalid id", + "must be C", + "divide by 0", + "constant out of range", + "data in BSS", + "segment overflow", + "Z180 instruction" +}; + +/* + * 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(ADDR_REQUIRED); +} diff --git a/Applications/MWC/cmd/asz80/ld.c b/Applications/MWC/cmd/asz80/ld.c index a1802f95..35efc3cb 100644 --- a/Applications/MWC/cmd/asz80/ld.c +++ b/Applications/MWC/cmd/asz80/ld.c @@ -281,7 +281,7 @@ struct object *load_object(FILE * fp, off_t off, int lib, const char *path) sp = o->syment; for (i = 0; i < nsym; i++) { type = fgetc(fp); - if (!(type & S_UNKNOWN) && (type & S_SEGMENT) > BSS) + if (!(type & S_UNKNOWN) && (type & S_SEGMENT) > ZP) error("bad symbol"); fread(name, 16, 1, fp); name[16] = 0; @@ -345,6 +345,8 @@ static void set_segment_bases(void) } base[3] = base[2] + size[2]; + /* ZP if any is assumed to be set on input */ + if (base[3] < base[2] || base[3] + size[3] < base[3]) error("image too large"); /* Whoopee it fits */ @@ -429,7 +431,7 @@ static void relocate_stream(struct object *o, FILE * op, FILE * ip) if (code & REL_SIMPLE) { uint8_t seg = code & S_SEGMENT; /* Check entry is valid */ - if (seg == ABSOLUTE || seg > BSS || size > 2) + if (seg == ABSOLUTE || seg > ZP || size > 2) error("invalid reloc"); /* If we are not building an absolute then keep the tag */ if (ldmode != LD_ABSOLUTE) { @@ -659,6 +661,8 @@ int main(int argc, char *argv[]) break; case 'b': ldmode = LD_ABSOLUTE; + strip = 1; + break; case 'v': printf("FuzixLD 0.1\n"); break; diff --git a/Applications/MWC/cmd/asz80/obj.h b/Applications/MWC/cmd/asz80/obj.h index 40309559..da1611d3 100644 --- a/Applications/MWC/cmd/asz80/obj.h +++ b/Applications/MWC/cmd/asz80/obj.h @@ -8,6 +8,7 @@ struct objhdr uint16_t o_magic; uint8_t o_arch; #define OA_8080 1 +#define OA_6502 2 uint8_t o_flags; uint16_t o_cpuflags; #define OA_8080_Z80 1 @@ -15,6 +16,15 @@ struct objhdr #define OA_8080_Z280 4 #define OA_8080_R800 8 #define OA_8080_8085 16 + +#define OA_6502_BCD 1 /* Uses BCD instructions */ +#define OA_6502_NMOS 2 /* Uses NMOS undocumented */ +#define OA_6502_65C02 4 /* Uses 65C02 */ +#define OA_6502_BITOPS 8 /* Uses extended bit operations */ +#define OA_6502_65C816 16 /* 65C816 and relatives */ +#define OA_6502_ZPAT0 32 /* Binary Assumes ZP is at 0 */ +#define OA_6502_65CE02 64 /* Does anyone really care ? */ + uint32_t o_segbase[OSEG]; uint16_t o_size[OSEG]; uint32_t o_symbase; @@ -66,3 +76,4 @@ struct objhdr #define CODE 1 #define DATA 2 #define BSS 3 +#define ZP 4 -- 2.34.1