Updated EuroPC NVR handling.

This commit is contained in:
waltje
2017-11-19 21:59:38 -05:00
parent 020bb4d92c
commit 1562c8bb99
4 changed files with 1625 additions and 109 deletions

View File

@@ -1,145 +1,705 @@
/* Copyright holders: Sarah Walker
see COPYING for more details
*/
/*
* 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 Schneider EuroPC system.
*
* NOTES: BIOS info (taken from MAME, thanks guys!!)
*
* f000:e107 bios checksum test
* memory test
* f000:e145 irq vector init
* f000:e156
* f000:e169-d774 test of special registers 254/354
* f000:e16c-e817
* f000:e16f
* f000:ec08 test of special registers 800a rtc time
* or date error, rtc corrected
* f000:ef66 0xf
* f000:db3e 0x8..0xc
* f000:d7f8
* f000:db5f
* f000:e172
* f000:ecc5 801a video setup error
* f000:d6c9 copyright output
* f000:e1b7
* f000:e1be DI bits set mean output text!!!,
* (801a)
* f000: 0x8000 output
* 1 rtc error
* 2 rtc time or date error
* 4 checksum error in setup
* 8 rtc status corrected
* 10 video setup error
* 20 video ram bad
* 40 monitor type not recogniced
* 80 mouse port enabled
* 100 joystick port enabled
* f000:e1e2-dc0c CPU speed is 4.77 mhz
* f000:e1e5-f9c0 keyboard processor error
* f000:e1eb-c617 external lpt1 at 0x3bc
* f000:e1ee-e8ee external coms at
*
* Routines:
* f000:c92d output text at bp
* f000:db3e RTC read reg cl
* f000:e8ee piep
* f000:e95e RTC write reg cl
* polls until JIM 0xa is zero,
* output cl at jim 0xa
* write ah hinibble as lownibble into jim 0xa
* write ah lownibble into jim 0xa
* f000:ef66 RTC read reg cl
* polls until jim 0xa is zero,
* output cl at jim 0xa
* read low 4 nibble at jim 0xa
* read low 4 nibble at jim 0xa
* return first nibble<<4|second nibble in ah
* f000:f046 seldom compares ret
* f000:fe87 0 -> ds
* 0000:0469 bit 0: b0000 memory available
* bit 1: b8000 memory available
* 0000:046a: 00 jim 250 01 jim 350
*
* WARNING THIS IS A WORK-IN-PROGRESS MODULE. USE AT OWN RISK.
*
* Version: @(#)europc.c 1.0.5 2017/11/18
*
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
*
* Inspired by the "jim.c" file originally present, but a
* fully re-written module, based on the information from
* Schneider's schematics and technical manuals, and the
* input from people with real EuroPC hardware.
*
* Copyright 2017 Fred N. van Kempen.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "../86box.h"
#include "../io.h"
#include "../nmi.h"
#include "../mem.h"
#include "../rom.h"
#include "../device.h"
#include "../nvr.h"
#include "../device.h"
#include "../disk/hdc.h"
#include "../keyboard.h"
#include "../lpt.h"
#include "../mouse.h"
#include "../game/gameport.h"
#include "../video/video.h"
#include "machine.h"
uint8_t europcdat[16];
#define EUROPC_DEBUG 1 /* current debugging level */
struct
{
/* M3002 RTC chip registers. */
#define MRTC_SECONDS 0x00 /* BCD, 00-59 */
#define MRTC_MINUTES 0x01 /* BCD, 00-59 */
#define MRTC_HOURS 0x02 /* BCD, 00-23 */
#define MRTC_DAYS 0x03 /* BCD, 01-31 */
#define MRTC_MONTHS 0x04 /* BCD, 01-12 */
#define MRTC_YEARS 0x05 /* BCD, 00-99 (2017 is 0x17) */
#define MRTC_WEEKDAY 0x06 /* BCD, 01-07 */
#define MRTC_WEEKNO 0x07 /* BCD, 01-52 */
#define MRTC_CONF_A 0x08 /* EuroPC config, binary */
#define MRTC_CONF_B 0x09 /* EuroPC config, binary */
#define MRTC_CONF_C 0x0a /* EuroPC config, binary */
#define MRTC_CONF_D 0x0b /* EuroPC config, binary */
#define MRTC_CONF_E 0x0c /* EuroPC config, binary */
#define MRTC_CHECK_LO 0x0d /* Checksum, low byte */
#define MRTC_CHECK_HI 0x0e /* Checksum, high byte */
#define MRTC_CTRLSTAT 0x0f /* RTC control/status, binary */
typedef struct {
uint16_t jim; /* JIM base address */
uint8_t regs[16]; /* JIM internal regs (8) */
struct {
uint8_t dat[16];
uint8_t stat;
uint8_t addr;
} europc_rtc;
} rtc;
nvr_t nvr; /* 86Box NVR */
} vm_t;
static uint8_t jim_load_nvr(void)
static vm_t *vm = NULL;
/* Load the relevant portion of the NVR to disk. */
static int8_t
load_nvr(wchar_t *fn)
{
FILE *f;
FILE *f;
f = nvr_fopen(L"europc_jim.nvr", L"rb");
if (f)
{
fread(europcdat, 1, 16, f);
fread(europc_rtc.dat, 1, 16, f);
fclose(f);
f = NULL;
return 1;
if (vm == NULL) return(0);
f = nvr_fopen(fn, L"rb");
if (f != NULL) {
(void)fread(vm->rtc.dat, 1, 16, f);
(void)fclose(f);
pclog("EuroPC: CMOS data loaded from file %ls !\n", fn);
return(1);
}
pclog("EuroPC: unable to load NVR !\n");
return(0);
}
/* Save the relevant portion of the NVR to disk. */
static int8_t
save_nvr(wchar_t *fn)
{
FILE *f;
if (vm == NULL) return(0);
f = nvr_fopen(fn, L"wb");
if (f != NULL) {
(void)fwrite(vm->rtc.dat, 1, 16, f);
(void)fclose(f);
return(1);
}
pclog("EuroPC: unable to save NVR !\n");
return(0);
}
/* This is called every second through the NVR/RTC hook. */
static void
rtc_hook(nvr_t *nvr)
{
#if 0
int month, year;
sys->rtc.dat[0] = bcd_adjust(sys->rtc.dat[0]+1);
if (sys->rtc.dat[0] >= 0x60) {
sys->rtc.dat[0] = 0;
sys->rtc.dat[1] = bcd_adjust(sys->rtc.dat[1]+1);
if (sys->rtc.dat[1] >= 0x60) {
sys->rtc.dat[1] = 0;
sys->rtc.dat[2] = bcd_adjust(sys->rtc.dat[2]+1);
if (sys->rtc.dat[2] >= 0x24) {
sys->rtc.dat[2] = 0;
sys->uropc_rtc.data[3]=bcd_adjust(sys->uropc_rtc.data[3]+1);
month = bcd_2_dec(sys->rtc.dat[4]);
/* Save for julian_days_in_month_calculation. */
year = bcd_2_dec(sys->rtc.dat[5])+2000;
if (sys->rtc.dat[3] > gregorian_days_in_month(month, year)) {
sys->rtc.dat[3] = 1;
sys->rtc.dat[4] = bcd_adjust(sys->rtc.dat[4]+1);
if (sys->rtc.dat[4]>0x12) {
sys->rtc.dat[4] = 1;
sys->rtc.dat[5] = bcd_adjust(sys->rtc.dat[5]+1)&0xff;
}
}
}
}
}
return 0;
}
#endif
}
void europc_save_nvr(void)
#if 0
void europc_rtc_init(void)
{
FILE *f;
europc_rtc.data[0xf]=1;
f = nvr_fopen(L"europc_jim.nvr", L"wb");
if (f)
europc_rtc.timer = timer_alloc(europc_rtc_timer);
timer_adjust(europc_rtc.timer, 0, 0, 1.0);
}
#endif
/* Execute a JIM control command. */
static void
jim_action(vm_t *sys, uint8_t reg, uint8_t val)
{
switch(reg) {
case 0: /* MISC control (WO) */
//pclog("EuroPC: write MISC = %02x\n", val);
// bit0: enable MOUSE
// bit1: enable joystick
break;
case 2: /* AGA control */
//pclog("EuroPC: write AGA = %02x\n", val);
if (! (val & 0x80)) {
/* Reset AGA. */
break;
}
switch (val) {
case 0x1f: /* 0001 1111 */
case 0x0b: /* 0000 1011 */
//europc_jim.mode=AGA_MONO;
pclog("EuroPC: AGA Monochrome mode!\n");
break;
case 0x18: /* 0001 1000 */
case 0x1a: /* 0001 1010 */
//europc_jim.mode=AGA_COLOR;
break;
case 0x0e: /* 0000 1100 */
/*80 columns? */
pclog("EuroPC: AGA 80-column mode!\n");
break;
case 0x0d: /* 0000 1011 */
/*40 columns? */
pclog("EuroPC: AGA 40-column mode!\n");
break;
default:
//europc_jim.mode=AGA_OFF;
break;
}
break;
case 4: /* CPU Speed control */
//pclog("EuroPC: write CPUCLK = %02x\n", val);
switch(val & 0xc0) {
case 0x00: /* 4.77 MHz */
// cpu_set_clockscale(0, 1.0/2);
break;
case 0x40: /* 7.16 MHz */
// cpu_set_clockscale(0, 3.0/4);
break;
default: /* 9.54 MHz */
// cpu_set_clockscale(0, 1);break;
break;
}
break;
default:
break;
}
sys->regs[reg] = val;
}
/* Write to one of the JIM registers. */
static void
jim_write(uint16_t addr, uint8_t val, void *priv)
{
vm_t *sys = (vm_t *)priv;
uint8_t b;
#if EUROPC_DEBUG > 1
pclog("EuroPC: jim_wr(%04x, %02x)\n", addr, val);
#endif
switch (addr & 0x000f) {
case 0x00: /* JIM internal registers (WRONLY) */
case 0x01:
case 0x02:
case 0x03:
case 0x04: /* JIM internal registers (R/W) */
case 0x05:
case 0x06:
case 0x07:
jim_action(sys, (addr & 0x07), val);
break;
case 0x0A: /* M3002 RTC INDEX/DATA register */
switch(sys->rtc.stat) {
case 0: /* save index */
sys->rtc.addr = val & 0x0f;
sys->rtc.stat++;
break;
case 1: /* save data HI nibble */
b = sys->rtc.dat[sys->rtc.addr] & 0x0f;
b |= (val << 4);
sys->rtc.dat[sys->rtc.addr] = b;
sys->rtc.stat++;
break;
case 2: /* save data LO nibble */
b = sys->rtc.dat[sys->rtc.addr] & 0xf0;
b |= (val & 0x0f);
sys->rtc.dat[sys->rtc.addr] = b;
sys->rtc.stat = 0;
break;
}
break;
default:
pclog("EuroPC: invalid JIM write %02x, val %02x\n", addr, val);
break;
}
}
/* Read from one of the JIM registers. */
static uint8_t
jim_read(uint16_t addr, void *priv)
{
vm_t *sys = (vm_t *)priv;
uint8_t r = 0xff;
switch (addr & 0x000f) {
case 0x00: /* JIM internal registers (WRONLY) */
case 0x01:
case 0x02:
case 0x03:
r = 0x00;
break;
case 0x04: /* JIM internal registers (R/W) */
case 0x05:
case 0x06:
case 0x07:
r = sys->regs[addr & 0x07];
break;
case 0x0A: /* M3002 RTC INDEX/DATA register */
switch(sys->rtc.stat) {
case 0:
r = 0x00;
break;
case 1: /* read data HI nibble */
r = (sys->rtc.dat[sys->rtc.addr] >> 4);
sys->rtc.stat++;
break;
case 2: /* read data LO nibble */
r = (sys->rtc.dat[sys->rtc.addr] & 0x0f);
sys->rtc.stat = 0;
break;
}
break;
default:
pclog("EuroPC: invalid JIM read %02x\n", addr);
break;
}
#if EUROPC_DEBUG > 1
pclog("EuroPC: jim_rd(%04x): %02x\n", addr, r);
#endif
return(r);
}
static uint8_t
rtc_checksum(uint8_t *ptr)
{
uint8_t sum;
int i;
/* Calculate all bytes with XOR. */
sum = 0x00;
for (i=MRTC_CONF_A; i<=MRTC_CONF_E; i++)
sum += ptr[i];
return(sum);
}
/*
* Initialize the mainboard 'device' of the machine.
*/
static void *
europc_boot(device_t *info)
{
vm_t *sys = vm;
uint8_t b;
#if EUROPC_DEBUG
pclog("EuroPC: booting mainboard..\n");
#endif
/* Try to load the NVR from file. */
if (! nvr_load()) {
/* Load failed, reset to defaults. */
sys->rtc.dat[0x00] = 0x00; /* RTC seconds */
sys->rtc.dat[0x01] = 0x00; /* RTC minutes */
sys->rtc.dat[0x02] = 0x00; /* RTC hours */
sys->rtc.dat[0x03] = 0x01; /* RTC days */
sys->rtc.dat[0x04] = 0x01; /* RTC months */
sys->rtc.dat[0x05] = 0x17; /* RTC years */
sys->rtc.dat[0x06] = 0x01; /* RTC weekday */
sys->rtc.dat[0x07] = 0x01; /* RTC weekno */
/*
* EuroPC System Configuration:
*
* [A] unknown
*
* [B] 7 1 bootdrive extern
* 0 bootdribe intern
* 6:5 11 invalid hard disk type
* 10 hard disk installed, type 2
* 01 hard disk installed, type 1
* 00 hard disk not installed
* 4:3 11 invalid external drive type
* 10 external drive 720K
* 01 external drive 360K
* 00 external drive disabled
* 2 unknown
* 1:0 11 invalid internal drive type
* 10 internal drive 360K
* 01 internal drive 720K
* 00 internal drive disabled
*
* [C] 7:6 unknown
* 5 monitor detection OFF
* 4 unknown
* 3:2 11 illegal memory size
* 10 512K
* 01 256K
* 00 640K
* 1:0 11 illegal game port
* 10 gameport as mouse port
* 01 gameport as joysticks
* 00 gameport disabled
*
* [D] 7:6 10 9MHz CPU speed
* 01 7MHz CPU speed
* 00 4.77 MHz CPU
* 5 unknown
* 4 external: color, internal: mono
* 3 unknown
* 2 internal video ON
* 1:0 11 mono
* 10 color80
* 01 color40
* 00 special (EGA,VGA etc)
*
* [E] 7:4 unknown
* 3:0 country (00=Deutschland, 0A=ASCII)
*/
sys->rtc.dat[MRTC_CONF_A] = 0x00; /* CONFIG A */
sys->rtc.dat[MRTC_CONF_B] = 0x0A; /* CONFIG B */
sys->rtc.dat[MRTC_CONF_C] = 0x28; /* CONFIG C */
sys->rtc.dat[MRTC_CONF_D] = 0x12; /* CONFIG D */
sys->rtc.dat[MRTC_CONF_E] = 0x0A; /* CONFIG E */
sys->rtc.dat[MRTC_CHECK_LO] = 0x44; /* checksum (LO) */
sys->rtc.dat[MRTC_CHECK_HI] = 0x00; /* checksum (HI) */
sys->rtc.dat[MRTC_CTRLSTAT] = 0x01; /* status/control */
/* Provide correct checksum. */
sys->rtc.dat[MRTC_CHECK_LO] = rtc_checksum(sys->rtc.dat);
}
pclog("EuroPC: NVR=[ %02x %02x %02x %02x %02x ] %sVALID\n",
sys->rtc.dat[MRTC_CONF_A], sys->rtc.dat[MRTC_CONF_B],
sys->rtc.dat[MRTC_CONF_C], sys->rtc.dat[MRTC_CONF_D],
sys->rtc.dat[MRTC_CONF_E],
(sys->rtc.dat[MRTC_CHECK_LO]!=rtc_checksum(sys->rtc.dat))?"IN":"");
/*
* Now that we have initialized the NVR (either from file,
* or by setting it to defaults) we can start overriding it
* with values set by the 86Box user.
*/
b = (sys->rtc.dat[MRTC_CONF_D] & ~0x17);
switch(gfxcard) {
case GFX_CGA: /* Color, CGA */
case GFX_COLORPLUS: /* Color, Hercules ColorPlus */
b |= 0x12; /* external video, CGA80 */
break;
case GFX_MDA: /* Monochrome, MDA */
case GFX_HERCULES: /* Monochrome, Hercules */
case GFX_INCOLOR: /* Color, ? */
b |= 0x03; /* external video, mono */
break;
default: /* EGA, VGA etc */
b |= 0x10; /* external video, special */
}
sys->rtc.dat[MRTC_CONF_D] = b;
/* Update the memory size. */
b = (sys->rtc.dat[MRTC_CONF_C] & 0xf3);
switch(mem_size) {
case 256:
b |= 0x04;
break;
case 512:
b |= 0x08;
break;
case 640:
b |= 0x00;
break;
}
sys->rtc.dat[MRTC_CONF_C] = b;
/* Update CPU speed. */
b = (sys->rtc.dat[MRTC_CONF_D] & 0x3f);
switch(cpu) {
case 0: /* 8088, 4.77 MHz */
b |= 0x00;
break;
case 1: /* 8088, 7.15 MHz */
b |= 0x40;
break;
case 2: /* 8088, 9.56 MHz */
b |= 0x80;
break;
}
sys->rtc.dat[MRTC_CONF_D] = b;
/* Set up game port. */
b = (sys->rtc.dat[MRTC_CONF_C] & 0xfc);
if (mouse_type == MOUSE_TYPE_LOGIBUS) {
b |= 0x01; /* enable port as MOUSE */
} else if (joystick_type != 7) {
b |= 0x02; /* enable port as joysticks */
device_add(&gameport_device);
}
sys->rtc.dat[MRTC_CONF_C] = b;
#if 0
/* Set up floppy types. */
sys->rtc.dat[MRTC_CONF_B] = 0x2A;
#endif
/* Validate the NVR checksum. */
sys->rtc.dat[MRTC_CHECK_LO] = rtc_checksum(sys->rtc.dat);
nvr_save();
/*
* Allocate the system's I/O handlers.
*
* The first one is for the JIM. Note that although JIM usually
* resides at 0x0250, a special solder jumper on the mainboard
* (JS9) can be used to "move" it to 0x0350, to get it out of
* the way of other cards that need this range.
*/
io_sethandler(sys->jim, 16,
jim_read, NULL, NULL,
jim_write, NULL, NULL, sys);
/* Only after JIM has been initialized. */
device_add(&keyboard_xt_device);
/*
* Set up and enable the HD20 disk controller.
*
* We only do this if we have not configured another one.
*/
if (hdc_current == 1)
device_add(&europc_hdc_device);
return(sys);
}
static void
europc_close(void *priv)
{
nvr_t *nvr = &vm->nvr;
if (nvr->fname != NULL)
free(nvr->fname);
free(vm);
vm = NULL;
}
static device_config_t europc_config[] = {
{
"js9", "JS9 Jumper (JIM)", CONFIG_INT, "", 0,
{
fwrite(europcdat, 1, 16, f);
fwrite(europc_rtc.dat, 1, 16, f);
fclose(f);
f = NULL;
}
}
{
"Disabled (250h)", 0
},
{
"Enabled (350h)", 1
},
{
""
}
},
},
{
"", "", -1
}
};
static void writejim(uint16_t addr, uint8_t val, void *p)
{
if ((addr&0xFF0)==0x250) europcdat[addr&0xF]=val;
switch (addr)
{
case 0x25A:
switch (europc_rtc.stat)
{
case 0:
europc_rtc.addr=val&0xF;
europc_rtc.stat++;
break;
case 1:
europc_rtc.dat[europc_rtc.addr]=(europc_rtc.dat[europc_rtc.addr]&0xF)|(val<<4);
europc_rtc.stat++;
break;
case 2:
europc_rtc.dat[europc_rtc.addr]=(europc_rtc.dat[europc_rtc.addr]&0xF0)|(val&0xF);
europc_rtc.stat=0;
break;
}
break;
}
}
static uint8_t readjim(uint16_t addr, void *p)
{
switch (addr)
{
case 0x250: case 0x251: case 0x252: case 0x253: return 0;
case 0x254: case 0x255: case 0x256: case 0x257: return europcdat[addr&0xF];
case 0x25A:
if (europc_rtc.stat==1)
{
europc_rtc.stat=2;
return europc_rtc.dat[europc_rtc.addr]>>4;
}
if (europc_rtc.stat==2)
{
europc_rtc.stat=0;
return europc_rtc.dat[europc_rtc.addr]&0xF;
}
return 0;
}
return 0;
}
static void jim_init(void)
{
uint8_t viddat;
memset(europc_rtc.dat,0,16);
if (!jim_load_nvr())
{
europc_rtc.dat[0xF]=1;
europc_rtc.dat[3]=1;
europc_rtc.dat[4]=1;
europc_rtc.dat[5]=0x88;
}
if (gfxcard==GFX_CGA || gfxcard == GFX_COLORPLUS) viddat=0x12;
else if (gfxcard==GFX_MDA || gfxcard==GFX_HERCULES || gfxcard==GFX_INCOLOR) viddat=3;
else viddat=0x10;
europc_rtc.dat[0xB]=viddat;
europc_rtc.dat[0xD]=viddat; /*Checksum*/
io_sethandler(0x250, 0x10, readjim, NULL, NULL, writejim, NULL, NULL, NULL);
}
device_t europc_device = {
"EuroPC System Board",
0, 0,
europc_boot, /* init */
europc_close, /* close */
NULL,
NULL, NULL, NULL, NULL,
europc_config
};
/*
* This function sets up the Scheider EuroPC machine.
*
* Its task is to allocate a clean machine data block,
* and then simply enable the mainboard "device" which
* allows it to reset (dev init) and configured by the
* user.
*/
void
machine_europc_init(machine_t *model)
{
machine_common_init(model);
vm_t *sys;
lpt3_init(0x3bc);
jim_init();
device_add(&keyboard_xt_device);
nmi_init();
if (joystick_type != 7)
device_add(&gameport_device);
/* Allocate machine data. */
sys = (vm_t *)malloc(sizeof(vm_t));
if (sys == NULL) {
pclog("EuroPC: unable to allocate machine data!\n");
return;
}
memset(sys, 0x00, sizeof(vm_t));
sys->jim = 0x0250;
vm = sys;
machine_common_init(model);
nmi_init();
mem_add_bios();
/* This is machine specific. */
vm->nvr.mask = model->nvrmask;
vm->nvr.irq = -1;
/* Set up any local handlers here. */
vm->nvr.load = load_nvr;
vm->nvr.save = save_nvr;
vm->nvr.hook = rtc_hook;
/* Initialize the actual NVR. */
nvr_init(&vm->nvr);
/* Enable and set up the mainboard device. */
device_add(&europc_device);
}

