From 415b9fc71cd773e5e2cc51a335a97a1fe8c26466 Mon Sep 17 00:00:00 2001 From: copy Date: Mon, 15 Feb 2016 22:23:26 +0100 Subject: [PATCH] Split ide into master and slave, fix some issues. Fixes #14. Fixes #19 --- src/ide.js | 1336 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 801 insertions(+), 535 deletions(-) diff --git a/src/ide.js b/src/ide.js index 5b59c4d8d6..1b48f6c4cf 100644 --- a/src/ide.js +++ b/src/ide.js @@ -24,8 +24,12 @@ var /** @const */ * */ function IDEDevice(cpu, buffer, is_cd, nr, bus) { - /** @const @type {BusConnector} */ - this.bus = bus; + this.master = new IDEInterface(this, cpu, buffer, is_cd, nr, 0, bus); + this.slave = new IDEInterface(this, cpu, undefined, false, nr, 1, bus); + + this.current_interface = this.master; + + this.cpu = cpu; // gets set via PCI in seabios, likely doesn't matter if(nr === 0) @@ -35,23 +39,362 @@ function IDEDevice(cpu, buffer, is_cd, nr, bus) this.pci_id = 0x1E << 3; } - else + else if(nr === 1) { this.ata_port = 0x170; this.irq = 15; this.pci_id = 0x1F << 3; } - - /** @const */ - this.nr = nr; + else + { + dbg_assert(false, "IDE device with nr " + nr + " ignored", LOG_DISK); + } // alternate status, starting at 3f4/374 /** @type {number} */ this.ata_port_high = this.ata_port | 0x204; /** @type {number} */ - this.master_port = 0xC000; + this.master_port = 0xB400; + + this.pci_space = [ + 0x86, 0x80, 0x20, 0x3a, 0x05, 0x00, 0xa0, 0x02, 0x00, 0x8f, 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, + 0x43, 0x10, 0xd4, 0x82, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, this.irq, 0x01, 0x00, 0x00, + + // 0x40 + 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 + 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, + }, + false, + false, + { + size: 0x10, + }, + ]; + this.name = "ide" + nr; + + cpu.devices.pci.register_device(this); + + /** @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(); + }, function() + { + return this.current_interface.read_data() | + this.current_interface.read_data() << 8; + }, function() + { + return this.current_interface.read_data() | + this.current_interface.read_data() << 8 | + this.current_interface.read_data() << 16 | + this.current_interface.read_data() << 24; + }); + + cpu.io.register_read(this.ata_port | 1, this, function() + { + dbg_log("Read error: " + h(this.error & 0xFF), LOG_DISK); + return this.current_interface.error; + }); + 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); + return this.current_interface.drive_head; + }); + + 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; + + cpu.io.register_write(this.ata_port | 7, this, function(data) + { + 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); + + 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; + }); +} + +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 " + + ((data & 2) ? "disabled" : "enabled"), LOG_DISK) + + this.device_control = data; + + if(data & 4) + { + dbg_log("Reset via control port", LOG_DISK); + + this.master.device_reset(); + this.slave.device_reset(); + } +}; + +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; +}; + +IDEDevice.prototype.dma_read_command = function() +{ + dbg_log("DMA read command", LOG_DISK); + return 1 | this.dma_read_status() << 16; +}; + +IDEDevice.prototype.dma_read_command8 = function() +{ + dbg_log("DMA read command8", LOG_DISK); + return 1; +}; + +IDEDevice.prototype.dma_write_command = function(value) +{ + dbg_log("DMA write command: " + h(value), LOG_DISK); + + if(value & 1) + { + this.push_irq(); + } + + this.dma_write_status(value >> 16 & 0xFF); +}; + +IDEDevice.prototype.dma_write_command8 = function(value) +{ + dbg_log("DMA write command8: " + h(value), LOG_DISK); + + if(value & 1) + { + this.push_irq(); + } +}; + +IDEDevice.prototype.push_irq = function() +{ + if((this.device_control & 2) === 0) + { + dbg_log("push irq", LOG_DISK); + 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; + return state; +}; + +IDEDevice.prototype.set_state = function(state) +{ + this.master = state[0]; + this.slave = 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; +}; + + +/** + * @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; /** @const @type {CPU} */ this.cpu = cpu; @@ -102,8 +445,6 @@ function IDEDevice(cpu, buffer, is_cd, nr, bus) this.sectors_per_track = 63; } - // disk translation translation -> lba - cpu.devices.rtc.cmos_write(CMOS_BIOS_DISKTRANSFLAG, 1); this.cylinder_count = this.sector_count / this.head_count / this.sectors_per_track; @@ -111,14 +452,35 @@ function IDEDevice(cpu, buffer, is_cd, nr, bus) { 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.sector_count = this.cylinder_count * this.head_count * // this.sectors_per_track * this.sector_size; } - if(this.cylinder_count > 16383) - { - this.cylinder_count = 16383; - } + //if(this.cylinder_count > 16383) + //{ + // this.cylinder_count = 16383; + //} + + + // disk translation translation: lba + var rtc = cpu.devices.rtc; + + // master + 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); + + //rtc.cmos_write(CMOS_BIOS_DISKTRANSFLAG, rtc.cmos_read(CMOS_BIOS_DISKTRANSFLAG) | 1 << (nr * 4 + 2)); // slave } /** @const */ @@ -130,237 +492,68 @@ function IDEDevice(cpu, buffer, is_cd, nr, bus) loading: false, }; - this.pci_space = [ - 0x86, 0x80, 0x20, 0x3a, 0x05, 0x00, 0xa0, 0x02, 0x00, 0x8f, 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, - 0x00, 0x00, 0x00, 0x00, - this.master_port & 0xFF | 1, this.master_port >> 8, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x43, 0x10, 0xd4, 0x82, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, this.irq, 0x01, 0x00, 0x00, - - // 0x40 - 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 - 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, - }, - false, - false, - { - size: 0x10, - }, - ]; - this.name = "ide" + nr; - - // 00:1f.2 IDE interface: Intel Corporation 82801JI (ICH10 Family) 4 port SATA IDE Controller #1 - //0x86, 0x80, 0x20, 0x3a, 0x05, 0x00, 0xb0, 0x02, 0x00, 0x8f, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - //0x01, 0x90, 0x00, 0x00, 0x01, 0x8c, 0x00, 0x00, 0x81, 0x88, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, - //0x81, 0x84, 0x00, 0x00, 0x01, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x10, 0xd4, 0x82, - //0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, - cpu.devices.pci.register_device(this); - // status - cpu.io.register_read(this.ata_port | 7, this, 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); + this.buffer = buffer; - /** @type {number} */ - this.device_control = 2; - /** @type {number} */ - this.last_drive = 0xFF; - /** @type {number} */ - this.data_pointer = 0; - this.pio_data = new Uint8Array(0); /** @type {number} */ this.is_lba = 0; + /** @type {number} */ this.bytecount = 0; + /** @type {number} */ this.sector = 0; + /** @type {number} */ this.lba_count = 0; + /** @type {number} */ this.cylinder_low = 0; + /** @type {number} */ this.cylinder_high = 0; + /** @type {number} */ this.head = 0; - /** @type {number} */ - this.drive_head = 0; - /** @type {number} */ - this.status = 0x50; - /** @type {number} */ - this.sectors_per_drq = 1; /** @type {number} */ - this.write_dest = 0; + this.drive_head = 0; /** @type {number} */ - this.data_port_count = 0; - /** @type {number} */ - this.data_port_current = 0; - this.data_port_buffer = new Uint8Array(0); - - this.data_port_callback = IDE_CALLBACK_NONE; + this.status = 0x50; /** @type {number} */ - this.next_status = -1; + this.sectors_per_drq = 1; /** @type {number} */ - this.prdt_addr = 0; - /** @type {number} */ - this.dma_status = 0; - - cpu.io.register_read(this.ata_port | 0, this, this.read_data_port8, this.read_data_port16, this.read_data_port32); - cpu.io.register_read(this.ata_port | 1, this, this.read_lba_port); - cpu.io.register_read(this.ata_port | 2, this, this.read_bytecount_port); - cpu.io.register_read(this.ata_port | 3, this, this.read_sector_port); - - cpu.io.register_read(this.ata_port | 4, this, function() - { - dbg_log("Read 1F4: " + h(this.cylinder_low & 0xFF), LOG_DISK); - return this.cylinder_low & 0xFF; - }); - cpu.io.register_read(this.ata_port | 5, this, function() - { - dbg_log("Read 1F5: " + h(this.cylinder_high & 0xFF), LOG_DISK); - return this.cylinder_high & 0xFF; - }); - cpu.io.register_read(this.ata_port | 6, this, function() - { - dbg_log("Read 1F6", LOG_DISK); - return this.drive_head; - }); - - - cpu.io.register_write(this.ata_port | 0, this, this.write_data_port8, this.write_data_port16, this.write_data_port32); - cpu.io.register_write(this.ata_port | 1, this, this.write_lba_port); - cpu.io.register_write(this.ata_port | 2, this, this.write_bytecount_port); - cpu.io.register_write(this.ata_port | 3, this, this.write_sector_port); - - cpu.io.register_write(this.ata_port | 4, this, function(data) - { - dbg_log("1F4/sector low: " + h(data), LOG_DISK); - this.cylinder_low = (this.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.cylinder_high = (this.cylinder_high << 8 | data) & 0xFFFF; - }); - cpu.io.register_write(this.ata_port | 6, this, function(data) - { - var slave = data & 0x10, - mode = data & 0xE0, - low = data & 0xF; - - dbg_log("1F6/drive: " + h(data, 2), LOG_DISK); + this.error = 0; - if(slave) - { - dbg_log("Slave", LOG_DISK); - return; - } - - if((mode & 0x40) === 0) - { - // chs mode - //dbg_log("CHS mode: Unimplemented", LOG_DISK); - //return; - } - - this.drive_head = data; - this.is_lba = data >> 6 & 1; - this.head = data & 0xF; - this.last_drive = data; - }); - - cpu.io.register_write(this.ata_port | 7, this, this.ata_command); - - 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); + /** @type {number} */ + this.data_pointer = 0; - 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); + this.pio_data = new Uint8Array(64 * 1024); - 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); -} + /** @type {number} */ + this.write_dest = 0; -IDEDevice.prototype.get_state = function() -{ - var state = []; + /** @type {number} */ + this.data_port_count = 0; - state[0] = this.device_control; - state[1] = this.last_drive; - state[2] = this.data_pointer; - state[3] = this.pio_data; - state[4] = this.is_lba; - state[5] = this.bytecount; - state[6] = this.sector; - state[7] = this.lba_count; - state[8] = this.cylinder_low; - state[9] = this.head; - state[10] = this.drive_head; - state[11] = this.status; - state[12] = this.sectors_per_drq; - state[13] = this.write_dest; - state[14] = this.data_port_count; - state[15] = this.data_port_current; - state[16] = this.data_port_buffer; - state[17] = this.next_status; - state[18] = this.prdt_addr; - state[19] = this.dma_status; + /** @type {number} */ + this.data_port_current = 0; - return state; -}; + this.data_port_buffer = new Uint8Array(4096); -IDEDevice.prototype.set_state = function(state) -{ - this.device_control = state[0]; - this.last_drive = state[1]; - this.data_pointer = state[2]; - this.pio_data = state[3]; - this.is_lba = state[4]; - this.bytecount = state[5]; - this.sector = state[6]; - this.lba_count = state[7]; - this.cylinder_low = state[8]; - this.head = state[9]; - this.drive_head = state[10]; - this.status = state[11]; - this.sectors_per_drq = state[12]; - this.write_dest = state[13]; - this.data_port_count = state[14]; - this.data_port_current = state[15]; - this.data_port_buffer = state[16]; - this.next_status = state[17]; - this.prdt_addr = state[18]; - this.dma_status = state[19]; -}; + this.data_port_callback = IDE_CALLBACK_NONE; +} -IDEDevice.prototype.device_reset = function() +IDEInterface.prototype.device_reset = function() { if(this.is_atapi) { - this.status = 0x50 | 1; + this.status = 0; this.bytecount = 1; - this.lba_count = 1; + this.error = 1; this.sector = 1; // lba_low this.cylinder_low = 0x14; // lba_mid this.cylinder_high = 0xeb; // lba_high @@ -369,14 +562,16 @@ IDEDevice.prototype.device_reset = function() { this.status = 0x50 | 1; this.bytecount = 1; - this.lba_count = 1; + this.error = 1; this.sector = 1; // lba_low - this.cylinder_low = 0x3c; // lba_mid - this.cylinder_high = 0xc3; // lba_high + + // 0, 0 needed by bochs bios + this.cylinder_low = 0; // lba_mid + this.cylinder_high = 0; // lba_high } }; -IDEDevice.prototype.do_callback = function() +IDEInterface.prototype.do_callback = function() { switch(this.data_port_callback) { @@ -396,20 +591,21 @@ IDEDevice.prototype.do_callback = function() } }; +IDEInterface.prototype.push_irq = function() +{ + this.device.push_irq(); +}; -IDEDevice.prototype.push_irq = function() +IDEInterface.prototype.ata_command = function(cmd) { - if((this.device_control & 2) === 0) - { - dbg_log("push irq", LOG_DISK); + dbg_log("ATA Command: " + h(cmd) + " slave=" + (this.drive_head >> 4 & 1), LOG_DISK); - this.cpu.device_raise_irq(this.irq); + if(!this.buffer) + { + return; } -}; -IDEDevice.prototype.ata_command = function(cmd) -{ - dbg_log("ATA Command: " + h(cmd), LOG_DISK); + this.error = 0; switch(cmd) { @@ -422,7 +618,7 @@ IDEDevice.prototype.ata_command = function(cmd) case 0x08: dbg_log("ATA device reset", LOG_DISK); this.data_pointer = 0; - this.pio_data = new Uint8Array(0); + this.pio_data_allocate(0); this.device_reset(); this.push_irq(); @@ -434,12 +630,13 @@ IDEDevice.prototype.ata_command = function(cmd) this.push_irq(); break; + case 0xF8: // XXX case 0x27: // read native max address ext - read the actual size of the HD // https://en.wikipedia.org/wiki/Host_protected_area dbg_log("ATA cmd 27", LOG_DISK); this.push_irq(); - this.pio_data = new Uint8Array([ + this.pio_data_set(new Uint8Array([ 0, 0, // error 0, 0, // count @@ -449,19 +646,18 @@ IDEDevice.prototype.ata_command = function(cmd) this.buffer.byteLength >> 16 & 0xff, this.buffer.byteLength >> 24 & 0xff, 0, 0, - - 0, 0, - ]); + 0, 0, + ])); this.status = 0x58; break; case 0x20: - case 0x29: case 0x24: + case 0x29: case 0xC4: // 0x20 read sectors // 0x24 read sectors ext - // 0xC4 read multiple + // 0xC4 read multiple // 0x29 read multiple ext this.ata_read_sectors(cmd); break; @@ -469,8 +665,10 @@ IDEDevice.prototype.ata_command = function(cmd) case 0x30: case 0x34: case 0x39: + case 0xC5: // 0x30 write sectors // 0x34 write sectors ext + // 0xC5 write multiple // 0x39 write multiple ext this.ata_write(cmd); break; @@ -479,7 +677,7 @@ IDEDevice.prototype.ata_command = function(cmd) // EXECUTE DEVICE DIAGNOSTIC dbg_log("ATA cmd 90", LOG_DISK); this.push_irq(); - this.lba_count = 0x101; + this.error = 0x101; this.status = 0x50; break; @@ -490,9 +688,9 @@ IDEDevice.prototype.ata_command = function(cmd) break; case 0xA0: + // ATA_CMD_PACKET if(this.is_atapi) { - // ATA_CMD_PACKET this.status = 0x58; this.allocate_in_buffer(12); this.data_port_callback = IDE_CALLBACK_ATAPI; @@ -510,6 +708,9 @@ IDEDevice.prototype.ata_command = function(cmd) this.create_identify_packet(); this.status = 0x58; + this.cylinder_low = 0x14; + this.cylinder_high = 0xeb; + this.push_irq(); } else @@ -535,11 +736,24 @@ IDEDevice.prototype.ata_command = function(cmd) this.ata_read_sectors_dma(cmd); break; + case 0x40: + // read verify sectors + this.push_irq(); + this.status = 0x50; + this.push_irq(); + break; + case 0xCA: // write dma this.ata_write_dma(cmd); break; + case 0xDA: + // get media status + this.status = 0x41; + this.error = 4; + break; + case 0xE1: dbg_log("ATA idle immediate", LOG_DISK); this.push_irq(); @@ -547,10 +761,12 @@ IDEDevice.prototype.ata_command = function(cmd) case 0xEC: dbg_log("ATA identify device", LOG_DISK); - // identify device if(this.is_atapi) { + this.status = 0x41; + this.error = 4; + this.push_irq(); return; } @@ -569,59 +785,64 @@ IDEDevice.prototype.ata_command = function(cmd) case 0xEF: // SET FEATURES dbg_log("ATA cmd EF", LOG_DISK); + this.push_irq(); + break; + case 0xF5: + // SECURITY FREEZE LOCK + this.status = 0x50; this.push_irq(); break; + case 0x35: // write dma ext + case 0x25: // read dma ext default: - dbg_log("New ATA cmd on 1F7: " + h(cmd), LOG_DISK); + dbg_assert(false, "New ATA cmd on 1F7: " + h(cmd), LOG_DISK); + this.status = 0x41; // abort bit set - this.lba_count = 4; + this.error = 4; } }; -IDEDevice.prototype.atapi_handle = function() +IDEInterface.prototype.atapi_handle = function() { - dbg_log("ATAPI Command: " + h(this.data_port_buffer[0]), LOG_DISK); + dbg_log("ATAPI Command: " + h(this.data_port_buffer[0]) + " slave=" + (this.drive_head >> 4 & 1), LOG_DISK); + + this.data_pointer = 0; - this.bytecount = 2; - switch(this.data_port_buffer[0]) { case 0x00: + dbg_log("TEST_UNIT_READY", LOG_DISK); // test unit ready - this.status = 0x40; - this.cylinder_low = 8; - this.cylinder_high = 0; - this.push_irq(); + this.pio_data_allocate(0); + this.status = 0x50; break; case 0x03: // request sense - this.pio_data = new Uint8Array(Math.min(this.data_port_buffer[4], 15)); + this.pio_data_allocate(this.data_port_buffer[4]); this.status = 0x58; this.pio_data[0] = 0x80 | 0x70; this.pio_data[7] = 8; - - this.data_pointer = 0; - this.bytecount = 2; - this.cylinder_low = 8; - this.cylinder_high = 0; - - this.push_irq(); break; case 0x12: // inquiry - this.pio_data = new Uint8Array(Math.min(this.data_port_buffer[4], 36)); + var length = this.data_port_buffer[4] | this.data_port_buffer[3] << 8; + this.pio_data_allocate(length); this.status = 0x58; + dbg_log("inquiry: " + h(this.data_port_buffer[1], 2) + " length=" + length, LOG_DISK); + // http://www.t10.org/ftp/x3t9.2/document.87/87-106r0.txt this.pio_data.set([ 0x05, 0x80, 0x01, 0x31, - 0, 0, 0, 0, + // additional length + 31, + 0, 0, 0, // 8 0x53, 0x4F, 0x4E, 0x59, @@ -636,42 +857,28 @@ IDEDevice.prototype.atapi_handle = function() // 32 0x31, 0x2E, 0x31, 0x61, ]); - - this.data_pointer = 0; - this.bytecount = 2; - this.push_irq(); break; case 0x1E: // prevent/allow medium removal - this.pio_data = new Uint8Array(0); + this.pio_data_allocate(0); this.status = 0x50; - this.data_pointer = 0; - this.bytecount = 2; - this.push_irq(); break; case 0x25: // read capacity - this.pio_data = new Uint8Array([ - this.sector_count >> 24 & 0xff, - this.sector_count >> 16 & 0xff, - this.sector_count >> 8 & 0xff, - this.sector_count & 0xff, + var count = this.sector_count - 1; + this.pio_data_set(new Uint8Array([ + count >> 24 & 0xff, + count >> 16 & 0xff, + count >> 8 & 0xff, + count & 0xff, 0, 0, this.sector_size >> 8 & 0xff, this.sector_size & 0xff, - ]); + ])); this.status = 0x58; - - this.data_pointer = 0; - - this.bytecount = 2; - this.cylinder_low = 8; - this.cylinder_high = 0; - - this.push_irq(); break; case 0x28: @@ -686,102 +893,140 @@ IDEDevice.prototype.atapi_handle = function() } break; + case 0x42: + var length = this.data_port_buffer[8]; + this.pio_data_allocate(Math.min(8, length)); + dbg_log("read q subcode: length=" + length, LOG_DISK); + this.status = 0x58; + break; + case 0x43: - // read header - this.pio_data = new Uint8Array(2048); - this.pio_data[0] = 0; - this.pio_data[1] = 10; - this.pio_data[2] = 1; - this.pio_data[3] = 1; + // read toc + var format = this.data_port_buffer[9] >> 6; + var length = this.data_port_buffer[8] | this.data_port_buffer[7] << 8; + this.pio_data_allocate(length); + dbg_log("read toc: " + h(format, 2) + " length=" + length, LOG_DISK); + + if(format === 1) + { + this.pio_data.set(new Uint8Array([ + 0, 10, // length + 1, 1, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + ])); + } + this.status = 0x58; - this.data_pointer = 0; - this.bytecount = 2; - this.cylinder_high = 8; - this.cylinder_low = 0; - this.push_irq(); break; case 0x46: // get configuration - this.pio_data = new Uint8Array(this.data_port_buffer[8] | this.data_port_buffer[7] << 8); + this.pio_data_allocate(this.data_port_buffer[8] | this.data_port_buffer[7] << 8); this.status = 0x58; - this.data_pointer = 0; - this.bytecount = 2; - this.push_irq(); break; case 0x4A: // get event status notification - this.pio_data = new Uint8Array(this.data_port_buffer[8] | this.data_port_buffer[7] << 8); + this.pio_data_allocate(this.data_port_buffer[8] | this.data_port_buffer[7] << 8); this.status = 0x58; - this.data_pointer = 0; - this.bytecount = 2; - this.push_irq(); break; case 0x51: // read disk information - this.pio_data = new Uint8Array(0); + this.pio_data_allocate(0); this.status = 0x50; - this.data_pointer = 0; - this.bytecount = 2; - this.push_irq(); + break; + + case 0x1A: + // mode sense (6) + this.pio_data_allocate(this.data_port_buffer[4]); + this.status = 0x58; break; case 0x5A: // mode sense - this.push_irq(); - this.status = 0x50; + var length = this.data_port_buffer[8] | this.data_port_buffer[7] << 8; + var page_code = this.data_port_buffer[2]; + dbg_log("mode sense: " + h(page_code) + " length=" + length, LOG_DISK); + if(page_code === 0x2A) + { + this.pio_data_allocate(Math.min(30, length)); + } + this.status = 0x58; + break; + + case 0xBD: + // mechanism status + this.pio_data_allocate(this.data_port_buffer[9] | this.data_port_buffer[8] << 8); + this.pio_data[5] = 1; + this.status = 0x58; break; default: this.status = 0x50; dbg_log("Unimplemented ATAPI command: " + h(this.data_port_buffer[0]), LOG_DISK); + dbg_assert(false); } -}; -IDEDevice.prototype.do_write = function() -{ - this.status = 0x50; - - var data = this.data_port_buffer.subarray(0, this.data_port_count); + this.bytecount = this.bytecount & ~7 | 2; - this.buffer.set(this.write_dest, data, function() + if((this.status & 0x80) === 0) { + this.update_size(); this.push_irq(); - }.bind(this)); + } - this.report_write(this.data_port_count); + if((this.status & 0x80) === 0 && this.pio_data_length === 0) + { + this.bytecount |= 1; + this.status &= ~8; + } }; -IDEDevice.prototype.read_status = function() +IDEInterface.prototype.update_size = function() { - var ret = this.status; - dbg_log("ATA read status: " + h(this.status), LOG_DISK); + var byte_count = this.pio_data_length; + var max_drq_size = this.cylinder_high << 8 & 0xFF00 | this.cylinder_low & 0xFF; - if(this.next_status >= 0) + if(!max_drq_size) { - this.status = this.next_status; - this.next_status = -1; + max_drq_size = 0x8000; } + //max_drq_size &= 0xFF00; + + var transfered_ata_blocks = Math.min(byte_count, max_drq_size); + transfered_ata_blocks &= ~1; + + this.cylinder_low = transfered_ata_blocks & 0xFF; + this.cylinder_high = transfered_ata_blocks >> 8 & 0xFF; - return ret; + //dbg_log("pio data length: " + h(this.pio_data_length)); + this.cylinder_low = transfered_ata_blocks; + this.cylinder_high = transfered_ata_blocks >> 8; }; -IDEDevice.prototype.write_control = function(data) +IDEInterface.prototype.do_write = function() { - dbg_log("device control: " + h(data), LOG_DISK); - this.device_control = data; + this.status = 0x50; - if(data & 4) + dbg_assert(this.data_port_count <= this.data_port_buffer.length); + var data = this.data_port_buffer.subarray(0, this.data_port_count); + + //dbg_log(hex_dump(data), LOG_DISK); + + this.buffer.set(this.write_dest, data, function() { - dbg_log("Reset via control port", LOG_DISK); + this.push_irq(); + }.bind(this)); - this.device_reset(); - } + this.report_write(this.data_port_count); }; -IDEDevice.prototype.allocate_in_buffer = function(size) + +IDEInterface.prototype.allocate_in_buffer = function(size) { // reuse old buffer if it's smaller or the same size if(size > this.data_port_buffer.length) @@ -793,82 +1038,83 @@ IDEDevice.prototype.allocate_in_buffer = function(size) this.data_port_current = 0; }; -IDEDevice.prototype.atapi_read = function(cmd) +IDEInterface.prototype.atapi_read = function(cmd) { // Note: Big Endian var lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5], - count = cmd[7] << 8 | cmd[8], + count = cmd[7] << 8 | cmd[8], flags = cmd[1], byte_count = count * this.sector_size, //transfered_ata_blocks = Math.min(this.bytecount / 512, this.cylinder_low << 8 | this.cylinder_high), - max_drq_size = (this.cylinder_high & 0xFF) << 8 | this.cylinder_low & 0xFF, - transfered_ata_blocks, start = lba * this.sector_size; - dbg_log("CD read lba=" + h(lba) + + dbg_log("CD read lba=" + h(lba) + " lbacount=" + h(count) + " bytecount=" + h(byte_count) + " flags=" + h(flags), LOG_CD); - if(!max_drq_size) - { - max_drq_size = 0x8000; - } - - transfered_ata_blocks = Math.min(byte_count, max_drq_size); + this.pio_data_allocate_noclear(0); + this.cylinder_low = this.cylinder_high = 0; - this.cylinder_low = transfered_ata_blocks & 0xFF; - this.cylinder_high = transfered_ata_blocks >> 8 & 0xFF; + //this.push_irq(); if(start >= this.buffer.byteLength) { - dbg_log("CD read: Outside of disk end=" + h(start + byte_count) + - " size=" + h(this.buffer.byteLength), LOG_DISK); + dbg_assert(false, "CD read: Outside of disk end=" + h(start + byte_count) + + " size=" + h(this.buffer.byteLength), LOG_DISK); this.status = 0xFF; this.push_irq(); } + else if(byte_count === 0) + { + this.status = 0x50; + + this.data_pointer = 0; + //this.push_irq(); + } else { byte_count = Math.min(byte_count, this.buffer.byteLength - start); - this.status = 0x80; + this.status = 0x50 | 0x80; this.report_read_start(); this.buffer.get(start, byte_count, function(data) { - this.pio_data = data; + dbg_log("cd read: data arrived", LOG_CD) + this.pio_data_set(data); this.status = 0x58; - - //this.cylinder_low = 0; - //this.cylinder_high = 8; + this.bytecount |= 2; this.data_pointer = 0; this.push_irq(); + this.update_size(); this.report_read_end(byte_count); }.bind(this)); } }; -IDEDevice.prototype.atapi_read_dma = function(cmd) +IDEInterface.prototype.atapi_read_dma = function(cmd) { // Note: Big Endian var lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5], - count = cmd[7] << 8 | cmd[8], + count = cmd[7] << 8 | cmd[8], flags = cmd[1], byte_count = count * this.sector_size, start = lba * this.sector_size; - dbg_log("CD read DMA lba=" + h(lba) + + dbg_log("CD read DMA lba=" + h(lba) + " lbacount=" + h(count) + " bytecount=" + h(byte_count) + " flags=" + h(flags), LOG_CD); + this.pio_data_allocate_noclear(byte_count); if(start >= this.buffer.byteLength) { - dbg_log("CD read: Outside of disk end=" + h(start + byte_count) + - " size=" + h(this.buffer.byteLength), LOG_DISK); + dbg_assert(false, "CD read: Outside of disk end=" + h(start + byte_count) + + " size=" + h(this.buffer.byteLength), LOG_DISK); this.status = 0xFF; this.push_irq(); @@ -881,7 +1127,7 @@ IDEDevice.prototype.atapi_read_dma = function(cmd) this.buffer.get(start, byte_count, function(data) { - var prdt_start = this.prdt_addr, + var prdt_start = this.device.prdt_addr, offset = 0; do { @@ -899,63 +1145,35 @@ IDEDevice.prototype.atapi_read_dma = function(cmd) offset += count; prdt_start += 8; + + dbg_assert(offset <= this.buffer.byteLength); } while(!end); this.status = 0x50; - this.dma_status &= ~2 & ~1; - this.dma_status |= 4; + this.device.dma_status &= ~2 & ~1; + this.device.dma_status |= 4; this.push_irq(); - + this.report_read_end(byte_count); }.bind(this)); } }; -IDEDevice.prototype.read_data_port8 = function() -{ - return this.read_data(); -}; - -IDEDevice.prototype.read_data_port16 = function() -{ - return this.read_data() | this.read_data() << 8; -}; - -IDEDevice.prototype.read_data_port32 = function() -{ - return this.read_data() | this.read_data() << 8 | - this.read_data() << 16 | this.read_data() << 24; -}; - -IDEDevice.prototype.read_lba_port = function() -{ - dbg_log("Read lba_count: " + h(this.lba_count & 0xFF), LOG_DISK); - return this.lba_count & 0xFF; -}; - -IDEDevice.prototype.read_bytecount_port = function() -{ - dbg_log("Read bytecount: " + h(this.bytecount & 0xFF), LOG_DISK); - return this.bytecount & 0xFF; -}; - -IDEDevice.prototype.read_sector_port = function() -{ - dbg_log("Read sector: " + h(this.sector & 0xFF), LOG_DISK); - return this.sector & 0xFF; -}; - -IDEDevice.prototype.read_data = function() +IDEInterface.prototype.read_data = function() { - if(this.data_pointer < this.pio_data.length) + //dbg_log("ptr: " + h(this.data_pointer, 2)); + if(this.data_pointer < this.pio_data_length) { - if((this.data_pointer + 1) % (this.sectors_per_drq * 512) === 0 || - this.data_pointer + 1 === this.pio_data.length) + var do_irq = false; + + if((this.data_pointer + 1) % (this.sectors_per_drq * 512) === 0 || + this.data_pointer + 1 === this.pio_data_length) { dbg_log("ATA IRQ", LOG_DISK); - this.push_irq(); + //this.push_irq(); + do_irq = true; } if(this.cylinder_low) @@ -973,7 +1191,7 @@ IDEDevice.prototype.read_data = function() if(!this.cylinder_low && !this.cylinder_high) { - var remaining = this.pio_data.length - this.data_pointer - 1; + var remaining = this.pio_data_length - this.data_pointer - 1; dbg_log("reset to " + h(remaining), LOG_DISK); if(remaining >= 0x10000) @@ -989,17 +1207,24 @@ IDEDevice.prototype.read_data = function() } - if(this.data_pointer + 1 >= this.pio_data.length) + if(this.data_pointer + 1 >= this.pio_data_length) { this.status = 0x50; - //this.bytecount = 3; + this.bytecount = this.bytecount & ~7 | 3; + //this.push_irq(); + do_irq = true; } if((this.data_pointer + 1 & 255) === 0) { - dbg_log("Read 1F0: " + h(this.pio_data[this.data_pointer], 2) + + dbg_log("Read 1F0: " + h(this.pio_data[this.data_pointer], 2) + " cur=" + h(this.data_pointer) + - " cnt=" + h(this.pio_data.length), LOG_DISK); + " cnt=" + h(this.pio_data_length), LOG_DISK); + } + + if(do_irq) + { + this.push_irq(); } return this.pio_data[this.data_pointer++]; @@ -1013,7 +1238,7 @@ IDEDevice.prototype.read_data = function() } }; -IDEDevice.prototype.write_data_port8 = function(data) +IDEInterface.prototype.write_data_port8 = function(data) { if(this.data_port_current >= this.data_port_count) { @@ -1043,13 +1268,13 @@ IDEDevice.prototype.write_data_port8 = function(data) } }; -IDEDevice.prototype.write_data_port16 = function(data) +IDEInterface.prototype.write_data_port16 = function(data) { this.write_data_port8(data & 0xFF); this.write_data_port8(data >> 8 & 0xFF); }; -IDEDevice.prototype.write_data_port32 = function(data) +IDEInterface.prototype.write_data_port32 = function(data) { this.write_data_port8(data & 0xFF); this.write_data_port8(data >> 8 & 0xFF); @@ -1057,25 +1282,7 @@ IDEDevice.prototype.write_data_port32 = function(data) this.write_data_port8(data >> 24 & 0xFF); }; -IDEDevice.prototype.write_lba_port = function(data) -{ - dbg_log("1F1/lba_count: " + h(data), LOG_DISK); - this.lba_count = (this.lba_count << 8 | data) & 0xFFFF; -}; - -IDEDevice.prototype.write_bytecount_port = function(data) -{ - dbg_log("1F2/bytecount: " + h(data), LOG_DISK); - this.bytecount = (this.bytecount << 8 | data) & 0xFFFF; -}; - -IDEDevice.prototype.write_sector_port = function(data) -{ - dbg_log("1F3/sector: " + h(data), LOG_DISK); - this.sector = (this.sector << 8 | data) & 0xFFFF; -}; - -IDEDevice.prototype.ata_read_sectors = function(cmd) +IDEInterface.prototype.ata_read_sectors = function(cmd) { if(cmd === 0x20 || cmd === 0xC4) { @@ -1090,7 +1297,7 @@ IDEDevice.prototype.ata_read_sectors = function(cmd) { // read sectors ext var count = this.bytecount, - lba = this.get_lba48(); + lba = this.get_lba48(); if(count === 0) count = 0x10000; @@ -1108,7 +1315,7 @@ IDEDevice.prototype.ata_read_sectors = function(cmd) dbg_log("ATA read cmd=" + h(cmd) + " mode=" + (this.is_lba ? "lba" : "chs") + - " lba=" + h(lba) + + " lba=" + h(lba) + " lbacount=" + h(count) + " bytecount=" + h(byte_count), LOG_DISK); @@ -1129,7 +1336,7 @@ IDEDevice.prototype.ata_read_sectors = function(cmd) this.buffer.get(start, byte_count, function(data) { - this.pio_data = data; + this.pio_data_set(data); this.status = 0x58; this.data_pointer = 0; @@ -1140,15 +1347,17 @@ IDEDevice.prototype.ata_read_sectors = function(cmd) } }; -IDEDevice.prototype.ata_read_sectors_dma = function(cmd) +IDEInterface.prototype.ata_read_sectors_dma = function(cmd) { var count = this.bytecount & 0xff, lba = this.get_lba28(); + dbg_assert(this.is_lba); + var byte_count = count * this.sector_size, start = lba * this.sector_size; - dbg_log("ATA DMA read lba=" + h(lba) + + dbg_log("ATA DMA read lba=" + h(lba) + " lbacount=" + h(count) + " bytecount=" + h(byte_count), LOG_DISK); @@ -1164,15 +1373,19 @@ IDEDevice.prototype.ata_read_sectors_dma = function(cmd) } //this.status = 0xFF & ~8; - this.status = 0x80; - this.dma_status |= 1; + this.status = 0x80 | 0x58; + this.device.dma_status |= 1; this.report_read_start(); + var orig_prdt_start = this.device.prdt_addr; + this.buffer.get(start, byte_count, function(data) { - var prdt_start = this.prdt_addr, + var prdt_start = this.device.prdt_addr, offset = 0; + dbg_assert(orig_prdt_start === prdt_start); + do { var addr = this.memory.read32s(prdt_start), count = this.memory.read16(prdt_start + 4), @@ -1183,7 +1396,7 @@ IDEDevice.prototype.ata_read_sectors_dma = function(cmd) count = 0x10000; } - dbg_log("dma read dest=" + h(addr) + " count=" + h(count), LOG_DISK); + dbg_log("dma read transfer dest=" + h(addr) + " count=" + h(count), LOG_DISK); this.memory.write_blob(data.subarray(offset, offset + count), addr); offset += count; @@ -1191,9 +1404,11 @@ IDEDevice.prototype.ata_read_sectors_dma = function(cmd) } while(!end); + dbg_assert(offset === byte_count); + this.status = 0x50; - this.dma_status &= ~2 & ~1; - this.dma_status |= 4; + this.device.dma_status &= ~2 & ~1; + this.device.dma_status |= 4; this.push_irq(); @@ -1201,9 +1416,9 @@ IDEDevice.prototype.ata_read_sectors_dma = function(cmd) }.bind(this)); }; -IDEDevice.prototype.ata_write = function(cmd) +IDEInterface.prototype.ata_write = function(cmd) { - if(cmd === 0x30) + if(cmd === 0x30 || cmd === 0xC5) { // write sectors var count = this.bytecount & 0xff, @@ -1215,7 +1430,7 @@ IDEDevice.prototype.ata_write = function(cmd) else if(cmd === 0x34 || cmd === 0x39) { var count = this.bytecount, - lba = this.get_lba48(); + lba = this.get_lba48(); if(count === 0) count = 0x10000; @@ -1230,7 +1445,7 @@ IDEDevice.prototype.ata_write = function(cmd) start = lba * this.sector_size; - dbg_log("ATA write lba=" + h(lba) + + dbg_log("ATA write lba=" + h(lba) + " lbacount=" + h(count) + " bytecount=" + h(byte_count), LOG_DISK); @@ -1238,15 +1453,14 @@ IDEDevice.prototype.ata_write = function(cmd) if(start + byte_count > this.buffer.byteLength) { - dbg_log("ATA write: Outside of disk", LOG_DISK); + dbg_assert(false, "ATA write: Outside of disk", LOG_DISK); this.status = 0xFF; this.push_irq(); } else { - this.status = 0x50; - this.next_status = 0x58; + this.status = 0x58; this.allocate_in_buffer(byte_count); @@ -1258,15 +1472,17 @@ IDEDevice.prototype.ata_write = function(cmd) } }; -IDEDevice.prototype.ata_write_dma = function(cmd) +IDEInterface.prototype.ata_write_dma = function(cmd) { var count = this.bytecount & 0xff, lba = this.get_lba28(); + dbg_assert(this.is_lba); + var byte_count = count * this.sector_size, start = lba * this.sector_size; - dbg_log("ATA DMA write lba=" + h(lba) + + dbg_log("ATA DMA write lba=" + h(lba) + " lbacount=" + h(count) + " bytecount=" + h(byte_count), LOG_DISK); @@ -1274,7 +1490,7 @@ IDEDevice.prototype.ata_write_dma = function(cmd) if(start + byte_count > this.buffer.byteLength) { - dbg_log("ATA DMA write: Outside of disk", LOG_DISK); + dbg_assert(false, "ATA DMA write: Outside of disk", LOG_DISK); this.status = 0xFF; this.push_irq(); @@ -1283,13 +1499,14 @@ IDEDevice.prototype.ata_write_dma = function(cmd) //status = 0xFF & ~8; this.status = 0x80; - this.dma_status |= 1; + this.device.dma_status |= 1; - var prdt_start = this.prdt_addr, + var prdt_start = this.device.prdt_addr, prdt_count = 0, prdt_write_count = 0, offset = 0; + dbg_log("prdt addr: " + h(prdt_start, 8), LOG_DISK); do { var prd_addr = this.memory.read32s(prdt_start), @@ -1299,11 +1516,18 @@ IDEDevice.prototype.ata_write_dma = function(cmd) if(!prd_count) { prd_count = 0x10000; + dbg_log("dma: prd count was 0", LOG_DISK); } - dbg_log("dma write dest=" + h(prd_addr) + " prd_count=" + h(prd_count), LOG_DISK); + dbg_log("dma write transfer dest=" + h(prd_addr) + " prd_count=" + h(prd_count), LOG_DISK); var slice = this.memory.mem8.subarray(prd_addr, prd_addr + prd_count); + dbg_assert(slice.length === prd_count); + + if(DEBUG) + { + //dbg_log(hex_dump(slice), LOG_DISK); + } this.buffer.set(start + offset, slice, function() { @@ -1314,8 +1538,8 @@ IDEDevice.prototype.ata_write_dma = function(cmd) dbg_log("dma write completed", LOG_DISK); this.status = 0x50; this.push_irq(); - this.dma_status &= ~2 & ~1; - this.dma_status |= 4; + this.device.dma_status &= ~2 & ~1; + this.device.dma_status |= 4; } }.bind(this)); @@ -1331,14 +1555,18 @@ IDEDevice.prototype.ata_write_dma = function(cmd) dbg_log("dma write completed", LOG_DISK); this.status = 0x50; this.push_irq(); - this.dma_status &= ~2 & ~1; - this.dma_status |= 4; + this.device.dma_status &= ~2 & ~1; + this.device.dma_status |= 4; + } + else + { + dbg_assert(false, "dma write not completed", LOG_DISK); } this.report_write(byte_count); }; -IDEDevice.prototype.get_chs = function() +IDEInterface.prototype.get_chs = function() { var c = this.cylinder_low & 0xFF | this.cylinder_high << 8 & 0xFF00, h = this.head, @@ -1347,22 +1575,22 @@ IDEDevice.prototype.get_chs = function() return (c * this.head_count + h) * this.sectors_per_track + s - 1; }; -IDEDevice.prototype.get_lba28 = function() +IDEInterface.prototype.get_lba28 = function() { - return this.sector & 0xFF | - this.cylinder_low << 8 & 0xFF00 | - this.cylinder_high << 16 & 0xFF0000 | - this.head << 24; + return this.sector & 0xFF | + this.cylinder_low << 8 & 0xFF00 | + this.cylinder_high << 16 & 0xFF0000 | + (this.head & 0xF) << 24; }; -IDEDevice.prototype.get_lba48 = function() +IDEInterface.prototype.get_lba48 = function() { // Note: Bits over 32 missing - return (this.sector & 0xFF | this.cylinder_low << 8 & 0xFF00 | this.cylinder_high << 16 & 0xFF0000 | + return (this.sector & 0xFF | this.cylinder_low << 8 & 0xFF00 | this.cylinder_high << 16 & 0xFF0000 | (this.sector >> 8) << 24 & 0xFF000000) >>> 0; }; -IDEDevice.prototype.create_identify_packet = function() +IDEInterface.prototype.create_identify_packet = function() { this.data_pointer = 0; // http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/harddrv.cc#L2821 @@ -1370,67 +1598,72 @@ IDEDevice.prototype.create_identify_packet = function() if(this.drive_head & 0x10) { // slave - this.pio_data = new Uint8Array(0); + this.pio_data_allocate(0); return; } - this.pio_data = new Uint8Array([ - 0x40, this.is_atapi ? 0x85 : 0, + for(var i = 0; i < 512; i++) + { + this.pio_data[i] = 0; + } + + this.pio_data_set([ + 0x40, this.is_atapi ? 0x85 : 0, // 1 cylinders - this.cylinder_count, this.cylinder_count >> 8, - 0, 0, + this.cylinder_count, this.cylinder_count >> 8, + 0, 0, // 3 heads - this.head_count, this.head_count >> 8, - 0, 0, + this.head_count, this.head_count >> 8, + this.sectors_per_track / 512, this.sectors_per_track / 512 >> 8, // 5 - 0, 0, + 0, 512 >> 8, // sectors per track - this.sectors_per_track, this.sectors_per_track >> 8, + this.sectors_per_track, this.sectors_per_track >> 8, 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, + 3, 0, + 0, 2, + 4, 0, // 23-26 firmware revision - 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, // 27 model number - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 56, 118, 32, 54, 68, 72, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, // 47 - 0xFF, 0, - 1, 0, + 0xFF, 0, + 1, 0, 0, 3, // capabilities, 2: Only LBA / 3: LBA and DMA // 50 - 0, 0, - 0, 2, - 0, 2, - 7, 0, + 0, 0, + 0, 2, + 0, 2, + 7, 0, // 54 cylinders - this.cylinder_count, this.cylinder_count >> 8, + this.cylinder_count, this.cylinder_count >> 8, // 55 heads - this.head_count, this.head_count >> 8, + this.head_count, this.head_count >> 8, // 56 sectors per track - this.sectors_per_track, 0, + this.sectors_per_track, 0, // capacity in sectors - this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF, - this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF, - + this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF, + this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF, + 0, 0, // 60 - this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF, - this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF, - - 0, 0, + this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF, + this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF, + + 0, 0, // 63, dma selected mode - 0, 4, + 0, 4, //0, 0, // no DMA 0, 0, @@ -1449,10 +1682,12 @@ IDEDevice.prototype.create_identify_packet = function() // 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, + this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF, + this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF, ]); + this.pio_data_length = 512; + if(this.cylinder_count > 16383) { this.pio_data[2] = this.pio_data[108] = 16383 & 0xFF; @@ -1460,72 +1695,44 @@ IDEDevice.prototype.create_identify_packet = function() } }; -IDEDevice.prototype.dma_read_addr = function() -{ - return this.prdt_addr; -}; - -IDEDevice.prototype.dma_set_addr = function(data) -{ - 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; -}; - - -IDEDevice.prototype.dma_read_command = function() +IDEInterface.prototype.pio_data_allocate = function(len) { - dbg_log("DMA read command", LOG_DISK); - return 1 | this.dma_read_status() << 16; -}; + this.pio_data_allocate_noclear(len); -IDEDevice.prototype.dma_read_command8 = function() -{ - dbg_log("DMA read command8", LOG_DISK); - return 1; + for(var i = 0; i < len; i++) + { + this.pio_data[i] = 0; + } }; - -IDEDevice.prototype.dma_write_command = function(value) +IDEInterface.prototype.pio_data_allocate_noclear = function(len) { - dbg_log("DMA write command: " + h(value), LOG_DISK); - - if(value & 1) + if(this.pio_data.length < len) { - this.push_irq(); + this.pio_data = new Uint8Array(len + 3 & ~3); } - this.dma_write_status(value >> 16 & 0xFF); + this.pio_data_length = len; }; - -IDEDevice.prototype.dma_write_command8 = function(value) +IDEInterface.prototype.pio_data_set = function(data) { - dbg_log("DMA write command8: " + h(value), LOG_DISK); - - if(value & 1) + if(this.pio_data.length < data.length) { - this.push_irq(); + this.pio_data = new Uint8Array(data.length + 3 & ~3); } + + this.pio_data.set(data); + this.pio_data_length = data.length; }; -IDEDevice.prototype.report_read_start = function() +IDEInterface.prototype.report_read_start = function() { this.stats.loading = true; this.bus.send("ide-read-start"); }; -IDEDevice.prototype.report_read_end = function(byte_count) +IDEInterface.prototype.report_read_end = function(byte_count) { this.stats.loading = false; @@ -1536,7 +1743,7 @@ IDEDevice.prototype.report_read_end = function(byte_count) this.bus.send("ide-read-end", [this.nr, byte_count, sector_count]); }; -IDEDevice.prototype.report_write = function(byte_count) +IDEInterface.prototype.report_write = function(byte_count) { var sector_count = byte_count / this.sector_size | 0; this.stats.sectors_written += sector_count; @@ -1545,3 +1752,62 @@ IDEDevice.prototype.report_write = function(byte_count) this.bus.send("ide-write-end", [this.nr, byte_count, sector_count]); }; +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; + state[5] = this.data_port_buffer; + state[6] = this.data_port_callback; + state[7] = this.data_port_count; + state[8] = this.data_port_current; + 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; + state[16] = this.pio_data; + state[17] = this.pio_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; + 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]; + this.data_port_buffer = state[5]; + this.data_port_callback = state[6]; + this.data_port_count = state[7]; + this.data_port_current = state[8]; + 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]; + this.pio_data = state[16]; + this.pio_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]; +};