diff --git a/src/machine/m_amstrad.c b/src/machine/m_amstrad.c index 868f4a2..f5224a5 100644 --- a/src/machine/m_amstrad.c +++ b/src/machine/m_amstrad.c @@ -1,1314 +1,1316 @@ -/* - * 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.5 2018/03/19 - * - * 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 -#include "../emu.h" -#include "../cpu/cpu.h" -#include "../io.h" -#include "../nmi.h" -#include "../pic.h" -#include "../pit.h" -#include "../ppi.h" -#include "../mem.h" -#include "../rom.h" -#include "../timer.h" -#include "../device.h" -#include "../nvr.h" -#include "../keyboard.h" -#include "../mouse.h" -#include "../game/gameport.h" -#include "../lpt.h" -#include "../floppy/fdd.h" -#include "../floppy/fdc.h" -#include "../sound/sound.h" -#include "../sound/snd_speaker.h" -#include "../video/video.h" -#include "../video/vid_cga.h" -#include "../video/vid_ega.h" -#include "../video/vid_paradise.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. */ - 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 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 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; - - egawrites++; - 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; - - egareads++; - 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) - hline(buffer, 0, vid->displine, (vid->crtc[1] << 3) + 16, cols[0]); - else - 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 *)malloc(sizeof(amsvid_t)); - memset(vid, 0x00, sizeof(amsvid_t)); - - vid->vram = malloc(0x10000); - vid->cgacol = 7; - vid->cgamode = 0x12; - - /* Load the PC1512 CGA Character Set ROM. */ - loadfont(L"roms/machines/amstrad/pc1512/40078", 2); - - timer_add(vid_poll_1512, &vid->vidtime, TIMER_ALWAYS_ENABLED, vid); - mem_mapping_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; - - 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, - 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_mapping_enable(&vid->cga.mapping); - mem_mapping_disable(&vid->ega.mapping); - } else { - mem_mapping_disable(&vid->cga.mapping); - switch (vid->ega.gdcreg[6] & 0xc) { - case 0x0: /*128k at A0000*/ - mem_mapping_set_addr(&vid->ega.mapping, - 0xa0000, 0x20000); - break; - - case 0x4: /*64k at A0000*/ - mem_mapping_set_addr(&vid->ega.mapping, - 0xa0000, 0x10000); - break; - - case 0x8: /*32k at B0000*/ - mem_mapping_set_addr(&vid->ega.mapping, - 0xb0000, 0x08000); - break; - - case 0xC: /*32k at B8000*/ - mem_mapping_set_addr(&vid->ega.mapping, - 0xb8000, 0x08000); - break; - } - } - 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; - - switch (addr) { - } - - 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) -{ - amsvid_t *vid; - - /* Allocate a video controller block. */ - vid = (amsvid_t *)malloc(sizeof(amsvid_t)); - memset(vid, 0x00, sizeof(amsvid_t)); - - rom_init(&vid->bios_rom, L"roms/machines/pc1640/40100", - 0xc0000, 0x8000, 0x7fff, 0, 0); - - ega_init(&vid->ega, 9, 0); - vid->cga.vram = vid->ega.vram; - vid->cga_enabled = 1; - cga_init(&vid->cga); - - mem_mapping_add(&vid->cga.mapping, 0xb8000, 0x08000, - cga_read, NULL, NULL, cga_write, NULL, NULL, NULL, 0, &vid->cga); - mem_mapping_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; - - 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, - 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 *)malloc(sizeof(amsvid_t)); - memset(vid, 0x00, sizeof(amsvid_t)); - - cga = &vid->cga; - cga->vram = malloc(0x4000); - cga_init(cga); - - /* Load the PC200 CGA Character Set ROM. */ - loadfont(L"roms/machines/amstrad/pc200/40109.bin", 1); - - mem_mapping_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; - - 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, - NULL -}; - - -static void -ms_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 -ms_read(uint16_t addr, void *priv) -{ - amstrad_t *ams = (amstrad_t *)priv; - - if (addr == 0x78) - return(ams->mousex); - - return(ams->mousey); -} - - -static int -ms_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] = val; -#if ENABLE_KEYBOARD_LOG - pclog("keyboard_amstrad : %02X added to key queue at %i\n", - val, key_queue_end); -#endif - 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; -#ifdef WALTJE - int i = 0; -#endif - -#if ENABLE_KEYBOARD_LOG - pclog("keyboard_amstrad : write %04X %02X %02X\n", port, val, ams->pb); -#endif - - 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. - */ -#if ENABLE_KEYBOARD_LOG - pclog("AMSkb: write PB %02x (%02x)\n", val, ams->pb); -#endif - if (!(ams->pb & 0x40) && (val & 0x40)) { /*Reset keyboard*/ -#if ENABLE_KEYBOARD_LOG - pclog("AMSkb: reset keyboard\n"); -#endif - 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: -#ifdef WALTJE - pclog("AMSkb: STAT1 = %02x (%02x)\n", val, ams->stat1); -#endif - ams->stat1 = val; - break; - - case 0x65: -#ifdef WALTJE - pclog("AMSkb: STAT2 = %02x (%02x)\n", val, ams->stat2); - i = 512 + (((val & 0x1f) - 0x0e) * 32); - pclog("AMSkb: %d KB RAM installed.\n", i); -#endif - ams->stat2 = val; - break; - - case 0x66: -#ifdef WALTJE - pclog("AMSkb: RESET REQUESTED !\n"); -#endif - pc_reset(1); - break; - - default: - pclog("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: - pclog("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); -#if ENABLE_KEYBOARD_LOG - pclog("keyboard_amstrad : take IRQ\n"); -#endif - } - - if (key_queue_start != key_queue_end && !ams->pa) { - ams->key_waiting = key_queue[key_queue_start]; -#if ENABLE_KEYBOARD_LOG - pclog("Reading %02X from the key queue at %i\n", - ams->key_waiting, key_queue_start); -#endif - 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(romset) { - case ROM_PC1512: - ret = 0x20; - break; - - case ROM_PC200: - ret = 0x80; - break; - - default: - ret = 0x00; - } - break; - - case 0xdead: - ret = ams->dead; - break; - } - - return(ret); -} - - -void -machine_amstrad_init(const machine_t *model) -{ - amstrad_t *ams; - - ams = (amstrad_t *)malloc(sizeof(amstrad_t)); - memset(ams, 0x00, sizeof(amstrad_t)); - - machine_common_init(model); - - nmi_init(); - - device_add(&amstrad_nvr_device); - - lpt2_remove_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); - - io_sethandler(0x0078, 1, - ms_read, NULL, NULL, ms_write, NULL, NULL, ams); - - io_sethandler(0x007a, 1, - ms_read, NULL, NULL, ms_write, NULL, NULL, ams); - - switch(model->id) { - case ROM_PC1512: - device_add(&fdc_xt_device); - if (vid_card == VID_INTERNAL) { - vid_init_1512(ams); - device_add_ex(&vid_1512_device, ams->vid); +/* + * 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.6 2018/03/20 + * + * 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 +#include "../emu.h" +#include "../cpu/cpu.h" +#include "../io.h" +#include "../nmi.h" +#include "../pic.h" +#include "../pit.h" +#include "../ppi.h" +#include "../mem.h" +#include "../rom.h" +#include "../timer.h" +#include "../device.h" +#include "../nvr.h" +#include "../keyboard.h" +#include "../mouse.h" +#include "../game/gameport.h" +#include "../lpt.h" +#include "../floppy/fdd.h" +#include "../floppy/fdc.h" +#include "../sound/sound.h" +#include "../sound/snd_speaker.h" +#include "../video/video.h" +#include "../video/vid_cga.h" +#include "../video/vid_ega.h" +#include "../video/vid_paradise.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. */ + 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 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 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); + } } - break; - - case ROM_PC1640: - device_add(&fdc_xt_device); - if (vid_card == VID_INTERNAL) { - vid_init_1640(ams); - device_add_ex(&vid_1640_device, ams->vid); + return; + + case 0x03d8: + if ((val & 0x12) == 0x12 && (vid->cgamode & 0x12) != 0x12) { + vid->plane_write = 0xf; + vid->plane_read = 0; } - break; - - case ROM_PC200: - device_add(&fdc_xt_device); - if (vid_card == VID_INTERNAL) { - vid_init_200(ams); - device_add_ex(&vid_200_device, ams->vid); + 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; + + egawrites++; + 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; + + egareads++; + 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(); } - break; - - case ROM_PC2086: - device_add(&fdc_at_actlow_device); - if (vid_card == VID_INTERNAL) { - device_add(¶dise_pvga1a_pc2086_device); + 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; + } } - break; - - case ROM_PC3086: - device_add(&fdc_at_actlow_device); - if (vid_card == VID_INTERNAL) { - device_add(¶dise_pvga1a_pc3086_device); + 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; + } + } } - break; - - case ROM_MEGAPC: - device_add(&fdc_at_actlow_device); - if (vid_card == VID_INTERNAL) { - device_add(¶dise_wd90c11_megapc_device); + } else { + cols[0] = ((vid->cgamode & 0x12) == 0x12) ? 0 : (vid->cgacol & 15) + 16; + if (vid->cgamode & 1) + hline(buffer, 0, vid->displine, (vid->crtc[1] << 3) + 16, cols[0]); + else + 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; } - 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. */ - mouse_reset(); - mouse_set_poll(ms_poll, ams); - - if (joystick_type != JOYSTICK_TYPE_NONE) - device_add(&gameport_device); -} + } 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 *)malloc(sizeof(amsvid_t)); + memset(vid, 0x00, sizeof(amsvid_t)); + + vid->vram = malloc(0x10000); + vid->cgacol = 7; + vid->cgamode = 0x12; + + /* Load the PC1512 CGA Character Set ROM. */ + loadfont(L"roms/machines/amstrad/pc1512/40078", 2); + + timer_add(vid_poll_1512, &vid->vidtime, TIMER_ALWAYS_ENABLED, vid); + mem_mapping_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; + + 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, + 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_mapping_enable(&vid->cga.mapping); + mem_mapping_disable(&vid->ega.mapping); + } else { + mem_mapping_disable(&vid->cga.mapping); + switch (vid->ega.gdcreg[6] & 0xc) { + case 0x0: /*128k at A0000*/ + mem_mapping_set_addr(&vid->ega.mapping, + 0xa0000, 0x20000); + break; + + case 0x4: /*64k at A0000*/ + mem_mapping_set_addr(&vid->ega.mapping, + 0xa0000, 0x10000); + break; + + case 0x8: /*32k at B0000*/ + mem_mapping_set_addr(&vid->ega.mapping, + 0xb0000, 0x08000); + break; + + case 0xC: /*32k at B8000*/ + mem_mapping_set_addr(&vid->ega.mapping, + 0xb8000, 0x08000); + break; + } + } + 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; + + switch (addr) { + } + + 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) +{ + amsvid_t *vid; + + /* Allocate a video controller block. */ + vid = (amsvid_t *)malloc(sizeof(amsvid_t)); + memset(vid, 0x00, sizeof(amsvid_t)); + + rom_init(&vid->bios_rom, L"roms/machines/amstrad/pc1640/40100", + 0xc0000, 0x8000, 0x7fff, 0, 0); + + ega_init(&vid->ega, 9, 0); + vid->cga.vram = vid->ega.vram; + vid->cga_enabled = 1; + cga_init(&vid->cga); + + mem_mapping_add(&vid->cga.mapping, 0xb8000, 0x08000, + cga_read, NULL, NULL, cga_write, NULL, NULL, NULL, 0, &vid->cga); + mem_mapping_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; + + 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, + 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 *)malloc(sizeof(amsvid_t)); + memset(vid, 0x00, sizeof(amsvid_t)); + + cga = &vid->cga; + cga->vram = malloc(0x4000); + cga_init(cga); + + /* Load the PC200 CGA Character Set ROM. */ + loadfont(L"roms/machines/amstrad/pc200/40109.bin", 1); + + mem_mapping_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; + + 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, + NULL +}; + + +static void +ms_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 +ms_read(uint16_t addr, void *priv) +{ + amstrad_t *ams = (amstrad_t *)priv; + + if (addr == 0x78) + return(ams->mousex); + + return(ams->mousey); +} + + +static int +ms_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] = val; +#if ENABLE_KEYBOARD_LOG + pclog("keyboard_amstrad : %02X added to key queue at %i\n", + val, key_queue_end); +#endif + 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; +#ifdef WALTJE + int i = 0; +#endif + +#if ENABLE_KEYBOARD_LOG + pclog("keyboard_amstrad : write %04X %02X %02X\n", port, val, ams->pb); +#endif + + 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. + */ +#if ENABLE_KEYBOARD_LOG + pclog("AMSkb: write PB %02x (%02x)\n", val, ams->pb); +#endif + if (!(ams->pb & 0x40) && (val & 0x40)) { /*Reset keyboard*/ +#if ENABLE_KEYBOARD_LOG + pclog("AMSkb: reset keyboard\n"); +#endif + 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: +#ifdef WALTJE + pclog("AMSkb: STAT1 = %02x (%02x)\n", val, ams->stat1); +#endif + ams->stat1 = val; + break; + + case 0x65: +#ifdef WALTJE + pclog("AMSkb: STAT2 = %02x (%02x)\n", val, ams->stat2); + i = 512 + (((val & 0x1f) - 0x0e) * 32); + pclog("AMSkb: %d KB RAM installed.\n", i); +#endif + ams->stat2 = val; + break; + + case 0x66: +#ifdef WALTJE + pclog("AMSkb: RESET REQUESTED !\n"); +#endif + pc_reset(1); + break; + + default: + pclog("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: + pclog("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); +#if ENABLE_KEYBOARD_LOG + pclog("keyboard_amstrad : take IRQ\n"); +#endif + } + + if (key_queue_start != key_queue_end && !ams->pa) { + ams->key_waiting = key_queue[key_queue_start]; +#if ENABLE_KEYBOARD_LOG + pclog("Reading %02X from the key queue at %i\n", + ams->key_waiting, key_queue_start); +#endif + 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(romset) { + case ROM_PC1512: + ret = 0x20; + break; + + case ROM_PC200: + ret = 0x80; + break; + + default: + ret = 0x00; + } + break; + + case 0xdead: + ret = ams->dead; + break; + } + + return(ret); +} + + +void +machine_amstrad_init(const machine_t *model) +{ + amstrad_t *ams; + + ams = (amstrad_t *)malloc(sizeof(amstrad_t)); + memset(ams, 0x00, sizeof(amstrad_t)); + + machine_common_init(model); + + nmi_init(); + + device_add(&amstrad_nvr_device); + + lpt2_remove_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); + + io_sethandler(0x0078, 1, + ms_read, NULL, NULL, ms_write, NULL, NULL, ams); + + io_sethandler(0x007a, 1, + ms_read, NULL, NULL, ms_write, NULL, NULL, ams); + + switch(model->id) { + case ROM_PC1512: + device_add(&fdc_xt_device); + if (vid_card == VID_INTERNAL) { + vid_init_1512(ams); + device_add_ex(&vid_1512_device, ams->vid); + } + break; + + case ROM_PC1640: + device_add(&fdc_xt_device); + if (vid_card == VID_INTERNAL) { + vid_init_1640(ams); + device_add_ex(&vid_1640_device, ams->vid); + } + break; + + case ROM_PC200: + device_add(&fdc_xt_device); + if (vid_card == VID_INTERNAL) { + vid_init_200(ams); + device_add_ex(&vid_200_device, ams->vid); + } + break; + + case ROM_PC2086: + device_add(&fdc_at_actlow_device); + if (vid_card == VID_INTERNAL) { + device_add(¶dise_pvga1a_pc2086_device); + } + break; + + case ROM_PC3086: + device_add(&fdc_at_actlow_device); + if (vid_card == VID_INTERNAL) { + device_add(¶dise_pvga1a_pc3086_device); + } + break; + + case ROM_MEGAPC: + device_add(&fdc_at_actlow_device); + if (vid_card == VID_INTERNAL) { + device_add(¶dise_wd90c11_megapc_device); + } + 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_TYPE_INTERNAL) { + mouse_reset(); + mouse_set_poll(ms_poll, ams); + } + + if (joystick_type != JOYSTICK_TYPE_NONE) + device_add(&gameport_device); +}