@@ -370,8 +370,12 @@ static void decode_immediate( | |||
sprintf(arg, "($%04X)", bytes[shift + 1] + (bytes[shift + 2] << 8)); | |||
break; | |||
case AT_IX_IY: // Indexed offset | |||
format = bytes[0] == 0xDD ? "(ix%+hhd)" : "(iy%+hhd)"; | |||
sprintf(arg, format, (int8_t) bytes[shift + 1]); | |||
if (bytes[shift + 1]) { | |||
format = bytes[0] == 0xDD ? "(ix%+hhd)" : "(iy%+hhd)"; | |||
sprintf(arg, format, (int8_t) bytes[shift + 1]); | |||
} else { | |||
sprintf(arg, bytes[0] == 0xDD ? "(ix)" : "(iy)"); | |||
} | |||
break; | |||
case AT_PORT_IM: // Immediate port | |||
sprintf(arg, "($%02X)", bytes[shift + 1]); | |||
@@ -72,6 +72,7 @@ void z80_power(Z80 *z80) | |||
regfile->iff1 = regfile->iff2 = 0; | |||
z80->except = false; | |||
z80->last_index = NULL; | |||
z80->pending_cycles = 0; | |||
z80->trace.fresh = true; | |||
@@ -251,14 +252,11 @@ static inline bool extract_cond(const Z80 *z80, uint8_t opcode) | |||
} | |||
/* | |||
Extract the current index register. | |||
Return the address signified by a indirect index instruction. | |||
*/ | |||
static inline uint16_t* extract_index(Z80 *z80) | |||
static inline uint16_t get_index_addr(Z80 *z80, int8_t offset) | |||
{ | |||
uint8_t prefix = mmu_read_byte(z80->mmu, z80->regfile.pc - 1); | |||
if (prefix != 0xDD && prefix != 0xFD) | |||
FATAL("invalid call: extract_index(z80, 0x%02X)", prefix) | |||
return prefix == 0xDD ? &z80->regfile.ix : &z80->regfile.iy; | |||
return *z80->last_index + offset; | |||
} | |||
/* | |||
@@ -36,6 +36,7 @@ typedef struct { | |||
IO *io; | |||
bool except; | |||
uint8_t exc_code, exc_data; | |||
uint16_t *last_index; | |||
double pending_cycles; | |||
Z80TraceInfo trace; | |||
} Z80; | |||
@@ -74,9 +74,18 @@ static uint8_t z80_inst_ld_r_hl(Z80 *z80, uint8_t opcode) | |||
} | |||
/* | |||
LD r, (IXY+d) | |||
LD r, (IXY+d) (0xDD46, 0xDD4E, 0xDD56, 0xDD5E, 0xDD66, 0xDD6E, 0xDD7E, | |||
0xFD46, 0xFD4E, 0xFD56, 0xFD5E, 0xFD66, 0xFD6E, 0xFD7E): | |||
Load (IX+d) or (IY+d) into r (8-bit register). | |||
*/ | |||
// static uint8_t z80_inst_ld_r_ixy(Z80 *z80, uint8_t opcode) | |||
static uint8_t z80_inst_ld_r_ixy(Z80 *z80, uint8_t opcode) | |||
{ | |||
uint8_t *reg = extract_reg(z80, opcode); | |||
uint16_t addr = get_index_addr(z80, ++z80->regfile.pc); | |||
*reg = mmu_read_byte(z80->mmu, addr); | |||
z80->regfile.pc++; | |||
return 19; | |||
} | |||
/* | |||
LD (HL), r (0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x77): | |||
@@ -92,9 +101,18 @@ static uint8_t z80_inst_ld_hl_r(Z80 *z80, uint8_t opcode) | |||
} | |||
/* | |||
LD (IXY+d), r | |||
LD (IXY+d), r (0xDD70, 0xDD71, 0xDD72, 0xDD73, 0xDD74, 0xDD75, 0xDD77, | |||
0xFD70, 0xFD71, 0xFD72, 0xFD73, 0xFD74, 0xFD75, 0xFD77): | |||
Load r (8-bit register) into (IX+d) or (IY+d). | |||
*/ | |||
// static uint8_t z80_inst_ld_ixy_r(Z80 *z80, uint8_t opcode) | |||
static uint8_t z80_inst_ld_ixy_r(Z80 *z80, uint8_t opcode) | |||
{ | |||
uint8_t *reg = extract_reg(z80, opcode << 3); | |||
uint16_t addr = get_index_addr(z80, ++z80->regfile.pc); | |||
mmu_write_byte(z80->mmu, addr, *reg); | |||
z80->regfile.pc++; | |||
return 19; | |||
} | |||
/* | |||
LD (HL), n (0x36): | |||
@@ -111,9 +129,18 @@ static uint8_t z80_inst_ld_hl_n(Z80 *z80, uint8_t opcode) | |||
} | |||
/* | |||
LD (IXY+d), n | |||
LD (IXY+d), n (0xDD36, 0xFD36): | |||
Load n (8-bit immediate) into (IX+d) or (IY+d). | |||
*/ | |||
// static uint8_t z80_inst_ld_ixy_n(Z80 *z80, uint8_t opcode) | |||
static uint8_t z80_inst_ld_ixy_n(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
uint16_t addr = get_index_addr(z80, ++z80->regfile.pc); | |||
uint8_t byte = mmu_read_byte(z80->mmu, ++z80->regfile.pc); | |||
mmu_write_byte(z80->mmu, addr, byte); | |||
z80->regfile.pc++; | |||
return 19; | |||
} | |||
/* | |||
LD A, (BC/DE) | |||
@@ -169,7 +196,7 @@ static uint8_t z80_inst_ld_nn_a(Z80 *z80, uint8_t opcode) | |||
// static uint8_t z80_inst_ld_a_r(Z80 *z80, uint8_t opcode) | |||
/* | |||
LD I,A | |||
LD I, A | |||
*/ | |||
// static uint8_t z80_inst_ld_i_a(Z80 *z80, uint8_t opcode) | |||
@@ -190,7 +217,17 @@ static uint8_t z80_inst_ld_dd_nn(Z80 *z80, uint8_t opcode) | |||
return 10; | |||
} | |||
// LD IXY, nn | |||
/* | |||
LD IXY, nn (0xDD21, 0xFD21): | |||
Load nn (16-bit immediate) into IX or IY. | |||
*/ | |||
static uint8_t z80_inst_ld_ixy_nn(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
*z80->last_index = mmu_read_double(z80->mmu, ++z80->regfile.pc); | |||
z80->regfile.pc += 2; | |||
return 14; | |||
} | |||
/* | |||
LD HL, (nn) (0x2A): | |||
@@ -221,6 +258,7 @@ static uint8_t z80_inst_ld_dd_inn(Z80 *z80, uint8_t opcode) | |||
} | |||
// LD IXY, (nn) | |||
// static uint8_t z80_inst_ld_ixy_inn(Z80 *z80, uint8_t opcode) | |||
/* | |||
LD (nn), HL: (0x22): | |||
@@ -262,7 +300,7 @@ static uint8_t z80_inst_push_qq(Z80 *z80, uint8_t opcode) | |||
static uint8_t z80_inst_push_ixy(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
stack_push(z80, *extract_index(z80)); | |||
stack_push(z80, *z80->last_index); | |||
z80->regfile.pc++; | |||
return 15; | |||
} | |||
@@ -286,7 +324,7 @@ static uint8_t z80_inst_pop_qq(Z80 *z80, uint8_t opcode) | |||
static uint8_t z80_inst_pop_ixy(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
*extract_index(z80) = stack_pop(z80); | |||
*z80->last_index = stack_pop(z80); | |||
z80->regfile.pc++; | |||
return 14; | |||
} | |||
@@ -444,9 +482,15 @@ static uint8_t z80_inst_lddr(Z80 *z80, uint8_t opcode) | |||
// ADD A, (IXY+d) | |||
// ADC A, s | |||
// ADC A, r | |||
// SUB s | |||
// ADC A, n | |||
// ADC A, (HL) | |||
// ADC A, (IXY+d) | |||
// SUB r | |||
/* | |||
SUB n (0xD6): | |||
@@ -469,9 +513,19 @@ static uint8_t z80_inst_sub_n(Z80 *z80, uint8_t opcode) | |||
return 7; | |||
} | |||
// SBC A, s | |||
// SUB (HL) | |||
// SUB (IXY+d) | |||
// SBC A, r | |||
// AND s | |||
// SBC A, n | |||
// SBC A, (HL) | |||
// SBC A, (IXY+d) | |||
// AND r | |||
/* | |||
AND n (0xE6): | |||
@@ -491,7 +545,9 @@ static uint8_t z80_inst_and_n(Z80 *z80, uint8_t opcode) | |||
return 7; | |||
} | |||
// OR s | |||
// AND (HL) | |||
// AND (IXY+d) | |||
/* | |||
OR r (0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB7): | |||
@@ -528,7 +584,26 @@ static uint8_t z80_inst_or_n(Z80 *z80, uint8_t opcode) | |||
return 7; | |||
} | |||
// XOR s | |||
// OR (HL) | |||
/* | |||
OR (IXY+d) (0xDDB6, 0xFDB6): | |||
Bitwise OR A with (IX+d) or (IY+d). | |||
*/ | |||
static uint8_t z80_inst_or_ixy(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
uint8_t addr = get_index_addr(z80, ++z80->regfile.pc); | |||
uint8_t val = mmu_read_byte(z80->mmu, addr); | |||
uint8_t a = (z80->regfile.a |= val); | |||
bool parity = !(__builtin_popcount(a) % 2); | |||
update_flags(z80, 0, 0, parity, !!(a & 0x08), 0, !!(a & 0x20), a == 0, | |||
!!(a & 0x80), 0xFF); | |||
z80->regfile.pc++; | |||
return 7; | |||
} | |||
/* | |||
XOR r (0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAF): | |||
@@ -547,7 +622,27 @@ static uint8_t z80_inst_xor_r(Z80 *z80, uint8_t opcode) | |||
return 4; | |||
} | |||
// CP s | |||
/* | |||
XOR n (0xEE): | |||
Bitwise XOR A with n (8-bit immediate). | |||
*/ | |||
static uint8_t z80_inst_xor_n(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
uint8_t imm = mmu_read_byte(z80->mmu, ++z80->regfile.pc); | |||
uint8_t a = (z80->regfile.a ^= imm); | |||
bool parity = !(__builtin_popcount(a) % 2); | |||
update_flags(z80, 0, 0, parity, !!(a & 0x08), 0, !!(a & 0x20), a == 0, | |||
!!(a & 0x80), 0xFF); | |||
z80->regfile.pc++; | |||
return 7; | |||
} | |||
// XOR (HL) | |||
// XOR (IXY+d) | |||
/* | |||
CP r (0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBF): | |||
@@ -589,6 +684,28 @@ static uint8_t z80_inst_cp_n(Z80 *z80, uint8_t opcode) | |||
} | |||
/* | |||
CP (HL) (0xBE): | |||
Set flags as if the memory pointed to by HL had been subtracted from A. | |||
*/ | |||
static uint8_t z80_inst_cp_hl(Z80 *z80, uint8_t opcode) | |||
{ | |||
(void) opcode; | |||
uint8_t n = mmu_read_byte(z80->mmu, get_pair(z80, REG_HL)); | |||
uint8_t d = z80->regfile.a - n; | |||
bool c = (z80->regfile.a - n) != d; | |||
bool v = (z80->regfile.a - n) != ((int8_t) d); | |||
bool h = !!(((z80->regfile.a & 0x0F) - (n & 0x0F)) & 0x10); | |||
update_flags(z80, c, 1, v, !!(n & 0x08), h, !!(n & 0x20), d == 0, | |||
!!(d & 0x80), 0xFF); | |||
z80->regfile.pc++; | |||
return 7; | |||
} | |||
// CP (IXY+d) | |||
/* | |||
INC r (0x04, 0x0C, 0x14, 0x1C, 0x24, 0x2C, 0x3C): | |||
Increment r (8-bit register). | |||
*/ | |||
@@ -1280,6 +1397,7 @@ static uint8_t z80_prefix_bits(Z80 *z80, uint8_t opcode) | |||
*/ | |||
static uint8_t z80_prefix_index(Z80 *z80, uint8_t opcode) | |||
{ | |||
z80->last_index = (opcode == 0xDD) ? &z80->regfile.ix : &z80->regfile.iy; | |||
opcode = mmu_read_byte(z80->mmu, ++z80->regfile.pc); | |||
return (*instruction_table_index[opcode])(z80, opcode); | |||
} | |||
@@ -192,7 +192,7 @@ static DispatchTable instruction_table = { | |||
[0xBB] = z80_inst_cp_r, | |||
[0xBC] = z80_inst_cp_r, | |||
[0xBD] = z80_inst_cp_r, | |||
[0xBE] = z80_inst_unimplemented, // TODO | |||
[0xBE] = z80_inst_cp_hl, | |||
[0xBF] = z80_inst_cp_r, | |||
[0xC0] = z80_inst_ret_cc, | |||
[0xC1] = z80_inst_pop_qq, | |||
@@ -240,7 +240,7 @@ static DispatchTable instruction_table = { | |||
[0xEB] = z80_inst_ex_de_hl, | |||
[0xEC] = z80_inst_call_cc_nn, | |||
[0xED] = z80_prefix_extended, | |||
[0xEE] = z80_inst_unimplemented, // TODO | |||
[0xEE] = z80_inst_xor_n, | |||
[0xEF] = z80_inst_rst_p, | |||
[0xF0] = z80_inst_ret_cc, | |||
[0xF1] = z80_inst_pop_qq, | |||
@@ -812,7 +812,7 @@ static DispatchTable instruction_table_index = { | |||
[0x1E] = z80_inst_nop2, | |||
[0x1F] = z80_inst_nop2, | |||
[0x20] = z80_inst_nop2, | |||
[0x21] = z80_inst_unimplemented, // TODO | |||
[0x21] = z80_inst_ld_ixy_nn, | |||
[0x22] = z80_inst_unimplemented, // TODO | |||
[0x23] = z80_inst_unimplemented, // TODO | |||
[0x24] = z80_inst_unimplemented, // TODO | |||
@@ -833,7 +833,7 @@ static DispatchTable instruction_table_index = { | |||
[0x33] = z80_inst_nop2, | |||
[0x34] = z80_inst_unimplemented, // TODO | |||
[0x35] = z80_inst_unimplemented, // TODO | |||
[0x36] = z80_inst_unimplemented, // TODO | |||
[0x36] = z80_inst_ld_ixy_n, | |||
[0x37] = z80_inst_nop2, | |||
[0x38] = z80_inst_nop2, | |||
[0x39] = z80_inst_unimplemented, // TODO | |||
@@ -849,7 +849,7 @@ static DispatchTable instruction_table_index = { | |||
[0x43] = z80_inst_nop2, | |||
[0x44] = z80_inst_unimplemented, // TODO | |||
[0x45] = z80_inst_unimplemented, // TODO | |||
[0x46] = z80_inst_unimplemented, // TODO | |||
[0x46] = z80_inst_ld_r_ixy, | |||
[0x47] = z80_inst_nop2, | |||
[0x48] = z80_inst_nop2, | |||
[0x49] = z80_inst_nop2, | |||
@@ -857,7 +857,7 @@ static DispatchTable instruction_table_index = { | |||
[0x4B] = z80_inst_nop2, | |||
[0x4C] = z80_inst_unimplemented, // TODO | |||
[0x4D] = z80_inst_unimplemented, // TODO | |||
[0x4E] = z80_inst_unimplemented, // TODO | |||
[0x4E] = z80_inst_ld_r_ixy, | |||
[0x4F] = z80_inst_nop2, | |||
[0x50] = z80_inst_nop2, | |||
[0x51] = z80_inst_nop2, | |||
@@ -865,7 +865,7 @@ static DispatchTable instruction_table_index = { | |||
[0x53] = z80_inst_nop2, | |||
[0x54] = z80_inst_unimplemented, // TODO | |||
[0x55] = z80_inst_unimplemented, // TODO | |||
[0x56] = z80_inst_unimplemented, // TODO | |||
[0x56] = z80_inst_ld_r_ixy, | |||
[0x57] = z80_inst_nop2, | |||
[0x58] = z80_inst_nop2, | |||
[0x59] = z80_inst_nop2, | |||
@@ -873,7 +873,7 @@ static DispatchTable instruction_table_index = { | |||
[0x5B] = z80_inst_nop2, | |||
[0x5C] = z80_inst_unimplemented, // TODO | |||
[0x5D] = z80_inst_unimplemented, // TODO | |||
[0x5E] = z80_inst_unimplemented, // TODO | |||
[0x5E] = z80_inst_ld_r_ixy, | |||
[0x5F] = z80_inst_nop2, | |||
[0x60] = z80_inst_unimplemented, // TODO | |||
[0x61] = z80_inst_unimplemented, // TODO | |||
@@ -881,7 +881,7 @@ static DispatchTable instruction_table_index = { | |||
[0x63] = z80_inst_unimplemented, // TODO | |||
[0x64] = z80_inst_unimplemented, // TODO | |||
[0x65] = z80_inst_unimplemented, // TODO | |||
[0x66] = z80_inst_unimplemented, // TODO | |||
[0x66] = z80_inst_ld_r_ixy, | |||
[0x67] = z80_inst_unimplemented, // TODO | |||
[0x68] = z80_inst_unimplemented, // TODO | |||
[0x69] = z80_inst_unimplemented, // TODO | |||
@@ -889,23 +889,23 @@ static DispatchTable instruction_table_index = { | |||
[0x6B] = z80_inst_unimplemented, // TODO | |||
[0x6C] = z80_inst_unimplemented, // TODO | |||
[0x6D] = z80_inst_unimplemented, // TODO | |||
[0x6E] = z80_inst_unimplemented, // TODO | |||
[0x6E] = z80_inst_ld_r_ixy, | |||
[0x6F] = z80_inst_unimplemented, // TODO | |||
[0x70] = z80_inst_unimplemented, // TODO | |||
[0x71] = z80_inst_unimplemented, // TODO | |||
[0x72] = z80_inst_unimplemented, // TODO | |||
[0x73] = z80_inst_unimplemented, // TODO | |||
[0x74] = z80_inst_unimplemented, // TODO | |||
[0x75] = z80_inst_unimplemented, // TODO | |||
[0x70] = z80_inst_ld_ixy_r, | |||
[0x71] = z80_inst_ld_ixy_r, | |||
[0x72] = z80_inst_ld_ixy_r, | |||
[0x73] = z80_inst_ld_ixy_r, | |||
[0x74] = z80_inst_ld_ixy_r, | |||
[0x75] = z80_inst_ld_ixy_r, | |||
[0x76] = z80_inst_nop2, | |||
[0x77] = z80_inst_unimplemented, // TODO | |||
[0x77] = z80_inst_ld_ixy_r, | |||
[0x78] = z80_inst_nop2, | |||
[0x79] = z80_inst_nop2, | |||
[0x7A] = z80_inst_nop2, | |||
[0x7B] = z80_inst_nop2, | |||
[0x7C] = z80_inst_unimplemented, // TODO | |||
[0x7D] = z80_inst_unimplemented, // TODO | |||
[0x7E] = z80_inst_unimplemented, // TODO | |||
[0x7E] = z80_inst_ld_r_ixy, | |||
[0x7F] = z80_inst_nop2, | |||
[0x80] = z80_inst_nop2, | |||
[0x81] = z80_inst_nop2, | |||
@@ -961,7 +961,7 @@ static DispatchTable instruction_table_index = { | |||
[0xB3] = z80_inst_nop2, | |||
[0xB4] = z80_inst_unimplemented, // TODO | |||
[0xB5] = z80_inst_unimplemented, // TODO | |||
[0xB6] = z80_inst_unimplemented, // TODO | |||
[0xB6] = z80_inst_or_ixy, | |||
[0xB7] = z80_inst_nop2, | |||
[0xB8] = z80_inst_nop2, | |||
[0xB9] = z80_inst_nop2, | |||