diff --git a/src/machine/m_europc.c b/src/machine/m_europc.c index e92e89f07..a417a3eda 100644 --- a/src/machine/m_europc.c +++ b/src/machine/m_europc.c @@ -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, + * + * 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 #include #include +#include #include #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); } diff --git a/src/machine/m_europc_hdc.c b/src/machine/m_europc_hdc.c new file mode 100644 index 000000000..4fb8a3dfd --- /dev/null +++ b/src/machine/m_europc_hdc.c @@ -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, + * 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 +#include +#include +#include +#include +#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<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->tracktracks; dev->track++) { + drive->cur_cyl = dev->track; + for (dev->head=0; dev->headhpc; 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; idrives[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 +}; diff --git a/src/machine/machine.h b/src/machine/machine.h index 3263f1fcc..676eb6d03 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -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 *); diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index 93b1d861f..ad71e1ecc 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -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, # Fred N. van Kempen, @@ -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 \