ref: 8bab7047ee0b8d7c786c84f6838d9fc64be579d5
dir: /src/cmd/as/target/x80/ins.c/
#include <stdlib.h>
#include <string.h>
#include <scc/scc.h>
#include "../../as.h"
#include "proc.h"
/*
* This implementation is based in:
* - Zilog Z80 CPU Specifications by Sean Young
* - Decoding Z80 opcodes - of use to disassembler and emulator
* writers - by Cristian Dinu.
*/
static int
getclass(Node *np)
{
if (np->addr != AREG)
return 0;
switch (np->sym->value) {
case AREG_C:
return RCLASS | PCLASS | QCLASS | CCCLASS | SSCLASS;
case AREG_A:
case AREG_B:
case AREG_D:
case AREG_E:
return RCLASS | PCLASS | QCLASS;
case AREG_H:
case AREG_L:
return RCLASS;
case AREG_IXL:
case AREG_IXH:
return PCLASS;
case AREG_IYL:
case AREG_IYH:
return QCLASS;
case AREG_HL:
return DDCLASS | QQCLASS;
case AREG_BC:
case AREG_DE:
return DDCLASS | QQCLASS | PPCLASS | RRCLASS;
case AREG_SP:
return DDCLASS | PPCLASS | RRCLASS;
case AREG_AF:
return QQCLASS;
case AREG_IX:
return PPCLASS;
case AREG_IY:
return RRCLASS;
case AREG_PO:
case AREG_PE:
case AREG_P:
case AREG_M:
return CCCLASS;
case AREG_NZ:
case AREG_Z:
case AREG_NC:
return CCCLASS | SSCLASS;
default:
return 0;
}
}
int
match(Op *op, Node **args)
{
unsigned char *p;
int arg, class, rep, opt;
Node *np;
if (!op->args)
return args == NULL;
opt = rep = 0;
for (p = op->args; arg = *p; ++p) {
if (rep)
--p;
if ((np = *args++) == NULL)
return (rep|opt) != 0;
switch (arg) {
case AOPT:
opt = 1;
break;
case AREP:
rep = 1;
break;
case AINDER_C:
arg = AREG_C;
goto indirect;
case AINDER_HL:
arg = AREG_HL;
goto indirect;
case AINDER_DE:
arg = AREG_DE;
goto indirect;
case AINDER_BC:
arg = AREG_BC;
goto indirect;
case AINDER_IX:
arg = AREG_IX;
goto indirect;
case AINDER_IY:
arg = AREG_IY;
goto indirect;
case AINDER_SP:
arg = AREG_SP;
indirect:
if (np->addr != AINDIR)
return 0;
np = np->left;
case AREG_A:
case AREG_I:
case AREG_R:
case AREG_F:
case AREG_HL:
case AREG_BC:
case AREG_DE:
case AREG_IY:
case AREG_IX:
case AREG_SP:
case AREG_AF:
case AREG_AF_:
if (np->addr != AREG || np->sym->value != arg)
return 0;
break;
case AREG_RCLASS:
class = RCLASS;
goto check_class;
case AREG_PCLASS:
class = PCLASS;
goto check_class;
case AREG_QCLASS:
class = QCLASS;
goto check_class;
case AREG_QQCLASS:
class = QQCLASS;
goto check_class;
case AREG_PPCLASS:
class = PPCLASS;
goto check_class;
case AREG_RRCLASS:
class = RRCLASS;
goto check_class;
case AREG_CCCLASS:
class = CCCLASS;
goto check_class;
case AREG_SSCLASS:
class = SSCLASS;
goto check_class;
case AREG_DDCLASS:
class = DDCLASS;
check_class:
if ((getclass(np) & class) == 0)
return 0;
break;
case AINDEX_IY:
arg = AREG_IY;
goto index_address;
case AINDEX_IX:
arg = AREG_IX;
index_address:
if (np->addr != AINDEX)
return 0;
np = np->left->left;
if (np->sym->value != arg)
return 0;
if (toobig(np, AIMM8))
error("overflow in index");
break;
case ARST:
if (np->addr != AIMM)
return 0;
if ((np->sym->value & ~0x38) != 0)
return 0;
break;
case AZERO:
if (np->addr != AIMM)
return 0;
break;
case AIMM3:
case AIMM8:
case AIMM16:
case AIMM32:
case AIMM64:
if (np->addr != AIMM)
return 0;
if (toobig(np, arg))
error("overflow in immediate operand");
break;
case ASYM:
if (np->addr != AIMM || np->op != IDEN)
return 0;
break;
case ADIRECT:
case ASTR:
if (np->addr != arg)
return 0;
break;
default:
abort();
}
}
return *args == NULL;
}
/*
* (expr) -> ADIRECT
* (REG) -> AINDIR
* (REG + expr) -> AINDEX
* (REG - expr) -> AINDEX
* expr (REG) -> AINDEX
*/
Node *
moperand(void)
{
int op;
Node *np, *dir, *off, *reg;
dir = off = reg = NULL;
if (accept('(')) {
if (yytoken != REG) {
dir = expr();
} else {
reg = getreg();
switch (yytoken) {
case '+':
case '-':
off = expr();
case ')':
break;
default:
unexpected();
}
}
} else {
off = expr();
expect('(');
reg = getreg();
}
expect(')');
if (dir) {
op = ADIRECT;
np = dir;
} else if (off) {
np = node(AREG_OFF, reg, off);
op = AINDEX;
} else {
np = reg;
op = AINDIR;
}
np = node(op, np, NULL);
np->addr = op;
return np;
}
static int
reg2int(Node *np)
{
switch (np->sym->value) {
case AREG_F:
case AREG_B: return 0;
case AREG_C: return 1;
case AREG_D: return 2;
case AREG_E: return 3;
case AREG_IXH:
case AREG_IYH:
case AREG_H: return 4;
case AREG_IXL:
case AREG_IYL:
case AREG_L: return 5;
case AREG_A: return 7;
case AREG_BC: return 0;
case AREG_DE: return 1;
case AREG_HL:
case AREG_IX:
case AREG_IY: return 2;
case AREG_AF:
case AREG_SP: return 3;
default: abort();
}
}
static int
cc2int(Node *np)
{
switch (np->sym->value) {
case AREG_NZ: return 0;
case AREG_Z: return 1;
case AREG_NC: return 2;
case AREG_C: return 3;
case AREG_PO: return 4;
case AREG_PE: return 5;
case AREG_P: return 6;
case AREG_M: return 7;
default: abort();
}
}
static int
ss2int(Node *np)
{
switch (np->sym->value) {
case AREG_NZ: return 4;
case AREG_Z: return 5;
case AREG_NC: return 6;
case AREG_C: return 7;
default: abort();
}
}
void
dir(Op *op, Node **args)
{
Node *imm;
unsigned char buf[4];
unsigned val;
int n = op->size;
imm = (args[1]->addr == ADIRECT) ? args[1] : args[0];
imm = imm->left;
memcpy(buf, op->bytes, n);
val = imm->sym->value;
buf[n-1] = val >> 8;
buf[n-2] = val;
emit(buf, n);
}
void
ld8(Op *op, Node **args)
{
Node *par1 = args[0], *par2 = args[1];
int n = op->size, i = n;;
unsigned regval = 0;
unsigned char buf[4];
memcpy(buf, op->bytes, n);
if (par1->addr == AREG)
regval |= reg2int(par1) << 3;
if (par2->addr == AREG)
regval |= reg2int(par2);
else if (par2->addr == AIMM)
buf[--i] = par2->sym->value;
buf[--i] |= regval;
emit(buf, n);
}
void
alu16(Op *op, Node **args)
{
Node *par;
int n = op->size;
unsigned val;
unsigned char buf[4];
par = (args[1]) ? args[1] : args[0];
val = reg2int(par);
memcpy(buf, op->bytes, n);
buf[n-1] |= val << 4;
emit(buf, n);
}
void
ld16(Op *op, Node **args)
{
Node *dst, *src, *tmp;
int n = op->size;
unsigned val;
unsigned char buf[4];
dst = args[0];
src = args[1];
if (!src) {
alu16(op, args);
return;
}
if (dst->addr != AREG) {
tmp = src;
src = dst;
dst = tmp;
}
memcpy(buf, op->bytes, n);
if (src->addr == ADIRECT)
src = src->left;
val = src->sym->value;
buf[n-1] = val >> 8;
buf[n-2] = val;
buf[n-3] |= reg2int(dst) << 4;
emit(buf, n);
}
void
alu8(Op *op, Node **args)
{
Node *par = args[1];
unsigned char buf[4];
int n = op->size, shift;
unsigned val;
if (args[1]) {
shift = 0;
par = args[1];
} else {
shift = 3;
par = args[0];
}
switch (par->addr) {
case AIMM:
val = par->sym->value;
break;
case AREG:
val = reg2int(par) << shift;
break;
case AINDEX:
val = par->left->right->sym->value;
break;
case AINDIR:
val = 0;
break;
default:
abort();
}
memcpy(buf, op->bytes, n);
buf[n-1] |= val;
emit(buf, n);
}
void
idx(Op *op, Node **args)
{
Node *tmp, *idx, *imm, *reg;
unsigned char buf[4];
int n = op->size, i = n, shift = 0;
imm = reg = NULL;
if (args[0]->addr != AINDEX) {
shift = 3;
tmp = args[0];
args[0] = args[1];
args[1] = tmp;
}
idx = args[0]->left->right;
if (args[1]->addr == AREG)
reg = args[1];
else
imm = args[1];
memcpy(buf, op->bytes, n);
if (imm)
buf[--i] = imm->sym->value;
buf[--i] = idx->sym->value;
if (reg)
buf[--i] |= reg2int(reg) << shift;
emit(buf, n);
}
void
inout(Op *op, Node **args)
{
Node *port, *value;
unsigned val;
int n = op->size;
unsigned char buf[5];
port = args[0];
value = args[1];
if (port->addr != ADIRECT && port->addr != AINDIR) {
value = port;
port = args[1];
}
if (port->addr == ADIRECT)
val = port->left->sym->value;
else if (value->addr == AREG)
val = reg2int(value) << 3;
else
val = 0;
memcpy(buf, op->bytes, n);
buf[n-1] |= val;
emit(buf, n);
}
void
rot_bit(Op *op, Node **args)
{
Node *par = args[0];
unsigned char buf[5];
int n = op->size;
unsigned val, npar = 0;
memcpy(buf, op->bytes, n);
par = args[0];
if (par->addr == AIMM) {
buf[n-1] |= par->sym->value << 3;
par = args[npar = 1];
}
switch (par->addr) {
case AINDEX:
val = par->left->right->sym->value;
buf[n-2] = val;
par = args[npar+1];
if (!par)
break;
case AREG:
val = reg2int(par);
buf[n-1] |= val;
case AINDIR:
break;
default:
abort();
}
emit(buf, n);
}
void
im(Op *op, Node **args)
{
unsigned val = args[0]->sym->value;
unsigned char buf[4];
int n = op->size;
if (val > 0)
++val;
memcpy(buf, op->bytes, n);
buf[n-1] |= val << 3;
emit(buf, n);
}
void
branch(int relative, Op *op, Node **args)
{
unsigned char buf[4];
Node *flag, *imm;
int n = op->size, i = n;
unsigned val;
int (*fun)(Node *);
flag = imm = NULL;
if (args[0]->addr == AREG) {
flag = args[0];
imm = args[1];
} else if (args[0]->addr == AIMM) {
imm = args[0];
}
memcpy(buf, op->bytes, n);
if (imm) {
val = imm->sym->value;
if (!relative)
buf[--i] = val >> 8;
else
val -= cursec->curpc - 2;
buf[--i] = val;
}
if (flag) {
fun = (relative) ? ss2int : cc2int;
buf[--i] |= (*fun)(flag) << 3;
}
emit(buf, n);
}
void
jp(Op *op, Node **args)
{
branch(0, op, args);
}
void
jr(Op *op, Node **args)
{
branch(1, op, args);
}
void
rst(Op *op, Node **args)
{
unsigned char buf[1];
buf[0] = op->bytes[0];
buf[0] |= args[0]->sym->value;
emit(buf, 1);
}