v86/src/ide.js

2093 lines
57 KiB
JavaScript
Raw Permalink Normal View History

"use strict";
2016-11-19 22:58:42 +01:00
/** @const */
var CDROM_SECTOR_SIZE = 2048;
/** @const */
var HD_SECTOR_SIZE = 512;
/**
* @constructor
2015-02-25 18:21:54 +01:00
* @param {CPU} cpu
* @param {boolean} is_cd
* @param {number} nr
* @param {BusConnector} bus
2015-02-25 18:21:54 +01:00
* */
2020-09-06 21:01:50 +02:00
function IDEDevice(cpu, master_buffer, slave_buffer, is_cd, nr, bus)
{
2020-09-06 21:01:50 +02:00
this.master = new IDEInterface(this, cpu, master_buffer, is_cd, nr, 0, bus);
2021-01-03 07:51:29 +01:00
this.slave = new IDEInterface(this, cpu, slave_buffer, false, nr, 1, bus);
this.current_interface = this.master;
this.cpu = cpu;
2015-01-13 23:14:05 +01:00
2013-12-29 00:34:10 +01:00
// gets set via PCI in seabios, likely doesn't matter
if(nr === 0)
{
this.ata_port = 0x1F0;
this.irq = 14;
2014-01-10 23:12:56 +01:00
this.pci_id = 0x1E << 3;
}
else if(nr === 1)
{
2015-08-22 15:40:41 +02:00
this.ata_port = 0x170;
this.irq = 15;
2014-01-10 23:12:56 +01:00
this.pci_id = 0x1F << 3;
}
else
{
dbg_assert(false, "IDE device with nr " + nr + " ignored", LOG_DISK);
}
// alternate status, starting at 3f4/374
2014-09-28 23:34:19 +02:00
/** @type {number} */
this.ata_port_high = this.ata_port | 0x204;
2015-09-15 21:58:40 +02:00
2014-09-28 23:34:19 +02:00
/** @type {number} */
this.master_port = 0xB400;
this.pci_space = [
0x86, 0x80, 0x10, 0x70, 0x05, 0x00, 0xA0, 0x02,
0x00, 0x80, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
this.ata_port & 0xFF | 1, this.ata_port >> 8, 0x00, 0x00,
this.ata_port_high & 0xFF | 1, this.ata_port_high >> 8, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // second device
0x00, 0x00, 0x00, 0x00, // second device
this.master_port & 0xFF | 1, this.master_port >> 8, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
2016-11-19 22:58:42 +01:00
0x43, 0x10, 0xD4, 0x82,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, this.irq, 0x01, 0x00, 0x00,
// 0x40
2016-11-19 22:58:42 +01:00
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x80
2016-11-19 22:58:42 +01:00
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
this.pci_bars = [
{
size: 8,
},
{
size: 4,
},
undefined,
undefined,
{
size: 0x10,
},
];
this.name = "ide" + nr;
/** @type {number} */
this.device_control = 2;
// status
cpu.io.register_read(this.ata_port | 7, this, function() {
dbg_log("lower irq", LOG_DISK);
this.cpu.device_lower_irq(this.irq);
return this.read_status();
});
cpu.io.register_read(this.ata_port_high | 2, this, this.read_status);
cpu.io.register_write(this.ata_port_high | 2, this, this.write_control);
cpu.io.register_read(this.ata_port | 0, this, function()
{
return this.current_interface.read_data(1);
}, function()
{
return this.current_interface.read_data(2);
}, function()
{
return this.current_interface.read_data(4);
});
cpu.io.register_read(this.ata_port | 1, this, function()
{
2016-11-19 22:58:42 +01:00
dbg_log("Read error: " + h(this.current_interface.error & 0xFF) +
" slave=" + (this.current_interface === this.slave), LOG_DISK);
Squash 2e469796 Minor fab422ef Improve generation of 0f instructions 08ad7fe9 Improved if-else generation 3f81014d Minor: Align test output 4a3a84ef Generate modrm tests 61aa1875 Simplify a6e47954 Generate decoding of immediate operands 435b2c10 Fix warnings e4933042 Add missing immediate operand 3f3810c7 Generate immediate operands for instructions with modrm byte a0aa7b1f Make memory layout in nasm tests clearer 6b8ef212 Remove 'g' property from instruction table (implied by 'e') bf15c58c Remove unused declarations 1e543035 Remove useless `| 0` and `>>> 0` javascriptisms 1ccc5d53 Fix headers 8b40c532 Update qemu tests with changes from qemu ec9b0fb5 Port xchg instructions to C c73613e7 Port virt_boundary_* to C d61d1241 Add headers fd19f22c Make written value in write8 and write16 int32_t 497dcaec Generate read_imm for instructions with a modrm byte 8b7003d6 Generate read_imm8s 0cc75498 Remove read_op 9d716086 Trigger unimplemented_sse for partial sse instructions with prefix 8d5edd03 Remove unimplmented sse c-to-js hack 585d3565 Remove | 0 308124b2 Use int32_t as return value f193f8e1 Use JS version of cvttsd2si for now 12747b97 Generate trigger_ud for missing modrm branches 770f674e Split 0f00 and 0f01 into multiple instructions depending on modrm bits 1cb372a3 Generate decoder for some 0f-prefix instructions cec7bc63 Disable unused parameter warnings in instruction functions 807665b1 Generate read_imm for 0f/jmpcc cdf6eccc Generate modrm decoding for shld 04528429 Create temporary files in /tmp/, not cwd d8f3fbd8 Generate modrm/imm decoding for shld 00ef0942 Generate modrm decoding for bts f531984b Generate modrm decoding for shrd and imul 07569c53 Generate modrm decoding cmpxchg 535ff190 Generate modrm decoding for lfs/lgs/lss 2f8ced8d Generate modrm decoding for btr and btc 95de6c66 Generate modrm decoding for movzx c4d07e7e Generate modrm decoding for bsf and bsr f0985d26 Generate modrm decoding for movsx 4b30937a Generate modrm decoding for xadd a422eb27 Generate modrm decoding for movnti e5501d3c Generate modrm decoding for mov to/from control/debug registers bce11ec5 Generate modrm decoding for lar/lsl 5729a23c Fix access to DR4 and DR5 when cr4.DE is clear 44269a81 Specify immediate size explicitly instead of inferring it 82b2867a Fix STR instruction 98a9cc89 Log failing assertion 6d2f9964 Fix rdtsc 00260694 Log GP exceptions 7916883d Port trigger_ud and trigger_nm to C 36fedae9 Remove unused code e08fabd0 Generate modrm decoding for 0f00 and 0f01 8ae8174d Generate modrm decoding for 0fae and 0fc7 (fxsave, cmpxchg8, etc.) 26168164 Generate modrm+immediate decoding for 0fba (bit test with immediate operand) 6adf7fa7 Simplify create_tests.js (unused prefix call) c77cbdd8 Add comments about the implementation of pop [addr] 4640b4fe Simplify prefix call a81a5497 Don't use var 3ca5d13d Separate call name and arguments in code generator 3191a543 Simplify other prefix call (8D/lea) 5185080e Update generated code (stylistic changes and #ud generation) 93b51d41 Remove unused wasm externals e4af0a7f Avoid hardcoding special cases in code generator (lea, pop r/m) 654a2b49 Avoid hardcoding special cases in code generator (enter/jumpf/callf) fd1a1e86 Commit generated code (only stylistic changes) 7310fd1a Simplify code generator by merging code for with and without 0f prefix e7eae4af Simplify code generator by merging code for immediate operands 00fafd8a Improve assertions db084e49 Simplify code generator (modrm if-else) 0a0e4c9e Improve code generation of switch-case ce292795 Clarify some comments 37cf33fa Generate code in if/else blocks cbcc33fc Document naming scheme e30b97eb Generate modrm decoding for 0f12 (sse) instruction 24b72c2f movlpd cannot be used for register-to-register moves 72d72995 Generate modrm decoding for 0f13 (sse) instruction and disable register-to-register moving 75d76fbb Generate modrm decoding for 0f14 (sse) instruction ac8965a7 Generate modrm decoding for 0f28-0f2b (movap, movntp) e919d33e Generate modrm decoding for cvttsd2si 5f2ca2b4 Generate modrm decoding for andp and xorp c8d1c6de Generate modrm decoding for 0f60-0f70 (sse instructions) ae4ed46d Add multi-byte nop and prefetch to nasm test, generate modrm decoding 718a1acf Print qemu test error message more useful d1ecc37e Generate modrm decoding for 0f70-0f80 (sse instructions) 6a7219a5 Generate modrm decoding for popcnt 25278217 Generate modrm decoding for 0f71-0f73 (sse shift with immediate byte) ed1ec81b Generate modrm decoding for the remaining sse instructions (0fc0-0fff) 42bc5a6f Use 64-bit multiplication for native code dda3fb39 Remove old modrm-decoding functions 717975ef Move register access functions to cpu.c aee8138f Remove read_op, read_sib, read_op0F, read_disp f31317f2 Rename xmm/mmx register access functions a525e70b Remove 32-bit access to reg_xmm and reg_mmx c803eabc Rename s8/s16/s32 to i8/i16/i32 9fbd2ddf Don't use uninitialised structs 942eb4f7 Use 64-bit load for mmx registers and assert reg64 and reg128 size f94ec612 Use 64-bit writes for write_xmm64 08022de9 Use more efficient method for some 128-bit stores 9d5b084c Make timestamp counter unsigned 2ef388b3 Pass 64-bit value to safe_write64 4cb2b1be Optimise safe_write64 and safe_write128 b0ab09fb Implement psllq (660ff3) 9935e5d4 Optimise safe_read64s and safe_read128s af9ea1cc Log cl in cpuid only if relevant be5fe23e Add multi-op cache (disabled by default through ENABLE_JIT macro) and JIT paging test (similar to QEMU test). aa2f286e Don't initialise group_dirtiness with 1 as it increases the binary size significantly b8e14ed9 Remove unused reg_xmm32s bc726e03 Implement dbg_log for native code with format characters 'd' and 'x' 454039d6 Fix store task register 63a4b349 Remove unnecessary parens and clean up some log statements 4cc96814 Add logop and dbg_trace imports 7940655d Only inhibit interrupts if the interrupt flag was 0 in STI 876c68a7 Split create_tests into create_tests and generate_interpreter aa82499f Move detection of string instructions to x86_table f3840ec2 Move C ast to separate file 90400703 Skip tests for lfence/mfence/sfence, clarify their encoding 4a9d8204 elf: Hide log messages when log level is zero a601c526 Allow setting log level via settings 8a624453 Add cpu_exception_hook to debug builds f9e335bf Nasm: Test exceptions 599ad088 logop: Format instruction pointer as unsigned f95cf22b Don't skip zero dividing tests 2a655a0e Remove get_seg_prefix_ds from read_moffs (preparation for calling read_moffs from the code generator) bc580b71 Remove obsolete comment e556cee0 Fix nasmtest dependencies in makefile and clean dcb1e72b Use all cores on travis 86efa737 Replace all instances of u32 & 0xFFFF with the respective u16 accesses 98b9f439 Use u8 instead of bit-shifts and masks from u32 b43f6569 Replace all instances of u32 >> 16 with the respective u16 accesses 9bfa72c7 Remove unnecessary parens 9cf93734 Clean up remaining instance of u32 with a mask instead of u16 22d4117f Correct order of writes in virt_boundary_write32 6734c7c1 Fix keyboard on ios, fixes #105 858a4506 Add missing file, c_ast.js 1d62e39e Move instruction naming scheme into function f4816852 Reorder some code 69d49788 Minor improvements 0493e05f Add util.js af9000c1 Improve full test e5feba31 Add missing export c7c42065 Replace prefix_call with custom_resolve_modrm 3186e6ad Add support for "%%" format string to dbg_log_wasm for printf import efe54fad Add barebones instrumentation profiler (disabled by default). c9f0d462 Implement movlps m64, xmm and enable its test 42869a12 Add tests for cross-page reads/writes confirmed with byte reads/writes d68976ea Mask word values in port byte reads 9758d51e Add PS2_LOG_VERBOSE 5f52f037 Update NASM Makefile to include all dependencies to prevent unnecessary recompilation 2c71f927 Have NASM test generator use a seedable PRNG to allow for faster incremental tests e4aa45bb Add chunk{16,32}_rw paging tests; instructions that read and write to memory bdf538a2 add codegen to cpu constructor aa76ce8e add resolve_modrm16 14d7ecf1 refactor codegen b710319f [rebased] Merge branch codegen 0565ea42 minor refactoring 071dff3f temporary fix for automatic cast warnings 57c504f2 fix modrm16 issue c2db5d9e jit modrm32 85c04245 reinstate modrm_fn0 and modrm_fn1 be65dafd add ip and previous ip manipulating functions ae00ef89 update codegen js interface 530a74fa squashed commit for refactor 2c692199 add codegen-test to build c15afe68 prefix gen to codegen api c9611533 codegen tests fixes
2020-05-08 23:39:42 +02:00
return this.current_interface.error & 0xFF;
});
cpu.io.register_read(this.ata_port | 2, this, function()
{
dbg_log("Read bytecount: " + h(this.current_interface.bytecount & 0xFF), LOG_DISK);
return this.current_interface.bytecount & 0xFF;
});
cpu.io.register_read(this.ata_port | 3, this, function()
{
dbg_log("Read sector: " + h(this.current_interface.sector & 0xFF), LOG_DISK);
return this.current_interface.sector & 0xFF;
});
cpu.io.register_read(this.ata_port | 4, this, function()
{
dbg_log("Read 1F4: " + h(this.current_interface.cylinder_low & 0xFF), LOG_DISK);
return this.current_interface.cylinder_low & 0xFF;
});
cpu.io.register_read(this.ata_port | 5, this, function()
{
dbg_log("Read 1F5: " + h(this.current_interface.cylinder_high & 0xFF), LOG_DISK);
return this.current_interface.cylinder_high & 0xFF;
});
cpu.io.register_read(this.ata_port | 6, this, function()
{
dbg_log("Read 1F6", LOG_DISK);
Squash 2e469796 Minor fab422ef Improve generation of 0f instructions 08ad7fe9 Improved if-else generation 3f81014d Minor: Align test output 4a3a84ef Generate modrm tests 61aa1875 Simplify a6e47954 Generate decoding of immediate operands 435b2c10 Fix warnings e4933042 Add missing immediate operand 3f3810c7 Generate immediate operands for instructions with modrm byte a0aa7b1f Make memory layout in nasm tests clearer 6b8ef212 Remove 'g' property from instruction table (implied by 'e') bf15c58c Remove unused declarations 1e543035 Remove useless `| 0` and `>>> 0` javascriptisms 1ccc5d53 Fix headers 8b40c532 Update qemu tests with changes from qemu ec9b0fb5 Port xchg instructions to C c73613e7 Port virt_boundary_* to C d61d1241 Add headers fd19f22c Make written value in write8 and write16 int32_t 497dcaec Generate read_imm for instructions with a modrm byte 8b7003d6 Generate read_imm8s 0cc75498 Remove read_op 9d716086 Trigger unimplemented_sse for partial sse instructions with prefix 8d5edd03 Remove unimplmented sse c-to-js hack 585d3565 Remove | 0 308124b2 Use int32_t as return value f193f8e1 Use JS version of cvttsd2si for now 12747b97 Generate trigger_ud for missing modrm branches 770f674e Split 0f00 and 0f01 into multiple instructions depending on modrm bits 1cb372a3 Generate decoder for some 0f-prefix instructions cec7bc63 Disable unused parameter warnings in instruction functions 807665b1 Generate read_imm for 0f/jmpcc cdf6eccc Generate modrm decoding for shld 04528429 Create temporary files in /tmp/, not cwd d8f3fbd8 Generate modrm/imm decoding for shld 00ef0942 Generate modrm decoding for bts f531984b Generate modrm decoding for shrd and imul 07569c53 Generate modrm decoding cmpxchg 535ff190 Generate modrm decoding for lfs/lgs/lss 2f8ced8d Generate modrm decoding for btr and btc 95de6c66 Generate modrm decoding for movzx c4d07e7e Generate modrm decoding for bsf and bsr f0985d26 Generate modrm decoding for movsx 4b30937a Generate modrm decoding for xadd a422eb27 Generate modrm decoding for movnti e5501d3c Generate modrm decoding for mov to/from control/debug registers bce11ec5 Generate modrm decoding for lar/lsl 5729a23c Fix access to DR4 and DR5 when cr4.DE is clear 44269a81 Specify immediate size explicitly instead of inferring it 82b2867a Fix STR instruction 98a9cc89 Log failing assertion 6d2f9964 Fix rdtsc 00260694 Log GP exceptions 7916883d Port trigger_ud and trigger_nm to C 36fedae9 Remove unused code e08fabd0 Generate modrm decoding for 0f00 and 0f01 8ae8174d Generate modrm decoding for 0fae and 0fc7 (fxsave, cmpxchg8, etc.) 26168164 Generate modrm+immediate decoding for 0fba (bit test with immediate operand) 6adf7fa7 Simplify create_tests.js (unused prefix call) c77cbdd8 Add comments about the implementation of pop [addr] 4640b4fe Simplify prefix call a81a5497 Don't use var 3ca5d13d Separate call name and arguments in code generator 3191a543 Simplify other prefix call (8D/lea) 5185080e Update generated code (stylistic changes and #ud generation) 93b51d41 Remove unused wasm externals e4af0a7f Avoid hardcoding special cases in code generator (lea, pop r/m) 654a2b49 Avoid hardcoding special cases in code generator (enter/jumpf/callf) fd1a1e86 Commit generated code (only stylistic changes) 7310fd1a Simplify code generator by merging code for with and without 0f prefix e7eae4af Simplify code generator by merging code for immediate operands 00fafd8a Improve assertions db084e49 Simplify code generator (modrm if-else) 0a0e4c9e Improve code generation of switch-case ce292795 Clarify some comments 37cf33fa Generate code in if/else blocks cbcc33fc Document naming scheme e30b97eb Generate modrm decoding for 0f12 (sse) instruction 24b72c2f movlpd cannot be used for register-to-register moves 72d72995 Generate modrm decoding for 0f13 (sse) instruction and disable register-to-register moving 75d76fbb Generate modrm decoding for 0f14 (sse) instruction ac8965a7 Generate modrm decoding for 0f28-0f2b (movap, movntp) e919d33e Generate modrm decoding for cvttsd2si 5f2ca2b4 Generate modrm decoding for andp and xorp c8d1c6de Generate modrm decoding for 0f60-0f70 (sse instructions) ae4ed46d Add multi-byte nop and prefetch to nasm test, generate modrm decoding 718a1acf Print qemu test error message more useful d1ecc37e Generate modrm decoding for 0f70-0f80 (sse instructions) 6a7219a5 Generate modrm decoding for popcnt 25278217 Generate modrm decoding for 0f71-0f73 (sse shift with immediate byte) ed1ec81b Generate modrm decoding for the remaining sse instructions (0fc0-0fff) 42bc5a6f Use 64-bit multiplication for native code dda3fb39 Remove old modrm-decoding functions 717975ef Move register access functions to cpu.c aee8138f Remove read_op, read_sib, read_op0F, read_disp f31317f2 Rename xmm/mmx register access functions a525e70b Remove 32-bit access to reg_xmm and reg_mmx c803eabc Rename s8/s16/s32 to i8/i16/i32 9fbd2ddf Don't use uninitialised structs 942eb4f7 Use 64-bit load for mmx registers and assert reg64 and reg128 size f94ec612 Use 64-bit writes for write_xmm64 08022de9 Use more efficient method for some 128-bit stores 9d5b084c Make timestamp counter unsigned 2ef388b3 Pass 64-bit value to safe_write64 4cb2b1be Optimise safe_write64 and safe_write128 b0ab09fb Implement psllq (660ff3) 9935e5d4 Optimise safe_read64s and safe_read128s af9ea1cc Log cl in cpuid only if relevant be5fe23e Add multi-op cache (disabled by default through ENABLE_JIT macro) and JIT paging test (similar to QEMU test). aa2f286e Don't initialise group_dirtiness with 1 as it increases the binary size significantly b8e14ed9 Remove unused reg_xmm32s bc726e03 Implement dbg_log for native code with format characters 'd' and 'x' 454039d6 Fix store task register 63a4b349 Remove unnecessary parens and clean up some log statements 4cc96814 Add logop and dbg_trace imports 7940655d Only inhibit interrupts if the interrupt flag was 0 in STI 876c68a7 Split create_tests into create_tests and generate_interpreter aa82499f Move detection of string instructions to x86_table f3840ec2 Move C ast to separate file 90400703 Skip tests for lfence/mfence/sfence, clarify their encoding 4a9d8204 elf: Hide log messages when log level is zero a601c526 Allow setting log level via settings 8a624453 Add cpu_exception_hook to debug builds f9e335bf Nasm: Test exceptions 599ad088 logop: Format instruction pointer as unsigned f95cf22b Don't skip zero dividing tests 2a655a0e Remove get_seg_prefix_ds from read_moffs (preparation for calling read_moffs from the code generator) bc580b71 Remove obsolete comment e556cee0 Fix nasmtest dependencies in makefile and clean dcb1e72b Use all cores on travis 86efa737 Replace all instances of u32 & 0xFFFF with the respective u16 accesses 98b9f439 Use u8 instead of bit-shifts and masks from u32 b43f6569 Replace all instances of u32 >> 16 with the respective u16 accesses 9bfa72c7 Remove unnecessary parens 9cf93734 Clean up remaining instance of u32 with a mask instead of u16 22d4117f Correct order of writes in virt_boundary_write32 6734c7c1 Fix keyboard on ios, fixes #105 858a4506 Add missing file, c_ast.js 1d62e39e Move instruction naming scheme into function f4816852 Reorder some code 69d49788 Minor improvements 0493e05f Add util.js af9000c1 Improve full test e5feba31 Add missing export c7c42065 Replace prefix_call with custom_resolve_modrm 3186e6ad Add support for "%%" format string to dbg_log_wasm for printf import efe54fad Add barebones instrumentation profiler (disabled by default). c9f0d462 Implement movlps m64, xmm and enable its test 42869a12 Add tests for cross-page reads/writes confirmed with byte reads/writes d68976ea Mask word values in port byte reads 9758d51e Add PS2_LOG_VERBOSE 5f52f037 Update NASM Makefile to include all dependencies to prevent unnecessary recompilation 2c71f927 Have NASM test generator use a seedable PRNG to allow for faster incremental tests e4aa45bb Add chunk{16,32}_rw paging tests; instructions that read and write to memory bdf538a2 add codegen to cpu constructor aa76ce8e add resolve_modrm16 14d7ecf1 refactor codegen b710319f [rebased] Merge branch codegen 0565ea42 minor refactoring 071dff3f temporary fix for automatic cast warnings 57c504f2 fix modrm16 issue c2db5d9e jit modrm32 85c04245 reinstate modrm_fn0 and modrm_fn1 be65dafd add ip and previous ip manipulating functions ae00ef89 update codegen js interface 530a74fa squashed commit for refactor 2c692199 add codegen-test to build c15afe68 prefix gen to codegen api c9611533 codegen tests fixes
2020-05-08 23:39:42 +02:00
return this.current_interface.drive_head & 0xFF;
});
cpu.io.register_write(this.ata_port | 0, this, function(data)
{
this.current_interface.write_data_port8(data);
}, function(data)
{
this.current_interface.write_data_port16(data);
}, function(data)
{
this.current_interface.write_data_port32(data);
});
cpu.io.register_write(this.ata_port | 1, this, function(data)
{
dbg_log("1F1/lba_count: " + h(data), LOG_DISK);
this.master.lba_count = (this.master.lba_count << 8 | data) & 0xFFFF;
this.slave.lba_count = (this.slave.lba_count << 8 | data) & 0xFFFF;
});
cpu.io.register_write(this.ata_port | 2, this, function(data)
{
dbg_log("1F2/bytecount: " + h(data), LOG_DISK);
this.master.bytecount = (this.master.bytecount << 8 | data) & 0xFFFF;
this.slave.bytecount = (this.slave.bytecount << 8 | data) & 0xFFFF;
});
cpu.io.register_write(this.ata_port | 3, this, function(data)
{
dbg_log("1F3/sector: " + h(data), LOG_DISK);
this.master.sector = (this.master.sector << 8 | data) & 0xFFFF;
this.slave.sector = (this.slave.sector << 8 | data) & 0xFFFF;
});
cpu.io.register_write(this.ata_port | 4, this, function(data)
{
dbg_log("1F4/sector low: " + h(data), LOG_DISK);
this.master.cylinder_low = (this.master.cylinder_low << 8 | data) & 0xFFFF;
this.slave.cylinder_low = (this.slave.cylinder_low << 8 | data) & 0xFFFF;
});
cpu.io.register_write(this.ata_port | 5, this, function(data)
{
dbg_log("1F5/sector high: " + h(data), LOG_DISK);
this.master.cylinder_high = (this.master.cylinder_high << 8 | data) & 0xFFFF;
this.slave.cylinder_high = (this.slave.cylinder_high << 8 | data) & 0xFFFF;
});
cpu.io.register_write(this.ata_port | 6, this, function(data)
{
var slave = data & 0x10;
var mode = data & 0xE0;
dbg_log("1F6/drive: " + h(data, 2), LOG_DISK);
if(slave)
{
dbg_log("Slave", LOG_DISK);
this.current_interface = this.slave;
}
else
{
this.current_interface = this.master;
}
this.master.drive_head = data;
this.slave.drive_head = data;
this.master.is_lba = this.slave.is_lba = data >> 6 & 1;
this.master.head = this.slave.head = data & 0xF;
});
/** @type {number} */
this.prdt_addr = 0;
/** @type {number} */
this.dma_status = 0;
/** @type {number} */
this.dma_command = 0;
cpu.io.register_write(this.ata_port | 7, this, function(data)
{
2016-11-19 22:58:42 +01:00
dbg_log("lower irq", LOG_DISK);
this.cpu.device_lower_irq(this.irq);
this.current_interface.ata_command(data);
});
cpu.io.register_read(this.master_port | 4, this, undefined, undefined, this.dma_read_addr);
cpu.io.register_write(this.master_port | 4, this, undefined, undefined, this.dma_set_addr);
2016-11-19 22:58:42 +01:00
cpu.io.register_read(this.master_port, this,
this.dma_read_command8, undefined, this.dma_read_command);
cpu.io.register_write(this.master_port, this,
this.dma_write_command8, undefined, this.dma_write_command);
cpu.io.register_read(this.master_port | 2, this, this.dma_read_status);
cpu.io.register_write(this.master_port | 2, this, this.dma_write_status);
cpu.io.register_read(this.master_port | 0x8, this, function() {
dbg_log("DMA read 0x8", LOG_DISK); return 0;
});
cpu.io.register_read(this.master_port | 0xA, this, function() {
dbg_log("DMA read 0xA", LOG_DISK); return 0;
});
2016-11-19 22:58:42 +01:00
cpu.devices.pci.register_device(this);
2016-11-19 22:58:42 +01:00
DEBUG && Object.seal(this);
}
IDEDevice.prototype.read_status = function()
{
if(this.current_interface.buffer)
{
var ret = this.current_interface.status;
dbg_log("ATA read status: " + h(ret, 2), LOG_DISK);
return ret;
}
else
{
return 0;
}
};
IDEDevice.prototype.write_control = function(data)
{
dbg_log("set device control: " + h(data, 2) + " interrupts " +
2017-05-09 19:27:52 +02:00
((data & 2) ? "disabled" : "enabled"), LOG_DISK);
if(data & 4)
{
dbg_log("Reset via control port", LOG_DISK);
2016-11-19 22:58:42 +01:00
this.cpu.device_lower_irq(this.irq);
this.master.device_reset();
this.slave.device_reset();
}
2016-11-19 22:58:42 +01:00
this.device_control = data;
};
IDEDevice.prototype.dma_read_addr = function()
{
dbg_log("dma get address: " + h(this.prdt_addr, 8), LOG_DISK);
return this.prdt_addr;
};
IDEDevice.prototype.dma_set_addr = function(data)
{
dbg_log("dma set address: " + h(data, 8), LOG_DISK);
this.prdt_addr = data;
};
IDEDevice.prototype.dma_read_status = function()
{
dbg_log("DMA read status: " + h(this.dma_status), LOG_DISK);
return this.dma_status;
};
IDEDevice.prototype.dma_write_status = function(value)
{
dbg_log("DMA set status: " + h(value), LOG_DISK);
this.dma_status &= ~(value & 6);
};
IDEDevice.prototype.dma_read_command = function()
{
return this.dma_read_command8() | this.dma_read_status() << 16;
};
IDEDevice.prototype.dma_read_command8 = function()
{
dbg_log("DMA read command: " + h(this.dma_command), LOG_DISK);
return this.dma_command;
};
IDEDevice.prototype.dma_write_command = function(value)
{
dbg_log("DMA write command: " + h(value), LOG_DISK);
2016-11-19 22:58:42 +01:00
this.dma_write_command8(value & 0xFF);
this.dma_write_status(value >> 16 & 0xFF);
};
IDEDevice.prototype.dma_write_command8 = function(value)
{
dbg_log("DMA write command8: " + h(value), LOG_DISK);
let old_command = this.dma_command;
this.dma_command = value & 0x9;
if((old_command & 1) === (value & 1))
{
return;
}
2016-11-19 22:58:42 +01:00
if((value & 1) === 0)
{
this.dma_status &= ~1;
2016-11-19 22:58:42 +01:00
return;
}
this.dma_status |= 1;
2016-11-19 22:58:42 +01:00
switch(this.current_interface.current_command)
{
case 0x25:
case 0xC8:
this.current_interface.do_ata_read_sectors_dma();
break;
case 0xCA:
case 0x35:
this.current_interface.do_ata_write_sectors_dma();
break;
case 0xA0:
this.current_interface.do_atapi_dma();
2016-11-19 22:58:42 +01:00
break;
default:
dbg_log("Spurious dma command write, current command: " +
h(this.current_interface.current_command), LOG_DISK);
dbg_assert(false);
}
};
IDEDevice.prototype.push_irq = function()
{
if((this.device_control & 2) === 0)
{
dbg_log("push irq", LOG_DISK);
2016-11-19 22:58:42 +01:00
this.dma_status |= 4;
this.cpu.device_raise_irq(this.irq);
}
};
IDEDevice.prototype.get_state = function()
{
var state = [];
state[0] = this.master;
state[1] = this.slave;
state[2] = this.ata_port;
state[3] = this.irq;
state[4] = this.pci_id;
state[5] = this.ata_port_high;
state[6] = this.master_port;
state[7] = this.name;
state[8] = this.device_control;
state[9] = this.prdt_addr;
state[10] = this.dma_status;
state[11] = this.current_interface === this.master;
state[12] = this.dma_command;
return state;
};
IDEDevice.prototype.set_state = function(state)
{
this.master.set_state(state[0]);
this.slave.set_state(state[1]);
this.ata_port = state[2];
this.irq = state[3];
this.pci_id = state[4];
this.ata_port_high = state[5];
this.master_port = state[6];
this.name = state[7];
this.device_control = state[8];
this.prdt_addr = state[9];
this.dma_status = state[10];
this.current_interface = state[11] ? this.master : this.slave;
this.dma_command = state[12];
};
/**
* @constructor
*/
function IDEInterface(device, cpu, buffer, is_cd, device_nr, interface_nr, bus)
{
this.device = device;
/** @const @type {BusConnector} */
this.bus = bus;
/**
* @const
* @type {number}
*/
this.nr = device_nr;
2015-09-15 21:58:40 +02:00
/** @const @type {CPU} */
this.cpu = cpu;
this.buffer = buffer;
2014-09-28 23:34:19 +02:00
/** @type {number} */
this.sector_size = is_cd ? CDROM_SECTOR_SIZE : HD_SECTOR_SIZE;
/** @type {boolean} */
this.is_atapi = is_cd;
2014-09-28 23:34:19 +02:00
/** @type {number} */
this.sector_count = 0;
/** @type {number} */
this.head_count = 0;
/** @type {number} */
this.sectors_per_track = 0;
/** @type {number} */
this.cylinder_count = 0;
2014-09-27 22:36:27 +02:00
if(this.buffer)
{
2014-09-27 22:36:27 +02:00
this.sector_count = this.buffer.byteLength / this.sector_size;
2014-01-10 23:12:56 +01:00
2014-09-28 23:34:19 +02:00
if(this.sector_count !== (this.sector_count | 0))
2014-02-15 23:33:48 +01:00
{
dbg_log("Warning: Disk size not aligned with sector size", LOG_DISK);
this.sector_count = Math.ceil(this.sector_count);
}
2014-01-10 23:12:56 +01:00
if(is_cd)
{
this.head_count = 1;
this.sectors_per_track = 0;
}
else
{
// "default" values: 16/63
// common: 255, 63
this.head_count = 16;
2014-01-10 23:12:56 +01:00
this.sectors_per_track = 63;
}
this.cylinder_count = this.sector_count / this.head_count / this.sectors_per_track;
2014-01-10 23:12:56 +01:00
2014-02-15 23:33:48 +01:00
if(this.cylinder_count !== (this.cylinder_count | 0))
{
2014-07-13 00:41:57 +02:00
dbg_log("Warning: Rounding up cylinder count. Choose different head number", LOG_DISK);
this.cylinder_count = Math.floor(this.cylinder_count);
//this.sector_count = this.cylinder_count * this.head_count *
// this.sectors_per_track * this.sector_size;
}
//if(this.cylinder_count > 16383)
//{
// this.cylinder_count = 16383;
//}
2016-11-19 22:58:42 +01:00
// disk translation: lba
var rtc = cpu.devices.rtc;
// master
2016-11-19 22:58:42 +01:00
rtc.cmos_write(CMOS_BIOS_DISKTRANSFLAG,
rtc.cmos_read(CMOS_BIOS_DISKTRANSFLAG) | 1 << this.nr * 4);
rtc.cmos_write(CMOS_DISK_DATA, rtc.cmos_read(CMOS_DISK_DATA) & 0x0F | 0xF0);
var reg = CMOS_DISK_DRIVE1_CYL;
rtc.cmos_write(reg + 0, this.cylinder_count & 0xFF);
rtc.cmos_write(reg + 1, this.cylinder_count >> 8 & 0xFF);
rtc.cmos_write(reg + 2, this.head_count & 0xFF);
rtc.cmos_write(reg + 3, 0xFF);
rtc.cmos_write(reg + 4, 0xFF);
rtc.cmos_write(reg + 5, 0xC8);
rtc.cmos_write(reg + 6, this.cylinder_count & 0xFF);
rtc.cmos_write(reg + 7, this.cylinder_count >> 8 & 0xFF);
rtc.cmos_write(reg + 8, this.sectors_per_track & 0xFF);
2016-11-19 22:58:42 +01:00
//rtc.cmos_write(CMOS_BIOS_DISKTRANSFLAG,
// rtc.cmos_read(CMOS_BIOS_DISKTRANSFLAG) | 1 << (nr * 4 + 2)); // slave
}
/** @const */
2014-01-05 03:19:09 +01:00
this.stats = {
sectors_read: 0,
sectors_written: 0,
bytes_read: 0,
bytes_written: 0,
loading: false,
2014-01-05 03:19:09 +01:00
};
this.buffer = buffer;
2014-09-27 22:36:27 +02:00
2014-09-28 23:34:19 +02:00
/** @type {number} */
2014-09-27 22:36:27 +02:00
this.is_lba = 0;
2014-09-28 23:34:19 +02:00
/** @type {number} */
2014-09-27 22:36:27 +02:00
this.bytecount = 0;
2014-09-28 23:34:19 +02:00
/** @type {number} */
2014-09-27 22:36:27 +02:00
this.sector = 0;
2014-09-28 23:34:19 +02:00
/** @type {number} */
2014-09-27 22:36:27 +02:00
this.lba_count = 0;
2014-09-28 23:34:19 +02:00
/** @type {number} */
2014-09-27 22:36:27 +02:00
this.cylinder_low = 0;
2014-09-28 23:34:19 +02:00
/** @type {number} */
2014-09-27 22:36:27 +02:00
this.cylinder_high = 0;
2014-09-28 23:34:19 +02:00
/** @type {number} */
2014-09-27 22:36:27 +02:00
this.head = 0;
2014-09-28 23:34:19 +02:00
/** @type {number} */
this.drive_head = 0;
2014-09-27 22:36:27 +02:00
2014-09-28 23:34:19 +02:00
/** @type {number} */
this.status = 0x50;
2014-09-27 22:36:27 +02:00
2014-09-28 23:34:19 +02:00
/** @type {number} */
2016-11-19 22:58:42 +01:00
this.sectors_per_drq = 0x80;
2014-09-27 22:36:27 +02:00
2014-09-28 23:34:19 +02:00
/** @type {number} */
this.error = 0;
2014-07-13 00:41:57 +02:00
/** @type {number} */
this.data_pointer = 0;
2013-12-29 00:34:10 +01:00
2016-11-19 22:58:42 +01:00
this.data = new Uint8Array(64 * 1024);
this.data16 = new Uint16Array(this.data.buffer);
this.data32 = new Int32Array(this.data.buffer);
2013-12-29 00:34:10 +01:00
/** @type {number} */
2016-11-19 22:58:42 +01:00
this.data_length = 0;
/** @type {number} */
this.data_end = 0;
2014-07-13 00:41:57 +02:00
/** @type {number} */
2016-11-19 22:58:42 +01:00
this.current_command = -1;
/** @type {number} */
2016-11-19 22:58:42 +01:00
this.current_atapi_command = -1;
2016-11-19 22:58:42 +01:00
/** @type {number} */
this.write_dest = 0;
// cancellation support
this.last_io_id = 0;
this.in_progress_io_ids = new Set();
this.cancelled_io_ids = new Set();
2016-11-19 22:58:42 +01:00
Object.seal(this);
}
IDEInterface.prototype.device_reset = function()
2015-05-18 22:20:39 +02:00
{
if(this.is_atapi)
{
this.status = 0;
2015-05-18 22:20:39 +02:00
this.bytecount = 1;
this.error = 1;
2015-05-18 22:20:39 +02:00
this.sector = 1; // lba_low
this.cylinder_low = 0x14; // lba_mid
2016-11-19 22:58:42 +01:00
this.cylinder_high = 0xEB; // lba_high
2015-05-18 22:20:39 +02:00
}
else
{
this.status = 0x50 | 1;
this.bytecount = 1;
this.error = 1;
2015-05-18 22:20:39 +02:00
this.sector = 1; // lba_low
// 0, 0 needed by bochs bios
this.cylinder_low = 0; // lba_mid
this.cylinder_high = 0; // lba_high
2015-05-18 22:20:39 +02:00
}
this.cancel_io_operations();
2015-05-18 22:20:39 +02:00
};
IDEInterface.prototype.push_irq = function()
{
this.device.push_irq();
};
IDEInterface.prototype.ata_command = function(cmd)
2014-09-27 22:36:27 +02:00
{
dbg_log("ATA Command: " + h(cmd) + " slave=" + (this.drive_head >> 4 & 1), LOG_DISK);
2013-12-29 00:34:10 +01:00
if(!this.buffer)
{
2016-11-19 22:58:42 +01:00
dbg_log("abort: No buffer", LOG_DISK);
this.error = 4;
this.status = 0x41;
this.push_irq();
return;
2013-12-29 00:34:10 +01:00
}
2016-11-19 22:58:42 +01:00
this.current_command = cmd;
this.error = 0;
2014-09-27 22:36:27 +02:00
switch(cmd)
{
case 0x08:
dbg_log("ATA device reset", LOG_DISK);
this.data_pointer = 0;
2016-11-19 22:58:42 +01:00
this.data_end = 0;
this.data_length = 0;
2015-05-18 22:20:39 +02:00
this.device_reset();
2014-09-27 22:36:27 +02:00
this.push_irq();
break;
case 0x10:
2016-11-19 22:58:42 +01:00
// calibrate drive
this.status = 0x50;
this.cylinder_low = 0;
this.push_irq();
break;
case 0xF8:
// read native max address
this.status = 0x50;
var last_sector = this.sector_count - 1;
this.sector = last_sector & 0xFF;
this.cylinder_low = last_sector >> 8 & 0xFF;
this.cylinder_high = last_sector >> 16 & 0xFF;
this.drive_head = this.drive_head & 0xF0 | last_sector >> 24 & 0x0F;
2014-09-27 22:36:27 +02:00
this.push_irq();
break;
case 0x27:
2016-11-19 22:58:42 +01:00
// read native max address ext
this.status = 0x50;
var last_sector = this.sector_count - 1;
this.sector = last_sector & 0xFF;
this.cylinder_low = last_sector >> 8 & 0xFF;
this.cylinder_high = last_sector >> 16 & 0xFF;
this.sector |= last_sector >> 24 << 8 & 0xFF00;
2014-09-27 22:36:27 +02:00
this.push_irq();
break;
case 0x20:
case 0x24:
case 0x29:
2014-09-27 22:36:27 +02:00
case 0xC4:
// 0x20 read sectors
// 0x24 read sectors ext
// 0xC4 read multiple
2014-09-27 22:36:27 +02:00
// 0x29 read multiple ext
this.ata_read_sectors(cmd);
break;
case 0x30:
case 0x34:
case 0x39:
case 0xC5:
2014-09-27 22:36:27 +02:00
// 0x30 write sectors
// 0x34 write sectors ext
// 0xC5 write multiple
2014-09-27 22:36:27 +02:00
// 0x39 write multiple ext
2016-11-19 22:58:42 +01:00
this.ata_write_sectors(cmd);
2014-09-27 22:36:27 +02:00
break;
case 0x90:
2016-11-19 22:58:42 +01:00
// execute device diagnostic
2014-09-27 22:36:27 +02:00
this.push_irq();
this.error = 0x101;
2014-09-27 22:36:27 +02:00
this.status = 0x50;
break;
case 0x91:
2016-11-19 22:58:42 +01:00
// initialize device parameters
this.status = 0x50;
2014-09-27 22:36:27 +02:00
this.push_irq();
break;
case 0xA0:
2016-11-19 22:58:42 +01:00
// ATA packet
2014-09-27 22:36:27 +02:00
if(this.is_atapi)
{
this.status = 0x58;
2016-11-19 22:58:42 +01:00
this.data_allocate(12);
this.data_end = 12;
2014-09-27 22:36:27 +02:00
this.bytecount = 1;
this.push_irq();
}
break;
2013-11-28 00:59:36 +01:00
2014-09-27 22:36:27 +02:00
case 0xA1:
dbg_log("ATA identify packet device", LOG_DISK);
2014-09-27 22:36:27 +02:00
if(this.is_atapi)
2013-11-28 00:59:36 +01:00
{
2014-09-27 22:36:27 +02:00
this.create_identify_packet();
this.status = 0x58;
2013-12-22 04:53:52 +01:00
this.cylinder_low = 0x14;
2016-11-19 22:58:42 +01:00
this.cylinder_high = 0xEB;
2014-09-27 22:36:27 +02:00
this.push_irq();
}
else
{
2017-03-12 02:00:09 +01:00
this.status = 0x41;
2014-09-27 22:36:27 +02:00
this.push_irq();
}
break;
2013-12-22 04:53:52 +01:00
2014-09-27 22:36:27 +02:00
case 0xC6:
2016-11-19 22:58:42 +01:00
// set multiple mode
2014-09-27 22:36:27 +02:00
// Logical sectors per DRQ Block in word 1
2016-11-19 22:58:42 +01:00
dbg_log("Logical sectors per DRQ Block: " + h(this.bytecount & 0xFF), LOG_DISK);
this.sectors_per_drq = this.bytecount & 0xFF;
this.status = 0x50;
2014-09-27 22:36:27 +02:00
this.push_irq();
break;
2013-12-29 00:34:10 +01:00
2016-11-19 22:58:42 +01:00
case 0x25: // read dma ext
case 0xC8: // read dma
2014-09-27 22:36:27 +02:00
this.ata_read_sectors_dma(cmd);
break;
2013-12-29 00:34:10 +01:00
2016-11-19 22:58:42 +01:00
case 0x35: // write dma ext
case 0xCA: // write dma
this.ata_write_sectors_dma(cmd);
break;
case 0x40:
2016-11-19 22:58:42 +01:00
dbg_log("read verify sectors", LOG_DISK);
this.status = 0x50;
this.push_irq();
break;
case 0xDA:
2016-11-19 22:58:42 +01:00
dbg_log("Unimplemented: get media status", LOG_DISK);
this.status = 0x41;
this.error = 4;
2016-11-19 22:58:42 +01:00
this.push_irq();
break;
2017-06-05 17:12:24 +02:00
case 0xE0:
dbg_log("ATA standby immediate", LOG_DISK);
this.status = 0x50;
this.push_irq();
break;
2014-09-27 22:36:27 +02:00
case 0xE1:
dbg_log("ATA idle immediate", LOG_DISK);
2016-11-19 22:58:42 +01:00
this.status = 0x50;
2014-09-27 22:36:27 +02:00
this.push_irq();
break;
2013-12-29 00:34:10 +01:00
2017-04-04 21:46:29 +02:00
case 0xE7:
dbg_log("ATA flush cache", LOG_DISK);
this.status = 0x50;
this.push_irq();
break;
2014-09-27 22:36:27 +02:00
case 0xEC:
dbg_log("ATA identify device", LOG_DISK);
2013-12-29 00:34:10 +01:00
2014-09-27 22:36:27 +02:00
if(this.is_atapi)
2013-12-29 00:34:10 +01:00
{
this.status = 0x41;
this.error = 4;
this.push_irq();
2014-09-27 22:36:27 +02:00
return;
}
2013-12-29 00:34:10 +01:00
2014-09-27 22:36:27 +02:00
this.create_identify_packet();
this.status = 0x58;
2013-12-29 00:34:10 +01:00
2014-09-27 22:36:27 +02:00
this.push_irq();
break;
2013-12-29 00:34:10 +01:00
2014-09-27 22:36:27 +02:00
case 0xEA:
2016-11-19 22:58:42 +01:00
dbg_log("flush cache ext", LOG_DISK);
this.status = 0x50;
2014-09-27 22:36:27 +02:00
this.push_irq();
break;
2013-12-29 00:34:10 +01:00
2014-09-27 22:36:27 +02:00
case 0xEF:
2016-11-19 22:58:42 +01:00
dbg_log("set features: " + h(this.bytecount & 0xFF), LOG_DISK);
this.status = 0x50;
this.push_irq();
break;
2013-12-29 00:34:10 +01:00
2021-01-16 22:22:16 +01:00
case 0xDE:
// obsolete
this.status = 0x50;
this.push_irq();
break;
case 0xF5:
2016-11-19 22:58:42 +01:00
dbg_log("security freeze lock", LOG_DISK);
this.status = 0x50;
2014-09-27 22:36:27 +02:00
this.push_irq();
break;
2013-12-29 00:34:10 +01:00
2016-11-19 22:58:42 +01:00
case 0xF9:
dbg_log("Unimplemented: set max address", LOG_DISK);
this.status = 0x41;
this.error = 4;
break;
2014-09-27 22:36:27 +02:00
default:
dbg_assert(false, "New ATA cmd on 1F7: " + h(cmd), LOG_DISK);
2013-12-29 00:34:10 +01:00
this.status = 0x41;
2014-09-27 22:36:27 +02:00
// abort bit set
this.error = 4;
2014-01-02 05:07:04 +01:00
}
2014-09-27 22:36:27 +02:00
};
2014-01-02 05:07:04 +01:00
IDEInterface.prototype.atapi_handle = function()
2014-09-27 22:36:27 +02:00
{
2016-11-19 22:58:42 +01:00
dbg_log("ATAPI Command: " + h(this.data[0]) +
" slave=" + (this.drive_head >> 4 & 1), LOG_DISK);
this.data_pointer = 0;
2016-11-19 22:58:42 +01:00
this.current_atapi_command = this.data[0];
2016-11-19 22:58:42 +01:00
switch(this.current_atapi_command)
2014-09-27 22:36:27 +02:00
{
case 0x00:
2016-11-19 22:58:42 +01:00
dbg_log("test unit ready", LOG_DISK);
2014-09-27 22:36:27 +02:00
// test unit ready
2016-11-19 22:58:42 +01:00
this.data_allocate(0);
this.data_end = this.data_length;
this.status = 0x50;
2014-09-27 22:36:27 +02:00
break;
case 0x03:
// request sense
2016-11-19 22:58:42 +01:00
this.data_allocate(this.data[4]);
this.data_end = this.data_length;
2014-09-27 22:36:27 +02:00
this.status = 0x58;
2016-11-19 22:58:42 +01:00
this.data[0] = 0x80 | 0x70;
this.data[2] = 5; // illegal request
this.data[7] = 8;
2014-09-27 22:36:27 +02:00
break;
case 0x12:
// inquiry
2016-11-19 22:58:42 +01:00
var length = this.data[4];
2014-09-27 22:36:27 +02:00
this.status = 0x58;
2016-11-19 22:58:42 +01:00
dbg_log("inquiry: " + h(this.data[1], 2) + " length=" + length, LOG_DISK);
2014-09-27 22:36:27 +02:00
// http://www.t10.org/ftp/x3t9.2/document.87/87-106r0.txt
2016-11-19 22:58:42 +01:00
//this.data_allocate(36);
this.data.set([
2014-09-27 22:36:27 +02:00
0x05, 0x80, 0x01, 0x31,
// additional length
31,
0, 0, 0,
2014-09-27 22:36:27 +02:00
// 8
0x53, 0x4F, 0x4E, 0x59,
0x20, 0x20, 0x20, 0x20,
// 16
0x43, 0x44, 0x2D, 0x52,
0x4F, 0x4D, 0x20, 0x43,
0x44, 0x55, 0x2D, 0x31,
0x30, 0x30, 0x30, 0x20,
// 32
0x31, 0x2E, 0x31, 0x61,
]);
2016-11-19 22:58:42 +01:00
this.data_end = this.data_length = Math.min(36, length);
break;
case 0x1A:
// mode sense (6)
this.data_allocate(this.data[4]);
this.data_end = this.data_length;
this.status = 0x58;
2014-09-27 22:36:27 +02:00
break;
case 0x1E:
// prevent/allow medium removal
2016-11-19 22:58:42 +01:00
this.data_allocate(0);
this.data_end = this.data_length;
2014-09-27 22:36:27 +02:00
this.status = 0x50;
break;
case 0x25:
// read capacity
var count = this.sector_count - 1;
2016-11-19 22:58:42 +01:00
this.data_set(new Uint8Array([
count >> 24 & 0xFF,
count >> 16 & 0xFF,
count >> 8 & 0xFF,
count & 0xFF,
2014-09-27 22:36:27 +02:00
0,
0,
2016-11-19 22:58:42 +01:00
this.sector_size >> 8 & 0xFF,
this.sector_size & 0xFF,
]));
2016-11-19 22:58:42 +01:00
this.data_end = this.data_length;
2014-09-27 22:36:27 +02:00
this.status = 0x58;
break;
case 0x28:
// read
if(this.lba_count & 1)
2014-01-02 05:07:04 +01:00
{
2016-11-19 22:58:42 +01:00
this.atapi_read_dma(this.data);
2014-01-02 05:07:04 +01:00
}
else
{
2016-11-19 22:58:42 +01:00
this.atapi_read(this.data);
2014-01-02 05:07:04 +01:00
}
2014-09-27 22:36:27 +02:00
break;
case 0x42:
2016-11-19 22:58:42 +01:00
var length = this.data[8];
this.data_allocate(Math.min(8, length));
this.data_end = this.data_length;
dbg_log("read q subcode: length=" + length, LOG_DISK);
this.status = 0x58;
break;
2014-09-27 22:36:27 +02:00
case 0x43:
// read toc
2016-11-19 22:58:42 +01:00
var length = this.data[8] | this.data[7] << 8;
var format = this.data[9] >> 6;
2016-11-19 22:58:42 +01:00
this.data_allocate(length);
this.data_end = this.data_length;
dbg_log("read toc: " + h(format, 2) +
" length=" + length +
" " + (this.data[1] & 2) +
" " + h(this.data[6]), LOG_DISK);
if(format === 0)
{
var sector_count = this.sector_count;
this.data.set(new Uint8Array([
0, 18, // length
1, 1, // first and last session
0,
0x14,
1, // track number
0,
0, 0, 0, 0,
0,
0x16,
0xAA, // track number
0,
sector_count >> 24,
sector_count >> 16 & 0xFF,
sector_count >> 8 & 0xFF,
sector_count & 0xFF,
]));
}
else if(format === 1)
{
2016-11-19 22:58:42 +01:00
this.data.set(new Uint8Array([
0, 10, // length
2016-11-19 22:58:42 +01:00
1, 1, // first and last session
0, 0,
0, 0,
0, 0,
0, 0,
]));
}
2016-11-19 22:58:42 +01:00
else
{
dbg_assert(false, "Unimplemented format: " + format);
}
2014-09-27 22:36:27 +02:00
this.status = 0x58;
break;
case 0x46:
// get configuration
2016-11-19 22:58:42 +01:00
var length = this.data[8] | this.data[7] << 8;
length = Math.min(length, 32);
this.data_allocate(length);
this.data_end = this.data_length;
this.data[0] = length - 4 >> 24 & 0xFF;
this.data[1] = length - 4 >> 16 & 0xFF;
this.data[2] = length - 4 >> 8 & 0xFF;
this.data[3] = length - 4 & 0xFF;
this.data[6] = 0x08;
this.data[10] = 3;
2014-09-27 22:36:27 +02:00
this.status = 0x58;
break;
case 0x51:
// read disk information
2016-11-19 22:58:42 +01:00
this.data_allocate(0);
this.data_end = this.data_length;
2014-09-27 22:36:27 +02:00
this.status = 0x50;
break;
2016-11-19 22:58:42 +01:00
case 0x52:
dbg_log("Unimplemented ATAPI command: " + h(this.data[0]), LOG_DISK);
this.status = 0x51;
this.data_length = 0;
this.error = 5 << 4;
2014-09-27 22:36:27 +02:00
break;
case 0x5A:
// mode sense
2016-11-19 22:58:42 +01:00
var length = this.data[8] | this.data[7] << 8;
var page_code = this.data[2];
dbg_log("mode sense: " + h(page_code) + " length=" + length, LOG_DISK);
if(page_code === 0x2A)
{
2016-11-19 22:58:42 +01:00
this.data_allocate(Math.min(30, length));
}
2016-11-19 22:58:42 +01:00
this.data_end = this.data_length;
this.status = 0x58;
break;
case 0xBD:
// mechanism status
2016-11-19 22:58:42 +01:00
this.data_allocate(this.data[9] | this.data[8] << 8);
this.data_end = this.data_length;
this.data[5] = 1;
this.status = 0x58;
2014-09-27 22:36:27 +02:00
break;
2016-11-19 22:58:42 +01:00
case 0x4A:
this.status = 0x51;
this.data_length = 0;
this.error = 5 << 4;
dbg_log("Unimplemented ATAPI command: " + h(this.data[0]), LOG_DISK);
break;
2021-03-31 04:22:30 +02:00
case 0xBE:
// Hiren's boot CD
dbg_log("Unimplemented ATAPI command: " + h(this.data[0]), LOG_DISK);
this.data_allocate(0);
this.data_end = this.data_length;
this.status = 0x50;
break;
2014-09-27 22:36:27 +02:00
default:
2016-11-19 22:58:42 +01:00
this.status = 0x51;
this.data_length = 0;
this.error = 5 << 4;
dbg_log("Unimplemented ATAPI command: " + h(this.data[0]), LOG_DISK);
dbg_assert(false);
2014-09-27 22:36:27 +02:00
}
2013-12-29 00:34:10 +01:00
this.bytecount = this.bytecount & ~7 | 2;
2014-01-02 05:07:04 +01:00
if((this.status & 0x80) === 0)
2014-09-27 22:36:27 +02:00
{
this.push_irq();
}
2014-01-02 05:07:04 +01:00
2016-11-19 22:58:42 +01:00
if((this.status & 0x80) === 0 && this.data_length === 0)
{
this.bytecount |= 1;
this.status &= ~8;
}
2014-09-27 22:36:27 +02:00
};
2014-01-02 05:07:04 +01:00
IDEInterface.prototype.do_write = function()
2014-09-27 22:36:27 +02:00
{
this.status = 0x50;
2016-11-19 22:58:42 +01:00
dbg_assert(this.data_length <= this.data.length);
var data = this.data.subarray(0, this.data_length);
//dbg_log(hex_dump(data), LOG_DISK);
2016-11-19 22:58:42 +01:00
dbg_assert(this.data_length % 512 === 0);
this.ata_advance(this.current_command, this.data_length / 512);
this.push_irq();
this.buffer.set(this.write_dest, data, function()
{
2016-11-19 22:58:42 +01:00
});
2014-09-27 22:36:27 +02:00
2016-11-19 22:58:42 +01:00
this.report_write(this.data_length);
2014-09-27 22:36:27 +02:00
};
IDEInterface.prototype.atapi_read = function(cmd)
2014-09-27 22:36:27 +02:00
{
// Note: Big Endian
2016-11-19 22:58:42 +01:00
var lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5];
var count = cmd[7] << 8 | cmd[8];
var flags = cmd[1];
var byte_count = count * this.sector_size;
var start = lba * this.sector_size;
2014-09-27 22:36:27 +02:00
dbg_log("CD read lba=" + h(lba) +
2014-09-27 22:36:27 +02:00
" lbacount=" + h(count) +
" bytecount=" + h(byte_count) +
2016-11-19 22:58:42 +01:00
" flags=" + h(flags), LOG_DISK);
2014-09-27 22:36:27 +02:00
2016-11-19 22:58:42 +01:00
this.data_length = 0;
var req_length = this.cylinder_high << 8 & 0xFF00 | this.cylinder_low & 0xFF;
dbg_log(h(this.cylinder_high, 2) + " " + h(this.cylinder_low, 2), LOG_DISK);
this.cylinder_low = this.cylinder_high = 0; // oak technology driver (windows 3.0)
2016-11-19 22:58:42 +01:00
if(req_length === 0xFFFF)
req_length--;
2016-11-19 22:58:42 +01:00
if(req_length > byte_count)
{
req_length = byte_count;
}
2014-09-27 22:36:27 +02:00
if(start >= this.buffer.byteLength)
{
dbg_assert(false, "CD read: Outside of disk end=" + h(start + byte_count) +
" size=" + h(this.buffer.byteLength), LOG_DISK);
2014-09-27 22:36:27 +02:00
this.status = 0xFF;
this.push_irq();
}
else if(byte_count === 0)
{
this.status = 0x50;
this.data_pointer = 0;
//this.push_irq();
}
2014-09-27 22:36:27 +02:00
else
{
2014-09-27 22:36:27 +02:00
byte_count = Math.min(byte_count, this.buffer.byteLength - start);
this.status = 0x50 | 0x80;
2015-01-13 20:37:01 +01:00
this.report_read_start();
this.read_buffer(start, byte_count, (data) =>
{
2016-11-19 22:58:42 +01:00
//setTimeout(() => {
2017-05-09 19:27:52 +02:00
dbg_log("cd read: data arrived", LOG_DISK);
2016-11-19 22:58:42 +01:00
this.data_set(data);
2014-09-27 22:36:27 +02:00
this.status = 0x58;
2016-11-19 22:58:42 +01:00
this.bytecount = this.bytecount & ~7 | 2;
2014-09-27 22:36:27 +02:00
this.push_irq();
2016-11-19 22:58:42 +01:00
req_length &= ~3;
this.data_end = req_length;
if(this.data_end > this.data_length)
{
this.data_end = this.data_length;
}
this.cylinder_low = this.data_end & 0xFF;
this.cylinder_high = this.data_end >> 8 & 0xFF;
2015-01-13 20:37:01 +01:00
this.report_read_end(byte_count);
2016-11-19 22:58:42 +01:00
//}, 10);
});
2014-09-27 22:36:27 +02:00
}
};
2013-12-29 00:34:10 +01:00
IDEInterface.prototype.atapi_read_dma = function(cmd)
2014-09-27 22:36:27 +02:00
{
// Note: Big Endian
2016-11-19 22:58:42 +01:00
var lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5];
var count = cmd[7] << 8 | cmd[8];
var flags = cmd[1];
var byte_count = count * this.sector_size;
var start = lba * this.sector_size;
2013-12-29 00:34:10 +01:00
dbg_log("CD read DMA lba=" + h(lba) +
2014-09-27 22:36:27 +02:00
" lbacount=" + h(count) +
" bytecount=" + h(byte_count) +
2016-11-19 22:58:42 +01:00
" flags=" + h(flags), LOG_DISK);
2013-12-29 00:34:10 +01:00
2014-09-27 22:36:27 +02:00
if(start >= this.buffer.byteLength)
{
dbg_assert(false, "CD read: Outside of disk end=" + h(start + byte_count) +
" size=" + h(this.buffer.byteLength), LOG_DISK);
2013-12-29 00:34:10 +01:00
2014-09-27 22:36:27 +02:00
this.status = 0xFF;
this.push_irq();
}
else
{
this.status = 0x50 | 0x80;
this.report_read_start();
this.read_buffer(start, byte_count, (data) =>
{
dbg_log("atapi_read_dma: Data arrived");
this.report_read_end(byte_count);
this.status = 0x58;
this.bytecount = this.bytecount & ~7 | 2;
this.data_set(data);
this.do_atapi_dma();
});
2016-11-19 22:58:42 +01:00
}
2017-05-09 19:27:52 +02:00
};
2013-12-29 00:34:10 +01:00
IDEInterface.prototype.do_atapi_dma = function()
2016-11-19 22:58:42 +01:00
{
if((this.device.dma_status & 1) === 0)
2016-11-19 22:58:42 +01:00
{
dbg_log("do_atapi_dma: Status not set", LOG_DISK);
return;
}
2016-11-19 22:58:42 +01:00
if((this.status & 0x8) === 0)
{
dbg_log("do_atapi_dma: DRQ not set", LOG_DISK);
return;
}
2016-11-19 22:58:42 +01:00
dbg_log("atapi dma transfer len=" + this.data_length, LOG_DISK);
2016-11-19 22:58:42 +01:00
var prdt_start = this.device.prdt_addr;
var offset = 0;
var data = this.data;
do {
var addr = this.cpu.read32s(prdt_start);
var count = this.cpu.read16(prdt_start + 4);
var end = this.cpu.read8(prdt_start + 7) & 0x80;
if(!count)
{
count = 0x10000;
}
dbg_log("dma read dest=" + h(addr) + " count=" + h(count) + " datalen=" + h(this.data_length), LOG_DISK);
this.cpu.write_blob(data.subarray(offset, Math.min(offset + count, this.data_length)), addr);
2016-11-19 22:58:42 +01:00
offset += count;
prdt_start += 8;
if(offset >= this.data_length && !end)
2016-11-19 22:58:42 +01:00
{
dbg_log("leave early end=" + (+end) +
" offset=" + h(offset) +
" data_length=" + h(this.data_length) +
" cmd=" + h(this.current_command), LOG_DISK);
break;
}
2014-09-27 22:36:27 +02:00
}
2016-11-19 22:58:42 +01:00
while(!end);
dbg_log("end offset=" + offset, LOG_DISK);
this.status = 0x50;
this.device.dma_status &= ~1;
this.bytecount = this.bytecount & ~7 | 3;
2016-11-19 22:58:42 +01:00
this.push_irq();
2014-09-27 22:36:27 +02:00
};
2013-12-29 00:34:10 +01:00
IDEInterface.prototype.read_data = function(length)
2014-09-27 22:36:27 +02:00
{
2016-11-19 22:58:42 +01:00
if(this.data_pointer < this.data_end)
2014-01-02 05:07:04 +01:00
{
2016-11-19 22:58:42 +01:00
dbg_assert(this.data_pointer + length - 1 < this.data_end);
dbg_assert(this.data_pointer % length === 0, h(this.data_pointer) + " " + length);
if(length === 1)
2014-01-02 05:07:04 +01:00
{
2016-11-19 22:58:42 +01:00
var result = this.data[this.data_pointer];
2014-01-02 05:07:04 +01:00
}
else if(length === 2)
2014-09-27 22:36:27 +02:00
{
2016-11-19 22:58:42 +01:00
var result = this.data16[this.data_pointer >>> 1];
2014-01-03 02:12:35 +01:00
}
2014-01-02 05:07:04 +01:00
else
{
2016-11-19 22:58:42 +01:00
var result = this.data32[this.data_pointer >>> 2];
2014-01-02 05:07:04 +01:00
}
2013-12-29 00:34:10 +01:00
this.data_pointer += length;
2016-11-19 22:58:42 +01:00
var align = (this.data_end & 0xFFF) === 0 ? 0xFFF : 0xFF;
if((this.data_pointer & align) === 0)
{
dbg_log("Read 1F0: " + h(this.data[this.data_pointer], 2) +
" cur=" + h(this.data_pointer) +
" cnt=" + h(this.data_length), LOG_DISK);
}
if(this.data_pointer >= this.data_end)
{
2016-11-19 22:58:42 +01:00
this.read_end();
}
2016-11-19 22:58:42 +01:00
return result;
}
else
{
dbg_log("Read 1F0: empty", LOG_DISK);
this.data_pointer += length;
return 0;
}
};
IDEInterface.prototype.read_end = function()
{
dbg_log("read_end cmd=" + h(this.current_command) + " data_pointer=" + h(this.data_pointer) +
" end=" + h(this.data_end) + " length=" + h(this.data_length), LOG_DISK);
if(this.current_command === 0xA0)
{
if(this.data_end === this.data_length)
2014-09-27 22:36:27 +02:00
{
this.status = 0x50;
this.bytecount = this.bytecount & ~7 | 3;
2016-11-19 22:58:42 +01:00
this.push_irq();
}
2016-11-19 22:58:42 +01:00
else
{
2016-11-19 22:58:42 +01:00
this.status = 0x58;
this.bytecount = this.bytecount & ~7 | 2;
this.push_irq();
2016-11-19 22:58:42 +01:00
var byte_count = this.cylinder_high << 8 & 0xFF00 | this.cylinder_low & 0xFF;
2013-12-29 00:34:10 +01:00
2016-11-19 22:58:42 +01:00
if(this.data_end + byte_count > this.data_length)
2014-09-27 22:36:27 +02:00
{
2016-11-19 22:58:42 +01:00
this.cylinder_low = (this.data_length - this.data_end) & 0xFF;
this.cylinder_high = (this.data_length - this.data_end) >> 8 & 0xFF;
this.data_end = this.data_length;
2014-09-27 22:36:27 +02:00
}
else
{
2016-11-19 22:58:42 +01:00
this.data_end += byte_count;
}
2016-11-19 22:58:42 +01:00
dbg_log("data_end=" + h(this.data_end), LOG_DISK);
2014-09-27 22:36:27 +02:00
}
}
else
{
2016-11-19 22:58:42 +01:00
this.error = 0;
if(this.data_pointer >= this.data_length)
{
this.status = 0x50;
this.push_irq();
}
else
{
if(this.current_command === 0xC4 || this.current_command === 0x29)
{
var sector_count = Math.min(this.sectors_per_drq,
(this.data_length - this.data_end) / 512);
dbg_assert(sector_count % 1 === 0);
}
else
{
dbg_assert(this.current_command === 0x20 || this.current_command === 0x24);
var sector_count = 1;
}
this.ata_advance(this.current_command, sector_count);
this.data_end += 512 * sector_count;
this.status = 0x58;
this.push_irq();
}
2014-09-27 22:36:27 +02:00
}
};
2016-11-19 22:58:42 +01:00
IDEInterface.prototype.write_data_port = function(data, length)
2014-09-27 22:36:27 +02:00
{
2016-11-19 22:58:42 +01:00
dbg_assert(this.data_pointer % length === 0);
if(this.data_pointer >= this.data_end)
{
2016-11-19 22:58:42 +01:00
dbg_log("Redundant write to data port: " + h(data) + " count=" + h(this.data_end) +
" cur=" + h(this.data_pointer), LOG_DISK);
}
else
2014-09-27 22:36:27 +02:00
{
2016-11-19 22:58:42 +01:00
var align = (this.data_end & 0xFFF) === 0 ? 0xFFF : 0xFF;
if((this.data_pointer + length & align) === 0 || this.data_end < 20)
2014-09-27 22:36:27 +02:00
{
2016-11-19 22:58:42 +01:00
dbg_log("Data port: " + h(data >>> 0) + " count=" + h(this.data_end) +
" cur=" + h(this.data_pointer), LOG_DISK);
2014-01-02 05:07:04 +01:00
}
2014-09-27 22:36:27 +02:00
2016-11-19 22:58:42 +01:00
if(length === 1)
{
2016-11-19 22:58:42 +01:00
this.data[this.data_pointer++] = data;
}
else if(length === 2)
{
this.data16[this.data_pointer >>> 1] = data;
this.data_pointer += 2;
}
else
{
this.data32[this.data_pointer >>> 2] = data;
this.data_pointer += 4;
2013-12-29 00:34:10 +01:00
}
2014-09-27 22:36:27 +02:00
2016-11-19 22:58:42 +01:00
dbg_assert(this.data_pointer <= this.data_end);
if(this.data_pointer === this.data_end)
{
2016-11-19 22:58:42 +01:00
this.write_end();
}
2014-09-27 22:36:27 +02:00
}
};
2016-11-19 22:58:42 +01:00
IDEInterface.prototype.write_data_port8 = function(data)
{
this.write_data_port(data, 1);
};
IDEInterface.prototype.write_data_port16 = function(data)
{
2016-11-19 22:58:42 +01:00
this.write_data_port(data, 2);
};
IDEInterface.prototype.write_data_port32 = function(data)
{
2016-11-19 22:58:42 +01:00
this.write_data_port(data, 4);
};
IDEInterface.prototype.write_end = function()
{
if(this.current_command === 0xA0)
{
2017-05-09 19:27:52 +02:00
this.atapi_handle();
2016-11-19 22:58:42 +01:00
}
else
{
dbg_log("write_end data_pointer=" + h(this.data_pointer) +
" data_length=" + h(this.data_length), LOG_DISK);
if(this.data_pointer >= this.data_length)
{
2017-05-09 19:27:52 +02:00
this.do_write();
2016-11-19 22:58:42 +01:00
}
else
{
2021-01-01 02:14:32 +01:00
dbg_assert(this.current_command === 0x30 ||
this.current_command === 0x34 ||
this.current_command === 0xC5,
2021-01-01 02:14:28 +01:00
"Unexpected command: " + h(this.current_command));
2016-11-19 22:58:42 +01:00
// XXX: Should advance here, but do_write does all the advancing
//this.ata_advance(this.current_command, 1);
this.status = 0x58;
this.data_end += 512;
2017-05-09 19:27:52 +02:00
this.push_irq();
2016-11-19 22:58:42 +01:00
}
}
};
IDEInterface.prototype.ata_advance = function(cmd, sectors)
{
dbg_log("Advance sectors=" + sectors + " old_bytecount=" + this.bytecount, LOG_DISK);
this.bytecount -= sectors;
2017-06-11 18:08:58 +02:00
if(cmd === 0x24 || cmd === 0x29 || cmd === 0x34 || cmd === 0x39 ||
cmd === 0x25 || cmd === 0x35)
2016-11-19 22:58:42 +01:00
{
var new_sector = sectors + this.get_lba48();
this.sector = new_sector & 0xFF | new_sector >> 16 & 0xFF00;
this.cylinder_low = new_sector >> 8 & 0xFF;
this.cylinder_high = new_sector >> 16 & 0xFF;
}
else if(this.is_lba)
{
var new_sector = sectors + this.get_lba28();
this.sector = new_sector & 0xFF;
this.cylinder_low = new_sector >> 8 & 0xFF;
this.cylinder_high = new_sector >> 16 & 0xFF;
this.head = this.head & ~0xF | new_sector & 0xF;
}
else // chs
{
var new_sector = sectors + this.get_chs();
var c = new_sector / (this.head_count * this.sectors_per_track) | 0;
this.cylinder_low = c & 0xFF;
this.cylinder_high = c >> 8 & 0xFF;
this.head = (new_sector / this.sectors_per_track | 0) % this.head_count & 0xF;
this.sector = (new_sector % this.sectors_per_track + 1) & 0xFF;
dbg_assert(new_sector === this.get_chs());
}
};
IDEInterface.prototype.ata_read_sectors = function(cmd)
2014-09-27 22:36:27 +02:00
{
2017-06-11 18:08:58 +02:00
var is_lba48 = cmd === 0x24 || cmd === 0x29;
var count = this.get_count(is_lba48);
var lba = this.get_lba(is_lba48);
2013-12-29 00:34:10 +01:00
2016-11-19 22:58:42 +01:00
var is_single = cmd === 0x20 || cmd === 0x24;
2013-12-29 00:34:10 +01:00
2016-11-19 22:58:42 +01:00
var byte_count = count * this.sector_size;
var start = lba * this.sector_size;
2013-12-29 00:34:10 +01:00
dbg_log("ATA read cmd=" + h(cmd) +
" mode=" + (this.is_lba ? "lba" : "chs") +
" lba=" + h(lba) +
2014-09-27 22:36:27 +02:00
" lbacount=" + h(count) +
" bytecount=" + h(byte_count), LOG_DISK);
if(start + byte_count > this.buffer.byteLength)
{
2016-11-19 22:58:42 +01:00
dbg_assert(false, "ATA read: Outside of disk", LOG_DISK);
2014-09-27 22:36:27 +02:00
this.status = 0xFF;
this.push_irq();
}
else
{
2016-11-19 22:58:42 +01:00
this.status = 0x80 | 0x40;
2015-01-13 20:37:01 +01:00
this.report_read_start();
2013-12-29 00:34:10 +01:00
this.read_buffer(start, byte_count, (data) =>
2014-01-02 05:07:04 +01:00
{
2016-11-19 22:58:42 +01:00
//setTimeout(() => {
dbg_log("ata_read: Data arrived", LOG_DISK);
this.data_set(data);
2014-09-27 22:36:27 +02:00
this.status = 0x58;
2016-11-19 22:58:42 +01:00
this.data_end = is_single ? 512 : Math.min(byte_count, this.sectors_per_drq * 512);
this.ata_advance(cmd, is_single ? 1 : Math.min(count, this.sectors_per_track));
2013-12-29 00:34:10 +01:00
2014-09-27 22:36:27 +02:00
this.push_irq();
2015-01-13 20:37:01 +01:00
this.report_read_end(byte_count);
2016-11-19 22:58:42 +01:00
//}, 10);
});
2014-09-27 22:36:27 +02:00
}
};
2013-12-29 00:34:10 +01:00
IDEInterface.prototype.ata_read_sectors_dma = function(cmd)
2014-09-27 22:36:27 +02:00
{
2017-06-11 18:08:58 +02:00
var is_lba48 = cmd === 0x25;
var count = this.get_count(is_lba48);
var lba = this.get_lba(is_lba48);
2016-11-19 22:58:42 +01:00
var byte_count = count * this.sector_size;
var start = lba * this.sector_size;
2014-09-27 22:36:27 +02:00
dbg_log("ATA DMA read lba=" + h(lba) +
2014-09-27 22:36:27 +02:00
" lbacount=" + h(count) +
" bytecount=" + h(byte_count), LOG_DISK);
2014-01-02 05:07:04 +01:00
2014-09-27 22:36:27 +02:00
if(start + byte_count > this.buffer.byteLength)
{
2016-11-19 22:58:42 +01:00
dbg_assert(false, "ATA read: Outside of disk", LOG_DISK);
2014-01-05 03:19:09 +01:00
2014-09-27 22:36:27 +02:00
this.status = 0xFF;
this.push_irq();
return;
2014-01-02 05:07:04 +01:00
}
2016-11-19 22:58:42 +01:00
this.status = 0x58;
this.device.dma_status |= 1;
2016-11-19 22:58:42 +01:00
};
IDEInterface.prototype.do_ata_read_sectors_dma = function()
{
2017-06-11 18:08:58 +02:00
var cmd = this.current_command;
2016-11-19 22:58:42 +01:00
2017-06-11 18:08:58 +02:00
var is_lba48 = cmd === 0x25;
var count = this.get_count(is_lba48);
var lba = this.get_lba(is_lba48);
2016-11-19 22:58:42 +01:00
var byte_count = count * this.sector_size;
var start = lba * this.sector_size;
dbg_assert(lba < this.buffer.byteLength);
2015-01-13 20:37:01 +01:00
this.report_read_start();
2014-09-27 22:36:27 +02:00
var orig_prdt_start = this.device.prdt_addr;
this.read_buffer(start, byte_count, (data) =>
2014-01-02 05:07:04 +01:00
{
2016-11-19 22:58:42 +01:00
//setTimeout(function() {
dbg_log("do_ata_read_sectors_dma: Data arrived", LOG_DISK);
var prdt_start = this.device.prdt_addr;
var offset = 0;
2014-07-13 00:41:57 +02:00
dbg_assert(orig_prdt_start === prdt_start);
2014-09-27 22:36:27 +02:00
do {
2016-11-19 22:58:42 +01:00
var prd_addr = this.cpu.read32s(prdt_start);
var prd_count = this.cpu.read16(prdt_start + 4);
var end = this.cpu.read8(prdt_start + 7) & 0x80;
2014-07-13 00:41:57 +02:00
2016-11-19 22:58:42 +01:00
if(!prd_count)
2014-09-27 22:36:27 +02:00
{
2016-11-19 22:58:42 +01:00
prd_count = 0x10000;
dbg_log("dma: prd count was 0", LOG_DISK);
2014-09-27 22:36:27 +02:00
}
2016-11-19 22:58:42 +01:00
dbg_log("dma read transfer dest=" + h(prd_addr) +
" prd_count=" + h(prd_count), LOG_DISK);
this.cpu.write_blob(data.subarray(offset, offset + prd_count), prd_addr);
2014-09-27 22:36:27 +02:00
2016-11-19 22:58:42 +01:00
offset += prd_count;
2014-09-27 22:36:27 +02:00
prdt_start += 8;
2014-01-03 02:12:35 +01:00
}
2014-09-27 22:36:27 +02:00
while(!end);
2014-01-02 05:07:04 +01:00
dbg_assert(offset === byte_count);
2016-11-19 22:58:42 +01:00
this.ata_advance(this.current_command, count);
2014-09-27 22:36:27 +02:00
this.status = 0x50;
this.device.dma_status &= ~1;
2016-11-19 22:58:42 +01:00
this.current_command = -1;
2014-01-02 05:07:04 +01:00
2014-09-27 22:36:27 +02:00
this.push_irq();
2014-01-02 05:07:04 +01:00
2015-01-13 20:37:01 +01:00
this.report_read_end(byte_count);
2016-11-19 22:58:42 +01:00
//}.bind(this), 10);
});
2014-09-27 22:36:27 +02:00
};
2014-01-02 05:07:04 +01:00
2016-11-19 22:58:42 +01:00
IDEInterface.prototype.ata_write_sectors = function(cmd)
2014-09-27 22:36:27 +02:00
{
2017-06-11 18:08:58 +02:00
var is_lba48 = cmd === 0x34 || cmd === 0x39;
var count = this.get_count(is_lba48);
var lba = this.get_lba(is_lba48);
2016-11-19 22:58:42 +01:00
var is_single = cmd === 0x30 || cmd === 0x34;
2016-11-19 22:58:42 +01:00
var byte_count = count * this.sector_size;
var start = lba * this.sector_size;
dbg_log("ATA write lba=" + h(lba) +
2016-11-19 22:58:42 +01:00
" mode=" + (this.is_lba ? "lba" : "chs") +
2014-09-27 22:36:27 +02:00
" lbacount=" + h(count) +
" bytecount=" + h(byte_count), LOG_DISK);
if(start + byte_count > this.buffer.byteLength)
2014-01-02 05:07:04 +01:00
{
dbg_assert(false, "ATA write: Outside of disk", LOG_DISK);
2014-01-02 05:07:04 +01:00
2014-09-27 22:36:27 +02:00
this.status = 0xFF;
this.push_irq();
}
else
{
this.status = 0x58;
2016-11-19 22:58:42 +01:00
this.data_allocate_noclear(byte_count);
this.data_end = is_single ? 512 : Math.min(byte_count, this.sectors_per_drq * 512);
2014-09-27 22:36:27 +02:00
this.write_dest = start;
}
};
2014-01-02 05:07:04 +01:00
2016-11-19 22:58:42 +01:00
IDEInterface.prototype.ata_write_sectors_dma = function(cmd)
2014-09-27 22:36:27 +02:00
{
2017-06-11 18:08:58 +02:00
var is_lba48 = cmd === 0x35;
var count = this.get_count(is_lba48);
var lba = this.get_lba(is_lba48);
2016-11-19 22:58:42 +01:00
var byte_count = count * this.sector_size;
var start = lba * this.sector_size;
2014-01-02 05:07:04 +01:00
dbg_log("ATA DMA write lba=" + h(lba) +
2014-09-27 22:36:27 +02:00
" lbacount=" + h(count) +
" bytecount=" + h(byte_count), LOG_DISK);
2014-01-02 05:07:04 +01:00
2014-09-27 22:36:27 +02:00
if(start + byte_count > this.buffer.byteLength)
{
dbg_assert(false, "ATA DMA write: Outside of disk", LOG_DISK);
2014-01-02 05:07:04 +01:00
2014-09-27 22:36:27 +02:00
this.status = 0xFF;
this.push_irq();
return;
}
2014-01-02 05:07:04 +01:00
2016-11-19 22:58:42 +01:00
this.status = 0x58;
this.device.dma_status |= 1;
2016-11-19 22:58:42 +01:00
};
IDEInterface.prototype.do_ata_write_sectors_dma = function()
{
2017-06-11 18:08:58 +02:00
var cmd = this.current_command;
2016-11-19 22:58:42 +01:00
2017-06-11 18:08:58 +02:00
var is_lba48 = cmd === 0x35;
var count = this.get_count(is_lba48);
var lba = this.get_lba(is_lba48);
2016-11-19 22:58:42 +01:00
var byte_count = count * this.sector_size;
var start = lba * this.sector_size;
var prdt_start = this.device.prdt_addr;
var offset = 0;
2014-01-02 05:07:04 +01:00
dbg_log("prdt addr: " + h(prdt_start, 8), LOG_DISK);
2014-01-02 05:07:04 +01:00
const buffer = new Uint8Array(byte_count);
2014-09-27 22:36:27 +02:00
do {
2016-11-19 22:58:42 +01:00
var prd_addr = this.cpu.read32s(prdt_start);
var prd_count = this.cpu.read16(prdt_start + 4);
var end = this.cpu.read8(prdt_start + 7) & 0x80;
2014-01-02 05:07:04 +01:00
2014-09-27 22:36:27 +02:00
if(!prd_count)
{
prd_count = 0x10000;
dbg_log("dma: prd count was 0", LOG_DISK);
}
2014-01-02 05:07:04 +01:00
dbg_log("dma write transfer dest=" + h(prd_addr) + " prd_count=" + h(prd_count), LOG_DISK);
2013-12-29 00:34:10 +01:00
2016-08-02 04:15:24 +02:00
var slice = this.cpu.mem8.subarray(prd_addr, prd_addr + prd_count);
dbg_assert(slice.length === prd_count);
buffer.set(slice, offset);
2016-07-28 00:15:23 +02:00
//if(DEBUG)
//{
// dbg_log(hex_dump(slice), LOG_DISK);
//}
2014-01-05 03:19:09 +01:00
2014-09-27 22:36:27 +02:00
offset += prd_count;
prdt_start += 8;
2014-07-13 00:41:57 +02:00
}
2014-09-27 22:36:27 +02:00
while(!end);
2014-07-13 00:41:57 +02:00
dbg_assert(offset === buffer.length);
this.buffer.set(start, buffer, () =>
2014-07-13 00:41:57 +02:00
{
2014-09-27 22:36:27 +02:00
dbg_log("dma write completed", LOG_DISK);
2016-11-19 22:58:42 +01:00
this.ata_advance(this.current_command, count);
2014-09-27 22:36:27 +02:00
this.status = 0x50;
this.push_irq();
this.device.dma_status &= ~1;
2016-11-19 22:58:42 +01:00
this.current_command = -1;
});
2013-12-29 00:34:10 +01:00
2015-01-13 20:33:07 +01:00
this.report_write(byte_count);
2014-09-27 22:36:27 +02:00
};
2013-12-29 00:34:10 +01:00
IDEInterface.prototype.get_chs = function()
2014-09-27 22:36:27 +02:00
{
2016-11-19 22:58:42 +01:00
var c = this.cylinder_low & 0xFF | this.cylinder_high << 8 & 0xFF00;
var h = this.head;
var s = this.sector & 0xFF;
dbg_log("get_chs: c=" + c + " h=" + h + " s=" + s, LOG_DISK);
2013-12-29 00:34:10 +01:00
2014-09-27 22:36:27 +02:00
return (c * this.head_count + h) * this.sectors_per_track + s - 1;
};
2013-12-29 00:34:10 +01:00
IDEInterface.prototype.get_lba28 = function()
2014-09-27 22:36:27 +02:00
{
return this.sector & 0xFF |
this.cylinder_low << 8 & 0xFF00 |
this.cylinder_high << 16 & 0xFF0000 |
(this.head & 0xF) << 24;
2014-09-27 22:36:27 +02:00
};
2013-12-29 00:34:10 +01:00
IDEInterface.prototype.get_lba48 = function()
2014-09-27 22:36:27 +02:00
{
// Note: Bits over 32 missing
2016-11-19 22:58:42 +01:00
return (this.sector & 0xFF |
this.cylinder_low << 8 & 0xFF00 |
this.cylinder_high << 16 & 0xFF0000 |
2014-09-27 22:36:27 +02:00
(this.sector >> 8) << 24 & 0xFF000000) >>> 0;
};
2013-12-29 00:34:10 +01:00
2017-06-11 18:08:58 +02:00
IDEInterface.prototype.get_lba = function(is_lba48)
{
if(is_lba48)
{
return this.get_lba48();
}
else if(this.is_lba)
{
return this.get_lba28();
}
else
{
return this.get_chs();
}
};
IDEInterface.prototype.get_count = function(is_lba48)
{
if(is_lba48)
{
var count = this.bytecount;
if(count === 0) count = 0x10000;
return count;
}
else
{
var count = this.bytecount & 0xFF;
if(count === 0) count = 0x100;
return count;
}
};
IDEInterface.prototype.create_identify_packet = function()
2014-09-27 22:36:27 +02:00
{
// http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/harddrv.cc#L2821
2013-12-29 00:34:10 +01:00
2014-09-27 22:36:27 +02:00
if(this.drive_head & 0x10)
2013-12-29 00:34:10 +01:00
{
2014-09-27 22:36:27 +02:00
// slave
2016-11-19 22:58:42 +01:00
this.data_allocate(0);
2014-09-27 22:36:27 +02:00
return;
2013-12-29 00:34:10 +01:00
}
for(var i = 0; i < 512; i++)
{
2016-11-19 22:58:42 +01:00
this.data[i] = 0;
}
2016-11-19 22:58:42 +01:00
var cylinder_count = Math.min(16383, this.cylinder_count);
this.data_set([
0x40, this.is_atapi ? 0x85 : 0,
2014-09-27 22:36:27 +02:00
// 1 cylinders
2016-11-19 22:58:42 +01:00
cylinder_count, cylinder_count >> 8,
0, 0,
2014-09-27 22:36:27 +02:00
// 3 heads
this.head_count, this.head_count >> 8,
this.sectors_per_track / 512, this.sectors_per_track / 512 >> 8,
2014-09-27 22:36:27 +02:00
// 5
0, 512 >> 8,
2014-09-27 22:36:27 +02:00
// sectors per track
this.sectors_per_track, this.sectors_per_track >> 8,
2014-09-27 22:36:27 +02:00
0, 0, 0, 0, 0, 0,
// 10-19 serial number
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 15
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 20
3, 0,
0, 2,
4, 0,
2014-09-27 22:36:27 +02:00
// 23-26 firmware revision
0, 0, 0, 0, 0, 0, 0, 0,
2014-09-27 22:36:27 +02:00
// 27 model number
56, 118, 32, 54, 68, 72, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2014-09-27 22:36:27 +02:00
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
2016-11-19 22:58:42 +01:00
// 47 max value for set multiple mode
0x80, 0,
1, 0,
2016-11-19 22:58:42 +01:00
//0, 3, // capabilities, 2: Only LBA / 3: LBA and DMA
0, 2, // capabilities, 2: Only LBA / 3: LBA and DMA
2014-09-27 22:36:27 +02:00
// 50
0, 0,
0, 2,
0, 2,
7, 0,
2014-09-27 22:36:27 +02:00
// 54 cylinders
2016-11-19 22:58:42 +01:00
cylinder_count, cylinder_count >> 8,
2014-09-27 22:36:27 +02:00
// 55 heads
this.head_count, this.head_count >> 8,
2014-09-27 22:36:27 +02:00
// 56 sectors per track
this.sectors_per_track, 0,
2014-09-27 22:36:27 +02:00
// capacity in sectors
this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF,
this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF,
2014-09-27 22:36:27 +02:00
0, 0,
// 60
this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF,
this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF,
0, 0,
2016-11-19 22:58:42 +01:00
// 63, dma supported mode, dma selected mode
this.current_command === 0xA0 ? 0 : 7, this.current_command === 0xA0 ? 0 : 4,
2014-09-27 22:36:27 +02:00
//0, 0, // no DMA
0, 0,
// 65
30, 0, 30, 0, 30, 0, 30, 0, 0, 0,
// 70
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 75
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 80
0x7E, 0, 0, 0, 0, 0, 0, 0x74, 0, 0x40,
// 85
0, 0x40, 0, 0x74, 0, 0x40, 0, 0, 0, 0,
// 90
0, 0, 0, 0, 0, 0, 1, 0x60, 0, 0,
// 95
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// 100
this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF,
this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF,
2014-09-27 22:36:27 +02:00
]);
2016-11-19 22:58:42 +01:00
this.data_length = 512;
this.data_end = 512;
2014-09-27 22:36:27 +02:00
};
2013-12-29 00:34:10 +01:00
2016-11-19 22:58:42 +01:00
IDEInterface.prototype.data_allocate = function(len)
2014-09-27 22:36:27 +02:00
{
2016-11-19 22:58:42 +01:00
this.data_allocate_noclear(len);
2016-11-19 22:58:42 +01:00
for(var i = 0; i < (len + 3 >> 2); i++)
{
2016-11-19 22:58:42 +01:00
this.data32[i] = 0;
}
2014-09-27 22:36:27 +02:00
};
2013-12-29 00:34:10 +01:00
2016-11-19 22:58:42 +01:00
IDEInterface.prototype.data_allocate_noclear = function(len)
2014-09-27 22:36:27 +02:00
{
2016-11-19 22:58:42 +01:00
if(this.data.length < len)
2014-09-27 22:36:27 +02:00
{
2016-11-19 22:58:42 +01:00
this.data = new Uint8Array(len + 3 & ~3);
this.data16 = new Uint16Array(this.data.buffer);
this.data32 = new Int32Array(this.data.buffer);
2013-12-29 00:34:10 +01:00
}
2016-11-19 22:58:42 +01:00
this.data_length = len;
this.data_pointer = 0;
2014-09-27 22:36:27 +02:00
};
2016-11-19 22:58:42 +01:00
IDEInterface.prototype.data_set = function(data)
2014-12-14 16:46:03 +01:00
{
2016-11-19 22:58:42 +01:00
this.data_allocate_noclear(data.length);
this.data.set(data);
2014-12-14 16:46:03 +01:00
};
2015-01-13 20:33:07 +01:00
IDEInterface.prototype.report_read_start = function()
2015-01-13 20:37:01 +01:00
{
this.stats.loading = true;
this.bus.send("ide-read-start");
2015-01-13 20:37:01 +01:00
};
IDEInterface.prototype.report_read_end = function(byte_count)
2015-01-13 20:33:07 +01:00
{
2015-01-13 20:37:01 +01:00
this.stats.loading = false;
var sector_count = byte_count / this.sector_size | 0;
this.stats.sectors_read += sector_count;
2015-01-13 20:33:07 +01:00
this.stats.bytes_read += byte_count;
this.bus.send("ide-read-end", [this.nr, byte_count, sector_count]);
2015-01-13 20:33:07 +01:00
};
IDEInterface.prototype.report_write = function(byte_count)
2015-01-13 20:33:07 +01:00
{
var sector_count = byte_count / this.sector_size | 0;
this.stats.sectors_written += sector_count;
2015-01-13 20:33:07 +01:00
this.stats.bytes_written += byte_count;
this.bus.send("ide-write-end", [this.nr, byte_count, sector_count]);
2015-01-13 20:33:07 +01:00
};
2015-01-13 20:37:01 +01:00
IDEInterface.prototype.read_buffer = function(start, length, callback)
{
const id = this.last_io_id++;
this.in_progress_io_ids.add(id);
this.buffer.get(start, length, data =>
{
if(this.cancelled_io_ids.delete(id))
{
dbg_assert(!this.in_progress_io_ids.has(id));
return;
}
const removed = this.in_progress_io_ids.delete(id);
dbg_assert(removed);
callback(data);
});
};
IDEInterface.prototype.cancel_io_operations = function()
{
for(const id of this.in_progress_io_ids)
{
this.cancelled_io_ids.add(id);
}
this.in_progress_io_ids.clear();
};
IDEInterface.prototype.get_state = function()
{
var state = [];
state[0] = this.bytecount;
state[1] = this.cylinder_count;
state[2] = this.cylinder_high;
state[3] = this.cylinder_low;
state[4] = this.data_pointer;
2016-11-19 22:58:42 +01:00
state[5] = 0;
state[6] = 0;
state[7] = 0;
state[8] = 0;
state[9] = this.drive_head;
state[10] = this.error;
state[11] = this.head;
state[12] = this.head_count;
state[13] = this.is_atapi;
state[14] = this.is_lba;
state[15] = this.lba_count;
2016-11-19 22:58:42 +01:00
state[16] = this.data;
state[17] = this.data_length;
state[18] = this.sector;
state[19] = this.sector_count;
state[20] = this.sector_size;
state[21] = this.sectors_per_drq;
state[22] = this.sectors_per_track;
state[23] = this.status;
state[24] = this.write_dest;
2016-11-19 22:58:42 +01:00
state[25] = this.current_command;
state[26] = this.data_end;
state[27] = this.current_atapi_command;
2018-04-09 22:17:01 +02:00
state[28] = this.buffer;
return state;
};
IDEInterface.prototype.set_state = function(state)
{
this.bytecount = state[0];
this.cylinder_count = state[1];
this.cylinder_high = state[2];
this.cylinder_low = state[3];
this.data_pointer = state[4];
2016-11-19 22:58:42 +01:00
this.drive_head = state[9];
this.error = state[10];
this.head = state[11];
this.head_count = state[12];
this.is_atapi = state[13];
this.is_lba = state[14];
this.lba_count = state[15];
2016-11-19 22:58:42 +01:00
this.data = state[16];
this.data_length = state[17];
this.sector = state[18];
this.sector_count = state[19];
this.sector_size = state[20];
this.sectors_per_drq = state[21];
this.sectors_per_track = state[22];
this.status = state[23];
this.write_dest = state[24];
2016-11-19 22:58:42 +01:00
this.current_command = state[25];
this.data_end = state[26];
this.current_atapi_command = state[27];
2016-11-19 22:58:42 +01:00
this.data16 = new Uint16Array(this.data.buffer);
this.data32 = new Int32Array(this.data.buffer);
2018-04-09 22:17:01 +02:00
this.buffer && this.buffer.set_state(state[28]);
};