OXYGENHUB
◆ rrdc-perf enhanced-x16-emulator / tests / test_debugon_contract.c
5.6 KiB · C · last changed chore(): initial commit 25 days ago source blame history raw
9120e874 Andy McCall 1
// Regression: the debugOn contract.
2
//
3
// real_read6502() carries a debugOn flag down to every device behind the I/O
4
// page. The rule it encodes, nowhere written down and nowhere enforced, has two
5
// halves:
6
//
7
//   * a debug read reports what is there and disturbs nothing -- no device
8
//     state moved, no cycles spent;
9
//   * a real read does both, because that is what the hardware does.
10
//
11
// It matters because the debugger reads memory constantly: a memory view
12
// refreshing over the I/O page would otherwise step every device it touches.
13
// That is not hypothetical -- a debug read of VIA port A was advancing the I2C
14
// bus until recently, which on this machine talks to the SMC and the RTC.
15
//
16
// Devices here are recording fakes (tests/support/fake_devices.c), so these
17
// assertions are about how memory.c routes and guards, not about how any real
18
// device behaves. memory.c, debug_core.c and the CPU core are the real thing.
19
20
#include "support/fake_devices.h"
21
#include "support/harness.h"
22
23
#include "memory.h"
24
#include "cpu/fake6502.h"
25
26
#include <stdio.h>
27
28
extern struct regs regs;
29
extern uint32_t clockticks6502;
30
31
// Addresses that reach a device, one per fake we can address by name.
32
#define ADDR_VIA1  0x9F01
33
#define ADDR_VIA2  0x9F11
34
#define ADDR_VERA  0x9F23
35
#define ADDR_MIDI  0x9F61
36
37
static uint8_t
38
debug_read(uint16_t addr)
39
{
40
	return debug_read6502(addr, 0, USE_CURRENT_X16_BANK);
41
}
42
43
// ---- A debug read reaches the right device, and says it is a debug read ----
44
45
static void
46
test_debug_read_routing(void)
47
{
48
	const struct {
49
		uint16_t   addr;
50
		fake_dev_t dev;
51
		uint8_t    reg;
52
	} cases[] = {
53
		{ ADDR_VIA1, FAKE_VIA1, 0x1 },
54
		{ ADDR_VIA2, FAKE_VIA2, 0x1 },
55
		{ ADDR_VERA, FAKE_VERA, 0x3 },
56
	};
57
58
	for (size_t i = 0; i < sizeof cases / sizeof cases[0]; i++) {
59
		char label[96];
60
		fake_devices_reset();
61
		debug_read(cases[i].addr);
62
63
		const fake_dev_log_t *d = &fake_dev[cases[i].dev];
64
		snprintf(label, sizeof label, "$%04X reaches %s",
65
		         cases[i].addr, fake_dev_name(cases[i].dev));
66
		check_eq(d->reads, 1, label);
67
68
		snprintf(label, sizeof label, "$%04X arrives at %s as a debug read",
69
		         cases[i].addr, fake_dev_name(cases[i].dev));
70
		check_eq(d->debug_reads, 1, label);
71
72
		snprintf(label, sizeof label, "$%04X selects register %X",
73
		         cases[i].addr, cases[i].reg);
74
		check_eq(d->last_reg, cases[i].reg, label);
75
	}
76
}
77
78
// ---- A real read is not flagged as a debug read ----------------------------
79
80
static void
81
test_real_read_is_not_flagged(void)
82
{
83
	fake_devices_reset();
84
	read6502(ADDR_VERA, 0);
85
	check_eq(fake_dev[FAKE_VERA].reads, 1, "a real read reaches the device");
86
	check_eq(fake_dev[FAKE_VERA].debug_reads, 0, "and is not flagged as a debug read");
87
	check_eq(fake_dev[FAKE_VERA].state, 1, "and is allowed to move device state");
88
}
89
90
// ---- A debug read moves no device state ------------------------------------
91
92
static void
93
test_debug_read_moves_no_state(void)
94
{
95
	fake_devices_reset();
96
	debug_read(ADDR_VERA);
97
	check_eq(fake_dev[FAKE_VERA].state, 0, "a debug read leaves VERA's state alone");
98
99
	fake_devices_reset();
100
	debug_read(ADDR_VIA1);
101
	check_eq(fake_dev[FAKE_VIA1].state, 0, "a debug read leaves VIA1's state alone");
102
103
	// Repeating it must stay free: a memory view refreshes constantly.
104
	fake_devices_reset();
105
	for (int i = 0; i < 16; i++) {
106
		debug_read(ADDR_VERA);
107
	}
108
	check_eq(fake_dev[FAKE_VERA].reads, 16, "sixteen debug reads all arrived");
109
	check_eq(fake_dev[FAKE_VERA].state, 0, "and none of them moved anything");
110
}
111
112
// ---- A debug read costs no cycles ------------------------------------------
113
// The slow I/O ranges charge three cycles on a real access. A debugger that
114
// spent those would change program timing just by being open.
115
116
static void
117
test_debug_read_costs_nothing(void)
118
{
119
	// $9F40-$9F5F, the slow IO2 range.
120
	fake_devices_reset();
121
	debug_read(0x9F41);
122
	check_eq(clockticks6502, 0, "a debug read of $9F41 costs no cycles");
123
124
	fake_devices_reset();
125
	read6502(0x9F41, 0);
126
	check_eq(clockticks6502, 3, "and a real read of $9F41 costs three");
127
128
	// $9FA0-$9FFF, the slow IO5-7 range.
129
	fake_devices_reset();
130
	debug_read(0x9FA0);
131
	check_eq(clockticks6502, 0, "a debug read of $9FA0 costs no cycles");
132
133
	fake_devices_reset();
134
	read6502(0x9FA0, 0);
135
	check_eq(clockticks6502, 3, "and a real read of $9FA0 costs three");
136
137
	// Plain RAM is free either way, which keeps the above honest.
138
	fake_devices_reset();
139
	read6502(0x0200, 0);
140
	check_eq(clockticks6502, 0, "a real read of RAM costs nothing");
141
}
142
143
// ---- Sweeping the whole I/O page disturbs nothing ---------------------------
144
// One test, no per-device maintenance, and it covers devices added later. The
145
// directed cases above are what stop it passing vacuously.
146
147
static void
148
test_io_page_sweep_is_pure(void)
149
{
150
	fake_devices_reset();
151
152
	for (uint16_t addr = 0x9F00; addr < 0xA000; addr++) {
153
		debug_read(addr);
154
	}
155
156
	check(fake_devices_total_reads() > 0, "the sweep reached at least one device");
157
	check_eq(clockticks6502, 0, "sweeping the I/O page costs no cycles");
158
159
	// The YM is the one exception, and it is a signature problem rather than a
160
	// routing one: YM_read_status() takes no debug flag, so memory.c has no way
161
	// to tell it this read is for the debugger.
162
	uint32_t moved = fake_devices_state_sum() - fake_dev[FAKE_YM].state;
163
	check_eq(moved, 0, "sweeping the I/O page moves no device state");
164
	check(fake_dev[FAKE_YM].state > 0,
165
	      "except the YM, whose read has no debug parameter to honour");
166
}
167
168
int
169
main(void)
170
{
171
	test_debug_read_routing();
172
	test_real_read_is_not_flagged();
173
	test_debug_read_moves_no_state();
174
	test_debug_read_costs_nothing();
175
	test_io_page_sweep_is_pure();
176
	return x16_test_summary("debugon_contract");
177
}