mirror of
https://github.com/mlaux/gb6.git
synced 2026-03-11 19:41:43 +00:00
337 lines
11 KiB
C
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);
|
|
}
|