From a5fb00d67e237fe4774c0a2b179a880980f018f1 Mon Sep 17 00:00:00 2001 From: copy Date: Tue, 26 Nov 2013 21:58:12 +0100 Subject: [PATCH] New: IDE emulation. Lots of cleanups in the ATA controller --- Makefile | 2 +- Readme.md | 5 +- loader.js | 2 +- src/cpu.macro.js | 18 +- src/disk.js | 545 ------------------------------------ src/ide.js | 633 ++++++++++++++++++++++++++++++++++++++++++ src/node/main.js | 2 +- tests/perf/runtest.js | 1 - 8 files changed, 647 insertions(+), 561 deletions(-) delete mode 100644 src/disk.js create mode 100644 src/ide.js diff --git a/Makefile b/Makefile index d7a8c425..135c6ea0 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ CLOSURE_FLAGS=\ --language_in ECMASCRIPT5_STRICT -CORE_FILES=const.js io.js cpu.js main.js disk.js pci.js floppy.js memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js +CORE_FILES=const.js io.js cpu.js main.js ide.js pci.js floppy.js memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js BROWSER_FILES=browser/main.js browser/screen.js browser/keyboard.js browser/mouse.js NODE_FILES=node/main.js node/keyboard.js node/screen.js diff --git a/Readme.md b/Readme.md index f0365330..8943e56a 100644 --- a/Readme.md +++ b/Readme.md @@ -26,9 +26,8 @@ v86 emulates an x86-compatible CPU and hardware. Here's a list of emulated hardw - A CMOS Real Time Clock (RTC). - A VGA controller with SVGA support and Bochs VBE Extensions. This includes support for large resolutions. -- A PCI bus. This one is mostly incomplete and not used by every device. -- An AT disk controller with some ATAPI and some SCSI commands. This is quite - incomplete and possibly buggy, too. +- A PCI bus. This one is partly incomplete and not used by every device. +- An IDE disk controller. How to build, run and embed? diff --git a/loader.js b/loader.js index 7876d763..ed0554e2 100644 --- a/loader.js +++ b/loader.js @@ -3,7 +3,7 @@ (function() { var PATH = "src/", - CORE_FILES="const.js io.js cpu.js main.js disk.js pci.js floppy.js memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js" + CORE_FILES="const.js io.js cpu.js main.js ide.js pci.js floppy.js memory.js dma.js pit.js vga.js ps2.js pic.js rtc.js uart.js" BROWSER_FILES="browser/main.js browser/screen.js browser/keyboard.js browser/mouse.js" window.onload = function() diff --git a/src/cpu.macro.js b/src/cpu.macro.js index 766ae770..fecf87b7 100644 --- a/src/cpu.macro.js +++ b/src/cpu.macro.js @@ -219,17 +219,17 @@ var pci, /** - * @type {CDRom} + * @type {IDEDevice} */ cdrom, /** - * @type {HDD} + * @type {IDEDevice} */ hda, /** - * @type {HDD} + * @type {IDEDevice} */ hdb, @@ -634,17 +634,17 @@ function cpu_init(settings) if(settings.cdrom_disk) { - cpu.dev.cdrom = cdrom = new CDRom(devapi, settings.cdrom_disk); + cpu.dev.cdrom = cdrom = new IDEDevice(devapi, settings.cdrom_disk, true, 0); } if(settings.hda_disk) { - cpu.dev.hda = hda = new HDD(devapi, settings.hda_disk, 0); - } - if(settings.hdb_disk) - { - cpu.dev.hdb = hdb = new HDD(devapi, settings.hdb_disk, 1); + cpu.dev.hda = hda = new IDEDevice(devapi, settings.hda_disk, false, 1); } + //if(settings.hdb_disk) + //{ + // cpu.dev.hdb = hdb = new IDEDevice(devapi, settings.hdb_disk, false, 1); + //} timer = new PIT(devapi); rtc = new RTC(devapi, fdc.type); diff --git a/src/disk.js b/src/disk.js deleted file mode 100644 index b13e5a87..00000000 --- a/src/disk.js +++ /dev/null @@ -1,545 +0,0 @@ -"use strict"; - - -var - /** @const */ - CDROM_SECTOR_SIZE = 2048, - /** @const */ - HD_SECTOR_SIZE = 512; - - -/** @constructor */ -function CDRom(dev, cd_buffer) -{ - this.io = dev.io; - this.memory = dev.memory; - this.pic = dev.pic; - this.pci = dev.pci; - - this.vendor_id = 0x1002; - this.class_revision = 0x106 << 16 | 0x01 << 8; - this.irq = 14; - this.iobase = 0xFFF10000; - this.sector_size = CDROM_SECTOR_SIZE; - this.buffer = cd_buffer; - this.atapi = true; - this.pci_id = 8; - - this.init(); -} -CDRom.prototype = new AHCIDevice(); - -/** @constructor */ -function HDD(dev, disk_buffer, nr) -{ - var port = nr === 0 ? 0x1F0 : 0x170, - irq = nr === 0 ? 14 : 15; - - var pic = dev.pic; - - this.io = dev.io; - this.memory = dev.memory; - this.pic = dev.pic; - this.pci = dev.pci; - - this.vendor_id = 0x1002; - this.class_revision = 0x106 << 16 | 0x01 << 8; - this.irq = irq; - this.iobase = 0xFFF00000; - this.sector_size = HD_SECTOR_SIZE; - this.sector_count = disk_buffer.byteLength / this.sector_size; - this.buffer = disk_buffer; - this.atapi = false; - this.pci_id = 0x10; - - this.head_count = 16; - this.sectors_per_track = 63; - - this.cylinder_count = disk_buffer.byteLength / - this.head_count / (this.sectors_per_track + 1) / this.sector_size; - - dbg_assert(this.cylinder_count === (this.cylinder_count | 0)); - dbg_assert(this.cylinder_count <= 16383); - - var me = this; - - // status - this.io.register_read(port | 7, read_status); - - // alternate status, starting at 3f6/376 - this.io.register_read(port | 0x206, read_status); - - function read_status() - { - dbg_log("ATA read status", LOG_DISK); - - var status = 0x50; - - if(data_pointer < pio_data.length) - status |= 8; - - return status; - } - - var last_drive = 0xFF, - data_pointer = 0, - pio_data = [], - drq = false, - is_lba = 0, - slave = 0, - bytecount = 0, - sector = 0, - cylinder = 0, - head = 0; - - - function push_irq() - { - pic.push_irq(me.irq); - } - - this.io.register_write(port | 6, function(data) - { - dbg_log("1F6 write " + h(data), LOG_DISK); - - var slave = data & 0x10, - mode = data & 0xE0, - low = data & 0xF; - - - if(slave) - { - //drq = false; - return; - } - - is_lba = data >> 6 & 1; - head = data & 0xF; - last_drive = data; - }); - - this.io.register_write(port | 2, function(data) - { - dbg_log("1F2 write: " + data, LOG_DISK); - if(data) - { - bytecount = data << 9; - } - else - { - bytecount = 256 << 9; - } - //bytecount = 1 << 9; - }); - this.io.register_write(port | 3, function(data) - { - sector = data; - }); - this.io.register_write(port | 4, function(data) - { - cylinder = cylinder & 0xFF00 | data; - }); - this.io.register_write(port | 5, function(data) - { - cylinder = cylinder & 0xFF | data << 8; - }); - - this.io.register_write(port | 7, function(cmd) - { - if(cmd === 0xEC) - { - dbg_log("ATA identify device", LOG_DISK); - // identify device - // http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/harddrv.cc#L2821 - - data_pointer = 0; - - pio_data = new Uint8Array([ - 0x40, 0, - // 1 cylinders - me.cylinder_count, me.cylinder_count >> 8, - 0, 0, - - // 3 heads - me.head_count, me.head_count >> 8, - 0, 0, - // 5 - 0, 0, - // sectors per track - me.sectors_per_track, 0, - 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, - // 23-26 firmware revision - 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, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - - // 47 - 0, 0, - 1, 0, - 0, 3, // capabilities - // 50 - 0, 0, - 0, 2, - 0, 2, - 7, 0, - - // 54 cylinders - me.cylinder_count, me.cylinder_count >> 8, - // 55 heads - me.head_count, me.head_count >> 8, - // 56 sectors per track - me.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, - - 0, 0, - // 60 - this.sector_count & 0xFF, this.sector_count >> 8 & 0xFF, - this.sector_count >> 16 & 0xFF, this.sector_count >> 24 & 0xFF, - - 0, 0, 0, 0, 0, 0, - // 65 - 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, - // 85 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // 90 - 0, 0, 0, 0, 0, 0, 0, 0, 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, - - - ]); - - push_irq(); - } - else if(cmd === 0x91) - { - dbg_log("ATA cmd 91", LOG_DISK); - push_irq(); - } - else if(cmd === 0x10) - { - // obsolete - dbg_log("ATA cmd 10", LOG_DISK); - push_irq(); - } - else if(cmd = 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); - push_irq(); - pio_data = [ - 0, 0, // error - 0, 0, // count - - // result - disk_buffer.byteLength & 0xff, - disk_buffer.byteLength >> 8 & 0xff, - disk_buffer.byteLength >> 16 & 0xff, - disk_buffer.byteLength >> 24 & 0xff, - 0, 0, - - 0, 0, // - ]; - } - else if(cmd === 0x20) - { - if(DEBUG && is_lba) - throw "unimplemented"; - - var lba = (cylinder * me.head_count + head) * me.sectors_per_track + sector - 1; - dbg_log("ATA read: from=" + h(lba * me.sector_size) + " chs=" + cylinder + "/" + head + "/" + sector + " length=" + h(bytecount), LOG_DISK); - - me.buffer.get(lba * me.sector_size, bytecount, function(data) - { - data_pointer = 0; - pio_data = data; - - push_irq(); - }); - } - else - { - dbg_log("New ATA cmd on 1F7: " + h(cmd), LOG_DISK); - } - }); - - this.io.register_read(port | 0, function() - { - if(data_pointer < pio_data.length) - { - dbg_log("Read 1F0: " + h(pio_data[data_pointer], 2), LOG_DISK); - - if((data_pointer & 511) === 0) - push_irq(); - - return pio_data[data_pointer++] & 0xFF; - } - else - { - dbg_log("Read 1F0: empty", LOG_DISK); - return 0; - } - }); - - - this.io.register_read(port | 1, function() - { - dbg_log("Read 1F1", LOG_DISK); - return 0xFF; - }); - - this.io.register_read(port | 2, function() - { - dbg_log("Read 1F2", LOG_DISK); - return 0xFF; - }); - - - this.io.register_read(port | 3, function() - { - dbg_log("Read 1F3", LOG_DISK); - return 0xFF; - }); - - this.io.register_read(port | 6, function() - { - dbg_log("Read 1F6", LOG_DISK); - return last_drive; - }); - - this.init(); -} -HDD.prototype = new AHCIDevice(); - - -/** @constructor */ -function AHCIDevice() -{ - var me, - memory; - - this.init = function() - { - me = this; - memory = this.memory; - - this.pci.register_device(this, this.pci_id); - - this.memory.mmap_register(this.iobase, 0x4000, true, mmio_read, mmio_write); - - }; - - var host_ctl = 0, - host_caps = 1, - host_ports_impl = 1, - host_intbits = 1, - port_lst_addr, - port_fis_addr; - - function atapi_command_read(atapi, dest, byte_len) - { - var lba = Math.to_be32(memory.read32s(atapi + 2)), - count = Math.to_be16(memory.read16(atapi + 7)), - flags = memory.read8(atapi + 1), - - //bytecount = Math.min(count * me.sector_size, byte_len + 1); - bytecount = count * me.sector_size; - - dbg_log("CD read lba=" + h(lba) + - " lbacount=" + h(count) + - " bytelen=" + h(byte_len) + - " copycount=" + h(bytecount) + - " flags=" + h(flags) + - " buf=" + h(dest, 8), LOG_CD); - - me.buffer.get(lba * me.sector_size, bytecount, function(data) - { - memory.write_blob(data, dest); - }); - - //pic.push_irq(me.irq); - } - - function ata_command_rw(cmd_fis, dest, is_read) - { - var lba = memory.read32s(cmd_fis + 4) & 0xFFFFFF, - count = memory.read16(cmd_fis + 12), - bytecount = count * me.sector_size; - - dbg_log("ahci " + (is_read ? "read" : "write") + ": lba=" + h(lba, 8) + - " count=" + h(count, 4) + - " dest=" + h(dest, 8)); - - if(is_read) - { - this.buffer.get(lba * me.sector_size, bytecount, function(data) - { - memory.write_blob(data, dest); - }); - } - else - { - this.buffer.set(lba * me.sector_size, - new Uint8Array(memory.buffer, dest, bytecount), - function() - { - }); - } - } - - - function mmio_read(addr) - { - switch(addr) - { - case 0: - return host_caps; - - case 4: - return host_ctl; - - case 0xC: - return host_ports_impl; - - case 0x128: - return 0x03; - - case 0x110: - return host_intbits; - - default: - dbg_log("New PCI mmio read from " + h(addr, 8), LOG_CD); - } - } - - function mmio_write(addr, value) - { - switch(addr) - { - case 0x100: - port_lst_addr = value; - dbg_log("lst at " + h(value, 8), LOG_CD); - break; - case 0x108: - port_fis_addr = value; - dbg_log("fis at " + h(value, 8), LOG_CD); - break; - - case 0x118: - dbg_log("port cmd: " + h(value, 8), LOG_CD); - break; - - case 0x138: - var - - ctba_addr = memory.read32s(port_lst_addr + 8), - - first_prdt_start = ctba_addr + 0x80, - flags = memory.read16(port_lst_addr), - prdt_addr = memory.read32s(first_prdt_start) + 0x100000000 * memory.read32s(first_prdt_start + 4), - prdt_len = memory.read32s(ctba_addr + 0x80 + 0xC) & 0xFFF, - atapi_command = memory.read8(ctba_addr + 0x40), - fis_command = memory.read8(ctba_addr + 2), - - dma_fis_start = port_fis_addr + 0, - pio_fis_start = port_fis_addr + 0x20, - d2h_fis_start = port_fis_addr + 0x40, - ufis_start = port_fis_addr + 0x60, - - command_fis_start = ctba_addr + 0, - atapi_command_start = ctba_addr + 0x40; - - if((fis_command === 0xA0 || fis_command === 0xA1) && - !me.atapi) - { - return; - } - - // status success - memory.write8(d2h_fis_start + 2, 0x40); - - dbg_log("ctba at " + h(ctba_addr), LOG_CD); - dbg_log("prdt at " + h(prdt_addr), LOG_CD); - dbg_log("flags: " + h(flags, 2), LOG_CD); - dbg_log("cmd fis command: " + h(fis_command, 2), LOG_CD); - - dbg_log("fis LBA=" + h(memory.read32s(command_fis_start + 4) & 0xffffff), LOG_CD); - - dbg_log("Prdts count: " + h(memory.read16(port_lst_addr + 2)), LOG_CD); - dbg_log("PRD byte count: " + h(memory.read32s(port_lst_addr + 4)), LOG_CD); - - dbg_log("First prdt byte count: " + h(memory.read32s(ctba_addr + 0x80 + 0xC)), LOG_CD); - - if(fis_command === 0xC8 || fis_command === 0xCA) - { - ata_command_rw(command_fis_start, prdt_addr, fis_command === 0xC8); - } - else if(fis_command === 0xEC) - { - // ATA_CMD_IDENTIFY_DEVICE - - // number of sectors - memory.write32(prdt_addr + 120, me.buffer.byteLength / me.sector_size); - } - else if(fis_command === 0xA1) - { - // ATA_CMD_IDENTIFY_PACKET_DEVICE - - // is CD - memory.write32(prdt_addr, 0x0500); - } - else if(fis_command === 0xA0) - { - // ATA_CMD_PACKET - if(atapi_command === 0x28) - { - atapi_command_read(ctba_addr + 0x40, prdt_addr, prdt_len); - } - else if(atapi_command === 0x2a) - { - // write - dbg_log("atapi - unimplemented write", LOG_CD); - } - else if(atapi_command === 0x25) - { - // read capacity - dbg_log("atapi - unimplemented read cap", LOG_CD); - } - else - { - dbg_log("atapi - unimplemented " + h(atapi_command, 2), LOG_CD); - } - } - else - { - dbg_log("unimplemented fis command: " + h(fis_command, 2)); - } - - break; - - default: - dbg_log("PCI mmio write addr=" + h(addr, 8) + " value=" + h(value, 8), LOG_CD); - } - } -} - diff --git a/src/ide.js b/src/ide.js new file mode 100644 index 00000000..fe524798 --- /dev/null +++ b/src/ide.js @@ -0,0 +1,633 @@ +"use strict"; + + +var + /** @const */ + CDROM_SECTOR_SIZE = 2048, + /** @const */ + HD_SECTOR_SIZE = 512; + + +/** @constructor */ +function IDEDevice(dev, buffer, is_cd, nr) +{ + var pic = dev.pic, + me = this; + + // the lower one is a bit buggy now + nr = 1; + + if(nr === 0) + { + this.ata_port = 0x1F0; + this.irq = 14; + } + else + { + this.ata_port = 0x170; + this.irq = 15; + } + + // alternate status, starting at 3f4/374 + this.ata_port_high = this.ata_port | 0x204; + + this.io = dev.io; + this.memory = dev.memory; + this.pic = dev.pic; + this.pci = dev.pci; + + this.sector_size = is_cd ? CDROM_SECTOR_SIZE : HD_SECTOR_SIZE; + this.buffer = buffer; + this.is_atapi = is_cd; + + this.sector_count = me.buffer.byteLength / this.sector_size; + + if(is_cd) + { + this.head_count = 1; + this.sectors_per_track = 0; + } + else + { + this.head_count = 1; + this.sectors_per_track = 63; + } + + this.cylinder_count = me.buffer.byteLength / + this.head_count / (this.sectors_per_track + 1) / this.sector_size; + + dbg_assert(this.cylinder_count === (this.cylinder_count | 0)); + + function push_irq() + { + if((device_control & 2) === 0) + { + pic.push_irq(me.irq); + } + } + + // 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, + dev.pci.register_device([ + 0x86, 0x80, 0x20, 0x3a, 0x05, 0x00, 0xb0, 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, + 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, + ], 0x1f << 3); + + // status + this.io.register_read(this.ata_port | 7, read_status); + this.io.register_read(this.ata_port_high | 2, read_status); + + this.io.register_write(this.ata_port | 7, write_control); + this.io.register_write(this.ata_port_high | 2, write_control); + + var device_control = 2, + last_drive = 0xFF, + data_pointer = 0, + pio_data = [], + is_lba = 0, + slave = 0, + bytecount = 0, + sector = 0, + lba_count = 0, + cylinder_low = 0, + cylinder_high = 0, + head = 0, + drive_head = 0, + status = 0x50, + sectors_per_drq = 1, + atapi_command = []; + + + function read_status() + { + dbg_log("ATA read status: " + h(status), LOG_DISK); + return status; + } + + function write_control(data) + { + dbg_log("device control: " + h(data), LOG_DISK); + device_control = data; + + if(data & 4) + { + // reset + if(me.is_atapi) + { + status = 1; + bytecount = 1; + sector = 1; // lba_low + cylinder_low = 0x14; // lba_mid + cylinder_high = 0xeb; // lba_high + } + else + { + status = 1; + bytecount = 1; + sector = 1; // lba_low + cylinder_low = 0x3c; // lba_mid + cylinder_high = 0xc3; // lba_high + } + } + } + + function atapi_read(cmd) + { + // Note: Big Endian + var lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5], + count = cmd[7] << 8 | cmd[8], + flags = cmd[1], + bytecount = count * me.sector_size, + transfered_ata_blocks = Math.min(bytecount / 512, cylinder_low << 8 | cylinder_high); + + //bytecount = count; + + dbg_log("CD read lba=" + h(lba) + + " lbacount=" + h(count) + + " bytecount=" + h(count * me.sector_size) + + " flags=" + h(flags), LOG_CD); + + + cylinder_low = transfered_ata_blocks >> 8; + cylinder_high = transfered_ata_blocks; + + me.buffer.get(lba * me.sector_size, count * me.sector_size, function(data) + { + //memory.write_blob(data, dest); + pio_data = data; + status = 0x58; + data_pointer = 0; + push_irq(); + }); + } + + function read_data_port(port_addr) + { + if(port_addr === me.ata_port) + { + if(data_pointer < pio_data.length) + { + if((data_pointer + 1) % (sectors_per_drq * 512) === 0 || + data_pointer + 1 === pio_data.length) + { + if(data_pointer + 1 === pio_data.length) + { + status = 0x50; + bytecount = 3; + } + + dbg_log("ATA IRQ", LOG_DISK); + push_irq(); + } + + if((data_pointer + 1 & 255) === 0) + { + dbg_log("Read 1F0: " + h(pio_data[data_pointer], 2) + + " cur=" + h(data_pointer) + + " cnt=" + h(pio_data.length), LOG_DISK); + } + + return pio_data[data_pointer++] & 0xFF; + } + else + { + //if((data_pointer + 1 & 255) === 0) + { + dbg_log("Read 1F0: empty", LOG_DISK); + } + data_pointer++; + return 0; + } + } + else if(port_addr === (me.ata_port | 1)) + { + dbg_log("Read lba_count", LOG_DISK); + return lba_count; + } + else if(port_addr === (me.ata_port | 2)) + { + dbg_log("Read bytecount: " + h(bytecount & 0xFF), LOG_DISK); + return bytecount & 0xFF; + } + else if(port_addr === (me.ata_port | 3)) + { + dbg_log("Read sector", LOG_DISK); + return sector & 0xFF; + } + } + this.io.register_read(me.ata_port | 0, read_data_port); + this.io.register_read(me.ata_port | 1, read_data_port); + this.io.register_read(me.ata_port | 2, read_data_port); + this.io.register_read(me.ata_port | 3, read_data_port); + + this.io.register_read(me.ata_port | 4, function() + { + dbg_log("Read 1F4: " + h(cylinder_low & 0xFF), LOG_DISK); + return cylinder_low & 0xFF; + }); + this.io.register_read(me.ata_port | 5, function(port) + { + dbg_log("Read 1F5: " + h(cylinder_high & 0xFF), LOG_DISK); + return cylinder_high & 0xFF; + }); + this.io.register_read(me.ata_port | 6, function() + { + dbg_log("Read 1F6", LOG_DISK); + return drive_head; + }); + + function write_data_port(data, port_addr) + { + if(port_addr === me.ata_port) + { + atapi_command.push(data); + + if(atapi_command.length === 12) + { + + dbg_log("ATAPI Command: " + h(atapi_command[0]), LOG_DISK); + dbg_log(atapi_command.join(","), LOG_DISK); + + bytecount = 2; + + switch(atapi_command[0]) + { + case 0: + status = 0x50; + //pio_data = new Uint8Array(512); + //data_pointer = 0; + push_irq(); + break; + case 0x28: + // read + atapi_read(atapi_command); + break; + case 0x5A: + // mode sense + push_irq(); + status = 0x50; + break; + case 0x25: + // read capacity + pio_data = new Uint8Array([ + me.sector_count >> 24 & 0xff, + me.sector_count >> 16 & 0xff, + me.sector_count >> 8 & 0xff, + me.sector_count & 0xff, + 0, + 0, + me.sector_size >> 8 & 0xff, + me.sector_size & 0xff, + ]); + status = 0x58; + + data_pointer = 0; + + bytecount = 2; + cylinder_low = 8; + cylinder_high = 0; + + push_irq(); + break; + case 0x43: + // read header + pio_data = new Uint8Array(Math.min(atapi_command[8], 4)); + status = 0x58; + data_pointer = 0; + bytecount = 2; + push_irq(); + break; + case 0x46: + // get configuration + pio_data = new Uint8Array(atapi_command[8] | atapi_command[7] << 8); + status = 0x58; + data_pointer = 0; + bytecount = 2; + push_irq(); + break; + case 0x46: + // prevent/allow medium removal + pio_data = []; + status = 0x50; + data_pointer = 0; + bytecount = 2; + push_irq(); + break; + case 0x51: + // read disk information + pio_data = new Uint8Array(0); + status = 0x50; + data_pointer = 0; + bytecount = 2; + push_irq(); + break; + case 0x12: + // inquiry + pio_data = new Uint8Array(Math.min(atapi_command[4], 35)); + status = 0x58; + + pio_data[0] = 5; + pio_data[1] = 0x80; + pio_data[3] = 1; + pio_data[4] = 0x31; + + data_pointer = 0; + bytecount = 2; + push_irq(); + break; + default: + status = 0x50; + dbg_log("Unimplemented ATAPI command: " + h(atapi_command[0]), LOG_DISK); + } + + atapi_command = []; + } + } + else if(port_addr === (me.ata_port | 1)) + { + dbg_log("lba_count: " + data, LOG_DISK); + lba_count = (lba_count << 8 | data) & 0xFFFF; + } + else if(port_addr === (me.ata_port | 2)) + { + dbg_log("bytecount: " + data, LOG_DISK); + bytecount = (bytecount << 8 | data) & 0xFFFF; + } + else if(port_addr === (me.ata_port | 3)) + { + dbg_log("sector: " + data, LOG_DISK); + sector = (sector << 8 | data) & 0xFFFF; + } + } + this.io.register_write(me.ata_port | 0, write_data_port); + this.io.register_write(me.ata_port | 1, write_data_port); + this.io.register_write(me.ata_port | 2, write_data_port); + this.io.register_write(me.ata_port | 3, write_data_port); + + this.io.register_write(me.ata_port | 4, function(data) + { + dbg_log("sector low: " + h(data), LOG_DISK); + cylinder_low = (cylinder_low << 8 | data) & 0xFFFF; + }); + this.io.register_write(me.ata_port | 5, function(data) + { + dbg_log("sector high: " + h(data), LOG_DISK); + cylinder_high = (cylinder_high << 8 | data) & 0xFFFF; + }); + this.io.register_write(me.ata_port | 6, function(data) + { + var slave = data & 0x10, + mode = data & 0xE0, + low = data & 0xF; + + dbg_log("1F6: " + h(data, 2), LOG_DISK); + + if(slave) + { + return; + } + + drive_head = data; + is_lba = data >> 6 & 1; + head = data & 0xF; + last_drive = data; + + }); + + this.io.register_write(me.ata_port | 7, function(cmd) + { + dbg_log("ATA Command: " + h(cmd), LOG_DISK); + + if(cmd === 0x08) + { + dbg_log("ATA device reset", LOG_DISK); + data_pointer = 0; + pio_data = []; + status = 0x50; + + push_irq(); + } + else if(cmd === 0xE1) + { + dbg_log("ATA idle immediate", LOG_DISK); + push_irq(); + } + else if(cmd === 0xA1) + { + dbg_log("ATA identify packet device", LOG_DISK); + + data_pointer = 0; + pio_data = new Uint8Array(512); + + pio_data[0] = 0x40; + pio_data[1] = 0x05 | me.is_atapi << 7; + status = 0x58; + + push_irq(); + } + else if(cmd === 0xEC) + { + dbg_log("ATA identify device", LOG_DISK); + // identify device + // http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/harddrv.cc#L2821 + + if(me.is_atapi) + { + return; + } + + data_pointer = 0; + + pio_data = new Uint8Array(512); + + pio_data.set([ + 0x40, 0, + // 1 cylinders + me.cylinder_count, me.cylinder_count >> 8, + 0, 0, + + // 3 heads + me.head_count, me.head_count >> 8, + 0, 0, + // 5 + 0, 0, + // sectors per track + me.sectors_per_track, 0, + 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, + // 23-26 firmware revision + 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, + 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, + 0, 3, // capabilities + // 50 + 0, 0, + 0, 2, + 0, 2, + 7, 0, + + // 54 cylinders + me.cylinder_count, me.cylinder_count >> 8, + // 55 heads + me.head_count, me.head_count >> 8, + // 56 sectors per track + me.sectors_per_track, 0, + // capacity in sectors + me.sector_count & 0xFF, me.sector_count >> 8 & 0xFF, + me.sector_count >> 16 & 0xFF, me.sector_count >> 24 & 0xFF, + + 0, 0, + // 60 + me.sector_count & 0xFF, me.sector_count >> 8 & 0xFF, + me.sector_count >> 16 & 0xFF, me.sector_count >> 24 & 0xFF, + + 0, 0, 0, 0, 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 + me.sector_count & 0xFF, me.sector_count >> 8 & 0xFF, + me.sector_count >> 16 & 0xFF, me.sector_count >> 24 & 0xFF, + + + ]); + + if(me.cylinder_count > 16383) + { + pio_data[2] = pio_data[108] = 16383 & 0xFF; + pio_data[3] = pio_data[109] = 16383 >> 8; + } + + status = 0x58; + + push_irq(); + } + else if(cmd === 0x29 || cmd === 0x24) + { + // 0x29 read multiple ext + // 0x24 read sectors ext + var count = bytecount, + lba = (cylinder_high << 16 | cylinder_low) >>> 0, + byte_count = count * me.sector_size; + + dbg_log("ATA read lba=" + h(lba) + + " lbacount=" + h(count) + + " bytecount=" + h(byte_count), LOG_DISK); + + cylinder_low += count; + + me.buffer.get(lba * me.sector_size, byte_count, function(data) + { + pio_data = data; + status = 0x58; + data_pointer = 0; + + push_irq(); + }); + + } + else if(cmd === 0xEA) + { + // FLUSH CACHE EXT + dbg_log("ATA cmd EA", LOG_DISK); + + push_irq(); + } + else if(cmd === 0x91) + { + // INITIALIZE DEVICE PARAMETERS + dbg_log("ATA cmd 91", LOG_DISK); + + push_irq(); + } + else if(cmd === 0x10) + { + // obsolete + dbg_log("ATA cmd 10", LOG_DISK); + + push_irq(); + } + else if(cmd === 0xC6) + { + // SET MULTIPLE MODE + dbg_log("ATA cmd C6", LOG_DISK); + + // Logical sectors per DRQ Block in word 1 + dbg_log("Logical sectors per DRQ Block: " + h(bytecount), LOG_DISK); + sectors_per_drq = bytecount; + + push_irq(); + } + else if(cmd === 0xEF) + { + // SET FEATURES + dbg_log("ATA cmd EF", LOG_DISK); + + push_irq(); + } + else if(cmd === 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); + push_irq(); + pio_data = [ + 0, 0, // error + 0, 0, // count + + // result + me.buffer.byteLength & 0xff, + me.buffer.byteLength >> 8 & 0xff, + me.buffer.byteLength >> 16 & 0xff, + me.buffer.byteLength >> 24 & 0xff, + 0, 0, + + 0, 0, // + ]; + status = 0x58; + } + else if(cmd === 0xA0) + { + // ATA_CMD_PACKET + status = 0x58; + atapi_command = []; + bytecount = 1; + push_irq(); + } + else + { + dbg_log("New ATA cmd on 1F7: " + h(cmd), LOG_DISK); + } + }); +} diff --git a/src/node/main.js b/src/node/main.js index bb25dd50..33b4d116 100644 --- a/src/node/main.js +++ b/src/node/main.js @@ -49,7 +49,7 @@ include(path + "floppy.js"); include(path + "memory.js"); include(path + "io.js"); include(path + "pci.js"); -include(path + "disk.js"); +include(path + "ide.js"); include(path + "dma.js"); include(path + "pit.js"); include(path + "vga.js"); diff --git a/tests/perf/runtest.js b/tests/perf/runtest.js index 5f7e3353..9e59df2c 100644 --- a/tests/perf/runtest.js +++ b/tests/perf/runtest.js @@ -6,7 +6,6 @@ load(path + "const.js"); load(path + "io.js"); load(path + "cpu.js"); load(path + "main.js"); -load(path + "disk.js"); load(path + "pci.js"); load(path + "test_helpers.js"); load(path + "memory.js");