Initial commit of z80as (and hex2bin) source.

This commit is contained in:
Bobbi Webber-Manners 2019-10-12 19:16:41 -04:00
parent fb8cfb89f0
commit 109a8caafa
13 changed files with 2259 additions and 0 deletions

27
z80as/LICENSE Normal file
View File

@ -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.

16
z80as/Makefile Normal file
View File

@ -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

168
z80as/as.h Normal file
View File

@ -0,0 +1,168 @@
/*
* Z-80 assembler.
* Header file, used by all
* parts of the assembler.
*/
#include <stdio.h>
#include <ctype.h>
#include <setjmp.h>
#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();

131
z80as/as0.c Normal file
View File

@ -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; i<argc; ++i) {
p = argv[i];
if (*p == '-') {
while ((c = *++p) != 0) {
switch (c) {
case 'l':
++lflag;
break;
default:
fprintf(stderr, "Bad option %c\n", c);
exit(BAD);
}
}
} else if (ifn == NULL)
ifn = p;
else {
fprintf(stderr, "Too many source files\n");
exit(BAD);
}
}
if (ifn == NULL) {
fprintf(stderr, "No source file\n");
exit(BAD);
}
if ((ifp=fopen(ifn, "r")) == NULL) {
fprintf(stderr, "%s: cannot open\n", ifn);
exit(BAD);
}
mkname(fn, ifn, "hex");
if ((ofp=fopen(fn, "w")) == NULL) {
fprintf(stderr, "%s: cannot create\n", fn);
exit(BAD);
}
if (lflag != 0) {
mkname(fn, ifn, "lis");
if ((lfp=fopen(fn, "w")) == NULL) {
fprintf(stderr, "%s: cannot create\n", fn);
exit(BAD);
}
}
syminit();
for (pass=0; pass<2; ++pass) {
line = 0;
dot = 0;
fseek(ifp, 0L, 0);
while (fgets(ib, NINPUT, ifp) != NULL) {
++line;
cp = &cb[0];
ep = &eb[0];
ip = &ib[0];
if (setjmp(env) == 0)
asmline();
if (pass != 0)
list();
}
}
outeof();
exit(GOOD);
}
/*
* 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.
*/
mkname(dfn, sfn, dft)
char *dfn;
char *sfn;
char *dft;
{
register char *p1;
register char *p2;
register int c;
p1 = sfn;
while (*p1 != 0)
++p1;
#ifdef vax
while (p1!=sfn && p1[-1]!=':' && p1[-1]!=']')
--p1;
#else
while (p1!=sfn && p1[-1]!='/')
--p1;
#endif
p2 = dfn;
while ((c = *p1++)!=0 && c!='.')
*p2++ = c;
*p2++ = '.';
p1 = dft;
while (*p2++ = *p1++)
;
}

686
z80as/as1.c Normal file
View File

