ac6502 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +261 -0
- package/dist/components/CPU.js +1170 -0
- package/dist/components/CPU.js.map +1 -0
- package/dist/components/Cart.js +23 -0
- package/dist/components/Cart.js.map +1 -0
- package/dist/components/IO/Empty.js +19 -0
- package/dist/components/IO/Empty.js.map +1 -0
- package/dist/components/IO/GPIOAttachments/GPIOAttachment.js +71 -0
- package/dist/components/IO/GPIOAttachments/GPIOAttachment.js.map +1 -0
- package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.js +90 -0
- package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.js.map +1 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.js +489 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.js.map +1 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.js +274 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.js.map +1 -0
- package/dist/components/IO/GPIOCard.js +597 -0
- package/dist/components/IO/GPIOCard.js.map +1 -0
- package/dist/components/IO/InputBoard.js +19 -0
- package/dist/components/IO/InputBoard.js.map +1 -0
- package/dist/components/IO/LCDCard.js +19 -0
- package/dist/components/IO/LCDCard.js.map +1 -0
- package/dist/components/IO/RAMCard.js +63 -0
- package/dist/components/IO/RAMCard.js.map +1 -0
- package/dist/components/IO/RTCCard.js +483 -0
- package/dist/components/IO/RTCCard.js.map +1 -0
- package/dist/components/IO/SerialCard.js +282 -0
- package/dist/components/IO/SerialCard.js.map +1 -0
- package/dist/components/IO/SoundCard.js +620 -0
- package/dist/components/IO/SoundCard.js.map +1 -0
- package/dist/components/IO/StorageCard.js +428 -0
- package/dist/components/IO/StorageCard.js.map +1 -0
- package/dist/components/IO/VGACard.js +9 -0
- package/dist/components/IO/VGACard.js.map +1 -0
- package/dist/components/IO/VideoCard.js +623 -0
- package/dist/components/IO/VideoCard.js.map +1 -0
- package/dist/components/IO.js +3 -0
- package/dist/components/IO.js.map +1 -0
- package/dist/components/Machine.js +310 -0
- package/dist/components/Machine.js.map +1 -0
- package/dist/components/RAM.js +24 -0
- package/dist/components/RAM.js.map +1 -0
- package/dist/components/ROM.js +23 -0
- package/dist/components/ROM.js.map +1 -0
- package/dist/index.js +441 -0
- package/dist/index.js.map +1 -0
- package/dist/tests/CPU.test.js +1626 -0
- package/dist/tests/CPU.test.js.map +1 -0
- package/dist/tests/Cart.test.js +119 -0
- package/dist/tests/Cart.test.js.map +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.js +339 -0
- package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.js.map +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.js +126 -0
- package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.js.map +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.js +779 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.js.map +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.js +355 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.js.map +1 -0
- package/dist/tests/IO/GPIOCard.test.js +503 -0
- package/dist/tests/IO/GPIOCard.test.js.map +1 -0
- package/dist/tests/IO/RAMCard.test.js +229 -0
- package/dist/tests/IO/RAMCard.test.js.map +1 -0
- package/dist/tests/IO/RTCCard.test.js +177 -0
- package/dist/tests/IO/RTCCard.test.js.map +1 -0
- package/dist/tests/IO/SerialCard.test.js +423 -0
- package/dist/tests/IO/SerialCard.test.js.map +1 -0
- package/dist/tests/IO/SoundCard.test.js +528 -0
- package/dist/tests/IO/SoundCard.test.js.map +1 -0
- package/dist/tests/IO/StorageCard.test.js +647 -0
- package/dist/tests/IO/StorageCard.test.js.map +1 -0
- package/dist/tests/IO/VideoCard.test.js +549 -0
- package/dist/tests/IO/VideoCard.test.js.map +1 -0
- package/dist/tests/Machine.test.js +383 -0
- package/dist/tests/Machine.test.js.map +1 -0
- package/dist/tests/RAM.test.js +160 -0
- package/dist/tests/RAM.test.js.map +1 -0
- package/dist/tests/ROM.test.js +123 -0
- package/dist/tests/ROM.test.js.map +1 -0
- package/jest.config.cjs +9 -0
- package/package.json +43 -0
- package/src/components/CPU.ts +1371 -0
- package/src/components/Cart.ts +20 -0
- package/src/components/IO/GPIOAttachments/GPIOAttachment.ts +189 -0
- package/src/components/IO/GPIOAttachments/GPIOJoystickAttachment.ts +99 -0
- package/src/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.ts +465 -0
- package/src/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.ts +287 -0
- package/src/components/IO/GPIOCard.ts +677 -0
- package/src/components/IO/RAMCard.ts +68 -0
- package/src/components/IO/RTCCard.ts +518 -0
- package/src/components/IO/SerialCard.ts +335 -0
- package/src/components/IO/SoundCard.ts +711 -0
- package/src/components/IO/StorageCard.ts +473 -0
- package/src/components/IO/VideoCard.ts +730 -0
- package/src/components/IO.ts +11 -0
- package/src/components/Machine.ts +364 -0
- package/src/components/RAM.ts +23 -0
- package/src/components/ROM.ts +19 -0
- package/src/index.ts +474 -0
- package/src/tests/CPU.test.ts +2045 -0
- package/src/tests/Cart.test.ts +149 -0
- package/src/tests/IO/GPIOAttachments/GPIOAttachment.test.ts +413 -0
- package/src/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.ts +147 -0
- package/src/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.ts +961 -0
- package/src/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.ts +449 -0
- package/src/tests/IO/GPIOCard.test.ts +644 -0
- package/src/tests/IO/RAMCard.test.ts +284 -0
- package/src/tests/IO/RTCCard.test.ts +222 -0
- package/src/tests/IO/SerialCard.test.ts +530 -0
- package/src/tests/IO/SoundCard.test.ts +659 -0
- package/src/tests/IO/StorageCard.test.ts +787 -0
- package/src/tests/IO/VideoCard.test.ts +668 -0
- package/src/tests/Machine.test.ts +437 -0
- package/src/tests/RAM.test.ts +196 -0
- package/src/tests/ROM.test.ts +154 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { RAMCard } from '../../components/IO/RAMCard'
|
|
2
|
+
|
|
3
|
+
describe('RAMCard', () => {
|
|
4
|
+
let ramCard: RAMCard
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
ramCard = new RAMCard()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
describe('Static Properties', () => {
|
|
11
|
+
it('should have correct total size', () => {
|
|
12
|
+
expect(RAMCard.TOTAL_SIZE).toBe(256 * 1024)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('should have correct bank size', () => {
|
|
16
|
+
expect(RAMCard.BANK_SIZE).toBe(1024)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('should have correct number of banks', () => {
|
|
20
|
+
expect(RAMCard.NUM_BANKS).toBe(256)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('should have correct bank control register address', () => {
|
|
24
|
+
expect(RAMCard.BANK_CONTROL_REGISTER).toBe(0x3FF)
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
describe('Initialization', () => {
|
|
29
|
+
it('should initialize with all data as 0x00', () => {
|
|
30
|
+
for (let i = 0; i < RAMCard.TOTAL_SIZE; i++) {
|
|
31
|
+
expect(ramCard.data[i]).toBe(0x00)
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should start on bank 0', () => {
|
|
36
|
+
expect(ramCard.currentBank).toBe(0)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should have IRQ and NMI callbacks', () => {
|
|
40
|
+
expect(typeof ramCard.raiseIRQ).toBe('function')
|
|
41
|
+
expect(typeof ramCard.raiseNMI).toBe('function')
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('Reading', () => {
|
|
46
|
+
it('should read data from current bank', () => {
|
|
47
|
+
ramCard.data[100] = 0x42
|
|
48
|
+
expect(ramCard.read(100)).toBe(0x42)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should read from address 0', () => {
|
|
52
|
+
ramCard.data[0] = 0xAB
|
|
53
|
+
expect(ramCard.read(0)).toBe(0xAB)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should read from address 0x3FE (last data address)', () => {
|
|
57
|
+
ramCard.write(0x3FE, 0xCD)
|
|
58
|
+
expect(ramCard.read(0x3FE)).toBe(0xCD)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('should read bank control register and return current bank', () => {
|
|
62
|
+
ramCard.currentBank = 0
|
|
63
|
+
expect(ramCard.read(RAMCard.BANK_CONTROL_REGISTER)).toBe(0)
|
|
64
|
+
|
|
65
|
+
ramCard.currentBank = 42
|
|
66
|
+
expect(ramCard.read(RAMCard.BANK_CONTROL_REGISTER)).toBe(42)
|
|
67
|
+
|
|
68
|
+
ramCard.currentBank = 255
|
|
69
|
+
expect(ramCard.read(RAMCard.BANK_CONTROL_REGISTER)).toBe(255)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe('Writing', () => {
|
|
74
|
+
it('should write data to current bank', () => {
|
|
75
|
+
ramCard.write(100, 0x42)
|
|
76
|
+
expect(ramCard.data[100]).toBe(0x42)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('should write to address 0', () => {
|
|
80
|
+
ramCard.write(0, 0xAB)
|
|
81
|
+
expect(ramCard.data[0]).toBe(0xAB)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('should write to address 0x3FE', () => {
|
|
85
|
+
ramCard.write(0x3FE, 0xCD)
|
|
86
|
+
expect(ramCard.read(0x3FE)).toBe(0xCD)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('should mask data to 0xFF', () => {
|
|
90
|
+
ramCard.write(100, 0x1FF)
|
|
91
|
+
expect(ramCard.data[100]).toBe(0xFF)
|
|
92
|
+
|
|
93
|
+
ramCard.write(101, 0x100)
|
|
94
|
+
expect(ramCard.data[101]).toBe(0x00)
|
|
95
|
+
|
|
96
|
+
ramCard.write(102, 0x142)
|
|
97
|
+
expect(ramCard.data[102]).toBe(0x42)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should switch banks via bank control register', () => {
|
|
101
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 5)
|
|
102
|
+
expect(ramCard.currentBank).toBe(5)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should mask bank number to 0xFF', () => {
|
|
106
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 0x1FF)
|
|
107
|
+
expect(ramCard.currentBank).toBe(0xFF)
|
|
108
|
+
|
|
109
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 0x100)
|
|
110
|
+
expect(ramCard.currentBank).toBe(0x00)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe('Bank Switching', () => {
|
|
115
|
+
it('should isolate data between banks', () => {
|
|
116
|
+
// Write to address 50 in bank 0
|
|
117
|
+
ramCard.write(50, 0x11)
|
|
118
|
+
expect(ramCard.read(50)).toBe(0x11)
|
|
119
|
+
|
|
120
|
+
// Switch to bank 1
|
|
121
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 1)
|
|
122
|
+
expect(ramCard.read(50)).toBe(0x00)
|
|
123
|
+
|
|
124
|
+
// Write different value to address 50 in bank 1
|
|
125
|
+
ramCard.write(50, 0x22)
|
|
126
|
+
expect(ramCard.read(50)).toBe(0x22)
|
|
127
|
+
|
|
128
|
+
// Switch back to bank 0
|
|
129
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 0)
|
|
130
|
+
expect(ramCard.read(50)).toBe(0x11)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('should persist data in each bank', () => {
|
|
134
|
+
// Bank 0
|
|
135
|
+
ramCard.write(100, 0xAA)
|
|
136
|
+
ramCard.write(200, 0xBB)
|
|
137
|
+
|
|
138
|
+
// Switch to bank 1
|
|
139
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 1)
|
|
140
|
+
ramCard.write(100, 0xCC)
|
|
141
|
+
ramCard.write(200, 0xDD)
|
|
142
|
+
|
|
143
|
+
// Switch to bank 2
|
|
144
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 2)
|
|
145
|
+
ramCard.write(100, 0xEE)
|
|
146
|
+
|
|
147
|
+
// Verify data in bank 2
|
|
148
|
+
expect(ramCard.read(100)).toBe(0xEE)
|
|
149
|
+
|
|
150
|
+
// Switch to bank 1
|
|
151
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 1)
|
|
152
|
+
expect(ramCard.read(100)).toBe(0xCC)
|
|
153
|
+
expect(ramCard.read(200)).toBe(0xDD)
|
|
154
|
+
|
|
155
|
+
// Switch to bank 0
|
|
156
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 0)
|
|
157
|
+
expect(ramCard.read(100)).toBe(0xAA)
|
|
158
|
+
expect(ramCard.read(200)).toBe(0xBB)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('should switch between all 256 banks', () => {
|
|
162
|
+
// Write unique val to each bank at address 0
|
|
163
|
+
for (let bank = 0; bank < RAMCard.NUM_BANKS; bank++) {
|
|
164
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, bank)
|
|
165
|
+
ramCard.write(0, bank & 0xFF)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Verify each bank has correct value
|
|
169
|
+
for (let bank = 0; bank < RAMCard.NUM_BANKS; bank++) {
|
|
170
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, bank)
|
|
171
|
+
expect(ramCard.read(0)).toBe(bank & 0xFF)
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
describe('Reset', () => {
|
|
177
|
+
it('should not reset on warm start', () => {
|
|
178
|
+
ramCard.write(100, 0x42)
|
|
179
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 42)
|
|
180
|
+
|
|
181
|
+
ramCard.reset(false)
|
|
182
|
+
|
|
183
|
+
expect(ramCard.data[100]).toBe(0x42)
|
|
184
|
+
expect(ramCard.currentBank).toBe(42)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('should reset all data on cold start', () => {
|
|
188
|
+
ramCard.write(100, 0x42)
|
|
189
|
+
ramCard.write(500, 0xAB)
|
|
190
|
+
ramCard.write(RAMCard.TOTAL_SIZE - 1, 0xFF)
|
|
191
|
+
|
|
192
|
+
ramCard.reset(true)
|
|
193
|
+
|
|
194
|
+
expect(ramCard.data[100]).toBe(0x00)
|
|
195
|
+
expect(ramCard.data[500]).toBe(0x00)
|
|
196
|
+
expect(ramCard.data[RAMCard.TOTAL_SIZE - 1]).toBe(0x00)
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
it('should reset to bank 0 on cold start', () => {
|
|
200
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 100)
|
|
201
|
+
expect(ramCard.currentBank).toBe(100)
|
|
202
|
+
|
|
203
|
+
ramCard.reset(true)
|
|
204
|
+
|
|
205
|
+
expect(ramCard.currentBank).toBe(0)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('should clear all RAM on cold start', () => {
|
|
209
|
+
// Fill multiple banks with data
|
|
210
|
+
for (let bank = 0; bank < 10; bank++) {
|
|
211
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, bank)
|
|
212
|
+
for (let addr = 0; addr < RAMCard.BANK_SIZE; addr++) {
|
|
213
|
+
ramCard.write(addr, bank)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
ramCard.reset(true)
|
|
218
|
+
|
|
219
|
+
// Verify all data is cleared
|
|
220
|
+
for (let i = 0; i < RAMCard.TOTAL_SIZE; i++) {
|
|
221
|
+
expect(ramCard.data[i]).toBe(0x00)
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
describe('Tick', () => {
|
|
227
|
+
it('should have tick method that does nothing', () => {
|
|
228
|
+
expect(() => {
|
|
229
|
+
ramCard.tick(1000000)
|
|
230
|
+
}).not.toThrow()
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
describe('IO Interface Implementation', () => {
|
|
235
|
+
it('should implement IO interface methods', () => {
|
|
236
|
+
expect(typeof ramCard.read).toBe('function')
|
|
237
|
+
expect(typeof ramCard.write).toBe('function')
|
|
238
|
+
expect(typeof ramCard.tick).toBe('function')
|
|
239
|
+
expect(typeof ramCard.reset).toBe('function')
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
it('should have raiseIRQ and raiseNMI callbacks', () => {
|
|
243
|
+
expect(ramCard.raiseIRQ()).toBeUndefined()
|
|
244
|
+
expect(ramCard.raiseNMI()).toBeUndefined()
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
describe('Integration', () => {
|
|
249
|
+
it('should allow multiple write/read cycles', () => {
|
|
250
|
+
const testValues = [0x00, 0x42, 0xFF, 0x80, 0x01]
|
|
251
|
+
|
|
252
|
+
for (const value of testValues) {
|
|
253
|
+
ramCard.write(250, value)
|
|
254
|
+
expect(ramCard.read(250)).toBe(value)
|
|
255
|
+
}
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
it('should handle rapid bank switches', () => {
|
|
259
|
+
for (let i = 0; i < 100; i++) {
|
|
260
|
+
ramCard.write(RAMCard.BANK_CONTROL_REGISTER, i % 256)
|
|
261
|
+
ramCard.write(0, i & 0xFF)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Check final state (last iteration i=99)
|
|
265
|
+
expect(ramCard.currentBank).toBe(99 % 256)
|
|
266
|
+
expect(ramCard.read(0)).toBe(99 & 0xFF)
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
it('should handle boundary addresses correctly', () => {
|
|
270
|
+
// Test address 0
|
|
271
|
+
ramCard.write(0, 0x11)
|
|
272
|
+
expect(ramCard.read(0)).toBe(0x11)
|
|
273
|
+
|
|
274
|
+
// Test address 0x3FE
|
|
275
|
+
ramCard.write(0x3FE, 0x22)
|
|
276
|
+
expect(ramCard.read(0x3FE)).toBe(0x22)
|
|
277
|
+
|
|
278
|
+
// Test bank control register
|
|
279
|
+
ramCard.write(0x3FF, 5)
|
|
280
|
+
expect(ramCard.currentBank).toBe(5)
|
|
281
|
+
expect(ramCard.read(0x3FF)).toBe(5)
|
|
282
|
+
})
|
|
283
|
+
})
|
|
284
|
+
})
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { RTCCard } from '../../components/IO/RTCCard'
|
|
2
|
+
|
|
3
|
+
const bcdToDecimal = (bcd: number): number => (((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f)
|
|
4
|
+
|
|
5
|
+
const enableTransfers = (rtc: RTCCard): void => {
|
|
6
|
+
rtc.write(0x0f, 0x80)
|
|
7
|
+
rtc.tick(1)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const setTime = (rtc: RTCCard, values: {
|
|
11
|
+
seconds: number
|
|
12
|
+
minutes: number
|
|
13
|
+
hours: number
|
|
14
|
+
dayOfWeek: number
|
|
15
|
+
date: number
|
|
16
|
+
month: number
|
|
17
|
+
year: number
|
|
18
|
+
century: number
|
|
19
|
+
}): void => {
|
|
20
|
+
enableTransfers(rtc)
|
|
21
|
+
rtc.write(0x00, values.seconds)
|
|
22
|
+
rtc.write(0x01, values.minutes)
|
|
23
|
+
rtc.write(0x02, values.hours)
|
|
24
|
+
rtc.write(0x03, values.dayOfWeek)
|
|
25
|
+
rtc.write(0x04, values.date)
|
|
26
|
+
rtc.write(0x05, values.month | 0x80)
|
|
27
|
+
rtc.write(0x06, values.year)
|
|
28
|
+
rtc.write(0x07, values.century)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe('RTCCard', () => {
|
|
32
|
+
let rtc: RTCCard
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
rtc = new RTCCard()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
describe('Initialization', () => {
|
|
39
|
+
it('should initialize with valid BCD time and EOSC enabled', () => {
|
|
40
|
+
const seconds = rtc.read(0x00)
|
|
41
|
+
const minutes = rtc.read(0x01)
|
|
42
|
+
const hours = rtc.read(0x02)
|
|
43
|
+
const dayOfWeek = rtc.read(0x03)
|
|
44
|
+
const date = rtc.read(0x04)
|
|
45
|
+
const month = rtc.read(0x05)
|
|
46
|
+
const year = rtc.read(0x06)
|
|
47
|
+
const century = rtc.read(0x07)
|
|
48
|
+
|
|
49
|
+
expect(dayOfWeek).toBeGreaterThanOrEqual(1)
|
|
50
|
+
expect(dayOfWeek).toBeLessThanOrEqual(7)
|
|
51
|
+
|
|
52
|
+
expect(bcdToDecimal(seconds)).toBeGreaterThanOrEqual(0)
|
|
53
|
+
expect(bcdToDecimal(seconds)).toBeLessThan(60)
|
|
54
|
+
expect(bcdToDecimal(minutes)).toBeGreaterThanOrEqual(0)
|
|
55
|
+
expect(bcdToDecimal(minutes)).toBeLessThan(60)
|
|
56
|
+
expect(bcdToDecimal(hours)).toBeGreaterThanOrEqual(0)
|
|
57
|
+
expect(bcdToDecimal(hours)).toBeLessThan(24)
|
|
58
|
+
expect(bcdToDecimal(date)).toBeGreaterThanOrEqual(1)
|
|
59
|
+
expect(bcdToDecimal(date)).toBeLessThanOrEqual(31)
|
|
60
|
+
expect(bcdToDecimal(month & 0x1f)).toBeGreaterThanOrEqual(1)
|
|
61
|
+
expect(bcdToDecimal(month & 0x1f)).toBeLessThanOrEqual(12)
|
|
62
|
+
expect(bcdToDecimal(year)).toBeGreaterThanOrEqual(0)
|
|
63
|
+
expect(bcdToDecimal(year)).toBeLessThanOrEqual(99)
|
|
64
|
+
expect(bcdToDecimal(century)).toBeGreaterThanOrEqual(0)
|
|
65
|
+
expect(bcdToDecimal(century)).toBeLessThanOrEqual(39)
|
|
66
|
+
|
|
67
|
+
expect(month & 0x80).toBe(0x80)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('Registers', () => {
|
|
72
|
+
it('should mask day of week reads to 3 bits', () => {
|
|
73
|
+
rtc.write(0x03, 0xff)
|
|
74
|
+
expect(rtc.read(0x03)).toBe(0x07)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should clear control flags on Control A read', () => {
|
|
78
|
+
rtc.reset(true)
|
|
79
|
+
const first = rtc.read(0x0e)
|
|
80
|
+
const second = rtc.read(0x0e)
|
|
81
|
+
|
|
82
|
+
expect(first & 0x04).toBe(0x04)
|
|
83
|
+
expect(second & 0x0f).toBe(0)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('should raise IRQ when KSF is set and KIE enabled', () => {
|
|
87
|
+
const mockIRQ = jest.fn()
|
|
88
|
+
rtc.raiseIRQ = mockIRQ
|
|
89
|
+
|
|
90
|
+
rtc.reset(true)
|
|
91
|
+
rtc.write(0x0f, 0x04)
|
|
92
|
+
|
|
93
|
+
expect(mockIRQ).toHaveBeenCalledTimes(1)
|
|
94
|
+
const controlA = rtc.read(0x0e)
|
|
95
|
+
expect(controlA & 0x01).toBe(0x01)
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe('Extended RAM', () => {
|
|
100
|
+
it('should write and read RAM at the address pointer', () => {
|
|
101
|
+
rtc.write(0x10, 0x10)
|
|
102
|
+
rtc.write(0x13, 0x5a)
|
|
103
|
+
rtc.write(0x10, 0x10)
|
|
104
|
+
|
|
105
|
+
expect(rtc.read(0x13)).toBe(0x5a)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should auto-increment RAM address when enabled', () => {
|
|
109
|
+
rtc.write(0x0f, 0x20)
|
|
110
|
+
rtc.write(0x10, 0x10)
|
|
111
|
+
rtc.write(0x13, 0x11)
|
|
112
|
+
rtc.write(0x13, 0x22)
|
|
113
|
+
|
|
114
|
+
expect(rtc.read(0x10)).toBe(0x12)
|
|
115
|
+
|
|
116
|
+
rtc.write(0x10, 0x10)
|
|
117
|
+
expect(rtc.read(0x13)).toBe(0x11)
|
|
118
|
+
expect(rtc.read(0x13)).toBe(0x22)
|
|
119
|
+
expect(rtc.read(0x10)).toBe(0x12)
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
describe('Timekeeping', () => {
|
|
124
|
+
it('should advance time when TE is enabled', () => {
|
|
125
|
+
setTime(rtc, {
|
|
126
|
+
seconds: 0x00,
|
|
127
|
+
minutes: 0x00,
|
|
128
|
+
hours: 0x00,
|
|
129
|
+
dayOfWeek: 0x01,
|
|
130
|
+
date: 0x01,
|
|
131
|
+
month: 0x01,
|
|
132
|
+
year: 0x00,
|
|
133
|
+
century: 0x20
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
rtc.tick(1)
|
|
137
|
+
|
|
138
|
+
expect(rtc.read(0x00)).toBe(0x01)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('should stop time when oscillator is disabled', () => {
|
|
142
|
+
setTime(rtc, {
|
|
143
|
+
seconds: 0x10,
|
|
144
|
+
minutes: 0x00,
|
|
145
|
+
hours: 0x00,
|
|
146
|
+
dayOfWeek: 0x01,
|
|
147
|
+
date: 0x01,
|
|
148
|
+
month: 0x01,
|
|
149
|
+
year: 0x00,
|
|
150
|
+
century: 0x20
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
rtc.write(0x05, 0x01)
|
|
154
|
+
rtc.tick(1)
|
|
155
|
+
|
|
156
|
+
expect(rtc.read(0x00)).toBe(0x10)
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
describe('Alarm', () => {
|
|
161
|
+
it('should set TDF and raise IRQ when alarm matches', () => {
|
|
162
|
+
const mockIRQ = jest.fn()
|
|
163
|
+
rtc.raiseIRQ = mockIRQ
|
|
164
|
+
|
|
165
|
+
setTime(rtc, {
|
|
166
|
+
seconds: 0x00,
|
|
167
|
+
minutes: 0x00,
|
|
168
|
+
hours: 0x00,
|
|
169
|
+
dayOfWeek: 0x01,
|
|
170
|
+
date: 0x01,
|
|
171
|
+
month: 0x01,
|
|
172
|
+
year: 0x00,
|
|
173
|
+
century: 0x20
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
rtc.write(0x08, 0x01)
|
|
177
|
+
rtc.write(0x09, 0x00)
|
|
178
|
+
rtc.write(0x0a, 0x00)
|
|
179
|
+
rtc.write(0x0b, 0x01)
|
|
180
|
+
rtc.write(0x0f, 0x88)
|
|
181
|
+
|
|
182
|
+
rtc.tick(1)
|
|
183
|
+
|
|
184
|
+
expect(mockIRQ).toHaveBeenCalledTimes(1)
|
|
185
|
+
const controlA = rtc.read(0x0e)
|
|
186
|
+
expect(controlA & 0x08).toBe(0x08)
|
|
187
|
+
expect(controlA & 0x01).toBe(0x01)
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
describe('Watchdog', () => {
|
|
192
|
+
it('should raise IRQ when watchdog expires with WDS=0', () => {
|
|
193
|
+
const mockIRQ = jest.fn()
|
|
194
|
+
rtc.raiseIRQ = mockIRQ
|
|
195
|
+
|
|
196
|
+
rtc.write(0x0f, 0x02)
|
|
197
|
+
rtc.write(0x0c, 0x01)
|
|
198
|
+
rtc.write(0x0d, 0x00)
|
|
199
|
+
|
|
200
|
+
rtc.tick(1)
|
|
201
|
+
|
|
202
|
+
expect(mockIRQ).toHaveBeenCalledTimes(1)
|
|
203
|
+
const controlA = rtc.read(0x0e)
|
|
204
|
+
expect(controlA & 0x02).toBe(0x02)
|
|
205
|
+
expect(controlA & 0x01).toBe(0x01)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('should raise NMI and clear WDE when WDS=1', () => {
|
|
209
|
+
const mockNMI = jest.fn()
|
|
210
|
+
rtc.raiseNMI = mockNMI
|
|
211
|
+
|
|
212
|
+
rtc.write(0x0f, 0x03)
|
|
213
|
+
rtc.write(0x0c, 0x01)
|
|
214
|
+
rtc.write(0x0d, 0x00)
|
|
215
|
+
|
|
216
|
+
rtc.tick(1)
|
|
217
|
+
|
|
218
|
+
expect(mockNMI).toHaveBeenCalledTimes(1)
|
|
219
|
+
expect(rtc.read(0x0f) & 0x02).toBe(0)
|
|
220
|
+
})
|
|
221
|
+
})
|
|
222
|
+
})
|