358 lines
9.8 KiB
C
Executable File
358 lines
9.8 KiB
C
Executable File
/*****************************************************************************
|
|
** $Source: /cygdrive/d/Private/_SVNROOT/bluemsx/blueMSX/Src/IoDevice/HarddiskIDE.c,v $
|
|
**
|
|
** $Revision: 1.11 $
|
|
**
|
|
** $Date: 2009-07-18 15:08:04 $
|
|
**
|
|
** More info: http://www.bluemsx.com
|
|
**
|
|
** Copyright (C) 2003-2006 Daniel Vik
|
|
**
|
|
** 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.
|
|
**
|
|
** 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
**
|
|
******************************************************************************
|
|
*/
|
|
#include "HarddiskIDE.h"
|
|
#include "Board.h"
|
|
#include "SaveState.h"
|
|
#include "Disk.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define STATUS_ERR 0x01
|
|
#define STATUS_DRQ 0x08
|
|
#define STATUS_DSC 0x10
|
|
#define STATUS_DRDY 0x40
|
|
|
|
|
|
|
|
struct HarddiskIde {
|
|
UInt8 errorReg;
|
|
UInt8 sectorCountReg;
|
|
UInt8 sectorNumReg;
|
|
UInt8 cylinderLowReg;
|
|
UInt8 cylinderHighReg;
|
|
UInt8 devHeadReg;
|
|
UInt8 statusReg;
|
|
UInt8 featureReg;
|
|
|
|
int transferRead;
|
|
int transferWrite;
|
|
UInt32 transferCount;
|
|
UInt32 transferSectorNumber;
|
|
|
|
int sectorDataOffset;
|
|
UInt8 sectorData[512 * 256];
|
|
|
|
int diskId;
|
|
};
|
|
|
|
|
|
static UInt32 getSectorNumber(HarddiskIde* hd)
|
|
{
|
|
return hd->sectorNumReg | (hd->cylinderLowReg << 8) |
|
|
(hd->cylinderHighReg << 16) | ((hd->devHeadReg & 0x0f) << 24);
|
|
}
|
|
|
|
static void setError(HarddiskIde* hd, UInt8 error)
|
|
{
|
|
hd->errorReg = error;
|
|
hd->statusReg |= STATUS_ERR;
|
|
hd->statusReg &= ~STATUS_DRQ;
|
|
hd->transferWrite = 0;
|
|
hd->transferRead = 0;
|
|
}
|
|
|
|
static UInt32 getNumSectors(HarddiskIde* hd)
|
|
{
|
|
return hd->sectorCountReg == 0 ? 256 : hd->sectorCountReg;
|
|
}
|
|
|
|
static void executeCommand(HarddiskIde* hd, UInt8 cmd)
|
|
{
|
|
hd->statusReg &= ~(STATUS_DRQ | STATUS_ERR);
|
|
hd->transferRead = 0;
|
|
hd->transferWrite = 0;
|
|
switch (cmd) {
|
|
case 0xef: // Set Feature
|
|
if (hd->featureReg != 0x03) {
|
|
setError(hd, 0x04);
|
|
}
|
|
break;
|
|
|
|
case 0xec: // ATA Identify Device
|
|
if (diskReadSector(hd->diskId, hd->sectorData, -1, 0, 0, 0, NULL) != DSKE_OK) {
|
|
setError(hd, 0x44);
|
|
break;
|
|
}
|
|
hd->transferCount = 512/2;
|
|
hd->sectorDataOffset = 0;
|
|
hd->transferRead = 1;
|
|
hd->statusReg |= STATUS_DRQ;
|
|
break;
|
|
|
|
case 0x91:
|
|
break;
|
|
|
|
case 0xf8: {
|
|
UInt32 sectorCount = diskGetSectorsPerTrack(hd->diskId);
|
|
hd->sectorNumReg = (UInt8)((sectorCount >> 0) & 0xff);
|
|
hd->cylinderLowReg = (UInt8)((sectorCount >> 8) & 0xff);
|
|
hd->cylinderHighReg = (UInt8)((sectorCount >> 16) & 0xff);
|
|
hd->devHeadReg = (UInt8)((sectorCount >> 24) & 0x0f);
|
|
break;
|
|
}
|
|
case 0x30: { // Write Sector
|
|
int sectorNumber = getSectorNumber(hd);
|
|
int numSectors = getNumSectors(hd);
|
|
if ((sectorNumber + numSectors) > diskGetSectorsPerTrack(hd->diskId)) {
|
|
setError(hd, 0x14);
|
|
break;
|
|
}
|
|
hd->transferSectorNumber = sectorNumber;
|
|
hd->transferCount = 512/2 * numSectors;
|
|
hd->sectorDataOffset = 0;
|
|
hd->transferWrite = 1;
|
|
hd->statusReg |= STATUS_DRQ;
|
|
break;
|
|
}
|
|
case 0x20: { // Read Sector
|
|
int sectorNumber = getSectorNumber(hd);
|
|
int numSectors = getNumSectors(hd);
|
|
int i;
|
|
|
|
if ((sectorNumber + numSectors) > diskGetSectorsPerTrack(hd->diskId)) {
|
|
setError(hd, 0x14);
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < numSectors; i++) {
|
|
if (diskReadSector(hd->diskId, hd->sectorData + i * 512, sectorNumber + i + 1, 0, 0, 0, NULL) != DSKE_OK) {
|
|
break;
|
|
}
|
|
}
|
|
if (i != numSectors) {
|
|
setError(hd, 0x44);
|
|
break;
|
|
}
|
|
|
|
hd->transferCount = 512/2 * numSectors;
|
|
hd->sectorDataOffset = 0;
|
|
hd->transferRead = 1;
|
|
hd->statusReg |= STATUS_DRQ;
|
|
break;
|
|
}
|
|
default: // all others
|
|
setError(hd, 0x04);
|
|
}
|
|
}
|
|
|
|
void harddiskIdeReset(HarddiskIde* hd)
|
|
{
|
|
hd->errorReg = 0x01;
|
|
hd->sectorCountReg = 0x01;
|
|
hd->sectorNumReg = 0x01;
|
|
hd->cylinderLowReg = 0x00;
|
|
hd->cylinderHighReg = 0x00;
|
|
hd->devHeadReg = 0x00;
|
|
hd->statusReg = STATUS_DSC | STATUS_DRDY;
|
|
hd->featureReg = 0x00;
|
|
hd->transferRead = 0;
|
|
hd->transferWrite = 0;
|
|
}
|
|
|
|
UInt16 harddiskIdeRead(HarddiskIde* hd)
|
|
{
|
|
UInt16 value;
|
|
|
|
if (!hd->transferRead || !diskPresent(hd->diskId)) {
|
|
return 0x7f7f;
|
|
}
|
|
|
|
value = hd->sectorData[hd->sectorDataOffset++];
|
|
value |= hd->sectorData[hd->sectorDataOffset++] << 8;
|
|
if (--hd->transferCount == 0) {
|
|
hd->transferRead = 0;
|
|
hd->statusReg &= ~STATUS_DRQ;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
UInt16 harddiskIdePeek(HarddiskIde* hd)
|
|
{
|
|
UInt16 value;
|
|
|
|
if (!hd->transferRead || !diskPresent(hd->diskId)) {
|
|
return 0x7f7f;
|
|
}
|
|
|
|
value = hd->sectorData[hd->sectorDataOffset];
|
|
value |= hd->sectorData[hd->sectorDataOffset + 1] << 8;
|
|
|
|
return value;
|
|
}
|
|
|
|
void harddiskIdeWrite(HarddiskIde* hd, UInt16 value)
|
|
{
|
|
if (!hd->transferWrite || !diskPresent(hd->diskId)) {
|
|
return;
|
|
}
|
|
|
|
hd->sectorData[hd->sectorDataOffset++] = value & 0xff;
|
|
hd->sectorData[hd->sectorDataOffset++] = value >> 8;
|
|
hd->transferCount--;
|
|
if ((hd->transferCount & 255) == 0) {
|
|
if (!diskWriteSector(hd->diskId, hd->sectorData, hd->transferSectorNumber + 1, 0, 0, 0)) {
|
|
setError(hd, 0x44);
|
|
hd->transferWrite = 0;
|
|
return;
|
|
}
|
|
hd->transferSectorNumber++;
|
|
hd->sectorDataOffset = 0;
|
|
}
|
|
if (hd->transferCount == 0) {
|
|
hd->transferWrite = 0;
|
|
hd->statusReg &= ~STATUS_DRQ;
|
|
}
|
|
}
|
|
|
|
UInt8 harddiskIdeReadRegister(HarddiskIde* hd, UInt8 reg)
|
|
{
|
|
if (!diskPresent(hd->diskId)) {
|
|
return 0x7f;
|
|
}
|
|
|
|
switch (reg) {
|
|
case 1:
|
|
return hd->errorReg;
|
|
case 2:
|
|
return hd->sectorCountReg;
|
|
case 3:
|
|
return hd->sectorNumReg;
|
|
case 4:
|
|
return hd->cylinderLowReg;
|
|
case 5:
|
|
return hd->cylinderHighReg;
|
|
case 6:
|
|
return hd->devHeadReg;
|
|
case 7:
|
|
return hd->statusReg;
|
|
case 8:
|
|
case 9:
|
|
case 10:
|
|
case 11:
|
|
case 12:
|
|
case 13:
|
|
case 15:
|
|
return 0x7f;
|
|
}
|
|
|
|
return 0x7f;
|
|
}
|
|
|
|
UInt8 harddiskIdePeekRegister(HarddiskIde* hd, UInt8 reg)
|
|
{
|
|
return harddiskIdeReadRegister(hd, reg);
|
|
}
|
|
|
|
void harddiskIdeWriteRegister(HarddiskIde* hd, UInt8 reg, UInt8 value)
|
|
{
|
|
if (!diskPresent(hd->diskId)) {
|
|
return;
|
|
}
|
|
switch (reg) {
|
|
case 1:
|
|
hd->featureReg = value;
|
|
break;
|
|
case 2:
|
|
hd->sectorCountReg = value;
|
|
break;
|
|
case 3:
|
|
hd->sectorNumReg = value;
|
|
break;
|
|
case 4:
|
|
hd->cylinderLowReg = value;
|
|
break;
|
|
case 5:
|
|
hd->cylinderHighReg = value;
|
|
break;
|
|
case 6:
|
|
hd->devHeadReg = value;
|
|
break;
|
|
case 7:
|
|
executeCommand(hd, value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void harddiskIdeLoadState(HarddiskIde* ide)
|
|
{
|
|
SaveState* state = saveStateOpenForRead("harddiskIde");
|
|
|
|
ide->errorReg = (UInt8)saveStateGet(state, "errorReg", 0);
|
|
ide->sectorCountReg = (UInt8)saveStateGet(state, "sectorCountReg", 0);
|
|
ide->sectorNumReg = (UInt8)saveStateGet(state, "sectorNumReg", 0);
|
|
ide->cylinderLowReg = (UInt8)saveStateGet(state, "cylinderLowReg", 0);
|
|
ide->cylinderHighReg = (UInt8)saveStateGet(state, "cylinderHighReg", 0);
|
|
ide->devHeadReg = (UInt8)saveStateGet(state, "devHeadReg", 0);
|
|
ide->statusReg = (UInt8)saveStateGet(state, "statusReg", 0);
|
|
ide->featureReg = (UInt8)saveStateGet(state, "featureReg", 0);
|
|
ide->transferRead = saveStateGet(state, "transferRead", 0);
|
|
ide->transferWrite = saveStateGet(state, "transferWrite", 0);
|
|
ide->transferCount = saveStateGet(state, "transferCount", 0);
|
|
ide->transferSectorNumber = saveStateGet(state, "transferSectorNumber", 0);
|
|
|
|
saveStateClose(state);
|
|
}
|
|
|
|
void harddiskIdeSaveState(HarddiskIde* ide)
|
|
{
|
|
SaveState* state = saveStateOpenForWrite("harddiskIde");
|
|
|
|
saveStateSet(state, "errorReg", ide->errorReg);
|
|
saveStateSet(state, "sectorCountReg", ide->sectorCountReg);
|
|
saveStateSet(state, "sectorNumReg", ide->sectorNumReg);
|
|
saveStateSet(state, "cylinderLowReg", ide->cylinderLowReg);
|
|
saveStateSet(state, "cylinderHighReg", ide->cylinderHighReg);
|
|
saveStateSet(state, "devHeadReg", ide->devHeadReg);
|
|
saveStateSet(state, "statusReg", ide->statusReg);
|
|
saveStateSet(state, "featureReg", ide->featureReg);
|
|
saveStateSet(state, "transferRead", ide->transferRead);
|
|
saveStateSet(state, "transferWrite", ide->transferWrite);
|
|
saveStateSet(state, "transferCount", ide->transferCount);
|
|
saveStateSet(state, "transferSectorNumber", ide->transferSectorNumber);
|
|
|
|
saveStateClose(state);
|
|
}
|
|
|
|
HarddiskIde* harddiskIdeCreate(int diskId)
|
|
{
|
|
HarddiskIde* hd = malloc(sizeof(HarddiskIde));
|
|
|
|
hd->diskId = diskId;
|
|
hd->transferRead = 0;
|
|
hd->transferWrite = 0;
|
|
|
|
harddiskIdeReset(hd);
|
|
|
|
return hd;
|
|
}
|
|
|
|
void harddiskIdeDestroy(HarddiskIde* hd)
|
|
{
|
|
free(hd);
|
|
}
|