/*
 * VARCem	Virtual ARchaeological Computer EMulator.
 *		An emulator of (mostly) x86-based PC systems and devices,
 *		using the ISA,EISA,VLB,MCA  and PCI system buses, roughly
 *		spanning the era between 1981 and 1995.
 *
 *		This file is part of the VARCem Project.
 *
 *		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
 *
 *		Memory:
 *		  0000:0469	bit 0: b0000 memory available
 *				bit 1: b8000 memory available
 *		  0000:046a:	00 jim 250 01 jim 350
 *
 * FIXME:	Find a new way to handle the switching of color/mono on
 *		external cards. New video_get_type(int card) function?
 *
 * Version:	@(#)m_europc.c	1.0.27	2019/05/17
 *
 * 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-2019 Fred N. van Kempen.
 *
 *		Redistribution and  use  in source  and binary forms, with
 *		or  without modification, are permitted  provided that the
 *		following conditions are met:
 *
 *		1. Redistributions of  source  code must retain the entire
 *		   above notice, this list of conditions and the following
 *		   disclaimer.
 *
 *		2. Redistributions in binary form must reproduce the above
 *		   copyright  notice,  this list  of  conditions  and  the
 *		   following disclaimer in  the documentation and/or other
 *		   materials provided with the distribution.
 *
 *		3. Neither the  name of the copyright holder nor the names
 *		   of  its  contributors may be used to endorse or promote
 *		   products  derived from  this  software without specific
 *		   prior written permission.
 *
 * THIS SOFTWARE  IS  PROVIDED BY THE  COPYRIGHT  HOLDERS AND CONTRIBUTORS
 * "AS IS" AND  ANY EXPRESS  OR  IMPLIED  WARRANTIES,  INCLUDING, BUT  NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE  ARE  DISCLAIMED. IN  NO  EVENT  SHALL THE COPYRIGHT
 * HOLDER OR  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL,  EXEMPLARY,  OR  CONSEQUENTIAL  DAMAGES  (INCLUDING,  BUT  NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE  GOODS OR SERVICES;  LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  AND ON  ANY
 * THEORY OF  LIABILITY, WHETHER IN  CONTRACT, STRICT  LIABILITY, OR  TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING  IN ANY  WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <time.h>
#include "../emu.h"
#include "../config.h"
#include "../timer.h"
#include "../cpu/cpu.h"
#include "../io.h"
#include "../mem.h"
#include "../rom.h"
#include "../device.h"
#include "../nvr.h"
#include "../devices/system/pit.h"
#include "../devices/system/nmi.h"
#include "../devices/ports/parallel.h"
#include "../devices/input/keyboard.h"
#include "../devices/input/mouse.h"
#include "../devices/floppy/fdd.h"
#include "../devices/floppy/fdc.h"
#include "../devices/disk/hdc.h"
#include "../devices/video/video.h"
#include "machine.h"


/* 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 (year only) */
#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) */

    nvr_t	nvr;				/* NVR */
    uint8_t	nvr_stat;
    uint8_t	nvr_addr;
} europc_t;


/*
 * This is called every second through the NVR/RTC hook.
 *
 * We fake a 'running' RTC by updating its registers on
 * each passing second. Not exactly accurate, but good
 * enough.
 *
 * Note that this code looks nasty because of all the
 * BCD to decimal vv going on.
 *
 * FIXME: should we mark NVR as dirty?
 */
