ac6502 1.1.1 → 1.3.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 (61) hide show
  1. package/dist/components/CPU.d.ts +162 -0
  2. package/dist/components/Cart.d.ts +9 -0
  3. package/dist/components/IO/DevOutputBoard.d.ts +19 -0
  4. package/dist/components/IO/DevOutputBoard.js +33 -0
  5. package/dist/components/IO/DevOutputBoard.js.map +1 -0
  6. package/dist/components/IO/EmptyCard.d.ts +9 -0
  7. package/dist/components/IO/GPIOAttachments/GPIOAttachment.d.ts +112 -0
  8. package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.d.ts +53 -0
  9. package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.d.ts +63 -0
  10. package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.d.ts +44 -0
  11. package/dist/components/IO/GPIOAttachments/GPIOKeypadAttachment.d.ts +47 -0
  12. package/dist/components/IO/GPIOAttachments/GPIOLCDAttachment.d.ts +110 -0
  13. package/dist/components/IO/GPIOCard.d.ts +105 -0
  14. package/dist/components/IO/RAMCard.d.ts +37 -0
  15. package/dist/components/IO/RTCCard.d.ts +107 -0
  16. package/dist/components/IO/SerialCard.d.ts +76 -0
  17. package/dist/components/IO/SoundCard.d.ts +120 -0
  18. package/dist/components/IO/SoundCard.js +2 -0
  19. package/dist/components/IO/SoundCard.js.map +1 -1
  20. package/dist/components/IO/StorageCard.d.ts +74 -0
  21. package/dist/components/IO/VideoCard.d.ts +141 -0
  22. package/dist/components/IO/VideoCard.js +19 -12
  23. package/dist/components/IO/VideoCard.js.map +1 -1
  24. package/dist/components/IO.d.ts +8 -0
  25. package/dist/components/Machine.d.ts +66 -0
  26. package/dist/components/Machine.js +38 -6
  27. package/dist/components/Machine.js.map +1 -1
  28. package/dist/components/RAM.d.ts +9 -0
  29. package/dist/components/ROM.d.ts +9 -0
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.js +35 -16
  32. package/dist/index.js.map +1 -1
  33. package/dist/lib.d.ts +22 -0
  34. package/dist/lib.js +47 -0
  35. package/dist/lib.js.map +1 -0
  36. package/dist/tests/CPU.test.d.ts +1 -0
  37. package/dist/tests/Cart.test.d.ts +1 -0
  38. package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.d.ts +1 -0
  39. package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.d.ts +1 -0
  40. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.d.ts +1 -0
  41. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.d.ts +1 -0
  42. package/dist/tests/IO/GPIOAttachments/GPIOKeypadAttachment.test.d.ts +1 -0
  43. package/dist/tests/IO/GPIOAttachments/GPIOLCDAttachment.test.d.ts +1 -0
  44. package/dist/tests/IO/GPIOCard.test.d.ts +1 -0
  45. package/dist/tests/IO/RAMCard.test.d.ts +1 -0
  46. package/dist/tests/IO/RTCCard.test.d.ts +1 -0
  47. package/dist/tests/IO/SerialCard.test.d.ts +1 -0
  48. package/dist/tests/IO/SoundCard.test.d.ts +1 -0
  49. package/dist/tests/IO/StorageCard.test.d.ts +1 -0
  50. package/dist/tests/IO/VideoCard.test.d.ts +1 -0
  51. package/dist/tests/Machine.test.d.ts +1 -0
  52. package/dist/tests/RAM.test.d.ts +1 -0
  53. package/dist/tests/ROM.test.d.ts +1 -0
  54. package/package.json +5 -3
  55. package/src/components/IO/DevOutputBoard.ts +34 -0
  56. package/src/components/IO/SoundCard.ts +3 -0
  57. package/src/components/IO/VideoCard.ts +21 -12
  58. package/src/components/Machine.ts +40 -7
  59. package/src/index.ts +35 -18
  60. package/src/lib.ts +27 -0
  61. package/tsconfig.json +1 -0
