ac6502 1.10.0 → 1.12.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.
Files changed (88) hide show
  1. package/README.md +12 -97
  2. package/dist/components/CPU.d.ts +4 -0
  3. package/dist/components/CPU.js +87 -30
  4. package/dist/components/CPU.js.map +1 -1
  5. package/dist/components/IO/ACIA.d.ts +13 -21
  6. package/dist/components/IO/ACIA.js +53 -151
  7. package/dist/components/IO/ACIA.js.map +1 -1
  8. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js +1 -1
  9. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js.map +1 -1
  10. package/dist/components/IO/Empty.d.ts +1 -3
  11. package/dist/components/IO/Empty.js +1 -5
  12. package/dist/components/IO/Empty.js.map +1 -1
  13. package/dist/components/IO/RAMBank.d.ts +3 -4
  14. package/dist/components/IO/RAMBank.js +4 -13
  15. package/dist/components/IO/RAMBank.js.map +1 -1
  16. package/dist/components/IO/RTC.d.ts +2 -3
  17. package/dist/components/IO/RTC.js +17 -7
  18. package/dist/components/IO/RTC.js.map +1 -1
  19. package/dist/components/IO/Sound.d.ts +1 -3
  20. package/dist/components/IO/Sound.js +13 -23
  21. package/dist/components/IO/Sound.js.map +1 -1
  22. package/dist/components/IO/Storage.d.ts +1 -3
  23. package/dist/components/IO/Storage.js +1 -3
  24. package/dist/components/IO/Storage.js.map +1 -1
  25. package/dist/components/IO/VIA.d.ts +1 -3
  26. package/dist/components/IO/VIA.js +6 -7
  27. package/dist/components/IO/VIA.js.map +1 -1
  28. package/dist/components/IO/Video.d.ts +1 -3
  29. package/dist/components/IO/Video.js +3 -5
  30. package/dist/components/IO/Video.js.map +1 -1
  31. package/dist/components/IO.d.ts +1 -3
  32. package/dist/components/Machine.d.ts +3 -9
  33. package/dist/components/Machine.js +72 -259
  34. package/dist/components/Machine.js.map +1 -1
  35. package/dist/index.js +14 -118
  36. package/dist/index.js.map +1 -1
  37. package/dist/lib.d.ts +0 -2
  38. package/dist/lib.js +1 -5
  39. package/dist/lib.js.map +1 -1
  40. package/dist/tests/IO/ACIA.test.js +57 -108
  41. package/dist/tests/IO/ACIA.test.js.map +1 -1
  42. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js +5 -0
  43. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js.map +1 -1
  44. package/dist/tests/IO/Empty.test.js +2 -14
  45. package/dist/tests/IO/Empty.test.js.map +1 -1
  46. package/dist/tests/IO/RAMBank.test.js +5 -12
  47. package/dist/tests/IO/RAMBank.test.js.map +1 -1
  48. package/dist/tests/IO/RTC.test.js +7 -16
  49. package/dist/tests/IO/RTC.test.js.map +1 -1
  50. package/dist/tests/IO/Sound.test.js +6 -8
  51. package/dist/tests/IO/Sound.test.js.map +1 -1
  52. package/dist/tests/IO/Storage.test.js +0 -6
  53. package/dist/tests/IO/Storage.test.js.map +1 -1
  54. package/dist/tests/IO/VIA.test.js +6 -10
  55. package/dist/tests/IO/VIA.test.js.map +1 -1
  56. package/dist/tests/IO/Video.test.js +7 -7
  57. package/dist/tests/IO/Video.test.js.map +1 -1
  58. package/dist/tests/Machine.test.js +1 -1
  59. package/dist/tests/Machine.test.js.map +1 -1
  60. package/package.json +1 -1
  61. package/src/components/CPU.ts +94 -31
  62. package/src/components/IO/ACIA.ts +57 -176
  63. package/src/components/IO/Attachments/KeyboardEncoderAttachment.ts +1 -1
  64. package/src/components/IO/Empty.ts +1 -4
  65. package/src/components/IO/RAMBank.ts +4 -15
  66. package/src/components/IO/RTC.ts +18 -7
  67. package/src/components/IO/Sound.ts +14 -27
  68. package/src/components/IO/Storage.ts +2 -5
  69. package/src/components/IO/VIA.ts +6 -8
  70. package/src/components/IO/Video.ts +5 -7
  71. package/src/components/IO.ts +1 -4
  72. package/src/components/Machine.ts +81 -300
  73. package/src/index.ts +12 -111
  74. package/src/lib.ts +0 -2
  75. package/src/tests/IO/ACIA.test.ts +60 -122
  76. package/src/tests/IO/Attachments/KeyboardEncoderAttachment.test.ts +6 -0
  77. package/src/tests/IO/Empty.test.ts +2 -17
  78. package/src/tests/IO/RAMBank.test.ts +5 -14
  79. package/src/tests/IO/RTC.test.ts +7 -20
  80. package/src/tests/IO/Sound.test.ts +6 -8
  81. package/src/tests/IO/Storage.test.ts +0 -7
  82. package/src/tests/IO/VIA.test.ts +6 -12
  83. package/src/tests/IO/Video.test.ts +7 -8
  84. package/src/tests/Machine.test.ts +1 -1
  85. package/src/components/IO/Attachments/KeypadAttachment.ts +0 -153
  86. package/src/components/IO/Attachments/LCDAttachment.ts +0 -791
  87. package/src/tests/IO/Attachments/KeypadAttachment.test.ts +0 -389
  88. package/src/tests/IO/Attachments/LCDAttachment.test.ts +0 -795
