#include "std.h"
#include "emul.h"
#include "vars.h"
#include "util.h"
//#define DUMP_HDD_IO 1
const unsigned MAX_DEVICES = MAX_PHYS_HD_DRIVES+2*MAX_PHYS_CD_DRIVES;
PHYS_DEVICE phys[MAX_DEVICES];
unsigned n_phys = 0;
/*
// this function is untested
void ATA_DEVICE::exec_mode_select()
{
intrq = 1;
command_ok();
struct {
SCSI_PASS_THROUGH_DIRECT p;
unsigned char sense[0x40];
} srb = { 0 }, dst;
srb.p.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
*(CDB*)&srb.p.Cdb = cdb;
srb.p.CdbLength = sizeof(CDB);
srb.p.DataIn = SCSI_IOCTL_DATA_OUT;
srb.p.TimeOutValue = 10;
srb.p.DataBuffer = transbf;
srb.p.DataTransferLength = transcount;
srb.p.SenseInfoLength = sizeof(srb.sense);
srb.p.SenseInfoOffset = sizeof(SCSI_PASS_THROUGH_DIRECT);
DWORD outsize;
int r = DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT,
&srb.p, sizeof(srb.p),
&dst, sizeof(dst),
&outsize, 0);
if (!r) return;
if (senselen = dst.p.SenseInfoLength) memcpy(sense, dst.sense, senselen);
return;
}
*/
void init_hdd_cd()
{
memset(&phys, 0, sizeof phys);
if (conf.ide_skip_real)
return;
n_phys = 0;
n_phys = ATA_PASSER::identify(phys + n_phys, MAX_DEVICES - n_phys);
n_phys += ATAPI_PASSER::identify(phys + n_phys, MAX_DEVICES - n_phys);
if (!n_phys)
errmsg("HDD/CD emulator can't access physical drives");
}
static void delstr_spaces(char *dst, char *src)
{
for (; *src; src++)
if (*src != ' ') *dst++ = *src;
*dst = 0;
}
unsigned find_hdd_device(char *name)
{
char s2[512];
delstr_spaces(s2, name);
// if(temp.win9x)
for (unsigned drive = 0; drive < n_phys; drive++)
{
char s1[512];
delstr_spaces(s1, phys[drive].viewname);
if (!stricmp(s1,s2))
return drive;
}
return -1U;
}
void ATA_DEVICE::configure(IDE_CONFIG *cfg)
{
atapi_p.close(); ata_p.close();
c = cfg->c; h = cfg->h; s = cfg->s; lba = cfg->lba; readonly = cfg->readonly;
memset(®, 0, sizeof(reg)); // ╬ўш∙рхь ЁхушёЄЁ√
command_ok(); // ╤сЁрё√трхь ёюёЄю эшх ш яючшЎш■ яхЁхфрўш фрээ√ї
phys_dev = -1U;
if (!*cfg->image)
return;
PHYS_DEVICE filedev, *dev;
phys_dev = find_hdd_device(cfg->image);
if (phys_dev == -1U)
{
if (cfg->image[0] == '<')
{
errmsg("no physical device %s", cfg->image);
*cfg->image = 0;
return;
}
strcpy(filedev.filename, cfg->image);
filedev.type = cfg->cd ? ATA_FILECD : ATA_FILEHDD;
dev = &filedev;
}
else
{
dev = &phys[phys_dev];
if (dev->type == ATA_NTHDD)
{
// read geometry from id sector
c = *(unsigned short*)(phys[phys_dev].idsector+2);
h = *(unsigned short*)(phys[phys_dev].idsector+6);
s = *(unsigned short*)(phys[phys_dev].idsector+12);
lba = *(unsigned*)(phys[phys_dev].idsector+0x78); // lba28
if(*((u16*)(phys[phys_dev].idsector+83*2)) & (1<<10))
{
lba = *(u64*)(phys[phys_dev].idsector+100*2); // lba48
}
if (!lba)
lba = c*h*s;
}
}
DWORD errcode = ERROR_DEVICE_NOT_AVAILABLE;
if (dev->type == ATA_NTHDD || dev->type == ATA_FILEHDD)
{
dev->usage = ATA_OP_USE;
errcode = ata_p.open(dev);
atapi = 0;
}
if (dev->type == ATA_SPTI_CD || dev->type == ATA_ASPI_CD || dev->type == ATA_FILECD)
{
dev->usage = ATA_OP_USE;
errcode = atapi_p.open(dev);
atapi = 1;
}
if (errcode == NO_ERROR)
return;
errmsg("failed to open %s", cfg->image);
err_win32(errcode);
*cfg->image = 0;
}
void ATA_PORT::reset()
{
dev[0].reset(ATA_DEVICE::RESET_HARD);
dev[1].reset(ATA_DEVICE::RESET_HARD);
}
unsigned char ATA_PORT::read(unsigned n_reg)
{
u8 val1 = dev[0].read(n_reg);
u8 val2 = dev[1].read(n_reg);
unsigned devs = 0;
devs |= (dev[0].loaded() ? 1 : 0);
devs |= (dev[1].loaded() ? 2 : 0);
u8 val = 0xFF;
switch(devs)
{
case 1: val = val1; break;
case 2: val = val2; break;
case 3: val = dev[0].selected() ? val1 : val2; break;
}
#ifdef DUMP_HDD_IO
printf("R%X:%02X ", n_reg, val);
#endif
return val;
}
unsigned ATA_PORT::read_data()
{
#ifdef DUMP_HDD_IO
unsigned val = dev[0].read_data() & dev[1].read_data();
printf("r%04X ", val & 0xFFFF);
return val;
#endif
return dev[0].read_data() & dev[1].read_data();
}
void ATA_PORT::write(unsigned n_reg, unsigned char data)
{
#ifdef DUMP_HDD_IO
printf("R%X=%02X ", n_reg, data);
#endif
dev[0].write(n_reg, data);
dev[1].write(n_reg, data);
}
void ATA_PORT::write_data(unsigned data)
{
#ifdef DUMP_HDD_IO
printf("w%04X ", data & 0xFFFF);
#endif
dev[0].write_data(data);
dev[1].write_data(data);
}
unsigned char ATA_PORT::read_intrq()
{
#ifdef DUMP_HDD_IO
unsigned char i = dev[0].read_intrq() & dev[1].read_intrq(); printf("i%d ", !!i); return i;
#endif
return dev[0].read_intrq() & dev[1].read_intrq();
}
void ATA_DEVICE::reset_signature(RESET_TYPE mode)
{
reg.count = reg.sec = 1;
reg.err = 1;
reg.cyl = atapi ? 0xEB14 : 0;
reg.devhead |= 0x50;
reg.devhead &= (atapi && mode == RESET_SOFT) ? 0x10 : 0;
reg.status = (mode == RESET_SOFT || !atapi) ? STATUS_DRDY | STATUS_DSC : 0;
}
void ATA_DEVICE::reset(RESET_TYPE mode)
{
reg.control = 0; // clear SRST
intrq = 0;
regs_sel = 0;
command_ok();
reset_signature(mode);
}
void ATA_DEVICE::command_ok()
{
state = S_IDLE;
transptr = -1U;
reg.err = 0;
reg.status = STATUS_DRDY | STATUS_DSC;
}
unsigned char ATA_DEVICE::read_intrq()
{
if (!loaded() || ((reg.devhead ^ device_id) & 0x10) || (reg.control & CONTROL_nIEN)) return 0xFF;
return intrq? 0xFF : 0x00;
}
unsigned char ATA_DEVICE::read(unsigned n_reg)
{
if (!loaded())
return 0xFF;
/*
if ((reg.devhead ^ device_id) & 0x10)
{
return 0xFF;
}
*/
if (n_reg == 7)
intrq = 0;
if (n_reg == 8)
n_reg = 7; // read alt.status -> read status
if ((n_reg == 7) && ((reg.devhead ^ device_id) & 0x10))
{
return 0;
}
if (n_reg == 7 || (reg.status & STATUS_BSY))
{
// printf("state=%d\n",state); //Alone Coder
return reg.status;
} // BSY=1 or read status
// BSY = 0
//// if (reg.status & STATUS_DRQ) return 0xFF; // DRQ. ATA-5: registers should not be queried while DRQ=1, but programs do this!
update_regs();
// DRQ = 0
unsigned sel = regs_sel;
if(lba > 0xFFFFFFFULL)
{ // lba48
sel ^= (reg.control & CONTROL_HOB) ? 1 : 0;
}
return *regs_r[sel][n_reg];
}
unsigned ATA_DEVICE::read_data()
{
if (!loaded())
return 0xFFFFFFFF;
if ((reg.devhead ^ device_id) & 0x10)
return 0xFFFFFFFF;
if (/* (reg.status & (STATUS_DRQ | STATUS_BSY)) != STATUS_DRQ ||*/ transptr >= transcount)
return 0xFFFFFFFF;
// DRQ=1, BSY=0, data present
unsigned result = *(unsigned*)(transbf + transptr*2);
transptr++;
// printf(__FUNCTION__" data=0x%04X\n", result & 0xFFFF);
if (transptr < transcount)
return result;
// look to state, prepare next block
if (state == S_READ_ID || state == S_READ_ATAPI)
command_ok();
if (state == S_READ_SECTORS)
{
// __debugbreak();
// printf("dev=%d, cnt=%d\n", device_id, reg.count);
if(!--reg.count)
command_ok();
else
{
next_sector();
read_sectors();
}
}
return result;
}
char ATA_DEVICE::exec_ata_cmd(unsigned char cmd)
{
// printf(__FUNCTION__" cmd=%02X\n", cmd);
// EXECUTE DEVICE DIAGNOSTIC for both ATA and ATAPI
if (cmd == 0x90)
{
reset_signature(RESET_SOFT);
return 1;
}
if (atapi)
return 0;
// INITIALIZE DEVICE PARAMETERS
if (cmd == 0x91)
{
// pos = (reg.cyl * h + (reg.devhead & 0x0F)) * s + reg.sec - 1;
h = (reg.devhead & 0xF) + 1;
s = reg.count;
if(s == 0)
{
reg.status = STATUS_DRDY | STATUS_DF | STATUS_DSC | STATUS_ERR;
return 1;
}
c = unsigned(lba / s / h);
reg.status = STATUS_DRDY | STATUS_DSC;
return 1;
}
if ((cmd & 0xFE) == 0x20) // ATA-3 (mandatory), read sectors (20-w-retr/21-wo-retr)
{ // cmd #21 obsolette, rqd for is-dos
// printf(__FUNCTION__" sec_cnt=%d\n", reg.count);
// __debugbreak();
read_sectors();
return 1;
}
if ((cmd == 0x24) && (lba > 0xFFFFFFFULL)) // ATA-6 read sectors ext (lba48)
{
read_sectors();
return 1;
}
if((cmd & 0xFE) == 0x40) // ATA-3 (mandatory), verify sectors
{ //rqd for is-dos
verify_sectors();
return 1;
}
if ((cmd == 0x42) && (lba > 0xFFFFFFFULL)) // ATA-6 verify sectors ext (lba48)
{
verify_sectors();
return 1;
}
if ((cmd & 0xFE) == 0x30 && !readonly) // ATA-3 (mandatory), write sectors (30-w-retr,31-wo-retr)
{
if (seek())
{
state = S_WRITE_SECTORS;
reg.status = STATUS_DRQ | STATUS_DSC;
transptr = 0;
transcount = 0x100;
}
return 1;
}
if ((cmd == 0x34) && (lba > 0xFFFFFFFULL) && !readonly) // ATA-6 write sectors ext (lba48)
{
if (seek())
{
state = S_WRITE_SECTORS;
reg.status = STATUS_DRQ | STATUS_DSC;
transptr = 0;
transcount = 0x100;
}
return 1;
}
if(cmd == 0x50) // format track (фрээр ЁхрышчрЎш - эшўхую эх фхырхЄ)
{
reg.sec = 1;
if (seek())
{
state = S_FORMAT_TRACK;
reg.status = STATUS_DRQ | STATUS_DSC;
transptr = 0;
transcount = 0x100;
}
return 1;
}
if (cmd == 0xEC)
{
prepare_id();
return 1;
}
if (cmd == 0xE7)
{ // FLUSH CACHE
if (ata_p.flush())
{
command_ok();
intrq = 1;
}
else
reg.status = STATUS_DRDY | STATUS_DF | STATUS_DSC | STATUS_ERR; // 0x71
return 1;
}
if (cmd == 0x10)
{
recalibrate();
command_ok();
intrq = 1;
return 1;
}
if (cmd == 0x70)
{ // seek
if (!seek())
return 1;
command_ok();
intrq = 1;
return 1;
}
printf("*** unknown ata cmd %02X ***\n", cmd);
return 0;
}
char ATA_DEVICE::exec_atapi_cmd(unsigned char cmd)
{
if (!atapi)
return 0;
// soft reset
if (cmd == 0x08)
{
reset(RESET_SOFT);
return 1;
}
if (cmd == 0xA1) // IDENTIFY PACKET DEVICE
{
prepare_id();
return 1;
}
if (cmd == 0xA0)
{ // packet
state = S_RECV_PACKET;
reg.status = STATUS_DRQ;
reg.intreason = INT_COD;
transptr = 0;
transcount = 6;
return 1;
}
if (cmd == 0xEC)
{
reg.count = 1;
reg.sec = 1;
reg.cyl = 0xEB14;
reg.status = STATUS_DSC | STATUS_DRDY | STATUS_ERR;
reg.err = ERR_ABRT;
state = S_IDLE;
intrq = 1;
return 1;
}
printf("*** unknown atapi cmd %02X ***\n", cmd);
// "command aborted" with ATAPI signature
reg.count = 1;
reg.sec = 1;
reg.cyl = 0xEB14;
return 0;
}
void ATA_DEVICE::write(unsigned n_reg, unsigned char data)
{
// printf("dev=%d, reg=%d, data=%02X\n", device_id, n_reg, data);
if (!loaded())
return;
reg.control &= ~CONTROL_HOB;
if (n_reg == 1)
{
reg.feat = data;
return;
}
if (n_reg != 7) // ═х ЁхушёЄЁ ъюьрэф
{
*regs_w[regs_sel][n_reg] = data;
regs_sel ^= (lba > 0xFFFFFFFULL) ? 1 : 0;
update_cur();
if (reg.control & CONTROL_SRST)
{
// printf("dev=%d, reset\n", device_id);
reset(RESET_SRST);
}
return;
}
// execute command!
if (((reg.devhead ^ device_id) & 0x10) && data != 0x90)
return;
if (!(reg.status & STATUS_DRDY) && !atapi)
{
printf("warning: hdd not ready cmd = %02X (ignored)\n", data);
return;
}
reg.err = 0; intrq = 0;
//{printf(" [");for (int q=1;q<9;q++) printf("-%02X",regs[q]);printf("]\n");}
if (exec_atapi_cmd(data))
return;
if (exec_ata_cmd(data))
return;
reg.status = STATUS_DSC | STATUS_DRDY | STATUS_ERR;
reg.err = ERR_ABRT;
state = S_IDLE; intrq = 1;
}
void ATA_DEVICE::write_data(unsigned data)
{
if (!loaded()) return;
if ((reg.devhead ^ device_id) & 0x10)
return;
if (/* (reg.status & (STATUS_DRQ | STATUS_BSY)) != STATUS_DRQ ||*/ transptr >= transcount)
return;
*(unsigned short*)(transbf + transptr*2) = (unsigned short)data; transptr++;
if (transptr < transcount)
return;
// look to state, prepare next block
if (state == S_WRITE_SECTORS)
{
write_sectors();
return;
}
if (state == S_FORMAT_TRACK)
{
format_track();
return;
}
if (state == S_RECV_PACKET)
{
handle_atapi_packet();
return;
}
/* if (state == S_MODE_SELECT) { exec_mode_select(); return; } */
}
char ATA_DEVICE::seek()
{
u64 pos;
if (reg.devhead & 0x40)
{
pos = lba_cur;
if (lba_cur >= lba)
{
// printf("seek error: lba %I64u:%I64u\n", lba, pos);
seek_err:
reg.status = STATUS_DRDY | STATUS_DF | STATUS_ERR;
reg.err = ERR_IDNF | ERR_ABRT;
intrq = 1;
return 0;
}
// printf("lba %I64u:%I64u\n", lba, pos);
}
else
{
if (c_cur >= c || h_cur >= h || s_cur > s || s_cur == 0)
{
// printf("seek error: chs %4d/%02d/%02d\n", c_cur, h_cur, s_cur);
goto seek_err;
}
pos = (c_cur * h + h_cur) * s + s_cur - 1;
// printf("chs %4d/%02d/%02d: %I64u\n", c_cur, h_cur, s_cur, pos);
}
//printf("[seek %I64u]", pos << 9);
if (!ata_p.seek(pos))
{
reg.status = STATUS_DRDY | STATUS_DF | STATUS_ERR;
reg.err = ERR_IDNF | ERR_ABRT;
intrq = 1;
return 0;
}
return 1;
}
void ATA_DEVICE::format_track()
{
intrq = 1;
if(!seek())
return;
command_ok();
return;
}
void ATA_DEVICE::write_sectors()
{
intrq = 1;
//printf(" [write] ");
if(!seek())
return;
if (!ata_p.write_sector(transbf))
{
reg.status = STATUS_DRDY | STATUS_DSC | STATUS_ERR;
reg.err = ERR_UNC;
state = S_IDLE;
return;
}
if (!--reg.count)
{
command_ok();
return;
}
next_sector();
transptr = 0; transcount = 0x100;
state = S_WRITE_SECTORS;
reg.err = 0;
reg.status = STATUS_DRQ | STATUS_DSC;
}
void ATA_DEVICE::read_sectors()
{
// __debugbreak();
intrq = 1;
if (!seek())
return;
if (!ata_p.read_sector(transbf))
{
reg.status = STATUS_DRDY | STATUS_DSC | STATUS_ERR;
reg.err = ERR_UNC | ERR_IDNF;
state = S_IDLE;
return;
}
transptr = 0;
transcount = 0x100;
state = S_READ_SECTORS;
reg.err = 0;
reg.status = STATUS_DRDY | STATUS_DRQ | STATUS_DSC;
/*
if(reg.devhead & 0x40)
printf("dev=%d lba=%d\n", device_id, *(unsigned*)(regs+3) & 0x0FFFFFFF);
else
printf("dev=%d c/h/s=%d/%d/%d\n", device_id, reg.cyl, (reg.devhead & 0xF), reg.sec);
*/
}
void ATA_DEVICE::verify_sectors()
{
intrq = 1;
// __debugbreak();
do
{
--n_cur;
/*
if(reg.devhead & 0x40)
printf("lba=%d\n", *(unsigned*)(regs+3) & 0x0FFFFFFF);
else
printf("c/h/s=%d/%d/%d\n", reg.cyl, (reg.devhead & 0xF), reg.sec);
*/
if (!seek())
return;
/*
u8 Buf[512];
if (!ata_p.read_sector(Buf))
{
reg.status = STATUS_DRDY | STATUS_DF | STATUS_CORR | STATUS_DSC | STATUS_ERR;
reg.err = ERR_UNC | ERR_IDNF | ERR_ABRT | ERR_AMNF;
state = S_IDLE;
return;
}
*/
if(n_cur)
next_sector();
}while(n_cur);
command_ok();
}
void ATA_DEVICE::next_sector()
{
if (reg.devhead & 0x40)
{ // LBA
lba_cur++;
return;
}
// need to recalc CHS for every sector, coz ATA registers
// should contain current position on failure
if (s_cur < s)
{
s_cur++;
return;
}
s_cur = 1;
if (++h_cur < h)
{
return;
}
h_cur = 0;
c_cur++;
}
void ATA_DEVICE::recalibrate()
{
lba_cur = 0;
c_cur = 0;
h_cur = 0;
s_cur = 1;
reg.cyl = 0;
reg.devhead &= 0xF0;
if (reg.devhead & 0x40) // LBA
{
reg.sec = 0;
return;
}
reg.sec = 1;
}
#define TOC_DATA_TRACK 0x04
// [vv] ╨рсюЄр ё Їрщыюь - юсЁрчюь фшёър эряЁ ьє■
void ATA_DEVICE::handle_atapi_packet_emulate()
{
// printf("%s\n", __FUNCTION__);
memcpy(&atapi_p.cdb, transbf, 12);
switch(atapi_p.cdb.CDB12.OperationCode)
{
case SCSIOP_TEST_UNIT_READY:; // 6
command_ok();
return;
case SCSIOP_READ:; // 10
{
unsigned cnt = (u32(atapi_p.cdb.CDB10.TransferBlocksMsb) << 8) | atapi_p.cdb.CDB10.TransferBlocksLsb;
unsigned pos = (u32(atapi_p.cdb.CDB10.LogicalBlockByte0) << 24) |
(u32(atapi_p.cdb.CDB10.LogicalBlockByte1) << 16) |
(u32(atapi_p.cdb.CDB10.LogicalBlockByte2) << 8) |
atapi_p.cdb.CDB10.LogicalBlockByte3;
if(cnt * 2048 > sizeof(transbf))
{
reg.status = STATUS_DRDY | STATUS_DSC | STATUS_ERR;
reg.err = ERR_UNC | ERR_IDNF;
state = S_IDLE;
return;
}
for(unsigned i = 0; i < cnt; i++, pos++)
{
if (!atapi_p.seek(pos))
{
reg.status = STATUS_DRDY | STATUS_DSC | STATUS_ERR;
reg.err = ERR_UNC | ERR_IDNF;
state = S_IDLE;
return;
}
if (!atapi_p.read_sector(transbf + i * 2048))
{
reg.status = STATUS_DRDY | STATUS_DSC | STATUS_ERR;
reg.err = ERR_UNC | ERR_IDNF;
state = S_IDLE;
return;
}
}
intrq = 1;
reg.atapi_count = u16(cnt * 2048);
reg.intreason = INT_IO;
reg.status = STATUS_DRQ;
transcount = (cnt * 2048)/2;
transptr = 0;
state = S_READ_ATAPI;
return;
}
case SCSIOP_READ_TOC:; // 10
{
static const u8 TOC_DATA[] =
{
0, 4+8*2 - 2, 1, 0xAA,
0, TOC_DATA_TRACK, 1, 0, 0, 0, 0, 0,
0, TOC_DATA_TRACK, 0xAA, 0, 0, 0, 0, 0,
};
unsigned len = sizeof(TOC_DATA);
memcpy(transbf, TOC_DATA, len);
reg.atapi_count = u16(len);
reg.intreason = INT_IO;
reg.status = STATUS_DRQ;
transcount = (len + 1)/2;
transptr = 0;
state = S_READ_ATAPI;
return;
}
case SCSIOP_START_STOP_UNIT:; // 10
command_ok();
return;
case SCSIOP_SET_CD_SPEED:; // 12
command_ok();
return;
}
printf("*** unknown scsi cmd %02X ***\n", atapi_p.cdb.CDB12.OperationCode);
reg.err = 0;
state = S_IDLE;
reg.status = STATUS_DSC | STATUS_ERR | STATUS_DRDY;
}
void ATA_DEVICE::handle_atapi_packet()
{
#if defined(DUMP_HDD_IO)
{
printf(" [packet");
for (int i = 0; i < 12; i++)
printf("-%02X", transbf[i]);
printf("]\n");
}
#endif
if(phys_dev == -1U)
return handle_atapi_packet_emulate();
memcpy(&atapi_p.cdb, transbf, 12);
intrq = 1;
if (atapi_p.cdb.MODE_SELECT10.OperationCode == 0x55)
{ // MODE SELECT requires additional data from host
state = S_MODE_SELECT;
reg.status = STATUS_DRQ;
reg.intreason = 0;
transptr = 0;
transcount = atapi_p.cdb.MODE_SELECT10.ParameterListLength[0]*0x100 + atapi_p.cdb.MODE_SELECT10.ParameterListLength[1];
return;
}
if (atapi_p.cdb.CDB6READWRITE.OperationCode == 0x03 && atapi_p.senselen)
{ // REQ.SENSE - read cached
memcpy(transbf, atapi_p.sense, atapi_p.senselen);
atapi_p.passed_length = atapi_p.senselen; atapi_p.senselen = 0; // next time read from device
goto ok;
}
if (atapi_p.pass_through(transbf, sizeof transbf))
{
if (atapi_p.senselen)
{
reg.err = u8(atapi_p.sense[2] << 4);
goto err;
} // err = sense key //win9x hangs on drq after atapi packet when emulator does goto err (see walkaround in SEND_ASPI_CMD)
ok:
if (!atapi_p.cdb.CDB6READWRITE.OperationCode)
atapi_p.passed_length = 0; // bugfix in cdrom driver: TEST UNIT READY has no data
if (!atapi_p.passed_length /* || atapi_p.passed_length == sizeof transbf */ )
{
command_ok();
return;
}
reg.atapi_count = u16(atapi_p.passed_length);
reg.intreason = INT_IO;
reg.status = STATUS_DRQ;
transcount = (atapi_p.passed_length+1)/2;
//printf("transcount=%d\n",transcount); //32768 in win9x
transptr = 0;
state = S_READ_ATAPI;
}
else
{ // bus error
reg.err = 0;
err:
state = S_IDLE;
reg.status = STATUS_DSC | STATUS_ERR | STATUS_DRDY;
}
}
void ATA_DEVICE::prepare_id()
{
if (phys_dev == -1U)
{
memset(transbf, 0, 512);
make_ata_string(transbf+54, 20, "UNREAL SPECCY HARD DRIVE IMAGE");
make_ata_string(transbf+20, 10, "0000");
make_ata_string(transbf+46, 4, VERS_STRING);
*(unsigned short*)transbf = 0x045A;
((unsigned short*)transbf)[1] = (unsigned short)c;
((unsigned short*)transbf)[3] = (unsigned short)h;
((unsigned short*)transbf)[6] = (unsigned short)s;
*(unsigned*)(transbf+60*2) = (lba > 0xFFFFFFFULL) ? 0xFFFFFFF : unsigned(lba); // lba28
((unsigned short*)transbf)[20] = 3; // a dual ported multi-sector buffer capable of simultaneous transfers with a read caching capability
((unsigned short*)transbf)[21] = 512; // cache size=256k
((unsigned short*)transbf)[22] = 4; // ECC bytes
((unsigned short*)transbf)[49] = 0x200; // LBA supported
((unsigned short*)transbf)[80] = 0x3E; // support specifications up to ATA-5
((unsigned short*)transbf)[81] = 0x13; // ATA/ATAPI-5 T13 1321D revision 3
((unsigned short*)transbf)[82] = 0x60; // supported look-ahead and write cache
if(lba > 0xFFFFFFFULL)
{
((unsigned short*)transbf)[83] = 0x400; // lba48 supported
((unsigned short*)transbf)[86] = 0x400; // lba48 supported
*(u64*)(transbf+100*2) = lba; // lba48
}
// make checksum
transbf[510] = 0xA5;
unsigned char cs = 0;
for (unsigned i = 0; i < 511; i++)
cs += transbf[i];
transbf[511] = 0-cs;
}
else
{ // copy as is...
memcpy(transbf, phys[phys_dev].idsector, 512);
}
state = S_READ_ID;
transptr = 0;
transcount = 0x100;
intrq = 1;
reg.status = STATUS_DRDY | STATUS_DRQ | STATUS_DSC;
reg.err = 0;
}
void ATA_DEVICE::update_regs()
{
if(reg.devhead & 0x40)
{ // lba
if(lba > 0xFFFFFFFULL)
{ // lba48
reg.lba0 = lba_cur & 0xFF;
reg.lba1 = (lba_cur >> 8) & 0xFF;
reg.lba2 = (lba_cur >> 16) & 0xFF;
reg.lba4 = (lba_cur >> 24) & 0xFF;
reg.lba5 = (lba_cur >> 32) & 0xFF;
reg.lba6 = (lba_cur >> 40) & 0xFF;
}
else
{ // lba28
reg.lba0 = lba_cur & 0xFF;
reg.lba1 = (lba_cur >> 8) & 0xFF;
reg.lba2 = (lba_cur >> 16) & 0xFF;
reg.lba3 &= ~0xF;
reg.lba3 |= (lba_cur >> 24) & 0xF;
}
}
else
{ // chs
reg.cyl = u16(c_cur);
reg.devhead &= ~0xF;
reg.devhead |= h_cur & 0xF;
reg.sec = u8(s_cur);
}
}
void ATA_DEVICE::update_cur()
{
n_cur = reg.count;
if(lba > 0xFFFFFFFULL)
{ // lba48
lba_cur = reg.lba0 | (u64(reg.lba1) << 8) | (u64(reg.lba2) << 16) | (u64(reg.lba4) << 24) | (u64(reg.lba5) << 32) | (u64(reg.lba6) << 40);
n_cur |= unsigned(reg.count1 << 8);
}
else
{ // lba28
lba_cur = reg.lba0 | (u64(reg.lba1) << 8) | (u64(reg.lba2) << 16) | (u64(reg.lba3 & 0xF) << 24);
}
c_cur = reg.cyl;
h_cur = reg.devhead & 0xF;
s_cur = reg.sec;
}