diff --git a/z80as/LICENSE b/z80as/LICENSE new file mode 100644 index 0000000..6b103c4 --- /dev/null +++ b/z80as/LICENSE @@ -0,0 +1,27 @@ +Copyright © 1977-1995 by Robert Swartz. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as is" +and any express or implied warranties, including, but not limited to, the +implied warranties of merchantability and fitness for a particular purpose +are disclaimed. In no event shall the copyright holder or contributors be +liable for any direct, indirect, incidental, special, exemplary, or +consequential damages (including, but not limited to, procurement of +substitute goods or services; loss of use, data, or profits; or business +interruption) however caused and on any theory of liability, whether in +contract, strict liability, or tort (including negligence or otherwise) +arising in any way out of the use of this software, even if advised of the +possibility of such damage. diff --git a/z80as/Makefile b/z80as/Makefile new file mode 100644 index 0000000..4f45a89 --- /dev/null +++ b/z80as/Makefile @@ -0,0 +1,16 @@ +CC = cc +CFLAGS = -O2 -Wall + +all: z80as hex2bin + +z80as: as0.o as1.o as2.o as3.o as4.o as5.o as6.o + $(CC) -o z80as.x86 as0.o as1.o as2.o as3.o as4.o as5.o as6.o + +hex2bin: hex2bin.c + $(CC) -o hex2bin hex2bin.c + +install: + su root cp z80as.x86 /usr/local/bin/z80as + +clean: + rm -f z80as.x86 hex2bin *.o diff --git a/z80as/as.h b/z80as/as.h new file mode 100644 index 0000000..43d29aa --- /dev/null +++ b/z80as/as.h @@ -0,0 +1,168 @@ +/* + * Z-80 assembler. + * Header file, used by all + * parts of the assembler. + */ +#include +#include +#include +#ifdef vax +#include stsdef +#include ssdef +#endif + +/* + * 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. + */ +#ifdef vax +#define GOOD (SS$_NORMAL) +#define BAD (STS$M_INHIB_MSG|SS$_ABORT) +#else +#define GOOD 0 +#define BAD 1 +#endif + +/* + * 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 SYM *lookup(); diff --git a/z80as/as0.c b/z80as/as0.c new file mode 100644 index 0000000..31184c3 --- /dev/null +++ b/z80as/as0.c @@ -0,0 +1,131 @@ +/* + * 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; + +main(argc, argv) +char *argv[]; +{ + register char *ifn; + register char *p; + register int i; + register 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. + */ +asmld() +{ + int mdst; + int rdst; + int msrc; + int rsrc; + ADDR *indexap; + ADDR dst; + ADDR src; + ADDR *getldaddr(); + + 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(ap, modep, regp, iap) +ADDR *ap; +int *modep; +int *regp; +ADDR *iap; +{ + register int mode; + register 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. + */ +outop(op, ap) +register int op; +register ADDR *ap; +{ + register 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. + */ +comma() +{ + if (getnb() != ',') + qerr(); +} + +/* + * Check if the mode of + * an ADDR is TUSER. If not, give + * an error. + */ +istuser(ap) +register 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. + */ +ccfetch(ap) +register 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/z80as/as2.c b/z80as/as2.c new file mode 100644 index 0000000..5532994 --- /dev/null +++ b/z80as/as2.c @@ -0,0 +1,244 @@ +/* + * 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. + */ +symhash(id) +register char *id; +{ + register int hash; + register 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. + */ +err(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. + */ +uerr(id) +char *id; +{ + if (pass != 0) { + if (lflag != 0) + storerror('u'); + else + printf("%04d u %.*s\n", line, NCPS, id); + } +} + +/* + * The "a" error is common. + */ +aerr() +{ + err('a'); +} + +/* + * Ditto the "q" error. + */ +qerr() +{ + err('q'); +} + +/* + * Put the error code + * "c" into the error buffer. + * Check that it is not already + * there. + */ +storerror(c) +register int c; +{ + register 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. + */ +getid(id, c) +register int c; +char *id; +{ + register 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(id, htable, cf) +char *id; +SYM *htable[]; +{ + register SYM *sp; + register 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. + */ +symeq(p1, p2) +register char *p1; +register char *p2; +{ + register 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. + */ +symcopy(p1, p2) +register char *p1; +register char *p2; +{ + register int n; + + n = NCPS; + do { + *p1++ = *p2++; + } while (--n); +} + +/* + * Get the next character + * from the input buffer. Do not + * step past the newline. + */ +get() +{ + register 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. + */ +getnb() +{ + register int c; + + while ((c = *ip)==' ' || c=='\t') + ++ip; + if (c != '\n') + ++ip; + return (c); +} + +/* + * Put a character back. + */ +unget(c) +{ + if (c != '\n') + --ip; +} diff --git a/z80as/as3.c b/z80as/as3.c new file mode 100644 index 0000000..2d952e2 --- /dev/null +++ b/z80as/as3.c @@ -0,0 +1,260 @@ +/* + * 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. + */ +getaddr(ap) +register ADDR *ap; +{ + register int reg; + register 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. + */ +expr1(ap, lpri, paren) +register ADDR *ap; +{ + register int c; + register 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. + */ +expr2(ap) +register ADDR *ap; +{ + register int c; + register SYM *sp; + register int mode; + char id[NCPS]; + + c = getnb(); + if (c == '[') { + expr1(ap, LOPRI); + if (getnb() != ']') + qerr(); + return; + } + if (c == '-') { + expr1(ap, HIPRI); + istuser(ap); + ap->a_value = -ap->a_value; + return; + } + if (c == '~') { + expr1(ap, HIPRI); + 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. + */ +expr3(ap, c) +register ADDR *ap; +register int c; +{ + register char *np1; + register char *np2; + register int radix; + register 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. + */ +isokaors(ap, paren) +register ADDR *ap; +{ + register int mode; + register 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/z80as/as4.c b/z80as/as4.c new file mode 100644 index 0000000..aec47d8 --- /dev/null +++ b/z80as/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). + */ +outaw(w) +{ + outab(w); + outab(w >> 8); +} + +/* + * Output an absolute + * byte to the code and listing + * streams. + */ +outab(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. + */ +outeof() +{ + 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. + */ +outbyte(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. + */ +outflush() +{ + register char *p; + register int b; + register 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. + */ +outhex(b) +{ + static 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/z80as/as5.c b/z80as/as5.c new file mode 100644 index 0000000..ee44a6a --- /dev/null +++ b/z80as/as5.c @@ -0,0 +1,77 @@ +/* + * 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". + */ +list() +{ + register char *wp; + register 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. + */ +list1(wp, nb, f) +register char *wp; +register int nb; +{ + register int d; + register int i; + + if (nb > 8) + nb = 8; + for (i=0; is_id); + sp->s_fp = phash[hash]; + phash[hash] = sp; + ++sp; + } +} diff --git a/z80as/basic.bin b/z80as/basic.bin new file mode 100644 index 0000000..1b201ee Binary files /dev/null and b/z80as/basic.bin differ diff --git a/z80as/basic.hex b/z80as/basic.hex new file mode 100644 index 0000000..91df5e9 --- /dev/null +++ b/z80as/basic.hex @@ -0,0 +1,174 @@ +:20010000312017AF32221721DE1822DA182A06002B22DC18213108CD010ECD330F3A6318C7 +:20012000FE4ECA4201FE59C214012ADA187EFE01CA3901CDAF02C32D0122D818CDCD02C3BB +:200140004501CDC5023E0C32CA18115F18218808CD7A0DCDAF0F212B08CD010E3E0132248A +:2001600017312017CDB20FCD330FCD8D07DA7601CDFE01C36701CD7C01C35C012163182292 +:200180002517CDE00DE6A0FEA011A908CA7C03CD6D03CDE80DFE0DC8015342C3B301014119 +:2001A00042C3B301015343C3B30101424FC3B301014D44C5CDB20FC1CD770F41CD770F21C1 +:2001C0004208CD010E3A2417B7C25301214908CD010E2ADA18444D5E160019EB212517CD1A +:2001E000130EEBDAD501030A6F030A67116318CDD80E3E0D12216318CDFC0DC353012ADA2A +:20020000187E3DCA1D02EB132A6118EBCD130E2BDA3202CA32027ECDAF02C301023A6018FD +:20022000FE04C8CDB4022AD818CD8602360122D818C94622CD183A6018DA4A02D604CA43E4 +:2002400002C60490CA7D02DA6902473A6018FE04C878CDB4022ACD18CDA3022AD818EB224E +:20026000D81803CD9802C37D022F3CCDAF02CDA302EB2ACD18C48D02360122D8182ACD18E2 +:200280003A6018FE04C81160181A4F06001A7713230B78B1C28D02C91A772B1B0B78B1C208 +:2002A0009802C93AD818954F3AD9189C47B1C9856FD024C92AD818CDAF02EB21DC18CD131C +:2002C0000ED2D20DC92ADA18360122D8182AD8182322AE18EB21DC18AF12CD130E13C2D8B0 +:2002E000022ADC18224318218A17360022451821451822AC18C9CDBE0EDA9E017D322217C3 +:20030000C35301CDE00DFE0D110000CA1103CDBE0E2ADA187E3DC823CD130E2BDA2903CACE +:2003200029037ECDAF02C31403D5116318CDF70723E5216318CDFC0DCDBC0FCDB20FE1D13D +:20034000C31403CDCD022ADA187E3DCA5301232323222517224118AF322417CDB20FCDBCBD +:200360000FCD6D03CD0C0DD25E03C35301CDE00DB7F28A03FE91D2980111B708CDE80DE69F +:200380001F076F260019CD1E0EE9CDC80CDA9801E506F5CDD80DCDC506D1CD9C0DC9CD414B +:2003A0000ECDC80CDA9801E506F5CDD80DCDC506D1D5CD9C0D069ECDD80DCDC506CDE00D2D +:2003C000FE9FCACE03115E08CD860DC3D403CDE80DCDC50611FEFFCDAB0DEBCD0C0DDAA438 +:2003E00001EB722B7311FBFFCDAB0DE511FBFFCDAB0DCD9B0DD1CD9C0D11FDFFCDAB0DD1DD +:20040000722B732B3601C36A04CD410E2A45187E3DC2A40123E5CDC80CDA2504EBE1E5CD4A +:20042000130EC2A401E1E5E511060019E3CD1E0E444DD1D5CD5813E12B7E11060019EBE188 +:20044000CD1E0ED5B7CA4904EB444DCD3B0AD1FA5D04CA5D0421030019224518C913EBCDCB +:200460001E0EEBCD0C0DEB222517215E03E3C90601CDC7062AAC1834F5CD9B0DF1C2F6042E +:20048000069DCDD80DCDBE0EDA6D03C39804AF322417CDBE0EDA9801EBCD4C0E232323C35F +:2004A0006204CD410E11FDFFCDAB0DE5CDBE0EDA9801EBCD0C0D444DE1702B712B3602CDB8 +:2004C0004C0E232323C36704CD410E3224172A45187EB7CAA401FE02CAE204110F0019C3CB +:2004E000D104235E235623224518EB7E3DC26204C35301CD410ECDE80DFE0DC2F6042B22B4 +:200500002517C9CD270DDA980179F6804FCD360DD2B001E506E0CDD80DCDC5060629CDD8A8 +:200520000DCD680E218308CD130ED2B001E1CDA40CCDE00DFE2CC0CDE80DC30305CD410EA3 +:20054000CDAF0F32CF18215308C3C201CD410E2A2517E52A4118222517CDE80DFE2CCA797E +:2005600005FE87CA79053DCAA605CDF604237E3DCAA605232323C35605CDC506CDE00DFE06 +:200580002CCA8705CD0C0D2A2517224118E1222517CDC80CDA9801CD9B0DCDE00DFE2CC0A6 +:2005A000CDE80DC34C05E1222517014452C3B3012ADA18232323224118C9CDE00DFE0DCAC0 +:2005C000B20FFE22CA0406FE9CCA4606FE25CA1106FE0DC8FE3BC8CDC506114B18CD9C0D5C +:2005E0003A2017FE38D4B20F214B18CD03100620CD770FCDE00DFE2CC2B20FCDE80DCDE00C +:200600000DC3C205CDE80DCD010E23222517C3F3053E0C32CA18CDE80DCDE80D21CA18FE86 +:2006200025CAF3050680FE5ACA40060601FE45CA4006CD8A12D29801D63007477EE6C177C7 +:200640007EB077C31906CDE80D06E0CDD80DCDC5060629CDD80DCD680E3A2017BBD2F30507 +:200660000620CD770FC35906CDE00DFE2CCAB906CDB20F063FCD770FCD330F116318D5CD0F +:20068000C80CDA9801D106001AFE2BCA9506FE2DC29606060113C5E5CDE711DABF06E12BD7 +:2006A000F177CDE00DFE2CC0CDE80D78FE2CCA7E06063FCD770FC37306CDE80DC3780601A4 +:2006C0004E49C3B301060021C509AF322117C5E5AF3223173A2317B7C2F506CDC80CD48557 +:2006E0000DD2F506CD250ED2F506CDE00DFEE021AD09CA7107CDE00DFEE0D21307FEC0D28E +:200700006207E13A2317B7CA9801F1212117BEC8C39801E61F2A23172DCA2E07FE05CA2C47 +:2007200007FE03C29801CDE80DC3D4063E09CD8307D11ABED20A07D5E5CDE80DE1E5060085 +:20074000CDCE06E1E57E2AAC18444DE601C259071105001922AC18545D21D406E323CD1E7A +:200760000EE9CDE80DE63F2A23172DCA9801CD8307E506E0CDD80DCDC5060629CDD80DE179 +:20078000C344074F060021AD09090909C9216318222517CDBE0E226118F52A25170E04118F +:2007A0006318D511D908E51A47131ABEC2B30723C3A907B7FAE507131AB7F2B707E1EEFFB5 +:2007C000C2A607D17EFE0DCAEE0712130C23FE22C2A2077EFE0DCAEE0712130C23FE22CA2C +:2007E000A207C3D307F1D17812130CC3A2073E0D1221601871F1C923E5CD1E0ECDD80E3EC9 +:20080000201213E123237EB7FA130812FE0DC813C30508E521D908BE23C217087EB7FA275B +:2008200008121323C31C08E1C3050852454144592250524F4752414D204C4F414445443F19 +:200840002022204552524F522220494E204C494E45202253544F5022FF001000000081163B +:2008600066670180833333007E198413017D275573007B2505210179FF0010000000815086 +:2008800000000180416667007F138889017E248016007C275573017A200000008115708061 +:2008A00000816366200080271743030303F602C5020001000000008A0309046F048E04A2C3 +:2008C00004C8044C05F3049E03BA05680603053D055301B005F604CD02804C4554814E459D +:2008E000585482494683474F544F84474F5355428552455455524E86524541448744415414 +:200900004188464F52895052494E54893A8A494E5055548B44494D8C53544F508D454E4499 +:200920008E524553544F52458F52454D90434C4541529F535445509E544F9D5448454E9C17 +:20094000544142A052554EA14C495354A24E554C4CA3534352A44D454DA554534156A65486 +:200960004C4F4144E028E22AE32BE52DE72FEF3E3DF03C3DF13C3EEA3D3EEB3D3CF43CF5E1 +:200980003DF63EC1414253C6494E54CC415247CD43414C4CCE524E44D253474ED353494EE6 +:2009A000C4535152D754414ED8434F53FF0F930A0FA30A0A740A06600A0F2E0B066A0A0FD6 +:2009C000810C0A7E0A0100000D940A04230A042F0A0F500C0F5D0C0F840B04230A042F0AEF +:2009E000041A0A0FA90A0FC40A04040A04110A04F8090F130B0F070BCD3B0ACA010AFA0DBE +:200A00000AAF12C9CD3B0ACA010AFA010A3EFF12C9CD3B0ACA0D0AC3010ACD3B0ACA010A96 +:200A2000C30D0ACD3B0ACA0D0AFA0D0AC3010ACD3B0ACA0D0AFA010AC30D0AD50B1B606974 +:200A40001A962313C2590A014B18CDAF133A4B18B7CA590A3A4A18073D3E01322117D1C9F4 +:200A60006069424BCD5813C3850A6069424BCDAF13C3850A6069424BCD2814C3850A6069E5 +:200A8000424BCD3115AF3221173AB316B7C82AAC183600C90AB7CA9E0A0B0AEE0102AF3214 +:200AA0002117C90BAF02C39E0ACD9E0A50590AB7C2B50A12C90B0AB7215E08CA7A0D2157B7 +:200AC00013C37A0DCD080C2AAC18545D015018CD780AF1F51FDAFE0A1155182AAC18CD7AE7 +:200AE0000D017708CDDA0BCD010C215518CD780AF147F107A82AAC182BD602F834C90197AA +:200B000008CDDA0BC3F00ACD010C21A108CD640AC3C40A2AAC18CD850DCD070B115A18CD72 +:200B20009C0DCDC40ACD010C215A18C3820A2AAC18115018CD7A0D2AAC187EB7C8D680FABF +:200B4000480B0FE67FC34F0B2F3C0FE67F2F3CC680772B7E01414EB7C2B3013E06F50155B5 +:200B6000181150182AAC18CD820A1155182AAC18444DCD640ACD010C219C08CD820AF13D3F +:200B8000C25D0BC9CD010C115F18215F18CD780A2AAC18368001FCFF09462323CDD50BCD6A +:200BA000D50BCDD50B2AAC1801FCFF097EE6F0C2CB0B2AAC187E3D32B916CD14160604CD51 +:200BC0005E16CD010CCD2A16C3A50B115F182AAC18CD7A0DC97E70472BC92AAC18EB6069E9 +:200BE000CD7A0DE5CD010C215018CD780ACD010CE1E5CD640AE101FAFF097E23B7F2E30B13 +:200C0000C92AAC18EB424BC92AAC182B46AF7760E3E52AAC18CD850DCD010C21A608CD78F4 +:200C20000ACD010CCD810C2AAC18CD850DCD680E7BF5CD010C21A108CD780A115018CD9CA1 +:200C40000DCD010C215018CD6E0AF1E603E1F5E9CD680EEB22D618114B18C3860DCD680EFB +:200C60002AD618EB01690CC5E911D018CDD80E3E0D1211D018214B18CDE711114B18C38645 +:200C80000D0AD681F2910CAF1605020B15C28A0CC9D605D0570B0B0AE6F00214C8AF0214AF +:200CA000C2960CC9E513D52100000E05CDF60DEB2AAE18E519CDC00D22AE18C1D1E1E57211 +:200CC0002B732B702B71E1C9CD5E0DD8CD2B0DCDE00DFEE0CADC0CCD360DB7C9CDE80D3EA6 +:200CE00080B14FCD360DE5110A00DCA40CCDC506CD680E0629CDD80DE12BCD130ED2AA01A5 +:200D00002B2BCD1E0E0E0513CDF60DC9CDE80DFE3BC8FE0DC298017E3DCA230D232323225C +:200D20002517C937C31F0DCD5E0DD8470E00CD6B0D3FD04FB7C92ADC1811F9FF7EB7CA508A +:200D40000DB8C24C0D2B7EB92BC8232319C33C0D702B7123EB192243181B1BEB37C9CDE075 +:200D60000DFE41D8FE5B3FD8C3750DCDE00DFE30D8FE3A3FD823222517C90E057E122B1B58 +:200D80000DC27C0DC9EB2AAC1801FBFF0922AC18EBCD7A0D3E01322317B7C9EB2AAC18E547 +:200DA0000105000922AC18E1C37A0D2A4518E519224518EB21A708CD130EDAA401E12BC912 +:200DC000EB21AE18CD130EDAD20D214318CD130EEBD8014F53C3B301CDE80DB8C8C39801B5 +:200DE000CDE80D2B222517C92A25177E23FE20CAEB0D222517C9190DC2F60DC90E0DC3033C +:200E00000E0E227E47B9C8FE0DCA9801CD770F23C3030E7B96237A9E2BC07B96B7C9F57E5B +:200E200023666FF1C92A2517EB214B18CDE711D81BEB222517114B18CD860DAF3C322317FF +:200E4000C93A2417B7C8014944C3B3012ADA1806004E79FE01CA620E23CD130E2BC809C3DE +:200E6000510E014E4CC3B3012AAC18444DE5CD810C214B18CD9B0DE14E2B7EB7C2AA01113D +:200E8000FCFF1911000079B7C80D237E0F0F0F0FCDA40EDAAA010DF07ECDA40EDAAA010DBB +:200EA000FA8A0EC9E53333626B29D829D819D829D8EB3B3BE1E60F835F7ACE0057C9CD6B12 +:200EC0000DD8110000C3CF0ECD6B0D626B3FD0D630CDA40ED2C80EC9AF01F0D8CDFD0E0114 +:200EE00018FCCDFD0E019CFFCDFD0E01F6FFCDFD0E01FFFFCDFD0EC03E301213C9D516FFE7 +:200F0000E533331409DA000F3B3BE142D1B0C83E30801213C9C5D5E50E01CD0500E67FFEFF +:200F200061DA2B0FFE7BD22B0FD620E1D1C147C9CDB20F2163180E49CD150FFE7FCA580F1E +:200F4000FE15CA300F77060AFE0DCA770F230DC2380F014C4CC3B301790607FE49CA630F41 +:200F60002B460CCD770FC3380FC5D5E50E0BCD0500E1D1C1E601C9C5D5E50E0258CD050051 +:200F8000E1D1C178FE0DC28D0FAFC3990FFE0ACA9D0FFE20D83A20173C322017C93A22171D +:200FA000B7C8C54F0600CD770F0DC2A60FC1C9CDB20F060DCD770F060AC3770FCD690FC8DD +:200FC000CD150FFE03CA4005C9E5EB3A2017B7C4B20FCD010ECD330F216318222517CDBE5A +:200FE0000EDA0001FE0DC20001D1226318216318CD130ED200012A63187E2F77BEC2000125 +:201000002F77C901FCFF09444D21BF183ACA1832C9181E033600230A571F1F1F1FE60F77E1 +:20102000237AE60F7723031DC217100A32B918AF7721C71877030AB7CAD811D680C24110F1 +:2010400034F247102F343C21C818775FFE0621C918DA58103E01B6777E1FD26B10E60FFE0C +:2010600006DA66103E05573CC3B110E60F5783FE0747DA7B107EE640C254103AC718B7C2E4 +:20108000861078C3B1107A93D2A2103ACA18B7F2D811E60ECAD8110F5F1D0E0121BE18C384 +:2010A0001311CA8B10C3B110061FA0FE07D83E07C9CDA8104F060021C0180922B2187EFE2F +:2010C00005DAD8102B347EB7CAD910FE0AC2D5103600C3C4102AB2182B3AC91817DAED105E +:2010E0007EB7C2ED102B0DFAD811C3E01021BF187EB7CA131106013AC718B7CA001106FF5C +:201100003AC818B7C20C1132C71806018032C8181C0C2B2379FE07C21B110D3AB9181FD28A +:201120002811CDD311C32B11CDDD113AC9181FDA66113AC718B7CA581179B7C24211CDD893 +:2011400011C9CDE211AFB3CA5111CDD8111DC24511CDC911C25111C9CDC9111DC258117980 +:20116000B7C8F8C34211CDC911CA7511CDE211CDC911C26F110645CD770F3AC718B7CA8B7F +:2011800011CDD3113AC8183CC39411062BCD770F3AC8183D0E641600CDBE11FE30CAA41123 +:2011A00014CD770F7B0E0ACDBE11FE30C2B31115C2B611CD770F7BC63047CD770FC9062FEB +:2011C0000491D2C011815F78C97EC63047CD770F230DC9062DC3770F0630C3770F0620C3CB +:2011E000770F062EC3770FE5D5EB2B22B01821B6180E09CDAB1211000021BA1822B418218F +:20120000FF11E5AF32C718CD7D12DA3412FE2ECA2512FE45CAB312473AB618E610C29112F4 +:20122000E1D1E137C9AFB2C22F12C6C0B35FC93E80B35FC9E60F4721B6183E30B677AFB098 +:20124000C24C12B2C24C12B35FC81CC97B17DA52121C7B32B818147AE67FFE07D0AFB2FA51 +:201260006F12F68057782AB4180707070777C9E67F57782AB418B67723C1C3FC112AB01859 +:20128000237EFE20CA801222B018FE3AD0FE303FC9110000C5CD0E13C1D1D1D10E0521BE21 +:2012A00018CD7A0D2AB018EB13B7C9AF77230DC2AC12C9CD7D12DAD812FEE3CAD212FE2BDB +:2012C000CAD212FE2DCACD12FEE5C20A133E0132C718CD7D12D20A13CDDE12C39412EB21FD +:2012E00000001ACD8A12D2F912D630444D292909294F06000913C3E212EB4722B0187AB7FE +:20130000C20A137B17DA0A131FC9C1C32012EB3ABA18B7CA1B13CD1F13C68032BE18C93AD1 +:20132000B8185FE63F473AC718B7CA4113243E40A3CA3C137D68CD4D132F3CC97D2F3C8018 +:20134000C93E40A3CA4C137845C33D137885F0E1C30A131000000181C5CDD2150E001BEBDD +:201360003AB816AE47EB1A1BA932B81621BA167EB7237ECA7A1307070707C6B0781FDABEC8 +:201380001317CD9D13D294130604CD381621B91634CA8416C1CD2A16C9E1C3941321B716B0 +:2013A00006031A8E27772B1B05C2A213D034C9C5CDD2153AB816EE0132B816C35E13173F55 +:2013C000CD121421B816DADD137EEE01772B06033E9A9EC60027772B053FC2D01321B5166F +:2013E0000103007EB7C2F6132304040DC2E313AF32B916C39413FE10D2FC130421B9167E7E +:2014000090CA8A16DA8A167778070747CD5E16C3941321B71606033E99CE0096EB8627EBB4 +:20142000772B1B05C21714C9C57EB7CA42141AB7CA421486DA3D14F28A16C34014FA84163B +:20144000D68032B9161B2B1AAE2B1BE521B816772BAF0605772B05C254143AB916B7CA99BD +:20146000130E032195161A772B1B0DC26614712B06FA1104004B19EB1904F2AE141A8F27BB +:20148000771B2B0DC27D1404C272142311A2160E04417E1223130DC292142199161B1A8ED6 +:2014A00027121B2B05C29E1406F9EBC37214EB233604C121B21635CAE4140A0BC52BEB87A1 +:2014C000DACE14CADC1421FCFF19EBC3BF144FB7CD9D131A862777791BC3BF140608CD38E2 +:2014E00016C3B2143AB516E6F0CAF5147AE6F021B716C30615060421B91635CA8A16CD5EBF +:20150000167A0F0F0F0FFE50DA2A153CE60F0E038E27773E002B0DC21015D29413233610EB +:2015200021B91634C29413C38416E60F8677C39413C57EB7CA84161AB7CA8F1696DA4615FC +:20154000FA8416C34915F28A16C681329616EBD5CD1416D1EB3AB8162BAE329516EB1B01E7 +:2015600092162E06C5E50E003721B71606033E99CE00EB96EB8627772B1B05C26E157E3F27 +:20158000DE00771F21030019EB0C17D26815B7CD9D1321030019EBC50604CD5E16C10DE122 +:2015A00061C17DC2B515FE06C2B51521961635CC8A16C362151F7CD2C5150A0707070784D7 +:2015C0000203C3C615022DC26415219616C1CD2D16C91A960E00D2DD150CEB2F3C471A3220 +:2015E000B91678FE06DAEA153E06070747E60432BA16C5D5CD14163E2890FE28CA0E16E6C1 +:20160000F81F1F1F835F7ACE00571A32BB16CD3816D1C1C911B8160E042B7E122B1B0DC2A0 +:201620001A16AF121B1232BB16C921B9161E057E020B2B1DC22F16C90E0421B41678D608B7 +:20164000D2511605F8B77E1F77230DC24616C3381647AF56777A230DC25316C338160E046F +:2016600021B71678D608D2771605F8B77E17772B0DC26C16C35E1647AF56777A2B0DC279A4 +:1216800016C35E16015046C3B3013EFF32B3163333C996 +:00000001FF diff --git a/z80as/hex2bin.c b/z80as/hex2bin.c new file mode 100644 index 0000000..526af8c --- /dev/null +++ b/z80as/hex2bin.c @@ -0,0 +1,218 @@ +/************************************************************* + * Convert Intel HEX file to BIN format + * Bobbi + * Oct 8 2019 + *************************************************************/ + +#include +#include +#include + +#undef DEBUG +#define FNAMELEN 15 + +/* Convert hex character to value + * Returns value or 127 on error */ +char hexchar(char c) { + c = toupper(c); + if ((c >= '0') && (c <= '9')) return c - '0'; + if ((c >= 'A') && (c <= 'F')) return c - 'A' + 10; + return 127; +} + +/* Do the actual work of converting a HEX file to a BIN file + * Returns 0 on success, 1 on error, 2 on EOF */ +char hextobin(FILE *in, FILE *out) { + int c, datalen, addr, rectype, i; + char byte; + static int endaddr = -1; + + /* Each line starts with a colon */ + c = fgetc(in); + if (c == -1) return 1; + if (c != ':') { + puts("Expected :"); + return 1; + } + + /* Then two hex digits representing the data length */ + c = fgetc(in); + if (c == -1) return 1; + c = hexchar(c); + if (c == 127) return 1; + datalen = c * 16; + c = fgetc(in); + if (c == -1) return 1; + c = hexchar(c); + if (c == 127) return 1; + datalen += c; + +#ifdef DEBUG + printf("datalen=%d\n", datalen); +#endif + + /* Then four hex digits representing the address */ + c = fgetc(in); + if (c == -1) return 1; + c = hexchar(c); + if (c == 127) return 1; + addr = c * 16 * 16 * 16; + c = fgetc(in); + if (c == -1) return 1; + c = hexchar(c); + if (c == 127) return 1; + addr += c * 16 * 16; + c = fgetc(in); + if (c == -1) return 1; + c = hexchar(c); + if (c == 127) return 1; + addr += c * 16; + c = fgetc(in); + if (c == -1) return 1; + c = hexchar(c); + if (c == 127) return 1; + addr += c; + +#ifdef DEBUG + printf("addr=%d\n", addr); +#endif + + /* Then two more hex digits representing the record type */ + c = fgetc(in); + if (c == -1) return 1; + c = hexchar(c); + if (c == 127) return 1; + rectype = c * 16; + c = fgetc(in); + if (c == -1) return 1; + c = hexchar(c); + if (c == 127) return 1; + rectype += c; + +#ifdef DEBUG + printf("rectype=%d\n", rectype); +#endif + + /* We only support record type 0 (data) and 1 (EOF) */ + if ((rectype != 0) && (rectype != 1)) { + printf("Unsupported record type %d\n", rectype); + return 1; + } + + /* Handle EOF record */ + if (rectype == 1) { + return 2; + } + + /* Initialize endaddr on first call */ + if (endaddr == -1) endaddr = addr; + + /* Check for overlapping addresses */ + if (addr < endaddr) { + puts("Overlap in data!"); + return 1; + } + + /* Zero fill gaps */ + if (addr > endaddr) { + for (i = 0; i < addr - endaddr; ++i) { + puts("FILL"); + fputc(0, out); + } + } + + endaddr = addr + datalen; + + /* Now datalen bytes stored as 2*datalen hex digits */ + for (i = 0; i < datalen; ++i) { + c = fgetc(in); + if (c == -1) return 1; + c = hexchar(c); + if (c == 127) return 1; + byte = c * 16; + c = fgetc(in); + if (c == -1) return 1; + c = hexchar(c); + if (c == 127) return 1; + byte += c; + fputc(byte, out); + } + + /* Finally two checksum hex digits (ignored) */ + c = fgetc(in); + if (c == -1) return 1; + c = hexchar(c); + if (c == 127) return 1; + c = fgetc(in); + if (c == -1) return 1; + c = hexchar(c); + if (c == 127) return 1; + + /* Now eat the newline */ + c = fgetc(in); + + return 0; +} + +int main(argc, argv) +int argc; +char *argv[]; +{ + char len, ret; + char inname[FNAMELEN+1], outname[FNAMELEN+1]; + FILE *in, *out; + + if (argc != 2) { + puts("usage: hex2bin hexfile"); + return 1; + } + + /* Strip off .HEX extension, if provided */ + len = strlen(argv[1]); + if (len > 4) { + if (((argv[1][len-1] == 'x') && (argv[1][len-2] == 'e') && + (argv[1][len-3] == 'h') && (argv[1][len-4] == '.')) || + ((argv[1][len-1] == 'X') && (argv[1][len-2] == 'E') && + (argv[1][len-3] == 'H') && (argv[1][len-4] == '.'))) + argv[1][len-4] = '\0'; + } + + strcpy(inname, argv[1]); + strcat(inname, ".hex"); + strcpy(outname, argv[1]); + strcat(outname, ".bin"); + printf("%s -> %s\n", inname, outname); + + in = fopen(inname, "r"); + if (!in) { + printf("Can't open %s for reading\n", inname); + goto done; + } + + out = fopen(outname, "w"); + if (!out) { + printf("Can't open %s for writing\n", outname); + goto done; + } + + while (!feof(in)) { + ret = hextobin(in, out); + putchar('.'); + if (ret == 1) { + printf("Error parsing %s\n", inname); + goto done; + } + if (ret == 2) { + puts("Done."); + goto done; + } + } + return 0; + +done: + if (in) fclose(in); + if (out) fclose(out); + return 0; +} + +