Mercurial > repos > blastem
view upd78k2.cpu @ 2707:a64c0e1ed6ac default tip
Implement speed control and reset for media player. Fix other bindings that could cause it to crash
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sun, 06 Jul 2025 20:43:37 -0700 |
parents | 0bd48217941a |
children |
line wrap: on
line source
#"Minimum instruction cycle is 333 ns for internal ROM 500 ns for external at 12 MHz" #That's 4 clks for internal ROM, 6 clocks external #Above seems to be defined based on external clock input, but there's an internal divider #Instruction timing tables seem to specified in terms of this divided clock info prefix upd78k2_ opcode_size 8 header upd78k2.h body upd78k2_run_op sync_cycle upd78k2_sync_cycle extra_tables sfr bit1 bit2 muldiv base sfrbit spmov indexed regind alt_base alt_indexed alt_regind mov_reg include upd78k2_util.c regs main 8 x a c b e d l h psw 8 pc 16 sp 16 rbs 8 int_enable 8 int_priority_flag 8 chflags 8 zflag 8 stbc 8 int_cycle 32 scratch1 32 scratch2 32 mem_pointers ptr8 4 port_data 8 8 port_mode 8 8 mm 8 if0 16 mk0 16 pr0 16 ism0 16 intm0 8 intm1 8 ist 8 iram 8 256 flags register psw I 7 none int_enable Z 6 zero zflag R 5 none rbs.1 A 4 half-carry chflags.3 B 3 none rbs.0 P 1 none int_priority_flag C 0 carry chflags.7 declare uint8_t upd78237_sfr_read(uint32_t address, void *context); void *upd78237_sfr_write(uint32_t address, void *context, uint8_t value); void init_upd78k2_opts(upd78k2_options *opts, memmap_chunk const *chunks, uint32_t num_chunks); upd78k2_context *init_upd78k2_context(upd78k2_options *opts); void upd78k2_sync_cycle(upd78k2_context *upd, uint32_t target_cycle); #Prefix bytes # 0000 0001 -> saddr becomes sfr, mem becomes &mem # 0000 0010 -> bit instructions/ conditional branches # 0000 0011 -> more bit instructions / conditional branches # 0000 0110 -> base mode # 0000 1000 -> saddr/sfr bit instructions / conditional branches # 0000 1001 -> mov !addr16/stbc # 0000 1010 -> indexed mode # 0001 0110 -> register indirect sfr_read arg offset 8 scratch1 = offset + 0xFF00 ocall read_8 sfr_write arg offset 8 arg value 8 scratch2 = offset + 0xFF00 scratch1 = value ocall write_8 iram_read arg offset 8 local normal_iram 8 normal_iram = 1 if offset >=U 0xE0 #register bank area switch rbs case 0 if offset >=U 0xF8 normal_iram = 0 end case 1 if offset >=U 0xF0 if offset >=U 0xF8 else normal_iram = 0 end end case 2 if offset >=U 0xE8 if offset >=U 0xF0 else normal_iram = 0 end end case 3 if offset >=U 0xE8 else normal_iram = 0 end end end if normal_iram scratch1 = iram.offset else scratch1 = offset & 7 scratch1 = main.scratch1 end iram_write arg offset 8 arg value 8 local normal_iram 8 local regnum 8 normal_iram = 1 if offset >=U 0xE0 #register bank area switch rbs case 0 if offset >=U 0xF8 normal_iram = 0 end case 1 if offset >=U 0xF0 if offset >=U 0xF8 else normal_iram = 0 end end case 2 if offset >=U 0xE8 if offset >=U 0xF0 else normal_iram = 0 end end case 3 if offset >=U 0xE8 else normal_iram = 0 end end end if normal_iram iram.offset = value else regnum = offset & 8 main.regnum = value end mem_read_no_exp arg addr 32 local offset 8 if addr >=U 0xFE00 if addr >=U 0xFF00 offset = addr sfr_read offset else offset = addr iram_read offset end else scratch1 = addr ocall read_8 end mem_write_no_exp arg addr 32 arg value 8 local offset 8 if addr >=U 0xFE00 if addr >=U 0xFF00 offset = addr sfr_write offset value else offset = addr iram_write offset value end else scratch2 = addr scratch1 = value ocall write_8 end upd78k2_op_fetch mem_read_no_exp pc pc += 1 upd78k2_op_fetch_word local tmp 8 upd78k2_op_fetch tmp = scratch1 upd78k2_op_fetch scratch1 <<= 8 scratch1 |= tmp upd78k2_run_op upd78k2_op_fetch dispatch scratch1 saddr_read arg offset 8 local tmp 8 if offset >=U 0xE0 #sfr area tmp = offset - 0xE0 sfr_read tmp else tmp = offset + 0x20 iram_read tmp end saddr_write arg offset 8 arg value 8 local tmp 8 if offset >=U 0xE0 #sfr area tmp = offset - 0xE0 sfr_write tmp value else tmp = offset + 0x20 iram_write tmp value end mem_read arg address 16 arg alt_bank 8 local full_address 32 local meg_enable 8 meg_enable = mm & 0x40 if meg_enable if alt_bank full_address = port_data.6 & 0xF else full_address = port_mode.6 & 0xF end full_address <<= 16 full_address |= address else full_address = address end mem_read_no_exp full_address mem_write arg address 16 arg value 8 arg alt_bank 8 local full_address 32 local meg_enable 8 meg_enable = mm & 0x40 if meg_enable if alt_bank full_address = port_data.6 & 0xF else full_address = port_mode.6 & 0xF end full_address <<= 16 full_address |= address else full_address = address end mem_write_no_exp full_address value push_word arg value 16 local tmp 8 tmp = value >> 8 sp -= 1 mem_write_no_exp sp tmp tmp = value sp -= 1 mem_write_no_exp sp tmp pop_word mem_read_no_exp sp dst = scratch1 sp += 1 mem_read_no_exp sp scratch1 <<= 8 dst |= scratch1 sp += 1 00000000 nop cycles 2 #minimum cycle time appears to be 4 (ignoring internal divider) 00000001 sfr_prefix upd78k2_op_fetch dispatch scratch1 sfr 00000010 bit1_prefix upd78k2_op_fetch dispatch scratch1 bit1 00000011 bit2_prefix upd78k2_op_fetch dispatch scratch1 bit2 00000101 muldiv_prefix upd78k2_op_fetch dispatch scratch1 muldiv 00000110 base_prefix upd78k2_op_fetch dispatch scratch1 base 00001000 sfrbit_prefix upd78k2_op_fetch dispatch scratch1 sfrbit 00001001 spmov_prefix upd78k2_op_fetch dispatch scratch1 spmov 00001010 indexed_prefix upd78k2_op_fetch dispatch scratch1 indexed 00010110 regind_prefix upd78k2_op_fetch dispatch scratch1 regind 00100100 mov_reg_prefix upd78k2_op_fetch dispatch scratch1 mov_reg sfr 00000110 alt_base_prefix upd78k2_op_fetch dispatch scratch1 alt_base sfr 00001010 alt_indexed_prefix upd78k2_op_fetch dispatch scratch1 alt_indexed sfr 00010110 alt_regind_prefix upd78k2_op_fetch dispatch scratch1 alt_regind aluop arg op 8 arg src 8 switch op case 0 dst += src update_flags ZAC case 1 adc dst src dst update_flags ZAC case 2 dst -= src update_flags ZAC case 3 sbc dst src dst update_flags ZAC case 4 dst &= src update_flags Z case 5 dst ^= src update_flags Z case 6 dst |= src update_flags Z case 7 cmp dst src update_flags ZAC end aluop16 arg op 8 arg src 16 switch op case 0 dst += src update_flags ZAC case 2 dst -= src update_flags ZAC case 7 dst -= src update_flags ZAC default #TODO: what happens in these invalid cases end 10001PPP alu_r_r local tmp_src 16 local tmp_dst 16 upd78k2_op_fetch scratch2 = scratch1 >> 4 if scratch2 >=U 7 #TODO: is MSB just ignored or is this treated as some kind of illegal instruction or nop? else scratch1 &= 0xF if scratch1 >=U 7 scratch1 &= 7 meta dst tmp_dst tmp_dst = a << 8 tmp_dst |= x switch scratch1 case 0 tmp_src = a << 8 tmp_src |= x case 2 tmp_src = b << 8 tmp_src |= c case 4 tmp_src = d << 8 tmp_src |= e case 6 tmp_src = h << 8 tmp_src |= l default #TODO: what happens in these invalid cases end aluop16 P tmp_src x = tmp_dst a = tmp_dst >> 8 else meta dst main.scratch2 cycles 2 #penalty for extra IRAM access since regs are technically stored there? aluop P main.scratch1 end end 10101PPP alu_immed upd78k2_op_fetch meta dst a aluop P scratch1 01101PPP alu_saddr_immed local offset 8 local immed 8 upd78k2_op_fetch offset = scratch1 upd78k2_op_fetch immed = scratch1 saddr_read offset meta dst scratch1 aluop P immed saddr_write offset scratch1 calc_addr_base arg regpair 8 local tmp 16 upd78k2_op_fetch switch regpair case 0 #[de+byte] tmp = d << 8 tmp |= e adst += tmp case 1 #[sp+byte] adst += sp case 2 #[hl+byte] tmp = h << 8 tmp |= l adst += tmp end read_base_mode arg regpair 8 arg alt_bank 8 meta adst scratch1 calc_addr_base regpair mem_read scratch1 alt_bank write_base_mode arg regpair 8 arg value 8 arg alt_bank 8 meta adst scratch2 calc_addr_base regpair mem_write scratch2 value alt_bank calc_addr_regind arg regpair 8 local tmp 16 switch regpair case 0 #[de+] adst = d << 8 adst |= e tmp = adst + 1 e = tmp d = tmp >> 8 case 1 #[hl+] adst = h << 8 adst |= l tmp = adst + 1 l = tmp h = tmp >> 8 case 2 #[de-] adst = d << 8 adst |= e tmp = adst - 1 e = tmp d = tmp >> 8 case 3 #[hl-] adst = h << 8 adst |= l tmp = adst - 1 l = tmp h = tmp >> 8 case 4 #[de] adst = d << 8 adst |= e case 5 #[hl] adst = h << 8 adst |= l end read_regind_mode arg regpair 8 arg alt_bank 8 meta adst scratch1 calc_addr_regind regpair mem_read scratch1 alt_bank write_regind_mode arg regpair 8 arg value 8 arg alt_bank 8 meta adst scratch2 calc_addr_regind regpair mem_write scratch2 value alt_bank calc_addr_indexed arg index_reg 8 local tmp 16 upd78k2_op_fetch_word switch index_reg case 0 tmp = d << 8 tmp |= e adst += tmp case 1 adst += a case 2 tmp = h << 8 tmp |= l adst += tmp case 3 adst += b end read_indexed_mode arg index_reg 8 arg alt_bank 8 meta adst scratch1 calc_addr_indexed index_reg mem_read scratch1 alt_bank write_indexed_mode arg index_reg 8 arg value 8 arg alt_bank 8 meta adst scratch2 calc_addr_indexed index_reg mem_write scratch2 value alt_bank base 00RR1PPP alu_base invalid R 3 read_base_mode R 0 meta dst a aluop P scratch1 alt_base 00RR1PPP alu_alt_base invalid R 3 read_base_mode R 1 meta dst a aluop P scratch1 base 00RR0000 mov_a_base invalid R 3 read_base_mode R 0 a = scratch1 alt_base 00RR0000 alt_mov_a_base invalid R 3 read_base_mode R 1 a = scratch1 base 10RR0000 mov_base_a invalid R 3 write_base_mode R a 0 alt_base 10RR0000 alt_mov_base_a invalid R 3 write_base_mode R a 1 regind 0RRR1PPP alu_reg_indirect invalid R 6 invalid R 7 read_regind_mode R 0 meta dst a aluop P scratch1 alt_regind 0RRR1PPP alu_alt_reg_indirect invalid R 6 invalid R 7 read_regind_mode R 1 meta dst a aluop P scratch1 regind 0RRR0000 mov_a_reg_indirect invalid R 6 invalid R 7 read_regind_mode R 0 a = scratch1 alt_regind 0RRR0000 alt_mov_a_reg_indirect invalid R 6 invalid R 7 read_regind_mode R 1 a = scratch1 regind 1RRR0000 mov_reg_indirect_a invalid R 6 invalid R 7 write_regind_mode R a 0 alt_regind 1RRR0000 alt_mov_reg_indirect_a invalid R 6 invalid R 7 write_regind_mode R a 1 01010RRR mov_reg_indirect_a_short invalid R 6 invalid R 7 write_regind_mode R a 0 01011RRR mov_a_reg_indirect_short invalid R 6 invalid R 7 read_regind_mode R 0 a = scratch1 indexed 00RR1PPP alu_indexed read_indexed_mode R 0 meta dst a aluop P scratch1 alt_indexed 00RR1PPP alu_alt_indexed read_indexed_mode R 1 meta dst a aluop P scratch1 indexed 00RR0000 mov_a_indexed read_indexed_mode R 0 a = scratch1 alt_indexed 00RR0000 alt_mov_a_indexed read_indexed_mode R 1 a = scratch1 indexed 10RR0000 mov_indexed_a write_indexed_mode R a 0 alt_indexed 10RR0000 alt_mov_indexed_a write_indexed_mode R a 1 mov_reg 0DD01SS0 movw_rp_rp local dst 8 local src 8 dst = D << 1 src = S << 1 main.dst = main.src dst += 1 src += 1 main.dst = main.src mov_reg 0DDD0SSS mov_r_r main.D = main.S 01100PP0 movw_rp_immed local dst 8 dst = P << 1 upd78k2_op_fetch main.dst = scratch1 dst += 1 upd78k2_op_fetch main.dst = scratch1 10111RRR mov_r_immed local dst 8 dst = R << 1 upd78k2_op_fetch main.dst = scratch1 spmov 11000000 mov_stbc_immed local tmp 8 upd78k2_op_fetch tmp = ~scratch1 upd78k2_op_fetch if tmp = scratch1 stbc = tmp #TODO: actually handle stbc changes end spmov 11110000 mov_a_abs upd78k2_op_fetch_word mem_read_no_exp scratch1 a = scratch1 spmov 11110001 mov_abs_a upd78k2_op_fetch_word scratch2 = scratch1 mem_write_no_exp scratch2 a 11000RRR inc_r cycles 2 main.R += 1 update_flags ZA 11001RRR dec_r cycles 2 main.R -= 1 update_flags ZA 11010RRR mov_a_r a = main.R muldiv 01001PP0 br_rp local reg 8 local tmp 16 reg = P << 1 pc = main.reg reg += 1 tmp = main.reg << 8 pc |= tmp 00010100 br_rel upd78k2_op_fetch sext 16 scratch1 scratch1 pc += scratch1 00101100 br_abs upd78k2_op_fetch_word pc = scratch1 100000FS bcc local flag 8 upd78k2_op_fetch if F flag = chflags >> 7 else flag = zflag end if flag = S sext 16 scratch1 scratch1 pc += scratch1 end bit1 10100BBB bf_psw local mask 8 upd78k2_op_fetch mask = 1 << B mask &= psw if = sext 16 scratch1 scratch1 pc += scratch1 end bit1 10110BBB bt_psw local mask 8 upd78k2_op_fetch mask = 1 << B mask &= psw if != sext 16 scratch1 scratch1 pc += scratch1 end bit2 1010RBBB bf_r local mask 8 upd78k2_op_fetch mask = 1 << B mask &= main.R if = sext 16 scratch1 scratch1 pc += scratch1 end bit2 1011RBBB bt_r local mask 8 upd78k2_op_fetch mask = 1 << B mask &= main.R if != sext 16 scratch1 scratch1 pc += scratch1 end sfrbit 10100BBB bf_saddr local mask 8 local tmp 8 upd78k2_op_fetch saddr_read scratch1 tmp = scratch1 mask = 1 << B upd78k2_op_fetch mask &= tmp if = sext 16 scratch1 scratch1 pc += scratch1 end 01110BBB bt_saddr local mask 8 local tmp 8 upd78k2_op_fetch saddr_read scratch1 tmp = scratch1 mask = 1 << B upd78k2_op_fetch mask &= tmp if != sext 16 scratch1 scratch1 pc += scratch1 end sfrbit 10101BBB bf_sfr local mask 8 local tmp 8 upd78k2_op_fetch sfr_read scratch1 tmp = scratch1 mask = 1 << B upd78k2_op_fetch mask &= tmp if = sext 16 scratch1 scratch1 pc += scratch1 end sfrbit 10111BBB bt_sfr local mask 8 local tmp 8 upd78k2_op_fetch sfr_read scratch1 tmp = scratch1 mask = 1 << B upd78k2_op_fetch mask &= tmp if != sext 16 scratch1 scratch1 pc += scratch1 end muldiv 01011PP0 call_rp local reg 8 local tmp 16 push_word pc reg = P << 1 pc = main.reg reg += 1 tmp = main.reg << 8 pc |= tmp 00101000 call_long local address 16 upd78k2_op_fetch_word address = scratch1 push_word pc pc = address 10010AAA call_short local address 16 address = A << 8 address |= 0x800 upd78k2_op_fetch address |= scratch1 push_word pc pc = address 111TTTTT call_table local address 16 local offset 8 offset = T << 1 offset += 0x40 mem_read_no_exp T address = scratch1 scratch1 = T + 1 mem_read_no_exp scratch1 scratch1 <<= 8 address |= 8 push_word pc pc = address 01001010 di int_enable = 0 update_sync 01001011 ei int_enable = 1 update_sync if cycles >=U int_cycle int_cycle = cycles + 1 end 01010110 ret meta dst pc pop_word 001101PP pop_rp local rp 8 local word 16 meta dst word pop_word main.rp = word rp += 1 main.rp = word >> 8 001111PP push_rp local rp 8 local word 16 rp = P << 1 rp += 1 word = main.rp << 8 rp -= 1 word |= main.rp push_word word muldiv 101010NN sel local offset 8 if N != rbs offset = rbs << 3 offset = 0xF8 - offset iram.offset = x offset += 1 iram.offset = a offset += 1 iram.offset = c offset += 1 iram.offset = b offset += 1 iram.offset = e offset += 1 iram.offset = d offset += 1 iram.offset = l offset += 1 iram.offset = h offset = N << 3 offset = 0xF8 - offset x = iram.offset offset += 1 a = iram.offset offset += 1 c = iram.offset offset += 1 b = iram.offset offset += 1 e = iram.offset offset += 1 d = iram.offset offset += 1 l = iram.offset offset += 1 h = iram.offset rbs = N end muldiv 11001000 incw_sp sp += 1 muldiv 11001001 decw_sp sp -= 1 00001011 movw_sfrp_immed local offset 8 upd78k2_op_fetch if scratch1 = 0xFC #unclear if SP is actually mapped in the SFR space #or if this is special cased, but the docs don't #suggest you can write to SP with other SFR-targeting #instructions so I'm assuming the latter. upd78k2_op_fetch sp = scratch1 upd78k2_op_fetch scratch1 <<= 8 sp |= scratch1 else offset = scratch1 upd78k2_op_fetch sfr_write offset scratch1 offset += 1 upd78k2_op_fetch sfr_write offset scratch1 end 00001100 movw_sadrp_word local offset 8 upd78k2_op_fetch offset = scratch1 upd78k2_op_fetch saddr_write offset scratch1 offset += 1 upd78k2_op_fetch saddr_write offset scratch1 00101011 mov_sfr_immed local offset 8 upd78k2_op_fetch offset = scratch1 upd78k2_op_fetch if offset = 0xFE psw = scratch1 else sfr_write offset scratch1 end 00111010 mov_saddr_immed local offset 8 upd78k2_op_fetch offset = scratch1 upd78k2_op_fetch saddr_write offset scratch1 sfrbit 00000BBB mov1_cy_saddr local mask 8 upd78k2_op_fetch saddr_read scratch1 mask = 1 << B scratch1 &= mask if = update_flags C0 else update_flags C1 end sfrbit 00001BBB mov1_cy_sfr local mask 8 upd78k2_op_fetch sfr_read scratch1 mask = 1 << B scratch1 &= mask if = update_flags C0 else update_flags C1 end sfrbit 00010BBB mov1_saddr_cy local mask 8 local offset 8 upd78k2_op_fetch offset = scratch1 saddr_read offset mask = 1 << B if chflags >=U 0x80 scratch1 |= mask else mask = ~mask scratch1 &= mask end saddr_write offset scratch1 sfrbit 00011BBB mov1_sfr_cy local mask 8 local offset 8 upd78k2_op_fetch offset = scratch1 sfr_read offset mask = 1 << B if chflags >=U 0x80 scratch1 |= mask else mask = ~mask scratch1 &= mask end sfr_write offset scratch1 sfrbit 001I0BBB and1_cy_saddr local mask 8 upd78k2_op_fetch saddr_read scratch1 if I scratch1 = ~scratch1 end mask = 1 << B scratch1 &= mask if = update_flags C0 end sfrbit 001I1BBB and1_cy_sfr local mask 8 upd78k2_op_fetch sfr_read scratch1 if I scratch1 = ~scratch1 end mask = 1 << B scratch1 &= mask if = update_flags C0 end sfrbit 010I0BBB or1_cy_saddr local mask 8 upd78k2_op_fetch saddr_read scratch1 if I scratch1 = ~scratch1 end mask = 1 << B scratch1 &= mask if != update_flags C1 end sfrbit 010I1BBB or1_cy_sfr local mask 8 upd78k2_op_fetch sfr_read scratch1 if I scratch1 = ~scratch1 end mask = 1 << B scratch1 &= mask if != update_flags C1 end sfrbit 01100BBB xor1_cy_saddr local mask 8 upd78k2_op_fetch saddr_read scratch1 mask = 1 << B scratch1 &= mask if != if chflags >=U 0x80 update_flags C0 else update_flags C1 end end sfrbit 01101BBB xor1_cy_sfr local mask 8 upd78k2_op_fetch sfr_read scratch1 mask = 1 << B scratch1 &= mask if != if chflags >=U 0x80 update_flags C0 else update_flags C1 end end sfrbit 01110BBB not1_saddr local mask 8 local offset 8 upd78k2_op_fetch offset = scratch1 saddr_read offset mask = 1 << B scratch1 ^= mask saddr_write offset scratch1 sfrbit 01111BBB not1_sfr local mask 8 local offset 8 upd78k2_op_fetch offset = scratch1 sfr_read offset mask = 1 << B scratch1 ^= mask sfr_write offset scratch1 sfrbit 10001BBB set1_sfr local mask 8 local offset 8 upd78k2_op_fetch offset = scratch1 sfr_read offset mask = 1 << B scratch1 |= mask sfr_write offset scratch1 sfrbit 10011BBB clr1_sfr local mask 8 local offset 8 upd78k2_op_fetch offset = scratch1 sfr_read offset mask = 1 << B mask = ~mask scratch1 &= mask sfr_write offset scratch1 10110BBB set1_saddr_cy local mask 8 local offset 8 upd78k2_op_fetch offset = scratch1 saddr_read offset mask = 1 << B scratch1 |= mask saddr_write offset scratch1 10100BBB clr1_saddr_cy local mask 8 local offset 8 upd78k2_op_fetch offset = scratch1 saddr_read offset mask = 1 << B mask = ~mask scratch1 &= mask saddr_write offset scratch1