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.
Files changed (141) hide show
  1. package/README.md +139 -32
  2. package/dist/components/IO/ACIA.d.ts +76 -0
  3. package/dist/components/IO/ACIA.js +282 -0
  4. package/dist/components/IO/ACIA.js.map +1 -0
  5. package/dist/components/IO/Attachments/Attachment.d.ts +112 -0
  6. package/dist/components/IO/Attachments/Attachment.js +71 -0
  7. package/dist/components/IO/Attachments/Attachment.js.map +1 -0
  8. package/dist/components/IO/Attachments/JoystickAttachment.d.ts +53 -0
  9. package/dist/components/IO/Attachments/JoystickAttachment.js +90 -0
  10. package/dist/components/IO/Attachments/JoystickAttachment.js.map +1 -0
  11. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.d.ts +63 -0
  12. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js +489 -0
  13. package/dist/components/IO/Attachments/KeyboardEncoderAttachment.js.map +1 -0
  14. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.d.ts +44 -0
  15. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.js +274 -0
  16. package/dist/components/IO/Attachments/KeyboardMatrixAttachment.js.map +1 -0
  17. package/dist/components/IO/Attachments/KeypadAttachment.d.ts +47 -0
  18. package/dist/components/IO/Attachments/KeypadAttachment.js +141 -0
  19. package/dist/components/IO/Attachments/KeypadAttachment.js.map +1 -0
  20. package/dist/components/IO/Attachments/LCDAttachment.d.ts +110 -0
  21. package/dist/components/IO/Attachments/LCDAttachment.js +716 -0
  22. package/dist/components/IO/Attachments/LCDAttachment.js.map +1 -0
  23. package/dist/components/IO/Attachments/SNESAttachment.d.ts +85 -0
  24. package/dist/components/IO/Attachments/SNESAttachment.js +184 -0
  25. package/dist/components/IO/Attachments/SNESAttachment.js.map +1 -0
  26. package/dist/components/IO/Empty.d.ts +9 -0
  27. package/dist/components/IO/Empty.js +5 -7
  28. package/dist/components/IO/Empty.js.map +1 -1
  29. package/dist/components/IO/GPIOCard.d.ts +5 -5
  30. package/dist/components/IO/GPIOCard.js.map +1 -1
  31. package/dist/components/IO/RAMBank.d.ts +37 -0
  32. package/dist/components/IO/RAMBank.js +63 -0
  33. package/dist/components/IO/RAMBank.js.map +1 -0
  34. package/dist/components/IO/RTC.d.ts +107 -0
  35. package/dist/components/IO/RTC.js +483 -0
  36. package/dist/components/IO/RTC.js.map +1 -0
  37. package/dist/components/IO/Sound.d.ts +120 -0
  38. package/dist/components/IO/Sound.js +622 -0
  39. package/dist/components/IO/Sound.js.map +1 -0
  40. package/dist/components/IO/Storage.d.ts +74 -0
  41. package/dist/components/IO/Storage.js +409 -0
  42. package/dist/components/IO/Storage.js.map +1 -0
  43. package/dist/components/IO/Terminal.d.ts +19 -0
  44. package/dist/components/IO/Terminal.js +33 -0
  45. package/dist/components/IO/Terminal.js.map +1 -0
  46. package/dist/components/IO/VIA.d.ts +105 -0
  47. package/dist/components/IO/VIA.js +597 -0
  48. package/dist/components/IO/VIA.js.map +1 -0
  49. package/dist/components/IO/Video.d.ts +141 -0
  50. package/dist/components/IO/Video.js +630 -0
  51. package/dist/components/IO/Video.js.map +1 -0
  52. package/dist/components/Machine.d.ts +20 -24
  53. package/dist/components/Machine.js +249 -166
  54. package/dist/components/Machine.js.map +1 -1
  55. package/dist/index.js +28 -14
  56. package/dist/index.js.map +1 -1
  57. package/dist/lib.d.ts +16 -16
  58. package/dist/lib.js +32 -32
  59. package/dist/lib.js.map +1 -1
  60. package/dist/tests/IO/ACIA.test.d.ts +1 -0
  61. package/dist/tests/IO/ACIA.test.js +423 -0
  62. package/dist/tests/IO/ACIA.test.js.map +1 -0
  63. package/dist/tests/IO/Attachments/Attachment.test.d.ts +1 -0
  64. package/dist/tests/IO/Attachments/Attachment.test.js +339 -0
  65. package/dist/tests/IO/Attachments/Attachment.test.js.map +1 -0
  66. package/dist/tests/IO/Attachments/JoystickAttachment.test.d.ts +1 -0
  67. package/dist/tests/IO/Attachments/JoystickAttachment.test.js +126 -0
  68. package/dist/tests/IO/Attachments/JoystickAttachment.test.js.map +1 -0
  69. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.d.ts +1 -0
  70. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js +779 -0
  71. package/dist/tests/IO/Attachments/KeyboardEncoderAttachment.test.js.map +1 -0
  72. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.d.ts +1 -0
  73. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.js +355 -0
  74. package/dist/tests/IO/Attachments/KeyboardMatrixAttachment.test.js.map +1 -0
  75. package/dist/tests/IO/Attachments/KeypadAttachment.test.d.ts +1 -0
  76. package/dist/tests/IO/Attachments/KeypadAttachment.test.js +323 -0
  77. package/dist/tests/IO/Attachments/KeypadAttachment.test.js.map +1 -0
  78. package/dist/tests/IO/Attachments/LCDAttachment.test.d.ts +1 -0
  79. package/dist/tests/IO/Attachments/LCDAttachment.test.js +627 -0
  80. package/dist/tests/IO/Attachments/LCDAttachment.test.js.map +1 -0
  81. package/dist/tests/IO/Attachments/SNESAttachment.test.d.ts +1 -0
  82. package/dist/tests/IO/Attachments/SNESAttachment.test.js +331 -0
  83. package/dist/tests/IO/Attachments/SNESAttachment.test.js.map +1 -0
  84. package/dist/tests/IO/Empty.test.d.ts +1 -0
  85. package/dist/tests/IO/Empty.test.js +121 -0
  86. package/dist/tests/IO/Empty.test.js.map +1 -0
  87. package/dist/tests/IO/GPIOCard.test.js.map +1 -1
  88. package/dist/tests/IO/RAMBank.test.d.ts +1 -0
  89. package/dist/tests/IO/RAMBank.test.js +229 -0
  90. package/dist/tests/IO/RAMBank.test.js.map +1 -0
  91. package/dist/tests/IO/RTC.test.d.ts +1 -0
  92. package/dist/tests/IO/RTC.test.js +177 -0
  93. package/dist/tests/IO/RTC.test.js.map +1 -0
  94. package/dist/tests/IO/Sound.test.d.ts +1 -0
  95. package/dist/tests/IO/Sound.test.js +528 -0
  96. package/dist/tests/IO/Sound.test.js.map +1 -0
  97. package/dist/tests/IO/Storage.test.d.ts +1 -0
  98. package/dist/tests/IO/Storage.test.js +656 -0
  99. package/dist/tests/IO/Storage.test.js.map +1 -0
  100. package/dist/tests/IO/VIA.test.d.ts +1 -0
  101. package/dist/tests/IO/VIA.test.js +503 -0
  102. package/dist/tests/IO/VIA.test.js.map +1 -0
  103. package/dist/tests/IO/Video.test.d.ts +1 -0
  104. package/dist/tests/IO/Video.test.js +549 -0
  105. package/dist/tests/IO/Video.test.js.map +1 -0
  106. package/dist/tests/Machine.test.js +27 -42
  107. package/dist/tests/Machine.test.js.map +1 -1
  108. package/package.json +1 -1
  109. package/src/components/IO/{SerialCard.ts → ACIA.ts} +2 -2
  110. package/src/components/IO/{GPIOAttachments/GPIOAttachment.ts → Attachments/Attachment.ts} +2 -2
  111. package/src/components/IO/{GPIOAttachments/GPIOJoystickAttachment.ts → Attachments/JoystickAttachment.ts} +3 -3
  112. package/src/components/IO/{GPIOAttachments/GPIOKeyboardEncoderAttachment.ts → Attachments/KeyboardEncoderAttachment.ts} +3 -3
  113. package/src/components/IO/{GPIOAttachments/GPIOKeyboardMatrixAttachment.ts → Attachments/KeyboardMatrixAttachment.ts} +5 -5
  114. package/src/components/IO/{GPIOAttachments/GPIOKeypadAttachment.ts → Attachments/KeypadAttachment.ts} +3 -3
  115. package/src/components/IO/{GPIOAttachments/GPIOLCDAttachment.ts → Attachments/LCDAttachment.ts} +7 -7
  116. package/src/components/IO/{EmptyCard.ts → Empty.ts} +1 -1
  117. package/src/components/IO/{RAMCard.ts → RAMBank.ts} +8 -8
  118. package/src/components/IO/{RTCCard.ts → RTC.ts} +1 -1
  119. package/src/components/IO/{SoundCard.ts → Sound.ts} +2 -2
  120. package/src/components/IO/{StorageCard.ts → Storage.ts} +70 -73
  121. package/src/components/IO/{DevOutputBoard.ts → Terminal.ts} +2 -2
  122. package/src/components/IO/{GPIOCard.ts → VIA.ts} +64 -64
  123. package/src/components/IO/{VideoCard.ts → Video.ts} +1 -1
  124. package/src/components/Machine.ts +276 -176
  125. package/src/index.ts +34 -21
  126. package/src/lib.ts +16 -16
  127. package/src/tests/IO/{SerialCard.test.ts → ACIA.test.ts} +5 -5
  128. package/src/tests/IO/{GPIOAttachments/GPIOAttachment.test.ts → Attachments/Attachment.test.ts} +12 -12
  129. package/src/tests/IO/{GPIOAttachments/GPIOJoystickAttachment.test.ts → Attachments/JoystickAttachment.test.ts} +23 -23
  130. package/src/tests/IO/{GPIOAttachments/GPIOKeyboardEncoderAttachment.test.ts → Attachments/KeyboardEncoderAttachment.test.ts} +4 -4
  131. package/src/tests/IO/{GPIOAttachments/GPIOKeyboardMatrixAttachment.test.ts → Attachments/KeyboardMatrixAttachment.test.ts} +5 -5
  132. package/src/tests/IO/{GPIOAttachments/GPIOKeypadAttachment.test.ts → Attachments/KeypadAttachment.test.ts} +38 -38
  133. package/src/tests/IO/{GPIOAttachments/GPIOLCDAttachment.test.ts → Attachments/LCDAttachment.test.ts} +12 -12
  134. package/src/tests/IO/Empty.test.ts +143 -0
  135. package/src/tests/IO/{RAMCard.test.ts → RAMBank.test.ts} +33 -33
  136. package/src/tests/IO/{RTCCard.test.ts → RTC.test.ts} +6 -6
  137. package/src/tests/IO/{SoundCard.test.ts → Sound.test.ts} +6 -6
  138. package/src/tests/IO/{StorageCard.test.ts → Storage.test.ts} +34 -25
  139. package/src/tests/IO/{GPIOCard.test.ts → VIA.test.ts} +7 -7
  140. package/src/tests/IO/{VideoCard.test.ts → Video.test.ts} +13 -13
  141. package/src/tests/Machine.test.ts +31 -38
