New: IDE emulation. Lots of cleanups in the ATA controller

This commit is contained in:
copy 2013-11-26 21:58:12 +01:00
commit a5fb00d67e
8 changed files with 647 additions and 561 deletions

View file

@ -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

View file

@ -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?

View file

@ -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()

View file

@ -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);

View file

@ -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
View 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);
}
});
}

View file

@ -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");

View file

@ -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");