397 lines
9.5 KiB
JavaScript
397 lines
9.5 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
/** @constructor */
|
||
|
function FloppyController(dev, floppy_image)
|
||
|
{
|
||
|
var
|
||
|
io = dev.io,
|
||
|
pic = dev.pic,
|
||
|
dma = dev.dma,
|
||
|
|
||
|
bytes_expecting = 0,
|
||
|
receiving_command = new Uint8Array(10),
|
||
|
receiving_index = 0,
|
||
|
next_command,
|
||
|
|
||
|
response_data = new Uint8Array(10),
|
||
|
response_index = 0,
|
||
|
response_length = 0,
|
||
|
|
||
|
/** @const */
|
||
|
byte_per_sector = 512,
|
||
|
|
||
|
/** @const */
|
||
|
number_of_heads = 2;
|
||
|
|
||
|
this.buffer = floppy_image;
|
||
|
|
||
|
if(!floppy_image)
|
||
|
{
|
||
|
this.type = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var number_of_cylinders,
|
||
|
number_of_sectors,
|
||
|
sectors_per_track;
|
||
|
|
||
|
if(floppy_image.byteLength === 1024 * 360)
|
||
|
{
|
||
|
this.type = 1;
|
||
|
number_of_cylinders = 40;
|
||
|
number_of_sectors = 2880;
|
||
|
sectors_per_track = 9;
|
||
|
}
|
||
|
if(floppy_image.byteLength === 1024 * 1200)
|
||
|
{
|
||
|
this.type = 2;
|
||
|
number_of_cylinders = 80;
|
||
|
number_of_sectors = 2400;
|
||
|
sectors_per_track = 15;
|
||
|
}
|
||
|
else if(floppy_image.byteLength === 1024 * 720)
|
||
|
{
|
||
|
this.type = 3;
|
||
|
number_of_cylinders = 80;
|
||
|
number_of_sectors = 2880;
|
||
|
sectors_per_track = 9;
|
||
|
}
|
||
|
else if(floppy_image.byteLength === 1024 * 1440)
|
||
|
{
|
||
|
this.type = 4;
|
||
|
number_of_cylinders = 80;
|
||
|
number_of_sectors = 2880;
|
||
|
sectors_per_track = 18;
|
||
|
}
|
||
|
else if(floppy_image.byteLength === 1024 * 2880)
|
||
|
{
|
||
|
this.type = 5;
|
||
|
number_of_cylinders = 80;
|
||
|
number_of_sectors = 5760;
|
||
|
sectors_per_track = 36;
|
||
|
}
|
||
|
else if(floppy_image.byteLength === 1024 * 1722)
|
||
|
{
|
||
|
// type is wrong, but only this works for seabios
|
||
|
this.type = 5;
|
||
|
number_of_cylinders = 82;
|
||
|
number_of_sectors = 3444;
|
||
|
sectors_per_track = 21;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw unimpl("Unknown floppy size: " + h(floppy_image.byteLength));
|
||
|
}
|
||
|
|
||
|
var status_reg0 = 0,
|
||
|
status_reg1 = 0,
|
||
|
status_reg2 = 0,
|
||
|
drive = 0;
|
||
|
|
||
|
var last_cylinder = 0,
|
||
|
last_head = 0,
|
||
|
last_sector = 1;
|
||
|
|
||
|
io.register_read(0x3F0, port3F0_read);
|
||
|
io.register_read(0x3F4, port3F4_read);
|
||
|
io.register_read(0x3F5, port3F5_read);
|
||
|
io.register_read(0x3F7, port3F7_read);
|
||
|
|
||
|
io.register_write(0x3F5, port3F5_write);
|
||
|
|
||
|
function port3F0_read()
|
||
|
{
|
||
|
dbg_log("3F0 read", LOG_DISK);
|
||
|
|
||
|
return 0;
|
||
|
};
|
||
|
|
||
|
|
||
|
function port3F4_read()
|
||
|
{
|
||
|
dbg_log("3F4 read", LOG_DISK);
|
||
|
|
||
|
var return_byte = 0x80;
|
||
|
|
||
|
if(response_index < response_length)
|
||
|
{
|
||
|
return_byte |= 0x40 | 0x10;
|
||
|
}
|
||
|
|
||
|
if((dor & 8) === 0)
|
||
|
{
|
||
|
return_byte |= 0x20;
|
||
|
}
|
||
|
|
||
|
return return_byte;
|
||
|
};
|
||
|
|
||
|
function port3F7_read()
|
||
|
{
|
||
|
dbg_log("3F7 read", LOG_DISK);
|
||
|
return 0x00;
|
||
|
}
|
||
|
|
||
|
function port3F5_read()
|
||
|
{
|
||
|
if(response_index < response_length)
|
||
|
{
|
||
|
dbg_log("3F5 read: " + response_data[response_index], LOG_DISK);
|
||
|
return response_data[response_index++];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dbg_log("3F5 read, empty", LOG_DISK);
|
||
|
return 0xFF;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function port3F5_write(reg_byte)
|
||
|
{
|
||
|
dbg_log("3F5 write " + h(reg_byte), LOG_DISK);
|
||
|
|
||
|
if(bytes_expecting > 0)
|
||
|
{
|
||
|
receiving_command[receiving_index++] = reg_byte;
|
||
|
|
||
|
bytes_expecting--;
|
||
|
|
||
|
if(bytes_expecting === 0)
|
||
|
{
|
||
|
if(DEBUG)
|
||
|
{
|
||
|
var log = "3F5 command received: ";
|
||
|
for(var i = 0; i < receiving_index; i++)
|
||
|
log += h(receiving_command[i]) + " ";
|
||
|
dbg_log(log, LOG_DISK);
|
||
|
}
|
||
|
|
||
|
next_command(receiving_command);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch(reg_byte)
|
||
|
{
|
||
|
// TODO
|
||
|
//case 2:
|
||
|
//next_command = read_complete_track;
|
||
|
//bytes_expecting = 8;
|
||
|
//break;
|
||
|
case 0x03:
|
||
|
next_command = fix_drive_data;
|
||
|
bytes_expecting = 2;
|
||
|
break;
|
||
|
case 0x04:
|
||
|
next_command = check_drive_status;
|
||
|
bytes_expecting = 1;
|
||
|
break;
|
||
|
case 0x05:
|
||
|
case 0xC5:
|
||
|
next_command = function(args) { do_sector(true, args); };
|
||
|
bytes_expecting = 8;
|
||
|
break;
|
||
|
case 0xE6:
|
||
|
next_command = function(args) { do_sector(false, args); };
|
||
|
bytes_expecting = 8;
|
||
|
break;
|
||
|
case 0x07:
|
||
|
next_command = calibrate;
|
||
|
bytes_expecting = 1;
|
||
|
break;
|
||
|
case 0x08:
|
||
|
check_interrupt_status();
|
||
|
break;
|
||
|
case 0x4A:
|
||
|
next_command = read_sector_id;
|
||
|
bytes_expecting = 1;
|
||
|
break;
|
||
|
case 0x0F:
|
||
|
bytes_expecting = 2;
|
||
|
next_command = seek;
|
||
|
break;
|
||
|
case 0x0E:
|
||
|
// dump regs
|
||
|
dbg_log("dump registers", LOG_DISK);
|
||
|
response_data[0] = 0x80;
|
||
|
response_index = 0;
|
||
|
response_length = 1;
|
||
|
|
||
|
bytes_expecting = 0;
|
||
|
break;
|
||
|
default:
|
||
|
if(DEBUG) throw "unimpl floppy command call " + h(reg_byte);
|
||
|
}
|
||
|
|
||
|
receiving_index = 0;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
// this should actually be write-only ... but people read it anyway
|
||
|
var dor = 0;
|
||
|
|
||
|
function port3F2_read()
|
||
|
{
|
||
|
dbg_log("read 3F2: DOR", LOG_DISK);
|
||
|
return dor;
|
||
|
}
|
||
|
io.register_read(0x3F2, port3F2_read);
|
||
|
|
||
|
function port3F2_write(value)
|
||
|
{
|
||
|
if((value & 4) === 4 && (dor & 4) === 0)
|
||
|
{
|
||
|
// reset
|
||
|
pic.push_irq(6);
|
||
|
}
|
||
|
|
||
|
dbg_log("start motors: " + h(value >> 4), LOG_DISK);
|
||
|
dbg_log("enable dma: " + !!(value & 8), LOG_DISK);
|
||
|
dbg_log("reset fdc: " + !!(value & 4), LOG_DISK);
|
||
|
dbg_log("drive select: " + (value & 3), LOG_DISK);
|
||
|
dbg_log("DOR = " + h(value), LOG_DISK);
|
||
|
|
||
|
dor = value;
|
||
|
|
||
|
}
|
||
|
io.register_write(0x3F2, port3F2_write);
|
||
|
|
||
|
function check_drive_status(args)
|
||
|
{
|
||
|
dbg_log("check drive status", LOG_DISK);
|
||
|
|
||
|
response_index = 0;
|
||
|
response_length = 1;
|
||
|
response_data[0] = 1 << 5;
|
||
|
}
|
||
|
|
||
|
function seek(args)
|
||
|
{
|
||
|
dbg_log("seek", LOG_DISK);
|
||
|
|
||
|
last_cylinder = args[1];
|
||
|
last_head = args[0] >> 2 & 1;
|
||
|
|
||
|
if(dor & 8)
|
||
|
{
|
||
|
pic.push_irq(6);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function calibrate(args)
|
||
|
{
|
||
|
dbg_log("floppy calibrate", LOG_DISK);
|
||
|
|
||
|
if(dor & 8)
|
||
|
{
|
||
|
pic.push_irq(6);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function check_interrupt_status()
|
||
|
{
|
||
|
// do not trigger an interrupt here
|
||
|
dbg_log("floppy check interrupt status", LOG_DISK);
|
||
|
|
||
|
response_index = 0;
|
||
|
response_length = 2;
|
||
|
|
||
|
response_data[0] = 1 << 5;
|
||
|
response_data[1] = last_cylinder;
|
||
|
}
|
||
|
|
||
|
function do_sector(is_write, args)
|
||
|
{
|
||
|
var head = args[2],
|
||
|
cylinder = args[1],
|
||
|
sector = args[3],
|
||
|
sector_size = 128 * (1 << args[4]),
|
||
|
read_count = args[5] - args[3] + 1,
|
||
|
|
||
|
read_offset = ((head + number_of_heads * cylinder) * sectors_per_track + sector - 1) * sector_size;
|
||
|
|
||
|
dbg_log("Floppy Read", LOG_DISK);
|
||
|
dbg_log("from " + h(read_offset) + " length " + h(read_count * sector_size), LOG_DISK);
|
||
|
dbg_log(cylinder + " / " + head + " / " + sector, LOG_DISK);
|
||
|
|
||
|
if(!args[4])
|
||
|
{
|
||
|
dbg_log("FDC: sector count is zero, use data length instead", LOG_DISK);
|
||
|
}
|
||
|
|
||
|
if(is_write)
|
||
|
{
|
||
|
dma.do_write(floppy_image, read_offset, read_count * sector_size, 2, done);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dma.do_read(floppy_image, read_offset, read_count * sector_size, 2, done);
|
||
|
}
|
||
|
|
||
|
function done()
|
||
|
{
|
||
|
sector++;
|
||
|
|
||
|
if(sector > sectors_per_track)
|
||
|
{
|
||
|
sector = 1;
|
||
|
head++;
|
||
|
|
||
|
if(head > 1)
|
||
|
{
|
||
|
head = 0;
|
||
|
cylinder++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
last_cylinder = cylinder;
|
||
|
last_head = head;
|
||
|
last_sector = sector;
|
||
|
|
||
|
response_index = 0;
|
||
|
response_length = 7;
|
||
|
|
||
|
response_data[0] = head << 2 | 0x20;
|
||
|
response_data[1] = 0;
|
||
|
response_data[2] = 0;
|
||
|
response_data[3] = cylinder;
|
||
|
response_data[4] = head;
|
||
|
response_data[5] = sector;
|
||
|
response_data[6] = args[4];
|
||
|
|
||
|
if(dor & 8)
|
||
|
{
|
||
|
pic.push_irq(6);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function fix_drive_data(args)
|
||
|
{
|
||
|
dbg_log("floppy fix drive data " + args, LOG_DISK);
|
||
|
}
|
||
|
|
||
|
function read_sector_id(args)
|
||
|
{
|
||
|
dbg_log("floppy read sector id " + args, LOG_DISK);
|
||
|
|
||
|
response_index = 0;
|
||
|
response_length = 7;
|
||
|
|
||
|
response_data[0] = 0;
|
||
|
response_data[1] = 0;
|
||
|
response_data[2] = 0;
|
||
|
response_data[3] = 0;
|
||
|
response_data[4] = 0;
|
||
|
response_data[5] = 0;
|
||
|
response_data[6] = 0;
|
||
|
|
||
|
if(dor & 8)
|
||
|
{
|
||
|
pic.push_irq(6);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|