OXYGENHUB
◆ main enhanced-x16-emulator / tests / test_via_timers.c
7.8 KiB · C · last changed chore(): initial commit 25 days ago source blame history raw
9120e874 Andy McCall 1
// The 65C22's interval timers: T1 ($04-$07) and T2 ($08-$09).
2
//
3
// Both count down at the PHI2 rate and raise an interrupt flag at zero. What
4
// differs between them, and between T1's two modes, is what happens next:
5
// whether the flag can be raised again, and where the next count comes from.
6
// That is the part programs get wrong, and it is entirely internal to the
7
// chip.
8
//
9
// As in tests/test_via_irq.c the subject is VIA 2, whose accessors pass
10
// straight through to the generic implementation both VIAs share.
11
//
12
// The counter is observed with debug reads. A real read of T1C-L or T2C-L
13
// clears the timer's interrupt flag (Table 2-11), which would make every
14
// observation change what the next assertion is measuring; the debug path is
15
// pinned side-effect-free in tests/test_via_irq.c.
16
//
17
// ORACLE: C -- WDC W65C22S datasheet, pages 17-19, quoted inline. Fetch it
18
// with `python tests/refs/fetch_refs.py`.
19
//
20
// NOT COVERED, because via.c computes them and nothing reads them back:
21
//   * PB7 output (ACR bit 7). via.c:171 toggles a pb7_output field on every
22
//     T1 timeout and via.c:121 clears it on a load, but no read path consults
23
//     it, so the square wave and the single negative pulse the datasheet
24
//     describes are invisible from outside.
25
//   * PB6 pulse counting. via.c:194 counts pb6_pulse_counts in T2's pulse
26
//     mode, and nothing anywhere increments that field.
27
// Both are recorded rather than asserted around.
28
29
#include "support/harness.h"
30
31
#include "via.h"
32
33
#include <stdint.h>
34
35
#define VIA_T1C_L 4
36
#define VIA_T1C_H 5
37
#define VIA_T1L_L 6
38
#define VIA_T1L_H 7
39
#define VIA_T2C_L 8
40
#define VIA_T2C_H 9
41
#define VIA_ACR   11
42
#define VIA_IFR   13
43
44
#define IFR_T2 0x20
45
#define IFR_T1 0x40
46
47
#define ACR_ONE_SHOT 0x00
48
#define ACR_FREE_RUN 0x40 // ACR bit 6
49
#define ACR_T2_PULSE 0x20 // ACR bit 5
50
51
static uint8_t
52
ifr(void)
53
{
54
	return via2_read(VIA_IFR, true);
55
}
56
57
static uint16_t
58
t1_count(void)
59
{
60
	return (uint16_t)(via2_read(VIA_T1C_L, true) | (via2_read(VIA_T1C_H, true) << 8));
61
}
62
63
static uint16_t
64
t2_count(void)
65
{
66
	return (uint16_t)(via2_read(VIA_T2C_L, true) | (via2_read(VIA_T2C_H, true) << 8));
67
}
68
69
// p17: "the microprocessor does not write directly into the T1 low order
70
//       counter. Instead, this half of the counter is loaded automatically
71
//       from the low order register when the microprocessor writes into the
72
//       high order register and counter."
73
static void
74
test_writing_t1_high_loads_the_counter_from_the_latch(void)
75
{
76
	via2_init();
77
	via2_write(VIA_ACR, ACR_ONE_SHOT);
78
79
	// Put a known value in the counter first. via_init() deliberately leaves
80
	// the timer counters alone, so starting from whatever the last test left
81
	// would make this assert against leftover state.
82
	via2_write(VIA_T1L_L, 0xFF);
83
	via2_write(VIA_T1C_H, 0xFF);
84
	check_eq(t1_count(), 0xFFFFu, "the counter holds a known value");
85
86
	via2_write(VIA_T1L_L, 0x34);
87
	check_eq(t1_count(), 0xFFFFu, "the low latch alone does not load the counter");
88
89
	via2_write(VIA_T1C_H, 0x12);
90
	check_eq(t1_count(), 0x1234u, "writing the high byte loads both halves at once");
91
}
92
93
static void
94
test_t1_counts_down_at_the_clock_rate(void)
95
{
96
	via2_init();
97
	via2_write(VIA_ACR, ACR_ONE_SHOT);
98
	via2_write(VIA_T1L_L, 0x00);
99
	via2_write(VIA_T1C_H, 0x20);
100
101
	via2_step(0x100);
102
	check_eq(t1_count(), 0x1F00u, "T1 decrements once per clock");
103
104
	via2_step(0x0F00);
105
	check_eq(t1_count(), 0x1000u, "and keeps going");
106
	check_eq(ifr() & IFR_T1, 0, "without raising its flag before zero");
107
}
108
109
// p17: "Interval Timer T1 may operate in the One-Shot Mode that allows the
110
//       generation of a single Interrupt Flag each time the Timer is loaded."
111
//
112
// p18: "When operating in the Free-Run Mode, the Interrupt Flag is set and the
113
//       signal on PB7 is inverted each time the counter reaches a count of
114
//       zero... it is not necessary to reload the timer in order to set the
115
//       Interrupt Flag on the next count of zero."
116
static void
117
test_one_shot_raises_the_flag_once_and_free_run_repeats(void)
118
{
119
	via2_init();
120
	via2_write(VIA_ACR, ACR_ONE_SHOT);
121
	via2_write(VIA_T1L_L, 0x10);
122
	via2_write(VIA_T1C_H, 0x00);
123
124
	via2_step(0x40);
125
	check_eq(ifr() & IFR_T1, IFR_T1, "one-shot raises the flag at zero");
126
127
	via2_write(VIA_IFR, IFR_T1);
128
	via2_step(0x4000);
129
	check_eq(ifr() & IFR_T1, 0, "and never again without another load");
130
131
	// The same sequence with only ACR bit 6 changed.
132
	via2_init();
133
	via2_write(VIA_ACR, ACR_FREE_RUN);
134
	via2_write(VIA_T1L_L, 0x10);
135
	via2_write(VIA_T1C_H, 0x00);
136
137
	via2_step(0x40);
138
	check_eq(ifr() & IFR_T1, IFR_T1, "free-run raises the flag at zero");
139
140
	via2_write(VIA_IFR, IFR_T1);
141
	via2_step(0x40);
142
	check_eq(ifr() & IFR_T1, IFR_T1, "and raises it again with no reload");
143
}
144
145
// p18: "By loading the latches only, the microprocessor can access the timer
146
//       during each countdown operation without affecting the time out in
147
//       progress. In this way, data loaded into the latches will determine the
148
//       length of the next subsequent time out period."
149
static void
150
test_loading_the_latches_does_not_disturb_a_count_in_progress(void)
151
{
152
	via2_init();
153
	via2_write(VIA_ACR, ACR_FREE_RUN);
154
	via2_write(VIA_T1L_L, 0x00);
155
	via2_write(VIA_T1C_H, 0x20);
156
	via2_step(0x100);
157
158
	const uint16_t before = t1_count();
159
	check_eq(before, 0x1F00u, "a count is in progress");
160
161
	via2_write(VIA_T1L_L, 0x55);
162
	check_eq(t1_count(), before, "writing the low latch leaves the counter alone");
163
164
	via2_write(VIA_T1L_H, 0x66);
165
	check_eq(t1_count(), before, "and so does writing the high latch");
166
}
167
168
// p19: "Timer 2 (T2) operates in the One-Shot Mode only (as an interval
169
//       timer), or as a pulse counter for counting negative pulses on PB6."
170
//
171
// Table 2-9: "T2H WRITE - 8 bits loaded into T2 high order counter. Also, low
172
//       order latches are transferred to low order counter."
173
static void
174
test_t2_loads_counts_and_fires_once(void)
175
{
176
	via2_init();
177
	via2_write(VIA_ACR, ACR_ONE_SHOT);
178
179
	via2_write(VIA_T2C_L, 0xFF);
180
	via2_write(VIA_T2C_H, 0xFF);
181
	check_eq(t2_count(), 0xFFFFu, "the counter holds a known value");
182
183
	via2_write(VIA_T2C_L, 0x78);
184
	check_eq(t2_count(), 0xFFFFu, "the low latch alone does not load T2");
185
186
	via2_write(VIA_T2C_H, 0x56);
187
	check_eq(t2_count(), 0x5678u, "writing the high byte loads both halves");
188
189
	via2_step(0x78);
190
	check_eq(t2_count(), 0x5600u, "T2 decrements once per clock");
191
	check_eq(ifr() & IFR_T2, 0, "with no flag before zero");
192
193
	via2_step(0x5700);
194
	check_eq(ifr() & IFR_T2, IFR_T2, "and raises its flag at zero");
195
196
	// One-shot only: there is no free-run bit for T2.
197
	via2_write(VIA_IFR, IFR_T2);
198
	via2_step(0x10000);
199
	check_eq(ifr() & IFR_T2, 0, "T2 has no free-run mode to raise it again");
200
}
201
202
// p19: "A single control bit within ACR5 is used to select between these two
203
//       modes." In pulse mode T2 counts negative pulses on PB6 rather than the
204
//       PHI2 clock, so with nothing driving PB6 -- VIA 2 is the user port, and
205
//       nothing is connected to it -- it does not count at all.
206
static void
207
test_acr_bit_5_takes_t2_off_the_clock(void)
208
{
209
	via2_init();
210
	via2_write(VIA_ACR, ACR_T2_PULSE);
211
	via2_write(VIA_T2C_L, 0x00);
212
	via2_write(VIA_T2C_H, 0x10);
213
214
	via2_step(0x4000);
215
	check_eq(t2_count(), 0x1000u, "T2 in pulse mode ignores the clock");
216
	check_eq(ifr() & IFR_T2, 0, "and never times out on it");
217
218
	// The same count with the mode bit clear, to show the step was doing
219
	// something and the assertion above is not measuring a stopped test.
220
	via2_init();
221
	via2_write(VIA_ACR, ACR_ONE_SHOT);
222
	via2_write(VIA_T2C_L, 0x00);
223
	via2_write(VIA_T2C_H, 0x10);
224
225
	via2_step(0x4000);
226
	check_eq(ifr() & IFR_T2, IFR_T2, "while on the clock the same count times out");
227
}
228
229
int
230
main(void)
231
{
232
	test_writing_t1_high_loads_the_counter_from_the_latch();
233
	test_t1_counts_down_at_the_clock_rate();
234
	test_one_shot_raises_the_flag_once_and_free_run_repeats();
235
	test_loading_the_latches_does_not_disturb_a_count_in_progress();
236
	test_t2_loads_counts_and_fires_once();
237
	test_acr_bit_5_takes_t2_off_the_clock();
238
	return x16_test_summary("via_timers");
239
}