This commit is contained in:
OBattler
2017-10-15 02:51:17 +02:00
381 changed files with 29855 additions and 23909 deletions

193
src/disk/hdc.c Normal file
View File

@@ -0,0 +1,193 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Common code to handle all sorts of disk controllers.
*
* Version: @(#)hdc.c 1.0.3 2017/10/04
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
* Copyright 2016,2017 Miran Grca.
* Copyright 2017 Fred N. van Kempen.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <wchar.h>
#include "../ibm.h"
#include "../cpu/cpu.h"
#include "../device.h"
#include "../machine/machine.h"
#include "hdc.h"
char hdc_name[16]; /* configured HDC name */
int hdc_current;
static void *
null_init(device_t *info)
{
return(NULL);
}
static void
null_close(void *priv)
{
}
static device_t null_device = {
"Null HDC", 0, 0,
null_init, null_close, NULL,
NULL, NULL, NULL, NULL, NULL
};
static void *
inthdc_init(device_t *info)
{
return(NULL);
}
static void
inthdc_close(void *priv)
{
}
static device_t inthdc_device = {
"Internal Controller", 0, 0,
inthdc_init, inthdc_close, NULL,
NULL, NULL, NULL, NULL, NULL
};
static struct {
char name[50];
char internal_name[16];
device_t *device;
int is_mfm;
} controllers[] = {
{ "None", "none",
&null_device, 0 },
{ "Internal Controller", "internal",
&inthdc_device, 0 },
{ "[ISA] [MFM] IBM PC Fixed Disk Adapter", "mfm_xebec",
&mfm_xt_xebec_device, 1 },
{ "[ISA] [MFM] DTC-5150X Fixed Disk Adapter", "mfm_dtc5150x",
&mfm_xt_dtc5150x_device, 1 },
{ "[ISA] [MFM] IBM PC/AT Fixed Disk Adapter", "mfm_at",
&mfm_at_wd1003_device, 1 },
{ "[ISA] [ESDI] PC/AT ESDI Fixed Disk Adapter", "esdi_wd1007vse1",
&esdi_at_wd1007vse1_device, 0 },
#if 0
{ "[ISA] [IDE] PC/AT IDE Adapter", "ide_isa",
&ide_isa_device, 0 },
{ "[PCI] [IDE] PCI IDE Adapter", "ide_pci",
&ide_pci_device, 0 },
#endif
{ "[ISA] [IDE] PC/XT XTIDE", "xtide",
&xtide_device , 0 },
{ "[ISA] [IDE] PC/AT XTIDE", "xtide_at",
&xtide_at_device, 0 },
{ "[ISA] [IDE] PS/2 XTIDE (Acculogic)", "xtide_ps2",
&xtide_ps2_device, 0 },
{ "[ISA] [IDE] PS/2 AT XTIDE (1.1.5)", "xtide_at_ps2",
&xtide_at_ps2_device, 0 },
{ "[MCA] [ESDI] IBM PS/2 ESDI Fixed Disk Adapter","esdi_mca",
&esdi_ps2_device, 1 },
{ "", "", NULL, 0 }
};
/* Initialize the 'hdc_current' value based on configured HDC name. */
void
hdc_init(char *name)
{
int c;
pclog("HDC: initializing..\n");
for (c=0; controllers[c].device; c++) {
if (! strcmp(name, controllers[c].internal_name)) {
hdc_current = c;
break;
}
}
}
/* Reset the HDC, whichever one that is. */
void
hdc_reset(void)
{
pclog("HDC: reset(current=%d, internal=%d)\n",
hdc_current, (machines[machine].flags & MACHINE_HAS_HDC)?1:0);
/* If we have a valid controller, add its device. */
if (hdc_current > 1)
device_add(controllers[hdc_current].device);
}
char *
hdc_get_name(int hdc)
{
return(controllers[hdc].name);
}
char *
hdc_get_internal_name(int hdc)
{
return(controllers[hdc].internal_name);
}
device_t *
hdc_get_device(int hdc)
{
return(controllers[hdc].device);
}
int
hdc_get_flags(int hdc)
{
return(controllers[hdc].device->flags);
}
int
hdc_available(int hdc)
{
return(device_available(controllers[hdc].device));
}
int
hdc_current_is_mfm(void)
{
return(controllers[hdc_current].is_mfm);
}

58
src/disk/hdc.h Normal file
View File

@@ -0,0 +1,58 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Definitions for the common disk controller handler.
*
* Version: @(#)hdc.h 1.0.3 2017/10/01
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
* Copyright 2016,2017 Miran Grca.
* Copyright 2017 Fred N. van Kempen.
*/
#ifndef EMU_HDC_H
# define EMU_HDC_H
#define MFM_NUM 2 /* 2 drives per controller supported */
#define ESDI_NUM 2 /* 2 drives per controller supported */
#define XTIDE_NUM 2 /* 2 drives per controller supported */
#define IDE_NUM 8
#define SCSI_NUM 16 /* theoretically the controller can have at
* least 7 devices, with each device being
* able to support 8 units, but hey... */
extern char hdc_name[16];
extern int hdc_current;
extern device_t mfm_xt_xebec_device; /* mfm_xt_xebec */
extern device_t mfm_xt_dtc5150x_device; /* mfm_xt_dtc */
extern device_t mfm_at_wd1003_device; /* mfm_at_wd1003 */
extern device_t esdi_at_wd1007vse1_device; /* esdi_at */
extern device_t esdi_ps2_device; /* esdi_mca */
extern device_t xtide_device; /* xtide_xt */
extern device_t xtide_at_device; /* xtide_at */
extern device_t xtide_ps2_device; /* xtide_ps2 */
extern device_t xtide_at_ps2_device; /* xtide_at_ps2 */
extern void hdc_init(char *name);
extern void hdc_reset(void);
extern char *hdc_get_name(int hdc);
extern char *hdc_get_internal_name(int hdc);
extern device_t *hdc_get_device(int hdc);
extern int hdc_get_flags(int hdc);
extern int hdc_available(int hdc);
extern int hdc_current_is_mfm(void);
#endif /*EMU_HDC_H*/

853
src/disk/hdc_esdi_at.c Normal file
View File

@@ -0,0 +1,853 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Driver for the ESDI controller (WD1007-vse1) for PC/AT.
*
* Version: @(#)hdc_esdi_at.c 1.0.4 2017/10/09
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2008-2017 Sarah Walker.
* Copyright 2016,2017 Miran Grca.
* Copyright 2017 Fred N. van Kempen.
*/
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "../ibm.h"
#include "../device.h"
#include "../io.h"
#include "../mem.h"
#include "../pic.h"
#include "../rom.h"
#include "../timer.h"
#include "../ui.h"
#include "hdc.h"
#include "hdd.h"
#define HDC_TIME (TIMER_USEC*10LL)
#define BIOS_FILE L"roms/hdd/esdi_at/62-000279-061.bin"
#define STAT_ERR 0x01
#define STAT_INDEX 0x02
#define STAT_CORRECTED_DATA 0x04
#define STAT_DRQ 0x08 /* Data request */
#define STAT_DSC 0x10
#define STAT_SEEK_COMPLETE 0x20
#define STAT_READY 0x40
#define STAT_BUSY 0x80
#define ERR_DAM_NOT_FOUND 0x01 /*Data Address Mark not found*/
#define ERR_TR000 0x02 /*Track 0 not found*/
#define ERR_ABRT 0x04 /*Command aborted*/
#define ERR_ID_NOT_FOUND 0x10 /*ID not found*/
#define ERR_DATA_CRC 0x40 /*Data CRC error*/
#define ERR_BAD_BLOCK 0x80 /*Bad Block detected*/
#define CMD_NOP 0x00
#define CMD_RESTORE 0x10
#define CMD_READ 0x20
#define CMD_WRITE 0x30
#define CMD_VERIFY 0x40
#define CMD_FORMAT 0x50
#define CMD_SEEK 0x70
#define CMD_DIAGNOSE 0x90
#define CMD_SET_PARAMETERS 0x91
#define CMD_READ_PARAMETERS 0xec
extern char ide_fn[4][512];
typedef struct {
int cfg_spt;
int cfg_hpc;
int current_cylinder;
int real_spt;
int real_hpc;
int real_tracks;
int present;
int hdd_num;
} drive_t;
typedef struct {
uint8_t status;
uint8_t error;
int secount,sector,cylinder,head,cylprecomp;
uint8_t command;
uint8_t fdisk;
int pos;
int drive_sel;
int reset;
uint16_t buffer[256];
int irqstat;
int64_t callback;
drive_t drives[2];
rom_t bios_rom;
} esdi_t;
static inline void
irq_raise(esdi_t *esdi)
{
if (! (esdi->fdisk&2))
picint(1<<14);
esdi->irqstat=1;
}
static inline void
irq_lower(esdi_t *esdi)
{
picintc(1<<14);
}
static void
irq_update(esdi_t *esdi)
{
if (esdi->irqstat && !((pic2.pend|pic2.ins)&0x40) && !(esdi->fdisk & 2))
picint(1<<14);
}
/* Return the sector offset for the current register values. */
static int
get_sector(esdi_t *esdi, off64_t *addr)
{
drive_t *drive = &esdi->drives[esdi->drive_sel];
int heads = drive->cfg_hpc;
int sectors = drive->cfg_spt;
if (esdi->head > heads) {
pclog("esdi_get_sector: past end of configured heads\n");
return 1;
}
if (esdi->sector >= sectors+1) {
pclog("esdi_get_sector: past end of configured sectors\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) + (esdi->sector - 1);
} else {
/*
* When performing translation, the firmware seems to leave 1
* sector per track inaccessible (spare sector)
*/
int c, h, s;
*addr = ((((off64_t) esdi->cylinder * heads) + esdi->head) *
sectors) + (esdi->sector - 1);
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)
{
drive_t *drive = &esdi->drives[esdi->drive_sel];
esdi->sector++;
if (esdi->sector == (drive->cfg_spt + 1)) {
esdi->sector = 1;
if (++esdi->head == drive->cfg_hpc) {
esdi->head = 0;
esdi->cylinder++;
if (drive->current_cylinder < drive->real_tracks)
drive->current_cylinder++;
}
}
}
static void
esdi_writew(uint16_t port, uint16_t val, void *priv)
{
esdi_t *esdi = (esdi_t *)priv;
esdi->buffer[esdi->pos >> 1] = val;
esdi->pos += 2;
if (esdi->pos >= 512) {
esdi->pos = 0;
esdi->status = STAT_BUSY;
timer_process();
esdi->callback = 6LL*HDC_TIME;
timer_update_outstanding();
}
}
static void
esdi_write(uint16_t port, uint8_t val, void *priv)
{
esdi_t *esdi = (esdi_t *)priv;
switch (port) {
case 0x1F0: /* Data */
esdi_writew(port, val | (val << 8), priv);
return;
case 0x1F1: /* Write precompenstation */
esdi->cylprecomp = val;
return;
case 0x1F2: /* Sector count */
esdi->secount = val;
return;
case 0x1F3: /* Sector */
esdi->sector = val;
return;
case 0x1F4: /* Cylinder low */
esdi->cylinder = (esdi->cylinder & 0xFF00) | val;
return;
case 0x1F5: /* Cylinder high */
esdi->cylinder = (esdi->cylinder & 0xFF) | (val << 8);
return;
case 0x1F6: /* Drive/Head */
esdi->head = val & 0xF;
esdi->drive_sel = (val & 0x10) ? 1 : 0;
if (esdi->drives[esdi->drive_sel].present)
esdi->status = 0;
else
esdi->status = STAT_READY | STAT_DSC;
return;
case 0x1F7: /* Command register */
irq_lower(esdi);
esdi->command = val;
esdi->error = 0;
switch (val & 0xf0) {
case CMD_RESTORE:
esdi->command &= ~0x0f; /*Mask off step rate*/
esdi->status = STAT_BUSY;
timer_process();
esdi->callback = 200LL*HDC_TIME;
timer_update_outstanding();
break;
case CMD_SEEK:
esdi->command &= ~0x0f; /*Mask off step rate*/
esdi->status = STAT_BUSY;
timer_process();
esdi->callback = 200LL*HDC_TIME;
timer_update_outstanding();
break;
default:
switch (val) {
case CMD_NOP:
esdi->status = STAT_BUSY;
timer_process();
esdi->callback = 200LL*HDC_TIME;
timer_update_outstanding();
break;
case CMD_READ:
case CMD_READ+1:
case CMD_READ+2:
case CMD_READ+3:
esdi->command &= ~3;
if (val & 2)
fatal("Read with ECC\n");
case 0xa0:
esdi->status = STAT_BUSY;
timer_process();
esdi->callback = 200LL*HDC_TIME;
timer_update_outstanding();
break;
case CMD_WRITE:
case CMD_WRITE+1:
case CMD_WRITE+2:
case CMD_WRITE+3:
esdi->command &= ~3;
if (val & 2)
fatal("Write with ECC\n");
esdi->status = STAT_DRQ | STAT_DSC;
esdi->pos=0;
break;
case CMD_VERIFY:
case CMD_VERIFY+1:
esdi->command &= ~1;
esdi->status = STAT_BUSY;
timer_process();
esdi->callback = 200LL*HDC_TIME;
timer_update_outstanding();
break;
case CMD_FORMAT:
esdi->status = STAT_DRQ;
esdi->pos=0;
break;
case CMD_SET_PARAMETERS: /* Initialize Drive Parameters */
esdi->status = STAT_BUSY;
timer_process();
esdi->callback = 30LL*HDC_TIME;
timer_update_outstanding();
break;
case CMD_DIAGNOSE: /* Execute Drive Diagnostics */
esdi->status = STAT_BUSY;
timer_process();
esdi->callback = 200LL*HDC_TIME;
timer_update_outstanding();
break;
case 0xe0: /*???*/
case CMD_READ_PARAMETERS:
esdi->status = STAT_BUSY;
timer_process();
esdi->callback = 200LL*HDC_TIME;
timer_update_outstanding();
break;
default:
pclog("Bad esdi command %02X\n", val);
case 0xe8: /*???*/
esdi->status = STAT_BUSY;
timer_process();
esdi->callback = 200LL*HDC_TIME;
timer_update_outstanding();
break;
}
}
break;
case 0x3F6: /* Device control */
if ((esdi->fdisk & 4) && !(val & 4)) {
timer_process();
esdi->callback = 500LL*HDC_TIME;
timer_update_outstanding();
esdi->reset = 1;
esdi->status = STAT_BUSY;
}
if (val & 4) {
/*Drive held in reset*/
timer_process();
esdi->callback = 0LL;
timer_update_outstanding();
esdi->status = STAT_BUSY;
}
esdi->fdisk = val;
irq_update(esdi);
}
}
static uint16_t
esdi_readw(uint16_t port, void *priv)
{
esdi_t *esdi = (esdi_t *)priv;
uint16_t temp;
temp = esdi->buffer[esdi->pos >> 1];
esdi->pos += 2;
if (esdi->pos >= 512) {
esdi->pos=0;
esdi->status = STAT_READY | STAT_DSC;
if (esdi->command == CMD_READ || esdi->command == 0xa0) {
esdi->secount = (esdi->secount - 1) & 0xff;
if (esdi->secount) {
next_sector(esdi);
esdi->status = STAT_BUSY;
timer_process();
esdi->callback = 6LL*HDC_TIME;
timer_update_outstanding();
}
}
}
return(temp);
}
static uint8_t
esdi_read(uint16_t port, void *priv)
{
esdi_t *esdi = (esdi_t *)priv;
uint8_t temp = 0xff;
switch (port) {
case 0x1F0: /* Data */
temp = esdi_readw(port, esdi) & 0xff;
break;
case 0x1F1: /* Error */
temp = esdi->error;
break;
case 0x1F2: /* Sector count */
temp = (uint8_t)esdi->secount;
break;
case 0x1F3: /* Sector */
temp = (uint8_t)esdi->sector;
break;
case 0x1F4: /* Cylinder low */
temp = (uint8_t)(esdi->cylinder&0xFF);
break;
case 0x1F5: /* Cylinder high */
temp = (uint8_t)(esdi->cylinder>>8);
break;
case 0x1F6: /* Drive/Head */
temp = (uint8_t)(esdi->head | (esdi->drive_sel?0x10:0) | 0xa0);
break;
case 0x1F7: /* Status */
irq_lower(esdi);
temp = esdi->status;
break;
}
return(temp);
}
static void
esdi_callback(void *priv)
{
esdi_t *esdi = (esdi_t *)priv;
drive_t *drive = &esdi->drives[esdi->drive_sel];
off64_t addr;
esdi->callback = 0;
if (esdi->reset) {
esdi->status = STAT_READY | STAT_DSC;
esdi->error = 1;
esdi->secount = 1;
esdi->sector = 1;
esdi->head = 0;
esdi->cylinder = 0;
esdi->reset = 0;
return;
}
switch (esdi->command) {
case CMD_RESTORE:
if (! drive->present) {
esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
esdi->error = ERR_ABRT;
} else {
drive->current_cylinder = 0;
esdi->status = STAT_READY | STAT_DSC;
}
irq_raise(esdi);
break;
case CMD_SEEK:
if (! drive->present) {
esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
esdi->error = ERR_ABRT;
} else {
esdi->status = STAT_READY | STAT_DSC;
}
irq_raise(esdi);
break;
case CMD_READ:
if (! drive->present) {
esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
}
if (get_sector(esdi, &addr)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(esdi);
break;
}
if (hdd_image_read_ex(drive->hdd_num, addr, 1,
(uint8_t *)esdi->buffer)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(esdi);
break;
}
esdi->pos = 0;
esdi->status = STAT_DRQ | STAT_READY | STAT_DSC;
irq_raise(esdi);
ui_sb_update_icon(SB_HDD | HDD_BUS_ESDI, 1);
break;
case CMD_WRITE:
if (! drive->present) {
esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
}
if (get_sector(esdi, &addr)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(esdi);
break;
}
if (hdd_image_write_ex(drive->hdd_num, addr, 1,
(uint8_t *)esdi->buffer)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(esdi);
break;
}
irq_raise(esdi);
esdi->secount = (esdi->secount - 1) & 0xff;
if (esdi->secount) {
esdi->status = STAT_DRQ | STAT_READY | STAT_DSC;
esdi->pos = 0;
next_sector(esdi);
} else {
esdi->status = STAT_READY | STAT_DSC;
}
ui_sb_update_icon(SB_HDD | HDD_BUS_ESDI, 1);
break;
case CMD_VERIFY:
if (! drive->present) {
esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
}
if (get_sector(esdi, &addr)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(esdi);
break;
}
if (hdd_image_read_ex(drive->hdd_num, addr, 1,
(uint8_t *)esdi->buffer)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(esdi);
break;
}
ui_sb_update_icon(SB_HDD | HDD_BUS_ESDI, 1);
next_sector(esdi);
esdi->secount = (esdi->secount - 1) & 0xff;
if (esdi->secount)
esdi->callback = 6LL*HDC_TIME;
else {
esdi->pos = 0;
esdi->status = STAT_READY | STAT_DSC;
irq_raise(esdi);
}
break;
case CMD_FORMAT:
if (! drive->present) {
esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
}
if (get_sector(esdi, &addr)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(esdi);
break;
}
if (hdd_image_zero_ex(drive->hdd_num, addr, esdi->secount)) {
esdi->error = ERR_ID_NOT_FOUND;
esdi->status = STAT_READY | STAT_DSC | STAT_ERR;
irq_raise(esdi);
break;
}
esdi->status = STAT_READY | STAT_DSC;
irq_raise(esdi);
ui_sb_update_icon(SB_HDD | HDD_BUS_ESDI, 1);
break;
case CMD_DIAGNOSE:
if (! drive->present) {
esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
esdi->error = ERR_ABRT;
} else {
esdi->error = 1; /*No error detected*/
esdi->status = STAT_READY | STAT_DSC;
}
irq_raise(esdi);
break;
case CMD_SET_PARAMETERS: /* Initialize Drive Parameters */
if (! drive->present) {
esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
}
drive->cfg_spt = esdi->secount;
drive->cfg_hpc = esdi->head+1;
pclog("Parameters: spt=%i hpc=%i\n", drive->cfg_spt,drive->cfg_hpc);
if (! esdi->secount)
fatal("secount=0\n");
esdi->status = STAT_READY | STAT_DSC;
irq_raise(esdi);
break;
case CMD_NOP:
esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
case 0xe0:
if (! drive->present) {
esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
}
switch (esdi->cylinder >> 8) {
case 0x31:
esdi->cylinder = drive->real_tracks;
break;
case 0x33:
esdi->cylinder = drive->real_hpc;
break;
case 0x35:
esdi->cylinder = 0x200;
break;
case 0x36:
esdi->cylinder = drive->real_spt;
break;
default:
pclog("EDSI Bad read config %02x\n",
esdi->cylinder >> 8);
}
esdi->status = STAT_READY | STAT_DSC;
irq_raise(esdi);
break;
case 0xa0:
if (! drive->present) {
esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
esdi->error = ERR_ABRT;
} else {
memset(esdi->buffer, 0, 512);
memset(&esdi->buffer[3], 0xff, 512-6);
esdi->pos = 0;
esdi->status = STAT_DRQ | STAT_READY | STAT_DSC;
}
irq_raise(esdi);
break;
case CMD_READ_PARAMETERS:
if (! drive->present) {
esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
}
memset(esdi->buffer, 0, 512);
esdi->buffer[0] = 0x44; /* general configuration */
esdi->buffer[1] = drive->real_tracks; /* number of non-removable cylinders */
esdi->buffer[2] = 0; /* number of removable cylinders */
esdi->buffer[3] = drive->real_hpc; /* number of heads */
esdi->buffer[4] = 600; /* number of unformatted bytes/track */
esdi->buffer[5] = esdi->buffer[4] * drive->real_spt; /* number of unformatted bytes/sector */
esdi->buffer[6] = drive->real_spt; /* number of sectors */
esdi->buffer[7] = 0; /*minimum bytes in inter-sector gap*/
esdi->buffer[8] = 0; /* minimum bytes in postamble */
esdi->buffer[9] = 0; /* number of words of vendor status */
/* controller info */
esdi->buffer[20] = 2; /* controller type */
esdi->buffer[21] = 1; /* sector buffer size, in sectors */
esdi->buffer[22] = 0; /* ecc bytes appended */
esdi->buffer[27] = 'W' | ('D' << 8);
esdi->buffer[28] = '1' | ('0' << 8);
esdi->buffer[29] = '0' | ('7' << 8);
esdi->buffer[30] = 'V' | ('-' << 8);
esdi->buffer[31] = 'S' | ('E' << 8);
esdi->buffer[32] = '1';
esdi->buffer[47] = 0; /* sectors per interrupt */
esdi->buffer[48] = 0;/* can use double word read/write? */
esdi->pos = 0;
esdi->status = STAT_DRQ | STAT_READY | STAT_DSC;
irq_raise(esdi);
break;
default:
pclog("ESDI Callback on unknown command %02x\n", esdi->command);
case 0xe8:
esdi->status = STAT_READY | STAT_ERR | STAT_DSC;
esdi->error = ERR_ABRT;
irq_raise(esdi);
break;
}
ui_sb_update_icon(SB_HDD | HDD_BUS_ESDI, 0);
}
static void
esdi_rom_write(uint32_t addr, uint8_t val, void *priv)
{
rom_t *rom = (rom_t *)priv;
addr &= rom->mask;
if (addr >= 0x1f00 && addr < 0x2000)
rom->rom[addr] = val;
}
static void
loadhd(esdi_t *esdi, int hdd_num, int d, const wchar_t *fn)
{
drive_t *drive = &esdi->drives[d];
if (! hdd_image_load(hdd_num)) {
drive->present = 0;
return;
}
drive->cfg_spt = drive->real_spt = hdd[hdd_num].spt;
drive->cfg_hpc = drive->real_hpc = hdd[hdd_num].hpc;
drive->real_tracks = hdd[hdd_num].tracks;
drive->hdd_num = hdd_num;
drive->present = 1;
}
static void *
wd1007vse1_init(device_t *info)
{
int i, c = 0;
esdi_t *esdi = malloc(sizeof(esdi_t));
memset(esdi, 0x00, sizeof(esdi_t));
esdi->drives[0].present = esdi->drives[1].present = 0;
for (i=0; i<HDD_NUM; i++) {
if ((hdd[i].bus==HDD_BUS_ESDI) && (hdd[i].esdi_channel<ESDI_NUM)) {
loadhd(esdi, i, hdd[i].esdi_channel, hdd[i].fn);
if (++c >= ESDI_NUM) break;
}
}
esdi->status = STAT_READY | STAT_DSC;
esdi->error = 1;
rom_init(&esdi->bios_rom,
BIOS_FILE, 0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL);
mem_mapping_set_handler(
&esdi->bios_rom.mapping,
rom_read, rom_readw, rom_readl,
esdi_rom_write, NULL, NULL);
io_sethandler(0x01f0, 1,
esdi_read, esdi_readw, NULL,
esdi_write, esdi_writew, NULL, esdi);
io_sethandler(0x01f1, 7,
esdi_read, NULL, NULL,
esdi_write, NULL, NULL, esdi);
io_sethandler(0x03f6, 1, NULL, NULL, NULL,
esdi_write, NULL, NULL, esdi);
timer_add(esdi_callback, &esdi->callback, &esdi->callback, esdi);
return(esdi);
}
static void
wd1007vse1_close(void *priv)
{
esdi_t *esdi = (esdi_t *)priv;
drive_t *drive;
int d;
esdi->drives[0].present = esdi->drives[1].present = 0;
for (d=0; d<2; d++) {
drive = &esdi->drives[d];
hdd_image_close(drive->hdd_num);
}
free(esdi);
}
static int
wd1007vse1_available(void)
{
return(rom_present(BIOS_FILE));
}
device_t esdi_at_wd1007vse1_device = {
"Western Digital WD1007V-SE1 (ESDI)",
DEVICE_ISA | DEVICE_AT,
0,
wd1007vse1_init,
wd1007vse1_close,
NULL,
wd1007vse1_available,
NULL, NULL, NULL, NULL
};

1074
src/disk/hdc_esdi_mca.c Normal file

File diff suppressed because it is too large Load Diff

2371
src/disk/hdc_ide.c Normal file

File diff suppressed because it is too large Load Diff

106
src/disk/hdc_ide.h Normal file
View File

@@ -0,0 +1,106 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Implementation of the IDE emulation for hard disks and ATAPI
* CD-ROM devices.
*
* Version: @(#)hdd_ide.h 1.0.4 2017/09/30
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Copyright 2008-2017 Sarah Walker.
* Copyright 2016,2017 Miran Grca.
*/
#ifndef EMU_IDE_H
# define EMU_IDE_H
typedef struct {
int type;
int board;
uint8_t atastat;
uint8_t error;
int secount,sector,cylinder,head,drive,cylprecomp;
uint8_t command;
uint8_t fdisk;
int pos;
int packlen;
int spt,hpc;
int tracks;
int packetstatus;
uint8_t asc;
int reset;
uint16_t buffer[65536];
int irqstat;
int service;
int lba;
int channel;
uint32_t lba_addr;
int skip512;
int blocksize, blockcount;
uint16_t dma_identify_data[3];
int hdi,base;
int hdd_num;
uint8_t specify_success;
int mdma_mode;
uint8_t sector_buffer[256*512];
int do_initial_read;
int sector_pos;
} IDE;
extern int ideboard;
extern int ide_enable[5];
extern int ide_irq[5];
extern IDE ide_drives[IDE_NUM + XTIDE_NUM];
extern int64_t idecallback[5];
extern void ide_irq_raise(IDE *ide);
extern void ide_irq_lower(IDE *ide);
extern void writeide(int ide_board, uint16_t addr, uint8_t val);
extern void writeidew(int ide_board, uint16_t val);
extern uint8_t readide(int ide_board, uint16_t addr);
extern uint16_t readidew(int ide_board);
extern void callbackide(int ide_board);
extern void ide_set_bus_master(int (*read)(int channel, uint8_t *data, int transfer_length), int (*write)(int channel, uint8_t *data, int transfer_length), void (*set_irq)(int channel));
extern void win_cdrom_eject(uint8_t id);
extern void win_cdrom_reload(uint8_t id);
extern void ide_set_base(int controller, uint16_t port);
extern void ide_set_side(int controller, uint16_t port);
extern void ide_init_first(void);
extern void ide_init(void);
extern void ide_reset(void);
extern void ide_xtide_init(void);
extern void ide_pri_enable(void);
extern void ide_pri_enable_ex(void);
extern void ide_pri_disable(void);
extern void ide_sec_enable(void);
extern void ide_sec_disable(void);
extern void ide_ter_enable(void);
extern void ide_ter_disable(void);
extern void ide_ter_init(void);
extern void ide_qua_enable(void);
extern void ide_qua_disable(void);
extern void ide_qua_init(void);
extern void secondary_ide_check(void);
extern void ide_padstr8(uint8_t *buf, int buf_size, const char *src);
#endif /*EMU_IDE_H*/

771
src/disk/hdc_mfm_at.c Normal file
View File

@@ -0,0 +1,771 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Driver for the IBM PC-AT MFM/RLL Fixed Disk controller.
*
* This controller was a 16bit ISA card, and it used a WD1003
* based design. Most cards were WD1003-WA2 or -WAH, where the
* -WA2 cards had a floppy controller as well (to save space.)
*
* Version: @(#)hdd_mfm_at.c 1.0.8 2017/10/14
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2008-2017 Sarah Walker.
* Copyright 2017 Fred N. van Kempen.
*/
#define __USE_LARGEFILE64
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "../ibm.h"
#include "../device.h"
#include "../io.h"
#include "../pic.h"
#include "../timer.h"
#include "../ui.h"
#include "hdc.h"
#include "hdd.h"
#define MFM_TIME (TIMER_USEC*10LL)
#define MFM_DEBUG 0
#define STAT_ERR 0x01
#define STAT_INDEX 0x02
#define STAT_ECC 0x04
#define STAT_DRQ 0x08 /* data request */
#define STAT_DSC 0x10
#define STAT_WRFLT 0x20
#define STAT_READY 0x40
#define STAT_BUSY 0x80
#define ERR_DAM_NOT_FOUND 0x01 /* Data Address Mark not found */
#define ERR_TR000 0x02 /* track 0 not found */
#define ERR_ABRT 0x04 /* command aborted */
#define ERR_ID_NOT_FOUND 0x10 /* ID not found */
#define ERR_DATA_CRC 0x40 /* data CRC error */
#define ERR_BAD_BLOCK 0x80 /* bad block detected */
#define CMD_RESTORE 0x10
#define CMD_READ 0x20
#define CMD_WRITE 0x30
#define CMD_VERIFY 0x40
#define CMD_FORMAT 0x50
#define CMD_SEEK 0x70
#define CMD_DIAGNOSE 0x90
#define CMD_SET_PARAMETERS 0x91
typedef struct {
int8_t present, /* drive is present */
hdd_num, /* drive number in system */
steprate, /* current servo step rate */
spt, /* physical #sectors per track */
hpc, /* physical #heads per cylinder */
pad;
int16_t tracks; /* physical #tracks per cylinder */
int8_t cfg_spt, /* configured #sectors per track */
cfg_hpc; /* configured #heads per track */
int16_t curcyl; /* current track number */
} drive_t;
typedef struct {
uint8_t precomp, /* 1: precomp/error register */
error,
secount, /* 2: sector count register */
sector, /* 3: sector number */
head, /* 6: head number + drive select */
command, /* 7: command/status */
status,
fdisk; /* 8: control register */
uint16_t cylinder; /* 4/5: cylinder LOW and HIGH */
int8_t reset, /* controller in reset */
irqstat, /* current IRQ status */
drvsel, /* current selected drive */
pad;
int pos; /* offset within data buffer */
int64_t callback; /* callback delay timer */
uint16_t buffer[256]; /* data buffer (16b wide) */
drive_t drives[MFM_NUM]; /* attached drives */
} mfm_t;
static __inline void irq_raise(mfm_t *mfm)
{
/* If not already pending.. */
if (! mfm->irqstat) {
/* If enabled in the control register.. */
if (! (mfm->fdisk&0x02)) {
/* .. raise IRQ14. */
picint(1<<14);
}
/* Remember this. */
mfm->irqstat = 1;
}
}
static __inline void irq_lower(mfm_t *mfm)
{
/* If raised.. */
if (mfm->irqstat) {
/* If enabled in the control register.. */
if (! (mfm->fdisk&0x02)) {
/* .. drop IRQ14. */
picintc(1<<14);
}
/* Remember this. */
mfm->irqstat = 0;
}
}
/*
* Return the sector offset for the current register values.
*
* According to the WD1002/WD1003 technical reference manual,
* this is not done entirely correct. It specifies that the
* parameters set with the SET_DRIVE_PARAMETERS command are
* to be used only for multi-sector operations, and that any
* such operation can only be executed AFTER these parameters
* have been set. This would imply that for regular single
* transfers, the controller uses (or, can use) the actual
* geometry information...
*/
static int
get_sector(mfm_t *mfm, off64_t *addr)
{
drive_t *drive = &mfm->drives[mfm->drvsel];
if (drive->curcyl != mfm->cylinder) {
pclog("WD1003(%d) sector: wrong cylinder\n");
return(1);
}
if (mfm->head > drive->cfg_hpc) {
pclog("WD1003(%d) get_sector: past end of configured heads\n",
mfm->drvsel);
return(1);
}
if (mfm->sector >= drive->cfg_spt+1) {
pclog("WD1003(%d) get_sector: past end of configured sectors\n",
mfm->drvsel);
return(1);
}
#if 1
/* We should check this in the SET_DRIVE_PARAMETERS command! --FvK */
if (mfm->head > drive->hpc) {
pclog("WD1003(%d) get_sector: past end of heads\n", mfm->drvsel);
return(1);
}
if (mfm->sector >= drive->spt+1) {
pclog("WD1003(%d) get_sector: past end of sectors\n", mfm->drvsel);
return(1);
}
#endif
*addr = ((((off64_t) mfm->cylinder * drive->cfg_hpc) + mfm->head) *
drive->cfg_spt) + (mfm->sector - 1);
return(0);
}
/* Move to the next sector using CHS addressing. */
static void
next_sector(mfm_t *mfm)
{
drive_t *drive = &mfm->drives[mfm->drvsel];
if (++mfm->sector == (drive->cfg_spt+1)) {
mfm->sector = 1;
if (++mfm->head == drive->cfg_hpc) {
mfm->head = 0;
mfm->cylinder++;
if (drive->curcyl < drive->tracks)
drive->curcyl++;
}
}
}
static void
mfm_cmd(mfm_t *mfm, uint8_t val)
{
drive_t *drive = &mfm->drives[mfm->drvsel];
if (! drive->present) {
/* This happens if sofware polls all drives. */
pclog("WD1003(%d) command %02x on non-present drive\n",
mfm->drvsel, val);
mfm->command = 0xff;
mfm->status = STAT_BUSY;
timer_process();
mfm->callback = 200LL*MFM_TIME;
timer_update_outstanding();
return;
}
irq_lower(mfm);
mfm->error = 0;
switch (val & 0xf0) {
case CMD_RESTORE:
drive->steprate = (val & 0x0f);
#if MFM_DEBUG
pclog("WD1003(%d) restore, step=%d\n",
mfm->drvsel, drive->steprate);
#endif
drive->curcyl = 0;
mfm->status = STAT_READY|STAT_DSC;
mfm->command = 0x00;
irq_raise(mfm);
break;
case CMD_SEEK:
drive->steprate = (val & 0x0f);
mfm->command = (val & 0xf0);
mfm->status = STAT_BUSY;
timer_process();
mfm->callback = 200LL*MFM_TIME;
timer_update_outstanding();
break;
default:
switch (val) {
case CMD_READ:
case CMD_READ+1:
case CMD_READ+2:
case CMD_READ+3:
#if MFM_DEBUG
pclog("WD1003(%d) read, opt=%d\n",
mfm->drvsel, val&0x03);
#endif
mfm->command = (val & 0xf0);
if (val & 2)
fatal("WD1003: READ with ECC\n");
mfm->status = STAT_BUSY;
timer_process();
mfm->callback = 200LL*MFM_TIME;
timer_update_outstanding();
break;
case CMD_WRITE:
case CMD_WRITE+1:
case CMD_WRITE+2:
case CMD_WRITE+3:
#if MFM_DEBUG
pclog("WD1003(%d) write, opt=%d\n",
mfm->drvsel, val & 0x03);
#endif
mfm->command = (val & 0xf0);
if (val & 2)
fatal("WD1003: WRITE with ECC\n");
mfm->status = STAT_DRQ|STAT_DSC;
mfm->pos = 0;
break;
case CMD_VERIFY:
case CMD_VERIFY+1:
mfm->command = (val & 0xfe);
mfm->status = STAT_BUSY;
timer_process();
mfm->callback = 200LL*MFM_TIME;
timer_update_outstanding();
break;
case CMD_FORMAT:
mfm->command = val;
mfm->status = STAT_DRQ|STAT_BUSY;
mfm->pos = 0;
break;
case CMD_DIAGNOSE:
mfm->command = val;
mfm->status = STAT_BUSY;
timer_process();
mfm->callback = 200LL*MFM_TIME;
timer_update_outstanding();
break;
case CMD_SET_PARAMETERS:
/*
* NOTE:
*
* We currently just set these parameters, and
* never bother to check if they "fit within"
* the actual parameters, as determined by the
* image loader.
*
* The difference in parameters is OK, and
* occurs when the BIOS or operating system
* decides to use a different translation
* scheme, but either way, it SHOULD always
* fit within the actual parameters!
*
* We SHOULD check that here!! --FvK
*/
if (drive->cfg_spt == 0) {
/* Only accept after RESET or DIAG. */
drive->cfg_spt = mfm->secount;
drive->cfg_hpc = mfm->head+1;
pclog("WD1003(%d) parameters: tracks=%d, spt=%i, hpc=%i\n",
mfm->drvsel, drive->tracks,
drive->cfg_spt, drive->cfg_hpc);
} else {
pclog("WD1003(%d) parameters: tracks=%d,spt=%i,hpc=%i (IGNORED)\n",
mfm->drvsel, drive->tracks,
drive->cfg_spt, drive->cfg_hpc);
}
mfm->command = 0x00;
mfm->status = STAT_READY|STAT_DSC;
mfm->error = 1;
irq_raise(mfm);
break;
default:
pclog("WD1003: bad command %02X\n", val);
mfm->status = STAT_BUSY;
timer_process();
mfm->callback = 200LL*MFM_TIME;
timer_update_outstanding();
break;
}
}
}
static void
mfm_writew(uint16_t port, uint16_t val, void *priv)
{
mfm_t *mfm = (mfm_t *)priv;
mfm->buffer[mfm->pos >> 1] = val;
mfm->pos += 2;
if (mfm->pos >= 512) {
mfm->pos = 0;
mfm->status = STAT_BUSY;
timer_process();
mfm->callback = 6LL*MFM_TIME;
timer_update_outstanding();
}
}
static void
mfm_write(uint16_t port, uint8_t val, void *priv)
{
mfm_t *mfm = (mfm_t *)priv;
#if MFM_DEBUG > 1
pclog("WD1003 write(%04x, %02x)\n", port, val);
#endif
switch (port) {
case 0x01f0: /* data */
mfm_writew(port, val | (val << 8), priv);
return;
case 0x01f1: /* write precompenstation */
mfm->precomp = val;
return;
case 0x01f2: /* sector count */
mfm->secount = val;
return;
case 0x01f3: /* sector */
mfm->sector = val;
return;
case 0x01f4: /* cylinder low */
mfm->cylinder = (mfm->cylinder & 0xff00) | val;
return;
case 0x01f5: /* cylinder high */
mfm->cylinder = (mfm->cylinder & 0xff) | (val << 8);
return;
case 0x01f6: /* drive/head */
mfm->head = val & 0xF;
mfm->drvsel = (val & 0x10) ? 1 : 0;
if (mfm->drives[mfm->drvsel].present)
mfm->status = STAT_READY|STAT_DSC;
else
mfm->status = 0;
return;
case 0x01f7: /* command register */
mfm_cmd(mfm, val);
break;
case 0x03f6: /* device control */
val &= 0x0f;
if ((mfm->fdisk & 0x04) && !(val & 0x04)) {
mfm->status = STAT_BUSY;
mfm->reset = 1;
timer_process();
mfm->callback = 500LL*MFM_TIME;
timer_update_outstanding();
}
if (val & 0x04) {
/* Drive held in reset. */
mfm->status = STAT_BUSY;
mfm->callback = 0LL;
timer_process();
timer_update_outstanding();
}
mfm->fdisk = val;
break;
}
}
static uint16_t
mfm_readw(uint16_t port, void *priv)
{
mfm_t *mfm = (mfm_t *)priv;
uint16_t ret;
ret = mfm->buffer[mfm->pos >> 1];
mfm->pos += 2;
if (mfm->pos >= 512) {
mfm->pos = 0;
mfm->status = STAT_READY|STAT_DSC;
if (mfm->command == CMD_READ) {
mfm->secount = (mfm->secount - 1) & 0xff;
if (mfm->secount) {
next_sector(mfm);
mfm->status = STAT_BUSY;
timer_process();
mfm->callback = 6LL*MFM_TIME;
timer_update_outstanding();
} else {
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 0);
}
}
}
return(ret);
}
static uint8_t
mfm_read(uint16_t port, void *priv)
{
mfm_t *mfm = (mfm_t *)priv;
uint8_t ret = 0xff;
switch (port) {
case 0x01f0: /* data */
ret = mfm_readw(port, mfm) & 0xff;
break;
case 0x01f1: /* error */
ret = mfm->error;
break;
case 0x01f2: /* sector count */
ret = mfm->secount;
break;
case 0x01f3: /* sector */
ret = mfm->sector;
break;
case 0x01f4: /* CYlinder low */
ret = (uint8_t)(mfm->cylinder&0xff);
break;
case 0x01f5: /* Cylinder high */
ret = (uint8_t)(mfm->cylinder>>8);
break;
case 0x01f6: /* drive/head */
ret = (uint8_t)(0xa0 | mfm->head | (mfm->drvsel?0x10:0));
break;
case 0x01f7: /* Status */
irq_lower(mfm);
ret = mfm->status;
break;
default:
break;
}
#if MFM_DEBUG > 1
pclog("WD1003 read(%04x) = %02x\n", port, ret);
#endif
return(ret);
}
static void
do_seek(mfm_t *mfm)
{
drive_t *drive = &mfm->drives[mfm->drvsel];
#if MFM_DEBUG
pclog("WD1003(%d) seek(%d) max=%d\n",
mfm->drvsel,mfm->cylinder,drive->tracks);
#endif
if (mfm->cylinder < drive->tracks)
drive->curcyl = mfm->cylinder;
else
drive->curcyl = drive->tracks-1;
}
static void
do_callback(void *priv)
{
mfm_t *mfm = (mfm_t *)priv;
drive_t *drive = &mfm->drives[mfm->drvsel];
off64_t addr;
mfm->callback = 0LL;
if (mfm->reset) {
#if MFM_DEBUG
pclog("WD1003(%d) reset\n", mfm->drvsel);
#endif
mfm->status = STAT_READY|STAT_DSC;
mfm->error = 1;
mfm->secount = 1;
mfm->sector = 1;
mfm->head = 0;
mfm->cylinder = 0;
drive->steprate = 0x0f; /* default steprate */
drive->cfg_spt = 0; /* need new parameters */
mfm->reset = 0;
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 0);
return;
}
switch (mfm->command) {
case CMD_SEEK:
#if MFM_DEBUG
pclog("WD1003(%d) seek, step=%d\n",
mfm->drvsel, drive->steprate);
#endif
do_seek(mfm);
mfm->status = STAT_READY|STAT_DSC;
irq_raise(mfm);
break;
case CMD_READ:
#if MFM_DEBUG
pclog("WD1003(%d) read(%d,%d,%d)\n",
mfm->drvsel, mfm->cylinder, mfm->head, mfm->sector);
#endif
do_seek(mfm);
if (get_sector(mfm, &addr)) {
mfm->error = ERR_ID_NOT_FOUND;
mfm->status = STAT_READY|STAT_DSC|STAT_ERR;
irq_raise(mfm);
break;
}
hdd_image_read(drive->hdd_num, addr, 1, (uint8_t *)mfm->buffer);
mfm->pos = 0;
mfm->status = STAT_DRQ|STAT_READY|STAT_DSC;
irq_raise(mfm);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 1);
break;
case CMD_WRITE:
#if MFM_DEBUG
pclog("WD1003(%d) write(%d,%d,%d)\n",
mfm->drvsel, mfm->cylinder, mfm->head, mfm->sector);
#endif
do_seek(mfm);
if (get_sector(mfm, &addr)) {
mfm->error = ERR_ID_NOT_FOUND;
mfm->status = STAT_READY|STAT_DSC|STAT_ERR;
irq_raise(mfm);
break;
}
hdd_image_write(drive->hdd_num, addr, 1,(uint8_t *)mfm->buffer);
mfm->status = STAT_READY|STAT_DSC;
mfm->secount = (mfm->secount - 1) & 0xff;
if (mfm->secount) {
/* More sectors to do.. */
mfm->status |= STAT_DRQ;
mfm->pos = 0;
next_sector(mfm);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 1);
} else {
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 0);
}
irq_raise(mfm);
break;
case CMD_VERIFY:
#if MFM_DEBUG
pclog("WD1003(%d) verify(%d,%d,%d)\n",
mfm->drvsel, mfm->cylinder, mfm->head, mfm->sector);
#endif
do_seek(mfm);
mfm->pos = 0;
mfm->status = STAT_READY|STAT_DSC;
irq_raise(mfm);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 1);
break;
case CMD_FORMAT:
#if MFM_DEBUG
pclog("WD1003(%d) format(%d,%d)\n",
mfm->drvsel, mfm->cylinder, mfm->head);
#endif
do_seek(mfm);
if (get_sector(mfm, &addr)) {
mfm->error = ERR_ID_NOT_FOUND;
mfm->status = STAT_READY|STAT_DSC|STAT_ERR;
irq_raise(mfm);
break;
}
hdd_image_zero(drive->hdd_num, addr, mfm->secount);
mfm->status = STAT_READY|STAT_DSC;
irq_raise(mfm);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 1);
break;
case CMD_DIAGNOSE:
#if MFM_DEBUG
pclog("WD1003(%d) diag\n", mfm->drvsel);
#endif
drive->steprate = 0x0f;
mfm->error = 1;
mfm->status = STAT_READY|STAT_DSC;
irq_raise(mfm);
break;
default:
pclog("WD1003(%d) callback on unknown command %02x\n",
mfm->drvsel, mfm->command);
mfm->status = STAT_READY|STAT_ERR|STAT_DSC;
mfm->error = ERR_ABRT;
irq_raise(mfm);
break;
}
}
static void
loadhd(mfm_t *mfm, int c, int d, const wchar_t *fn)
{
drive_t *drive = &mfm->drives[c];
if (! hdd_image_load(d)) {
drive->present = 0;
return;
}
drive->spt = hdd[d].spt;
drive->hpc = hdd[d].hpc;
drive->tracks = hdd[d].tracks;
drive->hdd_num = d;
drive->present = 1;
}
static void *
mfm_init(device_t *info)
{
mfm_t *mfm;
int c, d;
pclog("WD1003: ISA MFM/RLL Fixed Disk Adapter initializing ...\n");
mfm = malloc(sizeof(mfm_t));
memset(mfm, 0x00, sizeof(mfm_t));
c = 0;
for (d=0; d<HDD_NUM; d++) {
if ((hdd[d].bus == HDD_BUS_MFM) && (hdd[d].mfm_channel < MFM_NUM)) {
loadhd(mfm, hdd[d].mfm_channel, d, hdd[d].fn);
pclog("WD1003(%d): (%ls) geometry %d/%d/%d\n", c, hdd[d].fn,
(int)hdd[d].tracks, (int)hdd[d].hpc, (int)hdd[d].spt);
if (++c >= MFM_NUM) break;
}
}
mfm->status = STAT_READY|STAT_DSC; /* drive is ready */
mfm->error = 1; /* no errors */
io_sethandler(0x01f0, 1,
mfm_read, mfm_readw, NULL, mfm_write, mfm_writew, NULL, mfm);
io_sethandler(0x01f1, 7,
mfm_read, NULL, NULL, mfm_write, NULL, NULL, mfm);
io_sethandler(0x03f6, 1,
NULL, NULL, NULL, mfm_write, NULL, NULL, mfm);
timer_add(do_callback, &mfm->callback, &mfm->callback, mfm);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 0);
return(mfm);
}
static void
mfm_close(void *priv)
{
mfm_t *mfm = (mfm_t *)priv;
int d;
for (d=0; d<2; d++) {
drive_t *drive = &mfm->drives[d];
hdd_image_close(drive->hdd_num);
}
free(mfm);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 0);
}
device_t mfm_at_wd1003_device = {
"WD1003 AT MFM/RLL Controller",
DEVICE_ISA | DEVICE_AT,
0,
mfm_init, mfm_close, NULL,
NULL, NULL, NULL, NULL, NULL
};