954
src/machine/m_europc_hdc.c Normal file
View File

@@ -0,0 +1,954 @@
/*
* 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 EuroPC HD20 internal controller.
*
* The HD20 was an externally-connected drive, very often a
* 8425XT (20MB, 615/4/17) from Miniscribe. These drives used
* an 8-bit version of IDE called X-IDE, also known as XTA.
* Some older units had a 8225XT drive (20MB, 771/2/17.)
*
* To access the HD disk formatter, enter the "debug" program
* in DOS, and type "g=f000:a000" to start that utility, which
* is hidden in the PC's ROM BIOS.
*
* This driver is based on the information found in the IBM-PC
* Technical Reference manual, pp 187 and on.
*
* Version: @(#)europc_hdc.c 1.0.2 2017/11/18
*
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
* Based on the original "xebec.c" from Sarah Walker.
*
* Copyright 2008-2017 Sarah Walker.
* Copyright 2017 Fred N. van Kempen.
*/
#define __USE_LARGEFILE64
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "../86box.h"
#include "../io.h"
#include "../dma.h"
#include "../pic.h"
#include "../device.h"
#include "../timer.h"
#include "../disk/hdc.h"
#include "../disk/hdd.h"
#include "../ui.h"
#include "machine.h"
#define HDC_DEBUG 1
#define HDC_NEWPARAMS 1 /* use NEW parameter block */
#define HDD_IOADDR 0x0320
#define HDD_IRQCHAN 5
#define HDD_DMACHAN 3
#define HDC_TIME (200*TIMER_USEC)
enum {
STATE_IDLE,
STATE_CMD,
STATE_RUN,
STATE_RXDTA,
STATE_RDATA,
STATE_TXDTA,
STATE_TDATA,
STATE_COMPL
};
/* Command values. */
#define CMD_TEST_DRV_RDY 0x00
#define CMD_RECALIBRATE 0x01
/* unused 0x02 */
#define CMD_READ_SENSE 0x03
#define CMD_FORMAT_DRIVE 0x04
#define CMD_READY_VERIFY 0x05
#define CMD_FORMAT_TRACK 0x06
#define CMD_FORMAT_BAD_TRACK 0x07
#define CMD_READ_SECTORS 0x08
/* unused 0x09 */
#define CMD_WRITE_SECTORS 0x0a
#define CMD_SEEK 0x0b
#define CMD_SET_DRIVE_PARAMS 0x0c
#define CMD_READ_ECC_BURST 0x0d
#define CMD_READ_SECTOR_BUFFER 0x0e
#define CMD_WRITE_SECTOR_BUFFER 0x0f
#define CMD_RAM_DIAGS 0xe0
/* unused 0xe1 */
/* unused 0xe2 */
#define CMD_DRIVE_DIAGS 0xe3
#define CMD_CTRL_DIAGS 0xe4
#define CMD_READ_LONG 0xe5
#define CMD_WRITE_LONG 0xe6
/* STATUS register values. */
#define STAT_REQ 0x01
#define STAT_IO 0x02
#define STAT_CD 0x04
#define STAT_BSY 0x08
#define STAT_DRQ 0x10
#define STAT_IRQ 0x20
/* Sense Error codes. */
#define ERR_NOERROR 0x00 /* no error detected */
#define ERR_NOINDEX 0x01 /* drive did not detect IDX pulse */
#define ERR_NOSEEK 0x02 /* drive did not complete SEEK */
#define ERR_WRFAULT 0x03 /* write fault during last cmd */
#define ERR_NOTRDY 0x04 /* drive did not go READY after cmd */
#define ERR_NOTRK000 0x06 /* drive did not see TRK0 signal */
#define ERR_LONGSEEK 0x08 /* long seek in progress */
#define ERR_IDREAD 0x10 /* ECC error during ID field */
#define ERR_DATA 0x11 /* uncorrectable ECC err in data */
#define ERR_NOMARK 0x12 /* no address mark detected */
#define ERR_NOSECT 0x14 /* sector not found */
#define ERR_SEEK 0x15 /* seek error */
#define ERR_ECCDATA 0x18 /* ECC corrected data */
#define ERR_BADTRK 0x19 /* bad track detected */
#define ERR_ILLCMD 0x20 /* invalid command received */
#define ERR_ILLADDR 0x21 /* invalid disk address received */
#define ERR_BADRAM 0x30 /* bad RAM in sector data buffer */
#define ERR_BADROM 0x31 /* bad checksum in ROM test */
#define ERR_BADECC 0x32 /* ECC polynomial generator bad */
/* Completion Byte fields. */
#define COMP_DRIVE 0x20
#define COMP_ERR 0x02
#define IRQ_ENA 0x02
#define DMA_ENA 0x01
/* The device control block (6 bytes) */
#pragma pack(push,1)
struct dcb {
uint8_t cmd; /* [7:5] class, [4:0] opcode */
uint8_t head:5, /* [4:0] head number */
drvsel:1, /* [5] drive select */
unused:2; /* [7:6] unused MBZ */
uint8_t sector:6, /* [5:0] sector number 0-63 */
cylh:2; /* [7:6] cylinder [9:8] bits */
uint8_t cyl; /* [7:0] cylinder [7:0] bits */
uint8_t count; /* [7:0] blk count / interleave */
uint8_t ctrl; /* [7:0] control field */
};
#pragma pack(pop)
/*
* The (configured) Drive Parameters.
*
* Although the IBM specification calls for a total of 8 bytes
* in the Paramater Block, the EuroPC uses a 16-byte block. It
* looks like it has extended (translated?) information there,
* as well as the actual data we need.
*
* [ 03 ac 04 01 f4 02 67 0b 11 04 67 02 00 00 01 00]
*
* is what was sent for a standard 615/4/17 disk with rdwrcyl
* set to 500, and precomp to 615.
*
* For now, we will just look at the rest of the data.
*/
#pragma pack(push,1)
struct dprm {
#if HDC_NEWPARAMS
uint16_t tracks; /* total number of sectors on drive */
uint8_t heads; /* number of heads per cylinder */
uint16_t rwcurrent; /* (MSB) reduced write current cylinder */
uint16_t wprecomp; /* (MSB) write precompensation cylinder */
uint8_t maxecc; /* max ECC data burst length */
#else
uint16_t tracks; /* (MSB) max number of cylinders */
uint8_t heads; /* number of heads per cylinder */
uint16_t rwcurrent; /* (MSB) reduced write current cylinder */
uint16_t wprecomp; /* (MSB) write precompensation cylinder */
uint8_t maxecc; /* max ECC data burst length */
#endif
};
typedef struct {
uint8_t spt,
hpc;
uint16_t tracks;
struct dprm params;
uint8_t cfg_spt,
cfg_hpc;
uint16_t cfg_tracks;
uint16_t cur_cyl;
int8_t present,
hdd_num;
} drive_t;
#pragma pack(pop)
typedef struct {
uint16_t base;
int8_t irq;
int8_t dma;
uint8_t mask;
int8_t state;
int64_t callback;
uint8_t sense; /* current SENSE ERROR value */
uint8_t status; /* current operational status */
/* Current operation parameters. */
int16_t buf_idx, /* command buffer index and pointer */
buf_len;
uint8_t *buf_ptr;
uint16_t track; /* requested track# */
uint8_t head, /* requested head# */
sector, /* requested sector# */
comp; /* operation completion byte */
int count; /* requested sector count */
struct dcb dcb; /* device control block */
drive_t drives[MFM_NUM];
uint8_t data[512]; /* data buffer */
uint8_t sector_buf[512];
} hd20_t;
static void
hd20_intr(hd20_t *dev)
{
dev->status = STAT_REQ|STAT_CD|STAT_IO|STAT_BSY;
dev->state = STATE_COMPL;
if (dev->mask & IRQ_ENA) {
dev->status |= STAT_IRQ;
picint(1<<dev->irq);
}
}
static int
get_sector(hd20_t *dev, drive_t *drive, off64_t *addr)
{
int heads = drive->cfg_hpc;
if (drive->cur_cyl != dev->track) {
pclog("HD20: get_sector: wrong cylinder %d/%d\n",
drive->cur_cyl, dev->track);
dev->sense = ERR_ILLADDR;
return(1);
}
if (dev->head > heads) {
pclog("HD20: get_sector: past end of configured heads\n");
dev->sense = ERR_ILLADDR;
return(1);
}
if (dev->head > drive->hpc) {
pclog("HD20: get_sector: past end of heads\n");
dev->sense = ERR_ILLADDR;
return(1);
}
if (dev->sector >= 17) {
pclog("HD20: get_sector: past end of sectors\n");
dev->sense = ERR_ILLADDR;
return(1);
}
*addr = ((((off64_t) dev->track*heads) + dev->head)*17) + dev->sector;
return(0);
}
static void
next_sector(hd20_t *dev, drive_t *drive)
{
if (++dev->sector >= 17) {
dev->sector = 0;
if (++dev->head >= drive->cfg_hpc) {
dev->head = 0;
dev->track++;
drive->cur_cyl++;
if (drive->cur_cyl >= drive->cfg_tracks)
drive->cur_cyl = drive->cfg_tracks-1;
}
}
}
/* Execute the DCB we just received. */
static void
hd20_callback(void *priv)
{
hd20_t *dev = (hd20_t *)priv;
struct dcb *dcb = &dev->dcb;
drive_t *drive;
off64_t addr;
int val;
dev->callback = 0;
drive = &dev->drives[dcb->drvsel];
dev->comp = (dcb->drvsel) ? COMP_DRIVE : 0x00;
switch (dcb->cmd) {
case CMD_TEST_DRV_RDY:
#if HDC_DEBUG
if (dcb->drvsel == 0)
pclog("HD20: test_rdy(%d) ready=%d\n",
dcb->drvsel, drive->present);
#endif
if (! drive->present) {
dev->comp |= COMP_ERR;
dev->sense = ERR_NOTRDY;
}
hd20_intr(dev);
break;
case CMD_RECALIBRATE:
#if HDC_DEBUG
if (dcb->drvsel == 0)
pclog("HD20: recalibrate(%d) ready=%d\n",
dcb->drvsel, drive->present);
#endif
if (! drive->present) {
dev->comp |= COMP_ERR;
dev->sense = ERR_NOTRDY;
} else {
dev->track = drive->cur_cyl = 0;
}
hd20_intr(dev);
break;
case CMD_READ_SENSE:
if (dev->state == STATE_RUN) {
#if HDC_DEBUG
if (dcb->drvsel == 0)
pclog("HD20: sense(%d)\n", dcb->drvsel);
#endif
dev->buf_idx = 0;
dev->buf_len = 4;
dev->buf_ptr = dev->data;
dev->data[0] = dev->sense;
dev->data[1] = dcb->drvsel ? 0x20 : 0x00;
dev->data[2] = dev->data[3] = 0x00;
dev->sense = ERR_NOERROR;
dev->status = STAT_BSY|STAT_IO|STAT_REQ;
dev->state = STATE_TXDTA;
} else if (dev->state == STATE_TDATA) {
hd20_intr(dev);
}
break;
case CMD_READY_VERIFY:
if (dev->state == STATE_RUN) {
/* Seek to cylinder. */
dev->track = dcb->cyl | (dcb->cylh<<2);
if (dev->track >= drive->cfg_tracks)
drive->cur_cyl = drive->cfg_tracks-1;
else
drive->cur_cyl = dev->track;
dev->head = dcb->head;
dev->sector = dcb->sector;
#if HDC_DEBUG
pclog("HD20: verify_sector(%d) %d,%d,%d\n",
dcb->drvsel, dev->track, dev->head,dev->sector);
#endif
/* Get sector count; count=0 means 256. */
dev->count = (int)dcb->count;
if (dev->count == 0) dev->count = 256;
while (dev->count-- > 0) {
if (get_sector(dev, drive, &addr)) {
pclog("HD20: get_sector failed\n");
dev->comp |= COMP_ERR;
hd20_intr(dev);
return;
}
next_sector(dev, drive);
}
hd20_intr(dev);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 1);
}
break;
case CMD_FORMAT_DRIVE:
#if HDC_DEBUG
pclog("HD20: format_drive(%d)\n", dcb->drvsel);
#endif
for (dev->track=0; dev->track<drive->tracks; dev->track++) {
drive->cur_cyl = dev->track;
for (dev->head=0; dev->head<drive->hpc; dev->head++) {
dev->sector = 0;
if (get_sector(dev, drive, &addr)) {
pclog("HD20: get_sector failed\n");
dev->comp |= COMP_ERR;
hd20_intr(dev);
return;
}
hdd_image_zero(drive->hdd_num,addr,drive->spt);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 1);
}
}
hd20_intr(dev);
break;
case CMD_FORMAT_TRACK:
/* Seek to cylinder. */
dev->track = dcb->cyl | (dcb->cylh<<2);
if (dev->track >= drive->cfg_tracks)
drive->cur_cyl = drive->cfg_tracks-1;
else
drive->cur_cyl = dev->track;
dev->head = dcb->head;
dev->sector = 0;
#if HDC_DEBUG
pclog("HD20: format_track(%d) %d,%d\n",
dcb->drvsel, dev->track, dev->head);
#endif
if (get_sector(dev, drive, &addr)) {
pclog("HD20: get_sector failed\n");
dev->comp |= COMP_ERR;
hd20_intr(dev);
return;
}
hdd_image_zero(drive->hdd_num, addr, drive->spt);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 1);
hd20_intr(dev);
break;
case CMD_READ_SECTORS:
switch (dev->state) {
case STATE_RUN:
/* Seek to cylinder. */
dev->track = dcb->cyl | (dcb->cylh<<2);
if (dev->track >= drive->cfg_tracks)
drive->cur_cyl = drive->cfg_tracks-1;
else
drive->cur_cyl = dev->track;
dev->head = dcb->head;
dev->sector = dcb->sector;
/* Get sector count; count=0 means 256. */
dev->count = (int)dcb->count;
if (dev->count == 0) dev->count = 256;
#if HDC_DEBUG
pclog("HD20: read_sector(%d) %d,%d,%d cnt=%d\n",
dcb->drvsel, dev->track, dev->head,
dev->sector, dev->count);
#endif
if (get_sector(dev, drive, &addr)) {
dev->comp |= COMP_ERR;
hd20_intr(dev);
return;
}
hdd_image_read(drive->hdd_num, addr, 1,
(uint8_t *)dev->sector_buf);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 1);
/* Ready to transfer the data out. */
dev->buf_idx = 0;
dev->buf_len = 512;
dev->state = STATE_TXDTA;
if (! (dev->mask & DMA_ENA)) {
memcpy(dev->data, dev->sector_buf, 512);
dev->buf_ptr = dev->data;
dev->status = STAT_BSY|STAT_IO|STAT_REQ;
} else {
dev->callback = HDC_TIME;
dev->buf_ptr = dev->sector_buf;
}
break;
case STATE_TXDTA:
dev->status = STAT_BSY;
while (dev->buf_idx < dev->buf_len) {
val = dma_channel_write(dev->dma,
*dev->buf_ptr++);
if (val == DMA_NODATA) {
pclog("CMD_READ_SECTORS out of data!\n");
dev->status = STAT_BSY|STAT_CD|STAT_IO|STAT_REQ;
dev->callback = HDC_TIME;
return;
}
dev->buf_idx++;
}
dev->state = STATE_TDATA;
dev->callback = HDC_TIME;
break;
case STATE_TDATA:
next_sector(dev, drive);
dev->buf_idx = 0;
if (--dev->count == 0) {
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 0);
hd20_intr(dev);
return;
}
if (get_sector(dev, drive, &addr)) {
dev->comp |= COMP_ERR;
hd20_intr(dev);
return;
}
hdd_image_read(drive->hdd_num, addr, 1,
(uint8_t *)dev->sector_buf);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 1);
dev->state = STATE_TXDTA;
if (! (dev->mask & DMA_ENA)) {
memcpy(dev->data, dev->sector_buf, 512);
dev->buf_ptr = dev->data;
dev->status = STAT_BSY|STAT_IO|STAT_REQ;
} else {
dev->buf_ptr = dev->sector_buf;
dev->callback = HDC_TIME;
}
break;
}
break;
case CMD_WRITE_SECTORS:
switch (dev->state) {
case STATE_RUN:
/* Seek to cylinder. */
dev->track = dcb->cyl | (dcb->cylh<<2);
if (dev->track >= drive->cfg_tracks)
drive->cur_cyl = drive->cfg_tracks-1;
else
drive->cur_cyl = dev->track;
dev->head = dcb->head;
dev->sector = dcb->sector;
/* Get sector count; count=0 means 256. */
dev->count = (int)dev->dcb.count;
if (dev->count == 0) dev->count = 256;
#if HDC_DEBUG
pclog("HD20: write_sector(%d) %d,%d,%d cnt=%d\n",
dcb->drvsel, dev->track, dev->head,
dev->sector, dev->count);
#endif
dev->buf_idx = 0;
dev->buf_len = 512;
dev->state = STATE_RXDTA;
if (! (dev->mask & DMA_ENA)) {
dev->buf_ptr = dev->data;
dev->status = STAT_BSY|STAT_REQ;
} else {
dev->buf_ptr = dev->sector_buf;
dev->callback = HDC_TIME;
}
break;
case STATE_RXDTA:
dev->status = STAT_BSY;
while (dev->buf_idx < dev->buf_len) {
val = dma_channel_read(dev->dma);
if (val == DMA_NODATA) {
pclog("CMD_WRITE_SECTORS out of data!\n");
dev->status = STAT_BSY|STAT_CD|STAT_IO|STAT_REQ;
dev->callback = HDC_TIME;
return;
}
*dev->buf_ptr++ = (val & 0xff);
dev->buf_idx++;
}
dev->state = STATE_RDATA;
dev->callback = HDC_TIME;
break;
case STATE_RDATA:
#if 0
/* If I enable this, we get data corruption.. ??? -FvK */
if (! (dev->mask & DMA_ENA))
memcpy(dev->sector_buf, dev->data, 512);
#endif
if (get_sector(dev, drive, &addr)) {
dev->comp |= COMP_ERR;
hd20_intr(dev);
return;
}
hdd_image_write(drive->hdd_num, addr, 1,
(uint8_t *)dev->sector_buf);
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 1);
next_sector(dev, drive);
dev->buf_idx = 0;
if (--dev->count == 0) {
ui_sb_update_icon(SB_HDD|HDD_BUS_MFM, 0);
hd20_intr(dev);
break;
}
dev->state = STATE_RXDTA;
if (! (dev->mask & DMA_ENA)) {
dev->buf_ptr = dev->data;
dev->status = STAT_BSY|STAT_REQ;
} else {
dev->buf_ptr = dev->sector_buf;
dev->callback = HDC_TIME;
}
}
break;
case CMD_SEEK:
if (! drive->present) {
dev->comp |= COMP_ERR;
dev->sense = ERR_NOTRDY;
hd20_intr(dev);
break;
}
/* Seek to cylinder. */
val = dcb->cyl | (dcb->cylh<<2);
if (val >= drive->cfg_tracks)
drive->cur_cyl = drive->cfg_tracks-1;
else
drive->cur_cyl = val;
#if HDC_DEBUG
pclog("HD20: seek(%d) %d/%d\n",
dcb->drvsel, val, drive->cur_cyl);
#endif
if (val != drive->cur_cyl) {
dev->comp |= COMP_ERR;
dev->sense = ERR_SEEK;
}
hd20_intr(dev);
break;
case CMD_SET_DRIVE_PARAMS:
if (dev->state == STATE_RUN) {
dev->state = STATE_RXDTA;
dev->buf_idx = 0;
dev->buf_len = sizeof(struct dprm);
dev->buf_ptr = (uint8_t *)&drive->params;
dev->status = STAT_BSY|STAT_REQ;
} else {
dev->buf_ptr=(uint8_t *)&drive->params;
pclog("HD20: PARAMS=[");
for(val=0;val<8;val++)pclog(" %02x",*dev->buf_ptr++);
pclog(" ]\n");
#if 0
drive->cfg_tracks = drive->params.tracks;
drive->cfg_hpc = drive->params.heads;
drive->cfg_spt = drive->spt;
#endif
#if HDC_DEBUG
pclog("HD20: set_params(%d) cyl=%d,hd=%d,spt=%d\n",
dcb->drvsel, drive->cfg_tracks,
drive->cfg_hpc, drive->cfg_spt);
#endif
hd20_intr(dev);
}
break;
case CMD_WRITE_SECTOR_BUFFER:
switch (dev->state) {
case STATE_RUN:
#if HDC_DEBUG
pclog("HD20: write_sector_buffer(%d)\n",
dcb->drvsel);
#endif
dev->buf_idx = 0;
dev->buf_len = 512;
dev->state = STATE_RXDTA;
if (! (dev->mask & DMA_ENA)) {
dev->buf_ptr = dev->data;
dev->status = STAT_BSY|STAT_REQ;
} else {
dev->buf_ptr = dev->sector_buf;
dev->callback = HDC_TIME;
}
break;
case STATE_RXDTA:
dev->status = STAT_BSY;
if (! (dev->mask & DMA_ENA)) break;
while (dev->buf_idx++ < dev->buf_len) {
val = dma_channel_read(dev->dma);
if (val == DMA_NODATA) {
pclog("CMD_WRITE_SECTORS out of data!\n");
dev->status = STAT_BSY|STAT_CD|STAT_IO|STAT_REQ;
dev->callback = HDC_TIME;
return;
}
*dev->buf_ptr++ = (val & 0xff);
}
dev->state = STATE_RDATA;
dev->callback = HDC_TIME;
break;
case STATE_RDATA:
if (! (dev->mask & DMA_ENA))
memcpy(dev->sector_buf, dev->data, 512);
hd20_intr(dev);
break;
}
break;
case CMD_RAM_DIAGS:
#if HDC_DEBUG
pclog("HD20: ram_diags\n");
#endif
dev->callback = 5*HDC_TIME;
hd20_intr(dev);
break;
case CMD_DRIVE_DIAGS:
#if HDC_DEBUG
pclog("HD20: drive_diags(%d)\n", dcb->drvsel);
#endif
dev->callback = 5*HDC_TIME;
hd20_intr(dev);
break;
case CMD_CTRL_DIAGS:
#if HDC_DEBUG
pclog("HD20: ctrl_diags\n");
#endif
dev->callback = 5*HDC_TIME;
hd20_intr(dev);
break;
default:
pclog("HD20: unknown command - %02x\n", dcb->cmd);
dev->comp |= COMP_ERR;
dev->sense = ERR_ILLCMD;
hd20_intr(dev);
}
}
/* Read one of the HD controller registers. */
static uint8_t
hd20_read(uint16_t port, void *priv)
{
hd20_t *dev = (hd20_t *)priv;
uint8_t ret = 0xff;
switch (port-dev->base) {
case 0: /* read data */
dev->status &= ~STAT_IRQ;
if (dev->state == STATE_TXDTA) {
if ((dev->status & 0x0f) !=
(STAT_IO|STAT_REQ|STAT_BSY))
fatal("Read data STATE_COMPLETION_BYTE, status=%02x\n", dev->status);
if (dev->buf_idx > dev->buf_len) {
pclog("HD20: read with empty buffer!\n");
dev->comp |= COMP_ERR;
dev->sense = ERR_ILLCMD;
break;
}
ret = dev->data[dev->buf_idx++];
if (dev->buf_idx == dev->buf_len) {
dev->status = STAT_BSY;
dev->state = STATE_TDATA;
dev->callback = HDC_TIME;
}
} else if (dev->state == STATE_COMPL) {
if ((dev->status & 0x0f) !=
(STAT_CD|STAT_IO|STAT_REQ|STAT_BSY))
fatal("Read data STATE_COMPL, status=%02x\n", dev->status);
ret = dev->comp;
dev->status = 0x00;
dev->state = STATE_IDLE;
}
break;
case 1: /* read status */
ret = dev->status;
break;
case 2: /* read option jumpers */
ret = 0x00;
break;
}
#if HDC_DEBUG > 1
pclog("HD20: read(%04x) = %02x\n", port, ret);
#endif
return(ret);
}
static void
hd20_write(uint16_t port, uint8_t val, void *priv)
{
hd20_t *dev = (hd20_t *)priv;
#if HDC_DEBUG > 1
pclog("HD20: write(%04x,%02x)\n", port, val);
#endif
switch (port-dev->base) {
case 0: /* write command/data */
if (! (dev->status & STAT_REQ)) {
pclog("HD20: not ready for command/data!\n");
dev->comp |= COMP_ERR;
dev->sense = ERR_ILLCMD;
break;
}
if (dev->buf_idx >= dev->buf_len) {
pclog("HD20: write with full buffer!\n");
dev->comp |= COMP_ERR;
dev->sense = ERR_ILLCMD;
break;
}
/* Store the data into the buffer. */
*dev->buf_ptr++ = val;
if (++dev->buf_idx == dev->buf_len) {
/* We got all the data we need. */
dev->status &= ~STAT_REQ;
dev->state = (dev->state==STATE_CMD) ? STATE_RUN : STATE_RDATA;
dev->callback = HDC_TIME;
}
break;
case 1: /* controller reset */
dev->sense = 0x00;
/*FALLTHROUGH*/
case 2: /* generate controller-select-pulse */
dev->status = STAT_BSY|STAT_CD|STAT_REQ;
dev->buf_idx = 0;
dev->buf_len = sizeof(struct dcb);
dev->buf_ptr = (uint8_t *)&dev->dcb;
dev->state = STATE_CMD;
break;
case 3: /* DMA/IRQ mask register */
dev->mask = val;
break;
}
}
static void *
hd20_init(device_t *info)
{
drive_t *drive;
hd20_t *dev;
int c, i;
pclog("EuroPC: initializing HD20 controller.\n");
dev = malloc(sizeof(hd20_t));
memset(dev, 0x00, sizeof(hd20_t));
dev->base = HDD_IOADDR;
dev->irq = HDD_IRQCHAN;
dev->dma = HDD_DMACHAN;
for (c=0,i=0; i<HDD_NUM; i++) {
if ((hdd[i].bus == HDD_BUS_MFM) && (hdd[i].mfm_channel < MFM_NUM)) {
drive = &dev->drives[hdd[i].mfm_channel];
if (! hdd_image_load(i)) {
drive->present = 0;
continue;
}
/* These are the "hardware" parameters (from the image.) */
drive->spt = hdd[i].spt;
drive->hpc = hdd[i].hpc;
drive->tracks = hdd[i].tracks;
/* Use them as "configured" parameters until overwritten. */
drive->cfg_spt = drive->spt;
drive->cfg_hpc = drive->hpc;
drive->cfg_tracks = drive->tracks;
drive->hdd_num = i;
drive->present = 1;
pclog("HD20: drive%d (cyl=%d,hd=%d,spt=%d), disk %d\n",
hdd[i].mfm_channel,drive->tracks,drive->hpc,drive->spt,i);
if (++c > MFM_NUM) break;
}
}
io_sethandler(dev->base, 4,
hd20_read, NULL, NULL, hd20_write, NULL, NULL, dev);
timer_add(hd20_callback, &dev->callback, &dev->callback, dev);
return(dev);
}
static void
hd20_close(void *priv)
{
hd20_t *dev = (hd20_t *)priv;
drive_t *drive;
int d;
for (d=0; d<2; d++) {
drive = &dev->drives[d];
hdd_image_close(drive->hdd_num);
}
free(dev);
}
static int
hd20_available(void)
{
return(1);
}
device_t europc_hdc_device = {
"EuroPC HD20",
0, 0,
hd20_init, hd20_close, NULL,
hd20_available, NULL, NULL, NULL,
NULL
};

