ac6502 1.9.3 → 1.11.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/dist/components/CPU.d.ts +4 -0
- package/dist/components/CPU.js +87 -30
- package/dist/components/CPU.js.map +1 -1
- package/dist/components/IO/ACIA.d.ts +13 -21
- package/dist/components/IO/ACIA.js +53 -151
- package/dist/components/IO/ACIA.js.map +1 -1
- package/dist/components/IO/Attachments/KeyboardEncoderAttachment.d.ts +5 -10
- package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js +43 -266
- package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js.map +1 -1
- package/dist/components/IO/Empty.d.ts +1 -3
- package/dist/components/IO/Empty.js +1 -5
- package/dist/components/IO/Empty.js.map +1 -1
- package/dist/components/IO/RAMBank.d.ts +3 -4
- package/dist/components/IO/RAMBank.js +4 -13
- package/dist/components/IO/RAMBank.js.map +1 -1
- package/dist/components/IO/RTC.d.ts +2 -3
- package/dist/components/IO/RTC.js +17 -7
- package/dist/components/IO/RTC.js.map +1 -1
- package/dist/components/IO/Sound.d.ts +1 -3
- package/dist/components/IO/Sound.js +13 -23
- package/dist/components/IO/Sound.js.map +1 -1
- package/dist/components/IO/Storage.d.ts +1 -3
- package/dist/components/IO/Storage.js +1 -3
- package/dist/components/IO/Storage.js.map +1 -1
- package/dist/components/IO/VIA.d.ts +1 -3
- package/dist/components/IO/VIA.js +6 -7
- package/dist/components/IO/VIA.js.map +1 -1
- package/dist/components/IO/Video.d.ts +1 -3
- package/dist/components/IO/Video.js +3 -5
- package/dist/components/IO/Video.js.map +1 -1
- package/dist/components/IO.d.ts +1 -3
- package/dist/components/Machine.d.ts +1 -2
- package/dist/components/Machine.js +21 -74
- package/dist/components/Machine.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/tests/IO/ACIA.test.js +57 -108
- package/dist/tests/IO/ACIA.test.js.map +1 -1
- package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js +334 -574
- package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js.map +1 -1
- package/dist/tests/IO/Empty.test.js +2 -14
- package/dist/tests/IO/Empty.test.js.map +1 -1
- package/dist/tests/IO/RAMBank.test.js +5 -12
- package/dist/tests/IO/RAMBank.test.js.map +1 -1
- package/dist/tests/IO/RTC.test.js +7 -16
- package/dist/tests/IO/RTC.test.js.map +1 -1
- package/dist/tests/IO/Sound.test.js +6 -8
- package/dist/tests/IO/Sound.test.js.map +1 -1
- package/dist/tests/IO/Storage.test.js +0 -6
- package/dist/tests/IO/Storage.test.js.map +1 -1
- package/dist/tests/IO/VIA.test.js +6 -10
- package/dist/tests/IO/VIA.test.js.map +1 -1
- package/dist/tests/IO/Video.test.js +7 -7
- package/dist/tests/IO/Video.test.js.map +1 -1
- package/package.json +1 -1
- package/src/components/CPU.ts +94 -31
- package/src/components/IO/ACIA.ts +57 -176
- package/src/components/IO/Attachments/KeyboardEncoderAttachment.ts +45 -217
- package/src/components/IO/Empty.ts +1 -4
- package/src/components/IO/RAMBank.ts +4 -15
- package/src/components/IO/RTC.ts +18 -7
- package/src/components/IO/Sound.ts +14 -27
- package/src/components/IO/Storage.ts +2 -5
- package/src/components/IO/VIA.ts +6 -8
- package/src/components/IO/Video.ts +5 -7
- package/src/components/IO.ts +1 -4
- package/src/components/Machine.ts +22 -90
- package/src/index.ts +1 -1
- package/src/tests/IO/ACIA.test.ts +60 -122
- package/src/tests/IO/Attachments/KeyboardEncoderAttachment.test.ts +342 -676
- package/src/tests/IO/Empty.test.ts +2 -17
- package/src/tests/IO/RAMBank.test.ts +5 -14
- package/src/tests/IO/RTC.test.ts +7 -20
- package/src/tests/IO/Sound.test.ts +6 -8
- package/src/tests/IO/Storage.test.ts +0 -7
- package/src/tests/IO/VIA.test.ts +6 -12
- package/src/tests/IO/Video.test.ts +7 -8
package/src/components/CPU.ts
CHANGED
|
@@ -28,6 +28,8 @@ export class CPU {
|
|
|
28
28
|
cyclesRem: number = 0 // Counts how many cycles the current instruction has remaining
|
|
29
29
|
cycles: number = 0 // Counts the total number of cycles executed
|
|
30
30
|
|
|
31
|
+
private irqLine: boolean = false
|
|
32
|
+
|
|
31
33
|
a: number = 0x00
|
|
32
34
|
x: number = 0x00
|
|
33
35
|
y: number = 0x00
|
|
@@ -103,6 +105,14 @@ export class CPU {
|
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
|
|
108
|
+
irqTrigger(): void {
|
|
109
|
+
this.irqLine = true
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
irqClear(): void {
|
|
113
|
+
this.irqLine = false
|
|
114
|
+
}
|
|
115
|
+
|
|
106
116
|
nmi(): void {
|
|
107
117
|
// Push the program counter onto the stack
|
|
108
118
|
this.write(0x0100 + this.sp, (this.pc >> 8) & 0x00FF)
|
|
@@ -148,6 +158,11 @@ export class CPU {
|
|
|
148
158
|
// addrMode() and opcode() return 1 or 0 if additional clock cycles are required
|
|
149
159
|
this.cyclesRem += addCycleAddrMode & addCycleOpcode
|
|
150
160
|
this.cycles += addCycleAddrMode & addCycleOpcode
|
|
161
|
+
|
|
162
|
+
// Level-triggered IRQ: check after instruction decode
|
|
163
|
+
if (this.irqLine) {
|
|
164
|
+
this.irq()
|
|
165
|
+
}
|
|
151
166
|
}
|
|
152
167
|
|
|
153
168
|
this.cyclesRem--
|
|
@@ -405,13 +420,33 @@ export class CPU {
|
|
|
405
420
|
private ADC(): number {
|
|
406
421
|
this.fetch()
|
|
407
422
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
423
|
+
if (this.getFlag(CPU.D)) {
|
|
424
|
+
// BCD decimal mode (65C02)
|
|
425
|
+
const c = this.getFlag(CPU.C)
|
|
426
|
+
const bin = this.a + this.fetched + c
|
|
427
|
+
|
|
428
|
+
let lo = (this.a & 0x0F) + (this.fetched & 0x0F) + c
|
|
429
|
+
if (lo > 0x09) lo += 0x06
|
|
430
|
+
let hi = (this.a >> 4) + (this.fetched >> 4) + (lo > 0x0F ? 1 : 0)
|
|
431
|
+
if (hi > 0x09) hi += 0x06
|
|
432
|
+
|
|
433
|
+
const result = (hi << 4) | (lo & 0x0F)
|
|
434
|
+
|
|
435
|
+
this.setFlag(CPU.Z, (bin & 0xFF) === 0)
|
|
436
|
+
this.setFlag(CPU.V, ((this.a ^ bin) & (this.fetched ^ bin) & 0x80) !== 0)
|
|
437
|
+
this.setFlag(CPU.N, (bin & 0x80) !== 0)
|
|
438
|
+
this.setFlag(CPU.C, hi > 0x0F)
|
|
413
439
|
|
|
414
|
-
|
|
440
|
+
this.a = result & 0xFF
|
|
441
|
+
} else {
|
|
442
|
+
this.temp = this.a + this.fetched + this.getFlag(CPU.C)
|
|
443
|
+
this.setFlag(CPU.C, (this.temp & 0xFF00) != 0)
|
|
444
|
+
this.setFlag(CPU.Z, (this.temp & 0x00FF) == 0)
|
|
445
|
+
this.setFlag(CPU.V, ((this.temp ^ this.a) & (this.temp ^ this.fetched) & 0x0080) != 0)
|
|
446
|
+
this.setFlag(CPU.N, (this.temp & 0x0080) != 0)
|
|
447
|
+
|
|
448
|
+
this.a = this.temp & 0x00FF
|
|
449
|
+
}
|
|
415
450
|
|
|
416
451
|
return 1
|
|
417
452
|
}
|
|
@@ -441,11 +476,11 @@ export class CPU {
|
|
|
441
476
|
|
|
442
477
|
private BCC(): number {
|
|
443
478
|
if (this.getFlag(CPU.C) == 0) {
|
|
444
|
-
this.
|
|
479
|
+
this.cyclesRem++
|
|
445
480
|
this.addrAbs = this.pc + this.addrRel
|
|
446
481
|
|
|
447
482
|
if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
|
|
448
|
-
this.
|
|
483
|
+
this.cyclesRem++
|
|
449
484
|
}
|
|
450
485
|
|
|
451
486
|
this.pc = this.addrAbs
|
|
@@ -455,11 +490,11 @@ export class CPU {
|
|
|
455
490
|
|
|
456
491
|
private BCS(): number {
|
|
457
492
|
if (this.getFlag(CPU.C) == 1) {
|
|
458
|
-
this.
|
|
493
|
+
this.cyclesRem++
|
|
459
494
|
this.addrAbs = this.pc + this.addrRel
|
|
460
495
|
|
|
461
496
|
if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
|
|
462
|
-
this.
|
|
497
|
+
this.cyclesRem++
|
|
463
498
|
}
|
|
464
499
|
|
|
465
500
|
this.pc = this.addrAbs
|
|
@@ -469,10 +504,10 @@ export class CPU {
|
|
|
469
504
|
|
|
470
505
|
private BEQ(): number {
|
|
471
506
|
if (this.getFlag(CPU.Z) == 1) {
|
|
472
|
-
this.
|
|
507
|
+
this.cyclesRem++
|
|
473
508
|
this.addrAbs = this.pc + this.addrRel
|
|
474
509
|
if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
|
|
475
|
-
this.
|
|
510
|
+
this.cyclesRem++
|
|
476
511
|
}
|
|
477
512
|
|
|
478
513
|
this.pc = this.addrAbs
|
|
@@ -489,13 +524,21 @@ export class CPU {
|
|
|
489
524
|
return 0
|
|
490
525
|
}
|
|
491
526
|
|
|
527
|
+
private BIT_IMM(): number {
|
|
528
|
+
this.fetch()
|
|
529
|
+
this.temp = this.a & this.fetched
|
|
530
|
+
this.setFlag(CPU.Z, (this.temp & 0x00FF) == 0x00)
|
|
531
|
+
// N and V are NOT modified for BIT immediate (65C02)
|
|
532
|
+
return 0
|
|
533
|
+
}
|
|
534
|
+
|
|
492
535
|
private BMI(): number {
|
|
493
536
|
if (this.getFlag(CPU.N) == 1) {
|
|
494
|
-
this.
|
|
537
|
+
this.cyclesRem++
|
|
495
538
|
this.addrAbs = this.pc + this.addrRel
|
|
496
539
|
|
|
497
540
|
if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
|
|
498
|
-
this.
|
|
541
|
+
this.cyclesRem++
|
|
499
542
|
}
|
|
500
543
|
|
|
501
544
|
this.pc = this.addrAbs
|
|
@@ -505,11 +548,11 @@ export class CPU {
|
|
|
505
548
|
|
|
506
549
|
private BNE(): number {
|
|
507
550
|
if (this.getFlag(CPU.Z) == 0) {
|
|
508
|
-
this.
|
|
551
|
+
this.cyclesRem++
|
|
509
552
|
this.addrAbs = this.pc + this.addrRel
|
|
510
553
|
|
|
511
554
|
if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
|
|
512
|
-
this.
|
|
555
|
+
this.cyclesRem++
|
|
513
556
|
}
|
|
514
557
|
|
|
515
558
|
this.pc = this.addrAbs
|
|
@@ -519,11 +562,11 @@ export class CPU {
|
|
|
519
562
|
|
|
520
563
|
private BPL(): number {
|
|
521
564
|
if (this.getFlag(CPU.N) == 0) {
|
|
522
|
-
this.
|
|
565
|
+
this.cyclesRem++
|
|
523
566
|
this.addrAbs = this.pc + this.addrRel
|
|
524
567
|
|
|
525
568
|
if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
|
|
526
|
-
this.
|
|
569
|
+
this.cyclesRem++
|
|
527
570
|
}
|
|
528
571
|
|
|
529
572
|
this.pc = this.addrAbs
|
|
@@ -552,11 +595,11 @@ export class CPU {
|
|
|
552
595
|
|
|
553
596
|
private BVC(): number {
|
|
554
597
|
if (this.getFlag(CPU.V) == 0) {
|
|
555
|
-
this.
|
|
598
|
+
this.cyclesRem++
|
|
556
599
|
this.addrAbs = this.pc + this.addrRel
|
|
557
600
|
|
|
558
601
|
if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
|
|
559
|
-
this.
|
|
602
|
+
this.cyclesRem++
|
|
560
603
|
}
|
|
561
604
|
|
|
562
605
|
this.pc = this.addrAbs
|
|
@@ -566,11 +609,11 @@ export class CPU {
|
|
|
566
609
|
|
|
567
610
|
private BVS(): number {
|
|
568
611
|
if (this.getFlag(CPU.V) == 1) {
|
|
569
|
-
this.
|
|
612
|
+
this.cyclesRem++
|
|
570
613
|
this.addrAbs = this.pc + this.addrRel
|
|
571
614
|
|
|
572
615
|
if ((this.addrAbs & 0xFF00) != (this.pc & 0xFF00)) {
|
|
573
|
-
this.
|
|
616
|
+
this.cyclesRem++
|
|
574
617
|
}
|
|
575
618
|
|
|
576
619
|
this.pc = this.addrAbs
|
|
@@ -851,15 +894,35 @@ export class CPU {
|
|
|
851
894
|
private SBC(): number {
|
|
852
895
|
this.fetch()
|
|
853
896
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
897
|
+
if (this.getFlag(CPU.D)) {
|
|
898
|
+
// BCD decimal mode (65C02)
|
|
899
|
+
const c = this.getFlag(CPU.C)
|
|
900
|
+
const bin = this.a - this.fetched - (1 - c)
|
|
901
|
+
|
|
902
|
+
let lo = (this.a & 0x0F) - (this.fetched & 0x0F) - (1 - c)
|
|
903
|
+
if (lo < 0) lo = ((lo - 0x06) & 0x0F) | ((this.a & 0xF0) - (this.fetched & 0xF0) - 0x10)
|
|
904
|
+
else lo = (lo & 0x0F) | ((this.a & 0xF0) - (this.fetched & 0xF0))
|
|
905
|
+
if (lo < 0) lo -= 0x60
|
|
906
|
+
|
|
907
|
+
const result = lo & 0xFF
|
|
861
908
|
|
|
862
|
-
|
|
909
|
+
this.setFlag(CPU.Z, (bin & 0xFF) === 0)
|
|
910
|
+
this.setFlag(CPU.V, ((this.a ^ bin) & (~this.fetched ^ bin) & 0x80) !== 0)
|
|
911
|
+
this.setFlag(CPU.N, (bin & 0x80) !== 0)
|
|
912
|
+
this.setFlag(CPU.C, (bin & 0xFFFF) < 0x100)
|
|
913
|
+
|
|
914
|
+
this.a = result
|
|
915
|
+
} else {
|
|
916
|
+
const value = this.fetched ^ 0x00FF
|
|
917
|
+
|
|
918
|
+
this.temp = this.a + value + this.getFlag(CPU.C)
|
|
919
|
+
this.setFlag(CPU.C, (this.temp & 0xFF00) != 0)
|
|
920
|
+
this.setFlag(CPU.Z, (this.temp & 0x00FF) == 0)
|
|
921
|
+
this.setFlag(CPU.V, ((this.temp ^ this.a) & (this.temp ^ value) & 0x0080) != 0)
|
|
922
|
+
this.setFlag(CPU.N, (this.temp & 0x0080) != 0)
|
|
923
|
+
|
|
924
|
+
this.a = this.temp & 0x00FF
|
|
925
|
+
}
|
|
863
926
|
|
|
864
927
|
return 1
|
|
865
928
|
}
|
|
@@ -1254,7 +1317,7 @@ export class CPU {
|
|
|
1254
1317
|
{ name: 'STX', cycles: 3, opcode: this.STX.bind(this), addrMode: this.ZP0.bind(this) },
|
|
1255
1318
|
{ name: 'SMB0', cycles: 5, opcode: this.SMB0.bind(this), addrMode: this.ZP0.bind(this) },
|
|
1256
1319
|
{ name: 'DEY', cycles: 2, opcode: this.DEY.bind(this), addrMode: this.IMP.bind(this) },
|
|
1257
|
-
{ name: 'BIT', cycles: 2, opcode: this.
|
|
1320
|
+
{ name: 'BIT', cycles: 2, opcode: this.BIT_IMM.bind(this), addrMode: this.IMM.bind(this) },
|
|
1258
1321
|
{ name: 'TXA', cycles: 2, opcode: this.TXA.bind(this), addrMode: this.IMP.bind(this) },
|
|
1259
1322
|
{ name: '???', cycles: 2, opcode: this.XXX.bind(this), addrMode: this.IMP.bind(this) },
|
|
1260
1323
|
{ name: 'STY', cycles: 4, opcode: this.STY.bind(this), addrMode: this.ABS.bind(this) },
|
|
@@ -3,6 +3,9 @@ import { IO } from '../IO'
|
|
|
3
3
|
/**
|
|
4
4
|
* ACIA - Emulates a R6551 ACIA (Asynchronous Communications Interface Adapter)
|
|
5
5
|
*
|
|
6
|
+
* Simplified to match real R6551 hardware: single-byte TX/RX registers,
|
|
7
|
+
* no buffers, no baud rate timing (USB serial operates at USB speeds).
|
|
8
|
+
*
|
|
6
9
|
* Register Map:
|
|
7
10
|
* $00: Data Register (read/write)
|
|
8
11
|
* $01: Status Register (read) / Programmed Reset (write)
|
|
@@ -11,31 +14,24 @@ import { IO } from '../IO'
|
|
|
11
14
|
*/
|
|
12
15
|
export class ACIA implements IO {
|
|
13
16
|
|
|
14
|
-
raiseIRQ = () => {}
|
|
15
|
-
raiseNMI = () => {}
|
|
16
17
|
transmit?: (data: number) => void
|
|
17
18
|
|
|
18
19
|
// Registers
|
|
19
|
-
private
|
|
20
|
-
private
|
|
20
|
+
private txRegister: number = 0
|
|
21
|
+
private rxRegister: number = 0
|
|
21
22
|
private commandRegister: number = 0
|
|
22
23
|
private controlRegister: number = 0
|
|
23
24
|
|
|
24
|
-
// Buffers
|
|
25
|
-
private transmitBuffer: number[] = []
|
|
26
|
-
private receiveBuffer: number[] = []
|
|
27
|
-
|
|
28
25
|
// Status flags
|
|
26
|
+
private txRegEmpty: boolean = true
|
|
27
|
+
private rxRegFull: boolean = false
|
|
28
|
+
private txPending: boolean = false
|
|
29
|
+
private overrun: boolean = false
|
|
29
30
|
private parityError: boolean = false
|
|
30
31
|
private framingError: boolean = false
|
|
31
|
-
private overrun: boolean = false
|
|
32
32
|
private irqFlag: boolean = false
|
|
33
33
|
private echoMode: boolean = false
|
|
34
34
|
|
|
35
|
-
// Timing
|
|
36
|
-
private cycleCounter: number = 0
|
|
37
|
-
private baudRate: number = 115200
|
|
38
|
-
|
|
39
35
|
/**
|
|
40
36
|
* Read from ACIA register
|
|
41
37
|
*/
|
|
@@ -80,51 +76,32 @@ export class ACIA implements IO {
|
|
|
80
76
|
break
|
|
81
77
|
|
|
82
78
|
case 0x03: // Control Register
|
|
83
|
-
this.
|
|
79
|
+
this.controlRegister = data & 0xFF
|
|
84
80
|
break
|
|
85
81
|
}
|
|
86
82
|
}
|
|
87
83
|
|
|
88
84
|
/**
|
|
89
|
-
* Read data from receive
|
|
85
|
+
* Read data from receive register
|
|
90
86
|
*/
|
|
91
87
|
private readData(): number {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// Update status: clear Receive Data Register Full
|
|
97
|
-
this.statusRegister &= ~0x08
|
|
98
|
-
|
|
99
|
-
// Check for overrun if more data arrives
|
|
100
|
-
if (this.receiveBuffer.length === 0) {
|
|
101
|
-
this.overrun = false
|
|
102
|
-
this.statusRegister &= ~0x04
|
|
103
|
-
}
|
|
88
|
+
// Clear Receive Data Register Full
|
|
89
|
+
this.rxRegFull = false
|
|
90
|
+
this.overrun = false
|
|
104
91
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
this.irqFlag = true
|
|
108
|
-
this.statusRegister |= 0x80
|
|
109
|
-
} else {
|
|
110
|
-
this.irqFlag = false
|
|
111
|
-
this.statusRegister &= ~0x80
|
|
112
|
-
}
|
|
92
|
+
// Clear IRQ if it was from RX
|
|
93
|
+
this.irqFlag = false
|
|
113
94
|
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return this.dataRegister
|
|
95
|
+
return this.rxRegister
|
|
118
96
|
}
|
|
119
97
|
|
|
120
98
|
/**
|
|
121
|
-
* Write data to transmit
|
|
99
|
+
* Write data to transmit register
|
|
122
100
|
*/
|
|
123
101
|
private writeData(data: number): void {
|
|
124
|
-
this.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
this.statusRegister &= ~0x10
|
|
102
|
+
this.txRegister = data & 0xFF
|
|
103
|
+
this.txRegEmpty = false
|
|
104
|
+
this.txPending = true
|
|
128
105
|
}
|
|
129
106
|
|
|
130
107
|
/**
|
|
@@ -148,27 +125,26 @@ export class ACIA implements IO {
|
|
|
148
125
|
if (this.overrun) status |= 0x04
|
|
149
126
|
|
|
150
127
|
// Bit 3: Receive Data Register Full
|
|
151
|
-
if (this.
|
|
128
|
+
if (this.rxRegFull) status |= 0x08
|
|
152
129
|
|
|
153
130
|
// Bit 4: Transmit Data Register Empty
|
|
154
|
-
if (this.
|
|
131
|
+
if (this.txRegEmpty) status |= 0x10
|
|
155
132
|
|
|
156
|
-
// Bit 5: Data Carrier Detect (DCD)
|
|
133
|
+
// Bit 5: Data Carrier Detect (DCD) - always connected
|
|
157
134
|
status &= ~0x20
|
|
158
135
|
|
|
159
|
-
// Bit 6: Data Set Ready (DSR)
|
|
136
|
+
// Bit 6: Data Set Ready (DSR) - always ready
|
|
160
137
|
status |= 0x40
|
|
161
138
|
|
|
162
139
|
// Bit 7: Interrupt (IRQ)
|
|
163
140
|
if (this.irqFlag) status |= 0x80
|
|
164
141
|
|
|
165
|
-
// Clear IRQ and error flags after
|
|
142
|
+
// Clear IRQ and error flags after reading (R6551 spec)
|
|
166
143
|
this.irqFlag = false
|
|
167
144
|
this.parityError = false
|
|
168
145
|
this.framingError = false
|
|
169
146
|
this.overrun = false
|
|
170
147
|
|
|
171
|
-
this.statusRegister = status
|
|
172
148
|
return status
|
|
173
149
|
}
|
|
174
150
|
|
|
@@ -178,82 +154,16 @@ export class ACIA implements IO {
|
|
|
178
154
|
private writeCommand(data: number): void {
|
|
179
155
|
this.commandRegister = data & 0xFF
|
|
180
156
|
|
|
181
|
-
// Bits 0-1: DTR control
|
|
182
|
-
// const dtrControl = data & 0x03
|
|
183
|
-
|
|
184
|
-
// Bit 1: Receiver Interrupt Request Disable (RIIE) — 0 = IRQ enabled, 1 = disabled (active low)
|
|
185
|
-
const receiveIRQEnabled = (data & 0x02) === 0
|
|
186
|
-
|
|
187
|
-
// Bits 3-2: Transmitter Interrupt Control (TIC)
|
|
188
|
-
// const transmitControl = (data >> 2) & 0x03
|
|
189
|
-
|
|
190
157
|
// Bit 4: Echo Mode Enable (EME)
|
|
191
158
|
this.echoMode = (data & 0x10) !== 0
|
|
192
|
-
|
|
193
|
-
// Bits 6-7: Parity control
|
|
194
|
-
// const parityControl = (data >> 6) & 0x03
|
|
195
|
-
|
|
196
|
-
// Handle receive IRQ
|
|
197
|
-
if (receiveIRQEnabled && this.receiveBuffer.length > 0) {
|
|
198
|
-
this.irqFlag = true
|
|
199
|
-
this.statusRegister |= 0x80
|
|
200
|
-
this.raiseIRQ()
|
|
201
|
-
} else if (!receiveIRQEnabled) {
|
|
202
|
-
this.irqFlag = false
|
|
203
|
-
this.statusRegister &= ~0x80
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Write to control register
|
|
209
|
-
*/
|
|
210
|
-
private writeControl(data: number): void {
|
|
211
|
-
this.controlRegister = data & 0xFF
|
|
212
|
-
|
|
213
|
-
// Bits 0-3: Baud rate
|
|
214
|
-
const baudRateCode = data & 0x0F
|
|
215
|
-
this.baudRate = this.getBaudRate(baudRateCode)
|
|
216
|
-
|
|
217
|
-
// Bit 4: Receiver clock source (internal/external)
|
|
218
|
-
const receiverClockSource = (data & 0x10) !== 0
|
|
219
|
-
|
|
220
|
-
// Bits 5-6: Word length (5, 6, 7, or 8 bits)
|
|
221
|
-
const wordLength = ((data >> 5) & 0x03) + 5
|
|
222
|
-
|
|
223
|
-
// Bit 7: Stop bits (1 or 2)
|
|
224
|
-
const stopBits = (data & 0x80) ? 2 : 1
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Get baud rate from control register code
|
|
229
|
-
*/
|
|
230
|
-
private getBaudRate(code: number): number {
|
|
231
|
-
const baudRates = [
|
|
232
|
-
115200, // 0000 (actually 16x external clock, using 115200 as default)
|
|
233
|
-
50, // 0001
|
|
234
|
-
75, // 0010
|
|
235
|
-
110, // 0011
|
|
236
|
-
135, // 0100
|
|
237
|
-
150, // 0101
|
|
238
|
-
300, // 0110
|
|
239
|
-
600, // 0111
|
|
240
|
-
1200, // 1000
|
|
241
|
-
1800, // 1001
|
|
242
|
-
2400, // 1010
|
|
243
|
-
3600, // 1011
|
|
244
|
-
4800, // 1100
|
|
245
|
-
7200, // 1101
|
|
246
|
-
9600, // 1110
|
|
247
|
-
19200 // 1111
|
|
248
|
-
]
|
|
249
|
-
return baudRates[code] || 115200
|
|
250
159
|
}
|
|
251
160
|
|
|
252
161
|
/**
|
|
253
162
|
* Programmed reset
|
|
254
163
|
*/
|
|
255
164
|
private programmedReset(): void {
|
|
256
|
-
this.
|
|
165
|
+
this.txRegEmpty = true
|
|
166
|
+
this.txPending = false
|
|
257
167
|
this.parityError = false
|
|
258
168
|
this.framingError = false
|
|
259
169
|
this.overrun = false
|
|
@@ -261,100 +171,71 @@ export class ACIA implements IO {
|
|
|
261
171
|
}
|
|
262
172
|
|
|
263
173
|
/**
|
|
264
|
-
* Tick -
|
|
174
|
+
* Tick - process TX/RX each cycle, return interrupt status
|
|
265
175
|
*/
|
|
266
|
-
tick(frequency: number):
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if (!(this.commandRegister & 0x02) && this.receiveBuffer.length > 0) {
|
|
271
|
-
this.irqFlag = true
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (this.irqFlag) {
|
|
275
|
-
this.raiseIRQ()
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
this.cycleCounter++
|
|
176
|
+
tick(frequency: number): number {
|
|
177
|
+
// Handle pending transmit - send immediately (no baud timing)
|
|
178
|
+
if (this.txPending) {
|
|
179
|
+
this.txPending = false
|
|
279
180
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const cyclesPerByte = Math.floor((frequency / this.baudRate) * 10)
|
|
283
|
-
|
|
284
|
-
// Simulate transmission based on actual baud rate
|
|
285
|
-
if (this.cycleCounter >= cyclesPerByte && this.transmitBuffer.length > 0) {
|
|
286
|
-
this.cycleCounter = 0
|
|
287
|
-
|
|
288
|
-
// Transmit one byte
|
|
289
|
-
const byte = this.transmitBuffer.shift()
|
|
290
|
-
|
|
291
|
-
if (byte !== undefined && this.transmit) {
|
|
292
|
-
this.transmit(byte)
|
|
181
|
+
if (this.transmit) {
|
|
182
|
+
this.transmit(this.txRegister)
|
|
293
183
|
}
|
|
294
184
|
|
|
295
|
-
|
|
296
|
-
if (this.transmitBuffer.length === 0) {
|
|
297
|
-
this.statusRegister |= 0x10
|
|
185
|
+
this.txRegEmpty = true
|
|
298
186
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
this.statusRegister |= 0x80
|
|
303
|
-
this.raiseIRQ()
|
|
304
|
-
}
|
|
187
|
+
// Trigger transmit complete IRQ if enabled (TIC bits 3-2 = 01)
|
|
188
|
+
if ((this.commandRegister & 0x0C) === 0x04) {
|
|
189
|
+
this.irqFlag = true
|
|
305
190
|
}
|
|
191
|
+
|
|
192
|
+
// Echo mode: received data echoed back
|
|
193
|
+
// (echo of transmitted data is handled in onData)
|
|
306
194
|
}
|
|
195
|
+
|
|
196
|
+
// Return IRQ status
|
|
197
|
+
return this.irqFlag ? 0x80 : 0
|
|
307
198
|
}
|
|
308
199
|
|
|
309
200
|
/**
|
|
310
201
|
* Reset the ACIA
|
|
311
202
|
*/
|
|
312
203
|
reset(coldStart: boolean): void {
|
|
313
|
-
this.
|
|
314
|
-
this.
|
|
204
|
+
this.txRegister = 0
|
|
205
|
+
this.rxRegister = 0
|
|
315
206
|
this.commandRegister = 0
|
|
316
207
|
this.controlRegister = 0
|
|
317
|
-
|
|
318
|
-
this.
|
|
319
|
-
this.
|
|
320
|
-
|
|
208
|
+
|
|
209
|
+
this.txRegEmpty = true
|
|
210
|
+
this.rxRegFull = false
|
|
211
|
+
this.txPending = false
|
|
212
|
+
this.overrun = false
|
|
321
213
|
this.parityError = false
|
|
322
214
|
this.framingError = false
|
|
323
|
-
this.overrun = false
|
|
324
215
|
this.irqFlag = false
|
|
325
216
|
this.echoMode = false
|
|
326
|
-
|
|
327
|
-
this.cycleCounter = 0
|
|
328
|
-
this.baudRate = 115200
|
|
329
217
|
}
|
|
330
218
|
|
|
331
219
|
/**
|
|
332
220
|
* Receive data from external source
|
|
333
221
|
*/
|
|
334
222
|
onData(data: number): void {
|
|
335
|
-
if (this.
|
|
223
|
+
if (this.rxRegFull) {
|
|
336
224
|
// Overrun: new data arrived before the previous byte was read
|
|
337
225
|
this.overrun = true
|
|
338
|
-
this.statusRegister |= 0x04
|
|
339
226
|
}
|
|
340
227
|
|
|
341
|
-
this.
|
|
342
|
-
|
|
343
|
-
// Set Receive Data Register Full flag
|
|
344
|
-
this.statusRegister |= 0x08
|
|
228
|
+
this.rxRegister = data & 0xFF
|
|
229
|
+
this.rxRegFull = true
|
|
345
230
|
|
|
346
231
|
// Trigger receive IRQ if enabled (bit 1 = 0 means enabled, active low)
|
|
347
232
|
if (!(this.commandRegister & 0x02)) {
|
|
348
233
|
this.irqFlag = true
|
|
349
|
-
this.statusRegister |= 0x80
|
|
350
|
-
this.raiseIRQ()
|
|
351
234
|
}
|
|
352
235
|
|
|
353
236
|
// Echo mode: automatically transmit received data
|
|
354
|
-
if (this.echoMode) {
|
|
355
|
-
this.
|
|
356
|
-
// Clear Transmit Data Register Empty flag
|
|
357
|
-
this.statusRegister &= ~0x10
|
|
237
|
+
if (this.echoMode && this.transmit) {
|
|
238
|
+
this.transmit(data & 0xFF)
|
|
358
239
|
}
|
|
359
240
|
}
|
|
360
241
|
}
|