363 lines
9.9 KiB
C
363 lines
9.9 KiB
C
/* ===========================================================================
|
|
* uz80as, an assembler for the Zilog Z80 and several other microprocessors.
|
|
*
|
|
* Zilog Z80 CPU.
|
|
* ===========================================================================
|
|
*/
|
|
|
|
#include "pp.h"
|
|
#include "err.h"
|
|
#include "options.h"
|
|
#include "uz80as.h"
|
|
#include <stddef.h>
|
|
|
|
/* pat:
|
|
* a: expr
|
|
* b: B,C,D,E,H,L,A
|
|
* c: IX,IY (must be followed by + or -)
|
|
* d: BC,DE,HL,SP
|
|
* e: IX,IY
|
|
* f: BC,DE,HL,AF
|
|
* g: ADD,ADC,SUB,SBC,AND,XOR,OR,CP
|
|
* h: INC,DEC
|
|
* i: BC,DE,IX,SP
|
|
* j: BC,DE,IY,SP
|
|
* k: RLC,RRC,RL,RR,SLA,SRA,SRL
|
|
* l: BIT,RES,SET
|
|
* m: NZ,Z,NC,C,PO,PE,P,M
|
|
* n: NZ,Z,NC,C
|
|
* o: *
|
|
* p: B,C,D,E,IXH,IXL,A
|
|
* q: B,C,D,E,IYH,IYL,A
|
|
*
|
|
* gen:
|
|
* .: output lastbyte
|
|
* b: (op << 3) | lastbyte
|
|
* c: op | lastbyte
|
|
* d: lastbyte = op as 8 bit value
|
|
* e: output op as word (no '.' should follow)
|
|
* f: (op << 4) | lastbyte
|
|
* g: (op << 6) | lastbyte
|
|
* h: *
|
|
* i: relative jump to op
|
|
* j: possible value to RST
|
|
* k: possible value to IM
|
|
* m: check arithmetic used with A register
|
|
* n: check arithmetic used without A register
|
|
*/
|
|
|
|
static const struct matchtab s_matchtab_z80[] = {
|
|
{ "LD b,b", "40b0c1.", 3, 0 },
|
|
{ "LD p,p", "DD.40b0c1.", 1, 1 },
|
|
{ "LD q,q", "FD.40b0c1.", 1, 1 },
|
|
{ "LD b,(HL)", "46b0.", 3, 0 },
|
|
{ "LD b,(e)", "d1.46b0.00.", 3, 0, "ii" },
|
|
{ "LD b,(ca)", "d1.46b0.d2.", 3, 0, "ii" },
|
|
{ "LD A,I", "ED.57.", 3, 0 },
|
|
{ "LD A,R", "ED.5F.", 3, 0 },
|
|
{ "LD A,(BC)", "0A.", 3, 0 },
|
|
{ "LD A,(DE)", "1A.", 3, 0 },
|
|
{ "LD A,(a)", "3A.e0", 3, 0 },
|
|
{ "LD b,a", "06b0.d1.", 3, 0, "e8" },
|
|
{ "LD p,a", "DD.06b0.d1.", 1, 1, "e8" },
|
|
{ "LD q,a", "FD.06b0.d1.", 1, 1, "e8" },
|
|
{ "LD I,A", "ED.47.", 3, 0 },
|
|
{ "LD R,A", "ED.4F.", 3, 0 },
|
|
{ "LD SP,HL", "F9.", 3, 0 },
|
|
{ "LD SP,e", "d0.F9.", 3, 0 },
|
|
{ "LD HL,(a)", "2A.e0", 3, 0 },
|
|
{ "LD d,(a)", "ED.4Bf0.e1", 3, 0 },
|
|
{ "LD d,a", "01f0.e1", 3, 0 },
|
|
{ "LD e,(a)", "d0.2A.e1", 3, 0 },
|
|
{ "LD e,a", "d0.21.e1", 3, 0 },
|
|
{ "LD (HL),b", "70c0.", 3, 0 },
|
|
{ "LD (HL),a", "36.d0.", 3, 0, "e8" },
|
|
{ "LD (BC),A", "02.", 3, 0 },
|
|
{ "LD (DE),A", "12.", 3, 0 },
|
|
{ "LD (e),b", "d0.70c1.00.", 3, 0, "ii" },
|
|
{ "LD (ca),b", "d0.70c2.d1.", 3, 0, "ii" },
|
|
{ "LD (e),a", "d0.36.00.d1.", 3, 0, "iie8" },
|
|
{ "LD (ca),a", "d0.36.d1.d2.", 3, 0, "iie8" },
|
|
{ "LD (a),A", "32.e0", 3, 0 },
|
|
{ "LD (a),HL", "22.e0", 3, 0 },
|
|
{ "LD (a),d", "ED.43f1.e0", 3, 0 },
|
|
{ "LD (a),e", "d1.22.e0", 3, 0 },
|
|
{ "PUSH f", "C5f0.", 3, 0 },
|
|
{ "PUSH e", "d0.E5.", 3, 0 },
|
|
{ "POP f", "C1f0.", 3, 0 },
|
|
{ "POP e", "d0.E1.", 3, 0 },
|
|
{ "EX DE,HL", "EB.", 3, 0 },
|
|
{ "EX AF,AF'", "08.", 3, 0 },
|
|
{ "EX (SP),HL", "E3.", 3, 0 },
|
|
{ "EX (SP),e", "d0.E3.", 3, 0 },
|
|
{ "EXX", "D9.", 3, 0 },
|
|
{ "LDI", "ED.A0.", 3, 0 },
|
|
{ "LDIR", "ED.B0.", 3, 0 },
|
|
{ "LDD", "ED.A8.", 3, 0 },
|
|
{ "LDDR", "ED.B8.", 3, 0 },
|
|
{ "CPI", "ED.A1.", 3, 0 },
|
|
{ "CPIR", "ED.B1.", 3, 0 },
|
|
{ "CPD", "ED.A9.", 3, 0 },
|
|
{ "CPDR", "ED.B9.", 3, 0 },
|
|
{ "ADD HL,d", "09f0.", 3, 0 },
|
|
{ "ADD IX,i", "DD.09f0.", 3, 0 },
|
|
{ "ADD IY,j", "FD.09f0.", 3, 0 },
|
|
{ "ADC HL,d", "ED.4Af0.", 3, 0 },
|
|
{ "SBC HL,d", "ED.42f0.", 3, 0 },
|
|
{ "g A,b", "m080b0c1.", 3, 0 },
|
|
{ "g A,p", "DD.m080b0c1.", 1, 1 },
|
|
{ "g A,q", "FD.m080b0c1.", 1, 1 },
|
|
{ "g A,(HL)", "m086b0.", 3, 0 },
|
|
{ "g A,(ca)", "m0d1.86b0.d2.", 3, 0, "ii" },
|
|
{ "g A,a", "m0C6b0.d1.", 3, 0, "e8" },
|
|
{ "g b", "n080b0c1.", 3, 0 },
|
|
{ "g p", "DD.n080b0c1.", 1, 1 },
|
|
{ "g q", "FD.n080b0c1.", 1, 1 },
|
|
{ "g (HL)", "n086b0.", 3, 0 },
|
|
{ "g (ca)", "n0d1.86b0.d2.", 3, 0, "ii" },
|
|
{ "g a", "n0C6b0.d1.", 3, 0, "e8" },
|
|
{ "h b", "04b1c0.", 3, 0 },
|
|
{ "h p", "DD.04b1c0.", 1, 1 },
|
|
{ "h q", "FD.04b1c0.", 1, 1 },
|
|
{ "h (HL)", "34c0.", 3, 0 },
|
|
{ "h (ca)", "d1.34c0.d2.", 3, 0, "ii" },
|
|
{ "h (e)", "d1.34c0.00.", 3, 0, "ii" },
|
|
{ "INC d", "03f0.", 3, 0 },
|
|
{ "INC e", "d0.23.", 3, 0 },
|
|
{ "DEC d", "0Bf0.", 3, 0 },
|
|
{ "DEC e", "d0.2B.", 3, 0 },
|
|
{ "DAA", "27.", 3, 0 },
|
|
{ "CPL", "2F.", 3, 0 },
|
|
{ "NEG", "ED.44.", 3, 0 },
|
|
{ "CCF", "3F.", 3, 0 },
|
|
{ "SCF", "37.", 3, 0 },
|
|
{ "NOP", "00.", 3, 0 },
|
|
{ "HALT", "76.", 3, 0 },
|
|
{ "DI", "F3.", 3, 0 },
|
|
{ "EI", "FB.", 3, 0 },
|
|
{ "IM a", "ED.k0.", 3, 0, "tt" },
|
|
{ "RLCA", "07.", 3, 0 },
|
|
{ "RLA", "17.", 3, 0 },
|
|
{ "RRCA", "0F.", 3, 0 },
|
|
{ "RRA", "1F.", 3, 0 },
|
|
{ "SLL b", "CB.30c0.", 1, 1 },
|
|
{ "SLL (HL)", "CB.36.", 1, 1 },
|
|
{ "SLL (ca)", "d0.CB.d1.36.", 1, 1, "ii" },
|
|
{ "SLL (ca),b", "d0.CB.d1.30c2.", 1, 1, "ii" },
|
|
{ "k b", "CB.00b0c1.", 3, 0 },
|
|
{ "k (HL)", "CB.06b0.", 3, 0 },
|
|
{ "k (ca)", "d1.CB.d2.06b0.", 3, 0, "ii" },
|
|
{ "k (ca),b", "d1.CB.d2.00b0c3.", 1, 1, "ii" },
|
|
{ "RLD", "ED.6F.", 3, 0 },
|
|
{ "RRD", "ED.67.", 3, 0 },
|
|
{ "l a,b", "CB.00g0b1c2.", 3, 0, "b3" },
|
|
{ "l a,(HL)", "CB.06g0b1.", 3, 0, "b3" },
|
|
{ "l a,(ca)", "d2.CB.d3.06g0b1.", 3, 0, "b3ii" },
|
|
{ "RES a,(ca),b", "d1.CB.d2.80b0c3.", 1, 1, "b3ii" },
|
|
{ "SET a,(ca),b", "d1.CB.d2.C0b0c3.", 1, 1, "b3ii" },
|
|
{ "JP (HL)", "E9.", 3, 0 },
|
|
{ "JP (e)", "d0.E9.", 3, 0 },
|
|
{ "JP m,a", "C2b0.e1", 3, 0 },
|
|
{ "JP a", "C3.e0", 3, 0 },
|
|
{ "JR n,a", "20b0.i1.", 3, 0, "r8" },
|
|
{ "JR a", "18.i0.", 3, 0, "r8" },
|
|
{ "DJNZ a", "10.i0.", 3, 0, "r8" },
|
|
{ "CALL m,a", "C4b0.e1", 3, 0 },
|
|
{ "CALL a", "CD.e0", 3, 0 },
|
|
{ "RETI", "ED.4D.", 3, 0 },
|
|
{ "RETN", "ED.45.", 3, 0 },
|
|
{ "RET m", "C0b0.", 3, 0 },
|
|
{ "RET", "C9.", 3, 0 },
|
|
{ "RST a", "C7j0.", 3, 0, "ss" },
|
|
{ "IN b,(C)", "ED.40b0.", 3, 0 },
|
|
{ "IN A,(a)", "DB.d0.", 3, 0, "e8" },
|
|
{ "IN F,(a)", "ED.70.", 3, 0 },
|
|
{ "IN (C)", "ED.70.", 1, 1 },
|
|
{ "INI", "ED.A2.", 3, 0 },
|
|
{ "INIR", "ED.B2.", 3, 0 },
|
|
{ "IND", "ED.AA.", 3, 0 },
|
|
{ "INDR", "ED.BA.", 3, 0 },
|
|
{ "OUT (C),0", "ED.71.", 1, 1 },
|
|
{ "OUT (C),b", "ED.41b0.", 3, 0 },
|
|
{ "OUT (a),A", "D3.d0.", 3, 0, "e8" },
|
|
{ "OUTI", "ED.A3.", 3, 0 },
|
|
{ "OTIR", "ED.B3.", 3, 0 },
|
|
{ "OUTD", "ED.AB.", 3, 0 },
|
|
{ "OTDR", "ED.BB.", 3, 0 },
|
|
/* hd64180 added instructions */
|
|
{ "IN0 b,(a)", "ED.00b0.d1.", 2, 0, "e8" },
|
|
{ "OUT0 (a),b", "ED.01b1.d0.", 2, 0, "e8" },
|
|
{ "OTDM", "ED.8B.", 2, 0 },
|
|
{ "OTDMR", "ED.9B.", 2, 0 },
|
|
{ "OTIM", "ED.83.", 2, 0 },
|
|
{ "OTIMR", "ED.93.", 2, 0 },
|
|
{ "MLT d", "ED.4Cf0.", 2, 0 },
|
|
{ "SLP", "ED.76.", 2, 0 },
|
|
{ "TST b", "ED.04b0.", 2, 0 },
|
|
{ "TST (HL)", "ED.34.", 2, 0 },
|
|
{ "TST a", "ED.64.d0.", 2, 0, "e8" },
|
|
{ "TSTIO a", "ED.74.d0.", 2, 0, "e8" },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
static const char *const bval[] = { "B", "C", "D", "E",
|
|
"H", "L", "", "A", NULL };
|
|
static const char *const cval[] = { "IX", "IY", NULL };
|
|
static const char *const dval[] = { "BC", "DE", "HL", "SP", NULL };
|
|
static const char *const fval[] = { "BC", "DE", "HL", "AF", NULL };
|
|
static const char *const gval[] = { "ADD", "ADC", "SUB", "SBC",
|
|
"AND", "XOR", "OR", "CP", NULL };
|
|
static const char *const hval[] = { "INC", "DEC", NULL };
|
|
static const char *const ival[] = { "BC", "DE", "IX", "SP", NULL };
|
|
static const char *const jval[] = { "BC", "DE", "IY", "SP", NULL };
|
|
static const char *const kval[] = { "RLC", "RRC", "RL", "RR",
|
|
"SLA", "SRA", "", "SRL", NULL };
|
|
static const char *const lval[] = { "", "BIT", "RES", "SET", NULL };
|
|
static const char *const mval[] = { "NZ", "Z", "NC", "C",
|
|
"PO", "PE", "P", "M", NULL };
|
|
static const char *const nval[] = { "NZ", "Z", "NC", "C", NULL };
|
|
static const char *const pval[] = { "B", "C", "D", "E",
|
|
"IXH", "IXL", "", "A", NULL };
|
|
static const char *const qval[] = { "B", "C", "D", "E",
|
|
"IYH", "IYL", "", "A", NULL };
|
|
static const char *const nullv[] = { NULL };
|
|
|
|
static const char *const *const valtab[] = {
|
|
bval, cval, dval, dval, fval,
|
|
gval, hval, ival, jval, kval,
|
|
lval, mval, nval, nullv, pval,
|
|
qval
|
|
};
|
|
|
|
static int indval(const char *p, int disp, const char **q)
|
|
{
|
|
int v;
|
|
const char *r;
|
|
|
|
v = mreg(p, cval, &r);
|
|
if (v >= 0) {
|
|
v = (v == 0) ? 0xDD : 0xFD;
|
|
while (*r == ' ') r++;
|
|
if (!disp || *r == '+' || *r == '-') {
|
|
*q = r;
|
|
return v;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int match_z80(char c, const char *p, const char **q)
|
|
{
|
|
int v;
|
|
|
|
if (c == 'c' || c == 'e') {
|
|
v = indval(p, c == 'c', q);
|
|
} else if (c <= 'q') {
|
|
v = mreg(p, valtab[(int) (c - 'b')], q);
|
|
} else {
|
|
v = -1;
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
static int gen_z80(int *eb, char p, const int *vs, int i, int savepc)
|
|
{
|
|
int b;
|
|
|
|
b = *eb;
|
|
switch (p) {
|
|
case 'f': b |= (vs[i] << 4); break;
|
|
case 'g': b |= (vs[i] << 6); break;
|
|
case 'i': b = (vs[i] - savepc - 2); break;
|
|
case 'j': if (s_pass > 0 && (vs[i] & ~56) != 0) {
|
|
eprint(_("invalid RST argument (%d)\n"),
|
|
vs[i]);
|
|
eprcol(s_pline, s_pline_ep);
|
|
newerr();
|
|
}
|
|
b |= vs[i];
|
|
break;
|
|
case 'k': if (s_pass > 0 && (vs[i] < 0 || vs[i] > 2)) {
|
|
eprint(_("invalid IM argument (%d)\n"),
|
|
vs[i]);
|
|
eprcol(s_pline, s_pline_ep);
|
|
newerr();
|
|
}
|
|
b = 0x46;
|
|
if (vs[i] == 1)
|
|
b = 0x56;
|
|
else if (vs[i] == 2)
|
|
b = 0x5E;
|
|
break;
|
|
case 'm': if (s_pass == 0 && !s_extended_op) {
|
|
if (vs[i] != 0 && vs[i] != 1 && vs[i] != 3) {
|
|
eprint(_("unofficial syntax\n"));
|
|
eprcol(s_pline, s_pline_ep);
|
|
newerr();
|
|
}
|
|
}
|
|
break;
|
|
case 'n': if (s_pass == 0 && !s_extended_op) {
|
|
if (vs[i] == 0 || vs[i] == 1 || vs[i] == 3) {
|
|
eprint(_("unofficial syntax\n"));
|
|
eprcol(s_pline, s_pline_ep);
|
|
newerr();
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
*eb = b;
|
|
return 0;
|
|
}
|
|
|
|
static int s_pat_char = 'b';
|
|
static int s_pat_index;
|
|
|
|
static void pat_char_rewind_z80(int c)
|
|
{
|
|
s_pat_char = c;
|
|
s_pat_index = 0;
|
|
};
|
|
|
|
static const char *pat_next_str_z80(void)
|
|
{
|
|
const char *s;
|
|
|
|
if (s_pat_char >= 'b' && s_pat_char <= 'q') {
|
|
s = valtab[(int) (s_pat_char - 'b')][s_pat_index];
|
|
if (s != NULL) {
|
|
s_pat_index++;
|
|
}
|
|
} else {
|
|
s = NULL;
|
|
}
|
|
|
|
return s;
|
|
};
|
|
|
|
const struct target s_target_z80 = {
|
|
.id = "z80",
|
|
.descr = "Zilog Z80",
|
|
.matcht = s_matchtab_z80,
|
|
.matchf = match_z80,
|
|
.genf = gen_z80,
|
|
.pat_char_rewind = pat_char_rewind_z80,
|
|
.pat_next_str = pat_next_str_z80,
|
|
.mask = 1
|
|
};
|
|
|
|
const struct target s_target_hd64180 = {
|
|
.id = "hd64180",
|
|
.descr = "Hitachi HD64180",
|
|
.matcht = s_matchtab_z80,
|
|
.matchf = match_z80,
|
|
.genf = gen_z80,
|
|
.pat_char_rewind = pat_char_rewind_z80,
|
|
.pat_next_str = pat_next_str_z80,
|
|
.mask = 2
|
|
};
|