ac6502 1.2.0 → 1.4.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 (182) hide show
  1. package/README.md +12 -12
  2. package/dist/components/CPU.d.ts +162 -0
  3. package/dist/components/Cart.d.ts +9 -0
  4. package/dist/components/IO/ACIA.d.ts +76 -0
  5. package/dist/components/IO/ACIA.js +282 -0
  6. package/dist/components/IO/ACIA.js.map +1 -0
  7. package/dist/components/IO/Attachments/Attachment.d.ts +112 -0
  8. package/dist/components/IO/Attachments/Attachment.js +71 -0
  9. package/dist/components/IO/Attachments/Attachment.js.map +1 -0
  10. package/dist/components/IO/Attachments/JoystickAttachment.d.ts +53 -0
  11. package/dist/components/IO/Attachments/JoystickAttachment.js +90 -0
  12. package/dist/components/IO/Attachments/JoystickAttachment.js.map +1 -0
  13. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.d.ts +63 -0
  14. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js +489 -0
  15. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js.map +1 -0
  16. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.d.ts +44 -0
  17. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.js +274 -0
  18. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.js.map +1 -0
  19. package/dist/components/IO/Attachments/KeypadAttachment.d.ts +47 -0
  20. package/dist/components/IO/Attachments/KeypadAttachment.js +141 -0
  21. package/dist/components/IO/Attachments/KeypadAttachment.js.map +1 -0
  22. package/dist/components/IO/Attachments/LCDAttachment.d.ts +110 -0
  23. package/dist/components/IO/Attachments/LCDAttachment.js +716 -0
  24. package/dist/components/IO/Attachments/LCDAttachment.js.map +1 -0
  25. package/dist/components/IO/Attachments/SNESAttachment.d.ts +85 -0
  26. package/dist/components/IO/Attachments/SNESAttachment.js +184 -0
  27. package/dist/components/IO/Attachments/SNESAttachment.js.map +1 -0
  28. package/dist/components/IO/DevOutputBoard.d.ts +19 -0
  29. package/dist/components/IO/DevOutputBoard.js +33 -0
  30. package/dist/components/IO/DevOutputBoard.js.map +1 -0
  31. package/dist/components/IO/Empty.d.ts +9 -0
  32. package/dist/components/IO/Empty.js +5 -7
  33. package/dist/components/IO/Empty.js.map +1 -1
  34. package/dist/components/IO/EmptyCard.d.ts +9 -0
  35. package/dist/components/IO/GPIOAttachments/GPIOAttachment.d.ts +112 -0
  36. package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.d.ts +53 -0
  37. package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.d.ts +63 -0
  38. package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.d.ts +44 -0
  39. package/dist/components/IO/GPIOAttachments/GPIOKeypadAttachment.d.ts +47 -0
  40. package/dist/components/IO/GPIOAttachments/GPIOLCDAttachment.d.ts +110 -0
  41. package/dist/components/IO/GPIOCard.d.ts +105 -0
  42. package/dist/components/IO/GPIOCard.js.map +1 -1
  43. package/dist/components/IO/RAMBank.d.ts +37 -0
  44. package/dist/components/IO/RAMBank.js +63 -0
  45. package/dist/components/IO/RAMBank.js.map +1 -0
  46. package/dist/components/IO/RAMCard.d.ts +37 -0
  47. package/dist/components/IO/RTC.d.ts +107 -0
  48. package/dist/components/IO/RTC.js +483 -0
  49. package/dist/components/IO/RTC.js.map +1 -0
  50. package/dist/components/IO/RTCCard.d.ts +107 -0
  51. package/dist/components/IO/SerialCard.d.ts +76 -0
  52. package/dist/components/IO/Sound.d.ts +120 -0
  53. package/dist/components/IO/Sound.js +622 -0
  54. package/dist/components/IO/Sound.js.map +1 -0
  55. package/dist/components/IO/SoundCard.d.ts +120 -0
  56. package/dist/components/IO/Storage.d.ts +74 -0
  57. package/dist/components/IO/Storage.js +409 -0
  58. package/dist/components/IO/Storage.js.map +1 -0
  59. package/dist/components/IO/StorageCard.d.ts +74 -0
  60. package/dist/components/IO/Terminal.d.ts +19 -0
  61. package/dist/components/IO/Terminal.js +33 -0
  62. package/dist/components/IO/Terminal.js.map +1 -0
  63. package/dist/components/IO/VIA.d.ts +105 -0
  64. package/dist/components/IO/VIA.js +597 -0
  65. package/dist/components/IO/VIA.js.map +1 -0
  66. package/dist/components/IO/Video.d.ts +141 -0
  67. package/dist/components/IO/Video.js +630 -0
  68. package/dist/components/IO/Video.js.map +1 -0
  69. package/dist/components/IO/VideoCard.d.ts +141 -0
  70. package/dist/components/IO.d.ts +8 -0
  71. package/dist/components/Machine.d.ts +62 -0
  72. package/dist/components/Machine.js +260 -153
  73. package/dist/components/Machine.js.map +1 -1
  74. package/dist/components/RAM.d.ts +9 -0
  75. package/dist/components/ROM.d.ts +9 -0
  76. package/dist/index.d.ts +2 -0
  77. package/dist/index.js +61 -28
  78. package/dist/index.js.map +1 -1
  79. package/dist/lib.d.ts +22 -0
  80. package/dist/lib.js +47 -0
  81. package/dist/lib.js.map +1 -0
  82. package/dist/tests/CPU.test.d.ts +1 -0
  83. package/dist/tests/Cart.test.d.ts +1 -0
  84. package/dist/tests/IO/ACIA.test.d.ts +1 -0
  85. package/dist/tests/IO/ACIA.test.js +423 -0
  86. package/dist/tests/IO/ACIA.test.js.map +1 -0
  87. package/dist/tests/IO/Attachments/Attachment.test.d.ts +1 -0
  88. package/dist/tests/IO/Attachments/Attachment.test.js +339 -0
  89. package/dist/tests/IO/Attachments/Attachment.test.js.map +1 -0
  90. package/dist/tests/IO/Attachments/JoystickAttachment.test.d.ts +1 -0
  91. package/dist/tests/IO/Attachments/JoystickAttachment.test.js +126 -0
  92. package/dist/tests/IO/Attachments/JoystickAttachment.test.js.map +1 -0
  93. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.d.ts +1 -0
  94. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js +779 -0
  95. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js.map +1 -0
  96. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.d.ts +1 -0
  97. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.js +355 -0
  98. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.js.map +1 -0
  99. package/dist/tests/IO/Attachments/KeypadAttachment.test.d.ts +1 -0
  100. package/dist/tests/IO/Attachments/KeypadAttachment.test.js +323 -0
  101. package/dist/tests/IO/Attachments/KeypadAttachment.test.js.map +1 -0
  102. package/dist/tests/IO/Attachments/LCDAttachment.test.d.ts +1 -0
  103. package/dist/tests/IO/Attachments/LCDAttachment.test.js +627 -0
  104. package/dist/tests/IO/Attachments/LCDAttachment.test.js.map +1 -0
  105. package/dist/tests/IO/Attachments/SNESAttachment.test.d.ts +1 -0
  106. package/dist/tests/IO/Attachments/SNESAttachment.test.js +331 -0
  107. package/dist/tests/IO/Attachments/SNESAttachment.test.js.map +1 -0
  108. package/dist/tests/IO/Empty.test.d.ts +1 -0
  109. package/dist/tests/IO/Empty.test.js +121 -0
  110. package/dist/tests/IO/Empty.test.js.map +1 -0
  111. package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.d.ts +1 -0
  112. package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.d.ts +1 -0
  113. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.d.ts +1 -0
  114. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.d.ts +1 -0
  115. package/dist/tests/IO/GPIOAttachments/GPIOKeypadAttachment.test.d.ts +1 -0
  116. package/dist/tests/IO/GPIOAttachments/GPIOLCDAttachment.test.d.ts +1 -0
  117. package/dist/tests/IO/GPIOCard.test.d.ts +1 -0
  118. package/dist/tests/IO/GPIOCard.test.js.map +1 -1
  119. package/dist/tests/IO/RAMBank.test.d.ts +1 -0
  120. package/dist/tests/IO/RAMBank.test.js +229 -0
  121. package/dist/tests/IO/RAMBank.test.js.map +1 -0
  122. package/dist/tests/IO/RAMCard.test.d.ts +1 -0
  123. package/dist/tests/IO/RTC.test.d.ts +1 -0
  124. package/dist/tests/IO/RTC.test.js +177 -0
  125. package/dist/tests/IO/RTC.test.js.map +1 -0
  126. package/dist/tests/IO/RTCCard.test.d.ts +1 -0
  127. package/dist/tests/IO/SerialCard.test.d.ts +1 -0
  128. package/dist/tests/IO/Sound.test.d.ts +1 -0
  129. package/dist/tests/IO/Sound.test.js +528 -0
  130. package/dist/tests/IO/Sound.test.js.map +1 -0
  131. package/dist/tests/IO/SoundCard.test.d.ts +1 -0
  132. package/dist/tests/IO/Storage.test.d.ts +1 -0
  133. package/dist/tests/IO/Storage.test.js +656 -0
  134. package/dist/tests/IO/Storage.test.js.map +1 -0
  135. package/dist/tests/IO/StorageCard.test.d.ts +1 -0
  136. package/dist/tests/IO/VIA.test.d.ts +1 -0
  137. package/dist/tests/IO/VIA.test.js +503 -0
  138. package/dist/tests/IO/VIA.test.js.map +1 -0
  139. package/dist/tests/IO/Video.test.d.ts +1 -0
  140. package/dist/tests/IO/Video.test.js +549 -0
  141. package/dist/tests/IO/Video.test.js.map +1 -0
  142. package/dist/tests/IO/VideoCard.test.d.ts +1 -0
  143. package/dist/tests/Machine.test.d.ts +1 -0
  144. package/dist/tests/Machine.test.js +27 -42
  145. package/dist/tests/Machine.test.js.map +1 -1
  146. package/dist/tests/RAM.test.d.ts +1 -0
  147. package/dist/tests/ROM.test.d.ts +1 -0
  148. package/package.json +5 -3
  149. package/src/components/IO/{SerialCard.ts → ACIA.ts} +2 -2
  150. package/src/components/IO/{GPIOAttachments/GPIOAttachment.ts → Attachments/Attachment.ts} +2 -2
  151. package/src/components/IO/{GPIOAttachments/GPIOJoystickAttachment.ts → Attachments/JoystickAttachment.ts} +3 -3
  152. package/src/components/IO/{GPIOAttachments/GPIOKeyboardEncoderAttachment.ts → Attachments/KeyboardEncoderAttachment.ts} +3 -3
  153. package/src/components/IO/{GPIOAttachments/GPIOKeyboardMatrixAttachment.ts → Attachments/KeyboardMatrixAttachment.ts} +5 -5
  154. package/src/components/IO/{GPIOAttachments/GPIOKeypadAttachment.ts → Attachments/KeypadAttachment.ts} +3 -3
  155. package/src/components/IO/{GPIOAttachments/GPIOLCDAttachment.ts → Attachments/LCDAttachment.ts} +7 -7
  156. package/src/components/IO/{EmptyCard.ts → Empty.ts} +1 -1
  157. package/src/components/IO/{RAMCard.ts → RAMBank.ts} +8 -8
  158. package/src/components/IO/{RTCCard.ts → RTC.ts} +1 -1
  159. package/src/components/IO/{SoundCard.ts → Sound.ts} +2 -2
  160. package/src/components/IO/{StorageCard.ts → Storage.ts} +70 -73
  161. package/src/components/IO/Terminal.ts +34 -0
  162. package/src/components/IO/{GPIOCard.ts → VIA.ts} +64 -64
  163. package/src/components/IO/{VideoCard.ts → Video.ts} +1 -1
  164. package/src/components/Machine.ts +286 -160
  165. package/src/index.ts +65 -35
  166. package/src/lib.ts +27 -0
  167. package/src/tests/IO/{SerialCard.test.ts → ACIA.test.ts} +5 -5
  168. package/src/tests/IO/{GPIOAttachments/GPIOAttachment.test.ts → Attachments/Attachment.test.ts} +12 -12
  169. package/src/tests/IO/{GPIOAttachments/GPIOJoystickAttachment.test.ts → Attachments/JoystickAttachment.test.ts} +23 -23
  170. package/src/tests/IO/{GPIOAttachments/GPIOKeyboardEncoderAttachment.test.ts → Attachments/KeyboardEncoderAttachment.test.ts} +4 -4
  171. package/src/tests/IO/{GPIOAttachments/GPIOKeyboardMatrixAttachment.test.ts → Attachments/KeyboardMatrixAttachment.test.ts} +5 -5
  172. package/src/tests/IO/{GPIOAttachments/GPIOKeypadAttachment.test.ts → Attachments/KeypadAttachment.test.ts} +38 -38
  173. package/src/tests/IO/{GPIOAttachments/GPIOLCDAttachment.test.ts → Attachments/LCDAttachment.test.ts} +12 -12
  174. package/src/tests/IO/Empty.test.ts +143 -0
  175. package/src/tests/IO/{RAMCard.test.ts → RAMBank.test.ts} +33 -33
  176. package/src/tests/IO/{RTCCard.test.ts → RTC.test.ts} +6 -6
  177. package/src/tests/IO/{SoundCard.test.ts → Sound.test.ts} +6 -6
  178. package/src/tests/IO/{StorageCard.test.ts → Storage.test.ts} +34 -25
  179. package/src/tests/IO/{GPIOCard.test.ts → VIA.test.ts} +7 -7
  180. package/src/tests/IO/{VideoCard.test.ts → Video.test.ts} +13 -13
  181. package/src/tests/Machine.test.ts +31 -38
  182. package/tsconfig.json +1 -0
