Updated EuroPC NVR handling.
This commit is contained in:
@@ -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
954
src/machine/m_europc_hdc.c
Normal 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
|
||||
};
|
@@ -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 *);
|
||||
|
||||
|
@@ -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 \
|
||||
|
Reference in New Issue
Block a user