ac6502 1.0.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 (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +261 -0
  3. package/dist/components/CPU.js +1170 -0
  4. package/dist/components/CPU.js.map +1 -0
  5. package/dist/components/Cart.js +23 -0
  6. package/dist/components/Cart.js.map +1 -0
  7. package/dist/components/IO/Empty.js +19 -0
  8. package/dist/components/IO/Empty.js.map +1 -0
  9. package/dist/components/IO/GPIOAttachments/GPIOAttachment.js +71 -0
  10. package/dist/components/IO/GPIOAttachments/GPIOAttachment.js.map +1 -0
  11. package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.js +90 -0
  12. package/dist/components/IO/GPIOAttachments/GPIOJoystickAttachment.js.map +1 -0
  13. package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.js +489 -0
  14. package/dist/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.js.map +1 -0
  15. package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.js +274 -0
  16. package/dist/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.js.map +1 -0
  17. package/dist/components/IO/GPIOCard.js +597 -0
  18. package/dist/components/IO/GPIOCard.js.map +1 -0
  19. package/dist/components/IO/InputBoard.js +19 -0
  20. package/dist/components/IO/InputBoard.js.map +1 -0
  21. package/dist/components/IO/LCDCard.js +19 -0
  22. package/dist/components/IO/LCDCard.js.map +1 -0
  23. package/dist/components/IO/RAMCard.js +63 -0
  24. package/dist/components/IO/RAMCard.js.map +1 -0
  25. package/dist/components/IO/RTCCard.js +483 -0
  26. package/dist/components/IO/RTCCard.js.map +1 -0
  27. package/dist/components/IO/SerialCard.js +282 -0
  28. package/dist/components/IO/SerialCard.js.map +1 -0
  29. package/dist/components/IO/SoundCard.js +620 -0
  30. package/dist/components/IO/SoundCard.js.map +1 -0
  31. package/dist/components/IO/StorageCard.js +428 -0
  32. package/dist/components/IO/StorageCard.js.map +1 -0
  33. package/dist/components/IO/VGACard.js +9 -0
  34. package/dist/components/IO/VGACard.js.map +1 -0
  35. package/dist/components/IO/VideoCard.js +623 -0
  36. package/dist/components/IO/VideoCard.js.map +1 -0
  37. package/dist/components/IO.js +3 -0
  38. package/dist/components/IO.js.map +1 -0
  39. package/dist/components/Machine.js +310 -0
  40. package/dist/components/Machine.js.map +1 -0
  41. package/dist/components/RAM.js +24 -0
  42. package/dist/components/RAM.js.map +1 -0
  43. package/dist/components/ROM.js +23 -0
  44. package/dist/components/ROM.js.map +1 -0
  45. package/dist/index.js +441 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/tests/CPU.test.js +1626 -0
  48. package/dist/tests/CPU.test.js.map +1 -0
  49. package/dist/tests/Cart.test.js +119 -0
  50. package/dist/tests/Cart.test.js.map +1 -0
  51. package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.js +339 -0
  52. package/dist/tests/IO/GPIOAttachments/GPIOAttachment.test.js.map +1 -0
  53. package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.js +126 -0
  54. package/dist/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.js.map +1 -0
  55. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.js +779 -0
  56. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.js.map +1 -0
  57. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.js +355 -0
  58. package/dist/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.js.map +1 -0
  59. package/dist/tests/IO/GPIOCard.test.js +503 -0
  60. package/dist/tests/IO/GPIOCard.test.js.map +1 -0
  61. package/dist/tests/IO/RAMCard.test.js +229 -0
  62. package/dist/tests/IO/RAMCard.test.js.map +1 -0
  63. package/dist/tests/IO/RTCCard.test.js +177 -0
  64. package/dist/tests/IO/RTCCard.test.js.map +1 -0
  65. package/dist/tests/IO/SerialCard.test.js +423 -0
  66. package/dist/tests/IO/SerialCard.test.js.map +1 -0
  67. package/dist/tests/IO/SoundCard.test.js +528 -0
  68. package/dist/tests/IO/SoundCard.test.js.map +1 -0
  69. package/dist/tests/IO/StorageCard.test.js +647 -0
  70. package/dist/tests/IO/StorageCard.test.js.map +1 -0
  71. package/dist/tests/IO/VideoCard.test.js +549 -0
  72. package/dist/tests/IO/VideoCard.test.js.map +1 -0
  73. package/dist/tests/Machine.test.js +383 -0
  74. package/dist/tests/Machine.test.js.map +1 -0
  75. package/dist/tests/RAM.test.js +160 -0
  76. package/dist/tests/RAM.test.js.map +1 -0
  77. package/dist/tests/ROM.test.js +123 -0
  78. package/dist/tests/ROM.test.js.map +1 -0
  79. package/jest.config.cjs +9 -0
  80. package/package.json +43 -0
  81. package/src/components/CPU.ts +1371 -0
  82. package/src/components/Cart.ts +20 -0
  83. package/src/components/IO/GPIOAttachments/GPIOAttachment.ts +189 -0
  84. package/src/components/IO/GPIOAttachments/GPIOJoystickAttachment.ts +99 -0
  85. package/src/components/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.ts +465 -0
  86. package/src/components/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.ts +287 -0
  87. package/src/components/IO/GPIOCard.ts +677 -0
  88. package/src/components/IO/RAMCard.ts +68 -0
  89. package/src/components/IO/RTCCard.ts +518 -0
  90. package/src/components/IO/SerialCard.ts +335 -0
  91. package/src/components/IO/SoundCard.ts +711 -0
  92. package/src/components/IO/StorageCard.ts +473 -0
  93. package/src/components/IO/VideoCard.ts +730 -0
  94. package/src/components/IO.ts +11 -0
  95. package/src/components/Machine.ts +364 -0
  96. package/src/components/RAM.ts +23 -0
  97. package/src/components/ROM.ts +19 -0
  98. package/src/index.ts +474 -0
  99. package/src/tests/CPU.test.ts +2045 -0
  100. package/src/tests/Cart.test.ts +149 -0
  101. package/src/tests/IO/GPIOAttachments/GPIOAttachment.test.ts +413 -0
  102. package/src/tests/IO/GPIOAttachments/GPIOJoystickAttachment.test.ts +147 -0
  103. package/src/tests/IO/GPIOAttachments/GPIOKeyboardEncoderAttachment.test.ts +961 -0
  104. package/src/tests/IO/GPIOAttachments/GPIOKeyboardMatrixAttachment.test.ts +449 -0
  105. package/src/tests/IO/GPIOCard.test.ts +644 -0
  106. package/src/tests/IO/RAMCard.test.ts +284 -0
  107. package/src/tests/IO/RTCCard.test.ts +222 -0
  108. package/src/tests/IO/SerialCard.test.ts +530 -0
  109. package/src/tests/IO/SoundCard.test.ts +659 -0
  110. package/src/tests/IO/StorageCard.test.ts +787 -0
  111. package/src/tests/IO/VideoCard.test.ts +668 -0
  112. package/src/tests/Machine.test.ts +437 -0
  113. package/src/tests/RAM.test.ts +196 -0
  114. package/src/tests/ROM.test.ts +154 -0
  115. package/tsconfig.json +12 -0
@@ -0,0 +1,647 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const StorageCard_1 = require("../../components/IO/StorageCard");
13
+ const promises_1 = require("fs/promises");
14
+ const fs_1 = require("fs");
15
+ const os_1 = require("os");
16
+ const path_1 = require("path");
17
+ describe('StorageCard (Compact Flash in IDE Mode)', () => {
18
+ let storageCard;
19
+ beforeEach(() => {
20
+ storageCard = new StorageCard_1.StorageCard();
21
+ });
22
+ describe('Initialization', () => {
23
+ it('should initialize with correct default register values', () => {
24
+ expect(storageCard.read(0x01)).toBe(0x00); // Error Register
25
+ expect(storageCard.read(0x02)).toBe(0x00); // Sector Count
26
+ expect(storageCard.read(0x03)).toBe(0x00); // LBA0
27
+ expect(storageCard.read(0x04)).toBe(0x00); // LBA1
28
+ expect(storageCard.read(0x05)).toBe(0x00); // LBA2
29
+ expect(storageCard.read(0x06)).toBe(0xE0); // LBA3 (with mode bits)
30
+ expect(storageCard.read(0x07) & 0x40).toBe(0x40); // Status (RDY bit)
31
+ });
32
+ it('should have IRQ and NMI callbacks', () => {
33
+ expect(typeof storageCard.raiseIRQ).toBe('function');
34
+ expect(typeof storageCard.raiseNMI).toBe('function');
35
+ });
36
+ it('should initialize with all storage zeroed', () => {
37
+ // Verify first sector is all zeros
38
+ storageCard.write(0x02, 1); // Sector count = 1
39
+ storageCard.write(0x03, 0); // LBA = 0
40
+ storageCard.write(0x07, 0x20); // Read sector command
41
+ for (let i = 0; i < 512; i++) {
42
+ expect(storageCard.read(0x00)).toBe(0x00);
43
+ }
44
+ });
45
+ });
46
+ describe('Register Operations', () => {
47
+ describe('Address Masking', () => {
48
+ it('should mask address to lower 3 bits', () => {
49
+ storageCard.write(0x02, 0x42); // Sector count register
50
+ expect(storageCard.read(0x0A)).toBe(0x42); // 0x0A & 0x07 = 0x02
51
+ expect(storageCard.read(0x12)).toBe(0x42); // 0x12 & 0x07 = 0x02
52
+ expect(storageCard.read(0x1A)).toBe(0x42); // 0x1A & 0x07 = 0x02
53
+ });
54
+ });
55
+ describe('LBA Registers', () => {
56
+ it('should write and read LBA0', () => {
57
+ storageCard.write(0x03, 0xAB);
58
+ expect(storageCard.read(0x03)).toBe(0xAB);
59
+ });
60
+ it('should write and read LBA1', () => {
61
+ storageCard.write(0x04, 0xCD);
62
+ expect(storageCard.read(0x04)).toBe(0xCD);
63
+ });
64
+ it('should write and read LBA2', () => {
65
+ storageCard.write(0x05, 0xEF);
66
+ expect(storageCard.read(0x05)).toBe(0xEF);
67
+ });
68
+ it('should mask LBA3 to lower 4 bits and set mode bits', () => {
69
+ storageCard.write(0x06, 0xFF);
70
+ expect(storageCard.read(0x06)).toBe(0xEF); // 0xFF & 0x0F | 0xE0
71
+ storageCard.write(0x06, 0x05);
72
+ expect(storageCard.read(0x06)).toBe(0xE5); // 0x05 & 0x0F | 0xE0
73
+ });
74
+ });
75
+ describe('Sector Count Register', () => {
76
+ it('should write and read sector count', () => {
77
+ storageCard.write(0x02, 1);
78
+ expect(storageCard.read(0x02)).toBe(1);
79
+ storageCard.write(0x02, 10);
80
+ expect(storageCard.read(0x02)).toBe(10);
81
+ });
82
+ });
83
+ describe('Feature/Error Register', () => {
84
+ it('should read error register', () => {
85
+ const error = storageCard.read(0x01);
86
+ expect(error).toBe(0x00); // No error initially
87
+ });
88
+ });
89
+ describe('Status Register', () => {
90
+ it('should have RDY bit set initially', () => {
91
+ const status = storageCard.read(0x07);
92
+ expect(status & 0x40).toBe(0x40); // RDY bit
93
+ });
94
+ it('should not have ERR bit set initially', () => {
95
+ const status = storageCard.read(0x07);
96
+ expect(status & 0x01).toBe(0x00); // ERR bit
97
+ });
98
+ it('should not have DRQ bit set initially', () => {
99
+ const status = storageCard.read(0x07);
100
+ expect(status & 0x08).toBe(0x00); // DRQ bit
101
+ });
102
+ });
103
+ });
104
+ describe('Identify Drive Command (0xEC)', () => {
105
+ it('should set DRQ flag after identify command', () => {
106
+ storageCard.write(0x07, 0xEC);
107
+ const status = storageCard.read(0x07);
108
+ expect(status & 0x08).toBe(0x08); // DRQ set
109
+ });
110
+ it('should return 512 bytes of identity data', () => {
111
+ storageCard.write(0x07, 0xEC);
112
+ let byteCount = 0;
113
+ while (storageCard.read(0x07) & 0x08) { // While DRQ is set
114
+ storageCard.read(0x00);
115
+ byteCount++;
116
+ }
117
+ expect(byteCount).toBe(512);
118
+ });
119
+ it('should clear DRQ flag after reading all identity data', () => {
120
+ storageCard.write(0x07, 0xEC);
121
+ // Read all 512 bytes
122
+ for (let i = 0; i < 512; i++) {
123
+ storageCard.read(0x00);
124
+ }
125
+ const status = storageCard.read(0x07);
126
+ expect(status & 0x08).toBe(0x00); // DRQ cleared
127
+ });
128
+ it('should contain valid identity data', () => {
129
+ storageCard.write(0x07, 0xEC);
130
+ const identity = [];
131
+ for (let i = 0; i < 512; i++) {
132
+ identity.push(storageCard.read(0x00));
133
+ }
134
+ // Check general configuration (word 0)
135
+ expect(identity[0]).toBe(0x84);
136
+ expect(identity[1]).toBe(0x8A);
137
+ // Check serial number starts at byte 20
138
+ const serial = String.fromCharCode(...identity.slice(20, 40));
139
+ expect(serial).toBe('ACWD6502EMUCF1010101');
140
+ // Check firmware revision starts at byte 46
141
+ const firmware = String.fromCharCode(...identity.slice(46, 54));
142
+ expect(firmware).toBe('1.0 ');
143
+ // Check model number starts at byte 54
144
+ const model = String.fromCharCode(...identity.slice(54, 94));
145
+ expect(model).toContain('ACWD6502EMUCF');
146
+ });
147
+ });
148
+ describe('Read Sector Command (0x20/0x21)', () => {
149
+ it('should set DRQ flag after read sector command', () => {
150
+ storageCard.write(0x02, 1); // 1 sector
151
+ storageCard.write(0x03, 0); // LBA = 0
152
+ storageCard.write(0x07, 0x20); // Read sector
153
+ const status = storageCard.read(0x07);
154
+ expect(status & 0x08).toBe(0x08); // DRQ set
155
+ });
156
+ it('should read a single sector', () => {
157
+ storageCard.write(0x02, 1);
158
+ storageCard.write(0x03, 0);
159
+ storageCard.write(0x07, 0x20);
160
+ let byteCount = 0;
161
+ while (storageCard.read(0x07) & 0x08) {
162
+ storageCard.read(0x00);
163
+ byteCount++;
164
+ }
165
+ expect(byteCount).toBe(512);
166
+ });
167
+ it('should clear DRQ after reading all sector data', () => {
168
+ storageCard.write(0x02, 1);
169
+ storageCard.write(0x03, 0);
170
+ storageCard.write(0x07, 0x20);
171
+ for (let i = 0; i < 512; i++) {
172
+ storageCard.read(0x00);
173
+ }
174
+ expect(storageCard.read(0x07) & 0x08).toBe(0x00);
175
+ });
176
+ it('should report error for invalid sector (too high)', () => {
177
+ storageCard.write(0x02, 1);
178
+ storageCard.write(0x03, 0xFF); // LBA = 0x0FFFFFFF (invalid)
179
+ storageCard.write(0x04, 0xFF);
180
+ storageCard.write(0x05, 0xFF);
181
+ storageCard.write(0x06, 0xFF);
182
+ storageCard.write(0x07, 0x20);
183
+ const status = storageCard.read(0x07);
184
+ const error = storageCard.read(0x01);
185
+ expect(status & 0x01).toBe(0x01); // ERR bit set
186
+ expect(error & 0x10).toBe(0x10); // IDNF error
187
+ });
188
+ it('should read multiple sectors', () => {
189
+ storageCard.write(0x02, 3); // 3 sectors
190
+ storageCard.write(0x03, 0);
191
+ storageCard.write(0x07, 0x20);
192
+ let byteCount = 0;
193
+ while (storageCard.read(0x07) & 0x08) {
194
+ storageCard.read(0x00);
195
+ byteCount++;
196
+ }
197
+ expect(byteCount).toBe(512 * 3);
198
+ });
199
+ it('should work with command 0x21', () => {
200
+ storageCard.write(0x02, 1);
201
+ storageCard.write(0x03, 0);
202
+ storageCard.write(0x07, 0x21); // Alternate read command
203
+ expect(storageCard.read(0x07) & 0x08).toBe(0x08); // DRQ set
204
+ });
205
+ });
206
+ describe('Write Sector Command (0x30/0x31)', () => {
207
+ it('should set DRQ flag after write sector command', () => {
208
+ storageCard.write(0x02, 1);
209
+ storageCard.write(0x03, 0);
210
+ storageCard.write(0x07, 0x30); // Write sector
211
+ const status = storageCard.read(0x07);
212
+ expect(status & 0x08).toBe(0x08); // DRQ set
213
+ });
214
+ it('should write and read back a single sector', () => {
215
+ // Write sector 0
216
+ storageCard.write(0x02, 1);
217
+ storageCard.write(0x03, 0);
218
+ storageCard.write(0x07, 0x30);
219
+ for (let i = 0; i < 512; i++) {
220
+ storageCard.write(0x00, i & 0xFF);
221
+ }
222
+ // Read sector 0
223
+ storageCard.write(0x02, 1);
224
+ storageCard.write(0x03, 0);
225
+ storageCard.write(0x07, 0x20);
226
+ for (let i = 0; i < 512; i++) {
227
+ expect(storageCard.read(0x00)).toBe(i & 0xFF);
228
+ }
229
+ });
230
+ it('should clear DRQ after writing all sector data', () => {
231
+ storageCard.write(0x02, 1);
232
+ storageCard.write(0x03, 0);
233
+ storageCard.write(0x07, 0x30);
234
+ for (let i = 0; i < 512; i++) {
235
+ storageCard.write(0x00, 0xFF);
236
+ }
237
+ expect(storageCard.read(0x07) & 0x08).toBe(0x00);
238
+ });
239
+ it('should report error for invalid sector', () => {
240
+ storageCard.write(0x02, 1);
241
+ storageCard.write(0x03, 0xFF);
242
+ storageCard.write(0x04, 0xFF);
243
+ storageCard.write(0x05, 0xFF);
244
+ storageCard.write(0x06, 0xFF);
245
+ storageCard.write(0x07, 0x30);
246
+ const status = storageCard.read(0x07);
247
+ expect(status & 0x01).toBe(0x01); // ERR bit
248
+ });
249
+ it('should write multiple sectors', () => {
250
+ storageCard.write(0x02, 2);
251
+ storageCard.write(0x03, 0);
252
+ storageCard.write(0x07, 0x30);
253
+ for (let i = 0; i < 512 * 2; i++) {
254
+ storageCard.write(0x00, 0xAA);
255
+ }
256
+ // Verify data was written
257
+ storageCard.write(0x02, 2);
258
+ storageCard.write(0x03, 0);
259
+ storageCard.write(0x07, 0x20);
260
+ for (let i = 0; i < 512 * 2; i++) {
261
+ expect(storageCard.read(0x00)).toBe(0xAA);
262
+ }
263
+ });
264
+ it('should work with command 0x31', () => {
265
+ storageCard.write(0x02, 1);
266
+ storageCard.write(0x03, 0);
267
+ storageCard.write(0x07, 0x31); // Alternate write command
268
+ expect(storageCard.read(0x07) & 0x08).toBe(0x08); // DRQ set
269
+ });
270
+ });
271
+ describe('Erase Sector Command (0xC0)', () => {
272
+ it('should erase a sector', () => {
273
+ // First write some data
274
+ storageCard.write(0x02, 1);
275
+ storageCard.write(0x03, 5); // Sector 5
276
+ storageCard.write(0x07, 0x30);
277
+ for (let i = 0; i < 512; i++) {
278
+ storageCard.write(0x00, 0xFF);
279
+ }
280
+ // Erase the sector
281
+ storageCard.write(0x02, 1);
282
+ storageCard.write(0x03, 5);
283
+ storageCard.write(0x07, 0xC0);
284
+ // Read back and verify zeros
285
+ storageCard.write(0x02, 1);
286
+ storageCard.write(0x03, 5);
287
+ storageCard.write(0x07, 0x20);
288
+ for (let i = 0; i < 512; i++) {
289
+ expect(storageCard.read(0x00)).toBe(0x00);
290
+ }
291
+ });
292
+ it('should report error for invalid sector', () => {
293
+ storageCard.write(0x02, 1);
294
+ storageCard.write(0x03, 0xFF);
295
+ storageCard.write(0x04, 0xFF);
296
+ storageCard.write(0x05, 0xFF);
297
+ storageCard.write(0x06, 0xFF);
298
+ storageCard.write(0x07, 0xC0);
299
+ const status = storageCard.read(0x07);
300
+ expect(status & 0x01).toBe(0x01); // ERR bit
301
+ });
302
+ it('should not set DRQ flag', () => {
303
+ storageCard.write(0x02, 1);
304
+ storageCard.write(0x03, 0);
305
+ storageCard.write(0x07, 0xC0);
306
+ expect(storageCard.read(0x07) & 0x08).toBe(0x00); // DRQ not set
307
+ });
308
+ });
309
+ describe('Set Features Command (0xEF)', () => {
310
+ it('should accept command without error', () => {
311
+ storageCard.write(0x07, 0xEF);
312
+ const status = storageCard.read(0x07);
313
+ expect(status & 0x01).toBe(0x00); // No error
314
+ });
315
+ it('should not set DRQ flag', () => {
316
+ storageCard.write(0x07, 0xEF);
317
+ expect(storageCard.read(0x07) & 0x08).toBe(0x00);
318
+ });
319
+ });
320
+ describe('Unsupported Commands', () => {
321
+ it('should report error for unsupported command', () => {
322
+ storageCard.write(0x07, 0xFF); // Invalid command
323
+ const status = storageCard.read(0x07);
324
+ const error = storageCard.read(0x01);
325
+ expect(status & 0x01).toBe(0x01); // ERR bit
326
+ expect(error & 0x04).toBe(0x04); // ABRT error
327
+ });
328
+ it('should not set DRQ for unsupported command', () => {
329
+ storageCard.write(0x07, 0x99);
330
+ expect(storageCard.read(0x07) & 0x08).toBe(0x00);
331
+ });
332
+ });
333
+ describe('LBA Addressing', () => {
334
+ it('should correctly calculate 28-bit LBA address', () => {
335
+ // Write pattern to sector at LBA 0x00000045 (using valid low address)
336
+ storageCard.write(0x02, 1);
337
+ storageCard.write(0x03, 0x45); // LBA0
338
+ storageCard.write(0x04, 0x00); // LBA1
339
+ storageCard.write(0x05, 0x00); // LBA2
340
+ storageCard.write(0x06, 0x00); // LBA3 (0x00 & 0x0F | 0xE0 = 0xE0)
341
+ storageCard.write(0x07, 0x30);
342
+ for (let i = 0; i < 512; i++) {
343
+ storageCard.write(0x00, 0xCC);
344
+ }
345
+ // Read back
346
+ storageCard.write(0x02, 1);
347
+ storageCard.write(0x03, 0x45);
348
+ storageCard.write(0x04, 0x00);
349
+ storageCard.write(0x05, 0x00);
350
+ storageCard.write(0x06, 0x00);
351
+ storageCard.write(0x07, 0x20);
352
+ expect(storageCard.read(0x00)).toBe(0xCC);
353
+ });
354
+ it('should isolate different sectors', () => {
355
+ // Write to sector 0
356
+ storageCard.write(0x02, 1);
357
+ storageCard.write(0x03, 0);
358
+ storageCard.write(0x07, 0x30);
359
+ for (let i = 0; i < 512; i++) {
360
+ storageCard.write(0x00, 0x11);
361
+ }
362
+ // Write to sector 1
363
+ storageCard.write(0x02, 1);
364
+ storageCard.write(0x03, 1);
365
+ storageCard.write(0x07, 0x30);
366
+ for (let i = 0; i < 512; i++) {
367
+ storageCard.write(0x00, 0x22);
368
+ }
369
+ // Read sector 0 (must read all bytes to complete transfer)
370
+ storageCard.write(0x02, 1);
371
+ storageCard.write(0x03, 0);
372
+ storageCard.write(0x07, 0x20);
373
+ const firstByte0 = storageCard.read(0x00);
374
+ for (let i = 1; i < 512; i++) {
375
+ storageCard.read(0x00);
376
+ }
377
+ expect(firstByte0).toBe(0x11);
378
+ // Read sector 1 (must read all bytes to complete transfer)
379
+ storageCard.write(0x02, 1);
380
+ storageCard.write(0x03, 1);
381
+ storageCard.write(0x07, 0x20);
382
+ const firstByte1 = storageCard.read(0x00);
383
+ for (let i = 1; i < 512; i++) {
384
+ storageCard.read(0x00);
385
+ }
386
+ expect(firstByte1).toBe(0x22);
387
+ });
388
+ });
389
+ describe('Error Conditions', () => {
390
+ it('should abort if command issued while transferring', () => {
391
+ storageCard.write(0x02, 1);
392
+ storageCard.write(0x03, 0);
393
+ storageCard.write(0x07, 0x20); // Start read
394
+ // Issue another command while first is active
395
+ storageCard.write(0x07, 0x20);
396
+ const status = storageCard.read(0x07);
397
+ const error = storageCard.read(0x01);
398
+ expect(status & 0x01).toBe(0x01); // ERR bit
399
+ expect(error & 0x04).toBe(0x04); // ABRT error
400
+ });
401
+ it('should abort if command issued while identifying', () => {
402
+ storageCard.write(0x07, 0xEC); // Start identify
403
+ // Issue another command
404
+ storageCard.write(0x07, 0x20);
405
+ const status = storageCard.read(0x07);
406
+ const error = storageCard.read(0x01);
407
+ expect(status & 0x01).toBe(0x01); // ERR bit
408
+ expect(error & 0x04).toBe(0x04); // ABRT error
409
+ });
410
+ it('should clear error flags on new valid command', () => {
411
+ // Trigger an error
412
+ storageCard.write(0x07, 0xFF); // Invalid command
413
+ expect(storageCard.read(0x07) & 0x01).toBe(0x01);
414
+ // Issue valid command
415
+ storageCard.write(0x02, 1);
416
+ storageCard.write(0x03, 0);
417
+ storageCard.write(0x07, 0x20);
418
+ const status = storageCard.read(0x07);
419
+ expect(status & 0x01).toBe(0x00); // ERR cleared
420
+ });
421
+ });
422
+ describe('Reset', () => {
423
+ it('should reset all registers to default values', () => {
424
+ // Modify registers
425
+ storageCard.write(0x02, 0x42);
426
+ storageCard.write(0x03, 0x11);
427
+ storageCard.write(0x04, 0x22);
428
+ storageCard.write(0x05, 0x33);
429
+ storageCard.reset(true);
430
+ expect(storageCard.read(0x01)).toBe(0x00); // Error
431
+ expect(storageCard.read(0x02)).toBe(0x00); // Sector Count
432
+ expect(storageCard.read(0x03)).toBe(0x00); // LBA0
433
+ expect(storageCard.read(0x04)).toBe(0x00); // LBA1
434
+ expect(storageCard.read(0x05)).toBe(0x00); // LBA2
435
+ expect(storageCard.read(0x06)).toBe(0xE0); // LBA3
436
+ expect(storageCard.read(0x07) & 0x40).toBe(0x40); // Status RDY
437
+ });
438
+ it('should clear transfer state', () => {
439
+ storageCard.write(0x02, 1);
440
+ storageCard.write(0x03, 0);
441
+ storageCard.write(0x07, 0x20); // Start transfer
442
+ storageCard.reset(true);
443
+ expect(storageCard.read(0x07) & 0x08).toBe(0x00); // DRQ cleared
444
+ });
445
+ });
446
+ describe('Tick', () => {
447
+ it('should have tick method that does nothing', () => {
448
+ expect(() => {
449
+ storageCard.tick(1000000);
450
+ }).not.toThrow();
451
+ });
452
+ });
453
+ describe('IO Interface Implementation', () => {
454
+ it('should implement IO interface methods', () => {
455
+ expect(typeof storageCard.read).toBe('function');
456
+ expect(typeof storageCard.write).toBe('function');
457
+ expect(typeof storageCard.tick).toBe('function');
458
+ expect(typeof storageCard.reset).toBe('function');
459
+ expect(typeof storageCard.raiseIRQ).toBe('function');
460
+ expect(typeof storageCard.raiseNMI).toBe('function');
461
+ });
462
+ });
463
+ describe('Storage Persistence', () => {
464
+ const testDir = (0, os_1.tmpdir)();
465
+ const testFile = (0, path_1.join)(testDir, `storage-test-${Date.now()}.bin`);
466
+ const invalidSizeFile = (0, path_1.join)(testDir, `storage-invalid-${Date.now()}.bin`);
467
+ const nonExistentFile = (0, path_1.join)(testDir, `storage-nonexistent-${Date.now()}.bin`);
468
+ afterEach(() => __awaiter(void 0, void 0, void 0, function* () {
469
+ // Cleanup test files
470
+ const filesToClean = [testFile, invalidSizeFile, nonExistentFile];
471
+ for (const file of filesToClean) {
472
+ if ((0, fs_1.existsSync)(file)) {
473
+ yield (0, promises_1.unlink)(file);
474
+ }
475
+ }
476
+ }));
477
+ describe('saveToFile', () => {
478
+ it('should save storage data to a file', () => __awaiter(void 0, void 0, void 0, function* () {
479
+ // Write some known data to storage
480
+ storageCard.write(0x02, 1); // 1 sector
481
+ storageCard.write(0x03, 0); // LBA = 0
482
+ storageCard.write(0x07, 0x30); // Write sector command
483
+ for (let i = 0; i < 512; i++) {
484
+ storageCard.write(0x00, i & 0xFF);
485
+ }
486
+ // Save to file
487
+ yield storageCard.saveToFile(testFile);
488
+ // Verify file exists
489
+ expect((0, fs_1.existsSync)(testFile)).toBe(true);
490
+ // Verify file size is 128MB
491
+ const fileData = yield (0, promises_1.readFile)(testFile);
492
+ expect(fileData.length).toBe(128 * 1024 * 1024);
493
+ }));
494
+ it('should save complete storage contents', () => __awaiter(void 0, void 0, void 0, function* () {
495
+ // Write to multiple sectors
496
+ for (let sector = 0; sector < 5; sector++) {
497
+ storageCard.write(0x02, 1);
498
+ storageCard.write(0x03, sector);
499
+ storageCard.write(0x07, 0x30);
500
+ for (let i = 0; i < 512; i++) {
501
+ storageCard.write(0x00, (sector + i) & 0xFF);
502
+ }
503
+ }
504
+ yield storageCard.saveToFile(testFile);
505
+ // Read file directly and verify
506
+ const fileData = yield (0, promises_1.readFile)(testFile);
507
+ // Check first sector
508
+ for (let i = 0; i < 512; i++) {
509
+ expect(fileData[i]).toBe(i & 0xFF);
510
+ }
511
+ // Check second sector
512
+ for (let i = 0; i < 512; i++) {
513
+ expect(fileData[512 + i]).toBe((1 + i) & 0xFF);
514
+ }
515
+ }));
516
+ });
517
+ describe('loadFromFile', () => {
518
+ it('should load storage data from an existing file', () => __awaiter(void 0, void 0, void 0, function* () {
519
+ // Create a test file with known data
520
+ const testData = Buffer.alloc(128 * 1024 * 1024, 0x00);
521
+ // Fill first sector with pattern
522
+ for (let i = 0; i < 512; i++) {
523
+ testData[i] = (0xAA + i) & 0xFF;
524
+ }
525
+ yield (0, promises_1.writeFile)(testFile, testData);
526
+ // Load into storage card
527
+ yield storageCard.loadFromFile(testFile);
528
+ // Verify data was loaded
529
+ storageCard.write(0x02, 1);
530
+ storageCard.write(0x03, 0);
531
+ storageCard.write(0x07, 0x20); // Read sector
532
+ for (let i = 0; i < 512; i++) {
533
+ expect(storageCard.read(0x00)).toBe((0xAA + i) & 0xFF);
534
+ }
535
+ }));
536
+ it('should handle non-existent file gracefully', () => __awaiter(void 0, void 0, void 0, function* () {
537
+ // Try to load from a file that doesn't exist
538
+ yield expect(storageCard.loadFromFile(nonExistentFile)).resolves.not.toThrow();
539
+ // Storage should remain empty (zeros)
540
+ storageCard.write(0x02, 1);
541
+ storageCard.write(0x03, 0);
542
+ storageCard.write(0x07, 0x20);
543
+ for (let i = 0; i < 512; i++) {
544
+ expect(storageCard.read(0x00)).toBe(0x00);
545
+ }
546
+ }));
547
+ it('should reject file with incorrect size', () => __awaiter(void 0, void 0, void 0, function* () {
548
+ // Create a file that's too small
549
+ const smallData = Buffer.alloc(1024, 0xFF); // Only 1KB
550
+ yield (0, promises_1.writeFile)(invalidSizeFile, smallData);
551
+ yield storageCard.loadFromFile(invalidSizeFile);
552
+ // Storage should remain empty (zeros)
553
+ storageCard.write(0x02, 1);
554
+ storageCard.write(0x03, 0);
555
+ storageCard.write(0x07, 0x20);
556
+ for (let i = 0; i < 512; i++) {
557
+ expect(storageCard.read(0x00)).toBe(0x00);
558
+ }
559
+ }));
560
+ it('should load multiple sectors correctly', () => __awaiter(void 0, void 0, void 0, function* () {
561
+ const testData = Buffer.alloc(128 * 1024 * 1024, 0x00);
562
+ // Fill sectors with different patterns
563
+ for (let sector = 0; sector < 10; sector++) {
564
+ for (let i = 0; i < 512; i++) {
565
+ testData[sector * 512 + i] = (sector * 16 + i) & 0xFF;
566
+ }
567
+ }
568
+ yield (0, promises_1.writeFile)(testFile, testData);
569
+ yield storageCard.loadFromFile(testFile);
570
+ // Verify each sector
571
+ for (let sector = 0; sector < 10; sector++) {
572
+ storageCard.write(0x02, 1);
573
+ storageCard.write(0x03, sector);
574
+ storageCard.write(0x07, 0x20);
575
+ for (let i = 0; i < 512; i++) {
576
+ expect(storageCard.read(0x00)).toBe((sector * 16 + i) & 0xFF);
577
+ }
578
+ }
579
+ }));
580
+ });
581
+ describe('Round-trip persistence', () => {
582
+ it('should save and load data without loss', () => __awaiter(void 0, void 0, void 0, function* () {
583
+ // Write unique pattern to storage
584
+ for (let sector = 0; sector < 100; sector++) {
585
+ storageCard.write(0x02, 1);
586
+ storageCard.write(0x03, sector);
587
+ storageCard.write(0x07, 0x30);
588
+ for (let i = 0; i < 512; i++) {
589
+ storageCard.write(0x00, ((sector * 7 + i * 3) ^ 0x55) & 0xFF);
590
+ }
591
+ }
592
+ // Save to file
593
+ yield storageCard.saveToFile(testFile);
594
+ // Create new storage card and load
595
+ const newStorageCard = new StorageCard_1.StorageCard();
596
+ yield newStorageCard.loadFromFile(testFile);
597
+ // Verify all sectors match
598
+ for (let sector = 0; sector < 100; sector++) {
599
+ newStorageCard.write(0x02, 1);
600
+ newStorageCard.write(0x03, sector);
601
+ newStorageCard.write(0x07, 0x20);
602
+ for (let i = 0; i < 512; i++) {
603
+ expect(newStorageCard.read(0x00)).toBe(((sector * 7 + i * 3) ^ 0x55) & 0xFF);
604
+ }
605
+ }
606
+ }));
607
+ it('should preserve data across multiple save/load cycles', () => __awaiter(void 0, void 0, void 0, function* () {
608
+ // Initial write
609
+ storageCard.write(0x02, 1);
610
+ storageCard.write(0x03, 42);
611
+ storageCard.write(0x07, 0x30);
612
+ for (let i = 0; i < 512; i++) {
613
+ storageCard.write(0x00, 0xCC);
614
+ }
615
+ yield storageCard.saveToFile(testFile);
616
+ // Load and modify
617
+ const card2 = new StorageCard_1.StorageCard();
618
+ yield card2.loadFromFile(testFile);
619
+ card2.write(0x02, 1);
620
+ card2.write(0x03, 43);
621
+ card2.write(0x07, 0x30);
622
+ for (let i = 0; i < 512; i++) {
623
+ card2.write(0x00, 0xDD);
624
+ }
625
+ yield card2.saveToFile(testFile);
626
+ // Load again and verify both sectors
627
+ const card3 = new StorageCard_1.StorageCard();
628
+ yield card3.loadFromFile(testFile);
629
+ // Check sector 42
630
+ card3.write(0x02, 1);
631
+ card3.write(0x03, 42);
632
+ card3.write(0x07, 0x20);
633
+ expect(card3.read(0x00)).toBe(0xCC);
634
+ // Complete reading sector 42
635
+ for (let i = 1; i < 512; i++) {
636
+ card3.read(0x00);
637
+ }
638
+ // Check sector 43
639
+ card3.write(0x02, 1);
640
+ card3.write(0x03, 43);
641
+ card3.write(0x07, 0x20);
642
+ expect(card3.read(0x00)).toBe(0xDD);
643
+ }));
644
+ });
645
+ });
646
+ });
647
+ //# sourceMappingURL=StorageCard.test.js.map