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/index.ts
CHANGED
|
@@ -4,13 +4,15 @@ import figlet from 'figlet'
|
|
|
4
4
|
import { Machine } from './components/Machine'
|
|
5
5
|
import { Command, Option } from 'commander'
|
|
6
6
|
import { SerialPort } from 'serialport'
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
7
|
+
import { Video } from './components/IO/Video'
|
|
8
|
+
import { Terminal } from './components/IO/Terminal'
|
|
9
|
+
import { Storage } from './components/IO/Storage'
|
|
10
|
+
import { Sound } from './components/IO/Sound'
|
|
11
11
|
import sdl from '@kmamal/sdl'
|
|
12
|
+
import { readFile, writeFile } from 'fs/promises'
|
|
13
|
+
import { existsSync } from 'fs'
|
|
12
14
|
|
|
13
|
-
const VERSION = '1.
|
|
15
|
+
const VERSION = '1.4.1'
|
|
14
16
|
const WIDTH = 320
|
|
15
17
|
const HEIGHT = 240
|
|
16
18
|
|
|
@@ -20,7 +22,7 @@ const AUDIO_CHANNELS = 1
|
|
|
20
22
|
const AUDIO_FORMAT = 'f32'
|
|
21
23
|
const AUDIO_BUFFERED = 2048
|
|
22
24
|
|
|
23
|
-
// Joystick button bit masks (matching
|
|
25
|
+
// Joystick button bit masks (matching JoystickAttachment)
|
|
24
26
|
const BUTTON_UP = 0x01
|
|
25
27
|
const BUTTON_DOWN = 0x02
|
|
26
28
|
const BUTTON_LEFT = 0x04
|
|
@@ -92,21 +94,30 @@ class Emulator {
|
|
|
92
94
|
|
|
93
95
|
private async loadBinaries(): Promise<void> {
|
|
94
96
|
if (this.options.rom) {
|
|
95
|
-
await
|
|
97
|
+
const romData = await readFile(this.options.rom)
|
|
98
|
+
this.machine.loadROM(new Uint8Array(romData))
|
|
96
99
|
console.log(`Loaded ROM: ${this.options.rom}`)
|
|
97
100
|
} else {
|
|
98
101
|
console.log('Loaded ROM: NONE')
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
if (this.options.cart) {
|
|
102
|
-
await
|
|
105
|
+
const cartData = await readFile(this.options.cart)
|
|
106
|
+
this.machine.loadCart(new Uint8Array(cartData))
|
|
103
107
|
console.log(`Loaded Cart: ${this.options.cart}`)
|
|
104
108
|
} else {
|
|
105
109
|
console.log('Loaded Cart: NONE')
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
if (this.options.storage && this.options.target !== 'kim') {
|
|
109
|
-
|
|
113
|
+
if (existsSync(this.options.storage)) {
|
|
114
|
+
const storageData = await readFile(this.options.storage)
|
|
115
|
+
;(this.machine.io4 as Storage).loadData(new Uint8Array(storageData))
|
|
116
|
+
} else {
|
|
117
|
+
console.log(`Storage file not found: ${this.options.storage}`)
|
|
118
|
+
console.log('A new storage file will be created on exit.')
|
|
119
|
+
;(this.machine.io4 as Storage).loadData(null)
|
|
120
|
+
}
|
|
110
121
|
}
|
|
111
122
|
}
|
|
112
123
|
|
|
@@ -193,11 +204,11 @@ class Emulator {
|
|
|
193
204
|
buffered: AUDIO_BUFFERED,
|
|
194
205
|
})
|
|
195
206
|
|
|
196
|
-
// Configure
|
|
197
|
-
;(this.machine.io7 as
|
|
207
|
+
// Configure Sound sample rate to match audio device
|
|
208
|
+
;(this.machine.io7 as Sound).sampleRate = this.audioDevice.frequency
|
|
198
209
|
|
|
199
210
|
// Connect the Machine's audio callback to the SDL audio device
|
|
200
|
-
this.machine.
|
|
211
|
+
this.machine.play = (samples: Float32Array) => {
|
|
201
212
|
if (!this.audioDevice || this.audioDevice.closed) return
|
|
202
213
|
|
|
203
214
|
const { channels, bytesPerSample } = this.audioDevice
|
|
@@ -322,13 +333,13 @@ class Emulator {
|
|
|
322
333
|
this.window.render(renderWidth, renderHeight, renderWidth * 4, 'rgba32', rgbaBuffer)
|
|
323
334
|
}
|
|
324
335
|
} else if (this.options.target === 'cob' || this.options.target === 'vcs') {
|
|
325
|
-
const
|
|
336
|
+
const Video = this.machine.io8 as Video
|
|
326
337
|
this.machine.render = () => {
|
|
327
338
|
if (!this.window) { return }
|
|
328
|
-
this.window.render(WIDTH, HEIGHT, WIDTH * 4, 'rgba32',
|
|
339
|
+
this.window.render(WIDTH, HEIGHT, WIDTH * 4, 'rgba32', Video.buffer)
|
|
329
340
|
}
|
|
330
341
|
} else if (this.options.target === 'dev') {
|
|
331
|
-
const devBoard = this.machine.io8 as
|
|
342
|
+
const devBoard = this.machine.io8 as Terminal
|
|
332
343
|
const rgbaBuffer = Buffer.alloc(WIDTH * HEIGHT * 4)
|
|
333
344
|
this.machine.render = () => {
|
|
334
345
|
if (!this.window) { return }
|
|
@@ -516,7 +527,6 @@ class Emulator {
|
|
|
516
527
|
}
|
|
517
528
|
})
|
|
518
529
|
}
|
|
519
|
-
this.machine.end()
|
|
520
530
|
|
|
521
531
|
const uptime = Date.now() - this.machine.startTime
|
|
522
532
|
|
|
@@ -531,9 +541,12 @@ class Emulator {
|
|
|
531
541
|
|
|
532
542
|
// Save storage data if path was provided
|
|
533
543
|
if (this.options.storage && this.options.target !== 'kim') {
|
|
534
|
-
(this.machine.io4 as
|
|
544
|
+
const storageData = (this.machine.io4 as Storage).getData()
|
|
545
|
+
writeFile(this.options.storage, storageData).then(() => {
|
|
546
|
+
console.log(`Storage saved to: ${this.options.storage}`)
|
|
535
547
|
process.exit(0)
|
|
536
|
-
}).catch(() => {
|
|
548
|
+
}).catch((error) => {
|
|
549
|
+
console.error('Error saving storage file:', error)
|
|
537
550
|
process.exit(1)
|
|
538
551
|
})
|
|
539
552
|
} else {
|
|
@@ -541,8 +554,8 @@ class Emulator {
|
|
|
541
554
|
}
|
|
542
555
|
}
|
|
543
556
|
|
|
544
|
-
|
|
545
|
-
this.machine.
|
|
557
|
+
run(): void {
|
|
558
|
+
this.machine.run()
|
|
546
559
|
}
|
|
547
560
|
}
|
|
548
561
|
|
|
@@ -573,7 +586,7 @@ const options = program.opts()
|
|
|
573
586
|
async function main() {
|
|
574
587
|
const emulator = new Emulator(options)
|
|
575
588
|
await emulator.initialize()
|
|
576
|
-
emulator.
|
|
589
|
+
emulator.run()
|
|
577
590
|
}
|
|
578
591
|
|
|
579
592
|
// Run the main function
|
package/src/lib.ts
CHANGED
|
@@ -7,21 +7,21 @@ export { Cart } from './components/Cart'
|
|
|
7
7
|
export type { IO } from './components/IO'
|
|
8
8
|
|
|
9
9
|
// IO cards
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
14
|
-
export {
|
|
15
|
-
export { SIDVoice,
|
|
16
|
-
export {
|
|
17
|
-
export {
|
|
18
|
-
export {
|
|
10
|
+
export { Empty } from './components/IO/Empty'
|
|
11
|
+
export { VIA } from './components/IO/VIA'
|
|
12
|
+
export { RAMBank } from './components/IO/RAMBank'
|
|
13
|
+
export { RTC } from './components/IO/RTC'
|
|
14
|
+
export { ACIA } from './components/IO/ACIA'
|
|
15
|
+
export { SIDVoice, Sound } from './components/IO/Sound'
|
|
16
|
+
export { Storage } from './components/IO/Storage'
|
|
17
|
+
export { Video } from './components/IO/Video'
|
|
18
|
+
export { Terminal } from './components/IO/Terminal'
|
|
19
19
|
|
|
20
20
|
// GPIO attachments
|
|
21
|
-
export type {
|
|
22
|
-
export {
|
|
23
|
-
export {
|
|
24
|
-
export {
|
|
25
|
-
export {
|
|
26
|
-
export {
|
|
27
|
-
export {
|
|
21
|
+
export type { Attachment } from './components/IO/Attachments/Attachment'
|
|
22
|
+
export { AttachmentBase } from './components/IO/Attachments/Attachment'
|
|
23
|
+
export { JoystickAttachment } from './components/IO/Attachments/JoystickAttachment'
|
|
24
|
+
export { KeyboardEncoderAttachment } from './components/IO/Attachments/KeyboardEncoderAttachment'
|
|
25
|
+
export { KeyboardMatrixAttachment } from './components/IO/Attachments/KeyboardMatrixAttachment'
|
|
26
|
+
export { KeypadAttachment } from './components/IO/Attachments/KeypadAttachment'
|
|
27
|
+
export { LCDAttachment } from './components/IO/Attachments/LCDAttachment'
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ACIA } from '../../components/IO/ACIA'
|
|
2
2
|
|
|
3
|
-
describe('
|
|
4
|
-
let serialCard:
|
|
3
|
+
describe('ACIA (6551 ACIA)', () => {
|
|
4
|
+
let serialCard: ACIA
|
|
5
5
|
|
|
6
6
|
beforeEach(() => {
|
|
7
|
-
serialCard = new
|
|
7
|
+
serialCard = new ACIA()
|
|
8
8
|
})
|
|
9
9
|
|
|
10
10
|
describe('Initialization', () => {
|
|
@@ -399,7 +399,7 @@ describe('SerialCard (6551 ACIA)', () => {
|
|
|
399
399
|
]
|
|
400
400
|
|
|
401
401
|
baudRates.forEach(([code, rate]) => {
|
|
402
|
-
const card = new
|
|
402
|
+
const card = new ACIA()
|
|
403
403
|
card.write(0x03, code as number) // Control register with baud rate code
|
|
404
404
|
// Baud rate affects tick timing - verify no errors occur
|
|
405
405
|
})
|
package/src/tests/IO/{GPIOAttachments/GPIOAttachment.test.ts → Attachments/Attachment.test.ts}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Attachment, AttachmentBase } from '../../../components/IO/Attachments/Attachment'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Concrete implementation of
|
|
4
|
+
* Concrete implementation of AttachmentBase for testing
|
|
5
5
|
*/
|
|
6
|
-
class
|
|
6
|
+
class TestAttachment extends AttachmentBase {
|
|
7
7
|
public portAValue: number = 0xFF
|
|
8
8
|
public portBValue: number = 0xFF
|
|
9
9
|
public tickCount: number = 0
|
|
@@ -62,11 +62,11 @@ class TestGPIOAttachment extends GPIOAttachmentBase {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
describe('
|
|
66
|
-
let attachment:
|
|
65
|
+
describe('AttachmentBase', () => {
|
|
66
|
+
let attachment: TestAttachment
|
|
67
67
|
|
|
68
68
|
beforeEach(() => {
|
|
69
|
-
attachment = new
|
|
69
|
+
attachment = new TestAttachment()
|
|
70
70
|
})
|
|
71
71
|
|
|
72
72
|
describe('Initialization', () => {
|
|
@@ -75,7 +75,7 @@ describe('GPIOAttachmentBase', () => {
|
|
|
75
75
|
})
|
|
76
76
|
|
|
77
77
|
it('should initialize with custom priority', () => {
|
|
78
|
-
const customAttachment = new
|
|
78
|
+
const customAttachment = new TestAttachment(5)
|
|
79
79
|
expect(customAttachment.getPriority()).toBe(5)
|
|
80
80
|
})
|
|
81
81
|
|
|
@@ -91,7 +91,7 @@ describe('GPIOAttachmentBase', () => {
|
|
|
91
91
|
})
|
|
92
92
|
|
|
93
93
|
it('should initialize with specified interrupt states', () => {
|
|
94
|
-
const interruptAttachment = new
|
|
94
|
+
const interruptAttachment = new TestAttachment(0, true, true, true, true)
|
|
95
95
|
expect(interruptAttachment.hasCA1Interrupt()).toBe(true)
|
|
96
96
|
expect(interruptAttachment.hasCA2Interrupt()).toBe(true)
|
|
97
97
|
expect(interruptAttachment.hasCB1Interrupt()).toBe(true)
|
|
@@ -121,7 +121,7 @@ describe('GPIOAttachmentBase', () => {
|
|
|
121
121
|
})
|
|
122
122
|
|
|
123
123
|
it('should maintain priority after reset', () => {
|
|
124
|
-
const priorityAttachment = new
|
|
124
|
+
const priorityAttachment = new TestAttachment(10)
|
|
125
125
|
priorityAttachment.reset()
|
|
126
126
|
expect(priorityAttachment.getPriority()).toBe(10)
|
|
127
127
|
})
|
|
@@ -129,15 +129,15 @@ describe('GPIOAttachmentBase', () => {
|
|
|
129
129
|
|
|
130
130
|
describe('Priority', () => {
|
|
131
131
|
it('should return correct priority value', () => {
|
|
132
|
-
const lowPriority = new
|
|
133
|
-
const highPriority = new
|
|
132
|
+
const lowPriority = new TestAttachment(0)
|
|
133
|
+
const highPriority = new TestAttachment(10)
|
|
134
134
|
|
|
135
135
|
expect(lowPriority.getPriority()).toBe(0)
|
|
136
136
|
expect(highPriority.getPriority()).toBe(10)
|
|
137
137
|
})
|
|
138
138
|
|
|
139
139
|
it('should support negative priority values', () => {
|
|
140
|
-
const negativePriority = new
|
|
140
|
+
const negativePriority = new TestAttachment(-5)
|
|
141
141
|
expect(negativePriority.getPriority()).toBe(-5)
|
|
142
142
|
})
|
|
143
143
|
})
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JoystickAttachment } from '../../../components/IO/Attachments/JoystickAttachment'
|
|
2
2
|
|
|
3
|
-
describe('
|
|
4
|
-
let joystick:
|
|
3
|
+
describe('JoystickAttachment', () => {
|
|
4
|
+
let joystick: JoystickAttachment
|
|
5
5
|
|
|
6
6
|
beforeEach(() => {
|
|
7
|
-
joystick = new
|
|
7
|
+
joystick = new JoystickAttachment(true, 0)
|
|
8
8
|
})
|
|
9
9
|
|
|
10
10
|
describe('constructor and reset', () => {
|
|
@@ -26,20 +26,20 @@ describe('GPIOJoystickAttachment', () => {
|
|
|
26
26
|
})
|
|
27
27
|
|
|
28
28
|
it('should check if button is pressed', () => {
|
|
29
|
-
joystick.pressButton(
|
|
30
|
-
expect(joystick.isButtonPressed(
|
|
31
|
-
expect(joystick.isButtonPressed(
|
|
29
|
+
joystick.pressButton(JoystickAttachment.BUTTON_A)
|
|
30
|
+
expect(joystick.isButtonPressed(JoystickAttachment.BUTTON_A)).toBe(true)
|
|
31
|
+
expect(joystick.isButtonPressed(JoystickAttachment.BUTTON_B)).toBe(false)
|
|
32
32
|
})
|
|
33
33
|
|
|
34
34
|
it('should press a button', () => {
|
|
35
|
-
joystick.pressButton(
|
|
36
|
-
expect(joystick.getButtonState()).toBe(
|
|
35
|
+
joystick.pressButton(JoystickAttachment.BUTTON_START)
|
|
36
|
+
expect(joystick.getButtonState()).toBe(JoystickAttachment.BUTTON_START)
|
|
37
37
|
})
|
|
38
38
|
|
|
39
39
|
it('should release a button', () => {
|
|
40
40
|
joystick.updateJoystick(0xFF)
|
|
41
|
-
joystick.releaseButton(
|
|
42
|
-
expect(joystick.getButtonState()).toBe(0xFF & ~
|
|
41
|
+
joystick.releaseButton(JoystickAttachment.BUTTON_A)
|
|
42
|
+
expect(joystick.getButtonState()).toBe(0xFF & ~JoystickAttachment.BUTTON_A)
|
|
43
43
|
})
|
|
44
44
|
|
|
45
45
|
it('should release all buttons', () => {
|
|
@@ -51,7 +51,7 @@ describe('GPIOJoystickAttachment', () => {
|
|
|
51
51
|
|
|
52
52
|
describe('port reading - Port A', () => {
|
|
53
53
|
beforeEach(() => {
|
|
54
|
-
joystick = new
|
|
54
|
+
joystick = new JoystickAttachment(true, 0) // Attach to Port A
|
|
55
55
|
})
|
|
56
56
|
|
|
57
57
|
it('should return inverted button state on Port A when attached', () => {
|
|
@@ -70,7 +70,7 @@ describe('GPIOJoystickAttachment', () => {
|
|
|
70
70
|
})
|
|
71
71
|
|
|
72
72
|
it('should handle individual button presses correctly', () => {
|
|
73
|
-
joystick.updateJoystick(
|
|
73
|
+
joystick.updateJoystick(JoystickAttachment.BUTTON_UP)
|
|
74
74
|
const result = joystick.readPortA(0x00, 0x00)
|
|
75
75
|
expect(result & 0x01).toBe(0x00) // UP button bit should be low (pressed)
|
|
76
76
|
expect(result & 0xFE).toBe(0xFE) // Other bits should be high (not pressed)
|
|
@@ -79,7 +79,7 @@ describe('GPIOJoystickAttachment', () => {
|
|
|
79
79
|
|
|
80
80
|
describe('port reading - Port B', () => {
|
|
81
81
|
beforeEach(() => {
|
|
82
|
-
joystick = new
|
|
82
|
+
joystick = new JoystickAttachment(false, 0) // Attach to Port B
|
|
83
83
|
})
|
|
84
84
|
|
|
85
85
|
it('should return inverted button state on Port B when attached', () => {
|
|
@@ -100,20 +100,20 @@ describe('GPIOJoystickAttachment', () => {
|
|
|
100
100
|
|
|
101
101
|
describe('button constants', () => {
|
|
102
102
|
it('should have correct button bit values', () => {
|
|
103
|
-
expect(
|
|
104
|
-
expect(
|
|
105
|
-
expect(
|
|
106
|
-
expect(
|
|
107
|
-
expect(
|
|
108
|
-
expect(
|
|
109
|
-
expect(
|
|
110
|
-
expect(
|
|
103
|
+
expect(JoystickAttachment.BUTTON_UP).toBe(0x01)
|
|
104
|
+
expect(JoystickAttachment.BUTTON_DOWN).toBe(0x02)
|
|
105
|
+
expect(JoystickAttachment.BUTTON_LEFT).toBe(0x04)
|
|
106
|
+
expect(JoystickAttachment.BUTTON_RIGHT).toBe(0x08)
|
|
107
|
+
expect(JoystickAttachment.BUTTON_A).toBe(0x10)
|
|
108
|
+
expect(JoystickAttachment.BUTTON_B).toBe(0x20)
|
|
109
|
+
expect(JoystickAttachment.BUTTON_SELECT).toBe(0x40)
|
|
110
|
+
expect(JoystickAttachment.BUTTON_START).toBe(0x80)
|
|
111
111
|
})
|
|
112
112
|
})
|
|
113
113
|
|
|
114
114
|
describe('priority and enabled', () => {
|
|
115
115
|
it('should return correct priority', () => {
|
|
116
|
-
const j = new
|
|
116
|
+
const j = new JoystickAttachment(true, 5)
|
|
117
117
|
expect(j.getPriority()).toBe(5)
|
|
118
118
|
})
|
|
119
119
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { KeyboardEncoderAttachment } from '../../../components/IO/Attachments/KeyboardEncoderAttachment'
|
|
2
2
|
|
|
3
|
-
describe('
|
|
4
|
-
let encoder:
|
|
3
|
+
describe('KeyboardEncoderAttachment', () => {
|
|
4
|
+
let encoder: KeyboardEncoderAttachment
|
|
5
5
|
|
|
6
6
|
beforeEach(() => {
|
|
7
|
-
encoder = new
|
|
7
|
+
encoder = new KeyboardEncoderAttachment(5)
|
|
8
8
|
})
|
|
9
9
|
|
|
10
10
|
describe('Initialization', () => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { KeyboardMatrixAttachment } from '../../../components/IO/Attachments/KeyboardMatrixAttachment'
|
|
2
2
|
|
|
3
|
-
describe('
|
|
4
|
-
let keyboard:
|
|
3
|
+
describe('KeyboardMatrixAttachment', () => {
|
|
4
|
+
let keyboard: KeyboardMatrixAttachment
|
|
5
5
|
|
|
6
6
|
beforeEach(() => {
|
|
7
|
-
keyboard = new
|
|
7
|
+
keyboard = new KeyboardMatrixAttachment(0)
|
|
8
8
|
})
|
|
9
9
|
|
|
10
10
|
describe('Initialization', () => {
|
|
@@ -22,7 +22,7 @@ describe('GPIOKeyboardMatrixAttachment', () => {
|
|
|
22
22
|
})
|
|
23
23
|
|
|
24
24
|
it('should have correct priority', () => {
|
|
25
|
-
const kb = new
|
|
25
|
+
const kb = new KeyboardMatrixAttachment(5)
|
|
26
26
|
expect(kb.getPriority()).toBe(5)
|
|
27
27
|
})
|
|
28
28
|
})
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { KeypadAttachment } from '../../../components/IO/Attachments/KeypadAttachment'
|
|
2
2
|
|
|
3
|
-
describe('
|
|
4
|
-
let keypadA:
|
|
5
|
-
let keypadB:
|
|
3
|
+
describe('KeypadAttachment', () => {
|
|
4
|
+
let keypadA: KeypadAttachment // attached to Port A
|
|
5
|
+
let keypadB: KeypadAttachment // attached to Port B
|
|
6
6
|
|
|
7
7
|
beforeEach(() => {
|
|
8
|
-
keypadA = new
|
|
9
|
-
keypadB = new
|
|
8
|
+
keypadA = new KeypadAttachment(true, 0)
|
|
9
|
+
keypadB = new KeypadAttachment(false, 0)
|
|
10
10
|
})
|
|
11
11
|
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
@@ -36,7 +36,7 @@ describe('GPIOKeypadAttachment', () => {
|
|
|
36
36
|
})
|
|
37
37
|
|
|
38
38
|
it('should report the correct priority', () => {
|
|
39
|
-
const kp = new
|
|
39
|
+
const kp = new KeypadAttachment(true, 7)
|
|
40
40
|
expect(kp.getPriority()).toBe(7)
|
|
41
41
|
})
|
|
42
42
|
|
|
@@ -62,134 +62,134 @@ describe('GPIOKeypadAttachment', () => {
|
|
|
62
62
|
// ---------------------------------------------------------------------------
|
|
63
63
|
describe('Key press → keypad value mapping', () => {
|
|
64
64
|
// Helpers: assert OE (CA2/CB2 LOW) then press and read
|
|
65
|
-
const pressAndReadA = (kp:
|
|
65
|
+
const pressAndReadA = (kp: KeypadAttachment, hid: number) => {
|
|
66
66
|
kp.updateControlLines(false, false, false, true) // CA2 LOW → OE asserted for Port A
|
|
67
67
|
kp.updateKey(hid, true)
|
|
68
68
|
return kp.readPortA(0x00, 0x00)
|
|
69
69
|
}
|
|
70
|
-
const pressAndReadB = (kp:
|
|
70
|
+
const pressAndReadB = (kp: KeypadAttachment, hid: number) => {
|
|
71
71
|
kp.updateControlLines(false, true, false, false) // CB2 LOW → OE asserted for Port B
|
|
72
72
|
kp.updateKey(hid, true)
|
|
73
73
|
return kp.readPortB(0x00, 0x00)
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
it('Left Arrow (0x50) → $00 ◄', () => {
|
|
77
|
-
expect(pressAndReadA(new
|
|
77
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x50)).toBe(0x00)
|
|
78
78
|
})
|
|
79
79
|
|
|
80
80
|
it('Backspace is not on this keypad', () => {
|
|
81
|
-
const kp = new
|
|
81
|
+
const kp = new KeypadAttachment(true)
|
|
82
82
|
kp.updateControlLines(false, false, false, true)
|
|
83
83
|
kp.updateKey(0x2A, true) // Backspace – unmapped
|
|
84
84
|
expect(kp.readPortA(0x00, 0x00)).toBe(0xFF)
|
|
85
85
|
})
|
|
86
86
|
|
|
87
87
|
it('1 (0x1E) → $01', () => {
|
|
88
|
-
expect(pressAndReadA(new
|
|
88
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x1E)).toBe(0x01)
|
|
89
89
|
})
|
|
90
90
|
|
|
91
91
|
it('2 (0x1F) → $02', () => {
|
|
92
|
-
expect(pressAndReadA(new
|
|
92
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x1F)).toBe(0x02)
|
|
93
93
|
})
|
|
94
94
|
|
|
95
95
|
it('3 (0x20) → $03', () => {
|
|
96
|
-
expect(pressAndReadA(new
|
|
96
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x20)).toBe(0x03)
|
|
97
97
|
})
|
|
98
98
|
|
|
99
99
|
it('4 (0x21) → $04', () => {
|
|
100
|
-
expect(pressAndReadA(new
|
|
100
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x21)).toBe(0x04)
|
|
101
101
|
})
|
|
102
102
|
|
|
103
103
|
it('5 (0x22) → $05', () => {
|
|
104
|
-
expect(pressAndReadA(new
|
|
104
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x22)).toBe(0x05)
|
|
105
105
|
})
|
|
106
106
|
|
|
107
107
|
it('6 (0x23) → $06', () => {
|
|
108
|
-
expect(pressAndReadA(new
|
|
108
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x23)).toBe(0x06)
|
|
109
109
|
})
|
|
110
110
|
|
|
111
111
|
it('7 (0x24) → $07', () => {
|
|
112
|
-
expect(pressAndReadA(new
|
|
112
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x24)).toBe(0x07)
|
|
113
113
|
})
|
|
114
114
|
|
|
115
115
|
it('8 (0x25) → $08', () => {
|
|
116
|
-
expect(pressAndReadA(new
|
|
116
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x25)).toBe(0x08)
|
|
117
117
|
})
|
|
118
118
|
|
|
119
119
|
it('9 (0x26) → $09', () => {
|
|
120
|
-
expect(pressAndReadA(new
|
|
120
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x26)).toBe(0x09)
|
|
121
121
|
})
|
|
122
122
|
|
|
123
123
|
it('0 (0x27) → $0A', () => {
|
|
124
|
-
expect(pressAndReadA(new
|
|
124
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x27)).toBe(0x0A)
|
|
125
125
|
})
|
|
126
126
|
|
|
127
127
|
it('Right Arrow (0x4F) → $0B ►', () => {
|
|
128
|
-
expect(pressAndReadA(new
|
|
128
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x4F)).toBe(0x0B)
|
|
129
129
|
})
|
|
130
130
|
|
|
131
131
|
it('Enter is not mapped to $0B (it maps to $14)', () => {
|
|
132
|
-
const kp = new
|
|
132
|
+
const kp = new KeypadAttachment(true)
|
|
133
133
|
kp.updateControlLines(false, false, false, true)
|
|
134
134
|
kp.updateKey(0x28, true) // Enter → $14
|
|
135
135
|
expect(kp.readPortA(0x00, 0x00)).toBe(0x14)
|
|
136
136
|
})
|
|
137
137
|
|
|
138
138
|
it('f (0x09) → $0C F', () => {
|
|
139
|
-
expect(pressAndReadA(new
|
|
139
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x09)).toBe(0x0C)
|
|
140
140
|
})
|
|
141
141
|
|
|
142
142
|
it('e (0x08) → $0D E', () => {
|
|
143
|
-
expect(pressAndReadA(new
|
|
143
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x08)).toBe(0x0D)
|
|
144
144
|
})
|
|
145
145
|
|
|
146
146
|
it('d (0x07) → $0E D', () => {
|
|
147
|
-
expect(pressAndReadA(new
|
|
147
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x07)).toBe(0x0E)
|
|
148
148
|
})
|
|
149
149
|
|
|
150
150
|
it('c (0x06) → $0F C', () => {
|
|
151
|
-
expect(pressAndReadA(new
|
|
151
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x06)).toBe(0x0F)
|
|
152
152
|
})
|
|
153
153
|
|
|
154
154
|
it('Escape (0x29) → $10 ESC', () => {
|
|
155
|
-
expect(pressAndReadA(new
|
|
155
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x29)).toBe(0x10)
|
|
156
156
|
})
|
|
157
157
|
|
|
158
158
|
it('Insert (0x49) → $11 INS', () => {
|
|
159
|
-
expect(pressAndReadA(new
|
|
159
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x49)).toBe(0x11)
|
|
160
160
|
})
|
|
161
161
|
|
|
162
162
|
it('Page Up (0x4B) → $12 PGUP', () => {
|
|
163
|
-
expect(pressAndReadA(new
|
|
163
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x4B)).toBe(0x12)
|
|
164
164
|
})
|
|
165
165
|
|
|
166
166
|
it('a (0x04) → $13 A', () => {
|
|
167
|
-
expect(pressAndReadA(new
|
|
167
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x04)).toBe(0x13)
|
|
168
168
|
})
|
|
169
169
|
|
|
170
170
|
it('Up Arrow (0x52) → $14 ▲', () => {
|
|
171
|
-
expect(pressAndReadA(new
|
|
171
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x52)).toBe(0x14)
|
|
172
172
|
})
|
|
173
173
|
|
|
174
174
|
it('Enter (0x28) → $14 ▲', () => {
|
|
175
|
-
expect(pressAndReadA(new
|
|
175
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x28)).toBe(0x14)
|
|
176
176
|
})
|
|
177
177
|
|
|
178
178
|
it('Delete (0x4C) → $15 DEL', () => {
|
|
179
|
-
expect(pressAndReadA(new
|
|
179
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x4C)).toBe(0x15)
|
|
180
180
|
})
|
|
181
181
|
|
|
182
182
|
it('Page Down (0x4E) → $16 PGDN', () => {
|
|
183
|
-
expect(pressAndReadA(new
|
|
183
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x4E)).toBe(0x16)
|
|
184
184
|
})
|
|
185
185
|
|
|
186
186
|
it('b (0x05) → $17 B', () => {
|
|
187
|
-
expect(pressAndReadA(new
|
|
187
|
+
expect(pressAndReadA(new KeypadAttachment(true), 0x05)).toBe(0x17)
|
|
188
188
|
})
|
|
189
189
|
|
|
190
190
|
it('should also map correctly on Port B', () => {
|
|
191
|
-
expect(pressAndReadB(new
|
|
192
|
-
expect(pressAndReadB(new
|
|
191
|
+
expect(pressAndReadB(new KeypadAttachment(false), 0x1E)).toBe(0x01) // '1' → $01
|
|
192
|
+
expect(pressAndReadB(new KeypadAttachment(false), 0x05)).toBe(0x17) // 'b' → $17
|
|
193
193
|
})
|
|
194
194
|
})
|
|
195
195
|
|