2018-03-13 03:46:10 +01:00
|
|
|
/*
|
|
|
|
|
* 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 Toshiba T1000 and T1200 portables.
|
|
|
|
|
*
|
|
|
|
|
* The T1000 is the T3100e's little brother -- a real laptop
|
|
|
|
|
* with a rechargeable battery.
|
|
|
|
|
*
|
|
|
|
|
* Features: 80C88 at 4.77MHz
|
|
|
|
|
* - 512k system RAM
|
|
|
|
|
* - 640x200 monochrome LCD
|
|
|
|
|
* - 82-key keyboard
|
|
|
|
|
* - Real-time clock. Not the normal 146818, but a TC8521,
|
|
|
|
|
* which is a 4-bit chip.
|
|
|
|
|
* - A ROM drive (128k, 256k or 512k) which acts as a mini
|
|
|
|
|
* hard drive and contains a copy of DOS 2.11.
|
|
|
|
|
* - 160 bytes of non-volatile RAM for the CONFIG.SYS used
|
|
|
|
|
* when booting from the ROM drive. Possibly physically
|
|
|
|
|
* located in the keyboard controller RAM.
|
|
|
|
|
*
|
|
|
|
|
* An optional memory expansion board can be fitted. This adds
|
|
|
|
|
* 768k of RAM, which can be used for up to three purposes:
|
|
|
|
|
* > Conventional memory -- 128k between 512k and 640k
|
|
|
|
|
* > HardRAM -- a battery-backed RAM drive.
|
|
|
|
|
* > EMS
|
|
|
|
|
*
|
|
|
|
|
* This means that there are up to three different
|
|
|
|
|
* implementations of non-volatile RAM in the same computer
|
|
|
|
|
* (52 nibbles in the TC8521, 160 bytes of CONFIG.SYS, and
|
|
|
|
|
* up to 768k of HardRAM).
|
|
|
|
|
*
|
|
|
|
|
* The T1200 is a slightly upgraded version with a turbo mode
|
|
|
|
|
* (double CPU clock, 9.54MHz) and an optional hard drive.
|
|
|
|
|
* The interface for this is proprietary both at the physical
|
|
|
|
|
* and programming level.
|
|
|
|
|
*
|
|
|
|
|
* 01F2h: If hard drive is present, low 4 bits are 0Ch [20Mb]
|
|
|
|
|
* or 0Dh [10Mb].
|
|
|
|
|
*
|
|
|
|
|
* The hard drive is a 20MB (615/2/26) RLL 3.5" drive.
|
|
|
|
|
*
|
|
|
|
|
* The TC8521 is a 4-bit RTC, so each memory location can only
|
|
|
|
|
* hold a single BCD digit. Hence everything has 'ones' and
|
|
|
|
|
* 'tens' digits.
|
|
|
|
|
*
|
|
|
|
|
* NOTE: Still need to figure out a way to load/save ConfigSys and
|
|
|
|
|
* HardRAM stuff. Needs to be linked in to the NVR code.
|
|
|
|
|
*
|
2018-08-16 00:25:20 +02:00
|
|
|
* Version: @(#)m_xt_t1000.c 1.0.9 2018/08/16
|
2018-03-13 03:46:10 +01:00
|
|
|
*
|
|
|
|
|
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
|
|
|
|
|
* Miran Grca, <mgrca8@gmail.com>
|
|
|
|
|
* Sarah Walker, <tommowalker@tommowalker.co.uk>
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2018 Fred N. van Kempen.
|
|
|
|
|
* Copyright 2018 Miran Grca.
|
|
|
|
|
* Copyright 2018 Sarah Walker.
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the:
|
|
|
|
|
*
|
|
|
|
|
* Free Software Foundation, Inc.
|
|
|
|
|
* 59 Temple Place - Suite 330
|
|
|
|
|
* Boston, MA 02111-1307
|
|
|
|
|
* USA.
|
|
|
|
|
*/
|
2018-05-21 19:04:05 +02:00
|
|
|
#include <stdarg.h>
|
2018-03-13 03:46:10 +01:00
|
|
|
#include <stdint.h>
|
2018-05-21 19:04:05 +02:00
|
|
|
#include <stdio.h>
|
2018-03-13 03:46:10 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <wchar.h>
|
|
|
|
|
#include <time.h>
|
2018-05-21 19:04:05 +02:00
|
|
|
#define HAVE_STDARG_H
|
2018-03-13 03:46:10 +01:00
|
|
|
#include "../86box.h"
|
|
|
|
|
#include "../cpu/cpu.h"
|
|
|
|
|
#include "../io.h"
|
|
|
|
|
#include "../pit.h"
|
|
|
|
|
#include "../nmi.h"
|
|
|
|
|
#include "../mem.h"
|
|
|
|
|
#include "../rom.h"
|
|
|
|
|
#include "../device.h"
|
2018-04-25 23:51:13 +02:00
|
|
|
#include "../nvr.h"
|
2018-03-13 03:46:10 +01:00
|
|
|
#include "../keyboard.h"
|
|
|
|
|
#include "../lpt.h"
|
2018-03-19 04:19:19 +01:00
|
|
|
#include "../mem.h"
|
2018-03-13 03:46:10 +01:00
|
|
|
#include "../floppy/fdd.h"
|
|
|
|
|
#include "../floppy/fdc.h"
|
|
|
|
|
#include "../game/gameport.h"
|
|
|
|
|
#include "../video/video.h"
|
|
|
|
|
#include "../plat.h"
|
|
|
|
|
#include "machine.h"
|
|
|
|
|
#include "m_xt_t1000.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define T1000_ROMSIZE (512*1024UL) /* Maximum ROM drive size is 512k */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum TC8521_ADDR {
|
|
|
|
|
/* Page 0 registers */
|
|
|
|
|
TC8521_SECOND1 = 0,
|
|
|
|
|
TC8521_SECOND10,
|
|
|
|
|
TC8521_MINUTE1,
|
|
|
|
|
TC8521_MINUTE10,
|
|
|
|
|
TC8521_HOUR1,
|
|
|
|
|
TC8521_HOUR10,
|
|
|
|
|
TC8521_WEEKDAY,
|
|
|
|
|
TC8521_DAY1,
|
|
|
|
|
TC8521_DAY10,
|
|
|
|
|
TC8521_MONTH1,
|
|
|
|
|
TC8521_MONTH10,
|
|
|
|
|
TC8521_YEAR1,
|
|
|
|
|
TC8521_YEAR10,
|
|
|
|
|
TC8521_PAGE, /* PAGE register */
|
|
|
|
|
TC8521_TEST, /* TEST register */
|
|
|
|
|
TC8521_RESET, /* RESET register */
|
|
|
|
|
|
|
|
|
|
/* Page 1 registers */
|
|
|
|
|
TC8521_24HR = 0x1A,
|
|
|
|
|
TC8521_LEAPYEAR = 0x1B
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
/* ROM drive */
|
|
|
|
|
uint8_t *romdrive;
|
|
|
|
|
uint8_t rom_ctl;
|
|
|
|
|
uint32_t rom_offset;
|
|
|
|
|
mem_mapping_t rom_mapping;
|
|
|
|
|
|
|
|
|
|
/* CONFIG.SYS drive. */
|
2018-03-19 04:19:19 +01:00
|
|
|
uint8_t t1000_nvram[160];
|
2018-08-15 20:23:54 +02:00
|
|
|
uint8_t t1200_nvram[2048];
|
2018-03-13 03:46:10 +01:00
|
|
|
|
|
|
|
|
/* System control registers */
|
|
|
|
|
uint8_t sys_ctl[16];
|
|
|
|
|
uint8_t syskeys;
|
|
|
|
|
uint8_t turbo;
|
|
|
|
|
|
|
|
|
|
/* NVRAM control */
|
|
|
|
|
uint8_t nvr_c0;
|
|
|
|
|
uint8_t nvr_tick;
|
|
|
|
|
int nvr_addr;
|
|
|
|
|
uint8_t nvr_active;
|
2018-03-19 04:19:19 +01:00
|
|
|
mem_mapping_t nvr_mapping; /* T1200 NVRAM mapping */
|
2018-03-13 03:46:10 +01:00
|
|
|
|
|
|
|
|
/* EMS data */
|
|
|
|
|
uint8_t ems_reg[4];
|
|
|
|
|
mem_mapping_t mapping[4];
|
|
|
|
|
uint32_t page_exec[4];
|
|
|
|
|
uint8_t ems_port_index;
|
|
|
|
|
uint16_t ems_port;
|
|
|
|
|
uint8_t is_640k;
|
|
|
|
|
uint32_t ems_base;
|
|
|
|
|
int32_t ems_pages;
|
|
|
|
|
|
|
|
|
|
fdc_t *fdc;
|
|
|
|
|
|
|
|
|
|
nvr_t nvr;
|
|
|
|
|
} t1000_t;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static t1000_t t1000;
|
|
|
|
|
|
|
|
|
|
|
2018-05-21 19:04:05 +02:00
|
|
|
#ifdef ENABLE_T1000_LOG
|
|
|
|
|
int t1000_do_log = ENABLE_T1000_LOG;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
t1000_log(const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
#ifdef ENABLE_TANDY_LOG
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
|
|
if (t1000_do_log)
|
|
|
|
|
{
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
pclog_ex(fmt, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-13 03:46:10 +01:00
|
|
|
/* Set the chip time. */
|
|
|
|
|
static void
|
|
|
|
|
tc8521_time_set(uint8_t *regs, struct tm *tm)
|
|
|
|
|
{
|
|
|
|
|
regs[TC8521_SECOND1] = (tm->tm_sec % 10);
|
|
|
|
|
regs[TC8521_SECOND10] = (tm->tm_sec / 10);
|
|
|
|
|
regs[TC8521_MINUTE1] = (tm->tm_min % 10);
|
|
|
|
|
regs[TC8521_MINUTE10] = (tm->tm_min / 10);
|
|
|
|
|
if (regs[TC8521_24HR] & 0x01) {
|
|
|
|
|
regs[TC8521_HOUR1] = (tm->tm_hour % 10);
|
|
|
|
|
regs[TC8521_HOUR10] = (tm->tm_hour / 10);
|
|
|
|
|
} else {
|
|
|
|
|
regs[TC8521_HOUR1] = ((tm->tm_hour % 12) % 10);
|
|
|
|
|
regs[TC8521_HOUR10] = (((tm->tm_hour % 12) / 10) |
|
|
|
|
|
((tm->tm_hour >= 12) ? 2 : 0));
|
|
|
|
|
}
|
|
|
|
|
regs[TC8521_WEEKDAY] = tm->tm_wday;
|
|
|
|
|
regs[TC8521_DAY1] = (tm->tm_mday % 10);
|
|
|
|
|
regs[TC8521_DAY10] = (tm->tm_mday / 10);
|
|
|
|
|
regs[TC8521_MONTH1] = ((tm->tm_mon + 1) % 10);
|
|
|
|
|
regs[TC8521_MONTH10] = ((tm->tm_mon + 1) / 10);
|
|
|
|
|
regs[TC8521_YEAR1] = ((tm->tm_year - 80) % 10);
|
|
|
|
|
regs[TC8521_YEAR10] = (((tm->tm_year - 80) % 100) / 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Get the chip time. */
|
|
|
|
|
#define nibbles(a) (regs[(a##1)] + 10 * regs[(a##10)])
|
|
|
|
|
static void
|
|
|
|
|
tc8521_time_get(uint8_t *regs, struct tm *tm)
|
|
|
|
|
{
|
|
|
|
|
tm->tm_sec = nibbles(TC8521_SECOND);
|
|
|
|
|
tm->tm_min = nibbles(TC8521_MINUTE);
|
|
|
|
|
if (regs[TC8521_24HR] & 0x01)
|
|
|
|
|
tm->tm_hour = nibbles(TC8521_HOUR);
|
|
|
|
|
else
|
|
|
|
|
tm->tm_hour = ((nibbles(TC8521_HOUR) % 12) +
|
|
|
|
|
(regs[TC8521_HOUR10] & 0x02) ? 12 : 0);
|
|
|
|
|
//FIXME: wday
|
|
|
|
|
tm->tm_mday = nibbles(TC8521_DAY);
|
|
|
|
|
tm->tm_mon = (nibbles(TC8521_MONTH) - 1);
|
|
|
|
|
tm->tm_year = (nibbles(TC8521_YEAR) + 1980);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* This is called every second through the NVR/RTC hook. */
|
|
|
|
|
static void
|
|
|
|
|
tc8521_tick(nvr_t *nvr)
|
|
|
|
|
{
|
2018-05-21 19:04:05 +02:00
|
|
|
t1000_log("TC8521: ping\n");
|
2018-03-13 03:46:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tc8521_start(nvr_t *nvr)
|
|
|
|
|
{
|
|
|
|
|
struct tm tm;
|
|
|
|
|
|
|
|
|
|
/* Initialize the internal and chip times. */
|
2018-08-01 18:07:52 +02:00
|
|
|
if (time_sync & TIME_SYNC_ENABLED) {
|
2018-03-13 03:46:10 +01:00
|
|
|
/* Use the internal clock's time. */
|
|
|
|
|
nvr_time_get(&tm);
|
|
|
|
|
tc8521_time_set(nvr->regs, &tm);
|
|
|
|
|
} else {
|
|
|
|
|
/* Set the internal clock from the chip time. */
|
|
|
|
|
tc8521_time_get(nvr->regs, &tm);
|
|
|
|
|
nvr_time_set(&tm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
/* Start the RTC - BIOS will do this. */
|
|
|
|
|
nvr->regs[TC8521_PAGE] |= 0x80;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Write to one of the chip registers. */
|
|
|
|
|
static void
|
|
|
|
|
tc8521_write(uint16_t addr, uint8_t val, void *priv)
|
|
|
|
|
{
|
|
|
|
|
nvr_t *nvr = (nvr_t *)priv;
|
|
|
|
|
uint8_t page;
|
|
|
|
|
|
|
|
|
|
/* Get to the correct register page. */
|
|
|
|
|
addr &= 0x0f;
|
|
|
|
|
page = nvr->regs[0x0d] & 0x03;
|
|
|
|
|
if (addr < 0x0d)
|
|
|
|
|
addr += (16 * page);
|
|
|
|
|
|
|
|
|
|
if (addr >= 0x10 && nvr->regs[addr] != val)
|
|
|
|
|
nvr_dosave = 1;
|
|
|
|
|
|
|
|
|
|
/* Store the new value. */
|
|
|
|
|
nvr->regs[addr] = val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Read from one of the chip registers. */
|
|
|
|
|
static uint8_t
|
|
|
|
|
tc8521_read(uint16_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
nvr_t *nvr = (nvr_t *)priv;
|
|
|
|
|
uint8_t page;
|
|
|
|
|
|
|
|
|
|
/* Get to the correct register page. */
|
|
|
|
|
addr &= 0x0f;
|
|
|
|
|
page = nvr->regs[0x0d] & 0x03;
|
|
|
|
|
if (addr < 0x0d)
|
|
|
|
|
addr += (16 * page);
|
|
|
|
|
|
|
|
|
|
/* Grab and return the desired value. */
|
|
|
|
|
return(nvr->regs[addr]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Reset the 8521 to a default state. */
|
|
|
|
|
static void
|
|
|
|
|
tc8521_reset(nvr_t *nvr)
|
|
|
|
|
{
|
|
|
|
|
/* Clear the NVRAM. */
|
|
|
|
|
memset(nvr->regs, 0xff, nvr->size);
|
|
|
|
|
|
|
|
|
|
/* Reset the RTC registers. */
|
|
|
|
|
memset(nvr->regs, 0x00, 16);
|
|
|
|
|
nvr->regs[TC8521_WEEKDAY] = 0x01;
|
|
|
|
|
nvr->regs[TC8521_DAY1] = 0x01;
|
|
|
|
|
nvr->regs[TC8521_MONTH1] = 0x01;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tc8521_init(nvr_t *nvr, int size)
|
|
|
|
|
{
|
|
|
|
|
/* This is machine specific. */
|
|
|
|
|
nvr->size = size;
|
|
|
|
|
nvr->irq = -1;
|
|
|
|
|
|
|
|
|
|
/* Set up any local handlers here. */
|
|
|
|
|
nvr->reset = tc8521_reset;
|
|
|
|
|
nvr->start = tc8521_start;
|
|
|
|
|
nvr->tick = tc8521_tick;
|
|
|
|
|
|
|
|
|
|
/* Initialize the actual NVR. */
|
|
|
|
|
nvr_init(nvr);
|
|
|
|
|
|
|
|
|
|
io_sethandler(0x02c0, 16,
|
|
|
|
|
tc8521_read,NULL,NULL, tc8521_write,NULL,NULL, nvr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Given an EMS page ID, return its physical address in RAM. */
|
|
|
|
|
static uint32_t
|
|
|
|
|
ems_execaddr(t1000_t *sys, int pg, uint16_t val)
|
|
|
|
|
{
|
|
|
|
|
if (!(val & 0x80)) return(0); /* Bit 7 reset => not mapped */
|
|
|
|
|
if (!sys->ems_pages) return(0); /* No EMS available: all used by
|
|
|
|
|
* HardRAM or conventional RAM */
|
|
|
|
|
val &= 0x7f;
|
|
|
|
|
|
|
|
|
|
#if 0
|
2018-05-21 19:04:05 +02:00
|
|
|
t1000_log("Select EMS page: %d of %d\n", val, sys->ems_pages);
|
2018-03-13 03:46:10 +01:00
|
|
|
#endif
|
|
|
|
|
if (val < sys->ems_pages) {
|
|
|
|
|
/* EMS is any memory above 512k,
|
|
|
|
|
with ems_base giving the start address */
|
|
|
|
|
return((512 * 1024) + (sys->ems_base * 0x10000) + (0x4000 * val));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
|
ems_in(uint16_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
|
|
|
|
|
#if 0
|
2018-05-21 19:04:05 +02:00
|
|
|
t1000_log("ems_in(%04x)=%02x\n", addr, sys->ems_reg[(addr >> 14) & 3]);
|
2018-03-13 03:46:10 +01:00
|
|
|
#endif
|
|
|
|
|
return(sys->ems_reg[(addr >> 14) & 3]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ems_out(uint16_t addr, uint8_t val, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
int pg = (addr >> 14) & 3;
|
|
|
|
|
|
|
|
|
|
#if 0
|
2018-05-21 19:04:05 +02:00
|
|
|
t1000_log("ems_out(%04x, %02x) pg=%d\n", addr, val, pg);
|
2018-03-13 03:46:10 +01:00
|
|
|
#endif
|
|
|
|
|
sys->ems_reg[pg] = val;
|
|
|
|
|
sys->page_exec[pg] = ems_execaddr(sys, pg, val);
|
|
|
|
|
if (sys->page_exec[pg]) {
|
|
|
|
|
/* Page present */
|
|
|
|
|
mem_mapping_enable(&sys->mapping[pg]);
|
|
|
|
|
mem_mapping_set_exec(&sys->mapping[pg], ram + sys->page_exec[pg]);
|
|
|
|
|
} else {
|
|
|
|
|
mem_mapping_disable(&sys->mapping[pg]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Hardram size is in 64k units */
|
|
|
|
|
static void
|
|
|
|
|
ems_set_hardram(t1000_t *sys, uint8_t val)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
val &= 0x1f; /* Mask off pageframe address */
|
|
|
|
|
if (val && mem_size > 512)
|
|
|
|
|
sys->ems_base = val;
|
|
|
|
|
else
|
|
|
|
|
sys->ems_base = 0;
|
|
|
|
|
|
|
|
|
|
#if 0
|
2018-05-21 19:04:05 +02:00
|
|
|
t1000_log("EMS base set to %02x\n", val);
|
2018-03-13 03:46:10 +01:00
|
|
|
#endif
|
2018-08-16 00:25:20 +02:00
|
|
|
sys->ems_pages = ((mem_size - 512) / 16) - 4 * sys->ems_base;
|
2018-03-13 03:46:10 +01:00
|
|
|
if (sys->ems_pages < 0) sys->ems_pages = 0;
|
|
|
|
|
|
|
|
|
|
/* Recalculate EMS mappings */
|
|
|
|
|
for (n = 0; n < 4; n++)
|
|
|
|
|
ems_out(n << 14, sys->ems_reg[n], sys);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ems_set_640k(t1000_t *sys, uint8_t val)
|
|
|
|
|
{
|
|
|
|
|
if (val && mem_size >= 640) {
|
|
|
|
|
mem_mapping_set_addr(&ram_low_mapping, 0, 640 * 1024);
|
|
|
|
|
sys->is_640k = 1;
|
|
|
|
|
} else {
|
|
|
|
|
mem_mapping_set_addr(&ram_low_mapping, 0, 512 * 1024);
|
|
|
|
|
sys->is_640k = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ems_set_port(t1000_t *sys, uint8_t val)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
#if 0
|
2018-05-21 19:04:05 +02:00
|
|
|
t1000_log("ems_set_port(%d)", val & 0x0f);
|
2018-03-13 03:46:10 +01:00
|
|
|
#endif
|
|
|
|
|
if (sys->ems_port) {
|
|
|
|
|
for (n = 0; n <= 0xc000; n += 0x4000) {
|
|
|
|
|
io_removehandler(sys->ems_port+n, 1,
|
|
|
|
|
ems_in,NULL,NULL, ems_out,NULL,NULL, sys);
|
|
|
|
|
}
|
|
|
|
|
sys->ems_port = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val &= 0x0f;
|
|
|
|
|
sys->ems_port_index = val;
|
|
|
|
|
if (val == 7) {
|
|
|
|
|
/* No EMS */
|
|
|
|
|
sys->ems_port = 0;
|
|
|
|
|
} else {
|
|
|
|
|
sys->ems_port = 0x208 | (val << 4);
|
|
|
|
|
for (n = 0; n <= 0xc000; n += 0x4000) {
|
|
|
|
|
io_sethandler(sys->ems_port+n, 1,
|
|
|
|
|
ems_in,NULL,NULL, ems_out,NULL,NULL, sys);
|
|
|
|
|
}
|
|
|
|
|
sys->ems_port = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
2018-05-21 19:04:05 +02:00
|
|
|
t1000_log(" -> %04x\n", sys->ems_port);
|
2018-03-13 03:46:10 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
addr_to_page(uint32_t addr)
|
|
|
|
|
{
|
|
|
|
|
return((addr - 0xd0000) / 0x4000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Read RAM in the EMS page frame. */
|
|
|
|
|
static uint8_t
|
|
|
|
|
ems_read_ram(uint32_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
int pg = addr_to_page(addr);
|
|
|
|
|
|
|
|
|
|
if (pg < 0) return(0xff);
|
|
|
|
|
addr = sys->page_exec[pg] + (addr & 0x3fff);
|
|
|
|
|
|
|
|
|
|
return(ram[addr]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
|
ems_read_ramw(uint32_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
int pg = addr_to_page(addr);
|
|
|
|
|
|
|
|
|
|
if (pg < 0) return(0xff);
|
|
|
|
|
|
|
|
|
|
#if 0
|
2018-05-21 19:04:05 +02:00
|
|
|
t1000_log("ems_read_ramw addr=%05x ", addr);
|
2018-03-13 03:46:10 +01:00
|
|
|
#endif
|
|
|
|
|
addr = sys->page_exec[pg] + (addr & 0x3FFF);
|
|
|
|
|
|
|
|
|
|
#if 0
|
2018-05-21 19:04:05 +02:00
|
|
|
t1000_log("-> %06x val=%04x\n", addr, *(uint16_t *)&ram[addr]);
|
2018-03-13 03:46:10 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return(*(uint16_t *)&ram[addr]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
|
ems_read_raml(uint32_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
int pg = addr_to_page(addr);
|
|
|
|
|
|
|
|
|
|
if (pg < 0) return(0xff);
|
|
|
|
|
addr = sys->page_exec[pg] + (addr & 0x3fff);
|
|
|
|
|
|
|
|
|
|
return(*(uint32_t *)&ram[addr]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Write RAM in the EMS page frame. */
|
|
|
|
|
static void
|
|
|
|
|
ems_write_ram(uint32_t addr, uint8_t val, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
int pg = addr_to_page(addr);
|
|
|
|
|
|
|
|
|
|
if (pg < 0) return;
|
|
|
|
|
|
|
|
|
|
addr = sys->page_exec[pg] + (addr & 0x3fff);
|
|
|
|
|
if (ram[addr] != val) nvr_dosave = 1;
|
|
|
|
|
|
|
|
|
|
ram[addr] = val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ems_write_ramw(uint32_t addr, uint16_t val, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
int pg = addr_to_page(addr);
|
|
|
|
|
|
|
|
|
|
if (pg < 0) return;
|
|
|
|
|
|
|
|
|
|
#if 0
|
2018-05-21 19:04:05 +02:00
|
|
|
t1000_log("ems_write_ramw addr=%05x ", addr);
|
2018-03-13 03:46:10 +01:00
|
|
|
#endif
|
|
|
|
|
addr = sys->page_exec[pg] + (addr & 0x3fff);
|
|
|
|
|
|
|
|
|
|
#if 0
|
2018-05-21 19:04:05 +02:00
|
|
|
t1000_log("-> %06x val=%04x\n", addr, val);
|
2018-03-13 03:46:10 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (*(uint16_t *)&ram[addr] != val) nvr_dosave = 1;
|
|
|
|
|
|
|
|
|
|
*(uint16_t *)&ram[addr] = val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ems_write_raml(uint32_t addr, uint32_t val, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
int pg = addr_to_page(addr);
|
|
|
|
|
|
|
|
|
|
if (pg < 0) return;
|
|
|
|
|
|
|
|
|
|
addr = sys->page_exec[pg] + (addr & 0x3fff);
|
|
|
|
|
if (*(uint32_t *)&ram[addr] != val) nvr_dosave = 1;
|
|
|
|
|
|
|
|
|
|
*(uint32_t *)&ram[addr] = val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
|
read_ctl(uint16_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
uint8_t ret = 0xff;
|
|
|
|
|
|
|
|
|
|
switch (addr & 0x0f) {
|
|
|
|
|
case 1:
|
|
|
|
|
ret = sys->syskeys;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x0f: /* Detect EMS board */
|
|
|
|
|
switch (sys->sys_ctl[0x0e]) {
|
|
|
|
|
case 0x50:
|
|
|
|
|
if (mem_size > 512) break;
|
|
|
|
|
ret = (0x90 | sys->ems_port_index);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x51:
|
|
|
|
|
/* 0x60 is the page frame address:
|
|
|
|
|
(0xd000 - 0xc400) / 0x20 */
|
|
|
|
|
ret = (sys->ems_base | 0x60);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x52:
|
|
|
|
|
ret = (sys->is_640k ? 0x80 : 0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
ret = (sys->sys_ctl[addr & 0x0f]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return(ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
t1200_turbo_set(uint8_t value)
|
|
|
|
|
{
|
|
|
|
|
if (value == t1000.turbo) return;
|
|
|
|
|
|
|
|
|
|
t1000.turbo = value;
|
|
|
|
|
if (! value)
|
|
|
|
|
cpu_dynamic_switch(0);
|
|
|
|
|
else
|
|
|
|
|
cpu_dynamic_switch(cpu);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
write_ctl(uint16_t addr, uint8_t val, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
|
|
|
|
|
sys->sys_ctl[addr & 0x0f] = val;
|
|
|
|
|
switch (addr & 0x0f) {
|
|
|
|
|
case 4: /* Video control */
|
|
|
|
|
if (sys->sys_ctl[3] == 0x5A) {
|
|
|
|
|
t1000_video_options_set((val & 0x20) ? 1 : 0);
|
|
|
|
|
t1000_display_set((val & 0x40) ? 0 : 1);
|
|
|
|
|
if (romset == ROM_T1200)
|
|
|
|
|
t1200_turbo_set((val & 0x80) ? 1 : 0);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2018-08-16 00:25:20 +02:00
|
|
|
/* It looks as if the T1200, like the T3100, can disable
|
|
|
|
|
* its builtin video chipset if it detects the presence of
|
|
|
|
|
* another video card. */
|
|
|
|
|
case 6: if (romset == ROM_T1200)
|
|
|
|
|
{
|
|
|
|
|
t1000_video_enable(val & 0x01 ? 0 : 1);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2018-03-13 03:46:10 +01:00
|
|
|
case 0x0f: /* EMS control */
|
|
|
|
|
switch (sys->sys_ctl[0x0e]) {
|
|
|
|
|
case 0x50:
|
|
|
|
|
ems_set_port(sys, val);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x51:
|
|
|
|
|
ems_set_hardram(sys, val);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0x52:
|
|
|
|
|
ems_set_640k(sys, val);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Ports 0xC0 to 0xC3 appear to have two purposes:
|
|
|
|
|
*
|
|
|
|
|
* > Access to the 160 bytes of non-volatile RAM containing CONFIG.SYS
|
|
|
|
|
* > Reading the floppy changeline. I don't know why the Toshiba doesn't
|
|
|
|
|
* use the normal port 0x3F7 for this, but it doesn't.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static uint8_t
|
|
|
|
|
t1000_read_nvram(uint16_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
uint8_t tmp = 0xff;
|
|
|
|
|
|
|
|
|
|
switch (addr) {
|
|
|
|
|
case 0xc2: /* Read next byte from NVRAM */
|
|
|
|
|
if (sys->nvr_addr >= 0 && sys->nvr_addr < 160)
|
2018-03-19 04:19:19 +01:00
|
|
|
tmp = sys->t1000_nvram[sys->nvr_addr];
|
2018-03-13 03:46:10 +01:00
|
|
|
sys->nvr_addr++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0xc3: /* Read floppy changeline and NVRAM ready state */
|
|
|
|
|
tmp = fdc_read(0x03f7, t1000.fdc);
|
|
|
|
|
|
|
|
|
|
tmp = (tmp & 0x80) >> 3; /* Bit 4 is changeline */
|
|
|
|
|
tmp |= (sys->nvr_active & 0xc0);/* Bits 6,7 are r/w mode */
|
|
|
|
|
tmp |= 0x2e; /* Bits 5,3,2,1 always 1 */
|
|
|
|
|
tmp |= (sys->nvr_active & 0x40) >> 6; /* Ready state */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return(tmp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
t1000_write_nvram(uint16_t addr, uint8_t val, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* On the real T1000, port 0xC1 is only usable as the high byte
|
|
|
|
|
* of a 16-bit write to port 0xC0, with 0x5A in the low byte.
|
|
|
|
|
*/
|
|
|
|
|
switch (addr) {
|
|
|
|
|
case 0xc0:
|
|
|
|
|
sys->nvr_c0 = val;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0xc1: /* Write next byte to NVRAM */
|
|
|
|
|
if (sys->nvr_addr >= 0 && sys->nvr_addr < 160) {
|
2018-03-19 04:19:19 +01:00
|
|
|
if (sys->t1000_nvram[sys->nvr_addr] != val)
|
2018-03-13 03:46:10 +01:00
|
|
|
nvr_dosave = 1;
|
2018-03-19 04:19:19 +01:00
|
|
|
sys->t1000_nvram[sys->nvr_addr] = val;
|
2018-03-13 03:46:10 +01:00
|
|
|
}
|
|
|
|
|
sys->nvr_addr++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0xc2:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0xc3:
|
|
|
|
|
/*
|
|
|
|
|
* At start of NVRAM read / write, 0x80 is written to
|
|
|
|
|
* port 0xC3. This seems to reset the NVRAM address
|
|
|
|
|
* counter. A single byte is then written (0xff for
|
|
|
|
|
* write, 0x00 for read) which appears to be ignored.
|
|
|
|
|
* Simulate that by starting the address counter off
|
|
|
|
|
* at -1.
|
|
|
|
|
*/
|
|
|
|
|
sys->nvr_active = val;
|
|
|
|
|
if (val == 0x80) sys->nvr_addr = -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-19 04:19:19 +01:00
|
|
|
static
|
|
|
|
|
uint8_t read_t1200_nvram(uint32_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
|
|
|
|
|
return sys->t1200_nvram[addr & 0x7FF];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void write_t1200_nvram(uint32_t addr, uint8_t value, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
|
|
|
|
|
if (sys->t1200_nvram[addr & 0x7FF] != value)
|
|
|
|
|
nvr_dosave = 1;
|
|
|
|
|
|
|
|
|
|
sys->t1200_nvram[addr & 0x7FF] = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-13 03:46:10 +01:00
|
|
|
/* Port 0xC8 controls the ROM drive */
|
|
|
|
|
static uint8_t
|
|
|
|
|
t1000_read_rom_ctl(uint16_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
|
|
|
|
|
return(sys->rom_ctl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
t1000_write_rom_ctl(uint16_t addr, uint8_t val, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
|
|
|
|
|
sys->rom_ctl = val;
|
|
|
|
|
if (sys->romdrive && (val & 0x80)) {
|
|
|
|
|
/* Enable */
|
|
|
|
|
sys->rom_offset = ((val & 0x7f) * 0x10000) % T1000_ROMSIZE;
|
|
|
|
|
mem_mapping_set_addr(&sys->rom_mapping, 0xa0000, 0x10000);
|
|
|
|
|
mem_mapping_set_exec(&sys->rom_mapping, sys->romdrive + sys->rom_offset);
|
|
|
|
|
mem_mapping_enable(&sys->rom_mapping);
|
|
|
|
|
} else {
|
|
|
|
|
mem_mapping_disable(&sys->rom_mapping);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Read the ROM drive */
|
|
|
|
|
static uint8_t
|
|
|
|
|
t1000_read_rom(uint32_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
|
|
|
|
|
if (! sys->romdrive) return(0xff);
|
|
|
|
|
|
|
|
|
|
return(sys->romdrive[sys->rom_offset + (addr & 0xffff)]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
|
t1000_read_romw(uint32_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
|
|
|
|
|
if (! sys->romdrive) return(0xffff);
|
|
|
|
|
|
|
|
|
|
return(*(uint16_t *)(&sys->romdrive[sys->rom_offset + (addr & 0xffff)]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
|
t1000_read_roml(uint32_t addr, void *priv)
|
|
|
|
|
{
|
|
|
|
|
t1000_t *sys = (t1000_t *)priv;
|
|
|
|
|
|
|
|
|
|
if (! sys->romdrive) return(0xffffffff);
|
|
|
|
|
|
|
|
|
|
return(*(uint32_t *)(&sys->romdrive[sys->rom_offset + (addr & 0xffff)]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
const device_t *
|
2018-03-13 03:46:10 +01:00
|
|
|
t1000_get_device(void)
|
|
|
|
|
{
|
|
|
|
|
return(&t1000_video_device);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2018-03-19 01:02:04 +01:00
|
|
|
machine_xt_t1000_init(const machine_t *model)
|
2018-03-13 03:46:10 +01:00
|
|
|
{
|
|
|
|
|
FILE *f;
|
|
|
|
|
int pg;
|
|
|
|
|
|
|
|
|
|
memset(&t1000, 0x00, sizeof(t1000));
|
|
|
|
|
t1000.turbo = 0xff;
|
|
|
|
|
t1000.ems_port_index = 7; /* EMS disabled */
|
|
|
|
|
|
|
|
|
|
/* Load the T1000 CGA Font ROM. */
|
|
|
|
|
loadfont(L"roms/machines/t1000/t1000font.rom", 2);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The ROM drive is optional.
|
|
|
|
|
*
|
|
|
|
|
* If the file is missing, continue to boot; the BIOS will
|
|
|
|
|
* complain 'No ROM drive' but boot normally from floppy.
|
|
|
|
|
*/
|
|
|
|
|
f = rom_fopen(L"roms/machines/t1000/t1000dos.rom", L"rb");
|
|
|
|
|
if (f != NULL) {
|
|
|
|
|
t1000.romdrive = malloc(T1000_ROMSIZE);
|
|
|
|
|
if (t1000.romdrive) {
|
|
|
|
|
memset(t1000.romdrive, 0xff, T1000_ROMSIZE);
|
|
|
|
|
fread(t1000.romdrive, T1000_ROMSIZE, 1, f);
|
|
|
|
|
}
|
|
|
|
|
fclose(f);
|
|
|
|
|
}
|
|
|
|
|
mem_mapping_add(&t1000.rom_mapping, 0xa0000, 0x10000,
|
|
|
|
|
t1000_read_rom,t1000_read_romw,t1000_read_roml,
|
|
|
|
|
NULL,NULL,NULL, NULL, MEM_MAPPING_INTERNAL, &t1000);
|
|
|
|
|
mem_mapping_disable(&t1000.rom_mapping);
|
|
|
|
|
|
|
|
|
|
/* Map the EMS page frame */
|
|
|
|
|
for (pg = 0; pg < 4; pg++) {
|
|
|
|
|
mem_mapping_add(&t1000.mapping[pg], 0xd0000 + (0x4000 * pg), 16384,
|
|
|
|
|
ems_read_ram,ems_read_ramw,ems_read_raml,
|
|
|
|
|
ems_write_ram,ems_write_ramw,ems_write_raml,
|
|
|
|
|
NULL, MEM_MAPPING_EXTERNAL, &t1000);
|
|
|
|
|
|
|
|
|
|
/* Start them all off disabled */
|
|
|
|
|
mem_mapping_disable(&t1000.mapping[pg]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Non-volatile RAM for CONFIG.SYS */
|
|
|
|
|
io_sethandler(0xc0, 4,
|
|
|
|
|
t1000_read_nvram,NULL,NULL,
|
|
|
|
|
t1000_write_nvram,NULL,NULL, &t1000);
|
|
|
|
|
|
|
|
|
|
/* ROM drive */
|
|
|
|
|
io_sethandler(0xc8, 1,
|
|
|
|
|
t1000_read_rom_ctl,NULL,NULL,
|
|
|
|
|
t1000_write_rom_ctl,NULL,NULL, &t1000);
|
|
|
|
|
|
|
|
|
|
/* System control functions, and add-on memory board */
|
|
|
|
|
io_sethandler(0xe0, 16,
|
|
|
|
|
read_ctl,NULL,NULL, write_ctl,NULL,NULL, &t1000);
|
|
|
|
|
|
|
|
|
|
machine_common_init(model);
|
|
|
|
|
|
|
|
|
|
pit_set_out_func(&pit, 1, pit_refresh_timer_xt);
|
|
|
|
|
device_add(&keyboard_xt_device);
|
|
|
|
|
t1000.fdc = device_add(&fdc_xt_device);
|
|
|
|
|
nmi_init();
|
|
|
|
|
|
|
|
|
|
tc8521_init(&t1000.nvr, model->nvrmask + 1);
|
|
|
|
|
|
2018-08-16 00:25:20 +02:00
|
|
|
if (gfxcard == GFX_INTERNAL)
|
|
|
|
|
device_add(&t1000_video_device);
|
2018-03-13 03:46:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-19 01:02:04 +01:00
|
|
|
const device_t *
|
2018-03-13 03:46:10 +01:00
|
|
|
t1200_get_device(void)
|
|
|
|
|
{
|
|
|
|
|
return(&t1200_video_device);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2018-03-19 01:02:04 +01:00
|
|
|
machine_xt_t1200_init(const machine_t *model)
|
2018-03-13 03:46:10 +01:00
|
|
|
{
|
|
|
|
|
int pg;
|
|
|
|
|
|
|
|
|
|
memset(&t1000, 0x00, sizeof(t1000));
|
|
|
|
|
t1000.ems_port_index = 7; /* EMS disabled */
|
|
|
|
|
|
|
|
|
|
/* Load the T1200 CGA Font ROM. */
|
|
|
|
|
loadfont(L"roms/machines/t1200/t1000font.bin", 2);
|
|
|
|
|
|
|
|
|
|
/* Map the EMS page frame */
|
|
|
|
|
for (pg = 0; pg < 4; pg++) {
|
|
|
|
|
mem_mapping_add(&t1000.mapping[pg],
|
|
|
|
|
0xd0000 + (0x4000 * pg), 16384,
|
|
|
|
|
ems_read_ram,ems_read_ramw,ems_read_raml,
|
|
|
|
|
ems_write_ram,ems_write_ramw,ems_write_raml,
|
|
|
|
|
NULL, MEM_MAPPING_EXTERNAL, &t1000);
|
|
|
|
|
|
|
|
|
|
/* Start them all off disabled */
|
|
|
|
|
mem_mapping_disable(&t1000.mapping[pg]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* System control functions, and add-on memory board */
|
|
|
|
|
io_sethandler(0xe0, 16,
|
|
|
|
|
read_ctl,NULL,NULL, write_ctl,NULL,NULL, &t1000);
|
|
|
|
|
|
|
|
|
|
machine_common_init(model);
|
|
|
|
|
|
2018-03-19 04:19:19 +01:00
|
|
|
mem_mapping_add(&t1000.nvr_mapping,
|
|
|
|
|
0x000f0000, 2048,
|
|
|
|
|
read_t1200_nvram, NULL, NULL,
|
|
|
|
|
write_t1200_nvram, NULL, NULL,
|
|
|
|
|
NULL, 0, &t1000);
|
|
|
|
|
|
2018-03-13 03:46:10 +01:00
|
|
|
pit_set_out_func(&pit, 1, pit_refresh_timer_xt);
|
|
|
|
|
device_add(&keyboard_xt_device);
|
|
|
|
|
t1000.fdc = device_add(&fdc_xt_device);
|
|
|
|
|
nmi_init();
|
|
|
|
|
|
|
|
|
|
tc8521_init(&t1000.nvr, model->nvrmask + 1);
|
|
|
|
|
|
2018-08-16 00:25:20 +02:00
|
|
|
if (gfxcard == GFX_INTERNAL)
|
|
|
|
|
device_add(&t1200_video_device);
|
2018-03-13 03:46:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
t1000_syskey(uint8_t andmask, uint8_t ormask, uint8_t xormask)
|
|
|
|
|
{
|
|
|
|
|
t1000.syskeys &= ~andmask;
|
|
|
|
|
t1000.syskeys |= ormask;
|
|
|
|
|
t1000.syskeys ^= xormask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-19 04:19:19 +01:00
|
|
|
static void
|
2018-03-13 03:46:10 +01:00
|
|
|
t1000_configsys_load(void)
|
|
|
|
|
{
|
|
|
|
|
FILE *f;
|
|
|
|
|
|
2018-03-19 04:19:19 +01:00
|
|
|
memset(t1000.t1000_nvram, 0x1a, sizeof(t1000.t1000_nvram));
|
2018-03-13 03:46:10 +01:00
|
|
|
f = plat_fopen(nvr_path(L"t1000_config.nvr"), L"rb");
|
|
|
|
|
if (f != NULL) {
|
2018-03-19 04:19:19 +01:00
|
|
|
fread(t1000.t1000_nvram, sizeof(t1000.t1000_nvram), 1, f);
|
2018-03-13 03:46:10 +01:00
|
|
|
fclose(f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-19 04:19:19 +01:00
|
|
|
static void
|
2018-03-13 03:46:10 +01:00
|
|
|
t1000_configsys_save(void)
|
|
|
|
|
{
|
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
|
|
f = plat_fopen(nvr_path(L"t1000_config.nvr"), L"wb");
|
|
|
|
|
if (f != NULL) {
|
2018-03-19 04:19:19 +01:00
|
|
|
fwrite(t1000.t1000_nvram, sizeof(t1000.t1000_nvram), 1, f);
|
|
|
|
|
fclose(f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
t1200_state_load(void)
|
|
|
|
|
{
|
|
|
|
|
FILE *f;
|
|
|
|
|
|
2018-08-15 20:23:54 +02:00
|
|
|
memset(t1000.t1200_nvram, 0, sizeof(t1000.t1200_nvram));
|
2018-03-19 04:19:19 +01:00
|
|
|
f = plat_fopen(nvr_path(L"t1200_state.nvr"), L"rb");
|
|
|
|
|
if (f != NULL) {
|
|
|
|
|
fread(t1000.t1200_nvram, sizeof(t1000.t1200_nvram), 1, f);
|
|
|
|
|
fclose(f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
t1200_state_save(void)
|
|
|
|
|
{
|
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
|
|
f = plat_fopen(nvr_path(L"t1200_state.nvr"), L"wb");
|
|
|
|
|
if (f != NULL) {
|
|
|
|
|
fwrite(t1000.t1200_nvram, sizeof(t1000.t1200_nvram), 1, f);
|
2018-03-13 03:46:10 +01:00
|
|
|
fclose(f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* All RAM beyond 512k is non-volatile */
|
2018-03-19 04:19:19 +01:00
|
|
|
static void
|
2018-03-13 03:46:10 +01:00
|
|
|
t1000_emsboard_load(void)
|
|
|
|
|
{
|
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
|
|
if (mem_size > 512) {
|
|
|
|
|
f = plat_fopen(nvr_path(L"t1000_ems.nvr"), L"rb");
|
|
|
|
|
if (f != NULL) {
|
|
|
|
|
fread(&ram[512 * 1024], 1024, (mem_size - 512), f);
|
|
|
|
|
fclose(f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-03-19 04:19:19 +01:00
|
|
|
static void
|
2018-03-13 03:46:10 +01:00
|
|
|
t1000_emsboard_save(void)
|
|
|
|
|
{
|
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
|
|
if (mem_size > 512) {
|
|
|
|
|
f = plat_fopen(nvr_path(L"t1000_ems.nvr"), L"wb");
|
|
|
|
|
if (f != NULL) {
|
|
|
|
|
fwrite(&ram[512 * 1024], 1024, (mem_size - 512), f);
|
|
|
|
|
fclose(f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-19 04:19:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
t1000_nvr_load(void)
|
|
|
|
|
{
|
|
|
|
|
t1000_emsboard_load();
|
|
|
|
|
t1000_configsys_load();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
t1000_nvr_save(void)
|
|
|
|
|
{
|
|
|
|
|
t1000_emsboard_save();
|
|
|
|
|
t1000_configsys_save();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
t1200_nvr_load(void)
|
|
|
|
|
{
|
|
|
|
|
t1000_emsboard_load();
|
|
|
|
|
t1200_state_load();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
t1200_nvr_save(void)
|
|
|
|
|
{
|
|
|
|
|
t1000_emsboard_save();
|
|
|
|
|
t1200_state_save();
|
|
|
|
|
}
|