@ -0,0 +1,686 @@
/*
* Z-80 assembler.
* Assemble one line of input.
* Knows all the dirt.
*/
#include "as.h"
#define OPDJNZ 0x10 /* Opcode: djnz */
#define OPADD 0x80 /* Opcode: add */
#define OPDAD 0x09 /* Opcode: dad */
#define OPADC 0x88 /* Opcode: adc */
#define OPADCW 0x4A /* Opcode: adc hl */
#define OPSBCW 0x42 /* Opcode: sbc hl */
#define OPSUBI 0xC6 /* Opcode: make immediate */
#define OPXCHG 0xEB /* Opcode: xchg */
#define OPXTHL 0xE3 /* Opcode: xthl */
#define OPEXAF 0x08 /* Opcode: ex af,af' */
#define OPRST 0xC7 /* Opcode: rst 0 */
#define OPINCRP 0x03 /* Opcode: inc rp */
#define OPDECRP 0x0B /* Opcode: dec rp */
#define OPINC 0x04 /* Opcode: inc */
#define OPIIN 0x40 /* Opcode: indirect in */
#define OPIOUT 0x41 /* Opcode: indirect out */
#define OPIN 0xDB /* Opcode: in */
#define OPIM 0x46 /* Opcode: im */
#define OPPCHL 0xE9 /* Opcode: jp (hl) */
#define OPJP 0xC3 /* Opcode: jp cc base */
#define OPJR 0x20 /* Opcode: jr cc base */
#define OPRET 0xC0 /* Opcode: ret cc base */
/*
* Assemble one line.
* The line in in "ib", the "ip"
* scans along it. The code is written
* right out, and also stashed in the
* "cb" for the listing.
*/
asmline()
{
register SYM *sp;
register int c;
register int opcode;
register int disp;
register int reg;
register int srcreg;
register int cc;
register VALUE value;
register int delim;
register SYM *sp1;
char id[NCPS];
char id1[NCPS];
ADDR a1;
ADDR a2;
laddr = dot;
lmode = SLIST;
loop:
if ((c=getnb())=='\n' || c==';')
return;
if (isalpha(c) == 0)
qerr();
getid(id, c);
if ((c=getnb()) == ':') {
sp = lookup(id, uhash, 1);
if (pass == 0) {
if ((sp->s_type&TMMODE) != TNEW
&& (sp->s_type&TMASG) == 0)
sp->s_type |= TMMDF;
sp->s_type &= ~TMMODE;
sp->s_type |= TUSER;
sp->s_value = dot;
} else {
if ((sp->s_type&TMMDF) != 0)
err('m');
if (sp->s_value != dot)
err('p');
}
lmode = ALIST;
goto loop;
}
/*
* If the first token is an
* id and not an operation code,
* assume that it is the name in front
* of an "equ" assembler directive.
*/
if ((sp=lookup(id, phash, 0)) == NULL) {
getid(id1, c);
if ((sp1=lookup(id1, phash, 0)) == NULL
|| (sp1->s_type&TMMODE) != TEQU) {
err('o');
return;
}
getaddr(&a1);
istuser(&a1);
sp = lookup(id, uhash, 1);
if ((sp->s_type&TMMODE) != TNEW
&& (sp->s_type&TMASG) == 0)
err('m');
sp->s_type &= ~TMMODE;
sp->s_type |= TUSER|TMASG;
sp->s_value = a1.a_value;
laddr = a1.a_value;
lmode = ALIST;
goto loop;
}
unget(c);
lmode = BLIST;
opcode = sp->s_value;
switch (sp->s_type&TMMODE) {
case TORG:
getaddr(&a1);
istuser(&a1);
lmode = ALIST;
laddr = dot = a1.a_value;
break;
case TDEFB:
do {
getaddr(&a1);
istuser(&a1);
outab(a1.a_value);
} while ((c=getnb()) == ',');
unget(c);
break;
case TDEFW:
lmode = WLIST;
do {
getaddr(&a1);
istuser(&a1);
outaw(a1.a_value);
} while ((c=getnb()) == ',');
unget(c);
break;
case TDEFM:
if ((delim=getnb()) == '\n')
qerr();
while ((c=get()) != delim) {
if (c == '\n')
qerr();
outab(c);
}
break;
case TDEFS:
laddr = dot;
lmode = ALIST;
getaddr(&a1);
istuser(&a1);
dot += a1.a_value;
break;
case TNOP:
if ((opcode&0xFF00) != 0)
outab(opcode >> 8);
outab(opcode);
break;
case TRST:
getaddr(&a1);
istuser(&a1);
if (a1.a_value < 8) {
outab(OPRST|(a1.a_value<<3));
break;
}
aerr();
break;
case TREL:
getaddr(&a1);
if ((cc=ccfetch(&a1)) >= 0) {
if (opcode==OPDJNZ || cc>=CPO)
aerr();
opcode = OPJR | (cc<<3);
comma();
getaddr(&a1);
}
istuser(&a1);
disp = a1.a_value-dot-2;
if (disp<-128 || disp>127)
aerr();
outab(opcode);
outab(disp);
break;
case TRET:
unget(c = getnb());
if (c!='\n' && c!=';') {
getaddr(&a1);
if ((cc=ccfetch(&a1)) < 0)
aerr();
opcode = OPRET | (cc<<3);
}
outab(opcode);
break;
case TJMP:
getaddr(&a1);
if ((cc=ccfetch(&a1)) >= 0) {
opcode = (opcode&0x00C6) | (cc<<3);
comma();
getaddr(&a1);
}
if ((a1.a_type&TMMODE) == TBR) {
reg = a1.a_type&TMREG;
if (reg==M || reg==IX || reg==IY) {
if (opcode != OPJP)
aerr();
outop(OPPCHL, &a1);
break;
}
}
istuser(&a1);
outab(opcode);
outaw(a1.a_value);
break;
case TPUSH:
getaddr(&a1);
if ((a1.a_type&TMMODE) == TWR) {
reg = a1.a_type&TMREG;
switch (reg) {
case IX:
outab(0xDD);
reg = HL;
break;
case IY:
outab(0xFD);
reg = HL;
break;
case AF:
reg = SP;
break;
case SP:
case AFPRIME:
aerr();
}
outab(opcode|(reg<<4));
break;
}
aerr();
break;
case TIM:
getaddr(&a1);
istuser(&a1);
if ((value=a1.a_value) > 2)
aerr();
else if (value != 0)
++value;
outab(0xED);
outab(OPIM|(value<<3));
break;
case TIO:
getaddr(opcode==OPIN ? &a1 : &a2);
comma();
getaddr(opcode==OPIN ? &a2 : &a1);
if (a1.a_type==(TBR|A) && a2.a_type==(TUSER|TMINDIR)) {
outab(opcode);
outab(a2.a_value);
break;
}
if ((a1.a_type&TMMODE)==TBR && a2.a_type==(TBR|TMINDIR|C)) {
reg = a1.a_type&TMREG;
if (reg==M || reg==IX || reg==IY)
aerr();
outab(0xED);
if (opcode == OPIN)
opcode = OPIIN; else
opcode = OPIOUT;
outab(opcode|(reg<<3));
break;
}
aerr();
break;
case TBIT:
getaddr(&a1);
comma();
getaddr(&a2);
if ((a1.a_type&TMMODE) == TUSER
&& a1.a_value < 8
&& (a2.a_type&TMMODE) == TBR) {
if ((reg=a2.a_type&TMREG)==IX || reg==IY)
reg = M;
outop(opcode|(a1.a_value<<3)|reg, &a2);
break;
}
aerr();
break;
case TSHR:
getaddr(&a1);
if ((a1.a_type&TMMODE) == TBR) {
if ((reg=a1.a_type&TMREG)==IX || reg==IY)
reg = M;
outop(opcode|reg, &a1);
break;
}
aerr();
case TINC:
getaddr(&a1);
if ((a1.a_type&TMMODE) == TWR) {
reg = a1.a_type&TMREG;
switch (reg) {
case IX:
outab(0xDD);
reg = HL;
break;
case IY:
outab(0xFD);
reg = HL;
break;
case AF:
case AFPRIME:
aerr();
}
if (opcode == OPINC)
opcode = OPINCRP; else
opcode = OPDECRP;
outab(opcode|(reg<<4));
break;
}
if ((a1.a_type&TMMODE) == TBR) {
if ((reg=a1.a_type&TMREG)==IX || reg==IY)
reg = M;
outop(opcode|(reg<<3), &a1);
break;
}
aerr();
break;
case TEX:
getaddr(&a1);
comma();
getaddr(&a2);
if (a1.a_type==(TWR|AF) && a2.a_type==(TWR|AFPRIME)) {
outab(OPEXAF);
break;
}
if (a1.a_type == (TWR|DE))
opcode = OPXCHG;
else if (a1.a_type == (TWR|TMINDIR|SP))
opcode = OPXTHL;
else
aerr();
if (a2.a_type == (TWR|HL))
outab(opcode);
else if (a2.a_type == (TWR|IX)) {
outab(0xDD);
outab(opcode);
} else if (a2.a_type == (TWR|IY)) {
outab(0xFD);
outab(opcode);
} else
aerr();
break;
case TSUB:
getaddr(&a1);
if (a1.a_type == TUSER) {
outab(opcode | OPSUBI);
outab(a1.a_value);
break;
}
if ((a1.a_type&TMMODE) == TBR) {
if ((reg=a1.a_type&TMREG)==IX || reg==IY)
reg = M;
outop(opcode|reg, &a1);
break;
}
aerr();
break;
case TADD:
getaddr(&a1);
comma();
getaddr(&a2);
if (a1.a_type == (TBR|A)) {
if (a2.a_type == TUSER) {
outab(opcode | OPSUBI);
outab(a2.a_value);
break;
}
if ((a2.a_type&TMMODE) == TBR) {
if ((reg=a2.a_type&TMREG)==IX || reg==IY)
reg = M;
outop(opcode|reg, &a1);
break;
}
}
if ((a1.a_type&TMMODE) == TWR) {
switch(reg = a1.a_type&TMREG) {
case IX:
if (opcode != OPADD)
aerr();
outab(0xDD);
opcode = OPDAD;
srcreg = IX;
break;
case IY:
if (opcode != OPADD)
aerr();
outab(0xFD);
opcode = OPDAD;
srcreg = IY;
break;
case HL:
if (opcode == OPADD)
opcode = OPDAD;
else {
outab(0xED);
if (opcode == OPADC)
opcode = OPADCW;
else
opcode = OPSBCW;
}
srcreg = HL;
break;
default:
aerr();
}
if ((a2.a_type&TMMODE) == TWR) {
reg = a2.a_type&TMREG;
if (reg==BC || reg==DE || reg==SP) {
outab(opcode|(reg<<4));
break;
}
if (reg == srcreg) {
outab(opcode|(HL<<4));
break;
}
}
}
aerr();
break;
case TLD:
asmld();
break;
default:
err('o');
}
goto loop;
}
/*
* Handle the dreaded "ld" instruction,
* with its dozens of forms, formats and special
* encodings. The "getldaddr" routine performs most
* of the special stuff for index registers and for
* indexing. This layer just screens out the many
* cases, and emits the correct bytes.
*/
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);
}

