ac6502 1.3.0 → 1.4.1

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 (141) hide show
  1. package/README.md +139 -32
  2. package/dist/components/IO/ACIA.d.ts +76 -0
  3. package/dist/components/IO/ACIA.js +282 -0
  4. package/dist/components/IO/ACIA.js.map +1 -0
  5. package/dist/components/IO/Attachments/Attachment.d.ts +112 -0
  6. package/dist/components/IO/Attachments/Attachment.js +71 -0
  7. package/dist/components/IO/Attachments/Attachment.js.map +1 -0
  8. package/dist/components/IO/Attachments/JoystickAttachment.d.ts +53 -0
  9. package/dist/components/IO/Attachments/JoystickAttachment.js +90 -0
  10. package/dist/components/IO/Attachments/JoystickAttachment.js.map +1 -0
  11. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.d.ts +63 -0
  12. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js +489 -0
  13. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js.map +1 -0
  14. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.d.ts +44 -0
  15. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.js +274 -0
  16. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.js.map +1 -0
  17. package/dist/components/IO/Attachments/KeypadAttachment.d.ts +47 -0
  18. package/dist/components/IO/Attachments/KeypadAttachment.js +141 -0
  19. package/dist/components/IO/Attachments/KeypadAttachment.js.map +1 -0
  20. package/dist/components/IO/Attachments/LCDAttachment.d.ts +110 -0
  21. package/dist/components/IO/Attachments/LCDAttachment.js +716 -0
  22. package/dist/components/IO/Attachments/LCDAttachment.js.map +1 -0
  23. package/dist/components/IO/Attachments/SNESAttachment.d.ts +85 -0
  24. package/dist/components/IO/Attachments/SNESAttachment.js +184 -0
  25. package/dist/components/IO/Attachments/SNESAttachment.js.map +1 -0
  26. package/dist/components/IO/Empty.d.ts +9 -0
  27. package/dist/components/IO/Empty.js +5 -7
  28. package/dist/components/IO/Empty.js.map +1 -1
  29. package/dist/components/IO/GPIOCard.d.ts +5 -5
  30. package/dist/components/IO/GPIOCard.js.map +1 -1
  31. package/dist/components/IO/RAMBank.d.ts +37 -0
  32. package/dist/components/IO/RAMBank.js +63 -0
  33. package/dist/components/IO/RAMBank.js.map +1 -0
  34. package/dist/components/IO/RTC.d.ts +107 -0
  35. package/dist/components/IO/RTC.js +483 -0
  36. package/dist/components/IO/RTC.js.map +1 -0
  37. package/dist/components/IO/Sound.d.ts +120 -0
  38. package/dist/components/IO/Sound.js +622 -0
  39. package/dist/components/IO/Sound.js.map +1 -0
  40. package/dist/components/IO/Storage.d.ts +74 -0
  41. package/dist/components/IO/Storage.js +409 -0
  42. package/dist/components/IO/Storage.js.map +1 -0
  43. package/dist/components/IO/Terminal.d.ts +19 -0
  44. package/dist/components/IO/Terminal.js +33 -0
  45. package/dist/components/IO/Terminal.js.map +1 -0
  46. package/dist/components/IO/VIA.d.ts +105 -0
  47. package/dist/components/IO/VIA.js +597 -0
  48. package/dist/components/IO/VIA.js.map +1 -0
  49. package/dist/components/IO/Video.d.ts +141 -0
  50. package/dist/components/IO/Video.js +630 -0
  51. package/dist/components/IO/Video.js.map +1 -0
  52. package/dist/components/Machine.d.ts +20 -24
  53. package/dist/components/Machine.js +249 -166
  54. package/dist/components/Machine.js.map +1 -1
  55. package/dist/index.js +28 -14
  56. package/dist/index.js.map +1 -1
  57. package/dist/lib.d.ts +16 -16
  58. package/dist/lib.js +32 -32
  59. package/dist/lib.js.map +1 -1
  60. package/dist/tests/IO/ACIA.test.d.ts +1 -0
  61. package/dist/tests/IO/ACIA.test.js +423 -0
  62. package/dist/tests/IO/ACIA.test.js.map +1 -0
  63. package/dist/tests/IO/Attachments/Attachment.test.d.ts +1 -0
  64. package/dist/tests/IO/Attachments/Attachment.test.js +339 -0
  65. package/dist/tests/IO/Attachments/Attachment.test.js.map +1 -0
  66. package/dist/tests/IO/Attachments/JoystickAttachment.test.d.ts +1 -0
  67. package/dist/tests/IO/Attachments/JoystickAttachment.test.js +126 -0
  68. package/dist/tests/IO/Attachments/JoystickAttachment.test.js.map +1 -0
  69. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.d.ts +1 -0
  70. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js +779 -0
  71. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js.map +1 -0
  72. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.d.ts +1 -0
  73. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.js +355 -0
  74. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.js.map +1 -0
  75. package/dist/tests/IO/Attachments/KeypadAttachment.test.d.ts +1 -0
  76. package/dist/tests/IO/Attachments/KeypadAttachment.test.js +323 -0
  77. package/dist/tests/IO/Attachments/KeypadAttachment.test.js.map +1 -0
  78. package/dist/tests/IO/Attachments/LCDAttachment.test.d.ts +1 -0
  79. package/dist/tests/IO/Attachments/LCDAttachment.test.js +627 -0
  80. package/dist/tests/IO/Attachments/LCDAttachment.test.js.map +1 -0
  81. package/dist/tests/IO/Attachments/SNESAttachment.test.d.ts +1 -0
  82. package/dist/tests/IO/Attachments/SNESAttachment.test.js +331 -0
  83. package/dist/tests/IO/Attachments/SNESAttachment.test.js.map +1 -0
  84. package/dist/tests/IO/Empty.test.d.ts +1 -0
  85. package/dist/tests/IO/Empty.test.js +121 -0
  86. package/dist/tests/IO/Empty.test.js.map +1 -0
  87. package/dist/tests/IO/GPIOCard.test.js.map +1 -1
  88. package/dist/tests/IO/RAMBank.test.d.ts +1 -0
  89. package/dist/tests/IO/RAMBank.test.js +229 -0
  90. package/dist/tests/IO/RAMBank.test.js.map +1 -0
  91. package/dist/tests/IO/RTC.test.d.ts +1 -0
  92. package/dist/tests/IO/RTC.test.js +177 -0
  93. package/dist/tests/IO/RTC.test.js.map +1 -0
  94. package/dist/tests/IO/Sound.test.d.ts +1 -0
  95. package/dist/tests/IO/Sound.test.js +528 -0
  96. package/dist/tests/IO/Sound.test.js.map +1 -0
  97. package/dist/tests/IO/Storage.test.d.ts +1 -0
  98. package/dist/tests/IO/Storage.test.js +656 -0
  99. package/dist/tests/IO/Storage.test.js.map +1 -0
  100. package/dist/tests/IO/VIA.test.d.ts +1 -0
  101. package/dist/tests/IO/VIA.test.js +503 -0
  102. package/dist/tests/IO/VIA.test.js.map +1 -0
  103. package/dist/tests/IO/Video.test.d.ts +1 -0
  104. package/dist/tests/IO/Video.test.js +549 -0
  105. package/dist/tests/IO/Video.test.js.map +1 -0
  106. package/dist/tests/Machine.test.js +27 -42
  107. package/dist/tests/Machine.test.js.map +1 -1
  108. package/package.json +1 -1
  109. package/src/components/IO/{SerialCard.ts → ACIA.ts} +2 -2
  110. package/src/components/IO/{GPIOAttachments/GPIOAttachment.ts → Attachments/Attachment.ts} +2 -2
  111. package/src/components/IO/{GPIOAttachments/GPIOJoystickAttachment.ts → Attachments/JoystickAttachment.ts} +3 -3
  112. package/src/components/IO/{GPIOAttachments/GPIOKeyboardEncoderAttachment.ts → Attachments/KeyboardEncoderAttachment.ts} +3 -3
  113. package/src/components/IO/{GPIOAttachments/GPIOKeyboardMatrixAttachment.ts → Attachments/KeyboardMatrixAttachment.ts} +5 -5
  114. package/src/components/IO/{GPIOAttachments/GPIOKeypadAttachment.ts → Attachments/KeypadAttachment.ts} +3 -3
  115. package/src/components/IO/{GPIOAttachments/GPIOLCDAttachment.ts → Attachments/LCDAttachment.ts} +7 -7
  116. package/src/components/IO/{EmptyCard.ts → Empty.ts} +1 -1
  117. package/src/components/IO/{RAMCard.ts → RAMBank.ts} +8 -8
  118. package/src/components/IO/{RTCCard.ts → RTC.ts} +1 -1
  119. package/src/components/IO/{SoundCard.ts → Sound.ts} +2 -2
  120. package/src/components/IO/{StorageCard.ts → Storage.ts} +70 -73
  121. package/src/components/IO/{DevOutputBoard.ts → Terminal.ts} +2 -2
  122. package/src/components/IO/{GPIOCard.ts → VIA.ts} +64 -64
  123. package/src/components/IO/{VideoCard.ts → Video.ts} +1 -1
  124. package/src/components/Machine.ts +276 -176
  125. package/src/index.ts +34 -21
  126. package/src/lib.ts +16 -16
  127. package/src/tests/IO/{SerialCard.test.ts → ACIA.test.ts} +5 -5
  128. package/src/tests/IO/{GPIOAttachments/GPIOAttachment.test.ts → Attachments/Attachment.test.ts} +12 -12
  129. package/src/tests/IO/{GPIOAttachments/GPIOJoystickAttachment.test.ts → Attachments/JoystickAttachment.test.ts} +23 -23
  130. package/src/tests/IO/{GPIOAttachments/GPIOKeyboardEncoderAttachment.test.ts → Attachments/KeyboardEncoderAttachment.test.ts} +4 -4
  131. package/src/tests/IO/{GPIOAttachments/GPIOKeyboardMatrixAttachment.test.ts → Attachments/KeyboardMatrixAttachment.test.ts} +5 -5
  132. package/src/tests/IO/{GPIOAttachments/GPIOKeypadAttachment.test.ts → Attachments/KeypadAttachment.test.ts} +38 -38
  133. package/src/tests/IO/{GPIOAttachments/GPIOLCDAttachment.test.ts → Attachments/LCDAttachment.test.ts} +12 -12
  134. package/src/tests/IO/Empty.test.ts +143 -0
  135. package/src/tests/IO/{RAMCard.test.ts → RAMBank.test.ts} +33 -33
  136. package/src/tests/IO/{RTCCard.test.ts → RTC.test.ts} +6 -6
  137. package/src/tests/IO/{SoundCard.test.ts → Sound.test.ts} +6 -6
  138. package/src/tests/IO/{StorageCard.test.ts → Storage.test.ts} +34 -25
  139. package/src/tests/IO/{GPIOCard.test.ts → VIA.test.ts} +7 -7
  140. package/src/tests/IO/{VideoCard.test.ts → Video.test.ts} +13 -13
  141. package/src/tests/Machine.test.ts +31 -38
