/* * 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. * * Version: @(#)m_xt_t1000.c 1.0.14 2019/11/15 * * Authors: Fred N. van Kempen, * Miran Grca, * Sarah Walker, * * Copyright 2018,2019 Fred N. van Kempen. * Copyright 2018,2019 Miran Grca. * Copyright 2018,2019 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. */ #include #include #include #include #include #include #include #define HAVE_STDARG_H #include "../86box.h" #include "../cpu/cpu.h" #include "../io.h" #include "../timer.h" #include "../pit.h" #include "../nmi.h" #include "../mem.h" #include "../rom.h" #include "../device.h" #include "../nvr.h" #include "../keyboard.h" #include "../lpt.h" #include "../mem.h" #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. */ uint8_t t1000_nvram[160]; uint8_t t1200_nvram[2048]; /* 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; mem_mapping_t nvr_mapping; /* T1200 NVRAM mapping */ /* 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; int is_t1200; } t1000_t; static t1000_t t1000; #ifdef ENABLE_T1000_LOG int t1000_do_log = ENABLE_T1000_LOG; static void t1000_log(const char *fmt, ...) { va_list ap; if (t1000_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else #define t1000_log(fmt, ...) #endif /* 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_wday = 1; /* Dummy value so it is not uninitialized. */ 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) { t1000_log("TC8521: ping\n"); } static void tc8521_start(nvr_t *nvr) { struct tm tm; /* Initialize the internal and chip times. */ if (time_sync & TIME_SYNC_ENABLED) { /* 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 t1000_log("Select EMS page: %d of %d\n", val, sys->ems_pages); #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 t1000_log("ems_in(%04x)=%02x\n", addr, sys->ems_reg[(addr >> 14) & 3]); #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 t1000_log("ems_out(%04x, %02x) pg=%d\n", addr, val, pg); #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 t1000_log("EMS base set to %02x\n", val); #endif sys->ems_pages = ((mem_size - 512) / 16) - 4 * sys->ems_base; 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 t1000_log("ems_set_port(%d)", val & 0x0f); #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 t1000_log(" -> %04x\n", sys->ems_port); #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 t1000_log("ems_read_ramw addr=%05x ", addr); #endif addr = sys->page_exec[pg] + (addr & 0x3FFF); #if 0 t1000_log("-> %06x val=%04x\n", addr, *(uint16_t *)&ram[addr]); #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 t1000_log("ems_write_ramw addr=%05x ", addr); #endif addr = sys->page_exec[pg] + (addr & 0x3fff); #if 0 t1000_log("-> %06x val=%04x\n", addr, val); #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) 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 (sys->is_t1200) t1200_turbo_set((val & 0x80) ? 1 : 0); } break; /* 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 (sys->is_t1200) { t1000_video_enable(val & 0x01 ? 0 : 1); } break; 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) tmp = sys->t1000_nvram[sys->nvr_addr]; 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) { if (sys->t1000_nvram[sys->nvr_addr] != val) nvr_dosave = 1; sys->t1000_nvram[sys->nvr_addr] = val; } 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; } } 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; } /* 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)])); } const device_t * t1000_get_device(void) { return(&t1000_video_device); } int machine_xt_t1000_init(const machine_t *model) { FILE *f; int pg; int ret; ret = bios_load_linear(L"roms/machines/t1000/t1000.rom", 0x000f8000, 32768, 0); if (bios_only || !ret) return ret; memset(&t1000, 0x00, sizeof(t1000)); t1000.is_t1200 = 0; 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); if (fread(t1000.romdrive, 1, T1000_ROMSIZE, f) != T1000_ROMSIZE) fatal("machine_xt_t1000_init(): Error reading DOS ROM data\n"); } 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_ctr_set_out_func(&pit->counters[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); t1000_nvr_load(); nvr_set_ven_save(t1000_nvr_save); if (gfxcard == VID_INTERNAL) device_add(&t1000_video_device); return ret; } const device_t * t1200_get_device(void) { return(&t1200_video_device); } int machine_xt_t1200_init(const machine_t *model) { int pg; int ret; ret = bios_load_linear(L"roms/machines/t1200/t1200_019e.ic15.bin", 0x000f8000, 32768, 0); if (bios_only || !ret) return ret; memset(&t1000, 0x00, sizeof(t1000)); t1000.is_t1200 = 1; 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); mem_mapping_add(&t1000.nvr_mapping, 0x000f0000, 2048, read_t1200_nvram, NULL, NULL, write_t1200_nvram, NULL, NULL, NULL, 0, &t1000); pit_ctr_set_out_func(&pit->counters[1], pit_refresh_timer_xt); device_add(&keyboard_xt_device); t1000.fdc = device_add(&fdc_xt_t1x00_device); nmi_init(); tc8521_init(&t1000.nvr, model->nvrmask + 1); t1200_nvr_load(); nvr_set_ven_save(t1200_nvr_save); if (gfxcard == VID_INTERNAL) device_add(&t1200_video_device); return ret; } void t1000_syskey(uint8_t andmask, uint8_t ormask, uint8_t xormask) { t1000.syskeys &= ~andmask; t1000.syskeys |= ormask; t1000.syskeys ^= xormask; } static void t1000_configsys_load(void) { FILE *f; int size; memset(t1000.t1000_nvram, 0x1a, sizeof(t1000.t1000_nvram)); f = plat_fopen(nvr_path(L"t1000_config.nvr"), L"rb"); if (f != NULL) { size = sizeof(t1000.t1000_nvram); if (fread(t1000.t1000_nvram, size, 1, f) != size) fatal("t1000_configsys_load(): Error reading data\n"); fclose(f); } } static void t1000_configsys_save(void) { FILE *f; int size; f = plat_fopen(nvr_path(L"t1000_config.nvr"), L"wb"); if (f != NULL) { size = sizeof(t1000.t1000_nvram); if (fwrite(t1000.t1000_nvram, 1, size, f) != size) fatal("t1000_configsys_save(): Error writing data\n"); fclose(f); } } static void t1200_state_load(void) { FILE *f; int size; memset(t1000.t1200_nvram, 0, sizeof(t1000.t1200_nvram)); f = plat_fopen(nvr_path(L"t1200_state.nvr"), L"rb"); if (f != NULL) { size = sizeof(t1000.t1200_nvram); if (fread(t1000.t1200_nvram, 1, size, f) != size) fatal("t1200_state_load(): Error reading data\n"); fclose(f); } } static void t1200_state_save(void) { FILE *f; int size; f = plat_fopen(nvr_path(L"t1200_state.nvr"), L"wb"); if (f != NULL) { size = sizeof(t1000.t1200_nvram); if (fwrite(t1000.t1200_nvram, 1, size, f) != size) fatal("t1200_state_save(): Error writing data\n"); fclose(f); } } /* All RAM beyond 512k is non-volatile */ static void 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); } } } static void 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); } } } 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(); }