@aptre/v86 0.5.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 (111) hide show
  1. package/LICENSE +22 -0
  2. package/LICENSE.MIT +22 -0
  3. package/Readme.md +237 -0
  4. package/dist/v86.browser.js +26666 -0
  5. package/dist/v86.browser.js.map +7 -0
  6. package/dist/v86.js +26632 -0
  7. package/dist/v86.js.map +7 -0
  8. package/gen/generate_analyzer.ts +512 -0
  9. package/gen/generate_interpreter.ts +522 -0
  10. package/gen/generate_jit.ts +624 -0
  11. package/gen/rust_ast.ts +107 -0
  12. package/gen/util.ts +35 -0
  13. package/gen/x86_table.ts +1836 -0
  14. package/lib/9p.ts +1547 -0
  15. package/lib/filesystem.ts +1879 -0
  16. package/lib/marshall.ts +168 -0
  17. package/lib/softfloat/softfloat.c +32501 -0
  18. package/lib/zstd/zstddeclib.c +13520 -0
  19. package/package.json +75 -0
  20. package/src/acpi.ts +267 -0
  21. package/src/browser/dummy_screen.ts +106 -0
  22. package/src/browser/fake_network.ts +1771 -0
  23. package/src/browser/fetch_network.ts +361 -0
  24. package/src/browser/filestorage.ts +124 -0
  25. package/src/browser/inbrowser_network.ts +57 -0
  26. package/src/browser/keyboard.ts +564 -0
  27. package/src/browser/main.ts +3415 -0
  28. package/src/browser/mouse.ts +255 -0
  29. package/src/browser/network.ts +142 -0
  30. package/src/browser/print_stats.ts +336 -0
  31. package/src/browser/screen.ts +978 -0
  32. package/src/browser/serial.ts +316 -0
  33. package/src/browser/speaker.ts +1223 -0
  34. package/src/browser/starter.ts +1688 -0
  35. package/src/browser/wisp_network.ts +332 -0
  36. package/src/browser/worker_bus.ts +64 -0
  37. package/src/buffer.ts +652 -0
  38. package/src/bus.ts +78 -0
  39. package/src/const.ts +128 -0
  40. package/src/cpu.ts +2891 -0
  41. package/src/dma.ts +474 -0
  42. package/src/elf.ts +251 -0
  43. package/src/floppy.ts +1778 -0
  44. package/src/ide.ts +3455 -0
  45. package/src/io.ts +504 -0
  46. package/src/iso9660.ts +317 -0
  47. package/src/kernel.ts +250 -0
  48. package/src/lib.ts +645 -0
  49. package/src/log.ts +149 -0
  50. package/src/main.ts +199 -0
  51. package/src/ne2k.ts +1589 -0
  52. package/src/pci.ts +815 -0
  53. package/src/pit.ts +406 -0
  54. package/src/ps2.ts +820 -0
  55. package/src/rtc.ts +537 -0
  56. package/src/rust/analysis.rs +101 -0
  57. package/src/rust/codegen.rs +2660 -0
  58. package/src/rust/config.rs +3 -0
  59. package/src/rust/control_flow.rs +425 -0
  60. package/src/rust/cpu/apic.rs +658 -0
  61. package/src/rust/cpu/arith.rs +1207 -0
  62. package/src/rust/cpu/call_indirect.rs +2 -0
  63. package/src/rust/cpu/cpu.rs +4501 -0
  64. package/src/rust/cpu/fpu.rs +923 -0
  65. package/src/rust/cpu/global_pointers.rs +112 -0
  66. package/src/rust/cpu/instructions.rs +2486 -0
  67. package/src/rust/cpu/instructions_0f.rs +5261 -0
  68. package/src/rust/cpu/ioapic.rs +316 -0
  69. package/src/rust/cpu/memory.rs +351 -0
  70. package/src/rust/cpu/misc_instr.rs +613 -0
  71. package/src/rust/cpu/mod.rs +16 -0
  72. package/src/rust/cpu/modrm.rs +133 -0
  73. package/src/rust/cpu/pic.rs +402 -0
  74. package/src/rust/cpu/sse_instr.rs +361 -0
  75. package/src/rust/cpu/string.rs +701 -0
  76. package/src/rust/cpu/vga.rs +175 -0
  77. package/src/rust/cpu_context.rs +69 -0
  78. package/src/rust/dbg.rs +98 -0
  79. package/src/rust/gen/analyzer.rs +3807 -0
  80. package/src/rust/gen/analyzer0f.rs +3992 -0
  81. package/src/rust/gen/interpreter.rs +4447 -0
  82. package/src/rust/gen/interpreter0f.rs +5404 -0
  83. package/src/rust/gen/jit.rs +5080 -0
  84. package/src/rust/gen/jit0f.rs +5547 -0
  85. package/src/rust/gen/mod.rs +14 -0
  86. package/src/rust/jit.rs +2443 -0
  87. package/src/rust/jit_instructions.rs +7881 -0
  88. package/src/rust/js_api.rs +6 -0
  89. package/src/rust/leb.rs +46 -0
  90. package/src/rust/lib.rs +29 -0
  91. package/src/rust/modrm.rs +330 -0
  92. package/src/rust/opstats.rs +249 -0
  93. package/src/rust/page.rs +15 -0
  94. package/src/rust/paging.rs +25 -0
  95. package/src/rust/prefix.rs +15 -0
  96. package/src/rust/profiler.rs +155 -0
  97. package/src/rust/regs.rs +38 -0
  98. package/src/rust/softfloat.rs +286 -0
  99. package/src/rust/state_flags.rs +27 -0
  100. package/src/rust/wasmgen/mod.rs +2 -0
  101. package/src/rust/wasmgen/wasm_builder.rs +1047 -0
  102. package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
  103. package/src/rust/zstd.rs +105 -0
  104. package/src/sb16.ts +1928 -0
  105. package/src/state.ts +359 -0
  106. package/src/uart.ts +472 -0
  107. package/src/vga.ts +2791 -0
  108. package/src/virtio.ts +1756 -0
  109. package/src/virtio_balloon.ts +273 -0
  110. package/src/virtio_console.ts +372 -0
  111. package/src/virtio_net.ts +326 -0