@@ -0,0 +1,162 @@
1
+ export interface CPUInstruction {
2
+ name: string;
3
+ cycles: number;
4
+ opcode: () => number;
5
+ addrMode: () => number;
6
+ }
7
+ export declare class CPU {
8
+ static C: number;
9
+ static Z: number;
10
+ static I: number;
11
+ static D: number;
12
+ static B: number;
13
+ static U: number;
14
+ static V: number;
15
+ static N: number;
16
+ private fetched;
17
+ private temp;
18
+ private addrAbs;
19
+ private addrRel;
20
+ private opcode;
21
+ cyclesRem: number;
22
+ cycles: number;
23
+ a: number;
24
+ x: number;
25
+ y: number;
26
+ pc: number;
27
+ sp: number;
28
+ st: number;
29
+ read: (address: number) => number;
30
+ write: (address: number, data: number) => void;
31
+ constructor(read: (address: number) => number, write: (address: number, data: number) => void);
32
+ reset(): void;
33
+ irq(): void;
34
+ nmi(): void;
35
+ tick(): void;
36
+ step(): number;
37
+ private fetch;
38
+ private getFlag;
39
+ private setFlag;
40
+ private incPC;
41
+ private decPC;
42
+ private incSP;
43
+ private decSP;
44
+ private IMP;
45
+ private IMM;
46
+ private ZP0;
47
+ private ZPX;
48
+ private ZPY;
49
+ private REL;
50
+ private ABS;
51
+ private ABX;
52
+ private ABY;
53
+ private IND;
54
+ private IZX;
55
+ private IZY;
56
+ private IAX;
57
+ private ZPR;
58
+ private ADC;
59
+ private AND;
60
+ private ASL;
61
+ private BCC;
62
+ private BCS;
63
+ private BEQ;
64
+ private BIT;
65
+ private BMI;
66
+ private BNE;
67
+ private BPL;
68
+ private BRK;
69
+ private BVC;
70
+ private BVS;
71
+ private CLC;
72
+ private CLD;
73
+ private CLI;
74
+ private CLV;
75
+ private CMP;
76
+ private CPX;
77
+ private CPY;
78
+ private DEC;
79
+ private DEX;
80
+ private DEY;
81
+ private EOR;
82
+ private INC;
83
+ private INX;
84
+ private INY;
85
+ private JMP;
86
+ private JSR;
87
+ private LDA;
88
+ private LDX;
89
+ private LDY;
90
+ private LSR;
91
+ private NOP;
92
+ private ORA;
93
+ private PHA;
94
+ private PHP;
95
+ private PLA;
96
+ private PLP;
97
+ private ROL;
98
+ private ROR;
99
+ private RTI;
100
+ private RTS;
101
+ private SBC;
102
+ private SEC;
103
+ private SED;
104
+ private SEI;
105
+ private STA;
106
+ private STX;
107
+ private STY;
108
+ private TAX;
109
+ private TAY;
110
+ private TSX;
111
+ private TXA;
112
+ private TXS;
113
+ private TYA;
114
+ private BRA;
115
+ private PHX;
116
+ private PHY;
117
+ private PLX;
118
+ private PLY;
119
+ private STZ;
120
+ private TRB;
121
+ private TSB;
122
+ private STP;
123
+ private WAI;
124
+ private BBR;
125
+ private BBR0;
126
+ private BBR1;
127
+ private BBR2;
128
+ private BBR3;
129
+ private BBR4;
130
+ private BBR5;
131
+ private BBR6;
132
+ private BBR7;
133
+ private BBS;
134
+ private BBS0;
135
+ private BBS1;
136
+ private BBS2;
137
+ private BBS3;
138
+ private BBS4;
139
+ private BBS5;
140
+ private BBS6;
141
+ private BBS7;
142
+ private RMB;
143
+ private RMB0;
144
+ private RMB1;
145
+ private RMB2;
146
+ private RMB3;
147
+ private RMB4;
148
+ private RMB5;
149
+ private RMB6;
150
+ private RMB7;
151
+ private SMB;
152
+ private SMB0;
153
+ private SMB1;
154
+ private SMB2;
155
+ private SMB3;
156
+ private SMB4;
157
+ private SMB5;
158
+ private SMB6;
159
+ private SMB7;
160
+ private XXX;
161
+ instructionTable: CPUInstruction[];
162
+ }
@@ -0,0 +1,9 @@
1
+ export declare class Cart {
2
+ static START: number;
3
+ static END: number;
4
+ static CODE: number;
5
+ static SIZE: number;
6
+ data: number[];
7
+ read(address: number): number;
8
+ load(data: number[]): void;
9
+ }
@@ -0,0 +1,19 @@
1
+ import { IO } from '../IO';
2
+ import { VTAC } from 'vtac-terminal';
3
+ /**
4
+ * DevOutputBoard - Development output board using a VTAC terminal controller
5
+ *
6
+ * Register Map:
7
+ * $00: Data / Status Register
8
+ * Write: sends byte to VTAC for processing
9
+ * Read: always returns 0 (bit 7 is a busy flag on the real device; busy is never set here)
10
+ */
11
+ export declare class DevOutputBoard implements IO {
12
+ raiseIRQ: () => void;
13
+ raiseNMI: () => void;
14
+ readonly vtac: VTAC;
15
+ read(address: number): number;
16
+ write(address: number, data: number): void;
17
+ tick(frequency: number): void;
18
+ reset(coldStart: boolean): void;
19
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DevOutputBoard = void 0;
4
+ const vtac_terminal_1 = require("vtac-terminal");
5
+ /**
6
+ * DevOutputBoard - Development output board using a VTAC terminal controller
7
+ *
8
+ * Register Map:
9
+ * $00: Data / Status Register
10
+ * Write: sends byte to VTAC for processing
11
+ * Read: always returns 0 (bit 7 is a busy flag on the real device; busy is never set here)
12
+ */
13
+ class DevOutputBoard {
14
+ constructor() {
15
+ this.raiseIRQ = () => { };
16
+ this.raiseNMI = () => { };
17
+ this.vtac = new vtac_terminal_1.VTAC();
18
+ }
19
+ read(address) {
20
+ // Status register: bit 7 is busy flag on real device, never busy in emulation
21
+ return 0;
22
+ }
23
+ write(address, data) {
24
+ const register = address & 0x00;
25
+ if (register === 0x00) {
26
+ this.vtac.parse(data);
27
+ }
28
+ }
29
+ tick(frequency) { }
30
+ reset(coldStart) { }
31
+ }
32
+ exports.DevOutputBoard = DevOutputBoard;
33
+ //# sourceMappingURL=DevOutputBoard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DevOutputBoard.js","sourceRoot":"","sources":["../../../src/components/IO/DevOutputBoard.ts"],"names":[],"mappings":";;;AACA,iDAAoC;AAEpC;;;;;;;GAOG;AACH,MAAa,cAAc;IAA3B;QAEE,aAAQ,GAAG,GAAG,EAAE,GAAE,CAAC,CAAA;QACnB,aAAQ,GAAG,GAAG,EAAE,GAAE,CAAC,CAAA;QAEV,SAAI,GAAS,IAAI,oBAAI,EAAE,CAAA;IAiBlC,CAAC;IAfC,IAAI,CAAC,OAAe;QAClB,8EAA8E;QAC9E,OAAO,CAAC,CAAA;IACV,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAAY;QACjC,MAAM,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAA;QAC/B,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACvB,CAAC;IACH,CAAC;IAED,IAAI,CAAC,SAAiB,IAAS,CAAC;IAChC,KAAK,CAAC,SAAkB,IAAS,CAAC;CAEnC;AAtBD,wCAsBC"}
@@ -0,0 +1,9 @@
1
+ import { IO } from '../IO';
2
+ export declare class EmptyCard implements IO {
3
+ raiseIRQ: () => void;
4
+ raiseNMI: () => void;
5
+ read(address: number): number;
6
+ write(address: number, data: number): void;
7
+ tick(frequency: number): void;
8
+ reset(coldStart: boolean): void;
9
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Interface for devices that can be attached to GPIO ports
3
+ */
4
+ export interface GPIOAttachment {
5
+ /**
6
+ * Reset the attachment to its initial state
7
+ */
8
+ reset(): void;
9
+ /**
10
+ * Update the attachment state based on CPU clock ticks
11
+ * @param cpuFrequency - The CPU frequency in Hz
12
+ */
13
+ tick(cpuFrequency: number): void;
14
+ /**
15
+ * Read data from Port A
16
+ * @param ddr - Data Direction Register value
17
+ * @param or - Output Register value
18
+ * @returns The data to be read from the port
19
+ */
20
+ readPortA(ddr: number, or: number): number;
21
+ /**
22
+ * Read data from Port B
23
+ * @param ddr - Data Direction Register value
24
+ * @param or - Output Register value
25
+ * @returns The data to be read from the port
26
+ */
27
+ readPortB(ddr: number, or: number): number;
28
+ /**
29
+ * Write data to Port A
30
+ * @param value - The value being written
31
+ * @param ddr - Data Direction Register value
32
+ */
33
+ writePortA(value: number, ddr: number): void;
34
+ /**
35
+ * Write data to Port B
36
+ * @param value - The value being written
37
+ * @param ddr - Data Direction Register value
38
+ */
39
+ writePortB(value: number, ddr: number): void;
40
+ /**
41
+ * Check if the attachment is enabled
42
+ * @returns true if enabled, false otherwise
43
+ */
44
+ isEnabled(): boolean;
45
+ /**
46
+ * Get the priority of this attachment (lower values = higher priority)
47
+ * @returns The priority value
48
+ */
49
+ getPriority(): number;
50
+ /**
51
+ * Clear interrupt flags
52
+ * @param ca1 - Clear CA1 interrupt
53
+ * @param ca2 - Clear CA2 interrupt
54
+ * @param cb1 - Clear CB1 interrupt
55
+ * @param cb2 - Clear CB2 interrupt
56
+ */
57
+ clearInterrupts(ca1: boolean, ca2: boolean, cb1: boolean, cb2: boolean): void;
58
+ /**
59
+ * Update control line states
60
+ * @param ca1 - CA1 control line state
61
+ * @param ca2 - CA2 control line state
62
+ * @param cb1 - CB1 control line state
63
+ * @param cb2 - CB2 control line state
64
+ */
65
+ updateControlLines(ca1: boolean, ca2: boolean, cb1: boolean, cb2: boolean): void;
66
+ /**
67
+ * Check if CA1 interrupt is pending
68
+ * @returns true if interrupt is pending
69
+ */
70
+ hasCA1Interrupt(): boolean;
71
+ /**
72
+ * Check if CA2 interrupt is pending
73
+ * @returns true if interrupt is pending
74
+ */
75
+ hasCA2Interrupt(): boolean;
76
+ /**
77
+ * Check if CB1 interrupt is pending
78
+ * @returns true if interrupt is pending
79
+ */
80
+ hasCB1Interrupt(): boolean;
81
+ /**
82
+ * Check if CB2 interrupt is pending
83
+ * @returns true if interrupt is pending
84
+ */
85
+ hasCB2Interrupt(): boolean;
86
+ }
87
+ /**
88
+ * Base abstract class for GPIO attachments with common functionality
89
+ */
90
+ export declare abstract class GPIOAttachmentBase implements GPIOAttachment {
91
+ protected priority: number;
92
+ protected enabled: boolean;
93
+ protected ca1Interrupt: boolean;
94
+ protected ca2Interrupt: boolean;
95
+ protected cb1Interrupt: boolean;
96
+ protected cb2Interrupt: boolean;
97
+ constructor(priority: number, ca1Interrupt?: boolean, ca2Interrupt?: boolean, cb1Interrupt?: boolean, cb2Interrupt?: boolean);
98
+ reset(): void;
99
+ tick(cpuFrequency: number): void;
100
+ readPortA(ddr: number, or: number): number;
101
+ readPortB(ddr: number, or: number): number;
102
+ writePortA(value: number, ddr: number): void;
103
+ writePortB(value: number, ddr: number): void;
104
+ isEnabled(): boolean;
105
+ getPriority(): number;
106
+ clearInterrupts(ca1: boolean, ca2: boolean, cb1: boolean, cb2: boolean): void;
107
+ updateControlLines(ca1: boolean, ca2: boolean, cb1: boolean, cb2: boolean): void;
108
+ hasCA1Interrupt(): boolean;
109
+ hasCA2Interrupt(): boolean;
110
+ hasCB1Interrupt(): boolean;
111
+ hasCB2Interrupt(): boolean;
112
+ }
@@ -0,0 +1,53 @@
1
+ import { GPIOAttachmentBase } from './GPIOAttachment';
2
+ /**
3
+ * GPIOJoystickAttachment - Emulates a joystick/gamepad connected to GPIO port
4
+ *
5
+ * Buttons are active-low (0 = pressed, 1 = released)
6
+ * Can be attached to either Port A or Port B
7
+ */
8
+ export declare class GPIOJoystickAttachment extends GPIOAttachmentBase {
9
+ static readonly BUTTON_UP = 1;
10
+ static readonly BUTTON_DOWN = 2;
11
+ static readonly BUTTON_LEFT = 4;
12
+ static readonly BUTTON_RIGHT = 8;
13
+ static readonly BUTTON_A = 16;
14
+ static readonly BUTTON_B = 32;
15
+ static readonly BUTTON_SELECT = 64;
16
+ static readonly BUTTON_START = 128;
17
+ private buttonState;
18
+ private attachedToPortA;
19
+ constructor(attachToPortA?: boolean, priority?: number);
20
+ reset(): void;
21
+ readPortA(ddr: number, or: number): number;
22
+ readPortB(ddr: number, or: number): number;
23
+ /**
24
+ * Update the joystick button state
25
+ * @param buttons - Button state byte (1 = pressed, 0 = released)
26
+ */
27
+ updateJoystick(buttons: number): void;
28
+ /**
29
+ * Get the current button state
30
+ * @returns The button state byte
31
+ */
32
+ getButtonState(): number;
33
+ /**
34
+ * Check if a specific button is pressed
35
+ * @param button - Button bit mask to check
36
+ * @returns true if button is pressed
37
+ */
38
+ isButtonPressed(button: number): boolean;
39
+ /**
40
+ * Press a button (set its bit)
41
+ * @param button - Button bit mask to press
42
+ */
43
+ pressButton(button: number): void;
44
+ /**
45
+ * Release a button (clear its bit)
46
+ * @param button - Button bit mask to release
47
+ */
48
+ releaseButton(button: number): void;
49
+ /**
50
+ * Clear all button presses
51
+ */
52
+ releaseAllButtons(): void;
53
+ }
@@ -0,0 +1,63 @@
1
+ import { GPIOAttachmentBase } from './GPIOAttachment';
2
+ /**
3
+ * GPIOKeyboardEncoderAttachment - Emulates a keyboard encoder that provides ASCII-encoded
4
+ * key data on both GPIO ports A and B.
5
+ *
6
+ * This attachment uses the VIA control lines to signal data availability:
7
+ * - CA2 LOW enables Port A
8
+ * - CB2 LOW enables Port B
9
+ * - CA1 interrupt signals data ready on Port A
10
+ * - CB1 interrupt signals data ready on Port B
11
+ *
12
+ * The encoder supports extensive modifier key combinations:
13
+ * - MENU key: 0x80 (alone), 0x90 (with Alt)
14
+ * - Function keys F1-F15: 0x81-0x8F (alone), 0x91-0x9F (with Alt)
15
+ * - Ctrl combinations: Control codes 0x00-0x1F
16
+ * - Alt+Shift: Extended character set 0xA0-0xFF
17
+ * - Alt: Extended character set 0xE0-0xFF
18
+ * - Shift: Uppercase letters and shifted symbols
19
+ */
20
+ export declare class GPIOKeyboardEncoderAttachment extends GPIOAttachmentBase {
21
+ private asciiDataA;
22
+ private dataReadyA;
23
+ private interruptPendingA;
24
+ private enabledA;
25
+ private asciiDataB;
26
+ private dataReadyB;
27
+ private interruptPendingB;
28
+ private enabledB;
29
+ private shiftPressed;
30
+ private ctrlPressed;
31
+ private altPressed;
32
+ private menuPressed;
33
+ private stateCA1;
34
+ private stateCA2;
35
+ private stateCB1;
36
+ private stateCB2;
37
+ constructor(priority?: number);
38
+ reset(): void;
39
+ readPortA(ddrA: number, orA: number): number;
40
+ readPortB(ddrB: number, orB: number): number;
41
+ updateControlLines(ca1: boolean, ca2: boolean, cb1: boolean, cb2: boolean): void;
42
+ hasCA1Interrupt(): boolean;
43
+ hasCB1Interrupt(): boolean;
44
+ clearInterrupts(ca1: boolean, ca2: boolean, cb1: boolean, cb2: boolean): void;
45
+ /**
46
+ * Map a USB HID keycode to ASCII with modifier keys applied
47
+ */
48
+ private mapKeyWithModifiers;
49
+ /**
50
+ * Update the keyboard state based on a USB HID key press or release
51
+ * @param usbHidKeycode USB HID keycode
52
+ * @param pressed true for key press, false for key release
53
+ */
54
+ updateKey(usbHidKeycode: number, pressed: boolean): void;
55
+ /**
56
+ * Check if Port A has data ready
57
+ */
58
+ hasDataReadyA(): boolean;
59
+ /**
60
+ * Check if Port B has data ready
61
+ */
62
+ hasDataReadyB(): boolean;
63
+ }
@@ -0,0 +1,44 @@
1
+ import { GPIOAttachmentBase } from './GPIOAttachment';
2
+ /**
3
+ * GPIOKeyboardMatrixAttachment - Emulates a keyboard matrix connected to GPIO ports
4
+ *
5
+ * The keyboard matrix uses:
6
+ * - Port A (PA0-PA7): Rows (8 rows)
7
+ * - Port B (PB0-PB7): Columns (8 columns)
8
+ *
9
+ * Keys are active-low: when a key is pressed, the corresponding row/column intersection
10
+ * pulls the row line low when the column is selected (low).
11
+ */
12
+ export declare class GPIOKeyboardMatrixAttachment extends GPIOAttachmentBase {
13
+ private static readonly KEYBOARD_LAYOUT;
14
+ private keyboardMatrix;
15
+ private selectedColumns;
16
+ constructor(priority?: number);
17
+ reset(): void;
18
+ readPortA(ddr: number, or: number): number;
19
+ readPortB(ddr: number, or: number): number;
20
+ writePortB(value: number, ddr: number): void;
21
+ /**
22
+ * Update a key state based on USB HID keycode
23
+ * @param usbHidKeycode - USB HID usage ID for the key
24
+ * @param pressed - true if key is pressed, false if released
25
+ */
26
+ updateKey(usbHidKeycode: number, pressed: boolean): void;
27
+ /**
28
+ * Update a specific matrix position directly
29
+ * @param row - Row index (0-7)
30
+ * @param col - Column index (0-7)
31
+ * @param pressed - true if key is pressed, false if released
32
+ */
33
+ updateMatrixPosition(row: number, col: number, pressed: boolean): void;
34
+ /**
35
+ * Get the current state of the keyboard matrix
36
+ * @returns Array of 8 bytes representing the matrix state
37
+ */
38
+ getMatrixState(): number[];
39
+ /**
40
+ * Get the currently selected columns
41
+ * @returns Byte representing selected columns
42
+ */
43
+ getSelectedColumns(): number;
44
+ }
@@ -0,0 +1,47 @@
1
+ import { GPIOAttachmentBase } from './GPIOAttachment';
2
+ /**
3
+ * GPIOKeypadAttachment - Emulates a 4×6 matrix keypad with a built-in hardware encoder
4
+ *
5
+ * The encoder converts a key press into a 5-bit code (PA0–PA4) that appears on the GPIO
6
+ * port. Bits 5–7 are never driven by the keypad and always read as 0 when data is present.
7
+ *
8
+ * Behaviour mirrors a typical 74C922-style encoder:
9
+ * - On key press → the 5-bit keypad code is latched and a CA1/CB1 interrupt is asserted
10
+ * - On port read → the latched code is returned on bits 0–4 (bits 5–7 = 0)
11
+ * - clearInterrupts → clears the interrupt and the data-ready latch
12
+ * - Key releases → ignored (encoder only reports on the falling edge of a keypress)
13
+ *
14
+ * The attachment may be wired to either Port A or Port B via the constructor parameter.
15
+ * CA1/CB1 is the DA (Data Available) interrupt line from the 74C922.
16
+ * CA2/CB2 is connected to the 74C922 OE (Output Enable) pin; data is only driven onto the
17
+ * bus when OE is asserted LOW by the 6522.
18
+ */
19
+ export declare class GPIOKeypadAttachment extends GPIOAttachmentBase {
20
+ private keypadValue;
21
+ private dataReady;
22
+ private interruptPending;
23
+ private readonly attachedToPortA;
24
+ private oeState;
25
+ constructor(attachToPortA?: boolean, priority?: number);
26
+ reset(): void;
27
+ updateControlLines(ca1: boolean, ca2: boolean, cb1: boolean, cb2: boolean): void;
28
+ readPortA(ddr: number, or: number): number;
29
+ readPortB(ddr: number, or: number): number;
30
+ hasCA1Interrupt(): boolean;
31
+ hasCB1Interrupt(): boolean;
32
+ clearInterrupts(ca1: boolean, ca2: boolean, cb1: boolean, cb2: boolean): void;
33
+ /**
34
+ * Notify the attachment of a USB HID key event.
35
+ * Key releases are ignored; only presses generate output on the GPIO port.
36
+ *
37
+ * @param usbHidKeycode - USB HID usage ID for the key
38
+ * @param pressed - true for key-down, false for key-up
39
+ */
40
+ updateKey(usbHidKeycode: number, pressed: boolean): void;
41
+ /**
42
+ * Returns the current latched keypad code (bits 0–4) or 0xFF if no data is ready.
43
+ */
44
+ getCurrentKey(): number;
45
+ /** Returns true when a key has been pressed and the latch has not yet been cleared. */
46
+ hasDataReady(): boolean;
47
+ }
@@ -0,0 +1,110 @@
1
+ import { GPIOAttachmentBase } from './GPIOAttachment';
2
+ /**
3
+ * HD44780 LCD Controller Emulation — GPIO Attachment
4
+ *
5
+ * Emulates a 16×2 (or configurable) character LCD with HD44780 controller
6
+ * connected via 8-bit parallel interface on a 65C22 VIA.
7
+ *
8
+ * Pin mapping (accent on VIA ports):
9
+ * Port B (D0–D7): 8-bit data bus
10
+ * Port A bit 5: RS (Register Select — 0 = command, 1 = data)
11
+ * Port A bit 6: RW (Read/Write — 0 = write, 1 = read)
12
+ * Port A bit 7: E (Enable — active-high strobe, latched on falling edge)
13
+ *
14
+ * Display modes:
15
+ * Standard character display with 5×8 pixel characters
16
+ * Supports CGRAM for up to 8 user-defined characters
17
+ *
18
+ * Output: pixel buffer (cols*(5+1)-1) × (rows*(8+1)-1) with values:
19
+ * -1 = no pixel (inter-character gap)
20
+ * 0 = pixel off
21
+ * 1 = pixel on
22
+ *
23
+ * Reference: vrEmuLcd by Troy Schrapel
24
+ * https://github.com/visrealm/vrEmuLcd
25
+ */
26
+ export declare const LCD_CMD_CLEAR = 1;
27
+ export declare const LCD_CMD_HOME = 2;
28
+ export declare const LCD_CMD_ENTRY_MODE = 4;
29
+ export declare const LCD_CMD_ENTRY_MODE_INCREMENT = 2;
30
+ export declare const LCD_CMD_ENTRY_MODE_SHIFT = 1;
31
+ export declare const LCD_CMD_DISPLAY = 8;
32
+ export declare const LCD_CMD_DISPLAY_ON = 4;
33
+ export declare const LCD_CMD_DISPLAY_CURSOR = 2;
34
+ export declare const LCD_CMD_DISPLAY_CURSOR_BLINK = 1;
35
+ export declare const LCD_CMD_SHIFT = 16;
36
+ export declare const LCD_CMD_SHIFT_DISPLAY = 8;
37
+ export declare const LCD_CMD_SHIFT_RIGHT = 4;
38
+ export declare const LCD_CMD_FUNCTION = 32;
39
+ export declare const LCD_CMD_FUNCTION_LCD_2LINE = 8;
40
+ export declare const LCD_CMD_SET_CGRAM_ADDR = 64;
41
+ export declare const LCD_CMD_SET_DRAM_ADDR = 128;
42
+ export declare class GPIOLCDAttachment extends GPIOAttachmentBase {
43
+ readonly cols: number;
44
+ readonly rows: number;
45
+ private entryModeFlags;
46
+ private displayFlags;
47
+ private scrollOffset;
48
+ /** 128-byte Display Data RAM */
49
+ private ddRam;
50
+ /** Current DDRAM pointer offset */
51
+ private ddPtr;
52
+ private dataWidthCols;
53
+ /** Character Generator RAM — 16 characters × 8 rows, stored column-major
54
+ * as 16 × CHAR_WIDTH_PX bytes (matching vrEmuLcd cgRam layout) */
55
+ private cgRam;
56
+ /** Current CGRAM pointer (null when not in CGRAM mode) */
57
+ private cgPtr;
58
+ /** Pixel buffer: each byte is -1 (gap), 0 (off) or 1 (on) */
59
+ buffer: Int8Array;
60
+ readonly pixelsWidth: number;
61
+ readonly pixelsHeight: number;
62
+ private blinkAccumulator;
63
+ private blinkState;
64
+ private static readonly BLINK_PERIOD_MS;
65
+ private lastPortA;
66
+ private lastE;
67
+ constructor(cols?: number, rows?: number, priority?: number);
68
+ reset(): void;
69
+ tick(cpuFrequency: number): void;
70
+ /**
71
+ * Port A carries the control signals.
72
+ * We detect E falling edge to latch the bus.
73
+ */
74
+ writePortA(value: number, ddr: number): void;
75
+ readPortA(ddr: number, or: number): number;
76
+ readPortB(ddr: number, or: number): number;
77
+ private latchBus;
78
+ private lastPortBValue;
79
+ writePortB(value: number, ddr: number): void;
80
+ sendCommand(command: number): void;
81
+ writeByte(data: number): void;
82
+ private readByte;
83
+ readAddress(): number;
84
+ private incrementDdPtr;
85
+ private decrementDdPtr;
86
+ private doShift;
87
+ /**
88
+ * Get the 5-column font data for a character.
89
+ * Characters 0–15 come from CGRAM; 16–255 from ROM.
90
+ */
91
+ private charBits;
92
+ private getDataOffset;
93
+ updatePixels(): void;
94
+ /** Get the raw DDRAM contents */
95
+ getDDRam(): Uint8Array;
96
+ /** Get the current DDRAM address pointer */
97
+ getDDPtr(): number;
98
+ /** Get the display flags */
99
+ getDisplayFlags(): number;
100
+ /** Get the entry mode flags */
101
+ getEntryModeFlags(): number;
102
+ /** Get scroll offset */
103
+ getScrollOffset(): number;
104
+ /** Get CGRAM pointer (null if not in CGRAM mode) */
105
+ getCGPtr(): number | null;
106
+ /** Read the text content of a specific display row */
107
+ getRowText(row: number): string;
108
+ /** Pixel state at a given coordinate: -1 (gap), 0 (off), 1 (on) */
109
+ pixelState(x: number, y: number): number;
110
+ }