mirror of
https://github.com/mlaux/gb6.git
synced 2026-10-24 01:02:15 +00:00
849 lines
28 KiB
C
849 lines
28 KiB
C
#include "alu.h"
|
|
#include "emitters.h"
|
|
#include "flags.h"
|
|
#include "interop.h"
|
|
#include "branches.h"
|
|
#include "instructions.h"
|
|
|
|
// helper for reading GB memory during compilation
|
|
#define READ_BYTE(off) (ctx->read(ctx->dmg, src_address + (off)))
|
|
|
|
// ops that emit DAA tracking (the add/sub family)
|
|
static int daa_tracked_op(uint8_t op)
|
|
{
|
|
if (op >= 0x80 && op <= 0x9f)
|
|
return 1;
|
|
switch (op) {
|
|
case 0x3c: case 0x3d:
|
|
case 0xc6: case 0xce: case 0xd6: case 0xde:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// control flow the DAA scan cannot see past
|
|
static int daa_scan_barrier(uint8_t op)
|
|
{
|
|
switch (op) {
|
|
case 0x10: case 0x18: case 0x20: case 0x28: case 0x30: case 0x38:
|
|
case 0x76:
|
|
case 0xc0: case 0xc2: case 0xc3: case 0xc4: case 0xc7:
|
|
case 0xc8: case 0xc9: case 0xca: case 0xcc: case 0xcd: case 0xcf:
|
|
case 0xd0: case 0xd2: case 0xd4: case 0xd7:
|
|
case 0xd8: case 0xd9: case 0xda: case 0xdc: case 0xdf:
|
|
case 0xe7: case 0xe9: case 0xef:
|
|
case 0xf7: case 0xff:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// returns 0 when a later tracked ALU op re-establishes DAA state before any
|
|
// daa/branch/block end could observe this op's state, so tracking is dead
|
|
static int daa_track_needed(
|
|
struct compile_ctx *ctx,
|
|
uint16_t src_address,
|
|
uint16_t off
|
|
) {
|
|
while (off < 256) {
|
|
uint8_t op = READ_BYTE(off);
|
|
|
|
if (op == 0x27)
|
|
return 1;
|
|
if (daa_tracked_op(op))
|
|
return 0;
|
|
if (daa_scan_barrier(op))
|
|
return 1;
|
|
off += insn_length[op];
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// set per-op by compile_alu_op from the scan above
|
|
static int daa_track = 1;
|
|
|
|
// DAA tracking: save old_A and set N flag before ALU ops that affect A
|
|
// These are needed for DAA to compute the half-carry (H) and know add vs sub
|
|
static void compile_daa_track_add(struct code_block *block)
|
|
{
|
|
if (!daa_track)
|
|
return;
|
|
// Save old_A to context for H flag computation
|
|
emit_move_b_dn_disp_an(block, REG_68K_D_A, JIT_CTX_DAA_STATE, REG_68K_A_CTX);
|
|
// Set N=0 (addition)
|
|
emit_moveq_dn(block, REG_68K_D_SCRATCH_0, 0);
|
|
emit_move_b_dn_disp_an(block, REG_68K_D_SCRATCH_0, JIT_CTX_DAA_STATE + 1, REG_68K_A_CTX);
|
|
}
|
|
|
|
static void compile_daa_track_sub(struct code_block *block)
|
|
{
|
|
if (!daa_track)
|
|
return;
|
|
// Save old_A to context for H flag computation
|
|
emit_move_b_dn_disp_an(block, REG_68K_D_A, JIT_CTX_DAA_STATE, REG_68K_A_CTX);
|
|
// Set N=1 (subtraction)
|
|
emit_moveq_dn(block, REG_68K_D_SCRATCH_0, 1);
|
|
emit_move_b_dn_disp_an(block, REG_68K_D_SCRATCH_0, JIT_CTX_DAA_STATE + 1, REG_68K_A_CTX);
|
|
}
|
|
|
|
// DAA - Decimal Adjust Accumulator
|
|
// Adjusts A for BCD arithmetic based on N, H, C flags from prior ALU op
|
|
static void compile_daa(struct code_block *block)
|
|
{
|
|
size_t branch_to_sub, branch_add_h_done;
|
|
size_t branch_to_finish;
|
|
size_t skip;
|
|
|
|
// Save original A lower nibble into D1 for H computation later
|
|
// (before any DAA adjustments modify A)
|
|
emit_move_b_dn_dn(block, REG_68K_D_A, REG_68K_D_SCRATCH_1);
|
|
emit_andi_b_dn(block, REG_68K_D_SCRATCH_1, 0x0F); // D1 = original A & 0xF
|
|
|
|
// Load N flag into D0 and test it (we'll reload old_A later in each path)
|
|
emit_move_b_disp_an_dn(block, JIT_CTX_DAA_STATE + 1, REG_68K_A_CTX, REG_68K_D_SCRATCH_0);
|
|
emit_tst_b_dn(block, REG_68K_D_SCRATCH_0);
|
|
branch_to_sub = block->length;
|
|
emit_bne_b(block, 0); // branch to subtraction path if N=1
|
|
|
|
// === Addition path (N=0) ===
|
|
// First check C || A > 0x99 -> add 0x60
|
|
emit_btst_imm_dn(block, 0, REG_68K_D_FLAGS); // test C flag
|
|
size_t have_carry = block->length;
|
|
emit_bne_b(block, 0); // if C set, add 0x60 without the compare
|
|
emit_cmp_b_imm_dn(block, REG_68K_D_A, 0x99);
|
|
skip = block->length;
|
|
emit_bls_b(block, 0); // if A <= 0x99, skip add 0x60 and ori
|
|
// add 0x60 and set C
|
|
patch_branch_b(block, have_carry);
|
|
emit_addi_b_dn(block, REG_68K_D_A, 0x60);
|
|
emit_ori_b_dn(block, REG_68K_D_FLAGS, 0x01); // set C
|
|
patch_branch_b(block, skip);
|
|
|
|
// Now check H || (A & 0x0F) > 9 -> add 0x06
|
|
// Load old_A into D0 for H computation
|
|
emit_move_b_disp_an_dn(block, JIT_CTX_DAA_STATE, REG_68K_A_CTX, REG_68K_D_SCRATCH_0);
|
|
// D0 = old_A, D1 = original A & 0xF
|
|
// Compute H: D1 < (D0 & 0xF)?
|
|
emit_andi_b_dn(block, REG_68K_D_SCRATCH_0, 0x0F); // D0 = old_A & 0xF
|
|
emit_cmp_b_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_SCRATCH_1); // cmp D0, D1
|
|
skip = block->length;
|
|
emit_bcc_s(block, 0); // if D1 >= D0 (carry clear), H=0, check nibble value
|
|
// H=1, add 0x06
|
|
emit_addi_b_dn(block, REG_68K_D_A, 0x06);
|
|
branch_add_h_done = block->length;
|
|
emit_bra_b(block, 0); // skip to finish
|
|
|
|
// H=0, check if original (A & 0x0F) > 9 (D1 still has this value)
|
|
patch_branch_b(block, skip);
|
|
emit_cmp_b_imm_dn(block, REG_68K_D_SCRATCH_1, 0x09);
|
|
skip = block->length;
|
|
emit_bls_b(block, 0); // if <= 9, skip
|
|
emit_addi_b_dn(block, REG_68K_D_A, 0x06);
|
|
patch_branch_b(block, skip);
|
|
|
|
branch_to_finish = block->length;
|
|
emit_bra_b(block, 0); // jump to finish
|
|
|
|
// === Subtraction path (N=1) ===
|
|
patch_branch_b(block, branch_to_sub);
|
|
|
|
// Check C -> sub 0x60
|
|
emit_btst_imm_dn(block, 0, REG_68K_D_FLAGS);
|
|
skip = block->length;
|
|
emit_beq_b(block, 0); // if C clear, skip
|
|
emit_subi_b_dn(block, REG_68K_D_A, 0x60);
|
|
patch_branch_b(block, skip);
|
|
|
|
// Compute H: D1 > (old_A & 0xF) for subtraction
|
|
// Load old_A & 0xF into D0
|
|
emit_move_b_disp_an_dn(block, JIT_CTX_DAA_STATE, REG_68K_A_CTX, REG_68K_D_SCRATCH_0);
|
|
emit_andi_b_dn(block, REG_68K_D_SCRATCH_0, 0x0F); // D0 = old_A & 0xF
|
|
emit_cmp_b_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_SCRATCH_1); // cmp D0, D1
|
|
skip = block->length;
|
|
emit_bls_b(block, 0); // if D1 <= D0 (lower or same), no H, skip
|
|
emit_subi_b_dn(block, REG_68K_D_A, 0x06);
|
|
patch_branch_b(block, skip);
|
|
|
|
// === Finish: set Z flag === (sub path falls through)
|
|
patch_branch_b(block, branch_add_h_done);
|
|
patch_branch_b(block, branch_to_finish);
|
|
|
|
// Set Z flag based on A, preserve C
|
|
emit_andi_b_dn(block, REG_68K_D_FLAGS, 0x01); // keep only C
|
|
emit_tst_b_dn(block, REG_68K_D_A);
|
|
skip = block->length;
|
|
emit_bne_b(block, 0); // if not zero, skip setting Z
|
|
emit_ori_b_dn(block, REG_68K_D_FLAGS, 0x04); // set Z
|
|
patch_branch_b(block, skip);
|
|
}
|
|
|
|
// ADC core: expects operand already in D1.b
|
|
// Does A = A + D1 + carry using 16-bit arithmetic
|
|
static void compile_adc_core(struct code_block *block)
|
|
{
|
|
// Track for DAA: save old_A and set N=0 (addition)
|
|
compile_daa_track_add(block);
|
|
|
|
// Zero-extend operand: andi.w #0xff, D1
|
|
emit_andi_w_dn(block, REG_68K_D_SCRATCH_1, 0x00ff);
|
|
|
|
// Zero-extend A into D0: moveq #0, D0; move.b D4, D0
|
|
emit_moveq_dn(block, REG_68K_D_SCRATCH_0, 0);
|
|
emit_move_b_dn_dn(block, REG_68K_D_A, REG_68K_D_SCRATCH_0);
|
|
|
|
// Test old carry and conditionally add 1
|
|
emit_btst_imm_dn(block, 0, REG_68K_D_FLAGS);
|
|
size_t no_carry = block->length;
|
|
emit_beq_b(block, 0);
|
|
emit_addq_w_dn(block, REG_68K_D_SCRATCH_0, 1);
|
|
patch_branch_b(block, no_carry);
|
|
|
|
// Add operand: add.w D1, D0
|
|
emit_add_w_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_SCRATCH_0);
|
|
|
|
// Store result: move.b D0, D4
|
|
emit_move_b_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_A);
|
|
|
|
// Set flags
|
|
// Z from 8-bit result, C from bit 8 of 16-bit result
|
|
emit_tst_b_dn(block, REG_68K_D_A);
|
|
emit_move_sr_dn(block, REG_68K_D_FLAGS); // save Z
|
|
emit_andi_b_dn(block, REG_68K_D_FLAGS, 0xfe); // clear C
|
|
emit_lsr_w_imm_dn(block, 8, REG_68K_D_SCRATCH_0); // D0.b = carry out
|
|
emit_or_b_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_FLAGS);
|
|
}
|
|
|
|
// SBC core: expects operand already in D1.b
|
|
// Does A = A - D1 - carry using 16-bit arithmetic
|
|
static void compile_sbc_core(struct code_block *block)
|
|
{
|
|
// Track for DAA: save old_A and set N=1 (subtraction)
|
|
compile_daa_track_sub(block);
|
|
|
|
// Zero-extend operand: andi.w #0xff, D1
|
|
emit_andi_w_dn(block, REG_68K_D_SCRATCH_1, 0x00ff);
|
|
|
|
// Zero-extend A into D0: moveq #0, D0; move.b D4, D0
|
|
emit_moveq_dn(block, REG_68K_D_SCRATCH_0, 0);
|
|
emit_move_b_dn_dn(block, REG_68K_D_A, REG_68K_D_SCRATCH_0);
|
|
|
|
// Add old borrow to subtrahend
|
|
emit_btst_imm_dn(block, 0, REG_68K_D_FLAGS);
|
|
size_t no_borrow = block->length;
|
|
emit_beq_b(block, 0);
|
|
emit_addq_w_dn(block, REG_68K_D_SCRATCH_1, 1);
|
|
patch_branch_b(block, no_borrow);
|
|
|
|
// Subtract: D0 = A - (operand + borrow_in), sets CCR.C if borrow
|
|
emit_sub_w_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_SCRATCH_0);
|
|
|
|
// Save borrow before we clobber CCR
|
|
emit_scc(block, 0x05, REG_68K_D_SCRATCH_1); // scs D1: D1 = $ff if borrow
|
|
|
|
// Store result: move.b D0, D4
|
|
emit_move_b_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_A);
|
|
|
|
// Set flags
|
|
emit_tst_b_dn(block, REG_68K_D_A);
|
|
emit_move_sr_dn(block, REG_68K_D_FLAGS); // save Z
|
|
emit_andi_b_dn(block, REG_68K_D_FLAGS, 0xfe); // clear C
|
|
emit_andi_b_dn(block, REG_68K_D_SCRATCH_1, 0x01); // D1 = 0 or 1
|
|
emit_or_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_FLAGS);
|
|
}
|
|
|
|
int compile_alu_op(
|
|
struct code_block *block,
|
|
uint8_t op,
|
|
struct compile_ctx *ctx,
|
|
uint16_t src_address,
|
|
uint16_t *src_ptr
|
|
) {
|
|
daa_track = 1;
|
|
if (daa_tracked_op(op)) {
|
|
uint16_t next_off = *src_ptr;
|
|
if (op == 0xc6 || op == 0xce || op == 0xd6 || op == 0xde)
|
|
next_off++;
|
|
daa_track = daa_track_needed(ctx, src_address, next_off);
|
|
}
|
|
|
|
switch (op) {
|
|
// inc/dec register ops (0xn4, 0xn5, 0xnc, 0xnd where n = 0-3)
|
|
case 0x04: // inc b
|
|
emit_swap(block, REG_68K_D_BC);
|
|
emit_addq_b_dn(block, REG_68K_D_BC, 1);
|
|
compile_set_z_flag(block);
|
|
emit_swap(block, REG_68K_D_BC);
|
|
return 1;
|
|
|
|
case 0x05: // dec b
|
|
emit_swap(block, REG_68K_D_BC);
|
|
emit_subq_b_dn(block, REG_68K_D_BC, 1);
|
|
compile_set_z_flag(block);
|
|
emit_swap(block, REG_68K_D_BC);
|
|
return 1;
|
|
|
|
case 0x0c: // inc c
|
|
emit_addq_b_dn(block, REG_68K_D_BC, 1);
|
|
compile_set_z_flag(block);
|
|
return 1;
|
|
|
|
case 0x0d: // dec c
|
|
emit_subq_b_dn(block, REG_68K_D_BC, 1);
|
|
compile_set_z_flag(block);
|
|
return 1;
|
|
|
|
case 0x14: // inc d
|
|
emit_swap(block, REG_68K_D_DE);
|
|
emit_addq_b_dn(block, REG_68K_D_DE, 1);
|
|
compile_set_z_flag(block);
|
|
emit_swap(block, REG_68K_D_DE);
|
|
return 1;
|
|
|
|
case 0x15: // dec d
|
|
emit_swap(block, REG_68K_D_DE);
|
|
emit_subq_b_dn(block, REG_68K_D_DE, 1);
|
|
compile_set_z_flag(block);
|
|
emit_swap(block, REG_68K_D_DE);
|
|
return 1;
|
|
|
|
case 0x1c: // inc e
|
|
emit_addq_b_dn(block, REG_68K_D_DE, 1);
|
|
compile_set_z_flag(block);
|
|
return 1;
|
|
|
|
case 0x1d: // dec e
|
|
emit_subq_b_dn(block, REG_68K_D_DE, 1);
|
|
compile_set_z_flag(block);
|
|
return 1;
|
|
|
|
case 0x24: // inc 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_addq_b_dn(block, REG_68K_D_SCRATCH_1, 1);
|
|
compile_set_z_flag(block);
|
|
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);
|
|
return 1;
|
|
|
|
case 0x25: // dec 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_subq_b_dn(block, REG_68K_D_SCRATCH_1, 1);
|
|
compile_set_z_flag(block);
|
|
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);
|
|
return 1;
|
|
|
|
case 0x2c: // inc l
|
|
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
|
emit_addq_b_dn(block, REG_68K_D_SCRATCH_1, 1);
|
|
emit_movea_w_dn_an(block, REG_68K_D_SCRATCH_1, REG_68K_A_HL);
|
|
compile_set_z_flag(block);
|
|
return 1;
|
|
|
|
case 0x2d: // dec l
|
|
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
|
emit_subq_b_dn(block, REG_68K_D_SCRATCH_1, 1);
|
|
emit_movea_w_dn_an(block, REG_68K_D_SCRATCH_1, REG_68K_A_HL);
|
|
compile_set_z_flag(block);
|
|
return 1;
|
|
|
|
case 0x34: // inc (hl)
|
|
compile_call_dmg_read_hl(block);
|
|
emit_addq_b_dn(block, 0, 1);
|
|
compile_set_z_flag(block);
|
|
compile_call_dmg_write_hl_d0(block);
|
|
return 1;
|
|
|
|
case 0x35: // dec (hl)
|
|
compile_call_dmg_read_hl(block);
|
|
emit_subq_b_dn(block, 0, 1);
|
|
compile_set_z_flag(block);
|
|
compile_call_dmg_write_hl_d0(block);
|
|
return 1;
|
|
|
|
case 0x3c: // inc a
|
|
compile_daa_track_add(block); // Track for DAA (games use INC A + DAA)
|
|
emit_addq_b_dn(block, REG_68K_D_A, 1);
|
|
compile_set_z_flag(block);
|
|
return 1;
|
|
|
|
case 0x3d: // dec a
|
|
compile_daa_track_sub(block); // Track for DAA (games use DEC A + DAA)
|
|
emit_subq_b_dn(block, REG_68K_D_A, 1);
|
|
compile_set_z_flag(block);
|
|
return 1;
|
|
|
|
// misc ALU ops
|
|
case 0x27: // daa - decimal adjust accumulator
|
|
compile_daa(block);
|
|
return 1;
|
|
|
|
case 0x2f: // cpl - complement A
|
|
emit_not_b_dn(block, REG_68K_D_A);
|
|
return 1;
|
|
|
|
case 0x37: // scf - set carry flag
|
|
emit_ori_b_dn(block, REG_68K_D_FLAGS, 0x01);
|
|
return 1;
|
|
|
|
case 0x3f: // ccf - complement carry flag
|
|
emit_eor_b_imm_dn(block, 0x01, REG_68K_D_FLAGS);
|
|
return 1;
|
|
|
|
// 8-bit ALU register ops (0x80-0xbf)
|
|
case 0x80: // add a, b
|
|
compile_daa_track_add(block);
|
|
emit_swap(block, REG_68K_D_BC);
|
|
emit_add_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
|
compile_set_zc_flags(block); // capture before swap clobbers CCR
|
|
emit_swap(block, REG_68K_D_BC);
|
|
return 1;
|
|
|
|
case 0x81: // add a, c
|
|
compile_daa_track_add(block);
|
|
emit_add_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0x82: // add a, d
|
|
compile_daa_track_add(block);
|
|
emit_swap(block, REG_68K_D_DE);
|
|
emit_add_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_A);
|
|
compile_set_zc_flags(block); // capture before swap clobbers CCR
|
|
emit_swap(block, REG_68K_D_DE);
|
|
return 1;
|
|
|
|
case 0x83: // add a, e
|
|
compile_daa_track_add(block);
|
|
emit_add_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0x84: // add a, h
|
|
compile_daa_track_add(block);
|
|
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_add_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0x85: // add a, l
|
|
compile_daa_track_add(block);
|
|
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
|
emit_add_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0x86: // add a, (hl)
|
|
compile_daa_track_add(block);
|
|
compile_call_dmg_read_hl(block);
|
|
emit_add_b_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0x87: // add a, a
|
|
compile_daa_track_add(block);
|
|
emit_add_b_dn_dn(block, REG_68K_D_A, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
// adc a, r (0x88-0x8f)
|
|
case 0x88: // adc a, b
|
|
emit_swap(block, REG_68K_D_BC);
|
|
emit_move_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_SCRATCH_1);
|
|
emit_swap(block, REG_68K_D_BC);
|
|
compile_adc_core(block);
|
|
return 1;
|
|
|
|
case 0x89: // adc a, c
|
|
emit_move_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_SCRATCH_1);
|
|
compile_adc_core(block);
|
|
return 1;
|
|
|
|
case 0x8a: // adc a, d
|
|
emit_swap(block, REG_68K_D_DE);
|
|
emit_move_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_SCRATCH_1);
|
|
emit_swap(block, REG_68K_D_DE);
|
|
compile_adc_core(block);
|
|
return 1;
|
|
|
|
case 0x8b: // adc a, e
|
|
emit_move_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_SCRATCH_1);
|
|
compile_adc_core(block);
|
|
return 1;
|
|
|
|
case 0x8c: // adc 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);
|
|
compile_adc_core(block);
|
|
return 1;
|
|
|
|
case 0x8d: // adc a, l
|
|
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
|
compile_adc_core(block);
|
|
return 1;
|
|
|
|
case 0x8e: // adc a, (hl)
|
|
compile_call_dmg_read_hl(block);
|
|
emit_move_b_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_SCRATCH_1);
|
|
compile_adc_core(block);
|
|
return 1;
|
|
|
|
case 0x8f: // adc a, a
|
|
emit_move_b_dn_dn(block, REG_68K_D_A, REG_68K_D_SCRATCH_1);
|
|
compile_adc_core(block);
|
|
return 1;
|
|
|
|
case 0x90: // sub a, b
|
|
compile_daa_track_sub(block);
|
|
emit_swap(block, REG_68K_D_BC);
|
|
emit_sub_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
|
compile_set_zc_flags(block); // capture before swap clobbers CCR
|
|
emit_swap(block, REG_68K_D_BC);
|
|
return 1;
|
|
|
|
case 0x91: // sub a, c
|
|
compile_daa_track_sub(block);
|
|
emit_sub_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0x92: // sub a, d
|
|
compile_daa_track_sub(block);
|
|
emit_swap(block, REG_68K_D_DE);
|
|
emit_sub_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_A);
|
|
compile_set_zc_flags(block); // capture before swap clobbers CCR
|
|
emit_swap(block, REG_68K_D_DE);
|
|
return 1;
|
|
|
|
case 0x93: // sub a, e
|
|
compile_daa_track_sub(block);
|
|
emit_sub_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0x94: // sub a, h
|
|
compile_daa_track_sub(block);
|
|
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_sub_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0x95: // sub a, l
|
|
compile_daa_track_sub(block);
|
|
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
|
emit_sub_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0x96: // sub a, (hl)
|
|
compile_daa_track_sub(block);
|
|
compile_call_dmg_read_hl(block);
|
|
emit_sub_b_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0x97: // sub a, a - always results in 0
|
|
compile_daa_track_sub(block);
|
|
emit_moveq_dn(block, REG_68K_D_A, 0);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
// sbc a, r (0x98-0x9f)
|
|
case 0x98: // sbc a, b
|
|
emit_swap(block, REG_68K_D_BC);
|
|
emit_move_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_SCRATCH_1);
|
|
emit_swap(block, REG_68K_D_BC);
|
|
compile_sbc_core(block);
|
|
return 1;
|
|
|
|
case 0x99: // sbc a, c
|
|
emit_move_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_SCRATCH_1);
|
|
compile_sbc_core(block);
|
|
return 1;
|
|
|
|
case 0x9a: // sbc a, d
|
|
emit_swap(block, REG_68K_D_DE);
|
|
emit_move_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_SCRATCH_1);
|
|
emit_swap(block, REG_68K_D_DE);
|
|
compile_sbc_core(block);
|
|
return 1;
|
|
|
|
case 0x9b: // sbc a, e
|
|
emit_move_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_SCRATCH_1);
|
|
compile_sbc_core(block);
|
|
return 1;
|
|
|
|
case 0x9c: // sbc 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);
|
|
compile_sbc_core(block);
|
|
return 1;
|
|
|
|
case 0x9d: // sbc a, l
|
|
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
|
compile_sbc_core(block);
|
|
return 1;
|
|
|
|
case 0x9e: // sbc a, (hl)
|
|
compile_call_dmg_read_hl(block);
|
|
emit_move_b_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_SCRATCH_1);
|
|
compile_sbc_core(block);
|
|
return 1;
|
|
|
|
case 0x9f: // sbc a, a
|
|
emit_move_b_dn_dn(block, REG_68K_D_A, REG_68K_D_SCRATCH_1);
|
|
compile_sbc_core(block);
|
|
return 1;
|
|
|
|
case 0xa0: // and a, b
|
|
emit_swap(block, REG_68K_D_BC);
|
|
emit_and_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
emit_swap(block, REG_68K_D_BC);
|
|
return 1;
|
|
|
|
case 0xa1: // and a, c
|
|
emit_and_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xa2: // and a, d
|
|
emit_swap(block, REG_68K_D_DE);
|
|
emit_and_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
emit_swap(block, REG_68K_D_DE);
|
|
return 1;
|
|
|
|
case 0xa3: // and a, e
|
|
emit_and_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xa4: // and 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_and_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xa5: // and a, l
|
|
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
|
emit_and_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xa6: // and a, (hl)
|
|
compile_call_dmg_read_hl(block);
|
|
emit_and_b_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xa7: // and a, a - set flags based on A
|
|
emit_tst_b_dn(block, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xa8: // xor a, b
|
|
emit_swap(block, REG_68K_D_BC);
|
|
emit_eor_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
emit_swap(block, REG_68K_D_BC);
|
|
return 1;
|
|
|
|
case 0xa9: // xor a, c
|
|
emit_eor_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xaa: // xor a, d
|
|
emit_swap(block, REG_68K_D_DE);
|
|
emit_eor_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
emit_swap(block, REG_68K_D_DE);
|
|
return 1;
|
|
|
|
case 0xab: // xor a, e
|
|
emit_eor_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xac: // xor 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_eor_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xad: // xor a, l
|
|
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
|
emit_eor_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xae: // xor a, (hl)
|
|
compile_call_dmg_read_hl(block);
|
|
emit_eor_b_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xaf: // xor a, a - always results in 0, Z=1
|
|
emit_moveq_dn(block, REG_68K_D_A, 0);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xb0: // or a, b
|
|
emit_swap(block, REG_68K_D_BC);
|
|
emit_or_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
emit_swap(block, REG_68K_D_BC);
|
|
return 1;
|
|
|
|
case 0xb1: // or a, c
|
|
emit_or_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xb2: // or a, d
|
|
emit_swap(block, REG_68K_D_DE);
|
|
emit_or_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
emit_swap(block, REG_68K_D_DE);
|
|
return 1;
|
|
|
|
case 0xb3: // or a, e
|
|
emit_or_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xb4: // or 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_or_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xb5: // or a, l
|
|
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
|
emit_or_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xb6: // or a, (hl)
|
|
compile_call_dmg_read_hl(block);
|
|
emit_or_b_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xb7: // or a, a - set flags based on A
|
|
emit_tst_b_dn(block, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xb8: // cp a, b
|
|
// cannot fuse branch because it needs to swap back BC :/
|
|
emit_swap(block, REG_68K_D_BC);
|
|
emit_cmp_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
emit_swap(block, REG_68K_D_BC);
|
|
return 1;
|
|
|
|
case 0xb9: // cp a, c
|
|
emit_cmp_b_dn_dn(block, REG_68K_D_BC, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xba: // cp a, d
|
|
// can't fuse
|
|
emit_swap(block, REG_68K_D_DE);
|
|
emit_cmp_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
emit_swap(block, REG_68K_D_DE);
|
|
return 1;
|
|
|
|
case 0xbb: // cp a, e
|
|
emit_cmp_b_dn_dn(block, REG_68K_D_DE, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xbc: // cp 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_cmp_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xbd: // cp a, l
|
|
emit_move_w_an_dn(block, REG_68K_A_HL, REG_68K_D_SCRATCH_1);
|
|
emit_cmp_b_dn_dn(block, REG_68K_D_SCRATCH_1, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xbe: // cp a, (hl)
|
|
compile_call_dmg_read_hl(block);
|
|
emit_cmp_b_dn_dn(block, REG_68K_D_SCRATCH_0, REG_68K_D_A);
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xbf: // cp a, a - always Z=1, N=1, H=0, C=0
|
|
emit_moveq_dn(block, REG_68K_D_FLAGS, 0x04);
|
|
return 1;
|
|
|
|
// immediate ALU ops (0xc6, 0xce, 0xd6, 0xde, 0xe6, 0xee, 0xf6, 0xfe)
|
|
case 0xc6: // add a, #imm
|
|
compile_daa_track_add(block);
|
|
emit_addi_b_dn(block, REG_68K_D_A, READ_BYTE(*src_ptr));
|
|
(*src_ptr)++;
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xce: // adc a, #imm
|
|
emit_move_b_dn(block, REG_68K_D_SCRATCH_1, READ_BYTE(*src_ptr));
|
|
(*src_ptr)++;
|
|
compile_adc_core(block);
|
|
return 1;
|
|
|
|
case 0xd6: // sub a, #imm
|
|
compile_daa_track_sub(block);
|
|
emit_subi_b_dn(block, REG_68K_D_A, READ_BYTE(*src_ptr));
|
|
(*src_ptr)++;
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xde: // sbc a, #imm
|
|
emit_move_b_dn(block, REG_68K_D_SCRATCH_1, READ_BYTE(*src_ptr));
|
|
(*src_ptr)++;
|
|
compile_sbc_core(block);
|
|
return 1;
|
|
|
|
case 0xe6: // and a, #nn
|
|
emit_andi_b_dn(block, REG_68K_D_A, READ_BYTE(*src_ptr));
|
|
(*src_ptr)++;
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xee: // xor a, u8
|
|
emit_eor_b_imm_dn(block, READ_BYTE(*src_ptr), REG_68K_D_A);
|
|
(*src_ptr)++;
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xf6: // or a, #imm
|
|
emit_ori_b_dn(block, REG_68K_D_A, READ_BYTE(*src_ptr));
|
|
(*src_ptr)++;
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
case 0xfe: // cp a, imm8
|
|
emit_cmp_b_imm_dn(block, REG_68K_D_A, READ_BYTE(*src_ptr));
|
|
(*src_ptr)++;
|
|
compile_set_zc_flags(block);
|
|
return 1;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|