ac6502 1.6.0 → 1.8.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/src/index.ts CHANGED
@@ -12,7 +12,7 @@ import sdl from '@kmamal/sdl'
12
12
  import { readFile, writeFile } from 'fs/promises'
13
13
  import { existsSync } from 'fs'
14
14
 
15
- const VERSION = '1.6.0'
15
+ const VERSION = '1.8.0'
16
16
  const WIDTH = 320
17
17
  const HEIGHT = 240
18
18
 
@@ -47,6 +47,7 @@ interface EmulatorOptions {
47
47
  port?: string
48
48
  storage?: string
49
49
  target?: string
50
+ encoder?: string
50
51
  }
51
52
 
52
53
  class Emulator {
@@ -72,6 +73,7 @@ class Emulator {
72
73
  await this.loadBinaries()
73
74
  this.configureFrequency()
74
75
  this.configureScale()
76
+ this.configureEncoder()
75
77
  await this.setupSerialPort()
76
78
  this.setupAudio()
77
79
  this.setupWindow()
@@ -116,12 +118,23 @@ class Emulator {
116
118
  ;(this.machine.io4 as Storage).loadData(new Uint8Array(storageData))
117
119
  } else {
118
120
  console.log(`Storage file not found: ${this.options.storage}`)
119
- console.log('A new storage file will be created on exit.')
120
- ;(this.machine.io4 as Storage).loadData(null)
121
+ console.log('Initializing new storage file...')
122
+ const emptyStorage = (this.machine.io4 as Storage).getData()
123
+ await writeFile(this.options.storage, emptyStorage)
124
+ console.log(`Storage file created: ${this.options.storage}`)
121
125
  }
122
126
  }
123
127
  }
124
128
 
