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.
- package/README.md +139 -32
- package/dist/components/IO/ACIA.d.ts +76 -0
- package/dist/components/IO/ACIA.js +282 -0
- package/dist/components/IO/ACIA.js.map +1 -0
- package/dist/components/IO/Attachments/Attachment.d.ts +112 -0
- package/dist/components/IO/Attachments/Attachment.js +71 -0
- package/dist/components/IO/Attachments/Attachment.js.map +1 -0
- package/dist/components/IO/Attachments/JoystickAttachment.d.ts +53 -0
- package/dist/components/IO/Attachments/JoystickAttachment.js +90 -0
- package/dist/components/IO/Attachments/JoystickAttachment.js.map +1 -0
- package/dist/components/IO/Attachments/KeyboardEncoderAttachment.d.ts +63 -0
- package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js +489 -0
- package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js.map +1 -0
- package/dist/components/IO/Attachments/KeyboardMatrixAttachment.d.ts +44 -0
- package/dist/components/IO/Attachments/KeyboardMatrixAttachment.js +274 -0
- package/dist/components/IO/Attachments/KeyboardMatrixAttachment.js.map +1 -0
- package/dist/components/IO/Attachments/KeypadAttachment.d.ts +47 -0
- package/dist/components/IO/Attachments/KeypadAttachment.js +141 -0
- package/dist/components/IO/Attachments/KeypadAttachment.js.map +1 -0
- package/dist/components/IO/Attachments/LCDAttachment.d.ts +110 -0
- package/dist/components/IO/Attachments/LCDAttachment.js +716 -0
- package/dist/components/IO/Attachments/LCDAttachment.js.map +1 -0
- package/dist/components/IO/Attachments/SNESAttachment.d.ts +85 -0
- package/dist/components/IO/Attachments/SNESAttachment.js +184 -0
- package/dist/components/IO/Attachments/SNESAttachment.js.map +1 -0
- package/dist/components/IO/Empty.d.ts +9 -0
- package/dist/components/IO/Empty.js +5 -7
- package/dist/components/IO/Empty.js.map +1 -1
- package/dist/components/IO/GPIOCard.d.ts +5 -5
- package/dist/components/IO/GPIOCard.js.map +1 -1
- package/dist/components/IO/RAMBank.d.ts +37 -0
- package/dist/components/IO/RAMBank.js +63 -0
- package/dist/components/IO/RAMBank.js.map +1 -0
- package/dist/components/IO/RTC.d.ts +107 -0
- package/dist/components/IO/RTC.js +483 -0
- package/dist/components/IO/RTC.js.map +1 -0
- package/dist/components/IO/Sound.d.ts +120 -0
- package/dist/components/IO/Sound.js +622 -0
- package/dist/components/IO/Sound.js.map +1 -0
- package/dist/components/IO/Storage.d.ts +74 -0
- package/dist/components/IO/Storage.js +409 -0
- package/dist/components/IO/Storage.js.map +1 -0
- package/dist/components/IO/Terminal.d.ts +19 -0
- package/dist/components/IO/Terminal.js +33 -0
- package/dist/components/IO/Terminal.js.map +1 -0
- package/dist/components/IO/VIA.d.ts +105 -0
- package/dist/components/IO/VIA.js +597 -0
- package/dist/components/IO/VIA.js.map +1 -0
- package/dist/components/IO/Video.d.ts +141 -0
- package/dist/components/IO/Video.js +630 -0
- package/dist/components/IO/Video.js.map +1 -0
- package/dist/components/Machine.d.ts +20 -24
- package/dist/components/Machine.js +249 -166
- package/dist/components/Machine.js.map +1 -1
- package/dist/index.js +28 -14
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +16 -16
- package/dist/lib.js +32 -32
- package/dist/lib.js.map +1 -1
- package/dist/tests/IO/ACIA.test.d.ts +1 -0
- package/dist/tests/IO/ACIA.test.js +423 -0
- package/dist/tests/IO/ACIA.test.js.map +1 -0
- package/dist/tests/IO/Attachments/Attachment.test.d.ts +1 -0
- package/dist/tests/IO/Attachments/Attachment.test.js +339 -0
- package/dist/tests/IO/Attachments/Attachment.test.js.map +1 -0
- package/dist/tests/IO/Attachments/JoystickAttachment.test.d.ts +1 -0
- package/dist/tests/IO/Attachments/JoystickAttachment.test.js +126 -0
- package/dist/tests/IO/Attachments/JoystickAttachment.test.js.map +1 -0
- package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.d.ts +1 -0
- package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js +779 -0
- package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js.map +1 -0
- package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.d.ts +1 -0
- package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.js +355 -0
- package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.js.map +1 -0
- package/dist/tests/IO/Attachments/KeypadAttachment.test.d.ts +1 -0
- package/dist/tests/IO/Attachments/KeypadAttachment.test.js +323 -0
- package/dist/tests/IO/Attachments/KeypadAttachment.test.js.map +1 -0
- package/dist/tests/IO/Attachments/LCDAttachment.test.d.ts +1 -0
- package/dist/tests/IO/Attachments/LCDAttachment.test.js +627 -0
- package/dist/tests/IO/Attachments/LCDAttachment.test.js.map +1 -0
- package/dist/tests/IO/Attachments/SNESAttachment.test.d.ts +1 -0
- package/dist/tests/IO/Attachments/SNESAttachment.test.js +331 -0
- package/dist/tests/IO/Attachments/SNESAttachment.test.js.map +1 -0
- package/dist/tests/IO/Empty.test.d.ts +1 -0
- package/dist/tests/IO/Empty.test.js +121 -0
- package/dist/tests/IO/Empty.test.js.map +1 -0
- package/dist/tests/IO/GPIOCard.test.js.map +1 -1
- package/dist/tests/IO/RAMBank.test.d.ts +1 -0
- package/dist/tests/IO/RAMBank.test.js +229 -0
- package/dist/tests/IO/RAMBank.test.js.map +1 -0
- package/dist/tests/IO/RTC.test.d.ts +1 -0
- package/dist/tests/IO/RTC.test.js +177 -0
- package/dist/tests/IO/RTC.test.js.map +1 -0
- package/dist/tests/IO/Sound.test.d.ts +1 -0
- package/dist/tests/IO/Sound.test.js +528 -0
- package/dist/tests/IO/Sound.test.js.map +1 -0
- package/dist/tests/IO/Storage.test.d.ts +1 -0
- package/dist/tests/IO/Storage.test.js +656 -0
- package/dist/tests/IO/Storage.test.js.map +1 -0
- package/dist/tests/IO/VIA.test.d.ts +1 -0
- package/dist/tests/IO/VIA.test.js +503 -0
- package/dist/tests/IO/VIA.test.js.map +1 -0
- package/dist/tests/IO/Video.test.d.ts +1 -0
- package/dist/tests/IO/Video.test.js +549 -0
- package/dist/tests/IO/Video.test.js.map +1 -0
- package/dist/tests/Machine.test.js +27 -42
- package/dist/tests/Machine.test.js.map +1 -1
- package/package.json +1 -1
- package/src/components/IO/{SerialCard.ts → ACIA.ts} +2 -2
- package/src/components/IO/{GPIOAttachments/GPIOAttachment.ts → Attachments/Attachment.ts} +2 -2
- package/src/components/IO/{GPIOAttachments/GPIOJoystickAttachment.ts → Attachments/JoystickAttachment.ts} +3 -3
- package/src/components/IO/{GPIOAttachments/GPIOKeyboardEncoderAttachment.ts → Attachments/KeyboardEncoderAttachment.ts} +3 -3
- package/src/components/IO/{GPIOAttachments/GPIOKeyboardMatrixAttachment.ts → Attachments/KeyboardMatrixAttachment.ts} +5 -5
- package/src/components/IO/{GPIOAttachments/GPIOKeypadAttachment.ts → Attachments/KeypadAttachment.ts} +3 -3
- package/src/components/IO/{GPIOAttachments/GPIOLCDAttachment.ts → Attachments/LCDAttachment.ts} +7 -7
- package/src/components/IO/{EmptyCard.ts → Empty.ts} +1 -1
- package/src/components/IO/{RAMCard.ts → RAMBank.ts} +8 -8
- package/src/components/IO/{RTCCard.ts → RTC.ts} +1 -1
- package/src/components/IO/{SoundCard.ts → Sound.ts} +2 -2
- package/src/components/IO/{StorageCard.ts → Storage.ts} +70 -73
- package/src/components/IO/{DevOutputBoard.ts → Terminal.ts} +2 -2
- package/src/components/IO/{GPIOCard.ts → VIA.ts} +64 -64
- package/src/components/IO/{VideoCard.ts → Video.ts} +1 -1
- package/src/components/Machine.ts +276 -176
- package/src/index.ts +34 -21
- package/src/lib.ts +16 -16
- package/src/tests/IO/{SerialCard.test.ts → ACIA.test.ts} +5 -5
- package/src/tests/IO/{GPIOAttachments/GPIOAttachment.test.ts → Attachments/Attachment.test.ts} +12 -12
- package/src/tests/IO/{GPIOAttachments/GPIOJoystickAttachment.test.ts → Attachments/JoystickAttachment.test.ts} +23 -23
- package/src/tests/IO/{GPIOAttachments/GPIOKeyboardEncoderAttachment.test.ts → Attachments/KeyboardEncoderAttachment.test.ts} +4 -4
- package/src/tests/IO/{GPIOAttachments/GPIOKeyboardMatrixAttachment.test.ts → Attachments/KeyboardMatrixAttachment.test.ts} +5 -5
- package/src/tests/IO/{GPIOAttachments/GPIOKeypadAttachment.test.ts → Attachments/KeypadAttachment.test.ts} +38 -38
- package/src/tests/IO/{GPIOAttachments/GPIOLCDAttachment.test.ts → Attachments/LCDAttachment.test.ts} +12 -12
- package/src/tests/IO/Empty.test.ts +143 -0
- package/src/tests/IO/{RAMCard.test.ts → RAMBank.test.ts} +33 -33
- package/src/tests/IO/{RTCCard.test.ts → RTC.test.ts} +6 -6
- package/src/tests/IO/{SoundCard.test.ts → Sound.test.ts} +6 -6
- package/src/tests/IO/{StorageCard.test.ts → Storage.test.ts} +34 -25
- package/src/tests/IO/{GPIOCard.test.ts → VIA.test.ts} +7 -7
- package/src/tests/IO/{VideoCard.test.ts → Video.test.ts} +13 -13
- package/src/tests/Machine.test.ts +31 -38
package/src/tests/IO/{GPIOAttachments/GPIOLCDAttachment.test.ts → Attachments/LCDAttachment.test.ts}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
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/
|
|
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('
|
|
27
|
-
let lcd:
|
|
26
|
+
describe('LCDAttachment', () => {
|
|
27
|
+
let lcd: LCDAttachment
|
|
28
28
|
|
|
29
29
|
beforeEach(() => {
|
|
30
|
-
lcd = new
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
658
|
+
let lcd1: LCDAttachment
|
|
659
659
|
|
|
660
660
|
beforeEach(() => {
|
|
661
|
-
lcd1 = new
|
|
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 {
|
|
1
|
+
import { RAMBank } from '../../components/IO/RAMBank'
|
|
2
2
|
|
|
3
|
-
describe('
|
|
4
|
-
let ramCard:
|
|
3
|
+
describe('RAMBank', () => {
|
|
4
|
+
let ramCard: RAMBank
|
|
5
5
|
|
|
6
6
|
beforeEach(() => {
|
|
7
|
-
ramCard = new
|
|
7
|
+
ramCard = new RAMBank()
|
|
8
8
|
})
|
|
9
9
|
|
|
10
10
|
describe('Static Properties', () => {
|
|
11
11
|
it('should have correct total size', () => {
|
|
12
|
-
expect(
|
|
12
|
+
expect(RAMBank.TOTAL_SIZE).toBe(256 * 1024)
|
|
13
13
|
})
|
|
14
14
|
|
|
15
15
|
it('should have correct bank size', () => {
|
|
16
|
-
expect(
|
|
16
|
+
expect(RAMBank.BANK_SIZE).toBe(1024)
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
it('should have correct number of banks', () => {
|
|
20
|
-
expect(
|
|
20
|
+
expect(RAMBank.NUM_BANKS).toBe(256)
|
|
21
21
|
})
|
|
22
22
|
|
|
23
23
|
it('should have correct bank control register address', () => {
|
|
24
|
-
expect(
|
|
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 <
|
|
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(
|
|
63
|
+
expect(ramCard.read(RAMBank.BANK_CONTROL_REGISTER)).toBe(0)
|
|
64
64
|
|
|
65
65
|
ramCard.currentBank = 42
|
|
66
|
-
expect(ramCard.read(
|
|
66
|
+
expect(ramCard.read(RAMBank.BANK_CONTROL_REGISTER)).toBe(42)
|
|
67
67
|
|
|
68
68
|
ramCard.currentBank = 255
|
|
69
|
-
expect(ramCard.read(
|
|
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(
|
|
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(
|
|
106
|
+
ramCard.write(RAMBank.BANK_CONTROL_REGISTER, 0x1FF)
|
|
107
107
|
expect(ramCard.currentBank).toBe(0xFF)
|
|
108
108
|
|
|
109
|
-
ramCard.write(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 <
|
|
164
|
-
ramCard.write(
|
|
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 <
|
|
170
|
-
ramCard.write(
|
|
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(
|
|
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(
|
|
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[
|
|
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(
|
|
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(
|
|
212
|
-
for (let addr = 0; 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 <
|
|
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(
|
|
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 {
|
|
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:
|
|
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:
|
|
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('
|
|
32
|
-
let rtc:
|
|
31
|
+
describe('RTC', () => {
|
|
32
|
+
let rtc: RTC
|
|
33
33
|
|
|
34
34
|
beforeEach(() => {
|
|
35
|
-
rtc = new
|
|
35
|
+
rtc = new RTC()
|
|
36
36
|
})
|
|
37
37
|
|
|
38
38
|
describe('Initialization', () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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:
|
|
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('
|
|
47
|
+
describe('Sound (MOS 6581 SID)', () => {
|
|
48
48
|
|
|
49
|
-
let sid:
|
|
49
|
+
let sid: Sound
|
|
50
50
|
|
|
51
51
|
beforeEach(() => {
|
|
52
|
-
sid = new
|
|
52
|
+
sid = new Sound()
|
|
53
53
|
sid.sampleRate = 44100
|
|
54
54
|
sid.sidClock = SID_CLOCK_NTSC
|
|
55
55
|
})
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
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('
|
|
8
|
-
let storageCard:
|
|
7
|
+
describe('Storage (Compact Flash in IDE Mode)', () => {
|
|
8
|
+
let storageCard: Storage
|
|
9
9
|
|
|
10
10
|
beforeEach(() => {
|
|
11
|
-
storageCard = new
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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',
|
|
655
|
-
//
|
|
656
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
730
|
+
const storageData = storageCard.getData()
|
|
731
|
+
await writeFile(testFile, storageData)
|
|
726
732
|
|
|
727
733
|
// Create new storage card and load
|
|
728
|
-
const
|
|
729
|
-
await
|
|
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
|
-
|
|
734
|
-
|
|
735
|
-
|
|
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(
|
|
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.
|
|
758
|
+
await writeFile(testFile, storageCard.getData())
|
|
752
759
|
|
|
753
760
|
// Load and modify
|
|
754
|
-
const card2 = new
|
|
755
|
-
await
|
|
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.
|
|
770
|
+
await writeFile(testFile, card2.getData())
|
|
763
771
|
|
|
764
772
|
// Load again and verify both sectors
|
|
765
|
-
const card3 = new
|
|
766
|
-
await
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
-
} = {}):
|
|
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
|
|
49
|
+
} as Attachment & { setPortAValue: (v: number) => void; setPortBValue: (v: number) => void }
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
describe('
|
|
53
|
-
let gpio:
|
|
52
|
+
describe('VIA (65C22 VIA)', () => {
|
|
53
|
+
let gpio: VIA
|
|
54
54
|
|
|
55
55
|
beforeEach(() => {
|
|
56
|
-
gpio = new
|
|
56
|
+
gpio = new VIA()
|
|
57
57
|
})
|
|
58
58
|
|
|
59
59
|
describe('Initialization', () => {
|