static void
rtc_ticker(nvr_t *nvr)
{
    uint8_t *regs;
    int mon, yr;

    /* Only if RTC is running.. */
    regs = nvr->regs;
    if (! (regs[MRTC_CTRLSTAT] & 0x01)) return;

    regs[MRTC_SECONDS] = RTC_BCDINC(nvr->regs[MRTC_SECONDS], 1);
    if (regs[MRTC_SECONDS] >= RTC_BCD(60)) {
	regs[MRTC_SECONDS] = RTC_BCD(0);
	regs[MRTC_MINUTES] = RTC_BCDINC(regs[MRTC_MINUTES], 1);
	if (regs[MRTC_MINUTES] >= RTC_BCD(60)) {
		regs[MRTC_MINUTES] = RTC_BCD(0);
		regs[MRTC_HOURS] = RTC_BCDINC(regs[MRTC_HOURS], 1);
		if (regs[MRTC_HOURS] >= RTC_BCD(24)) {
			regs[MRTC_HOURS] = RTC_BCD(0);
			regs[MRTC_DAYS] = RTC_BCDINC(regs[MRTC_DAYS], 1);
			mon = RTC_DCB(regs[MRTC_MONTHS]);
			yr = RTC_DCB(regs[MRTC_YEARS]) + 1900;
			if (RTC_DCB(regs[MRTC_DAYS]) > nvr_get_days(mon, yr)) {
				regs[MRTC_DAYS] = RTC_BCD(1);
				regs[MRTC_MONTHS] = RTC_BCDINC(regs[MRTC_MONTHS], 1);
				if (regs[MRTC_MONTHS] > RTC_BCD(12)) {
					regs[MRTC_MONTHS] = RTC_BCD(1);
					regs[MRTC_YEARS] = RTC_BCDINC(regs[MRTC_YEARS], 1) & 0xff;
				}
			}
		}
	}
    }
}


/* Get the current NVR time. */
static void
rtc_time_get(uint8_t *regs, struct tm *tm)
{
    /* NVR is in BCD data mode. */
    tm->tm_sec = RTC_DCB(regs[MRTC_SECONDS]);
    tm->tm_min = RTC_DCB(regs[MRTC_MINUTES]);
    tm->tm_hour = RTC_DCB(regs[MRTC_HOURS]);
    tm->tm_wday = (RTC_DCB(regs[MRTC_WEEKDAY]) - 1);
    tm->tm_mday = RTC_DCB(regs[MRTC_DAYS]);
    tm->tm_mon = (RTC_DCB(regs[MRTC_MONTHS]) - 1);
    tm->tm_year = RTC_DCB(regs[MRTC_YEARS]);
#ifdef MRTC_CENTURY
    tm->tm_year += (RTC_DCB(regs[MRTC_CENTURY]) * 100) - 1900;
#endif
}


/* Set the current NVR time. */
static void
rtc_time_set(uint8_t *regs, const struct tm *tm)
{
    /* NVR is in BCD data mode. */
    regs[MRTC_SECONDS] = RTC_BCD(tm->tm_sec);
    regs[MRTC_MINUTES] = RTC_BCD(tm->tm_min);
    regs[MRTC_HOURS] = RTC_BCD(tm->tm_hour);
    regs[MRTC_WEEKDAY] = RTC_BCD(tm->tm_wday + 1);
    regs[MRTC_DAYS] = RTC_BCD(tm->tm_mday);
    regs[MRTC_MONTHS] = RTC_BCD(tm->tm_mon + 1);
    regs[MRTC_YEARS] = RTC_BCD(tm->tm_year % 100);
#ifdef MRTC_CENTURY
    regs[MRTC_CENTURY] = RTC_BCD((tm->tm_year+1900) / 100);
#endif
}


static void
rtc_start(nvr_t *nvr)
{
    struct tm tm;

    /* Initialize the internal and chip times. */
    if (config.time_sync != TIME_SYNC_DISABLED) {
	/* Use the internal clock's time. */
	nvr_time_get(&tm);
	rtc_time_set(nvr->regs, &tm);
    } else {
	/* Set the internal clock from the chip time. */
	rtc_time_get(nvr->regs, &tm);
	nvr_time_set(&tm);
    }
}


/* Create a valid checksum for the current NVR data. */
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);
}


