Skip to content

Commit

Permalink
z80 codegen wip: manually defined decoder steps
Browse files Browse the repository at this point in the history
  • Loading branch information
floooh committed Dec 31, 2024
1 parent 07d9e88 commit 96a7835
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 67 deletions.
112 changes: 53 additions & 59 deletions chips/z80.h
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,10 @@ bool z80_opdone(z80_t* cpu);
#define _Z80_MAP_IX (1)
#define _Z80_MAP_IY (2)

// extra/special decoder steps
// <% extra_step_defines
// %>

uint64_t z80_init(z80_t* cpu) {
CHIPS_ASSERT(cpu);
// initial state as described in 'The Undocumented Z80 Documented'
Expand Down Expand Up @@ -1015,80 +1019,70 @@ uint64_t z80_prefetch(z80_t* cpu, uint16_t new_pc) {
uint64_t z80_tick(z80_t* cpu, uint64_t pins) {
pins &= ~(Z80_CTRL_PIN_MASK|Z80_RETI);
switch (cpu->step) {
// <% decoder
// %>
//=== shared fetch machine cycle for non-DD/FD-prefixed ops
// M1/T2: load opcode from data bus
case 0: _wait(); cpu->opcode = _gd(); goto step_next;
case Z80_M1_T2: _wait(); cpu->opcode = _gd(); goto step_next;
// M1/T3: refresh cycle
case 1: pins = _z80_refresh(cpu, pins); goto step_next;
case Z80_M1_T3: pins = _z80_refresh(cpu, pins); goto step_next;
// M1/T4: branch to instruction 'payload'
case 2: {
cpu->step = _z80_optable[cpu->opcode];
// preload effective address for (HL) ops
case Z80_M1_T4:
cpu->step = cpu->opcode;
cpu->addr = cpu->hl;
} goto step_next;
goto step_to;
//=== shared fetch machine cycle for DD/FD-prefixed ops
// M1/T2: load opcode from data bus
case 3: _wait(); cpu->opcode = _gd(); goto step_next;
case Z80_DDFD_M1_T2: _wait(); cpu->opcode = _gd(); goto step_next;
// M1/T3: refresh cycle
case 4: pins = _z80_refresh(cpu, pins); goto step_next;
case Z80_DDFD_M1_T3: pins = _z80_refresh(cpu, pins); goto step_next;
// M1/T4: branch to instruction 'payload'
case 5: {
cpu->step = _z80_ddfd_optable[cpu->opcode];
case Z80_DDFD_M1_T4:
// FIXME: if indirect_table[cpu->opcode] => DDFD_D_T1 else cpu->opcode
cpu->step = _z80_indirect_table[cpu->opcode] ? Z80_DDFD_D_T1 : cpu->opcode;
cpu->addr = cpu->hlx[cpu->hlx_idx].hl;
} goto step_next;
goto step_to;
//=== optional d-loading cycle for (IX+d), (IY+d)
//--- mread
case 6: goto step_next;
case 7: _wait();_mread(cpu->pc++); goto step_next;
case 8: cpu->addr += (int8_t)_gd(); cpu->wz = cpu->addr; goto step_next;
//--- filler ticks
case 9: goto step_next;
case 10: goto step_next;
case 11: goto step_next;
case 12: goto step_next;
case 13: {
// branch to actual instruction
cpu->step = _z80_optable[cpu->opcode];
} goto step_next;
//=== special case d-loading cycle for (IX+d),n where the immediate load
// is hidden in the d-cycle load
//--- mread for d offset
case 14: goto step_next;
case 15: _wait();_mread(cpu->pc++); goto step_next;
case 16: cpu->addr += (int8_t)_gd(); cpu->wz = cpu->addr; goto step_next;
//--- mread for n
case 17: goto step_next;
case 18: _wait();_mread(cpu->pc++); goto step_next;
case 19: cpu->dlatch=_gd(); goto step_next;
//--- filler tick
case 20: goto step_next;
case 21: {
// branch to ld (hl),n and skip the original mread cycle for loading 'n'
cpu->step = _z80_optable[cpu->opcode] + 3;
} goto step_next;
//=== special opcode fetch machine cycle for CB-prefixed instructions
case 22: _wait(); cpu->opcode = _gd(); goto step_next;
case 23: pins = _z80_refresh(cpu, pins); goto step_next;
case 24: {
if ((cpu->opcode & 7) == 6) {
// this is a (HL) instruction
cpu->addr = cpu->hl;
cpu->step = _z80_special_optable[_Z80_OPSTATE_SLOT_CBHL];
}
else {
cpu->step = _z80_special_optable[_Z80_OPSTATE_SLOT_CB];
case Z80_DDFD_D_T1: goto step_next;
case Z80_DDFD_D_T2: _wait();_mread(cpu->pc++); goto step_next;
case Z80_DDFD_D_T3: cpu->addr += (int8_t)_gd(); cpu->wz = cpu->addr; goto step_next;
//--- special case LD (IX/IY+d),n or filler ticks
case Z80_DDFD_D_T4: goto step_next;
case Z80_DDFD_D_T5: if (cpu->opcode == 0x36) { _wait();_mread(cpu->pc++); }; goto step_next;
case Z80_DDFD_D_T6: if (cpu->opcode == 0x36) { cpu->dlatch = _gd(); }; goto step_next;
case Z80_DDFD_D_T7: goto step_next;
case Z80_DDFD_D_T8:
if (cpu->opcode == 0x36) {
cpu->step = Z80_DDFD_LDHLN_WR_T1;
} else {
cpu->step = cpu->opcode;
}
} goto step_next;
goto step_to;
//--- special case LD (IX/IY+d),n write mcycle
case Z80_DDFD_LDHLN_WR_T1: goto step_next;
case Z80_DDFD_LDHLN_WR_T2: _wait(); _mwrite(cpu->addr,cpu->dlatch); goto step_next;
case Z80_DDFD_LDHLN_WR_T3: goto step_next;
case Z80_DDFD_LDHLN_OVERLAPPED: goto fetch_next;
//=== special opcode fetch machine cycle for ED-prefixed instructions
// M1/T2: load opcode from data bus
case 25: _wait(); cpu->opcode = _gd(); goto step_next;
// M1/T3: refresh cycle
case 26: pins = _z80_refresh(cpu, pins); goto step_next;
// M1/T4: branch to instruction 'payload'
case 27: cpu->step = _z80_ed_optable[cpu->opcode]; goto step_next;
case Z80_ED_M1_T2: _wait(); cpu->opcode = _gd(); goto step_next;
case Z80_ED_M1_T3: pins = _z80_refresh(cpu, pins); goto step_next;
case Z80_ED_M1_T4: cpu->step = cpu->opcode + 256; goto step_to;
//=== special opcode fetch machine cycle for CB-prefixed instructions
// FIXME FIXME FIXME
// case 22: _wait(); cpu->opcode = _gd(); goto step_next;
// case 23: pins = _z80_refresh(cpu, pins); goto step_next;
// case 24: {
// if ((cpu->opcode & 7) == 6) {
// // this is a (HL) instruction
// cpu->addr = cpu->hl;
// cpu->step = _z80_special_optable[_Z80_OPSTATE_SLOT_CBHL];
// }
// else {
// cpu->step = _z80_special_optable[_Z80_OPSTATE_SLOT_CB];
// }
// } goto step_next;
//=== from here on code-generated
// <% decoder
// %>
default: _Z80_UNREACHABLE;
}
fetch_next: pins = _z80_fetch(cpu, pins);
Expand Down
46 changes: 38 additions & 8 deletions codegen/z80_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ def add_fetch(action):
# regular case, jump to the shared fetch block after the
add_fetch(f'{action}')
op.num_steps = op_step
return out_lines + out_extra_lines
return { 'out_lines': out_lines + out_extra_lines, 'max_step': cur_extra_step }

# def optable_to_string(type):
# global indent
Expand All @@ -397,20 +397,50 @@ def add_fetch(action):
# res += f' // {op_index&0xFF:02X}: {op.name} (M:{len(op.mcycles)-1} T:{op.num_cycles} steps:{op.num_steps})\n'
# return res

def write_result(out_lines):
def extra_step_defines_string(max_step):
extra_steps = [
"M1_T2",
"M1_T3",
"M1_T4",
"DDFD_M1_T2",
"DDFD_M1_T3",
"DDFD_M1_T4",
"DDFD_D_T1",
"DDFD_D_T2",
"DDFD_D_T3",
"DDFD_D_T4",
"DDFD_D_T5",
"DDFD_D_T6",
"DDFD_D_T7",
"DDFD_D_T8",
"DDFD_LDHLN_WR_T1",
"DDFD_LDHLN_WR_T2",
"DDFD_LDHLN_WR_T3",
"DDFD_LDHLN_OVERLAPPED",
"ED_M1_T2",
"ED_M1_T3",
"ED_M1_T4",
]
res = ''
step_index = max_step
for step in extra_steps:
res += f'#define Z80_{step} {step_index}\n'
step_index += 1
return res

def write_result(decoder_output):
out_lines = decoder_output['out_lines']
max_step = decoder_output['max_step']
with open(INOUT_PATH, 'r') as f:
lines = f.read().splitlines()
# lines = templ.replace(lines, 'optable_main', optable_to_string('main'))
# lines = templ.replace(lines, 'optable_ddfd', optable_to_string('ddfd'))
# lines = templ.replace(lines, 'optable_ed', optable_to_string('ed'))
# lines = templ.replace(lines, 'optable_special', optable_to_string('special'))
lines = templ.replace(lines, 'decoder', out_lines)
lines = templ.replace(lines, 'extra_step_defines', extra_step_defines_string(max_step))
out_str = '\n'.join(lines) + '\n'
with open('/Users/floh/scratch/z80.h', 'w') as f:
f.write(out_str)

if __name__ == '__main__':
parse_opdescs()
expand_optable()
out_lines = gen_decoder()
write_result(out_lines)
decoder_output = gen_decoder()
write_result(decoder_output)

0 comments on commit 96a7835

Please sign in to comment.