Skip to content

Commit 05e6120

Browse files
committed
tests: add baseline DMA test suite
DMA had zero unit-test coverage despite being critical for every game (compressed asset transfer, FIFO refill, scanline copies). Two starter tests pin the most basic contracts: - dma3_immediate_word_copy: writing SAD/DAD/count/control with the enable bit set on a timing=0 (immediate) transfer copies words through the bus dispatch. - dma_disabled_does_not_transfer: writing the same registers with enable=0 leaves the destination untouched. Room to grow this — VBlank/HBlank timing, FIFO triggers, repeat behavior, address adjustment modes — but having a suite at all prevents the next refactor from silently breaking everything.
1 parent b2761fb commit 05e6120

3 files changed

Lines changed: 80 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ set(TEST_SOURCES
179179
tests/test_savestate.c
180180
tests/test_bus.c
181181
tests/test_cpu.c
182+
tests/test_dma.c
182183
tests/test_rewind.c
183184
tests/test_screenshot.c
184185
tests/test_trace.c

tests/test_dma.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#include "test_harness.h"
2+
#include "gba.h"
3+
4+
/* DMA tests use a fully wired GBA so the controller can talk to the
5+
* real bus. The cartridge isn't loaded, so we keep all transfers
6+
* inside RAM regions. */
7+
static GBA* make_gba(void) {
8+
GBA* gba = calloc(1, sizeof(GBA));
9+
gba_init(gba);
10+
return gba;
11+
}
12+
13+
/* Enable DMA channel `ch` with control bits assembled as:
14+
* bit 15 = enable (1)
15+
* bit 10 = transfer_32 (1=32-bit, 0=16-bit)
16+
* bits 12-13 = timing (0=immediate, 1=VBlank, 2=HBlank, 3=special)
17+
* Other bits left at 0 (increment-on-both, no IRQ, no repeat). */
18+
static uint16_t dma_control(bool transfer_32, uint8_t timing) {
19+
uint16_t v = 0x8000; /* enable */
20+
if (transfer_32) v |= (1u << 10);
21+
v |= (uint16_t)(timing & 3) << 12;
22+
return v;
23+
}
24+
25+
TEST(dma3_immediate_word_copy) {
26+
/* Use channel 3 — the general-purpose DMA. Set up an immediate
27+
* 4-word copy from one IWRAM range to another, and verify the
28+
* destination matches after the rising-edge enable. */
29+
GBA* gba = make_gba();
30+
Bus* bus = &gba->bus;
31+
32+
/* Seed source data in IWRAM at 0x03000000. */
33+
for (int i = 0; i < 16; i++) {
34+
bus->iwram[i] = (uint8_t)(0x10 + i);
35+
}
36+
/* Clear the destination range. */
37+
memset(&bus->iwram[0x100], 0, 16);
38+
39+
/* DMA3 SAD = 0x03000000, DAD = 0x03000100, CNT_L = 4 words. */
40+
bus_write32(bus, 0x040000D4, 0x03000000); /* SAD */
41+
bus_write32(bus, 0x040000D8, 0x03000100); /* DAD */
42+
bus_write16(bus, 0x040000DC, 4); /* count */
43+
/* Writing CNT_H (0xDE) with enable=1 + immediate timing kicks the
44+
* transfer. dma_write_control runs synchronously when timing=0. */
45+
bus_write16(bus, 0x040000DE, dma_control(true, 0));
46+
47+
/* Verify the destination matches the source byte-for-byte. */
48+
for (int i = 0; i < 16; i++) {
49+
ASSERT_EQ(bus->iwram[0x100 + i], bus->iwram[i]);
50+
}
51+
free(gba);
52+
}
53+
54+
TEST(dma_disabled_does_not_transfer) {
55+
/* If the enable bit is clear, no transfer happens even with a
56+
* valid SAD/DAD/count. */
57+
GBA* gba = make_gba();
58+
Bus* bus = &gba->bus;
59+
60+
bus->iwram[0] = 0xAB;
61+
bus->iwram[0x100] = 0;
62+
63+
bus_write32(bus, 0x040000D4, 0x03000000);
64+
bus_write32(bus, 0x040000D8, 0x03000100);
65+
bus_write16(bus, 0x040000DC, 1);
66+
/* Control with enable=0. */
67+
bus_write16(bus, 0x040000DE, 0);
68+
69+
ASSERT_EQ(bus->iwram[0x100], 0);
70+
free(gba);
71+
}
72+
73+
void run_dma_tests(void) {
74+
TEST_SUITE("dma");
75+
RUN_TEST(dma3_immediate_word_copy);
76+
RUN_TEST(dma_disabled_does_not_transfer);
77+
}

tests/test_runner.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ int _test_failed_flag = 0;
99
void run_savestate_tests(void);
1010
void run_bus_tests(void);
1111
void run_cpu_tests(void);
12+
void run_dma_tests(void);
1213
void run_rewind_tests(void);
1314
void run_screenshot_tests(void);
1415
void run_trace_tests(void);
@@ -32,6 +33,7 @@ int main(void) {
3233
RUN_SUITE(run_savestate_tests);
3334
RUN_SUITE(run_bus_tests);
3435
RUN_SUITE(run_cpu_tests);
36+
RUN_SUITE(run_dma_tests);
3537
RUN_SUITE(run_rewind_tests);
3638
RUN_SUITE(run_screenshot_tests);
3739
RUN_SUITE(run_trace_tests);

0 commit comments

Comments
 (0)