mirror of
https://github.com/mlaux/gb6.git
synced 2026-10-25 07:02:33 +00:00
468 lines
17 KiB
C
468 lines
17 KiB
C
#include "compiler.h"
|
|
#include "emitters.h"
|
|
#include "interop.h"
|
|
|
|
// Retro68 uses D0-D2 as scratch so I have to push cycle count before calling
|
|
// back into C. i'm not sure if this is a mac calling convention or specific
|
|
// to this gcc port.
|
|
// also interestingly, it doesn't appear to use the "A5 world" or A6, so i can
|
|
// use those registers while in the JIT world. calling back into C won't mess
|
|
// them up
|
|
|
|
// look up the page table entry for the GB address in addr_dreg (clobbered)
|
|
// and leave it in dest_areg. table entries are biased so that
|
|
// entry + (s16)gb_address = host pointer (see PAGE_BIAS in dmg.h), which
|
|
// lets the full address in D1.w index the page directly with (An,Dn.w)
|
|
// sign-extended addressing
|
|
void compile_page_lookup(
|
|
struct code_block *block,
|
|
uint8_t table_areg,
|
|
uint8_t addr_dreg,
|
|
uint8_t dest_areg
|
|
) {
|
|
// (addr >> 12) * 4 == (addr >> 10) & 0x3c. shift immediates only go
|
|
// up to 8, but rol.w #6 puts bits 15-12 into bits 5-2 and the mask
|
|
// removes the rotated low bits
|
|
emit_rol_w_imm_dn(block, 6, addr_dreg);
|
|
emit_andi_w_dn(block, addr_dreg, 0x003c);
|
|
// movea.l (table,addr.w), dest
|
|
emit_movea_l_idx_an_an(block, 0, table_areg, addr_dreg, dest_areg);
|
|
}
|
|
|
|
// shared helper entry addresses, set by compile_emit_helpers
|
|
struct jit_helpers jit_helpers;
|
|
|
|
// scratch the helpers are emitted into
|
|
static struct code_block helper_block;
|
|
|
|
// C-call sequence for dmg_read - addr in D1.w, result in D0.b
|
|
static void emit_c_read_call(struct code_block *block)
|
|
{
|
|
// stash the cycle count for lazy DIV/LY evaluation and reload after
|
|
emit_move_l_dn_disp_an(block, REG_68K_D_CYCLE_COUNT, JIT_CTX_READ_CYCLES, REG_68K_A_CTX); // 4
|
|
emit_push_w_dn(block, REG_68K_D_SCRATCH_1); // 2
|
|
emit_push_l_disp_an(block, JIT_CTX_DMG, REG_68K_A_CTX); // 4
|
|
emit_movea_l_disp_an_an(block, JIT_CTX_READ, REG_68K_A_CTX, REG_68K_A_SCRATCH_1); // 4
|
|
emit_jsr_ind_an(block, REG_68K_A_SCRATCH_1); // 2
|
|
emit_addq_l_an(block, 7, 6); // 2
|
|
emit_move_l_disp_an_dn(block, JIT_CTX_READ_CYCLES, REG_68K_A_CTX, REG_68K_D_CYCLE_COUNT);
|
|
}
|
|
|
|
// C call sequence for a void (*)(long, word, byte) member of dmg
|
|
static void emit_c_write_call_via(struct code_block *block, int16_t fn_offset)
|
|
{
|
|
emit_move_l_dn_disp_an(block, REG_68K_D_CYCLE_COUNT, JIT_CTX_READ_CYCLES, REG_68K_A_CTX);
|
|
emit_push_b_dn(block, REG_68K_D_SCRATCH_0); // 2
|
|
emit_push_w_dn(block, REG_68K_D_SCRATCH_1); // 2
|
|
emit_push_l_disp_an(block, JIT_CTX_DMG, REG_68K_A_CTX); // 4
|
|
emit_movea_l_disp_an_an(block, fn_offset, REG_68K_A_CTX, REG_68K_A_SCRATCH_1); // 4
|
|
emit_jsr_ind_an(block, REG_68K_A_SCRATCH_1); // 2
|
|
emit_addq_l_an(block, 7, 8); // 2
|
|
emit_move_l_disp_an_dn(block, JIT_CTX_READ_CYCLES, REG_68K_A_CTX, REG_68K_D_CYCLE_COUNT);
|
|
}
|
|
|
|
static void emit_c_write_call(struct code_block *block)
|
|
{
|
|
emit_c_write_call_via(block, JIT_CTX_WRITE);
|
|
}
|
|
|
|
// start the next entry on a 16-byte I-cache line, not sure if this matters
|
|
static void pad_align16(struct code_block *block)
|
|
{
|
|
while (block->length & 15) {
|
|
emit_word(block, 0x4e71); // nop
|
|
}
|
|
}
|
|
|
|
const struct code_block *compile_emit_helpers(uint32_t base, void *hram_base)
|
|
{
|
|
struct code_block *b = &helper_block;
|
|
size_t unmapped, lo, ie;
|
|
|
|
b->length = 0;
|
|
lo = ie = 0;
|
|
|
|
// read chain - page lookup falls through to the hit and its rts
|
|
jit_helpers.read8_hl = base + b->length;
|
|
emit_move_w_an_dn(b, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
|
jit_helpers.read8 = base + b->length;
|
|
emit_move_w_dn_dn(b, REG_68K_D_SCRATCH_1, REG_68K_D_SCRATCH_0);
|
|
compile_page_lookup(b, REG_68K_A_READ_PAGE, REG_68K_D_SCRATCH_0,
|
|
REG_68K_A_SCRATCH_1);
|
|
emit_move_l_an_dn(b, REG_68K_A_SCRATCH_1, REG_68K_D_SCRATCH_0);
|
|
unmapped = b->length;
|
|
emit_beq_b(b, 0);
|
|
emit_move_b_idx_an_dn(b, REG_68K_A_SCRATCH_1, REG_68K_D_SCRATCH_1,
|
|
REG_68K_D_SCRATCH_0);
|
|
emit_rts(b);
|
|
|
|
patch_branch_b(b, unmapped);
|
|
|
|
emit_cmpi_w_imm_dn(b, 0xff80, REG_68K_D_SCRATCH_1);
|
|
lo = b->length;
|
|
emit_bcs_b(b, 0);
|
|
emit_movea_l_imm32(b, REG_68K_A_SCRATCH_1,
|
|
(uint32_t) (uintptr_t) hram_base + 0x80);
|
|
emit_move_b_idx_an_dn(b, REG_68K_A_SCRATCH_1, REG_68K_D_SCRATCH_1,
|
|
REG_68K_D_SCRATCH_0);
|
|
emit_rts(b);
|
|
patch_branch_b(b, lo);
|
|
|
|
jit_helpers.read8_slow = base + b->length;
|
|
emit_c_read_call(b);
|
|
emit_rts(b);
|
|
|
|
pad_align16(b);
|
|
|
|
// write chain
|
|
jit_helpers.write8_a = base + b->length;
|
|
emit_move_b_dn_dn(b, REG_68K_D_A, REG_68K_D_SCRATCH_0);
|
|
unmapped = b->length;
|
|
emit_bra_b(b, 0);
|
|
jit_helpers.write8_hl_a = base + b->length;
|
|
emit_move_b_dn_dn(b, REG_68K_D_A, REG_68K_D_SCRATCH_0);
|
|
jit_helpers.write8_hl = base + b->length;
|
|
emit_move_w_an_dn(b, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
|
patch_branch_b(b, unmapped);
|
|
jit_helpers.write8 = base + b->length;
|
|
// D0 holds the value, so the lookup scratches through D3
|
|
emit_move_w_dn_dn(b, REG_68K_D_SCRATCH_1, REG_68K_D_NEXT_PC);
|
|
compile_page_lookup(b, REG_68K_A_WRITE_PAGE, REG_68K_D_NEXT_PC,
|
|
REG_68K_A_SCRATCH_1);
|
|
emit_move_l_an_dn(b, REG_68K_A_SCRATCH_1, REG_68K_D_NEXT_PC);
|
|
unmapped = b->length;
|
|
emit_beq_b(b, 0);
|
|
emit_move_b_dn_idx_an(b, REG_68K_D_SCRATCH_0, REG_68K_A_SCRATCH_1,
|
|
REG_68K_D_SCRATCH_1);
|
|
emit_rts(b);
|
|
|
|
patch_branch_b(b, unmapped);
|
|
emit_cmpi_w_imm_dn(b, 0xff80, REG_68K_D_SCRATCH_1);
|
|
lo = b->length;
|
|
emit_bcs_b(b, 0);
|
|
// $ffff is IE, so need to go to C
|
|
emit_cmpi_w_imm_dn(b, 0xffff, REG_68K_D_SCRATCH_1);
|
|
ie = b->length;
|
|
emit_beq_b(b, 0);
|
|
emit_movea_l_imm32(b, REG_68K_A_SCRATCH_1,
|
|
(uint32_t) (uintptr_t) hram_base + 0x80);
|
|
emit_move_b_dn_idx_an(b, REG_68K_D_SCRATCH_0, REG_68K_A_SCRATCH_1,
|
|
REG_68K_D_SCRATCH_1);
|
|
emit_rts(b);
|
|
|
|
jit_helpers.write8_slow_a = base + b->length;
|
|
emit_move_b_dn_dn(b, REG_68K_D_A, REG_68K_D_SCRATCH_0);
|
|
jit_helpers.write8_slow = base + b->length;
|
|
|
|
patch_branch_b(b, lo);
|
|
patch_branch_b(b, ie);
|
|
|
|
emit_c_write_call(b);
|
|
emit_rts(b);
|
|
|
|
pad_align16(b);
|
|
|
|
// ROM-range write (MBC registers): go straight to mbc_write_func
|
|
jit_helpers.write8_mbc_a = base + b->length;
|
|
emit_move_b_dn_dn(b, REG_68K_D_A, REG_68K_D_SCRATCH_0);
|
|
emit_c_write_call_via(b, JIT_CTX_MBC_WRITE);
|
|
emit_rts(b);
|
|
|
|
if (b->length > JIT_HELPERS_SIZE) {
|
|
return NULL;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
// addr in D1, val_reg specifies value register
|
|
void compile_slow_dmg_write(struct code_block *block, uint8_t val_reg)
|
|
{
|
|
// D2 has to be exact for lazy register evaluation
|
|
flush_cycles(block);
|
|
if (val_reg == REG_68K_D_A) {
|
|
emit_jsr_abs_l(block, jit_helpers.write8_slow_a);
|
|
return;
|
|
}
|
|
if (val_reg != REG_68K_D_SCRATCH_0) {
|
|
emit_move_b_dn_dn(block, val_reg, REG_68K_D_SCRATCH_0);
|
|
}
|
|
emit_jsr_abs_l(block, jit_helpers.write8_slow);
|
|
}
|
|
|
|
// Call dmg_write(dmg, addr, val) - addr in D1, val in D4 (A register)
|
|
void compile_call_dmg_write_a(struct code_block *block)
|
|
{
|
|
flush_cycles(block);
|
|
emit_jsr_abs_l(block, jit_helpers.write8_a);
|
|
}
|
|
|
|
// Call mbc_write_func(dmg, addr, val) - addr in D1, val in D4 (A register)
|
|
void compile_call_dmg_write_mbc_a(struct code_block *block)
|
|
{
|
|
flush_cycles(block);
|
|
emit_jsr_abs_l(block, jit_helpers.write8_mbc_a);
|
|
}
|
|
|
|
// Call dmg_write(dmg, addr, val) - addr in D1, val is immediate
|
|
void compile_call_dmg_write_imm(struct code_block *block, uint8_t val)
|
|
{
|
|
emit_move_b_dn(block, 0, val);
|
|
flush_cycles(block);
|
|
emit_jsr_abs_l(block, jit_helpers.write8);
|
|
}
|
|
|
|
// Call dmg_write(dmg, addr, val) - addr in D1, val in D0
|
|
void compile_call_dmg_write_d0(struct code_block *block)
|
|
{
|
|
flush_cycles(block);
|
|
emit_jsr_abs_l(block, jit_helpers.write8);
|
|
}
|
|
|
|
// Call dmg_write(dmg, HL, val) - val in D0, D1 loaded inside the helper
|
|
void compile_call_dmg_write_hl_d0(struct code_block *block)
|
|
{
|
|
flush_cycles(block);
|
|
emit_jsr_abs_l(block, jit_helpers.write8_hl);
|
|
}
|
|
|
|
// Call dmg_write(dmg, HL, A)
|
|
void compile_call_dmg_write_hl_a(struct code_block *block)
|
|
{
|
|
flush_cycles(block);
|
|
emit_jsr_abs_l(block, jit_helpers.write8_hl_a);
|
|
}
|
|
|
|
// Call dmg_write(dmg, HL, val) - val is immediate
|
|
void compile_call_dmg_write_hl_imm(struct code_block *block, uint8_t val)
|
|
{
|
|
emit_move_b_dn(block, 0, val);
|
|
flush_cycles(block);
|
|
emit_jsr_abs_l(block, jit_helpers.write8_hl);
|
|
}
|
|
|
|
// Emit slow path call to dmg_read - expects address in D1, returns in D0
|
|
void compile_slow_dmg_read(struct code_block *block)
|
|
{
|
|
// D2 has to be exact for lazy DIV/LY evaluation; the helper stashes
|
|
// it around the C call
|
|
flush_cycles(block);
|
|
emit_jsr_abs_l(block, jit_helpers.read8_slow);
|
|
}
|
|
|
|
// Call dmg_read(dmg, addr) - addr in D1, result stays in D0
|
|
void compile_call_dmg_read(struct code_block *block)
|
|
{
|
|
flush_cycles(block);
|
|
emit_jsr_abs_l(block, jit_helpers.read8);
|
|
}
|
|
|
|
// Call dmg_read(dmg, HL) - D1 loaded inside the helper, result in D0
|
|
void compile_call_dmg_read_hl(struct code_block *block)
|
|
{
|
|
flush_cycles(block);
|
|
emit_jsr_abs_l(block, jit_helpers.read8_hl);
|
|
}
|
|
|
|
// Call dmg_read(dmg, addr) - addr in D1, result goes to D4 (A register)
|
|
void compile_call_dmg_read_a(struct code_block *block)
|
|
{
|
|
compile_call_dmg_read(block);
|
|
emit_move_b_dn_dn(block, 0, REG_68K_D_A);
|
|
}
|
|
|
|
// Call dmg_read(dmg, HL) - result goes to D4 (A register)
|
|
void compile_call_dmg_read_hl_a(struct code_block *block)
|
|
{
|
|
compile_call_dmg_read_hl(block);
|
|
emit_move_b_dn_dn(block, 0, REG_68K_D_A);
|
|
}
|
|
|
|
void compile_call_ei_di(struct code_block *block, int enabled)
|
|
{
|
|
flush_cycles(block);
|
|
// save cycle count
|
|
emit_move_l_dn_disp_an(block, REG_68K_D_CYCLE_COUNT, JIT_CTX_READ_CYCLES, REG_68K_A_CTX);
|
|
// push enabled
|
|
emit_moveq_dn(block, REG_68K_D_SCRATCH_1, (int8_t) enabled);
|
|
// i actually have this as a 16-bit int for some reason
|
|
emit_push_w_dn(block, REG_68K_D_SCRATCH_1);
|
|
// push dmg pointer
|
|
emit_push_l_disp_an(block, JIT_CTX_DMG, REG_68K_A_CTX);
|
|
// load address of function
|
|
emit_movea_l_disp_an_an(block, JIT_CTX_EI_DI, REG_68K_A_CTX, REG_68K_A_SCRATCH_1);
|
|
// call dmg_ei_di
|
|
emit_jsr_ind_an(block, REG_68K_A_SCRATCH_1);
|
|
// clean up stack
|
|
emit_addq_l_an(block, 7, 6);
|
|
emit_move_l_disp_an_dn(block, JIT_CTX_READ_CYCLES, REG_68K_A_CTX, REG_68K_D_CYCLE_COUNT);
|
|
}
|
|
|
|
// Slow path for dmg_read16 - addr in D1.w, result in D0.w
|
|
void compile_slow_dmg_read16(struct code_block *block)
|
|
{
|
|
flush_cycles(block);
|
|
emit_move_l_dn_disp_an(block, REG_68K_D_CYCLE_COUNT, JIT_CTX_READ_CYCLES, REG_68K_A_CTX);
|
|
emit_push_w_dn(block, REG_68K_D_SCRATCH_1);
|
|
emit_push_l_disp_an(block, JIT_CTX_DMG, REG_68K_A_CTX);
|
|
emit_movea_l_disp_an_an(block, JIT_CTX_READ16, REG_68K_A_CTX, REG_68K_A_SCRATCH_1);
|
|
emit_jsr_ind_an(block, REG_68K_A_SCRATCH_1);
|
|
emit_addq_l_an(block, 7, 6);
|
|
emit_move_l_disp_an_dn(block, JIT_CTX_READ_CYCLES, REG_68K_A_CTX, REG_68K_D_CYCLE_COUNT);
|
|
}
|
|
|
|
// Call dmg_read16(dmg, addr) - addr in D1.w, result in D0.w
|
|
// Inline fast path for page table hits when both bytes on same page
|
|
void compile_call_dmg_read16(struct code_block *block)
|
|
{
|
|
size_t cross, unmapped, done;
|
|
|
|
// flush before the fast/slow split so both paths see the same D2
|
|
flush_cycles(block);
|
|
|
|
// Check if both bytes on same page (addr & 0xfff != 0xfff)
|
|
// If the low bits are all set, the second byte crosses to next page
|
|
// move.w d1, d0
|
|
emit_move_w_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_SCRATCH_0);
|
|
// andi.w #$0fff, d0
|
|
emit_andi_w_dn(block, REG_68K_D_SCRATCH_0, 0x0fff);
|
|
// cmpi.w #$0fff, d0
|
|
emit_cmpi_w_imm_dn(block, 0x0fff, REG_68K_D_SCRATCH_0);
|
|
cross = block->length;
|
|
emit_beq_b(block, 0);
|
|
|
|
// Page table lookup
|
|
// move.w d1, d0
|
|
emit_move_w_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_SCRATCH_0);
|
|
// a0 = read_page[addr >> 12]
|
|
compile_page_lookup(block, REG_68K_A_READ_PAGE, REG_68K_D_SCRATCH_0, REG_68K_A_SCRATCH_1);
|
|
// move.l a0, d0 - sets Z, d0 is dead
|
|
emit_move_l_an_dn(block, REG_68K_A_SCRATCH_1, REG_68K_D_SCRATCH_0);
|
|
unmapped = block->length;
|
|
emit_beq_b(block, 0);
|
|
|
|
// Fast read - low byte at (a0,d1.w), high byte one address later
|
|
// (no page cross possible: low byte of the address is not 0xff)
|
|
// move.b (a0,d1.w), d3 - low byte
|
|
emit_move_b_idx_an_dn(block, REG_68K_A_SCRATCH_1, REG_68K_D_SCRATCH_1, REG_68K_D_NEXT_PC);
|
|
// move.w d1, d0
|
|
emit_move_w_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_SCRATCH_0);
|
|
// addq.w #1, d0
|
|
emit_addq_w_dn(block, REG_68K_D_SCRATCH_0, 1);
|
|
// move.b (a0,d0.w), d0 - high byte
|
|
emit_move_b_idx_an_dn(block, REG_68K_A_SCRATCH_1, REG_68K_D_SCRATCH_0, REG_68K_D_SCRATCH_0);
|
|
// lsl.w #8, d0 - shift high byte up
|
|
emit_lsl_w_imm_dn(block, 8, REG_68K_D_SCRATCH_0);
|
|
// move.b d3, d0 - combine low byte
|
|
emit_move_b_dn_dn(block, REG_68K_D_NEXT_PC, REG_68K_D_SCRATCH_0);
|
|
done = block->length;
|
|
emit_bra_b(block, 0);
|
|
|
|
patch_branch_b(block, cross);
|
|
patch_branch_b(block, unmapped);
|
|
compile_slow_dmg_read16(block);
|
|
patch_branch_b(block, done);
|
|
}
|
|
|
|
// Slow path for dmg_write16 - addr in D1.w, data in D0.w
|
|
void compile_slow_dmg_write16(struct code_block *block)
|
|
{
|
|
flush_cycles(block);
|
|
emit_move_l_dn_disp_an(block, REG_68K_D_CYCLE_COUNT, JIT_CTX_READ_CYCLES, REG_68K_A_CTX);
|
|
emit_push_w_dn(block, REG_68K_D_SCRATCH_0);
|
|
emit_push_w_dn(block, REG_68K_D_SCRATCH_1);
|
|
emit_push_l_disp_an(block, JIT_CTX_DMG, REG_68K_A_CTX);
|
|
emit_movea_l_disp_an_an(block, JIT_CTX_WRITE16, REG_68K_A_CTX, REG_68K_A_SCRATCH_1);
|
|
emit_jsr_ind_an(block, REG_68K_A_SCRATCH_1);
|
|
emit_addq_l_an(block, 7, 8);
|
|
emit_move_l_disp_an_dn(block, JIT_CTX_READ_CYCLES, REG_68K_A_CTX, REG_68K_D_CYCLE_COUNT);
|
|
}
|
|
|
|
// Call dmg_write16(dmg, addr, data) - addr in D1.w, data in D0.w
|
|
// Inline fast path for page table hits when both bytes on same page
|
|
void compile_call_dmg_write16_d0(struct code_block *block)
|
|
{
|
|
size_t cross, unmapped, done;
|
|
|
|
// flush before the fast/slow split so both paths see the same D2
|
|
flush_cycles(block);
|
|
|
|
// Save data to D3 before we use D0 as scratch
|
|
// move.w d0, d3
|
|
emit_move_w_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_NEXT_PC);
|
|
|
|
// Check if both bytes on same page (addr & 0xfff != 0xfff)
|
|
// move.w d1, d0
|
|
emit_move_w_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_SCRATCH_0);
|
|
// andi.w #$0fff, d0
|
|
emit_andi_w_dn(block, REG_68K_D_SCRATCH_0, 0x0fff);
|
|
// cmpi.w #$0fff, d0
|
|
emit_cmpi_w_imm_dn(block, 0x0fff, REG_68K_D_SCRATCH_0);
|
|
cross = block->length;
|
|
emit_beq_b(block, 0);
|
|
|
|
// Page table lookup
|
|
// move.w d1, d0
|
|
emit_move_w_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_SCRATCH_0);
|
|
// a0 = write_page[addr >> 12]
|
|
compile_page_lookup(block, REG_68K_A_WRITE_PAGE, REG_68K_D_SCRATCH_0, REG_68K_A_SCRATCH_1);
|
|
// move.l a0, d0 - sets Z, d0 is dead
|
|
emit_move_l_an_dn(block, REG_68K_A_SCRATCH_1, REG_68K_D_SCRATCH_0);
|
|
unmapped = block->length;
|
|
emit_beq_b(block, 0);
|
|
|
|
// Fast write - low byte at (a0,d1.w), high byte one address later
|
|
// (no page cross possible: low byte of the address is not 0xff)
|
|
// move.b d3, (a0,d1.w) - write low byte
|
|
emit_move_b_dn_idx_an(block, REG_68K_D_NEXT_PC, REG_68K_A_SCRATCH_1, REG_68K_D_SCRATCH_1);
|
|
// lsr.w #8, d3 - shift high byte down
|
|
emit_lsr_w_imm_dn(block, 8, REG_68K_D_NEXT_PC);
|
|
// move.w d1, d0
|
|
emit_move_w_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_SCRATCH_0);
|
|
// addq.w #1, d0
|
|
emit_addq_w_dn(block, REG_68K_D_SCRATCH_0, 1);
|
|
// move.b d3, (a0,d0.w) - write high byte
|
|
emit_move_b_dn_idx_an(block, REG_68K_D_NEXT_PC, REG_68K_A_SCRATCH_1, REG_68K_D_SCRATCH_0);
|
|
done = block->length;
|
|
emit_bra_b(block, 0);
|
|
|
|
// Slow path: restore data from D3 to D0
|
|
patch_branch_b(block, cross);
|
|
patch_branch_b(block, unmapped);
|
|
// move.w d3, d0
|
|
emit_move_w_dn_dn(block, REG_68K_D_NEXT_PC, REG_68K_D_SCRATCH_0);
|
|
compile_slow_dmg_write16(block);
|
|
patch_branch_b(block, done);
|
|
}
|
|
|
|
// Call stop_func(dmg) - returns 0 to continue, non-zero to halt
|
|
// If returns 0, execution continues after STOP
|
|
// If returns non-zero, we set HALT_SENTINEL and exit
|
|
void compile_call_stop(struct code_block *block, int next_pc)
|
|
{
|
|
flush_cycles(block);
|
|
emit_move_l_dn_disp_an(block, REG_68K_D_CYCLE_COUNT, JIT_CTX_READ_CYCLES, REG_68K_A_CTX);
|
|
// push dmg pointer
|
|
emit_push_l_disp_an(block, JIT_CTX_DMG, REG_68K_A_CTX); // 4
|
|
// load stop_func address
|
|
emit_movea_l_disp_an_an(block, JIT_CTX_STOP_FUNC, REG_68K_A_CTX, REG_68K_A_SCRATCH_1); // 4
|
|
// call stop_func
|
|
emit_jsr_ind_an(block, REG_68K_A_SCRATCH_1); // 2
|
|
// clean up stack
|
|
emit_addq_l_an(block, 7, 4); // 2
|
|
emit_move_l_disp_an_dn(block, JIT_CTX_READ_CYCLES, REG_68K_A_CTX, REG_68K_D_CYCLE_COUNT);
|
|
|
|
// D0 = return value. If 0, continue execution. If non-zero, halt.
|
|
emit_tst_b_dn(block, REG_68K_D_SCRATCH_0); // 2
|
|
size_t no_halt = block->length;
|
|
emit_beq_b(block, 0);
|
|
|
|
// Halt: set HALT_SENTINEL and dispatch
|
|
emit_move_l_dn(block, REG_68K_D_NEXT_PC, 0xffffffff); // 6
|
|
emit_dispatch_jump(block); // 6
|
|
|
|
// Continue: set next PC and dispatch
|
|
patch_branch_b(block, no_halt);
|
|
emit_move_l_dn(block, REG_68K_D_NEXT_PC, next_pc); // 6
|
|
emit_dispatch_jump(block); // 6
|
|
}
|