package/README.md CHANGED
@@ -4,7 +4,7 @@ A comprehensive, cycle-accurate emulator for the [A.C. Wright 6502](https://gith
4
4
 
5
5
  ## Overview
6
6
 
7
- This emulator provides a complete software implementation of a 65C02-based computer system, designed to run the same code as the hardware implementation. It features full I/O peripheral support, including video, sound, serial communication, storage, and GPIO interfaces. The emulator supports four different system configurations (targets) to accommodate various use cases from full-featured systems to minimal development boards.
7
+ This emulator provides a complete software implementation of a 65C02-based computer system, designed to run the same code as the hardware implementation. It features full I/O peripheral support, including video, sound, serial communication, storage, and GPIO interfaces.
8
8
 
9
9
  ## Features
10
10
 
@@ -42,7 +42,6 @@ This emulator provides a complete software implementation of a 65C02-based compu
42
42
  - **Input Devices**
43
43
  - Keyboard support (matrix and encoder modes)
44
44
  - Joystick/gamepad support with button mapping (dual controller support)
45
- - Keypad support (KIM mode)
46
45
  - SDL input handling
47
46
 
48
47
  - **Development Features**
@@ -59,16 +58,11 @@ Get up and running in seconds:
59
58
  # Install globally
60
59
  npm install -g ac6502
61
60
 
62
- # Run the emulator (default COB target)
61
+ # Run the emulator
63
62
  ac6502
64
63
 
65
64
  # Load a ROM
66
65
  ac6502 --rom /path/to/rom.bin
67
-
68
- # Run in different system configurations
69
- ac6502 --target kim --rom /path/to/kim.bin
70
- ac6502 --target dev --cart /path/to/program.bin
71
- ac6502 --target vcs --cart /path/to/game.bin
72
66
  ```
73
67
 
74
68
  ## Installation
@@ -120,7 +114,6 @@ ac6502 [options]
120
114
  - `-r, --rom <path>` - Load a system ROM file
121
115
  - `-f, --freq <frequency>` - Set CPU frequency in Hz (default: 1000000)
122
116
  - `-s, --scale <factor>` - Set display scale factor (default: 1)
123
- - `-T, --target <target>` - System target: cob, vcs, kim, dev (default: cob)
124
117
  - `-p, --port <device>` - Serial port device path
125
118
  - `-b, --baudrate <rate>` - Serial baud rate (default: 9600)
126
119
  - `-a, --parity <type>` - Serial parity: none, even, odd (default: none)
@@ -135,99 +128,29 @@ ac6502 [options]
135
128
  Load a cartridge:
136
129
 
137
130
  ```bash
138
- ac6502 --cart /path/to/game.bin
131
+ ac6502 --rom /path/to/rom.bin --cart /path/to/cart.bin
139
132
  ```
140
133
 
141
134
  Connect to serial hardware:
142
135
 
143
136
  ```bash
144
- ac6502 --port /dev/ttyUSB0 --baudrate 9600 --rom /path/to/monitor.bin
145
- ```
146
-
147
- Run in KIM mode with LCD and keypad:
148
-
149
- ```bash
150
- ac6502 --target kim --rom /path/to/kim.bin --scale 2
151
- ```
152
-
153
- Run in development mode with terminal:
154
-
155
- ```bash
156
- ac6502 --target dev --cart /path/to/program.bin
137
+ ac6502 --port /dev/ttyUSB0 --baudrate 9600 --rom /path/to/rom.bin
157
138
  ```
158
139
 
159
140
  Set custom CPU frequency and storage:
160
141
 
161
142
  ```bash
162
- ac6502 --freq 2000000 --storage ./disk.img --cart /path/to/program.bin
143
+ ac6502 --freq 2000000 --storage ./disk.img --rom /path/to/rom.bin --cart /path/to/cart.bin
163
144
  ```
164
145
 
165
146
  ## Architecture
166
147
 
167
- ### System Targets
168
-
169
- The emulator supports four different system configurations via the `--target` option:
170
-
171
- #### COB (Complete On-Board) - Default
172
-
173
- Full-featured system with all peripherals:
174
-
175
- ```
176
- Machine (COB)
177
- ├── CPU (65C02)
178
- ├── RAM (System Memory)
179
- ├── ROM (System BIOS/Monitor)
180
- ├── Cart (Optional Cartridge)
181
- └── I/O Cards (8 slots)
182
- ├── IO1: RAM Card (Expansion)
183
- ├── IO2: RAM Card (Expansion)
184
- ├── IO3: RTC Card (DS1511Y+ Real-Time Clock)
185
- ├── IO4: Storage Card (Compact Flash 8-bit IDE Mode)
186
- ├── IO5: Serial Card (6551 ACIA)
187
- ├── IO6: VIA Card (6522 GPIO)
188
- ├── IO7: Sound Card (6581 SID)
189
- └── IO8: Video Card (TMS9918)
190
- ```
191
-
192
- #### VCS (Video Computer System)
148
+ ### System Configuration
193
149
 
194
- Minimal system focused on video and sound:
150
+ The emulator implements a full-featured 65C02 system with all peripherals:
195
151
 
196
152
  ```
197
- Machine (VCS)
198
- ├── CPU (65C02)
199
- ├── RAM (System Memory)
200
- ├── ROM (System BIOS/Monitor)
201
- ├── Cart (Optional Cartridge)
202
- └── I/O Cards (8 slots)
203
- ├── IO6: VIA Card (6522 GPIO)
204
- ├── IO7: Sound Card (6581 SID)
205
- └── IO8: Video Card (TMS9918)
206
- ```
207
-
208
- #### KIM (Keyboard Input Monitor)
209
-
210
- Single-board computer with LCD and keypad:
211
-
212
- ```
213
- Machine (KIM)
214
- ├── CPU (65C02)
215
- ├── RAM (System Memory)
216
- ├── ROM (System BIOS/Monitor)
217
- ├── Cart (Optional Cartridge)
218
- └── I/O Cards (8 slots)
219
- ├── IO5: Serial Card (6551 ACIA)
220
- └── IO8: VIA Card (6522 GPIO)
221
- ├── LCD Attachment (HD44780 16×2 character display)
222
- └── Keypad Attachment (4×6 matrix keypad)
223
- ```
224
-
225
- #### DEV (Development)
226
-
227
- System for software development with terminal interface:
228
-
229
- ```
230
- Machine (DEV)
153
+ Machine
231
154
  ├── CPU (65C02)
232
155
  ├── RAM (System Memory)
233
156
  ├── ROM (System BIOS/Monitor)
@@ -245,17 +168,12 @@ Machine (DEV)
245
168
 
246
169
  ### VIA (GPIO) Attachments
247
170
 
248
- The VIA card supports multiple attachment types depending on the target:
171
+ The VIA card supports multiple attachment types:
249
172
 
250
- #### Standard Attachments (COB/DEV/VCS)
251
173
  - **Keyboard Matrix**: PS/2-style keyboard matrix scanning
252
174
  - **Keyboard Encoder**: Parallel keyboard encoder
253
175
  - **Joystick A/B**: Game controllers with 8 buttons and directional input
254
176
 
255
- #### KIM Attachments
256
- - **LCD Display**: HD44780-compatible 16×2 character LCD with 5×8 pixel characters
257
- - **Keypad**: 4×6 matrix keypad with 24-key layout
258
-
259
177
  ### Memory Map
260
178
 
261
179
  The system uses a standard 6502 memory layout with I/O cards mapped into the address space. Each I/O card occupies a dedicated region accessible via memory-mapped registers.
@@ -288,9 +206,7 @@ src/
288
206
  │ ├── Attachment.ts
289
207
  │ ├── JoystickAttachment.ts
290
208
  │ ├── KeyboardEncoderAttachment.ts
291
- ├── KeyboardMatrixAttachment.ts
292
- │ ├── KeypadAttachment.ts
293
- │ └── LCDAttachment.ts
209
+ └── KeyboardMatrixAttachment.ts
294
210
  └── tests/ # Comprehensive test suite
295
211
  ```
296
212
 
@@ -337,11 +253,10 @@ The emulator targets 1 MHz operation by default (configurable) and attempts to m
337
253
 
338
254
  ## Supported Input Devices
339
255
 
340
- - **Keyboard**: Full keyboard support via SDL for all targets
341
- - **Game Controllers**: Dual controller support (Player A and Player B) for VCS and COB targets
256
+ - **Keyboard**: Full keyboard support via SDL
257
+ - **Game Controllers**: Dual controller support (Player A and Player B)
342
258
  - D-pad and analog stick support
343
259
  - 8 buttons: Up, Down, Left, Right, A, B, Select, Start
344
- - **Keypad**: 4×6 matrix keypad for KIM target (24 keys including arrows, numbers, and function keys)
345
260
 
346
261
  ## Credits
347
262
 
@@ -20,6 +20,7 @@ export declare class CPU {
20
20
  private opcode;
21
21
  cyclesRem: number;
22
22
  cycles: number;
23
+ private irqLine;
23
24
  a: number;
24
25
  x: number;
25
26
  y: number;
@@ -31,6 +32,8 @@ export declare class CPU {
31
32
  constructor(read: (address: number) => number, write: (address: number, data: number) => void);
32
33
  reset(): void;
33
34
  irq(): void;
35
+ irqTrigger(): void;
36
+ irqClear(): void;
34
37
  nmi(): void;
35
38
  tick(): void;
36
39
  step(): number;
@@ -62,6 +65,7 @@ export declare class CPU {
62
65
  private BCS;
63
66
  private BEQ;
64
67
  private BIT;
68
+ private BIT_IMM;
65
69
  private BMI;
66
70
  private BNE;
67
71
  private BPL;
@@ -12,6 +12,7 @@ class CPU {
12
12
  this.opcode = 0x00; // The instruction byte
13
13
  this.cyclesRem = 0; // Counts how many cycles the current instruction has remaining
14
14
  this.cycles = 0; // Counts the total number of cycles executed
15
+ this.irqLine = false;
15
16
  this.a = 0x00;
16
17
  this.x = 0x00;
17
18
  this.y = 0x00;
@@ -159,7 +160,7 @@ class CPU {
159
160
  { name: 'STX', cycles: 3, opcode: this.STX.bind(this), addrMode: this.ZP0.bind(this) },
160
161
  { name: 'SMB0', cycles: 5, opcode: this.SMB0.bind(this), addrMode: this.ZP0.bind(this) },
161
162
  { name: 'DEY', cycles: 2, opcode: this.DEY.bind(this), addrMode: this.IMP.bind(this) },
162
- { name: 'BIT', cycles: 2, opcode: this.BIT.bind(this), addrMode: this.IMM.bind(this) },
163
+ { name: 'BIT', cycles: 2, opcode: this.BIT_IMM.bind(this), addrMode: this.IMM.bind(this) },
163
164
  { name: 'TXA', cycles: 2, opcode: this.TXA.bind(this), addrMode: this.IMP.bind(this) },
164
165
  { name: '???', cycles: 2, opcode: this.XXX.bind(this), addrMode: this.IMP.bind(this) },
165
166
  { name: 'STY', cycles: 4, opcode: this.STY.bind(this), addrMode: this.ABS.bind(this) },
@@ -329,6 +330,12 @@ class CPU {
329
330
  this.cycles += 7;
330
331
  }
331
332
  }
333
+ irqTrigger() {
334
+ this.irqLine = true;
335
+ }
336
+ irqClear() {
337
+ this.irqLine = false;
338
+ }
332
339
  nmi() {
333
340
  // Push the program counter onto the stack
334
341
  this.write(0x0100 + this.sp, (this.pc >> 8) & 0x00FF);
@@ -364,6 +371,10 @@ class CPU {
364
371
  // addrMode() and opcode() return 1 or 0 if additional clock cycles are required
365
372
  this.cyclesRem += addCycleAddrMode & addCycleOpcode;
366
373
  this.cycles += addCycleAddrMode & addCycleOpcode;
374
+ // Level-triggered IRQ: check after instruction decode
375
+ if (this.irqLine) {
376
+ this.irq();
377
+ }
367
378
  }
368
379
  this.cyclesRem--;
369
380
  }
@@ -576,12 +587,31 @@ class CPU {
576
587
  //
577
588
  ADC() {
578
589
  this.fetch();
579
- this.temp = this.a + this.fetched + this.getFlag(CPU.C);
580
- this.setFlag(CPU.C, (this.temp & 0xFF00) != 0);
581
- this.setFlag(CPU.Z, (this.temp & 0x00FF) == 0);
582
- this.setFlag(CPU.V, ((this.temp ^ this.a) & (this.temp ^ this.fetched) & 0x0080) != 0);
583
- this.setFlag(CPU.N, (this.temp & 0x0080) != 0);
584
- this.a = this.temp & 0x00FF;
590
+ if (this.getFlag(CPU.D)) {
591
+ // BCD decimal mode (65C02)
592
+ const c = this.getFlag(CPU.C);
593
+ const bin = this.a + this.fetched + c;
594
+ let lo = (this.a & 0x0F) + (this.fetched & 0x0F) + c;
595
+ if (lo > 0x09)
596
+ lo += 0x06;
597
+ let hi = (this.a >> 4) + (this.fetched >> 4) + (lo > 0x0F ? 1 : 0);
598
+ if (hi > 0x09)
599
+ hi += 0x06;
600
+ const result = (hi << 4) | (lo & 0x0F);
601
+ this.setFlag(CPU.Z, (bin & 0xFF) === 0);
602
+ this.setFlag(CPU.V, ((this.a ^ bin) & (this.fetched ^ bin) & 0x80) !== 0);
603
+ this.setFlag(CPU.N, (bin & 0x80) !== 0);
604
+ this.setFlag(CPU.C, hi > 0x0F);
605
+ this.a = result & 0xFF;
606
+ }
607
+ else {
608
+ this.temp = this.a + this.fetched + this.getFlag(CPU.C);
609
+ this.setFlag(CPU.C, (this.temp & 0xFF00) != 0);
610
+ this.setFlag(CPU.Z, (this.temp & 0x00FF) == 0);
611
+ this.setFlag(CPU.V, ((this.temp ^ this.a) & (this.temp ^ this.fetched) & 0x0080) != 0);
612
+ this.setFlag(CPU.N, (this.temp & 0x0080) != 0);
613
+ this.a = this.temp & 0x00FF;
614
+ }
585
615
  return 1;
586
616
  }
587
617
  AND() {
@@ -608,10 +638,10 @@ class CPU {
608
638
  }
609
639
  BCC() {
610
640
  if (this.getFlag(CPU.C) == 0) {
611
- this.cycles++;
641
+ this.cyclesRem++;
612
642
  this.addrAbs = this.pc + this.addrRel;
613
643
  if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
614
- this.cycles++;
644
+ this.cyclesRem++;
615
645
  }
616
646
  this.pc = this.addrAbs;
617
647
  }
@@ -619,10 +649,10 @@ class CPU {
619
649
  }
620
650
  BCS() {
621
651
  if (this.getFlag(CPU.C) == 1) {
622
- this.cycles++;
652
+ this.cyclesRem++;
623
653
  this.addrAbs = this.pc + this.addrRel;
624
654
  if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
625
- this.cycles++;
655
+ this.cyclesRem++;
626
656
  }
627
657
  this.pc = this.addrAbs;
628
658
  }
@@ -630,10 +660,10 @@ class CPU {
630
660
  }
631
661
  BEQ() {
632
662
  if (this.getFlag(CPU.Z) == 1) {
633
- this.cycles++;
663
+ this.cyclesRem++;
634
664
  this.addrAbs = this.pc + this.addrRel;
635
665
  if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
636
- this.cycles++;
666
+ this.cyclesRem++;
637
667
  }
638
668
  this.pc = this.addrAbs;
639
669
  }
@@ -647,12 +677,19 @@ class CPU {
647
677
  this.setFlag(CPU.V, (this.fetched & (1 << 6)) != 0);
648
678
  return 0;
649
679
  }
680
+ BIT_IMM() {
681
+ this.fetch();
682
+ this.temp = this.a & this.fetched;
683
+ this.setFlag(CPU.Z, (this.temp & 0x00FF) == 0x00);
684
+ // N and V are NOT modified for BIT immediate (65C02)
685
+ return 0;
686
+ }
650
687
  BMI() {
651
688
  if (this.getFlag(CPU.N) == 1) {
652
- this.cycles++;
689
+ this.cyclesRem++;
653
690
  this.addrAbs = this.pc + this.addrRel;
654
691
  if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
655
- this.cycles++;
692
+ this.cyclesRem++;
656
693
  }
657
694
  this.pc = this.addrAbs;
658
695
  }
@@ -660,10 +697,10 @@ class CPU {
660
697
  }
661
698
  BNE() {
662
699
  if (this.getFlag(CPU.Z) == 0) {
663
- this.cycles++;
700
+ this.cyclesRem++;
664
701
  this.addrAbs = this.pc + this.addrRel;
665
702
  if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
666
- this.cycles++;
703
+ this.cyclesRem++;
667
704
  }
668
705
  this.pc = this.addrAbs;
669
706
  }
@@ -671,10 +708,10 @@ class CPU {
671
708
  }
672
709
  BPL() {
673
710
  if (this.getFlag(CPU.N) == 0) {
674
- this.cycles++;
711
+ this.cyclesRem++;
675
712
  this.addrAbs = this.pc + this.addrRel;
676
713
  if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
677
- this.cycles++;
714
+ this.cyclesRem++;
678
715
  }
679
716
  this.pc = this.addrAbs;
680
717
  }
@@ -696,10 +733,10 @@ class CPU {
696
733
  }
697
734
  BVC() {
698
735
  if (this.getFlag(CPU.V) == 0) {
699
- this.cycles++;
736
+ this.cyclesRem++;
700
737
  this.addrAbs = this.pc + this.addrRel;
701
738
  if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
702
- this.cycles++;
739
+ this.cyclesRem++;
703
740
  }
704
741
  this.pc = this.addrAbs;
705
742
  }
@@ -707,10 +744,10 @@ class CPU {
707
744
  }
708
745
  BVS() {
709
746
  if (this.getFlag(CPU.V) == 1) {
710
- this.cycles++;
747
+ this.cyclesRem++;
711
748
  this.addrAbs = this.pc + this.addrRel;
712
749
  if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
713
- this.cycles++;
750
+ this.cyclesRem++;
714
751
  }
715
752
  this.pc = this.addrAbs;
716
753
  }
@@ -959,13 +996,33 @@ class CPU {
959
996
  }
960
997
  SBC() {
961
998
  this.fetch();
962
- const value = this.fetched ^ 0x00FF;
963
- this.temp = this.a + value + this.getFlag(CPU.C);
964
- this.setFlag(CPU.C, (this.temp & 0xFF00) != 0);
965
- this.setFlag(CPU.Z, (this.temp & 0x00FF) == 0);
966
- this.setFlag(CPU.V, ((this.temp ^ this.a) & (this.temp ^ value) & 0x0080) != 0);
967
- this.setFlag(CPU.N, (this.temp & 0x0080) != 0);
968
- this.a = this.temp & 0x00FF;
999
+ if (this.getFlag(CPU.D)) {
1000
+ // BCD decimal mode (65C02)
1001
+ const c = this.getFlag(CPU.C);
1002
+ const bin = this.a - this.fetched - (1 - c);
1003
+ let lo = (this.a & 0x0F) - (this.fetched & 0x0F) - (1 - c);
1004
+ if (lo < 0)
1005
+ lo = ((lo - 0x06) & 0x0F) | ((this.a & 0xF0) - (this.fetched & 0xF0) - 0x10);
1006
+ else
1007
+ lo = (lo & 0x0F) | ((this.a & 0xF0) - (this.fetched & 0xF0));
1008
+ if (lo < 0)
1009
+ lo -= 0x60;
1010
+ const result = lo & 0xFF;
1011
+ this.setFlag(CPU.Z, (bin & 0xFF) === 0);
1012
+ this.setFlag(CPU.V, ((this.a ^ bin) & (~this.fetched ^ bin) & 0x80) !== 0);
1013
+ this.setFlag(CPU.N, (bin & 0x80) !== 0);
1014
+ this.setFlag(CPU.C, (bin & 0xFFFF) < 0x100);
1015
+ this.a = result;
1016
+ }
1017
+ else {
1018
+ const value = this.fetched ^ 0x00FF;
1019
+ this.temp = this.a + value + this.getFlag(CPU.C);
1020
+ this.setFlag(CPU.C, (this.temp & 0xFF00) != 0);
1021
+ this.setFlag(CPU.Z, (this.temp & 0x00FF) == 0);
1022
+ this.setFlag(CPU.V, ((this.temp ^ this.a) & (this.temp ^ value) & 0x0080) != 0);
1023
+ this.setFlag(CPU.N, (this.temp & 0x0080) != 0);
1024
+ this.a = this.temp & 0x00FF;
1025
+ }
969
1026
  return 1;
970
1027
  }
971
1028
  SEC() {