@@ -1,5 +1,5 @@
1
1
  import {
2
- GPIOLCDAttachment,
2
+ LCDAttachment,
3
3
  LCD_CMD_CLEAR,
4
4
  LCD_CMD_HOME,
5
5
  LCD_CMD_ENTRY_MODE,
@@ -16,36 +16,36 @@ import {
16
16
  LCD_CMD_FUNCTION_LCD_2LINE,
17
17
  LCD_CMD_SET_CGRAM_ADDR,
18
18
  LCD_CMD_SET_DRAM_ADDR,
19
- } from '../../../components/IO/GPIOAttachments/GPIOLCDAttachment'
19
+ } from '../../../components/IO/Attachments/LCDAttachment'
20
20
 
21
21
  // VIA Port A pin masks
22
22
  const PIN_RS = 0x20
23
23
  const PIN_RW = 0x40
24
24
  const PIN_E = 0x80
25
25
 
26
- describe('GPIOLCDAttachment', () => {
27
- let lcd: GPIOLCDAttachment
26
+ describe('LCDAttachment', () => {
27
+ let lcd: LCDAttachment
28
28
 
29
29
  beforeEach(() => {
30
- lcd = new GPIOLCDAttachment(16, 2)
30
+ lcd = new LCDAttachment(16, 2)
31
31
  })
32
32
 
33
33
  // ── Helper: write a command via the GPIO bus ────────────────────
34
34
 
35
35
  /** Simulate a command write: RS=0, RW=0 */
36
- function writeCommand(lcd: GPIOLCDAttachment, cmd: number): void {
36
+ function writeCommand(lcd: LCDAttachment, cmd: number): void {
37
37
  lcd.sendCommand(cmd)
38
38
  lcd.updatePixels()
39
39
  }
40
40
 
41
41
  /** Simulate a data byte write: RS=1, RW=0 */
42
- function writeData(lcd: GPIOLCDAttachment, data: number): void {
42
+ function writeData(lcd: LCDAttachment, data: number): void {
43
43
  lcd.writeByte(data)
44
44
  lcd.updatePixels()
45
45
  }
46
46
 
47
47
  /** Write a string to the LCD */
48
- function writeString(lcd: GPIOLCDAttachment, str: string): void {
48
+ function writeString(lcd: LCDAttachment, str: string): void {
49
49
  for (let i = 0; i < str.length; i++) {
50
50
  writeData(lcd, str.charCodeAt(i))
51
51
  }
@@ -53,7 +53,7 @@ describe('GPIOLCDAttachment', () => {
53
53
 
54
54
  /** Simulate full GPIO bus cycle: set Port B data, set Port A control,
55
55
  * raise E, then lower E (falling-edge latch) */
56
- function gpioBusWrite(lcd: GPIOLCDAttachment, rs: boolean, data: number): void {
56
+ function gpioBusWrite(lcd: LCDAttachment, rs: boolean, data: number): void {
57
57
  const portAValue = (rs ? PIN_RS : 0) // RW = 0 (write)
58
58
  const ddr = 0xFF // all outputs
59
59
 
@@ -107,7 +107,7 @@ describe('GPIOLCDAttachment', () => {
107
107
  })
108
108
 
109
109
  it('should support different display sizes', () => {
110
- const lcd20x4 = new GPIOLCDAttachment(20, 4)
110
+ const lcd20x4 = new LCDAttachment(20, 4)
111
111
  expect(lcd20x4.cols).toBe(20)
112
112
  expect(lcd20x4.rows).toBe(4)
113
113
  // 20 × (5+1) - 1 = 119
@@ -655,10 +655,10 @@ describe('GPIOLCDAttachment', () => {
655
655
  })
656
656
 
657
657
  describe('1-row mode', () => {
658
- let lcd1: GPIOLCDAttachment
658
+ let lcd1: LCDAttachment
659
659
 
660
660
  beforeEach(() => {
661
- lcd1 = new GPIOLCDAttachment(16, 1)
661
+ lcd1 = new LCDAttachment(16, 1)
662
662
  })
663
663
 
664
664
  it('should wrap from position 79 to 0', () => {
@@ -0,0 +1,143 @@
1
+ import { Empty } from '../../components/IO/Empty'
2
+
3
+ describe('Empty', () => {
4
+ let empty: Empty
5
+
6
+ beforeEach(() => {
7
+ empty = new Empty()
8
+ })
9
+
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
+ })
26
+ })
27
+
28
+ describe('Reading', () => {
29
+ it('should return 0 for any address', () => {
30
+ expect(empty.read(0x00)).toBe(0)
31
+ expect(empty.read(0x42)).toBe(0)
32
+ expect(empty.read(0xFF)).toBe(0)
33
+ expect(empty.read(0x1000)).toBe(0)
34
+ })
35
+
36
+ it('should consistently return 0 for the same address', () => {
37
+ const address = 0x80
38
+ expect(empty.read(address)).toBe(0)
39
+ expect(empty.read(address)).toBe(0)
40
+ expect(empty.read(address)).toBe(0)
41
+ })
42
+ })
43
+
44
+ describe('Writing', () => {
45
+ it('should accept writes without throwing errors', () => {
46
+ expect(() => empty.write(0x00, 0x00)).not.toThrow()
47
+ expect(() => empty.write(0x42, 0xAA)).not.toThrow()
48
+ expect(() => empty.write(0xFF, 0xFF)).not.toThrow()
49
+ })
50
+
51
+ it('should not affect read values after writing', () => {
52
+ empty.write(0x10, 0x55)
53
+ expect(empty.read(0x10)).toBe(0)
54
+
55
+ empty.write(0x20, 0xAA)
56
+ expect(empty.read(0x20)).toBe(0)
57
+ })
58
+
59
+ it('should handle multiple writes to the same address', () => {
60
+ const address = 0x30
61
+ expect(() => {
62
+ empty.write(address, 0x11)
63
+ empty.write(address, 0x22)
64
+ empty.write(address, 0x33)
65
+ }).not.toThrow()
66
+
67
+ expect(empty.read(address)).toBe(0)
68
+ })
69
+ })
70
+
71
+ describe('Tick', () => {
72
+ it('should not throw when ticked', () => {
73
+ expect(() => empty.tick(1000000)).not.toThrow()
74
+ })
75
+
76
+ it('should handle various frequencies', () => {
77
+ expect(() => empty.tick(0)).not.toThrow()
78
+ expect(() => empty.tick(1)).not.toThrow()
79
+ expect(() => empty.tick(1000000)).not.toThrow()
80
+ expect(() => empty.tick(10000000)).not.toThrow()
81
+ })
82
+
83
+ it('should not affect read values', () => {
84
+ empty.tick(1000000)
85
+ expect(empty.read(0x50)).toBe(0)
86
+ })
87
+ })
88
+
89
+ describe('Reset', () => {
90
+ it('should not throw when reset with cold start', () => {
91
+ expect(() => empty.reset(true)).not.toThrow()
92
+ })
93
+
94
+ it('should not throw when reset without cold start', () => {
95
+ expect(() => empty.reset(false)).not.toThrow()
96
+ })
97
+
98
+ it('should not affect read values after reset', () => {
99
+ empty.reset(true)
100
+ expect(empty.read(0x60)).toBe(0)
101
+
102
+ empty.reset(false)
103
+ expect(empty.read(0x60)).toBe(0)
104
+ })
105
+
106
+ it('should handle multiple resets', () => {
107
+ expect(() => {
108
+ empty.reset(true)
109
+ empty.reset(false)
110
+ empty.reset(true)
111
+ }).not.toThrow()
112
+ })
113
+ })
114
+
115
+ describe('Integration', () => {
116
+ it('should handle a sequence of operations', () => {
117
+ expect(() => {
118
+ empty.reset(true)
119
+ empty.write(0x00, 0xFF)
120
+ const value = empty.read(0x00)
121
+ expect(value).toBe(0)
122
+ empty.tick(1000000)
123
+ empty.raiseIRQ()
124
+ empty.raiseNMI()
125
+ empty.reset(false)
126
+ }).not.toThrow()
127
+ })
128
+
129
+ it('should remain functional after extensive use', () => {
130
+ for (let i = 0; i < 100; i++) {
131
+ empty.write(i, i & 0xFF)
132
+ empty.tick(1000)
133
+ }
134
+
135
+ for (let i = 0; i < 100; i++) {
136
+ expect(empty.read(i)).toBe(0)
137
+ }
138
+
139
+ empty.reset(true)
140
+ expect(empty.read(0x00)).toBe(0)
141
+ })
142
+ })
143
+ })
@@ -1,33 +1,33 @@
1
- import { RAMCard } from '../../components/IO/RAMCard'
1
+ import { RAMBank } from '../../components/IO/RAMBank'
2
2
 
3
- describe('RAMCard', () => {
4
- let ramCard: RAMCard
3
+ describe('RAMBank', () => {
4
+ let ramCard: RAMBank
5
5
 
6
6
  beforeEach(() => {
7
- ramCard = new RAMCard()
7
+ ramCard = new RAMBank()
8
8
  })
9
9
 
10
10
  describe('Static Properties', () => {
11
11
  it('should have correct total size', () => {
12
- expect(RAMCard.TOTAL_SIZE).toBe(256 * 1024)
12
+ expect(RAMBank.TOTAL_SIZE).toBe(256 * 1024)
13
13
  })
14
14
 
15
15
  it('should have correct bank size', () => {
16
- expect(RAMCard.BANK_SIZE).toBe(1024)
16
+ expect(RAMBank.BANK_SIZE).toBe(1024)
17
17
  })
18
18
 
19
19
  it('should have correct number of banks', () => {
20
- expect(RAMCard.NUM_BANKS).toBe(256)
20
+ expect(RAMBank.NUM_BANKS).toBe(256)
21
21
  })
22
22
 
23
23
  it('should have correct bank control register address', () => {
24
- expect(RAMCard.BANK_CONTROL_REGISTER).toBe(0x3FF)
24
+ expect(RAMBank.BANK_CONTROL_REGISTER).toBe(0x3FF)
25
25
  })
26
26
  })
27
27
 
28
28
  describe('Initialization', () => {
29
29
  it('should initialize with all data as 0x00', () => {
30
- for (let i = 0; i < RAMCard.TOTAL_SIZE; i++) {
30
+ for (let i = 0; i < RAMBank.TOTAL_SIZE; i++) {
31
31
  expect(ramCard.data[i]).toBe(0x00)
32
32
  }
33
33
  })
@@ -60,13 +60,13 @@ describe('RAMCard', () => {
60
60
 
61
61
  it('should read bank control register and return current bank', () => {
62
62
  ramCard.currentBank = 0
63
- expect(ramCard.read(RAMCard.BANK_CONTROL_REGISTER)).toBe(0)
63
+ expect(ramCard.read(RAMBank.BANK_CONTROL_REGISTER)).toBe(0)
64
64
 
65
65
  ramCard.currentBank = 42
66
- expect(ramCard.read(RAMCard.BANK_CONTROL_REGISTER)).toBe(42)
66
+ expect(ramCard.read(RAMBank.BANK_CONTROL_REGISTER)).toBe(42)
67
67
 
68
68
  ramCard.currentBank = 255
69
- expect(ramCard.read(RAMCard.BANK_CONTROL_REGISTER)).toBe(255)
69
+ expect(ramCard.read(RAMBank.BANK_CONTROL_REGISTER)).toBe(255)
70
70
  })
71
71
  })
72
72
 
@@ -98,15 +98,15 @@ describe('RAMCard', () => {
98
98
  })
99
99
 
100
100
  it('should switch banks via bank control register', () => {
101
- ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 5)
101
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 5)
102
102
  expect(ramCard.currentBank).toBe(5)
103
103
  })
104
104
 
105
105
  it('should mask bank number to 0xFF', () => {
106
- ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 0x1FF)
106
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 0x1FF)
107
107
  expect(ramCard.currentBank).toBe(0xFF)
108
108
 
109
- ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 0x100)
109
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 0x100)
110
110
  expect(ramCard.currentBank).toBe(0x00)
111
111
  })
112
112
  })
@@ -118,7 +118,7 @@ describe('RAMCard', () => {
118
118
  expect(ramCard.read(50)).toBe(0x11)
119
119
 
120
120
  // Switch to bank 1
121
- ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 1)
121
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 1)
122
122
  expect(ramCard.read(50)).toBe(0x00)
123
123
 
124
124
  // Write different value to address 50 in bank 1
@@ -126,7 +126,7 @@ describe('RAMCard', () => {
126
126
  expect(ramCard.read(50)).toBe(0x22)
127
127
 
128
128
  // Switch back to bank 0
129
- ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 0)
129
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 0)
130
130
  expect(ramCard.read(50)).toBe(0x11)
131
131
  })
132
132
 
@@ -136,38 +136,38 @@ describe('RAMCard', () => {
136
136
  ramCard.write(200, 0xBB)
137
137
 
138
138
  // Switch to bank 1
139
- ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 1)
139
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 1)
140
140
  ramCard.write(100, 0xCC)
141
141
  ramCard.write(200, 0xDD)
142
142
 
143
143
  // Switch to bank 2
144
- ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 2)
144
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 2)
145
145
  ramCard.write(100, 0xEE)
146
146
 
147
147
  // Verify data in bank 2
148
148
  expect(ramCard.read(100)).toBe(0xEE)
149
149
 
150
150
  // Switch to bank 1
151
- ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 1)
151
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 1)
152
152
  expect(ramCard.read(100)).toBe(0xCC)
153
153
  expect(ramCard.read(200)).toBe(0xDD)
154
154
 
155
155
  // Switch to bank 0
156
- ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 0)
156
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 0)
157
157
  expect(ramCard.read(100)).toBe(0xAA)
158
158
  expect(ramCard.read(200)).toBe(0xBB)
159
159
  })
160
160
 
161
161
  it('should switch between all 256 banks', () => {
162
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)
163
+ for (let bank = 0; bank < RAMBank.NUM_BANKS; bank++) {
164
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, bank)
165
165
  ramCard.write(0, bank & 0xFF)
166
166
  }
167
167
 
168
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)
169
+ for (let bank = 0; bank < RAMBank.NUM_BANKS; bank++) {
170
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, bank)
171
171
  expect(ramCard.read(0)).toBe(bank & 0xFF)
172
172
  }
173
173
  })
