// Standalone unit tests for the VT1200 board register model. // g++ -std=c++17 -o /tmp/t vt1200_board.cpp test_vt1200_board.cpp && /tmp/t #include "vt1200_board.h" #include #include static int failures = 0; #define CHECK(cond) do { if (!(cond)) { \ std::printf("FAIL %s:%d %s\n", __FILE__, __LINE__, #cond); failures++; } } while (0) static void test_default_read(void) { vt1200_board b; b.set_default(0x0000); CHECK(b.read(VT1200_WIN2 + VT1200_OFF_STATUS) == 0x0000); } static void test_override_read(void) { vt1200_board b; b.set_override(VT1200_WIN2 + VT1200_OFF_STATUS, 0x00b7); CHECK(b.read(VT1200_WIN2 + VT1200_OFF_STATUS) == 0x00b7); } static void test_monitor_switch_is_mixed_with_latches(void) { vt1200_board b; b.set_monitor_switch(7); // position 7 -> bits 4-6 = 0x70 b.write(VT1200_WIN3 + VT1200_OFF_SWITCH, 0x0001); // output latch writes bit 0 const uint16_t v = b.read(VT1200_WIN3 + VT1200_OFF_SWITCH); CHECK((v & 0x0070) == 0x0070); // switch bits read back as input CHECK((v & 0x0001) == 0x0001); // output bits read back as latched } static void test_busy_bit_arms_on_write_and_clears(void) { vt1200_board b; b.set_model_handshake(true); b.set_busy_ticks(2); const uint32_t status = VT1200_WIN2 + VT1200_OFF_STATUS; const uint32_t cmd = VT1200_WIN2 + VT1200_OFF_CMD; CHECK((b.read(status) & 0x0001) == 0x0000); // idle b.write(cmd, 0x00ca); // command arms it CHECK((b.read(status) & 0x0001) == 0x0001); // busy b.tick(); CHECK((b.read(status) & 0x0001) == 0x0001); // still busy b.tick(); CHECK((b.read(status) & 0x0001) == 0x0000); // done } static void test_windows_are_independent(void) { vt1200_board b; b.set_model_handshake(true); b.set_window_busy_ticks(VT1200_WIN2, 1); b.set_window_busy_ticks(VT1200_WIN3, 3); b.write(VT1200_WIN2 + VT1200_OFF_CMD, 0x0001); b.write(VT1200_WIN3 + VT1200_OFF_CMD, 0x0001); CHECK((b.read(VT1200_WIN2 + VT1200_OFF_STATUS) & 1) == 1); CHECK((b.read(VT1200_WIN3 + VT1200_OFF_STATUS) & 1) == 1); b.tick(); CHECK((b.read(VT1200_WIN2 + VT1200_OFF_STATUS) & 1) == 0); // WIN2 finished CHECK((b.read(VT1200_WIN3 + VT1200_OFF_STATUS) & 1) == 1); // WIN3 still busy } static void test_monitor_switch_accepts_raw_bits_or_position(void) { vt1200_board raw; raw.set_monitor_switch(0x70); // raw register bits raw.write(VT1200_WIN3 + VT1200_OFF_SWITCH, 0x0001); CHECK((raw.read(VT1200_WIN3 + VT1200_OFF_SWITCH) & 0x0070) == 0x0070); vt1200_board pos; pos.set_monitor_switch(7); // position 7 pos.write(VT1200_WIN3 + VT1200_OFF_SWITCH, 0x0001); CHECK((pos.read(VT1200_WIN3 + VT1200_OFF_SWITCH) & 0x0070) == 0x0070); } static void test_switch_mix_only_when_configured(void) { vt1200_board b; // no set_monitor_switch call b.write(VT1200_WIN3 + VT1200_OFF_SWITCH, 0x0070); CHECK(b.read(VT1200_WIN3 + VT1200_OFF_SWITCH) == 0x0070); // returned verbatim } static void test_dma_pair_command_completes(void) { vt1200_board b; b.set_model_handshake(true); b.set_dma_ticks(2); b.write(VT1200_WIN0, 0x0000); // data port first (as the firmware does) b.write(VT1200_WIN1, 0x0001); // then a command CHECK(b.read(VT1200_WIN1) == 0x0001); // accepted b.tick(); b.tick(); CHECK(b.read(VT1200_WIN1) == 0x0000); // completed } // The two timers the sweep tunes (window busy and DMA completion) must advance // on the same tick(), or a later slower timer would silently stretch the other. static void test_one_tick_advances_window_and_dma_timers(void) { vt1200_board b; b.set_model_handshake(true); b.set_window_busy_ticks(VT1200_WIN2, 2); b.set_dma_ticks(2); b.write(VT1200_WIN2 + VT1200_OFF_CMD, 0x0001); // arms WIN2's busy bit b.write(VT1200_WIN0, 0x0000); // data port first b.write(VT1200_WIN1, 0x0042); // then a DMA command b.tick(); // one frame: both advance CHECK((b.read(VT1200_WIN2 + VT1200_OFF_STATUS) & 1) == 1); CHECK(b.read(VT1200_WIN1) == 0x0042); b.tick(); CHECK((b.read(VT1200_WIN2 + VT1200_OFF_STATUS) & 1) == 0); CHECK(b.read(VT1200_WIN1) == 0x0000); } int main(void) { test_default_read(); test_override_read(); test_monitor_switch_is_mixed_with_latches(); test_busy_bit_arms_on_write_and_clears(); test_windows_are_independent(); test_monitor_switch_accepts_raw_bits_or_position(); test_switch_mix_only_when_configured(); test_dma_pair_command_completes(); test_one_tick_advances_window_and_dma_timers(); std::printf(failures ? "%d FAILURE(S)\n" : "all board tests passed\n", failures); return failures ? 1 : 0; }