Files
gb6/compiler/tests/test_exec_loads.c
2026-01-22 01:42:55 -06:00

337 lines
11 KiB
C

#include "tests.h"
// 8-bit immediate loads
TEST_EXEC(test_exec_ld_a_imm8, REG_A, 0x55, 0x3e, 0x55, 0x10)
TEST_EXEC(test_exec_ld_a_zero, REG_A, 0x00, 0x3e, 0x00, 0x10)
TEST_EXEC(test_exec_ld_a_ff, REG_A, 0xff, 0x3e, 0xff, 0x10)
TEST_EXEC(test_exec_multiple_ld_a, REG_A, 0x33, 0x3e, 0x11, 0x3e, 0x22, 0x3e, 0x33, 0x10)
TEST_EXEC(test_exec_ld_b_imm8, REG_BC, 0x00110000, 0x06, 0x11, 0x10)
TEST_EXEC(test_exec_ld_c_imm8, REG_BC, 0x00000022, 0x0e, 0x22, 0x10)
TEST_EXEC(test_exec_ld_d_imm8, REG_DE, 0x00330000, 0x16, 0x33, 0x10)
TEST_EXEC(test_exec_ld_e_imm8, REG_DE, 0x00000044, 0x1e, 0x44, 0x10)
TEST_EXEC(test_exec_ld_h_imm8, REG_HL, 0x5500, 0x26, 0x55, 0x10)
TEST_EXEC(test_exec_ld_l_imm8, REG_HL, 0x0066, 0x2e, 0x66, 0x10)
// 16-bit immediate loads
TEST_EXEC(test_exec_ld_bc_imm16, REG_BC, 0x00110022, 0x01, 0x22, 0x11, 0x10)
TEST_EXEC(test_exec_ld_de_imm16, REG_DE, 0x00330044, 0x11, 0x44, 0x33, 0x10)
TEST_EXEC(test_exec_ld_hl_imm16, REG_HL, 0x5566, 0x21, 0x66, 0x55, 0x10)
TEST_EXEC(test_exec_ld_sp_imm16, REG_SP, 0x7788, 0x31, 0x88, 0x77, 0x10)
// Register-to-register loads
TEST_EXEC(test_ld_a_b, REG_A, 0x05, 0x06, 0x05, 0x78, 0x10)
TEST_EXEC(test_ld_b_a, REG_BC, 0x00110000, 0x3e, 0x11, 0x47, 0x10)
TEST_EXEC(test_ld_a_c, REG_A, 0x05, 0x0e, 0x05, 0x79, 0x10)
TEST_EXEC(test_ld_c_a, REG_BC, 0x00000011, 0x3e, 0x11, 0x4f, 0x10)
TEST_EXEC(test_ld_d_a, REG_DE, 0x00ab0000, 0x3e, 0xab, 0x57, 0x10)
TEST_EXEC(test_ld_e_a, REG_DE, 0x000000cd, 0x1e, 0xab, 0x3e, 0xcd, 0x5f, 0x10)
// ld b, $12; ld hl, $0000; ld h, b -> HL = 0x1200
TEST_EXEC(test_ld_h_b, REG_HL, 0x1200, 0x06, 0x12, 0x21, 0x00, 0x00, 0x60, 0x10)
// ld c, $34; ld hl, $0000; ld l, c -> HL = 0x0034
TEST_EXEC(test_ld_l_c, REG_HL, 0x0034, 0x0e, 0x34, 0x21, 0x00, 0x00, 0x69, 0x10)
// Memory indirect via BC
TEST(test_exec_ld_bc_ind_a)
{
// ld (bc), a - write A to memory address BC
uint8_t rom[] = {
0x01, 0x00, 0x50, // 0x0000: ld bc, 0x5000
0x3e, 0x42, // 0x0003: ld a, 0x42
0x02, // 0x0005: ld (bc), a
0x10 // 0x0006: stop
};
run_program(rom, 0);
ASSERT_EQ(get_mem_byte(0x5000), 0x42);
}
TEST(test_exec_ld_a_bc_ind)
{
// ld a, (bc) - read byte from memory address BC into A
uint8_t rom[] = {
0x01, 0x00, 0x50, // 0x0000: ld bc, 0x5000
0x3e, 0x42, // 0x0003: ld a, 0x42
0x02, // 0x0005: ld (bc), a ; write 0x42 to memory
0x3e, 0x00, // 0x0006: ld a, 0x00 ; clear A
0x0a, // 0x0008: ld a, (bc) ; read back from memory
0x10 // 0x0009: stop
};
run_program(rom, 0);
ASSERT_EQ(get_dreg(REG_68K_D_A) & 0xff, 0x42);
}
// Memory indirect via DE
TEST(test_exec_ld_de_ind_a)
{
// ld (de), a - write A to memory address DE
uint8_t rom[] = {
0x11, 0x00, 0x50, // 0x0000: ld de, 0x5000
0x3e, 0x42, // 0x0003: ld a, 0x42
0x12, // 0x0005: ld (de), a
0x10 // 0x0006: stop
};
run_program(rom, 0);
ASSERT_EQ(get_mem_byte(0x5000), 0x42);
}
TEST(test_exec_ld_a_de_ind)
{
// ld a, (de) - read byte from memory address DE into A
uint8_t rom[] = {
0x11, 0x00, 0x50, // 0x0000: ld de, 0x5000
0x3e, 0x42, // 0x0003: ld a, 0x42
0x12, // 0x0005: ld (de), a ; write 0x42 to memory
0x3e, 0x00, // 0x0006: ld a, 0x00 ; clear A
0x1a, // 0x0008: ld a, (de) ; read back from memory
0x10 // 0x0009: stop
};
run_program(rom, 0);
ASSERT_EQ(get_dreg(REG_68K_D_A) & 0xff, 0x42);
}
// Memory indirect via HL with post-increment/decrement
TEST(test_exec_ld_hld_a)
{
// ld (hl-), a - write A to memory address HL, then decrement HL
uint8_t rom[] = {
0x21, 0x02, 0x50, // 0x0000: ld hl, 0x5002
0x3e, 0xaa, // 0x0003: ld a, 0xaa
0x32, // 0x0005: ld (hl-), a ; write 0xaa to 0x5002, HL becomes 0x5001
0x3e, 0xbb, // 0x0006: ld a, 0xbb
0x32, // 0x0008: ld (hl-), a ; write 0xbb to 0x5001, HL becomes 0x5000
0x10 // 0x0009: stop
};
run_program(rom, 0);
ASSERT_EQ(get_mem_byte(0x5002), 0xaa);
ASSERT_EQ(get_mem_byte(0x5001), 0xbb);
ASSERT_EQ(get_areg(REG_68K_A_HL), 0x5000);
}
TEST(test_exec_ld_a_hli)
{
// ld a, (hl+) - write memory address HL to A, then increment HL
uint8_t rom[] = {
0x21, 0x00, 0x50, // 0x0000: ld hl, 0x5000
0x36, 0x11, // 0x0003: ld (hl), 0x11
0x2a, // 0x0005: ld a, (hl+)
0x10 // 0x0006: stop
};
run_program(rom, 0);
ASSERT_EQ(get_dreg(REG_68K_D_A), 0x11);
ASSERT_EQ(get_areg(REG_68K_A_HL), 0x5001);
}
// Load from (HL) to register
TEST(test_ld_d_hl_ind)
{
// ld d, (hl) - load byte at (HL) into D
uint8_t rom[] = {
0x21, 0x00, 0x50, // 0x0000: ld hl, 0x5000
0x36, 0xab, // 0x0003: ld (hl), 0xab
0x56, // 0x0005: ld d, (hl)
0x10 // 0x0006: stop
};
run_program(rom, 0);
ASSERT_EQ((get_dreg(REG_68K_D_DE) >> 16) & 0xff, 0xab);
}
TEST(test_ld_e_hl_ind)
{
// ld e, (hl) - load byte at (HL) into E
uint8_t rom[] = {
0x21, 0x00, 0x50, // 0x0000: ld hl, 0x5000
0x36, 0xcd, // 0x0003: ld (hl), 0xcd
0x5e, // 0x0005: ld e, (hl)
0x10 // 0x0006: stop
};
run_program(rom, 0);
ASSERT_EQ(get_dreg(REG_68K_D_DE) & 0xff, 0xcd);
}
TEST(test_ld_b_hl_ind)
{
// ld b, (hl) - load byte at (HL) into B
uint8_t rom[] = {
0x21, 0x00, 0x50, // 0x0000: ld hl, 0x5000
0x36, 0x12, // 0x0003: ld (hl), 0x12
0x46, // 0x0005: ld b, (hl)
0x10 // 0x0006: stop
};
run_program(rom, 0);
ASSERT_EQ((get_dreg(REG_68K_D_BC) >> 16) & 0xff, 0x12);
}
TEST(test_ld_c_hl_ind)
{
// ld c, (hl) - load byte at (HL) into C
uint8_t rom[] = {
0x21, 0x00, 0x50, // 0x0000: ld hl, 0x5000
0x36, 0x34, // 0x0003: ld (hl), 0x34
0x4e, // 0x0005: ld c, (hl)
0x10 // 0x0006: stop
};
run_program(rom, 0);
ASSERT_EQ(get_dreg(REG_68K_D_BC) & 0xff, 0x34);
}
// ld (hl), imm8
TEST(test_exec_ld_hl_ind_imm8)
{
// ld (hl), u8 - write immediate byte to memory at HL
uint8_t rom[] = {
0x21, 0x00, 0x50, // 0x0000: ld hl, 0x5000
0x36, 0x42, // 0x0003: ld (hl), 0x42
0x21, 0x01, 0x50, // 0x0005: ld hl, 0x5001
0x36, 0x99, // 0x0008: ld (hl), 0x99
0x10 // 0x000a: stop
};
run_program(rom, 0);
ASSERT_EQ(get_mem_byte(0x5000), 0x42);
ASSERT_EQ(get_mem_byte(0x5001), 0x99);
}
// LDH instructions
TEST(test_exec_ldh_imm8_a)
{
// ld ($ff00 + u8), a - write A to high memory
uint8_t rom[] = {
0x3e, 0x42, // 0x0000: ld a, 0x42
0xe0, 0x80, // 0x0002: ld ($ff80), a
0x3e, 0x99, // 0x0004: ld a, 0x99
0xe0, 0x81, // 0x0006: ld ($ff81), a
0x10 // 0x0008: stop
};
run_program(rom, 0);
ASSERT_EQ(get_mem_byte(0x4000), 0x42);
ASSERT_EQ(get_mem_byte(0x4001), 0x99);
}
TEST(test_exec_ldh_c_a)
{
// ld ($ff00 + c), a - write A to $ff00 + C
uint8_t rom[] = {
0x0e, 0x42, // 0x0000: ld c, 0x42
0x3e, 0xaa, // 0x0002: ld a, 0xaa
0xe2, // 0x0004: ld ($ff00 + c), a
0x0e, 0x43, // 0x0005: ld c, 0x43
0x3e, 0xbb, // 0x0007: ld a, 0xbb
0xe2, // 0x0009: ld ($ff00 + c), a
0x10 // 0x000a: stop
};
run_program(rom, 0);
ASSERT_EQ(get_mem_byte(0xff42), 0xaa);
ASSERT_EQ(get_mem_byte(0xff43), 0xbb);
}
TEST(test_exec_ldh_a_imm8)
{
// ld a, ($ff00 + u8) - read from high memory into A
uint8_t rom[] = {
0x3e, 0xab, // 0x0000: ld a, 0xab
0xe0, 0x90, // 0x0002: ld ($ff90), a ; write 0xab to $ff90
0x3e, 0x00, // 0x0004: ld a, 0x00 ; clear A
0xf0, 0x90, // 0x0006: ld a, ($ff90) ; read back from $ff90
0x10 // 0x0008: stop
};
run_program(rom, 0);
ASSERT_EQ(get_dreg(REG_68K_D_A) & 0xff, 0xab);
}
// LDH counter patterns
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);
ASSERT_EQ(get_mem_byte(0xff90), 0x00);
}
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(0x4011), 0x04);
}
void register_load_tests(void)
{
printf("\n8-bit immediate loads:\n");
RUN_TEST(test_exec_ld_a_imm8);
RUN_TEST(test_exec_ld_a_zero);
RUN_TEST(test_exec_ld_a_ff);
RUN_TEST(test_exec_multiple_ld_a);
RUN_TEST(test_exec_ld_b_imm8);
RUN_TEST(test_exec_ld_c_imm8);
RUN_TEST(test_exec_ld_d_imm8);
RUN_TEST(test_exec_ld_e_imm8);
RUN_TEST(test_exec_ld_h_imm8);
RUN_TEST(test_exec_ld_l_imm8);
printf("\n16-bit immediate loads:\n");
RUN_TEST(test_exec_ld_bc_imm16);
RUN_TEST(test_exec_ld_de_imm16);
RUN_TEST(test_exec_ld_hl_imm16);
RUN_TEST(test_exec_ld_sp_imm16);
printf("\nRegister-to-register loads:\n");
RUN_TEST(test_ld_a_b);
RUN_TEST(test_ld_b_a);
RUN_TEST(test_ld_a_c);
RUN_TEST(test_ld_c_a);
RUN_TEST(test_ld_d_a);
RUN_TEST(test_ld_e_a);
RUN_TEST(test_ld_h_b);
RUN_TEST(test_ld_l_c);
printf("\nMemory indirect (BC/DE):\n");
RUN_TEST(test_exec_ld_bc_ind_a);
RUN_TEST(test_exec_ld_a_bc_ind);
RUN_TEST(test_exec_ld_de_ind_a);
RUN_TEST(test_exec_ld_a_de_ind);
printf("\nMemory indirect (HL):\n");
RUN_TEST(test_exec_ld_hld_a);
RUN_TEST(test_exec_ld_a_hli);
RUN_TEST(test_exec_ld_hl_ind_imm8);
printf("\nLoad from (HL) to register:\n");
RUN_TEST(test_ld_d_hl_ind);
RUN_TEST(test_ld_e_hl_ind);
RUN_TEST(test_ld_b_hl_ind);
RUN_TEST(test_ld_c_hl_ind);
printf("\nLDH instructions:\n");
RUN_TEST(test_exec_ldh_imm8_a);
RUN_TEST(test_exec_ldh_c_a);
RUN_TEST(test_exec_ldh_a_imm8);
printf("\nLDH counter patterns:\n");
RUN_TEST(test_ldh_dec_ldh_loop);
RUN_TEST(test_ldh_dec_preserves_value);
}