package/src/ide.ts ADDED
@@ -0,0 +1,3455 @@
1
+ declare let DEBUG: boolean
2
+
3
+ import { LOG_DISK } from './const.js'
4
+ import { h } from './lib.js'
5
+ import { dbg_assert, dbg_log } from './log.js'
6
+ import {
7
+ CMOS_BIOS_DISKTRANSFLAG,
8
+ CMOS_DISK_DATA,
9
+ CMOS_DISK_DRIVE1_CYL,
10
+ CMOS_DISK_DRIVE2_CYL,
11
+ } from './rtc.js'
12
+
13
+ import { BusConnector } from './bus.js'
14
+ import { IO } from './io.js'
15
+ import { PCI } from './pci.js'
16
+
17
+ // Minimal interface for the buffer objects IDE uses (SyncBuffer, AsyncXHRBuffer, etc.)
18
+ interface IDEDiskBuffer {
19
+ byteLength: number
20
+ get(start: number, len: number, fn: (data: Uint8Array) => void): void
21
+ set(start: number, slice: Uint8Array, fn: () => void): void
22
+
23
+ set_state(state: any[]): void
24
+ }
25
+
26
+ // Minimal interface for the RTC fields IDE uses.
27
+ interface IDERTC {
28
+ cmos_read(index: number): number
29
+ cmos_write(index: number, value: number): void
30
+ }
31
+
32
+ // Minimal interface for CPU fields IDE uses.
33
+ interface IDECpu {
34
+ io: IO
35
+ mem8: Uint8Array
36
+ devices: {
37
+ pci: PCI
38
+ rtc: IDERTC
39
+ }
40
+ device_raise_irq(irq: number): void
41
+ device_lower_irq(irq: number): void
42
+ read8(addr: number): number
43
+ read16(addr: number): number
44
+ read32s(addr: number): number
45
+ write_blob(blob: Uint8Array, addr: number): void
46
+ }
47
+
48
+ // IDE device configuration entry
49
+ interface IDEDeviceConfig {
50
+ buffer?: IDEDiskBuffer
51
+ is_cdrom?: boolean
52
+ }
53
+
54
+ // ide_config: [ [primary-master, primary-slave], [secondary-master, secondary-slave] ]
55
+ type IDEConfig = [
56
+ [IDEDeviceConfig | undefined, IDEDeviceConfig | undefined],
57
+ [IDEDeviceConfig | undefined, IDEDeviceConfig | undefined],
58
+ ]
59
+
60
+ // ATA/ATAPI-6/8 IDE Controller
61
+ //
62
+ // References
63
+ // - [ATA8-ACS]
64
+ // ATA/ATAPI Command Set - 3 (ACS-3) (Rev. 5, Oct. 28, 2013)
65
+ // https://read.seas.harvard.edu/cs161/2019/pdf/ata-atapi-8.pdf
66
+ // - [ATA-6]
67
+ // AT Attachment with Packet Interface - 6 (ATA/ATAPI-6) (Rev. 3a; Dec. 14, 2001)
68
+ // https://technion-csl.github.io/ose/readings/hardware/ATA-d1410r3a.pdf
69
+ // - [CD-SCSI-2]
70
+ // PROPOSAL FOR CD-ROM IN SCSI-2 (X3T9.2/87) (Rev. 0, Jun. 30, 1987)
71
+ // https://www.t10.org/ftp/x3t9.2/document.87/87-106r0.txt
72
+ // https://www.t10.org/ftp/x3t9.2/document.87/87-106r1.txt (errata to r0)
73
+ // - [SAM-3]
74
+ // SCSI Architecture Model - 3 (SAM-3) (Sep. 21, 2004)
75
+ // https://dn790004.ca.archive.org/0/items/SCSISpecificationDocumentsSCSIDocuments/SCSI%20Architecture%20Model/SCSI%20Architecture%20Model%203%20rev%2014.pdf
76
+ // - [SPC-3]
77
+ // SCSI Primary Commands - 3 (SPC-3) (July 20, 2008)
78
+ // https://www.t10.org/ftp/t10/document.08/08-309r0.pdf
79
+ // - [MMC-3]
80
+ // SCSI Multimedia Commands - 3 (MMC-3) (Rev. 10g, Nov. 12, 2001)
81
+ // https://ia902808.us.archive.org/33/items/mmc3r10g/mmc3r10g.pdf
82
+ // - [MMC-2]
83
+ // Packet Commands for C/DVD Devices (1997)
84
+ // https://www.t10.org/ftp/t10/document.97/97-108r0.pdf
85
+ // - [BMI-1]
86
+ // Programming Interface for Bus Master IDE Controller, Revision 1.0, 5/16/94
87
+ // https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/u9proj/idems100.pdf
88
+ // - [SFF-8020]
89
+ // ATA Packet Interface for CD-ROMs (Rev. 1.2, Feb. 12, 1994)
90
+ // https://dn790009.ca.archive.org/0/items/SCSISpecificationDocumentsATAATAPI/SFF-8020_%20ATA%20Packet%20Interface%20for%20CD-ROMs%20-%20SFF.pdf
91
+
92
+ const CDROM_SECTOR_SIZE = 2048
93
+ const HD_SECTOR_SIZE = 512
94
+
95
+ const BUS_MASTER_BASE = 0xb400
96
+
97
+ // Per-channel ATA register offsets, legend:
98
+ // (*1*) Control block register (BAR1/3), else: Command block register (BAR0/2)
99
+ // Read-only registers:
100
+ const ATA_REG_ERROR = 0x01 // Error register, see [ATA-6] 7.9
101
+ const ATA_REG_STATUS = 0x07 // Status register, see [ATA-6] 7.15
102
+ const ATA_REG_ALT_STATUS = 0x00 // (*1*) Alternate Status register, see [ATA-6] 7.3
103
+ // Read-/writable registers:
104
+ const ATA_REG_DATA = 0x00 // Data register, see [ATA-6] 7.6
105
+ const ATA_REG_SECTOR = 0x02 // Sector Count register, see [ATA-6] 7.14
106
+ const ATA_REG_LBA_LOW = 0x03 // LBA Low register, see [ATA-6] 7.12
107
+ const ATA_REG_LBA_MID = 0x04 // LBA Mid register, see [ATA-6] 7.13
108
+ const ATA_REG_LBA_HIGH = 0x05 // LBA High register, see [ATA-6] 7.11
109
+ const ATA_REG_DEVICE = 0x06 // Device register, see [ATA-6] 7.7
110
+ // Write-only registers:
111
+ const ATA_REG_FEATURES = 0x01 // Features register, see [ATA-6] 7.10
112
+ const ATA_REG_COMMAND = 0x07 // Command register, see [ATA-6] 7.4
113
+ const ATA_REG_CONTROL = 0x00 // (*1*) Device Control register, see [ATA-6] 7.8
114
+
115
+ // Per-channel Bus Master IDE register offsets (BAR4), see [BMI-1] 2.0
116
+ // these are the primary channel's offsets, add 8 for secondary
117
+ const BMI_REG_COMMAND = 0x00 // Bus Master IDE Command register
118
+ const BMI_REG_STATUS = 0x02 // Bus Master IDE Status register
119
+ const BMI_REG_PRDT = 0x04 // Bus Master IDE PRD Table Address register
120
+
121
+ // Error register bits:
122
+ // All bits except for bit 0x04 are command dependent.
123
+ const ATA_ER_ABRT = 0x04 // Command aborted
124
+
125
+ // Status register bits:
126
+ const ATA_SR_ERR = 0x01 // Error (ATA)
127
+ const ATA_SR_COND = 0x01 // Check Condition (ATAPI)
128
+ const _ATA_SR_SENS = 0x02 // Sense Available (ATAPI)
129
+ const _ATA_SR_AERR = 0x04 // Alignment Error
130
+ const ATA_SR_DRQ = 0x08 // Data Request
131
+ const ATA_SR_DSC = 0x10 // Drive Seek Complete / Deferred Write Error
132
+ const ATA_SR_DF = 0x20 // Device Fault / Stream Error
133
+ const ATA_SR_DRDY = 0x40 // Drive Ready
134
+ const ATA_SR_BSY = 0x80 // Busy
135
+
136
+ // Device register bits:
137
+ // Bits 0x20/0x80 are obsolete and 0x01/0x02/0x04/0x08/0x40 are command dependent.
138
+ const ATA_DR_DEV = 0x10 // Device select; slave device if set, else master device
139
+
140
+ // Device Control register bits:
141
+ // Bits 0x08/0x10/0x20/0x40 are reserved and bit 0x01 is always zero.
142
+ const ATA_CR_NIEN = 0x02 // Interrupt disable (not Interrupt ENable)
143
+ const ATA_CR_SRST = 0x04 // Software reset
144
+ const _ATA_CR_HOB = 0x80 // 48-bit Address feature set
145
+
146
+ // ATA commands
147
+ const ATA_CMD_DEVICE_RESET = 0x08 // see [ATA8-ACS] 7.6
148
+ const ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC = 0x90 // see [ATA8-ACS] 7.9
149
+ const ATA_CMD_FLUSH_CACHE = 0xe7 // see [ATA8-ACS] 7.10
150
+ const ATA_CMD_FLUSH_CACHE_EXT = 0xea // see [ATA8-ACS] 7.11
151
+ const ATA_CMD_GET_MEDIA_STATUS = 0xda // see [ATA-6] 8.14
152
+ const ATA_CMD_IDENTIFY_DEVICE = 0xec // see [ATA8-ACS] 7.12
153
+ const ATA_CMD_IDENTIFY_PACKET_DEVICE = 0xa1 // see [ATA8-ACS] 7.13
154
+ const ATA_CMD_IDLE_IMMEDIATE = 0xe1 // see [ATA8-ACS] 7.15
155
+ const ATA_CMD_INITIALIZE_DEVICE_PARAMETERS = 0x91 // not mentioned in [ATA-6] or [ATA8-ACS]
156
+ const ATA_CMD_MEDIA_LOCK = 0xde // see [ATA-6] 8.20
157
+ const ATA_CMD_NOP = 0x00 // see [ATA8-ACS] 7.17
158
+ const ATA_CMD_PACKET = 0xa0 // see [ATA8-ACS] 7.18
159
+ const ATA_CMD_READ_DMA = 0xc8 // see [ATA8-ACS] 7.21
160
+ const ATA_CMD_READ_DMA_EXT = 0x25 // see [ATA8-ACS] 7.22
161
+ const ATA_CMD_READ_MULTIPLE = 0x29 // see [ATA8-ACS] 7.26
162
+ const ATA_CMD_READ_MULTIPLE_EXT = 0xc4 // see [ATA8-ACS] 7.27
163
+ const ATA_CMD_READ_NATIVE_MAX_ADDRESS = 0xf8 // see [ATA-6] 8.32
164
+ const ATA_CMD_READ_NATIVE_MAX_ADDRESS_EXT = 0x27 // see [ATA-6] 8.33
165
+ const ATA_CMD_READ_SECTORS = 0x20 // see [ATA8-ACS] 7.28
166
+ const ATA_CMD_READ_SECTORS_EXT = 0x24 // see [ATA8-ACS] 7.29
167
+ const ATA_CMD_READ_VERIFY_SECTORS = 0x40 // see [ATA8-ACS] 7.32
168
+ const ATA_CMD_SECURITY_FREEZE_LOCK = 0xf5 // see [ATA8-ACS] 7.40
169
+ const ATA_CMD_SET_FEATURES = 0xef // see [ATA8-ACS] 7.45
170
+ const ATA_CMD_SET_MAX = 0xf9 // see [ATA-6] 8.47
171
+ const ATA_CMD_SET_MULTIPLE_MODE = 0xc6 // see [ATA8-ACS] 7.46
172
+ const ATA_CMD_STANDBY_IMMEDIATE = 0xe0 // see [ATA8-ACS] 7.50
173
+ const ATA_CMD_WRITE_DMA = 0xca // see [ATA8-ACS] 7.58
174
+ const ATA_CMD_WRITE_DMA_EXT = 0x35 // see [ATA8-ACS] 7.59
175
+ const ATA_CMD_WRITE_MULTIPLE = 0x39 // see [ATA8-ACS] 7.64
176
+ const ATA_CMD_WRITE_MULTIPLE_EXT = 0xc5 // see [ATA8-ACS] 7.65
177
+ const ATA_CMD_WRITE_SECTORS = 0x30 // see [ATA8-ACS] 7.67
178
+ const ATA_CMD_WRITE_SECTORS_EXT = 0x34 // see [ATA8-ACS] 7.68
179
+ const ATA_CMD_10h = 0x10 // command obsolete/unknown, see [ATA-6] Table E.2
180
+ const ATA_CMD_F0h = 0xf0 // vendor-specific
181
+
182
+ const ATA_CMD_NAME: Record<number, string> = {
183
+ [ATA_CMD_DEVICE_RESET]: 'DEVICE RESET',
184
+ [ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC]: 'EXECUTE DEVICE DIAGNOSTIC',
185
+ [ATA_CMD_FLUSH_CACHE]: 'FLUSH CACHE',
186
+ [ATA_CMD_FLUSH_CACHE_EXT]: 'FLUSH CACHE EXT',
187
+ [ATA_CMD_GET_MEDIA_STATUS]: 'GET MEDIA STATUS',
188
+ [ATA_CMD_IDENTIFY_DEVICE]: 'IDENTIFY DEVICE',
189
+ [ATA_CMD_IDENTIFY_PACKET_DEVICE]: 'IDENTIFY PACKET DEVICE',
190
+ [ATA_CMD_IDLE_IMMEDIATE]: 'IDLE IMMEDIATE',
191
+ [ATA_CMD_INITIALIZE_DEVICE_PARAMETERS]: 'INITIALIZE DEVICE PARAMETERS',
192
+ [ATA_CMD_MEDIA_LOCK]: 'MEDIA LOCK',
193
+ [ATA_CMD_NOP]: 'NOP',
194
+ [ATA_CMD_PACKET]: 'PACKET',
195
+ [ATA_CMD_READ_DMA]: 'READ DMA',
196
+ [ATA_CMD_READ_DMA_EXT]: 'READ DMA EXT',
197
+ [ATA_CMD_READ_MULTIPLE]: 'READ MULTIPLE',
198
+ [ATA_CMD_READ_MULTIPLE_EXT]: 'READ MULTIPLE EXT',
199
+ [ATA_CMD_READ_NATIVE_MAX_ADDRESS]: 'READ NATIVE MAX ADDRESS',
200
+ [ATA_CMD_READ_NATIVE_MAX_ADDRESS_EXT]: 'READ NATIVE MAX ADDRESS EXT',
201
+ [ATA_CMD_READ_SECTORS]: 'READ SECTORS',
202
+ [ATA_CMD_READ_SECTORS_EXT]: 'READ SECTORS EXT',
203
+ [ATA_CMD_READ_VERIFY_SECTORS]: 'READ VERIFY SECTORS',
204
+ [ATA_CMD_SECURITY_FREEZE_LOCK]: 'SECURITY FREEZE LOCK',
205
+ [ATA_CMD_SET_FEATURES]: 'SET FEATURES',
206
+ [ATA_CMD_SET_MAX]: 'SET MAX',
207
+ [ATA_CMD_SET_MULTIPLE_MODE]: 'SET MULTIPLE MODE',
208
+ [ATA_CMD_STANDBY_IMMEDIATE]: 'STANDBY IMMEDIATE',
209
+ [ATA_CMD_WRITE_DMA]: 'WRITE DMA',
210
+ [ATA_CMD_WRITE_DMA_EXT]: 'WRITE DMA EXT',
211
+ [ATA_CMD_WRITE_MULTIPLE]: 'WRITE MULTIPLE',
212
+ [ATA_CMD_WRITE_MULTIPLE_EXT]: 'WRITE MULTIPLE EXT',
213
+ [ATA_CMD_WRITE_SECTORS]: 'WRITE SECTORS',
214
+ [ATA_CMD_WRITE_SECTORS_EXT]: 'WRITE SECTORS EXT',
215
+ [ATA_CMD_10h]: '<UNKNOWN 10h>',
216
+ [ATA_CMD_F0h]: '<VENDOR-SPECIFIC F0h>',
217
+ }
218
+
219
+ // ATAPI (SCSI-2/MMC-2) commands
220
+ const ATAPI_CMD_GET_CONFIGURATION = 0x46 // see [CD-SCSI-2]
221
+ const ATAPI_CMD_GET_EVENT_STATUS_NOTIFICATION = 0x4a // see [MMC-2] 9.1.2
222
+ const ATAPI_CMD_INQUIRY = 0x12 // see [MMC-2] 9.1.3
223
+ const ATAPI_CMD_MECHANISM_STATUS = 0xbd // see [MMC-2] 9.1.5
224
+ const ATAPI_CMD_MODE_SENSE_6 = 0x1a // see [CD-SCSI-2]
225
+ const ATAPI_CMD_MODE_SENSE_10 = 0x5a // see [MMC-2] 9.1.7
226
+ const ATAPI_CMD_PAUSE = 0x45 // see [CD-SCSI-2]
227
+ const ATAPI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1e // see [MMC-2] 9.1.9
228
+ const ATAPI_CMD_READ_10 = 0x28 // see [CD-SCSI-2]
229
+ const ATAPI_CMD_READ_12 = 0xa8 // see [SFF-8020] 9.8.14
230
+ const ATAPI_CMD_READ_CAPACITY = 0x25 // see [MMC-2] 9.1.12
231
+ const ATAPI_CMD_READ_CD = 0xbe // see [CD-SCSI-2]
232
+ const ATAPI_CMD_READ_DISK_INFORMATION = 0x51 // see [CD-SCSI-2]
233
+ const ATAPI_CMD_READ_SUBCHANNEL = 0x42 // see [CD-SCSI-2]
234
+ const ATAPI_CMD_READ_TOC_PMA_ATIP = 0x43 // see [CD-SCSI-2]
235
+ const ATAPI_CMD_READ_TRACK_INFORMATION = 0x52 // see [CD-SCSI-2]
236
+ const ATAPI_CMD_REQUEST_SENSE = 0x03 // see [MMC-2] 9.1.18
237
+ const ATAPI_CMD_START_STOP_UNIT = 0x1b // see [CD-SCSI-2]
238
+ const ATAPI_CMD_TEST_UNIT_READY = 0x00 // see [MMC-2] 9.1.20
239
+
240
+ // ATAPI command flags
241
+ const ATAPI_CF_NONE = 0x00 // no flags
242
+ const ATAPI_CF_NEEDS_DISK = 0x01 // command needs inserted disk
243
+ const _ATAPI_CF_UNIT_ATTN = 0x02 // bounce command if unit attention condition is active
244
+
245
+ // ATAPI commands, for flags see [MMC-3] 4.2.6
246
+ const ATAPI_CMD: Record<number, { name: string; flags: number }> = {
247
+ [ATAPI_CMD_GET_CONFIGURATION]: {
248
+ name: 'GET CONFIGURATION',
249
+ flags: ATAPI_CF_NONE,
250
+ },
251
+ [ATAPI_CMD_GET_EVENT_STATUS_NOTIFICATION]: {
252
+ name: 'GET EVENT STATUS NOTIFICATION',
253
+ flags: ATAPI_CF_NONE,
254
+ },
255
+ [ATAPI_CMD_INQUIRY]: { name: 'INQUIRY', flags: ATAPI_CF_NONE },
256
+ [ATAPI_CMD_MECHANISM_STATUS]: {
257
+ name: 'MECHANISM STATUS',
258
+ flags: ATAPI_CF_NONE,
259
+ },
260
+ [ATAPI_CMD_MODE_SENSE_6]: { name: 'MODE SENSE (6)', flags: ATAPI_CF_NONE },
261
+ [ATAPI_CMD_MODE_SENSE_10]: {
262
+ name: 'MODE SENSE (10)',
263
+ flags: ATAPI_CF_NONE,
264
+ },
265
+ [ATAPI_CMD_PAUSE]: { name: 'PAUSE', flags: ATAPI_CF_NEEDS_DISK },
266
+ [ATAPI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL]: {
267
+ name: 'PREVENT ALLOW MEDIUM REMOVAL',
268
+ flags: ATAPI_CF_NONE,
269
+ },
270
+ [ATAPI_CMD_READ_10]: { name: 'READ (10)', flags: ATAPI_CF_NEEDS_DISK },
271
+ [ATAPI_CMD_READ_12]: { name: 'READ (12)', flags: ATAPI_CF_NEEDS_DISK },
272
+ [ATAPI_CMD_READ_CAPACITY]: {
273
+ name: 'READ CAPACITY',
274
+ flags: ATAPI_CF_NEEDS_DISK,
275
+ },
276
+ [ATAPI_CMD_READ_CD]: { name: 'READ CD', flags: ATAPI_CF_NEEDS_DISK },
277
+ [ATAPI_CMD_READ_DISK_INFORMATION]: {
278
+ name: 'READ DISK INFORMATION',
279
+ flags: ATAPI_CF_NEEDS_DISK,
280
+ },
281
+ [ATAPI_CMD_READ_SUBCHANNEL]: {
282
+ name: 'READ SUBCHANNEL',
283
+ flags: ATAPI_CF_NEEDS_DISK,
284
+ },
285
+ [ATAPI_CMD_READ_TOC_PMA_ATIP]: {
286
+ name: 'READ TOC PMA ATIP',
287
+ flags: ATAPI_CF_NEEDS_DISK,
288
+ },
289
+ [ATAPI_CMD_READ_TRACK_INFORMATION]: {
290
+ name: 'READ TRACK INFORMATION',
291
+ flags: ATAPI_CF_NEEDS_DISK,
292
+ },
293
+ [ATAPI_CMD_REQUEST_SENSE]: { name: 'REQUEST SENSE', flags: ATAPI_CF_NONE },
294
+ [ATAPI_CMD_START_STOP_UNIT]: {
295
+ name: 'START STOP UNIT',
296
+ flags: ATAPI_CF_NONE,
297
+ },
298
+ [ATAPI_CMD_TEST_UNIT_READY]: {
299
+ name: 'TEST UNIT READY',
300
+ flags: ATAPI_CF_NEEDS_DISK,
301
+ },
302
+ }
303
+
304
+ // ATAPI device signature
305
+ const ATAPI_SIGNATURE_LO = 0x14
306
+ const ATAPI_SIGNATURE_HI = 0xeb
307
+
308
+ // ATAPI 4-bit Sense Keys, see [MMC-2] 9.1.18.3, Table 123
309
+ const _ATAPI_SK_NO_SENSE = 0
310
+ const _ATAPI_SK_RECOVERED_ERROR = 1
311
+ const ATAPI_SK_NOT_READY = 2
312
+ const _ATAPI_SK_MEDIUM_ERROR = 3
313
+ const _ATAPI_SK_HARDWARE_ERROR = 4
314
+ const ATAPI_SK_ILLEGAL_REQUEST = 5
315
+ const ATAPI_SK_UNIT_ATTENTION = 6
316
+ const _ATAPI_SK_DATA_PROTECT = 7
317
+ const _ATAPI_SK_BLANK_CHECK = 8
318
+ const _ATAPI_SK_ABORTED_COMMAND = 11
319
+
320
+ // ATAPI 8-bit Additional Sense Codes, see [MMC-2] 9.1.18.3, Table 124
321
+ // https://github.com/qemu/qemu/blob/3c5a5e213e5f08fbfe70728237f7799ac70f5b99/hw/ide/ide-internal.h#L288
322
+ const ATAPI_ASC_INV_FIELD_IN_CMD_PACKET = 0x24
323
+ const _ATAPI_ASC_MEDIUM_MAY_HAVE_CHANGED = 0x28
324
+ const ATAPI_ASC_MEDIUM_NOT_PRESENT = 0x3a
325
+
326
+ // Debug log detail bits (internal to this module)
327
+ const LOG_DETAIL_NONE = 0x00 // disable debug logging of details
328
+ const LOG_DETAIL_REG_IO = 0x01 // log register read/write access
329
+ const LOG_DETAIL_IRQ = 0x02 // log IRQ raise/lower events
330
+ const LOG_DETAIL_RW = 0x04 // log data read/write-related events
331
+ const LOG_DETAIL_RW_DMA = 0x08 // log DMA data read/write-related events
332
+ const LOG_DETAIL_CHS = 0x10 // log register-CHS to LBA conversions
333
+ const _LOG_DETAIL_ALL = 0xff // log all details
334
+ // the bitset of active log details (should be 0 when not in DEBUG mode)
335
+ const LOG_DETAILS = DEBUG ? LOG_DETAIL_NONE : 0
336
+
337
+ export class IDEController {
338
+ cpu: IDECpu
339
+ bus: BusConnector
340
+ primary: IDEChannel | undefined
341
+ secondary: IDEChannel | undefined
342
+ name: string
343
+ pci_id: number
344
+ pci_space: number[]
345
+ pci_bars: ({ size: number } | undefined)[]
346
+
347
+ constructor(
348
+ cpu: IDECpu,
349
+ bus: BusConnector,
350
+ ide_config: IDEConfig | undefined,
351
+ ) {
352
+ this.cpu = cpu
353
+ this.bus = bus
354
+
355
+ this.primary = undefined
356
+ this.secondary = undefined
357
+ this.name = 'ide'
358
+ this.pci_id = 0
359
+ this.pci_space = []
360
+ this.pci_bars = []
361
+
362
+ const has_primary = ide_config && ide_config[0][0]
363
+ const has_secondary = ide_config && ide_config[1][0]
364
+ if (has_primary || has_secondary) {
365
+ if (has_primary) {
366
+ this.primary = new IDEChannel(
367
+ this,
368
+ 0,
369
+ ide_config![0],
370
+ 0x1f0,
371
+ 0x3f6,
372
+ 14,
373
+ )
374
+ }
375
+ if (has_secondary) {
376
+ this.secondary = new IDEChannel(
377
+ this,
378
+ 1,
379
+ ide_config![1],
380
+ 0x170,
381
+ 0x376,
382
+ 15,
383
+ )
384
+ }
385
+
386
+ const vendor_id = 0x8086 // Intel Corporation
387
+ const device_id = 0x7010 // 82371SB PIIX3 IDE [Natoma/Triton II]
388
+ const class_code = 0x01 // Mass Storage Controller
389
+ const subclass = 0x01 // IDE Controller
390
+ const prog_if = 0x80 // ISA Compatibility mode-only controller, supports bus mastering
391
+ const interrupt_line = 0x00 // IRQs 14 and 15 are predefined in Compatibility mode and this field is ignored
392
+ const command_base0 = has_primary ? this.primary!.command_base : 0
393
+ const control_base0 = has_primary ? this.primary!.control_base : 0
394
+ const command_base1 = has_secondary
395
+ ? this.secondary!.command_base
396
+ : 0
397
+ const control_base1 = has_secondary
398
+ ? this.secondary!.control_base
399
+ : 0
400
+
401
+ this.name = 'ide'
402
+ this.pci_id = 0x1e << 3
403
+ this.pci_space = [
404
+ vendor_id & 0xff,
405
+ vendor_id >> 8,
406
+ device_id & 0xff,
407
+ device_id >> 8,
408
+ 0x05,
409
+ 0x00,
410
+ 0xa0,
411
+ 0x02,
412
+ 0x00,
413
+ prog_if,
414
+ subclass,
415
+ class_code,
416
+ 0x00,
417
+ 0x00,
418
+ 0x00,
419
+ 0x00,
420
+ (command_base0 & 0xff) | 1,
421
+ command_base0 >> 8,
422
+ 0x00,
423
+ 0x00,
424
+ (control_base0 & 0xff) | 1,
425
+ control_base0 >> 8,
426
+ 0x00,
427
+ 0x00,
428
+ (command_base1 & 0xff) | 1,
429
+ command_base1 >> 8,
430
+ 0x00,
431
+ 0x00,
432
+ (control_base1 & 0xff) | 1,
433
+ control_base1 >> 8,
434
+ 0x00,
435
+ 0x00,
436
+ (BUS_MASTER_BASE & 0xff) | 1,
437
+ BUS_MASTER_BASE >> 8,
438
+ 0x00,
439
+ 0x00,
440
+ 0x00,
441
+ 0x00,
442
+ 0x00,
443
+ 0x00,
444
+ 0x00,
445
+ 0x00,
446
+ 0x00,
447
+ 0x00,
448
+ 0x43,
449
+ 0x10,
450
+ 0xd4,
451
+ 0x82,
452
+ 0x00,
453
+ 0x00,
454
+ 0x00,
455
+ 0x00,
456
+ 0x00,
457
+ 0x00,
458
+ 0x00,
459
+ 0x00,
460
+ 0x00,
461
+ 0x00,
462
+ 0x00,
463
+ 0x00,
464
+ interrupt_line,
465
+ 0x01,
466
+ 0x00,
467
+ 0x00,
468
+ // 0x40
469
+ 0x00,
470
+ 0x00,
471
+ 0x00,
472
+ 0x00,
473
+ 0x00,
474
+ 0x00,
475
+ 0x00,
476
+ 0x00,
477
+ 0x00,
478
+ 0x00,
479
+ 0x00,
480
+ 0x00,
481
+ 0x00,
482
+ 0x00,
483
+ 0x00,
484
+ 0x00,
485
+ 0x00,
486
+ 0x00,
487
+ 0x00,
488
+ 0x00,
489
+ 0x00,
490
+ 0x00,
491
+ 0x00,
492
+ 0x00,
493
+ 0x00,
494
+ 0x00,
495
+ 0x00,
496
+ 0x00,
497
+ 0x00,
498
+ 0x00,
499
+ 0x00,
500
+ 0x00,
501
+ 0x00,
502
+ 0x00,
503
+ 0x00,
504
+ 0x00,
505
+ 0x00,
506
+ 0x00,
507
+ 0x00,
508
+ 0x00,
509
+ 0x00,
510
+ 0x00,
511
+ 0x00,
512
+ 0x00,
513
+ 0x00,
514
+ 0x00,
515
+ 0x00,
516
+ 0x00,
517
+ 0x00,
518
+ 0x00,
519
+ 0x00,
520
+ 0x00,
521
+ 0x00,
522
+ 0x00,
523
+ 0x00,
524
+ 0x00,
525
+ 0x00,
526
+ 0x00,
527
+ 0x00,
528
+ 0x00,
529
+ 0x00,
530
+ 0x00,
531
+ 0x00,
532
+ 0x00,
533
+ // 0x80
534
+ 0x00,
535
+ 0x00,
536
+ 0x00,
537
+ 0x00,
538
+ 0x00,
539
+ 0x00,
540
+ 0x00,
541
+ 0x00,
542
+ 0x00,
543
+ 0x00,
544
+ 0x00,
545
+ 0x00,
546
+ 0x00,
547
+ 0x00,
548
+ 0x00,
549
+ 0x00,
550
+ 0x00,
551
+ 0x00,
552
+ 0x00,
553
+ 0x00,
554
+ 0x00,
555
+ 0x00,
556
+ 0x00,
557
+ 0x00,
558
+ 0x00,
559
+ 0x00,
560
+ 0x00,
561
+ 0x00,
562
+ 0x00,
563
+ 0x00,
564
+ 0x00,
565
+ 0x00,
566
+ 0x00,
567
+ 0x00,
568
+ 0x00,
569
+ 0x00,
570
+ 0x00,
571
+ 0x00,
572
+ 0x00,
573
+ 0x00,
574
+ 0x00,
575
+ 0x00,
576
+ 0x00,
577
+ 0x00,
578
+ 0x00,
579
+ 0x00,
580
+ 0x00,
581
+ 0x00,
582
+ 0x00,
583
+ 0x00,
584
+ 0x00,
585
+ 0x00,
586
+ 0x00,
587
+ 0x00,
588
+ 0x00,
589
+ 0x00,
590
+ 0x00,
591
+ 0x00,
592
+ 0x00,
593
+ 0x00,
594
+ 0x00,
595
+ 0x00,
596
+ 0x00,
597
+ 0x00,
598
+ ]
599
+ this.pci_bars = [
600
+ has_primary ? { size: 8 } : undefined, // BAR0: Command block register address of primary channel
601
+ has_primary ? { size: 1 } : undefined, // BAR1: Control block register address of primary channel
602
+ has_secondary ? { size: 8 } : undefined, // BAR2: Command block register address of secondary channel
603
+ has_secondary ? { size: 1 } : undefined, // BAR3: Control block register address of secondary channel
604
+ { size: 16 }, // BAR4: Bus Master I/O register address of both channels (8+8)
605
+ ]
606
+ cpu.devices.pci.register_device(this)
607
+ }
608
+ }
609
+
610
+ get_state(): unknown[] {
611
+ const state: unknown[] = []
612
+ state[0] = this.primary
613
+ state[1] = this.secondary
614
+ return state
615
+ }
616
+
617
+ set_state(state: any[]): void {
618
+ if (this.primary) {
619
+ this.primary.set_state(state[0])
620
+ }
621
+ if (this.secondary) {
622
+ this.secondary.set_state(state[1])
623
+ }
624
+ }
625
+ }
626
+
627
+ class IDEChannel {
628
+ controller: IDEController
629
+ channel_nr: number
630
+ cpu: IDECpu
631
+ bus: BusConnector
632
+ command_base: number
633
+ control_base: number
634
+ irq: number
635
+ name: string
636
+ master: IDEInterface
637
+ slave: IDEInterface
638
+ current_interface: IDEInterface
639
+ device_control_reg: number
640
+ prdt_addr: number
641
+ dma_status: number
642
+ dma_command: number
643
+
644
+ constructor(
645
+ controller: IDEController,
646
+ channel_nr: number,
647
+ channel_config:
648
+ | [IDEDeviceConfig | undefined, IDEDeviceConfig | undefined]
649
+ | undefined,
650
+ command_base: number,
651
+ control_base: number,
652
+ irq: number,
653
+ ) {
654
+ this.controller = controller
655
+ this.channel_nr = channel_nr
656
+ this.cpu = controller.cpu
657
+ this.bus = controller.bus
658
+ this.command_base = command_base
659
+ this.control_base = control_base
660
+ this.irq = irq
661
+ this.name = 'ide' + channel_nr
662
+
663
+ const master_cfg = channel_config ? channel_config[0] : undefined
664
+ const slave_cfg = channel_config ? channel_config[1] : undefined
665
+ this.master = new IDEInterface(
666
+ this,
667
+ 0,
668
+ master_cfg?.buffer,
669
+ master_cfg?.is_cdrom,
670
+ )
671
+ this.slave = new IDEInterface(
672
+ this,
673
+ 1,
674
+ slave_cfg?.buffer,
675
+ slave_cfg?.is_cdrom,
676
+ )
677
+
678
+ this.current_interface = this.master
679
+
680
+ this.device_control_reg = ATA_CR_NIEN
681
+ this.prdt_addr = 0
682
+ this.dma_status = 0
683
+ this.dma_command = 0
684
+
685
+ const cpu = controller.cpu
686
+
687
+ //
688
+ // Command Block Registers: command_base + 0...7 (BAR0: 1F0h, BAR2: 170h)
689
+ //
690
+
691
+ cpu.io.register_read(
692
+ this.command_base | ATA_REG_DATA,
693
+ this,
694
+ () => {
695
+ return this.current_interface.read_data(1)
696
+ },
697
+ () => {
698
+ return this.current_interface.read_data(2)
699
+ },
700
+ () => {
701
+ return this.current_interface.read_data(4)
702
+ },
703
+ )
704
+
705
+ cpu.io.register_read(this.command_base | ATA_REG_ERROR, this, () => {
706
+ if (LOG_DETAILS & LOG_DETAIL_REG_IO) {
707
+ dbg_log(
708
+ this.current_interface.name +
709
+ ': read Error register: ' +
710
+ h(this.current_interface.error_reg & 0xff),
711
+ LOG_DISK,
712
+ )
713
+ }
714
+ return this.current_interface.error_reg & 0xff
715
+ })
716
+
717
+ cpu.io.register_read(this.command_base | ATA_REG_SECTOR, this, () => {
718
+ if (LOG_DETAILS & LOG_DETAIL_REG_IO) {
719
+ dbg_log(
720
+ this.current_interface.name +
721
+ ': read Sector Count register: ' +
722
+ h(this.current_interface.sector_count_reg & 0xff),
723
+ LOG_DISK,
724
+ )
725
+ }
726
+ return this.current_interface.sector_count_reg & 0xff
727
+ })
728
+
729
+ cpu.io.register_read(this.command_base | ATA_REG_LBA_LOW, this, () => {
730
+ if (LOG_DETAILS & LOG_DETAIL_REG_IO) {
731
+ dbg_log(
732
+ this.current_interface.name +
733
+ ': read LBA Low register: ' +
734
+ h(this.current_interface.lba_low_reg & 0xff),
735
+ LOG_DISK,
736
+ )
737
+ }
738
+ return this.current_interface.lba_low_reg & 0xff
739
+ })
740
+
741
+ cpu.io.register_read(this.command_base | ATA_REG_LBA_MID, this, () => {
742
+ if (LOG_DETAILS & LOG_DETAIL_REG_IO) {
743
+ dbg_log(
744
+ this.current_interface.name +
745
+ ': read LBA Mid register: ' +
746
+ h(this.current_interface.lba_mid_reg & 0xff),
747
+ LOG_DISK,
748
+ )
749
+ }
750
+ return this.current_interface.lba_mid_reg & 0xff
751
+ })
752
+
753
+ cpu.io.register_read(this.command_base | ATA_REG_LBA_HIGH, this, () => {
754
+ if (LOG_DETAILS & LOG_DETAIL_REG_IO) {
755
+ dbg_log(
756
+ this.current_interface.name +
757
+ ': read LBA High register: ' +
758
+ h(this.current_interface.lba_high_reg & 0xff),
759
+ LOG_DISK,
760
+ )
761
+ }
762
+ return this.current_interface.lba_high_reg & 0xff
763
+ })
764
+
765
+ cpu.io.register_read(this.command_base | ATA_REG_DEVICE, this, () => {
766
+ if (LOG_DETAILS & LOG_DETAIL_REG_IO) {
767
+ dbg_log(
768
+ this.current_interface.name + ': read Device register',
769
+ LOG_DISK,
770
+ )
771
+ }
772
+ return this.current_interface.device_reg & 0xff
773
+ })
774
+
775
+ cpu.io.register_read(this.command_base | ATA_REG_STATUS, this, () => {
776
+ const status = this.read_status()
777
+ if (LOG_DETAILS & (LOG_DETAIL_REG_IO | LOG_DETAIL_IRQ)) {
778
+ dbg_log(
779
+ `${this.current_interface.name}: read Status register: ${h(status, 2)} (lower IRQ ${this.irq})`,
780
+ LOG_DISK,
781
+ )
782
+ }
783
+ this.cpu.device_lower_irq(this.irq)
784
+ return status
785
+ })
786
+
787
+ cpu.io.register_write(
788
+ this.command_base | ATA_REG_DATA,
789
+ this,
790
+ (data: number) => {
791
+ this.current_interface.write_data_port8(data)
792
+ },
793
+ (data: number) => {
794
+ this.current_interface.write_data_port16(data)
795
+ },
796
+ (data: number) => {
797
+ this.current_interface.write_data_port32(data)
798
+ },
799
+ )
800
+
801
+ cpu.io.register_write(
802
+ this.command_base | ATA_REG_FEATURES,
803
+ this,
804
+ (data: number) => {
805
+ if (LOG_DETAILS & LOG_DETAIL_REG_IO) {
806
+ dbg_log(
807
+ this.current_interface.name +
808
+ ': write Features register: ' +
809
+ h(data),
810
+ LOG_DISK,
811
+ )
812
+ }
813
+ this.current_interface.features_reg =
814
+ ((this.current_interface.features_reg << 8) | data) & 0xffff
815
+ },
816
+ )
817
+
818
+ cpu.io.register_write(
819
+ this.command_base | ATA_REG_SECTOR,
820
+ this,
821
+ (data: number) => {
822
+ if (LOG_DETAILS & LOG_DETAIL_REG_IO) {
823
+ dbg_log(
824
+ this.current_interface.name +
825
+ ': write Sector Count register: ' +
826
+ h(data),
827
+ LOG_DISK,
828
+ )
829
+ }
830
+ this.current_interface.sector_count_reg =
831
+ ((this.current_interface.sector_count_reg << 8) | data) &
832
+ 0xffff
833
+ },
834
+ )
835
+
836
+ cpu.io.register_write(
837
+ this.command_base | ATA_REG_LBA_LOW,
838
+ this,
839
+ (data: number) => {
840
+ if (LOG_DETAILS & LOG_DETAIL_REG_IO) {
841
+ dbg_log(
842
+ this.current_interface.name +
843
+ ': write LBA Low register: ' +
844
+ h(data),
845
+ LOG_DISK,
846
+ )
847
+ }
848
+ this.current_interface.lba_low_reg =
849
+ ((this.current_interface.lba_low_reg << 8) | data) & 0xffff
850
+ },
851
+ )
852
+
853
+ cpu.io.register_write(
854
+ this.command_base | ATA_REG_LBA_MID,
855
+ this,
856
+ (data: number) => {
857
+ if (LOG_DETAILS & LOG_DETAIL_REG_IO) {
858
+ dbg_log(
859
+ this.current_interface.name +
860
+ ': write LBA Mid register: ' +
861
+ h(data),
862
+ LOG_DISK,
863
+ )
864
+ }
865
+ this.current_interface.lba_mid_reg =
866
+ ((this.current_interface.lba_mid_reg << 8) | data) & 0xffff
867
+ },
868
+ )
869
+
870
+ cpu.io.register_write(
871
+ this.command_base | ATA_REG_LBA_HIGH,
872
+ this,
873
+ (data: number) => {
874
+ if (LOG_DETAILS & LOG_DETAIL_REG_IO) {
875
+ dbg_log(
876
+ this.current_interface.name +
877
+ ': write LBA High register: ' +
878
+ h(data),
879
+ LOG_DISK,
880
+ )
881
+ }
882
+ this.current_interface.lba_high_reg =
883
+ ((this.current_interface.lba_high_reg << 8) | data) & 0xffff
884
+ },
885
+ )
886
+
887
+ cpu.io.register_write(
888
+ this.command_base | ATA_REG_DEVICE,
889
+ this,
890
+ (data: number) => {
891
+ const select_slave = data & ATA_DR_DEV
892
+ if (LOG_DETAILS & LOG_DETAIL_REG_IO) {
893
+ dbg_log(
894
+ this.current_interface.name +
895
+ ': write Device register: ' +
896
+ h(data, 2),
897
+ LOG_DISK,
898
+ )
899
+ }
900
+ if (
901
+ (select_slave && this.current_interface === this.master) ||
902
+ (!select_slave && this.current_interface === this.slave)
903
+ ) {
904
+ if (select_slave) {
905
+ dbg_log(
906
+ `${this.current_interface.name}: select slave device (${this.channel_nr ? 'secondary' : 'primary'})`,
907
+ LOG_DISK,
908
+ )
909
+ this.current_interface = this.slave
910
+ } else {
911
+ dbg_log(
912
+ `${this.current_interface.name}: select master device (${this.channel_nr ? 'secondary' : 'primary'})`,
913
+ LOG_DISK,
914
+ )
915
+ this.current_interface = this.master
916
+ }
917
+ }
918
+ this.current_interface.device_reg = data
919
+ this.current_interface.is_lba = (data >> 6) & 1 // TODO: where does this definition of bit 6 come from? not in [ATA-6] or [ATA-4]!
920
+ this.current_interface.head = data & 0xf // TODO: same for lower nibble?
921
+ },
922
+ )
923
+
924
+ cpu.io.register_write(
925
+ this.command_base | ATA_REG_COMMAND,
926
+ this,
927
+ (data: number) => {
928
+ if (LOG_DETAILS & LOG_DETAIL_REG_IO) {
929
+ dbg_log(
930
+ this.current_interface.name +
931
+ ': write Command register',
932
+ LOG_DISK,
933
+ )
934
+ }
935
+ this.current_interface.status_reg &= ~(ATA_SR_ERR | ATA_SR_DF)
936
+ this.current_interface.ata_command(data)
937
+ if (LOG_DETAILS & LOG_DETAIL_IRQ) {
938
+ dbg_log(
939
+ this.current_interface.name + ': lower IRQ ' + this.irq,
940
+ LOG_DISK,
941
+ )
942
+ }
943
+ this.cpu.device_lower_irq(this.irq)
944
+ },
945
+ )
946
+
947
+ //
948
+ // Control Block Register: control_base (BAR1: 3F6h, BAR3: 376h)
949
+ //
950
+
951
+ // read Alternate Status register
952
+ cpu.io.register_read(this.control_base | ATA_REG_ALT_STATUS, this, () =>
953
+ this.read_status(),
954
+ )
955
+
956
+ // write Device Control register
957
+ cpu.io.register_write(
958
+ this.control_base | ATA_REG_CONTROL,
959
+ this,
960
+ (data: number) => this.write_control(data),
961
+ )
962
+
963
+ //
964
+ // Bus Master Registers: bus_master_base + 0...15 (BAR4: B400h)
965
+ // primary channel: bus_master_base + 0...7, secondary: bus_master_base + 8...15
966
+ //
967
+
968
+ const bus_master_base = BUS_MASTER_BASE + channel_nr * 8
969
+
970
+ // read/write Bus Master IDE Command register
971
+ cpu.io.register_read(
972
+ bus_master_base | BMI_REG_COMMAND,
973
+ this,
974
+ () => this.dma_read_command8(),
975
+ undefined,
976
+ () => this.dma_read_command(),
977
+ )
978
+ cpu.io.register_write(
979
+ bus_master_base | BMI_REG_COMMAND,
980
+ this,
981
+ (data: number) => this.dma_write_command8(data),
982
+ undefined,
983
+ (data: number) => this.dma_write_command(data),
984
+ )
985
+
986
+ // read/write Bus Master IDE Status register
987
+ cpu.io.register_read(bus_master_base | BMI_REG_STATUS, this, () =>
988
+ this.dma_read_status(),
989
+ )
990
+ cpu.io.register_write(
991
+ bus_master_base | BMI_REG_STATUS,
992
+ this,
993
+ (data: number) => this.dma_write_status(data),
994
+ )
995
+
996
+ // read/write Bus Master IDE PRD Table Address register
997
+ cpu.io.register_read(
998
+ bus_master_base | BMI_REG_PRDT,
999
+ this,
1000
+ undefined,
1001
+ undefined,
1002
+ () => this.dma_read_addr(),
1003
+ )
1004
+ cpu.io.register_write(
1005
+ bus_master_base | BMI_REG_PRDT,
1006
+ this,
1007
+ undefined,
1008
+ undefined,
1009
+ (data: number) => this.dma_set_addr(data),
1010
+ )
1011
+
1012
+ if (DEBUG) {
1013
+ Object.seal(this)
1014
+ }
1015
+ }
1016
+
1017
+ read_status(): number {
1018
+ return this.current_interface.drive_connected
1019
+ ? this.current_interface.status_reg
1020
+ : 0
1021
+ }
1022
+
1023
+ write_control(data: number): void {
1024
+ if (LOG_DETAILS & (LOG_DETAIL_REG_IO | LOG_DETAIL_IRQ)) {
1025
+ dbg_log(
1026
+ this.current_interface.name +
1027
+ ': write Device Control register: ' +
1028
+ h(data, 2) +
1029
+ ' interrupts ' +
1030
+ (data & ATA_CR_NIEN ? 'disabled' : 'enabled'),
1031
+ LOG_DISK,
1032
+ )
1033
+ }
1034
+ if (data & ATA_CR_SRST) {
1035
+ dbg_log(
1036
+ `${this.current_interface.name}: soft reset via control port (lower IRQ ${this.irq})`,
1037
+ LOG_DISK,
1038
+ )
1039
+ this.cpu.device_lower_irq(this.irq)
1040
+ this.master.device_reset()
1041
+ this.slave.device_reset()
1042
+ }
1043
+ this.device_control_reg = data
1044
+ }
1045
+
1046
+ dma_read_addr(): number {
1047
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
1048
+ dbg_log(
1049
+ this.current_interface.name +
1050
+ ': DMA get address: ' +
1051
+ h(this.prdt_addr, 8),
1052
+ LOG_DISK,
1053
+ )
1054
+ }
1055
+ return this.prdt_addr
1056
+ }
1057
+
1058
+ dma_set_addr(data: number): void {
1059
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
1060
+ dbg_log(
1061
+ this.current_interface.name +
1062
+ ': DMA set address: ' +
1063
+ h(data, 8),
1064
+ LOG_DISK,
1065
+ )
1066
+ }
1067
+ this.prdt_addr = data
1068
+ }
1069
+
1070
+ dma_read_status(): number {
1071
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
1072
+ dbg_log(
1073
+ this.current_interface.name +
1074
+ ': DMA read status: ' +
1075
+ h(this.dma_status),
1076
+ LOG_DISK,
1077
+ )
1078
+ }
1079
+ return this.dma_status
1080
+ }
1081
+
1082
+ dma_write_status(value: number): void {
1083
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
1084
+ dbg_log(
1085
+ this.current_interface.name + ': DMA write status: ' + h(value),
1086
+ LOG_DISK,
1087
+ )
1088
+ }
1089
+ this.dma_status &= ~(value & 6)
1090
+ }
1091
+
1092
+ dma_read_command(): number {
1093
+ return this.dma_read_command8() | (this.dma_read_status() << 16)
1094
+ }
1095
+
1096
+ dma_read_command8(): number {
1097
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
1098
+ dbg_log(
1099
+ this.current_interface.name +
1100
+ ': DMA read command: ' +
1101
+ h(this.dma_command),
1102
+ LOG_DISK,
1103
+ )
1104
+ }
1105
+ return this.dma_command
1106
+ }
1107
+
1108
+ dma_write_command(value: number): void {
1109
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
1110
+ dbg_log(
1111
+ this.current_interface.name +
1112
+ ': DMA write command: ' +
1113
+ h(value),
1114
+ LOG_DISK,
1115
+ )
1116
+ }
1117
+ this.dma_write_command8(value & 0xff)
1118
+ this.dma_write_status((value >> 16) & 0xff)
1119
+ }
1120
+
1121
+ dma_write_command8(value: number): void {
1122
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
1123
+ dbg_log(
1124
+ this.current_interface.name +
1125
+ ': DMA write command8: ' +
1126
+ h(value),
1127
+ LOG_DISK,
1128
+ )
1129
+ }
1130
+
1131
+ const old_command = this.dma_command
1132
+ this.dma_command = value & 0x09
1133
+
1134
+ if ((old_command & 1) === (value & 1)) {
1135
+ return
1136
+ }
1137
+
1138
+ if ((value & 1) === 0) {
1139
+ this.dma_status &= ~1
1140
+ return
1141
+ }
1142
+
1143
+ this.dma_status |= 1
1144
+
1145
+ switch (this.current_interface.current_command) {
1146
+ case ATA_CMD_READ_DMA:
1147
+ case ATA_CMD_READ_DMA_EXT:
1148
+ this.current_interface.do_ata_read_sectors_dma()
1149
+ break
1150
+ case ATA_CMD_WRITE_DMA:
1151
+ case ATA_CMD_WRITE_DMA_EXT:
1152
+ this.current_interface.do_ata_write_sectors_dma()
1153
+ break
1154
+ case ATA_CMD_PACKET:
1155
+ this.current_interface.do_atapi_dma()
1156
+ break
1157
+ default:
1158
+ dbg_log(
1159
+ this.current_interface.name +
1160
+ ': spurious DMA command write, current command: ' +
1161
+ h(this.current_interface.current_command),
1162
+ LOG_DISK,
1163
+ )
1164
+ dbg_log(
1165
+ this.current_interface.name +
1166
+ ': DMA clear status bit 1h, set status bit 2h',
1167
+ LOG_DISK,
1168
+ )
1169
+ this.dma_status &= ~1
1170
+ this.dma_status |= 2
1171
+ this.push_irq()
1172
+ break
1173
+ }
1174
+ }
1175
+
1176
+ push_irq(): void {
1177
+ if ((this.device_control_reg & ATA_CR_NIEN) === 0) {
1178
+ if (LOG_DETAILS & LOG_DETAIL_IRQ) {
1179
+ dbg_log(
1180
+ this.current_interface.name + ': push IRQ ' + this.irq,
1181
+ LOG_DISK,
1182
+ )
1183
+ }
1184
+ this.dma_status |= 4
1185
+ this.cpu.device_raise_irq(this.irq)
1186
+ }
1187
+ }
1188
+
1189
+ get_state(): unknown[] {
1190
+ const state: unknown[] = []
1191
+ state[0] = this.master
1192
+ state[1] = this.slave
1193
+ state[2] = this.command_base
1194
+ state[3] = this.irq
1195
+ // state[4] = this.pci_id;
1196
+ state[5] = this.control_base
1197
+ // state[6] = this.bus_master_base;
1198
+ state[7] = this.name
1199
+ state[8] = this.device_control_reg
1200
+ state[9] = this.prdt_addr
1201
+ state[10] = this.dma_status
1202
+ state[11] = this.current_interface === this.master
1203
+ state[12] = this.dma_command
1204
+ return state
1205
+ }
1206
+
1207
+ set_state(state: any[]): void {
1208
+ this.master.set_state(state[0])
1209
+ this.slave.set_state(state[1])
1210
+ this.command_base = state[2]
1211
+ this.irq = state[3]
1212
+ // this.pci_id = state[4];
1213
+ this.control_base = state[5]
1214
+ // this.bus_master_base = state[6];
1215
+ this.name = state[7]
1216
+ this.device_control_reg = state[8]
1217
+ this.prdt_addr = state[9]
1218
+ this.dma_status = state[10]
1219
+ this.current_interface = state[11] ? this.master : this.slave
1220
+ this.dma_command = state[12]
1221
+ }
1222
+ }
1223
+
1224
+ class IDEInterface {
1225
+ channel: IDEChannel
1226
+ name: string
1227
+ bus: BusConnector
1228
+ channel_nr: number
1229
+ interface_nr: number
1230
+ cpu: IDECpu
1231
+ buffer: IDEDiskBuffer | null
1232
+ drive_connected: boolean
1233
+ sector_size: number
1234
+ is_atapi: boolean
1235
+ sector_count: number
1236
+ head_count: number
1237
+ sectors_per_track: number
1238
+ cylinder_count: number
1239
+ is_lba: number
1240
+ sector_count_reg: number
1241
+ lba_low_reg: number
1242
+ features_reg: number
1243
+ lba_mid_reg: number
1244
+ lba_high_reg: number
1245
+ head: number
1246
+ device_reg: number
1247
+ status_reg: number
1248
+ sectors_per_drq: number
1249
+ error_reg: number
1250
+ data_pointer: number
1251
+ data: Uint8Array
1252
+ data16: Uint16Array
1253
+ data32: Int32Array
1254
+ data_length: number
1255
+ data_end: number
1256
+ current_command: number
1257
+ write_dest: number
1258
+ last_io_id: number
1259
+ in_progress_io_ids: Set<number>
1260
+ cancelled_io_ids: Set<number>
1261
+ current_atapi_command: number
1262
+ atapi_sense_key: number
1263
+ atapi_add_sense: number
1264
+ medium_changed: boolean
1265
+
1266
+ constructor(
1267
+ channel: IDEChannel,
1268
+ interface_nr: number,
1269
+ buffer: IDEDiskBuffer | undefined,
1270
+ is_cd: boolean | undefined,
1271
+ ) {
1272
+ this.channel = channel
1273
+ this.name = channel.name + '.' + interface_nr
1274
+
1275
+ this.bus = channel.bus
1276
+ this.channel_nr = channel.channel_nr
1277
+ this.interface_nr = interface_nr
1278
+ this.cpu = channel.cpu
1279
+
1280
+ this.buffer = null
1281
+
1282
+ this.drive_connected = !!is_cd || !!buffer
1283
+ this.sector_size = is_cd ? CDROM_SECTOR_SIZE : HD_SECTOR_SIZE
1284
+ this.is_atapi = !!is_cd
1285
+ this.sector_count = 0
1286
+ this.head_count = this.is_atapi ? 1 : 0
1287
+ this.sectors_per_track = 0
1288
+ this.cylinder_count = 0
1289
+ this.is_lba = 0
1290
+ this.sector_count_reg = 0
1291
+ this.lba_low_reg = 0
1292
+ this.features_reg = 0
1293
+ this.lba_mid_reg = 0
1294
+ this.lba_high_reg = 0
1295
+ this.head = 0
1296
+ this.device_reg = 0
1297
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1298
+ this.sectors_per_drq = 0x80
1299
+ this.error_reg = 0
1300
+ this.data_pointer = 0
1301
+
1302
+ this.data = new Uint8Array(64 * 1024)
1303
+ this.data16 = new Uint16Array(this.data.buffer)
1304
+ this.data32 = new Int32Array(this.data.buffer)
1305
+
1306
+ this.data_length = 0
1307
+ this.data_end = 0
1308
+ this.current_command = -1
1309
+ this.write_dest = 0
1310
+
1311
+ // cancellation support
1312
+ this.last_io_id = 0
1313
+ this.in_progress_io_ids = new Set()
1314
+ this.cancelled_io_ids = new Set()
1315
+
1316
+ // ATAPI-only
1317
+ this.current_atapi_command = -1
1318
+ this.atapi_sense_key = 0
1319
+ this.atapi_add_sense = 0
1320
+ this.medium_changed = false
1321
+
1322
+ this.set_disk_buffer(buffer)
1323
+
1324
+ if (this.drive_connected) {
1325
+ dbg_log(
1326
+ `${this.name}: ${this.is_atapi ? 'ATAPI CD-ROM' : 'ATA HD'} device ready`,
1327
+ LOG_DISK,
1328
+ )
1329
+ }
1330
+
1331
+ Object.seal(this)
1332
+ }
1333
+
1334
+ has_disk(): boolean {
1335
+ return !!this.buffer
1336
+ }
1337
+
1338
+ eject(): void {
1339
+ if (this.is_atapi && this.buffer) {
1340
+ this.medium_changed = true
1341
+ this.buffer = null
1342
+ this.status_reg =
1343
+ ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ | ATA_SR_COND
1344
+ this.error_reg = ATAPI_SK_UNIT_ATTENTION << 4
1345
+ this.push_irq()
1346
+ }
1347
+ }
1348
+
1349
+ set_cdrom(buffer: IDEDiskBuffer): void {
1350
+ if (this.is_atapi && buffer) {
1351
+ this.set_disk_buffer(buffer)
1352
+ this.medium_changed = true
1353
+ }
1354
+ }
1355
+
1356
+ set_disk_buffer(buffer: IDEDiskBuffer | undefined): void {
1357
+ if (!buffer) {
1358
+ return
1359
+ }
1360
+
1361
+ this.buffer = buffer
1362
+ if (this.is_atapi) {
1363
+ this.status_reg =
1364
+ ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ | ATA_SR_COND
1365
+ this.error_reg = ATAPI_SK_UNIT_ATTENTION << 4
1366
+ }
1367
+ this.sector_count = this.buffer.byteLength / this.sector_size
1368
+
1369
+ if (this.sector_count !== (this.sector_count | 0)) {
1370
+ dbg_log(
1371
+ this.name + ': warning: disk size not aligned with sector size',
1372
+ LOG_DISK,
1373
+ )
1374
+ this.sector_count = Math.ceil(this.sector_count)
1375
+ }
1376
+
1377
+ if (this.is_atapi) {
1378
+ // default values: 1/2048
1379
+ this.head_count = 1
1380
+ this.sectors_per_track = 2048
1381
+ } else {
1382
+ // "default" values: 16/63
1383
+ // common: 255, 63
1384
+ this.head_count = 16
1385
+ this.sectors_per_track = 63
1386
+ }
1387
+
1388
+ this.cylinder_count =
1389
+ this.sector_count / this.head_count / this.sectors_per_track
1390
+
1391
+ if (this.cylinder_count !== (this.cylinder_count | 0)) {
1392
+ dbg_log(
1393
+ this.name +
1394
+ ': warning: rounding up cylinder count, choose different head number',
1395
+ LOG_DISK,
1396
+ )
1397
+ this.cylinder_count = Math.floor(this.cylinder_count)
1398
+ }
1399
+
1400
+ if (this.interface_nr === 0) {
1401
+ // for CMOS see:
1402
+ // https://github.com/copy/v86/blob/master/src/rtc.js
1403
+ // https://github.com/coreboot/seabios/blob/master/src/hw/rtc.h
1404
+ // https://web.archive.org/web/20240119203005/http://www.bioscentral.com/misc/cmosmap.htm
1405
+ const rtc = this.cpu.devices.rtc
1406
+
1407
+ // master
1408
+ rtc.cmos_write(
1409
+ CMOS_BIOS_DISKTRANSFLAG, // TODO: what is this doing, setting LBA translation?
1410
+ rtc.cmos_read(CMOS_BIOS_DISKTRANSFLAG) |
1411
+ (1 << (this.channel_nr * 4)),
1412
+ )
1413
+
1414
+ // set hard disk type (CMOS_DISK_DATA = 0x12) of C: to 0b1111, keep type of D:
1415
+ // bits 0-3: hard disk type of D:
1416
+ // bits 4-7: hard disk type of C:
1417
+ // TODO: should this not also set CMOS_DISK_DRIVE1_TYPE to a hard disk type (see SeaBIOS rtc.h)?
1418
+ rtc.cmos_write(
1419
+ CMOS_DISK_DATA,
1420
+ (rtc.cmos_read(CMOS_DISK_DATA) & 0x0f) | 0xf0,
1421
+ )
1422
+
1423
+ const drive_reg =
1424
+ this.channel_nr === 0
1425
+ ? CMOS_DISK_DRIVE1_CYL
1426
+ : CMOS_DISK_DRIVE2_CYL // 0x1B : 0x24 (drive C: or D:)
1427
+ rtc.cmos_write(drive_reg + 0, this.cylinder_count & 0xff) // number of cylinders least significant byte
1428
+ rtc.cmos_write(drive_reg + 1, (this.cylinder_count >> 8) & 0xff) // number of cylinders most significant byte
1429
+ rtc.cmos_write(drive_reg + 2, this.head_count & 0xff) // number of heads
1430
+ rtc.cmos_write(drive_reg + 3, 0xff) // write precomp cylinder least significant byte
1431
+ rtc.cmos_write(drive_reg + 4, 0xff) // write precomp cylinder most significant byte
1432
+ rtc.cmos_write(drive_reg + 5, 0xc8) // control byte
1433
+ rtc.cmos_write(drive_reg + 6, this.cylinder_count & 0xff) // landing zone least significant byte
1434
+ rtc.cmos_write(drive_reg + 7, (this.cylinder_count >> 8) & 0xff) // landing zone most significant byte
1435
+ rtc.cmos_write(drive_reg + 8, this.sectors_per_track & 0xff) // number of sectors
1436
+ }
1437
+
1438
+ if (this.channel.cpu) {
1439
+ this.push_irq()
1440
+ }
1441
+ }
1442
+
1443
+ device_reset(): void {
1444
+ if (this.is_atapi) {
1445
+ this.status_reg = 0
1446
+ this.sector_count_reg = 1
1447
+ this.error_reg = 1
1448
+ this.lba_low_reg = 1
1449
+ this.lba_mid_reg = ATAPI_SIGNATURE_LO // TODO: missing documentation
1450
+ this.lba_high_reg = ATAPI_SIGNATURE_HI
1451
+ } else {
1452
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_ERR
1453
+ this.sector_count_reg = 1
1454
+ this.error_reg = 1
1455
+ this.lba_low_reg = 1
1456
+ this.lba_mid_reg = 0
1457
+ this.lba_high_reg = 0
1458
+ }
1459
+ this.cancel_io_operations()
1460
+ }
1461
+
1462
+ push_irq(): void {
1463
+ this.channel.push_irq()
1464
+ }
1465
+
1466
+ ata_abort_command(): void {
1467
+ this.error_reg = ATA_ER_ABRT
1468
+ this.status_reg = ATA_SR_DRDY | ATA_SR_ERR
1469
+ this.push_irq()
1470
+ }
1471
+
1472
+ capture_regs(): string {
1473
+ return (
1474
+ `ST=${h(this.status_reg & 0xff)} ER=${h(this.error_reg & 0xff)} ` +
1475
+ `SC=${h(this.sector_count_reg & 0xff)} LL=${h(this.lba_low_reg & 0xff)} ` +
1476
+ `LM=${h(this.lba_mid_reg & 0xff)} LH=${h(this.lba_high_reg & 0xff)} ` +
1477
+ `FE=${h(this.features_reg & 0xff)}`
1478
+ )
1479
+ }
1480
+
1481
+ ata_command(cmd: number): void {
1482
+ if (
1483
+ !this.drive_connected &&
1484
+ cmd !== ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC
1485
+ ) {
1486
+ dbg_log(
1487
+ `${this.name}: ATA command ${ATA_CMD_NAME[cmd]} (${h(cmd)}) ignored: no slave drive connected`,
1488
+ LOG_DISK,
1489
+ )
1490
+ return
1491
+ }
1492
+
1493
+ const regs_pre = DEBUG ? this.capture_regs() : undefined
1494
+ let do_dbg_log = DEBUG
1495
+
1496
+ this.current_command = cmd
1497
+ this.error_reg = 0
1498
+
1499
+ switch (cmd) {
1500
+ case ATA_CMD_DEVICE_RESET:
1501
+ this.data_pointer = 0
1502
+ this.data_end = 0
1503
+ this.data_length = 0
1504
+ this.device_reset()
1505
+ this.push_irq()
1506
+ break
1507
+
1508
+ case ATA_CMD_10h:
1509
+ this.lba_mid_reg = 0
1510
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1511
+ this.push_irq()
1512
+ break
1513
+
1514
+ case ATA_CMD_READ_NATIVE_MAX_ADDRESS: {
1515
+ const last_sector = this.sector_count - 1
1516
+ this.lba_low_reg = last_sector & 0xff
1517
+ this.lba_mid_reg = (last_sector >> 8) & 0xff
1518
+ this.lba_high_reg = (last_sector >> 16) & 0xff
1519
+ this.device_reg =
1520
+ (this.device_reg & 0xf0) | ((last_sector >> 24) & 0x0f)
1521
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1522
+ this.push_irq()
1523
+ break
1524
+ }
1525
+
1526
+ case ATA_CMD_READ_NATIVE_MAX_ADDRESS_EXT: {
1527
+ const last_sector = this.sector_count - 1
1528
+ this.lba_low_reg = last_sector & 0xff
1529
+ this.lba_mid_reg = (last_sector >> 8) & 0xff
1530
+ this.lba_high_reg = (last_sector >> 16) & 0xff
1531
+ this.lba_low_reg |= ((last_sector >> 24) << 8) & 0xff00
1532
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1533
+ this.push_irq()
1534
+ break
1535
+ }
1536
+
1537
+ case ATA_CMD_READ_SECTORS:
1538
+ do_dbg_log = false
1539
+ if (this.is_atapi) {
1540
+ this.lba_mid_reg = ATAPI_SIGNATURE_LO // see [ATA8-ACS] 4.3
1541
+ this.lba_high_reg = ATAPI_SIGNATURE_HI
1542
+ this.ata_abort_command()
1543
+ } else {
1544
+ this.ata_read_sectors(cmd)
1545
+ }
1546
+ break
1547
+
1548
+ case ATA_CMD_READ_SECTORS_EXT:
1549
+ case ATA_CMD_READ_MULTIPLE:
1550
+ case ATA_CMD_READ_MULTIPLE_EXT:
1551
+ do_dbg_log = false
1552
+ if (this.is_atapi) {
1553
+ this.ata_abort_command()
1554
+ } else {
1555
+ this.ata_read_sectors(cmd)
1556
+ }
1557
+ break
1558
+
1559
+ case ATA_CMD_WRITE_SECTORS:
1560
+ case ATA_CMD_WRITE_SECTORS_EXT:
1561
+ case ATA_CMD_WRITE_MULTIPLE:
1562
+ case ATA_CMD_WRITE_MULTIPLE_EXT:
1563
+ do_dbg_log = false
1564
+ if (this.is_atapi) {
1565
+ this.ata_abort_command()
1566
+ } else {
1567
+ this.ata_write_sectors(cmd)
1568
+ }
1569
+ break
1570
+
1571
+ case ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC:
1572
+ // the behaviour of this command is independent of the selected device
1573
+ this.channel.master.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1574
+ this.channel.master.error_reg = 0x01 // Master drive passed, slave drive passed or not present
1575
+ this.channel.master.push_irq()
1576
+ if (this.channel.slave.drive_connected) {
1577
+ this.channel.slave.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1578
+ this.channel.slave.error_reg = 0x01 // Slave drive passed
1579
+ this.channel.slave.push_irq()
1580
+ }
1581
+ break
1582
+
1583
+ case ATA_CMD_INITIALIZE_DEVICE_PARAMETERS:
1584
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1585
+ this.push_irq()
1586
+ break
1587
+
1588
+ case ATA_CMD_PACKET:
1589
+ if (this.is_atapi) {
1590
+ do_dbg_log = false
1591
+ this.data_allocate(12)
1592
+ this.data_end = 12
1593
+ this.sector_count_reg = 0x01 // 0x01: indicates transfer of a command packet (C/D)
1594
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
1595
+ this.push_irq()
1596
+ } else {
1597
+ this.ata_abort_command()
1598
+ }
1599
+ break
1600
+
1601
+ case ATA_CMD_IDENTIFY_PACKET_DEVICE:
1602
+ if (this.is_atapi) {
1603
+ this.create_identify_packet()
1604
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
1605
+ this.push_irq()
1606
+ } else {
1607
+ this.ata_abort_command()
1608
+ }
1609
+ break
1610
+
1611
+ case ATA_CMD_SET_MULTIPLE_MODE:
1612
+ // Logical sectors per DRQ Block in word 1
1613
+ dbg_log(
1614
+ this.name +
1615
+ ': logical sectors per DRQ Block: ' +
1616
+ h(this.sector_count_reg & 0xff),
1617
+ LOG_DISK,
1618
+ )
1619
+ this.sectors_per_drq = this.sector_count_reg & 0xff
1620
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1621
+ this.push_irq()
1622
+ break
1623
+
1624
+ case ATA_CMD_READ_DMA:
1625
+ case ATA_CMD_READ_DMA_EXT:
1626
+ do_dbg_log = false
1627
+ this.ata_read_sectors_dma(cmd)
1628
+ break
1629
+
1630
+ case ATA_CMD_WRITE_DMA:
1631
+ case ATA_CMD_WRITE_DMA_EXT:
1632
+ do_dbg_log = false
1633
+ this.ata_write_sectors_dma(cmd)
1634
+ break
1635
+
1636
+ case ATA_CMD_READ_VERIFY_SECTORS:
1637
+ // TODO: check that lba_low/mid/high and sector_count regs are within the bounds of the disk's size
1638
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1639
+ this.push_irq()
1640
+ break
1641
+
1642
+ case ATA_CMD_GET_MEDIA_STATUS:
1643
+ if (this.is_atapi) {
1644
+ if (!this.buffer) {
1645
+ this.error_reg |= 0x02 // NM: No Media
1646
+ }
1647
+ if (this.medium_changed) {
1648
+ this.error_reg |= 0x20 // MC: Media Change
1649
+ this.medium_changed = false
1650
+ }
1651
+ this.error_reg |= 0x40 // WP: Write Protect
1652
+ }
1653
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1654
+ this.push_irq()
1655
+ break
1656
+
1657
+ case ATA_CMD_STANDBY_IMMEDIATE:
1658
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1659
+ this.push_irq()
1660
+ break
1661
+
1662
+ case ATA_CMD_IDLE_IMMEDIATE:
1663
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1664
+ this.push_irq()
1665
+ break
1666
+
1667
+ case ATA_CMD_FLUSH_CACHE:
1668
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1669
+ this.push_irq()
1670
+ break
1671
+
1672
+ case ATA_CMD_FLUSH_CACHE_EXT:
1673
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1674
+ this.push_irq()
1675
+ break
1676
+
1677
+ case ATA_CMD_IDENTIFY_DEVICE:
1678
+ if (this.is_atapi) {
1679
+ this.lba_mid_reg = ATAPI_SIGNATURE_LO // see [ATA8-ACS] 4.3
1680
+ this.lba_high_reg = ATAPI_SIGNATURE_HI
1681
+ this.ata_abort_command()
1682
+ } else {
1683
+ this.create_identify_packet()
1684
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
1685
+ this.push_irq()
1686
+ }
1687
+ break
1688
+
1689
+ case ATA_CMD_SET_FEATURES:
1690
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1691
+ this.push_irq()
1692
+ break
1693
+
1694
+ case ATA_CMD_MEDIA_LOCK:
1695
+ this.status_reg = ATA_SR_DRDY
1696
+ this.push_irq()
1697
+ break
1698
+
1699
+ case ATA_CMD_SECURITY_FREEZE_LOCK:
1700
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1701
+ this.push_irq()
1702
+ break
1703
+
1704
+ case ATA_CMD_SET_MAX:
1705
+ this.ata_abort_command()
1706
+ break
1707
+
1708
+ case ATA_CMD_NOP:
1709
+ this.ata_abort_command()
1710
+ break
1711
+
1712
+ case ATA_CMD_F0h:
1713
+ dbg_log(
1714
+ `${this.name}: error: unimplemented vendor-specific ATA command ${h(cmd)}: ABORT [${this.capture_regs()}]`,
1715
+ LOG_DISK,
1716
+ )
1717
+ this.ata_abort_command()
1718
+ break
1719
+
1720
+ default:
1721
+ dbg_assert(
1722
+ false,
1723
+ `${this.name}: error: unimplemented ATA command ${h(cmd)}: ABORT [${this.capture_regs()}]`,
1724
+ LOG_DISK,
1725
+ )
1726
+ this.ata_abort_command()
1727
+ break
1728
+ }
1729
+
1730
+ if (DEBUG && do_dbg_log) {
1731
+ const regs_msg = `[${regs_pre}] -> [${this.capture_regs()}]`
1732
+ const result =
1733
+ this.status_reg & ATA_SR_ERR
1734
+ ? this.error_reg & ATA_ER_ABRT
1735
+ ? 'ABORT'
1736
+ : 'ERROR'
1737
+ : 'OK'
1738
+ dbg_log(
1739
+ `${this.name}: ATA command ${ATA_CMD_NAME[cmd]} (${h(cmd)}): ${result} ${regs_msg}`,
1740
+ LOG_DISK,
1741
+ )
1742
+ }
1743
+ }
1744
+
1745
+ atapi_handle(): void {
1746
+ const cmd = this.data[0]
1747
+ const cmd_name = ATAPI_CMD[cmd] ? ATAPI_CMD[cmd].name : '<undefined>'
1748
+ const cmd_flags = ATAPI_CMD[cmd] ? ATAPI_CMD[cmd].flags : ATAPI_CF_NONE
1749
+ const regs_pre = DEBUG ? this.capture_regs() : undefined
1750
+
1751
+ let do_dbg_log = DEBUG
1752
+ let dbg_log_extra: string | undefined
1753
+
1754
+ this.data_pointer = 0
1755
+ this.current_atapi_command = cmd
1756
+
1757
+ if (cmd !== ATAPI_CMD_REQUEST_SENSE) // TODO
1758
+ {
1759
+ this.atapi_sense_key = 0
1760
+ this.atapi_add_sense = 0
1761
+ }
1762
+
1763
+ if (!this.buffer && cmd_flags & ATAPI_CF_NEEDS_DISK) {
1764
+ this.atapi_check_condition_response(
1765
+ ATAPI_SK_NOT_READY,
1766
+ ATAPI_ASC_MEDIUM_NOT_PRESENT,
1767
+ )
1768
+ this.push_irq()
1769
+ if (DEBUG) {
1770
+ dbg_log(
1771
+ `${this.name}: ATAPI command ${cmd_name} (${h(cmd)}) without medium: ERROR [${regs_pre}]`,
1772
+ LOG_DISK,
1773
+ )
1774
+ }
1775
+ return
1776
+ }
1777
+
1778
+ switch (cmd) {
1779
+ case ATAPI_CMD_TEST_UNIT_READY:
1780
+ if (this.buffer) {
1781
+ this.data_allocate(0)
1782
+ this.data_end = this.data_length
1783
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1784
+ } else {
1785
+ this.atapi_check_condition_response(
1786
+ ATAPI_SK_NOT_READY,
1787
+ ATAPI_ASC_MEDIUM_NOT_PRESENT,
1788
+ )
1789
+ }
1790
+ break
1791
+
1792
+ case ATAPI_CMD_REQUEST_SENSE:
1793
+ this.data_allocate(this.data[4])
1794
+ this.data_end = this.data_length
1795
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
1796
+ this.data[0] = 0x80 | 0x70 // valid | SCSI error code
1797
+ this.data[2] = this.atapi_sense_key // SCSI sense key
1798
+ this.data[7] = 8 // SCSI additional sense length (fixed 8 for this error code 0x70)
1799
+ this.data[12] = this.atapi_add_sense // SCSI additional sense code
1800
+ this.atapi_sense_key = 0
1801
+ this.atapi_add_sense = 0
1802
+ break
1803
+
1804
+ case ATAPI_CMD_INQUIRY: {
1805
+ const length = this.data[4]
1806
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
1807
+ dbg_log_extra =
1808
+ 'lun=' + h(this.data[1], 2) + ' length=' + length
1809
+ // for data layout see [CD-SCSI-2] "INQUIRY Command"
1810
+ this.data.set([
1811
+ // 0: Device-type, Removable, ANSI-Version, Response Format
1812
+ 0x05, 0x80, 0x01, 0x31,
1813
+ // 4: Additional length, Reserved, Reserved, Reserved
1814
+ 31, 0, 0, 0,
1815
+ // 8: Vendor Identification "SONY "
1816
+ 0x53, 0x4f, 0x4e, 0x59, 0x20, 0x20, 0x20, 0x20,
1817
+ // 16: Product Identification "CD-ROM CDU-1000 "
1818
+ 0x43, 0x44, 0x2d, 0x52, 0x4f, 0x4d, 0x20, 0x43, 0x44, 0x55,
1819
+ 0x2d, 0x31, 0x30, 0x30, 0x30, 0x20,
1820
+ // 32: Product Revision Level "1.1a"
1821
+ 0x31, 0x2e, 0x31, 0x61,
1822
+ ])
1823
+ this.data_end = this.data_length = Math.min(36, length)
1824
+ break
1825
+ }
1826
+
1827
+ case ATAPI_CMD_MODE_SENSE_6:
1828
+ this.data_allocate(this.data[4])
1829
+ this.data_end = this.data_length
1830
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
1831
+ break
1832
+
1833
+ case ATAPI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
1834
+ this.data_allocate(0)
1835
+ this.data_end = this.data_length
1836
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1837
+ break
1838
+
1839
+ case ATAPI_CMD_READ_CAPACITY: {
1840
+ const count = this.sector_count - 1
1841
+ this.data_set(
1842
+ new Uint8Array([
1843
+ (count >> 24) & 0xff,
1844
+ (count >> 16) & 0xff,
1845
+ (count >> 8) & 0xff,
1846
+ count & 0xff,
1847
+ 0,
1848
+ 0,
1849
+ (this.sector_size >> 8) & 0xff,
1850
+ this.sector_size & 0xff,
1851
+ ]),
1852
+ )
1853
+ this.data_end = this.data_length
1854
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
1855
+ break
1856
+ }
1857
+
1858
+ case ATAPI_CMD_READ_10:
1859
+ case ATAPI_CMD_READ_12:
1860
+ do_dbg_log = false
1861
+ if (this.features_reg & 1) {
1862
+ this.atapi_read_dma(this.data)
1863
+ } else {
1864
+ this.atapi_read(this.data)
1865
+ }
1866
+ break
1867
+
1868
+ case ATAPI_CMD_READ_SUBCHANNEL: {
1869
+ const length = this.data[8]
1870
+ dbg_log_extra = 'length=' + length
1871
+ this.data_allocate(Math.min(8, length))
1872
+ this.data_end = this.data_length
1873
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
1874
+ break
1875
+ }
1876
+
1877
+ case ATAPI_CMD_READ_TOC_PMA_ATIP: {
1878
+ const length = this.data[8] | (this.data[7] << 8)
1879
+ const format = this.data[9] >> 6
1880
+ dbg_log_extra = `${h(format, 2)} length=${length} ${!!(this.data[1] & 2)} ${h(this.data[6])}`
1881
+
1882
+ this.data_allocate(length)
1883
+ this.data_end = this.data_length
1884
+ if (format === 0) {
1885
+ const sector_count = this.sector_count
1886
+ this.data.set(
1887
+ new Uint8Array([
1888
+ 0,
1889
+ 18, // length
1890
+ 1,
1891
+ 1, // first and last session
1892
+
1893
+ 0,
1894
+ 0x14,
1895
+ 1, // track number
1896
+ 0,
1897
+ 0,
1898
+ 0,
1899
+ 0,
1900
+ 0,
1901
+
1902
+ 0,
1903
+ 0x16,
1904
+ 0xaa, // track number
1905
+ 0,
1906
+ sector_count >> 24,
1907
+ (sector_count >> 16) & 0xff,
1908
+ (sector_count >> 8) & 0xff,
1909
+ sector_count & 0xff,
1910
+ ]),
1911
+ )
1912
+ } else if (format === 1) {
1913
+ this.data.set(
1914
+ new Uint8Array([
1915
+ 0,
1916
+ 10, // length
1917
+ 1,
1918
+ 1, // first and last session
1919
+ 0,
1920
+ 0,
1921
+ 0,
1922
+ 0,
1923
+ 0,
1924
+ 0,
1925
+ 0,
1926
+ 0,
1927
+ ]),
1928
+ )
1929
+ } else {
1930
+ dbg_assert(
1931
+ false,
1932
+ this.name + ': error: unimplemented format: ' + format,
1933
+ )
1934
+ }
1935
+
1936
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
1937
+ break
1938
+ }
1939
+
1940
+ case ATAPI_CMD_GET_CONFIGURATION: {
1941
+ const length = Math.min(this.data[8] | (this.data[7] << 8), 32)
1942
+ dbg_log_extra = 'length=' + length
1943
+ this.data_allocate(length)
1944
+ this.data_end = this.data_length
1945
+ this.data[0] = ((length - 4) >> 24) & 0xff
1946
+ this.data[1] = ((length - 4) >> 16) & 0xff
1947
+ this.data[2] = ((length - 4) >> 8) & 0xff
1948
+ this.data[3] = (length - 4) & 0xff
1949
+ this.data[6] = 0x08
1950
+ this.data[10] = 3
1951
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
1952
+ break
1953
+ }
1954
+
1955
+ case ATAPI_CMD_READ_DISK_INFORMATION:
1956
+ this.data_allocate(0)
1957
+ this.data_end = this.data_length
1958
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1959
+ break
1960
+
1961
+ case ATAPI_CMD_READ_TRACK_INFORMATION:
1962
+ dbg_log_extra = 'unimplemented'
1963
+ this.atapi_check_condition_response(
1964
+ ATAPI_SK_ILLEGAL_REQUEST,
1965
+ ATAPI_ASC_INV_FIELD_IN_CMD_PACKET,
1966
+ )
1967
+ break
1968
+
1969
+ case ATAPI_CMD_MODE_SENSE_10: {
1970
+ const length = this.data[8] | (this.data[7] << 8)
1971
+ const page_code = this.data[2]
1972
+ dbg_log_extra =
1973
+ 'page_code=' + h(page_code) + ' length=' + length
1974
+ if (page_code === 0x2a) {
1975
+ this.data_allocate(Math.min(30, length))
1976
+ }
1977
+ this.data_end = this.data_length
1978
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
1979
+ break
1980
+ }
1981
+
1982
+ case ATAPI_CMD_MECHANISM_STATUS:
1983
+ this.data_allocate(this.data[9] | (this.data[8] << 8))
1984
+ this.data_end = this.data_length
1985
+ this.data[5] = 1
1986
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
1987
+ break
1988
+
1989
+ case ATAPI_CMD_START_STOP_UNIT: {
1990
+ const loej_start = this.data[4] & 0x3
1991
+ dbg_log_extra = `Immed=${h(this.data[1] & 1)} LoEj/Start=${h(loej_start)}`
1992
+ if (this.buffer && loej_start === 0x2) {
1993
+ dbg_log_extra += ': disk ejected'
1994
+ this.medium_changed = true
1995
+ this.buffer = null
1996
+ }
1997
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
1998
+ this.data_allocate(0)
1999
+ this.data_end = this.data_length
2000
+ break
2001
+ }
2002
+
2003
+ case ATAPI_CMD_PAUSE:
2004
+ case ATAPI_CMD_GET_EVENT_STATUS_NOTIFICATION:
2005
+ dbg_log_extra = 'unimplemented'
2006
+ this.atapi_check_condition_response(
2007
+ ATAPI_SK_ILLEGAL_REQUEST,
2008
+ ATAPI_ASC_INV_FIELD_IN_CMD_PACKET,
2009
+ )
2010
+ break
2011
+
2012
+ case ATAPI_CMD_READ_CD:
2013
+ dbg_log_extra = 'unimplemented'
2014
+ this.data_allocate(0)
2015
+ this.data_end = this.data_length
2016
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
2017
+ break
2018
+
2019
+ default:
2020
+ dbg_assert(
2021
+ false,
2022
+ `${this.name}: error: unimplemented ATAPI command ${h(this.data[0])}`,
2023
+ LOG_DISK,
2024
+ )
2025
+ this.atapi_check_condition_response(
2026
+ ATAPI_SK_ILLEGAL_REQUEST,
2027
+ ATAPI_ASC_INV_FIELD_IN_CMD_PACKET,
2028
+ )
2029
+ break
2030
+ }
2031
+
2032
+ this.sector_count_reg = (this.sector_count_reg & ~7) | 2
2033
+
2034
+ if ((this.status_reg & ATA_SR_BSY) === 0) {
2035
+ this.push_irq()
2036
+ }
2037
+
2038
+ if ((this.status_reg & ATA_SR_BSY) === 0 && this.data_length === 0) {
2039
+ this.sector_count_reg |= 1
2040
+ this.status_reg &= ~ATA_SR_DRQ
2041
+ }
2042
+
2043
+ if (DEBUG && do_dbg_log) {
2044
+ const regs_msg = `[${regs_pre}] -> [${this.capture_regs()}]`
2045
+ const result =
2046
+ this.status_reg & ATA_SR_ERR
2047
+ ? this.error_reg & ATA_ER_ABRT
2048
+ ? 'ABORT'
2049
+ : 'ERROR'
2050
+ : 'OK'
2051
+ dbg_log_extra = dbg_log_extra ? ` ${dbg_log_extra}:` : ''
2052
+ dbg_log(
2053
+ `${this.name}: ATAPI command ${cmd_name} (${h(cmd)}):${dbg_log_extra} ${result} ${regs_msg}`,
2054
+ LOG_DISK,
2055
+ )
2056
+ }
2057
+ }
2058
+
2059
+ atapi_check_condition_response(
2060
+ sense_key: number,
2061
+ additional_sense: number,
2062
+ ): void {
2063
+ // Setup ATA registers to CHECK CONDITION state.
2064
+ // The sense state (sense_key and additional_sense) must be requested
2065
+ // by the host using ATAPI_CMD_REQUEST_SENSE immediately following a
2066
+ // CHECK CONDITION response or else it will be lost.
2067
+ // https://github.com/qemu/qemu/blob/757a34115e7491744a63dfc3d291fd1de5297ee2/hw/ide/atapi.c#L186
2068
+ this.data_allocate(0)
2069
+ this.data_end = this.data_length
2070
+ this.status_reg = ATA_SR_DRDY | ATA_SR_COND
2071
+ this.error_reg = sense_key << 4
2072
+ this.sector_count_reg = (this.sector_count_reg & ~7) | 2 | 1
2073
+ this.atapi_sense_key = sense_key
2074
+ this.atapi_add_sense = additional_sense
2075
+ }
2076
+
2077
+ do_write(): void {
2078
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
2079
+
2080
+ dbg_assert(this.data_length <= this.data.length)
2081
+ const data = this.data.subarray(0, this.data_length)
2082
+
2083
+ //dbg_log(hex_dump(data), LOG_DISK);
2084
+ dbg_assert(this.data_length % 512 === 0)
2085
+ this.ata_advance(this.current_command, this.data_length / 512)
2086
+ this.push_irq()
2087
+
2088
+ this.buffer!.set(this.write_dest, data, function () {})
2089
+
2090
+ this.report_write(this.data_length)
2091
+ }
2092
+
2093
+ atapi_read(cmd: Uint8Array): void {
2094
+ // Note: Big Endian
2095
+ const lba = (cmd[2] << 24) | (cmd[3] << 16) | (cmd[4] << 8) | cmd[5]
2096
+ let count =
2097
+ cmd[0] === ATAPI_CMD_READ_12
2098
+ ? (cmd[6] << 24) | (cmd[7] << 16) | (cmd[8] << 8) | cmd[9]
2099
+ : (cmd[7] << 8) | cmd[8]
2100
+ count >>>= 0
2101
+ const flags = cmd[1]
2102
+ let byte_count = count * this.sector_size
2103
+ const start = lba * this.sector_size
2104
+
2105
+ if (LOG_DETAILS & LOG_DETAIL_RW) {
2106
+ dbg_log(
2107
+ this.name +
2108
+ ': CD read lba=' +
2109
+ h(lba) +
2110
+ ' lbacount=' +
2111
+ h(count) +
2112
+ ' bytecount=' +
2113
+ h(byte_count) +
2114
+ ' flags=' +
2115
+ h(flags),
2116
+ LOG_DISK,
2117
+ )
2118
+ }
2119
+
2120
+ this.data_length = 0
2121
+ let req_length =
2122
+ ((this.lba_high_reg << 8) & 0xff00) | (this.lba_mid_reg & 0xff)
2123
+ //dbg_log(this.name + ": " + h(this.lba_high_reg, 2) + " " + h(this.lba_mid_reg, 2), LOG_DISK);
2124
+ this.lba_mid_reg = this.lba_high_reg = 0 // oak technology driver (windows 3.0)
2125
+
2126
+ if (req_length === 0xffff) req_length--
2127
+
2128
+ if (req_length > byte_count) {
2129
+ req_length = byte_count
2130
+ }
2131
+
2132
+ if (!this.buffer) {
2133
+ dbg_assert(false, this.name + ': CD read: no buffer', LOG_DISK)
2134
+ this.status_reg = 0xff
2135
+ this.error_reg = 0x41
2136
+ this.push_irq()
2137
+ } else if (start >= this.buffer.byteLength) {
2138
+ dbg_assert(
2139
+ false,
2140
+ this.name +
2141
+ ': CD read: Outside of disk end=' +
2142
+ h(start + byte_count) +
2143
+ ' size=' +
2144
+ h(this.buffer.byteLength),
2145
+ LOG_DISK,
2146
+ )
2147
+
2148
+ this.status_reg = 0xff
2149
+ this.push_irq()
2150
+ } else if (byte_count === 0) {
2151
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
2152
+
2153
+ this.data_pointer = 0
2154
+ //this.push_irq();
2155
+ } else {
2156
+ byte_count = Math.min(byte_count, this.buffer.byteLength - start)
2157
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_BSY
2158
+ this.report_read_start()
2159
+
2160
+ this.read_buffer(start, byte_count, (data) => {
2161
+ if (LOG_DETAILS & LOG_DETAIL_RW) {
2162
+ dbg_log(this.name + ': CD read: data arrived', LOG_DISK)
2163
+ }
2164
+ this.data_set(data)
2165
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
2166
+ this.sector_count_reg = (this.sector_count_reg & ~7) | 2
2167
+
2168
+ this.push_irq()
2169
+
2170
+ req_length &= ~3
2171
+
2172
+ this.data_end = req_length
2173
+ if (this.data_end > this.data_length) {
2174
+ this.data_end = this.data_length
2175
+ }
2176
+ this.lba_mid_reg = this.data_end & 0xff
2177
+ this.lba_high_reg = (this.data_end >> 8) & 0xff
2178
+
2179
+ this.report_read_end(byte_count)
2180
+ })
2181
+ }
2182
+ }
2183
+
2184
+ atapi_read_dma(cmd: Uint8Array): void {
2185
+ // Note: Big Endian
2186
+ const lba = (cmd[2] << 24) | (cmd[3] << 16) | (cmd[4] << 8) | cmd[5]
2187
+ let count =
2188
+ cmd[0] === ATAPI_CMD_READ_12
2189
+ ? (cmd[6] << 24) | (cmd[7] << 16) | (cmd[8] << 8) | cmd[9]
2190
+ : (cmd[7] << 8) | cmd[8]
2191
+ count >>>= 0
2192
+ const flags = cmd[1]
2193
+ const byte_count = count * this.sector_size
2194
+ const start = lba * this.sector_size
2195
+
2196
+ dbg_log(
2197
+ this.name +
2198
+ ': CD read DMA lba=' +
2199
+ h(lba) +
2200
+ ' lbacount=' +
2201
+ h(count) +
2202
+ ' bytecount=' +
2203
+ h(byte_count) +
2204
+ ' flags=' +
2205
+ h(flags),
2206
+ LOG_DISK,
2207
+ )
2208
+
2209
+ if (start >= this.buffer!.byteLength) {
2210
+ dbg_assert(
2211
+ false,
2212
+ this.name +
2213
+ ': CD read: Outside of disk end=' +
2214
+ h(start + byte_count) +
2215
+ ' size=' +
2216
+ h(this.buffer!.byteLength),
2217
+ LOG_DISK,
2218
+ )
2219
+
2220
+ this.status_reg = 0xff
2221
+ this.push_irq()
2222
+ } else {
2223
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_BSY
2224
+ this.report_read_start()
2225
+
2226
+ this.read_buffer(start, byte_count, (data) => {
2227
+ dbg_log(this.name + ': atapi_read_dma: Data arrived', LOG_DISK)
2228
+ this.report_read_end(byte_count)
2229
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
2230
+ this.sector_count_reg = (this.sector_count_reg & ~7) | 2
2231
+ this.data_set(data)
2232
+
2233
+ this.do_atapi_dma()
2234
+ })
2235
+ }
2236
+ }
2237
+
2238
+ do_atapi_dma(): void {
2239
+ if ((this.channel.dma_status & 1) === 0) {
2240
+ dbg_log(this.name + ': do_atapi_dma: Status not set', LOG_DISK)
2241
+ return
2242
+ }
2243
+
2244
+ if ((this.status_reg & ATA_SR_DRQ) === 0) {
2245
+ dbg_log(this.name + ': do_atapi_dma: DRQ not set', LOG_DISK)
2246
+ return
2247
+ }
2248
+
2249
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
2250
+ dbg_log(
2251
+ this.name + ': ATAPI DMA transfer len=' + this.data_length,
2252
+ LOG_DISK,
2253
+ )
2254
+ }
2255
+
2256
+ let prdt_start = this.channel.prdt_addr
2257
+ let offset = 0
2258
+
2259
+ const data = this.data
2260
+
2261
+ let end: number
2262
+ do {
2263
+ const addr = this.cpu.read32s(prdt_start)
2264
+ let count = this.cpu.read16(prdt_start + 4)
2265
+ end = this.cpu.read8(prdt_start + 7) & 0x80
2266
+
2267
+ if (!count) {
2268
+ count = 0x10000
2269
+ }
2270
+
2271
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
2272
+ dbg_log(
2273
+ this.name +
2274
+ ': DMA read dest=' +
2275
+ h(addr) +
2276
+ ' count=' +
2277
+ h(count) +
2278
+ ' datalen=' +
2279
+ h(this.data_length),
2280
+ LOG_DISK,
2281
+ )
2282
+ }
2283
+ this.cpu.write_blob(
2284
+ data.subarray(
2285
+ offset,
2286
+ Math.min(offset + count, this.data_length),
2287
+ ),
2288
+ addr,
2289
+ )
2290
+
2291
+ offset += count
2292
+ prdt_start += 8
2293
+
2294
+ if (offset >= this.data_length && !end) {
2295
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
2296
+ dbg_log(
2297
+ this.name +
2298
+ ': leave early end=' +
2299
+ +end +
2300
+ ' offset=' +
2301
+ h(offset) +
2302
+ ' data_length=' +
2303
+ h(this.data_length) +
2304
+ ' cmd=' +
2305
+ h(this.current_command),
2306
+ LOG_DISK,
2307
+ )
2308
+ }
2309
+ break
2310
+ }
2311
+ } while (!end)
2312
+
2313
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
2314
+ dbg_log(this.name + ': end offset=' + offset, LOG_DISK)
2315
+ }
2316
+
2317
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
2318
+ this.channel.dma_status &= ~1
2319
+ this.sector_count_reg = (this.sector_count_reg & ~7) | 3
2320
+ this.push_irq()
2321
+ }
2322
+
2323
+ read_data(length: number): number {
2324
+ if (this.data_pointer < this.data_end) {
2325
+ dbg_assert(this.data_pointer + length - 1 < this.data_end)
2326
+ dbg_assert(
2327
+ this.data_pointer % length === 0,
2328
+ h(this.data_pointer) + ' ' + length,
2329
+ )
2330
+
2331
+ let result: number
2332
+ if (length === 1) {
2333
+ result = this.data[this.data_pointer]
2334
+ } else if (length === 2) {
2335
+ result = this.data16[this.data_pointer >>> 1]
2336
+ } else {
2337
+ result = this.data32[this.data_pointer >>> 2]
2338
+ }
2339
+
2340
+ this.data_pointer += length
2341
+
2342
+ const align = (this.data_end & 0xfff) === 0 ? 0xfff : 0xff
2343
+
2344
+ if (LOG_DETAILS & LOG_DETAIL_RW) {
2345
+ if ((this.data_pointer & align) === 0) {
2346
+ dbg_log(
2347
+ this.name +
2348
+ ': read 1F0: ' +
2349
+ h(this.data[this.data_pointer], 2) +
2350
+ ' cur=' +
2351
+ h(this.data_pointer) +
2352
+ ' cnt=' +
2353
+ h(this.data_length),
2354
+ LOG_DISK,
2355
+ )
2356
+ }
2357
+ }
2358
+
2359
+ if (this.data_pointer >= this.data_end) {
2360
+ this.read_end()
2361
+ }
2362
+
2363
+ return result
2364
+ } else {
2365
+ if (LOG_DETAILS & LOG_DETAIL_RW) {
2366
+ dbg_log(this.name + ': read 1F0: empty', LOG_DISK)
2367
+ }
2368
+ this.data_pointer += length
2369
+ return 0
2370
+ }
2371
+ }
2372
+
2373
+ read_end(): void {
2374
+ if (LOG_DETAILS & LOG_DETAIL_RW) {
2375
+ dbg_log(
2376
+ this.name +
2377
+ ': read_end cmd=' +
2378
+ h(this.current_command) +
2379
+ ' data_pointer=' +
2380
+ h(this.data_pointer) +
2381
+ ' end=' +
2382
+ h(this.data_end) +
2383
+ ' length=' +
2384
+ h(this.data_length),
2385
+ LOG_DISK,
2386
+ )
2387
+ }
2388
+
2389
+ if (this.current_command === ATA_CMD_PACKET) {
2390
+ if (this.data_end === this.data_length) {
2391
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
2392
+ this.sector_count_reg = (this.sector_count_reg & ~7) | 3
2393
+ this.push_irq()
2394
+ } else {
2395
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
2396
+ this.sector_count_reg = (this.sector_count_reg & ~7) | 2
2397
+ this.push_irq()
2398
+ const byte_count =
2399
+ ((this.lba_high_reg << 8) & 0xff00) |
2400
+ (this.lba_mid_reg & 0xff)
2401
+
2402
+ if (this.data_end + byte_count > this.data_length) {
2403
+ this.lba_mid_reg = (this.data_length - this.data_end) & 0xff
2404
+ this.lba_high_reg =
2405
+ ((this.data_length - this.data_end) >> 8) & 0xff
2406
+ this.data_end = this.data_length
2407
+ } else {
2408
+ this.data_end += byte_count
2409
+ }
2410
+ if (LOG_DETAILS & LOG_DETAIL_RW) {
2411
+ dbg_log(
2412
+ this.name + ': data_end=' + h(this.data_end),
2413
+ LOG_DISK,
2414
+ )
2415
+ }
2416
+ }
2417
+ } else {
2418
+ this.error_reg = 0
2419
+ if (this.data_pointer >= this.data_length) {
2420
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
2421
+ } else {
2422
+ let sector_count: number
2423
+ if (
2424
+ this.current_command === ATA_CMD_READ_MULTIPLE ||
2425
+ this.current_command === ATA_CMD_READ_MULTIPLE_EXT
2426
+ ) {
2427
+ sector_count = Math.min(
2428
+ this.sectors_per_drq,
2429
+ (this.data_length - this.data_end) / 512,
2430
+ )
2431
+ dbg_assert(sector_count % 1 === 0)
2432
+ } else {
2433
+ dbg_assert(
2434
+ this.current_command === ATA_CMD_READ_SECTORS ||
2435
+ this.current_command === ATA_CMD_READ_SECTORS_EXT,
2436
+ )
2437
+ sector_count = 1
2438
+ }
2439
+ this.ata_advance(this.current_command, sector_count)
2440
+ this.data_end += 512 * sector_count
2441
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
2442
+ this.push_irq()
2443
+ }
2444
+ }
2445
+ }
2446
+
2447
+ write_data_port(data: number, length: number): void {
2448
+ dbg_assert(this.data_pointer % length === 0)
2449
+
2450
+ if (this.data_pointer >= this.data_end) {
2451
+ dbg_log(
2452
+ this.name +
2453
+ ': redundant write to data port: ' +
2454
+ h(data) +
2455
+ ' count=' +
2456
+ h(this.data_end) +
2457
+ ' cur=' +
2458
+ h(this.data_pointer),
2459
+ LOG_DISK,
2460
+ )
2461
+ } else {
2462
+ const align = (this.data_end & 0xfff) === 0 ? 0xfff : 0xff
2463
+ if (LOG_DETAILS & LOG_DETAIL_RW) {
2464
+ if (
2465
+ ((this.data_pointer + length) & align) === 0 ||
2466
+ this.data_end < 20
2467
+ ) {
2468
+ dbg_log(
2469
+ this.name +
2470
+ ': data port: ' +
2471
+ h(data >>> 0) +
2472
+ ' count=' +
2473
+ h(this.data_end) +
2474
+ ' cur=' +
2475
+ h(this.data_pointer),
2476
+ LOG_DISK,
2477
+ )
2478
+ }
2479
+ }
2480
+
2481
+ if (length === 1) {
2482
+ this.data[this.data_pointer++] = data
2483
+ } else if (length === 2) {
2484
+ this.data16[this.data_pointer >>> 1] = data
2485
+ this.data_pointer += 2
2486
+ } else {
2487
+ this.data32[this.data_pointer >>> 2] = data
2488
+ this.data_pointer += 4
2489
+ }
2490
+
2491
+ dbg_assert(this.data_pointer <= this.data_end)
2492
+ if (this.data_pointer === this.data_end) {
2493
+ this.write_end()
2494
+ }
2495
+ }
2496
+ }
2497
+
2498
+ write_data_port8(data: number): void {
2499
+ this.write_data_port(data, 1)
2500
+ }
2501
+
2502
+ write_data_port16(data: number): void {
2503
+ this.write_data_port(data, 2)
2504
+ }
2505
+
2506
+ write_data_port32(data: number): void {
2507
+ this.write_data_port(data, 4)
2508
+ }
2509
+
2510
+ write_end(): void {
2511
+ if (this.current_command === ATA_CMD_PACKET) {
2512
+ this.atapi_handle()
2513
+ } else {
2514
+ if (LOG_DETAILS & LOG_DETAIL_RW) {
2515
+ dbg_log(
2516
+ this.name +
2517
+ ': write_end data_pointer=' +
2518
+ h(this.data_pointer) +
2519
+ ' data_length=' +
2520
+ h(this.data_length),
2521
+ LOG_DISK,
2522
+ )
2523
+ }
2524
+
2525
+ if (this.data_pointer >= this.data_length) {
2526
+ this.do_write()
2527
+ } else {
2528
+ dbg_assert(
2529
+ this.current_command === ATA_CMD_WRITE_SECTORS ||
2530
+ this.current_command === ATA_CMD_WRITE_SECTORS_EXT ||
2531
+ this.current_command === ATA_CMD_WRITE_MULTIPLE_EXT,
2532
+ 'Unexpected command: ' + h(this.current_command),
2533
+ )
2534
+
2535
+ // XXX: Should advance here, but do_write does all the advancing
2536
+ //this.ata_advance(this.current_command, 1);
2537
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
2538
+ this.data_end += 512
2539
+ this.push_irq()
2540
+ }
2541
+ }
2542
+ }
2543
+
2544
+ ata_advance(cmd: number, sectors: number): void {
2545
+ if (LOG_DETAILS & LOG_DETAIL_RW) {
2546
+ dbg_log(
2547
+ this.name +
2548
+ ': advance sectors=' +
2549
+ sectors +
2550
+ ' old_sector_count_reg=' +
2551
+ this.sector_count_reg,
2552
+ LOG_DISK,
2553
+ )
2554
+ }
2555
+ this.sector_count_reg -= sectors
2556
+
2557
+ let new_sector: number
2558
+ if (
2559
+ cmd === ATA_CMD_READ_SECTORS_EXT ||
2560
+ cmd === ATA_CMD_READ_MULTIPLE ||
2561
+ cmd === ATA_CMD_READ_DMA_EXT ||
2562
+ cmd === ATA_CMD_WRITE_SECTORS_EXT ||
2563
+ cmd === ATA_CMD_WRITE_MULTIPLE ||
2564
+ cmd === ATA_CMD_WRITE_DMA_EXT
2565
+ ) {
2566
+ new_sector = sectors + this.get_lba48()
2567
+ this.lba_low_reg =
2568
+ (new_sector & 0xff) | ((new_sector >> 16) & 0xff00)
2569
+ this.lba_mid_reg = (new_sector >> 8) & 0xff
2570
+ this.lba_high_reg = (new_sector >> 16) & 0xff
2571
+ } else if (this.is_lba) {
2572
+ new_sector = sectors + this.get_lba28()
2573
+ this.lba_low_reg = new_sector & 0xff
2574
+ this.lba_mid_reg = (new_sector >> 8) & 0xff
2575
+ this.lba_high_reg = (new_sector >> 16) & 0xff
2576
+ this.head = (this.head & ~0xf) | (new_sector & 0xf)
2577
+ } else // chs
2578
+ {
2579
+ new_sector = sectors + this.get_chs()
2580
+
2581
+ const c =
2582
+ (new_sector / (this.head_count * this.sectors_per_track)) | 0
2583
+ this.lba_mid_reg = c & 0xff
2584
+ this.lba_high_reg = (c >> 8) & 0xff
2585
+ this.head =
2586
+ (((new_sector / this.sectors_per_track) | 0) %
2587
+ this.head_count) &
2588
+ 0xf
2589
+ this.lba_low_reg =
2590
+ ((new_sector % this.sectors_per_track) + 1) & 0xff
2591
+
2592
+ dbg_assert(new_sector === this.get_chs())
2593
+ }
2594
+ }
2595
+
2596
+ ata_read_sectors(cmd: number): void {
2597
+ const is_lba48 =
2598
+ cmd === ATA_CMD_READ_SECTORS_EXT || cmd === ATA_CMD_READ_MULTIPLE
2599
+ const count = this.get_count(is_lba48)
2600
+ const lba = this.get_lba(is_lba48)
2601
+
2602
+ const is_single =
2603
+ cmd === ATA_CMD_READ_SECTORS || cmd === ATA_CMD_READ_SECTORS_EXT
2604
+
2605
+ const byte_count = count * this.sector_size
2606
+ const start = lba * this.sector_size
2607
+
2608
+ if (LOG_DETAILS & LOG_DETAIL_RW) {
2609
+ dbg_log(
2610
+ this.name +
2611
+ ': ATA read cmd=' +
2612
+ h(cmd) +
2613
+ ' mode=' +
2614
+ (this.is_lba ? 'lba' : 'chs') +
2615
+ ' lba=' +
2616
+ h(lba) +
2617
+ ' lbacount=' +
2618
+ h(count) +
2619
+ ' bytecount=' +
2620
+ h(byte_count),
2621
+ LOG_DISK,
2622
+ )
2623
+ }
2624
+
2625
+ if (start + byte_count > this.buffer!.byteLength) {
2626
+ dbg_assert(
2627
+ false,
2628
+ this.name + ': ATA read: Outside of disk',
2629
+ LOG_DISK,
2630
+ )
2631
+
2632
+ this.status_reg = 0xff
2633
+ this.push_irq()
2634
+ } else {
2635
+ this.status_reg = ATA_SR_DRDY | ATA_SR_BSY
2636
+ this.report_read_start()
2637
+
2638
+ this.read_buffer(start, byte_count, (data) => {
2639
+ if (LOG_DETAILS & LOG_DETAIL_RW) {
2640
+ dbg_log(this.name + ': ata_read: Data arrived', LOG_DISK)
2641
+ }
2642
+
2643
+ this.data_set(data)
2644
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
2645
+ this.data_end = is_single
2646
+ ? 512
2647
+ : Math.min(byte_count, this.sectors_per_drq * 512)
2648
+ this.ata_advance(
2649
+ cmd,
2650
+ is_single ? 1 : Math.min(count, this.sectors_per_track),
2651
+ )
2652
+
2653
+ this.push_irq()
2654
+ this.report_read_end(byte_count)
2655
+ })
2656
+ }
2657
+ }
2658
+
2659
+ ata_read_sectors_dma(cmd: number): void {
2660
+ const is_lba48 = cmd === ATA_CMD_READ_DMA_EXT
2661
+ const count = this.get_count(is_lba48)
2662
+ const lba = this.get_lba(is_lba48)
2663
+
2664
+ const byte_count = count * this.sector_size
2665
+ const start = lba * this.sector_size
2666
+
2667
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
2668
+ dbg_log(
2669
+ this.name +
2670
+ ': ATA DMA read lba=' +
2671
+ h(lba) +
2672
+ ' lbacount=' +
2673
+ h(count) +
2674
+ ' bytecount=' +
2675
+ h(byte_count),
2676
+ LOG_DISK,
2677
+ )
2678
+ }
2679
+
2680
+ if (start + byte_count > this.buffer!.byteLength) {
2681
+ dbg_assert(
2682
+ false,
2683
+ this.name + ': ATA read: Outside of disk',
2684
+ LOG_DISK,
2685
+ )
2686
+
2687
+ this.status_reg = 0xff
2688
+ this.push_irq()
2689
+ return
2690
+ }
2691
+
2692
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
2693
+ this.channel.dma_status |= 1
2694
+ }
2695
+
2696
+ do_ata_read_sectors_dma(): void {
2697
+ const cmd = this.current_command
2698
+
2699
+ const is_lba48 = cmd === ATA_CMD_READ_DMA_EXT
2700
+ const count = this.get_count(is_lba48)
2701
+ const lba = this.get_lba(is_lba48)
2702
+
2703
+ const byte_count = count * this.sector_size
2704
+ const start = lba * this.sector_size
2705
+
2706
+ dbg_assert(lba < this.buffer!.byteLength)
2707
+
2708
+ this.report_read_start()
2709
+
2710
+ const orig_prdt_start = this.channel.prdt_addr
2711
+
2712
+ this.read_buffer(start, byte_count, (data) => {
2713
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
2714
+ dbg_log(
2715
+ this.name + ': do_ata_read_sectors_dma: Data arrived',
2716
+ LOG_DISK,
2717
+ )
2718
+ }
2719
+ let prdt_start = this.channel.prdt_addr
2720
+ let offset = 0
2721
+
2722
+ dbg_assert(orig_prdt_start === prdt_start)
2723
+
2724
+ let end: number
2725
+ do {
2726
+ const prd_addr = this.cpu.read32s(prdt_start)
2727
+ let prd_count = this.cpu.read16(prdt_start + 4)
2728
+ end = this.cpu.read8(prdt_start + 7) & 0x80
2729
+
2730
+ if (!prd_count) {
2731
+ prd_count = 0x10000
2732
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
2733
+ dbg_log(this.name + ': DMA: prd count was 0', LOG_DISK)
2734
+ }
2735
+ }
2736
+
2737
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
2738
+ dbg_log(
2739
+ this.name +
2740
+ ': DMA read transfer dest=' +
2741
+ h(prd_addr) +
2742
+ ' prd_count=' +
2743
+ h(prd_count),
2744
+ LOG_DISK,
2745
+ )
2746
+ }
2747
+ this.cpu.write_blob(
2748
+ data.subarray(offset, offset + prd_count),
2749
+ prd_addr,
2750
+ )
2751
+
2752
+ offset += prd_count
2753
+ prdt_start += 8
2754
+ } while (!end)
2755
+
2756
+ dbg_assert(offset === byte_count)
2757
+
2758
+ this.ata_advance(this.current_command, count)
2759
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
2760
+ this.channel.dma_status &= ~1
2761
+ this.current_command = -1
2762
+
2763
+ this.report_read_end(byte_count)
2764
+
2765
+ this.push_irq()
2766
+ })
2767
+ }
2768
+
2769
+ ata_write_sectors(cmd: number): void {
2770
+ const is_lba48 =
2771
+ cmd === ATA_CMD_WRITE_SECTORS_EXT || cmd === ATA_CMD_WRITE_MULTIPLE
2772
+ const count = this.get_count(is_lba48)
2773
+ const lba = this.get_lba(is_lba48)
2774
+
2775
+ const is_single =
2776
+ cmd === ATA_CMD_WRITE_SECTORS || cmd === ATA_CMD_WRITE_SECTORS_EXT
2777
+
2778
+ const byte_count = count * this.sector_size
2779
+ const start = lba * this.sector_size
2780
+
2781
+ if (LOG_DETAILS & LOG_DETAIL_RW) {
2782
+ dbg_log(
2783
+ this.name +
2784
+ ': ATA write lba=' +
2785
+ h(lba) +
2786
+ ' mode=' +
2787
+ (this.is_lba ? 'lba' : 'chs') +
2788
+ ' lbacount=' +
2789
+ h(count) +
2790
+ ' bytecount=' +
2791
+ h(byte_count),
2792
+ LOG_DISK,
2793
+ )
2794
+ }
2795
+
2796
+ if (start + byte_count > this.buffer!.byteLength) {
2797
+ dbg_assert(
2798
+ false,
2799
+ this.name + ': ATA write: Outside of disk',
2800
+ LOG_DISK,
2801
+ )
2802
+
2803
+ this.status_reg = 0xff
2804
+ this.push_irq()
2805
+ } else {
2806
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
2807
+ this.data_allocate_noclear(byte_count)
2808
+ this.data_end = is_single
2809
+ ? 512
2810
+ : Math.min(byte_count, this.sectors_per_drq * 512)
2811
+ this.write_dest = start
2812
+ }
2813
+ }
2814
+
2815
+ ata_write_sectors_dma(cmd: number): void {
2816
+ const is_lba48 = cmd === ATA_CMD_WRITE_DMA_EXT
2817
+ const count = this.get_count(is_lba48)
2818
+ const lba = this.get_lba(is_lba48)
2819
+
2820
+ const byte_count = count * this.sector_size
2821
+ const start = lba * this.sector_size
2822
+
2823
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
2824
+ dbg_log(
2825
+ this.name +
2826
+ ': ATA DMA write lba=' +
2827
+ h(lba) +
2828
+ ' lbacount=' +
2829
+ h(count) +
2830
+ ' bytecount=' +
2831
+ h(byte_count),
2832
+ LOG_DISK,
2833
+ )
2834
+ }
2835
+
2836
+ if (start + byte_count > this.buffer!.byteLength) {
2837
+ dbg_assert(
2838
+ false,
2839
+ this.name + ': ATA DMA write: Outside of disk',
2840
+ LOG_DISK,
2841
+ )
2842
+
2843
+ this.status_reg = 0xff
2844
+ this.push_irq()
2845
+ return
2846
+ }
2847
+
2848
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC | ATA_SR_DRQ
2849
+ this.channel.dma_status |= 1
2850
+ }
2851
+
2852
+ do_ata_write_sectors_dma(): void {
2853
+ const cmd = this.current_command
2854
+
2855
+ const is_lba48 = cmd === ATA_CMD_WRITE_DMA_EXT
2856
+ const count = this.get_count(is_lba48)
2857
+ const lba = this.get_lba(is_lba48)
2858
+
2859
+ const byte_count = count * this.sector_size
2860
+ const start = lba * this.sector_size
2861
+
2862
+ let prdt_start = this.channel.prdt_addr
2863
+ let offset = 0
2864
+
2865
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
2866
+ dbg_log(this.name + ': prdt addr: ' + h(prdt_start, 8), LOG_DISK)
2867
+ }
2868
+
2869
+ const buffer = new Uint8Array(byte_count)
2870
+
2871
+ let end: number
2872
+ do {
2873
+ const prd_addr = this.cpu.read32s(prdt_start)
2874
+ let prd_count = this.cpu.read16(prdt_start + 4)
2875
+ end = this.cpu.read8(prdt_start + 7) & 0x80
2876
+
2877
+ if (!prd_count) {
2878
+ prd_count = 0x10000
2879
+ dbg_log(this.name + ': DMA: prd count was 0', LOG_DISK)
2880
+ }
2881
+
2882
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
2883
+ dbg_log(
2884
+ this.name +
2885
+ ': DMA write transfer dest=' +
2886
+ h(prd_addr) +
2887
+ ' prd_count=' +
2888
+ h(prd_count),
2889
+ LOG_DISK,
2890
+ )
2891
+ }
2892
+
2893
+ const slice = this.cpu.mem8.subarray(prd_addr, prd_addr + prd_count)
2894
+ dbg_assert(slice.length === prd_count)
2895
+
2896
+ buffer.set(slice, offset)
2897
+
2898
+ //if(DEBUG)
2899
+ //{
2900
+ // dbg_log(hex_dump(slice), LOG_DISK);
2901
+ //}
2902
+
2903
+ offset += prd_count
2904
+ prdt_start += 8
2905
+ } while (!end)
2906
+
2907
+ dbg_assert(offset === buffer.length)
2908
+
2909
+ this.buffer!.set(start, buffer, () => {
2910
+ if (LOG_DETAILS & LOG_DETAIL_RW_DMA) {
2911
+ dbg_log(this.name + ': DMA write completed', LOG_DISK)
2912
+ }
2913
+ this.ata_advance(this.current_command, count)
2914
+ this.status_reg = ATA_SR_DRDY | ATA_SR_DSC
2915
+ this.push_irq()
2916
+ this.channel.dma_status &= ~1
2917
+ this.current_command = -1
2918
+ })
2919
+
2920
+ this.report_write(byte_count)
2921
+ }
2922
+
2923
+ get_chs(): number {
2924
+ const c =
2925
+ (this.lba_mid_reg & 0xff) | ((this.lba_high_reg << 8) & 0xff00)
2926
+ const h = this.head
2927
+ const s = this.lba_low_reg & 0xff
2928
+
2929
+ if (LOG_DETAILS & LOG_DETAIL_CHS) {
2930
+ dbg_log(
2931
+ this.name + ': get_chs: c=' + c + ' h=' + h + ' s=' + s,
2932
+ LOG_DISK,
2933
+ )
2934
+ }
2935
+
2936
+ return (c * this.head_count + h) * this.sectors_per_track + s - 1
2937
+ }
2938
+
2939
+ get_lba28(): number {
2940
+ return (
2941
+ (this.lba_low_reg & 0xff) |
2942
+ ((this.lba_mid_reg << 8) & 0xff00) |
2943
+ ((this.lba_high_reg << 16) & 0xff0000) |
2944
+ ((this.head & 0xf) << 24)
2945
+ )
2946
+ }
2947
+
2948
+ get_lba48(): number {
2949
+ // Note: Bits over 32 missing
2950
+ return (
2951
+ ((this.lba_low_reg & 0xff) |
2952
+ ((this.lba_mid_reg << 8) & 0xff00) |
2953
+ ((this.lba_high_reg << 16) & 0xff0000) |
2954
+ (((this.lba_low_reg >> 8) << 24) & 0xff000000)) >>>
2955
+ 0
2956
+ )
2957
+ }
2958
+
2959
+ get_lba(is_lba48: boolean): number {
2960
+ if (is_lba48) {
2961
+ return this.get_lba48()
2962
+ } else if (this.is_lba) {
2963
+ return this.get_lba28()
2964
+ } else {
2965
+ return this.get_chs()
2966
+ }
2967
+ }
2968
+
2969
+ get_count(is_lba48: boolean): number {
2970
+ if (is_lba48) {
2971
+ let count = this.sector_count_reg
2972
+ if (count === 0) count = 0x10000
2973
+ return count
2974
+ } else {
2975
+ let count = this.sector_count_reg & 0xff
2976
+ if (count === 0) count = 0x100
2977
+ return count
2978
+ }
2979
+ }
2980
+
2981
+ create_identify_packet(): void {
2982
+ const cylinder_count = Math.min(16383, this.cylinder_count)
2983
+ const strcpy_be16 = (
2984
+ out_buffer: Uint8Array,
2985
+ ofs16: number,
2986
+ len16: number,
2987
+ str: string,
2988
+ ): void => {
2989
+ let ofs8 = ofs16 << 1
2990
+ const len8 = len16 << 1
2991
+ const end8 = ofs8 + len8
2992
+ out_buffer.fill(32, ofs8, len8) // fill output buffer with ASCII whitespace
2993
+ for (let i_str = 0; i_str < str.length && ofs8 < end8; i_str++) {
2994
+ if (i_str & 1) {
2995
+ out_buffer[ofs8] = str.charCodeAt(i_str)
2996
+ ofs8 += 2
2997
+ } else {
2998
+ out_buffer[ofs8 + 1] = str.charCodeAt(i_str)
2999
+ }
3000
+ }
3001
+ }
3002
+
3003
+ // Initialize array of 256 16-bit words (big-endian)
3004
+ // Best source for the lower 64 words of the memory layout used below:
3005
+ // - [ATA-retro]
3006
+ // AT Attachment Interface for Disk Drives, Revision 4c
3007
+ // https://dn790009.ca.archive.org/0/items/SCSISpecificationDocumentsATAATAPI/ATA_ATAPI/AT%20Attachment%20Interface%20for%20Disk%20Drives%20Revision%204c.pdf
3008
+ // For the words above 64 see [ATA-6] Table 27, [ATA8-ACS] 7.12 and 7.13.
3009
+ //
3010
+ // dead link: http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/harddrv.cc#L2821
3011
+
3012
+ // most significant bit indicates ATAPI CD-ROM device
3013
+ const general_cfg = this.is_atapi ? 0x8540 : 0x0040
3014
+ // multiword DMA transfer mode, meaning of 0x0407:
3015
+ // - 0x0007: Multiword DMA modes 2, 1 and 0 are supported
3016
+ // - 0x0400: Multiword DMA mode 2 is selected
3017
+ const multiword_dma_mode =
3018
+ this.current_command === ATA_CMD_PACKET ? 0 : 0x0407
3019
+ // Major version number: bits 3/4/5/6 indicate support for ATA/ATAPI-3/4/5/6 (bits 0/1/2 are obsolete in [ATA-6])
3020
+ const major_version = 0x0000 // device does not report version
3021
+ // supported ATA: NOP, FLUSH CACHE, FLUSH CACHE EXT, 48-bit addr
3022
+ // supported ATAPI: NOP, DEVICE RESET, PACKET and FLUSH CACHE
3023
+ const feat_82 = this.is_atapi
3024
+ ? (1 << 14) | (1 << 9) | (1 << 5)
3025
+ : 1 << 14
3026
+ const feat_83 = this.is_atapi
3027
+ ? (1 << 14) | (1 << 12)
3028
+ : (1 << 14) | (1 << 13) | (1 << 12) | (1 << 10)
3029
+ const feat_84 = this.is_atapi ? 1 << 14 : 1 << 14
3030
+
3031
+ this.data.fill(0, 0, 512)
3032
+ this.data_set([
3033
+ // 0: General configuration
3034
+ general_cfg & 0xff,
3035
+ (general_cfg >> 8) & 0xff,
3036
+ // 1: Number of cylinders
3037
+ cylinder_count & 0xff,
3038
+ (cylinder_count >> 8) & 0xff,
3039
+ // 2: reserved
3040
+ 0,
3041
+ 0,
3042
+ // 3: Number of heads
3043
+ this.head_count & 0xff,
3044
+ (this.head_count >> 8) & 0xff,
3045
+ // 4: Number of unformatted bytes per track
3046
+ (this.sectors_per_track / 512) & 0xff,
3047
+ ((this.sectors_per_track / 512) >> 8) & 0xff,
3048
+ // 5: Number of unformatted bytes per sector
3049
+ 0,
3050
+ 512 >> 8,
3051
+ // 6: Number of sectors per track
3052
+ this.sectors_per_track & 0xff,
3053
+ (this.sectors_per_track >> 8) & 0xff,
3054
+ // 7-9: Vendor-unique
3055
+ 0,
3056
+ 0,
3057
+ 0,
3058
+ 0,
3059
+ 0,
3060
+ 0,
3061
+ // 10-19: Serial number (20 ASCII characters, filled below)
3062
+ 0,
3063
+ 0,
3064
+ 0,
3065
+ 0,
3066
+ 0,
3067
+ 0,
3068
+ 0,
3069
+ 0,
3070
+ 0,
3071
+ 0,
3072
+ 0,
3073
+ 0,
3074
+ 0,
3075
+ 0,
3076
+ 0,
3077
+ 0,
3078
+ 0,
3079
+ 0,
3080
+ 0,
3081
+ 0,
3082
+ // 20: Buffer type
3083
+ 3,
3084
+ 0,
3085
+ // 21: Buffer size in 512 byte increments
3086
+ 0,
3087
+ 2,
3088
+ // 22: Number of ECC bytes avail on read/write long cmds
3089
+ 4,
3090
+ 0,
3091
+ // 23-26: Firmware revision (8 ASCII characters, filled below)
3092
+ 0,
3093
+ 0,
3094
+ 0,
3095
+ 0,
3096
+ 0,
3097
+ 0,
3098
+ 0,
3099
+ 0,
3100
+ // 27-46: Model number (40 ASCII characters, filled below)
3101
+ 0,
3102
+ 0,
3103
+ 0,
3104
+ 0,
3105
+ 0,
3106
+ 0,
3107
+ 0,
3108
+ 0,
3109
+ 0,
3110
+ 0,
3111
+ 0,
3112
+ 0,
3113
+ 0,
3114
+ 0,
3115
+ 0,
3116
+ 0,
3117
+ 0,
3118
+ 0,
3119
+ 0,
3120
+ 0,
3121
+ 0,
3122
+ 0,
3123
+ 0,
3124
+ 0,
3125
+ 0,
3126
+ 0,
3127
+ 0,
3128
+ 0,
3129
+ 0,
3130
+ 0,
3131
+ 0,
3132
+ 0,
3133
+ 0,
3134
+ 0,
3135
+ 0,
3136
+ 0,
3137
+ 0,
3138
+ 0,
3139
+ 0,
3140
+ 0,
3141
+ // 47: Max. number of sectors per interrupt on read/write multiple commands (1st byte) and Vendor-unique (2nd)
3142
+ 0x80,
3143
+ 0,
3144
+ // 48: Indicates whether can perform doubleword I/O (1st byte) [0: no, 1: yes]
3145
+ 1,
3146
+ 0,
3147
+ // 49: Vendor-unique (1st byte) and Capabilities (2nd) [2: Only LBA, 3: LBA and DMA]
3148
+ 0,
3149
+ 2,
3150
+ // 50: reserved
3151
+ 0,
3152
+ 0,
3153
+ // 51: PIO data transfer cycle timing mode
3154
+ 0,
3155
+ 2,
3156
+ // 52: DMA data transfer cycle timing mode
3157
+ 0,
3158
+ 2,
3159
+ // 53: Indicates whether fields 54-58 are valid (1st byte) [0: no, 1: yes]
3160
+ 7,
3161
+ 0,
3162
+ // 54: Number of current cylinders
3163
+ cylinder_count & 0xff,
3164
+ (cylinder_count >> 8) & 0xff,
3165
+ // 55: Number of current heads
3166
+ this.head_count & 0xff,
3167
+ (this.head_count >> 8) & 0xff,
3168
+ // 56: Number of current sectors per track
3169
+ this.sectors_per_track,
3170
+ 0,
3171
+ // 57-58: Current capacity in sectors
3172
+ this.sector_count & 0xff,
3173
+ (this.sector_count >> 8) & 0xff,
3174
+ (this.sector_count >> 16) & 0xff,
3175
+ (this.sector_count >> 24) & 0xff,
3176
+ // 59: Multiple sector setting
3177
+ 0,
3178
+ 0,
3179
+ // 60-61: Total number of user addressable sectors (LBA mode only)
3180
+ this.sector_count & 0xff,
3181
+ (this.sector_count >> 8) & 0xff,
3182
+ (this.sector_count >> 16) & 0xff,
3183
+ (this.sector_count >> 24) & 0xff,
3184
+ // 62: Single word DMA transfer mode
3185
+ 0,
3186
+ 0,
3187
+ // 63: Multiword DMA transfer mode (DMA supported mode, DMA selected mode)
3188
+ multiword_dma_mode & 0xff,
3189
+ (multiword_dma_mode >> 8) & 0xff,
3190
+
3191
+ // 64: PIO modes supported
3192
+ 0,
3193
+ 0,
3194
+ // 65-68: fields related to cycle-time
3195
+ 30,
3196
+ 0,
3197
+ 30,
3198
+ 0,
3199
+ 30,
3200
+ 0,
3201
+ 30,
3202
+ 0,
3203
+ // 69-74: reserved
3204
+ 0,
3205
+ 0,
3206
+ 0,
3207
+ 0,
3208
+ 0,
3209
+ 0,
3210
+ 0,
3211
+ 0,
3212
+ 0,
3213
+ 0,
3214
+ 0,
3215
+ 0,
3216
+ // 75: Queue depth
3217
+ 0,
3218
+ 0,
3219
+ // 76-79: reserved
3220
+ 0,
3221
+ 0,
3222
+ 0,
3223
+ 0,
3224
+ 0,
3225
+ 0,
3226
+ 0,
3227
+ 0,
3228
+ // 80: Major version number
3229
+ major_version & 0xff,
3230
+ (major_version >> 8) & 0xff,
3231
+ // 81: Minor version number
3232
+ 0,
3233
+ 0,
3234
+ // 82: Command set supported
3235
+ feat_82 & 0xff,
3236
+ (feat_82 >> 8) & 0xff,
3237
+ // 83: Command set supported
3238
+ feat_83 & 0xff,
3239
+ (feat_83 >> 8) & 0xff,
3240
+ // 84: Command set/feature supported extension
3241
+ feat_84 & 0xff,
3242
+ (feat_84 >> 8) & 0xff,
3243
+ // 85: Command set/feature enabled (copy of 82)
3244
+ feat_82 & 0xff,
3245
+ (feat_82 >> 8) & 0xff,
3246
+ // 86: Command set/feature enabled (copy of 83)
3247
+ feat_83 & 0xff,
3248
+ (feat_83 >> 8) & 0xff,
3249
+ // 87: Command set/feature default (copy of 84)
3250
+ feat_84 & 0xff,
3251
+ (feat_84 >> 8) & 0xff,
3252
+ // 88: DMA related field
3253
+ 0,
3254
+ 0,
3255
+ // 89: Time required for security erase unit completion
3256
+ 0,
3257
+ 0,
3258
+ // 90: Time required for Enhanced security erase completion
3259
+ 0,
3260
+ 0,
3261
+ // 91: Current advanced power management value
3262
+ 0,
3263
+ 0,
3264
+ // 92: Master Password Revision Code
3265
+ 0,
3266
+ 0,
3267
+ // 93: Hardware reset result
3268
+ 1,
3269
+ 0x60,
3270
+ // 94: Acoustic management value
3271
+ 0,
3272
+ 0,
3273
+ // 95-99: reserved
3274
+ 0,
3275
+ 0,
3276
+ 0,
3277
+ 0,
3278
+ 0,
3279
+ 0,
3280
+ 0,
3281
+ 0,
3282
+ 0,
3283
+ 0,
3284
+ // 100-101: Maximum user LBA for 48-bit Address feature set.
3285
+ this.sector_count & 0xff,
3286
+ (this.sector_count >> 8) & 0xff,
3287
+ (this.sector_count >> 16) & 0xff,
3288
+ (this.sector_count >> 24) & 0xff,
3289
+ ])
3290
+
3291
+ // 10-19 serial number
3292
+ strcpy_be16(
3293
+ this.data,
3294
+ 10,
3295
+ 10,
3296
+ `8086-86${this.channel_nr}${this.interface_nr}`,
3297
+ )
3298
+ // 23-26 firmware revision
3299
+ strcpy_be16(this.data, 23, 4, '1.00')
3300
+ // 27-46 model number
3301
+ strcpy_be16(
3302
+ this.data,
3303
+ 27,
3304
+ 20,
3305
+ this.is_atapi ? 'v86 ATAPI CD-ROM' : 'v86 ATA HD',
3306
+ )
3307
+
3308
+ this.data_length = 512
3309
+ this.data_end = 512
3310
+ }
3311
+
3312
+ data_allocate(len: number): void {
3313
+ this.data_allocate_noclear(len)
3314
+ this.data32.fill(0, 0, (len + 3) >> 2)
3315
+ }
3316
+
3317
+ data_allocate_noclear(len: number): void {
3318
+ if (this.data.length < len) {
3319
+ this.data = new Uint8Array((len + 3) & ~3)
3320
+ this.data16 = new Uint16Array(this.data.buffer)
3321
+ this.data32 = new Int32Array(this.data.buffer)
3322
+ }
3323
+
3324
+ this.data_length = len
3325
+ this.data_pointer = 0
3326
+ }
3327
+
3328
+ data_set(data: Uint8Array | number[]): void {
3329
+ this.data_allocate_noclear(data.length)
3330
+ this.data.set(data)
3331
+ }
3332
+
3333
+ report_read_start(): void {
3334
+ this.bus.send('ide-read-start')
3335
+ }
3336
+
3337
+ report_read_end(byte_count: number): void {
3338
+ const sector_count = (byte_count / this.sector_size) | 0
3339
+ this.bus.send('ide-read-end', [
3340
+ this.channel_nr,
3341
+ byte_count,
3342
+ sector_count,
3343
+ ])
3344
+ }
3345
+
3346
+ report_write(byte_count: number): void {
3347
+ const sector_count = (byte_count / this.sector_size) | 0
3348
+ this.bus.send('ide-write-end', [
3349
+ this.channel_nr,
3350
+ byte_count,
3351
+ sector_count,
3352
+ ])
3353
+ }
3354
+
3355
+ read_buffer(
3356
+ start: number,
3357
+ length: number,
3358
+ callback: (data: Uint8Array) => void,
3359
+ ): void {
3360
+ const id = this.last_io_id++
3361
+ this.in_progress_io_ids.add(id)
3362
+
3363
+ this.buffer!.get(start, length, (data) => {
3364
+ if (this.cancelled_io_ids.delete(id)) {
3365
+ dbg_assert(!this.in_progress_io_ids.has(id))
3366
+ return
3367
+ }
3368
+
3369
+ const removed = this.in_progress_io_ids.delete(id)
3370
+ dbg_assert(removed)
3371
+
3372
+ callback(data)
3373
+ })
3374
+ }
3375
+
3376
+ cancel_io_operations(): void {
3377
+ for (const id of this.in_progress_io_ids) {
3378
+ this.cancelled_io_ids.add(id)
3379
+ }
3380
+ this.in_progress_io_ids.clear()
3381
+ }
3382
+
3383
+ get_state(): unknown[] {
3384
+ const state: unknown[] = []
3385
+ state[0] = this.sector_count_reg
3386
+ state[1] = this.cylinder_count
3387
+ state[2] = this.lba_high_reg
3388
+ state[3] = this.lba_mid_reg
3389
+ state[4] = this.data_pointer
3390
+ state[5] = 0
3391
+ state[6] = 0
3392
+ state[7] = 0
3393
+ state[8] = 0
3394
+ state[9] = this.device_reg
3395
+ state[10] = this.error_reg
3396
+ state[11] = this.head
3397
+ state[12] = this.head_count
3398
+ state[13] = this.is_atapi
3399
+ state[14] = this.is_lba
3400
+ state[15] = this.features_reg
3401
+ state[16] = this.data
3402
+ state[17] = this.data_length
3403
+ state[18] = this.lba_low_reg
3404
+ state[19] = this.sector_count
3405
+ state[20] = this.sector_size
3406
+ state[21] = this.sectors_per_drq
3407
+ state[22] = this.sectors_per_track
3408
+ state[23] = this.status_reg
3409
+ state[24] = this.write_dest
3410
+ state[25] = this.current_command
3411
+ state[26] = this.data_end
3412
+ state[27] = this.current_atapi_command
3413
+ state[28] = this.buffer
3414
+ return state
3415
+ }
3416
+
3417
+ set_state(state: any[]): void {
3418
+ this.sector_count_reg = state[0]
3419
+ this.cylinder_count = state[1]
3420
+ this.lba_high_reg = state[2]
3421
+ this.lba_mid_reg = state[3]
3422
+ this.data_pointer = state[4]
3423
+
3424
+ this.device_reg = state[9]
3425
+ this.error_reg = state[10]
3426
+ this.head = state[11]
3427
+ this.head_count = state[12]
3428
+ this.is_atapi = state[13]
3429
+ this.is_lba = state[14]
3430
+ this.features_reg = state[15]
3431
+ this.data = state[16]
3432
+ this.data_length = state[17]
3433
+ this.lba_low_reg = state[18]
3434
+ this.sector_count = state[19]
3435
+ this.sector_size = state[20]
3436
+ this.sectors_per_drq = state[21]
3437
+ this.sectors_per_track = state[22]
3438
+ this.status_reg = state[23]
3439
+ this.write_dest = state[24]
3440
+ this.current_command = state[25]
3441
+
3442
+ this.data_end = state[26]
3443
+ this.current_atapi_command = state[27]
3444
+
3445
+ this.data16 = new Uint16Array(this.data.buffer)
3446
+ this.data32 = new Int32Array(this.data.buffer)
3447
+
3448
+ if (this.buffer) {
3449
+ this.buffer.set_state(state[28])
3450
+ }
3451
+
3452
+ this.drive_connected = this.is_atapi || !!this.buffer
3453
+ this.medium_changed = false
3454
+ }
3455
+ }