@@ -176,7 +176,7 @@ describe('RAMCard', () => {
176
176
  describe('Reset', () => {
177
177
  it('should not reset on warm start', () => {
178
178
  ramCard.write(100, 0x42)
179
- ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 42)
179
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 42)
180
180
 
181
181
  ramCard.reset(false)
182
182
 
@@ -187,17 +187,17 @@ describe('RAMCard', () => {
187
187
  it('should reset all data on cold start', () => {
188
188
  ramCard.write(100, 0x42)
189
189
  ramCard.write(500, 0xAB)
190
- ramCard.write(RAMCard.TOTAL_SIZE - 1, 0xFF)
190
+ ramCard.write(RAMBank.TOTAL_SIZE - 1, 0xFF)
191
191
 
192
192
  ramCard.reset(true)
193
193
 
194
194
  expect(ramCard.data[100]).toBe(0x00)
195
195
  expect(ramCard.data[500]).toBe(0x00)
196
- expect(ramCard.data[RAMCard.TOTAL_SIZE - 1]).toBe(0x00)
196
+ expect(ramCard.data[RAMBank.TOTAL_SIZE - 1]).toBe(0x00)
197
197
  })
198
198
 
199
199
  it('should reset to bank 0 on cold start', () => {
200
- ramCard.write(RAMCard.BANK_CONTROL_REGISTER, 100)
200
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 100)
201
201
  expect(ramCard.currentBank).toBe(100)
202
202
 
203
203
  ramCard.reset(true)
@@ -208,8 +208,8 @@ describe('RAMCard', () => {
208
208
  it('should clear all RAM on cold start', () => {
209
209
  // Fill multiple banks with data
210
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++) {
211
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, bank)
212
+ for (let addr = 0; addr < RAMBank.BANK_SIZE; addr++) {
213
213
  ramCard.write(addr, bank)
214
214
  }
215
215
  }
@@ -217,7 +217,7 @@ describe('RAMCard', () => {
217
217
  ramCard.reset(true)
218
218
 
219
219
  // Verify all data is cleared
220
- for (let i = 0; i < RAMCard.TOTAL_SIZE; i++) {
220
+ for (let i = 0; i < RAMBank.TOTAL_SIZE; i++) {
221
221
  expect(ramCard.data[i]).toBe(0x00)
222
222
  }
223
223
  })
@@ -257,7 +257,7 @@ describe('RAMCard', () => {
257
257
 
258
258
  it('should handle rapid bank switches', () => {
259
259
  for (let i = 0; i < 100; i++) {
260
- ramCard.write(RAMCard.BANK_CONTROL_REGISTER, i % 256)
260
+ ramCard.write(RAMBank.BANK_CONTROL_REGISTER, i % 256)
261
261
  ramCard.write(0, i & 0xFF)
262
262
  }
263
263
 
@@ -1,13 +1,13 @@
1
- import { RTCCard } from '../../components/IO/RTCCard'
1
+ import { RTC } from '../../components/IO/RTC'
2
2
 
3
3
  const bcdToDecimal = (bcd: number): number => (((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f)
4
4
 
5
- const enableTransfers = (rtc: RTCCard): void => {
5
+ const enableTransfers = (rtc: RTC): void => {
6
6
  rtc.write(0x0f, 0x80)
7
7
  rtc.tick(1)
8
8
  }
9
9
 
10
- const setTime = (rtc: RTCCard, values: {
10
+ const setTime = (rtc: RTC, values: {
11
11
  seconds: number
12
12
  minutes: number
13
13
  hours: number
@@ -28,11 +28,11 @@ const setTime = (rtc: RTCCard, values: {
28
28
  rtc.write(0x07, values.century)
29
29
  }
30
30
 
31
- describe('RTCCard', () => {
32
- let rtc: RTCCard
31
+ describe('RTC', () => {
32
+ let rtc: RTC
33
33
 
34
34
  beforeEach(() => {
35
- rtc = new RTCCard()
35
+ rtc = new RTC()
36
36
  })
37
37
 
38
38
  describe('Initialization', () => {
@@ -1,4 +1,4 @@
1
- import { SoundCard, SIDVoice, EnvelopeState, SID_CLOCK_NTSC } from '../../components/IO/SoundCard'
1
+ import { Sound, SIDVoice, EnvelopeState, SID_CLOCK_NTSC } from '../../components/IO/Sound'
2
2
 
3
3
  // Voice register offsets (relative to voice base)
4
4
  const VOICE1_BASE = 0x00
@@ -35,21 +35,21 @@ const CTRL_PULSE = 0x40
35
35
  const CTRL_NOISE = 0x80
36
36
 
37
37
  /**
38
- * Helper: tick the SoundCard for a given number of macro-ticks
38
+ * Helper: tick the Sound for a given number of macro-ticks
39
39
  * Each tick processes 128 SID clock cycles internally
40
40
  */
41
- const tickN = (sid: SoundCard, n: number): void => {
41
+ const tickN = (sid: Sound, n: number): void => {
42
42
  for (let i = 0; i < n; i++) {
43
43
  sid.tick(SID_CLOCK_NTSC)
44
44
  }
45
45
  }
46
46
 
47
- describe('SoundCard (MOS 6581 SID)', () => {
47
+ describe('Sound (MOS 6581 SID)', () => {
48
48
 
49
- let sid: SoundCard
49
+ let sid: Sound
50
50
 
51
51
  beforeEach(() => {
52
- sid = new SoundCard()
52
+ sid = new Sound()
53
53
  sid.sampleRate = 44100
54
54
  sid.sidClock = SID_CLOCK_NTSC
55
55
  })
@@ -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', () => {