From fe3c2ea8e0859a7fdd1193f0085b82146b5e97c4 Mon Sep 17 00:00:00 2001 From: Stuart Brady Date: Sun, 27 Sep 2009 12:54:06 +0100 Subject: [PATCH] =?utf8?q?Implement=20preliminary=20SAM=20Coup=C3=A9=20emu?= =?utf8?q?lation?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Currently only supports graphics mode 4, and lacks support for the palette, FDC, and other devices. --- Makefile | 3 +- Makefile.target | 5 +- hw/sam_coupe.c | 310 +++++++++++++++++++++++++++++++++++++++++++ hw/sam_key_template.h | 52 ++++++++ hw/sam_keyboard.c | 255 ++++++++++++++++++++++++++++++++++++ hw/sam_keyboard.h | 8 ++ hw/sam_video.c | 357 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/sam_video.h | 9 ++ 8 files changed, 996 insertions(+), 3 deletions(-) create mode 100644 hw/sam_coupe.c create mode 100644 hw/sam_key_template.h create mode 100644 hw/sam_keyboard.c create mode 100644 hw/sam_keyboard.h create mode 100644 hw/sam_video.c create mode 100644 hw/sam_video.h diff --git a/Makefile b/Makefile index d3e18c7a27..2ed7e5362f 100644 --- a/Makefile +++ b/Makefile @@ -271,7 +271,8 @@ ifdef INSTALL_BLOBS BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \ video.x openbios-sparc32 openbios-sparc64 openbios-ppc \ pxe-ne2k_pci.bin pxe-rtl8139.bin pxe-pcnet.bin pxe-e1000.bin \ -bamboo.dtb petalogix-s3adsp1800.dtb zx-rom.bin +bamboo.dtb petalogix-s3adsp1800.dtb \ +zx-rom.bin zx-rom128.bin sam-rom.bin else BLOBS= endif diff --git a/Makefile.target b/Makefile.target index a7552ea6b8..44f9913844 100644 --- a/Makefile.target +++ b/Makefile.target @@ -696,9 +696,10 @@ OBJS+= an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o OBJS+= m68k-semi.o dummy_m68k.o endif ifeq ($(TARGET_BASE_ARCH), z80) -OBJS+= zx_spectrum.o zx_keyboard.o zx_video.o dma.o -OBJS+= serial.o i8259.o +OBJS+= zx_spectrum.o zx_keyboard.o zx_video.o +OBJS+= sam_coupe.o sam_keyboard.o sam_video.o OBJS+= msx.o msx_mmu.o v9918.o +OBJS+= dma.o i8259.o endif ifdef CONFIG_COCOA COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit diff --git a/hw/sam_coupe.c b/hw/sam_coupe.c new file mode 100644 index 0000000000..5735b09b69 --- /dev/null +++ b/hw/sam_coupe.c @@ -0,0 +1,310 @@ +/* + * QEMU SAM Coupé Spectrum Emulator + * + * Copyright (c) 2007-2009 Stuart Brady + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw.h" +#include "qemu-timer.h" +#include "console.h" +#include "isa.h" +#include "sysemu.h" +#include "sam_video.h" +#include "sam_keyboard.h" +#include "boards.h" + +#ifdef CONFIG_LIBSPECTRUM +#include +#endif + +#define ROM_FILENAME "sam-rom.bin" + +#ifdef DEBUG_SAM_COUPE +#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) +#endif + +static CPUState *sam_env; + +static int page_tab[4]; + +static int lmpr = 0x00; +static int hmpr = 0x02; +static int vmpr = 0x00; + +static target_ulong sam_mapaddr(target_ulong addr) { + return (page_tab[addr >> 14] << 14) | (addr & ~(~0 << 14)); +} + +static void map_memory(void) +{ + if (!(lmpr & 0x20)) { + page_tab[0] = 0x20; /* ROM 0 */ + } else { + page_tab[0] = lmpr & 0x1f; + } + page_tab[1] = (lmpr + 1) & 0x1f; + + page_tab[2] = hmpr & 0x1f; + if (lmpr & 0x40) { + page_tab[3] = 0x21; /* ROM 1 */ + } else { + page_tab[3] = (hmpr + 1) & 0x1f; + } + + /* TODO: if lmpr bit 7 is set, page 0 is write-protected */ + + cpu_interrupt(first_cpu, CPU_INTERRUPT_EXITTB); + tlb_flush(first_cpu, 1); +} + +static uint32_t io_pen_read(void *opaque, uint32_t addr) +{ + DPRINTF("read from %s\n", __func__); + return 0xff; /* not implemented */ +} + +static void io_clut_write(void *opaque, uint32_t addr, uint32_t data) +{ + DPRINTF("write %02x to %s\n", data, __func__); + /* not implemented */ +} + +static uint32_t io_status_read(void *opaque, uint32_t addr) +{ + DPRINTF("read from %s\n", __func__); + return 0xf7 & (sam_keyboard_read(opaque, addr) | ~0xe0); +} + +static void io_lineirq_write(void *opaque, uint32_t addr, uint32_t data) +{ + DPRINTF("write %02x to %s\n", data, __func__); + /* not implemented */ +} + +static uint32_t io_lmpr_read(void *opaque, uint32_t addr) +{ + DPRINTF("read from %s\n", __func__); + return lmpr; +} + +static void io_lmpr_write(void *opaque, uint32_t addr, uint32_t data) +{ + DPRINTF("write %02x to %s\n", data, __func__); + lmpr = data; + map_memory(); +} + +static uint32_t io_hmpr_read(void *opaque, uint32_t addr) +{ + DPRINTF("read from %s\n", __func__); + return hmpr; +} + +static void io_hmpr_write(void *opaque, uint32_t addr, uint32_t data) +{ + DPRINTF("write %02x to %s\n", data, __func__); + hmpr = data; + map_memory(); +} + +static uint32_t io_vmpr_read(void *opaque, uint32_t addr) +{ + DPRINTF("read from %s\n", __func__); + return vmpr; +} + +static void io_vmpr_write(void *opaque, uint32_t addr, uint32_t data) +{ + DPRINTF("write %02x to %s\n", data, __func__); + vmpr = data; + /* not implemented */ +} + +static uint32_t io_midi_read(void *opaque, uint32_t addr) +{ + DPRINTF("read from %s\n", __func__); + return 0xff; /* not implemented */ +} + +static void io_midi_write(void *opaque, uint32_t addr, uint32_t data) +{ + DPRINTF("write %02x to %s\n", data, __func__); + /* not implemented */ +} + +static uint32_t io_attr_read(void *opaque, uint32_t addr) +{ + DPRINTF("read from %s\n", __func__); + return 0xff; /* not implemented */ +} + +static void io_sound_write(void *opaque, uint32_t addr, uint32_t data) +{ + DPRINTF("write %02x to %s\n", data, __func__); + /* not implemented */ +} + +static uint32_t io_ula_read(void *opaque, uint32_t addr) +{ + return sam_keyboard_read(opaque, addr) | ~0x1f; +} + +static void io_ula_write(void *opaque, uint32_t addr, uint32_t data) +{ + sam_video_set_border(data & 0x7); +} + +static void main_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + cpu_reset(env); + + page_tab[0] = 0x20 + 0; /* ROM 0 */ + page_tab[1] = 0x01; /* RAM 1 */ + + page_tab[2] = 0x02; /* RAM 2 */ + page_tab[3] = 0x03; /* RAM 3 */ +} + +static QEMUTimer *zx_ula_timer; + +static void zx_50hz_timer(void *opaque) +{ + int64_t next_time; + + CPUState *env = opaque; + cpu_interrupt(env, CPU_INTERRUPT_HARD); + + /* FIXME: not exactly 50 Hz */ + next_time = qemu_get_clock(vm_clock) + muldiv64(1, ticks_per_sec, 50); + qemu_mod_timer(zx_ula_timer, next_time); + + sam_video_do_retrace(); +} + +static void zx_timer_init(void) +{ + int64_t t = qemu_get_clock(vm_clock); + zx_ula_timer = qemu_new_timer(vm_clock, zx_50hz_timer, sam_env); + qemu_mod_timer(zx_ula_timer, t); +} + +/* ZX Spectrum initialisation */ +static void sam_coupe_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + char *filename; + int ret; + ram_addr_t ram_offset, rom_offset; + int rom_size; + int rom_base; + CPUState *env; + int i; + + /* init CPUs */ + if (!cpu_model) { + cpu_model = "z80"; + } + env = cpu_init(cpu_model); + sam_env = env; // XXX + register_savevm("cpu", 0, 4, cpu_save, cpu_load, env); + qemu_register_reset(main_cpu_reset, 0, env); + main_cpu_reset(env); + + env->mapaddr = sam_mapaddr; + + ram_size = 0x80000; // 512 K + + /* allocate RAM */ + ram_offset = qemu_ram_alloc(ram_size); + cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM); + + /* ROM load */ + if (bios_name == NULL) { + bios_name = ROM_FILENAME; + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (filename) { + rom_size = get_image_size(filename); + } else { + rom_size = -1; + } + if (rom_size <= 0 || + (rom_size % 0x4000) != 0) { + goto rom_error; + } + rom_base = ram_size; + rom_offset = qemu_ram_alloc(rom_size); + cpu_register_physical_memory(rom_base, rom_size, rom_offset | IO_MEM_ROM); + ret = load_image_targphys(filename, rom_base, rom_size); + if (ret != rom_size) { + rom_error: + fprintf(stderr, "qemu: could not load SAM Coupe ROM '%s'\n", + bios_name); + exit(1); + } + if (filename) { + qemu_free(filename); + } + + /* map entire I/O space */ + for (i = 0; i < 0x10000; i += 0x100) { + register_ioport_read(i + 0xf8, 1, 1, io_pen_read, NULL); + register_ioport_read(i + 0xf9, 1, 1, io_status_read, NULL); + register_ioport_read(i + 0xfa, 1, 1, io_lmpr_read, NULL); + register_ioport_read(i + 0xfb, 1, 1, io_hmpr_read, NULL); + register_ioport_read(i + 0xfc, 1, 1, io_vmpr_read, NULL); + register_ioport_read(i + 0xfd, 1, 1, io_midi_read, NULL); + register_ioport_read(i + 0xfe, 1, 1, io_ula_read, NULL); + register_ioport_read(i + 0xff, 1, 1, io_attr_read, NULL); + register_ioport_write(i + 0xf8, 1, 1, io_clut_write, NULL); + register_ioport_write(i + 0xf9, 1, 1, io_lineirq_write, NULL); + register_ioport_write(i + 0xfa, 1, 1, io_lmpr_write, NULL); + register_ioport_write(i + 0xfb, 1, 1, io_hmpr_write, NULL); + register_ioport_write(i + 0xfc, 1, 1, io_vmpr_write, NULL); + register_ioport_write(i + 0xfd, 1, 1, io_midi_write, NULL); + register_ioport_write(i + 0xfe, 1, 1, io_ula_write, NULL); + register_ioport_write(i + 0xff, 1, 1, io_sound_write, NULL); + } + + sam_video_init(ram_offset); + + sam_keyboard_init(); + zx_timer_init(); +} + +static QEMUMachine sam_machine = { + .name = "sam", + .desc = "SAM Coupe", + .init = sam_coupe_init, + .is_default = 0, +}; + +static void sam_machine_init(void) { + qemu_register_machine(&sam_machine); +} + +machine_init(sam_machine_init); diff --git a/hw/sam_key_template.h b/hw/sam_key_template.h new file mode 100644 index 0000000000..76765cf492 --- /dev/null +++ b/hw/sam_key_template.h @@ -0,0 +1,52 @@ +/* + * SAM Coupé Keyboard Layout + */ + +/* An extension of the ZX Spectrum keyboard layout */ + +#define DEF_ZX_KEY(name, row, column) DEF_SAM_KEY(name, row, column) +#include "zx_key_template.h" + +/* SAM Coupé-specific keys */ + +DEF_SAM_KEY(F1, 0, 5) +DEF_SAM_KEY(F2, 0, 6) +DEF_SAM_KEY(F3, 0, 7) + +DEF_SAM_KEY(F4, 1, 5) +DEF_SAM_KEY(F5, 1, 6) +DEF_SAM_KEY(F6, 1, 7) + +DEF_SAM_KEY(F7, 2, 5) +DEF_SAM_KEY(F8, 2, 6) +DEF_SAM_KEY(F9, 2, 7) + +DEF_SAM_KEY(ESCAPE, 3, 5) +DEF_SAM_KEY(TAB, 3, 6) +DEF_SAM_KEY(CAPSLOCK, 3, 7) + +DEF_SAM_KEY(MINUS, 4, 5) +DEF_SAM_KEY(PLUS, 4, 6) +DEF_SAM_KEY(DELETE, 4, 7) + +DEF_SAM_KEY(EQUALS, 5, 5) +DEF_SAM_KEY(DQUOTE, 5, 6) +DEF_SAM_KEY(F0, 5, 7) + +DEF_SAM_KEY(SEMICOLON, 6, 5) +DEF_SAM_KEY(COLON, 6, 6) +DEF_SAM_KEY(EDIT, 6, 7) + +DEF_SAM_KEY(COMMA, 7, 5) +DEF_SAM_KEY(PERIOD, 7, 6) +DEF_SAM_KEY(INVERSE, 7, 7) + +/* high bits = 0xff */ + +DEF_SAM_KEY(CONTROL, 8, 0) +DEF_SAM_KEY(UP, 8, 1) +DEF_SAM_KEY(DOWN, 8, 2) +DEF_SAM_KEY(LEFT, 8, 3) +DEF_SAM_KEY(RIGHT, 8, 4) + +#undef DEF_SAM_KEY diff --git a/hw/sam_keyboard.c b/hw/sam_keyboard.c new file mode 100644 index 0000000000..74bcc676ef --- /dev/null +++ b/hw/sam_keyboard.c @@ -0,0 +1,255 @@ +/* + * SAM Coupé Keyboard Emulation + * + * Copyright (c) 2007-2009 Stuart Brady + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw.h" +#include "qemu-timer.h" +#include "console.h" +#include "isa.h" +#include "sysemu.h" +#include "sam_keyboard.h" +#include "boards.h" + +//#define DEBUG_SAM_KEYBOARD + +#ifdef DEBUG_SAM_KEYBOARD +#define DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) +#endif + +static int keystate[9]; + +uint32_t sam_keyboard_read(void *opaque, uint32_t addr) +{ + int r = 0; + uint8_t colbits = 0xff; + uint32_t rowbits = ((addr >> 8) & 0xff); + + if (rowbits == 0xff) { + colbits &= keystate[8]; + } else { + for (r = 0; r < 8; r++) { + if (!(rowbits & (1 << r))) { + colbits &= keystate[r]; + } + } + } + return colbits; +} + +typedef struct { + int row; + int column; +} SAMKeypos; + +#define DEF_SAM_KEY(name, row, column) SAM_KEY_ ## name, +enum sam_keys { +#include "sam_key_template.h" +SAM_MAX_KEYS +}; + +#define DEF_SAM_KEY(name, row, column) [SAM_KEY_ ## name] = {row, column}, +static const SAMKeypos keypos[SAM_MAX_KEYS] = { +#include "sam_key_template.h" +}; + +static int sam_keypressed[SAM_MAX_KEYS]; +static int qemu_keypressed[0x100]; + +static const int map[0x100][2] = { + [0 ... 0xff] = {-1, -1}, /* Unmapped by default */ + + [0x01] = {SAM_KEY_ESCAPE, -1}, + + [0x02] = {SAM_KEY_1, -1}, + [0x03] = {SAM_KEY_2, -1}, + [0x04] = {SAM_KEY_3, -1}, + [0x05] = {SAM_KEY_4, -1}, + [0x06] = {SAM_KEY_5, -1}, + [0x07] = {SAM_KEY_6, -1}, + [0x08] = {SAM_KEY_7, -1}, + [0x09] = {SAM_KEY_8, -1}, + [0x0a] = {SAM_KEY_9, -1}, + [0x0b] = {SAM_KEY_0, -1}, + + [0x0c] = {SAM_KEY_MINUS, -1}, + + [0x0e] = {SAM_KEY_DELETE, -1}, /* Backspace */ + + [0x10] = {SAM_KEY_Q, -1}, + [0x11] = {SAM_KEY_W, -1}, + [0x12] = {SAM_KEY_E, -1}, + [0x13] = {SAM_KEY_R, -1}, + [0x14] = {SAM_KEY_T, -1}, + [0x15] = {SAM_KEY_Y, -1}, + [0x16] = {SAM_KEY_U, -1}, + [0x17] = {SAM_KEY_I, -1}, + [0x18] = {SAM_KEY_O, -1}, + [0x19] = {SAM_KEY_P, -1}, + + [0x0d] = {SAM_KEY_PLUS, -1}, + [0x0f] = {SAM_KEY_TAB, -1}, + + [0x1a] = {SAM_KEY_EQUALS, -1}, + [0x1b] = {SAM_KEY_DQUOTE, -1}, + + [0x1c] = {SAM_KEY_ENTER, -1}, + + [0x1d] = {SAM_KEY_CONTROL, -1}, /* Left Control */ + + [0x1e] = {SAM_KEY_A, -1}, + [0x1f] = {SAM_KEY_S, -1}, + [0x20] = {SAM_KEY_D, -1}, + [0x21] = {SAM_KEY_F, -1}, + [0x22] = {SAM_KEY_G, -1}, + [0x23] = {SAM_KEY_H, -1}, + [0x24] = {SAM_KEY_J, -1}, + [0x25] = {SAM_KEY_K, -1}, + [0x26] = {SAM_KEY_L, -1}, + + [0x27] = {SAM_KEY_SEMICOLON, -1}, /* Semicolon */ + [0x28] = {SAM_KEY_SYMBSHIFT, SAM_KEY_7}, /* Apostrophe */ + + [0x2a] = {SAM_KEY_CAPSSHIFT, -1}, /* Left Shift */ + + [0x2b] = {SAM_KEY_SYMBSHIFT, SAM_KEY_3}, /* Hash */ + + [0x2c] = {SAM_KEY_Z, -1}, + [0x2d] = {SAM_KEY_X, -1}, + [0x2e] = {SAM_KEY_C, -1}, + [0x2f] = {SAM_KEY_V, -1}, + [0x30] = {SAM_KEY_B, -1}, + [0x31] = {SAM_KEY_N, -1}, + [0x32] = {SAM_KEY_M, -1}, + + [0x33] = {SAM_KEY_COMMA, -1}, + [0x34] = {SAM_KEY_PERIOD, -1}, + [0x35] = {SAM_KEY_SYMBSHIFT, -1}, /* Slash */ + + [0x36] = {SAM_KEY_CAPSSHIFT, -1}, /* Right Shift */ + [0x37] = {SAM_KEY_SYMBSHIFT, SAM_KEY_B}, /* * (Numpad) */ + [0x38] = {SAM_KEY_SYMBSHIFT, -1}, /* Left Alt */ + [0x39] = {SAM_KEY_SPACE, -1}, /* Space Bar */ + + [0x47] = {SAM_KEY_F7, -1}, /* 7 (Numpad) */ + [0x48] = {SAM_KEY_F8, -1}, /* 8 (Numpad) */ + [0x49] = {SAM_KEY_F9, -1}, /* 9 (Numpad) */ + [0x4a] = {SAM_KEY_MINUS, -1}, /* Minus (Numpad) */ + [0x4b] = {SAM_KEY_F4, -1}, /* 4 (Numpad) */ + [0x4c] = {SAM_KEY_F5, -1}, /* 5 (Numpad) */ + [0x4d] = {SAM_KEY_F6, -1}, /* 6 (Numpad) */ + [0x4e] = {SAM_KEY_PLUS, -1}, /* Plus (Numpad) */ + [0x4f] = {SAM_KEY_F1, -1}, /* 1 (Numpad) */ + [0x50] = {SAM_KEY_F2, -1}, /* 2 (Numpad) */ + [0x51] = {SAM_KEY_F3, -1}, /* 3 (Numpad) */ + [0x52] = {SAM_KEY_F0, -1}, /* 0 (Numpad) */ + [0x53] = {SAM_KEY_PERIOD, -1}, /* Period (Numpad) */ + + [0x9c] = {SAM_KEY_ENTER, -1}, /* Enter (Numpad) */ + [0x9d] = {SAM_KEY_CONTROL, -1}, /* Right Control */ + [0xb5] = {SAM_KEY_SYMBSHIFT, SAM_KEY_V}, /* Slash (Numpad) */ + [0xb8] = {SAM_KEY_EDIT, -1}, /* Right Alt */ + + [0xc8] = {SAM_KEY_UP, SAM_KEY_7}, /* Up Arrow */ + [0xcb] = {SAM_KEY_LEFT, SAM_KEY_5}, /* Left Arrow */ + [0xcd] = {SAM_KEY_RIGHT, SAM_KEY_8}, /* Right Arrow */ + [0xd0] = {SAM_KEY_DOWN, SAM_KEY_6}, /* Down Arrow */ + + [0xdb] = {SAM_KEY_CONTROL, SAM_KEY_SYMBSHIFT}, /* Left Meta */ + [0xdc] = {SAM_KEY_EDIT, SAM_KEY_SYMBSHIFT}, /* Menu */ + [0xdd] = {SAM_KEY_CONTROL, SAM_KEY_SYMBSHIFT}, /* Right Meta */ +}; + +/* FIXME: + * Need to mappings from stepping on each other... + * or at least make them step on one another in a consistent manner? + * Could use separate state arrays for surpressing/adding keys + * and allow only one change to the modifier keys at a time... + * + * Also need to implement shifted mappings. + */ + +static void sam_put_keycode(void *opaque, int keycode) +{ + int release = keycode & 0x80; + int key, row, col; + static int ext_keycode = 0; + int i; + int valid; + + if (keycode == 0xe0) { + ext_keycode = 1; + } else { + if (ext_keycode) { + keycode |= 0x80; + } else { + keycode &= 0x7f; + } + ext_keycode = 0; + + DPRINTF("Keycode 0x%02x (%s)\n", keycode, + release ? "release" : "press"); + + if (release && qemu_keypressed[keycode]) { + valid = 1; + qemu_keypressed[keycode] = 0; + } else if (!release && !qemu_keypressed[keycode]) { + valid = 1; + qemu_keypressed[keycode] = 1; + } else { + valid = 0; + } + + if (valid) { + for (i = 0; i < 2; i++) { + key = map[keycode][i]; + if (key != -1) { + row = keypos[key].row; + col = keypos[key].column; + if (release) { + if (--sam_keypressed[key] <= 0) { + DPRINTF("Releasing 0x%02x\n", key); + sam_keypressed[key] = 0; + keystate[row] |= 1 << col; + } + } else { + DPRINTF("Pressing 0x%02x\n", key); + sam_keypressed[key]++; + keystate[row] &= ~(1 << col); + } + } + } + } + } +} + +void sam_keyboard_init(void) +{ + int i; + for (i=0; i<9; i++) { + keystate[i] = 0xff; + } + memset(sam_keypressed, 0, sizeof(sam_keypressed)); + memset(qemu_keypressed, 0, sizeof(qemu_keypressed)); + qemu_add_kbd_event_handler(sam_put_keycode, NULL); +} diff --git a/hw/sam_keyboard.h b/hw/sam_keyboard.h new file mode 100644 index 0000000000..3a1b96bbd8 --- /dev/null +++ b/hw/sam_keyboard.h @@ -0,0 +1,8 @@ +#ifndef HW_SAM_KEYBOARD_H +#define HW_SAM_KEYBOARD_H +/* SAM Coupé Keyboard */ + +void sam_keyboard_init(void); +uint32_t sam_keyboard_read(void *opaque, uint32_t addr); + +#endif diff --git a/hw/sam_video.c b/hw/sam_video.c new file mode 100644 index 0000000000..1339e7a1b4 --- /dev/null +++ b/hw/sam_video.c @@ -0,0 +1,357 @@ +/* + * SAM Coupé Video Emulation + * + * Copyright (c) 2007-2009 Stuart Brady + * + * Uses code from VGA emulation + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw.h" +#include "isa.h" +#include "console.h" +#include "sam_video.h" +#include "pixel_ops.h" +#include "pixel_ops_dup.h" + +typedef unsigned int rgb_to_pixel_dup_func(unsigned int r, + unsigned int g, + unsigned int b); + +typedef struct { + DisplayState *ds; + uint8_t *vram_ptr; + + int bwidth; + int bheight; + int swidth; + int sheight; + int twidth; + int theight; + + int border; + int prevborder; + + int flash; + int flashcount; + + int invalidate; + uint32_t palette[16]; + rgb_to_pixel_dup_func *rgb_to_pixel; +} ZXVState; + +static const uint32_t sam_cols[16] = { + 0x00000000, /* 0: Black */ + 0x000000c0, /* 1: Blue */ + 0x00c00000, /* 2: Red */ + 0x00c000c0, /* 3: Magenta */ + 0x0000c000, /* 4: Green */ + 0x0000c0c0, /* 5: Cyan */ + 0x00c0c000, /* 6: Yellow */ + 0x00c0c0c0, /* 7: Light grey */ + + 0x00000000, /* 8: Black */ + 0x000000ff, /* 9: Bright blue */ + 0x00ff0000, /* 10: Bright red */ + 0x00ff00ff, /* 11: Bright magenta */ + 0x0000ff00, /* 12: Bright green */ + 0x0000ffff, /* 13: Bright cyan */ + 0x00ffff00, /* 14: Bright yellow */ + 0x00ffffff, /* 15: White */ +}; + +/* copied from vga.c / vga_template.h */ + +#define cbswap_32(__x) \ +((uint32_t)( \ + (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) + +#ifdef WORDS_BIGENDIAN +#define PAT(x) (x) +#else +#define PAT(x) cbswap_32(x) +#endif + +static const uint32_t dmask16[16] = { + PAT(0x00000000), + PAT(0x000000ff), + PAT(0x0000ff00), + PAT(0x0000ffff), + PAT(0x00ff0000), + PAT(0x00ff00ff), + PAT(0x00ffff00), + PAT(0x00ffffff), + PAT(0xff000000), + PAT(0xff0000ff), + PAT(0xff00ff00), + PAT(0xff00ffff), + PAT(0xffff0000), + PAT(0xffff00ff), + PAT(0xffffff00), + PAT(0xffffffff), +}; + +static const uint32_t dmask4[4] = { + PAT(0x00000000), + PAT(0x0000ffff), + PAT(0xffff0000), + PAT(0xffffffff), +}; + +typedef void sam_draw_line_func(uint8_t *d, uint32_t font_data, + uint32_t xorcol, uint32_t bgcol); + +#define DEPTH 8 +#include "zx_glyphs.h" + +#define DEPTH 16 +#include "zx_glyphs.h" + +#define DEPTH 32 +#include "zx_glyphs.h" + +enum { + sam_pixfmt_8 = 0, + sam_pixfmt_15rgb, + sam_pixfmt_16rgb, + sam_pixfmt_32rgb, + sam_pixfmt_32bgr, + NB_DEPTHS +}; + +static sam_draw_line_func *sam_draw_line_table[NB_DEPTHS] = { + zx_draw_glyph_line_8, + zx_draw_glyph_line_16, + zx_draw_glyph_line_16, + zx_draw_glyph_line_32, + zx_draw_glyph_line_32, +}; + +static rgb_to_pixel_dup_func *rgb_to_pixel_dup_table[NB_DEPTHS] = { + rgb_to_pixel8_dup, + rgb_to_pixel15_dup, + rgb_to_pixel16_dup, + rgb_to_pixel32_dup, + rgb_to_pixel32bgr_dup, +}; + +static inline int get_pixfmt_index(DisplayState *s) +{ + switch(ds_get_bits_per_pixel(s)) { + default: + case 8: + return sam_pixfmt_8; + case 15: + return sam_pixfmt_15rgb; + case 16: + return sam_pixfmt_16rgb; + case 32: + if (is_surface_bgr(s->surface)) { + return sam_pixfmt_32bgr; + } else { + return sam_pixfmt_32rgb; + } + } +} + +/* end of code copied from vga.c / vga_template.h */ + +static ZXVState *samvstate; + +void sam_video_set_border(int col) +{ + ZXVState *s = samvstate; + + s->border = col; +}; + +void sam_video_do_retrace(void) +{ + ZXVState *s = samvstate; + + if (++s->flashcount == 16) { + s->flashcount = 0; + s->invalidate = 1; + s->flash = !s->flash; + } +} + +static void sam_draw_scanline(ZXVState *s1, uint8_t *d, + const uint8_t *s) +{ + int x, x_incr; + uint32_t col; + + x_incr = (ds_get_bits_per_pixel(s1->ds) + 7) >> 3; + + for (x = 0; x < 128; x++) { + col = s1->palette[*s >> 4]; + *(uint32_t *)d = col; + d += x_incr; + col = s1->palette[*s & 0x0f]; + *(uint32_t *)d = col; + d += x_incr; + s++; + } +} + +static void sam_border_row(ZXVState *s, uint8_t *d) +{ + int x, x_incr; + sam_draw_line_func *sam_draw_line; + + sam_draw_line = sam_draw_line_table[get_pixfmt_index(s->ds)]; + x_incr = (ds_get_bits_per_pixel(s->ds) + 7) >> 3; + + for (x = 0; x < s->twidth / 8; x++) { + sam_draw_line(d, 0xff, s->palette[s->border], 0); + d += 8 * x_incr; + } +} + +static void sam_border_sides(ZXVState *s, uint8_t *d) +{ + int x, x_incr; + sam_draw_line_func *sam_draw_line; + + sam_draw_line = sam_draw_line_table[get_pixfmt_index(s->ds)]; + x_incr = (ds_get_bits_per_pixel(s->ds) + 7) >> 3; + + for (x = 0; x < s->bwidth / 8; x++) { + sam_draw_line(d, 0xff, s->palette[s->border], 0); + d += 8 * x_incr; + } + d += s->swidth * x_incr; + for (x = 0; x < s->bwidth / 8; x++) { + sam_draw_line(d, 0xff, s->palette[s->border], 0); + d += 8 * x_incr; + } +} + +static void update_palette(ZXVState *s) +{ + int i, r, g, b; + for(i = 0; i < 16; i++) { + r = (sam_cols[i] >> 16) & 0xff; + g = (sam_cols[i] >> 8) & 0xff; + b = sam_cols[i] & 0xff; + s->palette[i] = s->rgb_to_pixel(r, g, b); + } +} + +static void sam_update_display(void *opaque) +{ + int y; + uint8_t *d; + ZXVState *s = (ZXVState *)opaque; + uint32_t addr; + int x_incr; + int dirty = s->invalidate; + static int inited = 0; + + x_incr = (ds_get_bits_per_pixel(s->ds) + 7) >> 3; + + if (unlikely(inited == 0)) { + s->rgb_to_pixel = rgb_to_pixel_dup_table[get_pixfmt_index(s->ds)]; + update_palette(s); + inited = 1; + } + + if (unlikely(ds_get_width(s->ds) != s->twidth || + ds_get_height(s->ds) != s->theight)) { + qemu_console_resize(s->ds, s->twidth, s->theight); + s->invalidate = 1; + s->prevborder = -1; + } + +// if (!dirty) { +// for (addr = 0; addr < 0x1b00; addr += TARGET_PAGE_SIZE) { +// if (cpu_physical_memory_get_dirty(addr, VGA_DIRTY_FLAG)) { +// dirty = 1; +// } +// } +// } + + dirty = 1; + + if (dirty) { + d = ds_get_data(s->ds); + d += s->bheight * ds_get_linesize(s->ds); + d += s->bwidth * x_incr; + + for (y = 0; y < 192; y++) { + addr = y << 7; + sam_draw_scanline(s, d, s->vram_ptr + addr); + d += ds_get_linesize(s->ds); + } + + s->invalidate = 0; +// cpu_physical_memory_reset_dirty(0, 0x1b00, VGA_DIRTY_FLAG); + } + + if (s->border != s->prevborder) { + d = ds_get_data(s->ds); + for (y = 0; y < s->bheight; y++) { + sam_border_row(s, d + (y * ds_get_linesize(s->ds))); + } + for (y = s->bheight; y < s->theight - s->bheight; y++) { + sam_border_sides(s, d + (y * ds_get_linesize(s->ds))); + } + for (y = s->theight - s->bheight; y < s->theight; y++) { + sam_border_row(s, d + (y * ds_get_linesize(s->ds))); + } + s->prevborder = s->border; + } + + dpy_update(s->ds, 0, 0, s->twidth, s->theight); +} + +static void sam_invalidate_display(void *opaque) +{ + ZXVState *s = (ZXVState *)opaque; + s->invalidate = 1; + s->prevborder = -1; +} + +void sam_video_init(ram_addr_t sam_vram_offset) +{ + ZXVState *s = qemu_mallocz(sizeof(ZXVState)); + samvstate = s; + s->invalidate = 1; + s->prevborder = -1; + s->flashcount = 0; + s->vram_ptr = qemu_get_ram_ptr(sam_vram_offset) + (30 << 14); + + s->ds = graphic_console_init(sam_update_display, sam_invalidate_display, + NULL, NULL, s); + + s->bwidth = 32; + s->bheight = 24; + s->swidth = 256; + s->sheight = 192; + s->twidth = s->swidth + s->bwidth * 2; + s->theight = s->sheight + s->bheight * 2; + s->border = 0; + s->flash = 0; +} diff --git a/hw/sam_video.h b/hw/sam_video.h new file mode 100644 index 0000000000..921010ba57 --- /dev/null +++ b/hw/sam_video.h @@ -0,0 +1,9 @@ +#ifndef HW_SAM_VIDEO_H +#define HW_SAM_VIDEO_H +/* SAM Coupé Video */ + +void sam_video_init(ram_addr_t sam_vram_offset); +void sam_video_do_retrace(void); +void sam_video_set_border(int col); + +#endif -- 2.11.4.GIT