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,656 @@
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 Storage_1 = require("../../components/IO/Storage");
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('Storage (Compact Flash in IDE Mode)', () => {
18
+ let storageCard;
19
+ beforeEach(() => {
20
+ storageCard = new Storage_1.Storage();
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
+ const storageData = storageCard.getData();
488
+ yield (0, promises_1.writeFile)(testFile, storageData);
489
+ // Verify file exists
490
+ expect((0, fs_1.existsSync)(testFile)).toBe(true);
491
+ // Verify file size is 128MB
492
+ const fileData = yield (0, promises_1.readFile)(testFile);
493
+ expect(fileData.length).toBe(128 * 1024 * 1024);
494
+ }));
495
+ it('should save complete storage contents', () => __awaiter(void 0, void 0, void 0, function* () {
496
+ // Write to multiple sectors
497
+ for (let sector = 0; sector < 5; sector++) {
498
+ storageCard.write(0x02, 1);
499
+ storageCard.write(0x03, sector);
500
+ storageCard.write(0x07, 0x30);
501
+ for (let i = 0; i < 512; i++) {
502
+ storageCard.write(0x00, (sector + i) & 0xFF);
503
+ }
504
+ }
505
+ const storageData = storageCard.getData();
506
+ yield (0, promises_1.writeFile)(testFile, storageData);
507
+ // Read file directly and verify
508
+ const fileData = yield (0, promises_1.readFile)(testFile);
509
+ // Check first sector
510
+ for (let i = 0; i < 512; i++) {
511
+ expect(fileData[i]).toBe(i & 0xFF);
512
+ }
513
+ // Check second sector
514
+ for (let i = 0; i < 512; i++) {
515
+ expect(fileData[512 + i]).toBe((1 + i) & 0xFF);
516
+ }
517
+ }));
518
+ });
519
+ describe('loadFromFile', () => {
520
+ it('should load storage data from an existing file', () => __awaiter(void 0, void 0, void 0, function* () {
521
+ // Create a test file with known data
522
+ const testData = Buffer.alloc(128 * 1024 * 1024, 0x00);
523
+ // Fill first sector with pattern
524
+ for (let i = 0; i < 512; i++) {
525
+ testData[i] = (0xAA + i) & 0xFF;
526
+ }
527
+ yield (0, promises_1.writeFile)(testFile, testData);
528
+ // Load into storage card
529
+ const fileData = yield (0, promises_1.readFile)(testFile);
530
+ storageCard.loadData(new Uint8Array(fileData));
531
+ // Verify data was loaded
532
+ storageCard.write(0x02, 1);
533
+ storageCard.write(0x03, 0);
534
+ storageCard.write(0x07, 0x20); // Read sector
535
+ for (let i = 0; i < 512; i++) {
536
+ expect(storageCard.read(0x00)).toBe((0xAA + i) & 0xFF);
537
+ }
538
+ }));
539
+ it('should handle non-existent file gracefully', () => {
540
+ // Load with null data
541
+ storageCard.loadData(null);
542
+ // Storage should remain empty (zeros)
543
+ storageCard.write(0x02, 1);
544
+ storageCard.write(0x03, 0);
545
+ storageCard.write(0x07, 0x20);
546
+ for (let i = 0; i < 512; i++) {
547
+ expect(storageCard.read(0x00)).toBe(0x00);
548
+ }
549
+ });
550
+ it('should reject file with incorrect size', () => __awaiter(void 0, void 0, void 0, function* () {
551
+ // Create a file that's too small
552
+ const smallData = Buffer.alloc(1024, 0xFF); // Only 1KB
553
+ yield (0, promises_1.writeFile)(invalidSizeFile, smallData);
554
+ const fileData = yield (0, promises_1.readFile)(invalidSizeFile);
555
+ storageCard.loadData(new Uint8Array(fileData));
556
+ // Storage should remain empty (zeros)
557
+ storageCard.write(0x02, 1);
558
+ storageCard.write(0x03, 0);
559
+ storageCard.write(0x07, 0x20);
560
+ for (let i = 0; i < 512; i++) {
561
+ expect(storageCard.read(0x00)).toBe(0x00);
562
+ }
563
+ }));
564
+ it('should load multiple sectors correctly', () => __awaiter(void 0, void 0, void 0, function* () {
565
+ const testData = Buffer.alloc(128 * 1024 * 1024, 0x00);
566
+ // Fill sectors with different patterns
567
+ for (let sector = 0; sector < 10; sector++) {
568
+ for (let i = 0; i < 512; i++) {
569
+ testData[sector * 512 + i] = (sector * 16 + i) & 0xFF;
570
+ }
571
+ }
572
+ yield (0, promises_1.writeFile)(testFile, testData);
573
+ const fileData = yield (0, promises_1.readFile)(testFile);
574
+ storageCard.loadData(new Uint8Array(fileData));
575
+ // Verify each sector
576
+ for (let sector = 0; sector < 10; sector++) {
577
+ storageCard.write(0x02, 1);
578
+ storageCard.write(0x03, sector);
579
+ storageCard.write(0x07, 0x20);
580
+ for (let i = 0; i < 512; i++) {
581
+ expect(storageCard.read(0x00)).toBe((sector * 16 + i) & 0xFF);
582
+ }
583
+ }
584
+ }));
585
+ });
586
+ describe('Round-trip persistence', () => {
587
+ it('should save and load data without loss', () => __awaiter(void 0, void 0, void 0, function* () {
588
+ // Write unique pattern to storage
589
+ for (let sector = 0; sector < 100; sector++) {
590
+ storageCard.write(0x02, 1);
591
+ storageCard.write(0x03, sector);
592
+ storageCard.write(0x07, 0x30);
593
+ for (let i = 0; i < 512; i++) {
594
+ storageCard.write(0x00, ((sector * 7 + i * 3) ^ 0x55) & 0xFF);
595
+ }
596
+ }
597
+ // Save to file
598
+ const storageData = storageCard.getData();
599
+ yield (0, promises_1.writeFile)(testFile, storageData);
600
+ // Create new storage card and load
601
+ const newStorage = new Storage_1.Storage();
602
+ const savedData = yield (0, promises_1.readFile)(testFile);
603
+ newStorage.loadData(new Uint8Array(savedData));
604
+ // Verify all sectors match
605
+ for (let sector = 0; sector < 100; sector++) {
606
+ newStorage.write(0x02, 1);
607
+ newStorage.write(0x03, sector);
608
+ newStorage.write(0x07, 0x20);
609
+ for (let i = 0; i < 512; i++) {
610
+ expect(newStorage.read(0x00)).toBe(((sector * 7 + i * 3) ^ 0x55) & 0xFF);
611
+ }
612
+ }
613
+ }));
614
+ it('should preserve data across multiple save/load cycles', () => __awaiter(void 0, void 0, void 0, function* () {
615
+ // Initial write
616
+ storageCard.write(0x02, 1);
617
+ storageCard.write(0x03, 42);
618
+ storageCard.write(0x07, 0x30);
619
+ for (let i = 0; i < 512; i++) {
620
+ storageCard.write(0x00, 0xCC);
621
+ }
622
+ yield (0, promises_1.writeFile)(testFile, storageCard.getData());
623
+ // Load and modify
624
+ const card2 = new Storage_1.Storage();
625
+ let fileData = yield (0, promises_1.readFile)(testFile);
626
+ card2.loadData(new Uint8Array(fileData));
627
+ card2.write(0x02, 1);
628
+ card2.write(0x03, 43);
629
+ card2.write(0x07, 0x30);
630
+ for (let i = 0; i < 512; i++) {
631
+ card2.write(0x00, 0xDD);
632
+ }
633
+ yield (0, promises_1.writeFile)(testFile, card2.getData());
634
+ // Load again and verify both sectors
635
+ const card3 = new Storage_1.Storage();
636
+ fileData = yield (0, promises_1.readFile)(testFile);
637
+ card3.loadData(new Uint8Array(fileData));
638
+ // Check sector 42
639
+ card3.write(0x02, 1);
640
+ card3.write(0x03, 42);
641
+ card3.write(0x07, 0x20);
642
+ expect(card3.read(0x00)).toBe(0xCC);
643
+ // Complete reading sector 42
644
+ for (let i = 1; i < 512; i++) {
645
+ card3.read(0x00);
646
+ }
647
+ // Check sector 43
648
+ card3.write(0x02, 1);
649
+ card3.write(0x03, 43);
650
+ card3.write(0x07, 0x20);
651
+ expect(card3.read(0x00)).toBe(0xDD);
652
+ }));
653
+ });
654
+ });
655
+ });
656
+ //# sourceMappingURL=Storage.test.js.map