/* Reset the machine's NVR to a sane state. */
static void
rtc_reset(nvr_t *nvr)
{
    /* Initialize the RTC to a known state. */
    nvr->regs[MRTC_SECONDS] = RTC_BCD(0);	/* seconds */
    nvr->regs[MRTC_MINUTES] = RTC_BCD(0);	/* minutes */
    nvr->regs[MRTC_HOURS] = RTC_BCD(0);		/* hours */
    nvr->regs[MRTC_DAYS] = RTC_BCD(1);		/* days */
    nvr->regs[MRTC_MONTHS] = RTC_BCD(1);	/* months */
    nvr->regs[MRTC_YEARS] = RTC_BCD(80);	/* years */
    nvr->regs[MRTC_WEEKDAY] = RTC_BCD(1);	/* weekday */
    nvr->regs[MRTC_WEEKNO] = RTC_BCD(1);	/* weekno */

    /*
     * EuroPC System Configuration:
     *
     * [A]	unknown
     *
     * [B]	7	1  bootdrive extern
     *			0  bootdrive 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 720K
     *			01 internal drive 360K
     *			00 internal drive disabled
     *
     *	 [C]	7:6	unknown
     *		5	monitor detection OFF
     *		4	unknown
     *		3:2	11 illegal memory size (768K ?)
     *			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)
     */
    nvr->regs[MRTC_CONF_A] = 0x00;	/* CONFIG A */
    nvr->regs[MRTC_CONF_B] = 0x0A;	/* CONFIG B */
    nvr->regs[MRTC_CONF_C] = 0x28;	/* CONFIG C */
    nvr->regs[MRTC_CONF_D] = 0x12;	/* CONFIG D */
    nvr->regs[MRTC_CONF_E] = 0x0A;	/* CONFIG E */

    nvr->regs[MRTC_CHECK_LO] = 0x00;	/* checksum (LO) */
    nvr->regs[MRTC_CHECK_HI] = 0x00;	/* checksum (HI) */

    nvr->regs[MRTC_CTRLSTAT] = 0x01;	/* status/control */

    /* Generate a valid checksum. */
    nvr->regs[MRTC_CHECK_LO] = rtc_checksum(nvr->regs);
}