@@ -0,0 +1,627 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const LCDAttachment_1 = require("../../../components/IO/Attachments/LCDAttachment");
4
+ // VIA Port A pin masks
5
+ const PIN_RS = 0x20;
6
+ const PIN_RW = 0x40;
7
+ const PIN_E = 0x80;
8
+ describe('LCDAttachment', () => {
9
+ let lcd;
10
+ beforeEach(() => {
11
+ lcd = new LCDAttachment_1.LCDAttachment(16, 2);
12
+ });
13
+ // ── Helper: write a command via the GPIO bus ────────────────────
14
+ /** Simulate a command write: RS=0, RW=0 */
15
+ function writeCommand(lcd, cmd) {
16
+ lcd.sendCommand(cmd);
17
+ lcd.updatePixels();
18
+ }
19
+ /** Simulate a data byte write: RS=1, RW=0 */
20
+ function writeData(lcd, data) {
21
+ lcd.writeByte(data);
22
+ lcd.updatePixels();
23
+ }
24
+ /** Write a string to the LCD */
25
+ function writeString(lcd, str) {
26
+ for (let i = 0; i < str.length; i++) {
27
+ writeData(lcd, str.charCodeAt(i));
28
+ }
29
+ }
30
+ /** Simulate full GPIO bus cycle: set Port B data, set Port A control,
31
+ * raise E, then lower E (falling-edge latch) */
32
+ function gpioBusWrite(lcd, rs, data) {
33
+ const portAValue = (rs ? PIN_RS : 0); // RW = 0 (write)
34
+ const ddr = 0xFF; // all outputs
35
+ // Set data on Port B
36
+ lcd.writePortB(data, ddr);
37
+ // Raise E
38
+ lcd.writePortA(portAValue | PIN_E, ddr);
39
+ // Lower E (falling edge triggers latch)
40
+ lcd.writePortA(portAValue, ddr);
41
+ }
42
+ // ── Constructor & Reset ────────────────────────────────────────
43
+ describe('constructor and reset', () => {
44
+ it('should initialize with correct dimensions', () => {
45
+ expect(lcd.cols).toBe(16);
46
+ expect(lcd.rows).toBe(2);
47
+ });
48
+ it('should calculate pixel dimensions correctly', () => {
49
+ // 16 chars × (5+1) - 1 = 95 pixels wide
50
+ // 2 rows × (8+1) - 1 = 17 pixels high
51
+ expect(lcd.pixelsWidth).toBe(95);
52
+ expect(lcd.pixelsHeight).toBe(17);
53
+ });
54
+ it('should allocate pixel buffer', () => {
55
+ expect(lcd.buffer).toBeDefined();
56
+ expect(lcd.buffer.length).toBe(95 * 17);
57
+ });
58
+ it('should initialize DDRAM with spaces', () => {
59
+ const ddRam = lcd.getDDRam();
60
+ for (let i = 0; i < ddRam.length; i++) {
61
+ expect(ddRam[i]).toBe(0x20);
62
+ }
63
+ });
64
+ it('should start with DDRAM address at 0', () => {
65
+ expect(lcd.getDDPtr()).toBe(0);
66
+ });
67
+ it('should start with display off', () => {
68
+ expect(lcd.getDisplayFlags() & LCDAttachment_1.LCD_CMD_DISPLAY_ON).toBe(0);
69
+ });
70
+ it('should start with increment mode', () => {
71
+ expect(lcd.getEntryModeFlags() & LCDAttachment_1.LCD_CMD_ENTRY_MODE_INCREMENT).toBeTruthy();
72
+ });
73
+ it('should support different display sizes', () => {
74
+ const lcd20x4 = new LCDAttachment_1.LCDAttachment(20, 4);
75
+ expect(lcd20x4.cols).toBe(20);
76
+ expect(lcd20x4.rows).toBe(4);
77
+ // 20 × (5+1) - 1 = 119
78
+ // 4 × (8+1) - 1 = 35
79
+ expect(lcd20x4.pixelsWidth).toBe(119);
80
+ expect(lcd20x4.pixelsHeight).toBe(35);
81
+ });
82
+ it('should reset all state', () => {
83
+ // Modify state
84
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON);
85
+ writeData(lcd, 0x41); // 'A'
86
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x40);
87
+ // Reset
88
+ lcd.reset();
89
+ expect(lcd.getDDPtr()).toBe(0);
90
+ expect(lcd.getDisplayFlags()).toBe(0);
91
+ expect(lcd.getScrollOffset()).toBe(0);
92
+ expect(lcd.getDDRam()[0]).toBe(0x20); // space
93
+ });
94
+ });
95
+ // ── Command Processing ─────────────────────────────────────────
96
+ describe('clear display command', () => {
97
+ it('should clear DDRAM to spaces', () => {
98
+ writeData(lcd, 0x41); // 'A'
99
+ writeData(lcd, 0x42); // 'B'
100
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_CLEAR);
101
+ const ddRam = lcd.getDDRam();
102
+ expect(ddRam[0]).toBe(0x20);
103
+ expect(ddRam[1]).toBe(0x20);
104
+ });
105
+ it('should reset DDRAM pointer to 0', () => {
106
+ writeData(lcd, 0x41);
107
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_CLEAR);
108
+ expect(lcd.getDDPtr()).toBe(0);
109
+ });
110
+ it('should reset scroll offset', () => {
111
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SHIFT | LCDAttachment_1.LCD_CMD_SHIFT_DISPLAY);
112
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_CLEAR);
113
+ expect(lcd.getScrollOffset()).toBe(0);
114
+ });
115
+ it('should reset entry mode to increment', () => {
116
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_ENTRY_MODE); // decrement mode
117
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_CLEAR);
118
+ expect(lcd.getEntryModeFlags() & LCDAttachment_1.LCD_CMD_ENTRY_MODE_INCREMENT).toBeTruthy();
119
+ });
120
+ });
121
+ describe('home command', () => {
122
+ it('should reset DDRAM pointer to 0', () => {
123
+ writeData(lcd, 0x41);
124
+ writeData(lcd, 0x42);
125
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_HOME);
126
+ expect(lcd.getDDPtr()).toBe(0);
127
+ });
128
+ it('should reset scroll offset', () => {
129
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SHIFT | LCDAttachment_1.LCD_CMD_SHIFT_DISPLAY);
130
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_HOME);
131
+ expect(lcd.getScrollOffset()).toBe(0);
132
+ });
133
+ it('should not clear DDRAM', () => {
134
+ writeData(lcd, 0x41);
135
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_HOME);
136
+ expect(lcd.getDDRam()[0]).toBe(0x41);
137
+ });
138
+ });
139
+ describe('entry mode command', () => {
140
+ it('should set increment mode', () => {
141
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_ENTRY_MODE | LCDAttachment_1.LCD_CMD_ENTRY_MODE_INCREMENT);
142
+ expect(lcd.getEntryModeFlags() & LCDAttachment_1.LCD_CMD_ENTRY_MODE_INCREMENT).toBeTruthy();
143
+ });
144
+ it('should set decrement mode', () => {
145
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_ENTRY_MODE); // no INCREMENT bit
146
+ expect(lcd.getEntryModeFlags() & LCDAttachment_1.LCD_CMD_ENTRY_MODE_INCREMENT).toBeFalsy();
147
+ });
148
+ it('should enable display shift on write', () => {
149
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_ENTRY_MODE | LCDAttachment_1.LCD_CMD_ENTRY_MODE_INCREMENT | LCDAttachment_1.LCD_CMD_ENTRY_MODE_SHIFT);
150
+ expect(lcd.getEntryModeFlags() & LCDAttachment_1.LCD_CMD_ENTRY_MODE_SHIFT).toBeTruthy();
151
+ });
152
+ });
153
+ describe('display control command', () => {
154
+ it('should turn display on', () => {
155
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON);
156
+ expect(lcd.getDisplayFlags() & LCDAttachment_1.LCD_CMD_DISPLAY_ON).toBeTruthy();
157
+ });
158
+ it('should turn display off', () => {
159
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON);
160
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY); // display off
161
+ expect(lcd.getDisplayFlags() & LCDAttachment_1.LCD_CMD_DISPLAY_ON).toBeFalsy();
162
+ });
163
+ it('should enable cursor', () => {
164
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON | LCDAttachment_1.LCD_CMD_DISPLAY_CURSOR);
165
+ expect(lcd.getDisplayFlags() & LCDAttachment_1.LCD_CMD_DISPLAY_CURSOR).toBeTruthy();
166
+ });
167
+ it('should enable cursor blink', () => {
168
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON | LCDAttachment_1.LCD_CMD_DISPLAY_CURSOR_BLINK);
169
+ expect(lcd.getDisplayFlags() & LCDAttachment_1.LCD_CMD_DISPLAY_CURSOR_BLINK).toBeTruthy();
170
+ });
171
+ });
172
+ describe('set DDRAM address command', () => {
173
+ it('should set DDRAM address', () => {
174
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x05);
175
+ expect(lcd.getDDPtr()).toBe(0x05);
176
+ });
177
+ it('should set second row address', () => {
178
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x40);
179
+ expect(lcd.getDDPtr()).toBe(0x40);
180
+ });
181
+ it('should clear CGRAM pointer', () => {
182
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_CGRAM_ADDR | 0x00);
183
+ expect(lcd.getCGPtr()).not.toBeNull();
184
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x00);
185
+ expect(lcd.getCGPtr()).toBeNull();
186
+ });
187
+ });
188
+ describe('set CGRAM address command', () => {
189
+ it('should set CGRAM address', () => {
190
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_CGRAM_ADDR | 0x10);
191
+ expect(lcd.getCGPtr()).toBe(0x10);
192
+ });
193
+ it('should mask to 6 bits', () => {
194
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_CGRAM_ADDR | 0x3F);
195
+ expect(lcd.getCGPtr()).toBe(0x3F);
196
+ });
197
+ });
198
+ describe('shift command', () => {
199
+ it('should shift cursor right', () => {
200
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SHIFT | LCDAttachment_1.LCD_CMD_SHIFT_RIGHT);
201
+ expect(lcd.getDDPtr()).toBe(1);
202
+ });
203
+ it('should shift cursor left', () => {
204
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x05);
205
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SHIFT); // left
206
+ expect(lcd.getDDPtr()).toBe(4);
207
+ });
208
+ it('should shift display right', () => {
209
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SHIFT | LCDAttachment_1.LCD_CMD_SHIFT_DISPLAY | LCDAttachment_1.LCD_CMD_SHIFT_RIGHT);
210
+ expect(lcd.getScrollOffset()).toBe(-1);
211
+ });
212
+ it('should shift display left', () => {
213
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SHIFT | LCDAttachment_1.LCD_CMD_SHIFT_DISPLAY);
214
+ expect(lcd.getScrollOffset()).toBe(1);
215
+ });
216
+ });
217
+ // ── Data Write ─────────────────────────────────────────────────
218
+ describe('data write to DDRAM', () => {
219
+ it('should write a byte to DDRAM', () => {
220
+ writeData(lcd, 0x41); // 'A'
221
+ expect(lcd.getDDRam()[0]).toBe(0x41);
222
+ });
223
+ it('should auto-increment address in increment mode', () => {
224
+ writeData(lcd, 0x41);
225
+ expect(lcd.getDDPtr()).toBe(1);
226
+ writeData(lcd, 0x42);
227
+ expect(lcd.getDDPtr()).toBe(2);
228
+ });
229
+ it('should auto-decrement address in decrement mode', () => {
230
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x05);
231
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_ENTRY_MODE); // decrement
232
+ writeData(lcd, 0x41);
233
+ expect(lcd.getDDPtr()).toBe(4);
234
+ });
235
+ it('should write a string to the first row', () => {
236
+ writeString(lcd, 'Hello');
237
+ expect(lcd.getRowText(0).substring(0, 5)).toBe('Hello');
238
+ });
239
+ it('should write to second row', () => {
240
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x40);
241
+ writeString(lcd, 'World');
242
+ expect(lcd.getRowText(1).substring(0, 5)).toBe('World');
243
+ });
244
+ it('should wrap from end of row 1 to row 2', () => {
245
+ // Write 40 characters to fill first row of DDRAM
246
+ for (let i = 0; i < 40; i++) {
247
+ writeData(lcd, 0x41 + (i % 26));
248
+ }
249
+ // Pointer should now be at 0x40 (second row)
250
+ expect(lcd.getDDPtr()).toBe(0x40);
251
+ });
252
+ it('should wrap from end of row 2 back to start', () => {
253
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x40);
254
+ // Write 40 characters to fill second row
255
+ for (let i = 0; i < 40; i++) {
256
+ writeData(lcd, 0x41 + (i % 26));
257
+ }
258
+ // Should wrap to beginning
259
+ expect(lcd.getDDPtr()).toBe(0x00);
260
+ });
261
+ it('should shift display when entry mode shift is enabled', () => {
262
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_ENTRY_MODE | LCDAttachment_1.LCD_CMD_ENTRY_MODE_INCREMENT | LCDAttachment_1.LCD_CMD_ENTRY_MODE_SHIFT);
263
+ writeData(lcd, 0x41);
264
+ expect(lcd.getScrollOffset()).toBe(1);
265
+ });
266
+ });
267
+ // ── Data Read ──────────────────────────────────────────────────
268
+ describe('data read from DDRAM', () => {
269
+ it('should read back written data via readAddress', () => {
270
+ writeData(lcd, 0x41);
271
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x00);
272
+ expect(lcd.readAddress()).toBe(0x00);
273
+ });
274
+ it('should read DDRAM address via readAddress', () => {
275
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x15);
276
+ expect(lcd.readAddress()).toBe(0x15);
277
+ });
278
+ it('should read CGRAM address when in CGRAM mode', () => {
279
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_CGRAM_ADDR | 0x18);
280
+ expect(lcd.readAddress()).toBe(0x18);
281
+ });
282
+ });
283
+ // ── CGRAM ──────────────────────────────────────────────────────
284
+ describe('CGRAM operations', () => {
285
+ it('should write custom character data to CGRAM', () => {
286
+ // Set CGRAM address for character 0, row 0
287
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_CGRAM_ADDR | 0x00);
288
+ // Write 8 rows of pattern (smile face)
289
+ const pattern = [0x00, 0x0A, 0x00, 0x00, 0x11, 0x0E, 0x00, 0x00];
290
+ for (const row of pattern) {
291
+ writeData(lcd, row);
292
+ }
293
+ // CGRAM pointer should have advanced
294
+ expect(lcd.getCGPtr()).toBe(8);
295
+ });
296
+ it('should render CGRAM character on display', () => {
297
+ // Define character 0 with a simple pattern (all pixels on in first row)
298
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_CGRAM_ADDR | 0x00);
299
+ writeData(lcd, 0x1F); // row 0: all 5 bits on
300
+ for (let i = 1; i < 8; i++) {
301
+ writeData(lcd, 0x00); // rows 1-7: all off
302
+ }
303
+ // Now write character 0 to DDRAM and enable display
304
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x00);
305
+ writeData(lcd, 0x00); // character 0 from CGRAM
306
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON);
307
+ lcd.updatePixels();
308
+ // Character 0 should be at position (0,0) - check first row pixels are on
309
+ // The pixel at (0,0) should be on since we set bit 4 (MSB of 5-bit) in row 0
310
+ const topLeftPixel = lcd.pixelState(0, 0);
311
+ expect(topLeftPixel).toBe(1);
312
+ });
313
+ it('should wrap CGRAM pointer in increment mode', () => {
314
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_CGRAM_ADDR | 0x00);
315
+ // Write 128 bytes (16 chars × 8 rows = full CGRAM)
316
+ for (let i = 0; i < 128; i++) {
317
+ writeData(lcd, 0x00);
318
+ }
319
+ // Should wrap back to 0
320
+ expect(lcd.getCGPtr()).toBe(0);
321
+ });
322
+ });
323
+ // ── Pixel Output ───────────────────────────────────────────────
324
+ describe('pixel output', () => {
325
+ it('should render all pixels off when display is off', () => {
326
+ writeData(lcd, 0x41); // 'A'
327
+ // Display is off by default
328
+ lcd.updatePixels();
329
+ for (let y = 0; y < lcd.pixelsHeight; y++) {
330
+ for (let x = 0; x < lcd.pixelsWidth; x++) {
331
+ const state = lcd.pixelState(x, y);
332
+ // All character pixels should be 0 (off), gaps are -1
333
+ if (state !== -1) {
334
+ expect(state).toBe(0);
335
+ }
336
+ }
337
+ }
338
+ });
339
+ it('should render character pixels when display is on', () => {
340
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON);
341
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x00);
342
+ writeData(lcd, 0xFF); // char 255 — all pixels on in A00 font
343
+ lcd.updatePixels();
344
+ // Check that some pixels are on in the first character cell
345
+ let hasOnPixel = false;
346
+ for (let y = 0; y < 8; y++) {
347
+ for (let x = 0; x < 5; x++) {
348
+ if (lcd.pixelState(x, y) === 1) {
349
+ hasOnPixel = true;
350
+ }
351
+ }
352
+ }
353
+ expect(hasOnPixel).toBe(true);
354
+ });
355
+ it('should have gap pixels between characters', () => {
356
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON);
357
+ lcd.updatePixels();
358
+ // Column 5 (between char 0 and char 1) should be gap (-1)
359
+ const gapPixel = lcd.pixelState(5, 0);
360
+ expect(gapPixel).toBe(-1);
361
+ });
362
+ it('should have gap pixels between rows', () => {
363
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON);
364
+ lcd.updatePixels();
365
+ // Row 8 (between row 0 and row 1) should be gap (-1)
366
+ const gapPixel = lcd.pixelState(0, 8);
367
+ expect(gapPixel).toBe(-1);
368
+ });
369
+ it('should return -1 for out-of-bounds coordinates', () => {
370
+ expect(lcd.pixelState(-1, 0)).toBe(-1);
371
+ expect(lcd.pixelState(0, -1)).toBe(-1);
372
+ expect(lcd.pixelState(lcd.pixelsWidth, lcd.pixelsHeight)).toBe(-1);
373
+ });
374
+ it('should render spaces as all-off pixels', () => {
375
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON);
376
+ // DDRAM is initialized to spaces
377
+ lcd.updatePixels();
378
+ // All pixels in space characters should be off
379
+ for (let y = 0; y < 8; y++) {
380
+ for (let x = 0; x < 5; x++) {
381
+ expect(lcd.pixelState(x, y)).toBe(0);
382
+ }
383
+ }
384
+ });
385
+ });
386
+ // ── Cursor Display ─────────────────────────────────────────────
387
+ describe('cursor display', () => {
388
+ it('should show underline cursor on bottom row of character', () => {
389
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON | LCDAttachment_1.LCD_CMD_DISPLAY_CURSOR);
390
+ lcd.updatePixels();
391
+ // Cursor at position 0: bottom row (y=7) should have pixels on
392
+ for (let x = 0; x < 5; x++) {
393
+ expect(lcd.pixelState(x, 7)).toBe(1);
394
+ }
395
+ });
396
+ it('should not show cursor when cursor flag is off', () => {
397
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON);
398
+ lcd.updatePixels();
399
+ // Bottom row of first char should be off (space + no cursor)
400
+ for (let x = 0; x < 5; x++) {
401
+ expect(lcd.pixelState(x, 7)).toBe(0);
402
+ }
403
+ });
404
+ it('should move cursor with DDRAM address', () => {
405
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON | LCDAttachment_1.LCD_CMD_DISPLAY_CURSOR);
406
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x01);
407
+ lcd.updatePixels();
408
+ // Row 0, column 0 character should NOT have cursor
409
+ for (let x = 0; x < 5; x++) {
410
+ expect(lcd.pixelState(x, 7)).toBe(0);
411
+ }
412
+ // Row 0, column 1 character should have cursor (x offset = 6)
413
+ for (let x = 0; x < 5; x++) {
414
+ expect(lcd.pixelState(6 + x, 7)).toBe(1);
415
+ }
416
+ });
417
+ });
418
+ // ── GPIO Bus Interface ─────────────────────────────────────────
419
+ describe('GPIO bus interface', () => {
420
+ it('should latch command on E falling edge', () => {
421
+ const ddr = 0xFF;
422
+ // Write clear command via GPIO
423
+ lcd.writePortB(LCDAttachment_1.LCD_CMD_CLEAR, ddr);
424
+ // Set RS=0, RW=0, raise E
425
+ lcd.writePortA(PIN_E, ddr);
426
+ // Lower E
427
+ lcd.writePortA(0x00, ddr);
428
+ // DDRAM should be cleared (all spaces)
429
+ expect(lcd.getDDPtr()).toBe(0);
430
+ });
431
+ it('should write data when RS is high', () => {
432
+ // First turn on display via GPIO
433
+ gpioBusWrite(lcd, false, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON);
434
+ // Write 'A' with RS=1
435
+ gpioBusWrite(lcd, true, 0x41);
436
+ expect(lcd.getDDRam()[0]).toBe(0x41);
437
+ });
438
+ it('should not latch on E rising edge', () => {
439
+ const ddr = 0xFF;
440
+ // Write data to Port B
441
+ lcd.writePortB(0x41, ddr);
442
+ // Raise E (should NOT latch)
443
+ lcd.writePortA(PIN_RS | PIN_E, ddr);
444
+ // Data should not have been written yet
445
+ expect(lcd.getDDRam()[0]).toBe(0x20); // still space
446
+ });
447
+ it('should read DDRAM address via Port B when RW=1, RS=0', () => {
448
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x15);
449
+ // Set RW=1, RS=0 on Port A
450
+ const ddr = 0xFF;
451
+ lcd.writePortA(PIN_RW, ddr);
452
+ const result = lcd.readPortB(ddr, 0);
453
+ expect(result & 0x7F).toBe(0x15);
454
+ });
455
+ it('should read DDRAM data via Port B when RW=1, RS=1', () => {
456
+ // Write 'A' at address 0
457
+ writeData(lcd, 0x41);
458
+ // Set address back to 0
459
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x00);
460
+ // Set RW=1, RS=1 on Port A for data read
461
+ const ddr = 0xFF;
462
+ lcd.writePortA(PIN_RW | PIN_RS, ddr);
463
+ const result = lcd.readPortB(ddr, 0);
464
+ expect(result).toBe(0x41);
465
+ });
466
+ it('should return 0xFF from Port B when RW is low', () => {
467
+ const ddr = 0xFF;
468
+ lcd.writePortA(0x00, ddr); // RW=0
469
+ expect(lcd.readPortB(ddr, 0)).toBe(0xFF);
470
+ });
471
+ it('should always return 0xFF from Port A', () => {
472
+ expect(lcd.readPortA(0xFF, 0x00)).toBe(0xFF);
473
+ });
474
+ it('should perform a full write sequence via GPIO bus', () => {
475
+ // Step 1: Function set (8-bit, 2-line)
476
+ gpioBusWrite(lcd, false, LCDAttachment_1.LCD_CMD_FUNCTION | LCDAttachment_1.LCD_CMD_FUNCTION_LCD_2LINE | 0x10);
477
+ // Step 2: Display ON, cursor ON
478
+ gpioBusWrite(lcd, false, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON | LCDAttachment_1.LCD_CMD_DISPLAY_CURSOR);
479
+ // Step 3: Clear display
480
+ gpioBusWrite(lcd, false, LCDAttachment_1.LCD_CMD_CLEAR);
481
+ // Step 4: Entry mode set — increment, no shift
482
+ gpioBusWrite(lcd, false, LCDAttachment_1.LCD_CMD_ENTRY_MODE | LCDAttachment_1.LCD_CMD_ENTRY_MODE_INCREMENT);
483
+ // Step 5: Write "Hi"
484
+ gpioBusWrite(lcd, true, 0x48); // 'H'
485
+ gpioBusWrite(lcd, true, 0x69); // 'i'
486
+ expect(lcd.getRowText(0).substring(0, 2)).toBe('Hi');
487
+ expect(lcd.getDDPtr()).toBe(2);
488
+ expect(lcd.getDisplayFlags() & LCDAttachment_1.LCD_CMD_DISPLAY_ON).toBeTruthy();
489
+ expect(lcd.getDisplayFlags() & LCDAttachment_1.LCD_CMD_DISPLAY_CURSOR).toBeTruthy();
490
+ });
491
+ });
492
+ // ── DDRAM Pointer Wrapping ─────────────────────────────────────
493
+ describe('DDRAM pointer wrapping', () => {
494
+ it('should wrap from 0x27 to 0x40 in 2-row mode', () => {
495
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x27);
496
+ writeData(lcd, 0x41); // triggers increment
497
+ expect(lcd.getDDPtr()).toBe(0x40);
498
+ });
499
+ it('should wrap from 0x67 to 0x00 in 2-row mode', () => {
500
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x67);
501
+ writeData(lcd, 0x41);
502
+ expect(lcd.getDDPtr()).toBe(0x00);
503
+ });
504
+ it('should decrement from 0x00 to 0x67', () => {
505
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_ENTRY_MODE); // decrement mode
506
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x00);
507
+ writeData(lcd, 0x41); // triggers decrement
508
+ expect(lcd.getDDPtr()).toBe(0x67);
509
+ });
510
+ it('should decrement from 0x40 to 0x27', () => {
511
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_ENTRY_MODE); // decrement mode
512
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x40);
513
+ writeData(lcd, 0x41); // triggers decrement
514
+ expect(lcd.getDDPtr()).toBe(0x27);
515
+ });
516
+ describe('1-row mode', () => {
517
+ let lcd1;
518
+ beforeEach(() => {
519
+ lcd1 = new LCDAttachment_1.LCDAttachment(16, 1);
520
+ });
521
+ it('should wrap from position 79 to 0', () => {
522
+ writeCommand(lcd1, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 79);
523
+ writeData(lcd1, 0x41);
524
+ expect(lcd1.getDDPtr()).toBe(0);
525
+ });
526
+ it('should wrap from position 0 to 79 when decrementing', () => {
527
+ writeCommand(lcd1, LCDAttachment_1.LCD_CMD_ENTRY_MODE); // decrement mode
528
+ writeCommand(lcd1, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x00);
529
+ writeData(lcd1, 0x41);
530
+ expect(lcd1.getDDPtr()).toBe(79);
531
+ });
532
+ });
533
+ });
534
+ // ── Display Shift ──────────────────────────────────────────────
535
+ describe('display shift', () => {
536
+ it('should shift displayed content left', () => {
537
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON);
538
+ // Write "AB" starting at position 0
539
+ writeData(lcd, 0x41); // A
540
+ writeData(lcd, 0x42); // B
541
+ // Shift display left
542
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SHIFT | LCDAttachment_1.LCD_CMD_SHIFT_DISPLAY);
543
+ // After shifting, the second character should now appear as first visible
544
+ expect(lcd.getScrollOffset()).toBe(1);
545
+ });
546
+ it('should shift displayed content right', () => {
547
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON);
548
+ writeData(lcd, 0x41);
549
+ // Shift display right
550
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SHIFT | LCDAttachment_1.LCD_CMD_SHIFT_DISPLAY | LCDAttachment_1.LCD_CMD_SHIFT_RIGHT);
551
+ expect(lcd.getScrollOffset()).toBe(-1);
552
+ });
553
+ });
554
+ // ── Tick (Cursor Blink) ────────────────────────────────────────
555
+ describe('tick and blink', () => {
556
+ it('should toggle blink state after enough ticks', () => {
557
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON | LCDAttachment_1.LCD_CMD_DISPLAY_CURSOR_BLINK);
558
+ const cpuFrequency = 1000000; // 1 MHz
559
+ // Each tick = 128 cycles at 1 MHz = 0.128 ms
560
+ // Need 350ms / 0.128ms ≈ 2734 ticks
561
+ for (let i = 0; i < 3000; i++) {
562
+ lcd.tick(cpuFrequency);
563
+ }
564
+ // Blink state should have toggled
565
+ lcd.updatePixels();
566
+ // We can verify by checking that the test doesn't crash and pixels update
567
+ });
568
+ it('should not crash with high frequency', () => {
569
+ lcd.tick(10000000); // 10 MHz
570
+ lcd.tick(10000000);
571
+ });
572
+ });
573
+ // ── Row Text Helper ────────────────────────────────────────────
574
+ describe('getRowText', () => {
575
+ it('should return text for row 0', () => {
576
+ writeString(lcd, 'Hello, World!');
577
+ expect(lcd.getRowText(0).substring(0, 13)).toBe('Hello, World!');
578
+ });
579
+ it('should return text for row 1', () => {
580
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x40);
581
+ writeString(lcd, 'Line Two');
582
+ expect(lcd.getRowText(1).substring(0, 8)).toBe('Line Two');
583
+ });
584
+ it('should return spaces for empty row', () => {
585
+ const text = lcd.getRowText(0);
586
+ expect(text.length).toBe(16);
587
+ expect(text.trim()).toBe('');
588
+ });
589
+ });
590
+ // ── Edge Cases ─────────────────────────────────────────────────
591
+ describe('edge cases', () => {
592
+ it('should handle multiple E transitions without crash', () => {
593
+ const ddr = 0xFF;
594
+ // Rapid E toggling
595
+ for (let i = 0; i < 100; i++) {
596
+ lcd.writePortB(0x20, ddr);
597
+ lcd.writePortA(PIN_E, ddr);
598
+ lcd.writePortA(0x00, ddr);
599
+ }
600
+ });
601
+ it('should handle function set command', () => {
602
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_FUNCTION | LCDAttachment_1.LCD_CMD_FUNCTION_LCD_2LINE | 0x10);
603
+ // Should not crash
604
+ });
605
+ it('should handle writing all character codes', () => {
606
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_DISPLAY | LCDAttachment_1.LCD_CMD_DISPLAY_ON);
607
+ for (let i = 0; i < 256; i++) {
608
+ writeCommand(lcd, LCDAttachment_1.LCD_CMD_SET_DRAM_ADDR | 0x00);
609
+ writeData(lcd, i);
610
+ lcd.updatePixels();
611
+ }
612
+ });
613
+ it('should handle isEnabled and getPriority', () => {
614
+ expect(lcd.isEnabled()).toBe(true);
615
+ expect(lcd.getPriority()).toBe(0);
616
+ });
617
+ it('should handle interrupt methods', () => {
618
+ expect(lcd.hasCA1Interrupt()).toBe(false);
619
+ expect(lcd.hasCA2Interrupt()).toBe(false);
620
+ expect(lcd.hasCB1Interrupt()).toBe(false);
621
+ expect(lcd.hasCB2Interrupt()).toBe(false);
622
+ lcd.clearInterrupts(true, true, true, true);
623
+ lcd.updateControlLines(false, false, false, false);
624
+ });
625
+ });
626
+ });
627
+ //# sourceMappingURL=LCDAttachment.test.js.map