View File

@@ -149,6 +149,10 @@ extern void machine_ps2_model_80_486_init(machine_t *);
extern void machine_amstrad_init(machine_t *);
extern void machine_europc_init(machine_t *);
#ifdef EMU_DEVICE_H
extern device_t europc_device,
europc_hdc_device;
#endif
extern void machine_olim24_init(machine_t *);

View File

@@ -8,7 +8,7 @@
#
# Makefile for Win32 (MinGW32) environment.
#
# Version: @(#)Makefile.mingw 1.0.78 2017/11/18
# Version: @(#)Makefile.mingw 1.0.79 2017/11/19
#
# Authors: Miran Grca, <mgrca8@gmail.com>
# Fred N. van Kempen, <decwiz@yahoo.com>
@@ -314,9 +314,6 @@ endif
ifndef SERIAL
SERIAL := serial.o
endif
ifndef EUROPC
EUROPC := m_europc.o
endif
# Final versions of the toolchain flags.
@@ -342,7 +339,8 @@ CPUOBJ := cpu.o cpu_table.o \
386_dynarec.o $(DYNARECOBJ)
MCHOBJ := machine.o machine_table.o \
m_amstrad.o $(EUROPC) m_olivetti_m24.o m_tandy.o \
m_amstrad.o m_europc.o m_europc_hdc.o \
m_olivetti_m24.o m_tandy.o \
m_xt.o m_xt_compaq.o m_xt_laserxt.o \
m_at.o m_at_ali1429.o m_at_commodore.o \
m_at_neat.o m_at_headland.o \