Even more cleanups. Also renamed hdd/ to disk/ as that makes more sense. Machines with an Internal HDC (usually IDE) can now also select an alternative one instead.
This commit is contained in:
186
src/disk/hdc.c
Normal file
186
src/disk/hdc.c
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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.2 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.
|
||||
*/
|
||||
#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(void)
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
null_close(void *priv)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static device_t null_device = {
|
||||
"Null HDC", 0,
|
||||
null_init, null_close,
|
||||
NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
|
||||
static void *
|
||||
inthdc_init(void)
|
||||
{
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
inthdc_close(void *priv)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static device_t inthdc_device = {
|
||||
"Internal Controller", 0,
|
||||
inthdc_init, inthdc_close,
|
||||
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 },
|
||||
|
||||
{ "[MFM] IBM PC Fixed Disk Adapter", "mfm_xebec",
|
||||
&mfm_xt_xebec_device, 1 },
|
||||
|
||||
{ "[MFM] DTC-5150X Fixed Disk Adapter", "mfm_dtc5150x",
|
||||
&mfm_xt_dtc5150x_device, 1 },
|
||||
|
||||
{ "[MFM] IBM PC/AT Fixed Disk Adapter", "mfm_at",
|
||||
&mfm_at_wd1003_device, 1 },
|
||||
|
||||
{ "[ESDI] PC/AT ESDI Fixed Disk Adapter", "esdi_wd1007vse1",
|
||||
&esdi_at_wd1007vse1_device, 0 },
|
||||
|
||||
{ "[ESDI] IBM PS/2 ESDI Fixed Disk Adapter","esdi_mca",
|
||||
&esdi_ps2_device, 1 },
|
||||
|
||||
#if 0
|
||||
{ "[IDE] PC/AT IDE Adapter", "ide_isa",
|
||||
&ide_isa_device, 0 },
|
||||
|
||||
{ "[IDE] PCI IDE Adapter", "ide_pci",
|
||||
&ide_pci_device, 0 },
|
||||
#endif
|
||||
|
||||
{ "[IDE] PC/XT XTIDE", "xtide",
|
||||
&xtide_device , 0 },
|
||||
|
||||
{ "[IDE] PC/AT XTIDE", "xtide_at",
|
||||
&xtide_at_device, 0 },
|
||||
|
||||
{ "[IDE] PS/2 XTIDE (Acculogic)", "xtide_ps2",
|
||||
&xtide_ps2_device, 0 },
|
||||
|
||||
{ "[IDE] PS/2 AT XTIDE (1.1.5)", "xtide_at_ps2",
|
||||
&xtide_at_ps2_device, 0 },
|
||||
|
||||
{ "", "", 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);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
57
src/disk/hdc.h
Normal file
57
src/disk/hdc.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 int hdc_get_flags(int hdc);
|
||||
extern int hdc_available(int hdc);
|
||||
extern int hdc_current_is_mfm(void);
|
||||
|
||||
|
||||
#endif /*EMU_HDC_H*/
|
||||
849
src/disk/hdc_esdi_at.c
Normal file
849
src/disk/hdc_esdi_at.c
Normal file
@@ -0,0 +1,849 @@
|
||||
/*
|
||||
* 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.2 2017/10/01
|
||||
*
|
||||
* 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 "hdc.h"
|
||||
#include "hdd.h"
|
||||
|
||||
|
||||
#define HDC_TIME (TIMER_USEC*10)
|
||||
#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;
|
||||
|
||||
int 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 = 6*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 = 200*HDC_TIME;
|
||||
timer_update_outstanding();
|
||||
break;
|
||||
|
||||
case CMD_SEEK:
|
||||
esdi->command &= ~0x0f; /*Mask off step rate*/
|
||||
esdi->status = STAT_BUSY;
|
||||
timer_process();
|
||||
esdi->callback = 200*HDC_TIME;
|
||||
timer_update_outstanding();
|
||||
break;
|
||||
|
||||
default:
|
||||
switch (val) {
|
||||
case CMD_NOP:
|
||||
esdi->status = STAT_BUSY;
|
||||
timer_process();
|
||||
esdi->callback = 200*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 = 200*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 = 200*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 = 30*HDC_TIME;
|
||||
timer_update_outstanding();
|
||||
break;
|
||||
|
||||
case CMD_DIAGNOSE: /* Execute Drive Diagnostics */
|
||||
esdi->status = STAT_BUSY;
|
||||
timer_process();
|
||||
esdi->callback = 200*HDC_TIME;
|
||||
timer_update_outstanding();
|
||||
break;
|
||||
|
||||
case 0xe0: /*???*/
|
||||
case CMD_READ_PARAMETERS:
|
||||
esdi->status = STAT_BUSY;
|
||||
timer_process();
|
||||
esdi->callback = 200*HDC_TIME;
|
||||
timer_update_outstanding();
|
||||
break;
|
||||
|
||||
default:
|
||||
pclog("Bad esdi command %02X\n", val);
|
||||
case 0xe8: /*???*/
|
||||
esdi->status = STAT_BUSY;
|
||||
timer_process();
|
||||
esdi->callback = 200*HDC_TIME;
|
||||
timer_update_outstanding();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x3F6: /* Device control */
|
||||
if ((esdi->fdisk & 4) && !(val & 4)) {
|
||||
timer_process();
|
||||
esdi->callback = 500*HDC_TIME;
|
||||
timer_update_outstanding();
|
||||
esdi->reset = 1;
|
||||
esdi->status = STAT_BUSY;
|
||||
}
|
||||
|
||||
if (val & 4) {
|
||||
/*Drive held in reset*/
|
||||
timer_process();
|
||||
esdi->callback = 0;
|
||||
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 = 6*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);
|
||||
update_status_bar_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;
|
||||
}
|
||||
update_status_bar_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;
|
||||
}
|
||||
|
||||
update_status_bar_icon(SB_HDD | HDD_BUS_ESDI, 1);
|
||||
next_sector(esdi);
|
||||
esdi->secount = (esdi->secount - 1) & 0xff;
|
||||
if (esdi->secount)
|
||||
esdi->callback = 6*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);
|
||||
update_status_bar_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;
|
||||
}
|
||||
|
||||
update_status_bar_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(void)
|
||||
{
|
||||
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_AT,
|
||||
wd1007vse1_init,
|
||||
wd1007vse1_close,
|
||||
wd1007vse1_available,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
1072
src/disk/hdc_esdi_mca.c
Normal file
1072
src/disk/hdc_esdi_mca.c
Normal file
File diff suppressed because it is too large
Load Diff
2363
src/disk/hdc_ide.c
Normal file
2363
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
106
src/disk/hdc_ide.h
Normal 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 int 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*/
|
||||
765
src/disk/hdc_mfm_at.c
Normal file
765
src/disk/hdc_mfm_at.c
Normal file
@@ -0,0 +1,765 @@
|
||||
/*
|
||||
* 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.4 2017/09/29
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#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 "hdc.h"
|
||||
#include "hdd.h"
|
||||
|
||||
|
||||
#define MFM_TIME (TIMER_USEC*10)
|
||||
#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 */
|
||||
int 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 = 200*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 = 200*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 = 200*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 = 200*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 = 200*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 = 200*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 = 6*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 = 500*MFM_TIME;
|
||||
timer_update_outstanding();
|
||||
}
|
||||
|
||||
if (val & 0x04) {
|
||||
/* Drive held in reset. */
|
||||
mfm->status = STAT_BUSY;
|
||||
mfm->callback = 0;
|
||||
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 = 6*MFM_TIME;
|
||||
timer_update_outstanding();
|
||||
} else {
|
||||
update_status_bar_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 = 0;
|
||||
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;
|
||||
|
||||
update_status_bar_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);
|
||||
update_status_bar_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);
|
||||
update_status_bar_icon(SB_HDD|HDD_BUS_MFM, 1);
|
||||
} else {
|
||||
update_status_bar_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);
|
||||
update_status_bar_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);
|
||||
update_status_bar_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(void)
|
||||
{
|
||||
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): (%S) 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);
|
||||
|
||||
update_status_bar_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);
|
||||
|
||||
update_status_bar_icon(SB_HDD|HDD_BUS_MFM, 0);
|
||||
}
|
||||
|
||||
|
||||
device_t mfm_at_wd1003_device = {
|
||||
"WD1003 AT MFM/RLL Controller",
|
||||
DEVICE_AT,
|
||||
mfm_init, mfm_close, NULL,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
912
src/disk/hdc_mfm_xt.c
Normal file
912
src/disk/hdc_mfm_xt.c
Normal file
@@ -0,0 +1,912 @@
|
||||
/*
|
||||
* 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.6 2017/09/30
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#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 "hdc.h"
|
||||
#include "hdd.h"
|
||||
|
||||
|
||||
#define MFM_TIME (2000*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;
|
||||
int 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 = 0;
|
||||
|
||||
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);
|
||||
|
||||
update_status_bar_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);
|
||||
update_status_bar_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);
|
||||
update_status_bar_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);
|
||||
update_status_bar_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);
|
||||
update_status_bar_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(void)
|
||||
{
|
||||
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",
|
||||
0,
|
||||
xebec_init, mfm_close, xebec_available,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
|
||||
static void *
|
||||
dtc5150x_init(void)
|
||||
{
|
||||
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",
|
||||
0,
|
||||
dtc5150x_init, mfm_close, dtc5150x_available,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
263
src/disk/hdc_xtide.c
Normal file
263
src/disk/hdc_xtide.c
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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: @(#)xtide.c 1.0.6 2017/10/10
|
||||
*
|
||||
* 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(void)
|
||||
{
|
||||
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(void)
|
||||
{
|
||||
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(void)
|
||||
{
|
||||
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(void)
|
||||
{
|
||||
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",
|
||||
0,
|
||||
xtide_init, xtide_close, xtide_available,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
device_t xtide_at_device = {
|
||||
"XTIDE (AT)",
|
||||
DEVICE_AT,
|
||||
xtide_at_init, xtide_close, xtide_at_available,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
device_t xtide_ps2_device = {
|
||||
"XTIDE (Acculogic)",
|
||||
0,
|
||||
xtide_ps2_init, xtide_close, xtide_ps2_available,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
device_t xtide_at_ps2_device = {
|
||||
"XTIDE (AT) (1.1.5)",
|
||||
DEVICE_PS2,
|
||||
xtide_at_ps2_init, xtide_close, xtide_at_ps2_available,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
39
src/disk/hdd.c
Normal file
39
src/disk/hdd.c
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.2 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 <wchar.h>
|
||||
#include "../ibm.h"
|
||||
#include "../cpu/cpu.h"
|
||||
#include "../device.h"
|
||||
#include "../machine/machine.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);
|
||||
}
|
||||
93
src/disk/hdd.h
Normal file
93
src/disk/hdd.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.2 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.
|
||||
*/
|
||||
#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_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*/
|
||||
551
src/disk/hdd_image.c
Normal file
551
src/disk/hdd_image.c
Normal file
@@ -0,0 +1,551 @@
|
||||
/*
|
||||
* 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.2 2017/10/01
|
||||
*
|
||||
* 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 "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 (wcsicmp(ext, L".HDI") == 0)
|
||||
{
|
||||
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 (wcsicmp(ext, L".HDX") == 0)
|
||||
{
|
||||
if (check_signature)
|
||||
{
|
||||
f = _wfopen(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 = _wfopen(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 = _wfopen(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(§or_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(§or_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(§or_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(§or_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
177
src/disk/hdd_table.c
Normal 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 }
|
||||
};
|
||||
Reference in New Issue
Block a user