/* Execute a JIM control command. */
static void
jim_set(europc_t *dev, uint8_t reg, uint8_t val)
{
    switch(reg) {
	case 0:		/* MISC control (WO) */
		/* bit0: enable MOUSE */
		/* bit1: enable joystick */
		break;

	case 2:		/* AGA control */
		if (! (val & 0x80)) {
			/* Reset AGA. */
			break;
		}

		switch (val) {
			case 0x1f:	/* 0001 1111 */
			case 0x0b:	/* 0000 1011 */
				/*europc_jim.mode=AGA_MONO; */
				DEBUG("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? */
				DEBUG("EuroPC: AGA 80-column mode!\n");
				break;

			case 0x0d:	/* 0000 1011 */
				/*40 columns? */
				DEBUG("EuroPC: AGA 40-column mode!\n");
				break;

			default:
				/*europc_jim.mode=AGA_OFF; */
				break;
		}
		break;

	case 4:		/* CPU Speed control */
		switch(val & 0xc0) {
			case 0x00:	/* 4.77 MHz */
/*FIXME:			cpu_set_clockscale(0, 1.0/2); */
				break;

			case 0x40:	/* 7.16 MHz */
/*FIXME:			cpu_set_clockscale(0, 3.0/4); */
				break;

			default:	/* 9.54 MHz */
/*FIXME:			cpu_set_clockscale(0, 1);break; */
				break;
		}
		break;

	default:
		break;
    }

    dev->regs[reg] = val;
}


/* Write to one of the JIM registers. */
static void
jim_write(uint16_t addr, uint8_t val, priv_t priv)
{
    europc_t *dev = (europc_t *)priv;
    uint8_t b;

    DBGLOG(2, "EuroPC: jim_wr(%04x, %02x)\n", addr, val);

    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_set(dev, (addr & 0x07), val);
		break;

	case 0x0a:		/* M3002 RTC INDEX/DATA register */
		switch(dev->nvr_stat) {
			case 0:		/* save index */
				dev->nvr_addr = val & 0x0f;
				dev->nvr_stat++;
				break;

			case 1:		/* save data HI nibble */
				b = dev->nvr.regs[dev->nvr_addr] & 0x0f;
				b |= (val << 4);
				dev->nvr.regs[dev->nvr_addr] = b;
				dev->nvr_stat++;
				nvr_dosave++;
				break;

			case 2:		/* save data LO nibble */
				b = dev->nvr.regs[dev->nvr_addr] & 0xf0;
				b |= (val & 0x0f);
				dev->nvr.regs[dev->nvr_addr] = b;
				dev->nvr_stat = 0;
				nvr_dosave++;
				break;
		}
		break;

	default:
		ERRLOG("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, priv_t priv)
{
    europc_t *dev = (europc_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 = dev->regs[addr & 0x07];
		break;

	case 0x0a:		/* M3002 RTC INDEX/DATA register */
		switch(dev->nvr_stat) {
			case 0:
				r = 0x00;
				break;

			case 1:		/* read data HI nibble */
				r = (dev->nvr.regs[dev->nvr_addr] >> 4);
				dev->nvr_stat++;
				break;

			case 2:		/* read data LO nibble */
				r = (dev->nvr.regs[dev->nvr_addr] & 0x0f);
				dev->nvr_stat = 0;
				break;
		}
		break;

	default:
		ERRLOG("EuroPC: invalid JIM read %02x\n", addr);
		break;
    }

    DBGLOG(2, "EuroPC: jim_rd(%04x): %02x\n", addr, r);

    return(r);
}


static void
europc_close(priv_t priv)
{
    europc_t *dev = (europc_t *)priv;
    nvr_t *nvr = &dev->nvr;

    if (nvr->fn != NULL) {
	free((wchar_t *)nvr->fn);
	nvr->fn = NULL;
    }

    free(dev);
}


/*
 * Initialize the mainboard 'device' of the 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.
 */
static priv_t
europc_init(const device_t *info, void *arg)
{
    europc_t *dev;
    void *priv;
    uint8_t b;
    int i;

    /* Clear the machine state. */
    dev = (europc_t *)mem_alloc(sizeof(europc_t));
    memset(dev, 0x00, sizeof(europc_t));

    /* Add the machine device. */
    device_add_ex(info, (priv_t)dev);

    /* Get configurable things. */
    i = machine_get_config_int("js9");
    dev->jim = (i == 1) ? 0x0350 : 0x0250;

    /* Set up and initialize the NVR. */
    dev->nvr.size = machine_get_nvrsize();
    dev->nvr.irq = -1;
    dev->nvr.reset = rtc_reset;
    dev->nvr.start = rtc_start;
    dev->nvr.tick = rtc_ticker;
    nvr_init(&dev->nvr);

    /*
     * This is not quite correct, but it works.
     *
     * The EuroPC has an onboard CGA-class video controller
     * (AGA) which is normally used. We currently do not yet
     * support it. To keep the NVRAM valid, however, we act
     * like we have it configured.
     */
    if (config.video_card == VID_INTERNAL) {
	INFO("EuroPC: enabling CGA in place of AGA!\n");
	device_add(&cga_device);
    }

    DEBUG("EuroPC: NVR=[ %02x %02x %02x %02x %02x ] %sVALID\n",
	  dev->nvr.regs[MRTC_CONF_A], dev->nvr.regs[MRTC_CONF_B],
	  dev->nvr.regs[MRTC_CONF_C], dev->nvr.regs[MRTC_CONF_D],
	  dev->nvr.regs[MRTC_CONF_E],
	  (dev->nvr.regs[MRTC_CHECK_LO]!=rtc_checksum(dev->nvr.regs))?"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 user.
     */
    b = (dev->nvr.regs[MRTC_CONF_D] & ~0x17);
    if (config.video_card != VID_INTERNAL) {
	/*
	 * OK, this is not exactly correct, either.
	 *
	 * If we use an external video card, that will not be
	 * installed until after we boot the mainboard, so we
	 * do not know about it yet. Therefore, we just peek
	 * at the configured video card type, and perform an
	 * "educated guess" as to its type..
	 */
	switch(config.video_card) {
		case VID_MDA:		/* MDA */
		case VID_HERCULES:	/* Hercules */
			b |= 0x03;	/* external video, mono */
			break;

		case VID_CGA:		/* Color, CGA */
			b |= 0x12;	/* external video, CGA80 */
			break;

		default:		/* all others */
			b |= 0x10;	/* external video, special */
	}
    } else {
    	i = video_type();
    	switch(i) {
		case VID_TYPE_MDA:	/* Monochrome, MDA, Hercules */
			b |= 0x03;	/* external video, mono */
			break;

		case VID_TYPE_CGA:	/* Color, CGA */
			b |= 0x12;	/* external video, CGA80 */
			break;

		case VID_TYPE_SPEC:	/* EGA, VGA etc */
			b |= 0x10;	/* external video, special */
			break;

		default:
			ERRLOG("EuroPC: unknown video type %i !\n", i);
			break;
	}
    }
    dev->nvr.regs[MRTC_CONF_D] = b;

    /* Update the memory size. */
    b = (dev->nvr.regs[MRTC_CONF_C] & 0xf3);
    switch(mem_size) {
	case 256:
		b |= 0x04;
		break;

	case 512:
		b |= 0x08;
		break;

	case 640:
		b |= 0x00;
		break;
    }
    dev->nvr.regs[MRTC_CONF_C] = b;

    /* Update CPU speed. */
    b = (dev->nvr.regs[MRTC_CONF_D] & 0x3f);
    switch(config.cpu_type) {
	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;
    }
    dev->nvr.regs[MRTC_CONF_D] = b;

    /* Set up game port. */
    b = (dev->nvr.regs[MRTC_CONF_C] & 0xfc);
    if (config.mouse_type == MOUSE_INTERNAL) {
	/* Enable the Logitech Bus Mouse device. */
	priv = device_add(&mouse_logibus_onboard_device);
	mouse_bus_set_irq(priv, 2);

	/* Configure the port for (Bus Mouse Compatible) Mouse. */
	b |= 0x01;
    } else if (config.game_enabled) {
	b |= 0x02;	/* enable port as joysticks */
    }
    dev->nvr.regs[MRTC_CONF_C] = b;

    /* Set up hard disks. */
    b = dev->nvr.regs[MRTC_CONF_B] & 0x84;
    if (config.hdc_type != HDC_NONE)
	b |= 0x20;			/* HD20 #1 */

    /* Set up floppy types. */
    if (fdd_get_type(0) != 0) {
	/* We have floppy A: */
	if (fdd_is_dd(0)) {
		if (fdd_is_525(0))
			b |= 0x01;	/* 5.25" DD */
		  else
			b |= 0x02;	/* 3.5" DD */
	} else
		ERRLOG("EuroPC: unsupported HD type for floppy drive 0\n");
    }
    if (fdd_get_type(1) != 0) {
	/* We have floppy B: */
	if (fdd_is_dd(1)) {
		b |= 0x04;		/* EXTERNAL */
		if (fdd_is_525(1))
			b |= 0x08;	/* 5.25" DD */
		  else
			b |= 0x10;	/* 3.5" DD */
	} else
		ERRLOG("EuroPC: unsupported HD type for floppy drive 1\n");
    }
    dev->nvr.regs[MRTC_CONF_B] = b;

    /* Validate the NVR checksum and save. */
    dev->nvr.regs[MRTC_CHECK_LO] = rtc_checksum(dev->nvr.regs);
    nvr_dosave++;

    machine_common_init();

    nmi_init();

    /* Set up our DRAM refresh timer. */
    pit_set_out_func(&pit, 1, m_xt_refresh_timer);

    /*
     * 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(dev->jim, 16,
		  jim_read,NULL,NULL, jim_write,NULL,NULL, dev);

    /* Only after JIM has been initialized. */
    device_add(&keyboard_xt_device);

    /* Enable and set up the FDC. */
    device_add(&fdc_xt_device);

    /*
     * Set up and enable the HD20 disk controller.
     *
     * We only do this if we have not configured another one.
     */
    if (config.hdc_type == HDC_INTERNAL)
	(void)device_add(&xta_hd20_device);

    return((priv_t)dev);
}


static const device_config_t europc_config[] = {
    {
	"js9", "JS9 Jumper (JIM)", CONFIG_INT, "", 0,
	{
		{
			"Disabled (250h)", 0
		},
		{
			"Enabled (350h)", 1
		},
		{
			NULL
		}
	},
    },
    {
	NULL
    }
};


static const CPU cpus_europc[] = {
    { "8088/4.77", CPU_8088, 4772728, 1, 0, 0, 0, 0, CPU_ALTERNATE_XTAL, 0,0,0,0, 1 },
    { "8088/7.16", CPU_8088, 7159092, 1, 0, 0, 0, 0, CPU_ALTERNATE_XTAL, 0,0,0,0, 1 },
    { "8088/9.54", CPU_8088, 9545456, 1, 0, 0, 0, 0, 0, 0,0,0,0, 1 },
    { NULL }
};

static const machine_t europc_info = {
    MACHINE_ISA | MACHINE_HDC | MACHINE_VIDEO | MACHINE_MOUSE,
    0,
    512, 640, 128, 16, 0,
    {{"Siemens",cpus_europc}}
};

const device_t m_europc = {
    "Schneider EuroPC",
    DEVICE_ROOT,
    0,
    L"schneider/europc",
    europc_init, europc_close, NULL,
    NULL, NULL, NULL,
    &europc_info,
    europc_config
};
