/*
JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine
Release Version 2.4
A project from the Physics Dept, The University of Oxford
Copyright (C) 2007-2010 The University of Oxford
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation.
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.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Details (including contact information) can be found at:
jpc.sourceforge.net
or the developer website
sourceforge.net/projects/jpc/
Conceived and Developed by:
Rhys Newman, Ian Preston, Chris Dennis
End of licence header
*/
package org.jpc.emulator.pci.peripheral;
import org.jpc.emulator.motherboard.*;
import org.jpc.support.BlockDevice;
import org.jpc.emulator.*;
import java.io.*;
import java.util.logging.*;
/**
*
* @author Chris Dennis
*/
class IDEChannel extends AbstractHardwareComponent implements IODevice {
private static final Logger LOGGING = Logger.getLogger(IDEChannel.class.getName());
private IDEState[] devices;
private IDEState currentDevice;
private int ioBase, ioBaseTwo, irq;
private InterruptController irqDevice;
private int nextDriveSerial;
public static final String CDLABEL = "CDROM";//"JPC CD-ROM";
public void saveState(DataOutput output) throws IOException {
output.writeInt(ioBase);
output.writeInt(ioBaseTwo);
output.writeInt(irq);
output.writeInt(nextDriveSerial);
for (IDEState device : devices) {
device.saveState(output);
}
int currentDeviceIndex = -1;
for (int i = 0; i < devices.length; i++) {
if (currentDevice == devices[i]) {
currentDeviceIndex = i;
}
}
output.writeInt(currentDeviceIndex);
}
public void loadState(DataInput input) throws IOException {
ioBase = input.readInt();
ioBaseTwo = input.readInt();
irq = input.readInt();
nextDriveSerial = input.readInt();
for (int i = 0; i < devices.length; i++) {
devices[i].loadState(input);
}
int currentDeviceIndex = input.readInt();
currentDevice = devices[currentDeviceIndex];
}
private static void shortToBigEndianBytes(byte[] buffer, int offset, short val) {
buffer[offset + 0] = (byte) (val >> 8);
buffer[offset + 1] = (byte) (val);
}
private static void intToBigEndianBytes(byte[] buffer, int offset, int val) {
buffer[offset + 0] = (byte) (val >> 24);
buffer[offset + 1] = (byte) (val >> 16);
buffer[offset + 2] = (byte) (val >> 8);
buffer[offset + 3] = (byte) (val);
}
private static int bigEndianBytesToInt(byte[] buffer, int offset) {
int val = 0;
val |= ((buffer[offset + 0] << 24) & 0xff000000);
val |= ((buffer[offset + 1] << 16) & 0x00ff0000);
val |= ((buffer[offset + 2] << 8) & 0x0000ff00);
val |= ((buffer[offset + 3] << 0) & 0x000000ff);
return val;
}
private static short bigEndianBytesToShort(byte[] buffer, int offset) {
short val = 0;
val |= ((buffer[offset + 0] << 8) & 0xff00);
val |= ((buffer[offset + 1] << 0) & 0x00ff);
return val;
}
private static void lbaToMSF(byte[] buffer, int offset, int lba) {
lba += 150;
buffer[offset + 0] = (byte) ((lba / 75) / 60);
buffer[offset + 1] = (byte) ((lba / 75) % 60);
buffer[offset + 2] = (byte) (lba % 75);
}
private static void putLE16InByte(byte[] dest, int offset, int data) {
dest[offset + 0] = (byte) data;
dest[offset + 1] = (byte) (data >>> 8);
}
private static void stringToBytes(String text, byte[] dest, int start, int length) {
byte[] temp = text.getBytes();
int i = 0;
for (; i < Math.min(temp.length, length); i++) {
dest[(start + i) ^ 1] = temp[i];
}
for (; i < length; i++) {
dest[(start + i) ^ 1] = 0x20;
}
}
public IDEChannel(int irq, InterruptController irqDevice, int ioBase, int ioBaseTwo, BlockDevice[] drives, BMDMAIORegion bmdma) {
this.irq = irq;
this.irqDevice = irqDevice;
this.ioBase = ioBase;
this.ioBaseTwo = ioBaseTwo;
this.nextDriveSerial = 1;
devices = new IDEState[2];
devices[0] = new IDEState(drives[0]); // master
devices[1] = new IDEState(drives[1]); // slave
devices[0].bmdma = bmdma;
devices[1].bmdma = bmdma;
currentDevice = devices[0];
}
public void setDrives(BlockDevice[] drives) {
devices[0].setDrive(drives[0]);
devices[1].setDrive(drives[1]);
}
public void ioPortWrite8(int address, int data) {
if (address == ioBaseTwo) {
writeCommand(data);
return;
} else {
writeIDE(address, data);
return;
}
}
public void ioPortWrite16(int address, int data) {
switch (address - ioBase) {
case 0:
case 1:
writeDataWord(data);
break;
default:
ioPortWrite8(address, data);
ioPortWrite8(address + 1, data >>> 8);
break;
}
}
public void ioPortWrite32(int address, int data) {
switch (address - ioBase) {
case 0:
case 1:
case 2:
case 3:
writeDataLong(data);
break;
default:
ioPortWrite16(address, data);
ioPortWrite16(address + 2, data >>> 16);
break;
}
}
public int ioPortRead8(int address) {
if (address == ioBaseTwo) {
return readStatus();
} else {
return readIDE(address);
}
}
public int ioPortRead16(int address) {
switch (address - ioBase) {
case 0:
case 1:
return readDataWord();
default:
return (0xff & ioPortRead8(address)) |
(0xff00 & (ioPortRead8(address + 1) << 8));
}
}
public int ioPortRead32(int address) {
switch (address - ioBase) {
case 0:
case 1:
case 2:
case 3:
return readDataLong();
default:
return (0xffff & ioPortRead16(address)) |
(0xffff0000 & (ioPortRead16(address + 2) << 16));
}
}
public int[] ioPortsRequested() {
if (ioBaseTwo == 0) {
return new int[]{ioBase, ioBase + 1,
ioBase + 2, ioBase + 3,
ioBase + 4, ioBase + 5,
ioBase + 6, ioBase + 7
};
} else {
return new int[]{ioBase, ioBase + 1,
ioBase + 2, ioBase + 3,
ioBase + 4, ioBase + 5,
ioBase + 6, ioBase + 7,
ioBaseTwo
};
}
}
private void writeCommand(int data) {
/* common for both drives */
if (((devices[0].command & IDEState.IDE_CMD_RESET) == 0) &&
((data & IDEState.IDE_CMD_RESET) != 0)) {
/* reset low to high */
devices[0].status = (byte) (IDEState.BUSY_STAT | IDEState.SEEK_STAT);
devices[0].error = 0x01;
devices[1].status = (byte) (IDEState.BUSY_STAT | IDEState.SEEK_STAT);
devices[1].error = 0x01;
} else if (((devices[0].command & IDEState.IDE_CMD_RESET) != 0) &&
((data & IDEState.IDE_CMD_RESET) == 0)) {
/* reset high to low */
for (int i = 0; i < 2; i++) {
if (devices[i].isCDROM) {
devices[i].status = 0x00; /* NOTE: READY is not set */
} else {
devices[i].status = (byte) (IDEState.READY_STAT | IDEState.SEEK_STAT);
}
devices[i].setSignature();
}
}
devices[0].command = (byte) data;
devices[1].command = (byte) data;
}
private int readStatus() {
if (((devices[0].drive == null) && (devices[1].drive == null)) ||
((currentDevice != devices[0]) && (currentDevice.drive == null))) {
return 0;
} else {
return currentDevice.status;
}
}
private void writeIDE(int address, int data) {
boolean lba48 = false;
address &= 7;
switch (address) {
case 0:
break;
case 1:
clearHob();
/* NOTE: data is written to the two drives */
devices[0].hobFeature = devices[0].feature;
devices[1].hobFeature = devices[1].feature;
devices[0].feature = (byte) data;
devices[1].feature = (byte) data;
break;
case 2:
clearHob();
devices[0].hobNSector = (byte) devices[0].nSector;
devices[1].hobNSector = (byte) devices[1].nSector;
devices[0].nSector = 0xff & data;
devices[1].nSector = 0xff & data;
break;
case 3:
clearHob();
devices[0].hobSector = devices[0].sector;
devices[1].hobSector = devices[1].sector;
devices[0].sector = (byte) data;
devices[1].sector = (byte) data;
break;
case 4:
clearHob();
devices[0].hobLCyl = devices[0].lcyl;
devices[1].hobLCyl = devices[1].lcyl;
devices[0].lcyl = (byte) data;
devices[1].lcyl = (byte) data;
break;
case 5:
clearHob();
devices[0].hobHCyl = devices[0].hcyl;
devices[1].hobHCyl = devices[1].hcyl;
devices[0].hcyl = (byte) data;
devices[1].hcyl = (byte) data;
break;
case 6:
// FIXME: HOB readback uses bit 7
devices[0].select = (byte) ((data & ~0x10) | 0xa0);
devices[1].select = (byte) (data | 0x10 | 0xa0);
/* select drive */
currentDevice = devices[(data >> 4) & 1];
break;
default:
case 7:
/* ignore commands to non existant slave */
if (currentDevice != devices[0] && currentDevice.drive == null) {
break;
}
switch (data) {
case IDEState.WIN_IDENTIFY:
if ((currentDevice.drive != null) && !currentDevice.isCDROM) {
currentDevice.identify();
currentDevice.status = (byte) (IDEState.READY_STAT | IDEState.SEEK_STAT);
currentDevice.transferStart(currentDevice.ioBuffer, 0, 512, IDEState.ETF_TRANSFER_STOP);
} else {
if (currentDevice.isCDROM) {
currentDevice.setSignature();
}
currentDevice.abortCommand();
}
currentDevice.setIRQ();
break;
case IDEState.WIN_SPECIFY:
case IDEState.WIN_RECAL:
currentDevice.error = 0;
currentDevice.status = (byte) (IDEState.READY_STAT | IDEState.SEEK_STAT);
currentDevice.setIRQ();
break;
case IDEState.WIN_SETMULT:
if (currentDevice.nSector > IDEState.MAX_MULT_SECTORS ||
currentDevice.nSector == 0 ||
(currentDevice.nSector & (currentDevice.nSector - 1)) != 0) {
currentDevice.abortCommand();
} else {
currentDevice.multSectors = currentDevice.nSector;
currentDevice.status = IDEState.READY_STAT;
}
currentDevice.setIRQ();
break;
case IDEState.WIN_VERIFY_EXT:
lba48 = true;
case IDEState.WIN_VERIFY:
case IDEState.WIN_VERIFY_ONCE:
currentDevice.commandLBA48Transform(lba48);
/* do sector number check ? */
currentDevice.status = IDEState.READY_STAT;
currentDevice.setIRQ();
break;
case IDEState.WIN_READ_EXT:
lba48 = true;
case IDEState.WIN_READ:
case IDEState.WIN_READ_ONCE:
if (currentDevice.drive == null) {
currentDevice.abortCommand();
currentDevice.setIRQ();
return;
}
currentDevice.commandLBA48Transform(lba48);
currentDevice.requiredNumberOfSectors = 1;
currentDevice.sectorRead();
break;
case IDEState.WIN_WRITE_EXT:
lba48 = true;
case IDEState.WIN_WRITE:
case IDEState.WIN_WRITE_ONCE:
currentDevice.commandLBA48Transform(lba48);
currentDevice.error = 0;
currentDevice.status = IDEState.SEEK_STAT | IDEState.READY_STAT;
currentDevice.requiredNumberOfSectors = 1;
currentDevice.transferStart(currentDevice.ioBuffer, 0, 512, IDEState.ETF_SECTOR_WRITE);
break;
case IDEState.WIN_MULTREAD_EXT:
lba48 = true;
case IDEState.WIN_MULTREAD:
if (currentDevice.multSectors == 0) {
currentDevice.abortCommand();
currentDevice.setIRQ();
return;
}
currentDevice.commandLBA48Transform(lba48);
currentDevice.requiredNumberOfSectors = currentDevice.multSectors;
currentDevice.sectorRead();
break;
case IDEState.WIN_MULTWRITE_EXT:
lba48 = true;
case IDEState.WIN_MULTWRITE:
if (currentDevice.multSectors == 0) {
currentDevice.abortCommand();
currentDevice.setIRQ();
return;
}
currentDevice.commandLBA48Transform(lba48);
currentDevice.error = 0;
currentDevice.status = IDEState.SEEK_STAT | IDEState.READY_STAT;
currentDevice.requiredNumberOfSectors = currentDevice.multSectors;
int n = currentDevice.nSector;
if (n > currentDevice.requiredNumberOfSectors) {
n = currentDevice.requiredNumberOfSectors;
}
currentDevice.transferStart(currentDevice.ioBuffer, 0, 512 * n, IDEState.ETF_SECTOR_WRITE);
break;
case IDEState.WIN_READDMA_EXT:
lba48 = true;
case IDEState.WIN_READDMA:
case IDEState.WIN_READDMA_ONCE:
if (currentDevice.drive == null) {
currentDevice.abortCommand();
currentDevice.setIRQ();
return;
}
currentDevice.commandLBA48Transform(lba48);
currentDevice.sectorReadDMA();
break;
case IDEState.WIN_WRITEDMA_EXT:
lba48 = true;
case IDEState.WIN_WRITEDMA:
case IDEState.WIN_WRITEDMA_ONCE:
if (currentDevice.drive == null) {
currentDevice.abortCommand();
currentDevice.setIRQ();
return;
}
currentDevice.commandLBA48Transform(lba48);
currentDevice.sectorWriteDMA();
break;
case IDEState.WIN_READ_NATIVE_MAX_EXT:
lba48 = true;
case IDEState.WIN_READ_NATIVE_MAX:
currentDevice.commandLBA48Transform(lba48);
currentDevice.setSector(currentDevice.drive.getTotalSectors() - 1);
currentDevice.status = IDEState.READY_STAT;
currentDevice.setIRQ();
break;
case IDEState.WIN_CHECKPOWERMODE1:
currentDevice.nSector = 0xff; /* device active or idle */
currentDevice.status = IDEState.READY_STAT;
currentDevice.setIRQ();
break;
case IDEState.WIN_SETFEATURES:
if (currentDevice.drive == null) {
currentDevice.abortCommand();
currentDevice.setIRQ();
return;
}
/* XXX: valid for CDROM ? */
switch (currentDevice.feature) {
case (byte) 0x02: /* write cache enable */
case (byte) 0x82: /* write cache disable */
case (byte) 0xaa: /* read look-ahead enable */
case (byte) 0x55: /* read look-ahead disable */
currentDevice.status = IDEState.READY_STAT | IDEState.SEEK_STAT;
currentDevice.setIRQ();
break;
case (byte) 0x03: /* set transfer mode */
int val = currentDevice.nSector & 0x07;
switch (currentDevice.nSector >>> 3) {
case 0x00: // pio default
case 0x01: // pio mode
putLE16InByte(currentDevice.identifyData, 126, 0x07);
putLE16InByte(currentDevice.identifyData, 176, 0x3f);
break;
case 0x04: /* mdma mode */
putLE16InByte(currentDevice.identifyData, 126, 0x07 | (1 << (val + 8)));
putLE16InByte(currentDevice.identifyData, 176, 0x3f);
break;
case 0x08: /* udma mode */
putLE16InByte(currentDevice.identifyData, 126, 0x07);
putLE16InByte(currentDevice.identifyData, 176, 0x3f | (1 << (val + 8)));
break;
default:
currentDevice.abortCommand();
currentDevice.setIRQ();
return;
}
currentDevice.status = IDEState.READY_STAT | IDEState.SEEK_STAT;
currentDevice.setIRQ();
break;
default:
currentDevice.abortCommand();
currentDevice.setIRQ();
return;
}
break;
case IDEState.WIN_FLUSH_CACHE:
case IDEState.WIN_FLUSH_CACHE_EXT:
if (currentDevice.drive != null) {
LOGGING.log(Level.INFO, "Should flush {0}", currentDevice.drive);
}
currentDevice.status = IDEState.READY_STAT;
currentDevice.setIRQ();
break;
case IDEState.WIN_STANDBYNOW1:
case IDEState.WIN_IDLEIMMEDIATE:
currentDevice.status = IDEState.READY_STAT;
currentDevice.setIRQ();
break;
/* ATAPI commands */
case IDEState.WIN_PIDENTIFY:
if (currentDevice.isCDROM) {
currentDevice.atapiIdentify();
currentDevice.status = IDEState.READY_STAT | IDEState.SEEK_STAT;
currentDevice.transferStart(currentDevice.ioBuffer, 0, 512, IDEState.ETF_TRANSFER_STOP);
} else {
currentDevice.abortCommand();
}
currentDevice.setIRQ();
break;
case IDEState.WIN_DIAGNOSE:
currentDevice.setSignature();
currentDevice.status = 0x00;
currentDevice.error = 0x01;
break;
case IDEState.WIN_SRST:
if (!currentDevice.isCDROM) {
currentDevice.abortCommand();
currentDevice.setIRQ();
return;
}
currentDevice.setSignature();
currentDevice.status = 0x00; /* NOTE: READY is _not_ set */
currentDevice.error = 0x01;
break;
case IDEState.WIN_PACKETCMD:
if (!currentDevice.isCDROM) {
currentDevice.abortCommand();
currentDevice.setIRQ();
return;
}
/* overlapping commands not supported */
if ((currentDevice.feature & 0x02) != 0) {
currentDevice.abortCommand();
currentDevice.setIRQ();
return;
}
currentDevice.atapiDMA = ((currentDevice.feature & 1) == 1);
currentDevice.nSector = 1;
currentDevice.transferStart(currentDevice.ioBuffer, 0, IDEState.ATAPI_PACKET_SIZE, IDEState.ETF_ATAPI_COMMAND);
break;
default:
currentDevice.abortCommand();
currentDevice.setIRQ();
return;
}
}
}
private int readIDE(int address) {
address &= 0x7;
//boolean hob = (currentDevice.select & (1 << 7)) != 0;
boolean hob = false;
switch (address) {
case 0:
return 0xff;
case 1:
if (devices[0].drive == null && devices[1].drive == null) {
return 0;
} else if (!hob) {
return currentDevice.error;
} else {
return currentDevice.hobFeature;
}
case 2:
if (devices[0].drive == null && devices[1].drive == null) {
return 0;
} else if (!hob) {
return currentDevice.nSector & 0xff;
} else {
return currentDevice.hobNSector;
}
case 3:
if (devices[0].drive == null && devices[1].drive == null) {
return 0;
} else if (!hob) {
return currentDevice.sector;
} else {
return currentDevice.hobSector;
}
case 4:
if (devices[0].drive == null && devices[1].drive == null) {
return 0;
} else if (!hob) {
return currentDevice.lcyl;
} else {
return currentDevice.hobLCyl;
}
case 5:
if (devices[0].drive == null && devices[1].drive == null) {
return 0;
} else if (!hob) {
return currentDevice.hcyl;
} else {
return currentDevice.hobHCyl;
}
case 6:
if (devices[0].drive == null && devices[1].drive == null) {
return 0;
} else {
return currentDevice.select;
}
default:
case 7:
if ((devices[0].drive == null && devices[1].drive == null) ||
(currentDevice != devices[0] && currentDevice.drive == null)) {
irqDevice.setIRQ(irq, 0);
return 0;
} else {
irqDevice.setIRQ(irq, 0);
return currentDevice.status;
}
}
}
private int readDataWord() {
int data = 0;
data |= 0xff & currentDevice.dataBuffer[currentDevice.dataBufferOffset++];
data |= 0xff00 & (currentDevice.dataBuffer[currentDevice.dataBufferOffset++] << 8);
if (currentDevice.dataBufferOffset >= currentDevice.dataBufferEnd) {
currentDevice.endTransfer(currentDevice.endTransferFunction);
}
return data;
}
private int readDataLong() {
int data = 0;
data |= 0xff & currentDevice.dataBuffer[currentDevice.dataBufferOffset++];
data |= 0xff00 & (currentDevice.dataBuffer[currentDevice.dataBufferOffset++] << 8);
data |= 0xff0000 & (currentDevice.dataBuffer[currentDevice.dataBufferOffset++] << 16);
data |= 0xff000000 & (currentDevice.dataBuffer[currentDevice.dataBufferOffset++] << 24);
if (currentDevice.dataBufferOffset >= currentDevice.dataBufferEnd) {
currentDevice.endTransfer(currentDevice.endTransferFunction);
}
return data;
}
private void writeDataWord(int data) {
currentDevice.dataBuffer[currentDevice.dataBufferOffset++] = (byte) (data);
currentDevice.dataBuffer[currentDevice.dataBufferOffset++] = (byte) (data >> 8);
if (currentDevice.dataBufferOffset >= currentDevice.dataBufferEnd) {
currentDevice.endTransfer(currentDevice.endTransferFunction);
}
}
private void writeDataLong(int data) {
currentDevice.dataBuffer[currentDevice.dataBufferOffset++] = (byte) (data);
currentDevice.dataBuffer[currentDevice.dataBufferOffset++] = (byte) (data >> 8);
currentDevice.dataBuffer[currentDevice.dataBufferOffset++] = (byte) (data >> 16);
currentDevice.dataBuffer[currentDevice.dataBufferOffset++] = (byte) (data >> 24);
if (currentDevice.dataBufferOffset >= currentDevice.dataBufferEnd) {
currentDevice.endTransfer(currentDevice.endTransferFunction);
}
}
private void clearHob() {
/* any write clears HOB high bit of device control register */
devices[0].select &= ~(1 << 7);
devices[1].select &= ~(1 << 7);
}
class IDEState implements Hibernatable {
/* Bits of HD_STATUS */
public static final int ERR_STAT = 0x01;
public static final int INDEX_STAT = 0x02;
public static final int ECC_STAT = 0x04;/* Corrected error */
public static final int DRQ_STAT = 0x08;
public static final int SEEK_STAT = 0x10;
public static final int SRV_STAT = 0x10;
public static final int WRERR_STAT = 0x20;
public static final int READY_STAT = 0x40;
public static final int BUSY_STAT = 0x80;
/* Bits for HD_ERROR */
private static final int MARK_ERR = 0x01; /* Bad address mark */
private static final int TRK0_ERR = 0x02; /* couldn't find track 0 */
private static final int ABRT_ERR = 0x04; /* Command aborted */
private static final int MCR_ERR = 0x08; /* media change request */
private static final int ID_ERR = 0x10; /* ID field not found */
private static final int MC_ERR = 0x20; /* media changed */
private static final int ECC_ERR = 0x40; /* Uncorrectable ECC error */
private static final int BBD_ERR = 0x80; /* pre-EIDE meaning: block marked bad */
private static final int ICRC_ERR = 0x80; /* new meaning: CRC error during transfer */
public static final int IDE_CMD_RESET = 0x04;
public static final int IDE_CMD_DISABLE_IRQ = 0x02;
/* ATA/ATAPI Commands pre T13 Spec */
public static final int WIN_NOP = 0x00;
/*
* 0x01->0x02 Reserved
*/
public static final int CFA_REQ_EXT_ERROR_CODE = 0x03; /* CFA Request Extended Error Code
*/
/*
* 0x04->0x07 Reserved
*/
public static final int WIN_SRST = 0x08; /* ATAPI soft reset command */
public static final int WIN_DEVICE_RESET = 0x08;
/*
* 0x09->0x0F Reserved
*/
public static final int WIN_RECAL = 0x10;
public static final int WIN_RESTORE = WIN_RECAL;
/*
* 0x10->0x1F Reserved
*/
public static final int WIN_READ = 0x20; /* 28-Bit */
public static final int WIN_READ_ONCE = 0x21; /* 28-Bit without retries */
public static final int WIN_READ_LONG = 0x22; /* 28-Bit */
public static final int WIN_READ_LONG_ONCE = 0x23; /* 28-Bit without retries */
public static final int WIN_READ_EXT = 0x24; /* 48-Bit */
public static final int WIN_READDMA_EXT = 0x25; /* 48-Bit */
public static final int WIN_READDMA_QUEUED_EXT = 0x26; /* 48-Bit */
public static final int WIN_READ_NATIVE_MAX_EXT = 0x27; /* 48-Bit */
/*
* 0x28
*/
public static final int WIN_MULTREAD_EXT = 0x29; /* 48-Bit */
/*
* 0x2A->0x2F Reserved
*/
public static final int WIN_WRITE = 0x30; /* 28-Bit */
public static final int WIN_WRITE_ONCE = 0x31; /* 28-Bit without retries */
public static final int WIN_WRITE_LONG = 0x32; /* 28-Bit */
public static final int WIN_WRITE_LONG_ONCE = 0x33; /* 28-Bit without retries */
public static final int WIN_WRITE_EXT = 0x34; /* 48-Bit */
public static final int WIN_WRITEDMA_EXT = 0x35; /* 48-Bit */
public static final int WIN_WRITEDMA_QUEUED_EXT = 0x36; /* 48-Bit */
public static final int WIN_SET_MAX_EXT = 0x37; /* 48-Bit */
public static final int CFA_WRITE_SECT_WO_ERASE = 0x38; /* CFA Write Sectors without erase
*/
public static final int WIN_MULTWRITE_EXT = 0x39; /* 48-Bit */
/*
* 0x3A->0x3B Reserved
*/
public static final int WIN_WRITE_VERIFY = 0x3C; /* 28-Bit */
/*
* 0x3D->0x3F Reserved
*/
public static final int WIN_VERIFY = 0x40; /* 28-Bit - Read Verify Sectors */
public static final int WIN_VERIFY_ONCE = 0x41; /* 28-Bit - without retries */
public static final int WIN_VERIFY_EXT = 0x42; /* 48-Bit */
/*
* 0x43->0x4F Reserved
*/
public static final int WIN_FORMAT = 0x50;
/*
* 0x51->0x5F Reserved
*/
public static final int WIN_INIT = 0x60;
/*
* 0x61->0x5F Reserved
*/
public static final int WIN_SEEK = 0x70; /* 0x70-0x7F Reserved */
public static final int CFA_TRANSLATE_SECTOR = 0x87; /* CFA Translate Sector */
public static final int WIN_DIAGNOSE = 0x90;
public static final int WIN_SPECIFY = 0x91; /* set drive geometry translation */
public static final int WIN_DOWNLOAD_MICROCODE = 0x92;
public static final int WIN_STANDBYNOW2 = 0x94;
public static final int WIN_STANDBY2 = 0x96;
public static final int WIN_SETIDLE2 = 0x97;
public static final int WIN_CHECKPOWERMODE2 = 0x98;
public static final int WIN_SLEEPNOW2 = 0x99;
/*
* 0x9A VENDOR
*/
public static final int WIN_PACKETCMD = 0xA0; /* Send a packet command. */
public static final int WIN_PIDENTIFY = 0xA1; /* identify ATAPI device */
public static final int WIN_QUEUED_SERVICE = 0xA2;
public static final int WIN_SMART = 0xB0; /* self-monitoring and reporting */
public static final int CFA_ERASE_SECTORS = 0xC0;
public static final int WIN_MULTREAD = 0xC4; /* read sectors using multiple mode
*/
public static final int WIN_MULTWRITE = 0xC5; /* write sectors using multiple mod
e */
public static final int WIN_SETMULT = 0xC6; /* enable/disable multiple mode */
public static final int WIN_READDMA_QUEUED = 0xC7; /* read sectors using Queued DMA tr
ansfers */
public static final int WIN_READDMA = 0xC8; /* read sectors using DMA transfers
*/
public static final int WIN_READDMA_ONCE = 0xC9; /* 28-Bit - without retries */
public static final int WIN_WRITEDMA = 0xCA; /* write sectors using DMA transfer
s */
public static final int WIN_WRITEDMA_ONCE = 0xCB; /* 28-Bit - without retries */
public static final int WIN_WRITEDMA_QUEUED = 0xCC; /* write sectors using Queued DMA t
ransfers */
public static final int CFA_WRITE_MULTI_WO_ERASE = 0xCD; /* CFA Write multiple without erase
*/
public static final int WIN_GETMEDIASTATUS = 0xDA;
public static final int WIN_ACKMEDIACHANGE = 0xDB; /* ATA-1, ATA-2 vendor */
public static final int WIN_POSTBOOT = 0xDC;
public static final int WIN_PREBOOT = 0xDD;
public static final int WIN_DOORLOCK = 0xDE; /* lock door on removable drives */
public static final int WIN_DOORUNLOCK = 0xDF; /* unlock door on removable drives
*/
public static final int WIN_STANDBYNOW1 = 0xE0;
public static final int WIN_IDLEIMMEDIATE = 0xE1; /* force drive to become "ready" */
public static final int WIN_STANDBY = 0xE2; /* Set device in Standby Mode */
public static final int WIN_SETIDLE1 = 0xE3;
public static final int WIN_READ_BUFFER = 0xE4; /* force read only 1 sector */
public static final int WIN_CHECKPOWERMODE1 = 0xE5;
public static final int WIN_SLEEPNOW1 = 0xE6;
public static final int WIN_FLUSH_CACHE = 0xE7;
public static final int WIN_WRITE_BUFFER = 0xE8; /* force write only 1 sector */
public static final int WIN_WRITE_SAME = 0xE9; /* read ata-2 to use */
/* SET_FEATURES 0x22 or 0xDD */
public static final int WIN_FLUSH_CACHE_EXT = 0xEA; /* 48-Bit */
public static final int WIN_IDENTIFY = 0xEC; /* ask drive to identify itself
*/
public static final int WIN_MEDIAEJECT = 0xED;
public static final int WIN_IDENTIFY_DMA = 0xEE; /* same as WIN_IDENTIFY, but DMA */
public static final int WIN_SETFEATURES = 0xEF; /* set special drive features */
public static final int EXABYTE_ENABLE_NEST = 0xF0;
public static final int WIN_SECURITY_SET_PASS = 0xF1;
public static final int WIN_SECURITY_UNLOCK = 0xF2;
public static final int WIN_SECURITY_ERASE_PREPARE = 0xF3;
public static final int WIN_SECURITY_ERASE_UNIT = 0xF4;
public static final int WIN_SECURITY_FREEZE_LOCK = 0xF5;
public static final int WIN_SECURITY_DISABLE = 0xF6;
public static final int WIN_READ_NATIVE_MAX = 0xF8; /* return the native maximum addres
s */
public static final int WIN_SET_MAX = 0xF9;
public static final int DISABLE_SEAGATE = 0xFB;
/* set to 1 set disable mult support */
public static final int MAX_MULT_SECTORS = 16;
/* ATAPI defines */
public static final int ATAPI_PACKET_SIZE = 12;
/* The generic packet command opcodes for CD/DVD Logical Units,
* From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
public static final int GPCMD_BLANK = 0xa1;
public static final int GPCMD_CLOSE_TRACK = 0x5b;
public static final int GPCMD_FLUSH_CACHE = 0x35;
public static final int GPCMD_FORMAT_UNIT = 0x04;
public static final int GPCMD_GET_CONFIGURATION = 0x46;
public static final int GPCMD_GET_EVENT_STATUS_NOTIFICATION = 0x4a;
public static final int GPCMD_GET_PERFORMANCE = 0xac;
public static final int GPCMD_INQUIRY = 0x12;
public static final int GPCMD_LOAD_UNLOAD = 0xa6;
public static final int GPCMD_MECHANISM_STATUS = 0xbd;
public static final int GPCMD_MODE_SELECT_10 = 0x55;
public static final int GPCMD_MODE_SENSE_10 = 0x5a;
public static final int GPCMD_PAUSE_RESUME = 0x4b;
public static final int GPCMD_PLAY_AUDIO_10 = 0x45;
public static final int GPCMD_PLAY_AUDIO_MSF = 0x47;
public static final int GPCMD_PLAY_AUDIO_TI = 0x48;
public static final int GPCMD_PLAY_CD = 0xbc;
public static final int GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1e;
public static final int GPCMD_READ_10 = 0x28;
public static final int GPCMD_READ_12 = 0xa8;
public static final int GPCMD_READ_CDVD_CAPACITY = 0x25;
public static final int GPCMD_READ_CD = 0xbe;
public static final int GPCMD_READ_CD_MSF = 0xb9;
public static final int GPCMD_READ_DISC_INFO = 0x51;
public static final int GPCMD_READ_DVD_STRUCTURE = 0xad;
public static final int GPCMD_READ_FORMAT_CAPACITIES = 0x23;
public static final int GPCMD_READ_HEADER = 0x44;
public static final int GPCMD_READ_TRACK_RZONE_INFO = 0x52;
public static final int GPCMD_READ_SUBCHANNEL = 0x42;
public static final int GPCMD_READ_TOC_PMA_ATIP = 0x43;
;
public static final int GPCMD_REPAIR_RZONE_TRACK = 0x58;
public static final int GPCMD_REPORT_KEY = 0xa4;
public static final int GPCMD_REQUEST_SENSE = 0x03;
public static final int GPCMD_RESERVE_RZONE_TRACK = 0x53;
public static final int GPCMD_SCAN = 0xba;
public static final int GPCMD_SEEK = 0x2b;
public static final int GPCMD_SEND_DVD_STRUCTURE = 0xad;
public static final int GPCMD_SEND_EVENT = 0xa2;
public static final int GPCMD_SEND_KEY = 0xa3;
public static final int GPCMD_SEND_OPC = 0x54;
public static final int GPCMD_SET_READ_AHEAD = 0xa7;
public static final int GPCMD_SET_STREAMING = 0xb6;
public static final int GPCMD_START_STOP_UNIT = 0x1b;
public static final int GPCMD_STOP_PLAY_SCAN = 0x4e;
public static final int GPCMD_TEST_UNIT_READY = 0x00;
public static final int GPCMD_VERIFY_10 = 0x2f;
public static final int GPCMD_WRITE_10 = 0x2a;
public static final int GPCMD_WRITE_AND_VERIFY_10 = 0x2e;
/* This is listed as optional in ATAPI 2.6, but is (curiously)
* missing from Mt. Fuji, Table 57. It _is_ mentioned in Mt. Fuji
* Table 377 as an MMC command for SCSi devices though... Most ATAPI
* drives support it. */
public static final int GPCMD_SET_SPEED = 0xbb;
/* This seems to be a SCSI specific CD-ROM opcode
* to play data at track/index */
public static final int GPCMD_PLAYAUDIO_TI = 0x48;
/*
* From MS Media Status Notification Support Specification. For
* older drives only.
*/
public static final int GPCMD_GET_MEDIA_STATUS = 0xda;
/* Mode page codes for mode sense/set */
public static final int GPMODE_R_W_ERROR_PAGE = 0x01;
public static final int GPMODE_WRITE_PARMS_PAGE = 0x05;
public static final int GPMODE_AUDIO_CTL_PAGE = 0x0e;
public static final int GPMODE_POWER_PAGE = 0x1a;
public static final int GPMODE_FAULT_FAIL_PAGE = 0x1c;
public static final int GPMODE_TO_PROTECT_PAGE = 0x1d;
public static final int GPMODE_CAPABILITIES_PAGE = 0x2a;
public static final int GPMODE_ALL_PAGES = 0x3f;
/* Not in Mt. Fuji, but in ATAPI 2.6 -- depricated now in favor
* of MODE_SENSE_POWER_PAGE */
public static final int GPMODE_CDROM_PAGE = 0x0d;
public static final int ATAPI_INT_REASON_CD = 0x01; /* 0 = data transfer */
public static final int ATAPI_INT_REASON_IO = 0x02; /* 1 = transfer to the host */
public static final int ATAPI_INT_REASON_REL = 0x04;
public static final int ATAPI_INT_REASON_TAG = 0xf8;
/* same constants as bochs */
public static final int ASC_ILLEGAL_OPCODE = 0x20;
public static final int ASC_LOGICAL_BLOCK_OOR = 0x21;
public static final int ASC_INV_FIELD_IN_CMD_PACKET = 0x24;
public static final int ASC_MEDIUM_NOT_PRESENT = 0x3a;
public static final int ASC_SAVING_PARAMETERS_NOT_SUPPORTED = 0x39;
public static final int SENSE_NONE = 0;
public static final int SENSE_NOT_READY = 2;
public static final int SENSE_ILLEGAL_REQUEST = 5;
public static final int SENSE_UNIT_ATTENTION = 6;
public static final int ETF_TRANSFER_STOP = 1;
public static final int ETF_SECTOR_WRITE = 2;
public static final int ETF_SECTOR_READ = 3;
public static final int ETF_ATAPI_COMMAND = 4;
public static final int ETF_ATAPI_COMMAND_REPLY_END = 5;
public static final int ETF_DUMMY_TRANSFER_STOP = 6;
public static final int IDF_NONE = 0;
public static final int IDF_WRITE_DMA_CB = 1;
public static final int IDF_READ_DMA_CB = 2;
public static final int IDF_ATAPI_READ_DMA_CB = 3;
public static final String HD_VERSION = "0.01";
private int cylinders, heads, sectors;
public byte status, command, error, feature, select;
public byte hcyl, lcyl;
public byte sector;
public int nSector;
public int endTransferFunction;
public boolean isCDROM;
public boolean atapiDMA;
public int requiredNumberOfSectors;
public int multSectors;
public int driveSerial; //lba48 support
public byte hobFeature;
public byte hobNSector;
public byte hobSector;
public byte hobLCyl;
public byte hobHCyl;
public boolean lba48;
public byte[] ioBuffer;
private int ioBufferSize;
private int ioBufferIndex;
public byte[] dataBuffer;
public int dataBufferOffset;
public int dataBufferEnd;
public boolean identifySet;
public byte[] identifyData;
public byte senseKey;
public byte asc;
public int elementaryTransferSize;
public int packetTransferSize;
public int lba;
public int cdSectorSize;
//public long numberOfSectors; //forwarded through to blockdevice, prevents need for cdrom callback
public BlockDevice drive;
public BMDMAIORegion bmdma;
public IDEState(BlockDevice drive) {
this.drive = drive;
if (drive != null) {
//this.numberOfSectors = drive.getTotalSectors();
this.cylinders = drive.getCylinders();
this.heads = drive.getHeads();
this.sectors = drive.getSectors();
if (drive.getType() == BlockDevice.Type.CDROM) {
this.isCDROM = true;
}
}
ioBuffer = new byte[MAX_MULT_SECTORS * 512 + 4];
identifyData = new byte[512];
identifySet = false;
lba48 = false;
this.driveSerial = nextDriveSerial++;
reset();
}
public void setDrive(BlockDevice drive) {
this.drive = drive;
}
public void saveState(DataOutput output) throws IOException {
output.writeInt(cylinders);
output.writeInt(heads);
output.writeInt(sectors);
output.writeByte(status);
output.writeByte(command);
output.writeByte(error);
output.writeByte(feature);
output.writeByte(select);
output.writeByte(hcyl);
output.writeByte(lcyl);
output.writeByte(sector);
output.writeInt(nSector);
output.writeInt(endTransferFunction);
output.writeBoolean(isCDROM);
output.writeBoolean(atapiDMA);
output.writeInt(requiredNumberOfSectors);
output.writeInt(multSectors);
output.writeInt(driveSerial);
output.writeByte(hobFeature);
output.writeByte(hobNSector);
output.writeByte(hobSector);
output.writeByte(hobLCyl);
output.writeByte(hobHCyl);
output.writeBoolean(lba48);
output.writeInt(ioBuffer.length);
output.write(ioBuffer);
output.writeInt(ioBufferSize);
output.writeInt(ioBufferIndex);
output.writeInt(dataBuffer.length);
output.write(dataBuffer);
output.writeInt(dataBufferOffset);
output.writeInt(dataBufferEnd);
output.writeBoolean(identifySet);
output.writeInt(identifyData.length);
output.write(identifyData);
output.writeByte(senseKey);
output.writeByte(asc);
output.writeInt(elementaryTransferSize);
output.writeInt(packetTransferSize);
output.writeInt(lba);
output.writeInt(cdSectorSize);
}
public void loadState(DataInput input) throws IOException {
cylinders = input.readInt();
heads = input.readInt();
sectors = input.readInt();
status = input.readByte();
command = input.readByte();
error = input.readByte();
feature = input.readByte();
select = input.readByte();
hcyl = input.readByte();
lcyl = input.readByte();
sector = input.readByte();
nSector = input.readInt();
endTransferFunction = input.readInt();
isCDROM = input.readBoolean();
atapiDMA = input.readBoolean();
requiredNumberOfSectors = input.readInt();
multSectors = input.readInt();
driveSerial = input.readInt();
hobFeature = input.readByte();
hobNSector = input.readByte();
hobSector = input.readByte();
hobLCyl = input.readByte();
hobHCyl = input.readByte();
lba48 = input.readBoolean();
int len = input.readInt();
ioBuffer = new byte[len];
input.readFully(ioBuffer, 0, len);
ioBufferSize = input.readInt();
ioBufferIndex = input.readInt();
len = input.readInt();
dataBuffer = new byte[len];
input.readFully(dataBuffer, 0, len);
dataBufferOffset = input.readInt();
dataBufferEnd = input.readInt();
identifySet = input.readBoolean();
len = input.readInt();
identifyData = new byte[len];
input.readFully(identifyData, 0, len);
senseKey = input.readByte();
asc = input.readByte();
elementaryTransferSize = input.readInt();
packetTransferSize = input.readInt();
lba = input.readInt();
cdSectorSize = input.readInt();
bmdma.setIDEDevice(this);
}
public int dmaCallback(int ideDMAFunction, int address, int size) {
switch (ideDMAFunction) {
case IDF_ATAPI_READ_DMA_CB:
return atapiCommandReadDMACallback(address, size);
// case IDF_READ_DMA_CB:
// return readDMACallback(address, size);
default:
LOGGING.log(Level.WARNING, "Need DMA callback function {0,number,integer}", Integer.valueOf(ideDMAFunction));
return 0;
}
}
public void setSignature() {
select &= 0xf0; /* clear head */
/* put signature */
nSector = 1;
sector = 1;
if (isCDROM) {
lcyl = (byte) 0x14;
hcyl = (byte) 0xeb;
} else if (drive != null) {
lcyl = 0;
hcyl = 0;
} else {
lcyl = (byte) 0xff;
hcyl = (byte) 0xff;
}
}
public void setIRQ() {
if ((command & IDE_CMD_DISABLE_IRQ) == 0) {
if (bmdma != null) {
bmdma.setIRQ();
}
irqDevice.setIRQ(irq, 1);
}
}
public void abortCommand() {
status = READY_STAT | ERR_STAT;
error = ABRT_ERR;
}
public void atapiIdentify() {
if (identifySet) {
System.arraycopy(identifyData, 0, ioBuffer, 0, identifyData.length);
return;
}
for (int i = 0; i < 512; i++) {
ioBuffer[i] = (byte) 0;
}
putLE16InByte(ioBuffer, 0, (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0));
stringToBytes("JPC" + driveSerial, ioBuffer, 20, 20);
putLE16InByte(ioBuffer, 40, 3); /* XXX: retired, remove ? */
putLE16InByte(ioBuffer, 42, 512); /* cache size in sectors */
putLE16InByte(ioBuffer, 44, 4); /* ecc bytes */
stringToBytes(HD_VERSION, ioBuffer, 46, 8);
stringToBytes(CDLABEL, ioBuffer, 54, 40);
putLE16InByte(ioBuffer, 96, 1); /* dword I/O */
putLE16InByte(ioBuffer, 98, (1 << 9)); /* DMA and LBA supported */
putLE16InByte(ioBuffer, 106, 3); /* words 54-58, 64-70 are valid */
putLE16InByte(ioBuffer, 126, 0x103);
putLE16InByte(ioBuffer, 128, 1);
putLE16InByte(ioBuffer, 130, 0xb4);
putLE16InByte(ioBuffer, 132, 0xb4);
putLE16InByte(ioBuffer, 134, 0x12c);
putLE16InByte(ioBuffer, 136, 0xb4);
putLE16InByte(ioBuffer, 142, 30);
putLE16InByte(ioBuffer, 144, 30);
putLE16InByte(ioBuffer, 160, 0x1e);
System.arraycopy(ioBuffer, 0, identifyData, 0, identifyData.length);
identifySet = true;
}
public void setSector(long sectorNumber) {
if ((select & 0x40) != 0) {
if (!lba48) {
select = (byte) ((select & 0xf0) | (sectorNumber >>> 24));
hcyl = (byte) (sectorNumber >>> 16);
lcyl = (byte) (sectorNumber >>> 8);
sector = (byte) sectorNumber;
} else {
sector = (byte) sectorNumber;
lcyl = (byte) (sectorNumber >>> 8);
hcyl = (byte) (sectorNumber >>> 16);
hobSector = (byte) (sectorNumber >>> 24);
hobLCyl = (byte) (sectorNumber >>> 32);
hobHCyl = (byte) (sectorNumber >>> 40);
}
} else {
int cyl = (int) (sectorNumber / (heads * sectors));
int r = (int) (sectorNumber % (heads * sectors));
hcyl = (byte) (cyl >>> 8);
lcyl = (byte) (cyl);
select = (byte) ((select & 0xf0) | ((r / sectors) & 0x0f));
sector = (byte) ((r % sectors) + 1);
}
}
public void sectorWriteDMA() {
status = READY_STAT | SEEK_STAT | DRQ_STAT;
int n = nSector;
if (n > MAX_MULT_SECTORS) {
n = MAX_MULT_SECTORS;
}
ioBufferIndex = 0;
ioBufferSize = n * 512;
dmaStart(IDF_WRITE_DMA_CB);
}
public void sectorReadDMA() {
status = READY_STAT | SEEK_STAT | DRQ_STAT;
ioBufferIndex = 0;
ioBufferSize = 0;
dmaStart(IDF_READ_DMA_CB);
}
public void sectorWrite() {
status = READY_STAT | SEEK_STAT;
long sectorNumber = getSector();
int n = nSector;
if (n > requiredNumberOfSectors) {
n = requiredNumberOfSectors;
}
drive.write(sectorNumber, ioBuffer, n);
nSector -= n;
if (nSector == 0) {
transferStop();
} else {
int n1 = nSector;
if (n1 > requiredNumberOfSectors) {
n1 = requiredNumberOfSectors;
}
transferStart(ioBuffer, 0, 512 * n1, ETF_SECTOR_WRITE);
}
setSector(sectorNumber + n);
setIRQ();
}
public void sectorRead() {
status = READY_STAT | SEEK_STAT;
error = 0; /* not needed by IDE spec, but needed by Windows */
long sectorNumber = getSector();
int n = nSector;
if (n == 0) // no more sectors to read from disk
{
transferStop();
} else {
n = Math.min(n, requiredNumberOfSectors);
drive.read(sectorNumber, ioBuffer, n);
transferStart(ioBuffer, 0, 512 * n, ETF_SECTOR_READ);
setIRQ();
setSector(sectorNumber + n);
nSector -= n;
}
}
public void identify() {
if (identifySet) {
System.arraycopy(identifyData, 0, ioBuffer, 0, identifyData.length);
return;
}
for (int i = 0; i < 512; i++) {
ioBuffer[i] = (byte) 0;
}
putLE16InByte(ioBuffer, 0, 0x0040);
putLE16InByte(ioBuffer, 2, cylinders);
putLE16InByte(ioBuffer, 6, heads);
putLE16InByte(ioBuffer, 8, 512 * sectors); /* XXX: retired, remove ? */
putLE16InByte(ioBuffer, 10, 512); /* XXX: retired, remove ? */
putLE16InByte(ioBuffer, 12, sectors);
stringToBytes("JPC" + driveSerial, ioBuffer, 20, 20);
putLE16InByte(ioBuffer, 40, 3); /* XXX: retired, remove ? */
putLE16InByte(ioBuffer, 42, 512); /* cache size in sectors */
putLE16InByte(ioBuffer, 44, 4); /* ecc bytes */
stringToBytes(HD_VERSION, ioBuffer, 46, 8);
stringToBytes("Generic 1234", ioBuffer, 54, 40); //TODO revert to "JPC HARDDISK"
putLE16InByte(ioBuffer, 94, 0x8000 | MAX_MULT_SECTORS);
putLE16InByte(ioBuffer, 96, 1); /* dword I/O */
putLE16InByte(ioBuffer, 98, (1 << 11) | (1 << 9) | (1 << 8)); /* DMA and LBA supported */
putLE16InByte(ioBuffer, 102, 0x200); /* PIO transfer cycle */
putLE16InByte(ioBuffer, 104, 0x200); /* DMA transfer cycle */
putLE16InByte(ioBuffer, 106, 1 | (1 << 1) | (1 << 2)); /* words 54-58, 64-70, 88 are valid */
putLE16InByte(ioBuffer, 108, cylinders);
putLE16InByte(ioBuffer, 110, heads);
putLE16InByte(ioBuffer, 112, sectors);
int oldsize = cylinders * heads * sectors;
putLE16InByte(ioBuffer, 114, oldsize);
putLE16InByte(ioBuffer, 116, oldsize >>> 16);
if (multSectors != 0) {
putLE16InByte(ioBuffer, 118, 0x100 | multSectors);
}
putLE16InByte(ioBuffer, 120, (short) drive.getTotalSectors());
putLE16InByte(ioBuffer, 122, (short) (drive.getTotalSectors() >>> 16));
putLE16InByte(ioBuffer, 126, 0x07); // mdma0-2 supported
putLE16InByte(ioBuffer, 130, 120);
putLE16InByte(ioBuffer, 132, 120);
putLE16InByte(ioBuffer, 134, 120);
putLE16InByte(ioBuffer, 136, 120);
putLE16InByte(ioBuffer, 160, 0xf0); // ata3 -> ata6 supported
putLE16InByte(ioBuffer, 162, 0x16); // conforms to ata5
putLE16InByte(ioBuffer, 164, 1 << 14);
putLE16InByte(ioBuffer, 166, (1 << 14) | (1 << 13) | (1 << 12));
//putLE16InByte(ioBuffer, 166, (1 << 14) | (1 << 13) | (1 << 12) | (1 << 10));
putLE16InByte(ioBuffer, 168, 1 << 14);
putLE16InByte(ioBuffer, 170, 1 << 14);
putLE16InByte(ioBuffer, 172, (1 << 14) | (1 << 13) | (1 << 12));
//putLE16InByte(ioBuffer, 172, (1 << 14) | (1 << 13) | (1 << 12) | (1 << 10));
putLE16InByte(ioBuffer, 174, 1 << 14);
putLE16InByte(ioBuffer, 176, 0x3f | (1 << 13));
putLE16InByte(ioBuffer, 186, 1 | (1 << 14) | 0x2000);
putLE16InByte(ioBuffer, 200, nSector);
putLE16InByte(ioBuffer, 202, nSector >>> 16);
putLE16InByte(ioBuffer, 204, /*nSector >>> 32*/ 0);
putLE16InByte(ioBuffer, 206, /*nSector >>> 48*/ 0);
System.arraycopy(ioBuffer, 0, identifyData, 0, identifyData.length);
identifySet = true;
}
public void transferStart(byte[] buffer, int offset, int size, int endTransferFunction) {
this.endTransferFunction = endTransferFunction;
dataBuffer = buffer;
dataBufferOffset = offset;
dataBufferEnd = size + offset;
status |= DRQ_STAT;
}
private void transferStop() {
endTransferFunction = ETF_TRANSFER_STOP;
dataBuffer = ioBuffer;
dataBufferEnd = 0;
dataBufferOffset = 0;
status &= ~DRQ_STAT;
}
private void commandLBA48Transform(boolean lba48) {
this.lba48 = lba48;
if (!this.lba48) {
if (nSector == 0) {
nSector = 256;
}
} else {
if ((nSector == 0) && (hobNSector == 0)) {
nSector = 65536;
} else {
int lo = 0xff & nSector;
int hi = 0xff & hobNSector;
nSector = (hi << 8) | lo;
}
}
}
private long getSector() {
if ((select & 0x40) != 0) { /* lba */
if (!lba48) {
return ((select & 0x0fl) << 24) |
((0xffl & hcyl) << 16) |
((0xffl & lcyl) << 8) |
(0xffl & sector);
} else {
return ((0xffl & hobHCyl) << 40) |
((0xffl & hobLCyl) << 32) |
((0xffl & hobSector) << 24) |
((0xffl & hcyl) << 16) |
((0xffl & lcyl) << 16) |
(0xffl & sector);
}
} else {
return ((((0xffl & hcyl) << 8) | (0xffl & lcyl)) * heads * sectors) + ((select & 0x0fl) * sectors) + ((0xffl & sector) - 1);
}
}
private void dmaStart(int ideDMAFunction) {
if (bmdma == null) {
return;
}
bmdma.setIDEDevice(this);
bmdma.setDMAFunction(ideDMAFunction);
if ((bmdma.getStatus() & BMDMAIORegion.BM_STATUS_DMAING) != 0) {
bmdma.ideDMALoop();
}
}
public void endTransfer(int mode) {
switch (mode) {
case ETF_TRANSFER_STOP:
transferStop();
break;
case ETF_SECTOR_WRITE:
sectorWrite();
break;
case ETF_SECTOR_READ:
sectorRead();
break;
case ETF_ATAPI_COMMAND:
atapiCommand();
break;
case ETF_ATAPI_COMMAND_REPLY_END:
atapiCommandReplyEnd();
break;
case ETF_DUMMY_TRANSFER_STOP:
dummyTransferStop();
break;
}
}
public void reset() {
multSectors = MAX_MULT_SECTORS;
select = (byte) 0xa0;
status = READY_STAT;
setSignature();
endTransferFunction = ETF_DUMMY_TRANSFER_STOP;
endTransfer(ETF_DUMMY_TRANSFER_STOP);
}
private void atapiCommand() {
switch (0xff & ioBuffer[0]) {
case GPCMD_TEST_UNIT_READY:
if (drive.isInserted()) {
atapiCommandOk();
} else {
atapiCommandError(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
}
break;
case GPCMD_MODE_SENSE_10:
{
int maxLength = bigEndianBytesToShort(ioBuffer, 7);
int action = (0xff & ioBuffer[2]) >>> 6;
int code = ioBuffer[2] & 0x3f;
switch (action) {
case 0: /* current values */
switch (code) {
case 0x01: /* error recovery */
shortToBigEndianBytes(ioBuffer, 0, (short) (16 + 6));
ioBuffer[2] = 0x70;
ioBuffer[3] = 0;
ioBuffer[4] = 0;
ioBuffer[5] = 0;
ioBuffer[6] = 0;
ioBuffer[7] = 0;
ioBuffer[8] = 0x01;
ioBuffer[9] = 0x06;
ioBuffer[10] = 0x00;
ioBuffer[11] = 0x05;
ioBuffer[12] = 0x00;
ioBuffer[13] = 0x00;
ioBuffer[14] = 0x00;
ioBuffer[15] = 0x00;
atapiCommandReply(16, maxLength);
break;
case 0x2a:
shortToBigEndianBytes(ioBuffer, 0, (short) (28 + 6));
ioBuffer[2] = 0x70;
ioBuffer[3] = 0;
ioBuffer[4] = 0;
ioBuffer[5] = 0;
ioBuffer[6] = 0;
ioBuffer[7] = 0;
ioBuffer[8] = 0x2a;
ioBuffer[9] = 0x12;
ioBuffer[10] = 0x00;
ioBuffer[11] = 0x00;
ioBuffer[12] = 0x70;
ioBuffer[13] = 3 << 5;
ioBuffer[14] = (1 << 0) | (1 << 3) | (1 << 5);
if (drive.isLocked()) {
ioBuffer[6] |= 1 << 1;
}
ioBuffer[15] = 0x00;
shortToBigEndianBytes(ioBuffer, 16, (short) 706);
ioBuffer[18] = 0;
ioBuffer[19] = 2;
shortToBigEndianBytes(ioBuffer, 20, (short) 512);
shortToBigEndianBytes(ioBuffer, 22, (short) 706);
ioBuffer[24] = 0;
ioBuffer[25] = 0;
ioBuffer[26] = 0;
ioBuffer[27] = 0;
atapiCommandReply(28, maxLength);
break;
default:
atapiCommandError(SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
}
break;
case 1: /* changeable values */
atapiCommandError(SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
break;
case 2: /* default values */
atapiCommandError(SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
break;
default:
case 3: /* saved values */
atapiCommandError(SENSE_ILLEGAL_REQUEST, ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
break;
}
}
break;
case GPCMD_REQUEST_SENSE:
{
int maxLength = 0xff & ioBuffer[4];
for (int i = 0; i < 18; i++) {
ioBuffer[i] = 0;
}
ioBuffer[0] = (byte) (0x70 | (1 << 7));
ioBuffer[2] = senseKey;
ioBuffer[7] = 10;
ioBuffer[12] = asc;
atapiCommandReply(18, maxLength);
}
break;
case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
if (drive.isInserted()) {
drive.setLock((ioBuffer[4] & 1) != 0);
atapiCommandOk();
} else {
atapiCommandError(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
}
break;
case GPCMD_READ_10:
case GPCMD_READ_12:
{
if (!drive.isInserted()) {
atapiCommandError(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
break;
}
int numSectors;
if (ioBuffer[0] == GPCMD_READ_10) {
numSectors = bigEndianBytesToShort(ioBuffer, 7);
} else {
numSectors = bigEndianBytesToInt(ioBuffer, 6);
}
int lba = bigEndianBytesToInt(ioBuffer, 2);
if (numSectors == 0) {
atapiCommandOk();
break;
}
if ((((0xffffffffl & lba) + (0xffffffffl & numSectors)) << 2) > drive.getTotalSectors()) {
atapiCommandError(SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
break;
}
atapiCommandRead(lba, numSectors, 2048);
}
break;
case GPCMD_READ_CD:
{
if (!drive.isInserted()) {
atapiCommandError(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
break;
}
int numSectors = ((0xff & ioBuffer[6]) << 16) | ((0xff & ioBuffer[7]) << 8) | (0xff & ioBuffer[8]);
int lba = bigEndianBytesToInt(ioBuffer, 2);
if (numSectors == 0) {
atapiCommandOk();
break;
}
if ((((0xffffffffl & lba) + (0xffffffffl & numSectors)) << 2) > drive.getTotalSectors()) {
atapiCommandError(SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
break;
}
int transferRequest = 0xff & ioBuffer[9];
switch (transferRequest & 0xf8) {
case 0x00:
/* nothing */
atapiCommandOk();
break;
case 0x10:
/* normal read */
atapiCommandRead(lba, numSectors, 2048);
break;
case 0xf8:
/* read all data */
atapiCommandRead(lba, numSectors, 2352);
break;
default:
atapiCommandError(SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
break;
}
}
break;
case GPCMD_SEEK:
{
if (!drive.isInserted()) {
atapiCommandError(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
break;
}
int lba = bigEndianBytesToInt(ioBuffer, 2);
if (((0xffffffffl & lba) << 2) > drive.getTotalSectors()) {
atapiCommandError(SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
break;
}
atapiCommandOk();
}
break;
case GPCMD_START_STOP_UNIT:
{
boolean start = ((ioBuffer[4] & 1) != 0);
boolean eject = ((ioBuffer[4] & 2) != 0);
if (eject && !start) {
/* eject the disk */
drive.close();
}
atapiCommandOk();
}
break;
case GPCMD_MECHANISM_STATUS:
{
int maxLength = bigEndianBytesToShort(ioBuffer, 8);
shortToBigEndianBytes(ioBuffer, 0, (short) 0);
/* no current LBA */
ioBuffer[2] = 0;
ioBuffer[3] = 0;
ioBuffer[4] = 0;
ioBuffer[5] = 1;
shortToBigEndianBytes(ioBuffer, 6, (short) 0);
atapiCommandReply(8, maxLength);
}
break;
case GPCMD_READ_TOC_PMA_ATIP:
{
if (!drive.isInserted()) {
atapiCommandError(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
break;
}
int maxLength = bigEndianBytesToShort(ioBuffer, 7);
int format = (0xff & ioBuffer[9]) >>> 6;
int msf = (ioBuffer[1] >>> 1) & 1;
int startTrack = 0xff & ioBuffer[6];
switch (format) {
case 0:
int length = cdromReadTOC((int) (drive.getTotalSectors() >>> 2), ioBuffer, msf, startTrack);
if (length < 0) {
atapiCommandError(SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
break;
}
atapiCommandReply(length, maxLength);
break;
case 1:
/* multi session : only a single session defined */
for (int i = 0; i < 12; i++) {
ioBuffer[i] = 0;
}
ioBuffer[1] = 0x0a;
ioBuffer[2] = 0x01;
ioBuffer[3] = 0x01;
atapiCommandReply(12, maxLength);
break;
case 2:
length = cdromReadTOCRaw((int) (drive.getTotalSectors() >>> 2), ioBuffer, msf, startTrack);
if (length < 0) {
atapiCommandError(SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
break;
}
atapiCommandReply(length, maxLength);
break;
default:
atapiCommandError(SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
break;
}
}
break;
case GPCMD_READ_CDVD_CAPACITY:
if (!drive.isInserted()) {
atapiCommandError(SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
break;
}
/* NOTE: it is really the number of sectors minus 1 */
intToBigEndianBytes(ioBuffer, 0, (int) ((drive.getTotalSectors() >>> 2) - 1));
intToBigEndianBytes(ioBuffer, 4, 2048);
atapiCommandReply(8, 8);
break;
case GPCMD_INQUIRY:
int maxLength = 0xff & ioBuffer[4];
ioBuffer[0] = 0x05; /* CD-ROM */
ioBuffer[1] = (byte) 0x80; /* removable */
ioBuffer[2] = 0x00; /* ISO */
ioBuffer[3] = 0x21; /* ATAPI-2 (XXX: put ATAPI-4 ?) */
ioBuffer[4] = 31; /* additionnal length */
ioBuffer[5] = 0; /* reserved */
ioBuffer[6] = 0; /* reserved */
ioBuffer[7] = 0; /* reserved */
{
byte[] temp = "JPC".getBytes();
int i = 8;
for (int j = 0; j < temp.length; i++, j++) {
ioBuffer[i] = temp[j];
}
for (; i < 16; i++) {
ioBuffer[i] = 0;
}
}
{
byte[] temp = CDLABEL.getBytes();
int i = 16;
for (int j = 0; j < temp.length; i++, j++) {
ioBuffer[i] = temp[j];
}
for (; i < 32; i++) {
ioBuffer[i] = 0;
}
}
{
byte[] temp = "1.0".getBytes();
int i = 32;
for (int j = 0; j < temp.length; i++, j++) {
ioBuffer[i] = temp[j];
}
for (; i < 36; i++) {
ioBuffer[i] = 0;
}
}
atapiCommandReply(36, maxLength);
break;
default:
atapiCommandError(SENSE_ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE);
break;
}
}
private void dummyTransferStop() {
dataBuffer = ioBuffer;
dataBufferEnd = 0;
ioBuffer[0] = (byte) 0xff;
ioBuffer[1] = (byte) 0xff;
ioBuffer[2] = (byte) 0xff;
ioBuffer[3] = (byte) 0xff;
}
private void atapiCommandOk() {
error = 0;
status = READY_STAT;
nSector = (nSector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
setIRQ();
}
private void atapiCommandError(int senseKey, int asc) {
error = (byte) (this.senseKey << 4);
status = READY_STAT | ERR_STAT;
nSector = (nSector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
this.senseKey = (byte) senseKey;
this.asc = (byte) asc;
setIRQ();
}
private void atapiCommandReply(int size, int maxSize) {
size = Math.min(size, maxSize);
lba = -1;
packetTransferSize = size;
elementaryTransferSize = 0;
ioBufferIndex = 0;
status = READY_STAT;
atapiCommandReplyEnd();
}
private void atapiCommandRead(int lba, int numSectors, int sectorSize) {
if (atapiDMA) {
atapiCommandReadDMA(lba, numSectors, sectorSize);
} else {
atapiCommandReadPIO(lba, numSectors, sectorSize);
}
}
private void atapiCommandReadDMA(int lba, int numSectors, int sectorSize) {
this.lba = lba;
packetTransferSize = numSectors * sectorSize;
ioBufferIndex = sectorSize;
cdSectorSize = sectorSize;
status = READY_STAT | DRQ_STAT;
dmaStart(IDF_ATAPI_READ_DMA_CB);
}
private void atapiCommandReadPIO(int lba, int numSectors, int sectorSize) {
this.lba = lba;
packetTransferSize = numSectors * sectorSize;
elementaryTransferSize = 0;
ioBufferIndex = sectorSize;
cdSectorSize = sectorSize;
status = READY_STAT;
atapiCommandReplyEnd();
}
private void atapiCommandReplyEnd() {
if (packetTransferSize <= 0) { //end of transfer
transferStop();
status = READY_STAT;
nSector = (nSector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
setIRQ();
} else {
/* see if a new sector must be read */
if (lba != -1 && ioBufferIndex >= cdSectorSize) {
cdReadSector(lba, ioBuffer, cdSectorSize);
lba++;
ioBufferIndex = 0;
}
if (elementaryTransferSize > 0) { // there is some data left to transmit in this elementary transfer
int size = Math.min(cdSectorSize - ioBufferIndex, elementaryTransferSize);
transferStart(ioBuffer, ioBufferIndex, size, ETF_ATAPI_COMMAND_REPLY_END);
packetTransferSize -= size;
elementaryTransferSize -= size;
ioBufferIndex += size;
} else {
/* a new transfer is needed */
nSector = (nSector & ~7) | ATAPI_INT_REASON_IO;
int byteCountLimit = (0xff & lcyl) | (0xff00 & (hcyl << 8));
if (byteCountLimit == 0xffff) {
byteCountLimit--;
}
int size = packetTransferSize;
if (size > byteCountLimit) {
/* byte count limit must be even if this case */
if ((byteCountLimit & 1) != 0) {
byteCountLimit--;
}
size = byteCountLimit;
}
lcyl = (byte) size;
hcyl = (byte) (size >>> 8);
elementaryTransferSize = size;
/* we cannot transmit more than one sector at a time */
if (lba != -1) {
size = Math.min(cdSectorSize - ioBufferIndex, size);
}
transferStart(ioBuffer, ioBufferIndex, size, ETF_ATAPI_COMMAND_REPLY_END);
packetTransferSize -= size;
elementaryTransferSize -= size;
ioBufferIndex += size;
setIRQ();
}
}
}
private int readDMACallback(int address, int size) {
int originalSize = size;
packetTransferSize = size;
while (size > 0) {
if (packetTransferSize <= 0)
break;
int length = drive.SECTOR_SIZE - ioBufferIndex;
if (length <= 0) {
ioBufferIndex = 0;
length = drive.SECTOR_SIZE;
}
if (length > size)
length = size;
int start = nSector;
sectorRead();
int end = nSector;
bmdma.writeMemory(address, ioBuffer, ioBufferIndex, 512*(start-end));
packetTransferSize -= length;
ioBufferIndex += length;
size -= length;
address += length;
}
if (packetTransferSize <= 0) {
status = READY_STAT | SEEK_STAT;
nSector = (nSector & ~0x7);
setIRQ();
return 0;
}
return originalSize - size;
}
private int atapiCommandReadDMACallback(int address, int size) {
System.out.println("CD DMA callback read");
int originalSize = size;
while (size > 0) {
if (packetTransferSize <= 0) {
break;
}
int length = cdSectorSize - ioBufferIndex;
if (length <= 0) {
lba++;
ioBufferIndex = 0;
length = cdSectorSize;
}
if (length > size) {
length = size;
}
cdReadSector(lba, ioBuffer, length);
bmdma.writeMemory(address, ioBuffer, ioBufferIndex, length);
packetTransferSize -= length;
ioBufferIndex += length;
size -= length;
address += length;
}
if (packetTransferSize <= 0) {
status = READY_STAT;
nSector = (nSector & ~0x7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
setIRQ();
return 0;
}
return originalSize - size;
}
private int cdromReadTOC(int nbSectors, byte[] buffer, int msf, int startTrack) {
if ((startTrack > 1) && (startTrack != 0xaa)) {
return -1;
}
int bufferOffset = 2;
buffer[bufferOffset++] = 1; // first session
buffer[bufferOffset++] = 1; // last session
if (startTrack <= 1) {
buffer[bufferOffset++] = 0; // reserved
buffer[bufferOffset++] = 0x14; // ADR, control
buffer[bufferOffset++] = 1; // track number
buffer[bufferOffset++] = 0; // reserved
if (msf != 0) {
buffer[bufferOffset++] = 0; // reserved
lbaToMSF(buffer, bufferOffset, 0);
bufferOffset += 3;
} else {
// sector 0
intToBigEndianBytes(buffer, bufferOffset, 0);
bufferOffset += 4;
}
}
// lead out track
buffer[bufferOffset++] = 0; // reserved
buffer[bufferOffset++] = 0x16; // ADR, control
buffer[bufferOffset++] = (byte) 0xaa; // track number
buffer[bufferOffset++] = 0; // reserved
if (msf != 0) {
buffer[bufferOffset++] = 0; // reserved
lbaToMSF(buffer, bufferOffset, nbSectors);
bufferOffset += 3;
} else {
intToBigEndianBytes(buffer, bufferOffset, nbSectors);
bufferOffset += 4;
}
shortToBigEndianBytes(buffer, bufferOffset, (short) (bufferOffset - 2));
return bufferOffset;
}
private int cdromReadTOCRaw(int nbSectors, byte[] buffer, int msf, int sessionNumber) {
int bufferOffset = 2;
buffer[bufferOffset++] = 1; // first session
buffer[bufferOffset++] = 1; // last session
buffer[bufferOffset++] = 1; /* session number */
buffer[bufferOffset++] = 0x14; /* data track */
buffer[bufferOffset++] = 0; /* track number */
buffer[bufferOffset++] = (byte) 0xa0; /* lead-in */
buffer[bufferOffset++] = 0; /* min */
buffer[bufferOffset++] = 0; /* sec */
buffer[bufferOffset++] = 0; /* frame */
buffer[bufferOffset++] = 0;
buffer[bufferOffset++] = 1; /* first track */
buffer[bufferOffset++] = 0x00; /* disk type */
buffer[bufferOffset++] = 0x00;
buffer[bufferOffset++] = 1; /* session number */
buffer[bufferOffset++] = 0x14; /* data track */
buffer[bufferOffset++] = 0; /* track number */
buffer[bufferOffset++] = (byte) 0xa1;
buffer[bufferOffset++] = 0; /* min */
buffer[bufferOffset++] = 0; /* sec */
buffer[bufferOffset++] = 0; /* frame */
buffer[bufferOffset++] = 0;
buffer[bufferOffset++] = 1; /* last track */
buffer[bufferOffset++] = 0x00;
buffer[bufferOffset++] = 0x00;
buffer[bufferOffset++] = 1; /* session number */
buffer[bufferOffset++] = 0x14; /* data track */
buffer[bufferOffset++] = 0; /* track number */
buffer[bufferOffset++] = (byte) 0xa2; /* lead-out */
buffer[bufferOffset++] = 0; /* min */
buffer[bufferOffset++] = 0; /* sec */
buffer[bufferOffset++] = 0; /* frame */
if (msf != 0) {
buffer[bufferOffset++] = 0; /* reserved */
lbaToMSF(buffer, bufferOffset, nbSectors);
bufferOffset += 3;
} else {
intToBigEndianBytes(buffer, bufferOffset, nbSectors);
bufferOffset += 4;
}
buffer[bufferOffset++] = 1; /* session number */
buffer[bufferOffset++] = 0x14; /* ADR, control */
buffer[bufferOffset++] = 0; /* track number */
buffer[bufferOffset++] = 1; /* point */
buffer[bufferOffset++] = 0; /* min */
buffer[bufferOffset++] = 0; /* sec */
buffer[bufferOffset++] = 0; /* frame */
if (msf != 0) {
buffer[bufferOffset++] = 0;
lbaToMSF(buffer, bufferOffset, 0);
bufferOffset += 3;
} else {
buffer[bufferOffset++] = 0;
buffer[bufferOffset++] = 0;
buffer[bufferOffset++] = 0;
buffer[bufferOffset++] = 0;
}
shortToBigEndianBytes(buffer, bufferOffset, (short) (bufferOffset - 2));
return bufferOffset;
}
private void hdReadSector(long sector, byte[] buffer, int sectorSize) {
drive.read(sector, buffer, 1);
System.out.println("DMA reading sector: " + sector);
}
private void cdReadSector(int lba, byte[] buffer, int sectorSize) {
switch (sectorSize) {
case 2048:
drive.read((0xffffffffl & lba) << 2, buffer, 4);
break;
case 2352:
drive.read((0xffffffffl & lba) << 2, buffer, 4);
System.arraycopy(buffer, 0, buffer, 16, 2048);
/* sync bytes */
buffer[0] = 0x00;
for (int i = 1; i < 11; i++) {
buffer[i] = (byte) 0xff;
}
buffer[11] = 0x00;
lbaToMSF(buffer, 12, lba); // MSF
buffer[12 + 3] = 0x01; // mode 1 data
for (int i = 2064; i < 2352; i++) {
buffer[i] = 0;
}
break;
default:
break;
}
}
}
public String toString() {
if (ioBaseTwo == 0) {
return "IDE Channel @ 0x" + Integer.toHexString(ioBase) + "-0x" + Integer.toHexString(ioBase + 7) + " on irq " + irq;
} else {
return "IDE Channel @ 0x" + Integer.toHexString(ioBase) + "-0x" + Integer.toHexString(ioBase + 7) + ", 0x" + Integer.toHexString(ioBaseTwo) + " on irq " + irq;
}
}
}