diff --git a/src/floppy/fdc.c b/src/floppy/fdc.c index 3f0a6ab71..45648c706 100644 --- a/src/floppy/fdc.c +++ b/src/floppy/fdc.c @@ -9,7 +9,7 @@ * Implementation of the NEC uPD-765 and compatible floppy disk * controller. * - * Version: @(#)fdc->c 1.0.16 2018/02/02 + * Version: @(#)fdc->c 1.0.17 2018/03/02 * * Authors: Sarah Walker, * Miran Grca, @@ -1140,7 +1140,7 @@ fdc_write(uint16_t addr, uint8_t val, void *priv) } -static uint8_t +uint8_t fdc_read(uint16_t addr, void *priv) { fdc_t *fdc = (fdc_t *) priv; diff --git a/src/floppy/fdc.h b/src/floppy/fdc.h index 7fa6cf3cd..4f59828b5 100644 --- a/src/floppy/fdc.h +++ b/src/floppy/fdc.h @@ -9,7 +9,7 @@ * Implementation of the NEC uPD-765 and compatible floppy disk * controller. * - * Version: @(#)fdc.h 1.0.4 2018/02/02 + * Version: @(#)fdc.h 1.0.5 2018/03/02 * * Authors: Sarah Walker, * Miran Grca, @@ -150,6 +150,7 @@ extern int fdc_data(fdc_t *fdc, uint8_t data); extern void fdc_sectorid(fdc_t *fdc, uint8_t track, uint8_t side, uint8_t sector, uint8_t size, uint8_t crc1, uint8_t crc2); +extern uint8_t fdc_read(uint16_t addr, void *priv); extern void fdc_reset(void *priv); extern uint8_t fdc_ps1_525(void); diff --git a/src/machine/m_xt_t1000.c b/src/machine/m_xt_t1000.c new file mode 100644 index 000000000..f6d6e846c --- /dev/null +++ b/src/machine/m_xt_t1000.c @@ -0,0 +1,674 @@ +#include +#include +#include +#include +#include +#include "../86box.h" +#include "../device.h" +#include "../io.h" +#include "../keyboard.h" +#include "../lpt.h" +#include "../mem.h" +#include "../nmi.h" +#include "../nvr.h" +#include "../nvr_tc8521.h" +#include "../pit.h" +#include "../plat.h" +#include "../rom.h" +#include "../cpu/cpu.h" +#include "../floppy/fdd.h" +#include "../floppy/fdc.h" +#include "../game/gameport.h" +#include "../video/vid_t1000.h" +#include "machine.h" + +/* 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]. + * + */ + +void common_init(); + +uint8_t config_sys[160]; + +static struct t1000_system +{ + /* ROM drive */ + uint8_t *romdrive; + uint8_t rom_ctl; + uint32_t rom_offset; + mem_mapping_t rom_mapping; + + /* 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; + + /* 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; +} t1000; + +#define T1000_ROMSIZE (512 * 1024) /* Maximum ROM drive size is 512k */ + +/* The CONFIG.SYS storage (160 bytes) and battery-backed EMS (all RAM + * above 512k) are implemented as NVR devices */ + +void t1000_configsys_loadnvr() +{ + FILE *f; + + memset(config_sys, 0x1A, sizeof(config_sys)); + f = plat_fopen(nvr_path(L"t1000_config.nvr"), L"rb"); + if (f) + { + fread(config_sys, sizeof(config_sys), 1, f); + fclose(f); + } +} + +/* All RAM beyond 512k is non-volatile */ +void t1000_emsboard_loadnvr() +{ + FILE *f; + + if (mem_size > 512) + { + f = plat_fopen(nvr_path(L"t1000_ems.nvr"), L"rb"); + if (f) + { + fread(&ram[512 * 1024], + 1024, (mem_size - 512), f); + fclose(f); + } + } +} + +void t1000_configsys_savenvr() +{ + FILE *f; + + f = plat_fopen(nvr_path(L"t1000_config.nvr"), L"wb"); + if (f) + { + fwrite(config_sys, sizeof(config_sys), 1, f); + fclose(f); + } +} + + +void t1000_emsboard_savenvr() +{ + FILE *f; + + if (mem_size > 512) + { + f = plat_fopen(nvr_path(L"t1000_ems.nvr"), L"wb"); + if (f) + { + fwrite(&ram[512 * 1024], + 1024, (mem_size - 512), f); + fclose(f); + } + } +} + + + +/* Given an EMS page ID, return its physical address in RAM. */ +uint32_t ems_execaddr(struct t1000_system *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; + +/* pclog("Select EMS page: %d of %d\n", val, sys->ems_pages); */ + 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) +{ + struct t1000_system *sys = (struct t1000_system *)priv; + +/* pclog("ems_in(%04x)=%02x\n", addr, sys->ems_reg[(addr >> 14) & 3]); */ + return sys->ems_reg[(addr >> 14) & 3]; +} + +static void ems_out(uint16_t addr, uint8_t val, void *priv) +{ + struct t1000_system *sys = (struct t1000_system *)priv; + int pg = (addr >> 14) & 3; + +/* pclog("ems_out(%04x, %02x) pg=%d\n", addr, val, pg); */ + 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(struct t1000_system *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; + } +/* pclog("EMS base set to %02x\n", val); */ + sys->ems_pages = 48 - 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(struct t1000_system *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(struct t1000_system *sys, uint8_t val) +{ + int n; + +/* pclog("ems_set_port(%d)", val & 0x0F); */ + if (sys->ems_port) + { + for (n = 0; n <= 0xC000; n += 0x4000) + { + io_removehandler(sys->ems_port + n, 0x01, + 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, 0x01, + ems_in, NULL, NULL, ems_out, NULL, NULL, sys); + } + sys->ems_port = 0; + } +/* pclog(" -> %04x\n", sys->ems_port); */ +} + +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) +{ + struct t1000_system *sys = (struct t1000_system *)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) +{ + struct t1000_system *sys = (struct t1000_system *)priv; + int pg = addr_to_page(addr); + + if (pg < 0) return 0xFF; + /* pclog("ems_read_ramw addr=%05x ", addr); */ + addr = sys->page_exec[pg] + (addr & 0x3FFF); + /* pclog("-> %06x val=%04x\n", addr, *(uint16_t *)&ram[addr]); */ + return *(uint16_t *)&ram[addr]; +} + + +static uint32_t ems_read_raml(uint32_t addr, void *priv) +{ + struct t1000_system *sys = (struct t1000_system *)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) +{ + struct t1000_system *sys = (struct t1000_system *)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) +{ + struct t1000_system *sys = (struct t1000_system *)priv; + int pg = addr_to_page(addr); + + if (pg < 0) return; + /* pclog("ems_write_ramw addr=%05x ", addr); */ + addr = sys->page_exec[pg] + (addr & 0x3FFF); + /* pclog("-> %06x val=%04x\n", addr, val); */ + + 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) +{ + struct t1000_system *sys = (struct t1000_system *)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; +} + + + + + +void t1000_syskey(uint8_t andmask, uint8_t ormask, uint8_t xormask) +{ + t1000.syskeys &= ~andmask; + t1000.syskeys |= ormask; + t1000.syskeys ^= xormask; +} + + + +static uint8_t read_t1000_ctl(uint16_t addr, void *priv) +{ + struct t1000_system *sys = (struct t1000_system *)priv; + + switch (addr & 0x0F) + { + case 1: return sys->syskeys; +/* Detect EMS board */ + case 0x0F: switch (sys->sys_ctl[0x0E]) + { + case 0x50: if (mem_size <= 512) + return 0xFF; + return 0x90 | sys->ems_port_index; +/* 0x60 is the page frame address: (0xD000 - 0xC400) / 0x20 */ + case 0x51: return sys->ems_base | 0x60; + case 0x52: return sys->is_640k ? 0x80 : 0; + } + return 0xFF; + default: return sys->sys_ctl[addr & 0x0F]; + } +} + +static void t1200_turbo_set(uint8_t value) +{ + if (value == t1000.turbo) + { + return; + } + t1000.turbo = value; + if (!value) + { + int c = cpu; + cpu = 0; /* 8088/4.77MHz */ + cpu_set(); + cpu = c; + } + else + { + cpu_set(); + } +} + +static void write_t1000_ctl(uint16_t addr, uint8_t val, void *priv) +{ + struct t1000_system *sys = (struct t1000_system *)priv; + + sys->sys_ctl[addr & 0x0F] = val; + switch (addr & 0x0F) + { + /* Video control */ + case 4: 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; + /* EMS control*/ + case 0x0F: + 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 read_t1000_nvram(uint16_t addr, void *priv) +{ + struct t1000_system *sys = (struct t1000_system *)priv; + uint8_t tmp; + + switch (addr) + { + case 0xC2: /* Read next byte from NVRAM */ + tmp = 0xFF; + if (sys->nvr_addr >= 0 && sys->nvr_addr < 160) + tmp = config_sys[sys->nvr_addr]; + ++sys->nvr_addr; + return tmp; + + case 0xC3: /* Read floppy changeline and NVRAM ready state */ + tmp = fdc_read(0x3F7, 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 */ + return tmp; + } + return 0xFF; +} + +static void write_t1000_nvram(uint16_t addr, uint8_t val, void *priv) +{ + struct t1000_system *sys = (struct t1000_system *)priv; + + switch (addr) + { +/* 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. */ + case 0xC0: sys->nvr_c0 = val; + break; +/* Write next byte to NVRAM */ + case 0xC1: if (sys->nvr_addr >= 0 && sys->nvr_addr < 160) + { + if (config_sys[sys->nvr_addr] != val) + nvr_dosave = 1; + config_sys[sys->nvr_addr] = val; + } + ++sys->nvr_addr; + break; + case 0xC2: break; +/* 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 */ + case 0xC3: sys->nvr_active = val; + if (val == 0x80) sys->nvr_addr = -1; + break; + } +} + +/* Port 0xC8 controls the ROM drive */ +static uint8_t read_t1000_rom_ctl(uint16_t addr, void *priv) +{ + struct t1000_system *sys = (struct t1000_system *)priv; + + return sys->rom_ctl; +} + +static void write_t1000_rom_ctl(uint16_t addr, uint8_t val, void *priv) +{ + struct t1000_system *sys = (struct t1000_system *)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) +{ + struct t1000_system *sys = (struct t1000_system *)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) +{ + struct t1000_system *sys = (struct t1000_system *)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) +{ + struct t1000_system *sys = (struct t1000_system *)priv; + + if (!sys->romdrive) return 0xFFFFFFFF; + return *(uint32_t *)(&sys->romdrive[sys->rom_offset + (addr & 0xFFFF)]); +} + + +device_t * +t1000_get_device(void) +{ + return &t1000_device; +} + + +void machine_xt_t1000_init(machine_t *model) +{ + FILE *f; + int pg; + + memset(&t1000, 0, sizeof(t1000)); + t1000.turbo = 0xff; + t1000.ems_port_index = 7; /* EMS disabled */ +/* 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) + { + 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, read_t1000_nvram, NULL, NULL, + write_t1000_nvram, NULL, NULL, &t1000); + /* ROM drive */ + io_sethandler(0xC8, 1, read_t1000_rom_ctl, NULL, NULL, + write_t1000_rom_ctl, NULL, NULL, &t1000); + /* System control functions, and add-on memory board */ + io_sethandler(0xE0, 0x10, read_t1000_ctl, NULL, NULL, + write_t1000_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(); + nvr_tc8521_init(); +/* No gameport, and no provision to fit one device_add(&gameport_device); */ +} + + +device_t * +t1200_get_device(void) +{ + return &t1200_device; +} + + +void machine_xt_t1200_init(machine_t *model) +{ + int pg; + + memset(&t1000, 0, sizeof(t1000)); + t1000.ems_port_index = 7; /* EMS disabled */ + + 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]); + } + + /* System control functions, and add-on memory board */ + io_sethandler(0xE0, 0x10, read_t1000_ctl, NULL, NULL, + write_t1000_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(); + nvr_tc8521_init(); +/* No gameport, and no provision to fit one device_add(&gameport_device); */ +} diff --git a/src/machine/m_xt_t1000.h b/src/machine/m_xt_t1000.h new file mode 100644 index 000000000..7c3e26f47 --- /dev/null +++ b/src/machine/m_xt_t1000.h @@ -0,0 +1,7 @@ +void t1000_syskey(uint8_t andmask, uint8_t ormask, uint8_t xormask); + +void t1000_configsys_loadnvr(); +void t1000_emsboard_loadnvr(); + +void t1000_configsys_savenvr(); +void t1000_emsboard_savenvr(); diff --git a/src/machine/machine.h b/src/machine/machine.h index b3493c633..68154743b 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -8,7 +8,7 @@ * * Handling of the emulated machines. * - * Version: @(#)machine.h 1.0.19 2018/02/09 + * Version: @(#)machine.h 1.0.20 2018/03/02 * * Authors: Sarah Walker, * Miran Grca, @@ -186,12 +186,18 @@ extern void machine_xt_compaq_init(machine_t *); extern void machine_xt_laserxt_init(machine_t *); #endif +extern void machine_xt_t1000_init(machine_t *); +extern void machine_xt_t1200_init(machine_t *); + #ifdef EMU_DEVICE_H extern device_t *pcjr_get_device(void); extern device_t *tandy1k_get_device(void); extern device_t *tandy1k_hx_get_device(void); +extern device_t *t1000_get_device(void); +extern device_t *t1200_get_device(void); + extern device_t *at_endeavor_get_device(void); #endif diff --git a/src/machine/machine_table.c b/src/machine/machine_table.c index 399150411..49e5d71bf 100644 --- a/src/machine/machine_table.c +++ b/src/machine/machine_table.c @@ -11,7 +11,7 @@ * NOTES: OpenAT wip for 286-class machine with open BIOS. * PS2_M80-486 wip, pending receipt of TRM's for machine. * - * Version: @(#)machine_table.c 1.0.19 2018/02/18 + * Version: @(#)machine_table.c 1.0.20 2018/03/02 * * Authors: Sarah Walker, * Miran Grca, @@ -47,6 +47,7 @@ machine_t machines[] = { { "[8088] Schneider EuroPC", ROM_EUROPC, "europc", {{"Siemens",cpus_europc}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA | MACHINE_HDC | MACHINE_VIDEO | MACHINE_MOUSE, 512, 640, 128, 0, machine_europc_init, NULL, NULL }, { "[8088] Tandy 1000", ROM_TANDY, "tandy", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA, 128, 640, 128, 0, machine_tandy1k_init, tandy1k_get_device, NULL }, { "[8088] Tandy 1000 HX", ROM_TANDY1000HX, "tandy1000hx", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA, 256, 640, 128, 0, machine_tandy1k_init, tandy1k_hx_get_device, NULL }, + { "[8088] Toshiba 1000", ROM_T1000, "t1000", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA, 512, 1280, 768, 0, machine_xt_t1000_init, t1000_get_device, NULL }, #if defined(DEV_BRANCH) && defined(USE_LASERXT) { "[8088] VTech Laser Turbo XT", ROM_LTXT, "ltxt", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA, 512, 512, 256, 0, machine_xt_laserxt_init, NULL, NULL }, #endif @@ -58,6 +59,7 @@ machine_t machines[] = { { "[8086] Amstrad PC20(0)", ROM_PC200, "pc200", {{"", cpus_8086}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA | MACHINE_VIDEO | MACHINE_MOUSE, 512, 640, 128, 63, machine_amstrad_init, NULL, nvr_at_close }, { "[8086] Olivetti M24", ROM_OLIM24, "olivetti_m24", {{"", cpus_8086}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA | MACHINE_VIDEO | MACHINE_MOUSE, 128, 640, 128, 0, machine_olim24_init, NULL, NULL }, { "[8086] Tandy 1000 SL/2", ROM_TANDY1000SL2, "tandy1000sl2", {{"", cpus_8086}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA, 512, 768, 128, 0, machine_tandy1k_init, NULL, NULL }, + { "[8086] Toshiba 1200", ROM_T1200, "t1200", {{"", cpus_8086}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA, 1024, 2048,1024, 0, machine_xt_t1200_init, t1200_get_device, NULL }, #if defined(DEV_BRANCH) && defined(USE_LASERXT) { "[8086] VTech Laser XT3", ROM_LXT3, "lxt3", {{"", cpus_8086}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA, 256, 512, 256, 0, machine_xt_laserxt_init, NULL, NULL }, #endif diff --git a/src/nvr_tc8521.c b/src/nvr_tc8521.c new file mode 100644 index 000000000..718221d84 --- /dev/null +++ b/src/nvr_tc8521.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include +#include "86box.h" +#include "mem.h" +#include "io.h" +#include "nvr.h" +#include "nvr_tc8521.h" +#include "rtc_tc8521.h" +#include "pic.h" +#include "pit.h" +#include "plat.h" +#include "rom.h" +#include "timer.h" +#include "config.h" +#include "nmi.h" +#include "machine/machine.h" + +int oldromset; +int nvrmask=63; +uint8_t nvrram[128]; +int nvraddr; + +int nvr_dosave = 0; + +static int64_t nvr_onesec_time = 0, nvr_onesec_cnt = 0; + +static void tc8521_onesec(void *p) +{ + nvr_onesec_cnt++; + if (nvr_onesec_cnt >= 100) + { + tc8521_tick(); + nvr_onesec_cnt = 0; + } + nvr_onesec_time += (int)(10000 * TIMER_USEC); +} + +void write_tc8521(uint16_t addr, uint8_t val, void *priv) +{ + uint8_t page = nvrram[0x0D] & 3; + + addr &= 0x0F; + if (addr < 0x0D) addr += 16 * page; + + if (addr >= 0x10 && nvrram[addr] != val) + nvr_dosave = 1; + + nvrram[addr] = val; +} + + +uint8_t read_tc8521(uint16_t addr, void *priv) +{ + uint8_t page = nvrram[0x0D] & 3; + + addr &= 0x0F; + if (addr < 0x0D) addr += 16 * page; + + return nvrram[addr]; +} + + +void tc8521_loadnvr() +{ + FILE *f; + + nvrmask=63; + oldromset=romset; + switch (romset) + { + case ROM_T1000: f = plat_fopen(nvr_path(L"t1000.nvr"), L"rb"); break; + case ROM_T1200: f = plat_fopen(nvr_path(L"t1200.nvr"), L"rb"); break; + default: return; + } + if (!f) + { + memset(nvrram,0xFF,64); + nvrram[0x0E] = 0; /* Test register */ + if (!enable_sync) + { + memset(nvrram, 0, 16); + } + return; + } + fread(nvrram,64,1,f); + if (enable_sync) + tc8521_internal_sync(nvrram); + else + tc8521_internal_set_nvrram(nvrram); /* Update the internal clock state based on the NVR registers. */ + fclose(f); +} + + +void tc8521_savenvr() +{ + FILE *f; + switch (oldromset) + { + case ROM_T1000: f = plat_fopen(nvr_path(L"t1000.nvr"), L"wb"); break; + case ROM_T1200: f = plat_fopen(nvr_path(L"t1200.nvr"), L"wb"); break; + default: return; + } + fwrite(nvrram,64,1,f); + fclose(f); +} + +void nvr_tc8521_init() +{ + io_sethandler(0x2C0, 0x10, read_tc8521, NULL, NULL, write_tc8521, NULL, NULL, NULL); + timer_add(tc8521_onesec, &nvr_onesec_time, TIMER_ALWAYS_ENABLED, NULL); +} diff --git a/src/nvr_tc8521.h b/src/nvr_tc8521.h new file mode 100644 index 000000000..c17342bd6 --- /dev/null +++ b/src/nvr_tc8521.h @@ -0,0 +1,12 @@ +void nvr_tc8521_init(); + +extern int enable_sync; + +extern int nvr_dosave; + +void tc8521_loadnvr(); +void tc8521_savenvr(); + +void tc8521_nvr_recalc(); + +FILE *nvrfopen(char *fn, char *mode); diff --git a/src/rom.c b/src/rom.c index 298f11af5..97bac6f83 100644 --- a/src/rom.c +++ b/src/rom.c @@ -13,7 +13,7 @@ * - c386sx16 BIOS fails checksum * - the loadfont() calls should be done elsewhere * - * Version: @(#)rom.c 1.0.30 2018/03/02 + * Version: @(#)rom.c 1.0.31 2018/03/02 * * Authors: Sarah Walker, * Miran Grca, @@ -828,6 +828,22 @@ rom_load_bios(int rom_id) return(1); #endif + case ROM_T1000: + loadfont(L"roms/machines/t1000/t1000font.bin", 2); + if (rom_load_linear( + L"roms/machines/t1000/t1000.rom", + 0x000000, 32768, 0, rom)) return(1); + memcpy(rom + 0x8000, rom, 0x8000); + break; + + case ROM_T1200: + loadfont(L"roms/machines/t1200/t1000font.bin", 2); + if (rom_load_linear( + L"roms/machines/t1200/t1200_019e.ic15.bin", + 0x000000, 32768, 0, rom)) return(1); + memcpy(rom + 0x8000, rom, 0x8000); + break; + case ROM_T3100E: loadfont(L"roms/machines/t3100e/t3100e_font.bin", 5); if (rom_load_linear( diff --git a/src/rom.h b/src/rom.h index 2069fdee9..1be17681c 100644 --- a/src/rom.h +++ b/src/rom.h @@ -8,7 +8,7 @@ * * Definitions for the ROM image handler. * - * Version: @(#)rom.h 1.0.13 2018/01/28 + * Version: @(#)rom.h 1.0.14 2018/03/02 * * Author: Fred N. van Kempen, * Copyright 2018 Fred N. van Kempen. @@ -52,6 +52,9 @@ enum { ROM_LXT3, #endif + ROM_T1000, + ROM_T1200, + ROM_IBMPCJR, ROM_TANDY, ROM_TANDY1000HX, @@ -63,6 +66,7 @@ enum { ROM_PC3086, ROM_OLIM24, ROM_TANDY1000SL2, + ROM_T3100E, ROM_AMI286, diff --git a/src/rtc_tc8521.c b/src/rtc_tc8521.c new file mode 100644 index 000000000..f3b64ba6a --- /dev/null +++ b/src/rtc_tc8521.c @@ -0,0 +1,221 @@ +/* Emulation of: + Toshiba TC8521 Real Time Clock */ + +#include +#include +#include +#include +#include +#include "nvr.h" +#include "rtc_tc8521.h" + +#define peek2(a) (nvrram[(a##1)] + 10 * nvrram[(a##10)]) + +struct +{ + int sec; + int min; + int hour; + int mday; + int mon; + int year; +} internal_clock; + +/* Table for days in each month */ +static int rtc_days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/* Called to determine whether the year is leap or not */ +static int rtc_is_leap(int org_year) +{ + if (org_year % 400 == 0) return 1; + if (org_year % 100 == 0) return 0; + if (org_year % 4 == 0) return 1; + return 0; +} + +/* Called to determine the days in the current month */ +static int rtc_get_days(int org_month, int org_year) +{ + if (org_month != 2) + return rtc_days_in_month[org_month]; + else + return rtc_is_leap(org_year) ? 29 : 28; +} + +/* Called when the internal clock gets updated */ +static void tc8521_recalc() +{ + if (internal_clock.sec == 60) + { + internal_clock.sec = 0; + internal_clock.min++; + } + if (internal_clock.min == 60) + { + internal_clock.min = 0; + internal_clock.hour++; + } + if (internal_clock.hour == 24) + { + internal_clock.hour = 0; + internal_clock.mday++; + } + if (internal_clock.mday == (rtc_get_days(internal_clock.mon, internal_clock.year) + 1)) + { + internal_clock.mday = 1; + internal_clock.mon++; + } + if (internal_clock.mon == 13) + { + internal_clock.mon = 1; + internal_clock.year++; + } +} + +/* Called when ticking the second */ +void tc8521_tick() +{ + internal_clock.sec++; + tc8521_recalc(); +} + +/* Called when modifying the NVR registers */ +void tc8521_update(uint8_t *nvrram, int reg) +{ + int temp; + + switch(reg) + { + case TC8521_SECOND1: + case TC8521_SECOND10: + internal_clock.sec = peek2(TC8521_SECOND); + break; + case TC8521_MINUTE1: + case TC8521_MINUTE10: + internal_clock.min = peek2(TC8521_MINUTE); + break; + case TC8521_HOUR1: + case TC8521_HOUR10: + temp = peek2(TC8521_HOUR); + if (nvrram[TC8521_24HR] & 1) + internal_clock.hour = temp; + else + internal_clock.hour = ((temp & ~0x20) % 12) + ((temp & 0x20) ? 12 : 0); + break; + case TC8521_DAY1: + case TC8521_DAY10: + internal_clock.mday = peek2(TC8521_DAY); + break; + case TC8521_MONTH1: + case TC8521_MONTH10: + internal_clock.mon = peek2(TC8521_MONTH); + break; + case TC8521_YEAR1: + case TC8521_YEAR10: + internal_clock.year = 1980 + peek2(TC8521_YEAR); + break; + } +} + +/* Called to obtain the current day of the week based on the internal clock */ +static int time_week_day() +{ + int day_of_month = internal_clock.mday; + int month2 = internal_clock.mon; + int year2 = internal_clock.year % 100; + int century = ((internal_clock.year - year2) / 100) % 4; + int sum = day_of_month + month2 + year2 + century; + /* (Sum mod 7) gives 0 for Saturday, we need it for Sunday, so +6 for Saturday to get 6 and Sunday 0 */ + int raw_wd = ((sum + 6) % 7); + return raw_wd; +} + +/* Called to get time into the internal clock */ +static void tc8521_internal_get(struct tm *time_var) +{ + time_var->tm_sec = internal_clock.sec; + time_var->tm_min = internal_clock.min; + time_var->tm_hour = internal_clock.hour; + time_var->tm_wday = time_week_day(); + time_var->tm_mday = internal_clock.mday; + time_var->tm_mon = internal_clock.mon - 1; + time_var->tm_year = internal_clock.year - 1900; +} + +static void tc8521_internal_set(struct tm *time_var) +{ + internal_clock.sec = time_var->tm_sec; + internal_clock.min = time_var->tm_min; + internal_clock.hour = time_var->tm_hour; + internal_clock.mday = time_var->tm_mday; + internal_clock.mon = time_var->tm_mon + 1; + internal_clock.year = time_var->tm_year + 1900; +} + +static void tc8521_set_nvrram(uint8_t *nvrram, struct tm *cur_time_tm) +{ + nvrram[TC8521_SECOND1] = cur_time_tm->tm_sec % 10; + nvrram[TC8521_SECOND10] = cur_time_tm->tm_sec / 10; + nvrram[TC8521_MINUTE1] = cur_time_tm->tm_min % 10; + nvrram[TC8521_MINUTE10] = cur_time_tm->tm_min / 10; + if (nvrram[TC8521_24HR] & 1) + { + nvrram[TC8521_HOUR1] = cur_time_tm->tm_hour % 10; + nvrram[TC8521_HOUR10] = cur_time_tm->tm_hour / 10; + } + else + { + nvrram[TC8521_HOUR1] = (cur_time_tm->tm_hour % 12) % 10; + nvrram[TC8521_HOUR10] = ((cur_time_tm->tm_hour % 12) / 10) + | (cur_time_tm->tm_hour >= 12) ? 2 : 0; + } + nvrram[TC8521_WEEKDAY] = cur_time_tm->tm_wday; + nvrram[TC8521_DAY1] = cur_time_tm->tm_mday % 10; + nvrram[TC8521_DAY10] = cur_time_tm->tm_mday / 10; + nvrram[TC8521_MONTH1] = (cur_time_tm->tm_mon + 1) / 10; + nvrram[TC8521_MONTH10] = (cur_time_tm->tm_mon + 1) % 10; + nvrram[TC8521_YEAR1] = (cur_time_tm->tm_year - 80) % 10; + nvrram[TC8521_YEAR10] = ((cur_time_tm->tm_year - 80) % 100) / 10; +} + + +void tc8521_internal_set_nvrram(uint8_t *nvrram) +{ + /* Load the entire internal clock state from the NVR. */ + internal_clock.sec = peek2(TC8521_SECOND); + internal_clock.min = peek2(TC8521_MINUTE); + if (nvrram[TC8521_24HR] & 1) + { + internal_clock.hour = peek2(TC8521_HOUR); + } + else + { + internal_clock.hour = (peek2(TC8521_HOUR) % 12) + + (nvrram[TC8521_HOUR10] & 2) ? 12 : 0; + } + internal_clock.mday = peek2(TC8521_DAY); + internal_clock.mon = peek2(TC8521_MONTH); + internal_clock.year = 1980 + peek2(TC8521_YEAR); +} + +void tc8521_internal_sync(uint8_t *nvrram) +{ + struct tm *cur_time_tm; + time_t cur_time; + + time(&cur_time); + cur_time_tm = localtime(&cur_time); + + tc8521_internal_set(cur_time_tm); + + tc8521_set_nvrram(nvrram, cur_time_tm); +} + +void tc8521_get(uint8_t *nvrram) +{ + struct tm cur_time_tm; + + tc8521_internal_get(&cur_time_tm); + + tc8521_set_nvrram(nvrram, &cur_time_tm); +} diff --git a/src/rtc_tc8521.h b/src/rtc_tc8521.h new file mode 100644 index 000000000..61c822af2 --- /dev/null +++ b/src/rtc_tc8521.h @@ -0,0 +1,33 @@ + +/* 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. */ +enum TC8521_ADDR +{ + /* Page 0 registers */ + TC8521_SECOND1, + 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 +}; + + +void tc8521_tick(); +void tc8521_update(uint8_t *nvrram, int reg); +void tc8521_get(uint8_t *nvrram); +void tc8521_internal_set_nvrram(uint8_t *nvrram); +void tc8521_internal_sync(uint8_t *nvrram); diff --git a/src/video/vid_t1000.c b/src/video/vid_t1000.c new file mode 100644 index 000000000..39aeb16f4 --- /dev/null +++ b/src/video/vid_t1000.c @@ -0,0 +1,721 @@ +/* Emulate the Toshiba 1000 plasma display. This display has a fixed 640x200 + * resolution. */ +#include +#include +#include +#include +#include + +#include "../86box.h" +#include "../device.h" +#include "../io.h" +#include "../mem.h" +#include "../timer.h" +#include "../cpu/cpu.h" +#include "video.h" +#include "vid_cga.h" +#include "vid_t1000.h" + +#define T1000_XSIZE 640 +#define T1000_YSIZE 200 + +/* Mapping of attributes to colours */ +static uint32_t blue, grey; +static uint8_t boldcols[256]; /* Which attributes use the bold font */ +static uint32_t blinkcols[256][2]; +static uint32_t normcols[256][2]; +static uint8_t language; + +/* Video options set by the motherboard; they will be picked up by the card + * on the next poll. + * + * Bit 1: Danish + * Bit 0: Thin font + */ +static uint8_t st_video_options; +static uint8_t st_display_internal = -1; + +void t1000_video_options_set(uint8_t options) +{ + st_video_options = options & 1; + st_video_options |= language; +} + +void t1000_display_set(uint8_t internal) +{ + st_display_internal = internal; +} + +uint8_t t1000_display_get() +{ + return st_display_internal; +} + + +typedef struct t1000_t +{ + mem_mapping_t mapping; + + cga_t cga; /* The CGA is used for the external + * display; most of its registers are + * ignored by the plasma display. */ + + int font; /* Current font, 0-3 */ + int enabled; /* Hardware enabled, 0 or 1 */ + int internal; /* Using internal display? */ + uint8_t attrmap; /* Attribute mapping register */ + + int dispontime, dispofftime; + + int linepos, displine; + int vc; + int dispon; + int vsynctime; + uint8_t video_options; + + uint8_t *vram; +} t1000_t; + + +static void t1000_recalctimings(t1000_t *t1000); +static void t1000_write(uint32_t addr, uint8_t val, void *p); +static uint8_t t1000_read(uint32_t addr, void *p); +static void t1000_recalcattrs(t1000_t *t1000); + + +static void t1000_out(uint16_t addr, uint8_t val, void *p) +{ + t1000_t *t1000 = (t1000_t *)p; + switch (addr) + { + /* Emulated CRTC, register select */ + case 0x3d0: case 0x3d2: case 0x3d4: case 0x3d6: + cga_out(addr, val, &t1000->cga); + break; + + /* Emulated CRTC, value */ + case 0x3d1: case 0x3d3: case 0x3d5: case 0x3d7: + /* Register 0x12 controls the attribute mappings for the + * LCD screen. */ + if (t1000->cga.crtcreg == 0x12) + { + t1000->attrmap = val; + t1000_recalcattrs(t1000); + return; + } + cga_out(addr, val, &t1000->cga); + + t1000_recalctimings(t1000); + return; + + /* CGA control register */ + case 0x3D8: + cga_out(addr, val, &t1000->cga); + return; + /* CGA colour register */ + case 0x3D9: + cga_out(addr, val, &t1000->cga); + return; + } +} + +static uint8_t t1000_in(uint16_t addr, void *p) +{ + t1000_t *t1000 = (t1000_t *)p; + uint8_t val; + + switch (addr) + { + case 0x3d1: case 0x3d3: case 0x3d5: case 0x3d7: + if (t1000->cga.crtcreg == 0x12) + { + val = t1000->attrmap & 0x0F; + if (t1000->internal) val |= 0x20; /* LCD / CRT */ + return val; + } + } + + return cga_in(addr, &t1000->cga); +} + + + + +static void t1000_write(uint32_t addr, uint8_t val, void *p) +{ + t1000_t *t1000 = (t1000_t *)p; + egawrites++; + +// pclog("CGA_WRITE %04X %02X\n", addr, val); + t1000->vram[addr & 0x3fff] = val; + cycles -= 4; +} + +static uint8_t t1000_read(uint32_t addr, void *p) +{ + t1000_t *t1000 = (t1000_t *)p; + egareads++; + cycles -= 4; + +// pclog("CGA_READ %04X\n", addr); + return t1000->vram[addr & 0x3fff]; +} + + + +static void t1000_recalctimings(t1000_t *t1000) +{ + double disptime; + double _dispontime, _dispofftime; + + if (!t1000->internal) + { + cga_recalctimings(&t1000->cga); + return; + } + disptime = 651; + _dispontime = 640; + _dispofftime = disptime - _dispontime; + t1000->dispontime = (int)(_dispontime * (1 << TIMER_SHIFT)); + t1000->dispofftime = (int)(_dispofftime * (1 << TIMER_SHIFT)); +} + +/* Draw a row of text in 80-column mode */ +static void t1000_text_row80(t1000_t *t1000) +{ + uint32_t cols[2]; + int x, c; + uint8_t chr, attr; + int drawcursor; + int cursorline; + int bold; + int blink; + uint16_t addr; + uint8_t sc; + uint16_t ma = (t1000->cga.crtc[13] | (t1000->cga.crtc[12] << 8)) & 0x3fff; + uint16_t ca = (t1000->cga.crtc[15] | (t1000->cga.crtc[14] << 8)) & 0x3fff; + + sc = (t1000->displine) & 7; + addr = ((ma & ~1) + (t1000->displine >> 3) * 80) * 2; + ma += (t1000->displine >> 3) * 80; + + if ((t1000->cga.crtc[10] & 0x60) == 0x20) + { + cursorline = 0; + } + else + { + cursorline = ((t1000->cga.crtc[10] & 0x0F) <= sc) && + ((t1000->cga.crtc[11] & 0x0F) >= sc); + } + for (x = 0; x < 80; x++) + { + chr = t1000->vram[(addr + 2 * x) & 0x3FFF]; + attr = t1000->vram[(addr + 2 * x + 1) & 0x3FFF]; + drawcursor = ((ma == ca) && cursorline && + (t1000->cga.cgamode & 8) && (t1000->cga.cgablink & 16)); + + blink = ((t1000->cga.cgablink & 16) && (t1000->cga.cgamode & 0x20) && + (attr & 0x80) && !drawcursor); + + if (t1000->video_options & 1) + bold = boldcols[attr] ? chr : chr + 256; + else + bold = boldcols[attr] ? chr + 256 : chr; + if (t1000->video_options & 2) + bold += 512; + + if (t1000->cga.cgamode & 0x20) /* Blink */ + { + cols[1] = blinkcols[attr][1]; + cols[0] = blinkcols[attr][0]; + if (blink) cols[1] = cols[0]; + } + else + { + cols[1] = normcols[attr][1]; + cols[0] = normcols[attr][0]; + } + if (drawcursor) + { + for (c = 0; c < 8; c++) + { + ((uint32_t *)buffer32->line[t1000->displine])[(x << 3) + c] = cols[(fontdat[bold][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ (blue ^ grey); + } + } + else + { + for (c = 0; c < 8; c++) + ((uint32_t *)buffer32->line[t1000->displine])[(x << 3) + c] = cols[(fontdat[bold][sc] & (1 << (c ^ 7))) ? 1 : 0]; + } + ++ma; + } +} + +/* Draw a row of text in 40-column mode */ +static void t1000_text_row40(t1000_t *t1000) +{ + uint32_t cols[2]; + int x, c; + uint8_t chr, attr; + int drawcursor; + int cursorline; + int bold; + int blink; + uint16_t addr; + uint8_t sc; + uint16_t ma = (t1000->cga.crtc[13] | (t1000->cga.crtc[12] << 8)) & 0x3fff; + uint16_t ca = (t1000->cga.crtc[15] | (t1000->cga.crtc[14] << 8)) & 0x3fff; + + sc = (t1000->displine) & 7; + addr = ((ma & ~1) + (t1000->displine >> 3) * 40) * 2; + ma += (t1000->displine >> 3) * 40; + + if ((t1000->cga.crtc[10] & 0x60) == 0x20) + { + cursorline = 0; + } + else + { + cursorline = ((t1000->cga.crtc[10] & 0x0F) <= sc) && + ((t1000->cga.crtc[11] & 0x0F) >= sc); + } + for (x = 0; x < 40; x++) + { + chr = t1000->vram[(addr + 2 * x) & 0x3FFF]; + attr = t1000->vram[(addr + 2 * x + 1) & 0x3FFF]; + drawcursor = ((ma == ca) && cursorline && + (t1000->cga.cgamode & 8) && (t1000->cga.cgablink & 16)); + + blink = ((t1000->cga.cgablink & 16) && (t1000->cga.cgamode & 0x20) && + (attr & 0x80) && !drawcursor); + + if (t1000->video_options & 1) + bold = boldcols[attr] ? chr : chr + 256; + else + bold = boldcols[attr] ? chr + 256 : chr; + if (t1000->video_options & 2) + bold += 512; + + if (t1000->cga.cgamode & 0x20) /* Blink */ + { + cols[1] = blinkcols[attr][1]; + cols[0] = blinkcols[attr][0]; + if (blink) cols[1] = cols[0]; + } + else + { + cols[1] = normcols[attr][1]; + cols[0] = normcols[attr][0]; + } + if (drawcursor) + { + for (c = 0; c < 8; c++) + { + ((uint32_t *)buffer32->line[t1000->displine])[(x << 4) + c*2] = + ((uint32_t *)buffer32->line[t1000->displine])[(x << 4) + c*2 + 1] = cols[(fontdat[bold][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ (blue ^ grey); + } + } + else + { + for (c = 0; c < 8; c++) + { + ((uint32_t *)buffer32->line[t1000->displine])[(x << 4) + c*2] = + ((uint32_t *)buffer32->line[t1000->displine])[(x << 4) + c*2+1] = cols[(fontdat[bold][sc] & (1 << (c ^ 7))) ? 1 : 0]; + } + } + ++ma; + } +} + +/* Draw a line in CGA 640x200 mode */ +static void t1000_cgaline6(t1000_t *t1000) +{ + int x, c; + uint8_t dat; + uint32_t ink = 0; + uint16_t addr; + uint32_t fg = (t1000->cga.cgacol & 0x0F) ? blue : grey; + uint32_t bg = grey; + + uint16_t ma = (t1000->cga.crtc[13] | (t1000->cga.crtc[12] << 8)) & 0x3fff; + + addr = ((t1000->displine) & 1) * 0x2000 + + (t1000->displine >> 1) * 80 + + ((ma & ~1) << 1); + + for (x = 0; x < 80; x++) + { + dat = t1000->vram[addr & 0x3FFF]; + addr++; + + for (c = 0; c < 8; c++) + { + ink = (dat & 0x80) ? fg : bg; + if (!(t1000->cga.cgamode & 8)) + ink = grey; + ((uint32_t *)buffer32->line[t1000->displine])[x*8+c] = ink; + dat = dat << 1; + } + } +} + +/* Draw a line in CGA 320x200 mode. Here the CGA colours are converted to + * dither patterns: colour 1 to 25% grey, colour 2 to 50% grey */ +static void t1000_cgaline4(t1000_t *t1000) +{ + int x, c; + uint8_t dat, pattern; + uint32_t ink0, ink1; + uint16_t addr; + + uint16_t ma = (t1000->cga.crtc[13] | (t1000->cga.crtc[12] << 8)) & 0x3fff; + addr = ((t1000->displine) & 1) * 0x2000 + + (t1000->displine >> 1) * 80 + + ((ma & ~1) << 1); + + for (x = 0; x < 80; x++) + { + dat = t1000->vram[addr & 0x3FFF]; + addr++; + + for (c = 0; c < 4; c++) + { + pattern = (dat & 0xC0) >> 6; + if (!(t1000->cga.cgamode & 8)) pattern = 0; + + switch (pattern & 3) + { + case 0: ink0 = ink1 = grey; break; + case 1: if (t1000->displine & 1) + { + ink0 = grey; ink1 = grey; + } + else + { + ink0 = blue; ink1 = grey; + } + break; + case 2: if (t1000->displine & 1) + { + ink0 = grey; ink1 = blue; + } + else + { + ink0 = blue; ink1 = grey; + } + break; + case 3: ink0 = ink1 = blue; break; + + } + ((uint32_t *)buffer32->line[t1000->displine])[x*8+2*c] = ink0; + ((uint32_t *)buffer32->line[t1000->displine])[x*8+2*c+1] = ink1; + dat = dat << 2; + } + } +} + +static void t1000_poll(void *p) +{ + t1000_t *t1000 = (t1000_t *)p; + + if (t1000->video_options != st_video_options) + { + t1000->video_options = st_video_options; + + /* Set the font used for the external display */ + t1000->cga.fontbase = ((t1000->video_options & 3) * 256); + } + /* Switch between internal plasma and external CRT display. */ + if (st_display_internal != -1 && st_display_internal != t1000->internal) + { + t1000->internal = st_display_internal; + t1000_recalctimings(t1000); + } + if (!t1000->internal) + { + cga_poll(&t1000->cga); + return; + } + + if (!t1000->linepos) + { + t1000->cga.vidtime += t1000->dispofftime; + t1000->cga.cgastat |= 1; + t1000->linepos = 1; + if (t1000->dispon) + { + if (t1000->displine == 0) + { + video_wait_for_buffer(); + } + + /* Graphics */ + if (t1000->cga.cgamode & 0x02) + { + if (t1000->cga.cgamode & 0x10) + t1000_cgaline6(t1000); + else t1000_cgaline4(t1000); + } + else + if (t1000->cga.cgamode & 0x01) /* High-res text */ + { + t1000_text_row80(t1000); + } + else + { + t1000_text_row40(t1000); + } + } + t1000->displine++; + /* Hardcode a fixed refresh rate and VSYNC timing */ + if (t1000->displine == 200) /* Start of VSYNC */ + { + t1000->cga.cgastat |= 8; + t1000->dispon = 0; + } + if (t1000->displine == 216) /* End of VSYNC */ + { + t1000->displine = 0; + t1000->cga.cgastat &= ~8; + t1000->dispon = 1; + } + } + else + { + if (t1000->dispon) + { + t1000->cga.cgastat &= ~1; + } + t1000->cga.vidtime += t1000->dispontime; + t1000->linepos = 0; + + if (t1000->displine == 200) + { + /* Hardcode 640x200 window size */ + if (T1000_XSIZE != xsize || T1000_YSIZE != ysize) + { + xsize = T1000_XSIZE; + ysize = T1000_YSIZE; + if (xsize < 64) xsize = 656; + if (ysize < 32) ysize = 200; + updatewindowsize(xsize, ysize); + } + video_blit_memtoscreen(0, 0, 0, ysize, xsize, ysize); + + frames++; + /* Fixed 640x200 resolution */ + video_res_x = T1000_XSIZE; + video_res_y = T1000_YSIZE; + + if (t1000->cga.cgamode & 0x02) + { + if (t1000->cga.cgamode & 0x10) + video_bpp = 1; + else video_bpp = 2; + + } + else video_bpp = 0; + t1000->cga.cgablink++; + } + } +} + +static void t1000_recalcattrs(t1000_t *t1000) +{ + int n; + + /* val behaves as follows: + * Bit 0: Attributes 01-06, 08-0E are inverse video + * Bit 1: Attributes 01-06, 08-0E are bold + * Bit 2: Attributes 11-16, 18-1F, 21-26, 28-2F ... F1-F6, F8-FF + * are inverse video + * Bit 3: Attributes 11-16, 18-1F, 21-26, 28-2F ... F1-F6, F8-FF + * are bold */ + + /* Set up colours */ + blue = makecol(0x2D, 0x39, 0x5A); + grey = makecol(0x85, 0xa0, 0xD6); + + /* Initialise the attribute mapping. Start by defaulting everything + * to grey on blue, and with bold set by bit 3 */ + for (n = 0; n < 256; n++) + { + boldcols[n] = (n & 8) != 0; + blinkcols[n][0] = normcols[n][0] = blue; + blinkcols[n][1] = normcols[n][1] = grey; + } + + /* Colours 0x11-0xFF are controlled by bits 2 and 3 of the + * passed value. Exclude x0 and x8, which are always grey on + * blue. */ + for (n = 0x11; n <= 0xFF; n++) + { + if ((n & 7) == 0) continue; + if (t1000->attrmap & 4) /* Inverse */ + { + blinkcols[n][0] = normcols[n][0] = blue; + blinkcols[n][1] = normcols[n][1] = grey; + } + else /* Normal */ + { + blinkcols[n][0] = normcols[n][0] = grey; + blinkcols[n][1] = normcols[n][1] = blue; + } + if (t1000->attrmap & 8) boldcols[n] = 1; /* Bold */ + } + /* Set up the 01-0E range, controlled by bits 0 and 1 of the + * passed value. When blinking is enabled this also affects 81-8E. */ + for (n = 0x01; n <= 0x0E; n++) + { + if (n == 7) continue; + if (t1000->attrmap & 1) + { + blinkcols[n][0] = normcols[n][0] = blue; + blinkcols[n][1] = normcols[n][1] = grey; + blinkcols[n+128][0] = blue; + blinkcols[n+128][1] = grey; + } + else + { + blinkcols[n][0] = normcols[n][0] = grey; + blinkcols[n][1] = normcols[n][1] = blue; + blinkcols[n+128][0] = grey; + blinkcols[n+128][1] = blue; + } + if (t1000->attrmap & 2) boldcols[n] = 1; + } + /* Colours 07 and 0F are always blue on grey. If blinking is + * enabled so are 87 and 8F. */ + for (n = 0x07; n <= 0x0F; n += 8) + { + blinkcols[n][0] = normcols[n][0] = grey; + blinkcols[n][1] = normcols[n][1] = blue; + blinkcols[n+128][0] = grey; + blinkcols[n+128][1] = blue; + } + /* When not blinking, colours 81-8F are always blue on grey. */ + for (n = 0x81; n <= 0x8F; n ++) + { + normcols[n][0] = grey; + normcols[n][1] = blue; + boldcols[n] = (n & 0x08) != 0; + } + + + /* Finally do the ones which are solid grey. These differ between + * the normal and blinking mappings */ + for (n = 0; n <= 0xFF; n += 0x11) + { + normcols[n][0] = normcols[n][1] = grey; + } + /* In the blinking range, 00 11 22 .. 77 and 80 91 A2 .. F7 are grey */ + for (n = 0; n <= 0x77; n += 0x11) + { + blinkcols[n][0] = blinkcols[n][1] = grey; + blinkcols[n+128][0] = blinkcols[n+128][1] = grey; + } +} + + +static void *t1000_init(device_t *info) +{ + t1000_t *t1000 = malloc(sizeof(t1000_t)); + memset(t1000, 0, sizeof(t1000_t)); + cga_init(&t1000->cga); + + t1000->internal = 1; + + /* 16k video RAM */ + t1000->vram = malloc(0x4000); + + timer_add(t1000_poll, &t1000->cga.vidtime, TIMER_ALWAYS_ENABLED, t1000); + + /* Occupy memory between 0xB8000 and 0xBFFFF */ + mem_mapping_add(&t1000->mapping, 0xb8000, 0x8000, t1000_read, NULL, NULL, t1000_write, NULL, NULL, NULL, 0, t1000); + /* Respond to CGA I/O ports */ + io_sethandler(0x03d0, 0x000c, t1000_in, NULL, NULL, t1000_out, NULL, NULL, t1000); + + /* Default attribute mapping is 4 */ + t1000->attrmap = 4; + t1000_recalcattrs(t1000); + + /* Start off in 80x25 text mode */ + t1000->cga.cgastat = 0xF4; + t1000->cga.vram = t1000->vram; + t1000->enabled = 1; + t1000->video_options = 0x01; + language = device_get_config_int("display_language") ? 2 : 0; + return t1000; +} + +static void t1000_close(void *p) +{ + t1000_t *t1000 = (t1000_t *)p; + + free(t1000->vram); + free(t1000); +} + +static void t1000_speed_changed(void *p) +{ + t1000_t *t1000 = (t1000_t *)p; + + t1000_recalctimings(t1000); +} + +static device_config_t t1000_config[] = +{ + { + .name = "display_language", + .description = "Language", + .type = CONFIG_SELECTION, + .selection = + { + { + .description = "USA", + .value = 0 + }, + { + .description = "Danish", + .value = 1 + } + }, + .default_int = 0 + }, + { + .type = -1 + } +}; + + +device_t t1000_device = +{ + "Toshiba T1000", + 0, + 0, + t1000_init, + t1000_close, + NULL, + NULL, + t1000_speed_changed, + NULL, + NULL, + t1000_config +}; + + +device_t t1200_device = +{ + "Toshiba T1200", + 0, + 0, + t1000_init, + t1000_close, + NULL, + NULL, + t1000_speed_changed, + NULL, + NULL, + t1000_config +}; diff --git a/src/video/vid_t1000.h b/src/video/vid_t1000.h new file mode 100644 index 000000000..bfd292a5f --- /dev/null +++ b/src/video/vid_t1000.h @@ -0,0 +1,5 @@ +extern device_t t1000_device; +extern device_t t1200_device; + +void t1000_video_options_set(uint8_t options); +void t1000_display_set(uint8_t internal); diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index c34c422d4..07a08fc50 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -8,7 +8,7 @@ # # Makefile for Win32 (MinGW32) environment. # -# Version: @(#)Makefile.mingw 1.0.104 2018/02/19 +# Version: @(#)Makefile.mingw 1.0.105 2018/03/02 # # Authors: Miran Grca, # Fred N. van Kempen, @@ -220,7 +220,11 @@ else endif endif endif -AFLAGS := -msse2 -mfpmath=sse +ifeq ($(W5580), y) + AFLAGS := -msse2 -msse3 -mssse3 -msse4 -msse4.1 -msse4.2 -mfpmath=sse +else + AFLAGS := -msse2 -mfpmath=sse +endif RFLAGS := --input-format=rc -O coff ifeq ($(RELEASE), y) OPTS += -DRELEASE_BUILD @@ -377,7 +381,8 @@ CFLAGS := $(CFLAGS) ######################################################################### MAINOBJ := pc.o config.o random.o timer.o io.o dma.o nmi.o pic.o \ pit.o ppi.o pci.o mca.o mcr.o mem.o memregs.o rom.o \ - device.o nvr.o nvr_at.o nvr_ps2.o $(VNCOBJ) $(RDPOBJ) + device.o nvr.o nvr_tc8521.o rtc_tc8521.o nvr_at.o \ + nvr_ps2.o $(VNCOBJ) $(RDPOBJ) INTELOBJ := intel.o \ intel_flash.o \ @@ -390,6 +395,7 @@ CPUOBJ := cpu.o cpu_table.o \ MCHOBJ := machine.o machine_table.o \ m_xt.o m_xt_compaq.o \ + m_xt_t1000.o \ m_pcjr.o \ m_amstrad.o \ m_europc.o m_europc_hdc.o \ @@ -486,7 +492,7 @@ VIDOBJ := video.o \ vid_hercules.o vid_herculesplus.o vid_incolor.o \ vid_colorplus.o \ vid_genius.o \ - vid_t3100e.o \ + vid_t1000.o vid_t3100e.o \ vid_wy700.o \ vid_ega.o vid_ega_render.o \ vid_svga.o vid_svga_render.o \