Mercurial > repos > blastem
diff z80_to_x86.c @ 2675:dbff641a33df
Implement Z80/PSG clock speed test register bit
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 14 Mar 2025 01:18:11 -0700 |
parents | 767ec72acca7 |
children |
line wrap: on
line diff
--- a/z80_to_x86.c Sun Mar 09 22:53:04 2025 -0700 +++ b/z80_to_x86.c Fri Mar 14 01:18:11 2025 -0700 @@ -3211,6 +3211,9 @@ options->gen.mem_ptr_off = offsetof(z80_context, mem_pointers); options->gen.ram_flags_off = offsetof(z80_context, ram_code_flags); options->gen.ram_flags_shift = 7; + options->io_memmap = io_chunks; + options->io_memmap_chunks = num_io_chunks; + options->io_address_mask = io_address_mask; options->flags = 0; #ifdef X86_64 @@ -3379,206 +3382,7 @@ options->gen.handle_code_write = (code_ptr)z80_handle_code_write; - options->read_8 = gen_mem_fun(&options->gen, chunks, num_chunks, READ_8, &options->read_8_noinc); - options->write_8 = gen_mem_fun(&options->gen, chunks, num_chunks, WRITE_8, &options->write_8_noinc); - - code_ptr skip_int = code->cur; - //calculate adjust size - add_ir(code, 16-sizeof(void *), RSP, SZ_PTR); - uint32_t adjust_size = code->cur - skip_int; - code->cur = skip_int; - - cmp_rdispr(code, options->gen.context_reg, offsetof(z80_context, sync_cycle), options->gen.cycles, SZ_D); - code_ptr skip_sync = code->cur + 1; - jcc(code, CC_B, skip_sync); - neg_r(code, options->gen.cycles, SZ_D); - add_rdispr(code, options->gen.context_reg, offsetof(z80_context, target_cycle), options->gen.cycles, SZ_D); - //save PC - mov_rrdisp(code, options->gen.scratch1, options->gen.context_reg, offsetof(z80_context, pc), SZ_D); - options->do_sync = code->cur; - call(code, options->gen.save_context); - tmp_stack_off = code->stack_off; - //pop return address off the stack and save for resume later - //pop_rind(code, options->gen.context_reg); - pop_r(code, RAX); - add_ir(code, adjust_size, RAX, SZ_PTR); - add_ir(code, 16-sizeof(void *), RSP, SZ_PTR); - mov_rrind(code, RAX, options->gen.context_reg, SZ_PTR); - - //restore callee saved registers - restore_callee_save_regs(code); - //return to caller of z80_run - retn(code); - *skip_sync = code->cur - (skip_sync+1); - neg_r(code, options->gen.cycles, SZ_D); - add_rdispr(code, options->gen.context_reg, offsetof(z80_context, target_cycle), options->gen.cycles, SZ_D); - retn(code); - code->stack_off = tmp_stack_off; - - options->gen.handle_cycle_limit_int = code->cur; - neg_r(code, options->gen.cycles, SZ_D); - add_rdispr(code, options->gen.context_reg, offsetof(z80_context, target_cycle), options->gen.cycles, SZ_D); - cmp_rdispr(code, options->gen.context_reg, offsetof(z80_context, int_cycle), options->gen.cycles, SZ_D); - jcc(code, CC_B, skip_int); - //check that we are not past the end of interrupt pulse - cmp_rrdisp(code, options->gen.cycles, options->gen.context_reg, offsetof(z80_context, int_pulse_end), SZ_D); - jcc(code, CC_B, skip_int); - //set limit to the cycle limit - mov_rdispr(code, options->gen.context_reg, offsetof(z80_context, sync_cycle), options->gen.scratch2, SZ_D); - mov_rrdisp(code, options->gen.scratch2, options->gen.context_reg, offsetof(z80_context, target_cycle), SZ_D); - neg_r(code, options->gen.cycles, SZ_D); - add_rr(code, options->gen.scratch2, options->gen.cycles, SZ_D); - //disable interrupts - cmp_irdisp(code, 0, options->gen.context_reg, offsetof(z80_context, int_is_nmi), SZ_B); - code_ptr is_nmi = code->cur + 1; - jcc(code, CC_NZ, is_nmi); - mov_irdisp(code, 0, options->gen.context_reg, offsetof(z80_context, iff1), SZ_B); - mov_irdisp(code, 0, options->gen.context_reg, offsetof(z80_context, iff2), SZ_B); - cycles(&options->gen, 6); //interupt ack cycle - code_ptr after_int_disable = code->cur + 1; - jmp(code, after_int_disable); - *is_nmi = code->cur - (is_nmi + 1); - mov_rdispr(code, options->gen.context_reg, offsetof(z80_context, iff1), options->gen.scratch2, SZ_B); - mov_irdisp(code, 0, options->gen.context_reg, offsetof(z80_context, iff1), SZ_B); - mov_rrdisp(code, options->gen.scratch2, options->gen.context_reg, offsetof(z80_context, iff2), SZ_B); - cycles(&options->gen, 5); //NMI processing cycles - *after_int_disable = code->cur - (after_int_disable + 1); - //save return address (in scratch1) to Z80 stack - sub_ir(code, 2, options->regs[Z80_SP], SZ_W); - mov_rr(code, options->regs[Z80_SP], options->gen.scratch2, SZ_W); - //we need to do check_cycles and cycles outside of the write_8 call - //so that the stack has the correct depth if we need to return to C - //for a synchronization - check_cycles(&options->gen); - cycles(&options->gen, 3); - //save word to write before call to write_8_noinc - push_r(code, options->gen.scratch1); - call(code, options->write_8_noinc); - //restore word to write - pop_r(code, options->gen.scratch1); - //write high byte to SP+1 - mov_rr(code, options->regs[Z80_SP], options->gen.scratch2, SZ_W); - add_ir(code, 1, options->gen.scratch2, SZ_W); - shr_ir(code, 8, options->gen.scratch1, SZ_W); - check_cycles(&options->gen); - cycles(&options->gen, 3); - call(code, options->write_8_noinc); - //dispose of return address as we'll be jumping somewhere else - add_ir(code, 16, RSP, SZ_PTR); - cmp_irdisp(code, 0, options->gen.context_reg, offsetof(z80_context, int_is_nmi), SZ_B); - is_nmi = code->cur + 1; - jcc(code, CC_NZ, is_nmi); - //TODO: Support interrupt mode 0, not needed for Genesis sit it seems to read $FF during intack - //which is conveniently rst $38, i.e. the same thing that im 1 does - //check interrupt mode - cmp_irdisp(code, 2, options->gen.context_reg, offsetof(z80_context, im), SZ_B); - code_ptr im2 = code->cur + 1; - jcc(code, CC_Z, im2); - mov_ir(code, 0x38, options->gen.scratch1, SZ_W); - cycles(&options->gen, 1); //total time for mode 0/1 is 13 t-states - code_ptr after_int_dest = code->cur + 1; - jmp(code, after_int_dest); - *im2 = code->cur - (im2 + 1); - //read vector address from I << 8 | vector - mov_rdispr(code, options->gen.context_reg, offsetof(z80_context, regs) + Z80_I, options->gen.scratch1, SZ_B); - shl_ir(code, 8, options->gen.scratch1, SZ_W); - movzx_rdispr(code, options->gen.context_reg, offsetof(z80_context, im2_vector), options->gen.scratch2, SZ_B, SZ_W); - or_rr(code, options->gen.scratch2, options->gen.scratch1, SZ_W); - push_r(code, options->gen.scratch1); - cycles(&options->gen, 3); - call(code, options->read_8_noinc); - pop_r(code, options->gen.scratch2); - push_r(code, options->gen.scratch1); - mov_rr(code, options->gen.scratch2, options->gen.scratch1, SZ_W); - add_ir(code, 1, options->gen.scratch1, SZ_W); - cycles(&options->gen, 3); - call(code, options->read_8_noinc); - pop_r(code, options->gen.scratch2); - shl_ir(code, 8, options->gen.scratch1, SZ_W); - movzx_rr(code, options->gen.scratch2, options->gen.scratch2, SZ_B, SZ_W); - or_rr(code, options->gen.scratch2, options->gen.scratch1, SZ_W); - code_ptr after_int_dest2 = code->cur + 1; - jmp(code, after_int_dest2); - *is_nmi = code->cur - (is_nmi + 1); - mov_irdisp(code, 0, options->gen.context_reg, offsetof(z80_context, int_is_nmi), SZ_B); - mov_irdisp(code, CYCLE_NEVER, options->gen.context_reg, offsetof(z80_context, nmi_start), SZ_D); - mov_ir(code, 0x66, options->gen.scratch1, SZ_W); - *after_int_dest = code->cur - (after_int_dest + 1); - *after_int_dest2 = code->cur - (after_int_dest2 + 1); - call(code, options->native_addr); - mov_rrind(code, options->gen.scratch1, options->gen.context_reg, SZ_PTR); - tmp_stack_off = code->stack_off; - restore_callee_save_regs(code); - //return to caller of z80_run to sync - retn(code); - code->stack_off = tmp_stack_off; - - //HACK - options->gen.address_size = SZ_D; - options->gen.address_mask = io_address_mask; - options->gen.bus_cycles = 4; - options->read_io = gen_mem_fun(&options->gen, io_chunks, num_io_chunks, READ_8, NULL); - options->write_io = gen_mem_fun(&options->gen, io_chunks, num_io_chunks, WRITE_8, NULL); - options->gen.address_size = SZ_W; - options->gen.address_mask = 0xFFFF; - options->gen.bus_cycles = 3; - - options->read_16 = code->cur; - cycles(&options->gen, 3); - check_cycles(&options->gen); - //TODO: figure out how to handle the extra wait state for word reads to bank area - //may also need special handling to avoid too much stack depth when access is blocked - push_r(code, options->gen.scratch1); - call(code, options->read_8_noinc); - mov_rr(code, options->gen.scratch1, options->gen.scratch2, SZ_B); -#ifndef X86_64 - //scratch 2 is a caller save register in 32-bit builds and may be clobbered by something called from the read8 fun - mov_rrdisp(code, options->gen.scratch1, options->gen.context_reg, offsetof(z80_context, scratch2), SZ_B); -#endif - pop_r(code, options->gen.scratch1); - add_ir(code, 1, options->gen.scratch1, SZ_W); - cycles(&options->gen, 3); - check_cycles(&options->gen); - call(code, options->read_8_noinc); - shl_ir(code, 8, options->gen.scratch1, SZ_W); -#ifdef X86_64 - mov_rr(code, options->gen.scratch2, options->gen.scratch1, SZ_B); -#else - mov_rdispr(code, options->gen.context_reg, offsetof(z80_context, scratch2), options->gen.scratch1, SZ_B); -#endif - retn(code); - - options->write_16_highfirst = code->cur; - cycles(&options->gen, 3); - check_cycles(&options->gen); - push_r(code, options->gen.scratch2); - push_r(code, options->gen.scratch1); - add_ir(code, 1, options->gen.scratch2, SZ_W); - shr_ir(code, 8, options->gen.scratch1, SZ_W); - call(code, options->write_8_noinc); - pop_r(code, options->gen.scratch1); - pop_r(code, options->gen.scratch2); - cycles(&options->gen, 3); - check_cycles(&options->gen); - //TODO: Check if we can get away with TCO here - call(code, options->write_8_noinc); - retn(code); - - options->write_16_lowfirst = code->cur; - cycles(&options->gen, 3); - check_cycles(&options->gen); - push_r(code, options->gen.scratch2); - push_r(code, options->gen.scratch1); - call(code, options->write_8_noinc); - pop_r(code, options->gen.scratch1); - pop_r(code, options->gen.scratch2); - add_ir(code, 1, options->gen.scratch2, SZ_W); - shr_ir(code, 8, options->gen.scratch1, SZ_W); - cycles(&options->gen, 3); - check_cycles(&options->gen); - //TODO: Check if we can get away with TCO here - call(code, options->write_8_noinc); - retn(code); + z80_clock_divider_updated(options); options->retrans_stub = code->cur; tmp_stack_off = code->stack_off; @@ -3865,6 +3669,212 @@ code->stack_off = start_stack_off; } +void z80_clock_divider_updated(z80_options *options) +{ + //TODO: make this not leak memory whenever the clock changes + options->read_8 = gen_mem_fun(&options->gen, options->gen.memmap, options->gen.memmap_chunks, READ_8, &options->read_8_noinc); + options->write_8 = gen_mem_fun(&options->gen, options->gen.memmap, options->gen.memmap_chunks, WRITE_8, &options->write_8_noinc); + + code_info *code = &options->gen.code; + code_ptr skip_int = code->cur; + //calculate adjust size + add_ir(code, 16-sizeof(void *), RSP, SZ_PTR); + uint32_t adjust_size = code->cur - skip_int; + code->cur = skip_int; + + cmp_rdispr(code, options->gen.context_reg, offsetof(z80_context, sync_cycle), options->gen.cycles, SZ_D); + code_ptr skip_sync = code->cur + 1; + jcc(code, CC_B, skip_sync); + neg_r(code, options->gen.cycles, SZ_D); + add_rdispr(code, options->gen.context_reg, offsetof(z80_context, target_cycle), options->gen.cycles, SZ_D); + //save PC + mov_rrdisp(code, options->gen.scratch1, options->gen.context_reg, offsetof(z80_context, pc), SZ_D); + options->do_sync = code->cur; + call(code, options->gen.save_context); + uint32_t tmp_stack_off = code->stack_off; + //pop return address off the stack and save for resume later + //pop_rind(code, options->gen.context_reg); + pop_r(code, RAX); + add_ir(code, adjust_size, RAX, SZ_PTR); + add_ir(code, 16-sizeof(void *), RSP, SZ_PTR); + mov_rrind(code, RAX, options->gen.context_reg, SZ_PTR); + + //restore callee saved registers + restore_callee_save_regs(code); + //return to caller of z80_run + retn(code); + *skip_sync = code->cur - (skip_sync+1); + neg_r(code, options->gen.cycles, SZ_D); + add_rdispr(code, options->gen.context_reg, offsetof(z80_context, target_cycle), options->gen.cycles, SZ_D); + retn(code); + code->stack_off = tmp_stack_off; + + options->gen.handle_cycle_limit_int = code->cur; + neg_r(code, options->gen.cycles, SZ_D); + add_rdispr(code, options->gen.context_reg, offsetof(z80_context, target_cycle), options->gen.cycles, SZ_D); + cmp_rdispr(code, options->gen.context_reg, offsetof(z80_context, int_cycle), options->gen.cycles, SZ_D); + jcc(code, CC_B, skip_int); + //check that we are not past the end of interrupt pulse + cmp_rrdisp(code, options->gen.cycles, options->gen.context_reg, offsetof(z80_context, int_pulse_end), SZ_D); + jcc(code, CC_B, skip_int); + //set limit to the cycle limit + mov_rdispr(code, options->gen.context_reg, offsetof(z80_context, sync_cycle), options->gen.scratch2, SZ_D); + mov_rrdisp(code, options->gen.scratch2, options->gen.context_reg, offsetof(z80_context, target_cycle), SZ_D); + neg_r(code, options->gen.cycles, SZ_D); + add_rr(code, options->gen.scratch2, options->gen.cycles, SZ_D); + //disable interrupts + cmp_irdisp(code, 0, options->gen.context_reg, offsetof(z80_context, int_is_nmi), SZ_B); + code_ptr is_nmi = code->cur + 1; + jcc(code, CC_NZ, is_nmi); + mov_irdisp(code, 0, options->gen.context_reg, offsetof(z80_context, iff1), SZ_B); + mov_irdisp(code, 0, options->gen.context_reg, offsetof(z80_context, iff2), SZ_B); + cycles(&options->gen, 6); //interupt ack cycle + code_ptr after_int_disable = code->cur + 1; + jmp(code, after_int_disable); + *is_nmi = code->cur - (is_nmi + 1); + mov_rdispr(code, options->gen.context_reg, offsetof(z80_context, iff1), options->gen.scratch2, SZ_B); + mov_irdisp(code, 0, options->gen.context_reg, offsetof(z80_context, iff1), SZ_B); + mov_rrdisp(code, options->gen.scratch2, options->gen.context_reg, offsetof(z80_context, iff2), SZ_B); + cycles(&options->gen, 5); //NMI processing cycles + *after_int_disable = code->cur - (after_int_disable + 1); + //save return address (in scratch1) to Z80 stack + sub_ir(code, 2, options->regs[Z80_SP], SZ_W); + mov_rr(code, options->regs[Z80_SP], options->gen.scratch2, SZ_W); + //we need to do check_cycles and cycles outside of the write_8 call + //so that the stack has the correct depth if we need to return to C + //for a synchronization + check_cycles(&options->gen); + cycles(&options->gen, 3); + //save word to write before call to write_8_noinc + push_r(code, options->gen.scratch1); + call(code, options->write_8_noinc); + //restore word to write + pop_r(code, options->gen.scratch1); + //write high byte to SP+1 + mov_rr(code, options->regs[Z80_SP], options->gen.scratch2, SZ_W); + add_ir(code, 1, options->gen.scratch2, SZ_W); + shr_ir(code, 8, options->gen.scratch1, SZ_W); + check_cycles(&options->gen); + cycles(&options->gen, 3); + call(code, options->write_8_noinc); + //dispose of return address as we'll be jumping somewhere else + add_ir(code, 16, RSP, SZ_PTR); + cmp_irdisp(code, 0, options->gen.context_reg, offsetof(z80_context, int_is_nmi), SZ_B); + is_nmi = code->cur + 1; + jcc(code, CC_NZ, is_nmi); + //TODO: Support interrupt mode 0, not needed for Genesis sit it seems to read $FF during intack + //which is conveniently rst $38, i.e. the same thing that im 1 does + //check interrupt mode + cmp_irdisp(code, 2, options->gen.context_reg, offsetof(z80_context, im), SZ_B); + code_ptr im2 = code->cur + 1; + jcc(code, CC_Z, im2); + mov_ir(code, 0x38, options->gen.scratch1, SZ_W); + cycles(&options->gen, 1); //total time for mode 0/1 is 13 t-states + code_ptr after_int_dest = code->cur + 1; + jmp(code, after_int_dest); + *im2 = code->cur - (im2 + 1); + //read vector address from I << 8 | vector + mov_rdispr(code, options->gen.context_reg, offsetof(z80_context, regs) + Z80_I, options->gen.scratch1, SZ_B); + shl_ir(code, 8, options->gen.scratch1, SZ_W); + movzx_rdispr(code, options->gen.context_reg, offsetof(z80_context, im2_vector), options->gen.scratch2, SZ_B, SZ_W); + or_rr(code, options->gen.scratch2, options->gen.scratch1, SZ_W); + push_r(code, options->gen.scratch1); + cycles(&options->gen, 3); + call(code, options->read_8_noinc); + pop_r(code, options->gen.scratch2); + push_r(code, options->gen.scratch1); + mov_rr(code, options->gen.scratch2, options->gen.scratch1, SZ_W); + add_ir(code, 1, options->gen.scratch1, SZ_W); + cycles(&options->gen, 3); + call(code, options->read_8_noinc); + pop_r(code, options->gen.scratch2); + shl_ir(code, 8, options->gen.scratch1, SZ_W); + movzx_rr(code, options->gen.scratch2, options->gen.scratch2, SZ_B, SZ_W); + or_rr(code, options->gen.scratch2, options->gen.scratch1, SZ_W); + code_ptr after_int_dest2 = code->cur + 1; + jmp(code, after_int_dest2); + *is_nmi = code->cur - (is_nmi + 1); + mov_irdisp(code, 0, options->gen.context_reg, offsetof(z80_context, int_is_nmi), SZ_B); + mov_irdisp(code, CYCLE_NEVER, options->gen.context_reg, offsetof(z80_context, nmi_start), SZ_D); + mov_ir(code, 0x66, options->gen.scratch1, SZ_W); + *after_int_dest = code->cur - (after_int_dest + 1); + *after_int_dest2 = code->cur - (after_int_dest2 + 1); + call(code, options->native_addr); + mov_rrind(code, options->gen.scratch1, options->gen.context_reg, SZ_PTR); + tmp_stack_off = code->stack_off; + restore_callee_save_regs(code); + //return to caller of z80_run to sync + retn(code); + code->stack_off = tmp_stack_off; + + //HACK + options->gen.address_size = SZ_D; + options->gen.address_mask = options->io_address_mask; + options->gen.bus_cycles = 4; + options->read_io = gen_mem_fun(&options->gen, options->io_memmap, options->io_memmap_chunks, READ_8, NULL); + options->write_io = gen_mem_fun(&options->gen, options->io_memmap, options->io_memmap_chunks, WRITE_8, NULL); + options->gen.address_size = SZ_W; + options->gen.address_mask = 0xFFFF; + options->gen.bus_cycles = 3; + + options->read_16 = code->cur; + cycles(&options->gen, 3); + check_cycles(&options->gen); + //TODO: figure out how to handle the extra wait state for word reads to bank area + //may also need special handling to avoid too much stack depth when access is blocked + push_r(code, options->gen.scratch1); + call(code, options->read_8_noinc); + mov_rr(code, options->gen.scratch1, options->gen.scratch2, SZ_B); +#ifndef X86_64 + //scratch 2 is a caller save register in 32-bit builds and may be clobbered by something called from the read8 fun + mov_rrdisp(code, options->gen.scratch1, options->gen.context_reg, offsetof(z80_context, scratch2), SZ_B); +#endif + pop_r(code, options->gen.scratch1); + add_ir(code, 1, options->gen.scratch1, SZ_W); + cycles(&options->gen, 3); + check_cycles(&options->gen); + call(code, options->read_8_noinc); + shl_ir(code, 8, options->gen.scratch1, SZ_W); +#ifdef X86_64 + mov_rr(code, options->gen.scratch2, options->gen.scratch1, SZ_B); +#else + mov_rdispr(code, options->gen.context_reg, offsetof(z80_context, scratch2), options->gen.scratch1, SZ_B); +#endif + retn(code); + + options->write_16_highfirst = code->cur; + cycles(&options->gen, 3); + check_cycles(&options->gen); + push_r(code, options->gen.scratch2); + push_r(code, options->gen.scratch1); + add_ir(code, 1, options->gen.scratch2, SZ_W); + shr_ir(code, 8, options->gen.scratch1, SZ_W); + call(code, options->write_8_noinc); + pop_r(code, options->gen.scratch1); + pop_r(code, options->gen.scratch2); + cycles(&options->gen, 3); + check_cycles(&options->gen); + //TODO: Check if we can get away with TCO here + call(code, options->write_8_noinc); + retn(code); + + options->write_16_lowfirst = code->cur; + cycles(&options->gen, 3); + check_cycles(&options->gen); + push_r(code, options->gen.scratch2); + push_r(code, options->gen.scratch1); + call(code, options->write_8_noinc); + pop_r(code, options->gen.scratch1); + pop_r(code, options->gen.scratch2); + add_ir(code, 1, options->gen.scratch2, SZ_W); + shr_ir(code, 8, options->gen.scratch1, SZ_W); + cycles(&options->gen, 3); + check_cycles(&options->gen); + //TODO: Check if we can get away with TCO here + call(code, options->write_8_noinc); + retn(code); +} + void zinsert_breakpoint(z80_context * context, uint16_t address, uint8_t * bp_handler) { context->bp_handler = bp_handler;