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.
Files changed (77) hide show
  1. package/dist/components/CPU.d.ts +4 -0
  2. package/dist/components/CPU.js +87 -30
  3. package/dist/components/CPU.js.map +1 -1
  4. package/dist/components/IO/ACIA.d.ts +13 -21
  5. package/dist/components/IO/ACIA.js +53 -151
  6. package/dist/components/IO/ACIA.js.map +1 -1
  7. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.d.ts +5 -10
  8. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js +43 -266
  9. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js.map +1 -1
  10. package/dist/components/IO/Empty.d.ts +1 -3
  11. package/dist/components/IO/Empty.js +1 -5
  12. package/dist/components/IO/Empty.js.map +1 -1
  13. package/dist/components/IO/RAMBank.d.ts +3 -4
  14. package/dist/components/IO/RAMBank.js +4 -13
  15. package/dist/components/IO/RAMBank.js.map +1 -1
  16. package/dist/components/IO/RTC.d.ts +2 -3
  17. package/dist/components/IO/RTC.js +17 -7
  18. package/dist/components/IO/RTC.js.map +1 -1
  19. package/dist/components/IO/Sound.d.ts +1 -3
  20. package/dist/components/IO/Sound.js +13 -23
  21. package/dist/components/IO/Sound.js.map +1 -1
  22. package/dist/components/IO/Storage.d.ts +1 -3
  23. package/dist/components/IO/Storage.js +1 -3
  24. package/dist/components/IO/Storage.js.map +1 -1
  25. package/dist/components/IO/VIA.d.ts +1 -3
  26. package/dist/components/IO/VIA.js +6 -7
  27. package/dist/components/IO/VIA.js.map +1 -1
  28. package/dist/components/IO/Video.d.ts +1 -3
  29. package/dist/components/IO/Video.js +3 -5
  30. package/dist/components/IO/Video.js.map +1 -1
  31. package/dist/components/IO.d.ts +1 -3
  32. package/dist/components/Machine.d.ts +1 -2
  33. package/dist/components/Machine.js +21 -74
  34. package/dist/components/Machine.js.map +1 -1
  35. package/dist/index.js +1 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/tests/IO/ACIA.test.js +57 -108
  38. package/dist/tests/IO/ACIA.test.js.map +1 -1
  39. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js +334 -574
  40. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js.map +1 -1
  41. package/dist/tests/IO/Empty.test.js +2 -14
  42. package/dist/tests/IO/Empty.test.js.map +1 -1
  43. package/dist/tests/IO/RAMBank.test.js +5 -12
  44. package/dist/tests/IO/RAMBank.test.js.map +1 -1
  45. package/dist/tests/IO/RTC.test.js +7 -16
  46. package/dist/tests/IO/RTC.test.js.map +1 -1
  47. package/dist/tests/IO/Sound.test.js +6 -8
  48. package/dist/tests/IO/Sound.test.js.map +1 -1
  49. package/dist/tests/IO/Storage.test.js +0 -6
  50. package/dist/tests/IO/Storage.test.js.map +1 -1
  51. package/dist/tests/IO/VIA.test.js +6 -10
  52. package/dist/tests/IO/VIA.test.js.map +1 -1
  53. package/dist/tests/IO/Video.test.js +7 -7
  54. package/dist/tests/IO/Video.test.js.map +1 -1
  55. package/package.json +1 -1
  56. package/src/components/CPU.ts +94 -31
  57. package/src/components/IO/ACIA.ts +57 -176
  58. package/src/components/IO/Attachments/KeyboardEncoderAttachment.ts +45 -217
  59. package/src/components/IO/Empty.ts +1 -4
  60. package/src/components/IO/RAMBank.ts +4 -15
  61. package/src/components/IO/RTC.ts +18 -7
  62. package/src/components/IO/Sound.ts +14 -27
  63. package/src/components/IO/Storage.ts +2 -5
  64. package/src/components/IO/VIA.ts +6 -8
  65. package/src/components/IO/Video.ts +5 -7
  66. package/src/components/IO.ts +1 -4
  67. package/src/components/Machine.ts +22 -90
  68. package/src/index.ts +1 -1
  69. package/src/tests/IO/ACIA.test.ts +60 -122
  70. package/src/tests/IO/Attachments/KeyboardEncoderAttachment.test.ts +342 -676
  71. package/src/tests/IO/Empty.test.ts +2 -17
  72. package/src/tests/IO/RAMBank.test.ts +5 -14
  73. package/src/tests/IO/RTC.test.ts +7 -20
  74. package/src/tests/IO/Sound.test.ts +6 -8
  75. package/src/tests/IO/Storage.test.ts +0 -7
  76. package/src/tests/IO/VIA.test.ts +6 -12
  77. package/src/tests/IO/Video.test.ts +7 -8
@@ -8,21 +8,8 @@ describe('Empty', () => {
8
8
  })
9
9
 
10
10
  describe('Initialization', () => {
11
- it('should have IRQ callback', () => {
12
- expect(typeof empty.raiseIRQ).toBe('function')
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 bank control register and return current bank', () => {
62
- ramCard.currentBank = 0
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.currentBank = 42
61
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 42)
66
62
  expect(ramCard.read(RAMBank.BANK_CONTROL_REGISTER)).toBe(42)
67
63
 
68
- ramCard.currentBank = 255
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', () => {
@@ -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(mockIRQ).toHaveBeenCalledTimes(1)
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(mockIRQ).toHaveBeenCalledTimes(1)
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 raise NMI and clear WDE when WDS=1', () => {
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(mockNMI).toHaveBeenCalledTimes(1)
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 processes 128 SID clock cycles internally
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 have raiseIRQ and raiseNMI callbacks', () => {
636
- expect(sid.raiseIRQ).toBeDefined()
637
- expect(sid.raiseNMI).toBeDefined()
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
 
@@ -374,31 +374,25 @@ describe('VIA (6522 VIA)', () => {
374
374
  })
375
375
 
376
376
  describe('IRQ Generation', () => {
377
- it('should call raiseIRQ when enabled interrupt is triggered', () => {
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(mockIRQ).toHaveBeenCalled()
385
+ expect(result & 0x80).toBe(0x80)
389
386
  })
390
387
 
391
- it('should not call raiseIRQ when interrupt is not enabled', () => {
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(mockIRQ).not.toHaveBeenCalled()
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
- // At 2MHz: cyclesPerFrame 33333, each tick = 128 cycles → ~261 ticks/frame
83
- const ticksPerFrame = Math.ceil((frequency / 60) / 128)
84
- for (let i = 0; i < ticksPerFrame; 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 call raiseIRQ when interrupt flag is set', () => {
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
- expect(irqFn).toHaveBeenCalled()
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