From 2a27221ba4eab334dda299f95c67793c3f2f441b Mon Sep 17 00:00:00 2001 From: waltje Date: Sat, 6 Oct 2018 19:14:58 -0400 Subject: [PATCH] Updated README.md and did a re-sync. --- .gitignore | 46 +- README.md | 2 +- src/devices/system/dma.c | 1828 ++++++++++++------------- src/machines/m_amstrad.c | 2704 ++++++++++++++++++------------------- src/machines/m_xt_t1000.c | 2214 +++++++++++++++--------------- src/ui/lang/Extra-KZ.txt | Bin 3876 -> 3957 bytes 6 files changed, 3411 insertions(+), 3383 deletions(-) diff --git a/.gitignore b/.gitignore index b67dec2..4548a82 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,37 @@ -# Used by IntelliJ CLion -.idea -CMakeLists.txt - -#used by MSVC -**/*.aps -src/win/msvc15/.vs/ -src/win/msvc15/Debug/ -src/win/msvc15/Release/ +# Used by GIT. +.gitignore +# Used by Code::Blocks +src/win/codeblocks/* +# Used by MSVC. +src/win/msvc/.depends +src/win/msvc/vc*/.vs/ +src/win/msvc/vc*/Debug/ +src/win/msvc/vc*/Release/ +# Used by MinGW +src/win/mingw/.depends +# Used by the UNIX backends. +src/unix +src/unix/.depends* +src/unix/* +# Used by the WxWidgets backend. +src/wx +src/wx/* +# Used by the Qt backend. +src/qt +src/qt/* +# Standard folders. +roms +roms/* +vm +vm/* +# Local stuff, unsorted etc. +src/Makefile +src/waltje +src/waltje/* +# Other files. +src/*.obj +src/*.d +src/*.o +src/*.exe +src/*.res +src/*.map diff --git a/README.md b/README.md index f85df6a..d5f3c7b 100644 --- a/README.md +++ b/README.md @@ -53,4 +53,4 @@ BUILD STATUS ------------ The auto-builds handled by Travis-CI are [![Build Status](https://travis-ci.org/VARCem/VARCem.svg?branch=master)](https://travis-ci.org/VARCem/VARCem) -Last Updated: 5/07/2018 +Last Updated: 10/06/2018 diff --git a/src/devices/system/dma.c b/src/devices/system/dma.c index d6e6b46..cbcad74 100644 --- a/src/devices/system/dma.c +++ b/src/devices/system/dma.c @@ -1,915 +1,915 @@ -/* - * 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 Intel DMA controllers. - * - * Version: @(#)dma.c 1.0.6 2018/06/25 - * - * Authors: Fred N. van Kempen, - * Miran Grca, - * Sarah Walker, - * - * Copyright 2017,2018 Fred N. van Kempen. - * Copyright 2016-2018 Miran Grca. - * Copyright 2008-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. - */ -#include -#include -#include -#include -#include "../../emu.h" -#include "../../cpu/cpu.h" -#include "../../cpu/x86.h" -#include "../../machines/machine.h" -#include "../../mem.h" -#include "../../io.h" -#include "../../plat.h" -#include "mca.h" -#include "dma.h" - - -dma_t dma[8]; - - -static uint8_t dmaregs[16]; -static uint8_t dma16regs[16]; -static uint8_t dmapages[16]; -static int dma_wp, - dma16_wp; -static uint8_t dma_m; -static uint8_t dma_stat; -static uint8_t dma_stat_rq; -static uint8_t dma_command, - dma16_command; -static struct { - int xfr_command, - xfr_channel; - int byte_ptr; - - int is_ps2; -} dma_ps2; - - -#define DMA_PS2_IOA (1 << 0) -#define DMA_PS2_XFER_MEM_TO_IO (1 << 2) -#define DMA_PS2_XFER_IO_TO_MEM (3 << 2) -#define DMA_PS2_XFER_MASK (3 << 2) -#define DMA_PS2_DEC2 (1 << 4) -#define DMA_PS2_SIZE16 (1 << 6) - - -static uint8_t -_dma_read(int32_t addr) -{ - uint8_t temp = mem_readb_phys_dma(addr); - - return(temp); -} - - -static void -_dma_write(uint32_t addr, uint8_t val) -{ - mem_writeb_phys_dma(addr, val); - mem_invalidate_range(addr, addr); -} - - -static void -dma_ps2_run(int channel) -{ - dma_t *dma_c = &dma[channel]; - - switch (dma_c->ps2_mode & DMA_PS2_XFER_MASK) { - case DMA_PS2_XFER_MEM_TO_IO: - do { - if (! dma_c->size) { - uint8_t temp = _dma_read(dma_c->ac); - - outb(dma_c->io_addr, temp); - - if (dma_c->ps2_mode & DMA_PS2_DEC2) - dma_c->ac--; - else - dma_c->ac++; - } else { - uint16_t temp = _dma_read(dma_c->ac) | (_dma_read(dma_c->ac + 1) << 8); - - outw(dma_c->io_addr, temp); - - if (dma_c->ps2_mode & DMA_PS2_DEC2) - dma_c->ac -= 2; - else - dma_c->ac += 2; - } - - dma_stat_rq |= (1 << channel); - dma_c->cc--; - } while (dma_c->cc > 0); - - dma_stat |= (1 << channel); - break; - - case DMA_PS2_XFER_IO_TO_MEM: - do { - if (! dma_c->size) { - uint8_t temp = inb(dma_c->io_addr); - - _dma_write(dma_c->ac, temp); - - if (dma_c->ps2_mode & DMA_PS2_DEC2) - dma_c->ac--; - else - dma_c->ac++; - } else { - uint16_t temp = inw(dma_c->io_addr); - - _dma_write(dma_c->ac, temp & 0xff); - _dma_write(dma_c->ac + 1, temp >> 8); - - if (dma_c->ps2_mode & DMA_PS2_DEC2) - dma_c->ac -= 2; - else - dma_c->ac += 2; - } - - dma_stat_rq |= (1 << channel); - dma_c->cc--; - } while (dma_c->cc > 0); - - ps2_cache_clean(); - dma_stat |= (1 << channel); - break; - - default: /*Memory verify*/ - do { - if (! dma_c->size) { - if (dma_c->ps2_mode & DMA_PS2_DEC2) - dma_c->ac--; - else - dma_c->ac++; - } else { - if (dma_c->ps2_mode & DMA_PS2_DEC2) - dma_c->ac -= 2; - else - dma_c->ac += 2; - } - - dma_stat_rq |= (1 << channel); - dma->cc--; - } while (dma->cc > 0); - - dma_stat |= (1 << channel); - break; - - } -} - - -static uint8_t -dma_ps2_read(uint16_t addr, UNUSED(void *priv)) -{ - dma_t *dma_c = &dma[dma_ps2.xfr_channel]; - uint8_t temp = 0xff; - - switch (addr) { - case 0x1a: - switch (dma_ps2.xfr_command) { - case 2: /*Address*/ - case 3: - switch (dma_ps2.byte_ptr) { - case 0: - temp = dma_c->ac & 0xff; - dma_ps2.byte_ptr = 1; - break; - case 1: - temp = (dma_c->ac >> 8) & 0xff; - dma_ps2.byte_ptr = 2; - break; - case 2: - temp = (dma_c->ac >> 16) & 0xff; - dma_ps2.byte_ptr = 0; - break; - } - break; - - case 4: /*Count*/ - case 5: - if (dma_ps2.byte_ptr) - temp = dma_c->cc >> 8; - else - temp = dma_c->cc & 0xff; - dma_ps2.byte_ptr = (dma_ps2.byte_ptr + 1) & 1; - break; - - case 6: /*Read DMA status*/ - if (dma_ps2.byte_ptr) { - temp = ((dma_stat_rq & 0xf0) >> 4) | (dma_stat & 0xf0); - dma_stat &= ~0xf0; - dma_stat_rq &= ~0xf0; - } else { - temp = (dma_stat_rq & 0xf) | ((dma_stat & 0xf) << 4); - dma_stat &= ~0xf; - dma_stat_rq &= ~0xf; - } - dma_ps2.byte_ptr = (dma_ps2.byte_ptr + 1) & 1; - break; - - case 7: /*Mode*/ - temp = dma_c->ps2_mode; - break; - - case 8: /*Arbitration Level*/ - temp = dma_c->arb_level; - break; - - default: - fatal("Bad XFR Read command %i channel %i\n", dma_ps2.xfr_command, dma_ps2.xfr_channel); - } - break; - } - - return(temp); -} - - -static void -dma_ps2_write(uint16_t addr, uint8_t val, UNUSED(void *priv)) -{ - dma_t *dma_c = &dma[dma_ps2.xfr_channel]; - uint8_t mode; - - switch (addr) { - case 0x18: - dma_ps2.xfr_channel = val & 0x7; - dma_ps2.xfr_command = val >> 4; - dma_ps2.byte_ptr = 0; - switch (dma_ps2.xfr_command) { - case 9: /*Set DMA mask*/ - dma_m |= (1 << dma_ps2.xfr_channel); - break; - - case 0xa: /*Reset DMA mask*/ - dma_m &= ~(1 << dma_ps2.xfr_channel); - break; - - case 0xb: - if (!(dma_m & (1 << dma_ps2.xfr_channel))) - dma_ps2_run(dma_ps2.xfr_channel); - break; - } - break; - - case 0x1a: - switch (dma_ps2.xfr_command) { - case 0: /*I/O address*/ - if (dma_ps2.byte_ptr) - dma_c->io_addr = (dma_c->io_addr & 0x00ff) | (val << 8); - else - dma_c->io_addr = (dma_c->io_addr & 0xff00) | val; - dma_ps2.byte_ptr = (dma_ps2.byte_ptr + 1) & 1; - break; - - case 2: /*Address*/ - switch (dma_ps2.byte_ptr) { - case 0: - dma_c->ac = (dma_c->ac & 0xffff00) | val; - dma_ps2.byte_ptr = 1; - break; - - case 1: - dma_c->ac = (dma_c->ac & 0xff00ff) | (val << 8); - dma_ps2.byte_ptr = 2; - break; - - case 2: - dma_c->ac = (dma_c->ac & 0x00ffff) | (val << 16); - dma_ps2.byte_ptr = 0; - break; - } - dma_c->ab = dma_c->ac; - break; - - case 4: /*Count*/ - if (dma_ps2.byte_ptr) - dma_c->cc = (dma_c->cc & 0xff) | (val << 8); - else - dma_c->cc = (dma_c->cc & 0xff00) | val; - dma_ps2.byte_ptr = (dma_ps2.byte_ptr + 1) & 1; - dma_c->cb = dma_c->cc; - break; - - case 7: /*Mode register*/ - mode = 0; - if (val & DMA_PS2_DEC2) - mode |= 0x20; - if ((val & DMA_PS2_XFER_MASK) == DMA_PS2_XFER_MEM_TO_IO) - mode |= 8; - else if ((val & DMA_PS2_XFER_MASK) == DMA_PS2_XFER_IO_TO_MEM) - mode |= 4; - dma_c->mode = (dma_c->mode & ~0x2c) | mode; - dma_c->ps2_mode = val; - dma_c->size = val & DMA_PS2_SIZE16; - break; - - case 8: /*Arbitration Level*/ - dma_c->arb_level = val; - break; - - default: - fatal("Bad XFR command %i channel %i val %02x\n", dma_ps2.xfr_command, dma_ps2.xfr_channel, val); - } - break; - } -} - - -static uint8_t -dma_read(uint16_t addr, UNUSED(void *priv)) -{ - int channel = (addr >> 1) & 3; - uint8_t temp; - - switch (addr & 0xf) { - case 0: - case 2: - case 4: - case 6: /*Address registers*/ - dma_wp ^= 1; - if (dma_wp) - return(dma[channel].ac & 0xff); - return((dma[channel].ac >> 8) & 0xff); - - case 1: - case 3: - case 5: - case 7: /*Count registers*/ - dma_wp ^= 1; - if (dma_wp) - temp = dma[channel].cc & 0xff; - else - temp = dma[channel].cc >> 8; - return(temp); - - case 8: /*Status register*/ - temp = dma_stat & 0xf; - dma_stat &= ~0xf; - return(temp); - - case 0xd: - return(0); - } - - return(dmaregs[addr & 0xf]); -} - - -static void -dma_write(uint16_t addr, uint8_t val, UNUSED(void *priv)) -{ - int channel = (addr >> 1) & 3; - - dmaregs[addr & 0xf] = val; - switch (addr & 0xf) { - case 0: - case 2: - case 4: - case 6: /*Address registers*/ - dma_wp ^= 1; - if (dma_wp) - dma[channel].ab = (dma[channel].ab & 0xffff00) | val; - else - dma[channel].ab = (dma[channel].ab & 0xff00ff) | (val << 8); - dma[channel].ac = dma[channel].ab; - return; - - case 1: - case 3: - case 5: - case 7: /*Count registers*/ - dma_wp ^= 1; - if (dma_wp) - dma[channel].cb = (dma[channel].cb & 0xff00) | val; - else - dma[channel].cb = (dma[channel].cb & 0x00ff) | (val << 8); - dma[channel].cc = dma[channel].cb; - return; - - case 8: /*Control register*/ - dma_command = val; - return; - - case 0xa: /*Mask*/ - if (val & 4) - dma_m |= (1 << (val & 3)); - else - dma_m &= ~(1 << (val & 3)); - return; - - case 0xb: /*Mode*/ - channel = (val & 3); - dma[channel].mode = val; - if (dma_ps2.is_ps2) { - dma[channel].ps2_mode &= ~0x1c; - if (val & 0x20) - dma[channel].ps2_mode |= 0x10; - if ((val & 0xc) == 8) - dma[channel].ps2_mode |= 4; - else if ((val & 0xc) == 4) - dma[channel].ps2_mode |= 0xc; - } - return; - - case 0xc: /*Clear FF*/ - dma_wp = 0; - return; - - case 0xd: /*Master clear*/ - dma_wp = 0; - dma_m |= 0xf; - return; - - case 0xf: /*Mask write*/ - dma_m = (dma_m & 0xf0) | (val & 0xf); - return; - } -} - - -static uint8_t -dma16_read(uint16_t addr, UNUSED(void *priv)) -{ - int channel = ((addr >> 2) & 3) + 4; - uint8_t temp; - - addr >>= 1; - switch (addr & 0xf) { - case 0: - case 2: - case 4: - case 6: /*Address registers*/ - dma16_wp ^= 1; - if (dma_ps2.is_ps2) { - if (dma16_wp) - return(dma[channel].ac); - return((dma[channel].ac >> 8) & 0xff); - } - if (dma16_wp) - return((dma[channel].ac >> 1) & 0xff); - return((dma[channel].ac >> 9) & 0xff); - - case 1: - case 3: - case 5: - case 7: /*Count registers*/ - dma16_wp ^= 1; - if (dma16_wp) - temp = dma[channel].cc & 0xff; - else - temp = dma[channel].cc >> 8; - return(temp); - - case 8: /*Status register*/ - temp = dma_stat >> 4; - dma_stat &= ~0xf0; - return(temp); - } - - return(dma16regs[addr & 0xf]); -} - - -static void -dma16_write(uint16_t addr, uint8_t val, UNUSED(void *priv)) -{ - int channel = ((addr >> 2) & 3) + 4; - addr >>= 1; - - dma16regs[addr & 0xf] = val; - switch (addr & 0xf) { - case 0: - case 2: - case 4: - case 6: /*Address registers*/ - dma16_wp ^= 1; - if (dma_ps2.is_ps2) { - if (dma16_wp) - dma[channel].ab = (dma[channel].ab & 0xffff00) | val; - else - dma[channel].ab = (dma[channel].ab & 0xff00ff) | (val << 8); - } else { - if (dma16_wp) - dma[channel].ab = (dma[channel].ab & 0xfffe00) | (val << 1); - else - dma[channel].ab = (dma[channel].ab & 0xfe01ff) | (val << 9); - } - dma[channel].ac = dma[channel].ab; - return; - - case 1: - case 3: - case 5: - case 7: /*Count registers*/ - dma16_wp ^= 1; - if (dma16_wp) - dma[channel].cb = (dma[channel].cb & 0xff00) | val; - else - dma[channel].cb = (dma[channel].cb & 0x00ff) | (val << 8); - dma[channel].cc = dma[channel].cb; - return; - - case 8: /*Control register*/ - return; - - case 0xa: /*Mask*/ - if (val & 4) - dma_m |= (0x10 << (val & 3)); - else - dma_m &= ~(0x10 << (val & 3)); - return; - - case 0xb: /*Mode*/ - channel = (val & 3) + 4; - dma[channel].mode = val; - if (dma_ps2.is_ps2) { - dma[channel].ps2_mode &= ~0x1c; - if (val & 0x20) - dma[channel].ps2_mode |= 0x10; - if ((val & 0xc) == 8) - dma[channel].ps2_mode |= 4; - else if ((val & 0xc) == 4) - dma[channel].ps2_mode |= 0xc; - } - return; - - case 0xc: /*Clear FF*/ - dma16_wp = 0; - return; - - case 0xd: /*Master clear*/ - dma16_wp = 0; - dma_m |= 0xf0; - return; - - case 0xf: /*Mask write*/ - dma_m = (dma_m & 0x0f) | ((val & 0xf) << 4); - return; - } -} - - -static uint8_t -dma_page_read(uint16_t addr, UNUSED(void *priv)) -{ - return(dmapages[addr & 0xf]); -} - - -static void -dma_page_write(uint16_t addr, uint8_t val, UNUSED(void *priv)) -{ - dmapages[addr & 0xf] = val; - - switch (addr & 0xf) { - case 1: - dma[2].page = (AT) ? val : val & 0xf; - dma[2].ab = (dma[2].ab & 0xffff) | (dma[2].page << 16); - dma[2].ac = (dma[2].ac & 0xffff) | (dma[2].page << 16); - break; - - case 2: - dma[3].page = (AT) ? val : val & 0xf; - dma[3].ab = (dma[3].ab & 0xffff) | (dma[3].page << 16); - dma[3].ac = (dma[3].ac & 0xffff) | (dma[3].page << 16); - break; - - case 3: - dma[1].page = (AT) ? val : val & 0xf; - dma[1].ab = (dma[1].ab & 0xffff) | (dma[1].page << 16); - dma[1].ac = (dma[1].ac & 0xffff) | (dma[1].page << 16); - break; - - case 7: - dma[0].page = (AT) ? val : val & 0xf; - dma[0].ab = (dma[0].ab & 0xffff) | (dma[0].page << 16); - dma[0].ac = (dma[0].ac & 0xffff) | (dma[0].page << 16); - break; - - case 0x9: - dma[6].page = val & 0xfe; - dma[6].ab = (dma[6].ab & 0x1ffff) | (dma[6].page << 16); - dma[6].ac = (dma[6].ac & 0x1ffff) | (dma[6].page << 16); - break; - - case 0xa: - dma[7].page = val & 0xfe; - dma[7].ab = (dma[7].ab & 0x1ffff) | (dma[7].page << 16); - dma[7].ac = (dma[7].ac & 0x1ffff) | (dma[7].page << 16); - break; - - case 0xb: - dma[5].page = val & 0xfe; - dma[5].ab = (dma[5].ab & 0x1ffff) | (dma[5].page << 16); - dma[5].ac = (dma[5].ac & 0x1ffff) | (dma[5].page << 16); - break; - - case 0xf: - dma[4].page = val & 0xfe; - dma[4].ab = (dma[4].ab & 0x1ffff) | (dma[4].page << 16); - dma[4].ac = (dma[4].ac & 0x1ffff) | (dma[4].page << 16); - break; - } -} - - -void -dma_reset(void) -{ - int c; - - dma_wp = dma16_wp = 0; - dma_m = 0; - - for (c = 0; c < 16; c++) - dmaregs[c] = 0; - for (c = 0; c < 8; c++) { - dma[c].mode = 0; - dma[c].ac = 0; - dma[c].cc = 0; - dma[c].ab = 0; - dma[c].cb = 0; - dma[c].size = (c & 4) ? 1 : 0; - } -} - - -void -dma_init(void) -{ - io_sethandler(0x0000, 16, - dma_read,NULL,NULL, dma_write,NULL,NULL, NULL); - io_sethandler(0x0080, 8, - dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); - dma_ps2.is_ps2 = 0; -} - - -void -dma16_init(void) -{ - io_sethandler(0x00C0, 32, - dma16_read,NULL,NULL, dma16_write,NULL,NULL, NULL); - io_sethandler(0x0088, 8, - dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); -} - - -void -dma_alias_set(void) -{ - io_sethandler(0x0090, 16, - dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); -} - - -void -dma_alias_remove(void) -{ - io_removehandler(0x0090, 16, - dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); -} - - -void -dma_alias_remove_piix(void) -{ - io_removehandler(0x0090, 1, - dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); - io_removehandler(0x0094, 3, - dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); - io_removehandler(0x0098, 1, - dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); - io_removehandler(0x009C, 3, - dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); -} - - -void -ps2_dma_init(void) -{ - io_sethandler(0x0018, 1, - dma_ps2_read,NULL,NULL, dma_ps2_write,NULL,NULL, NULL); - io_sethandler(0x001a, 1, - dma_ps2_read,NULL,NULL, dma_ps2_write,NULL,NULL, NULL); - dma_ps2.is_ps2 = 1; -} - - -int -dma_channel_read(int channel) -{ - dma_t *dma_c = &dma[channel]; - uint16_t temp; - int tc = 0; - - if (channel < 4) { - if (dma_command & 0x04) - return(DMA_NODATA); - } else { - if (dma16_command & 0x04) - return(DMA_NODATA); - } - - if (! AT) - refreshread(); - - if (dma_m & (1 << channel)) - return(DMA_NODATA); - if ((dma_c->mode & 0xC) != 8) - return(DMA_NODATA); - - if (! dma_c->size) { - temp = _dma_read(dma_c->ac); - - if (dma_c->mode & 0x20) { - if (dma_ps2.is_ps2) - dma_c->ac--; - else - dma_c->ac = (dma_c->ac & 0xff0000) | ((dma_c->ac - 1) & 0xffff); - } else { - if (dma_ps2.is_ps2) - dma_c->ac++; - else - dma_c->ac = (dma_c->ac & 0xff0000) | ((dma_c->ac + 1) & 0xffff); - } - } else { - temp = _dma_read(dma_c->ac) | (_dma_read(dma_c->ac + 1) << 8); - - if (dma_c->mode & 0x20) { - if (dma_ps2.is_ps2) - dma_c->ac -= 2; - else - dma_c->ac = (dma_c->ac & 0xfe0000) | ((dma_c->ac - 2) & 0x1ffff); - } else { - if (dma_ps2.is_ps2) - dma_c->ac += 2; - else - dma_c->ac = (dma_c->ac & 0xfe0000) | ((dma_c->ac + 2) & 0x1ffff); - } - } - - dma_stat_rq |= (1 << channel); - - dma_c->cc--; - if (dma_c->cc < 0) { - tc = 1; - if (dma_c->mode & 0x10) { /*Auto-init*/ - dma_c->cc = dma_c->cb; - dma_c->ac = dma_c->ab; - } else - dma_m |= (1 << channel); - dma_stat |= (1 << channel); - } - - if (tc) - return(temp | DMA_OVER); - - return(temp); -} - - -int -dma_channel_write(int channel, uint16_t val) -{ - dma_t *dma_c = &dma[channel]; - - if (channel < 4) { - if (dma_command & 0x04) - return(DMA_NODATA); - } else { - if (dma16_command & 0x04) - return(DMA_NODATA); - } - - if (! AT) - refreshread(); - - if (dma_m & (1 << channel)) - return(DMA_NODATA); - if ((dma_c->mode & 0xC) != 4) - return(DMA_NODATA); - - if (! dma_c->size) { - _dma_write(dma_c->ac, val & 0xff); - - if (dma_c->mode & 0x20) { - if (dma_ps2.is_ps2) - dma_c->ac--; - else - dma_c->ac = (dma_c->ac & 0xff0000) | ((dma_c->ac - 1) & 0xffff); - } else { - if (dma_ps2.is_ps2) - dma_c->ac++; - else - dma_c->ac = (dma_c->ac & 0xff0000) | ((dma_c->ac + 1) & 0xffff); - } - } else { - _dma_write(dma_c->ac, val & 0xff); - _dma_write(dma_c->ac + 1, val >> 8); - - if (dma_c->mode & 0x20) { - if (dma_ps2.is_ps2) - dma_c->ac -= 2; - else - dma_c->ac = (dma_c->ac & 0xfe0000) | ((dma_c->ac - 2) & 0x1ffff); - dma_c->ac = (dma_c->ac & 0xfe0000) | ((dma_c->ac - 2) & 0x1ffff); - } else { - if (dma_ps2.is_ps2) - dma_c->ac += 2; - else - dma_c->ac = (dma_c->ac & 0xfe0000) | ((dma_c->ac + 2) & 0x1ffff); +/* + * 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 Intel DMA controllers. + * + * Version: @(#)dma.c 1.0.6 2018/06/25 + * + * Authors: Fred N. van Kempen, + * Miran Grca, + * Sarah Walker, + * + * Copyright 2017,2018 Fred N. van Kempen. + * Copyright 2016-2018 Miran Grca. + * Copyright 2008-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. + */ +#include +#include +#include +#include +#include "../../emu.h" +#include "../../cpu/cpu.h" +#include "../../cpu/x86.h" +#include "../../machines/machine.h" +#include "../../mem.h" +#include "../../io.h" +#include "../../plat.h" +#include "mca.h" +#include "dma.h" + + +dma_t dma[8]; + + +static uint8_t dmaregs[16]; +static uint8_t dma16regs[16]; +static uint8_t dmapages[16]; +static int dma_wp, + dma16_wp; +static uint8_t dma_m; +static uint8_t dma_stat; +static uint8_t dma_stat_rq; +static uint8_t dma_command, + dma16_command; +static struct { + int xfr_command, + xfr_channel; + int byte_ptr; + + int is_ps2; +} dma_ps2; + + +#define DMA_PS2_IOA (1 << 0) +#define DMA_PS2_XFER_MEM_TO_IO (1 << 2) +#define DMA_PS2_XFER_IO_TO_MEM (3 << 2) +#define DMA_PS2_XFER_MASK (3 << 2) +#define DMA_PS2_DEC2 (1 << 4) +#define DMA_PS2_SIZE16 (1 << 6) + + +static uint8_t +_dma_read(int32_t addr) +{ + uint8_t temp = mem_readb_phys_dma(addr); + + return(temp); +} + + +static void +_dma_write(uint32_t addr, uint8_t val) +{ + mem_writeb_phys_dma(addr, val); + mem_invalidate_range(addr, addr); +} + + +static void +dma_ps2_run(int channel) +{ + dma_t *dma_c = &dma[channel]; + + switch (dma_c->ps2_mode & DMA_PS2_XFER_MASK) { + case DMA_PS2_XFER_MEM_TO_IO: + do { + if (! dma_c->size) { + uint8_t temp = _dma_read(dma_c->ac); + + outb(dma_c->io_addr, temp); + + if (dma_c->ps2_mode & DMA_PS2_DEC2) + dma_c->ac--; + else + dma_c->ac++; + } else { + uint16_t temp = _dma_read(dma_c->ac) | (_dma_read(dma_c->ac + 1) << 8); + + outw(dma_c->io_addr, temp); + + if (dma_c->ps2_mode & DMA_PS2_DEC2) + dma_c->ac -= 2; + else + dma_c->ac += 2; + } + + dma_stat_rq |= (1 << channel); + dma_c->cc--; + } while (dma_c->cc > 0); + + dma_stat |= (1 << channel); + break; + + case DMA_PS2_XFER_IO_TO_MEM: + do { + if (! dma_c->size) { + uint8_t temp = inb(dma_c->io_addr); + + _dma_write(dma_c->ac, temp); + + if (dma_c->ps2_mode & DMA_PS2_DEC2) + dma_c->ac--; + else + dma_c->ac++; + } else { + uint16_t temp = inw(dma_c->io_addr); + + _dma_write(dma_c->ac, temp & 0xff); + _dma_write(dma_c->ac + 1, temp >> 8); + + if (dma_c->ps2_mode & DMA_PS2_DEC2) + dma_c->ac -= 2; + else + dma_c->ac += 2; + } + + dma_stat_rq |= (1 << channel); + dma_c->cc--; + } while (dma_c->cc > 0); + + ps2_cache_clean(); + dma_stat |= (1 << channel); + break; + + default: /*Memory verify*/ + do { + if (! dma_c->size) { + if (dma_c->ps2_mode & DMA_PS2_DEC2) + dma_c->ac--; + else + dma_c->ac++; + } else { + if (dma_c->ps2_mode & DMA_PS2_DEC2) + dma_c->ac -= 2; + else + dma_c->ac += 2; + } + + dma_stat_rq |= (1 << channel); + dma->cc--; + } while (dma->cc > 0); + + dma_stat |= (1 << channel); + break; + + } +} + + +static uint8_t +dma_ps2_read(uint16_t addr, UNUSED(void *priv)) +{ + dma_t *dma_c = &dma[dma_ps2.xfr_channel]; + uint8_t temp = 0xff; + + switch (addr) { + case 0x1a: + switch (dma_ps2.xfr_command) { + case 2: /*Address*/ + case 3: + switch (dma_ps2.byte_ptr) { + case 0: + temp = dma_c->ac & 0xff; + dma_ps2.byte_ptr = 1; + break; + case 1: + temp = (dma_c->ac >> 8) & 0xff; + dma_ps2.byte_ptr = 2; + break; + case 2: + temp = (dma_c->ac >> 16) & 0xff; + dma_ps2.byte_ptr = 0; + break; + } + break; + + case 4: /*Count*/ + case 5: + if (dma_ps2.byte_ptr) + temp = dma_c->cc >> 8; + else + temp = dma_c->cc & 0xff; + dma_ps2.byte_ptr = (dma_ps2.byte_ptr + 1) & 1; + break; + + case 6: /*Read DMA status*/ + if (dma_ps2.byte_ptr) { + temp = ((dma_stat_rq & 0xf0) >> 4) | (dma_stat & 0xf0); + dma_stat &= ~0xf0; + dma_stat_rq &= ~0xf0; + } else { + temp = (dma_stat_rq & 0xf) | ((dma_stat & 0xf) << 4); + dma_stat &= ~0xf; + dma_stat_rq &= ~0xf; + } + dma_ps2.byte_ptr = (dma_ps2.byte_ptr + 1) & 1; + break; + + case 7: /*Mode*/ + temp = dma_c->ps2_mode; + break; + + case 8: /*Arbitration Level*/ + temp = dma_c->arb_level; + break; + + default: + fatal("Bad XFR Read command %i channel %i\n", dma_ps2.xfr_command, dma_ps2.xfr_channel); + } + break; + } + + return(temp); +} + + +static void +dma_ps2_write(uint16_t addr, uint8_t val, UNUSED(void *priv)) +{ + dma_t *dma_c = &dma[dma_ps2.xfr_channel]; + uint8_t mode; + + switch (addr) { + case 0x18: + dma_ps2.xfr_channel = val & 0x7; + dma_ps2.xfr_command = val >> 4; + dma_ps2.byte_ptr = 0; + switch (dma_ps2.xfr_command) { + case 9: /*Set DMA mask*/ + dma_m |= (1 << dma_ps2.xfr_channel); + break; + + case 0xa: /*Reset DMA mask*/ + dma_m &= ~(1 << dma_ps2.xfr_channel); + break; + + case 0xb: + if (!(dma_m & (1 << dma_ps2.xfr_channel))) + dma_ps2_run(dma_ps2.xfr_channel); + break; + } + break; + + case 0x1a: + switch (dma_ps2.xfr_command) { + case 0: /*I/O address*/ + if (dma_ps2.byte_ptr) + dma_c->io_addr = (dma_c->io_addr & 0x00ff) | (val << 8); + else + dma_c->io_addr = (dma_c->io_addr & 0xff00) | val; + dma_ps2.byte_ptr = (dma_ps2.byte_ptr + 1) & 1; + break; + + case 2: /*Address*/ + switch (dma_ps2.byte_ptr) { + case 0: + dma_c->ac = (dma_c->ac & 0xffff00) | val; + dma_ps2.byte_ptr = 1; + break; + + case 1: + dma_c->ac = (dma_c->ac & 0xff00ff) | (val << 8); + dma_ps2.byte_ptr = 2; + break; + + case 2: + dma_c->ac = (dma_c->ac & 0x00ffff) | (val << 16); + dma_ps2.byte_ptr = 0; + break; + } + dma_c->ab = dma_c->ac; + break; + + case 4: /*Count*/ + if (dma_ps2.byte_ptr) + dma_c->cc = (dma_c->cc & 0xff) | (val << 8); + else + dma_c->cc = (dma_c->cc & 0xff00) | val; + dma_ps2.byte_ptr = (dma_ps2.byte_ptr + 1) & 1; + dma_c->cb = dma_c->cc; + break; + + case 7: /*Mode register*/ + mode = 0; + if (val & DMA_PS2_DEC2) + mode |= 0x20; + if ((val & DMA_PS2_XFER_MASK) == DMA_PS2_XFER_MEM_TO_IO) + mode |= 8; + else if ((val & DMA_PS2_XFER_MASK) == DMA_PS2_XFER_IO_TO_MEM) + mode |= 4; + dma_c->mode = (dma_c->mode & ~0x2c) | mode; + dma_c->ps2_mode = val; + dma_c->size = val & DMA_PS2_SIZE16; + break; + + case 8: /*Arbitration Level*/ + dma_c->arb_level = val; + break; + + default: + fatal("Bad XFR command %i channel %i val %02x\n", dma_ps2.xfr_command, dma_ps2.xfr_channel, val); + } + break; + } +} + + +static uint8_t +dma_read(uint16_t addr, UNUSED(void *priv)) +{ + int channel = (addr >> 1) & 3; + uint8_t temp; + + switch (addr & 0xf) { + case 0: + case 2: + case 4: + case 6: /*Address registers*/ + dma_wp ^= 1; + if (dma_wp) + return(dma[channel].ac & 0xff); + return((dma[channel].ac >> 8) & 0xff); + + case 1: + case 3: + case 5: + case 7: /*Count registers*/ + dma_wp ^= 1; + if (dma_wp) + temp = dma[channel].cc & 0xff; + else + temp = dma[channel].cc >> 8; + return(temp); + + case 8: /*Status register*/ + temp = dma_stat & 0xf; + dma_stat &= ~0xf; + return(temp); + + case 0xd: + return(0); + } + + return(dmaregs[addr & 0xf]); +} + + +static void +dma_write(uint16_t addr, uint8_t val, UNUSED(void *priv)) +{ + int channel = (addr >> 1) & 3; + + dmaregs[addr & 0xf] = val; + switch (addr & 0xf) { + case 0: + case 2: + case 4: + case 6: /*Address registers*/ + dma_wp ^= 1; + if (dma_wp) + dma[channel].ab = (dma[channel].ab & 0xffff00) | val; + else + dma[channel].ab = (dma[channel].ab & 0xff00ff) | (val << 8); + dma[channel].ac = dma[channel].ab; + return; + + case 1: + case 3: + case 5: + case 7: /*Count registers*/ + dma_wp ^= 1; + if (dma_wp) + dma[channel].cb = (dma[channel].cb & 0xff00) | val; + else + dma[channel].cb = (dma[channel].cb & 0x00ff) | (val << 8); + dma[channel].cc = dma[channel].cb; + return; + + case 8: /*Control register*/ + dma_command = val; + return; + + case 0xa: /*Mask*/ + if (val & 4) + dma_m |= (1 << (val & 3)); + else + dma_m &= ~(1 << (val & 3)); + return; + + case 0xb: /*Mode*/ + channel = (val & 3); + dma[channel].mode = val; + if (dma_ps2.is_ps2) { + dma[channel].ps2_mode &= ~0x1c; + if (val & 0x20) + dma[channel].ps2_mode |= 0x10; + if ((val & 0xc) == 8) + dma[channel].ps2_mode |= 4; + else if ((val & 0xc) == 4) + dma[channel].ps2_mode |= 0xc; + } + return; + + case 0xc: /*Clear FF*/ + dma_wp = 0; + return; + + case 0xd: /*Master clear*/ + dma_wp = 0; + dma_m |= 0xf; + return; + + case 0xf: /*Mask write*/ + dma_m = (dma_m & 0xf0) | (val & 0xf); + return; + } +} + + +static uint8_t +dma16_read(uint16_t addr, UNUSED(void *priv)) +{ + int channel = ((addr >> 2) & 3) + 4; + uint8_t temp; + + addr >>= 1; + switch (addr & 0xf) { + case 0: + case 2: + case 4: + case 6: /*Address registers*/ + dma16_wp ^= 1; + if (dma_ps2.is_ps2) { + if (dma16_wp) + return(dma[channel].ac); + return((dma[channel].ac >> 8) & 0xff); + } + if (dma16_wp) + return((dma[channel].ac >> 1) & 0xff); + return((dma[channel].ac >> 9) & 0xff); + + case 1: + case 3: + case 5: + case 7: /*Count registers*/ + dma16_wp ^= 1; + if (dma16_wp) + temp = dma[channel].cc & 0xff; + else + temp = dma[channel].cc >> 8; + return(temp); + + case 8: /*Status register*/ + temp = dma_stat >> 4; + dma_stat &= ~0xf0; + return(temp); + } + + return(dma16regs[addr & 0xf]); +} + + +static void +dma16_write(uint16_t addr, uint8_t val, UNUSED(void *priv)) +{ + int channel = ((addr >> 2) & 3) + 4; + addr >>= 1; + + dma16regs[addr & 0xf] = val; + switch (addr & 0xf) { + case 0: + case 2: + case 4: + case 6: /*Address registers*/ + dma16_wp ^= 1; + if (dma_ps2.is_ps2) { + if (dma16_wp) + dma[channel].ab = (dma[channel].ab & 0xffff00) | val; + else + dma[channel].ab = (dma[channel].ab & 0xff00ff) | (val << 8); + } else { + if (dma16_wp) + dma[channel].ab = (dma[channel].ab & 0xfffe00) | (val << 1); + else + dma[channel].ab = (dma[channel].ab & 0xfe01ff) | (val << 9); + } + dma[channel].ac = dma[channel].ab; + return; + + case 1: + case 3: + case 5: + case 7: /*Count registers*/ + dma16_wp ^= 1; + if (dma16_wp) + dma[channel].cb = (dma[channel].cb & 0xff00) | val; + else + dma[channel].cb = (dma[channel].cb & 0x00ff) | (val << 8); + dma[channel].cc = dma[channel].cb; + return; + + case 8: /*Control register*/ + return; + + case 0xa: /*Mask*/ + if (val & 4) + dma_m |= (0x10 << (val & 3)); + else + dma_m &= ~(0x10 << (val & 3)); + return; + + case 0xb: /*Mode*/ + channel = (val & 3) + 4; + dma[channel].mode = val; + if (dma_ps2.is_ps2) { + dma[channel].ps2_mode &= ~0x1c; + if (val & 0x20) + dma[channel].ps2_mode |= 0x10; + if ((val & 0xc) == 8) + dma[channel].ps2_mode |= 4; + else if ((val & 0xc) == 4) + dma[channel].ps2_mode |= 0xc; + } + return; + + case 0xc: /*Clear FF*/ + dma16_wp = 0; + return; + + case 0xd: /*Master clear*/ + dma16_wp = 0; + dma_m |= 0xf0; + return; + + case 0xf: /*Mask write*/ + dma_m = (dma_m & 0x0f) | ((val & 0xf) << 4); + return; + } +} + + +static uint8_t +dma_page_read(uint16_t addr, UNUSED(void *priv)) +{ + return(dmapages[addr & 0xf]); +} + + +static void +dma_page_write(uint16_t addr, uint8_t val, UNUSED(void *priv)) +{ + dmapages[addr & 0xf] = val; + + switch (addr & 0xf) { + case 1: + dma[2].page = (AT) ? val : val & 0xf; + dma[2].ab = (dma[2].ab & 0xffff) | (dma[2].page << 16); + dma[2].ac = (dma[2].ac & 0xffff) | (dma[2].page << 16); + break; + + case 2: + dma[3].page = (AT) ? val : val & 0xf; + dma[3].ab = (dma[3].ab & 0xffff) | (dma[3].page << 16); + dma[3].ac = (dma[3].ac & 0xffff) | (dma[3].page << 16); + break; + + case 3: + dma[1].page = (AT) ? val : val & 0xf; + dma[1].ab = (dma[1].ab & 0xffff) | (dma[1].page << 16); + dma[1].ac = (dma[1].ac & 0xffff) | (dma[1].page << 16); + break; + + case 7: + dma[0].page = (AT) ? val : val & 0xf; + dma[0].ab = (dma[0].ab & 0xffff) | (dma[0].page << 16); + dma[0].ac = (dma[0].ac & 0xffff) | (dma[0].page << 16); + break; + + case 0x9: + dma[6].page = val & 0xfe; + dma[6].ab = (dma[6].ab & 0x1ffff) | (dma[6].page << 16); + dma[6].ac = (dma[6].ac & 0x1ffff) | (dma[6].page << 16); + break; + + case 0xa: + dma[7].page = val & 0xfe; + dma[7].ab = (dma[7].ab & 0x1ffff) | (dma[7].page << 16); + dma[7].ac = (dma[7].ac & 0x1ffff) | (dma[7].page << 16); + break; + + case 0xb: + dma[5].page = val & 0xfe; + dma[5].ab = (dma[5].ab & 0x1ffff) | (dma[5].page << 16); + dma[5].ac = (dma[5].ac & 0x1ffff) | (dma[5].page << 16); + break; + + case 0xf: + dma[4].page = val & 0xfe; + dma[4].ab = (dma[4].ab & 0x1ffff) | (dma[4].page << 16); + dma[4].ac = (dma[4].ac & 0x1ffff) | (dma[4].page << 16); + break; + } +} + + +void +dma_reset(void) +{ + int c; + + dma_wp = dma16_wp = 0; + dma_m = 0; + + for (c = 0; c < 16; c++) + dmaregs[c] = 0; + for (c = 0; c < 8; c++) { + dma[c].mode = 0; + dma[c].ac = 0; + dma[c].cc = 0; + dma[c].ab = 0; + dma[c].cb = 0; + dma[c].size = (c & 4) ? 1 : 0; + } +} + + +void +dma_init(void) +{ + io_sethandler(0x0000, 16, + dma_read,NULL,NULL, dma_write,NULL,NULL, NULL); + io_sethandler(0x0080, 8, + dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); + dma_ps2.is_ps2 = 0; +} + + +void +dma16_init(void) +{ + io_sethandler(0x00C0, 32, + dma16_read,NULL,NULL, dma16_write,NULL,NULL, NULL); + io_sethandler(0x0088, 8, + dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); +} + + +void +dma_alias_set(void) +{ + io_sethandler(0x0090, 16, + dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); +} + + +void +dma_alias_remove(void) +{ + io_removehandler(0x0090, 16, + dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); +} + + +void +dma_alias_remove_piix(void) +{ + io_removehandler(0x0090, 1, + dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); + io_removehandler(0x0094, 3, + dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); + io_removehandler(0x0098, 1, + dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); + io_removehandler(0x009C, 3, + dma_page_read,NULL,NULL, dma_page_write,NULL,NULL, NULL); +} + + +void +ps2_dma_init(void) +{ + io_sethandler(0x0018, 1, + dma_ps2_read,NULL,NULL, dma_ps2_write,NULL,NULL, NULL); + io_sethandler(0x001a, 1, + dma_ps2_read,NULL,NULL, dma_ps2_write,NULL,NULL, NULL); + dma_ps2.is_ps2 = 1; +} + + +int +dma_channel_read(int channel) +{ + dma_t *dma_c = &dma[channel]; + uint16_t temp; + int tc = 0; + + if (channel < 4) { + if (dma_command & 0x04) + return(DMA_NODATA); + } else { + if (dma16_command & 0x04) + return(DMA_NODATA); + } + + if (! AT) + refreshread(); + + if (dma_m & (1 << channel)) + return(DMA_NODATA); + if ((dma_c->mode & 0xC) != 8) + return(DMA_NODATA); + + if (! dma_c->size) { + temp = _dma_read(dma_c->ac); + + if (dma_c->mode & 0x20) { + if (dma_ps2.is_ps2) + dma_c->ac--; + else + dma_c->ac = (dma_c->ac & 0xff0000) | ((dma_c->ac - 1) & 0xffff); + } else { + if (dma_ps2.is_ps2) + dma_c->ac++; + else + dma_c->ac = (dma_c->ac & 0xff0000) | ((dma_c->ac + 1) & 0xffff); } - } - - dma_stat_rq |= (1 << channel); - - dma_c->cc--; - if (dma_c->cc < 0) { - if (dma_c->mode & 0x10) { /*Auto-init*/ - dma_c->cc = dma_c->cb; - dma_c->ac = dma_c->ab; - } else - dma_m |= (1 << channel); - dma_stat |= (1 << channel); - } - - if (dma_m & (1 << channel)) - return(DMA_OVER); - - return(0); -} - - -int -dma_mode(int channel) -{ - if (channel < 4) - return(dma[channel].mode); - else - return(dma[channel & 3].mode); -} - - -/* DMA Bus Master Page Read/Write */ -void -DMAPageRead(uint32_t PhysAddress, uint8_t *DataRead, uint32_t TotalSize) -{ - uint32_t i = 0; - -#if 0 - memcpy(DataRead, &ram[PhysAddress], TotalSize); -#else - for (i = 0; i < TotalSize; i++) - DataRead[i] = mem_readb_phys_dma(PhysAddress + i); -#endif -} - - -void -DMAPageWrite(uint32_t PhysAddress, const uint8_t *DataWrite, uint32_t TotalSize) -{ - uint32_t i = 0; - -#if 0 - mem_invalidate_range(PhysAddress, PhysAddress + TotalSize - 1); - memcpy(&ram[PhysAddress], DataWrite, TotalSize); -#else - for (i = 0; i < TotalSize; i++) - mem_writeb_phys_dma(PhysAddress + i, DataWrite[i]); - - mem_invalidate_range(PhysAddress, PhysAddress + TotalSize - 1); -#endif -} + } else { + temp = _dma_read(dma_c->ac) | (_dma_read(dma_c->ac + 1) << 8); + + if (dma_c->mode & 0x20) { + if (dma_ps2.is_ps2) + dma_c->ac -= 2; + else + dma_c->ac = (dma_c->ac & 0xfe0000) | ((dma_c->ac - 2) & 0x1ffff); + } else { + if (dma_ps2.is_ps2) + dma_c->ac += 2; + else + dma_c->ac = (dma_c->ac & 0xfe0000) | ((dma_c->ac + 2) & 0x1ffff); + } + } + + dma_stat_rq |= (1 << channel); + + dma_c->cc--; + if (dma_c->cc < 0) { + tc = 1; + if (dma_c->mode & 0x10) { /*Auto-init*/ + dma_c->cc = dma_c->cb; + dma_c->ac = dma_c->ab; + } else + dma_m |= (1 << channel); + dma_stat |= (1 << channel); + } + + if (tc) + return(temp | DMA_OVER); + + return(temp); +} + + +int +dma_channel_write(int channel, uint16_t val) +{ + dma_t *dma_c = &dma[channel]; + + if (channel < 4) { + if (dma_command & 0x04) + return(DMA_NODATA); + } else { + if (dma16_command & 0x04) + return(DMA_NODATA); + } + + if (! AT) + refreshread(); + + if (dma_m & (1 << channel)) + return(DMA_NODATA); + if ((dma_c->mode & 0xC) != 4) + return(DMA_NODATA); + + if (! dma_c->size) { + _dma_write(dma_c->ac, val & 0xff); + + if (dma_c->mode & 0x20) { + if (dma_ps2.is_ps2) + dma_c->ac--; + else + dma_c->ac = (dma_c->ac & 0xff0000) | ((dma_c->ac - 1) & 0xffff); + } else { + if (dma_ps2.is_ps2) + dma_c->ac++; + else + dma_c->ac = (dma_c->ac & 0xff0000) | ((dma_c->ac + 1) & 0xffff); + } + } else { + _dma_write(dma_c->ac, val & 0xff); + _dma_write(dma_c->ac + 1, val >> 8); + + if (dma_c->mode & 0x20) { + if (dma_ps2.is_ps2) + dma_c->ac -= 2; + else + dma_c->ac = (dma_c->ac & 0xfe0000) | ((dma_c->ac - 2) & 0x1ffff); + dma_c->ac = (dma_c->ac & 0xfe0000) | ((dma_c->ac - 2) & 0x1ffff); + } else { + if (dma_ps2.is_ps2) + dma_c->ac += 2; + else + dma_c->ac = (dma_c->ac & 0xfe0000) | ((dma_c->ac + 2) & 0x1ffff); + } + } + + dma_stat_rq |= (1 << channel); + + dma_c->cc--; + if (dma_c->cc < 0) { + if (dma_c->mode & 0x10) { /*Auto-init*/ + dma_c->cc = dma_c->cb; + dma_c->ac = dma_c->ab; + } else + dma_m |= (1 << channel); + dma_stat |= (1 << channel); + } + + if (dma_m & (1 << channel)) + return(DMA_OVER); + + return(0); +} + + +int +dma_mode(int channel) +{ + if (channel < 4) + return(dma[channel].mode); + else + return(dma[channel & 3].mode); +} + + +/* DMA Bus Master Page Read/Write */ +void +DMAPageRead(uint32_t PhysAddress, uint8_t *DataRead, uint32_t TotalSize) +{ + uint32_t i = 0; + +#if 0 + memcpy(DataRead, &ram[PhysAddress], TotalSize); +#else + for (i = 0; i < TotalSize; i++) + DataRead[i] = mem_readb_phys_dma(PhysAddress + i); +#endif +} + + +void +DMAPageWrite(uint32_t PhysAddress, const uint8_t *DataWrite, uint32_t TotalSize) +{ + uint32_t i = 0; + +#if 0 + mem_invalidate_range(PhysAddress, PhysAddress + TotalSize - 1); + memcpy(&ram[PhysAddress], DataWrite, TotalSize); +#else + for (i = 0; i < TotalSize; i++) + mem_writeb_phys_dma(PhysAddress + i, DataWrite[i]); + + mem_invalidate_range(PhysAddress, PhysAddress + TotalSize - 1); +#endif +} diff --git a/src/machines/m_amstrad.c b/src/machines/m_amstrad.c index bfa5d5a..40d1595 100644 --- a/src/machines/m_amstrad.c +++ b/src/machines/m_amstrad.c @@ -1,1352 +1,1352 @@ -/* - * 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. - * - * Emulation of the Amstrad series of PC's: PC1512, PC1640 and - * PC200, including their keyboard, mouse and video devices, as - * well as the PC2086 and PC3086 systems. - * - * PC1512: The PC1512 extends CGA with a bit-planar 640x200x16 mode. - * Most CRTC registers are fixed. - * - * The Technical Reference Manual lists the video waitstate - * time as between 12 and 46 cycles. We currently always use - * the lower number. - * - * PC1640: Mostly standard EGA, but with CGA & Hercules emulation. - * - * PC200: CGA with some NMI stuff. But we don't need that as it's only - * used for TV and LCD displays, and we're emulating a CRT. - * - * TODO: This module is not complete yet: - * PC1512: The BIOS assumes 512K RAM, because I cannot figure out how to - * read the status of the LK4 jumper on the mainboard, which is - * somehow linked to the bus gate array on the NDMACS line... - * PC1612: EGA mode does not seem to work in the PC1640; it works fine - * in alpha mode, but in highres ("ECD350") mode, it displays - * some semi-random junk. Video-memory pointer maybe? - * BIOSES: I need to re-do the bios.txt format so we can load non-BIOS - * ROM files for a given machine, such as font roms here.. - * - * Version: @(#)m_amstrad.c 1.0.19 2018/10/05 - * - * Authors: Fred N. van Kempen, - * Miran Grca, - * Sarah Walker, - * - * Copyright 2017,2018 Fred N. van Kempen. - * Copyright 2016-2018 Miran Grca. - * Copyright 2008-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. - */ -#include -#include -#include -#include -#include -#define dbglog kbd_log -#include "../emu.h" -#include "../cpu/cpu.h" -#include "../io.h" -#include "../mem.h" -#include "../rom.h" -#include "../timer.h" -#include "../device.h" -#include "../nvr.h" -#include "../devices/system/nmi.h" -#include "../devices/system/pic.h" -#include "../devices/system/pit.h" -#include "../devices/system/ppi.h" -#include "../devices/ports/parallel.h" -#include "../devices/input/keyboard.h" -#include "../devices/input/mouse.h" -#include "../devices/floppy/fdd.h" -#include "../devices/floppy/fdc.h" -#include "../devices/sound/sound.h" -#include "../devices/sound/snd_speaker.h" -#include "../devices/video/video.h" -#include "../devices/video/vid_cga.h" -#include "../devices/video/vid_ega.h" -#include "machine.h" - - -#define STAT_PARITY 0x80 -#define STAT_RTIMEOUT 0x40 -#define STAT_TTIMEOUT 0x20 -#define STAT_LOCK 0x10 -#define STAT_CD 0x08 -#define STAT_SYSFLAG 0x04 -#define STAT_IFULL 0x02 -#define STAT_OFULL 0x01 - - -typedef struct { - rom_t bios_rom; /* 1640 */ - cga_t cga; /* 1640/200 */ - ega_t ega; /* 1640 */ - uint8_t crtc[32]; - int crtcreg; - int cga_enabled; /* 1640 */ - uint8_t cgacol, - cgamode, - stat; - uint8_t plane_write, /* 1512/200 */ - plane_read, /* 1512/200 */ - border; /* 1512/200 */ - int linepos, - displine; - int sc, vc; - int cgadispon; - int con, coff, - cursoron, - cgablink; - int64_t vsynctime; - int vadj; - uint16_t ma, maback; - int dispon; - int blink; - int64_t dispontime, /* 1512/1640 */ - dispofftime; /* 1512/1640 */ - int64_t vidtime; /* 1512/1640 */ - int firstline, - lastline; - uint8_t *vram; -} amsvid_t; - -typedef struct { - /* Machine stuff. */ - int8_t type; - uint8_t dead; - uint8_t stat1, - stat2; - - /* Keyboard stuff. */ - int8_t wantirq; - uint8_t key_waiting; - uint8_t pa; - uint8_t pb; - - /* Mouse stuff. */ - uint8_t mousex, - mousey; - int oldb; - - /* Video stuff. */ - amsvid_t *vid; -} amstrad_t; - - -static uint8_t key_queue[16]; -static int key_queue_start = 0, - key_queue_end = 0; -static const uint8_t crtc_mask[32] = { - 0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f, - 0xf3, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff, - 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; -static const video_timings_t pc1512_timing = {VID_BUS,0,0,0,0,0,0}; -static const video_timings_t pc1640_timing = {VID_ISA,8,16,32,8,16,32}; -static const video_timings_t pc200_timing = {VID_ISA,8,16,32,8,16,32}; -static const video_timings_t pvga1a_timing = {VID_ISA,6,8,16,6,8,16}; -static const video_timings_t wd90c11_timing = {VID_ISA,3,3,6,5,5,10}; - - -static void -recalc_timings_1512(amsvid_t *vid) -{ - double _dispontime, _dispofftime, disptime; - - disptime = 128; /*Fixed on PC1512*/ - _dispontime = 80; - _dispofftime = disptime - _dispontime; - _dispontime *= CGACONST; - _dispofftime *= CGACONST; - vid->dispontime = (int64_t)(_dispontime * (1 << TIMER_SHIFT)); - vid->dispofftime = (int64_t)(_dispofftime * (1 << TIMER_SHIFT)); -} - - -static void -vid_out_1512(uint16_t addr, uint8_t val, void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - uint8_t old; - - switch (addr) { - case 0x03d4: - vid->crtcreg = val & 31; - return; - - case 0x03d5: - old = vid->crtc[vid->crtcreg]; - vid->crtc[vid->crtcreg] = val & crtc_mask[vid->crtcreg]; - if (old != val) { - if (vid->crtcreg < 0xe || vid->crtcreg > 0x10) { - fullchange = changeframecount; - recalc_timings_1512(vid); - } - } - return; - - case 0x03d8: - if ((val & 0x12) == 0x12 && (vid->cgamode & 0x12) != 0x12) { - vid->plane_write = 0xf; - vid->plane_read = 0; - } - vid->cgamode = val; - return; - - case 0x03d9: - vid->cgacol = val; - return; - - case 0x03dd: - vid->plane_write = val; - return; - - case 0x03de: - vid->plane_read = val & 3; - return; - - case 0x03df: - vid->border = val; - return; - } -} - - -static uint8_t -vid_in_1512(uint16_t addr, void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - uint8_t ret = 0xff; - - switch (addr) { - case 0x03d4: - ret = vid->crtcreg; - - case 0x03d5: - ret = vid->crtc[vid->crtcreg]; - - case 0x03da: - ret = vid->stat; - } - - return(ret); -} - - -static void -vid_write_1512(uint32_t addr, uint8_t val, void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - - cycles -= 12; - addr &= 0x3fff; - - if ((vid->cgamode & 0x12) == 0x12) { - if (vid->plane_write & 1) vid->vram[addr] = val; - if (vid->plane_write & 2) vid->vram[addr | 0x4000] = val; - if (vid->plane_write & 4) vid->vram[addr | 0x8000] = val; - if (vid->plane_write & 8) vid->vram[addr | 0xc000] = val; - } else - vid->vram[addr] = val; -} - - -static uint8_t -vid_read_1512(uint32_t addr, void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - - cycles -= 12; - addr &= 0x3fff; - - if ((vid->cgamode & 0x12) == 0x12) - return(vid->vram[addr | (vid->plane_read << 14)]); - - return(vid->vram[addr]); -} - - -static void -vid_poll_1512(void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - uint16_t ca = (vid->crtc[15] | (vid->crtc[14] << 8)) & 0x3fff; - int drawcursor; - int x, c; - uint8_t chr, attr; - uint16_t dat, dat2, dat3, dat4; - int cols[4]; - int col; - int oldsc; - - if (! vid->linepos) { - vid->vidtime += vid->dispofftime; - vid->stat |= 1; - vid->linepos = 1; - oldsc = vid->sc; - if (vid->dispon) { - if (vid->displine < vid->firstline) { - vid->firstline = vid->displine; - video_wait_for_buffer(); - } - vid->lastline = vid->displine; - for (c = 0; c < 8; c++) { - if ((vid->cgamode & 0x12) == 0x12) { - buffer->line[vid->displine][c] = (vid->border & 15) + 16; - if (vid->cgamode & 1) - buffer->line[vid->displine][c + (vid->crtc[1] << 3) + 8] = 0; - else - buffer->line[vid->displine][c + (vid->crtc[1] << 4) + 8] = 0; - } else { - buffer->line[vid->displine][c] = (vid->cgacol & 15) + 16; - if (vid->cgamode & 1) - buffer->line[vid->displine][c + (vid->crtc[1] << 3) + 8] = (vid->cgacol & 15) + 16; - else - buffer->line[vid->displine][c + (vid->crtc[1] << 4) + 8] = (vid->cgacol & 15) + 16; - } - } - if (vid->cgamode & 1) { - for (x = 0; x < 80; x++) { - chr = vid->vram[ ((vid->ma << 1) & 0x3fff)]; - attr = vid->vram[(((vid->ma << 1) + 1) & 0x3fff)]; - drawcursor = ((vid->ma == ca) && vid->con && vid->cursoron); - if (vid->cgamode & 0x20) { - cols[1] = (attr & 15) + 16; - cols[0] = ((attr >> 4) & 7) + 16; - if ((vid->blink & 16) && (attr & 0x80) && !drawcursor) - cols[1] = cols[0]; - } else { - cols[1] = (attr & 15) + 16; - cols[0] = (attr >> 4) + 16; - } - if (drawcursor) { - for (c = 0; c < 8; c++) - buffer->line[vid->displine][(x << 3) + c + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0] ^ 15; - } else { - for (c = 0; c < 8; c++) - buffer->line[vid->displine][(x << 3) + c + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0]; - } - vid->ma++; - } - } else if (! (vid->cgamode & 2)) { - for (x = 0; x < 40; x++) { - chr = vid->vram[((vid->ma << 1) & 0x3fff)]; - attr = vid->vram[(((vid->ma << 1) + 1) & 0x3fff)]; - drawcursor = ((vid->ma == ca) && vid->con && vid->cursoron); - if (vid->cgamode & 0x20) { - cols[1] = (attr & 15) + 16; - cols[0] = ((attr >> 4) & 7) + 16; - if ((vid->blink & 16) && (attr & 0x80)) - cols[1] = cols[0]; - } else { - cols[1] = (attr & 15) + 16; - cols[0] = (attr >> 4) + 16; - } - vid->ma++; - if (drawcursor) { - for (c = 0; c < 8; c++) - buffer->line[vid->displine][(x << 4) + (c << 1) + 8] = - buffer->line[vid->displine][(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0] ^ 15; - } else { - for (c = 0; c < 8; c++) - buffer->line[vid->displine][(x << 4) + (c << 1) + 8] = - buffer->line[vid->displine][(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0]; - } - } - } else if (! (vid->cgamode & 16)) { - cols[0] = (vid->cgacol & 15) | 16; - col = (vid->cgacol & 16) ? 24 : 16; - if (vid->cgamode & 4) { - cols[1] = col | 3; - cols[2] = col | 4; - cols[3] = col | 7; - } else if (vid->cgacol & 32) { - cols[1] = col | 3; - cols[2] = col | 5; - cols[3] = col | 7; - } else { - cols[1] = col | 2; - cols[2] = col | 4; - cols[3] = col | 6; - } - for (x = 0; x < 40; x++) { - dat = (vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000)] << 8) | vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000) + 1]; - vid->ma++; - for (c = 0; c < 8; c++) { - buffer->line[vid->displine][(x << 4) + (c << 1) + 8] = - buffer->line[vid->displine][(x << 4) + (c << 1) + 1 + 8] = cols[dat >> 14]; - dat <<= 2; - } - } - } else { - for (x = 0; x < 40; x++) { - ca = ((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000); - dat = (vid->vram[ca] << 8) | vid->vram[ca + 1]; - dat2 = (vid->vram[ca + 0x4000] << 8) | vid->vram[ca + 0x4001]; - dat3 = (vid->vram[ca + 0x8000] << 8) | vid->vram[ca + 0x8001]; - dat4 = (vid->vram[ca + 0xc000] << 8) | vid->vram[ca + 0xc001]; - - vid->ma++; - for (c = 0; c < 16; c++) { - buffer->line[vid->displine][(x << 4) + c + 8] = (((dat >> 15) | ((dat2 >> 15) << 1) | ((dat3 >> 15) << 2) | ((dat4 >> 15) << 3)) & (vid->cgacol & 15)) + 16; - dat <<= 1; - dat2 <<= 1; - dat3 <<= 1; - dat4 <<= 1; - } - } - } - } else { - cols[0] = ((vid->cgamode & 0x12) == 0x12) ? 0 : (vid->cgacol & 15) + 16; - if (vid->cgamode & 1) - cga_hline(buffer, 0, vid->displine, (vid->crtc[1] << 3) + 16, cols[0]); - else - cga_hline(buffer, 0, vid->displine, (vid->crtc[1] << 4) + 16, cols[0]); - } - - vid->sc = oldsc; - if (vid->vsynctime) - vid->stat |= 8; - vid->displine++; - if (vid->displine >= 360) - vid->displine = 0; - } else { - vid->vidtime += vid->dispontime; - if ((vid->lastline - vid->firstline) == 199) - vid->dispon = 0; /*Amstrad PC1512 always displays 200 lines, regardless of CRTC settings*/ - if (vid->dispon) - vid->stat &= ~1; - vid->linepos = 0; - if (vid->vsynctime) { - vid->vsynctime--; - if (! vid->vsynctime) - vid->stat &= ~8; - } - if (vid->sc == (vid->crtc[11] & 31)) { - vid->con = 0; - vid->coff = 1; - } - if (vid->vadj) { - vid->sc++; - vid->sc &= 31; - vid->ma = vid->maback; - vid->vadj--; - if (! vid->vadj) { - vid->dispon = 1; - vid->ma = vid->maback = (vid->crtc[13] | (vid->crtc[12] << 8)) & 0x3fff; - vid->sc = 0; - } - } else if (vid->sc == vid->crtc[9]) { - vid->maback = vid->ma; - vid->sc = 0; - vid->vc++; - vid->vc &= 127; - - if (vid->displine == 32) { - vid->vc = 0; - vid->vadj = 6; - if ((vid->crtc[10] & 0x60) == 0x20) - vid->cursoron = 0; - else - vid->cursoron = vid->blink & 16; - } - - if (vid->displine >= 262) { - vid->dispon = 0; - vid->displine = 0; - vid->vsynctime = 46; - - if (vid->cgamode&1) - x = (vid->crtc[1] << 3) + 16; - else - x = (vid->crtc[1] << 4) + 16; - vid->lastline++; - - if ((x != xsize) || ((vid->lastline - vid->firstline) != ysize) || video_force_resize_get()) { - xsize = x; - ysize = vid->lastline - vid->firstline; - if (xsize < 64) xsize = 656; - if (ysize < 32) ysize = 200; - set_screen_size(xsize, (ysize << 1) + 16); - - if (video_force_resize_get()) - video_force_resize_set(0); - } - - video_blit_memtoscreen_8(0, vid->firstline - 4, 0, (vid->lastline - vid->firstline) + 8, xsize, (vid->lastline - vid->firstline) + 8); - - video_res_x = xsize - 16; - video_res_y = ysize; - if (vid->cgamode & 1) { - video_res_x /= 8; - video_res_y /= vid->crtc[9] + 1; - video_bpp = 0; - } else if (! (vid->cgamode & 2)) { - video_res_x /= 16; - video_res_y /= vid->crtc[9] + 1; - video_bpp = 0; - } else if (! (vid->cgamode & 16)) { - video_res_x /= 2; - video_bpp = 2; - } else { - video_bpp = 4; - } - - vid->firstline = 1000; - vid->lastline = 0; - vid->blink++; - } - } else { - vid->sc++; - vid->sc &= 31; - vid->ma = vid->maback; - } - if (vid->sc == (vid->crtc[10] & 31)) - vid->con = 1; - } -} - - -static void -vid_init_1512(amstrad_t *ams) -{ - amsvid_t *vid; - - /* Allocate a video controller block. */ - vid = (amsvid_t *)mem_alloc(sizeof(amsvid_t)); - memset(vid, 0x00, sizeof(amsvid_t)); - - vid->vram = (uint8_t *)mem_alloc(0x10000); - vid->cgacol = 7; - vid->cgamode = 0x12; - - timer_add(vid_poll_1512, &vid->vidtime, TIMER_ALWAYS_ENABLED, vid); - mem_map_add(&vid->cga.mapping, 0xb8000, 0x08000, - vid_read_1512, NULL, NULL, vid_write_1512, NULL, NULL, - NULL, 0, vid); - io_sethandler(0x03d0, 16, - vid_in_1512, NULL, NULL, vid_out_1512, NULL, NULL, vid); - - overscan_x = overscan_y = 16; - - video_inform(VID_TYPE_CGA, &pc1512_timing); - - ams->vid = vid; -} - - -static void -vid_close_1512(void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - - free(vid->vram); - free(vid); -} - - -static void -vid_speed_change_1512(void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - - recalc_timings_1512(vid); -} - - -static const device_t vid_1512_device = { - "Amstrad PC1512 (video)", - 0, - 0, - NULL, vid_close_1512, NULL, - NULL, - vid_speed_change_1512, - NULL, - &pc1512_timing, - NULL -}; - - -static void -recalc_timings_1640(amsvid_t *vid) -{ - cga_recalctimings(&vid->cga); - ega_recalctimings(&vid->ega); - - if (vid->cga_enabled) { - overscan_x = overscan_y = 16; - - vid->dispontime = vid->cga.dispontime; - vid->dispofftime = vid->cga.dispofftime; - } else { - overscan_x = 16; overscan_y = 28; - - vid->dispontime = vid->ega.dispontime; - vid->dispofftime = vid->ega.dispofftime; - } -} - - -static void -vid_out_1640(uint16_t addr, uint8_t val, void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - - switch (addr) { - case 0x03db: - vid->cga_enabled = val & 0x40; - if (vid->cga_enabled) { - mem_map_enable(&vid->cga.mapping); - mem_map_disable(&vid->ega.mapping); - video_inform(VID_TYPE_CGA, &pc1640_timing); - } else { - mem_map_disable(&vid->cga.mapping); - switch (vid->ega.gdcreg[6] & 0xc) { - case 0x0: /*128k at A0000*/ - mem_map_set_addr(&vid->ega.mapping, - 0xa0000, 0x20000); - break; - - case 0x4: /*64k at A0000*/ - mem_map_set_addr(&vid->ega.mapping, - 0xa0000, 0x10000); - break; - - case 0x8: /*32k at B0000*/ - mem_map_set_addr(&vid->ega.mapping, - 0xb0000, 0x08000); - break; - - case 0xC: /*32k at B8000*/ - mem_map_set_addr(&vid->ega.mapping, - 0xb8000, 0x08000); - break; - } - video_inform(VID_TYPE_SPEC, &pc1640_timing); - } - return; - } - - if (vid->cga_enabled) - cga_out(addr, val, &vid->cga); - else - ega_out(addr, val, &vid->ega); -} - - -static uint8_t -vid_in_1640(uint16_t addr, void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - -#if 0 - switch (addr) { - } -#endif - - if (vid->cga_enabled) - return(cga_in(addr, &vid->cga)); - else - return(ega_in(addr, &vid->ega)); -} - - -static void -vid_poll_1640(void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - - if (vid->cga_enabled) { - overscan_x = overscan_y = 16; - - vid->cga.vidtime = vid->vidtime; - cga_poll(&vid->cga); - vid->vidtime = vid->cga.vidtime; - } else { - overscan_x = 16; overscan_y = 28; - - vid->ega.vidtime = vid->vidtime; - ega_poll(&vid->ega); - vid->vidtime = vid->ega.vidtime; - } -} - - -static void -vid_init_1640(amstrad_t *ams, wchar_t *fn, int sz) -{ - amsvid_t *vid; - - /* Allocate a video controller block. */ - vid = (amsvid_t *)mem_alloc(sizeof(amsvid_t)); - memset(vid, 0x00, sizeof(amsvid_t)); - - /* Load the BIOS. */ - rom_init(&vid->bios_rom, fn, 0xc0000, sz, sz - 1, 0, 0); - - ega_init(&vid->ega, 9, 0); - vid->cga.vram = vid->ega.vram; - vid->cga_enabled = 1; - cga_init(&vid->cga); - - mem_map_add(&vid->cga.mapping, 0xb8000, 0x08000, - cga_read,NULL,NULL, cga_write,NULL,NULL, NULL, 0, &vid->cga); - mem_map_add(&vid->ega.mapping, 0, 0, - ega_read,NULL,NULL, ega_write,NULL,NULL, NULL, 0, &vid->ega); - io_sethandler(0x03a0, 64, - vid_in_1640, NULL, NULL, vid_out_1640, NULL, NULL, vid); - - timer_add(vid_poll_1640, &vid->vidtime, TIMER_ALWAYS_ENABLED, vid); - - overscan_x = overscan_y = 16; - - video_inform(VID_TYPE_CGA, &pc1640_timing); - - ams->vid = vid; -} - - -static void -vid_close_1640(void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - - free(vid->ega.vram); - free(vid); -} - - -static void -vid_speed_changed_1640(void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - - recalc_timings_1640(vid); -} - - -static const device_t vid_1640_device = { - "Amstrad PC1640 (video)", - 0, - 0, - NULL, vid_close_1640, NULL, - NULL, - vid_speed_changed_1640, - NULL, - &pc1640_timing, - NULL -}; - - -static void -vid_out_200(uint16_t addr, uint8_t val, void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - cga_t *cga = &vid->cga; - uint8_t old; - - switch (addr) { - case 0x03d5: - if (!(vid->plane_read & 0x40) && cga->crtcreg <= 11) { - if (vid->plane_read & 0x80) - nmi = 1; - - vid->plane_write = 0x20 | (cga->crtcreg & 0x1f); - vid->border = val; - return; - } - old = cga->crtc[cga->crtcreg]; - cga->crtc[cga->crtcreg] = val & crtc_mask[cga->crtcreg]; - if (old != val) { - if (cga->crtcreg < 0xe || cga->crtcreg > 0x10) { - fullchange = changeframecount; - cga_recalctimings(cga); - } - } - return; - - case 0x03d8: - old = cga->cgamode; - cga->cgamode = val; - if ((cga->cgamode ^ old) & 3) - cga_recalctimings(cga); - vid->plane_write |= 0x80; - if (vid->plane_read & 0x80) - nmi = 1; - return; - - case 0x03de: - vid->plane_read = val; - vid->plane_write = 0x1f; - if (val & 0x80) - vid->plane_write |= 0x40; - return; - } - - cga_out(addr, val, cga); -} - - -static uint8_t -vid_in_200(uint16_t addr, void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - cga_t *cga = &vid->cga; - uint8_t ret; - - switch (addr) { - case 0x03d8: - return(cga->cgamode); - - case 0x03dd: - ret = vid->plane_write; - vid->plane_write &= 0x1f; - nmi = 0; - return(ret); - - case 0x03de: - return((vid->plane_read & 0xc7) | 0x10); /*External CGA*/ - - case 0x03df: - return(vid->border); - } - - return(cga_in(addr, cga)); -} - - -static void -vid_init_200(amstrad_t *ams) -{ - amsvid_t *vid; - cga_t *cga; - - /* Allocate a video controller block. */ - vid = (amsvid_t *)mem_alloc(sizeof(amsvid_t)); - memset(vid, 0x00, sizeof(amsvid_t)); - - cga = &vid->cga; - cga->vram = (uint8_t *)mem_alloc(0x4000); - cga_init(cga); - - mem_map_add(&vid->cga.mapping, 0xb8000, 0x08000, - cga_read,NULL,NULL, cga_write,NULL,NULL, NULL, 0, cga); - io_sethandler(0x03d0, 16, - vid_in_200, NULL, NULL, vid_out_200, NULL, NULL, vid); - - timer_add(cga_poll, &cga->vidtime, TIMER_ALWAYS_ENABLED, cga); - - overscan_x = overscan_y = 16; - - video_inform(VID_TYPE_CGA, &pc200_timing); - - ams->vid = vid; -} - - -static void -vid_close_200(void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - - free(vid->cga.vram); - - free(vid); -} - - -static void -vid_speed_changed_200(void *priv) -{ - amsvid_t *vid = (amsvid_t *)priv; - - cga_recalctimings(&vid->cga); -} - - -static const device_t vid_200_device = { - "Amstrad PC200 (video)", - 0, - 0, - NULL, vid_close_200, NULL, - NULL, - vid_speed_changed_200, - NULL, - &pc200_timing, - NULL -}; - - -static void -mse_write(uint16_t addr, uint8_t val, void *priv) -{ - amstrad_t *ams = (amstrad_t *)priv; - - if (addr == 0x78) - ams->mousex = 0; - else - ams->mousey = 0; -} - - -static uint8_t -mse_read(uint16_t addr, void *priv) -{ - amstrad_t *ams = (amstrad_t *)priv; - - if (addr == 0x78) - return(ams->mousex); - - return(ams->mousey); -} - - -static int -mse_poll(int x, int y, int z, int b, void *priv) -{ - amstrad_t *ams = (amstrad_t *)priv; - - ams->mousex += x; - ams->mousey -= y; - - if ((b & 1) && !(ams->oldb & 1)) - keyboard_send(0x7e); - if ((b & 2) && !(ams->oldb & 2)) - keyboard_send(0x7d); - if (!(b & 1) && (ams->oldb & 1)) - keyboard_send(0xfe); - if (!(b & 2) && (ams->oldb & 2)) - keyboard_send(0xfd); - - ams->oldb = b; - - return(0); -} - - -static void -kbd_adddata(uint16_t val) -{ - key_queue[key_queue_end] = (uint8_t)(val&0xff); - DBGLOG(1, "AMSkb: %02X added to key queue at %i\n", val, key_queue_end); - key_queue_end = (key_queue_end + 1) & 0xf; -} - - -static void -kbd_adddata_ex(uint16_t val) -{ - kbd_adddata_process(val, kbd_adddata); -} - - -static void -kbd_write(uint16_t port, uint8_t val, void *priv) -{ - amstrad_t *ams = (amstrad_t *)priv; - - DBGLOG(2, "AMSkb: write %04X %02X %02X\n", port, val, ams->pb); - - switch (port) { - case 0x61: - /* - * PortB - System Control. - * - * 7 Enable Status-1/Disable Keyboard Code on Port A. - * 6 Enable incoming Keyboard Clock. - * 5 Prevent external parity errors from causing NMI. - * 4 Disable parity checking of on-board system Ram. - * 3 Undefined (Not Connected). - * 2 Enable Port C LSB / Disable MSB. (See 1.8.3) - * 1 Speaker Drive. - * 0 8253 GATE 2 (Speaker Modulate). - * - * This register is controlled by BIOS and/or ROS. - */ - DBGLOG(1, "AMSkb: write PB %02x (%02x)\n", val, ams->pb); - if (!(ams->pb & 0x40) && (val & 0x40)) { /*Reset keyboard*/ - DEBUG("AMSkb: reset keyboard\n"); - kbd_adddata(0xaa); - } - ams->pb = val; - ppi.pb = val; - - timer_process(); - timer_update_outstanding(); - - speaker_update(); - speaker_gated = val & 0x01; - speaker_enable = val & 0x02; - if (speaker_enable) - was_speaker_enable = 1; - pit_set_gate(&pit, 2, val & 0x01); - - if (val & 0x80) { - /* Keyboard enabled, so enable PA reading. */ - ams->pa = 0x00; - } - break; - - case 0x63: - break; - - case 0x64: - ams->stat1 = val; - break; - - case 0x65: - ams->stat2 = val; - break; - - case 0x66: - pc_reset(1); - break; - - default: - ERRLOG("AMSkb: bad keyboard write %04X %02X\n", port, val); - } -} - - -static uint8_t -kbd_read(uint16_t port, void *priv) -{ - amstrad_t *ams = (amstrad_t *)priv; - uint8_t ret = 0xff; - - switch (port) { - case 0x60: - if (ams->pb & 0x80) { - /* - * PortA - System Status 1 - * - * 7 Always 0 (KBD7) - * 6 Second Floppy disk drive installed (KBD6) - * 5 DDM1 - Default Display Mode bit 1 (KBD5) - * 4 DDM0 - Default Display Mode bit 0 (KBD4) - * 3 Always 1 (KBD3) - * 2 Always 1 (KBD2) - * 1 8087 NDP installed (KBD1) - * 0 Always 1 (KBD0) - * - * DDM00 - * 00 unknown, external color? - * 01 Color,alpha,40x25, bright white on black. - * 10 Color,alpha,80x25, bright white on black. - * 11 External Monochrome,80x25. - * - * Following a reset, the hardware selects VDU mode - * 2. The ROS then sets the initial VDU state based - * on the DDM value. - */ - ret = (0x0d | ams->stat1) & 0x7f; - } else { - ret = ams->pa; - if (key_queue_start == key_queue_end) { - ams->wantirq = 0; - } else { - ams->key_waiting = key_queue[key_queue_start]; - key_queue_start = (key_queue_start + 1) & 0xf; - ams->wantirq = 1; - } - } - break; - - case 0x61: - ret = ams->pb; - break; - - case 0x62: - /* - * PortC - System Status 2. - * - * 7 On-board system RAM parity error. - * 6 External parity error (I/OCHCK from expansion bus). - * 5 8253 PIT OUT2 output. - * 4 Undefined (Not Connected). - *------------------------------------------- - * LSB MSB (depends on PB2) - *------------------------------------------- - * 3 RAM3 Undefined - * 2 RAM2 Undefined - * 1 RAM1 Undefined - * 0 RAM0 RAM4 - * - * PC7 is forced to 0 when on-board system RAM parity - * checking is disabled by PB4. - * - * RAM4:0 - * 01110 512K bytes on-board. - * 01111 544K bytes (32K external). - * 10000 576K bytes (64K external). - * 10001 608K bytes (96K external). - * 10010 640K bytes (128K external or fitted on-board). - */ - if (ams->pb & 0x04) - ret = ams->stat2 & 0x0f; - else - ret = ams->stat2 >> 4; - ret |= (ppispeakon ? 0x20 : 0); - if (nmi) - ret |= 0x40; - break; - - default: - ERRLOG("AMDkb: bad keyboard read %04X\n", port); - } - - return(ret); -} - - -static void -kbd_poll(void *priv) -{ - amstrad_t *ams = (amstrad_t *)priv; - - keyboard_delay += (1000 * TIMER_USEC); - - if (ams->wantirq) { - ams->wantirq = 0; - ams->pa = ams->key_waiting; - picint(2); - DBGLOG(1, "AMSkb: take IRQ\n"); - } - - if (key_queue_start != key_queue_end && !ams->pa) { - ams->key_waiting = key_queue[key_queue_start]; - DBGLOG(1, "AMSkb: reading %02X from the key queue at %i\n", - ams->key_waiting, key_queue_start); - key_queue_start = (key_queue_start + 1) & 0xf; - ams->wantirq = 1; - } -} - - -static void -ams_write(uint16_t port, uint8_t val, void *priv) -{ - amstrad_t *ams = (amstrad_t *)priv; - - switch (port) { - case 0xdead: - ams->dead = val; - break; - } -} - - -static uint8_t -ams_read(uint16_t port, void *priv) -{ - amstrad_t *ams = (amstrad_t *)priv; - uint8_t ret = 0xff; - - switch (port) { - case 0x0379: /* printer control, also set LK1-3. - * 0 English Language. - * 1 German Language. - * 2 French Language. - * 3 Spanish Language. - * 4 Danish Language. - * 5 Swedish Language. - * 6 Italian Language. - * 7 Diagnostic Mode. - */ - ret = 0x02; /* ENGLISH. no Diags mode */ - break; - - case 0x037a: /* printer status */ - switch(ams->type) { - case 0: - ret = 0x20; - break; - - case 2: - ret = 0x80; - break; - - default: - ret = 0x00; - } - break; - - case 0xdead: - ret = ams->dead; - break; - } - - return(ret); -} - - -static void -amstrad_common_init(const machine_t *model, void *arg, int type) -{ - romdef_t *roms = (romdef_t *)arg; - amstrad_t *ams; - - ams = (amstrad_t *)mem_alloc(sizeof(amstrad_t)); - memset(ams, 0x00, sizeof(amstrad_t)); - ams->type = type; - - machine_common_init(model, arg); - - nmi_init(); - - device_add(&amstrad_nvr_device); - -//FIXME: parallel_remove_amstrad(); - - io_sethandler(0x0078, 1, - mse_read, NULL, NULL, mse_write, NULL, NULL, ams); - - io_sethandler(0x007a, 1, - mse_read, NULL, NULL, mse_write, NULL, NULL, ams); - - io_sethandler(0x0379, 2, - ams_read, NULL, NULL, NULL, NULL, NULL, ams); - - io_sethandler(0xdead, 1, - ams_read, NULL, NULL, ams_write, NULL, NULL, ams); - - switch(ams->type) { - case 0: - device_add(&fdc_xt_device); - if (video_card == VID_INTERNAL) { - /* Load the PC1512 CGA Character Set ROM. */ - video_load_font(roms->fontfn, roms->fontnum); - - /* Initialize the internal CGA controller. */ - vid_init_1512(ams); - device_add_ex(&vid_1512_device, ams->vid); - } - break; - - case 1: - device_add(&fdc_xt_device); - if (video_card == VID_INTERNAL) { - /* Load the BIOS for the internal CGA/EGA. */ - vid_init_1640(ams, roms->vidfn, roms->vidsz); - device_add_ex(&vid_1640_device, ams->vid); - } - break; - - case 2: - device_add(&fdc_xt_device); - if (video_card == VID_INTERNAL) { - /* Load the PC200 CGA Character Set ROM. */ - video_load_font(roms->fontfn, roms->fontnum); - - vid_init_200(ams); - device_add_ex(&vid_200_device, ams->vid); - } - break; - - case 3: - device_add(&fdc_at_actlow_device); - if (video_card == VID_INTERNAL) { - device_add(¶dise_pvga1a_pc2086_device); - video_inform(VID_TYPE_SPEC, &pvga1a_timing); - } - break; - - case 4: - device_add(&fdc_at_actlow_device); - if (video_card == VID_INTERNAL) { - device_add(¶dise_pvga1a_pc3086_device); - video_inform(VID_TYPE_SPEC, &pvga1a_timing); - } - break; - - case 5: - device_add(&fdc_at_actlow_device); - if (video_card == VID_INTERNAL) { - device_add(¶dise_wd90c11_megapc_device); - video_inform(VID_TYPE_SPEC, &wd90c11_timing); - } - break; - } - - /* Initialize the (custom) keyboard/mouse interface. */ - ams->wantirq = 0; - io_sethandler(0x0060, 7, - kbd_read, NULL, NULL, kbd_write, NULL, NULL, ams); - timer_add(kbd_poll, &keyboard_delay, TIMER_ALWAYS_ENABLED, ams); - keyboard_set_table(scancode_xt); - keyboard_send = kbd_adddata_ex; - keyboard_scan = 1; - - /* Tell mouse driver about our internal mouse. */ - if (mouse_type == MOUSE_INTERNAL) { - mouse_reset(); - mouse_set_poll(mse_poll, ams); - } -} - - -void -machine_amstrad_1512_init(const machine_t *model, void *arg) -{ - amstrad_common_init(model, arg, 0); -} - - -void -machine_amstrad_1640_init(const machine_t *model, void *arg) -{ - amstrad_common_init(model, arg, 1); -} - - -void -machine_amstrad_200_init(const machine_t *model, void *arg) -{ - amstrad_common_init(model, arg, 2); -} - - -void -machine_amstrad_2086_init(const machine_t *model, void *arg) -{ - amstrad_common_init(model, arg, 3); -} - - -void -machine_amstrad_3086_init(const machine_t *model, void *arg) -{ - amstrad_common_init(model, arg, 4); -} - - -void -machine_amstrad_mega_init(const machine_t *model, void *arg) -{ - amstrad_common_init(model, arg, 5); -} +/* + * 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. + * + * Emulation of the Amstrad series of PC's: PC1512, PC1640 and + * PC200, including their keyboard, mouse and video devices, as + * well as the PC2086 and PC3086 systems. + * + * PC1512: The PC1512 extends CGA with a bit-planar 640x200x16 mode. + * Most CRTC registers are fixed. + * + * The Technical Reference Manual lists the video waitstate + * time as between 12 and 46 cycles. We currently always use + * the lower number. + * + * PC1640: Mostly standard EGA, but with CGA & Hercules emulation. + * + * PC200: CGA with some NMI stuff. But we don't need that as it's only + * used for TV and LCD displays, and we're emulating a CRT. + * + * TODO: This module is not complete yet: + * PC1512: The BIOS assumes 512K RAM, because I cannot figure out how to + * read the status of the LK4 jumper on the mainboard, which is + * somehow linked to the bus gate array on the NDMACS line... + * PC1612: EGA mode does not seem to work in the PC1640; it works fine + * in alpha mode, but in highres ("ECD350") mode, it displays + * some semi-random junk. Video-memory pointer maybe? + * BIOSES: I need to re-do the bios.txt format so we can load non-BIOS + * ROM files for a given machine, such as font roms here.. + * + * Version: @(#)m_amstrad.c 1.0.19 2018/10/05 + * + * Authors: Fred N. van Kempen, + * Miran Grca, + * Sarah Walker, + * + * Copyright 2017,2018 Fred N. van Kempen. + * Copyright 2016-2018 Miran Grca. + * Copyright 2008-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. + */ +#include +#include +#include +#include +#include +#define dbglog kbd_log +#include "../emu.h" +#include "../cpu/cpu.h" +#include "../io.h" +#include "../mem.h" +#include "../rom.h" +#include "../timer.h" +#include "../device.h" +#include "../nvr.h" +#include "../devices/system/nmi.h" +#include "../devices/system/pic.h" +#include "../devices/system/pit.h" +#include "../devices/system/ppi.h" +#include "../devices/ports/parallel.h" +#include "../devices/input/keyboard.h" +#include "../devices/input/mouse.h" +#include "../devices/floppy/fdd.h" +#include "../devices/floppy/fdc.h" +#include "../devices/sound/sound.h" +#include "../devices/sound/snd_speaker.h" +#include "../devices/video/video.h" +#include "../devices/video/vid_cga.h" +#include "../devices/video/vid_ega.h" +#include "machine.h" + + +#define STAT_PARITY 0x80 +#define STAT_RTIMEOUT 0x40 +#define STAT_TTIMEOUT 0x20 +#define STAT_LOCK 0x10 +#define STAT_CD 0x08 +#define STAT_SYSFLAG 0x04 +#define STAT_IFULL 0x02 +#define STAT_OFULL 0x01 + + +typedef struct { + rom_t bios_rom; /* 1640 */ + cga_t cga; /* 1640/200 */ + ega_t ega; /* 1640 */ + uint8_t crtc[32]; + int crtcreg; + int cga_enabled; /* 1640 */ + uint8_t cgacol, + cgamode, + stat; + uint8_t plane_write, /* 1512/200 */ + plane_read, /* 1512/200 */ + border; /* 1512/200 */ + int linepos, + displine; + int sc, vc; + int cgadispon; + int con, coff, + cursoron, + cgablink; + int64_t vsynctime; + int vadj; + uint16_t ma, maback; + int dispon; + int blink; + int64_t dispontime, /* 1512/1640 */ + dispofftime; /* 1512/1640 */ + int64_t vidtime; /* 1512/1640 */ + int firstline, + lastline; + uint8_t *vram; +} amsvid_t; + +typedef struct { + /* Machine stuff. */ + int8_t type; + uint8_t dead; + uint8_t stat1, + stat2; + + /* Keyboard stuff. */ + int8_t wantirq; + uint8_t key_waiting; + uint8_t pa; + uint8_t pb; + + /* Mouse stuff. */ + uint8_t mousex, + mousey; + int oldb; + + /* Video stuff. */ + amsvid_t *vid; +} amstrad_t; + + +static uint8_t key_queue[16]; +static int key_queue_start = 0, + key_queue_end = 0; +static const uint8_t crtc_mask[32] = { + 0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f, + 0xf3, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const video_timings_t pc1512_timing = {VID_BUS,0,0,0,0,0,0}; +static const video_timings_t pc1640_timing = {VID_ISA,8,16,32,8,16,32}; +static const video_timings_t pc200_timing = {VID_ISA,8,16,32,8,16,32}; +static const video_timings_t pvga1a_timing = {VID_ISA,6,8,16,6,8,16}; +static const video_timings_t wd90c11_timing = {VID_ISA,3,3,6,5,5,10}; + + +static void +recalc_timings_1512(amsvid_t *vid) +{ + double _dispontime, _dispofftime, disptime; + + disptime = 128; /*Fixed on PC1512*/ + _dispontime = 80; + _dispofftime = disptime - _dispontime; + _dispontime *= CGACONST; + _dispofftime *= CGACONST; + vid->dispontime = (int64_t)(_dispontime * (1 << TIMER_SHIFT)); + vid->dispofftime = (int64_t)(_dispofftime * (1 << TIMER_SHIFT)); +} + + +static void +vid_out_1512(uint16_t addr, uint8_t val, void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + uint8_t old; + + switch (addr) { + case 0x03d4: + vid->crtcreg = val & 31; + return; + + case 0x03d5: + old = vid->crtc[vid->crtcreg]; + vid->crtc[vid->crtcreg] = val & crtc_mask[vid->crtcreg]; + if (old != val) { + if (vid->crtcreg < 0xe || vid->crtcreg > 0x10) { + fullchange = changeframecount; + recalc_timings_1512(vid); + } + } + return; + + case 0x03d8: + if ((val & 0x12) == 0x12 && (vid->cgamode & 0x12) != 0x12) { + vid->plane_write = 0xf; + vid->plane_read = 0; + } + vid->cgamode = val; + return; + + case 0x03d9: + vid->cgacol = val; + return; + + case 0x03dd: + vid->plane_write = val; + return; + + case 0x03de: + vid->plane_read = val & 3; + return; + + case 0x03df: + vid->border = val; + return; + } +} + + +static uint8_t +vid_in_1512(uint16_t addr, void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + uint8_t ret = 0xff; + + switch (addr) { + case 0x03d4: + ret = vid->crtcreg; + + case 0x03d5: + ret = vid->crtc[vid->crtcreg]; + + case 0x03da: + ret = vid->stat; + } + + return(ret); +} + + +static void +vid_write_1512(uint32_t addr, uint8_t val, void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + + cycles -= 12; + addr &= 0x3fff; + + if ((vid->cgamode & 0x12) == 0x12) { + if (vid->plane_write & 1) vid->vram[addr] = val; + if (vid->plane_write & 2) vid->vram[addr | 0x4000] = val; + if (vid->plane_write & 4) vid->vram[addr | 0x8000] = val; + if (vid->plane_write & 8) vid->vram[addr | 0xc000] = val; + } else + vid->vram[addr] = val; +} + + +static uint8_t +vid_read_1512(uint32_t addr, void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + + cycles -= 12; + addr &= 0x3fff; + + if ((vid->cgamode & 0x12) == 0x12) + return(vid->vram[addr | (vid->plane_read << 14)]); + + return(vid->vram[addr]); +} + + +static void +vid_poll_1512(void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + uint16_t ca = (vid->crtc[15] | (vid->crtc[14] << 8)) & 0x3fff; + int drawcursor; + int x, c; + uint8_t chr, attr; + uint16_t dat, dat2, dat3, dat4; + int cols[4]; + int col; + int oldsc; + + if (! vid->linepos) { + vid->vidtime += vid->dispofftime; + vid->stat |= 1; + vid->linepos = 1; + oldsc = vid->sc; + if (vid->dispon) { + if (vid->displine < vid->firstline) { + vid->firstline = vid->displine; + video_wait_for_buffer(); + } + vid->lastline = vid->displine; + for (c = 0; c < 8; c++) { + if ((vid->cgamode & 0x12) == 0x12) { + buffer->line[vid->displine][c] = (vid->border & 15) + 16; + if (vid->cgamode & 1) + buffer->line[vid->displine][c + (vid->crtc[1] << 3) + 8] = 0; + else + buffer->line[vid->displine][c + (vid->crtc[1] << 4) + 8] = 0; + } else { + buffer->line[vid->displine][c] = (vid->cgacol & 15) + 16; + if (vid->cgamode & 1) + buffer->line[vid->displine][c + (vid->crtc[1] << 3) + 8] = (vid->cgacol & 15) + 16; + else + buffer->line[vid->displine][c + (vid->crtc[1] << 4) + 8] = (vid->cgacol & 15) + 16; + } + } + if (vid->cgamode & 1) { + for (x = 0; x < 80; x++) { + chr = vid->vram[ ((vid->ma << 1) & 0x3fff)]; + attr = vid->vram[(((vid->ma << 1) + 1) & 0x3fff)]; + drawcursor = ((vid->ma == ca) && vid->con && vid->cursoron); + if (vid->cgamode & 0x20) { + cols[1] = (attr & 15) + 16; + cols[0] = ((attr >> 4) & 7) + 16; + if ((vid->blink & 16) && (attr & 0x80) && !drawcursor) + cols[1] = cols[0]; + } else { + cols[1] = (attr & 15) + 16; + cols[0] = (attr >> 4) + 16; + } + if (drawcursor) { + for (c = 0; c < 8; c++) + buffer->line[vid->displine][(x << 3) + c + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0] ^ 15; + } else { + for (c = 0; c < 8; c++) + buffer->line[vid->displine][(x << 3) + c + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0]; + } + vid->ma++; + } + } else if (! (vid->cgamode & 2)) { + for (x = 0; x < 40; x++) { + chr = vid->vram[((vid->ma << 1) & 0x3fff)]; + attr = vid->vram[(((vid->ma << 1) + 1) & 0x3fff)]; + drawcursor = ((vid->ma == ca) && vid->con && vid->cursoron); + if (vid->cgamode & 0x20) { + cols[1] = (attr & 15) + 16; + cols[0] = ((attr >> 4) & 7) + 16; + if ((vid->blink & 16) && (attr & 0x80)) + cols[1] = cols[0]; + } else { + cols[1] = (attr & 15) + 16; + cols[0] = (attr >> 4) + 16; + } + vid->ma++; + if (drawcursor) { + for (c = 0; c < 8; c++) + buffer->line[vid->displine][(x << 4) + (c << 1) + 8] = + buffer->line[vid->displine][(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0] ^ 15; + } else { + for (c = 0; c < 8; c++) + buffer->line[vid->displine][(x << 4) + (c << 1) + 8] = + buffer->line[vid->displine][(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0]; + } + } + } else if (! (vid->cgamode & 16)) { + cols[0] = (vid->cgacol & 15) | 16; + col = (vid->cgacol & 16) ? 24 : 16; + if (vid->cgamode & 4) { + cols[1] = col | 3; + cols[2] = col | 4; + cols[3] = col | 7; + } else if (vid->cgacol & 32) { + cols[1] = col | 3; + cols[2] = col | 5; + cols[3] = col | 7; + } else { + cols[1] = col | 2; + cols[2] = col | 4; + cols[3] = col | 6; + } + for (x = 0; x < 40; x++) { + dat = (vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000)] << 8) | vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000) + 1]; + vid->ma++; + for (c = 0; c < 8; c++) { + buffer->line[vid->displine][(x << 4) + (c << 1) + 8] = + buffer->line[vid->displine][(x << 4) + (c << 1) + 1 + 8] = cols[dat >> 14]; + dat <<= 2; + } + } + } else { + for (x = 0; x < 40; x++) { + ca = ((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000); + dat = (vid->vram[ca] << 8) | vid->vram[ca + 1]; + dat2 = (vid->vram[ca + 0x4000] << 8) | vid->vram[ca + 0x4001]; + dat3 = (vid->vram[ca + 0x8000] << 8) | vid->vram[ca + 0x8001]; + dat4 = (vid->vram[ca + 0xc000] << 8) | vid->vram[ca + 0xc001]; + + vid->ma++; + for (c = 0; c < 16; c++) { + buffer->line[vid->displine][(x << 4) + c + 8] = (((dat >> 15) | ((dat2 >> 15) << 1) | ((dat3 >> 15) << 2) | ((dat4 >> 15) << 3)) & (vid->cgacol & 15)) + 16; + dat <<= 1; + dat2 <<= 1; + dat3 <<= 1; + dat4 <<= 1; + } + } + } + } else { + cols[0] = ((vid->cgamode & 0x12) == 0x12) ? 0 : (vid->cgacol & 15) + 16; + if (vid->cgamode & 1) + cga_hline(buffer, 0, vid->displine, (vid->crtc[1] << 3) + 16, cols[0]); + else + cga_hline(buffer, 0, vid->displine, (vid->crtc[1] << 4) + 16, cols[0]); + } + + vid->sc = oldsc; + if (vid->vsynctime) + vid->stat |= 8; + vid->displine++; + if (vid->displine >= 360) + vid->displine = 0; + } else { + vid->vidtime += vid->dispontime; + if ((vid->lastline - vid->firstline) == 199) + vid->dispon = 0; /*Amstrad PC1512 always displays 200 lines, regardless of CRTC settings*/ + if (vid->dispon) + vid->stat &= ~1; + vid->linepos = 0; + if (vid->vsynctime) { + vid->vsynctime--; + if (! vid->vsynctime) + vid->stat &= ~8; + } + if (vid->sc == (vid->crtc[11] & 31)) { + vid->con = 0; + vid->coff = 1; + } + if (vid->vadj) { + vid->sc++; + vid->sc &= 31; + vid->ma = vid->maback; + vid->vadj--; + if (! vid->vadj) { + vid->dispon = 1; + vid->ma = vid->maback = (vid->crtc[13] | (vid->crtc[12] << 8)) & 0x3fff; + vid->sc = 0; + } + } else if (vid->sc == vid->crtc[9]) { + vid->maback = vid->ma; + vid->sc = 0; + vid->vc++; + vid->vc &= 127; + + if (vid->displine == 32) { + vid->vc = 0; + vid->vadj = 6; + if ((vid->crtc[10] & 0x60) == 0x20) + vid->cursoron = 0; + else + vid->cursoron = vid->blink & 16; + } + + if (vid->displine >= 262) { + vid->dispon = 0; + vid->displine = 0; + vid->vsynctime = 46; + + if (vid->cgamode&1) + x = (vid->crtc[1] << 3) + 16; + else + x = (vid->crtc[1] << 4) + 16; + vid->lastline++; + + if ((x != xsize) || ((vid->lastline - vid->firstline) != ysize) || video_force_resize_get()) { + xsize = x; + ysize = vid->lastline - vid->firstline; + if (xsize < 64) xsize = 656; + if (ysize < 32) ysize = 200; + set_screen_size(xsize, (ysize << 1) + 16); + + if (video_force_resize_get()) + video_force_resize_set(0); + } + + video_blit_memtoscreen_8(0, vid->firstline - 4, 0, (vid->lastline - vid->firstline) + 8, xsize, (vid->lastline - vid->firstline) + 8); + + video_res_x = xsize - 16; + video_res_y = ysize; + if (vid->cgamode & 1) { + video_res_x /= 8; + video_res_y /= vid->crtc[9] + 1; + video_bpp = 0; + } else if (! (vid->cgamode & 2)) { + video_res_x /= 16; + video_res_y /= vid->crtc[9] + 1; + video_bpp = 0; + } else if (! (vid->cgamode & 16)) { + video_res_x /= 2; + video_bpp = 2; + } else { + video_bpp = 4; + } + + vid->firstline = 1000; + vid->lastline = 0; + vid->blink++; + } + } else { + vid->sc++; + vid->sc &= 31; + vid->ma = vid->maback; + } + if (vid->sc == (vid->crtc[10] & 31)) + vid->con = 1; + } +} + + +static void +vid_init_1512(amstrad_t *ams) +{ + amsvid_t *vid; + + /* Allocate a video controller block. */ + vid = (amsvid_t *)mem_alloc(sizeof(amsvid_t)); + memset(vid, 0x00, sizeof(amsvid_t)); + + vid->vram = (uint8_t *)mem_alloc(0x10000); + vid->cgacol = 7; + vid->cgamode = 0x12; + + timer_add(vid_poll_1512, &vid->vidtime, TIMER_ALWAYS_ENABLED, vid); + mem_map_add(&vid->cga.mapping, 0xb8000, 0x08000, + vid_read_1512, NULL, NULL, vid_write_1512, NULL, NULL, + NULL, 0, vid); + io_sethandler(0x03d0, 16, + vid_in_1512, NULL, NULL, vid_out_1512, NULL, NULL, vid); + + overscan_x = overscan_y = 16; + + video_inform(VID_TYPE_CGA, &pc1512_timing); + + ams->vid = vid; +} + + +static void +vid_close_1512(void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + + free(vid->vram); + free(vid); +} + + +static void +vid_speed_change_1512(void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + + recalc_timings_1512(vid); +} + + +static const device_t vid_1512_device = { + "Amstrad PC1512 (video)", + 0, + 0, + NULL, vid_close_1512, NULL, + NULL, + vid_speed_change_1512, + NULL, + &pc1512_timing, + NULL +}; + + +static void +recalc_timings_1640(amsvid_t *vid) +{ + cga_recalctimings(&vid->cga); + ega_recalctimings(&vid->ega); + + if (vid->cga_enabled) { + overscan_x = overscan_y = 16; + + vid->dispontime = vid->cga.dispontime; + vid->dispofftime = vid->cga.dispofftime; + } else { + overscan_x = 16; overscan_y = 28; + + vid->dispontime = vid->ega.dispontime; + vid->dispofftime = vid->ega.dispofftime; + } +} + + +static void +vid_out_1640(uint16_t addr, uint8_t val, void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + + switch (addr) { + case 0x03db: + vid->cga_enabled = val & 0x40; + if (vid->cga_enabled) { + mem_map_enable(&vid->cga.mapping); + mem_map_disable(&vid->ega.mapping); + video_inform(VID_TYPE_CGA, &pc1640_timing); + } else { + mem_map_disable(&vid->cga.mapping); + switch (vid->ega.gdcreg[6] & 0xc) { + case 0x0: /*128k at A0000*/ + mem_map_set_addr(&vid->ega.mapping, + 0xa0000, 0x20000); + break; + + case 0x4: /*64k at A0000*/ + mem_map_set_addr(&vid->ega.mapping, + 0xa0000, 0x10000); + break; + + case 0x8: /*32k at B0000*/ + mem_map_set_addr(&vid->ega.mapping, + 0xb0000, 0x08000); + break; + + case 0xC: /*32k at B8000*/ + mem_map_set_addr(&vid->ega.mapping, + 0xb8000, 0x08000); + break; + } + video_inform(VID_TYPE_SPEC, &pc1640_timing); + } + return; + } + + if (vid->cga_enabled) + cga_out(addr, val, &vid->cga); + else + ega_out(addr, val, &vid->ega); +} + + +static uint8_t +vid_in_1640(uint16_t addr, void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + +#if 0 + switch (addr) { + } +#endif + + if (vid->cga_enabled) + return(cga_in(addr, &vid->cga)); + else + return(ega_in(addr, &vid->ega)); +} + + +static void +vid_poll_1640(void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + + if (vid->cga_enabled) { + overscan_x = overscan_y = 16; + + vid->cga.vidtime = vid->vidtime; + cga_poll(&vid->cga); + vid->vidtime = vid->cga.vidtime; + } else { + overscan_x = 16; overscan_y = 28; + + vid->ega.vidtime = vid->vidtime; + ega_poll(&vid->ega); + vid->vidtime = vid->ega.vidtime; + } +} + + +static void +vid_init_1640(amstrad_t *ams, wchar_t *fn, int sz) +{ + amsvid_t *vid; + + /* Allocate a video controller block. */ + vid = (amsvid_t *)mem_alloc(sizeof(amsvid_t)); + memset(vid, 0x00, sizeof(amsvid_t)); + + /* Load the BIOS. */ + rom_init(&vid->bios_rom, fn, 0xc0000, sz, sz - 1, 0, 0); + + ega_init(&vid->ega, 9, 0); + vid->cga.vram = vid->ega.vram; + vid->cga_enabled = 1; + cga_init(&vid->cga); + + mem_map_add(&vid->cga.mapping, 0xb8000, 0x08000, + cga_read,NULL,NULL, cga_write,NULL,NULL, NULL, 0, &vid->cga); + mem_map_add(&vid->ega.mapping, 0, 0, + ega_read,NULL,NULL, ega_write,NULL,NULL, NULL, 0, &vid->ega); + io_sethandler(0x03a0, 64, + vid_in_1640, NULL, NULL, vid_out_1640, NULL, NULL, vid); + + timer_add(vid_poll_1640, &vid->vidtime, TIMER_ALWAYS_ENABLED, vid); + + overscan_x = overscan_y = 16; + + video_inform(VID_TYPE_CGA, &pc1640_timing); + + ams->vid = vid; +} + + +static void +vid_close_1640(void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + + free(vid->ega.vram); + free(vid); +} + + +static void +vid_speed_changed_1640(void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + + recalc_timings_1640(vid); +} + + +static const device_t vid_1640_device = { + "Amstrad PC1640 (video)", + 0, + 0, + NULL, vid_close_1640, NULL, + NULL, + vid_speed_changed_1640, + NULL, + &pc1640_timing, + NULL +}; + + +static void +vid_out_200(uint16_t addr, uint8_t val, void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + cga_t *cga = &vid->cga; + uint8_t old; + + switch (addr) { + case 0x03d5: + if (!(vid->plane_read & 0x40) && cga->crtcreg <= 11) { + if (vid->plane_read & 0x80) + nmi = 1; + + vid->plane_write = 0x20 | (cga->crtcreg & 0x1f); + vid->border = val; + return; + } + old = cga->crtc[cga->crtcreg]; + cga->crtc[cga->crtcreg] = val & crtc_mask[cga->crtcreg]; + if (old != val) { + if (cga->crtcreg < 0xe || cga->crtcreg > 0x10) { + fullchange = changeframecount; + cga_recalctimings(cga); + } + } + return; + + case 0x03d8: + old = cga->cgamode; + cga->cgamode = val; + if ((cga->cgamode ^ old) & 3) + cga_recalctimings(cga); + vid->plane_write |= 0x80; + if (vid->plane_read & 0x80) + nmi = 1; + return; + + case 0x03de: + vid->plane_read = val; + vid->plane_write = 0x1f; + if (val & 0x80) + vid->plane_write |= 0x40; + return; + } + + cga_out(addr, val, cga); +} + + +static uint8_t +vid_in_200(uint16_t addr, void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + cga_t *cga = &vid->cga; + uint8_t ret; + + switch (addr) { + case 0x03d8: + return(cga->cgamode); + + case 0x03dd: + ret = vid->plane_write; + vid->plane_write &= 0x1f; + nmi = 0; + return(ret); + + case 0x03de: + return((vid->plane_read & 0xc7) | 0x10); /*External CGA*/ + + case 0x03df: + return(vid->border); + } + + return(cga_in(addr, cga)); +} + + +static void +vid_init_200(amstrad_t *ams) +{ + amsvid_t *vid; + cga_t *cga; + + /* Allocate a video controller block. */ + vid = (amsvid_t *)mem_alloc(sizeof(amsvid_t)); + memset(vid, 0x00, sizeof(amsvid_t)); + + cga = &vid->cga; + cga->vram = (uint8_t *)mem_alloc(0x4000); + cga_init(cga); + + mem_map_add(&vid->cga.mapping, 0xb8000, 0x08000, + cga_read,NULL,NULL, cga_write,NULL,NULL, NULL, 0, cga); + io_sethandler(0x03d0, 16, + vid_in_200, NULL, NULL, vid_out_200, NULL, NULL, vid); + + timer_add(cga_poll, &cga->vidtime, TIMER_ALWAYS_ENABLED, cga); + + overscan_x = overscan_y = 16; + + video_inform(VID_TYPE_CGA, &pc200_timing); + + ams->vid = vid; +} + + +static void +vid_close_200(void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + + free(vid->cga.vram); + + free(vid); +} + + +static void +vid_speed_changed_200(void *priv) +{ + amsvid_t *vid = (amsvid_t *)priv; + + cga_recalctimings(&vid->cga); +} + + +static const device_t vid_200_device = { + "Amstrad PC200 (video)", + 0, + 0, + NULL, vid_close_200, NULL, + NULL, + vid_speed_changed_200, + NULL, + &pc200_timing, + NULL +}; + + +static void +mse_write(uint16_t addr, uint8_t val, void *priv) +{ + amstrad_t *ams = (amstrad_t *)priv; + + if (addr == 0x78) + ams->mousex = 0; + else + ams->mousey = 0; +} + + +static uint8_t +mse_read(uint16_t addr, void *priv) +{ + amstrad_t *ams = (amstrad_t *)priv; + + if (addr == 0x78) + return(ams->mousex); + + return(ams->mousey); +} + + +static int +mse_poll(int x, int y, int z, int b, void *priv) +{ + amstrad_t *ams = (amstrad_t *)priv; + + ams->mousex += x; + ams->mousey -= y; + + if ((b & 1) && !(ams->oldb & 1)) + keyboard_send(0x7e); + if ((b & 2) && !(ams->oldb & 2)) + keyboard_send(0x7d); + if (!(b & 1) && (ams->oldb & 1)) + keyboard_send(0xfe); + if (!(b & 2) && (ams->oldb & 2)) + keyboard_send(0xfd); + + ams->oldb = b; + + return(0); +} + + +static void +kbd_adddata(uint16_t val) +{ + key_queue[key_queue_end] = (uint8_t)(val&0xff); + DBGLOG(1, "AMSkb: %02X added to key queue at %i\n", val, key_queue_end); + key_queue_end = (key_queue_end + 1) & 0xf; +} + + +static void +kbd_adddata_ex(uint16_t val) +{ + kbd_adddata_process(val, kbd_adddata); +} + + +static void +kbd_write(uint16_t port, uint8_t val, void *priv) +{ + amstrad_t *ams = (amstrad_t *)priv; + + DBGLOG(2, "AMSkb: write %04X %02X %02X\n", port, val, ams->pb); + + switch (port) { + case 0x61: + /* + * PortB - System Control. + * + * 7 Enable Status-1/Disable Keyboard Code on Port A. + * 6 Enable incoming Keyboard Clock. + * 5 Prevent external parity errors from causing NMI. + * 4 Disable parity checking of on-board system Ram. + * 3 Undefined (Not Connected). + * 2 Enable Port C LSB / Disable MSB. (See 1.8.3) + * 1 Speaker Drive. + * 0 8253 GATE 2 (Speaker Modulate). + * + * This register is controlled by BIOS and/or ROS. + */ + DBGLOG(1, "AMSkb: write PB %02x (%02x)\n", val, ams->pb); + if (!(ams->pb & 0x40) && (val & 0x40)) { /*Reset keyboard*/ + DEBUG("AMSkb: reset keyboard\n"); + kbd_adddata(0xaa); + } + ams->pb = val; + ppi.pb = val; + + timer_process(); + timer_update_outstanding(); + + speaker_update(); + speaker_gated = val & 0x01; + speaker_enable = val & 0x02; + if (speaker_enable) + was_speaker_enable = 1; + pit_set_gate(&pit, 2, val & 0x01); + + if (val & 0x80) { + /* Keyboard enabled, so enable PA reading. */ + ams->pa = 0x00; + } + break; + + case 0x63: + break; + + case 0x64: + ams->stat1 = val; + break; + + case 0x65: + ams->stat2 = val; + break; + + case 0x66: + pc_reset(1); + break; + + default: + ERRLOG("AMSkb: bad keyboard write %04X %02X\n", port, val); + } +} + + +static uint8_t +kbd_read(uint16_t port, void *priv) +{ + amstrad_t *ams = (amstrad_t *)priv; + uint8_t ret = 0xff; + + switch (port) { + case 0x60: + if (ams->pb & 0x80) { + /* + * PortA - System Status 1 + * + * 7 Always 0 (KBD7) + * 6 Second Floppy disk drive installed (KBD6) + * 5 DDM1 - Default Display Mode bit 1 (KBD5) + * 4 DDM0 - Default Display Mode bit 0 (KBD4) + * 3 Always 1 (KBD3) + * 2 Always 1 (KBD2) + * 1 8087 NDP installed (KBD1) + * 0 Always 1 (KBD0) + * + * DDM00 + * 00 unknown, external color? + * 01 Color,alpha,40x25, bright white on black. + * 10 Color,alpha,80x25, bright white on black. + * 11 External Monochrome,80x25. + * + * Following a reset, the hardware selects VDU mode + * 2. The ROS then sets the initial VDU state based + * on the DDM value. + */ + ret = (0x0d | ams->stat1) & 0x7f; + } else { + ret = ams->pa; + if (key_queue_start == key_queue_end) { + ams->wantirq = 0; + } else { + ams->key_waiting = key_queue[key_queue_start]; + key_queue_start = (key_queue_start + 1) & 0xf; + ams->wantirq = 1; + } + } + break; + + case 0x61: + ret = ams->pb; + break; + + case 0x62: + /* + * PortC - System Status 2. + * + * 7 On-board system RAM parity error. + * 6 External parity error (I/OCHCK from expansion bus). + * 5 8253 PIT OUT2 output. + * 4 Undefined (Not Connected). + *------------------------------------------- + * LSB MSB (depends on PB2) + *------------------------------------------- + * 3 RAM3 Undefined + * 2 RAM2 Undefined + * 1 RAM1 Undefined + * 0 RAM0 RAM4 + * + * PC7 is forced to 0 when on-board system RAM parity + * checking is disabled by PB4. + * + * RAM4:0 + * 01110 512K bytes on-board. + * 01111 544K bytes (32K external). + * 10000 576K bytes (64K external). + * 10001 608K bytes (96K external). + * 10010 640K bytes (128K external or fitted on-board). + */ + if (ams->pb & 0x04) + ret = ams->stat2 & 0x0f; + else + ret = ams->stat2 >> 4; + ret |= (ppispeakon ? 0x20 : 0); + if (nmi) + ret |= 0x40; + break; + + default: + ERRLOG("AMDkb: bad keyboard read %04X\n", port); + } + + return(ret); +} + + +static void +kbd_poll(void *priv) +{ + amstrad_t *ams = (amstrad_t *)priv; + + keyboard_delay += (1000 * TIMER_USEC); + + if (ams->wantirq) { + ams->wantirq = 0; + ams->pa = ams->key_waiting; + picint(2); + DBGLOG(1, "AMSkb: take IRQ\n"); + } + + if (key_queue_start != key_queue_end && !ams->pa) { + ams->key_waiting = key_queue[key_queue_start]; + DBGLOG(1, "AMSkb: reading %02X from the key queue at %i\n", + ams->key_waiting, key_queue_start); + key_queue_start = (key_queue_start + 1) & 0xf; + ams->wantirq = 1; + } +} + + +static void +ams_write(uint16_t port, uint8_t val, void *priv) +{ + amstrad_t *ams = (amstrad_t *)priv; + + switch (port) { + case 0xdead: + ams->dead = val; + break; + } +} + + +static uint8_t +ams_read(uint16_t port, void *priv) +{ + amstrad_t *ams = (amstrad_t *)priv; + uint8_t ret = 0xff; + + switch (port) { + case 0x0379: /* printer control, also set LK1-3. + * 0 English Language. + * 1 German Language. + * 2 French Language. + * 3 Spanish Language. + * 4 Danish Language. + * 5 Swedish Language. + * 6 Italian Language. + * 7 Diagnostic Mode. + */ + ret = 0x02; /* ENGLISH. no Diags mode */ + break; + + case 0x037a: /* printer status */ + switch(ams->type) { + case 0: + ret = 0x20; + break; + + case 2: + ret = 0x80; + break; + + default: + ret = 0x00; + } + break; + + case 0xdead: + ret = ams->dead; + break; + } + + return(ret); +} + + +static void +amstrad_common_init(const machine_t *model, void *arg, int type) +{ + romdef_t *roms = (romdef_t *)arg; + amstrad_t *ams; + + ams = (amstrad_t *)mem_alloc(sizeof(amstrad_t)); + memset(ams, 0x00, sizeof(amstrad_t)); + ams->type = type; + + machine_common_init(model, arg); + + nmi_init(); + + device_add(&amstrad_nvr_device); + +//FIXME: parallel_remove_amstrad(); + + io_sethandler(0x0078, 1, + mse_read, NULL, NULL, mse_write, NULL, NULL, ams); + + io_sethandler(0x007a, 1, + mse_read, NULL, NULL, mse_write, NULL, NULL, ams); + + io_sethandler(0x0379, 2, + ams_read, NULL, NULL, NULL, NULL, NULL, ams); + + io_sethandler(0xdead, 1, + ams_read, NULL, NULL, ams_write, NULL, NULL, ams); + + switch(ams->type) { + case 0: + device_add(&fdc_xt_device); + if (video_card == VID_INTERNAL) { + /* Load the PC1512 CGA Character Set ROM. */ + video_load_font(roms->fontfn, roms->fontnum); + + /* Initialize the internal CGA controller. */ + vid_init_1512(ams); + device_add_ex(&vid_1512_device, ams->vid); + } + break; + + case 1: + device_add(&fdc_xt_device); + if (video_card == VID_INTERNAL) { + /* Load the BIOS for the internal CGA/EGA. */ + vid_init_1640(ams, roms->vidfn, roms->vidsz); + device_add_ex(&vid_1640_device, ams->vid); + } + break; + + case 2: + device_add(&fdc_xt_device); + if (video_card == VID_INTERNAL) { + /* Load the PC200 CGA Character Set ROM. */ + video_load_font(roms->fontfn, roms->fontnum); + + vid_init_200(ams); + device_add_ex(&vid_200_device, ams->vid); + } + break; + + case 3: + device_add(&fdc_at_actlow_device); + if (video_card == VID_INTERNAL) { + device_add(¶dise_pvga1a_pc2086_device); + video_inform(VID_TYPE_SPEC, &pvga1a_timing); + } + break; + + case 4: + device_add(&fdc_at_actlow_device); + if (video_card == VID_INTERNAL) { + device_add(¶dise_pvga1a_pc3086_device); + video_inform(VID_TYPE_SPEC, &pvga1a_timing); + } + break; + + case 5: + device_add(&fdc_at_actlow_device); + if (video_card == VID_INTERNAL) { + device_add(¶dise_wd90c11_megapc_device); + video_inform(VID_TYPE_SPEC, &wd90c11_timing); + } + break; + } + + /* Initialize the (custom) keyboard/mouse interface. */ + ams->wantirq = 0; + io_sethandler(0x0060, 7, + kbd_read, NULL, NULL, kbd_write, NULL, NULL, ams); + timer_add(kbd_poll, &keyboard_delay, TIMER_ALWAYS_ENABLED, ams); + keyboard_set_table(scancode_xt); + keyboard_send = kbd_adddata_ex; + keyboard_scan = 1; + + /* Tell mouse driver about our internal mouse. */ + if (mouse_type == MOUSE_INTERNAL) { + mouse_reset(); + mouse_set_poll(mse_poll, ams); + } +} + + +void +machine_amstrad_1512_init(const machine_t *model, void *arg) +{ + amstrad_common_init(model, arg, 0); +} + + +void +machine_amstrad_1640_init(const machine_t *model, void *arg) +{ + amstrad_common_init(model, arg, 1); +} + + +void +machine_amstrad_200_init(const machine_t *model, void *arg) +{ + amstrad_common_init(model, arg, 2); +} + + +void +machine_amstrad_2086_init(const machine_t *model, void *arg) +{ + amstrad_common_init(model, arg, 3); +} + + +void +machine_amstrad_3086_init(const machine_t *model, void *arg) +{ + amstrad_common_init(model, arg, 4); +} + + +void +machine_amstrad_mega_init(const machine_t *model, void *arg) +{ + amstrad_common_init(model, arg, 5); +} diff --git a/src/machines/m_xt_t1000.c b/src/machines/m_xt_t1000.c index bc9f6b4..1f5f96d 100644 --- a/src/machines/m_xt_t1000.c +++ b/src/machines/m_xt_t1000.c @@ -1,1107 +1,1107 @@ -/* - * 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 hard drive is a JVC JD-3824R00-1 (20MB, 615/2/34) RLL - * 3.5" drive with custom 26-pin interface. This is what is - * known about the drive: - * - * JD-3824G is a 20MB hard disk drive that JVC made as one - * of OEM products. This HDD was shipped to Laptop PC - * manufactures. - * - * This HDD was discontinued about 10 years ago and JVC - * America does not have detail information because of the - * OEM product. - * - * This is the information that I have. - * [JD-3824G] - * 1. 3.5" 20MB Hard Disk Drive - * 2. JVC original interface (26 pin) - * This is not the IDE interface. This drive was made long - * before IDE standard. It required special controller board. - * This interface is not compatible with any other disk - * interface. - * - * Pin 1 GND Pin 2 -Read Data - * Pin 3 GND Pin 4 -Write data - * Pin 5 GND Pin 6 Reserved - * Pin 7 -Drive Select/+Power Save Pin 8 -Ship Ready - * Pin 9 GND Pin 10 +Read/-Write control - * Pin 11 -Motor On Pin 12 Head Select(+Head 0/ - Head1) - * Pin 13 -Direction In Pin 14 -Step - * Pin 15 -Write Fault Pin 16 -Seek Complete - * Pin 17 -Servo Gate Pin 18 -Index - * Pin 19 -Track 000 Pin 20 -Drive Ready - * Pin 21 GND Pin 22 +5V - * Pin 23 GND Pin 24 +5V - * Pin 25 GND Pin 26 +12V - * - * 3. Parameters( 2 heads, 34 sectors, 615 cylinders) - * 4. 2-7 RLL coding - * 5. Spindle Rotation: 2597 rpm - * 6. Data Transfer: 7.5M bps - * 7. Average Access: 78ms - * 8. Power Voltage: 5V and 12V - * - * (from Jeff Kishida, JVC Americas, Corp. - * Tel: 714-827-6267 Fax: 714-827-8740 - * Email: Jeff.Kishida@worldnet.att.net) - * - * The controller is on a separate PCB, and contains a T7518, - * DC2090P166A and a 27256 EPROM (label "036A") with BIOS. The - * interface for this is proprietary at the rogramming level. - * - * 01F2h: If hard drive is present, low 4 bits are 0Ch [20Mb] - * or 0Dh [10Mb]. - * - * 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. - * - * FIXME: The ROM drive should be re-done using the "option file". - * - * Version: @(#)m_xt_t1000.c 1.0.14 2018/09/22 - * - * Authors: Fred N. van Kempen, - * Miran Grca, - * John Elliott, - * - * Copyright 2018 Fred N. van Kempen. - * Copyright 2018 Miran Grca. - * Copyright 2017,2018 John Elliott. - * - * 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 "../emu.h" -#include "../cpu/cpu.h" -#include "../io.h" -#include "../mem.h" -#include "../rom.h" -#include "../device.h" -#include "../nvr.h" -#include "../devices/system/nmi.h" -#include "../devices/system/pit.h" -#include "../devices/ports/parallel.h" -#include "../devices/input/keyboard.h" -#include "../devices/floppy/fdd.h" -#include "../devices/floppy/fdc.h" -#include "../devices/disk/hdc.h" -#include "../devices/video/video.h" -#include "../plat.h" -#include "machine.h" -#include "m_xt_t1000.h" - - -#define T1000_ROMDOS_SIZE (512*1024UL) /* Max romdrive size is 512k */ -#define T1000_ROMDOS_PATH L"machines/toshiba/t1000/t1000dos.rom" - - -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 */ - int8_t rom_dos, - is_t1200; - - uint8_t rom_ctl; - uint32_t rom_offset; - uint8_t *romdrive; - mem_map_t rom_mapping; - - /* CONFIG.SYS drive. */ - wchar_t cfgsys_fn[128]; - uint16_t cfgsys_len; - uint8_t *cfgsys; - - /* 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_map_t nvr_mapping; /* T1200 NVRAM mapping */ - - /* EMS data */ - uint8_t ems_reg[4]; - mem_map_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; - - -/* 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) -{ - DEBUG("TC8521: ping\n"); -} - - -static void -tc8521_start(nvr_t *nvr) -{ - struct tm tm; - - /* Initialize the internal and chip times. */ - if (time_sync != TIME_SYNC_DISABLED) { - /* 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; - - DBGLOG(1, "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) -{ - t1000_t *sys = (t1000_t *)priv; - uint8_t ret; - - ret = sys->ems_reg[(addr >> 14) & 3]; - DBGLOG(1, "ems_in(%04x)=%02x\n", addr, ret); - - return(ret); -} - - -static void -ems_out(uint16_t addr, uint8_t val, void *priv) -{ - t1000_t *sys = (t1000_t *)priv; - int pg = (addr >> 14) & 3; - - DBGLOG(1, "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_map_enable(&sys->mapping[pg]); - mem_map_set_exec(&sys->mapping[pg], ram + sys->page_exec[pg]); - } else { - mem_map_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; - - DEBUG("EMS base set to %02x\n", val); - 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_map_set_addr(&ram_low_mapping, 0, 640 * 1024); - sys->is_640k = 1; - } else { - mem_map_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; - - DBGLOG(1, "ems_set_port(%d)", val & 0x0f); - 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; - } - - DBGLOG(1, " -> %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) -{ - 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); - - DBGLOG(1, "ems_read_ramw addr=%05x ", addr); - addr = sys->page_exec[pg] + (addr & 0x3FFF); - DBGLOG(1, "-> %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) -{ - 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; - - DBGLOG(1, "ems_write_ramw addr=%05x ", addr); - addr = sys->page_exec[pg] + (addr & 0x3fff); - DBGLOG(1, "-> %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) -{ - 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); -} - - -/* Load contents of "CONFIG.SYS" device from file. */ -static void -cfgsys_load(t1000_t *dev) -{ - char temp[128]; - FILE *f; - - /* Set up the file's name. */ - sprintf(temp, "%s_cfgsys.nvr", machine_get_internal_name()); - mbstowcs(dev->cfgsys_fn, temp, sizeof_w(dev->cfgsys_fn)); - - /* Now attempt to load the file. */ - memset(dev->cfgsys, 0x1a, dev->cfgsys_len); - f = plat_fopen(nvr_path(dev->cfgsys_fn), L"rb"); - if (f != NULL) { - INFO("NVR: loaded CONFIG.SYS from '%ls'\n", dev->cfgsys_fn); - (void)fread(dev->cfgsys, dev->cfgsys_len, 1, f); - fclose(f); - } else - INFO("NVR: initialized CONFIG.SYS for '%ls'\n", dev->cfgsys_fn); -} - - -/* Write the contents of "CONFIG.SYS" to file. */ -static void -cfgsys_save(t1000_t *dev) -{ - FILE *f; - - /* Avoids writing empty files. */ - if (dev->cfgsys_len < 160) return; - - f = plat_fopen(nvr_path(dev->cfgsys_fn), L"wb"); - if (f != NULL) { - INFO("NVR: saved CONFIG.SYS to '%ls'\n", dev->cfgsys_fn); - (void)fwrite(dev->cfgsys, dev->cfgsys_len, 1, f); - fclose(f); - } -} - - -#if 0 /*NOT_USED*/ -/* All RAM beyond 512K is non-volatile */ -static void -emsboard_load(t1000_t *dev) -{ - 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 -emsboard_save(t1000_t *dev) -{ - 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); - } - } -} -#endif - - -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->cfgsys[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->cfgsys[sys->nvr_addr] != val) - nvr_dosave = 1; - sys->cfgsys[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; - } -} - - -/* 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_ROMDOS_SIZE; - mem_map_set_addr(&sys->rom_mapping, 0xa0000, 0x10000); - mem_map_set_exec(&sys->rom_mapping, sys->romdrive + sys->rom_offset); - mem_map_enable(&sys->rom_mapping); - } else { - mem_map_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)])); -} - - -static const device_config_t m_xt_t1000_config[] = { - { - "rom_dos", "ROM DOS", CONFIG_SELECTION, "", 0, - { - { - "Disabled", 0 - }, - { - "Enabled", 1 - }, - { - "" - } - } - }, - { - "", "", -1 - } -}; - - -const device_t m_xt_t1000_device = { - "Toshiba T1000", - MACHINE_ISA, - 0, - NULL, NULL, NULL, - NULL, - NULL, NULL, NULL, - m_xt_t1000_config -}; - - -void -machine_xt_t1000_init(const machine_t *model, void *arg) -{ - FILE *f; - int pg; - - memset(&t1000, 0x00, 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. - */ - t1000.rom_dos = machine_get_config_int("rom_dos"); - if (t1000.rom_dos) { - f = plat_fopen(rom_path(T1000_ROMDOS_PATH), L"rb"); - if (f != NULL) { - t1000.romdrive = (uint8_t *)mem_alloc(T1000_ROMDOS_SIZE); - if (t1000.romdrive) { - memset(t1000.romdrive, 0xff, T1000_ROMDOS_SIZE); - fread(t1000.romdrive, T1000_ROMDOS_SIZE, 1, f); - } - fclose(f); - } - mem_map_add(&t1000.rom_mapping, 0xa0000, 0x10000, - t1000_read_rom,t1000_read_romw,t1000_read_roml, - NULL,NULL,NULL, NULL, MEM_MAPPING_INTERNAL, &t1000); - mem_map_disable(&t1000.rom_mapping); - } - - /* Map the EMS page frame */ - for (pg = 0; pg < 4; pg++) { - mem_map_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_map_disable(&t1000.mapping[pg]); - } - - /* Non-volatile RAM for CONFIG.SYS */ - t1000.cfgsys_len = 160; - t1000.cfgsys = (uint8_t *)mem_alloc(t1000.cfgsys_len); - io_sethandler(0xc0, 4, - t1000_read_nvram,NULL,NULL, - t1000_write_nvram,NULL,NULL, &t1000); - cfgsys_load(&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, arg); - - pit_set_out_func(&pit, 1, pit_refresh_timer_xt); - device_add(&keyboard_xt_device); - t1000.fdc = (fdc_t *)device_add(&fdc_xt_device); - nmi_init(); - - tc8521_init(&t1000.nvr, model->nvrsz); - - if (video_card == VID_INTERNAL) { - /* Load the T1000 CGA Font ROM. */ - video_load_font(L"machines/toshiba/t1000/t1000font.rom", 2); - - device_add(&t1000_video_device); - } -} - - -static uint8_t -t1200_nvram_read(uint32_t addr, void *priv) -{ - t1000_t *sys = (t1000_t *)priv; - - return(sys->cfgsys[addr & 0x7ff]); -} - - -static void -t1200_nvram_write(uint32_t addr, uint8_t val, void *priv) -{ - t1000_t *sys = (t1000_t *)priv; - - if (sys->cfgsys[addr & 0x7ff] != val) - nvr_dosave = 1; - - sys->cfgsys[addr & 0x7ff] = val; -} - - -void -machine_xt_t1200_init(const machine_t *model, void *arg) -{ - int pg; - - memset(&t1000, 0x00, sizeof(t1000)); - t1000.ems_port_index = 7; /* EMS disabled */ - t1000.is_t1200 = 1; - - /* Map the EMS page frame */ - for (pg = 0; pg < 4; pg++) { - mem_map_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_map_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, arg); - - /* Non-volatile RAM for CONFIG.SYS */ - t1000.cfgsys_len = 2048; - t1000.cfgsys = (uint8_t *)mem_alloc(t1000.cfgsys_len); - mem_map_add(&t1000.nvr_mapping, - 0x000f0000, t1000.cfgsys_len, - t1200_nvram_read,NULL,NULL, - t1200_nvram_write,NULL,NULL, - NULL, 0, &t1000); - cfgsys_load(&t1000); - - pit_set_out_func(&pit, 1, pit_refresh_timer_xt); - device_add(&keyboard_xt_device); - t1000.fdc = (fdc_t *)device_add(&fdc_toshiba_device); - nmi_init(); - - tc8521_init(&t1000.nvr, model->nvrsz); - - if (video_card == VID_INTERNAL) { - /* Load the T1200 CGA Font ROM. */ - video_load_font(L"machines/toshiba/t1200/t1000font.bin", 2); - - device_add(&t1200_video_device); - } - - if (hdc_type == HDC_INTERNAL) - (void)device_add(&xta_t1200_device); -} - - -void -machine_xt_t1x00_close(void) -{ - cfgsys_save(&t1000); -} - - -void -t1000_syskey(uint8_t andmask, uint8_t ormask, uint8_t xormask) -{ - t1000.syskeys &= ~andmask; - t1000.syskeys |= ormask; - t1000.syskeys ^= xormask; -} +/* + * 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 hard drive is a JVC JD-3824R00-1 (20MB, 615/2/34) RLL + * 3.5" drive with custom 26-pin interface. This is what is + * known about the drive: + * + * JD-3824G is a 20MB hard disk drive that JVC made as one + * of OEM products. This HDD was shipped to Laptop PC + * manufactures. + * + * This HDD was discontinued about 10 years ago and JVC + * America does not have detail information because of the + * OEM product. + * + * This is the information that I have. + * [JD-3824G] + * 1. 3.5" 20MB Hard Disk Drive + * 2. JVC original interface (26 pin) + * This is not the IDE interface. This drive was made long + * before IDE standard. It required special controller board. + * This interface is not compatible with any other disk + * interface. + * + * Pin 1 GND Pin 2 -Read Data + * Pin 3 GND Pin 4 -Write data + * Pin 5 GND Pin 6 Reserved + * Pin 7 -Drive Select/+Power Save Pin 8 -Ship Ready + * Pin 9 GND Pin 10 +Read/-Write control + * Pin 11 -Motor On Pin 12 Head Select(+Head 0/ - Head1) + * Pin 13 -Direction In Pin 14 -Step + * Pin 15 -Write Fault Pin 16 -Seek Complete + * Pin 17 -Servo Gate Pin 18 -Index + * Pin 19 -Track 000 Pin 20 -Drive Ready + * Pin 21 GND Pin 22 +5V + * Pin 23 GND Pin 24 +5V + * Pin 25 GND Pin 26 +12V + * + * 3. Parameters( 2 heads, 34 sectors, 615 cylinders) + * 4. 2-7 RLL coding + * 5. Spindle Rotation: 2597 rpm + * 6. Data Transfer: 7.5M bps + * 7. Average Access: 78ms + * 8. Power Voltage: 5V and 12V + * + * (from Jeff Kishida, JVC Americas, Corp. + * Tel: 714-827-6267 Fax: 714-827-8740 + * Email: Jeff.Kishida@worldnet.att.net) + * + * The controller is on a separate PCB, and contains a T7518, + * DC2090P166A and a 27256 EPROM (label "036A") with BIOS. The + * interface for this is proprietary at the rogramming level. + * + * 01F2h: If hard drive is present, low 4 bits are 0Ch [20Mb] + * or 0Dh [10Mb]. + * + * 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. + * + * FIXME: The ROM drive should be re-done using the "option file". + * + * Version: @(#)m_xt_t1000.c 1.0.14 2018/09/22 + * + * Authors: Fred N. van Kempen, + * Miran Grca, + * John Elliott, + * + * Copyright 2018 Fred N. van Kempen. + * Copyright 2018 Miran Grca. + * Copyright 2017,2018 John Elliott. + * + * 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 "../emu.h" +#include "../cpu/cpu.h" +#include "../io.h" +#include "../mem.h" +#include "../rom.h" +#include "../device.h" +#include "../nvr.h" +#include "../devices/system/nmi.h" +#include "../devices/system/pit.h" +#include "../devices/ports/parallel.h" +#include "../devices/input/keyboard.h" +#include "../devices/floppy/fdd.h" +#include "../devices/floppy/fdc.h" +#include "../devices/disk/hdc.h" +#include "../devices/video/video.h" +#include "../plat.h" +#include "machine.h" +#include "m_xt_t1000.h" + + +#define T1000_ROMDOS_SIZE (512*1024UL) /* Max romdrive size is 512k */ +#define T1000_ROMDOS_PATH L"machines/toshiba/t1000/t1000dos.rom" + + +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 */ + int8_t rom_dos, + is_t1200; + + uint8_t rom_ctl; + uint32_t rom_offset; + uint8_t *romdrive; + mem_map_t rom_mapping; + + /* CONFIG.SYS drive. */ + wchar_t cfgsys_fn[128]; + uint16_t cfgsys_len; + uint8_t *cfgsys; + + /* 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_map_t nvr_mapping; /* T1200 NVRAM mapping */ + + /* EMS data */ + uint8_t ems_reg[4]; + mem_map_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; + + +/* 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) +{ + DEBUG("TC8521: ping\n"); +} + + +static void +tc8521_start(nvr_t *nvr) +{ + struct tm tm; + + /* Initialize the internal and chip times. */ + if (time_sync != TIME_SYNC_DISABLED) { + /* 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; + + DBGLOG(1, "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) +{ + t1000_t *sys = (t1000_t *)priv; + uint8_t ret; + + ret = sys->ems_reg[(addr >> 14) & 3]; + DBGLOG(1, "ems_in(%04x)=%02x\n", addr, ret); + + return(ret); +} + + +static void +ems_out(uint16_t addr, uint8_t val, void *priv) +{ + t1000_t *sys = (t1000_t *)priv; + int pg = (addr >> 14) & 3; + + DBGLOG(1, "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_map_enable(&sys->mapping[pg]); + mem_map_set_exec(&sys->mapping[pg], ram + sys->page_exec[pg]); + } else { + mem_map_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; + + DEBUG("EMS base set to %02x\n", val); + 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_map_set_addr(&ram_low_mapping, 0, 640 * 1024); + sys->is_640k = 1; + } else { + mem_map_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; + + DBGLOG(1, "ems_set_port(%d)", val & 0x0f); + 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; + } + + DBGLOG(1, " -> %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) +{ + 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); + + DBGLOG(1, "ems_read_ramw addr=%05x ", addr); + addr = sys->page_exec[pg] + (addr & 0x3FFF); + DBGLOG(1, "-> %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) +{ + 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; + + DBGLOG(1, "ems_write_ramw addr=%05x ", addr); + addr = sys->page_exec[pg] + (addr & 0x3fff); + DBGLOG(1, "-> %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) +{ + 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); +} + + +/* Load contents of "CONFIG.SYS" device from file. */ +static void +cfgsys_load(t1000_t *dev) +{ + char temp[128]; + FILE *f; + + /* Set up the file's name. */ + sprintf(temp, "%s_cfgsys.nvr", machine_get_internal_name()); + mbstowcs(dev->cfgsys_fn, temp, sizeof_w(dev->cfgsys_fn)); + + /* Now attempt to load the file. */ + memset(dev->cfgsys, 0x1a, dev->cfgsys_len); + f = plat_fopen(nvr_path(dev->cfgsys_fn), L"rb"); + if (f != NULL) { + INFO("NVR: loaded CONFIG.SYS from '%ls'\n", dev->cfgsys_fn); + (void)fread(dev->cfgsys, dev->cfgsys_len, 1, f); + fclose(f); + } else + INFO("NVR: initialized CONFIG.SYS for '%ls'\n", dev->cfgsys_fn); +} + + +/* Write the contents of "CONFIG.SYS" to file. */ +static void +cfgsys_save(t1000_t *dev) +{ + FILE *f; + + /* Avoids writing empty files. */ + if (dev->cfgsys_len < 160) return; + + f = plat_fopen(nvr_path(dev->cfgsys_fn), L"wb"); + if (f != NULL) { + INFO("NVR: saved CONFIG.SYS to '%ls'\n", dev->cfgsys_fn); + (void)fwrite(dev->cfgsys, dev->cfgsys_len, 1, f); + fclose(f); + } +} + + +#if 0 /*NOT_USED*/ +/* All RAM beyond 512K is non-volatile */ +static void +emsboard_load(t1000_t *dev) +{ + 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 +emsboard_save(t1000_t *dev) +{ + 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); + } + } +} +#endif + + +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->cfgsys[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->cfgsys[sys->nvr_addr] != val) + nvr_dosave = 1; + sys->cfgsys[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; + } +} + + +/* 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_ROMDOS_SIZE; + mem_map_set_addr(&sys->rom_mapping, 0xa0000, 0x10000); + mem_map_set_exec(&sys->rom_mapping, sys->romdrive + sys->rom_offset); + mem_map_enable(&sys->rom_mapping); + } else { + mem_map_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)])); +} + + +static const device_config_t m_xt_t1000_config[] = { + { + "rom_dos", "ROM DOS", CONFIG_SELECTION, "", 0, + { + { + "Disabled", 0 + }, + { + "Enabled", 1 + }, + { + "" + } + } + }, + { + "", "", -1 + } +}; + + +const device_t m_xt_t1000_device = { + "Toshiba T1000", + MACHINE_ISA, + 0, + NULL, NULL, NULL, + NULL, + NULL, NULL, NULL, + m_xt_t1000_config +}; + + +void +machine_xt_t1000_init(const machine_t *model, void *arg) +{ + FILE *f; + int pg; + + memset(&t1000, 0x00, 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. + */ + t1000.rom_dos = machine_get_config_int("rom_dos"); + if (t1000.rom_dos) { + f = plat_fopen(rom_path(T1000_ROMDOS_PATH), L"rb"); + if (f != NULL) { + t1000.romdrive = (uint8_t *)mem_alloc(T1000_ROMDOS_SIZE); + if (t1000.romdrive) { + memset(t1000.romdrive, 0xff, T1000_ROMDOS_SIZE); + fread(t1000.romdrive, T1000_ROMDOS_SIZE, 1, f); + } + fclose(f); + } + mem_map_add(&t1000.rom_mapping, 0xa0000, 0x10000, + t1000_read_rom,t1000_read_romw,t1000_read_roml, + NULL,NULL,NULL, NULL, MEM_MAPPING_INTERNAL, &t1000); + mem_map_disable(&t1000.rom_mapping); + } + + /* Map the EMS page frame */ + for (pg = 0; pg < 4; pg++) { + mem_map_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_map_disable(&t1000.mapping[pg]); + } + + /* Non-volatile RAM for CONFIG.SYS */ + t1000.cfgsys_len = 160; + t1000.cfgsys = (uint8_t *)mem_alloc(t1000.cfgsys_len); + io_sethandler(0xc0, 4, + t1000_read_nvram,NULL,NULL, + t1000_write_nvram,NULL,NULL, &t1000); + cfgsys_load(&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, arg); + + pit_set_out_func(&pit, 1, pit_refresh_timer_xt); + device_add(&keyboard_xt_device); + t1000.fdc = (fdc_t *)device_add(&fdc_xt_device); + nmi_init(); + + tc8521_init(&t1000.nvr, model->nvrsz); + + if (video_card == VID_INTERNAL) { + /* Load the T1000 CGA Font ROM. */ + video_load_font(L"machines/toshiba/t1000/t1000font.rom", 2); + + device_add(&t1000_video_device); + } +} + + +static uint8_t +t1200_nvram_read(uint32_t addr, void *priv) +{ + t1000_t *sys = (t1000_t *)priv; + + return(sys->cfgsys[addr & 0x7ff]); +} + + +static void +t1200_nvram_write(uint32_t addr, uint8_t val, void *priv) +{ + t1000_t *sys = (t1000_t *)priv; + + if (sys->cfgsys[addr & 0x7ff] != val) + nvr_dosave = 1; + + sys->cfgsys[addr & 0x7ff] = val; +} + + +void +machine_xt_t1200_init(const machine_t *model, void *arg) +{ + int pg; + + memset(&t1000, 0x00, sizeof(t1000)); + t1000.ems_port_index = 7; /* EMS disabled */ + t1000.is_t1200 = 1; + + /* Map the EMS page frame */ + for (pg = 0; pg < 4; pg++) { + mem_map_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_map_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, arg); + + /* Non-volatile RAM for CONFIG.SYS */ + t1000.cfgsys_len = 2048; + t1000.cfgsys = (uint8_t *)mem_alloc(t1000.cfgsys_len); + mem_map_add(&t1000.nvr_mapping, + 0x000f0000, t1000.cfgsys_len, + t1200_nvram_read,NULL,NULL, + t1200_nvram_write,NULL,NULL, + NULL, 0, &t1000); + cfgsys_load(&t1000); + + pit_set_out_func(&pit, 1, pit_refresh_timer_xt); + device_add(&keyboard_xt_device); + t1000.fdc = (fdc_t *)device_add(&fdc_toshiba_device); + nmi_init(); + + tc8521_init(&t1000.nvr, model->nvrsz); + + if (video_card == VID_INTERNAL) { + /* Load the T1200 CGA Font ROM. */ + video_load_font(L"machines/toshiba/t1200/t1000font.bin", 2); + + device_add(&t1200_video_device); + } + + if (hdc_type == HDC_INTERNAL) + (void)device_add(&xta_t1200_device); +} + + +void +machine_xt_t1x00_close(void) +{ + cfgsys_save(&t1000); +} + + +void +t1000_syskey(uint8_t andmask, uint8_t ormask, uint8_t xormask) +{ + t1000.syskeys &= ~andmask; + t1000.syskeys |= ormask; + t1000.syskeys ^= xormask; +} diff --git a/src/ui/lang/Extra-KZ.txt b/src/ui/lang/Extra-KZ.txt index e54833632935234ebef112573b2846c11adfba25..0f9a6e0716fa177241fa49b9762b979bdabea370 100644 GIT binary patch delta 500 zcmZ1?_f>8}9wYC>ygY7RE(Tr(2r==A4I}Sl1x7122!C=MqZ=ddFo6?{UW~kx z#aL7(n=xs@1Or%p~c8Mc@2jb zOyw^QKStik9-MA4fk~VujJ%VNal&nr;L2j*@c_(Y}RKWyR@We6lPG;i`feFO%g3O-8s{s?(#|v`K3to6=$nYgF@=mVd^MI*{ Zc{TLl0B delta 418 zcmew=w?uA2-o*So4lV{>1`wY3!e+86qZJE?Jvo)pZSopM1qkN{v(aQ0CW*-#n3N{l zGwDDC3z_UDZ(wqQa1O9YO%`I7nXJ$301+reOFDhTH_PuyfL-Vg|939rfI zo4gtj4m+RLWFtNUc2LN2G1yJC7oS|hB`|psAH?PTeDafD^Ql49iu0>Yw&OQp;$i>* DVXj|#