ac6502 1.9.3 → 1.11.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/dist/components/CPU.d.ts +4 -0
- package/dist/components/CPU.js +87 -30
- package/dist/components/CPU.js.map +1 -1
- package/dist/components/IO/ACIA.d.ts +13 -21
- package/dist/components/IO/ACIA.js +53 -151
- package/dist/components/IO/ACIA.js.map +1 -1
- package/dist/components/IO/Attachments/KeyboardEncoderAttachment.d.ts +5 -10
- package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js +43 -266
- package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js.map +1 -1
- package/dist/components/IO/Empty.d.ts +1 -3
- package/dist/components/IO/Empty.js +1 -5
- package/dist/components/IO/Empty.js.map +1 -1
- package/dist/components/IO/RAMBank.d.ts +3 -4
- package/dist/components/IO/RAMBank.js +4 -13
- package/dist/components/IO/RAMBank.js.map +1 -1
- package/dist/components/IO/RTC.d.ts +2 -3
- package/dist/components/IO/RTC.js +17 -7
- package/dist/components/IO/RTC.js.map +1 -1
- package/dist/components/IO/Sound.d.ts +1 -3
- package/dist/components/IO/Sound.js +13 -23
- package/dist/components/IO/Sound.js.map +1 -1
- package/dist/components/IO/Storage.d.ts +1 -3
- package/dist/components/IO/Storage.js +1 -3
- package/dist/components/IO/Storage.js.map +1 -1
- package/dist/components/IO/VIA.d.ts +1 -3
- package/dist/components/IO/VIA.js +6 -7
- package/dist/components/IO/VIA.js.map +1 -1
- package/dist/components/IO/Video.d.ts +1 -3
- package/dist/components/IO/Video.js +3 -5
- package/dist/components/IO/Video.js.map +1 -1
- package/dist/components/IO.d.ts +1 -3
- package/dist/components/Machine.d.ts +1 -2
- package/dist/components/Machine.js +21 -74
- package/dist/components/Machine.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/tests/IO/ACIA.test.js +57 -108
- package/dist/tests/IO/ACIA.test.js.map +1 -1
- package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js +334 -574
- package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js.map +1 -1
- package/dist/tests/IO/Empty.test.js +2 -14
- package/dist/tests/IO/Empty.test.js.map +1 -1
- package/dist/tests/IO/RAMBank.test.js +5 -12
- package/dist/tests/IO/RAMBank.test.js.map +1 -1
- package/dist/tests/IO/RTC.test.js +7 -16
- package/dist/tests/IO/RTC.test.js.map +1 -1
- package/dist/tests/IO/Sound.test.js +6 -8
- package/dist/tests/IO/Sound.test.js.map +1 -1
- package/dist/tests/IO/Storage.test.js +0 -6
- package/dist/tests/IO/Storage.test.js.map +1 -1
- package/dist/tests/IO/VIA.test.js +6 -10
- package/dist/tests/IO/VIA.test.js.map +1 -1
- package/dist/tests/IO/Video.test.js +7 -7
- package/dist/tests/IO/Video.test.js.map +1 -1
- package/package.json +1 -1
- package/src/components/CPU.ts +94 -31
- package/src/components/IO/ACIA.ts +57 -176
- package/src/components/IO/Attachments/KeyboardEncoderAttachment.ts +45 -217
- package/src/components/IO/Empty.ts +1 -4
- package/src/components/IO/RAMBank.ts +4 -15
- package/src/components/IO/RTC.ts +18 -7
- package/src/components/IO/Sound.ts +14 -27
- package/src/components/IO/Storage.ts +2 -5
- package/src/components/IO/VIA.ts +6 -8
- package/src/components/IO/Video.ts +5 -7
- package/src/components/IO.ts +1 -4
- package/src/components/Machine.ts +22 -90
- package/src/index.ts +1 -1
- package/src/tests/IO/ACIA.test.ts +60 -122
- package/src/tests/IO/Attachments/KeyboardEncoderAttachment.test.ts +342 -676
- package/src/tests/IO/Empty.test.ts +2 -17
- package/src/tests/IO/RAMBank.test.ts +5 -14
- package/src/tests/IO/RTC.test.ts +7 -20
- package/src/tests/IO/Sound.test.ts +6 -8
- package/src/tests/IO/Storage.test.ts +0 -7
- package/src/tests/IO/VIA.test.ts +6 -12
- package/src/tests/IO/Video.test.ts +7 -8
|
@@ -8,21 +8,8 @@ describe('Empty', () => {
|
|
|
8
8
|
})
|
|
9
9
|
|
|
10
10
|
describe('Initialization', () => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('should have NMI callback', () => {
|
|
16
|
-
expect(typeof empty.raiseNMI).toBe('function')
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it('should not throw when calling raiseIRQ', () => {
|
|
20
|
-
expect(() => empty.raiseIRQ()).not.toThrow()
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('should not throw when calling raiseNMI', () => {
|
|
24
|
-
expect(() => empty.raiseNMI()).not.toThrow()
|
|
25
|
-
})
|
|
11
|
+
// raiseIRQ/raiseNMI callbacks removed; interrupts are now
|
|
12
|
+
// communicated via tick() return value
|
|
26
13
|
})
|
|
27
14
|
|
|
28
15
|
describe('Reading', () => {
|
|
@@ -120,8 +107,6 @@ describe('Empty', () => {
|
|
|
120
107
|
const value = empty.read(0x00)
|
|
121
108
|
expect(value).toBe(0)
|
|
122
109
|
empty.tick(1000000)
|
|
123
|
-
empty.raiseIRQ()
|
|
124
|
-
empty.raiseNMI()
|
|
125
110
|
empty.reset(false)
|
|
126
111
|
}).not.toThrow()
|
|
127
112
|
})
|
|
@@ -35,11 +35,6 @@ describe('RAMBank', () => {
|
|
|
35
35
|
it('should start on bank 0', () => {
|
|
36
36
|
expect(ramCard.currentBank).toBe(0)
|
|
37
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
38
|
})
|
|
44
39
|
|
|
45
40
|
describe('Reading', () => {
|
|
@@ -58,14 +53,15 @@ describe('RAMBank', () => {
|
|
|
58
53
|
expect(ramCard.read(0x3FE)).toBe(0xCD)
|
|
59
54
|
})
|
|
60
55
|
|
|
61
|
-
it('should read
|
|
62
|
-
|
|
56
|
+
it('should read $3FF from data array (contains bank number after write-through)', () => {
|
|
57
|
+
// Writing to $3FF sets the bank and writes the value into the new bank's data
|
|
58
|
+
ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 0)
|
|
63
59
|
expect(ramCard.read(RAMBank.BANK_CONTROL_REGISTER)).toBe(0)
|
|
64
60
|
|
|
65
|
-
ramCard.
|
|
61
|
+
ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 42)
|
|
66
62
|
expect(ramCard.read(RAMBank.BANK_CONTROL_REGISTER)).toBe(42)
|
|
67
63
|
|
|
68
|
-
ramCard.
|
|
64
|
+
ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 255)
|
|
69
65
|
expect(ramCard.read(RAMBank.BANK_CONTROL_REGISTER)).toBe(255)
|
|
70
66
|
})
|
|
71
67
|
})
|
|
@@ -238,11 +234,6 @@ describe('RAMBank', () => {
|
|
|
238
234
|
expect(typeof ramCard.tick).toBe('function')
|
|
239
235
|
expect(typeof ramCard.reset).toBe('function')
|
|
240
236
|
})
|
|
241
|
-
|
|
242
|
-
it('should have raiseIRQ and raiseNMI callbacks', () => {
|
|
243
|
-
expect(ramCard.raiseIRQ()).toBeUndefined()
|
|
244
|
-
expect(ramCard.raiseNMI()).toBeUndefined()
|
|
245
|
-
})
|
|
246
237
|
})
|
|
247
238
|
|
|
248
239
|
describe('Integration', () => {
|
package/src/tests/IO/RTC.test.ts
CHANGED
|
@@ -80,13 +80,9 @@ describe('RTC', () => {
|
|
|
80
80
|
})
|
|
81
81
|
|
|
82
82
|
it('should raise IRQ when KSF is set and KIE enabled', () => {
|
|
83
|
-
const mockIRQ = jest.fn()
|
|
84
|
-
rtc.raiseIRQ = mockIRQ
|
|
85
|
-
|
|
86
83
|
rtc.reset(true)
|
|
87
84
|
rtc.write(0x0f, 0x04)
|
|
88
85
|
|
|
89
|
-
expect(mockIRQ).toHaveBeenCalledTimes(1)
|
|
90
86
|
const controlA = rtc.read(0x0e)
|
|
91
87
|
expect(controlA & 0x01).toBe(0x01)
|
|
92
88
|
})
|
|
@@ -155,9 +151,6 @@ describe('RTC', () => {
|
|
|
155
151
|
|
|
156
152
|
describe('Alarm', () => {
|
|
157
153
|
it('should set TDF and raise IRQ when alarm matches', () => {
|
|
158
|
-
const mockIRQ = jest.fn()
|
|
159
|
-
rtc.raiseIRQ = mockIRQ
|
|
160
|
-
|
|
161
154
|
setTime(rtc, {
|
|
162
155
|
seconds: 0x00,
|
|
163
156
|
minutes: 0x00,
|
|
@@ -175,9 +168,9 @@ describe('RTC', () => {
|
|
|
175
168
|
rtc.write(0x0b, 0x01)
|
|
176
169
|
rtc.write(0x0f, 0x88)
|
|
177
170
|
|
|
178
|
-
rtc.tick(1)
|
|
171
|
+
const result = rtc.tick(1)
|
|
179
172
|
|
|
180
|
-
expect(
|
|
173
|
+
expect(result & 0x80).toBe(0x80) // IRQ signalled via return value
|
|
181
174
|
const controlA = rtc.read(0x0e)
|
|
182
175
|
expect(controlA & 0x08).toBe(0x08)
|
|
183
176
|
expect(controlA & 0x01).toBe(0x01)
|
|
@@ -186,32 +179,26 @@ describe('RTC', () => {
|
|
|
186
179
|
|
|
187
180
|
describe('Watchdog', () => {
|
|
188
181
|
it('should raise IRQ when watchdog expires with WDS=0', () => {
|
|
189
|
-
const mockIRQ = jest.fn()
|
|
190
|
-
rtc.raiseIRQ = mockIRQ
|
|
191
|
-
|
|
192
182
|
rtc.write(0x0f, 0x02)
|
|
193
183
|
rtc.write(0x0c, 0x01)
|
|
194
184
|
rtc.write(0x0d, 0x00)
|
|
195
185
|
|
|
196
|
-
rtc.tick(1)
|
|
186
|
+
const result = rtc.tick(1)
|
|
197
187
|
|
|
198
|
-
expect(
|
|
188
|
+
expect(result & 0x80).toBe(0x80) // IRQ signalled via return value
|
|
199
189
|
const controlA = rtc.read(0x0e)
|
|
200
190
|
expect(controlA & 0x02).toBe(0x02)
|
|
201
191
|
expect(controlA & 0x01).toBe(0x01)
|
|
202
192
|
})
|
|
203
193
|
|
|
204
|
-
it('should
|
|
205
|
-
const mockNMI = jest.fn()
|
|
206
|
-
rtc.raiseNMI = mockNMI
|
|
207
|
-
|
|
194
|
+
it('should signal NMI and clear WDE when WDS=1', () => {
|
|
208
195
|
rtc.write(0x0f, 0x03)
|
|
209
196
|
rtc.write(0x0c, 0x01)
|
|
210
197
|
rtc.write(0x0d, 0x00)
|
|
211
198
|
|
|
212
|
-
rtc.tick(1)
|
|
199
|
+
const result = rtc.tick(1)
|
|
213
200
|
|
|
214
|
-
expect(
|
|
201
|
+
expect(result & 0x40).toBe(0x40) // NMI signalled via return value bit 6
|
|
215
202
|
expect(rtc.read(0x0f) & 0x02).toBe(0)
|
|
216
203
|
})
|
|
217
204
|
})
|
|
@@ -35,11 +35,11 @@ const CTRL_PULSE = 0x40
|
|
|
35
35
|
const CTRL_NOISE = 0x80
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
* Helper: tick the Sound for a given number of macro-ticks
|
|
39
|
-
* Each tick
|
|
38
|
+
* Helper: tick the Sound for a given number of macro-ticks.
|
|
39
|
+
* Each macro-tick = 128 individual ticks (matching the old CYCLES_PER_TICK batch size).
|
|
40
40
|
*/
|
|
41
41
|
const tickN = (sid: Sound, n: number): void => {
|
|
42
|
-
for (let i = 0; i < n; i++) {
|
|
42
|
+
for (let i = 0; i < n * 128; i++) {
|
|
43
43
|
sid.tick(SID_CLOCK_NTSC)
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -632,11 +632,9 @@ describe('Sound (MOS 6581 SID)', () => {
|
|
|
632
632
|
|
|
633
633
|
describe('IO interface', () => {
|
|
634
634
|
|
|
635
|
-
test('should
|
|
636
|
-
|
|
637
|
-
expect(sid.
|
|
638
|
-
expect(typeof sid.raiseIRQ).toBe('function')
|
|
639
|
-
expect(typeof sid.raiseNMI).toBe('function')
|
|
635
|
+
test('tick should return interrupt status', () => {
|
|
636
|
+
// Sound does not generate interrupts
|
|
637
|
+
expect(sid.tick(1000000)).toBe(0)
|
|
640
638
|
})
|
|
641
639
|
|
|
642
640
|
test('read should handle any address value', () => {
|
|
@@ -22,11 +22,6 @@ describe('Storage (Compact Flash in IDE Mode)', () => {
|
|
|
22
22
|
expect(storageCard.read(0x07) & 0x40).toBe(0x40) // Status (RDY bit)
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
-
it('should have IRQ and NMI callbacks', () => {
|
|
26
|
-
expect(typeof storageCard.raiseIRQ).toBe('function')
|
|
27
|
-
expect(typeof storageCard.raiseNMI).toBe('function')
|
|
28
|
-
})
|
|
29
|
-
|
|
30
25
|
it('should initialize with all storage zeroed', () => {
|
|
31
26
|
// Verify first sector is all zeros
|
|
32
27
|
storageCard.write(0x02, 1) // Sector count = 1
|
|
@@ -554,8 +549,6 @@ describe('Storage (Compact Flash in IDE Mode)', () => {
|
|
|
554
549
|
expect(typeof storageCard.write).toBe('function')
|
|
555
550
|
expect(typeof storageCard.tick).toBe('function')
|
|
556
551
|
expect(typeof storageCard.reset).toBe('function')
|
|
557
|
-
expect(typeof storageCard.raiseIRQ).toBe('function')
|
|
558
|
-
expect(typeof storageCard.raiseNMI).toBe('function')
|
|
559
552
|
})
|
|
560
553
|
})
|
|
561
554
|
|
package/src/tests/IO/VIA.test.ts
CHANGED
|
@@ -374,31 +374,25 @@ describe('VIA (6522 VIA)', () => {
|
|
|
374
374
|
})
|
|
375
375
|
|
|
376
376
|
describe('IRQ Generation', () => {
|
|
377
|
-
it('should
|
|
378
|
-
const mockIRQ = jest.fn()
|
|
379
|
-
gpio.raiseIRQ = mockIRQ
|
|
380
|
-
|
|
377
|
+
it('should return IRQ status from tick when enabled interrupt is triggered', () => {
|
|
381
378
|
gpio.write(0x0E, 0xC0) // Enable T1 interrupt
|
|
382
379
|
gpio.write(0x04, 0x01)
|
|
383
380
|
gpio.write(0x05, 0x00)
|
|
384
381
|
|
|
385
382
|
gpio.tick(1000000)
|
|
386
|
-
gpio.tick(1000000)
|
|
383
|
+
const result = gpio.tick(1000000)
|
|
387
384
|
|
|
388
|
-
expect(
|
|
385
|
+
expect(result & 0x80).toBe(0x80)
|
|
389
386
|
})
|
|
390
387
|
|
|
391
|
-
it('should not
|
|
392
|
-
const mockIRQ = jest.fn()
|
|
393
|
-
gpio.raiseIRQ = mockIRQ
|
|
394
|
-
|
|
388
|
+
it('should not return IRQ status from tick when interrupt is not enabled', () => {
|
|
395
389
|
gpio.write(0x04, 0x01)
|
|
396
390
|
gpio.write(0x05, 0x00)
|
|
397
391
|
|
|
398
392
|
gpio.tick(1000000)
|
|
399
|
-
gpio.tick(1000000)
|
|
393
|
+
const result = gpio.tick(1000000)
|
|
400
394
|
|
|
401
|
-
expect(
|
|
395
|
+
expect(result & 0x80).toBe(0)
|
|
402
396
|
})
|
|
403
397
|
})
|
|
404
398
|
|
|
@@ -79,9 +79,9 @@ const setupTextMode = (vdp: Video): void => {
|
|
|
79
79
|
* frame clears the status register during sprite processing).
|
|
80
80
|
*/
|
|
81
81
|
const renderOneFrame = (vdp: Video, frequency: number = 1000000): void => {
|
|
82
|
-
//
|
|
83
|
-
const
|
|
84
|
-
for (let i = 0; i <
|
|
82
|
+
// Each tick = 1 cycle. Cycles per frame = frequency / 60.
|
|
83
|
+
const cyclesPerFrame = Math.ceil(frequency / 60)
|
|
84
|
+
for (let i = 0; i < cyclesPerFrame; i++) {
|
|
85
85
|
vdp.tick(frequency)
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -389,16 +389,15 @@ describe('Video (TMS9918 VDP)', () => {
|
|
|
389
389
|
expect(vdp.getStatus() & 0x80).toBe(0)
|
|
390
390
|
})
|
|
391
391
|
|
|
392
|
-
it('should
|
|
393
|
-
const irqFn = jest.fn()
|
|
394
|
-
vdp.raiseIRQ = irqFn
|
|
395
|
-
|
|
392
|
+
it('should return IRQ status from tick when interrupt flag is set', () => {
|
|
396
393
|
writeRegister(vdp, 1, 0x60) // Display active + interrupts enabled
|
|
397
394
|
clearSprites(vdp)
|
|
398
395
|
|
|
399
396
|
renderOneFrame(vdp)
|
|
400
397
|
|
|
401
|
-
|
|
398
|
+
// After frame, tick should return 0x80 indicating IRQ
|
|
399
|
+
const result = vdp.tick(1000000)
|
|
400
|
+
expect(result & 0x80).toBe(0x80)
|
|
402
401
|
})
|
|
403
402
|
})
|
|
404
403
|
|