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
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LCDAttachment = exports.LCD_CMD_SET_DRAM_ADDR = exports.LCD_CMD_SET_CGRAM_ADDR = exports.LCD_CMD_FUNCTION_LCD_2LINE = exports.LCD_CMD_FUNCTION = exports.LCD_CMD_SHIFT_RIGHT = exports.LCD_CMD_SHIFT_DISPLAY = exports.LCD_CMD_SHIFT = exports.LCD_CMD_DISPLAY_CURSOR_BLINK = exports.LCD_CMD_DISPLAY_CURSOR = exports.LCD_CMD_DISPLAY_ON = exports.LCD_CMD_DISPLAY = exports.LCD_CMD_ENTRY_MODE_SHIFT = exports.LCD_CMD_ENTRY_MODE_INCREMENT = exports.LCD_CMD_ENTRY_MODE = exports.LCD_CMD_HOME = exports.LCD_CMD_CLEAR = void 0;
|
|
4
|
+
const Attachment_1 = require("./Attachment");
|
|
5
|
+
/**
|
|
6
|
+
* HD44780 LCD Controller Emulation — GPIO Attachment
|
|
7
|
+
*
|
|
8
|
+
* Emulates a 16×2 (or configurable) character LCD with HD44780 controller
|
|
9
|
+
* connected via 8-bit parallel interface on a 65C22 VIA.
|
|
10
|
+
*
|
|
11
|
+
* Pin mapping (accent on VIA ports):
|
|
12
|
+
* Port B (D0–D7): 8-bit data bus
|
|
13
|
+
* Port A bit 5: RS (Register Select — 0 = command, 1 = data)
|
|
14
|
+
* Port A bit 6: RW (Read/Write — 0 = write, 1 = read)
|
|
15
|
+
* Port A bit 7: E (Enable — active-high strobe, latched on falling edge)
|
|
16
|
+
*
|
|
17
|
+
* Display modes:
|
|
18
|
+
* Standard character display with 5×8 pixel characters
|
|
19
|
+
* Supports CGRAM for up to 8 user-defined characters
|
|
20
|
+
*
|
|
21
|
+
* Output: pixel buffer (cols*(5+1)-1) × (rows*(8+1)-1) with values:
|
|
22
|
+
* -1 = no pixel (inter-character gap)
|
|
23
|
+
* 0 = pixel off
|
|
24
|
+
* 1 = pixel on
|
|
25
|
+
*
|
|
26
|
+
* Reference: vrEmuLcd by Troy Schrapel
|
|
27
|
+
* https://github.com/visrealm/vrEmuLcd
|
|
28
|
+
*/
|
|
29
|
+
// ── HD44780 Command bit masks ──────────────────────────────────────
|
|
30
|
+
exports.LCD_CMD_CLEAR = 0x01;
|
|
31
|
+
exports.LCD_CMD_HOME = 0x02;
|
|
32
|
+
exports.LCD_CMD_ENTRY_MODE = 0x04;
|
|
33
|
+
exports.LCD_CMD_ENTRY_MODE_INCREMENT = 0x02;
|
|
34
|
+
exports.LCD_CMD_ENTRY_MODE_SHIFT = 0x01;
|
|
35
|
+
exports.LCD_CMD_DISPLAY = 0x08;
|
|
36
|
+
exports.LCD_CMD_DISPLAY_ON = 0x04;
|
|
37
|
+
exports.LCD_CMD_DISPLAY_CURSOR = 0x02;
|
|
38
|
+
exports.LCD_CMD_DISPLAY_CURSOR_BLINK = 0x01;
|
|
39
|
+
exports.LCD_CMD_SHIFT = 0x10;
|
|
40
|
+
exports.LCD_CMD_SHIFT_DISPLAY = 0x08;
|
|
41
|
+
exports.LCD_CMD_SHIFT_RIGHT = 0x04;
|
|
42
|
+
exports.LCD_CMD_FUNCTION = 0x20;
|
|
43
|
+
exports.LCD_CMD_FUNCTION_LCD_2LINE = 0x08;
|
|
44
|
+
exports.LCD_CMD_SET_CGRAM_ADDR = 0x40;
|
|
45
|
+
exports.LCD_CMD_SET_DRAM_ADDR = 0x80;
|
|
46
|
+
// ── Constants ──────────────────────────────────────────────────────
|
|
47
|
+
const CHAR_WIDTH_PX = 5;
|
|
48
|
+
const CHAR_HEIGHT_PX = 8;
|
|
49
|
+
const DDRAM_SIZE = 128;
|
|
50
|
+
const CGRAM_STORAGE_CHARS = 16;
|
|
51
|
+
const ROM_FONT_CHARS = 256 - CGRAM_STORAGE_CHARS;
|
|
52
|
+
const DEFAULT_CGRAM_BYTE = 0xAA;
|
|
53
|
+
const DATA_WIDTH_CHARS_1ROW = 80;
|
|
54
|
+
const DATA_WIDTH_CHARS_2ROW = 40;
|
|
55
|
+
const CURSOR_MASK = exports.LCD_CMD_DISPLAY_CURSOR_BLINK | exports.LCD_CMD_DISPLAY_CURSOR;
|
|
56
|
+
// DDRAM row start addresses for multi-row displays
|
|
57
|
+
const ROW_OFFSETS = [0x00, 0x40, 0x14, 0x54];
|
|
58
|
+
// ── VIA Port A pin positions ───────────────────────────────────────
|
|
59
|
+
const PIN_RS = 0x20; // bit 5
|
|
60
|
+
const PIN_RW = 0x40; // bit 6
|
|
61
|
+
const PIN_E = 0x80; // bit 7
|
|
62
|
+
// ── HD44780 ROM A00 (Japanese) font ────────────────────────────────
|
|
63
|
+
// 240 characters (indices 16–255), each stored as 5 bytes — one per
|
|
64
|
+
// column, MSB at top. First 16 slots reserved for CGRAM.
|
|
65
|
+
const FONT_A00 = [
|
|
66
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 16
|
|
67
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 17
|
|
68
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 18
|
|
69
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 19
|
|
70
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 20
|
|
71
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 21
|
|
72
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 22
|
|
73
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 23
|
|
74
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 24
|
|
75
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 25
|
|
76
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 26
|
|
77
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 27
|
|
78
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 28
|
|
79
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 29
|
|
80
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 30
|
|
81
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 31
|
|
82
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 32 - (space)
|
|
83
|
+
[0x00, 0x00, 0xf2, 0x00, 0x00], // 33 - !
|
|
84
|
+
[0x00, 0xe0, 0x00, 0xe0, 0x00], // 34 - "
|
|
85
|
+
[0x28, 0xfe, 0x28, 0xfe, 0x28], // 35 - #
|
|
86
|
+
[0x24, 0x54, 0xfe, 0x54, 0x48], // 36 - $
|
|
87
|
+
[0xc4, 0xc8, 0x10, 0x26, 0x46], // 37 - %
|
|
88
|
+
[0x6c, 0x92, 0xaa, 0x44, 0x0a], // 38 - &
|
|
89
|
+
[0x00, 0xa0, 0xc0, 0x00, 0x00], // 39 - '
|
|
90
|
+
[0x00, 0x38, 0x44, 0x82, 0x00], // 40 - (
|
|
91
|
+
[0x00, 0x82, 0x44, 0x38, 0x00], // 41 - )
|
|
92
|
+
[0x28, 0x10, 0x7c, 0x10, 0x28], // 42 - *
|
|
93
|
+
[0x10, 0x10, 0x7c, 0x10, 0x10], // 43 - +
|
|
94
|
+
[0x00, 0x0a, 0x0c, 0x00, 0x00], // 44 - ,
|
|
95
|
+
[0x10, 0x10, 0x10, 0x10, 0x10], // 45 - -
|
|
96
|
+
[0x00, 0x06, 0x06, 0x00, 0x00], // 46 - .
|
|
97
|
+
[0x04, 0x08, 0x10, 0x20, 0x40], // 47 - /
|
|
98
|
+
[0x7c, 0x8a, 0x92, 0xa2, 0x7c], // 48 - 0
|
|
99
|
+
[0x00, 0x42, 0xfe, 0x02, 0x00], // 49 - 1
|
|
100
|
+
[0x42, 0x86, 0x8a, 0x92, 0x62], // 50 - 2
|
|
101
|
+
[0x84, 0x82, 0xa2, 0xd2, 0x8c], // 51 - 3
|
|
102
|
+
[0x18, 0x28, 0x48, 0xfe, 0x08], // 52 - 4
|
|
103
|
+
[0xe4, 0xa2, 0xa2, 0xa2, 0x9c], // 53 - 5
|
|
104
|
+
[0x3c, 0x52, 0x92, 0x92, 0x0c], // 54 - 6
|
|
105
|
+
[0x80, 0x8e, 0x90, 0xa0, 0xc0], // 55 - 7
|
|
106
|
+
[0x6c, 0x92, 0x92, 0x92, 0x6c], // 56 - 8
|
|
107
|
+
[0x60, 0x92, 0x92, 0x94, 0x78], // 57 - 9
|
|
108
|
+
[0x00, 0x6c, 0x6c, 0x00, 0x00], // 58 - :
|
|
109
|
+
[0x00, 0x6a, 0x6c, 0x00, 0x00], // 59 - ;
|
|
110
|
+
[0x10, 0x28, 0x44, 0x82, 0x00], // 60 - <
|
|
111
|
+
[0x28, 0x28, 0x28, 0x28, 0x28], // 61 - =
|
|
112
|
+
[0x00, 0x82, 0x44, 0x28, 0x10], // 62 - >
|
|
113
|
+
[0x40, 0x80, 0x8a, 0x90, 0x60], // 63 - ?
|
|
114
|
+
[0x4c, 0x92, 0x9e, 0x82, 0x7c], // 64 - @
|
|
115
|
+
[0x7e, 0x90, 0x90, 0x90, 0x7e], // 65 - A
|
|
116
|
+
[0xfe, 0x92, 0x92, 0x92, 0x6c], // 66 - B
|
|
117
|
+
[0x7c, 0x82, 0x82, 0x82, 0x44], // 67 - C
|
|
118
|
+
[0xfe, 0x82, 0x82, 0x44, 0x38], // 68 - D
|
|
119
|
+
[0xfe, 0x92, 0x92, 0x92, 0x82], // 69 - E
|
|
120
|
+
[0xfe, 0x90, 0x90, 0x90, 0x80], // 70 - F
|
|
121
|
+
[0x7c, 0x82, 0x92, 0x92, 0x5e], // 71 - G
|
|
122
|
+
[0xfe, 0x10, 0x10, 0x10, 0xfe], // 72 - H
|
|
123
|
+
[0x00, 0x82, 0xfe, 0x82, 0x00], // 73 - I
|
|
124
|
+
[0x04, 0x82, 0x82, 0xfc, 0x00], // 74 - J
|
|
125
|
+
[0xfe, 0x10, 0x28, 0x44, 0x82], // 75 - K
|
|
126
|
+
[0xfe, 0x02, 0x02, 0x02, 0x02], // 76 - L
|
|
127
|
+
[0xfe, 0x40, 0x30, 0x40, 0xfe], // 77 - M
|
|
128
|
+
[0xfe, 0x20, 0x10, 0x08, 0xfe], // 78 - N
|
|
129
|
+
[0x7c, 0x82, 0x82, 0x82, 0x7c], // 79 - O
|
|
130
|
+
[0xfe, 0x90, 0x90, 0x90, 0x60], // 80 - P
|
|
131
|
+
[0x7c, 0x82, 0x8a, 0x84, 0x7a], // 81 - Q
|
|
132
|
+
[0xfe, 0x90, 0x98, 0x94, 0x62], // 82 - R
|
|
133
|
+
[0x62, 0x92, 0x92, 0x92, 0x8c], // 83 - S
|
|
134
|
+
[0x80, 0x80, 0xfe, 0x80, 0x80], // 84 - T
|
|
135
|
+
[0xfc, 0x02, 0x02, 0x02, 0xfc], // 85 - U
|
|
136
|
+
[0xf8, 0x04, 0x02, 0x04, 0xf8], // 86 - V
|
|
137
|
+
[0xfc, 0x02, 0x1c, 0x02, 0xfc], // 87 - W
|
|
138
|
+
[0xc6, 0x28, 0x10, 0x28, 0xc6], // 88 - X
|
|
139
|
+
[0xe0, 0x10, 0x0e, 0x10, 0xe0], // 89 - Y
|
|
140
|
+
[0x86, 0x8a, 0x92, 0xa2, 0xc2], // 90 - Z
|
|
141
|
+
[0x00, 0xfe, 0x82, 0x82, 0x00], // 91 - [
|
|
142
|
+
[0xa8, 0x68, 0x3e, 0x68, 0xa8], // 92 - yen
|
|
143
|
+
[0x00, 0x82, 0x82, 0xfe, 0x00], // 93 - ]
|
|
144
|
+
[0x20, 0x40, 0x80, 0x40, 0x20], // 94 - ^
|
|
145
|
+
[0x02, 0x02, 0x02, 0x02, 0x02], // 95 - _
|
|
146
|
+
[0x00, 0x80, 0x40, 0x20, 0x00], // 96 - `
|
|
147
|
+
[0x04, 0x2a, 0x2a, 0x2a, 0x1e], // 97 - a
|
|
148
|
+
[0xfe, 0x12, 0x22, 0x22, 0x1c], // 98 - b
|
|
149
|
+
[0x1c, 0x22, 0x22, 0x22, 0x04], // 99 - c
|
|
150
|
+
[0x1c, 0x22, 0x22, 0x12, 0xfe], // 100 - d
|
|
151
|
+
[0x1c, 0x2a, 0x2a, 0x2a, 0x18], // 101 - e
|
|
152
|
+
[0x10, 0x7e, 0x90, 0x80, 0x40], // 102 - f
|
|
153
|
+
[0x30, 0x4a, 0x4a, 0x4a, 0x7c], // 103 - g
|
|
154
|
+
[0xfe, 0x10, 0x20, 0x20, 0x1e], // 104 - h
|
|
155
|
+
[0x00, 0x22, 0xbe, 0x02, 0x00], // 105 - i
|
|
156
|
+
[0x04, 0x02, 0x22, 0xbc, 0x00], // 106 - j
|
|
157
|
+
[0xfe, 0x08, 0x14, 0x22, 0x00], // 107 - k
|
|
158
|
+
[0x02, 0x82, 0xfe, 0x02, 0x02], // 108 - l
|
|
159
|
+
[0x3e, 0x20, 0x18, 0x20, 0x1e], // 109 - m
|
|
160
|
+
[0x3e, 0x10, 0x20, 0x20, 0x1e], // 110 - n
|
|
161
|
+
[0x1c, 0x22, 0x22, 0x22, 0x1c], // 111 - o
|
|
162
|
+
[0x3e, 0x28, 0x28, 0x28, 0x10], // 112 - p
|
|
163
|
+
[0x10, 0x28, 0x28, 0x18, 0x3e], // 113 - q
|
|
164
|
+
[0x3e, 0x10, 0x20, 0x20, 0x10], // 114 - r
|
|
165
|
+
[0x12, 0x2a, 0x2a, 0x2a, 0x04], // 115 - s
|
|
166
|
+
[0x20, 0xfc, 0x22, 0x02, 0x04], // 116 - t
|
|
167
|
+
[0x3c, 0x02, 0x02, 0x04, 0x3e], // 117 - u
|
|
168
|
+
[0x38, 0x04, 0x02, 0x04, 0x38], // 118 - v
|
|
169
|
+
[0x3c, 0x02, 0x0c, 0x02, 0x3c], // 119 - w
|
|
170
|
+
[0x22, 0x14, 0x08, 0x14, 0x22], // 120 - x
|
|
171
|
+
[0x30, 0x0a, 0x0a, 0x0a, 0x3c], // 121 - y
|
|
172
|
+
[0x22, 0x26, 0x2a, 0x32, 0x22], // 122 - z
|
|
173
|
+
[0x00, 0x10, 0x6c, 0x82, 0x00], // 123 - {
|
|
174
|
+
[0x00, 0x00, 0xfe, 0x00, 0x00], // 124 - |
|
|
175
|
+
[0x00, 0x82, 0x6c, 0x10, 0x00], // 125 - }
|
|
176
|
+
[0x10, 0x10, 0x54, 0x38, 0x10], // 126 - ->
|
|
177
|
+
[0x10, 0x38, 0x54, 0x10, 0x10], // 127 - <-
|
|
178
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 128
|
|
179
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 129
|
|
180
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 130
|
|
181
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 131
|
|
182
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 132
|
|
183
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 133
|
|
184
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 134
|
|
185
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 135
|
|
186
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 136
|
|
187
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 137
|
|
188
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 138
|
|
189
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 139
|
|
190
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 140
|
|
191
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 141
|
|
192
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 142
|
|
193
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 143
|
|
194
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 144
|
|
195
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 145
|
|
196
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 146
|
|
197
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 147
|
|
198
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 148
|
|
199
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 149
|
|
200
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 150
|
|
201
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 151
|
|
202
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 152
|
|
203
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 153
|
|
204
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 154
|
|
205
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 155
|
|
206
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 156
|
|
207
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 157
|
|
208
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 158
|
|
209
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 159
|
|
210
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 160
|
|
211
|
+
[0x0e, 0x0a, 0x0e, 0x00, 0x00], // 161
|
|
212
|
+
[0x00, 0x00, 0xf0, 0x80, 0x80], // 162
|
|
213
|
+
[0x02, 0x02, 0x1e, 0x00, 0x00], // 163
|
|
214
|
+
[0x08, 0x04, 0x02, 0x00, 0x00], // 164
|
|
215
|
+
[0x00, 0x18, 0x18, 0x00, 0x00], // 165
|
|
216
|
+
[0x50, 0x50, 0x52, 0x54, 0x78], // 166
|
|
217
|
+
[0x20, 0x22, 0x2c, 0x28, 0x30], // 167
|
|
218
|
+
[0x04, 0x08, 0x1e, 0x20, 0x00], // 168
|
|
219
|
+
[0x18, 0x12, 0x32, 0x12, 0x1c], // 169
|
|
220
|
+
[0x12, 0x12, 0x1e, 0x12, 0x12], // 170
|
|
221
|
+
[0x12, 0x14, 0x18, 0x3e, 0x10], // 171
|
|
222
|
+
[0x10, 0x3e, 0x10, 0x14, 0x18], // 172
|
|
223
|
+
[0x02, 0x12, 0x12, 0x1e, 0x02], // 173
|
|
224
|
+
[0x2a, 0x2a, 0x2a, 0x3e, 0x00], // 174
|
|
225
|
+
[0x18, 0x00, 0x1a, 0x02, 0x1c], // 175
|
|
226
|
+
[0x10, 0x10, 0x10, 0x10, 0x10], // 176
|
|
227
|
+
[0x80, 0x82, 0xbc, 0x90, 0xe0], // 177
|
|
228
|
+
[0x08, 0x10, 0x3e, 0x40, 0x80], // 178
|
|
229
|
+
[0x70, 0x40, 0xc2, 0x44, 0x78], // 179
|
|
230
|
+
[0x42, 0x42, 0x7e, 0x42, 0x42], // 180
|
|
231
|
+
[0x44, 0x48, 0x50, 0xfe, 0x40], // 181
|
|
232
|
+
[0x42, 0xfc, 0x40, 0x42, 0x7c], // 182
|
|
233
|
+
[0x50, 0x50, 0xfe, 0x50, 0x50], // 183
|
|
234
|
+
[0x10, 0x62, 0x42, 0x44, 0x78], // 184
|
|
235
|
+
[0x20, 0xc0, 0x42, 0x7c, 0x40], // 185
|
|
236
|
+
[0x42, 0x42, 0x42, 0x42, 0x7e], // 186
|
|
237
|
+
[0x40, 0xf2, 0x44, 0xf8, 0x40], // 187
|
|
238
|
+
[0x52, 0x52, 0x02, 0x04, 0x38], // 188
|
|
239
|
+
[0x42, 0x44, 0x48, 0x54, 0x62], // 189
|
|
240
|
+
[0x40, 0xfc, 0x42, 0x52, 0x62], // 190
|
|
241
|
+
[0x60, 0x12, 0x02, 0x04, 0x78], // 191
|
|
242
|
+
[0x10, 0x62, 0x52, 0x4c, 0x78], // 192
|
|
243
|
+
[0x50, 0x52, 0x7c, 0x90, 0x10], // 193
|
|
244
|
+
[0x70, 0x00, 0x72, 0x04, 0x78], // 194
|
|
245
|
+
[0x20, 0xa2, 0xbc, 0xa0, 0x20], // 195
|
|
246
|
+
[0x00, 0xfe, 0x10, 0x08, 0x00], // 196
|
|
247
|
+
[0x22, 0x24, 0xf8, 0x20, 0x20], // 197
|
|
248
|
+
[0x02, 0x42, 0x42, 0x42, 0x02], // 198
|
|
249
|
+
[0x42, 0x54, 0x48, 0x54, 0x60], // 199
|
|
250
|
+
[0x44, 0x48, 0xde, 0x68, 0x44], // 200
|
|
251
|
+
[0x00, 0x02, 0x04, 0xf8, 0x00], // 201
|
|
252
|
+
[0x1e, 0x00, 0x40, 0x20, 0x1e], // 202
|
|
253
|
+
[0xfc, 0x22, 0x22, 0x22, 0x22], // 203
|
|
254
|
+
[0x40, 0x42, 0x42, 0x44, 0x78], // 204
|
|
255
|
+
[0x20, 0x40, 0x20, 0x10, 0x0c], // 205
|
|
256
|
+
[0x4c, 0x40, 0xfe, 0x40, 0x4c], // 206
|
|
257
|
+
[0x40, 0x48, 0x44, 0x4a, 0x70], // 207
|
|
258
|
+
[0x00, 0x54, 0x54, 0x54, 0x02], // 208
|
|
259
|
+
[0x1c, 0x24, 0x44, 0x04, 0x0e], // 209
|
|
260
|
+
[0x02, 0x14, 0x08, 0x14, 0x60], // 210
|
|
261
|
+
[0x50, 0x7c, 0x52, 0x52, 0x52], // 211
|
|
262
|
+
[0x20, 0xfe, 0x20, 0x28, 0x30], // 212
|
|
263
|
+
[0x02, 0x42, 0x42, 0x7e, 0x02], // 213
|
|
264
|
+
[0x52, 0x52, 0x52, 0x52, 0x7e], // 214
|
|
265
|
+
[0x20, 0xa0, 0xa2, 0xa4, 0x38], // 215
|
|
266
|
+
[0xf0, 0x02, 0x04, 0xf8, 0x00], // 216
|
|
267
|
+
[0x3e, 0x00, 0x7e, 0x02, 0x0c], // 217
|
|
268
|
+
[0x7e, 0x02, 0x04, 0x08, 0x10], // 218
|
|
269
|
+
[0x7e, 0x42, 0x42, 0x42, 0x7e], // 219
|
|
270
|
+
[0x70, 0x40, 0x42, 0x44, 0x78], // 220
|
|
271
|
+
[0x42, 0x42, 0x02, 0x04, 0x18], // 221
|
|
272
|
+
[0x40, 0x20, 0x80, 0x40, 0x00], // 222
|
|
273
|
+
[0xe0, 0xa0, 0xe0, 0x00, 0x00], // 223
|
|
274
|
+
[0x1c, 0x22, 0x12, 0x0c, 0x32], // 224
|
|
275
|
+
[0x04, 0xaa, 0x2a, 0xaa, 0x1e], // 225
|
|
276
|
+
[0x1f, 0x2a, 0x2a, 0x2a, 0x14], // 226
|
|
277
|
+
[0x14, 0x2a, 0x2a, 0x22, 0x04], // 227
|
|
278
|
+
[0x3f, 0x02, 0x02, 0x04, 0x3e], // 228
|
|
279
|
+
[0x1c, 0x22, 0x32, 0x2a, 0x24], // 229
|
|
280
|
+
[0x0f, 0x12, 0x22, 0x22, 0x1c], // 230
|
|
281
|
+
[0x1c, 0x22, 0x22, 0x22, 0x3f], // 231
|
|
282
|
+
[0x04, 0x02, 0x3c, 0x20, 0x20], // 232
|
|
283
|
+
[0x20, 0x20, 0x00, 0x70, 0x00], // 233
|
|
284
|
+
[0x00, 0x00, 0x20, 0xbf, 0x00], // 234
|
|
285
|
+
[0x50, 0x20, 0x50, 0x00, 0x00], // 235
|
|
286
|
+
[0x18, 0x24, 0x7e, 0x24, 0x08], // 236
|
|
287
|
+
[0x28, 0xfe, 0x2a, 0x02, 0x02], // 237
|
|
288
|
+
[0x3e, 0x90, 0xa0, 0xa0, 0x1e], // 238
|
|
289
|
+
[0x1c, 0xa2, 0x22, 0xa2, 0x1c], // 239
|
|
290
|
+
[0x3f, 0x12, 0x22, 0x22, 0x1c], // 240
|
|
291
|
+
[0x1c, 0x22, 0x22, 0x12, 0x3f], // 241
|
|
292
|
+
[0x3c, 0x52, 0x52, 0x52, 0x3c], // 242
|
|
293
|
+
[0x0c, 0x14, 0x08, 0x14, 0x18], // 243
|
|
294
|
+
[0x1a, 0x26, 0x20, 0x26, 0x1a], // 244
|
|
295
|
+
[0x3c, 0x82, 0x02, 0x84, 0x3e], // 245
|
|
296
|
+
[0xc6, 0xaa, 0x92, 0x82, 0x82], // 246
|
|
297
|
+
[0x22, 0x3c, 0x20, 0x3e, 0x22], // 247
|
|
298
|
+
[0xa2, 0x94, 0x88, 0x94, 0xa2], // 248
|
|
299
|
+
[0x3c, 0x02, 0x02, 0x02, 0x3f], // 249
|
|
300
|
+
[0x28, 0x28, 0x3e, 0x28, 0x48], // 250
|
|
301
|
+
[0x22, 0x3c, 0x28, 0x28, 0x2e], // 251
|
|
302
|
+
[0x3e, 0x28, 0x38, 0x28, 0x3e], // 252
|
|
303
|
+
[0x08, 0x08, 0x2a, 0x08, 0x08], // 253
|
|
304
|
+
[0x00, 0x00, 0x00, 0x00, 0x00], // 254
|
|
305
|
+
[0xff, 0xff, 0xff, 0xff, 0xff], // 255
|
|
306
|
+
];
|
|
307
|
+
class LCDAttachment extends Attachment_1.AttachmentBase {
|
|
308
|
+
constructor(cols = 16, rows = 2, priority = 0) {
|
|
309
|
+
super(priority, false, false, false, false);
|
|
310
|
+
// ── HD44780 internal state ─────────────────────────────────────
|
|
311
|
+
this.entryModeFlags = exports.LCD_CMD_ENTRY_MODE_INCREMENT;
|
|
312
|
+
this.displayFlags = 0x00;
|
|
313
|
+
this.scrollOffset = 0;
|
|
314
|
+
/** Current DDRAM pointer offset */
|
|
315
|
+
this.ddPtr = 0;
|
|
316
|
+
/** Current CGRAM pointer (null when not in CGRAM mode) */
|
|
317
|
+
this.cgPtr = null;
|
|
318
|
+
// ── Cursor blink timing ────────────────────────────────────────
|
|
319
|
+
this.blinkAccumulator = 0;
|
|
320
|
+
this.blinkState = false;
|
|
321
|
+
// ── VIA bus latch state ────────────────────────────────────────
|
|
322
|
+
this.lastPortA = 0;
|
|
323
|
+
this.lastE = false;
|
|
324
|
+
this.lastPortBValue = 0;
|
|
325
|
+
this.cols = cols;
|
|
326
|
+
this.rows = rows;
|
|
327
|
+
this.ddRam = new Uint8Array(DDRAM_SIZE);
|
|
328
|
+
this.cgRam = new Uint8Array(CGRAM_STORAGE_CHARS * CHAR_WIDTH_PX);
|
|
329
|
+
this.dataWidthCols = rows <= 1 ? DATA_WIDTH_CHARS_1ROW : DATA_WIDTH_CHARS_2ROW;
|
|
330
|
+
this.pixelsWidth = cols * (CHAR_WIDTH_PX + 1) - 1;
|
|
331
|
+
this.pixelsHeight = rows * (CHAR_HEIGHT_PX + 1) - 1;
|
|
332
|
+
this.buffer = new Int8Array(this.pixelsWidth * this.pixelsHeight);
|
|
333
|
+
this.reset();
|
|
334
|
+
}
|
|
335
|
+
// ── Attachment interface ───────────────────────────────────
|
|
336
|
+
reset() {
|
|
337
|
+
super.reset();
|
|
338
|
+
this.entryModeFlags = exports.LCD_CMD_ENTRY_MODE_INCREMENT;
|
|
339
|
+
this.displayFlags = 0x00;
|
|
340
|
+
this.scrollOffset = 0;
|
|
341
|
+
this.ddRam.fill(0x20); // space
|
|
342
|
+
this.ddPtr = 0;
|
|
343
|
+
this.cgRam.fill(DEFAULT_CGRAM_BYTE);
|
|
344
|
+
this.cgPtr = null;
|
|
345
|
+
this.blinkAccumulator = 0;
|
|
346
|
+
this.blinkState = false;
|
|
347
|
+
this.lastPortA = 0;
|
|
348
|
+
this.lastE = false;
|
|
349
|
+
this.buffer.fill(-1);
|
|
350
|
+
this.updatePixels();
|
|
351
|
+
}
|
|
352
|
+
tick(cpuFrequency) {
|
|
353
|
+
// Advance cursor blink timer
|
|
354
|
+
const msPerTick = (128 / cpuFrequency) * 1000;
|
|
355
|
+
this.blinkAccumulator += msPerTick;
|
|
356
|
+
if (this.blinkAccumulator >= LCDAttachment.BLINK_PERIOD_MS) {
|
|
357
|
+
this.blinkAccumulator -= LCDAttachment.BLINK_PERIOD_MS;
|
|
358
|
+
this.blinkState = !this.blinkState;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Port A carries the control signals.
|
|
363
|
+
* We detect E falling edge to latch the bus.
|
|
364
|
+
*/
|
|
365
|
+
writePortA(value, ddr) {
|
|
366
|
+
const maskedValue = value & ddr;
|
|
367
|
+
const currentE = !!(maskedValue & PIN_E);
|
|
368
|
+
const prevE = this.lastE;
|
|
369
|
+
// Latch on falling edge of E — use the PREVIOUS lastPortA so that
|
|
370
|
+
// RS/RW reflect the state while E was still HIGH (HD44780 setup-time
|
|
371
|
+
// requirement). If the CPU drops RS and E in the same VIA write,
|
|
372
|
+
// this preserves the RS value that was active during the E=1 phase.
|
|
373
|
+
if (prevE && !currentE) {
|
|
374
|
+
this.latchBus();
|
|
375
|
+
}
|
|
376
|
+
this.lastPortA = maskedValue;
|
|
377
|
+
this.lastE = currentE;
|
|
378
|
+
}
|
|
379
|
+
readPortA(ddr, or) {
|
|
380
|
+
return 0xFF;
|
|
381
|
+
}
|
|
382
|
+
readPortB(ddr, or) {
|
|
383
|
+
// If R/W is high (read mode), provide data on Port B
|
|
384
|
+
const portA = this.lastPortA;
|
|
385
|
+
if (portA & PIN_RW) {
|
|
386
|
+
if (portA & PIN_RS) {
|
|
387
|
+
// RS=1, RW=1 → Read data
|
|
388
|
+
return this.readByte();
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
// RS=0, RW=1 → Read address / busy flag
|
|
392
|
+
return this.readAddress();
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return 0xFF;
|
|
396
|
+
}
|
|
397
|
+
// ── Bus latch (E falling edge) ────────────────────────────────
|
|
398
|
+
latchBus() {
|
|
399
|
+
const portA = this.lastPortA;
|
|
400
|
+
const rw = !!(portA & PIN_RW);
|
|
401
|
+
const rs = !!(portA & PIN_RS);
|
|
402
|
+
if (rw) {
|
|
403
|
+
// Read operations are handled via readPortB
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
// Write operation — capture data from Port B output register.
|
|
407
|
+
// Since we can't directly read the OR, the 6502 software must have
|
|
408
|
+
// written data to Port B *before* toggling E. We store it in writePortB
|
|
409
|
+
// — but the VIA card calls writePortB with the actual value. By the time
|
|
410
|
+
// E falls the data on the bus is the Port B output register value.
|
|
411
|
+
// We need the data value — it's available from the last writePortB call.
|
|
412
|
+
// However, writePortB doesn't store anything here. The VIA resolves
|
|
413
|
+
// the actual output from OR & DDR and passes it to us.
|
|
414
|
+
//
|
|
415
|
+
// For writes, the data on Port B is the value written by the CPU.
|
|
416
|
+
// The VIA will have called writePortB with the data already.
|
|
417
|
+
// We need to capture it — store it via writePortB override.
|
|
418
|
+
// Actually — we need to capture the Port B value. Let's store it.
|
|
419
|
+
const data = this.lastPortBValue;
|
|
420
|
+
if (rs) {
|
|
421
|
+
this.writeByte(data);
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
this.sendCommand(data);
|
|
425
|
+
}
|
|
426
|
+
this.updatePixels();
|
|
427
|
+
}
|
|
428
|
+
writePortB(value, ddr) {
|
|
429
|
+
this.lastPortBValue = value & ddr;
|
|
430
|
+
}
|
|
431
|
+
// ── HD44780 Command Processing ────────────────────────────────
|
|
432
|
+
sendCommand(command) {
|
|
433
|
+
if (command & exports.LCD_CMD_SET_DRAM_ADDR) {
|
|
434
|
+
// Set DDRAM address — remaining 7 bits
|
|
435
|
+
this.ddPtr = command & 0x7F;
|
|
436
|
+
this.cgPtr = null;
|
|
437
|
+
}
|
|
438
|
+
else if (command & exports.LCD_CMD_SET_CGRAM_ADDR) {
|
|
439
|
+
// Set CGRAM address — remaining 6 bits
|
|
440
|
+
this.cgPtr = command & 0x3F;
|
|
441
|
+
}
|
|
442
|
+
else if (command & exports.LCD_CMD_FUNCTION) {
|
|
443
|
+
// Function set — we just acknowledge (8-bit mode, 2-line assumed)
|
|
444
|
+
}
|
|
445
|
+
else if (command & exports.LCD_CMD_SHIFT) {
|
|
446
|
+
if (command & exports.LCD_CMD_SHIFT_DISPLAY) {
|
|
447
|
+
// Shift entire display
|
|
448
|
+
if (command & exports.LCD_CMD_SHIFT_RIGHT) {
|
|
449
|
+
--this.scrollOffset;
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
++this.scrollOffset;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
// Shift cursor
|
|
457
|
+
if (command & exports.LCD_CMD_SHIFT_RIGHT) {
|
|
458
|
+
this.incrementDdPtr();
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
this.decrementDdPtr();
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
else if (command & exports.LCD_CMD_DISPLAY) {
|
|
466
|
+
this.displayFlags = command;
|
|
467
|
+
}
|
|
468
|
+
else if (command & exports.LCD_CMD_ENTRY_MODE) {
|
|
469
|
+
this.entryModeFlags = command;
|
|
470
|
+
}
|
|
471
|
+
else if (command & exports.LCD_CMD_HOME) {
|
|
472
|
+
this.ddPtr = 0;
|
|
473
|
+
this.scrollOffset = 0;
|
|
474
|
+
}
|
|
475
|
+
else if (command === exports.LCD_CMD_CLEAR) {
|
|
476
|
+
this.ddRam.fill(0x20);
|
|
477
|
+
this.ddPtr = 0;
|
|
478
|
+
this.scrollOffset = 0;
|
|
479
|
+
this.entryModeFlags = exports.LCD_CMD_ENTRY_MODE_INCREMENT;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// ── Data Write / Read ─────────────────────────────────────────
|
|
483
|
+
writeByte(data) {
|
|
484
|
+
if (this.cgPtr !== null) {
|
|
485
|
+
// Write to CGRAM
|
|
486
|
+
const row = this.cgPtr % CHAR_HEIGHT_PX;
|
|
487
|
+
const charBase = this.cgPtr - row;
|
|
488
|
+
for (let i = 0; i < CHAR_WIDTH_PX; i++) {
|
|
489
|
+
const bit = data & ((0x01 << (CHAR_WIDTH_PX - 1)) >> i);
|
|
490
|
+
const addr = charBase * CHAR_WIDTH_PX / CHAR_HEIGHT_PX * CHAR_HEIGHT_PX;
|
|
491
|
+
// CGRAM is stored column-major like vrEmuLcd: cgRam[char][col]
|
|
492
|
+
// Each column byte has rows packed as bits (MSB = row 0)
|
|
493
|
+
const idx = Math.floor(this.cgPtr / CHAR_HEIGHT_PX) * CHAR_WIDTH_PX + i;
|
|
494
|
+
if (idx < this.cgRam.length) {
|
|
495
|
+
if (bit) {
|
|
496
|
+
this.cgRam[idx] |= (0x80 >> row);
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
this.cgRam[idx] &= ~(0x80 >> row);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
// Write to DDRAM
|
|
506
|
+
if (this.ddPtr < DDRAM_SIZE) {
|
|
507
|
+
this.ddRam[this.ddPtr] = data;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
this.doShift();
|
|
511
|
+
}
|
|
512
|
+
readByte() {
|
|
513
|
+
if (this.cgPtr !== null) {
|
|
514
|
+
const row = this.cgPtr % CHAR_HEIGHT_PX;
|
|
515
|
+
const charIdx = Math.floor(this.cgPtr / CHAR_HEIGHT_PX);
|
|
516
|
+
let data = 0;
|
|
517
|
+
for (let i = 0; i < CHAR_WIDTH_PX; i++) {
|
|
518
|
+
const idx = charIdx * CHAR_WIDTH_PX + i;
|
|
519
|
+
if (idx < this.cgRam.length && (this.cgRam[idx] & (0x80 >> row))) {
|
|
520
|
+
data |= ((0x01 << (CHAR_WIDTH_PX - 1)) >> i);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return data;
|
|
524
|
+
}
|
|
525
|
+
return this.ddPtr < DDRAM_SIZE ? this.ddRam[this.ddPtr] : 0x20;
|
|
526
|
+
}
|
|
527
|
+
readAddress() {
|
|
528
|
+
if (this.cgPtr !== null) {
|
|
529
|
+
return this.cgPtr & 0x3F;
|
|
530
|
+
}
|
|
531
|
+
return this.ddPtr & 0x7F;
|
|
532
|
+
}
|
|
533
|
+
// ── DDRAM pointer management ──────────────────────────────────
|
|
534
|
+
incrementDdPtr() {
|
|
535
|
+
this.ddPtr++;
|
|
536
|
+
if (this.rows > 1) {
|
|
537
|
+
if (this.ddPtr === 0x28) {
|
|
538
|
+
this.ddPtr = 0x40;
|
|
539
|
+
}
|
|
540
|
+
else if (this.ddPtr === 0x68 || this.ddPtr >= DDRAM_SIZE) {
|
|
541
|
+
this.ddPtr = 0x00;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
else if (this.ddPtr >= 80) {
|
|
545
|
+
this.ddPtr = 0;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
decrementDdPtr() {
|
|
549
|
+
this.ddPtr--;
|
|
550
|
+
if (this.rows > 1) {
|
|
551
|
+
if (this.ddPtr < 0) {
|
|
552
|
+
this.ddPtr = 0x67;
|
|
553
|
+
}
|
|
554
|
+
else if (this.ddPtr === 0x3F) {
|
|
555
|
+
this.ddPtr = 0x27;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
if (this.ddPtr < 0) {
|
|
560
|
+
this.ddPtr = 79;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
doShift() {
|
|
565
|
+
if (this.cgPtr !== null) {
|
|
566
|
+
// Shift CGRAM pointer
|
|
567
|
+
if (this.entryModeFlags & exports.LCD_CMD_ENTRY_MODE_INCREMENT) {
|
|
568
|
+
this.cgPtr++;
|
|
569
|
+
if (this.cgPtr >= CGRAM_STORAGE_CHARS * CHAR_HEIGHT_PX) {
|
|
570
|
+
this.cgPtr = 0;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
else {
|
|
574
|
+
this.cgPtr--;
|
|
575
|
+
if (this.cgPtr < 0) {
|
|
576
|
+
this.cgPtr = CGRAM_STORAGE_CHARS * CHAR_HEIGHT_PX - 1;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
// Shift display or cursor
|
|
582
|
+
if (this.entryModeFlags & exports.LCD_CMD_ENTRY_MODE_SHIFT) {
|
|
583
|
+
if (this.entryModeFlags & exports.LCD_CMD_ENTRY_MODE_INCREMENT) {
|
|
584
|
+
++this.scrollOffset;
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
--this.scrollOffset;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (this.entryModeFlags & exports.LCD_CMD_ENTRY_MODE_INCREMENT) {
|
|
591
|
+
this.incrementDdPtr();
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
this.decrementDdPtr();
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
// ── Character Data Lookup ─────────────────────────────────────
|
|
598
|
+
/**
|
|
599
|
+
* Get the 5-column font data for a character.
|
|
600
|
+
* Characters 0–15 come from CGRAM; 16–255 from ROM.
|
|
601
|
+
*/
|
|
602
|
+
charBits(c) {
|
|
603
|
+
if (c < CGRAM_STORAGE_CHARS) {
|
|
604
|
+
// Return a slice of cgRam for this character
|
|
605
|
+
const start = c * CHAR_WIDTH_PX;
|
|
606
|
+
return this.cgRam.subarray(start, start + CHAR_WIDTH_PX);
|
|
607
|
+
}
|
|
608
|
+
return FONT_A00[c - CGRAM_STORAGE_CHARS];
|
|
609
|
+
}
|
|
610
|
+
// ── Data Offset Helper ────────────────────────────────────────
|
|
611
|
+
getDataOffset(row, col) {
|
|
612
|
+
if (row >= this.rows)
|
|
613
|
+
row = this.rows - 1;
|
|
614
|
+
// Normalize negative scroll offset
|
|
615
|
+
let scroll = this.scrollOffset;
|
|
616
|
+
while (scroll < 0) {
|
|
617
|
+
scroll += this.dataWidthCols;
|
|
618
|
+
}
|
|
619
|
+
const dataCol = (col + scroll) % this.dataWidthCols;
|
|
620
|
+
if (this.rows > 1) {
|
|
621
|
+
return ROW_OFFSETS[row] + dataCol;
|
|
622
|
+
}
|
|
623
|
+
return dataCol;
|
|
624
|
+
}
|
|
625
|
+
// ── Pixel Buffer Update ───────────────────────────────────────
|
|
626
|
+
updatePixels() {
|
|
627
|
+
var _a;
|
|
628
|
+
const displayOn = !!(this.displayFlags & exports.LCD_CMD_DISPLAY_ON);
|
|
629
|
+
// Determine cursor state
|
|
630
|
+
let cursorOn = this.displayFlags & CURSOR_MASK;
|
|
631
|
+
if (this.displayFlags & exports.LCD_CMD_DISPLAY_CURSOR_BLINK) {
|
|
632
|
+
if (this.blinkState) {
|
|
633
|
+
cursorOn &= ~exports.LCD_CMD_DISPLAY_CURSOR_BLINK;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
for (let row = 0; row < this.rows; row++) {
|
|
637
|
+
for (let col = 0; col < this.cols; col++) {
|
|
638
|
+
// Top-left pixel for this character cell
|
|
639
|
+
const charTopLeftX = col * (CHAR_WIDTH_PX + 1);
|
|
640
|
+
const charTopLeftY = row * (CHAR_HEIGHT_PX + 1);
|
|
641
|
+
// DDRAM offset
|
|
642
|
+
const ddOffset = this.getDataOffset(row, col);
|
|
643
|
+
const charCode = (_a = this.ddRam[ddOffset]) !== null && _a !== void 0 ? _a : 0x20;
|
|
644
|
+
// Should we draw cursor here?
|
|
645
|
+
const drawCursor = cursorOn && (ddOffset === this.ddPtr) && this.cgPtr === null;
|
|
646
|
+
// Get font data
|
|
647
|
+
const bits = this.charBits(charCode);
|
|
648
|
+
// Render 5×8 character
|
|
649
|
+
for (let y = 0; y < CHAR_HEIGHT_PX; y++) {
|
|
650
|
+
for (let x = 0; x < CHAR_WIDTH_PX; x++) {
|
|
651
|
+
const pixelIdx = (charTopLeftY + y) * this.pixelsWidth + (charTopLeftX + x);
|
|
652
|
+
if (!displayOn) {
|
|
653
|
+
this.buffer[pixelIdx] = 0;
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
// Font data is column-major: bits[x] has row bits, MSB = row 0
|
|
657
|
+
let pixel = (bits[x] & (0x80 >> y)) ? 1 : 0;
|
|
658
|
+
// Cursor override
|
|
659
|
+
if (drawCursor) {
|
|
660
|
+
if ((cursorOn & exports.LCD_CMD_DISPLAY_CURSOR_BLINK) ||
|
|
661
|
+
((cursorOn & exports.LCD_CMD_DISPLAY_CURSOR) && y === CHAR_HEIGHT_PX - 1)) {
|
|
662
|
+
pixel = 1;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
this.buffer[pixelIdx] = pixel;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
// ── Public Accessors (for rendering / debugging) ──────────────
|
|
672
|
+
/** Get the raw DDRAM contents */
|
|
673
|
+
getDDRam() {
|
|
674
|
+
return this.ddRam;
|
|
675
|
+
}
|
|
676
|
+
/** Get the current DDRAM address pointer */
|
|
677
|
+
getDDPtr() {
|
|
678
|
+
return this.ddPtr;
|
|
679
|
+
}
|
|
680
|
+
/** Get the display flags */
|
|
681
|
+
getDisplayFlags() {
|
|
682
|
+
return this.displayFlags;
|
|
683
|
+
}
|
|
684
|
+
/** Get the entry mode flags */
|
|
685
|
+
getEntryModeFlags() {
|
|
686
|
+
return this.entryModeFlags;
|
|
687
|
+
}
|
|
688
|
+
/** Get scroll offset */
|
|
689
|
+
getScrollOffset() {
|
|
690
|
+
return this.scrollOffset;
|
|
691
|
+
}
|
|
692
|
+
/** Get CGRAM pointer (null if not in CGRAM mode) */
|
|
693
|
+
getCGPtr() {
|
|
694
|
+
return this.cgPtr;
|
|
695
|
+
}
|
|
696
|
+
/** Read the text content of a specific display row */
|
|
697
|
+
getRowText(row) {
|
|
698
|
+
let text = '';
|
|
699
|
+
for (let col = 0; col < this.cols; col++) {
|
|
700
|
+
const offset = this.getDataOffset(row, col);
|
|
701
|
+
text += String.fromCharCode(this.ddRam[offset]);
|
|
702
|
+
}
|
|
703
|
+
return text;
|
|
704
|
+
}
|
|
705
|
+
/** Pixel state at a given coordinate: -1 (gap), 0 (off), 1 (on) */
|
|
706
|
+
pixelState(x, y) {
|
|
707
|
+
const offset = y * this.pixelsWidth + x;
|
|
708
|
+
if (offset >= 0 && offset < this.buffer.length) {
|
|
709
|
+
return this.buffer[offset];
|
|
710
|
+
}
|
|
711
|
+
return -1;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
exports.LCDAttachment = LCDAttachment;
|
|
715
|
+
LCDAttachment.BLINK_PERIOD_MS = 350;
|
|
716
|
+
//# sourceMappingURL=LCDAttachment.js.map
|