Fixed the FORMAT command on almost every emulated hard disk controller.

This commit is contained in:
OBattler
2024-04-18 03:47:49 +02:00
parent 691e3f2fa9
commit 9947af00d4
6 changed files with 169 additions and 17 deletions

View File

@@ -214,6 +214,41 @@ get_sector(esdi_t *esdi, off64_t *addr)
return 0;
}
static int
get_sector_format(esdi_t *esdi, off64_t *addr)
{
const drive_t *drive = &esdi->drives[esdi->drive_sel];
int heads = drive->cfg_hpc;
int sectors = drive->cfg_spt;
int c;
int h;
int s;
if (esdi->head > heads) {
esdi_at_log("esdi_get_sector: past end of configured heads\n");
return 1;
}
if (drive->cfg_spt == drive->real_spt && drive->cfg_hpc == drive->real_hpc) {
*addr = ((((off64_t) esdi->cylinder * heads) + esdi->head) * sectors);
} else {
/*
* When performing translation, the firmware seems to leave 1
* sector per track inaccessible (spare sector)
*/
*addr = ((((off64_t) esdi->cylinder * heads) + esdi->head) * sectors);
s = *addr % (drive->real_spt - 1);
h = (*addr / (drive->real_spt - 1)) % drive->real_hpc;
c = (*addr / (drive->real_spt - 1)) / drive->real_hpc;
*addr = ((((off64_t) c * drive->real_hpc) + h) * drive->real_spt) + s;
}
return 0;
}
/* Move to the next sector using CHS addressing. */
static void
next_sector(esdi_t *esdi)
@@ -655,7 +690,7 @@ esdi_callback(void *priv)
irq_raise(esdi);
break;
} else {
if (get_sector(esdi, &addr)) {
if (get_sector_format(esdi, &addr)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(esdi);

View File

@@ -831,14 +831,12 @@ esdi_callback(void *priv)
switch (dev->cmd_state) {
case 0:
dev->rba = (dev->cmd_data[2] | (dev->cmd_data[3] << 16)) & 0x0fffffff;
dev->rba = hdd_image_get_last_sector(drive->hdd_num);
dev->sector_count = dev->cmd_data[1];
if ((dev->rba + dev->sector_count) > hdd_image_get_last_sector(drive->hdd_num)) {
rba_out_of_range(dev);
return;
}
if (dev->command == CMD_FORMAT_UNIT)
dev->sector_count = dev->cmd_data[1];
else
dev->sector_count = 0;
dev->status = STATUS_IRQ | STATUS_CMD_IN_PROGRESS | STATUS_TRANSFER_REQ;
dev->irq_status = dev->cmd_dev | IRQ_DATA_TRANSFER_READY;
@@ -855,7 +853,8 @@ esdi_callback(void *priv)
return;
}
hdd_image_zero(drive->hdd_num, dev->rba, dev->sector_count);
if (dev->command == CMD_FORMAT_UNIT)
hdd_image_zero(drive->hdd_num, 0, hdd_image_get_last_sector(drive->hdd_num) + 1);
dev->status = STATUS_CMD_IN_PROGRESS;
dev->cmd_state = 2;

View File

@@ -738,6 +738,22 @@ ide_get_sector(ide_t *ide)
}
}
static off64_t
ide_get_sector_format(ide_t *ide)
{
uint32_t heads;
uint32_t sectors;
if (ide->tf->lba)
return (off64_t) ide->lba_addr;
else {
heads = ide->cfg_hpc;
sectors = ide->cfg_spt;
return ((((off64_t) ide->tf->cylinder * heads) + (off64_t) ide->tf->head) * sectors);
}
}
/**
* Move to the next sector using CHS addressing
*/
@@ -2147,8 +2163,9 @@ ide_callback(void *priv)
if (ide->type == IDE_ATAPI)
atapi_error_no_ready(ide);
else {
if (chk_chs && ((ide->tf->cylinder >= ide->tracks) || (ide->tf->head >= ide->hpc) ||
!ide->tf->sector || (ide->tf->sector > ide->spt)))
/* The J-Bond PCI400C-A Phoenix BIOS implies that this command is supposed to
ignore the sector number. */
if (chk_chs && ((ide->tf->cylinder >= ide->tracks) || (ide->tf->head >= ide->hpc)))
err = IDNF_ERR;
else {
ide->tf->atastat = DRDY_STAT | DSC_STAT;
@@ -2434,7 +2451,7 @@ ide_callback(void *priv)
else if (!ide->tf->lba && (ide->cfg_spt == 0))
err = IDNF_ERR;
else {
hdd_image_zero(ide->hdd_num, ide_get_sector(ide), ide->tf->secount);
hdd_image_zero(ide->hdd_num, ide_get_sector_format(ide), ide->tf->secount);
ide->tf->atastat = DRDY_STAT | DSC_STAT;
ide_irq_raise(ide);
@@ -2628,6 +2645,15 @@ ide_set_base_addr(int board, int base, uint16_t port)
ide_boards[board]->base[base] = port;
}
void
ide_set_irq(int board, int irq)
{
ide_log("ide_set_irq(%i, %i)\n", board, irq);
if (ide_boards[board] != NULL)
ide_boards[board]->irq = irq;
}
static void
ide_clear_bus_master(int board)
{
@@ -2803,6 +2829,36 @@ ide_board_init(int board, int irq, int base_main, int side_main, int type, int b
ide_boards[board]->inited = 1;
}
/* Needed for ESS ES1688/968 PnP. */
void
ide_pnp_config_changed_1addr(uint8_t ld, isapnp_device_config_t *config, void *priv)
{
intptr_t board = (intptr_t) priv;
if (ld)
return;
if (ide_boards[board]->base[0] || ide_boards[board]->base[1]) {
ide_remove_handlers(board);
ide_boards[board]->base[0] = ide_boards[board]->base[1] = 0;
}
ide_boards[board]->irq = -1;
if (config->activate) {
ide_boards[board]->base[0] = (config->io[0].base != ISAPNP_IO_DISABLED) ?
config->io[0].base : 0x0000;
ide_boards[board]->base[1] = (config->io[0].base != ISAPNP_IO_DISABLED) ?
(config->io[0].base + 0x0206) : 0x0000;
if (ide_boards[board]->base[0] && ide_boards[board]->base[1])
ide_set_handlers(board);
if (config->irq[0].irq != ISAPNP_IRQ_DISABLED)
ide_boards[board]->irq = config->irq[0].irq;
}
}
void
ide_pnp_config_changed(uint8_t ld, isapnp_device_config_t *config, void *priv)
{
@@ -3526,5 +3582,5 @@ const device_t ide_qua_pnp_device = {
{ .available = NULL },
.speed_changed = NULL,
.force_redraw = NULL,
.config = ide_qua_config
.config = NULL
};

View File

@@ -187,7 +187,7 @@ get_sector(mfm_t *mfm, off64_t *addr)
return 1;
}
if (mfm->sector >= drive->cfg_spt + 1) {
if (mfm->sector >= (drive->cfg_spt + 1)) {
st506_at_log("WD1003(%d) get_sector: past end of configured sectors\n",
mfm->drvsel);
return 1;
@@ -199,7 +199,7 @@ get_sector(mfm_t *mfm, off64_t *addr)
return 1;
}
if (mfm->sector >= drive->spt + 1) {
if (mfm->sector >= (drive->spt + 1)) {
st506_at_log("WD1003(%d) get_sector: past end of sectors\n", mfm->drvsel);
return 1;
}
@@ -209,6 +209,35 @@ get_sector(mfm_t *mfm, off64_t *addr)
return 0;
}
static int
get_sector_format(mfm_t *mfm, off64_t *addr)
{
const drive_t *drive = &mfm->drives[mfm->drvsel];
/* FIXME: See if this is even needed - if the code is present, IBM AT
diagnostics v2.07 will error with: ERROR 152 - SYSTEM BOARD. */
if (drive->curcyl != mfm->cylinder) {
st506_at_log("WD1003(%d) sector: wrong cylinder\n");
return 1;
}
if (mfm->head > drive->cfg_hpc) {
st506_at_log("WD1003(%d) get_sector: past end of configured heads\n",
mfm->drvsel);
return 1;
}
/* We should check this in the SET_DRIVE_PARAMETERS command! --FvK */
if (mfm->head > drive->hpc) {
st506_at_log("WD1003(%d) get_sector: past end of heads\n", mfm->drvsel);
return 1;
}
*addr = ((((off64_t) mfm->cylinder * drive->cfg_hpc) + mfm->head) * drive->cfg_spt);
return 0;
}
/* Move to the next sector using CHS addressing. */
static void
next_sector(mfm_t *mfm)
@@ -634,7 +663,7 @@ do_callback(void *priv)
st506_at_log("WD1003(%d) format(%d,%d)\n",
mfm->drvsel, mfm->cylinder, mfm->head);
do_seek(mfm);
if (get_sector(mfm, &addr)) {
if (get_sector_format(mfm, &addr)) {
mfm->error = ERR_ID_NOT_FOUND;
mfm->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(mfm);

View File

@@ -457,6 +457,37 @@ get_chs(hdc_t *dev, drive_t *drive)
return 1;
}
static int
get_chs_format(hdc_t *dev, drive_t *drive)
{
dev->err_bv = 0x80;
dev->head = dev->command[1] & 0x1f;
/* 6 bits are used for the sector number even on the IBM PC controller. */
dev->sector = 1;
dev->count = dev->command[4];
if (((dev->type == ST506_XT_TYPE_ST11M) || (dev->type == ST506_XT_TYPE_ST11R)) && (dev->command[0] >= 0xf0))
dev->cylinder = 0;
else {
dev->cylinder = dev->command[3] | ((dev->command[2] & 0xc0) << 2);
dev->cylinder += dev->cyl_off; /* for ST-11 */
}
if (dev->cylinder >= drive->cfg_cyl) {
/*
* This really is an error, we cannot move
* past the end of the drive, which should
* result in an ERR_ILLEGAL_ADDR. --FvK
*/
drive->cylinder = drive->cfg_cyl - 1;
return 0;
}
drive->cylinder = dev->cylinder;
return 1;
}
static void
st506_callback(void *priv)
{
@@ -628,7 +659,7 @@ st506_callback(void *priv)
case CMD_FORMAT_BAD_TRACK:
switch (dev->state) {
case STATE_START_COMMAND:
(void) get_chs(dev, drive);
(void) get_chs_format(dev, drive);
st506_xt_log("ST506: FORMAT_%sTRACK(%i, %i/%i)\n",
(dev->command[0] == CMD_FORMAT_BAD_TRACK) ? "BAD_" : "",
dev->drive_sel, dev->cylinder, dev->head);