ScriptedAmigaEmulator/sae/ide.js
Rupert Hausberger fbf36a4fa5 update 0.9.0
2016-07-15 19:44:16 +02:00

1499 lines
44 KiB
JavaScript

/*-------------------------------------------------------------------------
| SAE - Scripted Amiga Emulator
| https://github.com/naTmeg/ScriptedAmigaEmulator
|
| Copyright (C) 2012-2016 Rupert Hausberger
|
| This program is free software; you can redistribute it and/or
| modify it under the terms of the GNU General Public License
| as published by the Free Software Foundation; either version 2
| of the License, or (at your option) any later version.
|
| This program is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU General Public License for more details.
|
| Note: ported from WinUAE 3.2.x
-------------------------------------------------------------------------*/
function SAEO_IDE_threadState() { //ide_thread_state
this.idetable = null; //**
this.idetotal = 0;
this.state = 0; //volatile
this.requests = new smp_comm_pipe();
};
/*---------------------------------*/
/*#define IDE_MEMORY_FUNCTIONS(x, y, z) \
static void REGPARAM2 x ## _put8(uaecptr addr, uae_u32 b) \
{ \
y ## _write_byte(z, addr, b); \
} \
static void REGPARAM2 x ## _put16(uaecptr addr, uae_u32 b) \
{ \
y ## _write_word(z, addr, b); \
} \
static void REGPARAM2 x ## _put32(uaecptr addr, uae_u32 b) \
{ \
y ## _write_word(z, addr, b >> 16); \
y ## _write_word(z, addr + 2, b); \
} \
static uae_u32 REGPARAM2 x ## _get8(uaecptr addr) \
{ \
return y ## _read_byte(z, addr); \
} \
static uae_u32 REGPARAM2 x ## _get16(uaecptr addr) \
{ \
return y ## _read_word(z, addr); \
} \
static uae_u32 REGPARAM2 x ## _get32(uaecptr addr) \
{ \
uae_u32 v = y ## _read_word(z, addr) << 16; \
v |= y ## _read_word(z, addr + 2); \
return v; \
}*/
function SAEO_IDE() {
const IDE_LOG = 0; //0-3
const IDE_DATA = 0x00;
const IDE_ERROR = 0x01; /* see err-bits */
const IDE_NSECTOR = 0x02; /* sector count, nr of sectors to read/write */
const IDE_SECTOR = 0x03; /* starting sector */
const IDE_LCYL = 0x04; /* starting cylinder */
const IDE_HCYL = 0x05; /* high byte of starting cyl */
const IDE_SELECT = 0x06; /* 101dhhhh , d=drive, hhhh=head */
const IDE_STATUS = 0x07; /* see status-bits */
const IDE_SECONDARY = 0x0400;
const IDE_DEVCON = 0x0406;
const IDE_DRVADDR = 0x0407;
/* STATUS bits */
const IDE_STATUS_ERR = 0x01; // 0
const IDE_STATUS_IDX = 0x02; // 1
const IDE_STATUS_DRQ = 0x08; // 3
const IDE_STATUS_DSC = 0x10; // 4
const IDE_STATUS_DRDY = 0x40;// 6
const IDE_STATUS_BSY = 0x80; // 7
//const ATAPI_STATUS_CHK = IDE_STATUS_ERR;
/* ERROR bits */
const IDE_ERR_UNC = 0x40;
const IDE_ERR_MC = 0x20;
const IDE_ERR_IDNF = 0x10;
const IDE_ERR_MCR = 0x08;
const IDE_ERR_ABRT = 0x04;
const IDE_ERR_NM = 0x02;
const ATAPI_ERR_EOM = 0x02;
const ATAPI_ERR_ILI = 0x01;
/* ATAPI interrupt reason (Sector Count) */
/*const ATAPI_IO = 0x02;
const ATAPI_CD = 0x01;
const ATAPI_MAX_TRANSFER = 32768;*/
const MAX_IDE_MULTIPLE_SECTORS = 128;
function ide_registers() { //all u8
this.ide_select = 0;
this.ide_nsector = 0;
this.ide_sector = 0;
this.ide_lcyl = 0;
this.ide_hcyl = 0;
this.ide_devcon = 0;
this.ide_error = 0;
this.ide_feat = 0;
this.ide_nsector2 = 0;
this.ide_sector2 = 0;
this.ide_lcyl2 = 0;
this.ide_hcyl2 = 0;
this.ide_feat2 = 0;
this.ide_status = 0;
};
/*const MAX_IDE_PORTS_BOARD = 2;
function ide_board() {
uae_u8 *rom;
uae_u8 acmemory[128];
int rom_size;
int rom_start;
int rom_mask;
uaecptr baseaddress;
int configured;
bool keepautoconfig;
int mask;
addrbank *bank;
struct ide_hdf *ide[MAX_IDE_PORTS_BOARD];
bool irq;
bool intena;
bool enabled;
int state;
int type;
int userdata;
int subtype;
uae_u16 data_latch;
struct romconfig *rc, *original_rc;
struct ide_board **self_ptr;
};*/
function ide_hdf() {
this.hdhfd = new SAEO_Hardfile_Data_HD();
//struct ide_board *board;
this.regs = new ide_registers();
this.regs0 = null;
this.regs1 = null;
this.pair = null; // master<>slave
this.its = null; //new SAEO_IDE_threadState()
this.byteswap = false;
this.byteswapped_buffer = 0;
this.adide = false;
this.secbuf = null; //u8 *
this.secbuf_size = 0;
this.buffer_offset = 0;
this.data_offset = 0;
this.data_size = 0;
this.data_multi = 0;
this.direction = 0; // 0 = read, 1 = write
this.intdrq = false;
this.lba48 = false;
this.lba48cmd = false;
this.start_lba = 0; //u64
this.start_nsec = 0;
this.multiple_mode = 0; //u8
this.irq_delay = 0;
this.irq = 0;
this.irq_new = false;
this.num = 0;
this.blocksize = 0;
this.maxtransferstate = 0;
this.ata_level = 0;
this.ide_drv = 0;
this.media_type = 0;
this.mode_8bit = false;
this.atapi = false;
this.atapi_drdy = false;
this.cd_unit_num = 0;
this.packet_state = 0;
this.packet_data_size = 0;
this.packet_data_offset = 0;
this.packet_transfer_size = 0;
//struct scsi_data *scsi;*/
};
/*-----------------------------------------------------------------------*/
this.adide_decode_word = function(w) {
var o = 0;
if (w & 0x8000) o |= 0x0001;
if (w & 0x0001) o |= 0x0002;
if (w & 0x4000) o |= 0x0004;
if (w & 0x0002) o |= 0x0008;
if (w & 0x2000) o |= 0x0010;
if (w & 0x0004) o |= 0x0020;
if (w & 0x1000) o |= 0x0040;
if (w & 0x0008) o |= 0x0080;
if (w & 0x0800) o |= 0x0100;
if (w & 0x0010) o |= 0x0200;
if (w & 0x0400) o |= 0x0400;
if (w & 0x0020) o |= 0x0800;
if (w & 0x0200) o |= 0x1000;
if (w & 0x0040) o |= 0x2000;
if (w & 0x0100) o |= 0x4000;
if (w & 0x0080) o |= 0x8000;
return o;
}
this.adide_encode_word = function(w) {
var o = 0;
if (w & 0x0001) o |= 0x8000;
if (w & 0x0002) o |= 0x0001;
if (w & 0x0004) o |= 0x4000;
if (w & 0x0008) o |= 0x0002;
if (w & 0x0010) o |= 0x2000;
if (w & 0x0020) o |= 0x0004;
if (w & 0x0040) o |= 0x1000;
if (w & 0x0080) o |= 0x0008;
if (w & 0x0100) o |= 0x0800;
if (w & 0x0200) o |= 0x0010;
if (w & 0x0400) o |= 0x0400;
if (w & 0x0800) o |= 0x0020;
if (w & 0x1000) o |= 0x0200;
if (w & 0x2000) o |= 0x0040;
if (w & 0x4000) o |= 0x0100;
if (w & 0x8000) o |= 0x0080;
return o;
}
function pw(ide, offset, w) {
if (ide.byteswap)
w = ((w << 8) & 0xffff) | (w >> 8);
if (ide.adide)
w = SAER.ide.adide_decode_word(w);
ide.secbuf[offset * 2 + 0] = w & 0xff;
ide.secbuf[offset * 2 + 1] = w >> 8;
}
function ps(ide, offset, src, max) {
var s = src; //ua(src);
var len = s.length; //strlen(s);
for (var i = 0; i < max; i += 2) {
var c1 = ' ';
if (i < len)
c1 = s.charCodeAt(i);
var c2 = ' ';
if (i + 1 < len)
c2 = s.charCodeAt(i + 1);
var w = (c2 << 8) | c1;
if (ide.byteswap)
w = ((w << 8) & 0xffff) | (w >> 8);
if (ide.adide)
w = SAER.ide.adide_decode_word(w);
ide.secbuf[offset * 2 + 0] = w >> 8;
ide.secbuf[offset * 2 + 1] = w & 0xff;
offset++;
}
//xfree(s);
}
this.ide_isdrive = function(ide) {
return ide !== null && (ide.hdhfd.size != 0 || ide.atapi);
}
function ide_grow_buffer(ide, newsize) {
if (ide.secbuf_size >= newsize)
return;
var oldbuf = ide.secbuf;
var oldsize = ide.secbuf_size;
ide.secbuf_size = newsize + 16384;
ide.secbuf = new Uint8Array(ide.secbuf_size);
if (oldsize) {
ide.secbuf.set(oldbuf); //memcpy(ide->secbuf, oldbuf, oldsize);
SAEF_log("ide.ide_grow_buffer() IDE%d buffer %d -> %d", ide.num, oldsize, ide.secbuf_size);
}
}
function ide_interrupt_do(ide) {
var os = ide.regs.ide_status;
ide.regs.ide_status &= ~IDE_STATUS_DRQ;
if (ide.intdrq)
ide.regs.ide_status |= IDE_STATUS_DRQ;
ide.regs.ide_status &= ~IDE_STATUS_BSY;
if (IDE_LOG > 1)
SAEF_log("ide.ide_interrupt_do() INT %02X -> %02X", os, ide.regs.ide_status);
ide.intdrq = false;
ide.irq_delay = 0;
if (ide.regs.ide_devcon & 2)
return false;
ide.irq_new = true;
ide.irq = 1;
return true;
}
/*bool ide_drq_check(struct ide_hdf *idep) {
for (int i = 0; idep && i < 2; i++) {
struct ide_hdf *ide = i == 0 ? idep : idep->pair;
if (ide) {
if (ide->regs.ide_status & IDE_STATUS_DRQ)
return true;
}
}
return false;
}
bool ide_irq_check(struct ide_hdf *idep, bool edge_triggered) {
for (int i = 0; idep && i < 2; i++) {
struct ide_hdf *ide = i == 0 ? idep : idep->pair;
if (ide->irq) {
if (edge_triggered) {
if (ide->irq_new) {
ide->irq_new = false;
return true;
}
continue;
}
return true;
}
}
return false;
}*/
this.ide_interrupt_hsync = function(idep) {
var irq = false;
for (var i = 0; idep && i < 2; i++) {
var ide = i == 0 ? idep : idep.pair;
if (ide) {
if (ide.irq_delay > 0) {
ide.irq_delay--;
if (ide.irq_delay == 0) {
ide_interrupt_do(ide);
}
}
if (ide.irq && !(ide.regs.ide_devcon & 2))
irq = true;
}
}
return irq;
}
/*-----------------------------------------------------------------------*/
function ide_interrupt(ide) {
ide.regs.ide_status |= IDE_STATUS_BSY;
ide.regs.ide_status &= ~IDE_STATUS_DRQ;
ide.irq_delay = 2;
}
function ide_fast_interrupt(ide) {
ide.regs.ide_status |= IDE_STATUS_BSY;
ide.regs.ide_status &= ~IDE_STATUS_DRQ;
ide.irq_delay = 1;
}
function ide_fail_err(ide, err) {
ide.regs.ide_error |= err;
if (ide.ide_drv == 1 && !SAER.ide.ide_isdrive(ide.pair)) {
ide.pair.regs.ide_status |= IDE_STATUS_ERR;
}
ide.regs.ide_status |= IDE_STATUS_ERR;
ide_interrupt(ide);
}
function ide_fail(ide) {
ide_fail_err(ide, IDE_ERR_ABRT);
}
function ide_data_ready(ide) {
//memset(ide.secbuf, 0, ide.blocksize);
SAEF_memset(ide.secbuf,0, 0, ide.blocksize);
ide.data_offset = 0;
ide.data_size = ide.blocksize;
ide.data_multi = 1;
ide.intdrq = true;
ide_interrupt(ide);
}
function ide_recalibrate(ide) {
SAEF_log("ide.ide_recalibrate() IDE%d recalibrate", ide.num);
ide.regs.ide_sector = 0;
ide.regs.ide_lcyl = ide.regs.ide_hcyl = 0;
ide_interrupt(ide);
}
function ide_identify_drive(ide) {
var totalsecs; //u64
var v;
//var buf = ide.secbuf;
var tmp = "";
var atapi = ide.atapi;
var cf = ide.media_type > 0;
if (!SAER.ide.ide_isdrive(ide)) {
ide_fail(ide);
return;
}
//memset(buf, 0, ide.blocksize);
SAEF_memset(ide.secbuf,0, 0, ide.blocksize);
ide.byteswapped_buffer = 1;
if (IDE_LOG > 0)
SAEF_log("ide.ide_identify_drive() IDE%d identify drive", ide.num);
ide_data_ready(ide);
ide.direction = 0;
pw(ide, 0, atapi ? 0x85c0 : (cf ? 0x848a : (1 << 6)));
pw(ide, 1, ide.hdhfd.cyls_def);
pw(ide, 2, 0xc837);
pw(ide, 3, ide.hdhfd.heads_def);
pw(ide, 4, ide.blocksize * ide.hdhfd.secspertrack_def);
pw(ide, 5, ide.blocksize);
pw(ide, 6, ide.hdhfd.secspertrack_def);
ps(ide, 10, "68000", 20); /* serial */
pw(ide, 20, 3);
pw(ide, 21, ide.blocksize);
pw(ide, 22, 4);
ps(ide, 23, "0.7", 8); /* firmware revision */
if (ide.atapi) //OPT
tmp = "UAE-ATAPI";
else
tmp = sprintf("UAE-IDE %s", ide.hdhfd.hfd.product_id);
ps(ide, 27, tmp, 40); /* model */
pw(ide, 47, MAX_IDE_MULTIPLE_SECTORS >> (ide.blocksize / 512 - 1)); /* max sectors in multiple mode */
pw(ide, 48, 1);
pw(ide, 49, (1 << 9) | (1 << 8)); /* LBA and DMA supported */
pw(ide, 51, 0x200); /* PIO cycles */
pw(ide, 52, 0x200); /* DMA cycles */
pw(ide, 53, 1 | 2 | 4);
pw(ide, 54, ide.hdhfd.cyls);
pw(ide, 55, ide.hdhfd.heads);
pw(ide, 56, ide.hdhfd.secspertrack);
totalsecs = ide.hdhfd.cyls * ide.hdhfd.heads * ide.hdhfd.secspertrack;
pw(ide, 57, totalsecs & 0xffff);
pw(ide, 58, totalsecs >>> 16);
v = ide.multiple_mode;
pw(ide, 59, (v > 0 ? 0x100 : 0) | v);
totalsecs = ide.blocksize ? Math.floor(ide.hdhfd.size / ide.blocksize) : 0;
if (totalsecs > 0x0fffffff)
totalsecs = 0x0fffffff;
pw(ide, 60, totalsecs & 0xffff);
pw(ide, 61, totalsecs >>> 16);
pw(ide, 62, 0x0f);
pw(ide, 63, 0x0f);
if (ide.ata_level) {
pw(ide, 64, ide.ata_level ? 0x03 : 0x00); /* PIO4|PIO3 */
pw(ide, 65, 120); /* MDMA2 supported */
pw(ide, 66, 120);
pw(ide, 67, 120);
pw(ide, 68, 120);
pw(ide, 80, (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6)); /* ATA-1 to ATA-6 */
pw(ide, 81, 0x1c); /* ATA revision */
pw(ide, 82, (1 << 14) | (atapi ? 0x10 | 4 : 0)); /* NOP, ATAPI: PACKET and Removable media features supported */
pw(ide, 83, (1 << 14) | (1 << 13) | (1 << 12) | (ide.lba48 ? (1 << 10) : 0)); /* cache flushes, LBA 48 supported */
pw(ide, 84, 1 << 14);
pw(ide, 85, 1 << 14);
pw(ide, 86, (1 << 14) | (1 << 13) | (1 << 12) | (ide.lba48 ? (1 << 10) : 0)); /* cache flushes, LBA 48 enabled */
pw(ide, 87, 1 << 14);
pw(ide, 88, (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); /* UDMA modes */
pw(ide, 93, (1 << 14) | (1 << 13) | (1 << 0));
if (ide.lba48) { //ATT
totalsecs = Math.floor(ide.hdhfd.size / ide.blocksize);
var hi = Math.floor(totalsecs / 0x100000000);
var lo = totalsecs % 0x100000000;
pw(ide, 100, lo & 0xffff);
pw(ide, 101, lo >>> 16);
pw(ide, 102, hi & 0xffff);
pw(ide, 103, hi >>> 16);
}
}
}
function set_signature(ide) {
if (ide.atapi) {
ide.regs.ide_sector = 1;
ide.regs.ide_nsector = 1;
ide.regs.ide_lcyl = 0x14;
ide.regs.ide_hcyl = 0xeb;
ide.regs.ide_status = 0;
ide.atapi_drdy = false;
} else {
ide.regs.ide_nsector = 1;
ide.regs.ide_sector = 1;
ide.regs.ide_lcyl = 0;
ide.regs.ide_hcyl = 0;
ide.regs.ide_status = 0;
}
ide.regs.ide_error = 0x01; // device ok
ide.packet_state = 0;
}
function reset_device(ide, both) {
set_signature(ide);
if (both)
set_signature(ide.pair);
}
/*this.ide_reset_device = function(ide) {
reset_device(ide, true);
}*/
function ide_execute_drive_diagnostics(ide, irq) {
reset_device(ide, irq);
if (irq)
ide_interrupt(ide);
else
ide.regs.ide_status &= ~IDE_STATUS_BSY;
}
function ide_initialize_drive_parameters(ide) {
var p = ide.hdhfd;
if (p.size) {
p.secspertrack = ide.regs.ide_nsector == 0 ? 256 : ide.regs.ide_nsector;
p.heads = (ide.regs.ide_select & 15) + 1;
if (p.hfd.ci.pcyls)
p.cyls = p.hfd.ci.pcyls;
else
p.cyls = Math.floor(Math.floor(p.size / ide.blocksize) / (p.secspertrack * p.heads));
if (p.heads * p.cyls * p.secspertrack > 16515072 || ide.lba48) {
p.cyls = p.hfd.ci.pcyls ? p.hfd.ci.pcyls : p.cyls_def;
p.heads = p.heads_def;
p.secspertrack = p.secspertrack_def;
}
} else {
ide.regs.ide_error |= IDE_ERR_ABRT;
ide.regs.ide_status |= IDE_STATUS_ERR;
}
SAEF_log("ide.ide_initialize_drive_parameters() IDE%d initialize drive parameters, CYL=%d,SPT=%d,HEAD=%d", ide.num, p.cyls, p.secspertrack, p.heads);
ide_interrupt(ide);
}
function ide_set_multiple_mode(ide) {
SAEF_log("ide.ide_set_multiple_mode() IDE%d drive multiple mode = %d", ide.num, ide.regs.ide_nsector);
ide.multiple_mode = ide.regs.ide_nsector;
ide_interrupt(ide);
}
function ide_set_features(ide) {
var type = ide.regs.ide_nsector >> 3;
var mode = ide.regs.ide_nsector & 7;
SAEF_log("ide.ide_set_features() IDE%d set features %02X (%02X)", ide.num, ide.regs.ide_feat, ide.regs.ide_nsector);
switch (ide.regs.ide_feat) {
// 8-bit mode
case 1:
ide.mode_8bit = true;
ide_interrupt(ide);
break;
case 0x81:
ide.mode_8bit = false;
ide_interrupt(ide);
break;
// write cache
case 2:
case 0x82:
ide_interrupt(ide);
break;
default:
ide_fail(ide);
}
}
function get_nsec(ide) {
if (ide.lba48 && ide.lba48cmd)
//return (ide.regs.ide_nsector == 0 && ide.regs.ide_nsector2 == 0) ? 65536 : (ide.regs.ide_nsector2 * 256 + ide.regs.ide_nsector);
return (ide.regs.ide_nsector == 0 && ide.regs.ide_nsector2 == 0) ? 65536 : ((ide.regs.ide_nsector2 << 8) | ide.regs.ide_nsector);
else
return ide.regs.ide_nsector == 0 ? 256 : ide.regs.ide_nsector;
}
function dec_nsec(ide, v) {
if (ide.lba48 && ide.lba48cmd) {
var nsec = (ide.regs.ide_nsector2 << 8) | ide.regs.ide_nsector;
nsec -= v;
if (nsec < 0) nsec += 0x10000;
ide.regs.ide_nsector2 = nsec >> 8;
ide.regs.ide_nsector = nsec & 0xff;
return nsec;
} else {
ide.regs.ide_nsector -= v;
if (ide.regs.ide_nsector < 0) ide.regs.ide_nsector += 0x100;
return ide.regs.ide_nsector;
}
}
//function get_lbachs(ide, uae_u64 *lbap, unsigned int *cyl, unsigned int *head, unsigned int *sec) {
function get_lbachs(ide, ptr) {
if (ide.lba48 && ide.lba48cmd && (ide.regs.ide_select & 0x40)) {
/*ATT
uae_u64 lba;
lba = (ide.regs.ide_hcyl << 16) | (ide.regs.ide_lcyl << 8) | ide.regs.ide_sector;
lba |= ((ide.regs.ide_hcyl2 << 16) | (ide.regs.ide_lcyl2 << 8) | ide.regs.ide_sector2) << 24;
ptr.lba = lba;*/
var lo = (ide.regs.ide_hcyl << 16) | (ide.regs.ide_lcyl << 8) | ide.regs.ide_sector;
var hi = (ide.regs.ide_hcyl2 << 16) | (ide.regs.ide_lcyl2 << 8) | ide.regs.ide_sector2;
ptr.lba = hi * 0x1000000 + lo;
} else {
if (ide.regs.ide_select & 0x40) {
ptr.lba = (((ide.regs.ide_select & 15) << 24) | (ide.regs.ide_hcyl << 16) | (ide.regs.ide_lcyl << 8) | ide.regs.ide_sector) >>> 0;
} else {
ptr.cyl = (ide.regs.ide_hcyl << 8) | ide.regs.ide_lcyl;
ptr.head = ide.regs.ide_select & 15;
ptr.sec = ide.regs.ide_sector;
ptr.lba = ((ptr.cyl * ide.hdhfd.heads + ptr.head) * ide.hdhfd.secspertrack) + ptr.sec - 1;
}
}
}
function put_lbachs(ide, lba, cyl, head, sec, inc) {
if (ide.lba48 && ide.lba48cmd) {
lba += inc;
/*ATT
ide.regs.ide_hcyl = (lba >> 16) & 0xff;
ide.regs.ide_lcyl = (lba >> 8) & 0xff;
ide.regs.ide_sector = lba & 0xff;
lba >>= 24;
ide.regs.ide_hcyl2 = (lba >> 16) & 0xff;
ide.regs.ide_lcyl2 = (lba >> 8) & 0xff;
ide.regs.ide_sector2 = lba & 0xff;*/
var lo = lba % 0x1000000;
var hi = Math.floor(lba / 0x1000000);
ide.regs.ide_hcyl = (lo >>> 16) & 0xff;
ide.regs.ide_lcyl = (lo >>> 8) & 0xff;
ide.regs.ide_sector = lo & 0xff;
ide.regs.ide_hcyl2 = (hi >>> 16) & 0xff;
ide.regs.ide_lcyl2 = (hi >>> 8) & 0xff;
ide.regs.ide_sector2 = hi & 0xff;
} else {
if (ide.regs.ide_select & 0x40) {
lba += inc;
ide.regs.ide_select &= ~15;
ide.regs.ide_select |= (lba >>> 24) & 15;
ide.regs.ide_hcyl = (lba >>> 16) & 0xff;
ide.regs.ide_lcyl = (lba >>> 8) & 0xff;
ide.regs.ide_sector = lba & 0xff;
} else {
sec += inc;
while (sec >= ide.hdhfd.secspertrack) {
sec -= ide.hdhfd.secspertrack;
head++;
if (head >= ide.hdhfd.heads) {
head -= ide.hdhfd.heads;
cyl++;
}
}
ide.regs.ide_select &= ~15;
ide.regs.ide_select |= head;
ide.regs.ide_sector = sec;
ide.regs.ide_hcyl = cyl >> 8;
ide.regs.ide_lcyl = cyl & 0xff;
}
}
}
function check_maxtransfer(ide, state) {
if (state == 1) {
// transfer was started
if (ide.maxtransferstate < 2 && ide.regs.ide_nsector == 0)
ide.maxtransferstate = 1;
else if (ide.maxtransferstate == 2) {
// second transfer was started (part of split)
SAEF_log("ide.check_maxtransfer() maxtransfer check detected split >256 block transfer");
ide.maxtransferstate = 0;
} else
ide.maxtransferstate = 0;
} else if (state == 2) {
// address was read
if (ide.maxtransferstate == 1)
ide.maxtransferstate++;
else
ide.maxtransferstate = 0;
}
}
function setdrq(ide) {
ide.regs.ide_status |= IDE_STATUS_DRQ;
ide.regs.ide_status &= ~IDE_STATUS_BSY;
}
function setbsy(ide) {
ide.regs.ide_status |= IDE_STATUS_BSY;
ide.regs.ide_status &= ~IDE_STATUS_DRQ;
}
function process_rw_command(ide) {
setbsy(ide);
//write_comm_pipe_u32(ide.its.requests, ide.num, 1);
do_process_rw_command(ide);
}
function process_packet_command(ide) {
setbsy(ide);
write_comm_pipe_u32(ide.its.requests, ide.num | 0x80, 1);
}
/*static void atapi_data_done (struct ide_hdf *ide) {
ide->regs.ide_nsector = ATAPI_IO | ATAPI_CD;
ide->regs.ide_status = IDE_STATUS_DRDY;
ide->data_size = 0;
ide->packet_data_offset = 0;
ide->data_offset = 0;
}
static bool atapi_set_size (struct ide_hdf *ide) {
int size;
size = ide->data_size;
ide->data_offset = 0;
if (!size) {
ide->packet_state = 0;
ide->packet_transfer_size = 0;
return false;
}
if (ide->packet_state == 2) {
if (size > ide->packet_data_size)
size = ide->packet_data_size;
if (size > ATAPI_MAX_TRANSFER)
size = ATAPI_MAX_TRANSFER;
ide->packet_transfer_size = size & ~1;
ide->regs.ide_lcyl = size & 0xff;
ide->regs.ide_hcyl = size >> 8;
} else {
ide->packet_transfer_size = 12;
}
if (IDE_LOG > 1)
write_log (_T("ATAPI data transfer %d/%d bytes\n"), ide->packet_transfer_size, ide->data_size);
return true;
}
static void atapi_packet (struct ide_hdf *ide) {
ide->packet_data_offset = 0;
ide->packet_data_size = (ide->regs.ide_hcyl << 8) | ide->regs.ide_lcyl;
if (ide->packet_data_size == 65535)
ide->packet_data_size = 65534;
ide->data_size = 12;
if (IDE_LOG > 0)
write_log (_T("ATAPI packet command. Data size = %d\n"), ide->packet_data_size);
ide->packet_state = 1;
ide->data_multi = 1;
ide->data_offset = 0;
ide->regs.ide_nsector = ATAPI_CD;
ide->regs.ide_error = 0;
if (atapi_set_size (ide))
setdrq (ide);
}*/
/*static void do_packet_command (struct ide_hdf *ide) {
memcpy (ide->scsi->cmd, ide->secbuf, 12);
ide->scsi->cmd_len = 12;
if (IDE_LOG > 0) {
uae_u8 *c = ide->scsi->cmd;
write_log (_T("ATASCSI %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x\n"),
c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8], c[9], c[10], c[11]);
}
ide->direction = 0;
scsi_emulate_analyze (ide->scsi);
if (ide->scsi->direction <= 0) {
// data in
scsi_emulate_cmd (ide->scsi);
ide->data_size = ide->scsi->data_len;
ide->regs.ide_status = 0;
if (ide->scsi->status) {
// error
ide->regs.ide_error = (ide->scsi->sense[2] << 4) | 4;
atapi_data_done (ide);
ide->regs.ide_status |= ATAPI_STATUS_CHK;
atapi_set_size (ide);
return;
} else if (ide->scsi->data_len) {
// data in
ide_grow_buffer(ide, ide->scsi->data_len);
memcpy (ide->secbuf, ide->scsi->buffer, ide->scsi->data_len);
ide->regs.ide_nsector = ATAPI_IO;
} else {
// no data
atapi_data_done (ide);
}
} else {
// data out
ide->direction = 1;
ide->regs.ide_nsector = 0;
ide->data_size = ide->scsi->data_len;
}
ide->packet_state = 2; // data phase
if (atapi_set_size (ide))
ide->intdrq = true;
}*/
/*static void do_process_packet_command (struct ide_hdf *ide) {
if (ide->packet_state == 1) {
do_packet_command (ide);
} else {
ide->packet_data_offset += ide->packet_transfer_size;
if (!ide->direction) {
// data still remaining, next transfer
if (atapi_set_size (ide))
ide->intdrq = true;
} else {
if (atapi_set_size (ide)) {
ide->intdrq = true;
} else {
if (IDE_LOG > 1)
write_log(_T("IDE%d ATAPI write finished, %d bytes\n"), ide->num, ide->data_size);
memcpy (&ide->scsi->buffer, ide->secbuf, ide->data_size);
ide->scsi->data_len = ide->data_size;
scsi_emulate_cmd (ide->scsi);
}
}
}
ide_fast_interrupt (ide);
}*/
function do_process_rw_command(ide) {
//SAEF_log("ide.do_process_rw_command()");
//unsigned int cyl, head, sec;
//uae_u64 lba;
var ptr = {};
ide.data_offset = 0;
var nsec = get_nsec(ide);
//get_lbachs(ide, &lba, &cyl, &head, &sec);
get_lbachs(ide, ptr);
if (IDE_LOG > 1)
SAEF_log("ide.do_process_rw_command() IDE%d off=%d, nsec=%d (%d) lba48=%d bs=%d", ide.num, ptr.lba, nsec, ide.multiple_mode, ide.lba48 + ide.lba48cmd, ide.blocksize);
if (nsec * ide.blocksize > ide.hdhfd.size - ptr.lba * ide.blocksize) {
nsec = Math.truncate((ide.hdhfd.size - ptr.lba * ide.blocksize) / ide.blocksize);
if (IDE_LOG > 1)
SAEF_log("ide.do_process_rw_command() IDE%d nsec changed to %d", ide.num, nsec);
}
if (nsec <= 0) {
ide_data_ready(ide);
ide_fail_err(ide, IDE_ERR_IDNF);
return;
}
var nsec_total = nsec;
ide_grow_buffer(ide, nsec_total * ide.blocksize);
if (nsec > ide.data_multi)
nsec = ide.data_multi;
if (ide.buffer_offset == 0) {
// store initial lba and number of sectors to transfer
ide.start_lba = ptr.lba;
ide.start_nsec = nsec_total;
}
if (ide.direction) {
if (IDE_LOG > 1)
SAEF_log("ide.do_process_rw_command() IDE%d write, %d/%d bytes, buffer offset %d", ide.num, nsec * ide.blocksize, nsec_total * ide.blocksize, ide.buffer_offset);
} else {
if (ide.buffer_offset == 0) {
SAER.hardfile.hdf_read(ide.hdhfd.hfd, ide.secbuf, ide.start_lba * ide.blocksize, ide.start_nsec * ide.blocksize);
if (IDE_LOG > 1)
SAEF_log("ide.do_process_rw_command() IDE%d initial read, %d bytes", ide.num, nsec_total * ide.blocksize);
}
if (IDE_LOG > 1)
SAEF_log("ide.do_process_rw_command() IDE%d read, read %d/%d bytes, buffer offset=%d", ide.num, nsec * ide.blocksize, nsec_total * ide.blocksize, ide.buffer_offset);
}
ide.intdrq = true;
var last = dec_nsec(ide, nsec) == 0;
// ATA-2 spec says CHS/LBA does only need to be updated if error condition
if (ide.ata_level != SAEC_Config_Mount_Controller_Level_ATA_2S || !last)
put_lbachs(ide, ptr.lba, ptr.cyl, ptr.head, ptr.sec, last ? nsec - 1 : nsec);
if (last && ide.direction) {
if (IDE_LOG > 1)
SAEF_log("ide.do_process_rw_command() IDE%d write finished, %d bytes", ide.num, ide.start_nsec * ide.blocksize);
ide.intdrq = false;
SAER.hardfile.hdf_write(ide.hdhfd.hfd, ide.secbuf, ide.start_lba * ide.blocksize, ide.start_nsec * ide.blocksize);
}
if (ide.direction) {
if (last)
ide_fast_interrupt(ide);
else
ide.irq_delay = 1;
} else {
if (ide.buffer_offset == 0)
ide_fast_interrupt(ide);
else
ide.irq_delay = 1;
}
}
function ide_read_sectors(ide, flags) {
//unsigned int cyl, head, sec, nsec;
//uae_u64 lba;
var ptr = {};
var multi = flags & 1;
ide.lba48cmd = (flags & 2) != 0;
if (multi && ide.multiple_mode == 0) {
ide_fail(ide);
return;
}
check_maxtransfer(ide, 1);
SAER.gui.flicker_led(SAEC_GUI_LED_HD, ide.num, 1);
var nsec = get_nsec(ide);
//get_lbachs(ide, &lba, &cyl, &head, &sec);
get_lbachs(ide, ptr);
if (ptr.lba * ide.blocksize >= ide.hdhfd.size) {
ide_data_ready(ide);
ide_fail_err(ide, IDE_ERR_IDNF);
return;
}
if (IDE_LOG > 0)
SAEF_log("ide.ide_read_sectors() IDE%d %s off=%d, sec=%d (%d) lba48=%d", ide.num, (flags & 4) ? "verify" : "read", ptr.lba, nsec, ide.multiple_mode, ide.lba48 + ide.lba48cmd);
if (flags & 4) {
// verify
ide_interrupt(ide);
return;
}
ide.data_multi = multi ? ide.multiple_mode : 1;
ide.data_offset = 0;
ide.data_size = nsec * ide.blocksize;
ide.direction = 0;
ide.buffer_offset = 0;
// read start: preload sector(s), then trigger interrupt.
process_rw_command(ide);
}
function ide_write_sectors(ide, flags) {
//unsigned int cyl, head, sec, nsec;
//uae_u64 lba;
var ptr = {};
var multi = flags & 1;
ide.lba48cmd = (flags & 2) != 0;
if (multi && ide.multiple_mode == 0) {
ide_fail(ide);
return;
}
check_maxtransfer(ide, 1);
SAER.gui.flicker_led(SAEC_GUI_LED_HD, ide.num, 2);
nsec = get_nsec(ide);
//get_lbachs(ide, &lba, &cyl, &head, &sec);
get_lbachs(ide, ptr);
if (ptr.lba * ide.blocksize >= ide.hdhfd.size) {
ide_data_ready(ide);
ide_fail_err(ide, IDE_ERR_IDNF);
return;
}
if (IDE_LOG > 0)
SAEF_log("ide.ide_write_sectors() IDE%d write off=%d, sec=%d (%d) lba48=%d", ide.num, ptr.lba, nsec, ide.multiple_mode, ide.lba48 + ide.lba48cmd);
if (nsec * ide.blocksize > ide.hdhfd.size - ptr.lba * ide.blocksize)
nsec = Math.truncate((ide.hdhfd.size - ptr.lba * ide.blocksize) / ide.blocksize);
if (nsec <= 0) {
ide_data_ready(ide);
ide_fail_err(ide, IDE_ERR_IDNF);
return;
}
ide.data_multi = multi ? ide.multiple_mode : 1;
ide.data_offset = 0;
ide.data_size = nsec * ide.blocksize;
ide.direction = 1;
ide.buffer_offset = 0;
// write start: set DRQ and clear BSY. No interrupt.
ide.regs.ide_status |= IDE_STATUS_DRQ;
ide.regs.ide_status &= ~IDE_STATUS_BSY;
}
function ide_do_command(ide, cmd) {
var lba48 = ide.lba48;
if (IDE_LOG > 1)
SAEF_log("ide.ide_do_command() IDE%d command %02X", ide.num, cmd);
ide.regs.ide_status &= ~ (IDE_STATUS_DRDY | IDE_STATUS_DRQ | IDE_STATUS_ERR);
ide.regs.ide_error = 0;
ide.intdrq = false;
ide.lba48cmd = false;
ide.byteswapped_buffer = 0;
if (ide.atapi) {
//SAER.gui.flicker_led(SAEC_GUI_LED_CD, ide.num, 1);
ide.atapi_drdy = true;
if (cmd == 0x00) { /* nop */
ide_interrupt(ide);
} else if (cmd == 0x08) { /* device reset */
ide_execute_drive_diagnostics(ide, true);
} else if (cmd == 0xa1) { /* identify packet device */
ide_identify_drive(ide);
} else if (cmd == 0xa0) { /* packet */
atapi_packet(ide);
} else if (cmd == 0x90) { /* execute drive diagnostics */
ide_execute_drive_diagnostics(ide, true);
} else {
ide_execute_drive_diagnostics(ide, false);
ide.atapi_drdy = false;
ide_fail(ide);
SAEF_warn("ide.ide_do_command() IDE%d: unknown ATAPI command 0x%02x", ide.num, cmd);
}
} else {
if (cmd == 0x10) { /* recalibrate */
ide_recalibrate (ide);
} else if (cmd == 0xec) { /* identify drive */
ide_identify_drive(ide);
} else if (cmd == 0x90) { /* execute drive diagnostics */
ide_execute_drive_diagnostics(ide, true);
} else if (cmd == 0x91) { /* initialize drive parameters */
ide_initialize_drive_parameters(ide);
} else if (cmd == 0xc6) { /* set multiple mode */
ide_set_multiple_mode(ide);
} else if (cmd == 0x20 || cmd == 0x21) { /* read sectors */
ide_read_sectors(ide, 0);
} else if (cmd == 0x40 || cmd == 0x41) { /* verify sectors */
ide_read_sectors(ide, 4);
} else if (cmd == 0x24 && lba48) { /* read sectors ext */
ide_read_sectors(ide, 2);
} else if (cmd == 0xc4) { /* read multiple */
ide_read_sectors(ide, 1);
} else if (cmd == 0x29 && lba48) { /* read multiple ext */
ide_read_sectors(ide, 1|2);
} else if (cmd == 0x30 || cmd == 0x31) { /* write sectors */
ide_write_sectors(ide, 0);
} else if (cmd == 0x34 && lba48) { /* write sectors ext */
ide_write_sectors(ide, 2);
} else if (cmd == 0xc5) { /* write multiple */
ide_write_sectors(ide, 1);
} else if (cmd == 0x39 && lba48) { /* write multiple ext */
ide_write_sectors(ide, 1|2);
} else if (cmd == 0x50) { /* format track (nop) */
ide_interrupt(ide);
} else if (cmd == 0xef) { /* set features */
ide_set_features(ide);
} else if (cmd == 0x00) { /* nop */
ide_fail(ide);
} else if (cmd == 0x70) { /* seek */
ide_interrupt(ide);
} else if (cmd == 0xe0 || cmd == 0xe1 || cmd == 0xe7 || cmd == 0xea) { /* standby now/idle/flush cache/flush cache ext */
ide_interrupt(ide);
} else if (cmd == 0xe5) { /* check power mode */
ide.regs.ide_nsector = 0xff;
ide_interrupt(ide);
} else {
ide_fail(ide);
SAEF_warn("ide.ide_do_command() IDE%d: unknown ATA command 0x%02x", ide.num, cmd);
}
}
}
/*-----------------------------------------------------------------------*/
function ide_get_data_2(ide, bussize) {
var irq = false;
var v;
var inc = bussize ? 2 : 1;
if (ide.data_size == 0) {
if (IDE_LOG > 0)
SAEF_warn("ide.ide_get_data_2() IDE%d DATA but no data left!? 0x%02X, PC 0x%08X", ide.num, ide.regs.ide_status, SAER_CPU_getPC());
if (!SAER.ide.ide_isdrive(ide))
return 0xffff;
return 0;
}
if (ide.packet_state) {
if (bussize)
v = (ide.secbuf[ide.packet_data_offset + ide.data_offset] << 8) | ide.secbuf[ide.packet_data_offset + ide.data_offset + 1];
else
v = ide.secbuf[ide.packet_data_offset + ide.data_offset];
if (IDE_LOG > 4)
SAEF_log("ide.ide_get_data_2() IDE%d DATA read 0x%04x", ide.num, v);
ide.data_offset += inc;
if (ide.data_size < 0)
ide.data_size += inc;
else
ide.data_size -= inc;
if (ide.data_offset == ide.packet_transfer_size) {
if (IDE_LOG > 1)
SAEF_log("ide.ide_get_data_2() IDE%d ATAPI partial read finished, %d bytes remaining", ide.num, ide.data_size);
if (ide.data_size == 0) {
ide.packet_state = 0;
atapi_data_done(ide);
if (IDE_LOG > 1)
SAEF_log("ide.ide_get_data_2() IDE%d ATAPI read finished, %d bytes", ide.num, ide.packet_data_offset + ide.data_offset);
irq = true;
} else
process_packet_command(ide);
}
} else {
if (bussize)
v = (ide.secbuf[ide.buffer_offset + ide.data_offset] << 8) | ide.secbuf[ide.buffer_offset + ide.data_offset + 1];
else
v = ide.secbuf[ide.buffer_offset + ide.data_offset];
if (IDE_LOG > 4)
SAEF_log("ide.ide_get_data_2() IDE%d DATA read 0x%04x %d/%d", ide.num, v, ide.data_offset, ide.data_size);
ide.data_offset += inc;
if (ide.data_size < 0)
ide.data_size += inc;
else {
ide.data_size -= inc;
if (((ide.data_offset % ide.blocksize) == 0) && (Math.floor(ide.data_offset / ide.blocksize) % ide.data_multi) == 0) {
if (ide.data_size) {
ide.buffer_offset += ide.data_offset;
do_process_rw_command(ide);
}
}
}
if (ide.data_size == 0) {
if (!(ide.regs.ide_status & IDE_STATUS_DRQ)) {
SAEF_warn("ide.ide_get_data_2() IDE%d read finished but DRQ was not active?", ide.num);
}
ide.regs.ide_status &= ~IDE_STATUS_DRQ;
if (IDE_LOG > 1)
SAEF_log("ide.ide_get_data_2() IDE%d read finished", ide.num);
}
}
if (irq)
ide_fast_interrupt(ide);
return v;
}
this.ide_get_data = function(ide) {
return ide_get_data_2(ide, 1);
}
/*this.ide_get_data_8bit = function(ide) {
return ide_get_data_2(ide, 0) & 0xff;
}*/
function ide_put_data_2(ide, v, bussize) {
var inc = bussize ? 2 : 1;
if (IDE_LOG > 4)
SAEF_log("ide.ide_put_data_2() IDE%d DATA write 0x%04x %d/%d", ide.num, v, ide.data_offset, ide.data_size);
if (ide.data_size == 0) {
if (IDE_LOG > 0)
SAEF_warn("ide.ide_put_data_2() IDE%d DATA write without request!? 0x%02X, PC 0x%08X", ide.num, ide.regs.ide_status, SAER_CPU_getPC());
return;
}
ide_grow_buffer(ide, ide.packet_data_offset + ide.data_offset + 2);
if (ide.packet_state) {
if (bussize) {
ide.secbuf[ide.packet_data_offset + ide.data_offset + 1] = v & 0xff;
ide.secbuf[ide.packet_data_offset + ide.data_offset ] = v >> 8;
} else
ide.secbuf[(ide.packet_data_offset + ide.data_offset) ^ 1] = v;
} else {
if (bussize) {
ide.secbuf[ide.buffer_offset + ide.data_offset + 1] = v & 0xff;
ide.secbuf[ide.buffer_offset + ide.data_offset ] = v >> 8;
} else
ide.secbuf[ide.buffer_offset + ide.data_offset] = v;
}
ide.data_offset += inc;
ide.data_size -= inc;
if (ide.packet_state) {
if (ide.data_offset == ide.packet_transfer_size) {
if (IDE_LOG > 0) {
var v = (ide.regs.ide_hcyl << 8) | ide.regs.ide_lcyl;
SAEF_warn("ide.ide_put_data_2() Data size after command received = %d (%d)", v, ide.packet_data_size);
}
process_packet_command(ide);
}
} else {
if (ide.data_size == 0) {
process_rw_command(ide);
} else if (((ide.data_offset % ide.blocksize) == 0) && (Math.floor(ide.data_offset / ide.blocksize) % ide.data_multi) == 0) {
var off = ide.data_offset;
do_process_rw_command(ide);
ide.buffer_offset += off;
}
}
}
this.ide_put_data = function(ide, v) {
ide_put_data_2(ide, v, 1);
}
this.ide_put_data_8bit = function(ide, v) {
ide_put_data_2(ide, v, 0);
}
/*-----------------------------------------------------------------------*/
this.ide_read_reg = function(ide, ide_reg) {
var isdrv = this.ide_isdrive(ide);
var v = 0;
if (ide === null) {
SAEF_warn("ide.ide_read_reg() no handle");
//goto end;
return v;
}
if (ide.regs.ide_status & IDE_STATUS_BSY)
ide_reg = IDE_STATUS;
if (!this.ide_isdrive(ide)) {
if (ide_reg == IDE_STATUS) {
if (ide.pair.irq)
ide.pair.irq = 0;
if (this.ide_isdrive(ide.pair))
v = 0x01;
else
v = 0xff;
} else
v = 0;
//goto end;
if (IDE_LOG > 2 && ide_reg > 0 && (1 || ide.num > 0))
SAEF_log("ide.ide_read_reg() IDE%d GET register %d=%02X (%08X)", ide.num, ide_reg, v & 0xff, SAER_CPU_getPC());
return v;
}
switch (ide_reg) {
case IDE_SECONDARY:
case IDE_SECONDARY + 1:
case IDE_SECONDARY + 2:
case IDE_SECONDARY + 3:
case IDE_SECONDARY + 4:
case IDE_SECONDARY + 5:
v = 0xff;
break;
case IDE_DRVADDR:
v = ((ide.ide_drv ? 2 : 1) | ((ide.regs.ide_select & 15) << 2)) ^ 0xff;
break;
case IDE_DATA:
break;
case IDE_ERROR:
v = ide.regs.ide_error;
break;
case IDE_NSECTOR:
if (isdrv) {
if (ide.regs.ide_devcon & 0x80)
v = ide.regs.ide_nsector2;
else
v = ide.regs.ide_nsector;
}
break;
case IDE_SECTOR:
if (isdrv) {
if (ide.regs.ide_devcon & 0x80)
v = ide.regs.ide_sector2;
else
v = ide.regs.ide_sector;
check_maxtransfer(ide, 2);
}
break;
case IDE_LCYL:
if (isdrv) {
if (ide.regs.ide_devcon & 0x80)
v = ide.regs.ide_lcyl2;
else
v = ide.regs.ide_lcyl;
}
break;
case IDE_HCYL:
if (isdrv) {
if (ide.regs.ide_devcon & 0x80)
v = ide.regs.ide_hcyl2;
else
v = ide.regs.ide_hcyl;
}
break;
case IDE_SELECT:
v = ide.regs.ide_select;
break;
case IDE_STATUS:
ide.irq = 0;
ide.irq_new = false;
// fall through
case IDE_DEVCON: // ALTSTATUS when reading
if (!isdrv) {
v = 0;
if (ide.regs.ide_error)
v |= IDE_STATUS_ERR;
} else {
v = ide.regs.ide_status;
if (!ide.atapi || (ide.atapi && ide.atapi_drdy))
v |= IDE_STATUS_DRDY | IDE_STATUS_DSC;
}
break;
}
//end:
if (IDE_LOG > 2 && ide_reg > 0 && (1 || ide.num > 0))
SAEF_log("ide.ide_read_reg() IDE%d GET register %d=%02X (%08X)", ide.num, ide_reg, v & 0xff, SAER_CPU_getPC());
return v;
}
this.ide_write_reg = function(ide, ide_reg, val) {
if (ide === null) {
SAEF_warn("ide.ide_write_reg() no handle");
return;
}
ide.regs1.ide_devcon &= ~0x80; // clear HOB
ide.regs0.ide_devcon &= ~0x80; // clear HOB
if (IDE_LOG > 2 && ide_reg > 0 && (1 || ide.num > 0))
SAEF_log("ide.ide_write_reg() IDE%d PUT register %d=%02X (%08X)", ide.num, ide_reg, val & 0xff, SAER_CPU_getPC());
switch (ide_reg) {
case IDE_DRVADDR:
break;
case IDE_DEVCON:
if ((ide.regs.ide_devcon & 4) == 0 && (val & 4) != 0) {
reset_device(ide, true);
if (IDE_LOG > 1)
SAEF_log("ide.ide_write_reg() IDE%d: SRST", ide.num);
}
ide.regs0.ide_devcon = val;
ide.regs1.ide_devcon = val;
break;
case IDE_DATA:
break;
case IDE_ERROR:
ide.regs0.ide_feat2 = ide.regs0.ide_feat;
ide.regs0.ide_feat = val;
ide.regs1.ide_feat2 = ide.regs1.ide_feat;
ide.regs1.ide_feat = val;
break;
case IDE_NSECTOR:
ide.regs0.ide_nsector2 = ide.regs0.ide_nsector;
ide.regs0.ide_nsector = val;
ide.regs1.ide_nsector2 = ide.regs1.ide_nsector;
ide.regs1.ide_nsector = val;
break;
case IDE_SECTOR:
ide.regs0.ide_sector2 = ide.regs0.ide_sector;
ide.regs0.ide_sector = val;
ide.regs1.ide_sector2 = ide.regs1.ide_sector;
ide.regs1.ide_sector = val;
break;
case IDE_LCYL:
ide.regs0.ide_lcyl2 = ide.regs0.ide_lcyl;
ide.regs0.ide_lcyl = val;
ide.regs1.ide_lcyl2 = ide.regs1.ide_lcyl;
ide.regs1.ide_lcyl = val;
break;
case IDE_HCYL:
ide.regs0.ide_hcyl2 = ide.regs0.ide_hcyl;
ide.regs0.ide_hcyl = val;
ide.regs1.ide_hcyl2 = ide.regs1.ide_hcyl;
ide.regs1.ide_hcyl = val;
break;
case IDE_SELECT:
ide.regs0.ide_select = val;
ide.regs1.ide_select = val;
if (IDE_LOG > 2) {
if (ide.ide_drv != (val & 0x10) ? 1 : 0)
SAEF_log("ide.ide_write_reg() DRIVE=%d", (val & 0x10) ? 1 : 0);
}
ide.pair.ide_drv = ide.ide_drv = (val & 0x10) ? 1 : 0;
break;
case IDE_STATUS:
ide.irq = 0;
ide.irq_new = false;
if (this.ide_isdrive(ide)) {
ide.regs.ide_status |= IDE_STATUS_BSY;
ide_do_command(ide, val);
}
break;
}
}
/*-----------------------------------------------------------------------*/
//function ide_thread(idedata) {
this.ide_thread = function(its) {
//struct ide_thread_state *its = (struct ide_thread_state*)idedata;
var quit = false; //OWN
//for (;;)
{
var unit = read_comm_pipe_u32_blocking(its.requests);
if (unit) SAEF_log("ide.ide_thread() unit 0x%08x", unit);
if (its.state == 0 || unit == 0xfffffff) {
SAEF_log("ide.ide_thread() QUIT");
quit = true;
//break;
} else {
var ide = its.idetable[unit & 0x7f];
if (SAER.ide.ide_isdrive(ide)) //OWN
{
if (unit & 0x80)
do_process_packet_command(ide);
else
do_process_rw_command(ide);
} else {
//SAEF_fatal(SAEE_Internal, "ide.ide_thread() no ide");
}
}
}
if (quit) {
its.state = -1;
return 0;
}
setTimeout(function() { SAER.ide.ide_thread(its); }, 10);
}
this.start_ide_thread = function(its) {
if (!its.state) {
SAEF_log("ide.start_ide_thread() state %d", its.state);
its.state = 1;
init_comm_pipe(its.requests, 100, 1);
//uae_start_thread("ide", ide_thread, its, null);
setTimeout(function() { SAER.ide.ide_thread(its); }, 0);
}
}
this.stop_ide_thread = function(its) {
if (its.state > 0) {
SAEF_log("ide.stop_ide_thread() state %d", its.state);
its.state = 0;
write_comm_pipe_u32(its.requests, 0xffffffff, 1);
//while (its.state == 0) SAEF_sleep(10); //FIX will never break
its.state = 0;
}
}
/*-----------------------------------------------------------------------*/
this.ide_initialize = function(idetable, chpair) {
var ide0 = idetable[chpair * 2 + 0];
var ide1 = idetable[chpair * 2 + 1];
ide0.regs0 = ide0.regs;
ide0.regs1 = ide1.regs;
ide0.pair = ide1;
ide1.regs1 = ide1.regs;
ide1.regs0 = ide0.regs;
ide1.pair = ide0;
ide0.num = chpair * 2 + 0;
ide1.num = chpair * 2 + 1;
reset_device(ide0, true);
}
this.alloc_ide_mem = function(idetable, max, its) {
for (var i = 0; i < max; i++) {
var ide;
if (idetable[i] === null) {
ide = idetable[i] = new ide_hdf();
ide.cd_unit_num = -1;
}
ide = idetable[i];
ide_grow_buffer(ide, 1024);
if (its !== null)
ide.its = its;
}
}
this.add_ide_unit = function(idetable, max, ch, ci, rc) {
this.alloc_ide_mem(idetable, max, null);
if (ch < 0)
return null;
var ide = idetable[ch];
if (ci !== null) {
ide.hdhfd.hfd.ci = ci.clone(); //memcpy(&ide.hdhfd.hfd.ci, ci, sizeof(struct uaedev_config_info)); //ATT
}
/*if (ci.type == UAEDEV_CD && ci.device_emu_unit >= 0) {
device_func_init (0);
ide.scsi = scsi_alloc_cd (ch, ci.device_emu_unit, true);
if (!ide.scsi) {
SAEF_log("ide.add_ide_unit() IDE: CD EMU unit %d failed to open", ide.cd_unit_num);
return null;
}
ide.cd_unit_num = ci.device_emu_unit;
ide.atapi = true;
ide.blocksize = 512;
SAER.gui.flicker_led(SAEC_GUI_LED_CD, ch, -1);
SAEF_log("ide.add_ide_unit() IDE%d CD %d", ch, ide.cd_unit_num);
} else if (ci.type == UAEDEV_HDF)*/
{
if (!SAER.hardfile.hdf_hd_open(ide.hdhfd))
return null;
ide.blocksize = ide.hdhfd.hfd.ci.blocksize;
ide.lba48 = (ide.hdhfd.hfd.ci.unit_special_flags & 1) || ide.hdhfd.size >= 128 * 0x40000000 ? 1 : 0;
SAER.gui.flicker_led(SAEC_GUI_LED_HD, ch, -1);
ide.cd_unit_num = -1;
ide.media_type = ci.controller_media_type;
ide.ata_level = ci.unit_feature_level;
if (ide.ata_level == SAEC_Config_Mount_Controller_Level_ATA_1 && (ide.hdhfd.size >= 4 * 0x40000000 || ide.media_type))
ide.ata_level = SAEC_Config_Mount_Controller_Level_ATA_2;
SAEF_log("ide.add_ide_unit() IDE%d HD '%s', LCHS=%d/%d/%d. PCHS=%d/%d/%d %dM. LBA48=%d",
ch, ide.hdhfd.hfd.ci.file.name,
ide.hdhfd.cyls, ide.hdhfd.heads, ide.hdhfd.secspertrack,
ide.hdhfd.hfd.ci.pcyls, ide.hdhfd.hfd.ci.pheads, ide.hdhfd.hfd.ci.psecs,
Math.floor(ide.hdhfd.size / (1024 * 1024)), ide.lba48);
}
ide.regs.ide_status = 0;
ide.data_offset = 0;
ide.data_size = 0;
return ide;
}
this.remove_ide_unit = function(idetable, ch) {
if (idetable === null)
return;
var ide = idetable[ch];
if (ide) {
SAER.hardfile.hdf_hd_close(ide.hdhfd);
//scsi_free(ide.scsi);
//xfree(ide.secbuf);
var its = ide.its;
//clear(ide); //memset(ide, 0, sizeof(struct ide_hdf));
ide.its = its;
}
}
}