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.
- package/README.md +12 -12
- package/dist/components/CPU.d.ts +162 -0
- package/dist/components/Cart.d.ts +9 -0
- 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/DevOutputBoard.d.ts +19 -0
- package/dist/components/IO/DevOutputBoard.js +33 -0
- package/dist/components/IO/DevOutputBoard.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/EmptyCard.d.ts +9 -0
- package/dist/components/IO/GPIOAttachments/GPIOAttachment.d.ts +112 -0
- package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.d.ts +53 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.d.ts +63 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.d.ts +44 -0
- package/dist/components/IO/GPIOAttachments/GPIOKeypadAttachment.d.ts +47 -0
- package/dist/components/IO/GPIOAttachments/GPIOLCDAttachment.d.ts +110 -0
- package/dist/components/IO/GPIOCard.d.ts +105 -0
- 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/RAMCard.d.ts +37 -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/RTCCard.d.ts +107 -0
- package/dist/components/IO/SerialCard.d.ts +76 -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/SoundCard.d.ts +120 -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/StorageCard.d.ts +74 -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/IO/VideoCard.d.ts +141 -0
- package/dist/components/IO.d.ts +8 -0
- package/dist/components/Machine.d.ts +62 -0
- package/dist/components/Machine.js +260 -153
- package/dist/components/Machine.js.map +1 -1
- package/dist/components/RAM.d.ts +9 -0
- package/dist/components/ROM.d.ts +9 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +61 -28
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +22 -0
- package/dist/lib.js +47 -0
- package/dist/lib.js.map +1 -0
- package/dist/tests/CPU.test.d.ts +1 -0
- package/dist/tests/Cart.test.d.ts +1 -0
- 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/GPIOAttachments/GPIOAttachment.test.d.ts +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.d.ts +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.d.ts +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.d.ts +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOKeypadAttachment.test.d.ts +1 -0
- package/dist/tests/IO/GPIOAttachments/GPIOLCDAttachment.test.d.ts +1 -0
- package/dist/tests/IO/GPIOCard.test.d.ts +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/RAMCard.test.d.ts +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/RTCCard.test.d.ts +1 -0
- package/dist/tests/IO/SerialCard.test.d.ts +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/SoundCard.test.d.ts +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/StorageCard.test.d.ts +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/IO/VideoCard.test.d.ts +1 -0
- package/dist/tests/Machine.test.d.ts +1 -0
- package/dist/tests/Machine.test.js +27 -42
- package/dist/tests/Machine.test.js.map +1 -1
- package/dist/tests/RAM.test.d.ts +1 -0
- package/dist/tests/ROM.test.d.ts +1 -0
- package/package.json +5 -3
- 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/Terminal.ts +34 -0
- 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 +286 -160
- package/src/index.ts +65 -35
- package/src/lib.ts +27 -0
- 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/tsconfig.json +1 -0
|
@@ -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', () => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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('
|
|
103
|
-
let vdp:
|
|
102
|
+
describe('Video (TMS9918 VDP)', () => {
|
|
103
|
+
let vdp: Video
|
|
104
104
|
|
|
105
105
|
beforeEach(() => {
|
|
106
|
-
vdp = new
|
|
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.
|
|
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',
|
|
219
|
-
//
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
expect(machine.cart).
|
|
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',
|
|
228
|
-
//
|
|
229
|
-
|
|
230
|
-
|
|
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
|
|
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
|
-
|
|
277
|
-
|
|
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
|
-
|
|
287
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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('
|
|
369
|
+
test('ACIA uses transmit callback when set', () => {
|
|
378
370
|
const mockTransmit = jest.fn()
|
|
379
371
|
machine.transmit = mockTransmit
|
|
380
|
-
// Trigger
|
|
381
|
-
machine.io5
|
|
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
|
})
|