@@ -1,14 +1,14 @@
1
- import { StorageCard } from '../../components/IO/StorageCard'
1
+ import { Storage } from '../../components/IO/Storage'
2
2
  import { writeFile, unlink, readFile } from 'fs/promises'
3
3
  import { existsSync } from 'fs'
4
4
  import { tmpdir } from 'os'
5
5
  import { join } from 'path'
6
6
 
7
- describe('StorageCard (Compact Flash in IDE Mode)', () => {
8
- let storageCard: StorageCard
7
+ describe('Storage (Compact Flash in IDE Mode)', () => {
8
+ let storageCard: Storage
9
9
 
10
10
  beforeEach(() => {
11
- storageCard = new StorageCard()
11
+ storageCard = new Storage()
12
12
  })
13
13
 
14
14
  describe('Initialization', () => {
@@ -587,7 +587,8 @@ describe('StorageCard (Compact Flash in IDE Mode)', () => {
587
587
  }
588
588
 
589
589
  // Save to file
590
- await storageCard.saveToFile(testFile)
590
+ const storageData = storageCard.getData()
591
+ await writeFile(testFile, storageData)
591
592
 
592
593
  // Verify file exists
593
594
  expect(existsSync(testFile)).toBe(true)
@@ -609,7 +610,8 @@ describe('StorageCard (Compact Flash in IDE Mode)', () => {
609
610
  }
610
611
  }
611
612
 
612
- await storageCard.saveToFile(testFile)
613
+ const storageData = storageCard.getData()
614
+ await writeFile(testFile, storageData)
613
615
 
614
616
  // Read file directly and verify
615
617
  const fileData = await readFile(testFile)
@@ -639,7 +641,8 @@ describe('StorageCard (Compact Flash in IDE Mode)', () => {
639
641
  await writeFile(testFile, testData)
640
642
 
641
643
  // Load into storage card
642
- await storageCard.loadFromFile(testFile)
644
+ const fileData = await readFile(testFile)
645
+ storageCard.loadData(new Uint8Array(fileData))
643
646
 
644
647
  // Verify data was loaded
645
648
  storageCard.write(0x02, 1)
@@ -651,9 +654,9 @@ describe('StorageCard (Compact Flash in IDE Mode)', () => {
651
654
  }
652
655
  })
653
656
 
654
- it('should handle non-existent file gracefully', async () => {
655
- // Try to load from a file that doesn't exist
656
- await expect(storageCard.loadFromFile(nonExistentFile)).resolves.not.toThrow()
657
+ it('should handle non-existent file gracefully', () => {
658
+ // Load with null data
659
+ storageCard.loadData(null)
657
660
 
658
661
  // Storage should remain empty (zeros)
659
662
  storageCard.write(0x02, 1)
@@ -670,7 +673,8 @@ describe('StorageCard (Compact Flash in IDE Mode)', () => {
670
673
  const smallData = Buffer.alloc(1024, 0xFF) // Only 1KB
671
674
  await writeFile(invalidSizeFile, smallData)
672
675
 
673
- await storageCard.loadFromFile(invalidSizeFile)
676
+ const fileData = await readFile(invalidSizeFile)
677
+ storageCard.loadData(new Uint8Array(fileData))
674
678
 
675
679
  // Storage should remain empty (zeros)
676
680
  storageCard.write(0x02, 1)
@@ -693,7 +697,8 @@ describe('StorageCard (Compact Flash in IDE Mode)', () => {
693
697
  }
694
698
 
695
699
  await writeFile(testFile, testData)
696
- await storageCard.loadFromFile(testFile)
700
+ const fileData = await readFile(testFile)
701
+ storageCard.loadData(new Uint8Array(fileData))
697
702
 
698
703
  // Verify each sector
699
704
  for (let sector = 0; sector < 10; sector++) {
@@ -722,20 +727,22 @@ describe('StorageCard (Compact Flash in IDE Mode)', () => {
722
727
  }
723
728
 
724
729
  // Save to file
725
- await storageCard.saveToFile(testFile)
730
+ const storageData = storageCard.getData()
731
+ await writeFile(testFile, storageData)
726
732
 
727
733
  // Create new storage card and load
728
- const newStorageCard = new StorageCard()
729
- await newStorageCard.loadFromFile(testFile)
734
+ const newStorage = new Storage()
735
+ const savedData = await readFile(testFile)
736
+ newStorage.loadData(new Uint8Array(savedData))
730
737
 
731
738
  // Verify all sectors match
732
739
  for (let sector = 0; sector < 100; sector++) {
733
- newStorageCard.write(0x02, 1)
734
- newStorageCard.write(0x03, sector)
735
- newStorageCard.write(0x07, 0x20)
740
+ newStorage.write(0x02, 1)
741
+ newStorage.write(0x03, sector)
742
+ newStorage.write(0x07, 0x20)
736
743
 
737
744
  for (let i = 0; i < 512; i++) {
738
- expect(newStorageCard.read(0x00)).toBe(((sector * 7 + i * 3) ^ 0x55) & 0xFF)
745
+ expect(newStorage.read(0x00)).toBe(((sector * 7 + i * 3) ^ 0x55) & 0xFF)
739
746
  }
740
747
  }
741
748
  })
@@ -748,22 +755,24 @@ describe('StorageCard (Compact Flash in IDE Mode)', () => {
748
755
  for (let i = 0; i < 512; i++) {
749
756
  storageCard.write(0x00, 0xCC)
750
757
  }
751
- await storageCard.saveToFile(testFile)
758
+ await writeFile(testFile, storageCard.getData())
752
759
 
753
760
  // Load and modify
754
- const card2 = new StorageCard()
755
- await card2.loadFromFile(testFile)
761
+ const card2 = new Storage()
762
+ let fileData = await readFile(testFile)
763
+ card2.loadData(new Uint8Array(fileData))
756
764
  card2.write(0x02, 1)
757
765
  card2.write(0x03, 43)
758
766
  card2.write(0x07, 0x30)
759
767
  for (let i = 0; i < 512; i++) {
760
768
  card2.write(0x00, 0xDD)
761
769
  }
762
- await card2.saveToFile(testFile)
770
+ await writeFile(testFile, card2.getData())
763
771
 
764
772
  // Load again and verify both sectors
765
- const card3 = new StorageCard()
766
- await card3.loadFromFile(testFile)
773
+ const card3 = new Storage()
774
+ fileData = await readFile(testFile)
775
+ card3.loadData(new Uint8Array(fileData))
767
776
 
768
777
  // Check sector 42
769
778
  card3.write(0x02, 1)
@@ -1,5 +1,5 @@
1
- import { GPIOCard } from '../../components/IO/GPIOCard'
2
- import { GPIOAttachment } from '../../components/IO/GPIOAttachments/GPIOAttachment'
1
+ import { VIA } from '../../components/IO/VIA'
2
+ import { Attachment } from '../../components/IO/Attachments/Attachment'
3
3
 
4
4
  /**
5
5
  * Helper function to create a mock GPIO attachment
@@ -13,7 +13,7 @@ const createMockAttachment = (options: {
13
13
  ca2Interrupt?: boolean
14
14
  cb1Interrupt?: boolean
15
15
  cb2Interrupt?: boolean
16
- } = {}): GPIOAttachment => {
16
+ } = {}): Attachment => {
17
17
  const {
18
18
  priority = 0,
19
19
  enabled = true,
@@ -46,14 +46,14 @@ const createMockAttachment = (options: {
46
46
  // Helper method to update values (not part of interface)
47
47
  setPortAValue: (value: number) => { currentPortAValue = value },
48
48
  setPortBValue: (value: number) => { currentPortBValue = value },
49
- } as GPIOAttachment & { setPortAValue: (v: number) => void; setPortBValue: (v: number) => void }
49
+ } as Attachment & { setPortAValue: (v: number) => void; setPortBValue: (v: number) => void }
50
50
  }
51
51
 
52
- describe('GPIOCard (65C22 VIA)', () => {
53
- let gpio: GPIOCard
52
+ describe('VIA (65C22 VIA)', () => {
53
+ let gpio: VIA
54
54
 
55
55
  beforeEach(() => {
56
- gpio = new GPIOCard()
56
+ gpio = new VIA()
57
57
  })
58
58
 
59
59
  describe('Initialization', () => {
@@ -1,9 +1,9 @@
1
- import { VideoCard, TmsMode, TmsColor } from '../../components/IO/VideoCard'
1
+ import { Video, TmsMode, TmsColor } from '../../components/IO/Video'
2
2
 
3
3
  /**
4
4
  * Helper: write a register value through the control port (two-stage write)
5
5
  */
6
- const writeRegister = (vdp: VideoCard, reg: number, value: number): void => {
6
+ const writeRegister = (vdp: Video, reg: number, value: number): void => {
7
7
  vdp.write(1, value) // Stage 0: register value
8
8
  vdp.write(1, 0x80 | reg) // Stage 1: register number with bit 7 set
9
9
  }
@@ -11,7 +11,7 @@ const writeRegister = (vdp: VideoCard, reg: number, value: number): void => {
11
11
  /**
12
12
  * Helper: set VRAM write address through the control port
13
13
  */
14
- const setWriteAddress = (vdp: VideoCard, addr: number): void => {
14
+ const setWriteAddress = (vdp: Video, addr: number): void => {
15
15
  vdp.write(1, addr & 0xFF) // Stage 0: address low byte
16
16
  vdp.write(1, ((addr >> 8) & 0x3F) | 0x40) // Stage 1: address high + write flag
17
17
  }
@@ -19,7 +19,7 @@ const setWriteAddress = (vdp: VideoCard, addr: number): void => {
19
19
  /**
20
20
  * Helper: set VRAM read address through the control port
21
21
  */
22
- const setReadAddress = (vdp: VideoCard, addr: number): void => {
22
+ const setReadAddress = (vdp: Video, addr: number): void => {
23
23
  vdp.write(1, addr & 0xFF) // Stage 0: address low byte
24
24
  vdp.write(1, (addr >> 8) & 0x3F) // Stage 1: address high (no write flag)
25
25
  }
@@ -27,7 +27,7 @@ const setReadAddress = (vdp: VideoCard, addr: number): void => {
27
27
  /**
28
28
  * Helper: write a sequence of bytes to VRAM starting at an address
29
29
  */
30
- const writeVramBytes = (vdp: VideoCard, addr: number, bytes: number[]): void => {
30
+ const writeVramBytes = (vdp: Video, addr: number, bytes: number[]): void => {
31
31
  setWriteAddress(vdp, addr)
32
32
  for (const b of bytes) {
33
33
  vdp.write(0, b) // Data port
@@ -37,7 +37,7 @@ const writeVramBytes = (vdp: VideoCard, addr: number, bytes: number[]): void =>
37
37
  /**
38
38
  * Helper: setup Graphics I mode with standard table addresses
39
39
  */
40
- const setupGraphicsI = (vdp: VideoCard): void => {
40
+ const setupGraphicsI = (vdp: Video): void => {
41
41
  writeRegister(vdp, 0, 0x00) // No external VDP, Graphics I
42
42
  writeRegister(vdp, 1, 0x60) // 16K, display active, interrupts enabled, Graphics I
43
43
  writeRegister(vdp, 2, 0x0E) // Name table at 0x3800
@@ -51,7 +51,7 @@ const setupGraphicsI = (vdp: VideoCard): void => {
51
51
  /**
52
52
  * Helper: setup Graphics II mode with standard table addresses
53
53
  */
54
- const setupGraphicsII = (vdp: VideoCard): void => {
54
+ const setupGraphicsII = (vdp: Video): void => {
55
55
  writeRegister(vdp, 0, 0x02) // Graphics II mode
56
56
  writeRegister(vdp, 1, 0x60) // 16K, display active, interrupts enabled
57
57
  writeRegister(vdp, 2, 0x0E) // Name table at 0x3800
@@ -65,7 +65,7 @@ const setupGraphicsII = (vdp: VideoCard): void => {
65
65
  /**
66
66
  * Helper: setup Text mode
67
67
  */
68
- const setupTextMode = (vdp: VideoCard): void => {
68
+ const setupTextMode = (vdp: Video): void => {
69
69
  writeRegister(vdp, 0, 0x00) // No external VDP
70
70
  writeRegister(vdp, 1, 0x70) // 16K, display active, interrupts, Text mode
71
71
  writeRegister(vdp, 2, 0x0E) // Name table at 0x3800
@@ -78,7 +78,7 @@ const setupTextMode = (vdp: VideoCard): void => {
78
78
  * Must not overshoot into the next frame (scanline 0 of the next
79
79
  * frame clears the status register during sprite processing).
80
80
  */
81
- const renderOneFrame = (vdp: VideoCard, frequency: number = 2000000): void => {
81
+ const renderOneFrame = (vdp: Video, frequency: number = 2000000): void => {
82
82
  // At 2MHz: cyclesPerFrame ≈ 33333, each tick = 128 cycles → ~261 ticks/frame
83
83
  const ticksPerFrame = Math.ceil((frequency / 60) / 128)
84
84
  for (let i = 0; i < ticksPerFrame; i++) {
@@ -89,7 +89,7 @@ const renderOneFrame = (vdp: VideoCard, frequency: number = 2000000): void => {
89
89
  /**
90
90
  * Helper: clear sprite attribute table (set all Y positions to 0xD0 = stop)
91
91
  */
92
- const clearSprites = (vdp: VideoCard, spriteAttrAddr: number = 0x3B00): void => {
92
+ const clearSprites = (vdp: Video, spriteAttrAddr: number = 0x3B00): void => {
93
93
  setWriteAddress(vdp, spriteAttrAddr)
94
94
  for (let i = 0; i < 32; i++) {
95
95
  vdp.write(0, 0xD0) // Y = stop sentinel
@@ -99,11 +99,11 @@ const clearSprites = (vdp: VideoCard, spriteAttrAddr: number = 0x3B00): void =>
99
99
  }
100
100
  }
101
101
 
102
- describe('VideoCard (TMS9918 VDP)', () => {
103
- let vdp: VideoCard
102
+ describe('Video (TMS9918 VDP)', () => {
103
+ let vdp: Video
104
104
 
105
105
  beforeEach(() => {
106
- vdp = new VideoCard()
106
+ vdp = new Video()
107
107
  })
108
108
 
109
109
  // ================================================================
@@ -2,17 +2,18 @@ import { Machine } from '../components/Machine'
2
2
  import { RAM } from '../components/RAM'
3
3
  import { ROM } from '../components/ROM'
4
4
  import { Cart } from '../components/Cart'
5
+ import { ACIA } from '../components/IO/ACIA'
5
6
 
6
7
  describe('Machine', () => {
7
8
  let machine: Machine
8
9
 
9
10
  beforeEach(() => {
10
- machine = new Machine()
11
+ machine = new Machine('cob')
11
12
  })
12
13
 
13
14
  afterEach(() => {
14
15
  // Ensure the machine loop is stopped after each test
15
- machine.end()
16
+ machine.stop()
16
17
  })
17
18
 
18
19
  describe('Initialization', () => {
@@ -22,7 +23,6 @@ describe('Machine', () => {
22
23
  })
23
24
 
24
25
  test('Machine initializes with correct default properties', () => {
25
- expect(machine.isAlive).toBe(false)
26
26
  expect(machine.isRunning).toBe(false)
27
27
  expect(machine.frequency).toBe(2000000)
28
28
  expect(machine.scale).toBe(2)
@@ -54,26 +54,11 @@ describe('Machine', () => {
54
54
  })
55
55
 
56
56
  describe('State Management', () => {
57
- test('start() sets isRunning and isAlive to true', () => {
58
- expect(machine.isRunning).toBe(false)
59
- expect(machine.isAlive).toBe(false)
60
- machine.start()
61
- expect(machine.isRunning).toBe(true)
62
- expect(machine.isAlive).toBe(true)
63
- })
64
-
65
- test('end() sets isRunning and isAlive to false', () => {
66
- machine.start()
67
- expect(machine.isRunning).toBe(true)
68
- machine.end()
69
- expect(machine.isRunning).toBe(false)
70
- expect(machine.isAlive).toBe(false)
71
- })
72
-
73
57
  test('run() sets isRunning to true', () => {
74
58
  expect(machine.isRunning).toBe(false)
75
59
  machine.run()
76
60
  expect(machine.isRunning).toBe(true)
61
+ machine.stop() // Cleanup
77
62
  })
78
63
 
79
64
  test('stop() sets isRunning to false', () => {
@@ -215,19 +200,19 @@ describe('Machine', () => {
215
200
  expect(machine.cart).toBeUndefined()
216
201
  })
217
202
 
218
- test('loadCart should load cart data', async () => {
219
- // Create a mock cart file path - this will test the error handling
220
- await machine.loadCart('/nonexistent/path.bin')
221
- // The machine should handle the error gracefully
222
- expect(machine.cart).toBeUndefined()
203
+ test('loadCart should load cart data', () => {
204
+ // Load cart with test data
205
+ const testData = new Uint8Array(16384).fill(0xEA) // NOP instruction
206
+ machine.loadCart(testData)
207
+ expect(machine.cart).toBeDefined()
223
208
  })
224
209
  })
225
210
 
226
211
  describe('ROM Operations', () => {
227
- test('loadROM should load ROM data', async () => {
228
- // Create a mock ROM file path - this will test the error handling
229
- await machine.loadROM('/nonexistent/path.bin')
230
- // The machine should handle the error gracefully
212
+ test('loadROM should load ROM data', () => {
213
+ // Load ROM with test data
214
+ const testData = new Uint8Array(16384).fill(0xEA) // NOP instruction
215
+ machine.loadROM(testData)
231
216
  expect(machine.rom).toBeDefined()
232
217
  })
233
218
  })
@@ -266,15 +251,18 @@ describe('Machine', () => {
266
251
 
267
252
  describe('Input Handling', () => {
268
253
  test('onReceive() passes data to Serial card', () => {
269
- const spy = jest.spyOn(machine.io5, 'onData')
254
+ const acia = machine.io5 as ACIA
255
+ const spy = jest.spyOn(acia, 'onData')
270
256
  machine.onReceive(0x41)
271
257
  expect(spy).toHaveBeenCalledWith(0x41)
272
258
  spy.mockRestore()
273
259
  })
274
260
 
275
261
  test('onKeyDown() routes key to GPIO attachments', () => {
276
- const matrixSpy = jest.spyOn(machine.keyboardMatrixAttachment, 'updateKey')
277
- const encoderSpy = jest.spyOn(machine.keyboardEncoderAttachment, 'updateKey')
262
+ expect(machine.keyboardMatrixAttachment).toBeDefined()
263
+ expect(machine.keyboardEncoderAttachment).toBeDefined()
264
+ const matrixSpy = jest.spyOn(machine.keyboardMatrixAttachment!, 'updateKey')
265
+ const encoderSpy = jest.spyOn(machine.keyboardEncoderAttachment!, 'updateKey')
278
266
  machine.onKeyDown(0x52) // Arrow Up USB HID keycode
279
267
  expect(matrixSpy).toHaveBeenCalledWith(0x52, true)
280
268
  expect(encoderSpy).toHaveBeenCalledWith(0x52, true)
@@ -283,8 +271,10 @@ describe('Machine', () => {
283
271
  })
284
272
 
285
273
  test('onKeyUp() routes key to GPIO attachments', () => {
286
- const matrixSpy = jest.spyOn(machine.keyboardMatrixAttachment, 'updateKey')
287
- const encoderSpy = jest.spyOn(machine.keyboardEncoderAttachment, 'updateKey')
274
+ expect(machine.keyboardMatrixAttachment).toBeDefined()
275
+ expect(machine.keyboardEncoderAttachment).toBeDefined()
276
+ const matrixSpy = jest.spyOn(machine.keyboardMatrixAttachment!, 'updateKey')
277
+ const encoderSpy = jest.spyOn(machine.keyboardEncoderAttachment!, 'updateKey')
288
278
  machine.onKeyUp(0x52) // Arrow Up USB HID keycode
289
279
  expect(matrixSpy).toHaveBeenCalledWith(0x52, false)
290
280
  expect(encoderSpy).toHaveBeenCalledWith(0x52, false)
@@ -293,14 +283,16 @@ describe('Machine', () => {
293
283
  })
294
284
 
295
285
  test('onJoystickA() routes button state to joystick A attachment', () => {
296
- const spy = jest.spyOn(machine.joystickAttachmentA, 'updateJoystick')
286
+ expect(machine.joystickAttachmentA).toBeDefined()
287
+ const spy = jest.spyOn(machine.joystickAttachmentA!, 'updateJoystick')
297
288
  machine.onJoystickA(0xFF)
298
289
  expect(spy).toHaveBeenCalledWith(0xFF)
299
290
  spy.mockRestore()
300
291
  })
301
292
 
302
293
  test('onJoystickB() routes button state to joystick B attachment', () => {
303
- const spy = jest.spyOn(machine.joystickAttachmentB, 'updateJoystick')
294
+ expect(machine.joystickAttachmentB).toBeDefined()
295
+ const spy = jest.spyOn(machine.joystickAttachmentB!, 'updateJoystick')
304
296
  machine.onJoystickB(0xFF)
305
297
  expect(spy).toHaveBeenCalledWith(0xFF)
306
298
  spy.mockRestore()
@@ -374,11 +366,12 @@ describe('Machine', () => {
374
366
  expect(machine.render).toBe(mockRender)
375
367
  })
376
368
 
377
- test('SerialCard uses transmit callback when set', () => {
369
+ test('ACIA uses transmit callback when set', () => {
378
370
  const mockTransmit = jest.fn()
379
371
  machine.transmit = mockTransmit
380
- // Trigger SerialCard to transmit if possible
381
- machine.io5.transmit?.(0x41)
372
+ // Trigger ACIA to transmit if possible
373
+ const acia = machine.io5 as ACIA
374
+ acia.transmit?.(0x41)
382
375
  expect(mockTransmit).toHaveBeenCalledWith(0x41)
383
376
  })
384
377
  })
package/tsconfig.json CHANGED
@@ -6,6 +6,7 @@
6
6
  "target": "es6",
7
7
  "module": "commonjs",
8
8
  "sourceMap": true,
9
+ "declaration": true,
9
10
  "esModuleInterop": true,
10
11
  "moduleResolution": "node"
11
12
  }