From 693830b09adcca0dd2f1e7d6ed632e1bbdb40475 Mon Sep 17 00:00:00 2001 From: em Date: Fri, 29 Mar 1985 21:10:43 +0000 Subject: [PATCH] *** empty log message *** --- mach/z80/cg/mach.c | 103 ++++ mach/z80/cg/mach.h | 30 + mach/z80/cg/table | 1440 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1573 insertions(+) create mode 100644 mach/z80/cg/mach.c create mode 100644 mach/z80/cg/mach.h create mode 100644 mach/z80/cg/table diff --git a/mach/z80/cg/mach.c b/mach/z80/cg/mach.c new file mode 100644 index 000000000..89abf0c96 --- /dev/null +++ b/mach/z80/cg/mach.c @@ -0,0 +1,103 @@ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#ifndef NORCSID +static char rcs_mach[] = "$Header$" ; +static char rcs_h[] = RCS_H ; +#endif + +/* + * machine dependent back end routines for the Zilog Z80 (as well as Intel 8080) + */ + +con_part(sz,w) register sz; word w; { + + while (part_size % sz) + part_size++; + if (part_size == TEM_WSIZE) + part_flush(); + if (sz == 1) { + w &= 0xFF; + if (part_size) + w <<= 8; + part_word |= w; + } else { + assert(sz == 2); + part_word = w; + } + part_size += sz; +} + +con_mult(sz) word sz; { + long l; + + if (argval != 4) + fatal("bad icon/ucon size"); + l = atol(str); + fprintf(codefile,".short\t%d\n",(int)l); + fprintf(codefile,".short\t%d\n",(int)(l>>16)); +} + +con_float() { + + fatal("no reals"); +} + + +prolog(nlocals) full nlocals; { + + fprintf(codefile,"\tpush\tiy\n\tld\thl,0\n\tadd\thl,sp\n\tpush\thl\n\tpop\tiy\n"); + switch (nlocals) { + case 8: fprintf(codefile,"\tpush\thl\n"); + case 6: fprintf(codefile,"\tpush\thl\n"); + case 4: fprintf(codefile,"\tpush\thl\n"); + case 2: fprintf(codefile,"\tpush\thl\n"); + case 0: break; + default: + fprintf(codefile,"\tld\thl,%d\n\tadd\thl,sp\n\tld\tsp,hl\n",-nlocals); + break; + } +} + +mes(type) word type ; { + int argt ; + + switch ( (int)type ) { + case ms_ext : + for (;;) { + switch ( argt=getarg( + ptyp(sp_cend)|ptyp(sp_pnam)|sym_ptyp) ) { + case sp_cend : + return ; + default: + strarg(argt) ; + fprintf(codefile,".define %s\n",argstr) ; + break ; + } + } + default : + while ( getarg(any_ptyp) != sp_cend ) ; + break ; + } +} + +char *segname[] = { + ".text", + ".data", + ".data", + ".bss" +}; diff --git a/mach/z80/cg/mach.h b/mach/z80/cg/mach.h new file mode 100644 index 000000000..8648d8c7a --- /dev/null +++ b/mach/z80/cg/mach.h @@ -0,0 +1,30 @@ +#ifndef NORCSID +#define RCS_H "$Header$" +#endif + +#define ex_ap(y) fprintf(codefile,".extern %s\n",y) +#define in_ap(y) /* nothing */ + +#define newilb(x) fprintf(codefile,"%s:\n",x) +#define newdlb(x) fprintf(codefile,"%s:\n",x) +#define dlbdlb(x,y) fprintf(codefile,"%s = %s\n",x,y) +#define newlbss(l,x) fprintf(codefile,"%s:.space\t%d\n",l,x); + +#define cst_fmt "%d" +#define off_fmt "%d" +#define ilb_fmt "I%03x%x" +#define dlb_fmt "_%d" +#define hol_fmt "hol%d" + +#define loc_off "%d(bp)" +#define arg_off "4+%d(bp)" +#define hol_off "%d+hol%d" + +#define con_cst(x) fprintf(codefile,".word\t%d\n",x) +#define con_ilb(x) fprintf(codefile,".word\t%s\n",x) +#define con_dlb(x) fprintf(codefile,".word\t%s\n",x) + +#define modhead "" + +#define id_first '_' +#define BSS_INIT 0 diff --git a/mach/z80/cg/table b/mach/z80/cg/table new file mode 100644 index 000000000..8ebc66743 --- /dev/null +++ b/mach/z80/cg/table @@ -0,0 +1,1440 @@ +"$Header$" +/******************************************************************** + **** **** + **** Z 8 0 B A C K E N D T A B L E S **** + **** **** + ********************************************************************/ + + + +/* + * INTEGER SIZE: 16 bits + * POINTER SIZE: 16 bits + */ + + +/* We store LB in the iy index-register, because this results in the + * cheapest addressing schemes for instruction like lol and stl. + * For a lol with an argument that is too low (< 0-128) or too high + * (>= 127) there is an overhead, because the offset cannot be + * expressed in 8 bits. In these cases we copy the LB to the hl-register, + * compute the required address and use register indirect mode to do + * the load. These cases occur rarely (about 11.5 % in C and 5.5 % + * in Pascal. + */ + +EM_WSIZE = 2 +EM_PSIZE = 2 +EM_BSIZE = 4 + + +/************************** + ** R E G I S T E R S ** + **************************/ + + + +REGISTERS: +AA = ("a",1) REG1, AREG. +BB = ("b",1) REG1. +CC = ("c",1) REG1. +DD = ("d",1) REG1. +EE = ("e",1) REG1. +HH = ("h",1) REG1. +LL = ("l",1) REG1. + + +BC = ("bc",2,BB,CC) REG, GEN_REG, BC_REG, ANY_REG. +DE = ("de",2,DD,EE) REG, GEN_REG, DE_REG, ANY_REG. +HL = ("hl",2,HH,LL) GEN_REG, HL_REG, ANY_REG. + +LB = ("iy",2) LOCALBASE. +IX = ("ix",2) INDEXREG, IX_REG, ANY_REG. + + + +pseudo0 = ("",2,BC,DE). +pseudo1 = ("",2,HL,IX). +pseudo2 = ("",2,pseudo0,pseudo1). +pseudo3 = ("",2,pseudo2,AA) ALL_REG. + + +/***************** + ** T O K E N S ** + *****************/ + + +TOKENS: +IMMEDIATE1 = {INT off;} 1 "%[off]" +IMMEDIATE = {INT off;} 2 "%[off]" +IREG1 = {REGISTER reg;} 1 "(%[reg])" +INDEXED = {REGISTER reg; + INT off; } 1 "(%[reg]+%[off])" +EXTENDED = {STRING off; } 2 "(%[off])" +LOCAL_ADDR = {INT off; } 2 +EXTENDED_ADDR = {STRING off; } 2 "%[off]" + + + +/************************************* + ** T O K E N E X P R E S S I O N S ** + *************************************/ + + +TOKENEXPRESSIONS: +ANYTOK = IMMEDIATE + EXTENDED +ANY1 = REG1 + IMMEDIATE1 + IREG1 + INDEXED +ANY_ADDR = EXTENDED_ADDR + LOCAL_ADDR +ANY = ANYTOK + ANY_REG + ANY_ADDR +MEM_ALL = ANY - ANY_REG +REG_SCR = REG * SCRATCH + +ANY_REG_SCR = ANY_REG * SCRATCH +GEN_REG_SCR = GEN_REG * SCRATCH +HL_REG_SCR = HL_REG * SCRATCH +BC_REG_SCR = BC_REG * SCRATCH +DE_REG_SCR = DE_REG * SCRATCH +IX_REG_SCR = IX_REG * SCRATCH + + + + + +/************* + ** C O D E ** + *************/ + + +CODE: + + +/* G R O U P I : L O A D S */ + + +loc | | | {IMMEDIATE,$1} | | +ldc | | | {IMMEDIATE,highw(1)} {IMMEDIATE,loww(1)} | | +lol sfit($1,8) | | allocate(REG) + move({INDEXED,LB,$1} , %[a.2] ) + move({INDEXED,LB,$1+1} , %[a.1] ) | %[a] | | +lol | | allocate(HL_REG,REG) + "push iy" + "pop %[b]" + move({IMMEDIATE,$1} , %[a] ) + "add hl,%[b]" + "ld %[b.2],(hl)" + "inc hl" + "ld %[b.1],(hl)" + erase(HL) + erase(%[a]) | %[b] | | +ldl $1<126 && $1>0-128 | | allocate(REG,REG) + move({INDEXED,LB,$1} , %[a.2]) + move({INDEXED,LB,$1+1},%[a.1]) + move({INDEXED,LB,$1+2},%[b.2]) + move({INDEXED,LB,$1+3},%[b.1]) | %[b] %[a] | | +ldl | | + remove(ALL) + allocate(BC_REG,DE_REG,HL_REG={IMMEDIATE,$1}) + "push iy" + "pop bc" + "add hl,bc" + "ld e,(hl)" + "inc hl" + "ld d,(hl)" + "inc hl" + "ld c,(hl)" + "inc hl" + "ld b,(hl)" + | BC DE | | +loe | | | {EXTENDED,$1} | | +lde | | | {EXTENDED,$1+"+2"} {EXTENDED,$1} | | +lil sfit($1,8) | | allocate(HL_REG,REG) + move({INDEXED,LB,$1} , %[a.2]) + move({INDEXED,LB,$1+1} , %[a.1]) + "ld %[b.2],(hl)" + "inc hl" + "ld %[b.1],(hl)" + erase(HL) + erase(%[a]) | %[b] | | +lil | | allocate(HL_REG = {IMMEDIATE,$1} , REG) + "push iy" + "pop %[b]" + "add hl,%[b]" + "ld %[b.2],(hl)" + "inc hl" + "ld %[b.1],(hl)" + "ld h,%[b.1]" + "ld l,%[b.2]" + "ld %[b.2],(hl)" + "inc hl" + "ld %[b.1],(hl)" + erase(HL) + erase(%[a]) | %[b] | | +lof | HL_REG_SCR | allocate(REG = {IMMEDIATE,$1} ) + "add hl,%[a]" + "ld %[a.2],(hl)" + "inc hl" + "ld %[a.1],(hl)" + erase(HL) + erase(%[a]) | %[a] | | +... | REG_SCR | allocate(HL_REG = {IMMEDIATE,$1} ) + "add hl,%[1]" + "ld %[1.2],(hl)" + "inc hl" + "ld %[1.1],(hl)" + erase(HL) + erase(%[1]) | %[1] | | +ldf | HL_REG_SCR | allocate(REG = {IMMEDIATE,$1} , REG) + "add hl,%[a]" + "ld %[a.2],(hl)" + "inc hl" + "ld %[a.1],(hl)" + "inc hl" + "ld %[b.2],(hl)" + "inc hl" + "ld %[b.1],(hl)" + erase(HL) + erase(%[a]) | %[b] %[a] | | +... | REG_SCR | allocate(HL_REG = {IMMEDIATE,$1} , REG) + "add hl,%[1]" + "ld %[1.2],(hl)" + "inc hl" + "ld %[1.1],(hl)" + "inc hl" + "ld %[b.2],(hl)" + "inc hl" + "ld %[b.1],(hl)" + erase(HL) + erase(%[1]) | %[b] %[1] | | +lal | | remove(ALL) + allocate(HL_REG, REG = {IMMEDIATE,$1} ) + "push iy" + "pop hl" + "add hl,%[b]" + erase(%[a]) | %[a] | | +lae | | | {EXTENDED_ADDR,$1} | | +lxl $1 == 0 | | remove(ALL) + "push iy" + | | | +lxl | | remove(ALL) + allocate(AREG = {IMMEDIATE1,$1} , + HL_REG,BC_REG) + "push iy" + "pop %[c]" + "1:" + "ld hl,4" + "add hl,%[c]" + "ld %[c.2],(hl)" + "inc hl" + "ld %[c.1],(hl)" + "dec a" + "jr nz,1b" + "push %[c]" + erase(AA) | | | +lxa $1 == 0 | | | | lxl $1 adp 4 | +lxa $1 == 1 | | remove(ALL) + allocate(HL_REG,BC_REG) + "ld %[b],4" + "push iy" + "pop hl" + "add hl,%[b]" + "ld %[b.2],(hl)" + "inc hl" + "ld %[b.1],(hl)" + "ld hl,4" + "add hl,%[b]" | HL | | +lxa $1 > 1 | | remove (ANY) + allocate(AREG = {IMMEDIATE1,$1}, + HL_REG,BC_REG) + "push iy" + "pop %[c]" + "1:" + "ld hl,4" + "add hl,%[c]" + "ld %[c.2],(hl)" + "inc hl" + "ld %[c.1],(hl)" + "inc hl" + "dec a" + "jr nz,1b" + "ld hl,4" + "add hl,%[c]" + erase(AA) | HL | | +dch | | | | loi 2 | +loi $1 == 1 | HL_REG | allocate(REG = {IMMEDIATE,0} ) + "ld %[a.2],(hl)" + erase(%[a]) | %[a] | | +... | REG | allocate(REG,AREG) + move({IREG1,%[1]} , AA) + move(AA , %[a.2]) + move({IMMEDIATE1,0} , %[a.1]) + erase(AA) | %[a] | | +loi $1 == 2 | HL_REG_SCR | remove(ALL) allocate(REG) + "ld %[a.2],(hl)" + "inc hl" + "ld %[a.1],(hl)" + erase(%[1]) | %[a] | | +loi $1 == 4 | HL_REG_SCR | remove(ALL) + allocate(AREG,REG) + "ld %[b.2],(hl)" + "inc hl" + "ld %[b.1],(hl)" + "inc hl" + "ld a,(hl)" + "inc hl" + "ld h,(hl)" + "ld l,a" + erase(HL) | HL %[b] | | +loi $1 <= 511 && $1 > 4 | HL_REG_SCR | + remove(ALL) + allocate(AREG,REG) + move({IMMEDIATE,$1-1} , %[b]) + "add hl,%[b]" + move({IMMEDIATE1,$1/2} , AA) + "1:" + "ld %[b.1],(hl)" + "dec hl" + "ld %[b.2],(hl)" + "dec hl" + "push %[b]" + "dec a" + "jr nz,1b" + erase(%[a]) + erase(%[b]) | | | +loi $1 > 511 | STACK | remove(ALL) allocate(ALL_REG) + move({IMMEDIATE,$1}, HL) + "push hl" + "call .los" + erase(HL) | | | +los $1 == 2 | STACK | remove(ALL) allocate(ALL_REG) + "call .los" | | | +lpi | | | {EXTENDED_ADDR,$1} | | + + + +/* G R O U P II : S T O R E S */ + + + +stl sfit($1,8) | REG + HL_REG | + remove(ALL) + move(%[1.2] , {INDEXED,LB,$1} ) + move(%[1.1] , {INDEXED,LB,$1+1} ) + | | | +stl | REG | + remove(ALL) + allocate(HL_REG = {IMMEDIATE,$1} , REG) + "push iy" + "pop %[b]" + "add hl,%[b]" + "ld (hl),%[1.2]" + "inc hl" + "ld (hl),%[1.1]" + erase(%[a]) | | | +ste | ANY_REG | remove(EXTENDED,%[off] == $1) + remove(ALL) + move(%[1] , {EXTENDED,$1} ) | | | +sil ($1 >= 0-128 && $1 < 127) | REG | + remove(ALL) + allocate(HL_REG) + move({INDEXED,LB,$1} , %[a.2]) + move({INDEXED,LB,$1+1} , %[a.1]) + "ld (hl),%[1.2]" + "inc hl" + "ld (hl),%[1.1]" + erase(%[a]) | | | +sil ($1 < 0-128 || $1 >= 127) | REG | + remove(ALL) + allocate(HL_REG,REG) + "push iy" + "pop %[b]" + move({IMMEDIATE,$1} , %[a]) + "add hl,%[b]" + "ld %[b.2],(hl)" + "inc hl" + "ld %[b.1],(hl)" + "ld h,%[b.1]" + "ld l,%[b.2]" + "ld (hl),%[1.2]" + "inc hl" + "ld (hl),%[1.1]" + erase(%[a]) | | | +stf | HL_REG_SCR REG | remove(ALL) + allocate(REG = {IMMEDIATE,$1}) + "add hl,%[a]" + "ld (hl),%[2.2]" + "inc hl" + "ld (hl),%[2.1]" + erase(%[1]) | | | +sti $1 == 1 | HL_REG REG | remove(ALL) + "ld (hl),%[2.2]" | | | +sti $1 == 2 | HL_REG_SCR REG | remove(ALL) + "ld (hl),%[2.2]" + "inc hl" + "ld (hl),%[2.1]" + erase(%[1]) | | | +sti $1 == 4 | HL_REG_SCR REG REG | + remove(ALL) + "ld (hl),%[2.2]" + "inc hl" + "ld (hl),%[2.1]" + "inc hl" + "ld (hl),%[3.2]" + "inc hl" + "ld (hl),%[3.1]" + erase(HL) | | | +sti $1 <= 511 && $1 > 4 | HL_REG_SCR | + remove(ALL) + allocate(AREG,REG) + move({IMMEDIATE1,$1/2} , AA) + "1:" + "pop %[b]" + "ld (hl),%[b.2]" + "inc hl" + "ld (hl),%[b.1]" + "inc hl" + "dec a" + "jr nz,1b" + erase(AA) + erase(HL) | | | +sti $1 > 511 | STACK | remove(ALL) allocate(ALL_REG) + move({IMMEDIATE,$1}, HL) + "push hl" + "call .sts" + erase(HL) | | | +sts $1 == 2 | STACK | remove(ALL) allocate(ALL_REG) + "call .sts" | | | +sdl ($1 >= 0-128 && $1 < 125) | REG REG | + remove(ALL) + move(%[1.2] , {INDEXED,LB,$1}) + move(%[1.1] , {INDEXED,LB,$1+1}) + move(%[2.2] , {INDEXED,LB,$1+2}) + move(%[2.1] , {INDEXED,LB,$1+3}) | | | +sdl ($1 < 0-128 || $1 >= 125) | STACK | + remove(ALL) + allocate(HL_REG = {IMMEDIATE,$1} , + BC_REG,DE_REG,IX_REG,AREG ) + "call .sdl" + erase(HL) | | | +sde | ANY_REG ANY_REG | + remove(ALL) + move(%[1] , {EXTENDED,$1}) + move(%[2] , {EXTENDED,$1+"+2"}) | | | +sdf | | remove(ALL) + allocate(ALL_REG) + move({IMMEDIATE,$1},DE) + "call .sdf" + | | | + + + +/* G R O U P III & IV : I N T E G E R A R I T H M E T I C */ + + + +adi $1 == 2 | HL_REG_SCR GEN_REG | + "add hl,%[2]" + erase(HL) | HL | | +... | GEN_REG HL_REG_SCR | + "add hl,%[1]" + erase(HL) | HL | | +adi $1 == 4 | HL_REG_SCR DE_REG_SCR STACK | allocate(BC_REG) + "pop bc" + "add hl,bc" + "ex de,hl" + "pop bc" + "adc hl,bc" + erase(DE) + erase(HL) | HL DE | | +sbi $1 == 2 | GEN_REG HL_REG_SCR | + "or a" + "sbc hl,%[1]" + erase(HL) | HL | | +sbi $1 == 4 | BC_REG_SCR DE_REG_SCR STACK | + allocate(HL_REG) + "or a" + "pop hl" + "sbc hl,bc" /* least sign. */ + "ex (sp),hl" + "sbc hl,de" + "pop de" + erase(DE) + erase(HL) | HL DE | | +mli $1 == 2 | DE_REG_SCR BC_REG STACK | + allocate(HL_REG,IX_REG,AREG) + "call .mli2" + erase(DE) | HL | | +mli $1 == 4 | STACK | allocate(ALL_REG) + "call .mli4" | | | +dvi $1 == 2 | BC_REG_SCR DE_REG_SCR STACK | + allocate(HL_REG,IX_REG,AREG) + "call .dvi2" + "push de" + erase(BC) + erase(DE) | | | +dvi $1 == 4 | STACK | allocate(ALL_REG) + "call .dvi4" | | | +dvu $1 == 2 | BC_REG_SCR DE_REG_SCR STACK | + allocate(HL_REG,IX_REG,AREG) + "call .dvu2" + erase(BC) + erase(DE) | DE | | +dvu $1 == 4 | STACK | allocate(ALL_REG) + "call .dvu4" | | | +rmi $1 == 2 | BC_REG_SCR DE_REG_SCR STACK | + allocate(HL_REG,IX_REG,AREG) + "call .rmi2" + erase(BC) + erase(DE) | DE | | +rmi $1 == 4 | STACK | allocate(ALL_REG) + "call .dvi4" + "pop hl" + "pop hl" + erase(HL) + | BC DE | | +rmu $1 == 2 | BC_REG_SCR DE_REG_SCR STACK | + allocate(HL_REG,IX_REG,AREG) + "call .dvu2" + erase(BC) + erase(DE) | HL | | +rmu $1 == 4 | STACK | allocate(ALL_REG) + "call .dvu4" | BC DE | | +ngi $1 == 2 | REG | allocate(HL_REG = {IMMEDIATE,0}) + "or a" + "sbc hl,%[1]" + erase(HL) | HL | | +ngi $1 == 4 | DE_REG_SCR BC_REG_SCR | + allocate(HL_REG,AREG) + "xor a" + "ld h,a" + "ld l,a" + "sbc hl,de" + "ex de,hl" + "ld h,a" + "ld l,a" + "sbc hl,bc" + erase(DE) | HL DE | | +sli $1 == 2 | REG_SCR HL_REG_SCR | + "inc %[1.1]" /* see if count >> 15 */ + "dec %[1.1]" + "jr z,1f" + "ld %[1.2],15" + "1:" + "dec %[1.2]" + "jp m,2f" + "add hl,hl" + "jr 1b" + "2:" + erase(%[1]) + erase(HL) | HL | | +sli $1 == 4 | REG_SCR IX_REG_SCR HL_REG_SCR | + "inc %[1.1]" + "dec %[1.1]" + "jr z,1f" + "ld %[1.2],31" + "1:" + "dec %[1.2]" + "jp m,2f" + "add ix,ix" + "adc hl,hl" + "jr 1b" + "2:" + erase(%[1]) + erase(HL) + erase(IX) | HL IX | | +sri $1 == 2 | GEN_REG_SCR GEN_REG_SCR | + "inc %[1.1]" + "dec %[1.1]" + "jr z,1f" + "ld %[1.2],15" + "1:" + "dec %[1.2]" + "jp m,2f" + "sra %[2.1]" + "rr %[2.2]" + "jr 1b" + "2:" + erase(%[1]) + erase(%[2]) | %[2] | | +sri $1 == 4 | GEN_REG_SCR GEN_REG_SCR GEN_REG_SCR | + "inc %[1.1]" + "dec %[1.1]" + "jr z,1f" + "ld %[1.2],31" + "1:" + "dec %[1.2]" + "jp m,2f" + "sra %[3.1]" + "rr %[3.2]" + "rr %[2.1]" + "rr %[2.2]" + "jr 1b" + "2:" + erase(%[1]) + erase(%[2]) + erase(%[3]) | %[3] %[2] | | +mlu | | | | mli $1 | +sru $1 == 2 | GEN_REG_SCR GEN_REG_SCR | + "inc %[1.1]" + "dec %[1.1]" + "jr z,1f" + "ld %[1.2],15" + "1:" + "dec %[1.2]" + "jp m,2f" + "srl %[2.1]" + "rr %[2.2]" + "jr 1b" + "2:" + erase(%[1]) + erase(%[2]) | %[2] | | +sru $1 == 4 | GEN_REG_SCR GEN_REG_SCR GEN_REG_SCR | + "inc %[1.1]" + "dec %[1.1]" + "jr z,1f" + "ld %[1.2],31" + "1:" + "dec %[1.2]" + "jp m,2f" + "srl %[3.1]" + "rr %[3.2]" + "rr %[2.1]" + "rr %[2.2]" + "jr 1b" + "2:" + erase(%[1]) + erase(%[2]) + erase(%[3]) | %[3] %[2] | | +adu | | | | adi $1 | +sbu | | | | sbi $1 | +slu | | | | sli $1 | + + + +/* G R O U P V : F L O A T I N G P O I N T */ + + +adf | | + "call .unimpld" | | | +sbf | | + "call .unimpld" | | | +mlf | | + "call .unimpld" | | | +dvf | | + "call .unimpld" | | | +ngf | | + "call .unimpld" | | | +fif | | + "call .unimpld" | | | +fef | | + "call .unimpld" | | | + + + +/* G R O U P VI : P O I N T E R A R I T H M E T I C */ + + + +adp $1 == 0 | | | | | +adp $1 == 1 | ANY_REG_SCR | "inc %[1]" + erase(%[1]) | %[1] | | +adp $1 == 2 | ANY_REG_SCR | "inc %[1]" + "inc %[1]" + erase(%[1]) | %[1] | | +adp $1 == 0-1 | ANY_REG_SCR | "dec %[1]" + erase(%[1]) | %[1] | | +adp $1 == 0-2 | ANY_REG_SCR | "dec %[1]" + "dec %[1]" + erase(%[1]) | %[1] | | +adp $1 < 0-2 || $1 > 2 | HL_REG_SCR | + allocate(REG = {IMMEDIATE,$1} ) + "add hl,%[a]" + erase(HL) | HL | | +... | REG | + allocate(HL_REG = {IMMEDIATE,$1} ) + "add hl,%[1]" + erase(HL) | HL | | +... | LOCAL_ADDR | remove(ALL) | {LOCAL_ADDR, %[1.off]+$1} | | +ads $1 == 2 | HL_REG_SCR REG | remove(ALL) "add hl,%[2]" + erase(HL) | HL | | +... | REG HL_REG_SCR | "add hl,%[1]" + erase(HL) | HL | | +sbs $1 == 2 | REG HL_REG_SCR | "or a" + "sbc hl,%[1]" + erase(HL) | HL | | + + + +/* G R O U P VII : I N C R E M E N T / D E C R E M E N T */ + + + +inc | ANY_REG_SCR | "inc %[1]" + erase(%[1]) | %[1] | | + +/* There is no efficient way on the Z80 to increment or decrement + * a local or external. We first fetch the variable into a register, + * increment/decrement it and then store it. + */ + +inl | | | | lol $1 inc stl $1 | +ine | | | | loe $1 inc ste $1 | +dec | ANY_REG_SCR | "dec %[1]" + erase(%[1]) | %[1] | | +del | | | | lol $1 dec stl $1 | +dee | | | | loe $1 dec ste $1 | +zrl ($1 >= 0-128 && $1 < 127) | | + remove(ALL) + allocate(AREG) + "xor a" + move(AA , {INDEXED,LB,$1}) + move(AA , {INDEXED,LB,$1+1}) | | | +zrl ($1 < 0-128 || $1 >= 127) | | + remove(ALL) + allocate(HL_REG = {IMMEDIATE,$1} , REG, AREG) + "push iy" + "pop %[b]" + "add hl,%[b]" + "xor a" + "ld (hl),a" + "inc hl" + "ld (hl),a" + erase(HL) | | | +zre | | remove(EXTENDED,%[off] == $1) + remove(ALL) + allocate(ANY_REG = {IMMEDIATE,0} ) + move(%[a] , {EXTENDED,$1}) | | | +zer $1 == 2 | | | {IMMEDIATE,0} | | +zer $1 == 4 | | | {IMMEDIATE,0} {IMMEDIATE,0} | | +zer $1 > 4 && $1 < 256 | STACK | + + allocate(BC_REG , GEN_REG) + "ld b,$1" + "ld %[b],0" + "1:" + "push %[b]" + "djnz 1b" + | | | + + + +/* G R O U P VIII : C O N V E R T */ + + + +cii | STACK | remove(ALL) allocate(ALL_REG) + "call .cii" | | | +cuu | STACK | remove(ALL) allocate(ALL_REG) + "call .cuu" | | | +cui | | | | cuu | +ciu | | | | cuu | +cfi | | + "call .unimpld" | | | +cif | | + "call .unimpld" | | | +cuf | | + "call .unimpld" | | | +cff | | + "call .unimpld" | | | +cfu | | + "call .unimpld" | | | +cmf | | + "call .unimpld" | | | + + + +/* G R O U P IX : L O G I C A L */ + + + +and $1 == 2 | GEN_REG GEN_REG_SCR | + allocate(AREG) + "ld a,%[1.2]" + "and %[2.2]" + "ld %[2.2],a" + "ld a,%[1.1]" + "and %[2.1]" + "ld %[2.1],a" + erase(%[2]) | %[2] | | +and defined($1) && $1 > 2 | STACK | + allocate(DE_REG = {IMMEDIATE,$1}, + BC_REG,HL_REG,IX_REG,AREG) + "call .and" + erase(DE) | | | +and ! defined($1) | DE_REG_SCR STACK | + allocate(BC_REG,HL_REG,IX_REG,AREG) + "call .and" + erase(DE) | | | +ior $1 == 2 | GEN_REG GEN_REG_SCR | + allocate(AREG) + "ld a,%[1.2]" + "or %[2.2]" + "ld %[2.2],a" + "ld a,%[1.1]" + "or %[2.1]" + "ld %[2.1],a" + erase(%[2]) | %[2] | | +ior defined($1) && $1 > 2 | STACK | + allocate(DE_REG = {IMMEDIATE,$1}, + BC_REG,HL_REG,IX_REG,AREG) + "call .ior" + erase(DE) | | | +ior ! defined($1) | DE_REG_SCR STACK | + allocate(BC_REG,HL_REG,IX_REG,AREG) + "call .ior" + erase(DE) | | | +xor $1 == 2 | GEN_REG GEN_REG_SCR | + allocate(AREG) + "ld a,%[1.2]" + "xor %[2.2]" + "ld %[2.2],a" + "ld a,%[1.1]" + "xor %[2.1]" + "ld %[2.1],a" + erase(%[2]) | %[2] | | +xor defined($1) && $1 > 2 | STACK | + allocate(DE_REG = {IMMEDIATE,$1}, + BC_REG,HL_REG,IX_REG,AREG) + "call .xor" + erase(DE) | | | +xor ! defined($1) | DE_REG_SCR STACK | + allocate(BC_REG,HL_REG,IX_REG,AREG) + "call .xor" + erase(DE) | | | +com $1 == 2 | GEN_REG_SCR | allocate(AREG) + "ld a,%[1.2]" + "cpl" + "ld %[1.2],a" + "ld a,%[1.1]" + "cpl" + "ld %[1.1],a" + erase(%[1]) | %[1] | | +com defined($1) && $1 > 2 | STACK | + allocate(AREG, + HL_REG = {IMMEDIATE,$1} ) + "add hl,sp" + "1:" + "dec hl" + "ld a,(hl)" + "cpl" + "ld (hl),a" + "xor a" + "sbc hl,sp" + "jr z,2f" + "add hl,sp" + "jr 1b" + "2:" + erase(HL) | | | +com ! defined($1) | HL_REG_SCR STACK | + allocate(AREG) + "add hl,sp" + "1:" + "dec hl" + "ld a,(hl)" + "cpl" + "ld (hl),a" + "xor a" + "sbc hl,sp" + "jr z,2f" + "add hl,sp" + "jr 1b" + "2:" + erase(HL) | | | +rol $1 == 2 | REG_SCR HL_REG_SCR | + allocate(AREG) + "ld a,%[1.2]" + "and 15" + "jr z,2f" + "ld %[1],0" + "1:" + "add hl,hl" + "adc hl,%[1]" + "dec a" + "jr nz,1b" + "2:" + erase(%[1]) + erase(%[2]) | HL | | +rol $1 == 4 | REG IX_REG_SCR HL_REG_SCR | + allocate(AREG) + "ld a,%[1.2]" + "and 31" + "jr z,3f" + "1:" + "add ix,ix" + "adc hl,hl" + "jr nc,2f" + "inc ix" + "2:" + "dec a" + "jr nz,1b" + "3:" + erase(HL) + erase(IX) | HL IX | | +ror $1 == 2 | GEN_REG_SCR GEN_REG_SCR | + allocate(AREG) + "ld a,%[1.2]" + "and 15" + "jr z,0f" + "1:" + "srl %[2.1]" + "rr %[2.2]" + "jr nc,2f" + "set 7,%[2.1]" + "2:" + "dec a" + "jr nz,1b" + "0:" + erase(%[1]) + erase(%[2]) | %[2] | | +ror $1 == 4 | GEN_REG_SCR GEN_REG_SCR GEN_REG_SCR | + allocate(AREG) + "ld a,%[1.2]" + "and 31" + "jr z,0f" + "1:" + "srl %[3.1]" + "rr %[3.2]" + "rr %[2.1]" + "rr %[2.2]" + "jr nc,2f" + "set 7,%[3.1]" + "2:" + "dec a" + "jr nz,1b" + "0:" + erase(%[1]) + erase(%[2]) + erase(%[3]) | %[3] %[2] | | + + + +/* G R O U P X : S E T S */ + + +inn defined($1) | STACK | allocate(HL_REG = {IMMEDIATE,$1}, + BC_REG,DE_REG,IX_REG,AREG) + "call .inn" + erase(HL) | | | +inn !defined($1) | HL_REG_SCR STACK | + allocate(BC_REG,DE_REG,IX_REG,AREG) + "call .inn" + erase(HL) | | | +set defined($1) | STACK | allocate(HL_REG = {IMMEDIATE,$1}, + BC_REG,DE_REG,IX_REG,AREG) + "call .set" + erase(HL) | | | +set !defined($1) | HL_REG_SCR STACK | + allocate(BC_REG,DE_REG,IX_REG,AREG) + "call .set" + erase(HL) | | | + + + +/* G R O U P XI : A R R A Y S */ + + +lae aar $2 == 2 && rom(1,3) == 2 | STACK | + allocate(ALL_REG) + move({EXTENDED_ADDR,$1},BC) + "push bc" + "call .aar2" | | | +lae aar $2 == 2 && rom(1,3) != 2 | STACK | + allocate(ALL_REG) + move({EXTENDED_ADDR,$1},BC) + "push bc" + "call .aar" | | | +aar $1==2 | | + remove(ALL) + allocate(ALL_REG) + "call .aar" + | | | +aar !defined($1) | | remove(ALL) + allocate(ALL_REG) + "call .aaru" | | | +lae lar $2 == 2 && rom(1,3) == 2 | STACK | + remove(ALL) allocate(ALL_REG) + move({EXTENDED_ADDR,$1},BC) + "push bc" + "call .lar2" | | | +lae lar $2 == 2 && rom(1,3) != 2 | STACK | + remove(ALL) allocate(ALL_REG) + move({EXTENDED_ADDR,$1},BC) + "push bc" + "call .lar" | | | +lar $1==2 | | remove(ALL) + allocate(ALL_REG) + "call .lar" | | | +lar !defined($1) | | remove(ALL) + allocate(ALL_REG) + "call .laru" | | | +lae sar $2 == 2 && rom(1,3) == 2 | STACK | + remove(ALL) allocate(ALL_REG) + move({EXTENDED_ADDR, $1},BC) + "push bc" + "call .sar2" | | | +lae sar $2 == 2 && rom(1,3) != 2 | STACK | + remove(ALL) allocate(ALL_REG) + move({EXTENDED_ADDR, $1},BC) + "push bc" + "call .sar" | | | +sar $1==2 | | remove(ALL) + allocate(ALL_REG) + "call .sar" | | | +sar !defined($1) | | remove(ALL) + allocate(ALL_REG) + "call .saru" | | | + + +/* G R O U P XII : C O M P A R E */ + + + +cmi $1 == 2 | REG_SCR HL_REG_SCR | + remove(ALL) allocate(AREG) + "ld a,h" + "xor %[1.1]" + "jp m,1f" + "sbc hl,%[1]" + "jr 2f" + "1:" + "xor %[1.1]" + "jp m,2f" + "set 0,l" + "2:" + erase(%[1]) + erase(%[2]) | HL | | +cmi $1 == 4 | STACK | remove(ALL) allocate(AREG = {IMMEDIATE1,1}, + BC_REG,DE_REG,HL_REG,IX_REG) + "call .cmu4" + erase(AA) | DE | | +cmu $1 == 2 | REG_SCR HL_REG_SCR | + remove(ALL) allocate(AREG) + "ld a,h" + "xor %[1.1]" /* resets carry bit */ + "jp m,1f" + "sbc hl,%[1]" + "jr 2f" + "1:" + "xor %[1.1]" + "cpl" + "set 0,l" + "2:" + erase(%[1]) + erase(%[2]) | HL | | +cmu $1 == 4 | STACK | remove(ALL) allocate(AREG = {IMMEDIATE1,0}, + BC_REG,DE_REG,HL_REG,IX_REG) + "call .cmu4" + erase(AA) | DE | | +cmp | | | | cmu 2 | +cms $1 == 2 | GEN_REG GEN_REG_SCR | + remove(ALL) allocate(AREG) + "ld a,%[1.1]" + "xor %[2.1]" + "ld %[2.1],a" + "ld a,%[1.2]" + "xor %[2.2]" + "ld %[2.2],a" | %[2] | | +/*** +cmu defined($1) | STACK | allocate(HL_REG = {IMMEDIATE,$1}, + BC_REG,DE_REG,IX_REG,AREG) + "call .cmu" + erase(HL) | | | +cmu ! defined($1) | HL_REG_SCR STACK | + allocate(BC_REG,DE_REG,IX_REG,AREG) + "call .cmu" + erase(HL) | | | +*/ +cms defined($1) | STACK | remove(ALL) allocate(HL_REG = {IMMEDIATE,$1}, + BC_REG,DE_REG,IX_REG,AREG) + "call .cms" + erase(HL) | | | +cms ! defined($1) | HL_REG_SCR STACK | + allocate(BC_REG,DE_REG,IX_REG,AREG) + "call .cms" + erase(HL) | | | + + + + +tlt | GEN_REG | remove(ALL) + allocate(GEN_REG = {IMMEDIATE,0}) + "bit 7,%[1.1]" + "jr z,1f" + "inc %[a.2]" + "1:" + erase(%[a]) | %[a] | | +tle | GEN_REG | remove(ALL) + allocate(AREG, GEN_REG = {IMMEDIATE,1}) + "xor a" + "add a,%[1.1]" + "jp m,2f" + "jr nz,1f" + "xor a" + "add a,%[1.2]" + "jr z,2f" + "1:" + "dec %[b.2]" + "2:" + erase(%[b]) | %[b] | | +teq | GEN_REG | remove(ALL) + allocate(AREG,GEN_REG = {IMMEDIATE,0}) + "ld a,%[1.1]" + "or a" + "jp m,1f" + "xor %[1.2]" + "jr nz,1f" + "inc %[b.2]" + "1:" + erase(%[b]) | %[b] | | +tne | GEN_REG | remove(ALL) + allocate(AREG, GEN_REG = {IMMEDIATE,0}) + "ld a,%[1.1]" + "or %[1.2]" + "jr z,1f" + "inc %[b.2]" + "1:" + erase(%[b]) | %[b] | | +tge | GEN_REG | remove(ALL) + allocate(GEN_REG = {IMMEDIATE,0}) + "bit 7,%[1.1]" + "jr nz,1f" + "inc %[a.2]" + "1:" + erase(%[a]) | %[a] | | +tgt | GEN_REG | remove(ALL) + allocate(AREG, GEN_REG = {IMMEDIATE,0}) + "xor a" + "add a,%[1.1]" + "jp m,2f" + "jr nz,1f" + "xor a" + "add a,%[1.2]" + "jr z,2f" + "1:" + "inc %[b.2]" + "2:" + erase(%[b]) | %[b] | | + + + +/* G R O U P XIII : B R A N C H */ + + + +bra | | remove(ALL) + "jp $1" | | | +blt | GEN_REG GEN_REG | remove(ALL) allocate(AREG) + "ld a,%[2.2]" + "sub %[1.2]" + "ld a,%[2.1]" + "sbc a,%[1.1]" + "jp m,$1" | | | +ble | GEN_REG GEN_REG | remove(ALL) allocate(AREG) + "ld a,%[2.2]" + "sub %[1.2]" + "ld a,%[2.1]" + "sbc a,%[1.1]" + "jp m,$1" + "jr nz,1f" + "ld a,%[2.2]" + "cp %[1.2]" + "jr z,$1" + "1:" | | | +beq | GEN_REG GEN_REG | remove(ALL) allocate(AREG) + "ld a,%[2.2]" + "sub %[1.2]" + "jr nz,1f" + "ld a,%[2.1]" + "sbc a,%[1.1]" + "jr z,$1" + "1:" | | | +bne | GEN_REG GEN_REG | remove(ALL) allocate(AREG) + "ld a,%[2.2]" + "sub %[1.2]" + "jr nz,$1" + "ld a,%[2.1]" + "sbc a,%[1.1]" + "jr nz,$1" + erase(AA) | | | + +bge | GEN_REG GEN_REG | remove(ALL) allocate(AREG) + "ld a,%[2.2]" + "sub %[1.2]" + "ld a,%[2.1]" + "sbc a,%[1.1]" + "jp p,$1" | | | +bgt | GEN_REG GEN_REG_SCR | remove(ALL) allocate(AREG) + "ld a,%[2.2]" + "sub %[1.2]" + "ld %[2.2],a" + "ld a,%[2.1]" + "sbc a,%[1.1]" + "jp m,1f" + "jr nz,$1" + "xor a" + "cp %[2.2]" + "jr nz,$1" + "1:" + erase(%[2]) | | | +zlt | GEN_REG | remove(ALL) "bit 7,%[1.1]" + "jr nz,$1" | | | +zle | GEN_REG | remove(ALL) allocate(AREG) + "xor a" + "add a,%[1.1]" + "jp m,$1" + "jr nz,1f" + "xor a" + "add a,%[1.2]" + "jr z,$1" + "1:" | | | +zeq | GEN_REG | remove(ALL) allocate(AREG) + "ld a,%[1.1]" + "xor %[1.2]" + "jr z,$1" | | | +zne | GEN_REG | remove(ALL) allocate(AREG) + "ld a,%[1.1]" + "xor %[1.2]" + "jr nz,$1" | | | +zge | GEN_REG | remove(ALL) "bit 7,%[1.1]" + "jr z,$1" | | | +zgt | GEN_REG | remove(ALL) allocate(AREG) + "xor a" + "add a,%[1.1]" + "jp m,1f" + "jr nz,$1" + "xor a" + "add a,%[1.2]" + "jr nz,$1" + "1:" | | | + + + +/* G R O U P XIV : P R O C E D U R E C A L L S */ + + + +cai | HL_REG | remove(ALL) + allocate( GEN_REG ) + "ld %[a],1f" + "push %[a]" + "jp (hl)" + "1:" + | | | +cal | | remove(ALL) + allocate(ALL_REG) + "call $1" + | | | +lfr $1 == 2 | | remove(ALL) + "push de" + | | | +lfr $1 == 4 | STACK | "push de" + "push bc" | | | +lfr | | remove(ALL) + "call .lfr" + | | | +ret $1 == 0 | STACK | + remove(ALL) + allocate(HL_REG,LOCALBASE) + "push iy" + "pop hl" + "ld sp,hl" + "pop iy" + "ret" | | | +ret $1 == 2 | STACK | remove(ALL) + allocate(HL_REG,DE_REG,LOCALBASE) + "pop de" + "push iy" + "pop hl" + "ld sp,hl" + "pop iy" + "ret" | | | +ret $1 == 4 | STACK | + remove(ALL) + allocate(BC_REG, DE_REG, HL_REG, LOCALBASE) + "pop bc" + "pop de" + "push iy" + "pop hl" + "ld sp,hl" + "pop iy" + "ret" | | | +ret | | remove(ALL) + move({IMMEDIATE,$1},BC) + "call .ret" + erase(BC) | | | + + + +/* G R O U P XV : M I S C E L L A N E O U S */ + + + +asp $1 == 0 | | | | | +asp $1 > 0 | STACK | remove(ALL) allocate(HL_REG) + move({IMMEDIATE,$1} , HL) + "add hl,sp" + "ld sp,hl" + erase(HL) | | | +ass $1 == 2 | HL_REG_SCR STACK | + remove(ALL) + "add hl,sp" + "ld sp,hl" + erase(HL) | | | +blm $1 == 0 | | | | | +blm $1 > 0 |DE_REG_SCR HL_REG_SCR | + + allocate(BC_REG = {IMMEDIATE,$1} ) + "ldir" + erase(HL) + erase(BC) + erase(DE) | | | +bls $1 == 2 | BC_REG_SCR DE_REG_SCR HL_REG_SCR | + remove(MEM_ALL) + "ldir" + erase(HL) + erase(BC) + erase(DE) | | | +csa $1 == 2 | STACK | allocate(ALL_REG) + "jr .csa" | | | +csb $1 == 2 | STACK | allocate(ALL_REG) + "jr .csb" | | | +dus $1 == 2 | BC_REG_SCR | remove(MEM_ALL) allocate(HL_REG,DE_REG) + move({IMMEDIATE,0} , HL) + "add hl,sp" + "ld d,h" + "ld e,l" /* destination */ + "sbc hl,bc" /* source */ + "ld sp,hl" + "ex de,hl" + "ldir" + erase(HL) + erase(BC) | | | +dup $1 == 2 | ANY | | %[1] %[1] | | +dup $1 == 4 | ANY ANY | | %[2] %[1] %[2] %[1] | | +dup $1 == 6 | ANY ANY ANY | | %[3] %[2] %[1] %[3] %[2] %[1] | | +dup $1 > 6 | STACK | allocate(HL_REG,BC_REG,DE_REG) + move({IMMEDIATE,0} , HL) + "add hl,sp" + "ld d,h" + "ld e,l" /* destination */ + move ({IMMEDIATE,$1},BC) /* count */ + "sbc hl,bc" /* source */ + "ld sp,hl" + "ex de,hl" + "ldir" + erase(HL) + erase(BC) | | | +lor $1 == 0 | | "push iy" | | | +fil | | allocate(HL_REG) + move({EXTENDED_ADDR,$1},HL) + "ld (hol0+4),hl" + erase(HL) | | | +lor $1 == 1 | STACK | allocate(HL_REG) + move({IMMEDIATE,0} , HL) + "add hl,sp" + erase(HL) | HL | | +lor $1 == 2 | STACK | | {EXTENDED,".reghp"} | | +exg $1 == 2 | ANY ANY | | %[1] %[2] | | +exg | STACK | remove(MEM_ALL) + allocate(HL_REG) + move({IMMEDIATE,$1},HL) + "push hl" + "call .exg" | | | +gto | | remove(ALL) + allocate(ALL_REG) + move({EXTENDED_ADDR,$1},HL) + "call .gto" + | | | +lim | | | {EXTENDED,"(ignmask)"} | | +lin | | remove(ALL) allocate(HL_REG) + move({IMMEDIATE,$1},HL) + "ld (hol0),hl" + erase(HL) | | | +lni | | allocate(HL_REG) + "ld hl,hol0" + "inc (hl)" + erase(HL) | | | +lpb | | | | adp 4 | +nop | | allocate(ALL_REG) + "call .nop" | | | +rck $1 == 2 | STACK | allocate(ALL_REG) + "call .rck" + | | | +rtt | | | | ret 0| +sig | HL_REG_SCR | + + "ld (trapproc),hl" + "ld hl,trapproc" + | HL | | +sim | HL_REG | remove(MEM_ALL) + "pop hl" + "ld (ignmask),hl" | | | +str $1 == 0 | IMMEDIATE STACK | move({IMMEDIATE,%[1.off]} , LB) | | | +str $1 == 1 | STACK | allocate(HL_REG) + "pop hl" + "ld sp,hl" | | | +str $1 == 2 | STACK | allocate(ALL_REG) + "call .strhp" + | | | +trp| | remove(ALL) + allocate(ALL_REG) + "call .trp.z" | | | +mon | | remove(MEM_ALL) + "call .mon" | | | + + + +/* C O E R C I O N S */ + + + +/* from 4 bytes to 2 */ + + +/* to a register */ +| ANYTOK | allocate(ANY_REG) + move(%[1],%[a]) | %[a] | | + +| STACK | allocate(ANY_REG) + "pop %[a]" | %[a] | | +| LOCAL_ADDR | allocate(ANY_REG) + move(LB,%[a]) + "add %[a],%[1]" + | %[a] | | + + +/* between registers */ + +| GEN_REG | allocate(GEN_REG) + "ld %[a.1],%[1.1]" + "ld %[a.2],%[1.2]" | %[a] | | +| LOCALBASE | allocate(ANY_REG) + "push iy" + "pop %[a]" | %[a] | | + + +/*********** + ** MOVES ** + ***********/ + + + +MOVES: +(ANY,ANY,"ld %[2],%[1]") +(ANY1,ANY1,"ld %[2],%[1]") + + + + +/************ + * STACKS * + ************/ + + +STACKS: + +(ANY_REG, , "push %[1]" ) +(MEM_ALL, ANY_REG, move(%[1],%[a]) + "push %[a]" ) +(MEM_ALL, , "push hl" + move(%[1],HL) + "ex (sp),hl" ) -- 2.34.1