/*
casm - Simple, portable assembler
Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.co.uk)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-------------------------------------------------------------------------
Z80 Assembler
*/
#include
#include
#include
#include "global.h"
#include "expr.h"
#include "label.h"
#include "parse.h"
#include "cmd.h"
#include "codepage.h"
#include "varchar.h"
#include "z80.h"
/* ---------------------------------------- TYPES AND GLOBALS
*/
typedef enum
{
A8,
B8,
C8,
D8,
E8,
H8,
L8,
F8,
IXH8,
IXL8,
IYH8,
IYL8,
I8,
R8,
AF16,
AF16_ALT,
BC16,
DE16,
HL16,
SP16,
IX16,
IY16,
BC_ADDRESS,
DE_ADDRESS,
HL_ADDRESS,
SP_ADDRESS,
IX_ADDRESS,
IY_ADDRESS,
IX_OFFSET,
IY_OFFSET,
C_PORT,
ADDRESS,
VALUE,
INVALID_REG
} RegisterMode;
typedef enum
{
NZ_FLAG,
Z_FLAG,
NC_FLAG,
C_FLAG,
PO_FLAG,
PE_FLAG,
P_FLAG,
M_FLAG
} ProcessorFlag;
typedef enum
{
IS_NORMAL_8_BIT = 0x001,
IS_SPECIAL_8_BIT = 0x002,
IS_16_BIT = 0x004,
IS_MEMORY = 0x008,
IS_INDEX_X = 0x010,
IS_INDEX_Y = 0x020,
IS_SP = 0x040,
IS_VALUE = 0x080,
IS_SPECIAL_16_BIT = 0x100,
IS_ALTERNATE = 0x200,
IS_IO_PORT = 0x400
} RegisterType;
#define IsNormal8Bit(t) ((t) & IS_NORMAL_8_BIT)
#define IsSpecial8Bit(t) ((t) & IS_SPECIAL_8_BIT)
#define Is16Bit(t) ((t) & IS_16_BIT)
#define IsMemory(t) ((t) & IS_MEMORY)
#define IsIndexX(t) ((t) & IS_INDEX_X)
#define IsIndexY(t) ((t) & IS_INDEX_Y)
#define IsIndex(t) (IsIndexX(t) || IsIndexY(t))
#define IsSP(t) ((t) & IS_SP)
#define IsAddress(t) (((t) & IS_VALUE) && ((t) & IS_MEMORY))
#define IsValue(t) ((t) & IS_VALUE)
#define IsSimpleValue(t) ((t) == IS_VALUE)
#define IsAlternate(t) ((t) & IS_ALTERNATE)
#define SHIFT_IX 0xdd
#define SHIFT_IY 0xfd
#define WriteShift(r) \
do \
{ \
switch(r) \
{ \
case IXH8: \
case IXL8: \
case IX16: \
case IX_OFFSET: \
case IX_ADDRESS: \
PCWrite(SHIFT_IX); \
break; \
case IYH8: \
case IYL8: \
case IY16: \
case IY_OFFSET: \
case IY_ADDRESS: \
PCWrite(SHIFT_IY); \
break; \
default: \
break; \
} \
} while(0)
#define WriteEitherShift(r1,r2) \
do \
{ \
int h_handled = FALSE; \
switch(r1) \
{ \
case IXH8: \
case IXL8: \
case IX16: \
case IX_OFFSET: \
case IX_ADDRESS: \
PCWrite(SHIFT_IX); \
h_handled = TRUE; \
break; \
case IYH8: \
case IYL8: \
case IY16: \
case IY_OFFSET: \
case IY_ADDRESS: \
PCWrite(SHIFT_IY); \
h_handled = TRUE; \
break; \
default: \
break; \
} \
if (!h_handled) \
switch(r2) \
{ \
case IXH8: \
case IXL8: \
case IX16: \
case IX_OFFSET: \
case IX_ADDRESS: \
PCWrite(SHIFT_IX); \
h_handled = TRUE; \
break; \
case IYH8: \
case IYL8: \
case IY16: \
case IY_OFFSET: \
case IY_ADDRESS: \
PCWrite(SHIFT_IY); \
h_handled = TRUE; \
break; \
default: \
break; \
} \
} while(0)
#define WriteOffset(r,o) \
do \
{ \
switch(r) \
{ \
case IX_OFFSET: \
case IY_OFFSET: \
PCWrite(o); \
break; \
default: \
break; \
} \
} while(0)
#define CheckRange(arg,num,min,max) \
do \
{ \
if (IsFinalPass() && (num < min || num > max)) \
{ \
snprintf(err, errsize, "%s: outside valid " \
"range of %d - %d", arg, min, max); \
return CMD_FAILED; \
} \
} while(0)
#define CheckOffset(a,o) CheckRange(a,0,-128,127)
static const int flag_bitmask[] =
{
0x00, /* NZ_FLAG */
0x01, /* Z_FLAG */
0x02, /* NC_FLAG */
0x03, /* C_FLAG */
0x04, /* PO_FLAG */
0x05, /* PE_FLAG */
0x06, /* P_FLAG */
0x07 /* M_FLAG */
};
static const char *flag_text[] =
{
"NZ", /* NZ_FLAG */
"Z", /* Z_FLAG */
"NC", /* NC_FLAG */
"C", /* C_FLAG */
"PO", /* PO_FLAG */
"PE", /* PE_FLAG */
"P", /* P_FLAG */
"M", /* M_FLAG */
NULL
};
static const char *register_mode_name[] =
{
"A",
"B",
"C",
"D",
"E",
"H",
"L",
"F",
"IXH",
"IXL",
"IYH",
"IYL",
"I",
"R",
"AF",
"AF'",
"BC",
"DE",
"HL",
"SP",
"IX",
"IY",
"(BC)",
"(DE)",
"(HL)",
"(SP)",
"(IX)",
"(IY)",
"(IX+offset)",
"(IY+offset)",
"(C)",
"(address)",
"value",
0
};
static const int register_bitmask[] =
{
0x7, /* A8 */
0x0, /* B8 */
0x1, /* C8 */
0x2, /* D8 */
0x3, /* E8 */
0x4, /* H8 */
0x5, /* L8 */
0x6, /* F8 */
0x4, /* IXH8 - In effect H */
0x5, /* IXL8 - In effect L */
0x4, /* IYH8 - In effect H */
0x5, /* IYL8 - In effect L */
-1, /* I8 */
-1, /* R8 */
0x3, /* AF16 */
-1, /* AF16_ALT */
0x0, /* BC16 */
0x1, /* DE16 */
0x2, /* HL16 */
0x3, /* SP16 */
0x2, /* IX16 - In effect HL */
0x2, /* IY16 - In effect HL */
0x0, /* BC_ADDRESS */
0x0, /* DE_ADDRESS */
0x0, /* HL_ADDRESS */
0x0, /* SP_ADDRESS */
0x0, /* IX_ADDRESS */
0x0, /* IY_ADDRESS */
0x0, /* IX_OFFSET */
0x0, /* IY_OFFSET */
0x0, /* C_PORT */
0x0, /* ADDRESS */
0x0, /* VALUE */
};
typedef struct
{
RegisterMode mode;
int quote;
int starts_with;
int take_offset;
int take_value;
const char *ident;
RegisterType type;
} RegisterModeTable;
static RegisterModeTable register_mode_table[] =
{
{
A8,
0,
FALSE,
FALSE,
FALSE,
"A",
IS_NORMAL_8_BIT
},
{
B8,
0,
FALSE,
FALSE,
FALSE,
"B",
IS_NORMAL_8_BIT
},
{
C8,
0,
FALSE,
FALSE,
FALSE,
"C",
IS_NORMAL_8_BIT
},
{
D8,
0,
FALSE,
FALSE,
FALSE,
"D",
IS_NORMAL_8_BIT
},
{
E8,
0,
FALSE,
FALSE,
FALSE,
"E",
IS_NORMAL_8_BIT
},
{
H8,
0,
FALSE,
FALSE,
FALSE,
"H",
IS_NORMAL_8_BIT
},
{
L8,
0,
FALSE,
FALSE,
FALSE,
"L",
IS_NORMAL_8_BIT
},
{
F8,
0,
FALSE,
FALSE,
FALSE,
"F",
IS_SPECIAL_8_BIT
},
{
IXL8,
0,
FALSE,
FALSE,
FALSE,
"IXL",
IS_NORMAL_8_BIT|IS_INDEX_X
},
{
IXH8,
0,
FALSE,
FALSE,
FALSE,
"IXH",
IS_NORMAL_8_BIT|IS_INDEX_X
},
{
IYL8,
0,
FALSE,
FALSE,
FALSE,
"IYL",
IS_NORMAL_8_BIT|IS_INDEX_Y
},
{
IYH8,
0,
FALSE,
FALSE,
FALSE,
"IYH",
IS_NORMAL_8_BIT|IS_INDEX_Y
},
{
I8,
0,
FALSE,
FALSE,
FALSE,
"I",
IS_SPECIAL_8_BIT
},
{
R8,
0,
FALSE,
FALSE,
FALSE,
"R",
IS_SPECIAL_8_BIT
},
{
AF16,
0,
FALSE,
FALSE,
FALSE,
"AF",
IS_SPECIAL_16_BIT
},
{
AF16_ALT,
0,
FALSE,
FALSE,
FALSE,
"AF'",
IS_SPECIAL_16_BIT|IS_ALTERNATE
},
{
BC16,
0,
FALSE,
FALSE,
FALSE,
"BC",
IS_16_BIT
},
{
DE16,
0,
FALSE,
FALSE,
FALSE,
"DE",
IS_16_BIT
},
{
HL16,
0,
FALSE,
FALSE,
FALSE,
"HL",
IS_16_BIT
},
{
IX16,
0,
FALSE,
FALSE,
FALSE,
"IX",
IS_16_BIT|IS_INDEX_X
},
{
IY16,
0,
FALSE,
FALSE,
FALSE,
"IY",
IS_16_BIT|IS_INDEX_Y
},
{
SP16,
0,
FALSE,
FALSE,
FALSE,
"SP",
IS_16_BIT|IS_SP
},
{
BC_ADDRESS,
'(',
FALSE,
FALSE,
FALSE,
"BC",
IS_16_BIT|IS_MEMORY
},
{
DE_ADDRESS,
'(',
FALSE,
FALSE,
FALSE,
"DE",
IS_16_BIT|IS_MEMORY
},
{
HL_ADDRESS,
'(',
FALSE,
FALSE,
FALSE,
"HL",
IS_16_BIT|IS_MEMORY
},
{
IX_ADDRESS,
'(',
FALSE,
FALSE,
FALSE,
"IX",
IS_16_BIT|IS_MEMORY|IS_INDEX_X
},
{
IY_ADDRESS,
'(',
FALSE,
FALSE,
FALSE,
"IY",
IS_16_BIT|IS_MEMORY|IS_INDEX_Y
},
{
IX_OFFSET,
'(',
TRUE,
TRUE,
FALSE,
"IX",
IS_16_BIT|IS_MEMORY|IS_INDEX_X
},
{
IY_OFFSET,
'(',
TRUE,
TRUE,
FALSE,
"IY",
IS_16_BIT|IS_MEMORY|IS_INDEX_Y
},
{
SP_ADDRESS,
'(',
FALSE,
FALSE,
FALSE,
"SP",
IS_SPECIAL_16_BIT|IS_MEMORY|IS_SP
},
{
C_PORT,
'(',
FALSE,
FALSE,
FALSE,
"C",
IS_IO_PORT
},
/* These cheat -- basically anything that doesn't match until here is either
an address or a value. No distinction is made between 8/16 bit values.
*/
{
ADDRESS,
'(',
TRUE,
FALSE,
TRUE,
"",
IS_VALUE|IS_MEMORY
},
{
VALUE,
0,
TRUE,
FALSE,
TRUE,
"",
IS_VALUE
},
{0}
};
typedef enum
{
WRITE_BYTE_LHS = -1,
WRITE_WORD_LHS = -2,
WRITE_BYTE_RHS = -3,
WRITE_WORD_RHS = -4
} StreamCodes;
typedef struct
{
RegisterMode lhs;
RegisterMode rhs;
int code[10];
} RegisterPairCodes;
#define NUM_REGISTER_CODES(a) ((sizeof a)/(sizeof a[1]))
/* ---------------------------------------- PRIVATE FUNCTIONS
*/
static int CalcRegisterMode(const char *arg, int quote,
RegisterMode *mode,
RegisterType *type,
int *offset,
char *err, size_t errsize)
{
int f;
if (IsNullOrEmpty(arg))
{
snprintf(err, errsize, "empty argument supplied");
return FALSE;
}
for(f = 0; register_mode_table[f].ident; f++)
{
RegisterModeTable *t = register_mode_table + f;
if (quote == t->quote)
{
int match;
if (t->starts_with)
{
match = CompareStart(arg, t->ident);
}
else
{
match = CompareString(arg, t->ident);
}
if (match)
{
*mode = t->mode;
*type = t->type;
*offset = 0;
if (t->take_offset || t->take_value)
{
size_t l = strlen(t->ident);
if (!ExprEval(arg + l, offset))
{
snprintf(err, errsize, "%s: expression error: %s",
arg, ExprError());
return FALSE;
}
}
if (t->take_offset)
{
if (IsFinalPass() && (*offset < -128 || *offset > 127))
{
snprintf(err, errsize, "%s: outside valid range "
"for offset", arg);
return FALSE;
}
}
return TRUE;
}
}
}
snprintf(err, errsize, "%s: couldn't calculate register/addressing mode",
arg);
return FALSE;
}
static int CalcFlagMode(const char *arg, ProcessorFlag *flag, int *mask,
char *err, size_t errsize)
{
int f;
for(f = 0; flag_text[f]; f++)
{
if (CompareString(arg, flag_text[f]))
{
*flag = f;
*mask = flag_bitmask[f];
return TRUE;
}
}
snprintf(err, errsize, "%s: unknown flag", arg);
return FALSE;
}
static int WriteRegisterPairModes(const RegisterPairCodes *codes, size_t count,
RegisterMode lhs, RegisterMode rhs,
int val_lhs, int val_rhs,
char *err, size_t errsize)
{
size_t f;
for(f = 0; f < count; f++)
{
if (codes[f].lhs == lhs && codes[f].rhs == rhs)
{
int r;
for(r = 0; codes[f].code[r]; r++)
{
switch(codes[f].code[r])
{
case WRITE_BYTE_LHS:
PCWrite(val_lhs);
break;
case WRITE_WORD_LHS:
PCWriteWord(val_lhs);
break;
case WRITE_BYTE_RHS:
PCWrite(val_rhs);
break;
case WRITE_WORD_RHS:
PCWriteWord(val_rhs);
break;
default:
PCWrite(codes[f].code[r]);
break;
}
}
return TRUE;
}
}
snprintf(err, errsize, "LD: no code generation for register pair %s,%s",
register_mode_name[lhs], register_mode_name[rhs]);
return FALSE;
}
/* Assume accumulator if only one argument
*/
static CommandStatus AssumeAccumulator(int argc, char *argv[], int quoted[],
char *err, size_t errsize,
RegisterMode *r1, RegisterMode *r2,
RegisterType *t1, RegisterType *t2,
int *off1, int *off2)
{
CMD_ARGC_CHECK(2);
if (argc == 2)
{
CalcRegisterMode("A", 0, r1, t1, off1, err, errsize);
if (!CalcRegisterMode(argv[1], quoted[1], r2, t2, off2,
err, errsize))
{
return CMD_FAILED;
}
}
else
{
if (!CalcRegisterMode(argv[1], quoted[1], r1, t1, off1, err, errsize))
{
return CMD_FAILED;
}
if (!CalcRegisterMode(argv[2], quoted[2], r2, t2, off2, err, errsize))
{
return CMD_FAILED;
}
}
return CMD_OK;
}
/* Returns true if the passed register is any of the passed ones. The list of
registers is terminated with INVALID_REG
*/
static int IsAnyOf(RegisterMode reg, ...)
{
va_list ap;
RegisterMode m;
va_start(ap, reg);
m = va_arg(ap, RegisterMode);
while(m != INVALID_REG)
{
if (reg == m)
{
return TRUE;
}
m = va_arg(ap, RegisterMode);
}
return FALSE;
}
static CommandStatus IllegalArgs(int argc, char *argv[], int quoted[],
char *err, size_t errsize)
{
Varchar *str;
int f;
str = VarcharCreate(NULL);
switch(argc)
{
case 0:
VarcharPrintf(str, "no command/arguments");
break;
case 1:
VarcharPrintf(str, "%s: no arguments", argv[0]);
break;
case 2:
VarcharPrintf(str, "%s: illegal argument", argv[0]);
break;
default:
VarcharPrintf(str, "%s: illegal arguments", argv[0]);
break;
}
for(f = 1; f < argc; f++)
{
if (f == 1)
{
VarcharAdd(str, " ");
}
else
{
VarcharAdd(str, ", ");
}
if (quoted[f] && quoted[f] == '(')
{
VarcharPrintf(str, "(%s)", argv[f]);
}
else if (quoted[f])
{
VarcharPrintf(str, "%c%s%c", quoted[f], argv[f], quoted[f]);
}
else
{
VarcharAdd(str, argv[f]);
}
}
snprintf(err, errsize, "%s", VarcharContents(str));
VarcharFree(str);
return CMD_FAILED;
}
/* ---------------------------------------- COMMAND HANDLERS
*/
static CommandStatus LD(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
A8, BC_ADDRESS, {0x0a},
A8, DE_ADDRESS, {0x1a},
A8, ADDRESS, {0x3a, WRITE_WORD_RHS},
BC_ADDRESS, A8, {0x02},
DE_ADDRESS, A8, {0x12},
ADDRESS, A8, {0x32, WRITE_WORD_RHS},
A8, I8, {0xed, 0x57},
A8, R8, {0xed, 0x5f},
I8, A8, {0xed, 0x47},
R8, A8, {0xed, 0x4f}
};
RegisterMode r1, r2;
RegisterType t1, t2;
int off1, off2;
CMD_ARGC_CHECK(3);
if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
{
return CMD_FAILED;
}
if (!CalcRegisterMode(argv[2], quoted[2], &r2, &t2, &off2, err, errsize))
{
return CMD_FAILED;
}
if ((IsIndexX(t1) && IsIndexY(t2)) ||
(IsIndexY(t1) && IsIndexX(t2)))
{
snprintf(err, errsize,
"%s: can't have mixed IX/IY registers", argv[0]);
return CMD_FAILED;
}
/* LD r,r'
*/
if (IsNormal8Bit(t1) && IsNormal8Bit(t2))
{
CommandStatus s = CMD_OK;
if ((IsIndex(t1) || IsIndex(t2)) &&
(IsAnyOf(r1, H8, L8, INVALID_REG) ||
IsAnyOf(r2, H8, L8, INVALID_REG)))
{
snprintf(err, errsize, "%s: H/L will actually be the index "
"register low/high register", argv[0]);
s = CMD_OK_WARNING;
}
WriteEitherShift(r1,r2);
PCWrite(0x40 | register_bitmask[r1] << 3 | register_bitmask[r2]);
return s;
}
/* LD r,n
*/
if (IsNormal8Bit(t1) && IsSimpleValue(t2))
{
WriteShift(r1);
PCWrite(register_bitmask[r1] << 3 | 0x6);
PCWrite(off2);
return CMD_OK;
}
/* LD r,(HL)/(IX+d)/(IY+d)
*/
if ((IsNormal8Bit(t1) && !IsIndex(t1)) &&
(r2 == HL_ADDRESS || IsIndex(t2)))
{
WriteShift(r2);
PCWrite(0x46 | register_bitmask[r1] << 3);
WriteOffset(r2, off2);
return CMD_OK;
}
/* LD (HL)/(IX+d)/(IY+d),r
*/
if ((IsNormal8Bit(t2) && !IsIndex(t2)) &&
(r1 == HL_ADDRESS || IsIndex(t1)))
{
WriteShift(r1);
PCWrite(0x70 | register_bitmask[r2]);
WriteOffset(r1, off1);
return CMD_OK;
}
/* LD (HL)/(IX+d)/(IY+d),n
*/
if ((r1 == HL_ADDRESS || r1 == IX_OFFSET || r1 == IY_OFFSET) && r2 == VALUE)
{
WriteShift(r1);
PCWrite(0x36);
WriteOffset(r1, off1);
PCWrite(off2);
return CMD_OK;
}
/* LD rr,nn
*/
if (Is16Bit(t1) && !IsMemory(t1) && r2 == VALUE)
{
WriteShift(r1);
PCWrite(register_bitmask[r1] << 4 | 0x01);
PCWriteWord(off2);
return CMD_OK;
}
/* LD HL/IX/IY,(nn)
*/
if ((r1 == HL16 || r1 == IX16 || r1 == IY16) && r2 == ADDRESS)
{
WriteShift(r1);
PCWrite(0x2a);
PCWriteWord(off2);
return CMD_OK;
}
/* LD rr,(nn)
*/
if (Is16Bit(t1) && !IsMemory(t1) && r2 == ADDRESS)
{
PCWrite(0xed);
PCWrite(0x4b | register_bitmask[r1] << 4);
PCWriteWord(off2);
return CMD_OK;
}
/* LD (nn),HL/IX/IY
*/
if ((r2 == HL16 || r2 == IX16 || r2 == IY16) && r1 == ADDRESS)
{
WriteShift(r2);
PCWrite(0x22);
PCWriteWord(off1);
return CMD_OK;
}
/* LD (nn),rr
*/
if (Is16Bit(t2) && !IsMemory(t2) && r1 == ADDRESS)
{
PCWrite(0xed);
PCWrite(0x43 | register_bitmask[r2] << 4);
PCWriteWord(off1);
return CMD_OK;
}
/* LD SP,HL/IX/IY
*/
if ((r2 == HL16 || r2 == IX16 || r2 == IY16) && r1 == SP16)
{
WriteShift(r2);
PCWrite(0xf9);
return CMD_OK;
}
/* Custom opcode generation using the codes table
*/
if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
r1, r2, off1, off2, err, errsize))
{
return CMD_FAILED;
}
return CMD_OK;
}
static CommandStatus PUSH(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
RegisterMode r1;
RegisterType t1;
int off1;
CMD_ARGC_CHECK(2);
if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
{
return CMD_FAILED;
}
/* PUSH HL/IX/IY
*/
if (r1 == HL16 || r1 == IX16 || r1 == IY16)
{
WriteShift(r1);
PCWrite(0xe5);
return CMD_OK;
}
/* PUSH rr
*/
if (r1 == AF16 || r1 == BC16 || r1 == DE16)
{
PCWrite(0xc5 | register_bitmask[r1] << 4);
return CMD_OK;
}
snprintf(err, errsize, "%s: invalid argument %s", argv[0], argv[1]);
return CMD_OK;
}
static CommandStatus POP(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
RegisterMode r1;
RegisterType t1;
int off1;
CMD_ARGC_CHECK(2);
if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
{
return CMD_FAILED;
}
/* POP HL/IX/IY
*/
if (r1 == HL16 || r1 == IX16 || r1 == IY16)
{
WriteShift(r1);
PCWrite(0xe1);
return CMD_OK;
}
/* POP rr
*/
if (r1 == AF16 || r1 == BC16 || r1 == DE16)
{
PCWrite(0xc1 | register_bitmask[r1] << 4);
return CMD_OK;
}
snprintf(err, errsize, "%s: invalid argument %s", argv[0], argv[1]);
return CMD_OK;
}
static CommandStatus EX(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
DE16, HL16, {0xeb},
HL16, DE16, {0xeb},
AF16, AF16_ALT, {0x08},
AF16_ALT, AF16, {0x08},
SP_ADDRESS, HL16, {0xe3},
HL16, SP_ADDRESS, {0xe3},
SP_ADDRESS, IX16, {SHIFT_IX, 0xe3},
IX16, SP_ADDRESS, {SHIFT_IX, 0xe3},
SP_ADDRESS, IY16, {SHIFT_IY, 0xe3},
IY16, SP_ADDRESS, {SHIFT_IY, 0xe3}
};
RegisterMode r1, r2;
RegisterType t1, t2;
int off1, off2;
CMD_ARGC_CHECK(3);
if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
{
return CMD_FAILED;
}
if (!CalcRegisterMode(argv[2], quoted[2], &r2, &t2, &off2, err, errsize))
{
return CMD_FAILED;
}
if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
r1, r2, off1, off2, err, errsize))
{
return CMD_FAILED;
}
return CMD_OK;
}
static CommandStatus ADD(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
A8, VALUE, {0xc6, WRITE_BYTE_RHS},
A8, HL_ADDRESS, {0x86},
A8, IX_OFFSET, {SHIFT_IX, 0x86, WRITE_BYTE_RHS},
A8, IY_OFFSET, {SHIFT_IY, 0x86, WRITE_BYTE_RHS},
};
RegisterMode r1, r2;
RegisterType t1, t2;
int off1, off2;
if (AssumeAccumulator(argc, argv, quoted, err, errsize,
&r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
{
return CMD_FAILED;
}
/* ADD A,r
*/
if (r1 == A8 && IsNormal8Bit(t2))
{
WriteShift(r2);
PCWrite(0x80 | register_bitmask[r2]);
return CMD_OK;
}
/* ADD HL,rr
*/
if (r1 == HL16 && IsAnyOf(r2, BC16, DE16, HL16, SP16, INVALID_REG))
{
PCWrite(0x09 | register_bitmask[r2] << 4);
return CMD_OK;
}
/* ADD IX,rr
*/
if (r1 == IX16 && IsAnyOf(r2, BC16, DE16, IX16, SP16, INVALID_REG))
{
PCWrite(SHIFT_IX);
PCWrite(0x09 | register_bitmask[r2] << 4);
return CMD_OK;
}
/* ADD IY,rr
*/
if (r1 == IY16 && IsAnyOf(r2, BC16, DE16, IY16, SP16, INVALID_REG))
{
PCWrite(SHIFT_IY);
PCWrite(0x09 | register_bitmask[r2] << 4);
return CMD_OK;
}
if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
r1, r2, off1, off2, err, errsize))
{
return CMD_FAILED;
}
return CMD_OK;
}
static CommandStatus ADC(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
A8, VALUE, {0xce, WRITE_BYTE_RHS},
A8, HL_ADDRESS, {0x8e},
A8, IX_OFFSET, {SHIFT_IX, 0x8e, WRITE_BYTE_RHS},
A8, IY_OFFSET, {SHIFT_IY, 0x8e, WRITE_BYTE_RHS},
};
RegisterMode r1, r2;
RegisterType t1, t2;
int off1, off2;
if (AssumeAccumulator(argc, argv, quoted, err, errsize,
&r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
{
return CMD_FAILED;
}
/* ADC A,r
*/
if (r1 == A8 && IsNormal8Bit(t2))
{
WriteShift(r2);
PCWrite(0x88 | register_bitmask[r2]);
return CMD_OK;
}
/* ADC HL,rr
*/
if (r1 == HL16 && IsAnyOf(r2, BC16, DE16, HL16, SP16, INVALID_REG))
{
PCWrite(0xed);
PCWrite(0x4a | register_bitmask[r2] << 4);
return CMD_OK;
}
if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
r1, r2, off1, off2, err, errsize))
{
return CMD_FAILED;
}
return CMD_OK;
}
static CommandStatus SUB(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
A8, VALUE, {0xd6, WRITE_BYTE_RHS},
A8, HL_ADDRESS, {0x96},
A8, IX_OFFSET, {SHIFT_IX, 0x96, WRITE_BYTE_RHS},
A8, IY_OFFSET, {SHIFT_IY, 0x96, WRITE_BYTE_RHS},
};
RegisterMode r1, r2;
RegisterType t1, t2;
int off1, off2;
if (AssumeAccumulator(argc, argv, quoted, err, errsize,
&r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
{
return CMD_FAILED;
}
/* SUB A,r
*/
if (r1 == A8 && IsNormal8Bit(t2))
{
WriteShift(r2);
PCWrite(0x90 | register_bitmask[r2]);
return CMD_OK;
}
if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
r1, r2, off1, off2, err, errsize))
{
return CMD_FAILED;
}
return CMD_OK;
}
static CommandStatus SBC(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
A8, VALUE, {0xde, WRITE_BYTE_RHS},
A8, HL_ADDRESS, {0x9e},
A8, IX_OFFSET, {SHIFT_IX, 0x9e, WRITE_BYTE_RHS},
A8, IY_OFFSET, {SHIFT_IY, 0x9e, WRITE_BYTE_RHS},
};
RegisterMode r1, r2;
RegisterType t1, t2;
int off1, off2;
if (AssumeAccumulator(argc, argv, quoted, err, errsize,
&r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
{
return CMD_FAILED;
}
/* SBC A,r
*/
if (r1 == A8 && IsNormal8Bit(t2))
{
WriteShift(r2);
PCWrite(0x98 | register_bitmask[r2]);
return CMD_OK;
}
/* SBC HL,rr
*/
if (r1 == HL16 && IsAnyOf(r2, BC16, DE16, HL16, SP16, INVALID_REG))
{
PCWrite(0xed);
PCWrite(0x42 | register_bitmask[r2] << 4);
return CMD_OK;
}
if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
r1, r2, off1, off2, err, errsize))
{
return CMD_FAILED;
}
return CMD_OK;
}
static CommandStatus AND(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
A8, VALUE, {0xe6, WRITE_BYTE_RHS},
A8, HL_ADDRESS, {0xa6},
A8, IX_OFFSET, {SHIFT_IX, 0xa6, WRITE_BYTE_RHS},
A8, IY_OFFSET, {SHIFT_IY, 0xa6, WRITE_BYTE_RHS},
};
RegisterMode r1, r2;
RegisterType t1, t2;
int off1, off2;
if (AssumeAccumulator(argc, argv, quoted, err, errsize,
&r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
{
return CMD_FAILED;
}
/* AND A,r
*/
if (r1 == A8 && IsNormal8Bit(t2))
{
WriteShift(r2);
PCWrite(0xa0 | register_bitmask[r2]);
return CMD_OK;
}
if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
r1, r2, off1, off2, err, errsize))
{
return CMD_FAILED;
}
return CMD_OK;
}
static CommandStatus OR(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
A8, VALUE, {0xf6, WRITE_BYTE_RHS},
A8, HL_ADDRESS, {0xb6},
A8, IX_OFFSET, {SHIFT_IX, 0xb6, WRITE_BYTE_RHS},
A8, IY_OFFSET, {SHIFT_IY, 0xb6, WRITE_BYTE_RHS},
};
RegisterMode r1, r2;
RegisterType t1, t2;
int off1, off2;
if (AssumeAccumulator(argc, argv, quoted, err, errsize,
&r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
{
return CMD_FAILED;
}
/* OR A,r
*/
if (r1 == A8 && IsNormal8Bit(t2))
{
WriteShift(r2);
PCWrite(0xb0 | register_bitmask[r2]);
return CMD_OK;
}
if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
r1, r2, off1, off2, err, errsize))
{
return CMD_FAILED;
}
return CMD_OK;
}
static CommandStatus XOR(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
A8, VALUE, {0xee, WRITE_BYTE_RHS},
A8, HL_ADDRESS, {0xae},
A8, IX_OFFSET, {SHIFT_IX, 0xae, WRITE_BYTE_RHS},
A8, IY_OFFSET, {SHIFT_IY, 0xae, WRITE_BYTE_RHS},
};
RegisterMode r1, r2;
RegisterType t1, t2;
int off1, off2;
if (AssumeAccumulator(argc, argv, quoted, err, errsize,
&r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
{
return CMD_FAILED;
}
/* XOR A,r
*/
if (r1 == A8 && IsNormal8Bit(t2))
{
WriteShift(r2);
PCWrite(0xa8 | register_bitmask[r2]);
return CMD_OK;
}
if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
r1, r2, off1, off2, err, errsize))
{
return CMD_FAILED;
}
return CMD_OK;
}
static CommandStatus CP(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
static RegisterPairCodes codes[] =
{
A8, VALUE, {0xfe, WRITE_BYTE_RHS},
A8, HL_ADDRESS, {0xbe},
A8, IX_OFFSET, {SHIFT_IX, 0xbe, WRITE_BYTE_RHS},
A8, IY_OFFSET, {SHIFT_IY, 0xbe, WRITE_BYTE_RHS},
};
RegisterMode r1, r2;
RegisterType t1, t2;
int off1, off2;
if (AssumeAccumulator(argc, argv, quoted, err, errsize,
&r1, &r2, &t1, &t2, &off1, &off2) != CMD_OK)
{
return CMD_FAILED;
}
/* CP A,r
*/
if (r1 == A8 && IsNormal8Bit(t2))
{
WriteShift(r2);
PCWrite(0xb8 | register_bitmask[r2]);
return CMD_OK;
}
if (!WriteRegisterPairModes(codes, NUM_REGISTER_CODES(codes),
r1, r2, off1, off2, err, errsize))
{
return CMD_FAILED;
}
return CMD_OK;
}
static CommandStatus IM(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
static int im[3] = {0x46, 0x56, 0x5e};
RegisterMode r1;
RegisterType t1;
int off1;
CMD_ARGC_CHECK(2);
if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
{
return CMD_FAILED;
}
if (r1 != VALUE || (off1 < 0 || off1 > 2))
{
snprintf(err, errsize, "%s: invalid argument %s", argv[0], argv[1]);
return CMD_FAILED;
}
PCWrite(0xed);
PCWrite(im[off1]);
return CMD_OK;
}
static CommandStatus INC(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
RegisterMode r1;
RegisterType t1;
int off1;
CMD_ARGC_CHECK(2);
if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
{
return CMD_FAILED;
}
/* INC r
*/
if (IsNormal8Bit(t1))
{
WriteShift(r1);
PCWrite(0x04 | register_bitmask[r1] << 3);
return CMD_OK;
}
/* INC (HL)/(IX+d)/(IY+d)
*/
if (r1 == HL_ADDRESS || r1 == IX_OFFSET || r1 == IY_OFFSET)
{
WriteShift(r1);
PCWrite(0x34);
WriteOffset(r1, off1);
return CMD_OK;
}
/* INC rr
*/
if (Is16Bit(t1) && !IsMemory(t1))
{
WriteShift(r1);
PCWrite(0x03 | register_bitmask[r1] << 4);
return CMD_OK;
}
return IllegalArgs(argc, argv, quoted, err, errsize);
}
static CommandStatus DEC(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
RegisterMode r1;
RegisterType t1;
int off1;
CMD_ARGC_CHECK(2);
if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
{
return CMD_FAILED;
}
/* DEC r
*/
if (IsNormal8Bit(t1))
{
WriteShift(r1);
PCWrite(0x05 | register_bitmask[r1] << 3);
return CMD_OK;
}
/* DEC (HL)/(IX+d)/(IY+d)
*/
if (r1 == HL_ADDRESS || r1 == IX_OFFSET || r1 == IY_OFFSET)
{
WriteShift(r1);
PCWrite(0x35);
WriteOffset(r1, off1);
return CMD_OK;
}
/* DEC rr
*/
if (Is16Bit(t1) && !IsMemory(t1))
{
WriteShift(r1);
PCWrite(0x0b | register_bitmask[r1] << 4);
return CMD_OK;
}
return IllegalArgs(argc, argv, quoted, err, errsize);
}
static CommandStatus RLC_RL_RRC_RR_ETC(const char *label,
int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
RegisterMode r1;
RegisterType t1;
int off1;
int opcode_mask;
CMD_ARGC_CHECK(2);
if (CompareString(argv[0], "RLC"))
{
opcode_mask = 0x00;
}
else if (CompareString(argv[0], "RL"))
{
opcode_mask = 0x10;
}
else if (CompareString(argv[0], "RRC"))
{
opcode_mask = 0x08;
}
else if (CompareString(argv[0], "RR"))
{
opcode_mask = 0x18;
}
else if (CompareString(argv[0], "SLA"))
{
opcode_mask = 0x20;
}
else if (CompareString(argv[0], "SRA"))
{
opcode_mask = 0x28;
}
else if (CompareString(argv[0], "SRL"))
{
opcode_mask = 0x38;
}
else if (CompareString(argv[0], "SLL"))
{
opcode_mask = 0x30;
}
if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
{
return CMD_FAILED;
}
/* Normal opcodes
*/
if (argc == 2)
{
/* OP r
*/
if (IsNormal8Bit(t1) && !IsIndex(t1))
{
PCWrite(0xcb);
PCWrite(opcode_mask | register_bitmask[r1]);
return CMD_OK;
}
/* OP (HL)/(IX+d)/(IY+d)
*/
if (r1 == HL_ADDRESS || r1 == IX_OFFSET || r1 == IY_OFFSET)
{
WriteShift(r1);
PCWrite(0xcb);
WriteOffset(r1, off1);
PCWrite(opcode_mask | 0x06);
return CMD_OK;
}
}
/* Undocumented opcodes
*/
if (argc == 3)
{
RegisterMode r2;
RegisterType t2;
int off2;
if (!CalcRegisterMode(argv[2], quoted[2], &r2, &t2,
&off2, err, errsize))
{
return CMD_FAILED;
}
/* OP (IX+d)/(IY+d),r
*/
if ((r1 == IX_OFFSET || r1 == IY_OFFSET) && (r2 >= A8 && r2 <= L8))
{
WriteShift(r1);
PCWrite(0xcb);
WriteOffset(r1, off1);
PCWrite(opcode_mask | register_bitmask[r2]);
return CMD_OK;
}
}
return IllegalArgs(argc, argv, quoted, err, errsize);
}
static CommandStatus BIT_SET_RES(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
RegisterMode r1, r2;
RegisterType t1, t2;
int off1, off2;
int opcode_mask;
CMD_ARGC_CHECK(3);
if (CompareString(argv[0], "BIT"))
{
opcode_mask = 0x40;
}
else if (CompareString(argv[0], "SET"))
{
opcode_mask = 0xc0;
}
else if (CompareString(argv[0], "RES"))
{
opcode_mask = 0x80;
}
if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
{
return CMD_FAILED;
}
if (!CalcRegisterMode(argv[2], quoted[2], &r2, &t2, &off2, err, errsize))
{
return CMD_FAILED;
}
if (r1 != VALUE || (off1 < 0 || off1 > 7))
{
snprintf(err, errsize, "%s: illegal value %s for bit number",
argv[0], argv[1]);
return CMD_FAILED;
}
/* Normal opcodes
*/
if (argc == 3)
{
/* OP b,r
*/
if (IsNormal8Bit(t2) && !IsIndex(t2))
{
PCWrite(0xcb);
PCWrite(opcode_mask | off1 << 3 | register_bitmask[r2]);
return CMD_OK;
}
/* OP b,(HL)/(IX+d)/(IY+d)
*/
if (r2 == HL_ADDRESS || r2 == IX_OFFSET || r2 == IY_OFFSET)
{
WriteShift(r2);
PCWrite(0xcb);
WriteOffset(r2, off2);
PCWrite(opcode_mask | off1 << 3 | 0x06);
return CMD_OK;
}
}
/* Undocumented opcodes
*/
if (argc > 3 && (CompareString(argv[0], "SET") ||
CompareString(argv[0], "RES")))
{
RegisterMode r3;
RegisterType t3;
int off3;
if (!CalcRegisterMode(argv[3], quoted[3], &r3, &t3,
&off3, err, errsize))
{
return CMD_FAILED;
}
/* OP b,(IX+d)/(IY+d),r
*/
if ((r2 == IX_OFFSET || r2 == IY_OFFSET) && (r3 >= A8 && r3 <= L8))
{
WriteShift(r2);
PCWrite(0xcb);
WriteOffset(r2, off2);
PCWrite(opcode_mask | off1 << 3 | register_bitmask[r3]);
return CMD_OK;
}
}
return IllegalArgs(argc, argv, quoted, err, errsize);
}
static CommandStatus JP(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
if (argc == 2)
{
RegisterMode mode;
RegisterType type;
int val;
if (!CalcRegisterMode(argv[1], quoted[1], &mode, &type, &val,
err, errsize))
{
return CMD_FAILED;
}
if (mode == VALUE)
{
PCWrite(0xc3);
PCWriteWord(val);
return CMD_OK;
}
if (mode == HL_ADDRESS || mode == IX_ADDRESS || mode == IY_ADDRESS)
{
WriteShift(mode);
PCWrite(0xe9);
return CMD_OK;
}
}
else if (argc == 3)
{
RegisterMode mode;
RegisterType type;
int val;
ProcessorFlag flag;
int mask;
if (!CalcFlagMode(argv[1], &flag, &mask, err, errsize) ||
!CalcRegisterMode(argv[2], quoted[2], &mode, &type, &val,
err, errsize))
{
return CMD_FAILED;
}
if (mode == VALUE)
{
PCWrite(0xc2 | mask << 3);
PCWriteWord(val);
return CMD_OK;
}
}
return IllegalArgs(argc, argv, quoted, err, errsize);
}
static CommandStatus JR(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
if (argc == 2)
{
RegisterMode mode;
RegisterType type;
int val;
if (!CalcRegisterMode(argv[1], quoted[1], &mode, &type, &val,
err, errsize))
{
return CMD_FAILED;
}
if (mode == VALUE)
{
int rel;
rel = val - ((PC() + 2) % 0x10000);
CheckOffset(argv[1], rel);
PCWrite(0x18);
PCWrite(rel);
return CMD_OK;
}
}
else if (argc == 3)
{
RegisterMode mode;
RegisterType type;
int val;
ProcessorFlag flag;
int mask;
if (!CalcFlagMode(argv[1], &flag, &mask, err, errsize) ||
!CalcRegisterMode(argv[2], quoted[2], &mode, &type, &val,
err, errsize))
{
return CMD_FAILED;
}
if (mode == VALUE && (flag >= NZ_FLAG && flag <= C_FLAG))
{
int rel;
rel = val - ((PC() + 2) % 0x10000);
CheckOffset(argv[2], rel);
PCWrite(0x20 | mask << 3);
PCWrite(rel);
return CMD_OK;
}
}
return IllegalArgs(argc, argv, quoted, err, errsize);
}
static CommandStatus DJNZ(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
RegisterMode mode;
RegisterType type;
int val;
CMD_ARGC_CHECK(2);
if (!CalcRegisterMode(argv[1], quoted[1], &mode, &type, &val,
err, errsize))
{
return CMD_FAILED;
}
if (mode == VALUE)
{
int rel;
rel = val - ((PC() + 2) % 0x10000);
CheckOffset(argv[1], rel);
PCWrite(0x10);
PCWrite(rel);
return CMD_OK;
}
return IllegalArgs(argc, argv, quoted, err, errsize);
}
static CommandStatus CALL(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
if (argc == 2)
{
RegisterMode mode;
RegisterType type;
int val;
if (!CalcRegisterMode(argv[1], quoted[1], &mode, &type, &val,
err, errsize))
{
return CMD_FAILED;
}
if (mode == VALUE)
{
PCWrite(0xcd);
PCWriteWord(val);
return CMD_OK;
}
}
else if (argc == 3)
{
RegisterMode mode;
RegisterType type;
int val;
ProcessorFlag flag;
int mask;
if (!CalcFlagMode(argv[1], &flag, &mask, err, errsize) ||
!CalcRegisterMode(argv[2], quoted[2], &mode, &type, &val,
err, errsize))
{
return CMD_FAILED;
}
if (mode == VALUE)
{
PCWrite(0xc4 | mask << 3);
PCWriteWord(val);
return CMD_OK;
}
}
return IllegalArgs(argc, argv, quoted, err, errsize);
}
static CommandStatus RET(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
if (argc == 1)
{
PCWrite(0xc9);
return CMD_OK;
}
else if (argc == 2)
{
ProcessorFlag flag;
int mask;
if (!CalcFlagMode(argv[1], &flag, &mask, err, errsize))
{
return CMD_FAILED;
}
PCWrite(0xc0 | mask << 3);
return CMD_OK;
}
return IllegalArgs(argc, argv, quoted, err, errsize);
}
static CommandStatus RST(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
static struct
{
int dec;
int hex;
} rst_arg[] =
{
{0, 0x00},
{8, 0x08},
{10, 0x10},
{18, 0x18},
{20, 0x20},
{28, 0x28},
{30, 0x30},
{38, 0x38},
{-1}
};
RegisterMode mode;
RegisterType type;
int val;
CMD_ARGC_CHECK(2);
if (!CalcRegisterMode(argv[1], quoted[1], &mode, &type, &val,
err, errsize))
{
return CMD_FAILED;
}
if (mode == VALUE)
{
int f;
int mask = -1;
for(f = 0; rst_arg[f].dec != -1; f++)
{
if (rst_arg[f].dec == val || rst_arg[f].hex == val)
{
mask = f << 3;
}
}
if (mask != -1)
{
PCWrite(0xc7 | mask);
return CMD_OK;
}
}
return IllegalArgs(argc, argv, quoted, err, errsize);
}
static CommandStatus IN(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
RegisterMode r1, r2;
RegisterType t1, t2;
int off1, off2;
int opcode_mask;
CMD_ARGC_CHECK(2);
if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
{
return CMD_FAILED;
}
if (r1 == C_PORT && argc == 2)
{
PCWrite(0xed);
PCWrite(0x70);
return CMD_OK;
}
CMD_ARGC_CHECK(3);
if (!CalcRegisterMode(argv[2], quoted[2], &r2, &t2, &off2, err, errsize))
{
return CMD_FAILED;
}
if (r1 == A8 && r2 == ADDRESS)
{
CheckRange(argv[2], off2, 0, 255);
PCWrite(0xdb);
PCWrite(off2);
return CMD_OK;
}
if (!IsIndex(t1) && (IsNormal8Bit(t1) || r1 == F8) && r2 == C_PORT)
{
PCWrite(0xed);
PCWrite(0x40 | register_bitmask[r1] << 3);
return CMD_OK;
}
return IllegalArgs(argc, argv, quoted, err, errsize);
}
static CommandStatus OUT(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
RegisterMode r1, r2;
RegisterType t1, t2;
int off1, off2;
int opcode_mask;
CMD_ARGC_CHECK(3);
if (!CalcRegisterMode(argv[1], quoted[1], &r1, &t1, &off1, err, errsize))
{
return CMD_FAILED;
}
if (!CalcRegisterMode(argv[2], quoted[2], &r2, &t2, &off2, err, errsize))
{
return CMD_FAILED;
}
if (r1 == ADDRESS && r2 == A8)
{
CheckRange(argv[1], off1, 0, 255);
PCWrite(0xd3);
PCWrite(off1);
return CMD_OK;
}
if (r1 == C_PORT && !IsIndex(t2) && (IsNormal8Bit(t2) || r2 == F8))
{
PCWrite(0xed);
PCWrite(0x41 | register_bitmask[r2] << 3);
return CMD_OK;
}
if (r1 == C_PORT && r2 == VALUE)
{
PCWrite(0xed);
PCWrite(0x71);
return CMD_OK;
}
return IllegalArgs(argc, argv, quoted, err, errsize);
}
/* ---------------------------------------- OPCODE TABLES
*/
typedef struct
{
const char *op;
int code[3]; /* Zero ends, but code[0] always used. */
} OpcodeTable;
typedef struct
{
const char *op;
Command cmd;
} HandlerTable;
static const HandlerTable handler_table[] =
{
{"LD", LD},
{"PUSH", PUSH},
{"POP", POP},
{"EX", EX},
{"ADD", ADD},
{"ADC", ADC},
{"SUB", SUB},
{"SBC", SBC},
{"AND", AND},
{"OR", OR},
{"XOR", XOR},
{"EOR", XOR},
{"CP", CP},
{"INC", INC},
{"DEC", DEC},
{"IM", IM},
{"RLC", RLC_RL_RRC_RR_ETC},
{"RL", RLC_RL_RRC_RR_ETC},
{"RRC", RLC_RL_RRC_RR_ETC},
{"RR", RLC_RL_RRC_RR_ETC},
{"SLA", RLC_RL_RRC_RR_ETC},
{"SRA", RLC_RL_RRC_RR_ETC},
{"SRL", RLC_RL_RRC_RR_ETC},
{"SLL", RLC_RL_RRC_RR_ETC},
{"BIT", BIT_SET_RES},
{"RES", BIT_SET_RES},
{"SET", BIT_SET_RES},
{"JP", JP},
{"JR", JR},
{"DJNZ", DJNZ},
{"CALL", CALL},
{"RET", RET},
{"RST", RST},
{"IN", IN},
{"OUT", OUT},
{NULL}
};
static const OpcodeTable implied_opcodes[] =
{
{"NOP", {0x00}},
{"DI", {0xf3}},
{"EI", {0xfb}},
{"HALT", {0x76}},
{"HLT", {0x76}},
{"EXX", {0xd9}},
{"DAA", {0x27}},
{"CPL", {0x2f}},
{"SCF", {0x37}},
{"CCF", {0x3f}},
{"NEG", {0xed, 0x44}},
{"RLCA", {0x07}},
{"RRCA", {0x0f}},
{"RLA", {0x17}},
{"RRA", {0x1f}},
{"CPI", {0xed, 0xa1}},
{"CPIR", {0xed, 0xb1}},
{"CPD", {0xed, 0xa9}},
{"CPDR", {0xed, 0xb9}},
{"INI", {0xed, 0xa2}},
{"INIR", {0xed, 0xb2}},
{"IND", {0xed, 0xaa}},
{"INDR", {0xed, 0xba}},
{"OUTI", {0xed, 0xa3}},
{"OTIR", {0xed, 0xb3}},
{"OUTD", {0xed, 0xab}},
{"OTDR", {0xed, 0xbb}},
{"LDI", {0xed, 0xa0}},
{"LDIR", {0xed, 0xb0}},
{"LDD", {0xed, 0xa8}},
{"LDDR", {0xed, 0xb8}},
{"RRD", {0xed, 0x67}},
{"RLD", {0xed, 0x6f}},
{"RETI", {0xed, 0x4d}},
{"RETN", {0xed, 0x45}},
{NULL}
};
/* ---------------------------------------- PUBLIC INTERFACES
*/
void Init_Z80(void)
{
}
const ValueTable *Options_Z80(void)
{
return NULL;
}
CommandStatus SetOption_Z80(int opt, int argc, char *argv[], int quoted[],
char *err, size_t errsize)
{
return CMD_OK;
}
CommandStatus Handler_Z80(const char *label, int argc, char *argv[],
int quoted[], char *err, size_t errsize)
{
int f;
/* Check for simple (implied addressing) opcodes
*/
for(f = 0; implied_opcodes[f].op; f++)
{
if (CompareString(argv[0], implied_opcodes[f].op))
{
int r;
PCWrite(implied_opcodes[f].code[0]);
for(r = 1; implied_opcodes[f].code[r]; r++)
{
PCWrite(implied_opcodes[f].code[r]);
}
return CMD_OK;
}
}
/* Check for other opcodes
*/
for(f = 0; handler_table[f].op; f++)
{
if (CompareString(argv[0], handler_table[f].op))
{
return handler_table[f].cmd(label, argc, argv,
quoted, err, errsize);;
}
}
return CMD_NOT_KNOWN;
}
/*
vim: ai sw=4 ts=8 expandtab
*/