New: IDE emulation. Lots of cleanups in the ATA controller
This commit is contained in:
parent
eda0cf5995
commit
a5fb00d67e
8 changed files with 647 additions and 561 deletions
2
Makefile
2
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
545
src/disk.js
545
src/disk.js
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
633
src/ide.js
Normal file
633
src/ide.js
Normal file
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue