/* * 86Box A hypervisor and IBM PC system emulator that specializes in * running old operating systems and software designed for IBM * PC systems and compatibles from 1981 through fairly recent * system designs based on the PCI bus. * * This file is part of the 86Box distribution. * * 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. * * PPC512/640: Portable with both CGA-compatible and MDA-compatible monitors. * * TODO: This module is not complete yet: * * All models: The internal mouse controller does not work correctly with * version 7.04 of the mouse driver. * * * * Authors: Sarah Walker, * Miran Grca, * Fred N. van Kempen, * John Elliott, * * Copyright 2008-2019 Sarah Walker. * Copyright 2016-2019 Miran Grca. * Copyright 2017-2019 Fred N. van Kempen. * Copyright 2019 John Elliott. */ #include #include #include #include #include #include #define HAVE_STDARG_H #include <86box/86box.h> #include "cpu.h" #include <86box/timer.h> #include <86box/io.h> #include <86box/nmi.h> #include <86box/pic.h> #include <86box/pit.h> #include <86box/ppi.h> #include <86box/mem.h> #include <86box/rom.h> #include <86box/device.h> #include <86box/nvr.h> #include <86box/keyboard.h> #include <86box/mouse.h> #include <86box/gameport.h> #include <86box/lpt.h> #include <86box/fdd.h> #include <86box/fdc.h> #include <86box/sound.h> #include <86box/snd_speaker.h> #include <86box/video.h> #include <86box/vid_cga.h> #include <86box/vid_ega.h> #include <86box/vid_mda.h> #include <86box/machine.h> #include <86box/m_amstrad.h> #include <86box/plat_unused.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 amsvid_t { rom_t bios_rom; /* 1640 */ cga_t cga; /* 1640/200 */ mda_t mda; /* 1512/200/PPC512/640*/ ega_t ega; /* 1640 */ uint8_t emulation; /* Which display are we emulating? */ uint8_t dipswitches; /* DIP switches 1-3 */ uint8_t crtc_index; /* CRTC index readback * Bit 7: CGA control port written * Bit 6: Operation control port written * Bit 5: CRTC register written * Bits 0-4: Last CRTC register selected */ uint8_t operation_ctrl; uint8_t reg_3df; uint8_t type; uint8_t crtc[32]; int crtcreg; int cga_enabled; /* 1640 */ uint8_t cgacol; uint8_t cgamode; uint8_t stat; uint8_t plane_write; /* 1512/200 */ uint8_t plane_read; /* 1512/200 */ uint8_t border; /* 1512/200 */ uint8_t invert; /* 512/640 */ int fontbase; /* 1512/200 */ int linepos; int displine; int sc; int vc; int cgadispon; int con; int cursoron; int cgablink; int vsynctime; int fullchange; int vadj; uint16_t ma; uint16_t maback; int dispon; int blink; uint64_t dispontime; /* 1512/1640 */ uint64_t dispofftime; /* 1512/1640 */ pc_timer_t timer; /* 1512/1640 */ int firstline; int lastline; uint8_t *vram; void *ams; } amsvid_t; typedef struct amstrad_t { /* Machine stuff. */ uint8_t dead; uint8_t stat1; uint8_t stat2; uint8_t type; uint8_t language; /* Keyboard stuff. */ int8_t wantirq; uint8_t key_waiting; uint8_t pa; uint8_t pb; pc_timer_t send_delay_timer; /* Mouse stuff. */ int oldb; /* Video stuff. */ amsvid_t *vid; fdc_t *fdc; } amstrad_t; uint32_t amstrad_latch; static uint8_t key_queue[16]; static int key_queue_start = 0; static int 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 video_timings_t timing_pc1512 = { VIDEO_BUS, 0, 0, 0, 0, 0, 0 }; /*PC1512 video code handles waitstates itself*/ static video_timings_t timing_pc1640 = { VIDEO_ISA, 8, 16, 32, 8, 16, 32 }; static video_timings_t timing_pc200 = { VIDEO_ISA, 8, 16, 32, 8, 16, 32 }; enum { AMS_PC1512, AMS_PC1640, AMS_PC200, AMS_PPC512, AMS_PC2086, AMS_PC3086 }; #ifdef ENABLE_AMSTRAD_LOG int amstrad_do_log = ENABLE_AMSTRAD_LOG; static void amstrad_log(const char *fmt, ...) { va_list ap; if (amstrad_do_log) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); } } #else # define amstrad_log(fmt, ...) #endif static void recalc_timings_1512(amsvid_t *vid) { double _dispontime; double _dispofftime; double disptime; disptime = /*128*/ 114; /*Fixed on PC1512*/ _dispontime = 80; _dispofftime = disptime - _dispontime; _dispontime *= CGACONST; _dispofftime *= CGACONST; vid->dispontime = (uint64_t) _dispontime; vid->dispofftime = (uint64_t) _dispofftime; } static void vid_out_1512(uint16_t addr, uint8_t val, void *priv) { amsvid_t *vid = (amsvid_t *) priv; uint8_t old; if ((addr >= 0x3d0) && (addr <= 0x3d7)) addr = (addr & 0xff9) | 0x004; 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) { vid->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; default: return; } } static uint8_t vid_in_1512(uint16_t addr, void *priv) { const amsvid_t *vid = (amsvid_t *) priv; uint8_t ret = 0xff; if ((addr >= 0x3d0) && (addr <= 0x3d7)) addr = (addr & 0xff9) | 0x004; switch (addr) { case 0x03d4: ret = vid->crtcreg; break; case 0x03d5: ret = vid->crtc[vid->crtcreg]; break; case 0x03da: ret = vid->stat; break; default: break; } 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) { const 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; int c; int xs_temp; int ys_temp; uint8_t chr; uint8_t attr; uint16_t dat; uint16_t dat2; uint16_t dat3; uint16_t dat4; int cols[4]; int col; int oldsc; if (!vid->linepos) { timer_advance_u64(&vid->timer, 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) { buffer32->line[vid->displine << 1][c] = buffer32->line[(vid->displine << 1) + 1][c] = (vid->border & 15) + 16; if (vid->cgamode & 1) { buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 3) + 8] = buffer32->line[(vid->displine << 1) + 1][c + (vid->crtc[1] << 3) + 8] = 0; } else { buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 4) + 8] = buffer32->line[(vid->displine << 1) + 1][c + (vid->crtc[1] << 4) + 8] = 0; } } else { buffer32->line[vid->displine << 1][c] = buffer32->line[(vid->displine << 1) + 1][c] = (vid->cgacol & 15) + 16; if (vid->cgamode & 1) { buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 3) + 8] = buffer32->line[(vid->displine << 1) + 1][c + (vid->crtc[1] << 3) + 8] = (vid->cgacol & 15) + 16; } else { buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 4) + 8] = buffer32->line[(vid->displine << 1) + 1][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++) { buffer32->line[vid->displine << 1][(x << 3) + c + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + c + 8] = cols[(fontdat[vid->fontbase + chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0] ^ 15; } } else { for (c = 0; c < 8; c++) { buffer32->line[vid->displine << 1][(x << 3) + c + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + c + 8] = cols[(fontdat[vid->fontbase + 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++) { buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 8] = buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 1 + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + (c << 1) + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[vid->fontbase + chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0] ^ 15; } } else { for (c = 0; c < 8; c++) { buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 8] = buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 1 + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + (c << 1) + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[vid->fontbase + 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++) { buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 8] = buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 1 + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + (c << 1) + 8] = buffer32->line[(vid->displine << 1) + 1][(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++) { buffer32->line[vid->displine << 1][(x << 4) + c + 8] = buffer32->line[(vid->displine << 1) + 1][(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(buffer32, 0, (vid->displine << 1), (vid->crtc[1] << 3) + 16, cols[0]); hline(buffer32, 0, (vid->displine << 1) + 1, (vid->crtc[1] << 3) + 16, cols[0]); } else { hline(buffer32, 0, (vid->displine << 1), (vid->crtc[1] << 4) + 16, cols[0]); hline(buffer32, 0, (vid->displine << 1), (vid->crtc[1] << 4) + 16, cols[0]); } } if (vid->cgamode & 1) x = (vid->crtc[1] << 3) + 16; else x = (vid->crtc[1] << 4) + 16; video_process_8(x, vid->displine << 1); video_process_8(x, (vid->displine << 1) + 1); vid->sc = oldsc; if (vid->vsynctime) vid->stat |= 8; vid->displine++; if (vid->displine >= 360) vid->displine = 0; } else { timer_advance_u64(&vid->timer, 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; } 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++; xs_temp = x; ys_temp = (vid->lastline - vid->firstline) << 1; if ((xs_temp > 0) && (ys_temp > 0)) { if (xs_temp < 64) xs_temp = 656; if (ys_temp < 32) ys_temp = 400; if (!enable_overscan) xs_temp -= 16; if ((xs_temp != xsize) || (ys_temp != ysize) || video_force_resize_get()) { xsize = xs_temp; ysize = ys_temp; set_screen_size(xsize, ysize + (enable_overscan ? 16 : 0)); if (video_force_resize_get()) video_force_resize_set(0); } if (enable_overscan) { video_blit_memtoscreen(0, (vid->firstline - 4) << 1, xsize, ((vid->lastline - vid->firstline) + 8) << 1); } else { video_blit_memtoscreen(8, vid->firstline << 1, xsize, (vid->lastline - vid->firstline) << 1); } } video_res_x = xsize; 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 *) calloc(1, sizeof(amsvid_t)); video_inform(VIDEO_FLAG_TYPE_CGA, &timing_pc1512); vid->vram = malloc(0x10000); vid->cgacol = 7; vid->cgamode = 0x12; timer_add(&vid->timer, vid_poll_1512, vid, 1); 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; vid->fontbase = (device_get_config_int("codepage") & 3) * 256; cga_palette = (device_get_config_int("display_type") << 1); cgapal_rebuild(); 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); } const device_config_t vid_1512_config[] = { // clang-format off { .name = "display_type", .description = "Display type", .type = CONFIG_SELECTION, .default_string = "", .default_int = 0, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "PC-CM (Colour)", .value = 0 }, { .description = "PC-MM (Monochrome)", .value = 3 }, { .description = "" } } }, { .name = "codepage", .description = "Hardware font", .type = CONFIG_SELECTION, .default_string = "", .default_int = 3, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "US English", .value = 3 }, { .description = "Danish", .value = 1 }, { .description = "Greek", .value = 0 }, { .description = "" } } }, { .name = "language", .description = "BIOS language", .type = CONFIG_SELECTION, .default_string = "", .default_int = 7, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "English", .value = 7 }, { .description = "German", .value = 6 }, { .description = "French", .value = 5 }, { .description = "Spanish", .value = 4 }, { .description = "Danish", .value = 3 }, { .description = "Swedish", .value = 2 }, { .description = "Italian", .value = 1 }, { .description = "Diagnostic mode", .value = 0 }, { .description = "" } } }, { .name = "", .description = "", .type = CONFIG_END } // clang-format on }; const device_t vid_1512_device = { .name = "Amstrad PC1512 (video)", .internal_name = "vid_1512", .flags = 0, .local = 0, .init = NULL, .close = vid_close_1512, .reset = NULL, .available = NULL, .speed_changed = vid_speed_change_1512, .force_redraw = NULL, .config = vid_1512_config }; 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) { timer_disable(&vid->ega.timer); timer_set_delay_u64(&vid->cga.timer, 0); mem_mapping_enable(&vid->cga.mapping); mem_mapping_disable(&vid->ega.mapping); } else { timer_disable(&vid->cga.timer); timer_set_delay_u64(&vid->ega.timer, 0); 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; default: break; } } return; default: break; } 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 (vid->cga_enabled) return (cga_in(addr, &vid->cga)); else return (ega_in(addr, &vid->ega)); } static void vid_init_1640(amstrad_t *ams) { amsvid_t *vid; /* Allocate a video controller block. */ vid = (amsvid_t *) calloc(1, sizeof(amsvid_t)); rom_init(&vid->bios_rom, "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); timer_disable(&vid->ega.timer); video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_pc1640); 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); overscan_x = overscan_y = 16; vid->fontbase = 768; cga_palette = 0; cgapal_rebuild(); 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); } const device_config_t vid_1640_config[] = { // clang-format off { .name = "language", .description = "BIOS language", .type = CONFIG_SELECTION, .default_string = "", .default_int = 7, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "English", .value = 7 }, { .description = "German", .value = 6 }, { .description = "French", .value = 5 }, { .description = "Spanish", .value = 4 }, { .description = "Danish", .value = 3 }, { .description = "Swedish", .value = 2 }, { .description = "Italian", .value = 1 }, { .description = "Diagnostic mode", .value = 0 }, { .description = "" } } }, { .name = "", .description = "", .type = CONFIG_END } // clang-format on }; const device_t vid_1640_device = { .name = "Amstrad PC1640 (video)", .internal_name = "vid_1640", .flags = 0, .local = 0, .init = NULL, .close = vid_close_1640, .reset = NULL, .available = NULL, .speed_changed = vid_speed_changed_1640, .force_redraw = NULL, .config = vid_1640_config }; /* Display type */ #define PC200_CGA 0 /* CGA monitor */ #define PC200_MDA 1 /* MDA monitor */ #define PC200_TV 2 /* Television */ #define PC200_LCDC 3 /* PPC512 LCD as CGA*/ #define PC200_LCDM 4 /* PPC512 LCD as MDA*/ extern int nmi_mask; static uint32_t blue; static uint32_t green; static uint32_t lcdcols[256][2][2]; static void ams_inform(amsvid_t *vid) { switch (vid->emulation) { case PC200_CGA: case PC200_TV: case PC200_LCDC: video_inform(VIDEO_FLAG_TYPE_CGA, &timing_pc200); break; case PC200_MDA: case PC200_LCDM: video_inform(VIDEO_FLAG_TYPE_MDA, &timing_pc200); break; default: break; } } static void vid_speed_changed_200(void *priv) { amsvid_t *vid = (amsvid_t *) priv; cga_recalctimings(&vid->cga); mda_recalctimings(&vid->mda); } /* LCD colour mappings * * 0 => solid green * 1 => blue on green * 2 => green on blue * 3 => solid blue */ static unsigned char mapping1[256] = { // clang-format off /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ /*00*/ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*10*/ 2, 0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, /*20*/ 2, 2, 0, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, /*30*/ 2, 2, 2, 0, 2, 2, 1, 1, 2, 2, 2, 1, 2, 2, 1, 1, /*40*/ 2, 2, 1, 1, 0, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, /*50*/ 2, 2, 1, 1, 2, 0, 1, 1, 2, 2, 1, 1, 2, 1, 1, 1, /*60*/ 2, 2, 2, 2, 2, 2, 0, 1, 2, 2, 2, 2, 2, 2, 1, 1, /*70*/ 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 1, /*80*/ 2, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, /*90*/ 2, 2, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1, 1, 1, 1, 1, /*A0*/ 2, 2, 2, 1, 2, 2, 1, 1, 2, 2, 0, 1, 2, 2, 1, 1, /*B0*/ 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 0, 2, 2, 1, 1, /*C0*/ 2, 2, 1, 1, 2, 1, 1, 1, 2, 2, 1, 1, 0, 1, 1, 1, /*D0*/ 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 0, 1, 1, /*E0*/ 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 0, 1, /*F0*/ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, // clang-format on }; static unsigned char mapping2[256] = { // clang-format off /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ /*00*/ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*10*/ 1, 3, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, /*20*/ 1, 1, 3, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /*30*/ 1, 1, 1, 3, 1, 1, 2, 2, 1, 1, 1, 2, 1, 1, 2, 2, /*40*/ 1, 1, 2, 2, 3, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, /*50*/ 1, 1, 2, 2, 1, 3, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, /*60*/ 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, 2, 2, /*70*/ 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 2, /*80*/ 2, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, /*90*/ 1, 1, 2, 2, 2, 2, 2, 2, 1, 3, 2, 2, 2, 2, 2, 2, /*A0*/ 1, 1, 1, 2, 1, 1, 2, 2, 1, 1, 3, 2, 1, 1, 2, 2, /*B0*/ 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 3, 1, 1, 2, 2, /*C0*/ 1, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 3, 2, 2, 2, /*D0*/ 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 3, 2, 2, /*E0*/ 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 2, /*F0*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, // clang-format on }; static void set_lcd_cols(uint8_t mode_reg) { const unsigned char *mapping = (mode_reg & 0x80) ? mapping2 : mapping1; for (uint16_t c = 0; c < 256; c++) { switch (mapping[c]) { case 0: lcdcols[c][0][0] = lcdcols[c][1][0] = green; lcdcols[c][0][1] = lcdcols[c][1][1] = green; break; case 1: lcdcols[c][0][0] = lcdcols[c][1][0] = lcdcols[c][1][1] = green; lcdcols[c][0][1] = blue; break; case 2: lcdcols[c][0][0] = lcdcols[c][1][0] = lcdcols[c][1][1] = blue; lcdcols[c][0][1] = green; break; case 3: lcdcols[c][0][0] = lcdcols[c][1][0] = blue; lcdcols[c][0][1] = lcdcols[c][1][1] = blue; break; default: break; } } } static uint8_t vid_in_200(uint16_t addr, void *priv) { amsvid_t *vid = (amsvid_t *) priv; cga_t *cga = &vid->cga; mda_t *mda = &vid->mda; uint8_t ret; switch (addr) { case 0x03b8: return (mda->ctrl); case 0x03d8: return (cga->cgamode); case 0x03dd: ret = vid->crtc_index; /* Read NMI reason */ vid->crtc_index &= 0x1f; /* Reset NMI reason */ nmi = 0; /* And reset NMI flag */ return ret; case 0x03de: return ((vid->operation_ctrl & 0xc7) | vid->dipswitches); /*External CGA*/ case 0x03df: return (vid->reg_3df); default: break; } if (addr >= 0x3D0 && addr <= 0x3DF) return cga_in(addr, cga); if (addr >= 0x3B0 && addr <= 0x3BB) return mda_in(addr, mda); return 0xFF; } static void vid_out_200(uint16_t addr, uint8_t val, void *priv) { amsvid_t *vid = (amsvid_t *) priv; cga_t *cga = &vid->cga; mda_t *mda = &vid->mda; uint8_t old; switch (addr) { /* MDA writes ============================================================== */ case 0x3b1: case 0x3b3: case 0x3b5: case 0x3b7: /* Writes banned to CRTC registers 0-11? */ if (!(vid->operation_ctrl & 0x40) && mda->crtcreg <= 11) { vid->crtc_index = 0x20 | (mda->crtcreg & 0x1f); if (vid->operation_ctrl & 0x80) nmi_raise(); vid->reg_3df = val; return; } old = mda->crtc[mda->crtcreg]; mda->crtc[mda->crtcreg] = val & crtc_mask[mda->crtcreg]; if (old != val) { if (mda->crtcreg < 0xe || mda->crtcreg > 0x10) { vid->fullchange = changeframecount; mda_recalctimings(mda); } } return; case 0x3b8: old = mda->ctrl; mda->ctrl = val; if ((mda->ctrl ^ old) & 3) mda_recalctimings(mda); vid->crtc_index &= 0x1F; vid->crtc_index |= 0x80; if (vid->operation_ctrl & 0x80) nmi_raise(); return; /* CGA writes ============================================================== */ case 0x03d1: case 0x03d3: case 0x03d5: case 0x03d7: if (!(vid->operation_ctrl & 0x40) && cga->crtcreg <= 11) { vid->crtc_index = 0x20 | (cga->crtcreg & 0x1f); if (vid->operation_ctrl & 0x80) nmi_raise(); vid->reg_3df = 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) { vid->fullchange = changeframecount; cga_recalctimings(cga); } } return; case 0x03d8: old = cga->cgamode; cga->cgamode = val; if ((cga->cgamode ^ old) & 3) cga_recalctimings(cga); vid->crtc_index &= 0x1f; vid->crtc_index |= 0x80; if (vid->operation_ctrl & 0x80) nmi_raise(); else set_lcd_cols(val); return; /* PC200 control port writes ============================================== */ case 0x03de: vid->crtc_index = 0x1f; /* NMI only seems to be triggered if the value being written has the high * bit set (enable NMI). So it only protects writes to this port if you * let it? */ if (val & 0x80) { vid->operation_ctrl = val; vid->crtc_index |= 0x40; nmi_raise(); return; } timer_disable(&vid->cga.timer); timer_disable(&vid->mda.timer); timer_disable(&vid->timer); vid->operation_ctrl = val; /* Bits 0 and 1 control emulation and output mode */ amstrad_log("emulation and mode = %02X\n", val & 0x03); if (val & 1) /* Monitor */ vid->emulation = (val & 2) ? PC200_MDA : PC200_CGA; else if (vid->type == AMS_PPC512) vid->emulation = (val & 2) ? PC200_LCDM : PC200_LCDC; else vid->emulation = PC200_TV; if (vid->emulation == PC200_CGA || vid->emulation == PC200_TV) timer_advance_u64(&vid->cga.timer, 1); else if (vid->emulation == PC200_MDA) timer_advance_u64(&vid->mda.timer, 1); else timer_advance_u64(&vid->timer, 1); /* Bit 2 disables the IDA. We don't support dynamic enabling * and disabling of the IDA (instead, PCEM disconnects the * IDA from the bus altogether) so don't implement this */ /* Enable the appropriate memory ranges depending whether * the IDA is configured as MDA or CGA */ if (vid->emulation == PC200_MDA || vid->emulation == PC200_LCDM) { mem_mapping_disable(&vid->cga.mapping); mem_mapping_enable(&vid->mda.mapping); } else { mem_mapping_disable(&vid->mda.mapping); mem_mapping_enable(&vid->cga.mapping); } return; default: break; } if (addr >= 0x3D0 && addr <= 0x3DF) cga_out(addr, val, cga); if (addr >= 0x3B0 && addr <= 0x3BB) mda_out(addr, val, mda); } static void lcd_draw_char_80(amsvid_t *vid, uint32_t *buffer, uint8_t chr, uint8_t attr, int drawcursor, int blink, int sc, int mode160, uint8_t control) { int c; uint8_t bits = fontdat[chr + vid->cga.fontbase][sc]; uint8_t bright = 0; uint16_t mask; if (attr & 8) { /* bright */ /* The brightness algorithm appears to be: replace any bit sequence 011 * with 001 (assuming an extra 0 to the left of the byte). */ bright = bits; for (c = 0, mask = 0x100; c < 7; c++, mask >>= 1) { if (((bits & mask) == 0) && ((bits & (mask >> 1)) != 0) && ((bits & (mask >> 2)) != 0)) bright &= ~(mask >> 1); } bits = bright; } if (drawcursor) bits ^= 0xFF; for (c = 0, mask = 0x80; c < 8; c++, mask >>= 1) { if (mode160) buffer[c] = (attr & mask) ? blue : green; else if (control & 0x20) /* blinking */ buffer[c] = lcdcols[attr & 0x7F][blink][(bits & mask) ? 1 : 0]; else buffer[c] = lcdcols[attr][blink][(bits & mask) ? 1 : 0]; } } static void lcd_draw_char_40(amsvid_t *vid, uint32_t *buffer, uint8_t chr, uint8_t attr, int drawcursor, int blink, int sc, uint8_t control) { uint8_t bits = fontdat[chr + vid->cga.fontbase][sc]; uint8_t mask = 0x80; if (attr & 8) /* bright */ bits = bits & (bits >> 1); if (drawcursor) bits ^= 0xFF; for (uint8_t c = 0; c < 8; c++, mask >>= 1) { if (control & 0x20) { buffer[c * 2] = buffer[c * 2 + 1] = lcdcols[attr & 0x7F][blink][(bits & mask) ? 1 : 0]; } else { buffer[c * 2] = buffer[c * 2 + 1] = lcdcols[attr][blink][(bits & mask) ? 1 : 0]; } } } static void lcdm_poll(amsvid_t *vid) { mda_t *mda = &vid->mda; uint16_t ca = (mda->crtc[15] | (mda->crtc[14] << 8)) & 0x3fff; int drawcursor; int x; int oldvc; uint8_t chr; uint8_t attr; int oldsc; int blink; if (!mda->linepos) { timer_advance_u64(&vid->timer, mda->dispofftime); mda->stat |= 1; mda->linepos = 1; oldsc = mda->sc; if ((mda->crtc[8] & 3) == 3) mda->sc = (mda->sc << 1) & 7; if (mda->dispon) { if (mda->displine < mda->firstline) mda->firstline = mda->displine; mda->lastline = mda->displine; for (x = 0; x < mda->crtc[1]; x++) { chr = mda->vram[(mda->ma << 1) & 0xfff]; attr = mda->vram[((mda->ma << 1) + 1) & 0xfff]; drawcursor = ((mda->ma == ca) && mda->con && mda->cursoron); blink = ((mda->blink & 16) && (mda->ctrl & 0x20) && (attr & 0x80) && !drawcursor); lcd_draw_char_80(vid, &(buffer32->line[mda->displine])[x * 8], chr, attr, drawcursor, blink, mda->sc, 0, mda->ctrl); mda->ma++; } } mda->sc = oldsc; if (mda->vc == mda->crtc[7] && !mda->sc) mda->stat |= 8; mda->displine++; if (mda->displine >= 500) mda->displine = 0; } else { timer_advance_u64(&vid->timer, mda->dispontime); if (mda->dispon) mda->stat &= ~1; mda->linepos = 0; if (mda->vsynctime) { mda->vsynctime--; if (!mda->vsynctime) mda->stat &= ~8; } if (mda->sc == (mda->crtc[11] & 31) || ((mda->crtc[8] & 3) == 3 && mda->sc == ((mda->crtc[11] & 31) >> 1))) { mda->con = 0; } if (mda->vadj) { mda->sc++; mda->sc &= 31; mda->ma = mda->maback; mda->vadj--; if (!mda->vadj) { mda->dispon = 1; mda->ma = mda->maback = (mda->crtc[13] | (mda->crtc[12] << 8)) & 0x3fff; mda->sc = 0; } } else if (mda->sc == mda->crtc[9] || ((mda->crtc[8] & 3) == 3 && mda->sc == (mda->crtc[9] >> 1))) { mda->maback = mda->ma; mda->sc = 0; oldvc = mda->vc; mda->vc++; mda->vc &= 127; if (mda->vc == mda->crtc[6]) mda->dispon = 0; if (oldvc == mda->crtc[4]) { mda->vc = 0; mda->vadj = mda->crtc[5]; if (!mda->vadj) mda->dispon = 1; if (!mda->vadj) mda->ma = mda->maback = (mda->crtc[13] | (mda->crtc[12] << 8)) & 0x3fff; if ((mda->crtc[10] & 0x60) == 0x20) mda->cursoron = 0; else mda->cursoron = mda->blink & 16; } if (mda->vc == mda->crtc[7]) { mda->dispon = 0; mda->displine = 0; mda->vsynctime = 16; if (mda->crtc[7]) { x = mda->crtc[1] * 8; mda->lastline++; if ((x != xsize) || ((mda->lastline - mda->firstline) != ysize) || video_force_resize_get()) { xsize = x; ysize = mda->lastline - mda->firstline; if (xsize < 64) xsize = 656; if (ysize < 32) ysize = 200; set_screen_size(xsize, ysize); if (video_force_resize_get()) video_force_resize_set(0); } video_blit_memtoscreen(0, mda->firstline, xsize, ysize); frames++; video_res_x = mda->crtc[1]; video_res_y = mda->crtc[6]; video_bpp = 0; } mda->firstline = 1000; mda->lastline = 0; mda->blink++; } } else { mda->sc++; mda->sc &= 31; mda->ma = mda->maback; } if (mda->sc == (mda->crtc[10] & 31) || ((mda->crtc[8] & 3) == 3 && mda->sc == ((mda->crtc[10] & 31) >> 1))) mda->con = 1; } } static void lcdc_poll(amsvid_t *vid) { cga_t *cga = &vid->cga; int drawcursor; int x; int xs_temp; int ys_temp; int oldvc; uint8_t chr; uint8_t attr; uint16_t dat; int oldsc; uint16_t ca; int blink; ca = (cga->crtc[15] | (cga->crtc[14] << 8)) & 0x3fff; if (!cga->linepos) { timer_advance_u64(&vid->timer, cga->dispofftime); cga->cgastat |= 1; cga->linepos = 1; oldsc = cga->sc; if ((cga->crtc[8] & 3) == 3) cga->sc = ((cga->sc << 1) + cga->oddeven) & 7; if (cga->cgadispon) { if (cga->displine < cga->firstline) { cga->firstline = cga->displine; video_wait_for_buffer(); } cga->lastline = cga->displine; if (cga->cgamode & 1) { for (x = 0; x < cga->crtc[1]; x++) { chr = cga->charbuffer[x << 1]; attr = cga->charbuffer[(x << 1) + 1]; drawcursor = ((cga->ma == ca) && cga->cursorvisible && cga->cursoron); blink = ((cga->cgablink & 16) && (cga->cgamode & 0x20) && (attr & 0x80) && !drawcursor); lcd_draw_char_80(vid, &(buffer32->line[cga->displine << 1])[x * 8], chr, attr, drawcursor, blink, cga->sc, cga->cgamode & 0x40, cga->cgamode); lcd_draw_char_80(vid, &(buffer32->line[(cga->displine << 1) + 1])[x * 8], chr, attr, drawcursor, blink, cga->sc, cga->cgamode & 0x40, cga->cgamode); cga->ma++; } } else if (!(cga->cgamode & 2)) { for (x = 0; x < cga->crtc[1]; x++) { chr = cga->vram[(cga->ma << 1) & 0x3fff]; attr = cga->vram[((cga->ma << 1) + 1) & 0x3fff]; drawcursor = ((cga->ma == ca) && cga->cursorvisible && cga->cursoron); blink = ((cga->cgablink & 16) && (cga->cgamode & 0x20) && (attr & 0x80) && !drawcursor); lcd_draw_char_40(vid, &(buffer32->line[cga->displine << 1])[x * 16], chr, attr, drawcursor, blink, cga->sc, cga->cgamode); lcd_draw_char_40(vid, &(buffer32->line[(cga->displine << 1) + 1])[x * 16], chr, attr, drawcursor, blink, cga->sc, cga->cgamode); cga->ma++; } } else { /* Graphics mode */ for (x = 0; x < cga->crtc[1]; x++) { dat = (cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000)] << 8) | cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000) + 1]; cga->ma++; for (uint8_t c = 0; c < 16; c++) { buffer32->line[cga->displine << 1][(x << 4) + c] = buffer32->line[(cga->displine << 1) + 1][(x << 4) + c] = (dat & 0x8000) ? blue : green; dat <<= 1; } } } } else { if (cga->cgamode & 1) { hline(buffer32, 0, (cga->displine << 1), (cga->crtc[1] << 3), green); hline(buffer32, 0, (cga->displine << 1) + 1, (cga->crtc[1] << 3), green); } else { hline(buffer32, 0, (cga->displine << 1), (cga->crtc[1] << 4), green); hline(buffer32, 0, (cga->displine << 1) + 1, (cga->crtc[1] << 4), green); } } if (cga->cgamode & 1) x = (cga->crtc[1] << 3); else x = (cga->crtc[1] << 4); cga->sc = oldsc; if (cga->vc == cga->crtc[7] && !cga->sc) cga->cgastat |= 8; cga->displine++; if (cga->displine >= 360) cga->displine = 0; } else { timer_advance_u64(&vid->timer, cga->dispontime); cga->linepos = 0; if (cga->vsynctime) { cga->vsynctime--; if (!cga->vsynctime) cga->cgastat &= ~8; } if (cga->sc == (cga->crtc[11] & 31) || ((cga->crtc[8] & 3) == 3 && cga->sc == ((cga->crtc[11] & 31) >> 1))) { cga->cursorvisible = 0; } if ((cga->crtc[8] & 3) == 3 && cga->sc == (cga->crtc[9] >> 1)) cga->maback = cga->ma; if (cga->vadj) { cga->sc++; cga->sc &= 31; cga->ma = cga->maback; cga->vadj--; if (!cga->vadj) { cga->cgadispon = 1; cga->ma = cga->maback = (cga->crtc[13] | (cga->crtc[12] << 8)) & 0x3fff; cga->sc = 0; } } else if (cga->sc == cga->crtc[9]) { cga->maback = cga->ma; cga->sc = 0; oldvc = cga->vc; cga->vc++; cga->vc &= 127; if (cga->vc == cga->crtc[6]) cga->cgadispon = 0; if (oldvc == cga->crtc[4]) { cga->vc = 0; cga->vadj = cga->crtc[5]; if (!cga->vadj) cga->cgadispon = 1; if (!cga->vadj) cga->ma = cga->maback = (cga->crtc[13] | (cga->crtc[12] << 8)) & 0x3fff; if ((cga->crtc[10] & 0x60) == 0x20) cga->cursoron = 0; else cga->cursoron = cga->cgablink & 8; } if (cga->vc == cga->crtc[7]) { cga->cgadispon = 0; cga->displine = 0; cga->vsynctime = 16; if (cga->crtc[7]) { if (cga->cgamode & 1) x = (cga->crtc[1] << 3); else x = (cga->crtc[1] << 4); cga->lastline++; xs_temp = x; ys_temp = (cga->lastline - cga->firstline) << 1; if ((xs_temp > 0) && (ys_temp > 0)) { if (xs_temp < 64) xs_temp = 640; if (ys_temp < 32) ys_temp = 400; if ((cga->cgamode & 8) && ((xs_temp != xsize) || (ys_temp != ysize) || video_force_resize_get())) { xsize = xs_temp; ysize = ys_temp; set_screen_size(xsize, ysize); if (video_force_resize_get()) video_force_resize_set(0); } video_blit_memtoscreen(0, cga->firstline << 1, xsize, (cga->lastline - cga->firstline) << 1); } frames++; video_res_x = xsize; video_res_y = ysize; if (cga->cgamode & 1) { video_res_x /= 8; video_res_y /= cga->crtc[9] + 1; video_bpp = 0; } else if (!(cga->cgamode & 2)) { video_res_x /= 16; video_res_y /= cga->crtc[9] + 1; video_bpp = 0; } else if (!(cga->cgamode & 16)) { video_res_x /= 2; video_bpp = 2; } else video_bpp = 1; } cga->firstline = 1000; cga->lastline = 0; cga->cgablink++; cga->oddeven ^= 1; } } else { cga->sc++; cga->sc &= 31; cga->ma = cga->maback; } if (cga->cgadispon) cga->cgastat &= ~1; if (cga->sc == (cga->crtc[10] & 31) || ((cga->crtc[8] & 3) == 3 && cga->sc == ((cga->crtc[10] & 31) >> 1))) cga->cursorvisible = 1; if (cga->cgadispon && (cga->cgamode & 1)) { for (x = 0; x < (cga->crtc[1] << 1); x++) cga->charbuffer[x] = cga->vram[((cga->ma << 1) + x) & 0x3fff]; } } } static void vid_poll_200(void *priv) { amsvid_t *vid = (amsvid_t *) priv; switch (vid->emulation) { case PC200_LCDM: lcdm_poll(vid); return; case PC200_LCDC: lcdc_poll(vid); return; default: break; } } static void vid_init_200(amstrad_t *ams) { amsvid_t *vid; cga_t *cga; mda_t *mda; /* Allocate a video controller block. */ vid = (amsvid_t *) calloc(1, sizeof(amsvid_t)); vid->emulation = device_get_config_int("video_emulation"); /* Default to CGA */ vid->dipswitches = 0x10; vid->type = ams->type; if (ams->type == AMS_PC200) switch (vid->emulation) { /* DIP switches for PC200. Switches 2,3 give video emulation. * Switch 1 is 'swap floppy drives' (not implemented) */ case PC200_CGA: vid->dipswitches = 0x10; break; case PC200_MDA: vid->dipswitches = 0x30; break; case PC200_TV: vid->dipswitches = 0x00; break; /* The other combination is 'IDA disabled' (0x20) - see * m_amstrad.c */ default: break; } else switch (vid->emulation) { /* DIP switches for PPC512. Switch 1 is CRT/LCD. Switch 2 * is MDA / CGA. Switch 3 disables IDA, not implemented. */ /* 1 = on, 0 = off SW1: off = crt, on = lcd; SW2: off = mda, on = cga; SW3: off = disable built-in card, on = enable */ case PC200_CGA: vid->dipswitches = 0x08; break; case PC200_MDA: vid->dipswitches = 0x18; break; case PC200_LCDC: vid->dipswitches = 0x00; break; case PC200_LCDM: vid->dipswitches = 0x10; break; default: break; } cga = &vid->cga; mda = &vid->mda; cga->vram = mda->vram = malloc(0x4000); cga_init(cga); mda_init(mda); cga_palette = (device_get_config_int("display_type") << 1); ams_inform(vid); /* Attribute 8 is white on black (on a real MDA it's black on black) */ mda_setcol(0x08, 0, 1, 15); mda_setcol(0x88, 0, 1, 15); /* Attribute 64 is black on black (on a real MDA it's white on black) */ mda_setcol(0x40, 0, 1, 0); mda_setcol(0xC0, 0, 1, 0); cga->fontbase = (device_get_config_int("codepage") & 3) * 256; mda->fontbase = cga->fontbase; timer_add(&vid->timer, vid_poll_200, vid, 1); mem_mapping_add(&vid->mda.mapping, 0xb0000, 0x08000, mda_read, NULL, NULL, mda_write, NULL, NULL, NULL, 0, mda); 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); io_sethandler(0x03b0, 0x000c, vid_in_200, NULL, NULL, vid_out_200, NULL, NULL, vid); overscan_x = overscan_y = 16; if (ams->type == AMS_PC200) vid->invert = 0; else vid->invert = device_get_config_int("invert"); if (vid->invert) { blue = makecol(0x1C, 0x71, 0x31); green = makecol(0x0f, 0x21, 0x3f); } else { green = makecol(0x1C, 0x71, 0x31); blue = makecol(0x0f, 0x21, 0x3f); } cgapal_rebuild(); set_lcd_cols(0); timer_disable(&vid->cga.timer); timer_disable(&vid->mda.timer); timer_disable(&vid->timer); if (vid->emulation == PC200_CGA || vid->emulation == PC200_TV) timer_enable(&vid->cga.timer); else if (vid->emulation == PC200_MDA) timer_enable(&vid->mda.timer); else timer_enable(&vid->timer); ams->vid = vid; } static void vid_close_200(void *priv) { amsvid_t *vid = (amsvid_t *) priv; if (vid->cga.vram != vid->mda.vram) { free(vid->cga.vram); free(vid->mda.vram); } else free(vid->cga.vram); vid->cga.vram = vid->mda.vram = NULL; free(vid); } const device_config_t vid_200_config[] = { /* TODO: Should have options here for: * * > Display port (TTL or RF) */ // clang-format off { .name = "video_emulation", .description = "Display type", .type = CONFIG_SELECTION, .default_string = "", .default_int = PC200_CGA, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "CGA monitor", .value = PC200_CGA }, { .description = "MDA monitor", .value = PC200_MDA }, { .description = "Television", .value = PC200_TV }, { .description = "" } } }, { .name = "display_type", .description = "Monitor type", .type = CONFIG_SELECTION, .default_string = "", .default_int = 0, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "RGB", .value = 0 }, { .description = "RGB (no brown)", .value = 4 }, { .description = "Green Monochrome", .value = 1 }, { .description = "Amber Monochrome", .value = 2 }, { .description = "White Monochrome", .value = 3 }, { .description = "" } } }, { .name = "codepage", .description = "Hardware font", .type = CONFIG_SELECTION, .default_string = "", .default_int = 3, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "US English", .value = 3 }, { .description = "Portugese", .value = 2 }, { .description = "Norwegian", .value = 1 }, { .description = "Greek", .value = 0 }, { .description = "" } } }, { .name = "language", .description = "BIOS language", .type = CONFIG_SELECTION, .default_string = "", .default_int = 7, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "English", .value = 7 }, { .description = "German", .value = 6 }, { .description = "French", .value = 5 }, { .description = "Spanish", .value = 4 }, { .description = "Danish", .value = 3 }, { .description = "Swedish", .value = 2 }, { .description = "Italian", .value = 1 }, { .description = "Diagnostic mode", .value = 0 }, { .description = "" } } }, { .name = "", .description = "", .type = CONFIG_END } // clang-format on }; const device_t vid_200_device = { .name = "Amstrad PC200 (video)", .internal_name = "vid_200", .flags = 0, .local = 0, .init = NULL, .close = vid_close_200, .reset = NULL, .available = NULL, .speed_changed = vid_speed_changed_200, .force_redraw = NULL, .config = vid_200_config }; const device_config_t vid_ppc512_config[] = { /* TODO: Should have options here for: * * > Display port (TTL or RF) */ // clang-format off { .name = "video_emulation", .description = "Display type", .type = CONFIG_SELECTION, .default_string = "", .default_int = PC200_LCDC, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "CGA monitor", .value = PC200_CGA }, { .description = "MDA monitor", .value = PC200_MDA }, { .description = "LCD (CGA mode)", .value = PC200_LCDC }, { .description = "LCD (MDA mode)", .value = PC200_LCDM }, { .description = "" } }, }, { .name = "display_type", .description = "Monitor type", .type = CONFIG_SELECTION, .default_string = "", .default_int = 0, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "RGB", .value = 0 }, { .description = "RGB (no brown)", .value = 4 }, { .description = "Green Monochrome", .value = 1 }, { .description = "Amber Monochrome", .value = 2 }, { .description = "White Monochrome", .value = 3 }, { .description = "" } }, }, { .name = "codepage", .description = "Hardware font", .type = CONFIG_SELECTION, .default_string = "", .default_int = 3, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "US English", .value = 3 }, { .description = "Portugese", .value = 2 }, { .description = "Norwegian", .value = 1 }, { .description = "Greek", .value = 0 }, { .description = "" } }, }, { .name = "language", .description = "BIOS language", .type = CONFIG_SELECTION, .default_string = "", .default_int = 7, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "English", .value = 7 }, { .description = "German", .value = 6 }, { .description = "French", .value = 5 }, { .description = "Spanish", .value = 4 }, { .description = "Danish", .value = 3 }, { .description = "Swedish", .value = 2 }, { .description = "Italian", .value = 1 }, { .description = "Diagnostic mode", .value = 0 }, { .description = "" } } }, { .name = "invert", .description = "Invert LCD colors", .type = CONFIG_BINARY, .default_string = "", .default_int = 0 }, { .name = "", .description = "", .type = CONFIG_END } // clang-format on }; const device_t vid_ppc512_device = { .name = "Amstrad PPC512 (video)", .internal_name = "vid_ppc512", .flags = 0, .local = 0, .init = NULL, .close = vid_close_200, .reset = NULL, .available = NULL, .speed_changed = vid_speed_changed_200, .force_redraw = NULL, .config = vid_ppc512_config }; const device_config_t vid_pc2086_config[] = { // clang-format off { .name = "language", .description = "BIOS language", .type = CONFIG_SELECTION, .default_string = "", .default_int = 7, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "English", .value = 7 }, { .description = "Diagnostic mode", .value = 0 }, { .description = "" } } }, { .name = "", .description = "", .type = CONFIG_END } // clang-format on }; const device_t vid_pc2086_device = { .name = "Amstrad PC2086", .internal_name = "vid_pc2086", .flags = 0, .local = 0, .init = NULL, .close = NULL, .reset = NULL, .available = NULL, .speed_changed = NULL, .force_redraw = NULL, .config = vid_pc2086_config }; const device_config_t vid_pc3086_config[] = { // clang-format off { .name = "language", .description = "BIOS language", .type = CONFIG_SELECTION, .default_string = "", .default_int = 7, .file_filter = "", .spinner = { 0 }, .selection = { { .description = "English", .value = 7 }, { .description = "Diagnostic mode", .value = 3 }, { .description = "" } } }, { .name = "", .description = "", .type = CONFIG_END } // clang-format on }; const device_t vid_pc3086_device = { .name = "Amstrad PC3086", .internal_name = "vid_pc3086", .flags = 0, .local = 0, .init = NULL, .close = NULL, .reset = NULL, .available = NULL, .speed_changed = NULL, .force_redraw = NULL, .config = vid_pc3086_config }; static void ms_write(uint16_t addr, UNUSED(uint8_t val), UNUSED(void *priv)) { if ((addr == 0x78) || (addr == 0x79)) mouse_clear_x(); else mouse_clear_y(); } static uint8_t ms_read(uint16_t addr, UNUSED(void *priv)) { uint8_t ret; int delta = 0; if ((addr == 0x78) || (addr == 0x79)) { mouse_subtract_x(&delta, NULL, -128, 127, 0); mouse_clear_x(); } else { mouse_subtract_y(&delta, NULL, -128, 127, 1, 0); mouse_clear_y(); } ret = (uint8_t) (int8_t) delta; return ret; } static int ms_poll(void *priv) { amstrad_t *ams = (amstrad_t *) priv; int b = mouse_get_buttons_ex(); if ((b & 1) && !(ams->oldb & 1)) keyboard_send(0x7e); if (!(b & 1) && (ams->oldb & 1)) keyboard_send(0xfe); if ((b & 2) && !(ams->oldb & 2)) keyboard_send(0x7d); 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; 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; amstrad_log("keyboard_amstrad : 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. */ amstrad_log("AMSkb: write PB %02x (%02x)\n", val, ams->pb); if (!(ams->pb & 0x40) && (val & 0x40)) { /*Reset keyboard*/ amstrad_log("AMSkb: reset keyboard\n"); kbd_adddata(0xaa); } ams->pb = val; ppi.pb = val; speaker_update(); speaker_gated = val & 0x01; speaker_enable = val & 0x02; if (speaker_enable) was_speaker_enable = 1; pit_devs[0].set_gate(pit_devs[0].data, 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: softresetx86(); break; default: amstrad_log("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 = (ams->stat1 | 0x0d) & 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: amstrad_log("AMDkb: bad keyboard read %04X\n", port); } return ret; } static void kbd_poll(void *priv) { amstrad_t *ams = (amstrad_t *) priv; timer_advance_u64(&ams->send_delay_timer, 1000 * TIMER_USEC); if (ams->wantirq) { ams->wantirq = 0; ams->pa = ams->key_waiting; picint(2); } if (key_queue_start != key_queue_end && !ams->pa) { ams->key_waiting = key_queue[key_queue_start]; key_queue_start = (key_queue_start + 1) & 0x0f; ams->wantirq = 1; } } static void ams_write(uint16_t port, uint8_t val, void *priv) { amstrad_t *ams = (amstrad_t *) priv; switch (port) { case 0x0378: case 0x0379: case 0x037a: lpt_write(port, val, &lpt_ports[0]); break; case 0xdead: ams->dead = val; break; default: break; } } static uint8_t ams_read(uint16_t port, void *priv) { amstrad_t *ams = (amstrad_t *) priv; uint8_t ret = 0xff; switch (port) { case 0x0378: ret = lpt_read(port, &lpt_ports[0]); break; case 0x0379: /* printer control, also set LK1-3. * per John Elliott's site, this is xor'ed with 0x07 * 7 English Language. * 6 German Language. * 5 French Language. * 4 Spanish Language. * 3 Danish Language. * 2 Swedish Language. * 1 Italian Language. * 0 Diagnostic Mode. */ ret = (lpt_read(port, &lpt_ports[0]) & 0xf8) | ams->language; break; case 0x037a: /* printer status */ ret = lpt_read(port, &lpt_ports[0]) & 0x1f; switch (ams->type) { case AMS_PC1512: ret |= 0x20; break; case AMS_PC200: case AMS_PPC512: if (video_is_cga()) ret |= 0x80; else if (video_is_mda()) ret |= 0xc0; if (fdc_read(0x037f, ams->fdc) & 0x80) ret |= 0x20; break; case AMS_PC1640: if (video_is_cga()) ret |= 0x80; else if (video_is_mda()) ret |= 0xc0; switch (amstrad_latch & 0x7fffffff) { case AMSTRAD_NOLATCH: ret &= ~0x20; break; case AMSTRAD_SW9: ret &= ~0x20; break; case AMSTRAD_SW10: ret |= 0x20; break; default: break; } break; default: break; } break; case 0x03de: ret = 0x20; break; case 0xdead: ret = ams->dead; break; default: break; } return ret; } static const scancode scancode_pc200[512] = { // clang-format off { .mk = { 0 }, .brk = { 0 } }, /* 000 */ { .mk = { 0x01, 0 }, .brk = { 0x81, 0 } }, /* 001 */ { .mk = { 0x02, 0 }, .brk = { 0x82, 0 } }, /* 002 */ { .mk = { 0x03, 0 }, .brk = { 0x83, 0 } }, /* 003 */ { .mk = { 0x04, 0 }, .brk = { 0x84, 0 } }, /* 004 */ { .mk = { 0x05, 0 }, .brk = { 0x85, 0 } }, /* 005 */ { .mk = { 0x06, 0 }, .brk = { 0x86, 0 } }, /* 006 */ { .mk = { 0x07, 0 }, .brk = { 0x87, 0 } }, /* 007 */ { .mk = { 0x08, 0 }, .brk = { 0x88, 0 } }, /* 008 */ { .mk = { 0x09, 0 }, .brk = { 0x89, 0 } }, /* 009 */ { .mk = { 0x0a, 0 }, .brk = { 0x8a, 0 } }, /* 00a */ { .mk = { 0x0b, 0 }, .brk = { 0x8b, 0 } }, /* 00b */ { .mk = { 0x0c, 0 }, .brk = { 0x8c, 0 } }, /* 00c */ { .mk = { 0x0d, 0 }, .brk = { 0x8d, 0 } }, /* 00d */ { .mk = { 0x0e, 0 }, .brk = { 0x8e, 0 } }, /* 00e */ { .mk = { 0x0f, 0 }, .brk = { 0x8f, 0 } }, /* 00f */ { .mk = { 0x10, 0 }, .brk = { 0x90, 0 } }, /* 010 */ { .mk = { 0x11, 0 }, .brk = { 0x91, 0 } }, /* 011 */ { .mk = { 0x12, 0 }, .brk = { 0x92, 0 } }, /* 012 */ { .mk = { 0x13, 0 }, .brk = { 0x93, 0 } }, /* 013 */ { .mk = { 0x14, 0 }, .brk = { 0x94, 0 } }, /* 014 */ { .mk = { 0x15, 0 }, .brk = { 0x95, 0 } }, /* 015 */ { .mk = { 0x16, 0 }, .brk = { 0x96, 0 } }, /* 016 */ { .mk = { 0x17, 0 }, .brk = { 0x97, 0 } }, /* 017 */ { .mk = { 0x18, 0 }, .brk = { 0x98, 0 } }, /* 018 */ { .mk = { 0x19, 0 }, .brk = { 0x99, 0 } }, /* 019 */ { .mk = { 0x1a, 0 }, .brk = { 0x9a, 0 } }, /* 01a */ { .mk = { 0x1b, 0 }, .brk = { 0x9b, 0 } }, /* 01b */ { .mk = { 0x1c, 0 }, .brk = { 0x9c, 0 } }, /* 01c */ { .mk = { 0x1d, 0 }, .brk = { 0x9d, 0 } }, /* 01d */ { .mk = { 0x1e, 0 }, .brk = { 0x9e, 0 } }, /* 01e */ { .mk = { 0x1f, 0 }, .brk = { 0x9f, 0 } }, /* 01f */ { .mk = { 0x20, 0 }, .brk = { 0xa0, 0 } }, /* 020 */ { .mk = { 0x21, 0 }, .brk = { 0xa1, 0 } }, /* 021 */ { .mk = { 0x22, 0 }, .brk = { 0xa2, 0 } }, /* 022 */ { .mk = { 0x23, 0 }, .brk = { 0xa3, 0 } }, /* 023 */ { .mk = { 0x24, 0 }, .brk = { 0xa4, 0 } }, /* 024 */ { .mk = { 0x25, 0 }, .brk = { 0xa5, 0 } }, /* 025 */ { .mk = { 0x26, 0 }, .brk = { 0xa6, 0 } }, /* 026 */ { .mk = { 0x27, 0 }, .brk = { 0xa7, 0 } }, /* 027 */ { .mk = { 0x28, 0 }, .brk = { 0xa8, 0 } }, /* 028 */ { .mk = { 0x29, 0 }, .brk = { 0xa9, 0 } }, /* 029 */ { .mk = { 0x2a, 0 }, .brk = { 0xaa, 0 } }, /* 02a */ { .mk = { 0x2b, 0 }, .brk = { 0xab, 0 } }, /* 02b */ { .mk = { 0x2c, 0 }, .brk = { 0xac, 0 } }, /* 02c */ { .mk = { 0x2d, 0 }, .brk = { 0xad, 0 } }, /* 02d */ { .mk = { 0x2e, 0 }, .brk = { 0xae, 0 } }, /* 02e */ { .mk = { 0x2f, 0 }, .brk = { 0xaf, 0 } }, /* 02f */ { .mk = { 0x30, 0 }, .brk = { 0xb0, 0 } }, /* 030 */ { .mk = { 0x31, 0 }, .brk = { 0xb1, 0 } }, /* 031 */ { .mk = { 0x32, 0 }, .brk = { 0xb2, 0 } }, /* 032 */ { .mk = { 0x33, 0 }, .brk = { 0xb3, 0 } }, /* 033 */ { .mk = { 0x34, 0 }, .brk = { 0xb4, 0 } }, /* 034 */ { .mk = { 0x35, 0 }, .brk = { 0xb5, 0 } }, /* 035 */ { .mk = { 0x36, 0 }, .brk = { 0xb6, 0 } }, /* 036 */ { .mk = { 0x37, 0 }, .brk = { 0xb7, 0 } }, /* 037 */ { .mk = { 0x38, 0 }, .brk = { 0xb8, 0 } }, /* 038 */ { .mk = { 0x39, 0 }, .brk = { 0xb9, 0 } }, /* 039 */ { .mk = { 0x3a, 0 }, .brk = { 0xba, 0 } }, /* 03a */ { .mk = { 0x3b, 0 }, .brk = { 0xbb, 0 } }, /* 03b */ { .mk = { 0x3c, 0 }, .brk = { 0xbc, 0 } }, /* 03c */ { .mk = { 0x3d, 0 }, .brk = { 0xbd, 0 } }, /* 03d */ { .mk = { 0x3e, 0 }, .brk = { 0xbe, 0 } }, /* 03e */ { .mk = { 0x3f, 0 }, .brk = { 0xbf, 0 } }, /* 03f */ { .mk = { 0x40, 0 }, .brk = { 0xc0, 0 } }, /* 040 */ { .mk = { 0x41, 0 }, .brk = { 0xc1, 0 } }, /* 041 */ { .mk = { 0x42, 0 }, .brk = { 0xc2, 0 } }, /* 042 */ { .mk = { 0x43, 0 }, .brk = { 0xc3, 0 } }, /* 043 */ { .mk = { 0x44, 0 }, .brk = { 0xc4, 0 } }, /* 044 */ { .mk = { 0x45, 0 }, .brk = { 0xc5, 0 } }, /* 045 */ { .mk = { 0x46, 0 }, .brk = { 0xc6, 0 } }, /* 046 */ { .mk = { 0x47, 0 }, .brk = { 0xc7, 0 } }, /* 047 */ { .mk = { 0x48, 0 }, .brk = { 0xc8, 0 } }, /* 048 */ { .mk = { 0x49, 0 }, .brk = { 0xc9, 0 } }, /* 049 */ { .mk = { 0x4a, 0 }, .brk = { 0xca, 0 } }, /* 04a */ { .mk = { 0x4b, 0 }, .brk = { 0xcb, 0 } }, /* 04b */ { .mk = { 0x4c, 0 }, .brk = { 0xcc, 0 } }, /* 04c */ { .mk = { 0x4d, 0 }, .brk = { 0xcd, 0 } }, /* 04d */ { .mk = { 0x4e, 0 }, .brk = { 0xce, 0 } }, /* 04e */ { .mk = { 0x4f, 0 }, .brk = { 0xcf, 0 } }, /* 04f */ { .mk = { 0x50, 0 }, .brk = { 0xd0, 0 } }, /* 050 */ { .mk = { 0x51, 0 }, .brk = { 0xd1, 0 } }, /* 051 */ { .mk = { 0x52, 0 }, .brk = { 0xd2, 0 } }, /* 052 */ { .mk = { 0x53, 0 }, .brk = { 0xd3, 0 } }, /* 053 */ { .mk = { 0x54, 0 }, .brk = { 0xd4, 0 } }, /* 054 */ { .mk = { 0x55, 0 }, .brk = { 0xd5, 0 } }, /* 055 */ { .mk = { 0x56, 0 }, .brk = { 0xd6, 0 } }, /* 056 */ { .mk = { 0x57, 0 }, .brk = { 0xd7, 0 } }, /* 057 */ { .mk = { 0x58, 0 }, .brk = { 0xd8, 0 } }, /* 058 */ { .mk = { 0x59, 0 }, .brk = { 0xd9, 0 } }, /* 059 */ { .mk = { 0x5a, 0 }, .brk = { 0xda, 0 } }, /* 05a */ { .mk = { 0x5b, 0 }, .brk = { 0xdb, 0 } }, /* 05b */ { .mk = { 0x5c, 0 }, .brk = { 0xdc, 0 } }, /* 05c */ { .mk = { 0x5d, 0 }, .brk = { 0xdd, 0 } }, /* 05d */ { .mk = { 0x5e, 0 }, .brk = { 0xde, 0 } }, /* 05e */ { .mk = { 0x5f, 0 }, .brk = { 0xdf, 0 } }, /* 05f */ { .mk = { 0x60, 0 }, .brk = { 0xe0, 0 } }, /* 060 */ { .mk = { 0x61, 0 }, .brk = { 0xe1, 0 } }, /* 061 */ { .mk = { 0x62, 0 }, .brk = { 0xe2, 0 } }, /* 062 */ { .mk = { 0x63, 0 }, .brk = { 0xe3, 0 } }, /* 063 */ { .mk = { 0x64, 0 }, .brk = { 0xe4, 0 } }, /* 064 */ { .mk = { 0x65, 0 }, .brk = { 0xe5, 0 } }, /* 065 */ { .mk = { 0x66, 0 }, .brk = { 0xe6, 0 } }, /* 066 */ { .mk = { 0x67, 0 }, .brk = { 0xe7, 0 } }, /* 067 */ { .mk = { 0x68, 0 }, .brk = { 0xe8, 0 } }, /* 068 */ { .mk = { 0x69, 0 }, .brk = { 0xe9, 0 } }, /* 069 */ { .mk = { 0x6a, 0 }, .brk = { 0xea, 0 } }, /* 06a */ { .mk = { 0x6b, 0 }, .brk = { 0xeb, 0 } }, /* 06b */ { .mk = { 0x6c, 0 }, .brk = { 0xec, 0 } }, /* 06c */ { .mk = { 0x6d, 0 }, .brk = { 0xed, 0 } }, /* 06d */ { .mk = { 0x6e, 0 }, .brk = { 0xee, 0 } }, /* 06e */ { .mk = { 0x6f, 0 }, .brk = { 0xef, 0 } }, /* 06f */ { .mk = { 0x70, 0 }, .brk = { 0xf0, 0 } }, /* 070 */ { .mk = { 0x71, 0 }, .brk = { 0xf1, 0 } }, /* 071 */ { .mk = { 0x72, 0 }, .brk = { 0xf2, 0 } }, /* 072 */ { .mk = { 0x73, 0 }, .brk = { 0xf3, 0 } }, /* 073 */ { .mk = { 0x74, 0 }, .brk = { 0xf4, 0 } }, /* 074 */ { .mk = { 0x75, 0 }, .brk = { 0xf5, 0 } }, /* 075 */ { .mk = { 0x76, 0 }, .brk = { 0xf6, 0 } }, /* 076 */ { .mk = { 0x77, 0 }, .brk = { 0xf7, 0 } }, /* 077 */ { .mk = { 0x78, 0 }, .brk = { 0xf8, 0 } }, /* 078 */ { .mk = { 0x79, 0 }, .brk = { 0xf9, 0 } }, /* 079 */ { .mk = { 0x7a, 0 }, .brk = { 0xfa, 0 } }, /* 07a */ { .mk = { 0x7b, 0 }, .brk = { 0xfb, 0 } }, /* 07b */ { .mk = { 0x7c, 0 }, .brk = { 0xfc, 0 } }, /* 07c */ { .mk = { 0x7d, 0 }, .brk = { 0xfd, 0 } }, /* 07d */ { .mk = { 0x7e, 0 }, .brk = { 0xfe, 0 } }, /* 07e */ { .mk = { 0x7f, 0 }, .brk = { 0xff, 0 } }, /* 07f */ { .mk = { 0x80, 0 }, .brk = { 0 } }, /* 080 */ { .mk = { 0x81, 0 }, .brk = { 0 } }, /* 081 */ { .mk = { 0x82, 0 }, .brk = { 0 } }, /* 082 */ { .mk = { 0 }, .brk = { 0 } }, /* 083 */ { .mk = { 0 }, .brk = { 0 } }, /* 084 */ { .mk = { 0x85, 0 }, .brk = { 0 } }, /* 085 */ { .mk = { 0x86, 0 }, .brk = { 0 } }, /* 086 */ { .mk = { 0x87, 0 }, .brk = { 0 } }, /* 087 */ { .mk = { 0x88, 0 }, .brk = { 0 } }, /* 088 */ { .mk = { 0x89, 0 }, .brk = { 0 } }, /* 089 */ { .mk = { 0x8a, 0 }, .brk = { 0 } }, /* 08a */ { .mk = { 0x8b, 0 }, .brk = { 0 } }, /* 08b */ { .mk = { 0x8c, 0 }, .brk = { 0 } }, /* 08c */ { .mk = { 0x8d, 0 }, .brk = { 0 } }, /* 08d */ { .mk = { 0x8e, 0 }, .brk = { 0 } }, /* 08e */ { .mk = { 0x8f, 0 }, .brk = { 0 } }, /* 08f */ { .mk = { 0x90, 0 }, .brk = { 0 } }, /* 090 */ { .mk = { 0x91, 0 }, .brk = { 0 } }, /* 091 */ { .mk = { 0x92, 0 }, .brk = { 0 } }, /* 092 */ { .mk = { 0x93, 0 }, .brk = { 0 } }, /* 093 */ { .mk = { 0x94, 0 }, .brk = { 0 } }, /* 094 */ { .mk = { 0x95, 0 }, .brk = { 0 } }, /* 095 */ { .mk = { 0x96, 0 }, .brk = { 0 } }, /* 096 */ { .mk = { 0x97, 0 }, .brk = { 0 } }, /* 097 */ { .mk = { 0x98, 0 }, .brk = { 0 } }, /* 098 */ { .mk = { 0x99, 0 }, .brk = { 0 } }, /* 099 */ { .mk = { 0x9a, 0 }, .brk = { 0 } }, /* 09a */ { .mk = { 0x9b, 0 }, .brk = { 0 } }, /* 09b */ { .mk = { 0x9c, 0 }, .brk = { 0 } }, /* 09c */ { .mk = { 0x9d, 0 }, .brk = { 0 } }, /* 09d */ { .mk = { 0x9e, 0 }, .brk = { 0 } }, /* 09e */ { .mk = { 0x9f, 0 }, .brk = { 0 } }, /* 09f */ { .mk = { 0xa0, 0 }, .brk = { 0 } }, /* 0a0 */ { .mk = { 0xa1, 0 }, .brk = { 0 } }, /* 0a1 */ { .mk = { 0xa2, 0 }, .brk = { 0 } }, /* 0a2 */ { .mk = { 0xa3, 0 }, .brk = { 0 } }, /* 0a3 */ { .mk = { 0xa4, 0 }, .brk = { 0 } }, /* 0a4 */ { .mk = { 0xa5, 0 }, .brk = { 0 } }, /* 0a5 */ { .mk = { 0xa6, 0 }, .brk = { 0 } }, /* 0a6 */ { .mk = { 0xa7, 0 }, .brk = { 0 } }, /* 0a7 */ { .mk = { 0xa8, 0 }, .brk = { 0 } }, /* 0a8 */ { .mk = { 0xa9, 0 }, .brk = { 0 } }, /* 0a9 */ { .mk = { 0xaa, 0 }, .brk = { 0 } }, /* 0aa */ { .mk = { 0xab, 0 }, .brk = { 0 } }, /* 0ab */ { .mk = { 0xac, 0 }, .brk = { 0 } }, /* 0ac */ { .mk = { 0xad, 0 }, .brk = { 0 } }, /* 0ad */ { .mk = { 0xae, 0 }, .brk = { 0 } }, /* 0ae */ { .mk = { 0xaf, 0 }, .brk = { 0 } }, /* 0af */ { .mk = { 0xb0, 0 }, .brk = { 0 } }, /* 0b0 */ { .mk = { 0xb1, 0 }, .brk = { 0 } }, /* 0b1 */ { .mk = { 0xb2, 0 }, .brk = { 0 } }, /* 0b2 */ { .mk = { 0xb3, 0 }, .brk = { 0 } }, /* 0b3 */ { .mk = { 0xb4, 0 }, .brk = { 0 } }, /* 0b4 */ { .mk = { 0xb5, 0 }, .brk = { 0 } }, /* 0b5 */ { .mk = { 0xb6, 0 }, .brk = { 0 } }, /* 0b6 */ { .mk = { 0xb7, 0 }, .brk = { 0 } }, /* 0b7 */ { .mk = { 0xb8, 0 }, .brk = { 0 } }, /* 0b8 */ { .mk = { 0xb9, 0 }, .brk = { 0 } }, /* 0b9 */ { .mk = { 0xba, 0 }, .brk = { 0 } }, /* 0ba */ { .mk = { 0xbb, 0 }, .brk = { 0 } }, /* 0bb */ { .mk = { 0xbc, 0 }, .brk = { 0 } }, /* 0bc */ { .mk = { 0xbd, 0 }, .brk = { 0 } }, /* 0bd */ { .mk = { 0xbe, 0 }, .brk = { 0 } }, /* 0be */ { .mk = { 0xbf, 0 }, .brk = { 0 } }, /* 0bf */ { .mk = { 0xc0, 0 }, .brk = { 0 } }, /* 0c0 */ { .mk = { 0xc1, 0 }, .brk = { 0 } }, /* 0c1 */ { .mk = { 0xc2, 0 }, .brk = { 0 } }, /* 0c2 */ { .mk = { 0xc3, 0 }, .brk = { 0 } }, /* 0c3 */ { .mk = { 0xc4, 0 }, .brk = { 0 } }, /* 0c4 */ { .mk = { 0xc5, 0 }, .brk = { 0 } }, /* 0c5 */ { .mk = { 0xc6, 0 }, .brk = { 0 } }, /* 0c6 */ { .mk = { 0xc7, 0 }, .brk = { 0 } }, /* 0c7 */ { .mk = { 0xc8, 0 }, .brk = { 0 } }, /* 0c8 */ { .mk = { 0xc9, 0 }, .brk = { 0 } }, /* 0c9 */ { .mk = { 0xca, 0 }, .brk = { 0 } }, /* 0ca */ { .mk = { 0xcb, 0 }, .brk = { 0 } }, /* 0cb */ { .mk = { 0xcc, 0 }, .brk = { 0 } }, /* 0cc */ { .mk = { 0xcd, 0 }, .brk = { 0 } }, /* 0cd */ { .mk = { 0xce, 0 }, .brk = { 0 } }, /* 0ce */ { .mk = { 0xcf, 0 }, .brk = { 0 } }, /* 0cf */ { .mk = { 0xd0, 0 }, .brk = { 0 } }, /* 0d0 */ { .mk = { 0xd1, 0 }, .brk = { 0 } }, /* 0d1 */ { .mk = { 0xd2, 0 }, .brk = { 0 } }, /* 0d2 */ { .mk = { 0xd3, 0 }, .brk = { 0 } }, /* 0d3 */ { .mk = { 0xd4, 0 }, .brk = { 0 } }, /* 0d4 */ { .mk = { 0xd5, 0 }, .brk = { 0 } }, /* 0d5 */ { .mk = { 0xd6, 0 }, .brk = { 0 } }, /* 0d6 */ { .mk = { 0xd7, 0 }, .brk = { 0 } }, /* 0d7 */ { .mk = { 0xd8, 0 }, .brk = { 0 } }, /* 0d8 */ { .mk = { 0xd9, 0 }, .brk = { 0 } }, /* 0d9 */ { .mk = { 0xda, 0 }, .brk = { 0 } }, /* 0da */ { .mk = { 0xdb, 0 }, .brk = { 0 } }, /* 0db */ { .mk = { 0xdc, 0 }, .brk = { 0 } }, /* 0dc */ { .mk = { 0xdd, 0 }, .brk = { 0 } }, /* 0dd */ { .mk = { 0xde, 0 }, .brk = { 0 } }, /* 0de */ { .mk = { 0xdf, 0 }, .brk = { 0 } }, /* 0df */ { .mk = { 0xe0, 0 }, .brk = { 0 } }, /* 0e0 */ { .mk = { 0xe1, 0 }, .brk = { 0 } }, /* 0e1 */ { .mk = { 0xe2, 0 }, .brk = { 0 } }, /* 0e2 */ { .mk = { 0xe3, 0 }, .brk = { 0 } }, /* 0e3 */ { .mk = { 0xe4, 0 }, .brk = { 0 } }, /* 0e4 */ { .mk = { 0xe5, 0 }, .brk = { 0 } }, /* 0e5 */ { .mk = { 0xe6, 0 }, .brk = { 0 } }, /* 0e6 */ { .mk = { 0xe7, 0 }, .brk = { 0 } }, /* 0e7 */ { .mk = { 0xe8, 0 }, .brk = { 0 } }, /* 0e8 */ { .mk = { 0xe9, 0 }, .brk = { 0 } }, /* 0e9 */ { .mk = { 0xea, 0 }, .brk = { 0 } }, /* 0ea */ { .mk = { 0xeb, 0 }, .brk = { 0 } }, /* 0eb */ { .mk = { 0xec, 0 }, .brk = { 0 } }, /* 0ec */ { .mk = { 0xed, 0 }, .brk = { 0 } }, /* 0ed */ { .mk = { 0xee, 0 }, .brk = { 0 } }, /* 0ee */ { .mk = { 0xef, 0 }, .brk = { 0 } }, /* 0ef */ { .mk = { 0 }, .brk = { 0 } }, /* 0f0 */ { .mk = { 0xf1, 0 }, .brk = { 0 } }, /* 0f1 */ { .mk = { 0xf2, 0 }, .brk = { 0 } }, /* 0f2 */ { .mk = { 0xf3, 0 }, .brk = { 0 } }, /* 0f3 */ { .mk = { 0xf4, 0 }, .brk = { 0 } }, /* 0f4 */ { .mk = { 0xf5, 0 }, .brk = { 0 } }, /* 0f5 */ { .mk = { 0xf6, 0 }, .brk = { 0 } }, /* 0f6 */ { .mk = { 0xf7, 0 }, .brk = { 0 } }, /* 0f7 */ { .mk = { 0xf8, 0 }, .brk = { 0 } }, /* 0f8 */ { .mk = { 0xf9, 0 }, .brk = { 0 } }, /* 0f9 */ { .mk = { 0xfa, 0 }, .brk = { 0 } }, /* 0fa */ { .mk = { 0xfb, 0 }, .brk = { 0 } }, /* 0fb */ { .mk = { 0xfc, 0 }, .brk = { 0 } }, /* 0fc */ { .mk = { 0xfd, 0 }, .brk = { 0 } }, /* 0fd */ { .mk = { 0xfe, 0 }, .brk = { 0 } }, /* 0fe */ { .mk = { 0xff, 0 }, .brk = { 0 } }, /* 0ff */ { .mk = {0xe1, 0x1d, 0 }, .brk = { 0xe1, 0x9d, 0 } }, /* 100 */ { .mk = {0xe0, 0x01, 0 }, .brk = { 0xe0, 0x81, 0 } }, /* 101 */ { .mk = {0xe0, 0x02, 0 }, .brk = { 0xe0, 0x82, 0 } }, /* 102 */ { .mk = {0xe0, 0x03, 0 }, .brk = { 0xe0, 0x83, 0 } }, /* 103 */ { .mk = {0xe0, 0x04, 0 }, .brk = { 0xe0, 0x84, 0 } }, /* 104 */ { .mk = {0xe0, 0x05, 0 }, .brk = { 0xe0, 0x85, 0 } }, /* 105 */ { .mk = {0xe0, 0x06, 0 }, .brk = { 0xe0, 0x86, 0 } }, /* 106 */ { .mk = {0xe0, 0x07, 0 }, .brk = { 0xe0, 0x87, 0 } }, /* 107 */ { .mk = {0xe0, 0x08, 0 }, .brk = { 0xe0, 0x88, 0 } }, /* 108 */ { .mk = {0xe0, 0x09, 0 }, .brk = { 0xe0, 0x89, 0 } }, /* 109 */ { .mk = {0xe0, 0x0a, 0 }, .brk = { 0xe0, 0x8a, 0 } }, /* 10a */ { .mk = {0xe0, 0x0b, 0 }, .brk = { 0xe0, 0x8b, 0 } }, /* 10b */ { .mk = {0xe0, 0x0c, 0 }, .brk = { 0xe0, 0x8c, 0 } }, /* 10c */ { .mk = { 0 }, .brk = { 0 } }, /* 10d */ { .mk = {0xe0, 0x0e, 0 }, .brk = { 0xe0, 0x8e, 0 } }, /* 10e */ { .mk = {0xe0, 0x0f, 0 }, .brk = { 0xe0, 0x8f, 0 } }, /* 10f */ { .mk = {0xe0, 0x10, 0 }, .brk = { 0xe0, 0x90, 0 } }, /* 110 */ { .mk = {0xe0, 0x11, 0 }, .brk = { 0xe0, 0x91, 0 } }, /* 111 */ { .mk = {0xe0, 0x12, 0 }, .brk = { 0xe0, 0x92, 0 } }, /* 112 */ { .mk = {0xe0, 0x13, 0 }, .brk = { 0xe0, 0x93, 0 } }, /* 113 */ { .mk = {0xe0, 0x14, 0 }, .brk = { 0xe0, 0x94, 0 } }, /* 114 */ { .mk = {0xe0, 0x15, 0 }, .brk = { 0xe0, 0x95, 0 } }, /* 115 */ { .mk = {0xe0, 0x16, 0 }, .brk = { 0xe0, 0x96, 0 } }, /* 116 */ { .mk = {0xe0, 0x17, 0 }, .brk = { 0xe0, 0x97, 0 } }, /* 117 */ { .mk = {0xe0, 0x18, 0 }, .brk = { 0xe0, 0x98, 0 } }, /* 118 */ { .mk = {0xe0, 0x19, 0 }, .brk = { 0xe0, 0x99, 0 } }, /* 119 */ { .mk = {0xe0, 0x1a, 0 }, .brk = { 0xe0, 0x9a, 0 } }, /* 11a */ { .mk = {0xe0, 0x1b, 0 }, .brk = { 0xe0, 0x9b, 0 } }, /* 11b */ { .mk = {0xe0, 0x1c, 0 }, .brk = { 0xe0, 0x9c, 0 } }, /* 11c */ { .mk = {0xe0, 0x1d, 0 }, .brk = { 0xe0, 0x9d, 0 } }, /* 11d */ { .mk = {0xe0, 0x1e, 0 }, .brk = { 0xe0, 0x9e, 0 } }, /* 11e */ { .mk = {0xe0, 0x1f, 0 }, .brk = { 0xe0, 0x9f, 0 } }, /* 11f */ { .mk = {0xe0, 0x20, 0 }, .brk = { 0xe0, 0xa0, 0 } }, /* 120 */ { .mk = {0xe0, 0x21, 0 }, .brk = { 0xe0, 0xa1, 0 } }, /* 121 */ { .mk = {0xe0, 0x22, 0 }, .brk = { 0xe0, 0xa2, 0 } }, /* 122 */ { .mk = {0xe0, 0x23, 0 }, .brk = { 0xe0, 0xa3, 0 } }, /* 123 */ { .mk = {0xe0, 0x24, 0 }, .brk = { 0xe0, 0xa4, 0 } }, /* 124 */ { .mk = {0xe0, 0x25, 0 }, .brk = { 0xe0, 0xa5, 0 } }, /* 125 */ { .mk = {0xe0, 0x26, 0 }, .brk = { 0xe0, 0xa6, 0 } }, /* 126 */ { .mk = { 0 }, .brk = { 0 } }, /* 127 */ { .mk = { 0 }, .brk = { 0 } }, /* 128 */ { .mk = { 0 }, .brk = { 0 } }, /* 129 */ { .mk = { 0 }, .brk = { 0 } }, /* 12a */ { .mk = { 0 }, .brk = { 0 } }, /* 12b */ { .mk = {0xe0, 0x2c, 0 }, .brk = { 0xe0, 0xac, 0 } }, /* 12c */ { .mk = {0xe0, 0x2d, 0 }, .brk = { 0xe0, 0xad, 0 } }, /* 12d */ { .mk = {0xe0, 0x2e, 0 }, .brk = { 0xe0, 0xae, 0 } }, /* 12e */ { .mk = {0xe0, 0x2f, 0 }, .brk = { 0xe0, 0xaf, 0 } }, /* 12f */ { .mk = {0xe0, 0x30, 0 }, .brk = { 0xe0, 0xb0, 0 } }, /* 130 */ { .mk = {0xe0, 0x31, 0 }, .brk = { 0xe0, 0xb1, 0 } }, /* 131 */ { .mk = {0xe0, 0x32, 0 }, .brk = { 0xe0, 0xb2, 0 } }, /* 132 */ { .mk = { 0 }, .brk = { 0 } }, /* 133 */ { .mk = {0xe0, 0x34, 0 }, .brk = { 0xe0, 0xb4, 0 } }, /* 134 */ { .mk = {0xe0, 0x35, 0 }, .brk = { 0xe0, 0xb5, 0 } }, /* 135 */ { .mk = { 0 }, .brk = { 0 } }, /* 136 */ { .mk = {0xe0, 0x37, 0 }, .brk = { 0xe0, 0xb7, 0 } }, /* 137 */ { .mk = {0xe0, 0x38, 0 }, .brk = { 0xe0, 0xb8, 0 } }, /* 138 */ { .mk = { 0 }, .brk = { 0 } }, /* 139 */ { .mk = {0xe0, 0x3a, 0 }, .brk = { 0xe0, 0xba, 0 } }, /* 13a */ { .mk = {0xe0, 0x3b, 0 }, .brk = { 0xe0, 0xbb, 0 } }, /* 13b */ { .mk = {0xe0, 0x3c, 0 }, .brk = { 0xe0, 0xbc, 0 } }, /* 13c */ { .mk = {0xe0, 0x3d, 0 }, .brk = { 0xe0, 0xbd, 0 } }, /* 13d */ { .mk = {0xe0, 0x3e, 0 }, .brk = { 0xe0, 0xbe, 0 } }, /* 13e */ { .mk = {0xe0, 0x3f, 0 }, .brk = { 0xe0, 0xbf, 0 } }, /* 13f */ { .mk = {0xe0, 0x40, 0 }, .brk = { 0xe0, 0xc0, 0 } }, /* 140 */ { .mk = {0xe0, 0x41, 0 }, .brk = { 0xe0, 0xc1, 0 } }, /* 141 */ { .mk = {0xe0, 0x42, 0 }, .brk = { 0xe0, 0xc2, 0 } }, /* 142 */ { .mk = {0xe0, 0x43, 0 }, .brk = { 0xe0, 0xc3, 0 } }, /* 143 */ { .mk = {0xe0, 0x44, 0 }, .brk = { 0xe0, 0xc4, 0 } }, /* 144 */ { .mk = { 0 }, .brk = { 0 } }, /* 145 */ { .mk = {0xe0, 0x46, 0 }, .brk = { 0xe0, 0xc6, 0 } }, /* 146 */ { .mk = {0xe0, 0x47, 0 }, .brk = { 0xe0, 0xc7, 0 } }, /* 147 */ { .mk = {0xe0, 0x48, 0 }, .brk = { 0xe0, 0xc8, 0 } }, /* 148 */ { .mk = {0xe0, 0x49, 0 }, .brk = { 0xe0, 0xc9, 0 } }, /* 149 */ { .mk = { 0 }, .brk = { 0 } }, /* 14a */ { .mk = {0xe0, 0x4b, 0 }, .brk = { 0xe0, 0xcb, 0 } }, /* 14b */ { .mk = {0xe0, 0x4c, 0 }, .brk = { 0xe0, 0xcc, 0 } }, /* 14c */ { .mk = {0xe0, 0x4d, 0 }, .brk = { 0xe0, 0xcd, 0 } }, /* 14d */ { .mk = {0xe0, 0x4e, 0 }, .brk = { 0xe0, 0xce, 0 } }, /* 14e */ { .mk = {0xe0, 0x4f, 0 }, .brk = { 0xe0, 0xcf, 0 } }, /* 14f */ { .mk = {0xe0, 0x50, 0 }, .brk = { 0xe0, 0xd0, 0 } }, /* 150 */ { .mk = {0xe0, 0x51, 0 }, .brk = { 0xe0, 0xd1, 0 } }, /* 151 */ { .mk = {0xe0, 0x52, 0 }, .brk = { 0xe0, 0xd2, 0 } }, /* 152 */ { .mk = {0xe0, 0x53, 0 }, .brk = { 0xe0, 0xd3, 0 } }, /* 153 */ { .mk = { 0 }, .brk = { 0 } }, /* 154 */ { .mk = {0xe0, 0x55, 0 }, .brk = { 0xe0, 0xd5, 0 } }, /* 155 */ { .mk = { 0 }, .brk = { 0 } }, /* 156 */ { .mk = {0xe0, 0x57, 0 }, .brk = { 0xe0, 0xd7, 0 } }, /* 157 */ { .mk = {0xe0, 0x58, 0 }, .brk = { 0xe0, 0xd8, 0 } }, /* 158 */ { .mk = {0xe0, 0x59, 0 }, .brk = { 0xe0, 0xd9, 0 } }, /* 159 */ { .mk = {0xe0, 0x5a, 0 }, .brk = { 0xe0, 0xaa, 0 } }, /* 15a */ { .mk = {0xe0, 0x5b, 0 }, .brk = { 0xe0, 0xdb, 0 } }, /* 15b */ { .mk = {0xe0, 0x5c, 0 }, .brk = { 0xe0, 0xdc, 0 } }, /* 15c */ { .mk = {0xe0, 0x5d, 0 }, .brk = { 0xe0, 0xdd, 0 } }, /* 15d */ { .mk = {0xe0, 0x5e, 0 }, .brk = { 0xe0, 0xee, 0 } }, /* 15e */ { .mk = {0xe0, 0x5f, 0 }, .brk = { 0xe0, 0xdf, 0 } }, /* 15f */ { .mk = { 0 }, .brk = { 0 } }, /* 160 */ { .mk = {0xe0, 0x61, 0 }, .brk = { 0xe0, 0xe1, 0 } }, /* 161 */ { .mk = {0xe0, 0x62, 0 }, .brk = { 0xe0, 0xe2, 0 } }, /* 162 */ { .mk = {0xe0, 0x63, 0 }, .brk = { 0xe0, 0xe3, 0 } }, /* 163 */ { .mk = {0xe0, 0x64, 0 }, .brk = { 0xe0, 0xe4, 0 } }, /* 164 */ { .mk = {0xe0, 0x65, 0 }, .brk = { 0xe0, 0xe5, 0 } }, /* 165 */ { .mk = {0xe0, 0x66, 0 }, .brk = { 0xe0, 0xe6, 0 } }, /* 166 */ { .mk = {0xe0, 0x67, 0 }, .brk = { 0xe0, 0xe7, 0 } }, /* 167 */ { .mk = {0xe0, 0x68, 0 }, .brk = { 0xe0, 0xe8, 0 } }, /* 168 */ { .mk = {0xe0, 0x69, 0 }, .brk = { 0xe0, 0xe9, 0 } }, /* 169 */ { .mk = {0xe0, 0x6a, 0 }, .brk = { 0xe0, 0xea, 0 } }, /* 16a */ { .mk = {0xe0, 0x6b, 0 }, .brk = { 0xe0, 0xeb, 0 } }, /* 16b */ { .mk = {0xe0, 0x6c, 0 }, .brk = { 0xe0, 0xec, 0 } }, /* 16c */ { .mk = {0xe0, 0x6d, 0 }, .brk = { 0xe0, 0xed, 0 } }, /* 16d */ { .mk = {0xe0, 0x6e, 0 }, .brk = { 0xe0, 0xee, 0 } }, /* 16e */ { .mk = { 0 }, .brk = { 0 } }, /* 16f */ { .mk = {0xe0, 0x70, 0 }, .brk = { 0xe0, 0xf0, 0 } }, /* 170 */ { .mk = {0xe0, 0x71, 0 }, .brk = { 0xe0, 0xf1, 0 } }, /* 171 */ { .mk = {0xe0, 0x72, 0 }, .brk = { 0xe0, 0xf2, 0 } }, /* 172 */ { .mk = {0xe0, 0x73, 0 }, .brk = { 0xe0, 0xf3, 0 } }, /* 173 */ { .mk = {0xe0, 0x74, 0 }, .brk = { 0xe0, 0xf4, 0 } }, /* 174 */ { .mk = {0xe0, 0x75, 0 }, .brk = { 0xe0, 0xf5, 0 } }, /* 175 */ { .mk = { 0 }, .brk = { 0 } }, /* 176 */ { .mk = {0xe0, 0x77, 0 }, .brk = { 0xe0, 0xf7, 0 } }, /* 177 */ { .mk = {0xe0, 0x78, 0 }, .brk = { 0xe0, 0xf8, 0 } }, /* 178 */ { .mk = {0xe0, 0x79, 0 }, .brk = { 0xe0, 0xf9, 0 } }, /* 179 */ { .mk = {0xe0, 0x7a, 0 }, .brk = { 0xe0, 0xfa, 0 } }, /* 17a */ { .mk = {0xe0, 0x7b, 0 }, .brk = { 0xe0, 0xfb, 0 } }, /* 17b */ { .mk = {0xe0, 0x7c, 0 }, .brk = { 0xe0, 0xfc, 0 } }, /* 17c */ { .mk = {0xe0, 0x7d, 0 }, .brk = { 0xe0, 0xfd, 0 } }, /* 17d */ { .mk = {0xe0, 0x7e, 0 }, .brk = { 0xe0, 0xfe, 0 } }, /* 17e */ { .mk = {0xe0, 0x7f, 0 }, .brk = { 0xe0, 0xff, 0 } }, /* 17f */ { .mk = { 0 }, .brk = { 0 } }, /* 180 */ { .mk = { 0 }, .brk = { 0 } }, /* 181 */ { .mk = { 0 }, .brk = { 0 } }, /* 182 */ { .mk = { 0 }, .brk = { 0 } }, /* 183 */ { .mk = { 0 }, .brk = { 0 } }, /* 184 */ { .mk = { 0 }, .brk = { 0 } }, /* 185 */ { .mk = { 0 }, .brk = { 0 } }, /* 186 */ { .mk = { 0 }, .brk = { 0 } }, /* 187 */ { .mk = { 0 }, .brk = { 0 } }, /* 188 */ { .mk = { 0 }, .brk = { 0 } }, /* 189 */ { .mk = { 0 }, .brk = { 0 } }, /* 18a */ { .mk = { 0 }, .brk = { 0 } }, /* 18b */ { .mk = { 0 }, .brk = { 0 } }, /* 18c */ { .mk = { 0 }, .brk = { 0 } }, /* 18d */ { .mk = { 0 }, .brk = { 0 } }, /* 18e */ { .mk = { 0 }, .brk = { 0 } }, /* 18f */ { .mk = { 0 }, .brk = { 0 } }, /* 190 */ { .mk = { 0 }, .brk = { 0 } }, /* 191 */ { .mk = { 0 }, .brk = { 0 } }, /* 192 */ { .mk = { 0 }, .brk = { 0 } }, /* 193 */ { .mk = { 0 }, .brk = { 0 } }, /* 194 */ { .mk = { 0 }, .brk = { 0 } }, /* 195 */ { .mk = { 0 }, .brk = { 0 } }, /* 196 */ { .mk = { 0 }, .brk = { 0 } }, /* 197 */ { .mk = { 0 }, .brk = { 0 } }, /* 198 */ { .mk = { 0 }, .brk = { 0 } }, /* 199 */ { .mk = { 0 }, .brk = { 0 } }, /* 19a */ { .mk = { 0 }, .brk = { 0 } }, /* 19b */ { .mk = { 0 }, .brk = { 0 } }, /* 19c */ { .mk = { 0 }, .brk = { 0 } }, /* 19d */ { .mk = { 0 }, .brk = { 0 } }, /* 19e */ { .mk = { 0 }, .brk = { 0 } }, /* 19f */ { .mk = { 0 }, .brk = { 0 } }, /* 1a0 */ { .mk = { 0 }, .brk = { 0 } }, /* 1a1 */ { .mk = { 0 }, .brk = { 0 } }, /* 1a2 */ { .mk = { 0 }, .brk = { 0 } }, /* 1a3 */ { .mk = { 0 }, .brk = { 0 } }, /* 1a4 */ { .mk = { 0 }, .brk = { 0 } }, /* 1a5 */ { .mk = { 0 }, .brk = { 0 } }, /* 1a6 */ { .mk = { 0 }, .brk = { 0 } }, /* 1a7 */ { .mk = { 0 }, .brk = { 0 } }, /* 1a8 */ { .mk = { 0 }, .brk = { 0 } }, /* 1a9 */ { .mk = { 0 }, .brk = { 0 } }, /* 1aa */ { .mk = { 0 }, .brk = { 0 } }, /* 1ab */ { .mk = { 0 }, .brk = { 0 } }, /* 1ac */ { .mk = { 0 }, .brk = { 0 } }, /* 1ad */ { .mk = { 0 }, .brk = { 0 } }, /* 1ae */ { .mk = { 0 }, .brk = { 0 } }, /* 1af */ { .mk = { 0 }, .brk = { 0 } }, /* 1b0 */ { .mk = { 0 }, .brk = { 0 } }, /* 1b1 */ { .mk = { 0 }, .brk = { 0 } }, /* 1b2 */ { .mk = { 0 }, .brk = { 0 } }, /* 1b3 */ { .mk = { 0 }, .brk = { 0 } }, /* 1b4 */ { .mk = { 0 }, .brk = { 0 } }, /* 1b5 */ { .mk = { 0 }, .brk = { 0 } }, /* 1b6 */ { .mk = { 0 }, .brk = { 0 } }, /* 1b7 */ { .mk = { 0 }, .brk = { 0 } }, /* 1b8 */ { .mk = { 0 }, .brk = { 0 } }, /* 1b9 */ { .mk = { 0 }, .brk = { 0 } }, /* 1ba */ { .mk = { 0 }, .brk = { 0 } }, /* 1bb */ { .mk = { 0 }, .brk = { 0 } }, /* 1bc */ { .mk = { 0 }, .brk = { 0 } }, /* 1bd */ { .mk = { 0 }, .brk = { 0 } }, /* 1be */ { .mk = { 0 }, .brk = { 0 } }, /* 1bf */ { .mk = { 0 }, .brk = { 0 } }, /* 1c0 */ { .mk = { 0 }, .brk = { 0 } }, /* 1c1 */ { .mk = { 0 }, .brk = { 0 } }, /* 1c2 */ { .mk = { 0 }, .brk = { 0 } }, /* 1c3 */ { .mk = { 0 }, .brk = { 0 } }, /* 1c4 */ { .mk = { 0 }, .brk = { 0 } }, /* 1c5 */ { .mk = { 0 }, .brk = { 0 } }, /* 1c6 */ { .mk = { 0 }, .brk = { 0 } }, /* 1c7 */ { .mk = { 0 }, .brk = { 0 } }, /* 1c8 */ { .mk = { 0 }, .brk = { 0 } }, /* 1c9 */ { .mk = { 0 }, .brk = { 0 } }, /* 1ca */ { .mk = { 0 }, .brk = { 0 } }, /* 1cb */ { .mk = { 0 }, .brk = { 0 } }, /* 1cc */ { .mk = { 0 }, .brk = { 0 } }, /* 1cd */ { .mk = { 0 }, .brk = { 0 } }, /* 1ce */ { .mk = { 0 }, .brk = { 0 } }, /* 1cf */ { .mk = { 0 }, .brk = { 0 } }, /* 1d0 */ { .mk = { 0 }, .brk = { 0 } }, /* 1d1 */ { .mk = { 0 }, .brk = { 0 } }, /* 1d2 */ { .mk = { 0 }, .brk = { 0 } }, /* 1d3 */ { .mk = { 0 }, .brk = { 0 } }, /* 1d4 */ { .mk = { 0 }, .brk = { 0 } }, /* 1d5 */ { .mk = { 0 }, .brk = { 0 } }, /* 1d6 */ { .mk = { 0 }, .brk = { 0 } }, /* 1d7 */ { .mk = { 0 }, .brk = { 0 } }, /* 1d8 */ { .mk = { 0 }, .brk = { 0 } }, /* 1d9 */ { .mk = { 0 }, .brk = { 0 } }, /* 1da */ { .mk = { 0 }, .brk = { 0 } }, /* 1db */ { .mk = { 0 }, .brk = { 0 } }, /* 1dc */ { .mk = { 0 }, .brk = { 0 } }, /* 1dd */ { .mk = { 0 }, .brk = { 0 } }, /* 1de */ { .mk = { 0 }, .brk = { 0 } }, /* 1df */ { .mk = { 0 }, .brk = { 0 } }, /* 1e0 */ { .mk = {0xe0, 0xe1, 0 }, .brk = { 0 } }, /* 1e1 */ { .mk = { 0 }, .brk = { 0 } }, /* 1e2 */ { .mk = { 0 }, .brk = { 0 } }, /* 1e3 */ { .mk = { 0 }, .brk = { 0 } }, /* 1e4 */ { .mk = { 0 }, .brk = { 0 } }, /* 1e5 */ { .mk = { 0 }, .brk = { 0 } }, /* 1e6 */ { .mk = { 0 }, .brk = { 0 } }, /* 1e7 */ { .mk = { 0 }, .brk = { 0 } }, /* 1e8 */ { .mk = { 0 }, .brk = { 0 } }, /* 1e9 */ { .mk = { 0 }, .brk = { 0 } }, /* 1ea */ { .mk = { 0 }, .brk = { 0 } }, /* 1eb */ { .mk = { 0 }, .brk = { 0 } }, /* 1ec */ { .mk = { 0 }, .brk = { 0 } }, /* 1ed */ { .mk = {0xe0, 0xee, 0 }, .brk = { 0 } }, /* 1ee */ { .mk = { 0 }, .brk = { 0 } }, /* 1ef */ { .mk = { 0 }, .brk = { 0 } }, /* 1f0 */ { .mk = {0xe0, 0xf1, 0 }, .brk = { 0 } }, /* 1f1 */ { .mk = { 0 }, .brk = { 0 } }, /* 1f2 */ { .mk = { 0 }, .brk = { 0 } }, /* 1f3 */ { .mk = { 0 }, .brk = { 0 } }, /* 1f4 */ { .mk = { 0 }, .brk = { 0 } }, /* 1f5 */ { .mk = { 0 }, .brk = { 0 } }, /* 1f6 */ { .mk = { 0 }, .brk = { 0 } }, /* 1f7 */ { .mk = { 0 }, .brk = { 0 } }, /* 1f8 */ { .mk = { 0 }, .brk = { 0 } }, /* 1f9 */ { .mk = { 0 }, .brk = { 0 } }, /* 1fa */ { .mk = { 0 }, .brk = { 0 } }, /* 1fb */ { .mk = { 0 }, .brk = { 0 } }, /* 1fc */ { .mk = { 0 }, .brk = { 0 } }, /* 1fd */ { .mk = {0xe0, 0xfe, 0 }, .brk = { 0 } }, /* 1fe */ { .mk = {0xe0, 0xff, 0 }, .brk = { 0 } } /* 1ff */ // clang-format on }; static void machine_amstrad_init(const machine_t *model, int type) { amstrad_t *ams; ams = (amstrad_t *) calloc(1, sizeof(amstrad_t)); ams->type = type; amstrad_latch = 0x80000000; switch (type) { case AMS_PC200: case AMS_PPC512: device_add(&amstrad_no_nmi_nvr_device); break; default: device_add(&amstrad_nvr_device); break; } machine_common_init(model); nmi_init(); lpt1_remove_ams(); lpt2_remove(); io_sethandler(0x0378, 3, ams_read, NULL, NULL, ams_write, NULL, NULL, ams); io_sethandler(0xdead, 1, ams_read, NULL, NULL, ams_write, NULL, NULL, ams); switch (type) { case AMS_PC1512: case AMS_PC1640: case AMS_PC200: case AMS_PPC512: ams->fdc = device_add(&fdc_xt_device); break; case AMS_PC2086: case AMS_PC3086: ams->fdc = device_add(&fdc_at_actlow_device); break; default: break; } ams->language = 7; video_reset(gfxcard[0]); if (gfxcard[0] == VID_INTERNAL) switch (type) { case AMS_PC1512: loadfont("roms/machines/pc1512/40078", 8); device_context(&vid_1512_device); ams->language = device_get_config_int("language"); vid_init_1512(ams); device_context_restore(); device_add_ex(&vid_1512_device, ams->vid); break; case AMS_PPC512: loadfont("roms/machines/ppc512/40109", 1); device_context(&vid_ppc512_device); ams->language = device_get_config_int("language"); vid_init_200(ams); device_context_restore(); device_add_ex(&vid_ppc512_device, ams->vid); break; case AMS_PC1640: loadfont(FONT_IBM_MDA_437_PATH, 0); device_context(&vid_1640_device); ams->language = device_get_config_int("language"); vid_init_1640(ams); device_context_restore(); device_add_ex(&vid_1640_device, ams->vid); break; case AMS_PC200: loadfont("roms/machines/pc200/40109", 1); device_context(&vid_200_device); ams->language = device_get_config_int("language"); vid_init_200(ams); device_context_restore(); device_add_ex(&vid_200_device, ams->vid); break; case AMS_PC2086: device_context(&vid_pc2086_device); ams->language = device_get_config_int("language"); device_context_restore(); device_add(¶dise_pvga1a_pc2086_device); break; case AMS_PC3086: device_context(&vid_pc3086_device); ams->language = device_get_config_int("language"); device_context_restore(); device_add(¶dise_pvga1a_pc3086_device); break; default: break; } else if ((type == AMS_PC200) || (type == AMS_PPC512)) io_sethandler(0x03de, 1, ams_read, NULL, NULL, ams_write, NULL, NULL, ams); /* Initialize the (custom) keyboard/mouse interface. */ ams->wantirq = 0; io_sethandler(0x0060, 7, kbd_read, NULL, NULL, kbd_write, NULL, NULL, ams); timer_add(&ams->send_delay_timer, kbd_poll, ams, 1); if (type == AMS_PC1512) keyboard_set_table(scancode_xt); else keyboard_set_table(scancode_pc200); keyboard_send = kbd_adddata_ex; keyboard_scan = 1; keyboard_set_is_amstrad(((type == AMS_PC1512) || (type == AMS_PC1640)) ? 0 : 1); io_sethandler(0x0078, 2, ms_read, NULL, NULL, ms_write, NULL, NULL, ams); io_sethandler(0x007a, 2, ms_read, NULL, NULL, ms_write, NULL, NULL, ams); if (mouse_type == MOUSE_TYPE_INTERNAL) { /* Tell mouse driver about our internal mouse. */ mouse_reset(); mouse_set_buttons(2); mouse_set_poll(ms_poll, ams); } standalone_gameport_type = &gameport_device; } int machine_pc1512_init(const machine_t *model) { int ret; ret = bios_load_interleaved("roms/machines/pc1512/40044", "roms/machines/pc1512/40043", 0x000fc000, 16384, 0); ret &= rom_present("roms/machines/pc1512/40078"); if (bios_only || !ret) return ret; machine_amstrad_init(model, AMS_PC1512); return ret; } int machine_pc1640_init(const machine_t *model) { int ret; ret = bios_load_interleaved("roms/machines/pc1640/40044.v3", "roms/machines/pc1640/40043.v3", 0x000fc000, 16384, 0); ret &= rom_present("roms/machines/pc1640/40100"); if (bios_only || !ret) return ret; machine_amstrad_init(model, AMS_PC1640); return ret; } int machine_pc200_init(const machine_t *model) { int ret; ret = bios_load_interleaved("roms/machines/pc200/pc20v2.1", "roms/machines/pc200/pc20v2.0", 0x000fc000, 16384, 0); ret &= rom_present("roms/machines/pc200/40109"); if (bios_only || !ret) return ret; machine_amstrad_init(model, AMS_PC200); return ret; } int machine_ppc512_init(const machine_t *model) { int ret; ret = bios_load_interleaved("roms/machines/ppc512/40107.v2", "roms/machines/ppc512/40108.v2", 0x000fc000, 16384, 0); ret &= rom_present("roms/machines/ppc512/40109"); if (bios_only || !ret) return ret; machine_amstrad_init(model, AMS_PPC512); return ret; } int machine_pc2086_init(const machine_t *model) { int ret; ret = bios_load_interleavedr("roms/machines/pc2086/40179.ic129", "roms/machines/pc2086/40180.ic132", 0x000fc000, 65536, 0); ret &= rom_present("roms/machines/pc2086/40186.ic171"); if (bios_only || !ret) return ret; machine_amstrad_init(model, AMS_PC2086); return ret; } int machine_pc3086_init(const machine_t *model) { int ret; ret = bios_load_linearr("roms/machines/pc3086/fc00.bin", 0x000fc000, 65536, 0); ret &= rom_present("roms/machines/pc3086/c000.bin"); if (bios_only || !ret) return ret; machine_amstrad_init(model, AMS_PC3086); return ret; }