129
+ private configureEncoder(): void {
130
+ const enc = this.options.encoder ?? 'matrix'
131
+ const activePort = enc === 'ps2' ? 'A' : enc === 'matrix' ? 'B' : 'both'
132
+ if (this.machine.keyboardEncoderAttachment) {
133
+ this.machine.keyboardEncoderAttachment.activePort = activePort as 'A' | 'B' | 'both'
134
+ console.log(`Keyboard encoder: ${enc} (Port ${activePort})`)
135
+ }
136
+ }
137
+
125
138
  private configureFrequency(): void {
126
139
  if (this.options.freq) {
127
140
  const frequency = Number(this.options.freq)
@@ -537,6 +550,9 @@ class Emulator {
537
550
  }
538
551
 
539
552
  private shutdown(): void {
553
+ // Stop the machine loop to prevent further rendering after window close
554
+ this.machine.stop()
555
+
540
556
  // Close all connected controllers
541
557
  for (const [id, controller] of this.controllers.entries()) {
542
558
  if (!controller.closed) {
@@ -598,17 +614,18 @@ program
598
614
  .description('Emulator for the A.C. Wright 6502 project.')
599
615
  .version(VERSION, '-v, --version', 'Output the current emulator version')
600
616
  .helpOption('-h, --help', 'Output help / options')
601
- .option('-a, --parity <parity>', 'Parity (odd | even | none)', 'none')
602
- .option('-b, --baudrate <baudrate>', 'Baud Rate', '9600')
603
- .option('-c, --cart <path>', 'Path to 32K Cart binary file')
604
- .option('-d, --databits <databits>', 'Data Bits (5 | 6 | 7 | 8)', '8')
605
- .option('-f, --freq <freq>', 'Set the clock frequency in Hz', '1000000')
606
- .option('-p, --port <port>', 'Path to the serial port (e.g., /dev/ttyUSB0)')
607
- .option('-r, --rom <path>', 'Path to 32K ROM binary file')
608
- .option('-s, --scale <scale>', 'Set the emulator scale', '2')
609
- .option('-S, --storage <path>', 'Path to storage data file for Compact Flash card persistence')
610
- .option('-t, --stopbits <stopbits>', 'Stop Bits (1 | 1.5 | 2)', '1')
617
+ .addOption(new Option('-a, --parity <parity>', 'Parity (odd | even | none)').default('none'))
618
+ .addOption(new Option('-b, --baudrate <baudrate>', 'Baud Rate').default('9600'))
619
+ .addOption(new Option('-c, --cart <path>', 'Path to 32K Cart binary file'))
620
+ .addOption(new Option('-d, --databits <databits>', 'Data Bits (5 | 6 | 7 | 8)').default('8'))
621
+ .addOption(new Option('-f, --freq <freq>', 'Set the clock frequency in Hz').default('1000000'))
622
+ .addOption(new Option('-p, --port <port>', 'Path to the serial port (e.g., /dev/ttyUSB0)'))
623
+ .addOption(new Option('-r, --rom <path>', 'Path to 32K ROM binary file'))
624
+ .addOption(new Option('-s, --scale <scale>', 'Set the emulator scale').default('2'))
625
+ .addOption(new Option('-S, --storage <path>', 'Path to storage data file for Compact Flash card persistence'))
626
+ .addOption(new Option('-t, --stopbits <stopbits>', 'Stop Bits (1 | 1.5 | 2)').default('1'))
611
627
  .addOption(new Option('-T, --target <target>', 'System target').choices(['cob', 'vcs', 'kim', 'dev']).default('cob'))
628
+ .addOption(new Option('-e, --encoder <mode>', 'Keyboard encoder active port (ps2 = Port A / CA1, matrix = Port B / CB1)').choices(['ps2', 'matrix', 'both']).default('matrix'))
612
629
  .addHelpText('beforeAll', figlet.textSync('6502 Emulator', { font: 'cricket' }) + '\n' + `Version: ${VERSION} | A.C. Wright Design\n`)
613
630
  .parse(process.argv)
614
631
 
@@ -2,11 +2,6 @@ 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: RTC): void => {
6
- rtc.write(0x0f, 0x80)
7
- rtc.tick(1)
8
- }
9
-
10
5
  const setTime = (rtc: RTC, values: {
11
6
  seconds: number
12
7
  minutes: number
@@ -17,15 +12,16 @@ const setTime = (rtc: RTC, values: {
17
12
  year: number
18
13
  century: number
19
14
  }): void => {
20
- enableTransfers(rtc)
15
+ rtc.write(0x0f, 0x80) // Set TE to inhibit transfers
21
16
  rtc.write(0x00, values.seconds)
22
17
  rtc.write(0x01, values.minutes)
23
18
  rtc.write(0x02, values.hours)
24
19
  rtc.write(0x03, values.dayOfWeek)
25
20
  rtc.write(0x04, values.date)
26
- rtc.write(0x05, values.month | 0x80)
21
+ rtc.write(0x05, values.month) // EOSC=0: oscillator enabled (DS1511Y default)
27
22
  rtc.write(0x06, values.year)
28
23
  rtc.write(0x07, values.century)
24
+ rtc.write(0x0f, 0x00) // Clear TE: falling edge commits user to internal
29
25
  }
30
26
 
31
27
  describe('RTC', () => {
@@ -64,7 +60,7 @@ describe('RTC', () => {
64
60
  expect(bcdToDecimal(century)).toBeGreaterThanOrEqual(0)
65
61
  expect(bcdToDecimal(century)).toBeLessThanOrEqual(39)
66
62
 
67
- expect(month & 0x80).toBe(0x80)
63
+ expect(month & 0x80).toBe(0x00)
68
64
  })
69
65
  })
70
66
 
@@ -150,7 +146,7 @@ describe('RTC', () => {
150
146
  century: 0x20
151
147
  })
152
148
 
153
- rtc.write(0x05, 0x01)
149
+ rtc.write(0x05, 0x81) // EOSC=1: oscillator disabled
154
150
  rtc.tick(1)
155
151
 
156
152
  expect(rtc.read(0x00)).toBe(0x10)
@@ -78,7 +78,7 @@ const setupTextMode = (vdp: Video): 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: Video, frequency: number = 2000000): void => {
81
+ const renderOneFrame = (vdp: Video, frequency: number = 1000000): 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++) {
@@ -24,7 +24,7 @@ describe('Machine', () => {
24
24
 
25
25
  test('Machine initializes with correct default properties', () => {
26
26
  expect(machine.isRunning).toBe(false)
27
- expect(machine.frequency).toBe(2000000)
27
+ expect(machine.frequency).toBe(1000000)
28
28
  expect(machine.scale).toBe(2)
29
29
  expect(machine.frames).toBe(0)
30
30
  })