244
z80as/as2.c Normal file
View File

@ -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;
}

260
z80as/as3.c Normal file
View File

@ -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();
}

121
z80as/as4.c Normal file
View File

@ -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);
}

77
z80as/as5.c Normal file
View File

@ -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; i<nb; ++i) {
d = (*wp++) & 0xFF;
if (lmode == BLIST)
fprintf(lfp, " %02x", d);
else {
d |= *wp++ << 8;
fprintf(lfp, " %04x", d);
++i;
}
}
if (f != 0) {
while (i < 8) {
fprintf(lfp, " ");
++i;
}
}
}

137
z80as/as6.c Normal file
View File

@ -0,0 +1,137 @@
/*
* Z-80 assembler.
* Basic symbol tables.
* Contain all of the instructions
* and register names.
*/
#include "as.h"
/*
* This array of symbol nodes
* make up the basic symbol table.
* The "syminit" routine links these
* nodes into the builtin symbol hash
* table at start-up time.
*/
SYM sym[] = {
0, "b", TBR, B,
0, "c", TBR, C,
0, "d", TBR, D,
0, "e", TBR, E,
0, "h", TBR, H,
0, "l", TBR, L,
0, "a", TBR, A,
0, "bc", TWR, BC,
0, "de", TWR, DE,
0, "hl", TWR, HL,
0, "sp", TWR, SP,
0, "af", TWR, AF,
0, "af'", TWR, AFPRIME,
0, "ix", TWR, IX,
0, "iy", TWR, IY,
0, "i", TSR, I,
0, "r", TSR, R,
0, "nz", TCC, CNZ,
0, "z", TCC, CZ,
0, "nc", TCC, CNC,
0, "po", TCC, CPO,
0, "pe", TCC, CPE,
0, "p", TCC, CP,
0, "m", TCC, CM,
0, "defb", TDEFB, XXXX,
0, "defw", TDEFW, XXXX,
0, "defs", TDEFS, XXXX,
0, "defm", TDEFM, XXXX,
0, "org", TORG, XXXX,
0, "equ", TEQU, XXXX,
0, "cond", TCOND, XXXX,
0, "endc", TENDC, XXXX,
0, "nop", TNOP, 0x0000,
0, "rlca", TNOP, 0x0007,
0, "rrca", TNOP, 0x000F,
0, "rla", TNOP, 0x0017,
0, "rra", TNOP, 0x001F,
0, "daa", TNOP, 0x0027,
0, "cpl", TNOP, 0x002F,
0, "scf", TNOP, 0x0037,
0, "ccf", TNOP, 0x003F,
0, "halt", TNOP, 0x0076,
0, "exx", TNOP, 0x00D9,
0, "di", TNOP, 0x00F3,
0, "ei", TNOP, 0x00FB,
0, "neg", TNOP, 0xED44,
0, "retn", TNOP, 0xED45,
0, "reti", TNOP, 0xED4D,
0, "rrd", TNOP, 0xED67,
0, "rld", TNOP, 0xED6F,
0, "ldi", TNOP, 0xEDA0,
0, "cpi", TNOP, 0xEDA1,
0, "ini", TNOP, 0xEDA2,
0, "outi", TNOP, 0xEDA3,
0, "ldd", TNOP, 0xEDA8,
0, "cpd", TNOP, 0xEDA9,
0, "ind", TNOP, 0xEDAA,
0, "outd", TNOP, 0xEDAB,
0, "ldir", TNOP, 0xEDB0,
0, "cpir", TNOP, 0xEDB1,
0, "inir", TNOP, 0xEDB2,
0, "otir", TNOP, 0xEDB3,
0, "lddr", TNOP, 0xEDB8,
0, "cpdr", TNOP, 0xEDB9,
0, "indr", TNOP, 0xEDBA,
0, "otdr", TNOP, 0xEDBB,
0, "rst", TRST, XXXX,
0, "djnz", TREL, 0x0010,
0, "jr", TREL, 0x0018,
0, "ret", TRET, 0x00C9,
0, "call", TJMP, 0x00CD,
0, "jp", TJMP, 0x00C3,
0, "push", TPUSH, 0x00C5,
0, "pop", TPUSH, 0x00C1,
0, "im", TIM, XXXX,
0, "in", TIO, 0x00DB,
0, "out", TIO, 0x00D3,
0, "bit", TBIT, 0xCB40,
0, "res", TBIT, 0xCB80,
0, "set", TBIT, 0xCBC0,
0, "rlc", TSHR, 0xCB00,
0, "rrc", TSHR, 0xCB08,
0, "rl", TSHR, 0xCB10,
0, "rr", TSHR, 0xCB18,
0, "sla", TSHR, 0xCB20,
0, "sra", TSHR, 0xCB28,
0, "srl", TSHR, 0xCB38,
0, "inc", TINC, 0x0004,
0, "dec", TINC, 0x0005,
0, "ex", TEX, XXXX,
0, "add", TADD, 0x0080,
0, "adc", TADD, 0x0088,
0, "sbc", TADD, 0x0098,
0, "sub", TSUB, 0x0090,
0, "and", TSUB, 0x00A0,
0, "xor", TSUB, 0x00A8,
0, "or", TSUB, 0x00B0,
0, "cp", TSUB, 0x00B8,
0, "ld", TLD, XXXX
};
/*
* Set up the symbol table.
* Sweep through the initializations
* of the "phash", and link them into the
* buckets. Because it is here, a
* "sizeof" works.
*/
syminit()
{
register SYM *sp;
register int hash;
sp = &sym[0];
while (sp < &sym[sizeof(sym)/sizeof(SYM)]) {
hash = symhash(sp->s_id);
sp->s_fp = phash[hash];
phash[hash] = sp;
++sp;
}
}

BIN
z80as/basic.bin Normal file

Binary file not shown.

174
z80as/basic.hex Normal file
View File

@ -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

218
z80as/hex2bin.c Normal file
View File

@ -0,0 +1,218 @@
/*************************************************************
* Convert Intel HEX file to BIN format
* Bobbi
* Oct 8 2019
*************************************************************/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#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;
}