mirror of
https://github.com/mlaux/gb6.git
synced 2026-04-21 00:17:12 +00:00
split out compile_dmg_*, fix alignment bug for dmg_write, add more instructions, hacky lcd update for jit
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cb_prefix.h"
|
||||
#include "compiler.h"
|
||||
#include "emitters.h"
|
||||
#include "flags.h"
|
||||
#include "interop.h"
|
||||
|
||||
// GB register encoding in CB-prefix instructions:
|
||||
// 0=B, 1=C, 2=D, 3=E, 4=H, 5=L, 6=(HL), 7=A
|
||||
//
|
||||
// 68k register mapping:
|
||||
// D4 = A
|
||||
// D5 = BC in split format: 0x00BB00CC (B in bits 16-23, C in bits 0-7)
|
||||
// D6 = DE in split format: 0x00DD00EE (D in bits 16-23, E in bits 0-7)
|
||||
// A2 = HL (contiguous 16-bit value)
|
||||
|
||||
// Set GB flags for BIT instruction: Z from 68k Z flag, N=0, H=1, C unchanged
|
||||
static void compile_bit_flags(struct code_block *block)
|
||||
{
|
||||
// After btst, 68k Z flag is set if bit was 0
|
||||
// GB Z flag is bit 7, H flag is bit 5
|
||||
// We need: Z set if tested bit was 0, N=0, H=1, C unchanged (bit 4)
|
||||
|
||||
// Capture 68k Z flag FIRST before other instructions clobber it
|
||||
// scc stores 0xff if condition true, 0x00 if false
|
||||
emit_scc(block, 0x07, REG_68K_D_SCRATCH_1); // seq: set if Z=1
|
||||
emit_andi_b_dn(block, REG_68K_D_SCRATCH_1, 0x80); // mask to just Z position
|
||||
|
||||
// Keep only C flag (bit 4)
|
||||
emit_andi_b_dn(block, REG_68K_D_FLAGS, 0x10);
|
||||
|
||||
// Set H flag (bit 5)
|
||||
emit_ori_b_dn(block, REG_68K_D_FLAGS, 0x20);
|
||||
|
||||
// OR in the Z flag
|
||||
emit_or_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_FLAGS);
|
||||
}
|
||||
|
||||
// BIT n, reg - test bit n of register
|
||||
static void compile_bit_reg(struct code_block *block, int bit, int gb_reg)
|
||||
{
|
||||
switch (gb_reg) {
|
||||
case 0: // B - high byte of D5 (bits 16-23)
|
||||
emit_btst_imm_dn(block, bit + 16, REG_68K_D_BC);
|
||||
break;
|
||||
case 1: // C - low byte of D5 (bits 0-7)
|
||||
emit_btst_imm_dn(block, bit, REG_68K_D_BC);
|
||||
break;
|
||||
case 2: // D - high byte of D6 (bits 16-23)
|
||||
emit_btst_imm_dn(block, bit + 16, REG_68K_D_DE);
|
||||
break;
|
||||
case 3: // E - low byte of D6 (bits 0-7)
|
||||
emit_btst_imm_dn(block, bit, REG_68K_D_DE);
|
||||
break;
|
||||
case 4: // H - high byte of HL
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
emit_btst_imm_dn(block, bit + 8, REG_68K_D_SCRATCH_1);
|
||||
break;
|
||||
case 5: // L - low byte of HL
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
emit_btst_imm_dn(block, bit, REG_68K_D_SCRATCH_1);
|
||||
break;
|
||||
case 6: // (HL) - memory indirect
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
compile_call_dmg_read_to_d0(block); // result in D0
|
||||
emit_btst_imm_dn(block, bit, REG_68K_D_NEXT_PC);
|
||||
break;
|
||||
case 7: // A
|
||||
emit_btst_imm_dn(block, bit, REG_68K_D_A);
|
||||
break;
|
||||
}
|
||||
compile_bit_flags(block);
|
||||
}
|
||||
|
||||
// RES n, reg - clear bit n of register
|
||||
static void compile_res_reg(struct code_block *block, int bit, int gb_reg)
|
||||
{
|
||||
switch (gb_reg) {
|
||||
case 0: // B
|
||||
emit_bclr_imm_dn(block, bit + 16, REG_68K_D_BC);
|
||||
break;
|
||||
case 1: // C
|
||||
emit_bclr_imm_dn(block, bit, REG_68K_D_BC);
|
||||
break;
|
||||
case 2: // D
|
||||
emit_bclr_imm_dn(block, bit + 16, REG_68K_D_DE);
|
||||
break;
|
||||
case 3: // E
|
||||
emit_bclr_imm_dn(block, bit, REG_68K_D_DE);
|
||||
break;
|
||||
case 4: // H - need to move from A2, modify, move back
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
emit_bclr_imm_dn(block, bit + 8, REG_68K_D_SCRATCH_1);
|
||||
emit_movea_w_dn_an(block, REG_68K_D_SCRATCH_1, REG_68K_A_HL);
|
||||
break;
|
||||
case 5: // L
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
emit_bclr_imm_dn(block, bit, REG_68K_D_SCRATCH_1);
|
||||
emit_movea_w_dn_an(block, REG_68K_D_SCRATCH_1, REG_68K_A_HL);
|
||||
break;
|
||||
case 6: // (HL) - read, modify, write
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
compile_call_dmg_read_to_d0(block); // D0 = memory[HL]
|
||||
emit_bclr_imm_dn(block, bit, REG_68K_D_NEXT_PC); // clear bit in D0
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1); // D1 = address
|
||||
compile_call_dmg_write_d0(block); // write D2 to address D1
|
||||
break;
|
||||
case 7: // A
|
||||
emit_bclr_imm_dn(block, bit, REG_68K_D_A);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// SET n, reg - set bit n of register
|
||||
static void compile_set_reg(struct code_block *block, int bit, int gb_reg)
|
||||
{
|
||||
switch (gb_reg) {
|
||||
case 0: // B
|
||||
emit_bset_imm_dn(block, bit + 16, REG_68K_D_BC);
|
||||
break;
|
||||
case 1: // C
|
||||
emit_bset_imm_dn(block, bit, REG_68K_D_BC);
|
||||
break;
|
||||
case 2: // D
|
||||
emit_bset_imm_dn(block, bit + 16, REG_68K_D_DE);
|
||||
break;
|
||||
case 3: // E
|
||||
emit_bset_imm_dn(block, bit, REG_68K_D_DE);
|
||||
break;
|
||||
case 4: // H
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
emit_bset_imm_dn(block, bit + 8, REG_68K_D_SCRATCH_1);
|
||||
emit_movea_w_dn_an(block, REG_68K_D_SCRATCH_1, REG_68K_A_HL);
|
||||
break;
|
||||
case 5: // L
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
emit_bset_imm_dn(block, bit, REG_68K_D_SCRATCH_1);
|
||||
emit_movea_w_dn_an(block, REG_68K_D_SCRATCH_1, REG_68K_A_HL);
|
||||
break;
|
||||
case 6: // (HL) - read, modify, write
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
compile_call_dmg_read_to_d0(block); // D0 = memory[HL]
|
||||
emit_bset_imm_dn(block, bit, REG_68K_D_NEXT_PC); // set bit in D0
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1); // D1 = address
|
||||
compile_call_dmg_write_d0(block); // write D0 to address D1
|
||||
break;
|
||||
case 7: // A
|
||||
emit_bset_imm_dn(block, bit, REG_68K_D_A);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int compile_cb_insn(struct code_block *block, uint8_t cb_op)
|
||||
{
|
||||
int op = cb_op >> 6;
|
||||
int bit = (cb_op >> 3) & 0x7;
|
||||
int reg = cb_op & 0x7;
|
||||
|
||||
switch (op) {
|
||||
case 0: // rotates/shifts - only swap is implemented for now
|
||||
if (cb_op == 0x37) {
|
||||
// swap a
|
||||
emit_ror_b_imm(block, 4, REG_68K_D_A);
|
||||
emit_cmp_b_imm_dn(block, REG_68K_D_A, 0);
|
||||
compile_set_z_flag(block);
|
||||
emit_andi_b_dn(block, REG_68K_D_FLAGS, 0x80);
|
||||
return 1;
|
||||
}
|
||||
return 0; // other rotates/shifts not yet implemented
|
||||
|
||||
case 1: // BIT
|
||||
compile_bit_reg(block, bit, reg);
|
||||
return 1;
|
||||
|
||||
case 2: // RES
|
||||
compile_res_reg(block, bit, reg);
|
||||
return 1;
|
||||
|
||||
case 3: // SET
|
||||
compile_set_reg(block, bit, reg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef CB_PREFIX_H
|
||||
#define CB_PREFIX_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "compiler.h"
|
||||
|
||||
// Compile a CB-prefixed instruction (BIT, RES, SET, and rotates/shifts)
|
||||
// Returns 1 if successfully compiled, 0 if unknown opcode
|
||||
int compile_cb_insn(struct code_block *block, uint8_t cb_op);
|
||||
|
||||
#endif
|
||||
+103
-10
@@ -6,6 +6,7 @@
|
||||
#include "branches.h"
|
||||
#include "flags.h"
|
||||
#include "interop.h"
|
||||
#include "cb_prefix.h"
|
||||
|
||||
// helper for reading GB memory during compilation
|
||||
#define READ_BYTE(off) (ctx->read(ctx->dmg, src_address + (off)))
|
||||
@@ -191,6 +192,14 @@ struct code_block *compile_block(uint16_t src_address, struct compile_ctx *ctx)
|
||||
emit_move_b_dn(block, REG_68K_D_BC, READ_BYTE(src_ptr++));
|
||||
emit_swap(block, REG_68K_D_BC);
|
||||
break;
|
||||
|
||||
case 0x07: // rlca - rotate A left, old bit 7 to carry and bit 0
|
||||
emit_rol_b_imm(block, 1, REG_68K_D_A);
|
||||
// Capture 68k carry into scratch, mask to GB C position
|
||||
emit_scc(block, 0x05, REG_68K_D_SCRATCH_1); // scs: set if carry
|
||||
emit_andi_b_dn(block, REG_68K_D_SCRATCH_1, 0x10); // mask to bit 4
|
||||
emit_move_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_FLAGS); // Z=N=H=0, C=result
|
||||
break;
|
||||
|
||||
case 0x0b: // dec bc
|
||||
emit_ext_w_dn(block, REG_68K_D_BC);
|
||||
@@ -214,6 +223,18 @@ struct code_block *compile_block(uint16_t src_address, struct compile_ctx *ctx)
|
||||
emit_move_b_dn(block, REG_68K_D_BC, READ_BYTE(src_ptr++));
|
||||
break;
|
||||
|
||||
case 0x0f: // rrca - rotate A right, old bit 0 to carry and bit 7
|
||||
emit_ror_b_imm(block, 1, REG_68K_D_A);
|
||||
emit_scc(block, 0x05, REG_68K_D_SCRATCH_1); // scs: set if carry
|
||||
emit_andi_b_dn(block, REG_68K_D_SCRATCH_1, 0x10);
|
||||
emit_move_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_FLAGS);
|
||||
break;
|
||||
|
||||
case 0x13: // inc de
|
||||
emit_ext_w_dn(block, REG_68K_D_DE);
|
||||
emit_addq_l_dn(block, REG_68K_D_DE, 1);
|
||||
break;
|
||||
|
||||
case 0x16: // ld d, imm8
|
||||
emit_swap(block, REG_68K_D_DE);
|
||||
emit_move_b_dn(block, REG_68K_D_DE, READ_BYTE(src_ptr++));
|
||||
@@ -311,16 +332,63 @@ struct code_block *compile_block(uint16_t src_address, struct compile_ctx *ctx)
|
||||
emit_move_b_dn_dn(block, REG_68K_D_A, REG_68K_D_DE);
|
||||
break;
|
||||
|
||||
case 0x67: // ld h, a
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
emit_rol_w_8(block, REG_68K_D_SCRATCH_1);
|
||||
emit_move_b_dn_dn(block, REG_68K_D_A, REG_68K_D_SCRATCH_1);
|
||||
emit_ror_w_8(block, REG_68K_D_SCRATCH_1);
|
||||
emit_movea_w_dn_an(block, REG_68K_D_SCRATCH_1, REG_68K_A_HL);
|
||||
break;
|
||||
|
||||
case 0x6f: // ld l, a
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
emit_move_b_dn_dn(block, REG_68K_D_A, REG_68K_D_SCRATCH_1);
|
||||
emit_movea_w_dn_an(block, REG_68K_D_SCRATCH_1, REG_68K_A_HL);
|
||||
break;
|
||||
|
||||
case 0x78: // ld a, b
|
||||
emit_swap(block, REG_68K_D_BC);
|
||||
emit_move_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
||||
emit_swap(block, REG_68K_D_BC);
|
||||
break;
|
||||
|
||||
case 0x77: // ld (hl), a
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
compile_call_dmg_write(block);
|
||||
break;
|
||||
case 0x22: // ld (hl+), a
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
compile_call_dmg_write(block);
|
||||
emit_addq_w_an(block, REG_68K_A_HL, 1);
|
||||
break;
|
||||
case 0x35: // dec (hl)
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
compile_call_dmg_read_to_d0(block);
|
||||
emit_subq_b_dn(block, 0, 1);
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
compile_call_dmg_write_d0(block);
|
||||
break;
|
||||
|
||||
case 0x79: // ld a, c
|
||||
emit_move_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
||||
break;
|
||||
|
||||
case 0x7c: // ld a, h
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
emit_rol_w_8(block, REG_68K_D_SCRATCH_1);
|
||||
emit_move_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
||||
break;
|
||||
|
||||
case 0x7d: // ld a, l
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
emit_move_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
||||
break;
|
||||
|
||||
case 0x7e: // ld a, (hl)
|
||||
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
||||
compile_call_dmg_read(block);
|
||||
break;
|
||||
|
||||
case 0x87: // add a, a
|
||||
emit_add_b_dn_dn(block, REG_68K_D_A, REG_68K_D_A);
|
||||
compile_set_z_flag(block);
|
||||
@@ -333,6 +401,13 @@ struct code_block *compile_block(uint16_t src_address, struct compile_ctx *ctx)
|
||||
compile_set_znc_flags(block);
|
||||
break;
|
||||
|
||||
case 0xb8: // cp a, b
|
||||
emit_swap(block, REG_68K_D_BC);
|
||||
emit_cmp_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
||||
compile_set_znc_flags(block);
|
||||
emit_swap(block, REG_68K_D_BC);
|
||||
break;
|
||||
|
||||
case 0xaf: // xor a, a - always results in 0, Z=1
|
||||
emit_moveq_dn(block, REG_68K_D_A, 0);
|
||||
emit_move_b_dn(block, REG_68K_D_FLAGS, 0x80); // Z=1, N=0, H=0, C=0
|
||||
@@ -381,22 +456,13 @@ struct code_block *compile_block(uint16_t src_address, struct compile_ctx *ctx)
|
||||
case 0xcb: // CB prefix
|
||||
{
|
||||
uint8_t cb_op = READ_BYTE(src_ptr++);
|
||||
switch (cb_op) {
|
||||
case 0x37: // swap a
|
||||
emit_ror_b_imm(block, 4, REG_68K_D_A);
|
||||
// set Z if result is 0, clear N, H, C
|
||||
emit_cmp_b_imm_dn(block, REG_68K_D_A, 0);
|
||||
compile_set_z_flag(block);
|
||||
emit_andi_b_dn(block, REG_68K_D_FLAGS, 0x80);
|
||||
break;
|
||||
default:
|
||||
if (!compile_cb_insn(block, cb_op)) {
|
||||
block->error = 1;
|
||||
block->failed_opcode = 0xcb00 | cb_op;
|
||||
block->failed_address = src_address + src_ptr - 2;
|
||||
emit_move_l_dn(block, REG_68K_D_NEXT_PC, 0xffffffff);
|
||||
emit_rts(block);
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -420,6 +486,13 @@ struct code_block *compile_block(uint16_t src_address, struct compile_ctx *ctx)
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xc6: // add a, #imm
|
||||
emit_addi_b_dn(block, REG_68K_D_A, READ_BYTE(src_ptr++));
|
||||
compile_set_z_flag(block);
|
||||
// TODO: proper H and C flags
|
||||
emit_andi_b_dn(block, REG_68K_D_FLAGS, 0x80); // keep Z, clear N, H, C
|
||||
break;
|
||||
|
||||
case 0xca: // jp z, imm16
|
||||
compile_jp_cond(block, ctx, &src_ptr, src_address, 7, 1);
|
||||
break;
|
||||
@@ -459,6 +532,11 @@ struct code_block *compile_block(uint16_t src_address, struct compile_ctx *ctx)
|
||||
emit_move_b_dn_disp_an(block, REG_68K_D_SCRATCH_1, 1, REG_68K_A_SP);
|
||||
break;
|
||||
|
||||
case 0xd6: // sub a, #imm
|
||||
emit_subi_b_dn(block, REG_68K_D_A, READ_BYTE(src_ptr++));
|
||||
compile_set_znc_flags(block);
|
||||
break;
|
||||
|
||||
case 0xd8: // ret c
|
||||
compile_ret_cond(block, 4, 1);
|
||||
break;
|
||||
@@ -544,6 +622,21 @@ struct code_block *compile_block(uint16_t src_address, struct compile_ctx *ctx)
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xee: // xor a, u8
|
||||
emit_eor_b_imm_dn(block, READ_BYTE(src_ptr++), REG_68K_D_A);
|
||||
compile_set_z_flag(block);
|
||||
emit_andi_b_dn(block, REG_68K_D_FLAGS, 0x80); // clear N, H, C
|
||||
break;
|
||||
|
||||
case 0xfa: // ld a, (u16)
|
||||
{
|
||||
uint16_t addr = READ_BYTE(src_ptr) | (READ_BYTE(src_ptr + 1) << 8);
|
||||
src_ptr += 2;
|
||||
emit_move_w_dn(block, REG_68K_D_SCRATCH_1, addr);
|
||||
compile_call_dmg_read(block);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// unknown opcode - set error info and halt
|
||||
block->error = 1;
|
||||
|
||||
+2
-2
@@ -45,8 +45,8 @@
|
||||
|
||||
|
||||
struct code_block {
|
||||
uint8_t code[256];
|
||||
uint16_t m68k_offsets[256];
|
||||
uint8_t code[2048];
|
||||
uint16_t m68k_offsets[2048];
|
||||
size_t length;
|
||||
uint16_t gb_cycles; // for timing
|
||||
uint16_t src_address;
|
||||
|
||||
@@ -156,6 +156,19 @@ void emit_addq_b_dn(struct code_block *block, uint8_t dreg, uint8_t val)
|
||||
emit_word(block, 0x5000 | ddd << 9 | dreg);
|
||||
}
|
||||
|
||||
void emit_addq_l_dn(struct code_block *block, uint8_t dreg, uint8_t val)
|
||||
{
|
||||
uint16_t ddd;
|
||||
|
||||
// 0101 ddd 0 10 000 rrr
|
||||
if (val == 0 || val > 8) {
|
||||
printf("can only addq values between 1 and 8\n");
|
||||
exit(1);
|
||||
}
|
||||
ddd = val == 8 ? 0 : val;
|
||||
emit_word(block, 0x5080 | ddd << 9 | dreg);
|
||||
}
|
||||
|
||||
// addq.w #val, An
|
||||
void emit_addq_w_an(struct code_block *block, uint8_t areg, uint8_t val)
|
||||
{
|
||||
@@ -230,6 +243,13 @@ void emit_cmp_b_imm_dn(struct code_block *block, uint8_t dreg, uint8_t imm)
|
||||
emit_word(block, imm);
|
||||
}
|
||||
|
||||
// cmp.b Ds, Dd - compare data registers (Dd - Ds, set flags)
|
||||
void emit_cmp_b_dn_dn(struct code_block *block, uint8_t src, uint8_t dest)
|
||||
{
|
||||
// 1011 ddd 000 000 sss
|
||||
emit_word(block, 0xb000 | (dest << 9) | src);
|
||||
}
|
||||
|
||||
// scc Dn - set byte based on condition
|
||||
// cond: 0x7 = seq (Z=1), 0x6 = sne (Z=0), 0x5 = scs (C=1), 0x4 = scc (C=0)
|
||||
void emit_scc(struct code_block *block, uint8_t cond, uint8_t dreg)
|
||||
@@ -254,6 +274,22 @@ void emit_ori_b_dn(struct code_block *block, uint8_t dreg, uint8_t imm)
|
||||
emit_word(block, imm);
|
||||
}
|
||||
|
||||
// subi.b #imm, Dn
|
||||
void emit_subi_b_dn(struct code_block *block, uint8_t dreg, uint8_t imm)
|
||||
{
|
||||
// 0000 0100 00 000 rrr
|
||||
emit_word(block, 0x0400 | dreg);
|
||||
emit_word(block, imm);
|
||||
}
|
||||
|
||||
// addi.b #imm, Dn
|
||||
void emit_addi_b_dn(struct code_block *block, uint8_t dreg, uint8_t imm)
|
||||
{
|
||||
// 0000 0110 00 000 rrr
|
||||
emit_word(block, 0x0600 | dreg);
|
||||
emit_word(block, imm);
|
||||
}
|
||||
|
||||
// or.b Ds, Dd (result to Dd)
|
||||
void emit_or_b_dn_dn(struct code_block *block, uint8_t src, uint8_t dest)
|
||||
{
|
||||
@@ -296,6 +332,22 @@ void emit_btst_imm_dn(struct code_block *block, uint8_t bit, uint8_t dreg)
|
||||
emit_word(block, bit);
|
||||
}
|
||||
|
||||
// bclr #bit, Dn - clear bit, sets Z based on previous bit value
|
||||
void emit_bclr_imm_dn(struct code_block *block, uint8_t bit, uint8_t dreg)
|
||||
{
|
||||
// 0000 1000 10 000 rrr, followed by bit number
|
||||
emit_word(block, 0x0880 | dreg);
|
||||
emit_word(block, bit);
|
||||
}
|
||||
|
||||
// bset #bit, Dn - set bit, sets Z based on previous bit value
|
||||
void emit_bset_imm_dn(struct code_block *block, uint8_t bit, uint8_t dreg)
|
||||
{
|
||||
// 0000 1000 11 000 rrr, followed by bit number
|
||||
emit_word(block, 0x08c0 | dreg);
|
||||
emit_word(block, bit);
|
||||
}
|
||||
|
||||
// move.l Ds, Dd - copy data register to data register (long)
|
||||
void emit_move_l_dn_dn(struct code_block *block, uint8_t src, uint8_t dest)
|
||||
{
|
||||
@@ -324,6 +376,14 @@ void emit_lsl_w_imm_dn(struct code_block *block, uint8_t count, uint8_t dreg)
|
||||
emit_word(block, 0xe148 | (ccc << 9) | dreg);
|
||||
}
|
||||
|
||||
// move.b #imm, -(A7) - push immediate word
|
||||
void emit_push_b_imm(struct code_block *block, uint16_t val)
|
||||
{
|
||||
// 00 01 111 100 111 100 (dest = -(A7), src = immediate word)
|
||||
emit_word(block, 0x1f3c);
|
||||
emit_word(block, val);
|
||||
}
|
||||
|
||||
// move.w #imm, -(A7) - push immediate word
|
||||
void emit_push_w_imm(struct code_block *block, uint16_t val)
|
||||
{
|
||||
@@ -332,6 +392,14 @@ void emit_push_w_imm(struct code_block *block, uint16_t val)
|
||||
emit_word(block, val);
|
||||
}
|
||||
|
||||
// move.b Dn, -(A7) - push byte from data register
|
||||
// this actually decreases SP by 2
|
||||
void emit_push_b_dn(struct code_block *block, uint8_t dreg)
|
||||
{
|
||||
// 00 01 111 100 000 ddd (dest = -(A7), src = Dn)
|
||||
emit_word(block, 0x1f00 | dreg);
|
||||
}
|
||||
|
||||
// move.w Dn, -(A7) - push word from data register
|
||||
void emit_push_w_dn(struct code_block *block, uint8_t dreg)
|
||||
{
|
||||
@@ -418,6 +486,14 @@ void emit_eor_b_dn_dn(struct code_block *block, uint8_t src, uint8_t dest)
|
||||
emit_word(block, 0xb100 | (src << 9) | dest);
|
||||
}
|
||||
|
||||
// eori.b #imm, Dn - XOR immediate with data register
|
||||
void emit_eor_b_imm_dn(struct code_block *block, uint8_t imm, uint8_t dreg)
|
||||
{
|
||||
// 0000 1010 00 000 rrr
|
||||
emit_word(block, 0x0a00 | dreg);
|
||||
emit_word(block, imm);
|
||||
}
|
||||
|
||||
void emit_ext_w_dn(struct code_block *block, uint8_t dreg)
|
||||
{
|
||||
// 0100 100 010 000 ddd
|
||||
@@ -452,6 +528,20 @@ void emit_ror_b_imm(struct code_block *block, uint8_t count, uint8_t dreg)
|
||||
emit_word(block, 0xe018 | (ccc << 9) | dreg);
|
||||
}
|
||||
|
||||
// rol.b #count, Dn - rotate left byte by immediate (1-8)
|
||||
void emit_rol_b_imm(struct code_block *block, uint8_t count, uint8_t dreg)
|
||||
{
|
||||
uint16_t ccc;
|
||||
|
||||
// 1110 ccc 1 00 0 11 rrr (d=1 for left, size=00 for byte, i=0 for imm)
|
||||
if (count == 0 || count > 8) {
|
||||
printf("can only rol by 1-8\n");
|
||||
exit(1);
|
||||
}
|
||||
ccc = count == 8 ? 0 : count;
|
||||
emit_word(block, 0xe118 | (ccc << 9) | dreg);
|
||||
}
|
||||
|
||||
// add.b Ds, Dd - ADD data registers (result to Dd)
|
||||
void emit_add_b_dn_dn(struct code_block *block, uint8_t src, uint8_t dest)
|
||||
{
|
||||
|
||||
+11
-1
@@ -24,7 +24,8 @@ void emit_movea_w_imm16(struct code_block *block, uint8_t areg, uint16_t val);
|
||||
void emit_subq_b_dn(struct code_block *block, uint8_t dreg, uint8_t val);
|
||||
void emit_subq_l_dn(struct code_block *block, uint8_t dreg, uint8_t val);
|
||||
void emit_subq_w_an(struct code_block *block, uint8_t areg, uint8_t val);
|
||||
void emit_addq_b_dn(struct code_block *block, uint8_t areg, uint8_t val);
|
||||
void emit_addq_b_dn(struct code_block *block, uint8_t dreg, uint8_t val);
|
||||
void emit_addq_l_dn(struct code_block *block, uint8_t dreg, uint8_t val);
|
||||
void emit_addq_w_an(struct code_block *block, uint8_t areg, uint8_t val);
|
||||
void emit_move_w_dn_ind_an(struct code_block *block, uint8_t dreg, uint8_t areg);
|
||||
void emit_move_w_ind_an_dn(struct code_block *block, uint8_t areg, uint8_t dreg);
|
||||
@@ -34,9 +35,12 @@ void emit_move_b_ind_an_dn(struct code_block *block, uint8_t areg, uint8_t dreg)
|
||||
void emit_move_b_disp_an_dn(struct code_block *block, int16_t disp, uint8_t areg, uint8_t dreg);
|
||||
void emit_andi_l_dn(struct code_block *block, uint8_t dreg, uint32_t imm);
|
||||
void emit_cmp_b_imm_dn(struct code_block *block, uint8_t dreg, uint8_t imm);
|
||||
void emit_cmp_b_dn_dn(struct code_block *block, uint8_t src, uint8_t dest);
|
||||
void emit_scc(struct code_block *block, uint8_t cond, uint8_t dreg);
|
||||
void emit_andi_b_dn(struct code_block *block, uint8_t dreg, uint8_t imm);
|
||||
void emit_ori_b_dn(struct code_block *block, uint8_t dreg, uint8_t imm);
|
||||
void emit_subi_b_dn(struct code_block *block, uint8_t dreg, uint8_t imm);
|
||||
void emit_addi_b_dn(struct code_block *block, uint8_t dreg, uint8_t imm);
|
||||
void emit_or_b_dn_dn(struct code_block *block, uint8_t src, uint8_t dest);
|
||||
|
||||
void emit_rts(struct code_block *block);
|
||||
@@ -44,11 +48,15 @@ void emit_bra_w(struct code_block *block, int16_t disp);
|
||||
void emit_beq_w(struct code_block *block, int16_t disp);
|
||||
void emit_bne_w(struct code_block *block, int16_t disp);
|
||||
void emit_btst_imm_dn(struct code_block *block, uint8_t bit, uint8_t dreg);
|
||||
void emit_bclr_imm_dn(struct code_block *block, uint8_t bit, uint8_t dreg);
|
||||
void emit_bset_imm_dn(struct code_block *block, uint8_t bit, uint8_t dreg);
|
||||
|
||||
void emit_move_l_dn_dn(struct code_block *block, uint8_t src, uint8_t dest);
|
||||
void emit_move_w_dn_dn(struct code_block *block, uint8_t src, uint8_t dest);
|
||||
void emit_lsl_w_imm_dn(struct code_block *block, uint8_t count, uint8_t dreg);
|
||||
void emit_push_b_imm(struct code_block *block, uint16_t val);
|
||||
void emit_push_w_imm(struct code_block *block, uint16_t val);
|
||||
void emit_push_b_dn(struct code_block *block, uint8_t dreg);
|
||||
void emit_push_w_dn(struct code_block *block, uint8_t dreg);
|
||||
void emit_push_l_disp_an(struct code_block *block, int16_t disp, uint8_t areg);
|
||||
void emit_movea_l_disp_an_an(struct code_block *block, int16_t disp, uint8_t src_areg, uint8_t dest_areg);
|
||||
@@ -59,11 +67,13 @@ void emit_movem_l_from_postinc(struct code_block *block, uint16_t mask);
|
||||
void emit_move_b_dn_dn(struct code_block *block, uint8_t src, uint8_t dest);
|
||||
void emit_movea_l_imm32(struct code_block *block, uint8_t areg, uint32_t val);
|
||||
void emit_eor_b_dn_dn(struct code_block *block, uint8_t src, uint8_t dest);
|
||||
void emit_eor_b_imm_dn(struct code_block *block, uint8_t imm, uint8_t dreg);
|
||||
|
||||
void emit_ext_w_dn(struct code_block *block, uint8_t dreg);
|
||||
void emit_not_b_dn(struct code_block *block, uint8_t dreg);
|
||||
void emit_and_b_dn_dn(struct code_block *block, uint8_t src, uint8_t dest);
|
||||
void emit_ror_b_imm(struct code_block *block, uint8_t count, uint8_t dreg);
|
||||
void emit_rol_b_imm(struct code_block *block, uint8_t count, uint8_t dreg);
|
||||
void emit_add_b_dn_dn(struct code_block *block, uint8_t src, uint8_t dest);
|
||||
void emit_add_w_dn_dn(struct code_block *block, uint8_t src, uint8_t dest);
|
||||
void emit_adda_w_dn_an(struct code_block *block, uint8_t dreg, uint8_t areg);
|
||||
|
||||
+14
-2
@@ -6,7 +6,7 @@
|
||||
void compile_call_dmg_write(struct code_block *block)
|
||||
{
|
||||
// Push args right-to-left: val, addr, dmg
|
||||
emit_push_w_dn(block, REG_68K_D_A); // push value (A register = D4)
|
||||
emit_push_b_dn(block, REG_68K_D_A); // push value (A register = D4)
|
||||
emit_push_w_dn(block, REG_68K_D_SCRATCH_1); // push address (D1)
|
||||
emit_push_l_disp_an(block, JIT_CTX_DMG, REG_68K_A_CTX); // push dmg pointer
|
||||
emit_movea_l_disp_an_an(block, JIT_CTX_WRITE, REG_68K_A_CTX, REG_68K_A_SCRATCH_1); // A0 = write func
|
||||
@@ -17,7 +17,7 @@ void compile_call_dmg_write(struct code_block *block)
|
||||
// Call dmg_write(dmg, addr, val) - addr in D1
|
||||
void compile_call_dmg_write_imm(struct code_block *block, uint8_t val)
|
||||
{
|
||||
emit_push_w_imm(block, val);
|
||||
emit_push_b_imm(block, val);
|
||||
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_WRITE, REG_68K_A_CTX, REG_68K_A_SCRATCH_1);
|
||||
@@ -25,6 +25,17 @@ void compile_call_dmg_write_imm(struct code_block *block, uint8_t val)
|
||||
emit_addq_l_an(block, 7, 8);
|
||||
}
|
||||
|
||||
// Call dmg_write(dmg, addr, val) - addr in D1, val in D0
|
||||
void compile_call_dmg_write_d0(struct code_block *block)
|
||||
{
|
||||
emit_push_b_dn(block, REG_68K_D_NEXT_PC); // push value (D0)
|
||||
emit_push_w_dn(block, REG_68K_D_SCRATCH_1); // push address (D1)
|
||||
emit_push_l_disp_an(block, JIT_CTX_DMG, REG_68K_A_CTX); // push dmg pointer
|
||||
emit_movea_l_disp_an_an(block, JIT_CTX_WRITE, REG_68K_A_CTX, REG_68K_A_SCRATCH_1); // A0 = write func
|
||||
emit_jsr_ind_an(block, REG_68K_A_SCRATCH_1); // call dmg_write
|
||||
emit_addq_l_an(block, 7, 8); // clean up stack (4 + 2 + 2 = 8 bytes)
|
||||
}
|
||||
|
||||
// Call dmg_read(dmg, addr) - addr in D1, result stays in D0 (scratch)
|
||||
void compile_call_dmg_read_to_d0(struct code_block *block)
|
||||
{
|
||||
@@ -48,6 +59,7 @@ void compile_call_ei_di(struct code_block *block, int enabled)
|
||||
{
|
||||
// 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);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
void compile_call_dmg_write(struct code_block *block);
|
||||
void compile_call_dmg_write_imm(struct code_block *block, uint8_t val);
|
||||
void compile_call_dmg_write_d0(struct code_block *block); // value in D0, address in D1
|
||||
void compile_call_dmg_read(struct code_block *block);
|
||||
void compile_call_dmg_read_to_d0(struct code_block *block);
|
||||
void compile_call_ei_di(struct code_block *block, int enabled);
|
||||
|
||||
@@ -97,18 +97,17 @@ void m68k_write_memory_32(unsigned int address, unsigned int value)
|
||||
static void setup_runtime_stubs(void)
|
||||
{
|
||||
// stub_write: writes value byte to address
|
||||
// Stack layout after jsr: ret(4), dmg(4), addr(2), val(2)
|
||||
// Note: must zero-extend address since movea.w sign-extends
|
||||
// Stack layout after jsr: ret(4), dmg(4), addr(2), val(1), empty(1)
|
||||
// moveq #0, d0 ; clear d0
|
||||
// move.w 8(sp), d0 ; d0 = address (zero-extended)
|
||||
// movea.l d0, a0 ; a0 = address
|
||||
// move.b 11(sp), (a0) ; write value to memory
|
||||
// move.b 10(sp), (a0) ; write value to memory
|
||||
// rts
|
||||
static const uint8_t stub_write[] = {
|
||||
0x70, 0x00, // moveq #0, d0
|
||||
0x30, 0x2f, 0x00, 0x08, // move.w 8(sp), d0
|
||||
0x20, 0x40, // movea.l d0, a0
|
||||
0x10, 0xaf, 0x00, 0x0b, // move.b 11(sp), (a0)
|
||||
0x10, 0xaf, 0x00, 0x0a, // move.b 10(sp), (a0)
|
||||
0x4e, 0x75 // rts
|
||||
};
|
||||
|
||||
@@ -129,7 +128,7 @@ static void setup_runtime_stubs(void)
|
||||
};
|
||||
|
||||
static const uint8_t stub_ei_di[] = {
|
||||
0x11, 0xef, 0x00, 0x09, 0x40, 0x00, // move.b 9(sp), (U8_INTERRUPTS_ENABLED)
|
||||
0x31, 0xef, 0x00, 0x08, 0x40, 0x00, // move.w 8(sp), (U16_INTERRUPTS_ENABLED)
|
||||
0x4e, 0x75 // rts
|
||||
};
|
||||
|
||||
@@ -242,7 +241,7 @@ void run_program(uint8_t *gb_rom, uint16_t start_pc)
|
||||
m68k_set_reg(M68K_REG_SP, STACK_BASE - 4);
|
||||
m68k_set_reg(M68K_REG_PC, CODE_BASE);
|
||||
|
||||
m68k_execute(1000);
|
||||
int cycles = m68k_execute(5000);
|
||||
|
||||
// Check D0 for next PC or halt
|
||||
pc = get_dreg(REG_68K_D_NEXT_PC);
|
||||
|
||||
@@ -780,7 +780,8 @@ TEST(test_ei)
|
||||
0x10 // 0x0001: stop
|
||||
};
|
||||
run_program(rom, 0);
|
||||
ASSERT_EQ(get_mem_byte(U8_INTERRUPTS_ENABLED), 1);
|
||||
// big endian...
|
||||
ASSERT_EQ(get_mem_byte(U16_INTERRUPTS_ENABLED + 1), 1);
|
||||
}
|
||||
|
||||
TEST(test_di)
|
||||
@@ -793,7 +794,7 @@ TEST(test_di)
|
||||
0x10 // 0x0001: stop
|
||||
};
|
||||
run_program(rom, 0);
|
||||
ASSERT_EQ(get_mem_byte(U8_INTERRUPTS_ENABLED), 0);
|
||||
ASSERT_EQ(get_mem_byte(U16_INTERRUPTS_ENABLED + 1), 0);
|
||||
}
|
||||
|
||||
TEST(test_push_pop_de_hl)
|
||||
@@ -868,6 +869,43 @@ TEST(test_rst_28)
|
||||
ASSERT_EQ((get_dreg(REG_68K_D_BC) >> 16) & 0xff, 0x22);
|
||||
}
|
||||
|
||||
TEST(test_ldh_dec_ldh_loop)
|
||||
{
|
||||
// Test the pattern: ldh a, (n); dec a; ldh (n), a; jr nz, loop
|
||||
// This is similar to the sprite counter decrement loop
|
||||
uint8_t rom[] = {
|
||||
0x3e, 0x03, // 0x0000: ld a, 3
|
||||
0xe0, 0x90, // 0x0002: ldh ($ff90), a ; counter = 3
|
||||
// loop:
|
||||
0xf0, 0x90, // 0x0004: ldh a, ($ff90) ; read counter
|
||||
0x3d, // 0x0006: dec a
|
||||
0xe0, 0x90, // 0x0007: ldh ($ff90), a ; write counter
|
||||
0x20, 0xf9, // 0x0009: jr nz, -7 (back to 0x0004)
|
||||
0x10 // 0x000b: stop
|
||||
};
|
||||
run_program(rom, 0);
|
||||
ASSERT_EQ(get_dreg(REG_68K_D_A) & 0xff, 0x00); // A should be 0
|
||||
ASSERT_EQ(get_mem_byte(0xff90), 0x00); // counter should be 0
|
||||
}
|
||||
|
||||
TEST(test_ldh_dec_preserves_value)
|
||||
{
|
||||
// Verify that ldh read -> dec -> ldh write preserves the correct byte value
|
||||
// even if other registers have garbage in high bytes
|
||||
uint8_t rom[] = {
|
||||
0x3e, 0x05, // 0x0000: ld a, 5
|
||||
0xe0, 0x91, // 0x0002: ldh ($ff91), a ; mem = 5
|
||||
0x3e, 0xff, // 0x0004: ld a, 0xff ; pollute A with 0xff
|
||||
0xf0, 0x91, // 0x0006: ldh a, ($ff91) ; read back (should be 5)
|
||||
0x3d, // 0x0008: dec a ; a = 4
|
||||
0xe0, 0x91, // 0x0009: ldh ($ff91), a ; write 4
|
||||
0x10 // 0x000b: stop
|
||||
};
|
||||
run_program(rom, 0);
|
||||
ASSERT_EQ(get_dreg(REG_68K_D_A) & 0xff, 0x04);
|
||||
ASSERT_EQ(get_mem_byte(0xff91), 0x04);
|
||||
}
|
||||
|
||||
void register_exec_tests(void)
|
||||
{
|
||||
printf("\nExecution tests:\n");
|
||||
@@ -992,4 +1030,8 @@ void register_exec_tests(void)
|
||||
RUN_TEST(test_ei);
|
||||
RUN_TEST(test_di);
|
||||
RUN_TEST(test_exec_jr_nz_then_call);
|
||||
|
||||
printf("\nLDH counter tests:\n");
|
||||
RUN_TEST(test_ldh_dec_ldh_loop);
|
||||
RUN_TEST(test_ldh_dec_preserves_value);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ static uint8_t test_read(void *dmg, uint16_t address)
|
||||
/*
|
||||
* Assembly stubs that match the JIT calling convention.
|
||||
* JIT code pushes: value(2), addr(2), dmg(4), then JSR
|
||||
* So stack after jsr: ret(4), dmg(4), addr(2), value(2)
|
||||
* So stack after jsr: ret(4), dmg(4), addr(2), value(1), empty(1)
|
||||
*
|
||||
* These access the mem[] array using indexed addressing.
|
||||
*/
|
||||
@@ -63,7 +63,7 @@ asm(
|
||||
" moveq #0, %d0\n" /* clear d0 */
|
||||
" move.w 8(%sp), %d0\n" /* d0 = gb addr (zero-extended) */
|
||||
" lea mem, %a0\n" /* a0 = mem base */
|
||||
" move.b 11(%sp), (%a0,%d0.l)\n" /* write value */
|
||||
" move.b 10(%sp), (%a0,%d0.l)\n" /* write value */
|
||||
" rts\n"
|
||||
|
||||
".globl stub_read_asm\n"
|
||||
@@ -80,7 +80,7 @@ asm(
|
||||
".globl stub_ei_di\n"
|
||||
"stub_ei_di:\n"
|
||||
" lea mem, %a0\n" /* a0 = mem base */
|
||||
" move.b 9(%sp), 0x4000(%a0)\n" /* write to mem[U8_INTERRUPTS_ENABLED] */
|
||||
" move.b 8(%sp), 0x4000(%a0)\n" /* write to mem[U8_INTERRUPTS_ENABLED] */
|
||||
" rts\n"
|
||||
);
|
||||
|
||||
|
||||
@@ -84,6 +84,6 @@ void register_unit_tests(void);
|
||||
void register_exec_tests(void);
|
||||
|
||||
#define GLOBALS_BASE 0x4000 // random variables
|
||||
#define U8_INTERRUPTS_ENABLED 0x4000
|
||||
#define U16_INTERRUPTS_ENABLED 0x4000
|
||||
|
||||
#endif
|
||||
|
||||
@@ -124,48 +124,13 @@ static u8 get_button_state(struct dmg *dmg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Advance LCD state based on elapsed CPU cycles (for JIT mode)
|
||||
static void sync_lcd(struct dmg *dmg)
|
||||
{
|
||||
int cycle_diff = dmg->cpu->cycle_count - dmg->last_lcd_update;
|
||||
|
||||
while (cycle_diff >= 456) {
|
||||
int next_ly = lcd_step(dmg->lcd);
|
||||
dmg->last_lcd_update += 456;
|
||||
cycle_diff -= 456;
|
||||
|
||||
// render at vblank
|
||||
if (next_ly == 144) {
|
||||
int lcdc = lcd_read(dmg->lcd, REG_LCDC);
|
||||
if (lcdc & LCDC_ENABLE_BG) {
|
||||
int window_enabled = lcdc & LCDC_ENABLE_WINDOW;
|
||||
lcd_render_background(dmg, lcdc, window_enabled);
|
||||
}
|
||||
if (lcdc & LCDC_ENABLE_OBJ) {
|
||||
lcd_render_objs(dmg);
|
||||
}
|
||||
lcd_draw(dmg->lcd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static u8 dmg_read_slow(struct dmg *dmg, u16 address)
|
||||
{
|
||||
// Hacky: advance LCD every time LY is read (for JIT mode)
|
||||
if (address == REG_LY) {
|
||||
int next_ly = lcd_step(dmg->lcd);
|
||||
if (next_ly == 144) {
|
||||
int lcdc = lcd_read(dmg->lcd, REG_LCDC);
|
||||
if (lcdc & LCDC_ENABLE_BG) {
|
||||
int window_enabled = lcdc & LCDC_ENABLE_WINDOW;
|
||||
lcd_render_background(dmg, lcdc, window_enabled);
|
||||
}
|
||||
if (lcdc & LCDC_ENABLE_OBJ) {
|
||||
lcd_render_objs(dmg);
|
||||
}
|
||||
lcd_draw(dmg->lcd);
|
||||
}
|
||||
return next_ly;
|
||||
dmg->cpu->cycle_count += 456;
|
||||
dmg_sync_hw(dmg);
|
||||
return lcd_read(dmg->lcd, REG_LY);
|
||||
}
|
||||
|
||||
// OAM and LCD registers
|
||||
@@ -215,19 +180,27 @@ static u8 dmg_read_slow(struct dmg *dmg, u16 address)
|
||||
|
||||
return 0xff;
|
||||
}
|
||||
extern void debug_log_string(const char *str);
|
||||
|
||||
u8 dmg_read(void *_dmg, u16 address)
|
||||
{
|
||||
// char buf[128];
|
||||
// u8 val;
|
||||
struct dmg *dmg = (struct dmg *) _dmg;
|
||||
u8 *page = dmg->read_page[address >> 8];
|
||||
if (page) {
|
||||
return page[address & 0xff];
|
||||
}
|
||||
// sprintf(buf, "dmg_read pc=%04x addr=%04x value=%02x\n", dmg->cpu->pc, address, val);
|
||||
// debug_log_string(buf);
|
||||
return dmg_read_slow(dmg, address);
|
||||
}
|
||||
|
||||
static void dmg_write_slow(struct dmg *dmg, u16 address, u8 data)
|
||||
{
|
||||
// char buf[128];
|
||||
// sprintf(buf, "dmg_write pc=%04x addr=%04x value=%02x\n", dmg->cpu->pc, address, data);
|
||||
// debug_log_string(buf);
|
||||
// ROM region writes go to MBC for bank switching
|
||||
if (address < 0x8000) {
|
||||
mbc_write(dmg->rom->mbc, dmg, address, data);
|
||||
@@ -338,13 +311,9 @@ static void timer_step(struct dmg *dmg)
|
||||
dmg->last_timer_update = dmg->cpu->cycle_count;
|
||||
}
|
||||
|
||||
void dmg_step(void *_dmg)
|
||||
// Sync hardware state without running CPU - for JIT mode
|
||||
void dmg_sync_hw(struct dmg *dmg)
|
||||
{
|
||||
struct dmg *dmg = (struct dmg *) _dmg;
|
||||
|
||||
// order of dependencies? i think cpu needs to step first then update
|
||||
// all other hw
|
||||
cpu_step(dmg->cpu);
|
||||
timer_step(dmg);
|
||||
|
||||
// each line takes 456 cycles
|
||||
@@ -405,7 +374,15 @@ void dmg_step(void *_dmg)
|
||||
}
|
||||
}
|
||||
|
||||
void dmg_ei_di(void *dmg, u16 enabled)
|
||||
void dmg_step(void *_dmg)
|
||||
{
|
||||
((struct dmg *) dmg)->interrupt_enabled = enabled;
|
||||
struct dmg *dmg = (struct dmg *) _dmg;
|
||||
cpu_step(dmg->cpu);
|
||||
dmg_sync_hw(dmg);
|
||||
}
|
||||
|
||||
void dmg_ei_di(void *_dmg, u16 enabled)
|
||||
{
|
||||
struct dmg *dmg = (struct dmg *) _dmg;
|
||||
dmg->cpu->interrupt_enable = enabled ? 1 : 0;
|
||||
}
|
||||
@@ -64,6 +64,7 @@ u8 dmg_read(void *dmg, u16 address);
|
||||
void dmg_write(void *dmg, u16 address, u8 data);
|
||||
|
||||
void dmg_step(void *dmg);
|
||||
void dmg_sync_hw(struct dmg *dmg);
|
||||
void dmg_request_interrupt(struct dmg *dmg, int nr);
|
||||
|
||||
// page table management
|
||||
|
||||
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.9)
|
||||
|
||||
include_directories(../src ../compiler)
|
||||
|
||||
set(CMAKE_C_FLAGS "-O3")
|
||||
set(CMAKE_C_FLAGS "-O0")
|
||||
|
||||
add_application(Emulator
|
||||
../src/bootstrap.c
|
||||
@@ -18,6 +18,7 @@ add_application(Emulator
|
||||
../compiler/branches.c
|
||||
../compiler/interop.c
|
||||
../compiler/flags.c
|
||||
../compiler/cb_prefix.c
|
||||
emulator.c
|
||||
resources.r
|
||||
)
|
||||
)
|
||||
|
||||
+80
-4
@@ -34,6 +34,29 @@ static struct code_block *block_cache[MAX_CACHED_BLOCKS];
|
||||
// Debug logging - open/close each time to avoid losing data on crash
|
||||
static int debug_enabled = 1;
|
||||
|
||||
void debug_log_string(const char *str)
|
||||
{
|
||||
short fref;
|
||||
char buf[128];
|
||||
long len;
|
||||
OSErr err;
|
||||
err = FSOpen("\pjit_log.txt", 0, &fref);
|
||||
if (err == fnfErr) {
|
||||
Create("\pjit_log.txt", 0, 'ttxt', 'TEXT');
|
||||
err = FSOpen("\pjit_log.txt", 0, &fref);
|
||||
}
|
||||
if (err != noErr) return;
|
||||
// Seek to end
|
||||
SetFPos(fref, fsFromLEOF, 0);
|
||||
len = strlen(str);
|
||||
FSWrite(fref, &len, str);
|
||||
buf[0] = '\n';
|
||||
len = 1;
|
||||
FSWrite(fref, &len, buf);
|
||||
|
||||
FSClose(fref);
|
||||
}
|
||||
|
||||
static void debug_log_block(struct code_block *block)
|
||||
{
|
||||
short fref;
|
||||
@@ -322,6 +345,8 @@ static int jit_step(void)
|
||||
if (!block) {
|
||||
sprintf(buf, "JIT: alloc fail pc=%04x", cpu.pc);
|
||||
set_status_bar(buf);
|
||||
|
||||
lcd_draw(dmg.lcd);
|
||||
jit_halted = 1;
|
||||
return 0;
|
||||
}
|
||||
@@ -330,13 +355,13 @@ static int jit_step(void)
|
||||
if (block->error) {
|
||||
sprintf(buf, "pc=%04x op=%02x", block->failed_address, block->failed_opcode);
|
||||
set_status_bar(buf);
|
||||
|
||||
lcd_draw(dmg.lcd);
|
||||
jit_halted = 1;
|
||||
block_free(block);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Log the compiled block
|
||||
debug_log_block(block);
|
||||
|
||||
// Cache the block
|
||||
// if (cpu.pc < MAX_CACHED_BLOCKS) {
|
||||
@@ -344,6 +369,8 @@ static int jit_step(void)
|
||||
// }
|
||||
}
|
||||
|
||||
// Log the compiled block
|
||||
debug_log_block(block);
|
||||
// Execute the block
|
||||
execute_block(block->code);
|
||||
|
||||
@@ -356,6 +383,54 @@ static int jit_step(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Debug: show interrupt state every ~1000 blocks
|
||||
{
|
||||
static int dbg_cnt = 0;
|
||||
static u16 min_pc = 0xffff, max_pc = 0;
|
||||
if (cpu.pc < min_pc) min_pc = cpu.pc;
|
||||
if (cpu.pc > max_pc) max_pc = cpu.pc;
|
||||
if (++dbg_cnt >= 1000) {
|
||||
dbg_cnt = 0;
|
||||
sprintf(buf, "PC=%04x-%04x IE=%02x", min_pc, max_pc,
|
||||
dmg.interrupt_enabled);
|
||||
set_status_bar(buf);
|
||||
lcd_draw(dmg.lcd);
|
||||
min_pc = 0xffff;
|
||||
max_pc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for pending interrupts
|
||||
if (cpu.interrupt_enable) {
|
||||
u8 pending = dmg.interrupt_enabled & dmg.interrupt_requested & 0x1f;
|
||||
if (pending) {
|
||||
static const u16 handlers[] = { 0x40, 0x48, 0x50, 0x58, 0x60 };
|
||||
int k;
|
||||
for (k = 0; k < 5; k++) {
|
||||
if (pending & (1 << k)) {
|
||||
|
||||
sprintf(buf, "INT %d -> %04x", k, handlers[k]);
|
||||
set_status_bar(buf);
|
||||
lcd_draw(dmg.lcd);
|
||||
// Clear IF bit and disable IME
|
||||
dmg.interrupt_requested &= ~(1 << k);
|
||||
cpu.interrupt_enable = 0;
|
||||
|
||||
// Push PC to stack
|
||||
u16 sp = (u16) jit_aregs[REG_68K_A_SP];
|
||||
sp -= 2;
|
||||
dmg_write(&dmg, sp + 1, (next_pc >> 8) & 0xff);
|
||||
dmg_write(&dmg, sp, next_pc & 0xff);
|
||||
jit_aregs[REG_68K_A_SP] = sp;
|
||||
|
||||
// Jump to handler
|
||||
next_pc = handlers[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update CPU state
|
||||
cpu.pc = (u16) next_pc;
|
||||
|
||||
@@ -363,6 +438,9 @@ static int jit_step(void)
|
||||
// for now
|
||||
cpu.cycle_count += block->gb_cycles ? block->gb_cycles : 16;
|
||||
|
||||
// Sync LCD/timer state
|
||||
dmg_sync_hw(&dmg);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -714,8 +792,6 @@ int main(int argc, char *argv[])
|
||||
while ((long) (frame_end - cpu.cycle_count) > 0 && !jit_halted) {
|
||||
jit_step();
|
||||
}
|
||||
// since dmg_step is bypassed...
|
||||
lcd_draw(&lcd);
|
||||
} else {
|
||||
// Interpreter mode
|
||||
while ((long) (frame_end - cpu.cycle_count) > 0) {
|
||||
|
||||
Reference in New Issue
Block a user