921
src/disk/hdc_mfm_xt.c Normal file
View File

@@ -0,0 +1,921 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Driver for the IBM PC-XT Fixed Disk controller.
*
* The original controller shipped by IBM was made by Xebec, and
* several variations had been made:
*
* #1 Original, single drive (ST412), 10MB, 2 heads.
* #2 Update, single drive (ST412) but with option for a
* switch block that can be used to 'set' the actual
* drive type. Four switches are define, where switches
* 1 and 2 define drive0, and switches 3 and 4 drive1.
*
* 0 ON ON 306 2 0
* 1 ON OFF 375 8 0
* 2 OFF ON 306 6 256
* 3 OFF OFF 306 4 0
*
* The latter option is the default, in use on boards
* without the switch block option.
*
* #3 Another updated board, mostly to accomodate the new
* 20MB disk now being shipped. The controller can have
* up to 2 drives, the type of which is set using the
* switch block:
*
* SW1 SW2 CYLS HD SPT WPC
* 0 ON ON 306 4 17 0
* 1 ON OFF 612 4 17 0 (type 16)
* 2 OFF ON 615 4 17 300 (Seagate ST-225, 2)
* 3 OFF OFF 306 8 17 128 (IBM WD25, 13)
*
* Examples of #3 are IBM/Xebec, WD10004A-WX1 and ST11R.
*
* Since all controllers (including the ones made by DTC) use
* (mostly) the same API, we keep them all in this module.
*
* Version: @(#)hdd_mfm_xt.c 1.0.9 2017/10/11
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2008-2017 Sarah Walker.
* Copyright 2017 Fred N. van Kempen.
*/
#define __USE_LARGEFILE64
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "../ibm.h"
#include "../device.h"
#include "../dma.h"
#include "../io.h"
#include "../mem.h"
#include "../pic.h"
#include "../rom.h"
#include "../timer.h"
#include "../ui.h"
#include "hdc.h"
#include "hdd.h"
#define MFM_TIME (2000LL*TIMER_USEC)
#define XEBEC_BIOS_FILE L"roms/hdd/mfm_xebec/ibm_xebec_62x0822_1985.bin"
#define DTC_BIOS_FILE L"roms/hdd/mfm_xebec/dtc_cxd21a.bin"
enum {
STATE_IDLE,
STATE_RECEIVE_COMMAND,
STATE_START_COMMAND,
STATE_RECEIVE_DATA,
STATE_RECEIVED_DATA,
STATE_SEND_DATA,
STATE_SENT_DATA,
STATE_COMPLETION_BYTE,
STATE_DUNNO
};
typedef struct {
int spt, hpc;
int tracks;
int cfg_spt;
int cfg_hpc;
int cfg_cyl;
int current_cylinder;
int present;
int hdd_num;
} drive_t;
typedef struct {
rom_t bios_rom;
int64_t callback;
int state;
uint8_t status;
uint8_t command[6];
int command_pos;
uint8_t data[512];
int data_pos, data_len;
uint8_t sector_buf[512];
uint8_t irq_dma_mask;
uint8_t completion_byte;
uint8_t error;
int drive_sel;
drive_t drives[2];
int sector, head, cylinder;
int sector_count;
uint8_t switches;
} mfm_t;
#define STAT_IRQ 0x20
#define STAT_DRQ 0x10
#define STAT_BSY 0x08
#define STAT_CD 0x04
#define STAT_IO 0x02
#define STAT_REQ 0x01
#define IRQ_ENA 0x02
#define DMA_ENA 0x01
#define CMD_TEST_DRIVE_READY 0x00
#define CMD_RECALIBRATE 0x01
#define CMD_READ_STATUS 0x03
#define CMD_VERIFY_SECTORS 0x05
#define CMD_FORMAT_TRACK 0x06
#define CMD_READ_SECTORS 0x08
#define CMD_WRITE_SECTORS 0x0a
#define CMD_SEEK 0x0b
#define CMD_INIT_DRIVE_PARAMS 0x0c
#define CMD_WRITE_SECTOR_BUFFER 0x0f
#define CMD_BUFFER_DIAGNOSTIC 0xe0
#define CMD_CONTROLLER_DIAGNOSTIC 0xe4
#define CMD_DTC_GET_DRIVE_PARAMS 0xfb
#define CMD_DTC_SET_STEP_RATE 0xfc
#define CMD_DTC_SET_GEOMETRY 0xfe
#define CMD_DTC_GET_GEOMETRY 0xff
#define ERR_NOT_READY 0x04
#define ERR_SEEK_ERROR 0x15
#define ERR_ILLEGAL_SECTOR_ADDRESS 0x21
static uint8_t
mfm_read(uint16_t port, void *priv)
{
mfm_t *mfm = (mfm_t *)priv;
uint8_t temp = 0xff;
switch (port) {
case 0x320: /*Read data*/
mfm->status &= ~STAT_IRQ;
switch (mfm->state) {
case STATE_COMPLETION_BYTE:
if ((mfm->status & 0xf) != (STAT_CD | STAT_IO | STAT_REQ | STAT_BSY))
fatal("Read data STATE_COMPLETION_BYTE, status=%02x\n", mfm->status);
temp = mfm->completion_byte;
mfm->status = 0;
mfm->state = STATE_IDLE;
break;
case STATE_SEND_DATA:
if ((mfm->status & 0xf) != (STAT_IO | STAT_REQ | STAT_BSY))
fatal("Read data STATE_COMPLETION_BYTE, status=%02x\n", mfm->status);
if (mfm->data_pos >= mfm->data_len)
fatal("Data write with full data!\n");
temp = mfm->data[mfm->data_pos++];
if (mfm->data_pos == mfm->data_len) {
mfm->status = STAT_BSY;
mfm->state = STATE_SENT_DATA;
mfm->callback = MFM_TIME;
}
break;
default:
fatal("Read data register - %i, %02x\n", mfm->state, mfm->status);
}
break;
case 0x321: /*Read status*/
temp = mfm->status;
break;
case 0x322: /*Read option jumpers*/
temp = mfm->switches;
break;
}
return(temp);
}
static void
mfm_write(uint16_t port, uint8_t val, void *priv)
{
mfm_t *mfm = (mfm_t *)priv;
switch (port) {
case 0x320: /*Write data*/
switch (mfm->state) {
case STATE_RECEIVE_COMMAND:
if ((mfm->status & 0xf) != (STAT_BSY | STAT_CD | STAT_REQ))
fatal("Bad write data state - STATE_START_COMMAND, status=%02x\n", mfm->status);
if (mfm->command_pos >= 6)
fatal("Command write with full command!\n");
/*Command data*/
mfm->command[mfm->command_pos++] = val;
if (mfm->command_pos == 6) {
mfm->status = STAT_BSY;
mfm->state = STATE_START_COMMAND;
mfm->callback = MFM_TIME;
}
break;
case STATE_RECEIVE_DATA:
if ((mfm->status & 0xf) != (STAT_BSY | STAT_REQ))
fatal("Bad write data state - STATE_RECEIVE_DATA, status=%02x\n", mfm->status);
if (mfm->data_pos >= mfm->data_len)
fatal("Data write with full data!\n");
/*Command data*/
mfm->data[mfm->data_pos++] = val;
if (mfm->data_pos == mfm->data_len) {
mfm->status = STAT_BSY;
mfm->state = STATE_RECEIVED_DATA;
mfm->callback = MFM_TIME;
}
break;
default:
fatal("Write data unknown state - %i %02x\n", mfm->state, mfm->status);
}
break;
case 0x321: /*Controller reset*/
mfm->status = 0;
break;
case 0x322: /*Generate controller-select-pulse*/
mfm->status = STAT_BSY | STAT_CD | STAT_REQ;
mfm->command_pos = 0;
mfm->state = STATE_RECEIVE_COMMAND;
break;
case 0x323: /*DMA/IRQ mask register*/
mfm->irq_dma_mask = val;
break;
}
}
static void mfm_complete(mfm_t *mfm)
{
mfm->status = STAT_REQ | STAT_CD | STAT_IO | STAT_BSY;
mfm->state = STATE_COMPLETION_BYTE;
if (mfm->irq_dma_mask & IRQ_ENA) {
mfm->status |= STAT_IRQ;
picint(1 << 5);
}
}
static void
mfm_error(mfm_t *mfm, uint8_t error)
{
mfm->completion_byte |= 0x02;
mfm->error = error;
pclog("mfm_error - %02x\n", mfm->error);
}
static int
get_sector(mfm_t *mfm, off64_t *addr)
{
drive_t *drive = &mfm->drives[mfm->drive_sel];
int heads = drive->cfg_hpc;
if (drive->current_cylinder != mfm->cylinder) {
pclog("mfm_get_sector: wrong cylinder\n");
mfm->error = ERR_ILLEGAL_SECTOR_ADDRESS;
return(1);
}
if (mfm->head > heads) {
pclog("mfm_get_sector: past end of configured heads\n");
mfm->error = ERR_ILLEGAL_SECTOR_ADDRESS;
return(1);
}
if (mfm->head > drive->hpc) {
pclog("mfm_get_sector: past end of heads\n");
mfm->error = ERR_ILLEGAL_SECTOR_ADDRESS;
return(1);
}
if (mfm->sector >= 17) {
pclog("mfm_get_sector: past end of sectors\n");
mfm->error = ERR_ILLEGAL_SECTOR_ADDRESS;
return(1);
}
*addr = ((((off64_t) mfm->cylinder * heads) + mfm->head) *
17) + mfm->sector;
return(0);
}
static void
next_sector(mfm_t *mfm)
{
drive_t *drive = &mfm->drives[mfm->drive_sel];
mfm->sector++;
if (mfm->sector >= 17) {
mfm->sector = 0;
mfm->head++;
if (mfm->head >= drive->cfg_hpc) {
mfm->head = 0;
mfm->cylinder++;
drive->current_cylinder++;
if (drive->current_cylinder >= drive->cfg_cyl)
drive->current_cylinder = drive->cfg_cyl-1;
}
}
}
static void
mfm_callback(void *priv)
{
mfm_t *mfm = (mfm_t *)priv;
drive_t *drive;
off64_t addr;
mfm->callback = 0LL;
mfm->drive_sel = (mfm->command[1] & 0x20) ? 1 : 0;
mfm->completion_byte = mfm->drive_sel & 0x20;
drive = &mfm->drives[mfm->drive_sel];
switch (mfm->command[0]) {
case CMD_TEST_DRIVE_READY:
if (!drive->present)
mfm_error(mfm, ERR_NOT_READY);
mfm_complete(mfm);
break;
case CMD_RECALIBRATE:
if (!drive->present)
mfm_error(mfm, ERR_NOT_READY);
else {
mfm->cylinder = 0;
drive->current_cylinder = 0;
}
mfm_complete(mfm);
break;
case CMD_READ_STATUS:
switch (mfm->state) {
case STATE_START_COMMAND:
mfm->state = STATE_SEND_DATA;
mfm->data_pos = 0;
mfm->data_len = 4;
mfm->status = STAT_BSY | STAT_IO | STAT_REQ;
mfm->data[0] = mfm->error;
mfm->data[1] = mfm->drive_sel ? 0x20 : 0;
mfm->data[2] = mfm->data[3] = 0;
mfm->error = 0;
break;
case STATE_SENT_DATA:
mfm_complete(mfm);
break;
}
break;
case CMD_VERIFY_SECTORS:
switch (mfm->state) {
case STATE_START_COMMAND:
mfm->cylinder = mfm->command[3] | ((mfm->command[2] & 0xc0) << 2);
drive->current_cylinder = (mfm->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : mfm->cylinder;
mfm->head = mfm->command[1] & 0x1f;
mfm->sector = mfm->command[2] & 0x1f;
mfm->sector_count = mfm->command[4];
do {
if (get_sector(mfm, &addr)) {
pclog("get_sector failed\n");
mfm_error(mfm, mfm->error);
mfm_complete(mfm);
return;
}
next_sector(mfm);
mfm->sector_count = (mfm->sector_count-1) & 0xff;
} while (mfm->sector_count);
mfm_complete(mfm);
ui_sb_update_icon(SB_HDD | HDD_BUS_MFM, 1);
break;
default:
fatal("CMD_VERIFY_SECTORS: bad state %i\n", mfm->state);
}
break;
case CMD_FORMAT_TRACK:
mfm->cylinder = mfm->command[3] | ((mfm->command[2] & 0xc0) << 2);
drive->current_cylinder = (mfm->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : mfm->cylinder;
mfm->head = mfm->command[1] & 0x1f;
if (get_sector(mfm, &addr)) {
pclog("get_sector failed\n");
mfm_error(mfm, mfm->error);
mfm_complete(mfm);
return;
}
hdd_image_zero(drive->hdd_num, addr, 17);
mfm_complete(mfm);
break;
case CMD_READ_SECTORS:
switch (mfm->state) {
case STATE_START_COMMAND:
mfm->cylinder = mfm->command[3] | ((mfm->command[2] & 0xc0) << 2);
drive->current_cylinder = (mfm->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : mfm->cylinder;
mfm->head = mfm->command[1] & 0x1f;
mfm->sector = mfm->command[2] & 0x1f;
mfm->sector_count = mfm->command[4];
mfm->state = STATE_SEND_DATA;
mfm->data_pos = 0;
mfm->data_len = 512;
if (get_sector(mfm, &addr)) {
mfm_error(mfm, mfm->error);
mfm_complete(mfm);
return;
}
hdd_image_read(drive->hdd_num, addr, 1,
(uint8_t *) mfm->sector_buf);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 1);
if (mfm->irq_dma_mask & DMA_ENA)
mfm->callback = MFM_TIME;
else {
mfm->status = STAT_BSY | STAT_IO | STAT_REQ;
memcpy(mfm->data, mfm->sector_buf, 512);
}
break;
case STATE_SEND_DATA:
mfm->status = STAT_BSY;
if (mfm->irq_dma_mask & DMA_ENA) {
for (; mfm->data_pos < 512; mfm->data_pos++) {
int val = dma_channel_write(3, mfm->sector_buf[mfm->data_pos]);
if (val == DMA_NODATA) {
pclog("CMD_READ_SECTORS out of data!\n");
mfm->status = STAT_BSY | STAT_CD | STAT_IO | STAT_REQ;
mfm->callback = MFM_TIME;
return;
}
}
mfm->state = STATE_SENT_DATA;
mfm->callback = MFM_TIME;
} else
fatal("Read sectors no DMA! - shouldn't get here\n");
break;
case STATE_SENT_DATA:
next_sector(mfm);
mfm->data_pos = 0;
mfm->sector_count = (mfm->sector_count-1) & 0xff;
if (mfm->sector_count) {
if (get_sector(mfm, &addr)) {
mfm_error(mfm, mfm->error);
mfm_complete(mfm);
return;
}
hdd_image_read(drive->hdd_num, addr, 1,
(uint8_t *) mfm->sector_buf);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 1);
mfm->state = STATE_SEND_DATA;
if (mfm->irq_dma_mask & DMA_ENA)
mfm->callback = MFM_TIME;
else {
mfm->status = STAT_BSY | STAT_IO | STAT_REQ;
memcpy(mfm->data, mfm->sector_buf, 512);
}
} else {
mfm_complete(mfm);
ui_sb_update_icon(SB_HDD | HDD_BUS_MFM, 0);
}
break;
default:
fatal("CMD_READ_SECTORS: bad state %i\n", mfm->state);
}
break;
case CMD_WRITE_SECTORS:
switch (mfm->state) {
case STATE_START_COMMAND:
mfm->cylinder = mfm->command[3] | ((mfm->command[2] & 0xc0) << 2);
drive->current_cylinder = (mfm->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : mfm->cylinder;
mfm->head = mfm->command[1] & 0x1f;
mfm->sector = mfm->command[2] & 0x1f;
mfm->sector_count = mfm->command[4];
mfm->state = STATE_RECEIVE_DATA;
mfm->data_pos = 0;
mfm->data_len = 512;
if (mfm->irq_dma_mask & DMA_ENA)
mfm->callback = MFM_TIME;
else
mfm->status = STAT_BSY | STAT_REQ;
break;
case STATE_RECEIVE_DATA:
mfm->status = STAT_BSY;
if (mfm->irq_dma_mask & DMA_ENA) {
for (; mfm->data_pos < 512; mfm->data_pos++) {
int val = dma_channel_read(3);
if (val == DMA_NODATA) {
pclog("CMD_WRITE_SECTORS out of data!\n");
mfm->status = STAT_BSY | STAT_CD | STAT_IO | STAT_REQ;
mfm->callback = MFM_TIME;
return;
}
mfm->sector_buf[mfm->data_pos] = val & 0xff;
}
mfm->state = STATE_RECEIVED_DATA;
mfm->callback = MFM_TIME;
} else
fatal("Write sectors no DMA! - should never get here\n");
break;
case STATE_RECEIVED_DATA:
if (! (mfm->irq_dma_mask & DMA_ENA))
memcpy(mfm->sector_buf, mfm->data, 512);
if (get_sector(mfm, &addr))
{
mfm_error(mfm, mfm->error);
mfm_complete(mfm);
return;
}
hdd_image_write(drive->hdd_num, addr, 1,
(uint8_t *) mfm->sector_buf);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 1);
next_sector(mfm);
mfm->data_pos = 0;
mfm->sector_count = (mfm->sector_count-1) & 0xff;
if (mfm->sector_count) {
mfm->state = STATE_RECEIVE_DATA;
if (mfm->irq_dma_mask & DMA_ENA)
mfm->callback = MFM_TIME;
else
mfm->status = STAT_BSY | STAT_REQ;
} else
mfm_complete(mfm);
break;
default:
fatal("CMD_WRITE_SECTORS: bad state %i\n", mfm->state);
}
break;
case CMD_SEEK:
if (! drive->present)
mfm_error(mfm, ERR_NOT_READY);
else {
int cylinder = mfm->command[3] | ((mfm->command[2] & 0xc0) << 2);
drive->current_cylinder = (cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : cylinder;
if (cylinder != drive->current_cylinder)
mfm_error(mfm, ERR_SEEK_ERROR);
}
mfm_complete(mfm);
break;
case CMD_INIT_DRIVE_PARAMS:
switch (mfm->state) {
case STATE_START_COMMAND:
mfm->state = STATE_RECEIVE_DATA;
mfm->data_pos = 0;
mfm->data_len = 8;
mfm->status = STAT_BSY | STAT_REQ;
break;
case STATE_RECEIVED_DATA:
drive->cfg_cyl = mfm->data[1] | (mfm->data[0] << 8);
drive->cfg_hpc = mfm->data[2];
pclog("Drive %i: cylinders=%i, heads=%i\n", mfm->drive_sel, drive->cfg_cyl, drive->cfg_hpc);
mfm_complete(mfm);
break;
default:
fatal("CMD_INIT_DRIVE_PARAMS bad state %i\n", mfm->state);
}
break;
case CMD_WRITE_SECTOR_BUFFER:
switch (mfm->state) {
case STATE_START_COMMAND:
mfm->state = STATE_RECEIVE_DATA;
mfm->data_pos = 0;
mfm->data_len = 512;
if (mfm->irq_dma_mask & DMA_ENA)
mfm->callback = MFM_TIME;
else
mfm->status = STAT_BSY | STAT_REQ;
break;
case STATE_RECEIVE_DATA:
if (mfm->irq_dma_mask & DMA_ENA) {
mfm->status = STAT_BSY;
for (; mfm->data_pos < 512; mfm->data_pos++) {
int val = dma_channel_read(3);
if (val == DMA_NODATA) {
pclog("CMD_WRITE_SECTOR_BUFFER out of data!\n");
mfm->status = STAT_BSY | STAT_CD | STAT_IO | STAT_REQ;
mfm->callback = MFM_TIME;
return;
}
mfm->data[mfm->data_pos] = val & 0xff;
}
mfm->state = STATE_RECEIVED_DATA;
mfm->callback = MFM_TIME;
} else
fatal("CMD_WRITE_SECTOR_BUFFER - should never get here!\n");
break;
case STATE_RECEIVED_DATA:
memcpy(mfm->sector_buf, mfm->data, 512);
mfm_complete(mfm);
break;
default:
fatal("CMD_WRITE_SECTOR_BUFFER bad state %i\n", mfm->state);
}
break;
case CMD_BUFFER_DIAGNOSTIC:
case CMD_CONTROLLER_DIAGNOSTIC:
mfm_complete(mfm);
break;
case 0xfa:
mfm_complete(mfm);
break;
case CMD_DTC_SET_STEP_RATE:
mfm_complete(mfm);
break;
case CMD_DTC_GET_DRIVE_PARAMS:
switch (mfm->state) {
case STATE_START_COMMAND:
mfm->state = STATE_SEND_DATA;
mfm->data_pos = 0;
mfm->data_len = 4;
mfm->status = STAT_BSY | STAT_IO | STAT_REQ;
memset(mfm->data, 0, 4);
mfm->data[0] = drive->tracks & 0xff;
mfm->data[1] = 17 | ((drive->tracks >> 2) & 0xc0);
mfm->data[2] = drive->hpc-1;
pclog("Get drive params %02x %02x %02x %i\n", mfm->data[0], mfm->data[1], mfm->data[2], drive->tracks);
break;
case STATE_SENT_DATA:
mfm_complete(mfm);
break;
default:
fatal("CMD_INIT_DRIVE_PARAMS bad state %i\n", mfm->state);
}
break;
case CMD_DTC_GET_GEOMETRY:
switch (mfm->state) {
case STATE_START_COMMAND:
mfm->state = STATE_SEND_DATA;
mfm->data_pos = 0;
mfm->data_len = 16;
mfm->status = STAT_BSY | STAT_IO | STAT_REQ;
memset(mfm->data, 0, 16);
mfm->data[0x4] = drive->tracks & 0xff;
mfm->data[0x5] = (drive->tracks >> 8) & 0xff;
mfm->data[0xa] = drive->hpc;
break;
case STATE_SENT_DATA:
mfm_complete(mfm);
break;
}
break;
case CMD_DTC_SET_GEOMETRY:
switch (mfm->state) {
case STATE_START_COMMAND:
mfm->state = STATE_RECEIVE_DATA;
mfm->data_pos = 0;
mfm->data_len = 16;
mfm->status = STAT_BSY | STAT_REQ;
break;
case STATE_RECEIVED_DATA:
/*Bit of a cheat here - we always report the actual geometry of the drive in use*/
mfm_complete(mfm);
break;
}
break;
default:
fatal("Unknown Xebec command - %02x %02x %02x %02x %02x %02x\n",
mfm->command[0], mfm->command[1],
mfm->command[2], mfm->command[3],
mfm->command[4], mfm->command[5]);
}
}
static void
loadhd(mfm_t *mfm, int c, int d, const wchar_t *fn)
{
drive_t *drive = &mfm->drives[d];
if (! hdd_image_load(d)) {
drive->present = 0;
return;
}
drive->spt = hdd[c].spt;
drive->hpc = hdd[c].hpc;
drive->tracks = hdd[c].tracks;
drive->hdd_num = c;
drive->present = 1;
}
static struct {
int tracks, hpc;
} hd_types[4] = {
{ 306, 4 }, /* Type 0 */
{ 612, 4 }, /* Type 16 */
{ 615, 4 }, /* Type 2 */
{ 306, 8 } /* Type 13 */
};
static void
mfm_set_switches(mfm_t *mfm)
{
int c, d;
mfm->switches = 0;
for (d=0; d<2; d++) {
drive_t *drive = &mfm->drives[d];
if (! drive->present) continue;
for (c=0; c<4; c++) {
if (drive->spt == 17 &&
drive->hpc == hd_types[c].hpc &&
drive->tracks == hd_types[c].tracks) {
mfm->switches |= (c << (d ? 0 : 2));
break;
}
}
if (c == 4)
pclog("WARNING: Drive %c: has format not supported by Fixed Disk Adapter", d ? 'D' : 'C');
}
}
static void *
xebec_init(device_t *info)
{
int i, c = 0;
mfm_t *xebec = malloc(sizeof(mfm_t));
memset(xebec, 0x00, sizeof(mfm_t));
for (i=0; i<HDD_NUM; i++) {
if ((hdd[i].bus == HDD_BUS_MFM) && (hdd[i].mfm_channel < MFM_NUM)) {
loadhd(xebec, i, hdd[i].mfm_channel, hdd[i].fn);
if (++c > MFM_NUM) break;
}
}
mfm_set_switches(xebec);
rom_init(&xebec->bios_rom, XEBEC_BIOS_FILE,
0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL);
io_sethandler(0x0320, 4,
mfm_read, NULL, NULL, mfm_write, NULL, NULL, xebec);
timer_add(mfm_callback, &xebec->callback, &xebec->callback, xebec);
return(xebec);
}
static void
mfm_close(void *priv)
{
mfm_t *mfm = (mfm_t *)priv;
int d;
for (d=0; d<2; d++) {
drive_t *drive = &mfm->drives[d];
hdd_image_close(drive->hdd_num);
}
free(mfm);
}
static int
xebec_available(void)
{
return(rom_present(XEBEC_BIOS_FILE));
}
device_t mfm_xt_xebec_device = {
"IBM PC Fixed Disk Adapter",
DEVICE_ISA,
0,
xebec_init, mfm_close, NULL,
xebec_available, NULL, NULL, NULL,
NULL
};
static void *
dtc5150x_init(device_t *info)
{
int i, c = 0;
mfm_t *dtc = malloc(sizeof(mfm_t));
memset(dtc, 0x00, sizeof(mfm_t));
for (i=0; i<HDD_NUM; i++) {
if ((hdd[i].bus == HDD_BUS_MFM) && (hdd[i].mfm_channel < MFM_NUM)) {
loadhd(dtc, i, hdd[i].mfm_channel, hdd[i].fn);
if (++c > MFM_NUM) break;
}
}
dtc->switches = 0xff;
dtc->drives[0].cfg_cyl = dtc->drives[0].tracks;
dtc->drives[0].cfg_hpc = dtc->drives[0].hpc;
dtc->drives[1].cfg_cyl = dtc->drives[1].tracks;
dtc->drives[1].cfg_hpc = dtc->drives[1].hpc;
rom_init(&dtc->bios_rom, DTC_BIOS_FILE,
0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL);
io_sethandler(0x0320, 4,
mfm_read, NULL, NULL, mfm_write, NULL, NULL, dtc);
timer_add(mfm_callback, &dtc->callback, &dtc->callback, dtc);
return(dtc);
}
static int
dtc5150x_available(void)
{
return(rom_present(DTC_BIOS_FILE));
}
device_t mfm_xt_dtc5150x_device = {
"DTC 5150X",
DEVICE_ISA,
0,
dtc5150x_init, mfm_close, NULL,
dtc5150x_available, NULL, NULL, NULL,
NULL
};

272
src/disk/hdc_xtide.c Normal file
View File

@@ -0,0 +1,272 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* XT-IDE controller emulation.
*
* The XT-IDE project is intended to allow 8-bit ("XT") systems
* to use regular IDE drives. IDE is a standard based on the
* 16b PC/AT design, and so a special board (with its own BIOS)
* had to be created for this.
*
* XT-IDE is *NOT* the same as XTA, or X-IDE, which is an older
* standard where the actual MFM/RLL controller for the PC/XT
* was placed on the hard drive (hard drives where its drive
* type would end in "X" or "XT", such as the 8425XT.) This was
* more or less the original IDE, but since those systems were
* already on their way out, the newer IDE standard based on the
* PC/AT controller and 16b design became the IDE we now know.
*
* Version: @(#)hdc_xtide.c 1.0.8 2017/10/09
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2008-2017 Sarah Walker.
* Copyright 2016,2017 Miran Grca.
* Copyright 2017 Fred N. van Kempen.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "../ibm.h"
#include "../io.h"
#include "../mem.h"
#include "../rom.h"
#include "../device.h"
#include "hdc.h"
#include "hdc_ide.h"
#define ROM_PATH_XT L"roms/hdd/xtide/ide_xt.bin"
#define ROM_PATH_AT L"roms/hdd/xtide/ide_at.bin"
#define ROM_PATH_PS2 L"roms/hdd/xtide/SIDE1V12.BIN"
#define ROM_PATH_PS2AT L"roms/hdd/xtide/ide_at_1_1_5.bin"
typedef struct {
uint8_t data_high;
rom_t bios_rom;
} xtide_t;
static void
xtide_write(uint16_t port, uint8_t val, void *priv)
{
xtide_t *xtide = (xtide_t *)priv;
switch (port & 0xf) {
case 0x0:
writeidew(4, val | (xtide->data_high << 8));
return;
case 0x1:
case 0x2:
case 0x3:
case 0x4:
case 0x5:
case 0x6:
case 0x7:
writeide(4, (port & 0xf) | 0x1f0, val);
return;
case 0x8:
xtide->data_high = val;
return;
case 0xe:
writeide(4, 0x3f6, val);
return;
}
}
static uint8_t
xtide_read(uint16_t port, void *priv)
{
xtide_t *xtide = (xtide_t *)priv;
uint16_t tempw = 0xffff;
switch (port & 0xf) {
case 0x0:
tempw = readidew(4);
xtide->data_high = tempw >> 8;
break;
case 0x1:
case 0x2:
case 0x3:
case 0x4:
case 0x5:
case 0x6:
case 0x7:
tempw = readide(4, (port & 0xf) | 0x1f0);
break;
case 0x8:
tempw = xtide->data_high;
break;
case 0xe:
tempw = readide(4, 0x3f6);
break;
default:
break;
}
return(tempw & 0xff);
}
static void *
xtide_init(device_t *info)
{
xtide_t *xtide = malloc(sizeof(xtide_t));
memset(xtide, 0x00, sizeof(xtide_t));
rom_init(&xtide->bios_rom, ROM_PATH_XT,
0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL);
ide_xtide_init();
io_sethandler(0x0300, 16,
xtide_read, NULL, NULL,
xtide_write, NULL, NULL, xtide);
return(xtide);
}
static int
xtide_available(void)
{
return(rom_present(ROM_PATH_XT));
}
static void *
xtide_at_init(device_t *info)
{
xtide_t *xtide = malloc(sizeof(xtide_t));
memset(xtide, 0x00, sizeof(xtide_t));
rom_init(&xtide->bios_rom, ROM_PATH_AT,
0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL);
ide_init();
return(xtide);
}
static int
xtide_at_available(void)
{
return(rom_present(ROM_PATH_AT));
}
static void *
xtide_ps2_init(device_t *info)
{
xtide_t *xtide = malloc(sizeof(xtide_t));
memset(xtide, 0x00, sizeof(xtide_t));
rom_init(&xtide->bios_rom, ROM_PATH_PS2,
0xc8000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL);
ide_xtide_init();
io_sethandler(0x0360, 16,
xtide_read, NULL, NULL,
xtide_write, NULL, NULL, xtide);
return(xtide);
}
static int
xtide_ps2_available(void)
{
return(rom_present(ROM_PATH_PS2));
}
static void *
xtide_at_ps2_init(device_t *info)
{
xtide_t *xtide = malloc(sizeof(xtide_t));
memset(xtide, 0x00, sizeof(xtide_t));
rom_init(&xtide->bios_rom, ROM_PATH_PS2AT,
0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL);
ide_init();
return(xtide);
}
static int
xtide_at_ps2_available(void)
{
return(rom_present(ROM_PATH_PS2AT));
}
static void
xtide_close(void *priv)
{
xtide_t *xtide = (xtide_t *)priv;
free(xtide);
}
device_t xtide_device = {
"XTIDE",
DEVICE_ISA,
0,
xtide_init, xtide_close, NULL,
xtide_available, NULL, NULL, NULL,
NULL
};
device_t xtide_at_device = {
"XTIDE (AT)",
DEVICE_ISA | DEVICE_AT,
0,
xtide_at_init, xtide_close, NULL,
xtide_at_available, NULL, NULL, NULL,
NULL
};
device_t xtide_ps2_device = {
"XTIDE (Acculogic)",
DEVICE_ISA,
0,
xtide_ps2_init, xtide_close, NULL,
xtide_ps2_available, NULL, NULL, NULL,
NULL
};
device_t xtide_at_ps2_device = {
"XTIDE (AT) (1.1.5)",
DEVICE_ISA | DEVICE_PS2,
0,
xtide_at_ps2_init, xtide_close, NULL,
xtide_at_ps2_available, NULL, NULL, NULL,
NULL
};

173
src/disk/hdd.c Normal file
View File

@@ -0,0 +1,173 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Common code to handle all sorts of hard disk images.
*
* Version: @(#)hdd.c 1.0.4 2017/10/09
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2016,2017 Miran Grca.
* Copyright 2017 Fred N. van Kempen.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <wchar.h>
#include "../ibm.h"
#include "../ui.h"
#include "hdd.h"
hard_disk_t hdd[HDD_NUM];
int
hdd_init(void)
{
/* Clear all global data. */
memset(hdd, 0x00, sizeof(hdd));
return(0);
}
int
hdd_string_to_bus(char *str, int cdrom)
{
if (! strcmp(str, "none"))
return(HDD_BUS_DISABLED);
if (! strcmp(str, "mfm")) {
if (cdrom) {
no_cdrom:
ui_msgbox(MBX_ERROR, (wchar_t *)IDS_4114);
return(0);
}
return(HDD_BUS_MFM);
}
/* FIXME: delete 'rll' in a year or so.. --FvK */
if (!strcmp(str, "esdi") || !strcmp(str, "rll")) {
if (cdrom) goto no_cdrom;
return(HDD_BUS_ESDI);
}
if (! strcmp(str, "ide_pio_only"))
return(HDD_BUS_IDE_PIO_ONLY);
if (! strcmp(str, "ide"))
return(HDD_BUS_IDE_PIO_ONLY);
if (! strcmp(str, "atapi_pio_only"))
return(HDD_BUS_IDE_PIO_ONLY);
if (! strcmp(str, "atapi"))
return(HDD_BUS_IDE_PIO_ONLY);
if (! strcmp(str, "eide"))
return(HDD_BUS_IDE_PIO_ONLY);
if (! strcmp(str, "xtide"))
return(HDD_BUS_XTIDE);
if (! strcmp(str, "atide"))
return(HDD_BUS_IDE_PIO_ONLY);
if (! strcmp(str, "ide_pio_and_dma"))
return(HDD_BUS_IDE_PIO_AND_DMA);
if (! strcmp(str, "atapi_pio_and_dma"))
return(HDD_BUS_IDE_PIO_AND_DMA);
if (! strcmp(str, "scsi"))
return(HDD_BUS_SCSI);
if (! strcmp(str, "removable")) {
if (cdrom) goto no_cdrom;
return(HDD_BUS_SCSI_REMOVABLE);
}
if (! strcmp(str, "scsi_removable")) {
if (cdrom) goto no_cdrom;
return(HDD_BUS_SCSI_REMOVABLE);
}
if (! strcmp(str, "removable_scsi")) {
if (cdrom) goto no_cdrom;
return(HDD_BUS_SCSI_REMOVABLE);
}
if (! strcmp(str, "usb"))
ui_msgbox(MBX_ERROR, (wchar_t *)IDS_4110);
return(0);
}
char *
hdd_bus_to_string(int bus, int cdrom)
{
char *s = "none";
switch (bus) {
case HDD_BUS_DISABLED:
default:
break;
case HDD_BUS_MFM:
s = "mfm";
break;
case HDD_BUS_XTIDE:
s = "xtide";
break;
case HDD_BUS_ESDI:
s = "esdi";
break;
case HDD_BUS_IDE_PIO_ONLY:
s = cdrom ? "atapi_pio_only" : "ide_pio_only";
break;
case HDD_BUS_IDE_PIO_AND_DMA:
s = cdrom ? "atapi_pio_and_dma" : "ide_pio_and_dma";
break;
case HDD_BUS_SCSI:
s = "scsi";
break;
case HDD_BUS_SCSI_REMOVABLE:
s = "scsi_removable";
break;
}
return(s);
}
int
hdd_is_valid(int c)
{
if (hdd[c].bus == HDD_BUS_DISABLED) return(0);
if ((wcslen(hdd[c].fn) == 0) &&
(hdd[c].bus != HDD_BUS_SCSI_REMOVABLE)) return(0);
if ((hdd[c].tracks==0) || (hdd[c].hpc==0) || (hdd[c].spt==0)) return(0);
return(1);
}

96
src/disk/hdd.h Normal file
View File

@@ -0,0 +1,96 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Definitions for the hard disk image handler.
*
* Version: @(#)hdd.h 1.0.3 2017/10/05
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
* Copyright 2016,2017 Miran Grca.
* Copyright 2017 Fred N. van Kempen.
*/
#ifndef EMU_HDD_H
# define EMU_HDD_H
#define HDD_NUM 30 /* total of 30 images supported */
/* Hard Disk bus types. */
enum {
HDD_BUS_DISABLED = 0,
HDD_BUS_MFM,
HDD_BUS_XTIDE,
HDD_BUS_ESDI,
HDD_BUS_IDE_PIO_ONLY,
HDD_BUS_IDE_PIO_AND_DMA,
HDD_BUS_SCSI,
HDD_BUS_SCSI_REMOVABLE,
HDD_BUS_USB
};
/* Define the virtual Hard Disk. */
typedef struct {
int8_t is_hdi; /* image type (should rename) */
int8_t wp; /* disk has been mounted READ-ONLY */
uint8_t bus;
uint8_t mfm_channel; /* should rename and/or unionize */
uint8_t esdi_channel;
uint8_t xtide_channel;
uint8_t ide_channel;
uint8_t scsi_id;
uint8_t scsi_lun;
uint32_t base;
uint64_t spt,
hpc, /* physical geometry parameters */
tracks;
uint64_t at_spt, /* [Translation] parameters */
at_hpc;
FILE *f; /* current file handle to image */
wchar_t fn[260]; /* name of current image file */
wchar_t prev_fn[260]; /* name of previous image file */
} hard_disk_t;
extern hard_disk_t hdd[HDD_NUM];
extern uint64_t hdd_table[128][3];
extern int hdd_init(void);
extern int hdd_string_to_bus(char *str, int cdrom);
extern char *hdd_bus_to_string(int bus, int cdrom);
extern int hdd_is_valid(int c);
extern int hdd_image_load(int id);
extern void hdd_image_seek(uint8_t id, uint32_t sector);
extern void hdd_image_read(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer);
extern int hdd_image_read_ex(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer);
extern void hdd_image_write(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer);
extern int hdd_image_write_ex(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer);
extern void hdd_image_zero(uint8_t id, uint32_t sector, uint32_t count);
extern int hdd_image_zero_ex(uint8_t id, uint32_t sector, uint32_t count);
extern uint32_t hdd_image_get_last_sector(uint8_t id);
extern uint8_t hdd_image_get_type(uint8_t id);
extern void hdd_image_specify(uint8_t id, uint64_t hpc, uint64_t spt);
extern void hdd_image_unload(uint8_t id, int fn_preserve);
extern void hdd_image_close(uint8_t id);
extern int image_is_hdi(const wchar_t *s);
extern int image_is_hdx(const wchar_t *s, int check_signature);
#endif /*EMU_HDD_H*/

553
src/disk/hdd_image.c Normal file
View File

@@ -0,0 +1,553 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Handling of hard disk image files.
*
* Version: @(#)hdd_image.c 1.0.5 2017/11/13
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2008-2017 Sarah Walker.
* Copyright 2016-2017 Miran Grca.
* Copyright 2017 Fred N. van Kempen.
*/
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <wchar.h>
#include <errno.h>
#include "../ibm.h"
#include "../plat.h"
#include "hdd.h"
typedef struct
{
FILE *file;
uint32_t base;
uint32_t last_sector;
uint8_t type;
uint8_t loaded;
} hdd_image_t;
hdd_image_t hdd_images[HDD_NUM];
static char empty_sector[512];
static char *empty_sector_1mb;
int hdd_image_do_log = 0;
void hdd_image_log(const char *format, ...)
{
#ifdef ENABLE_HDD_IMAGE_LOG
if (hdd_image_do_log)
{
va_list ap;
va_start(ap, format);
vprintf(format, ap);
va_end(ap);
fflush(stdout);
}
#endif
}
int image_is_hdi(const wchar_t *s)
{
int len;
wchar_t ext[5] = { 0, 0, 0, 0, 0 };
char *ws = (char *) s;
len = wcslen(s);
if ((len < 4) || (s[0] == L'.'))
{
return 0;
}
memcpy(ext, ws + ((len - 4) << 1), 8);
if (! wcscasecmp(ext, L".HDI"))
{
return 1;
}
else
{
return 0;
}
}
int
image_is_hdx(const wchar_t *s, int check_signature)
{
int len;
FILE *f;
uint64_t filelen;
uint64_t signature;
char *ws = (char *) s;
wchar_t ext[5] = { 0, 0, 0, 0, 0 };
len = wcslen(s);
if ((len < 4) || (s[0] == L'.'))
{
return 0;
}
memcpy(ext, ws + ((len - 4) << 1), 8);
if (wcscasecmp(ext, L".HDX") == 0)
{
if (check_signature)
{
f = plat_fopen((wchar_t *)s, L"rb");
if (!f)
{
return 0;
}
fseeko64(f, 0, SEEK_END);
filelen = ftello64(f);
fseeko64(f, 0, SEEK_SET);
if (filelen < 44)
{
return 0;
}
fread(&signature, 1, 8, f);
fclose(f);
if (signature == 0xD778A82044445459ll)
{
return 1;
}
else
{
return 0;
}
}
else
{
return 1;
}
}
else
{
return 0;
}
}
int hdd_image_load(int id)
{
uint32_t sector_size = 512;
uint32_t zero = 0;
uint64_t signature = 0xD778A82044445459ll;
uint64_t full_size = 0;
uint64_t spt = 0, hpc = 0, tracks = 0;
int c;
uint64_t i = 0, s = 0, t = 0;
wchar_t *fn = hdd[id].fn;
int is_hdx[2] = { 0, 0 };
memset(empty_sector, 0, sizeof(empty_sector));
hdd_images[id].base = 0;
if (hdd_images[id].loaded)
{
if (hdd_images[id].file)
{
fclose(hdd_images[id].file);
hdd_images[id].file = NULL;
}
hdd_images[id].loaded = 0;
}
is_hdx[0] = image_is_hdx(fn, 0);
is_hdx[1] = image_is_hdx(fn, 1);
/* Try to open existing hard disk image */
if (fn[0] == '.')
{
hdd_image_log("File name starts with .\n");
memset(hdd[id].fn, 0, sizeof(hdd[id].fn));
return 0;
}
hdd_images[id].file = plat_fopen(fn, L"rb+");
if (hdd_images[id].file == NULL)
{
/* Failed to open existing hard disk image */
if (errno == ENOENT)
{
/* Failed because it does not exist,
so try to create new file */
if (hdd[id].wp)
{
hdd_image_log("A write-protected image must exist\n");
memset(hdd[id].fn, 0, sizeof(hdd[id].fn));
return 0;
}
hdd_images[id].file = plat_fopen(fn, L"wb+");
if (hdd_images[id].file == NULL)
{
hdd_image_log("Unable to open image\n");
memset(hdd[id].fn, 0, sizeof(hdd[id].fn));
return 0;
}
else
{
if (image_is_hdi(fn))
{
full_size = hdd[id].spt * hdd[id].hpc * hdd[id].tracks * 512;
hdd_images[id].base = 0x1000;
fwrite(&zero, 1, 4, hdd_images[id].file);
fwrite(&zero, 1, 4, hdd_images[id].file);
fwrite(&(hdd_images[id].base), 1, 4, hdd_images[id].file);
fwrite(&full_size, 1, 4, hdd_images[id].file);
fwrite(&sector_size, 1, 4, hdd_images[id].file);
fwrite(&(hdd[id].spt), 1, 4, hdd_images[id].file);
fwrite(&(hdd[id].hpc), 1, 4, hdd_images[id].file);
fwrite(&(hdd[id].tracks), 1, 4, hdd_images[id].file);
for (c = 0; c < 0x3f8; c++)
{
fwrite(&zero, 1, 4, hdd_images[id].file);
}
hdd_images[id].type = 1;
}
else if (is_hdx[0])
{
full_size = hdd[id].spt * hdd[id].hpc * hdd[id].tracks * 512;
hdd_images[id].base = 0x28;
fwrite(&signature, 1, 8, hdd_images[id].file);
fwrite(&full_size, 1, 8, hdd_images[id].file);
fwrite(&sector_size, 1, 4, hdd_images[id].file);
fwrite(&(hdd[id].spt), 1, 4, hdd_images[id].file);
fwrite(&(hdd[id].hpc), 1, 4, hdd_images[id].file);
fwrite(&(hdd[id].tracks), 1, 4, hdd_images[id].file);
fwrite(&zero, 1, 4, hdd_images[id].file);
fwrite(&zero, 1, 4, hdd_images[id].file);
hdd_images[id].type = 2;
}
else
{
hdd_images[id].type = 0;
}
hdd_images[id].last_sector = 0;
}
s = full_size = hdd[id].spt * hdd[id].hpc * hdd[id].tracks * 512;
goto prepare_new_hard_disk;
}
else
{
/* Failed for another reason */
hdd_image_log("Failed for another reason\n");
memset(hdd[id].fn, 0, sizeof(hdd[id].fn));
return 0;
}
}
else
{
if (image_is_hdi(fn))
{
fseeko64(hdd_images[id].file, 0x8, SEEK_SET);
fread(&(hdd_images[id].base), 1, 4, hdd_images[id].file);
fseeko64(hdd_images[id].file, 0xC, SEEK_SET);
full_size = 0;
fread(&full_size, 1, 4, hdd_images[id].file);
fseeko64(hdd_images[id].file, 0x10, SEEK_SET);
fread(&sector_size, 1, 4, hdd_images[id].file);
if (sector_size != 512)
{
/* Sector size is not 512 */
hdd_image_log("HDI: Sector size is not 512\n");
fclose(hdd_images[id].file);
hdd_images[id].file = NULL;
memset(hdd[id].fn, 0, sizeof(hdd[id].fn));
return 0;
}
fread(&spt, 1, 4, hdd_images[id].file);
fread(&hpc, 1, 4, hdd_images[id].file);
fread(&tracks, 1, 4, hdd_images[id].file);
if (hdd[id].bus == HDD_BUS_SCSI_REMOVABLE)
{
if ((spt != hdd[id].spt) || (hpc != hdd[id].hpc) || (tracks != hdd[id].tracks))
{
hdd_image_log("HDI: Geometry mismatch\n");
fclose(hdd_images[id].file);
hdd_images[id].file = NULL;
memset(hdd[id].fn, 0, sizeof(hdd[id].fn));
return 0;
}
}
hdd[id].spt = spt;
hdd[id].hpc = hpc;
hdd[id].tracks = tracks;
hdd_images[id].type = 1;
}
else if (is_hdx[1])
{
hdd_images[id].base = 0x28;
fseeko64(hdd_images[id].file, 8, SEEK_SET);
fread(&full_size, 1, 8, hdd_images[id].file);
fseeko64(hdd_images[id].file, 0x10, SEEK_SET);
fread(&sector_size, 1, 4, hdd_images[id].file);
if (sector_size != 512)
{
/* Sector size is not 512 */
hdd_image_log("HDX: Sector size is not 512\n");
fclose(hdd_images[id].file);
hdd_images[id].file = NULL;
memset(hdd[id].fn, 0, sizeof(hdd[id].fn));
return 0;
}
fread(&spt, 1, 4, hdd_images[id].file);
fread(&hpc, 1, 4, hdd_images[id].file);
fread(&tracks, 1, 4, hdd_images[id].file);
if (hdd[id].bus == HDD_BUS_SCSI_REMOVABLE)
{
if ((spt != hdd[id].spt) || (hpc != hdd[id].hpc) || (tracks != hdd[id].tracks))
{
hdd_image_log("HDX: Geometry mismatch\n");
fclose(hdd_images[id].file);
hdd_images[id].file = NULL;
memset(hdd[id].fn, 0, sizeof(hdd[id].fn));
return 0;
}
}
hdd[id].spt = spt;
hdd[id].hpc = hpc;
hdd[id].tracks = tracks;
fread(&(hdd[id].at_spt), 1, 4, hdd_images[id].file);
fread(&(hdd[id].at_hpc), 1, 4, hdd_images[id].file);
hdd_images[id].type = 2;
}
else
{
full_size = hdd[id].spt * hdd[id].hpc * hdd[id].tracks * 512;
hdd_images[id].type = 0;
}
}
fseeko64(hdd_images[id].file, 0, SEEK_END);
if (ftello64(hdd_images[id].file) < (full_size + hdd_images[id].base))
{
s = (full_size + hdd_images[id].base) - ftello64(hdd_images[id].file);
prepare_new_hard_disk:
s >>= 9;
t = (s >> 11) << 11;
s -= t;
t >>= 11;
empty_sector_1mb = (char *) malloc(1048576);
memset(empty_sector_1mb, 0, 1048576);
if (s > 0)
{
for (i = 0; i < s; i++)
{
fwrite(empty_sector, 1, 512, hdd_images[id].file);
}
}
if (t > 0)
{
for (i = 0; i < t; i++)
{
fwrite(empty_sector_1mb, 1, 1045876, hdd_images[id].file);
}
}
free(empty_sector_1mb);
}
hdd_images[id].last_sector = (uint32_t) (full_size >> 9) - 1;
hdd_images[id].loaded = 1;
return 1;
}
void hdd_image_seek(uint8_t id, uint32_t sector)
{
uint64_t addr = sector;
addr <<= 9;
addr += hdd_images[id].base;
fseeko64(hdd_images[id].file, addr, SEEK_SET);
}
void hdd_image_read(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer)
{
count <<= 9;
hdd_image_seek(id, sector);
memset(buffer, 0, count);
fread(buffer, 1, count, hdd_images[id].file);
}
uint32_t hdd_sectors(uint8_t id)
{
fseeko64(hdd_images[id].file, 0, SEEK_END);
return (uint32_t) (ftello64(hdd_images[id].file) >> 9);
}
int hdd_image_read_ex(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer)
{
uint32_t transfer_sectors = count;
uint32_t sectors = hdd_sectors(id);
if ((sectors - sector) < transfer_sectors)
{
transfer_sectors = sectors - sector;
}
hdd_image_seek(id, sector);
memset(buffer, 0, transfer_sectors << 9);
fread(buffer, 1, transfer_sectors << 9, hdd_images[id].file);
if (count != transfer_sectors)
{
return 1;
}
return 0;
}
void hdd_image_write(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer)
{
count <<= 9;
hdd_image_seek(id, sector);
fwrite(buffer, 1, count, hdd_images[id].file);
}
int hdd_image_write_ex(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer)
{
uint32_t transfer_sectors = count;
uint32_t sectors = hdd_sectors(id);
if ((sectors - sector) < transfer_sectors)
{
transfer_sectors = sectors - sector;
}
hdd_image_seek(id, sector);
memset(buffer, 0, transfer_sectors << 9);
fwrite(buffer, 1, transfer_sectors << 9, hdd_images[id].file);
if (count != transfer_sectors)
{
return 1;
}
return 0;
}
void hdd_image_zero(uint8_t id, uint32_t sector, uint32_t count)
{
int i = 0;
uint8_t *b;
b = (uint8_t *) malloc(512);
memset(b, 0, 512);
hdd_image_seek(id, sector);
for (i = 0; i < count; i++)
{
fwrite(b, 1, 512, hdd_images[id].file);
}
free(b);
}
int hdd_image_zero_ex(uint8_t id, uint32_t sector, uint32_t count)
{
int i = 0;
uint8_t *b;
uint32_t transfer_sectors = count;
uint32_t sectors = hdd_sectors(id);
if ((sectors - sector) < transfer_sectors)
{
transfer_sectors = sectors - sector;
}
b = (uint8_t *) malloc(512);
memset(b, 0, 512);
hdd_image_seek(id, sector);
for (i = 0; i < transfer_sectors; i++)
{
fwrite(b, 1, 512, hdd_images[id].file);
}
if (count != transfer_sectors)
{
return 1;
}
return 0;
}
uint32_t hdd_image_get_last_sector(uint8_t id)
{
return hdd_images[id].last_sector;
}
uint8_t hdd_image_get_type(uint8_t id)
{
return hdd_images[id].type;
}
void hdd_image_specify(uint8_t id, uint64_t hpc, uint64_t spt)
{
if (hdd_images[id].type == 2)
{
hdd[id].at_hpc = hpc;
hdd[id].at_spt = spt;
fseeko64(hdd_images[id].file, 0x20, SEEK_SET);
fwrite(&(hdd[id].at_spt), 1, 4, hdd_images[id].file);
fwrite(&(hdd[id].at_hpc), 1, 4, hdd_images[id].file);
}
}
void hdd_image_unload(uint8_t id, int fn_preserve)
{
if (wcslen(hdd[id].fn) == 0)
{
return;
}
if (hdd_images[id].loaded)
{
if (hdd_images[id].file != NULL)
{
fclose(hdd_images[id].file);
hdd_images[id].file = NULL;
}
hdd_images[id].loaded = 0;
}
hdd_images[id].last_sector = -1;
memset(hdd[id].prev_fn, 0, sizeof(hdd[id].prev_fn));
if (fn_preserve)
wcscpy(hdd[id].prev_fn, hdd[id].fn);
memset(hdd[id].fn, 0, sizeof(hdd[id].fn));
}
void hdd_image_close(uint8_t id)
{
if (hdd_images[id].file != NULL)
{
fclose(hdd_images[id].file);
hdd_images[id].file = NULL;
}
hdd_images[id].loaded = 0;
}

177
src/disk/hdd_table.c Normal file
View File

@@ -0,0 +1,177 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Implementation of the IDE emulation for hard disks and ATAPI
* CD-ROM devices.
*
* Version: @(#)hdd_table.c 1.0.3 2017/09/30
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
* Copyright 2016,2017 Miran Grca.
* Copyright 2017 Fred N. van Kempen.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <wchar.h>
#include "../86box.h"
#include "../ibm.h"
#include "../io.h"
#include "../pic.h"
#include "../pci.h"
#include "../timer.h"
#include "hdd.h"
uint64_t hdd_table[128][3] = {
{ 306, 4, 17 }, /* 0 - 7 */
{ 615, 2, 17 },
{ 306, 4, 26 },
{ 1024, 2, 17 },
{ 697, 3, 17 },
{ 306, 8, 17 },
{ 614, 4, 17 },
{ 615, 4, 17 },
{ 670, 4, 17 }, /* 8 - 15 */
{ 697, 4, 17 },
{ 987, 3, 17 },
{ 820, 4, 17 },
{ 670, 5, 17 },
{ 697, 5, 17 },
{ 733, 5, 17 },
{ 615, 6, 17 },
{ 462, 8, 17 }, /* 016-023 */
{ 306, 8, 26 },
{ 615, 4, 26 },
{ 1024, 4, 17 },
{ 855, 5, 17 },
{ 925, 5, 17 },
{ 932, 5, 17 },
{ 1024, 2, 40 },
{ 809, 6, 17 }, /* 024-031 */
{ 976, 5, 17 },
{ 977, 5, 17 },
{ 698, 7, 17 },
{ 699, 7, 17 },
{ 981, 5, 17 },
{ 615, 8, 17 },
{ 989, 5, 17 },
{ 820, 4, 26 }, /* 032-039 */
{ 1024, 5, 17 },
{ 733, 7, 17 },
{ 754, 7, 17 },
{ 733, 5, 26 },
{ 940, 6, 17 },
{ 615, 6, 26 },
{ 462, 8, 26 },
{ 830, 7, 17 }, /* 040-047 */
{ 855, 7, 17 },
{ 751, 8, 17 },
{ 1024, 4, 26 },
{ 918, 7, 17 },
{ 925, 7, 17 },
{ 855, 5, 26 },
{ 977, 7, 17 },
{ 987, 7, 17 }, /* 048-055 */
{ 1024, 7, 17 },
{ 823, 4, 38 },
{ 925, 8, 17 },
{ 809, 6, 26 },
{ 976, 5, 26 },
{ 977, 5, 26 },
{ 698, 7, 26 },
{ 699, 7, 26 }, /* 056-063 */
{ 940, 8, 17 },
{ 615, 8, 26 },
{ 1024, 5, 26 },
{ 733, 7, 26 },
{ 1024, 8, 17 },
{ 823, 10, 17 },
{ 754, 11, 17 },
{ 830, 10, 17 }, /* 064-071 */
{ 925, 9, 17 },
{ 1224, 7, 17 },
{ 940, 6, 26 },
{ 855, 7, 26 },
{ 751, 8, 26 },
{ 1024, 9, 17 },
{ 965, 10, 17 },
{ 969, 5, 34 }, /* 072-079 */
{ 980, 10, 17 },
{ 960, 5, 35 },
{ 918, 11, 17 },
{ 1024, 10, 17 },
{ 977, 7, 26 },
{ 1024, 7, 26 },
{ 1024, 11, 17 },
{ 940, 8, 26 }, /* 080-087 */
{ 776, 8, 33 },
{ 755, 16, 17 },
{ 1024, 12, 17 },
{ 1024, 8, 26 },
{ 823, 10, 26 },
{ 830, 10, 26 },
{ 925, 9, 26 },
{ 960, 9, 26 }, /* 088-095 */
{ 1024, 13, 17 },
{ 1224, 11, 17 },
{ 900, 15, 17 },
{ 969, 7, 34 },
{ 917, 15, 17 },
{ 918, 15, 17 },
{ 1524, 4, 39 },
{ 1024, 9, 26 }, /* 096-103 */
{ 1024, 14, 17 },
{ 965, 10, 26 },
{ 980, 10, 26 },
{ 1020, 15, 17 },
{ 1023, 15, 17 },
{ 1024, 15, 17 },
{ 1024, 16, 17 },
{ 1224, 15, 17 }, /* 104-111 */
{ 755, 16, 26 },
{ 903, 8, 46 },
{ 984, 10, 34 },
{ 900, 15, 26 },
{ 917, 15, 26 },
{ 1023, 15, 26 },
{ 684, 16, 38 },
{ 1930, 4, 62 }, /* 112-119 */
{ 967, 16, 31 },
{ 1013, 10, 63 },
{ 1218, 15, 36 },
{ 654, 16, 63 },
{ 659, 16, 63 },
{ 702, 16, 63 },
{ 1002, 13, 63 },
{ 854, 16, 63 }, /* 119-127 */
{ 987, 16, 63 },
{ 995, 16, 63 },
{ 1024, 16, 63 },
{ 1036, 16, 63 },
{ 1120, 16, 59 },
{ 1054, 16, 63 },
{ 0, 0, 0 }
};