Mercurial > repos > blastem
changeset 2521:8cf7cadc17ee
Initial SC-3000 support
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 11 Oct 2024 00:46:53 -0700 |
parents | 0e9d7ef03983 |
children | 1de9eb7cbf38 |
files | Makefile blastem.c config.c default.cfg i8255.c i8255.h rom.db sms.c sms.h system.c systems.cfg |
diffstat | 11 files changed, 581 insertions(+), 9 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Mon Oct 07 21:38:54 2024 -0700 +++ b/Makefile Fri Oct 11 00:46:53 2024 -0700 @@ -264,8 +264,8 @@ ifdef NOZ80 CFLAGS+=-DNO_Z80 else -MAINOBJS+= sms.o $(Z80OBJS) -LIBOBJS+= sms.o $(Z80OBJS) +MAINOBJS+= sms.o i8255.o $(Z80OBJS) +LIBOBJS+= sms.o i8255.o $(Z80OBJS) endif ifeq ($(OS),Windows)
--- a/blastem.c Mon Oct 07 21:38:54 2024 -0700 +++ b/blastem.c Fri Oct 11 00:46:53 2024 -0700 @@ -130,7 +130,7 @@ uint32_t load_media_zip(const char *filename, system_media *dst) { - static const char *valid_exts[] = {"bin", "md", "gen", "sms", "gg", "rom", "smd", "sg"}; + static const char *valid_exts[] = {"bin", "md", "gen", "sms", "gg", "rom", "smd", "sg", "sc", "sf7"}; const uint32_t num_exts = sizeof(valid_exts)/sizeof(*valid_exts); zip_file *z = zip_open(filename); if (!z) {
--- a/config.c Mon Oct 07 21:38:54 2024 -0700 +++ b/config.c Fri Oct 11 00:46:53 2024 -0700 @@ -316,7 +316,7 @@ *pads = tern_insert_node(*pads, key, val.ptrval); } -#define CONFIG_VERSION 9 +#define CONFIG_VERSION 10 static tern_node *migrate_config(tern_node *config, int from_version) { tern_node *def_config = parse_bundled_config("default.cfg"); @@ -479,6 +479,43 @@ } free(exts[0]);//All extensions in this list share an allocation, first one is a pointer to the buffer free(exts); + break; + } + case 9: { + //Add pre-SMS 8-bit image formats to ui.extensions + uint32_t num_exts; + char **ext_list = get_extension_list(config, &num_exts); + char *old = num_exts ? ext_list[0] : NULL; + uint32_t new_size = num_exts + 3; + uint8_t need_sc = 1, need_sg = 1, need_sf7 = 1; + for (uint32_t i = 0; i < num_exts; i++) + { + if (!strcmp(ext_list[i], "sc")) { + need_sc = 0; + new_size--; + } else if (!strcmp(ext_list[i], "sg")) { + need_sg = 0; + new_size--; + } else if (!strcmp(ext_list[i], "sf7")) { + need_sf7 = 0; + new_size--; + } + } + if (new_size != num_exts) { + ext_list = realloc(ext_list, sizeof(char*) * new_size); + if (need_sc) { + ext_list[num_exts++] = "sc"; + } + if (need_sg) { + ext_list[num_exts++] = "sg"; + } + if (need_sf7) { + ext_list[num_exts++] = "sf7"; + } + } + char *combined = alloc_join(new_size, (char const **)ext_list, ' '); + config = tern_insert_path(config, "ui\0extensions\0", (tern_val){.ptrval = combined}, TVAL_PTR); + break; } } char buffer[16];
--- a/default.cfg Mon Oct 07 21:38:54 2024 -0700 +++ b/default.cfg Fri Oct 11 00:46:53 2024 -0700 @@ -401,7 +401,7 @@ #accepts special variables $HOME, $EXEDIR, $USERDATA, $ROMNAME save_path $USERDATA/blastem/$ROMNAME #space delimited list of file extensions to filter against in menu - extensions bin gen md smd sms gg zip gz cue iso vgm vgz flac wav col + extensions bin gen md smd sms gg sg sc sf7 zip gz cue iso vgm vgz flac wav col #specifies the preferred save-state format, set to gst for Genecyst compatible states state_format native #set to on to use the native file picker on your OS instead of the builtin one @@ -437,4 +437,4 @@ } #Don't manually edit `version`, it's used for automatic config migration -version 9 +version 10
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/i8255.c Fri Oct 11 00:46:53 2024 -0700 @@ -0,0 +1,290 @@ +#include "i8255.h" + +#include <string.h> + +#define BIT_OBFA 0x80 +#define BIT_ACKA 0x40 +#define BIT_IBFA 0x20 +#define BIT_STBA 0x10 +#define BIT_INTRA 0x08 +#define BIT_STB_ACKB 0x04 +#define BIT_IBF_OBFB 0x02 +#define BIT_INTRB 0x01 + +#define BIT_INTE1 BIT_ACKA +#define BIT_INTE2 BIT_STBA +#define BIT_INTEB BIT_STB_ACKB + +void i8255_init(i8255 *ppi, i8255_out_update out, i8255_in_sample in) +{ + memset(ppi->latches, 0, sizeof(ppi->latches)); + ppi->control = 0x1B; //all ports start as input + ppi->portc_write_mask = 0xFF; + ppi->portc_out_mask = 0; + ppi->out_handler = out; + ppi->in_handler = in; +} + +static uint8_t porta_out_enabled(i8255 *ppi) +{ + return (ppi->control & 0x40) || !(ppi->control & 0x10); +} + +void i8255_write(uint32_t address, i8255 *ppi, uint8_t value, uint32_t cycle) +{ + switch(address) + { + case 0: + ppi->latches[0] = value; + if (porta_out_enabled(ppi)) { + if (ppi->control & 0x60) { + //Mode 1 or 2 + ppi->latches[2] &= ~BIT_OBFA; + if ((ppi->control & 0x60) == 0x20 || !(ppi->latches[2] & BIT_IBFA)) { + ppi->latches[2] &= ~BIT_INTRA; + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } + if (ppi->out_handler && !(ppi->control & 0x40)) { + ppi->out_handler(ppi, cycle, address, value); + } + } + break; + case 1: + if (!(ppi->control & 0x02)) { + ppi->latches[1] = value; + if (ppi->control & 0x04) { + //Mode 1 + ppi->latches[2] &= ~(BIT_IBF_OBFB|BIT_INTRB); + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, address, value); + } + } + break; + case 2: + ppi->latches[2] &= ~ppi->portc_write_mask; + ppi->latches[2] |= value & ppi->portc_write_mask; + if (ppi->out_handler && ppi->portc_out_mask) { + ppi->out_handler(ppi, cycle, address, ppi->latches[2] & ppi->portc_out_mask); + } + break; + case 3: + if (value & 0x80) { + uint8_t changed = ppi->control ^ value; + //datasheet says "output" state is cleared on mode changes + if (changed & 0x60) { + //group A mode changed + ppi->latches[0] = 0; + ppi->latches[2] &= 0x0F; + } + if (changed & 4) { + //group B mode changed + ppi->latches[1] = 0; + if (value & 0x60) { + //PC4 is INTRa + ppi->latches[2] &= 0xF8; + } else { + ppi->latches[2] &= 0xF0; + } + } + ppi->control = value; + ppi->portc_write_mask = ppi->portc_out_mask = 0; + if (value & 0x40) { + //Port A Mode 2 + ppi->portc_out_mask |= BIT_OBFA | BIT_IBFA | BIT_INTRA; + ppi->portc_write_mask |= BIT_INTE1 | BIT_INTE2; + } else if (value & 0x20) { + //Port A Mode 1 + ppi->portc_out_mask |= BIT_INTRA; + if (value & 0x10) { + //Input + ppi->portc_out_mask |= BIT_IBFA; + ppi->portc_write_mask |= BIT_INTE2 | 0xC0; + if (!(value & 0x08)) { + //Port C upper Output + ppi->portc_out_mask |= 0xC0; + } + } else { + //Output + ppi->portc_out_mask |= BIT_OBFA; + ppi->portc_out_mask |= BIT_INTE1 | 0x30; + if (!(value & 0x08)) { + //Port C upper Output + ppi->portc_out_mask |= 0x30; + } + } + } else { + ppi->portc_write_mask |= 0xF0; + if (!(value & 0x08)) { + //Port C upper Output + ppi->portc_out_mask |= 0xF0; + } + } + if (value & 0x04) { + //Port B Mode 1 + ppi->portc_out_mask |= BIT_IBF_OBFB | BIT_INTRB; + ppi->portc_write_mask |= BIT_INTEB; + if (!(ppi->portc_out_mask & BIT_INTRA) && !(value & 1)) { + //Port C lower Output + ppi->portc_out_mask |= 0x08; + ppi->portc_write_mask |= 0x08; + } + } else { + if (!(value & 1)) { + //Port C lower Output + ppi->portc_out_mask |= 0x07; + ppi->portc_write_mask |= 0x07; + if (!(ppi->portc_out_mask & BIT_INTRA)) { + ppi->portc_out_mask |= 0x08; + ppi->portc_write_mask |= 0x08; + } + } + } + } else { + uint8_t bit = 1 << ((value >> 1) & 7); + if (ppi->portc_write_mask & bit) { + if (value & 1) { + ppi->latches[2] |= bit; + } else { + ppi->latches[2] &= bit; + } + if (ppi->out_handler && ppi->portc_out_mask) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } + } + + } +} + +uint8_t i8255_read(uint32_t address, i8255 *ppi, uint32_t cycle) +{ + switch(address) + { + case 0: + if (ppi->control & 0x60) { + //Mode 1 or 2 + if (ppi->control & 0x50) { + //Mode 2 or Mode 1 input + ppi->latches[2] &= ~BIT_IBFA; + if (!(ppi->control & 0x40) || (ppi->latches[2] & BIT_OBFA)) { + ppi->latches[2] &= ~BIT_INTRA; + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } + return ppi->latches[3]; + } + if (ppi->control & 0x10) { + if (ppi->in_handler) { + return ppi->in_handler(ppi, cycle, address); + } + return 0xFF; + } + return ppi->latches[0]; + case 1: + if (ppi->control & 0x40) { + //Mode 1 + if (ppi->control & 0x2) { + //input + ppi->latches[2] &= ~(BIT_IBF_OBFB|BIT_INTRB); + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } + return ppi->latches[1]; + } + if (ppi->control & 0x2) { + //input + if (ppi->in_handler) { + return ppi->in_handler(ppi, cycle, address); + } + return 0xFF; + } + return ppi->latches[1]; + case 2: + return ppi->latches[2]; + case 3: + default: + return 0xFF;//described as illegal in datasheet + } +} + +void i8255_input_strobe_a(i8255 *ppi, uint8_t value, uint32_t cycle) +{ + if ((ppi->control & 0x70) == 0x30 || (ppi->control & 0x40)) { + //Mode 2 or Mode 1 input + ppi->latches[3] = value; + ppi->latches[2] |= BIT_IBFA; + if (ppi->latches[2] & BIT_INTE2) { + ppi->latches[2] |= BIT_INTRA; + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } +} + +void i8255_input_strobe_b(i8255 *ppi, uint8_t value, uint32_t cycle) +{ + if ((ppi->control & 6) == 6) { + //Mode 1 input + ppi->latches[1] = value; + ppi->latches[2] |= BIT_IBF_OBFB; + if (ppi->latches[2] & BIT_INTEB) { + ppi->latches[2] |= BIT_INTRB; + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + } +} + +uint8_t i8255_output_ack_a(i8255 *ppi, uint32_t cycle) +{ + if ((ppi->control & 0x70) == 0x20 || (ppi->control & 0x40)) { + //Mode 2 or Mode 1 output + ppi->latches[2] |= BIT_OBFA; + if (ppi->latches[2] & BIT_INTE1) { + ppi->latches[2] |= BIT_INTRA; + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + return ppi->latches[0]; + } + if (ppi->control & 0x10) { + //input mode + return 0xFF; + } + //Mode 0 output + return ppi->latches[0]; +} + +uint8_t i8255_output_ack_b(i8255 *ppi, uint32_t cycle) +{ + if ((ppi->control & 0x06) == 0x04) { + //Mode 1 output + ppi->latches[2] |= BIT_IBF_OBFB; + if (ppi->latches[2] & BIT_INTEB) { + ppi->latches[2] |= BIT_INTRB; + } + if (ppi->out_handler) { + ppi->out_handler(ppi, cycle, 2, ppi->latches[2] & ppi->portc_out_mask); + } + return ppi->latches[1]; + } + if (ppi->control & 2) { + //input mode + return 0xFF; + } + //Mode 0 output + return ppi->latches[1]; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/i8255.h Fri Oct 11 00:46:53 2024 -0700 @@ -0,0 +1,28 @@ +#ifndef I8255_H_ +#define I8255_H_ + +#include <stdint.h> + +typedef struct i8255 i8255; +typedef void (*i8255_out_update)(i8255 *ppi, uint32_t cycle, uint32_t port, uint8_t data); +typedef uint8_t (*i8255_in_sample)(i8255 *ppi, uint32_t cycle, uint32_t port); + +struct i8255 { + uint8_t latches[4]; + uint8_t control; + uint8_t portc_write_mask; + uint8_t portc_out_mask; + i8255_out_update out_handler; + i8255_in_sample in_handler; + void *system; +}; + +void i8255_init(i8255 *ppi, i8255_out_update out, i8255_in_sample in); +void i8255_write(uint32_t address, i8255 *ppi, uint8_t value, uint32_t cycle); +uint8_t i8255_read(uint32_t address, i8255 *ppi, uint32_t cycle); +void i8255_input_strobe_a(i8255 *ppi, uint8_t value, uint32_t cycle); +void i8255_input_strobe_b(i8255 *ppi, uint8_t value, uint32_t cycle); +uint8_t i8255_output_ack_a(i8255 *ppi, uint32_t cycle); +uint8_t i8255_output_ack_b(i8255 *ppi, uint32_t cycle); + +#endif //I8255_H_
--- a/rom.db Mon Oct 07 21:38:54 2024 -0700 +++ b/rom.db Fri Oct 11 00:46:53 2024 -0700 @@ -1689,3 +1689,17 @@ #German version of this game has 8G in the region field regions E } +a43aef367857a681decea52377c2e7a992c2ac68 { + name Level III BASIC + map { + 0 { + device ROM + last 7FFF + } + 8000 { + device RAM + size 8000 + last FFFF + } + } +}
--- a/sms.c Mon Oct 07 21:38:54 2024 -0700 +++ b/sms.c Fri Oct 11 00:46:53 2024 -0700 @@ -126,6 +126,41 @@ return 0xFF; } +static void i8255_output_updated(i8255 *ppi, uint32_t cycle, uint32_t port, uint8_t data) +{ + if (port == 2) { + sms_context *sms = ppi->system; + sms->kb_mux = data & 0x7; + } +} + +static uint8_t i8255_input_poll(i8255 *ppi, uint32_t cycle, uint32_t port) +{ + if (port > 1) { + return 0xFF; + } + sms_context *sms = ppi->system; + if (sms->kb_mux == 7) { + if (port) { + //TODO: cassette-in + //TODO: printer port BUSY/FAULT + uint8_t port_b = io_data_read(sms->io.ports+1, cycle); + return (port_b >> 2 & 0xF) | 0x10; + } else { + uint8_t port_a = io_data_read(sms->io.ports, cycle); + uint8_t port_b = io_data_read(sms->io.ports+1, cycle); + return (port_a & 0x3F) | (port_b << 6); + } + } + //TODO: keyboard matrix ghosting + if (port) { + //TODO: cassette-in + //TODO: printer port BUSY/FAULT + return (sms->keystate[sms->kb_mux] >> 8) | 0x10; + } + return sms->keystate[sms->kb_mux]; +} + static void update_mem_map(uint32_t location, sms_context *sms, uint8_t value) { z80_context *z80 = sms->z80; @@ -249,6 +284,54 @@ sms->psg->pan = value; return vcontext; } + +static void *ppi_write(uint32_t location, void *vcontext, uint8_t value) +{ + z80_context *z80 = vcontext; + sms_context *sms = z80->system; + i8255_write(location, sms->i8255, value, z80->Z80_CYCLE); + return vcontext; +} + +static uint8_t ppi_read(uint32_t location, void *vcontext) +{ + z80_context *z80 = vcontext; + sms_context *sms = z80->system; + return i8255_read(location, sms->i8255, z80->Z80_CYCLE); +} + +static void *all_write(uint32_t location, void *vcontext, uint8_t value) +{ + vdp_write(location, vcontext, value); + sms_psg_write(location, vcontext, value); + return ppi_write(location, vcontext, value); +} + +static uint8_t ppi_vdp_read(uint32_t location, void *vcontext) +{ + //TODO: "corrupt" PPI value by VDP value + vdp_read(location, vcontext); + return ppi_read(location, vcontext); +} + +static void *vdp_psg_write(uint32_t location, void *vcontext, uint8_t value) +{ + vdp_write(location, vcontext, value); + return sms_psg_write(location, vcontext, value); +} + +static void *ppi_psg_write(uint32_t location, void *vcontext, uint8_t value) +{ + vdp_write(location, vcontext, value); + return ppi_write(location, vcontext, value); +} + +static void *ppi_vdp_write(uint32_t location, void *vcontext, uint8_t value) +{ + vdp_write(location, vcontext, value); + return ppi_write(location, vcontext, value); +} + static memmap_chunk io_map[] = { {0x00, 0x40, 0xFF, .write_8 = memory_io_write}, {0x40, 0x80, 0xFF, .read_8 = hv_read, .write_8 = sms_psg_write}, @@ -265,6 +348,16 @@ {0xC0, 0x100,0xFF, .read_8 = io_read} }; +static memmap_chunk io_sc[] = { + {0x00, 0x20, 0x03, .read_8 = ppi_vdp_read, .write_8 = all_write}, + {0x20, 0x40, 0xFF, .read_8 = vdp_read, .write_8 = vdp_psg_write}, + {0x40, 0x60, 0x03, .read_8 = ppi_read, .write_8 = ppi_psg_write}, + {0x60, 0x80, 0xFF, .write_8 = sms_psg_write}, + {0x80, 0xA0, 0x03, .read_8 = ppi_vdp_read, .write_8 = ppi_vdp_write}, + {0xA0, 0xC0, 0xFF, .read_8 = vdp_read, .write_8 = vdp_write}, + {0xD0, 0x100, 0x03, .read_8 = ppi_read, .write_8 = ppi_write} +}; + static void set_speed_percent(system_header * system, uint32_t percent) { sms_context *context = (sms_context *)system; @@ -589,6 +682,7 @@ z80_options_free(sms->z80->Z80_OPTS); free(sms->z80); psg_free(sms->psg); + free(sms->i8255); free(sms); } @@ -670,16 +764,92 @@ io_mouse_motion_relative(&sms->io, mouse_num, x, y); } +uint16_t scancode_map[0x90] = { + [0x1C] = 0x0004,//A + [0x32] = 0x4008,//B + [0x21] = 0x2008,//C + [0x23] = 0x2004,//D + [0x24] = 0x2002,//E + [0x2B] = 0x3004,//F + [0x34] = 0x4004,//G + [0x33] = 0x5004,//H + [0x43] = 0x0080,//I + [0x3B] = 0x6004,//J + [0x42] = 0x0040,//K + [0x4B] = 0x1040,//L + [0x3A] = 0x6008,//M + [0x31] = 0x5008,//N + [0x44] = 0x1080,//O + [0x4D] = 0x2080,//P + [0x15] = 0x0002,//Q + [0x2D] = 0x3002,//R + [0x1B] = 0x1004,//S + [0x2C] = 0x4002,//T + [0x3C] = 0x6002,//U + [0x2A] = 0x3008,//V + [0x1D] = 0x1002,//W + [0x22] = 0x1008,//X + [0x35] = 0x5002,//Y + [0x1A] = 0x0008,//Z + [0x16] = 0x0001,//1 + [0x1E] = 0x1001,//2 + [0x26] = 0x2001,//3 + [0x25] = 0x3001,//4 + [0x2E] = 0x4001,//5 + [0x36] = 0x5001,//6 + [0x3D] = 0x6001,//7 + [0x3E] = 0x0100,//8 + [0x46] = 0x1100,//9 + [0x45] = 0x2100,//0 + [0x5A] = 0x5040,//return + [0x29] = 0x1010,//space + [0x0D] = 0x5800,//tab mapped to FUNC + [0x66] = 0x3010,//backspace mapped to INS/DEL + [0x4E] = 0x3100,// - + [0x55] = 0x4100,// = mapped to ^ based on position + [0x54] = 0x4080,// [ + [0x5B] = 0x4040,// ] + [0x5D] = 0x5100,// \ mapped to Yen based on position/correspondence on PC keyboards + [0x4C] = 0x2040,// ; + [0x52] = 0x3040,// ' mapped to : based on position + [0x0E] = 0x3020,// ` mapped to PI because of lack of good options + [0x41] = 0x0020,// , + [0x49] = 0x1020,// . + [0x4A] = 0x2020,// / + [0x14] = 0x6400,//lctrl mapped to ctrl + //rctrl is default keybind for toggle keyboard capture + //[0x18] = 0x6400,//rctrl mapped to ctrl + [0x12] = 0x6800,//lshift mapped to shift + [0x59] = 0x6800,//lshift mapped to shift + [0x11] = 0x6200,//lalt mapped to GRAPH + [0x17] = 0x6200,//ralt mapped to GRAPH + [0x81] = 0x0010,//insert mapped to kana/dieresis key + [0x86] = 0x5020,//left arrow + [0x87] = 0x2010,//home mapped to HOME/CLR + [0x88] = 0x6100,//end mapped to BREAK + [0x89] = 0x6040,//up arrow + [0x8A] = 0x4020,//down arrow + [0x8D] = 0x6020,//right arrow +}; + static void keyboard_down(system_header *system, uint8_t scancode) { sms_context *sms = (sms_context *)system; io_keyboard_down(&sms->io, scancode); + if (sms->keystate && scancode < 0x90 && scancode_map[scancode]) { + uint16_t row = scancode_map[scancode] >> 12; + sms->keystate[row] &= ~(scancode_map[scancode] & 0xFFF); + } } static void keyboard_up(system_header *system, uint8_t scancode) { sms_context *sms = (sms_context *)system; io_keyboard_up(&sms->io, scancode); + if (sms->keystate && scancode < 0x90 && scancode_map[scancode]) { + uint16_t row = scancode_map[scancode] >> 12; + sms->keystate[row] |= scancode_map[scancode] & 0xFFF; + } } static void set_gain_config(sms_context *sms) @@ -728,15 +898,18 @@ z80_options *zopts = malloc(sizeof(z80_options)); tern_node *model_def; uint8_t is_gamegear = !strcasecmp(media->extension, "gg"); + uint8_t is_sc3000 = !strcasecmp(media->extension, "sc"); if (is_gamegear) { model_def = tern_find_node(get_systems_config(), "gg"); } else if (!strcasecmp(media->extension, "sg")) { model_def = tern_find_node(get_systems_config(), "sg1000"); + } else if (is_sc3000) { + model_def = tern_find_node(get_systems_config(), "sc3000"); } else { model_def = get_model(config, SYSTEM_SMS); } char *vdp_str = tern_find_ptr(model_def, "vdp"); - uint8_t vdp_type = is_gamegear ? VDP_GENESIS : VDP_GAMEGEAR; + uint8_t vdp_type = is_gamegear ? VDP_GAMEGEAR : is_sc3000 ? VDP_TMS9918A : VDP_SMS2; if (vdp_str) { if (!strcmp(vdp_str, "sms1")) { vdp_type = VDP_SMS; @@ -759,9 +932,27 @@ chunk->buffer = sms->ram + ((chunk->start - 0xC000) & 0x1FFF); } } + char *io_type = tern_find_ptr(model_def, "io"); + if (io_type) { + if (!strcmp(io_type, "gamegear")) { + is_gamegear = 1; + is_sc3000 = 0; + } else if (!strcmp(io_type, "i8255")) { + is_gamegear = 0; + is_sc3000 = 1; + } + } if (is_gamegear) { init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_gg, 6, 15, 0xFF); sms->start_button_region = 0xC0; + } else if (is_sc3000) { + sms->keystate = calloc(sizeof(uint16_t), 7); + memset(sms->keystate, 0xFF, sizeof(uint16_t) * 7); + sms->i8255 = calloc(1, sizeof(i8255)); + i8255_init(sms->i8255, i8255_output_updated, i8255_input_poll); + sms->i8255->system = sms; + sms->kb_mux = 7; + init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_sc, 7, 15, 0xFF); } else { init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_map, 4, 15, 0xFF); } @@ -803,7 +994,7 @@ } } setup_io_devices(io_config_root, &sms->header.info, &sms->io); - sms->header.has_keyboard = io_has_keyboard(&sms->io); + sms->header.has_keyboard = io_has_keyboard(&sms->io) || sms->keystate; sms->header.set_speed_percent = set_speed_percent; sms->header.start_context = start_sms;
--- a/sms.h Mon Oct 07 21:38:54 2024 -0700 +++ b/sms.h Fri Oct 11 00:46:53 2024 -0700 @@ -10,6 +10,7 @@ #include "z80_to_x86.h" #endif #include "io.h" +#include "i8255.h" #define SMS_RAM_SIZE (8*1024) #define SMS_CART_RAM_SIZE (32*1024) @@ -20,6 +21,8 @@ vdp_context *vdp; psg_context *psg; sega_io io; + i8255 *i8255; + uint16_t *keystate; uint8_t *rom; uint32_t rom_size; uint32_t master_clock; @@ -30,6 +33,7 @@ uint8_t ram[SMS_RAM_SIZE]; uint8_t bank_regs[4]; uint8_t cart_ram[SMS_CART_RAM_SIZE]; + uint8_t kb_mux; } sms_context; sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t force_region);
--- a/system.c Mon Oct 07 21:38:54 2024 -0700 +++ b/system.c Fri Oct 11 00:46:53 2024 -0700 @@ -86,7 +86,8 @@ if (!strcmp("md", media->extension) || !strcmp("gen", media->extension)) { return SYSTEM_GENESIS; } - if (!strcmp("sms", media->extension) || !strcmp("sg", media->extension) || !strcmp("gg", media->extension)) { + if (!strcmp("sms", media->extension) || !strcmp("sg", media->extension) || !strcmp("gg", media->extension) + || !strcmp("sc", media->extension) || !strcmp("sf7", media->extension)) { return SYSTEM_SMS; } if (!strcmp("j64", media->extension)) {