#include #include #include #include #include #include #include #include #include #include #include #include "lib/imlib2/Imlib2.h" #include "hardware.h" #include "osd.h" #include "user_io.h" #include "debug.h" #include "spi.h" #include "cfg.h" #include "input.h" #include "fpga_io.h" #include "file_io.h" #include "menu.h" #include "DiskImage.h" #include "brightness.h" #include "sxmlc.h" #include "bootcore.h" #include "charrom.h" #include "scaler.h" #include "miniz.h" #include "cheats.h" #include "video.h" #include "audio.h" #include "shmem.h" #include "ide.h" #include "ide_cdrom.h" #include "profiling.h" #include "support.h" static char core_path[1024] = {}; static char rbf_path[1024] = {}; static fileTYPE sd_image[16] = {}; #define SD_TYPE_DEFAULT 0 #define SD_TYPE_C64 1 #define SD_TYPE_A2 2 static int sd_type[16] = {}; static int sd_image_cangrow[16] = {}; static uint64_t buffer_lba[16] = { ULLONG_MAX,ULLONG_MAX,ULLONG_MAX,ULLONG_MAX, ULLONG_MAX,ULLONG_MAX,ULLONG_MAX,ULLONG_MAX, ULLONG_MAX,ULLONG_MAX,ULLONG_MAX,ULLONG_MAX, ULLONG_MAX,ULLONG_MAX,ULLONG_MAX,ULLONG_MAX }; static int use_save = 0; // mouse and keyboard emulation state static int emu_mode = EMU_NONE; // keep state over core type and its capabilities static unsigned char core_type = CORE_TYPE_UNKNOWN; static unsigned char dual_sdr = 0; static int fio_size = 0; static int io_ver = 0; // keep state of caps lock static char caps_lock_toggle = 0; #define LED_FREQ 100 // 100 ms static unsigned long led_timer; static char keyboard_leds = 0; static bool caps_status = 0; static bool num_status = 0; static bool scrl_status = 0; static bool winkey_pressed = 0; static uint16_t sdram_cfg = 0; static char last_filename[1024] = {}; void user_io_store_filename(char *filename) { char *p = strrchr(filename, '/'); if (p) strcpy(last_filename, p + 1); else strcpy(last_filename, filename); p = strrchr(last_filename, '.'); if (p) *p = 0; } const char *get_image_name(int i) { if (!sd_image[i].size) return NULL; char *p = strrchr(sd_image[i].name, '/'); if (!p) p = sd_image[i].name; else p++; return p; } fileTYPE *get_image(int i) { return &sd_image[i]; } static uint32_t uart_mode; uint32_t user_io_get_uart_mode() { return uart_mode; } // set by OSD code to suppress forwarding of those keys to the core which // may be in use by an active OSD static char osd_is_visible = 0; char user_io_osd_is_visible() { return osd_is_visible; } unsigned char user_io_core_type() { return core_type; } static char config_ver[10] = {}; char* user_io_create_config_name(int with_ver) { static char str[40]; str[0] = 0; char *p = user_io_get_core_name(); if (p[0]) { strcpy(str, p); if (with_ver) strcat(str, config_ver); strcat(str, ".CFG"); } return str; } static char core_name[32] = {}; static char ovr_name[32] = {}; static char orig_name[32] = {}; static int ovr_samedir = 0; char *user_io_make_filepath(const char *path, const char *filename) { static char filepath_store[1024]; snprintf(filepath_store, 1024, "%s/%s", path, filename); return filepath_store; } void user_io_name_override(const char* name, int samedir) { snprintf(ovr_name, sizeof(ovr_name), "%s", name); ovr_samedir = samedir; } void user_io_set_core_name(const char *name) { snprintf(core_name, sizeof(core_name), name); printf("Core name set to \"%s\"\n", core_name); } char *user_io_get_core_name(int orig) { return orig ? orig_name : core_name; } char *user_io_get_core_name2() { return (ovr_name[0] && ovr_samedir) ? orig_name : core_name; } char *user_io_get_core_path(const char *suffix, int recheck) { static char old_name[256] = {}; static char tmp[1024] = {}; char *name = (ovr_name[0] && ovr_samedir) ? orig_name : core_name; if (!suffix) suffix = (!strcasecmp(name, "minimig")) ? "Amiga" : name; if (recheck || strcmp(old_name, suffix) || !tmp[0]) { strcpy(old_name, suffix); strcpy(tmp, suffix); prefixGameDir(tmp, sizeof(tmp)); } return tmp; } static char is_arcade_type = 0; char is_arcade() { return is_arcade_type; } static int is_menu_type = 0; char is_menu() { if (!is_menu_type) is_menu_type = strcasecmp(orig_name, "MENU") ? 2 : 1; return (is_menu_type == 1); } static int is_x86_type = 0; char is_x86() { if (!is_x86_type) is_x86_type = strcasecmp(orig_name, "AO486") ? 2 : 1; return (is_x86_type == 1); } static int is_snes_type = 0; char is_snes() { if (!is_snes_type) is_snes_type = strcasecmp(orig_name, "SNES") ? 2 : 1; return (is_snes_type == 1); } static int is_sgb_type = 0; char is_sgb() { if (!is_sgb_type) is_sgb_type = strcasecmp(orig_name, "SGB") ? 2 : 1; return (is_sgb_type == 1); } static int is_cpc_type = 0; char is_cpc() { if (!is_cpc_type) is_cpc_type = strcasecmp(orig_name, "amstrad") ? 2 : 1; return (is_cpc_type == 1); } static int is_zx81_type = 0; char is_zx81() { if (!is_zx81_type) is_zx81_type = strcasecmp(orig_name, "zx81") ? 2 : 1; return (is_zx81_type == 1); } static int is_neogeo_type = 0; char is_neogeo() { if (!is_neogeo_type) is_neogeo_type = strcasecmp(orig_name, "neogeo") ? 2 : 1; return (is_neogeo_type == 1); } char is_neogeo_cd() { return is_neogeo() && neocd_is_en(); } static int is_minimig_type = 0; char is_minimig() { if (!is_minimig_type) is_minimig_type = strcasecmp(orig_name, "minimig") ? 2 : 1; return (is_minimig_type == 1); } static int is_megacd_type = 0; char is_megacd() { if (!is_megacd_type) is_megacd_type = strcasecmp(orig_name, "MEGACD") ? 2 : 1; return (is_megacd_type == 1); } static int is_pce_type = 0; char is_pce() { if (!is_pce_type) is_pce_type = strcasecmp(orig_name, "TGFX16") ? 2 : 1; return (is_pce_type == 1); } static int is_archie_type = 0; char is_archie() { if (!is_archie_type) is_archie_type = strcasecmp(orig_name, "ARCHIE") ? 2 : 1; return (is_archie_type == 1); } static int is_pcxt_type = 0; char is_pcxt() { if (!is_pcxt_type) is_pcxt_type = strcasecmp(orig_name, "PCXT") ? 2 : 1; return (is_pcxt_type == 1); } static int is_gba_type = 0; char is_gba() { if (!is_gba_type) is_gba_type = strcasecmp(orig_name, "GBA") ? 2 : 1; return (is_gba_type == 1); } static int is_c64_type = 0; char is_c64() { if (!is_c64_type) is_c64_type = strcasecmp(orig_name, "C64") ? 2 : 1; return (is_c64_type == 1); } static int is_c128_type = 0; char is_c128() { if (!is_c128_type) is_c128_type = strcasecmp(orig_name, "C128") ? 2 : 1; return (is_c128_type == 1); } static int is_psx_type = 0; char is_psx() { if (!is_psx_type) is_psx_type = strcasecmp(orig_name, "PSX") ? 2 : 1; return (is_psx_type == 1); } static int is_cdi_type = 0; char is_cdi() { if (!is_cdi_type) is_cdi_type = strcasecmp(orig_name, "CD-i") ? 2 : 1; return (is_cdi_type == 1); } static int is_st_type = 0; char is_st() { if (!is_st_type) is_st_type = strcasecmp(orig_name, "AtariST") ? 2 : 1; return (is_st_type == 1); } char is_sharpmz() { return(core_type == CORE_TYPE_SHARPMZ); } static int is_electron_type = 0; char is_electron() { if (!is_electron_type) is_electron_type = strcasecmp(orig_name, "AcornElectron") ? 2 : 1; return (is_electron_type == 1); } static int is_saturn_type = 0; char is_saturn() { if (!is_saturn_type) is_saturn_type = strcasecmp(orig_name, "Saturn") ? 2 : 1; return (is_saturn_type == 1); } static int is_n64_type = 0; char is_n64() { if (!is_n64_type) is_n64_type = strcasecmp(orig_name, "N64") ? 2 : 1; return (is_n64_type == 1); } static int is_uneon_type = 0; char is_uneon() { if (!is_uneon_type) is_uneon_type = strcasecmp(orig_name, "Uneon") ? 2 : 1; return (is_uneon_type == 1); } static int is_no_type = 0; static int disable_osd = 0; char has_menu() { if (disable_osd) return 0; if (!is_no_type) is_no_type = user_io_get_core_name()[0] ? 1 : 2; return (is_no_type == 1); } void user_io_read_core_name() { is_menu_type = 0; is_x86_type = 0; is_no_type = 0; is_snes_type = 0; is_sgb_type = 0; is_cpc_type = 0; is_zx81_type = 0; is_neogeo_type = 0; is_minimig_type = 0; is_megacd_type = 0; is_pce_type = 0; is_archie_type = 0; is_gba_type = 0; is_c64_type = 0; is_c128_type = 0; is_psx_type = 0; is_cdi_type = 0; is_st_type = 0; is_pcxt_type = 0; is_electron_type = 0; is_saturn_type = 0; is_n64_type = 0; is_uneon_type = 0; core_name[0] = 0; char *p = user_io_get_confstr(0); if (p && p[0]) snprintf(orig_name, sizeof(orig_name), "%s", p); // get core name if (ovr_name[0]) strcpy(core_name, ovr_name); else if (orig_name[0]) strcpy(core_name, p); printf("Core name is \"%s\"\n", core_name); } int substrcpy(char *d, const char *s, char idx) { char p = 0; char *b = d; while (*s) { if ((p == idx) && *s && (*s != ',')) *d++ = *s; if (*s == ',') { if (p == idx) break; p++; } s++; } *d = 0; return (int)(d - b); } static char cur_status[16] = {}; int user_io_status_bits(const char *opt, int *s, int *e, int ex, int single) { uint32_t start = 0, end = 0; if (opt[0] == '[') { if (!single && sscanf(opt, "[%u:%u]", &end, &start) == 2) { if (start > 127 || end > 127 || end <= start) return 0; } else if (sscanf(opt, "[%u]", &start) == 1) { if (start > 127) return 0; end = start; } else return 0; } else { if ((opt[0] >= '0') && (opt[0] <= '9')) start = opt[0] - '0'; else if ((opt[0] >= 'A') && (opt[0] <= 'V')) start = opt[0] - 'A' + 10; else return 0; if (!single && (opt[1] >= '0') && (opt[1] <= '9')) end = opt[1] - '0'; else if (!single && (opt[1] >= 'A') && (opt[1] <= 'V')) end = opt[1] - 'A' + 10; else { single = 1; end = start; } if (ex) { start += 32; end += 32; } if (start > 127 || end > 127 || (!single && end <= start)) return 0; } //max 8 bits per option if (end - start > 8) return 0; if (s) *s = (int)start; if (e) *e = (int)end; return 1 + end - start; } uint32_t user_io_status_get(const char *opt, int ex) { int start, end; int size = user_io_status_bits(opt, &start, &end, ex); if (!size) return 0; uint32_t x = (cur_status[end / 8] << 8) | cur_status[start / 8]; x >>= start % 8; return x & ~(0xffffffff << size); } uint32_t user_io_status_mask(const char *opt) { return ~(0xffffffff << user_io_status_bits(opt, 0, 0, 0)); } uint32_t user_io_hd_mask(const char *opt) { int start; int size = user_io_status_bits(opt, &start, 0, 0, 1); if (!size) return 0; return start; } void user_io_status_set(const char *opt, uint32_t value, int ex) { int start, end; int size = user_io_status_bits(opt, &start, &end, ex); if (!size) return; int s = start / 8; int e = end / 8; uint32_t mask = ~(0xffffffff << size); mask <<= start % 8; uint32_t x = (cur_status[e] << 8) | cur_status[s]; x = (x & ~mask) | ((value << (start % 8)) & mask); cur_status[s] = (char)x; if (e != s) cur_status[e] = (char)(x >> 8); if (!is_st()) { spi_uio_cmd_cont(UIO_SET_STATUS2); for (uint32_t i = 0; i < sizeof(cur_status); i += 2) spi_w((cur_status[i + 1] << 8) | cur_status[i]); DisableIO(); } } int user_io_status_save(const char *filename) { return FileSaveConfig(filename, cur_status, sizeof(cur_status)); } void user_io_status_reset() { memset(cur_status, 0, sizeof(cur_status)); user_io_status_set("[0]", 0); } static void set_kbd_led(int led, int state) { if (led & HID_LED_CAPS_LOCK) { caps_status = state&HID_LED_CAPS_LOCK; if (!(keyboard_leds & KBD_LED_CAPS_CONTROL)) set_kbdled(led&HID_LED_CAPS_LOCK, caps_status); } if (led & HID_LED_NUM_LOCK) { num_status = state&HID_LED_NUM_LOCK; if (!(keyboard_leds & KBD_LED_NUM_CONTROL)) set_kbdled(led&HID_LED_NUM_LOCK, num_status); } if (led & HID_LED_SCROLL_LOCK) { scrl_status = state&HID_LED_SCROLL_LOCK; if (!(keyboard_leds & KBD_LED_SCRL_CONTROL)) set_kbdled(led&HID_LED_SCROLL_LOCK, scrl_status); } } static void set_emu_mode(int mode) { uint8_t emu_led; emu_mode = mode; switch (emu_mode) { case EMU_JOY0: emu_led = 0x20; set_kbd_led(HID_LED_NUM_LOCK | HID_LED_SCROLL_LOCK, HID_LED_NUM_LOCK); Info("Kbd mode: Joystick 1", 1000); break; case EMU_JOY1: emu_led = 0x40; set_kbd_led(HID_LED_NUM_LOCK | HID_LED_SCROLL_LOCK, HID_LED_SCROLL_LOCK); Info("Kbd mode: Joystick 2", 1000); break; case EMU_MOUSE: emu_led = 0x60; set_kbd_led(HID_LED_NUM_LOCK | HID_LED_SCROLL_LOCK, HID_LED_NUM_LOCK | HID_LED_SCROLL_LOCK); Info("Kbd mode: Mouse", 1000); break; default: emu_led = 0; set_kbd_led(HID_LED_NUM_LOCK | HID_LED_SCROLL_LOCK, 0); Info("Kbd mode: Normal", 1000); } spi_uio_cmd16(UIO_LEDS, 0x6000 | emu_led); input_notify_mode(); } int user_io_get_kbdemu() { return emu_mode; } static int joy_force = 0; // Analog/Digital Joystick translation // 0 - translate Analog to Digital (default) // 1 - translate Digital to Analog // 2 - do not translate static int joy_transl = 0; int user_io_get_joy_transl() { return joy_transl; } static int use_cheats = 0; static uint32_t ss_base = 0; static uint32_t ss_size = 0; static uint32_t uart_speeds[13] = {}; static char uart_speed_labels[13][32] = {}; static uint32_t midi_speeds[13] = {}; static char midi_speed_labels[13][32] = {}; static const uint32_t mlink_speeds[13] = { 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 31250, 38400, 57600, 115200 }; static const char mlink_speed_labels[13][32] = { "110", "300", "600", "1200", "2400", "4800", "9600", "14400", "19200", "31250/MIDI", "38400", "57600", "115200" }; static char defmra[1024] = {}; static int boot0_loaded = 0; static int boot0_mounted = 0; static void parse_config() { static char str[1024]; static char ext[256]; char mask[sizeof(cur_status) * 8] = {}; char overlap[sizeof(cur_status) * 8] = {}; int start, end, sz; int i = 0; char *p; joy_force = 0; joy_bcount = 0; do { p = user_io_get_confstr(i); printf("get cfgstring %d = %s\n", i, p ? p : "NULL"); if (!i) { OsdCoreNameSet((p && p[0]) ? p : "CORE"); } if (i == 1 && p) { while (p && *p) { if (!strncasecmp(p, "SS", 2)) { char *end = 0; ss_base = strtoul(p+2, &end, 16); p = end; if (p && *p == ':') { p++; ss_size = strtoul(p, &end, 16); } printf("Got save state parameters: base=0x%X, size=0x%X\n", ss_base, ss_size); if (!ss_size || ss_size > (128 * 1024 * 1024)) { ss_size = 0; ss_base = 0; printf("Invalid size!\n"); } else if (ss_base < 0x20000000 || ss_base >= 0x40000000 || (ss_base + ss_size) >= 0x40000000) { ss_size = 0; ss_base = 0; printf("Invalid base!\n"); } } if (!strncasecmp(p, "UART", 4)) { p += 4; for (int i = 0; i < 10 && p && *p; i++) { char *end = 0; uart_speeds[i] = strtoul(p, &end, 10); p = end; if (p && *p == '(') { p++; int n = 0; while (*p != ';' && *p != ':' && *p != ')' && *p != ',') { if (n < 16) uart_speed_labels[i][n] = *p; p++; n++; } if (*p == ')') p++; } else { sprintf(uart_speed_labels[i], "%d", uart_speeds[i]); } if (p && *p == ':') p++; } printf("Got UART speeds:"); for(int i=0; i<10; i++) printf(" %d", uart_speeds[i]); printf("\n"); } if (!strncasecmp(p, "MIDI", 4)) { p += 4; for (int i = 0; i < 10 && p && *p; i++) { char *end = 0; midi_speeds[i] = strtoul(p, &end, 10); p = end; if (p && *p == '(') { p++; int n = 0; while (*p != ';' && *p != ':' && *p != ')' && *p != ',') { if (n < 16) midi_speed_labels[i][n] = *p; p++; n++; } if (*p == ')') p++; } else { sprintf(midi_speed_labels[i], "%d", midi_speeds[i]); } if (p && *p == ':') p++; } if (!midi_speeds[0]) { midi_speeds[0] = 31250; strcpy(midi_speed_labels[0], "31250"); } printf("Got MIDI speeds:"); for (int i = 0; i < 10; i++) printf(" %d", midi_speeds[i]); printf("\n"); } p = strchr(p, ','); if (p) p++; } } if (i >= 2 && p && p[0]) { if (!strncmp(p, "DEFMRA,", 7)) { snprintf(defmra, sizeof(defmra), (p[7] == '/') ? "%s%s" : "%s/_Arcades/%s", getRootDir(), p + 7); } else if (!strncmp(p, "DIP", 3)) { } else { //skip Disable/Hide masks while ((p[0] == 'H' || p[0] == 'D' || p[0] == 'h' || p[0] == 'd') && strlen(p) >= 2) p += 2; } if (p[0] == 'P') p += 2; if (p[0] == 'R' || p[0] == 'T' || p[0] == 'r' || p[0] == 't') { sz = user_io_status_bits(p + 1, &start, &end, p[0] == 'r' || p[0] == 't'); if (sz == 1) { overlap[start] |= mask[start]; mask[start] |= 1; } else { printf("Invalid OSD option: %s\n", p); } } else if (p[0] == 'O' || p[0] == 'o') { char *opt = (p[1] == 'X') ? (p + 2) : (p + 1); sz = user_io_status_bits(opt, &start, &end, p[0] == 'o'); if (sz) { while (sz) { overlap[start] |= mask[start]; mask[start] |= 1; sz--; start++; } } else { printf("Invalid OSD option: %s\n", p); } } if (p[0] == 'J') { int n = 1; if (p[1] == 'D') { joy_transl = 0; n++; } if (p[1] == 'A') { joy_transl = 1; n++; } if (p[1] == 'N') { joy_transl = 2; n++; } if (p[n] == '1') { joy_force = 1; set_emu_mode(EMU_JOY0); } } if (p[0] == 'O' && p[1] == 'X') { int x = user_io_status_get(p + 2); printf("found OX option: %s: %d\n", p, x); if (is_x86()) { if (p[2] == '2') x86_set_fdd_boot(!(x & 1)); } } if (p[0] == 'X') { disable_osd = 1; } if (p[0] == 'V') { // get version string char s[128]; strcpy(s, OsdCoreNameGet()); strcat(s, " "); substrcpy(s + strlen(s), p, 1); OsdCoreNameSet(s); } if (p[0] == 'v') { static char str[256]; substrcpy(str, p, 1); str[2] = 0; int v = strtoul(str, 0, 10); if(v) snprintf(config_ver, sizeof(config_ver), "_v%d", v); } if (p[0] == 'C') { use_cheats = 1; } if (p[0] == 'F') { int opensave = 0; int idx = 1; if (p[idx] == 'S') { opensave = 1; idx++; } if (p[idx] == 'C') { idx++; uint32_t load_addr = 0; if (substrcpy(str, p, 3)) { load_addr = strtoul(str, NULL, 16); if (load_addr < 0x20000000 || load_addr >= 0x40000000) { printf("Loading address 0x%X is outside the supported range! Using normal load.\n", load_addr); load_addr = 0; } } sprintf(str, "%s.f%c", user_io_get_core_name(), p[idx]); substrcpy(ext, p, 1); while (strlen(ext) % 3) strcat(ext, " "); if (FileLoadConfig(str, str, sizeof(str)) && str[0]) { idx = p[idx] - '0'; StoreIdx_F(idx, str); user_io_file_tx(str, (user_io_ext_idx(str, ext) << 6) | idx, opensave, 0, 0, load_addr); if (!idx) boot0_loaded = 1; if (is_cpc()) { char *p = strrchr(str, '.'); if (p && (!strcasecmp(p, ".eZZ") || !strcasecmp(p, ".eZ0"))) boot0_loaded = 1; } } } } if (p[0] == 'S' && p[1] == 'C') { sprintf(str, "%s.s%c", user_io_get_core_name(), p[2]); substrcpy(ext, p, 1); while (strlen(ext) % 3) strcat(ext, " "); if (FileLoadConfig(str, str, sizeof(str)) && str[0]) { int idx = p[2] - '0'; StoreIdx_S(idx, str); if (is_x86() || is_pcxt() || (is_uneon() && idx >= 2)) { x86_set_image(idx, str); } else if (is_megacd()) { mcd_set_image(idx, str); } else if (is_pce()) { pcecd_set_image(idx, str); cheats_init(str, 0); } else { if (!is_c128()) user_io_set_index((user_io_ext_idx(str, ext) << 6) | idx); user_io_file_mount(str, idx); } if (!idx) boot0_mounted = 1; } } } i++; } while (p || i<3); mask[0] = 1; // reset is always on bit 0 printf("\n// Status Bit Map:\n"); printf("// Upper Lower\n"); printf("// 0 1 2 3 4 5 6 \n"); printf("// 01234567890123456789012345678901 23456789012345678901234567890123\n"); printf("// 0123456789ABCDEFGHIJKLMNOPQRSTUV 0123456789ABCDEFGHIJKLMNOPQRSTUV\n"); strcpy(str, "// "); for (int i = 0; i < 32; i++) strcat(str, mask[i] ? "X" : " "); strcat(str, " "); for (int i = 32; i < 64; i++) strcat(str, mask[i] ? "X" : " "); strcat(str, "\n"); printf(str); int ovr = 0; for (int i = 0; i < 64; i++) ovr |= overlap[i]; if (ovr) { strcpy(str, "// "); for (int i = 0; i < 32; i++) strcat(str, overlap[i] ? "^" : " "); strcat(str, " "); for (int i = 32; i < 64; i++) strcat(str, overlap[i] ? "^" : " "); strcat(str, "\n"); printf(str); printf("// *Overlapped bits!* (can be intentional)\n"); } printf("\n"); ovr = 0; for (int i = 64; i < 128; i++) ovr |= mask[i]; if (ovr) { printf("// 0 0 0 0 1 1 1 \n"); printf("// 6 7 8 9 0 1 2 \n"); printf("// 45678901234567890123456789012345 67890123456789012345678901234567\n"); strcpy(str, "// "); for (int i = 64; i < 96; i++) strcat(str, mask[i] ? "X" : " "); strcat(str, " "); for (int i = 96; i < 128; i++) strcat(str, mask[i] ? "X" : " "); strcat(str, "\n"); printf(str); ovr = 0; for (int i = 64; i < 128; i++) ovr |= overlap[i]; if (ovr) { strcpy(str, "// "); for (int i = 64; i < 96; i++) strcat(str, overlap[i] ? "^" : " "); strcat(str, " "); for (int i = 96; i < 128; i++) strcat(str, overlap[i] ? "^" : " "); strcat(str, "\n"); printf(str); printf("// *Overlapped bits!* (can be intentional)\n"); } printf("\n"); } // legacy GBA versions if (is_gba() && !ss_base) { ss_base = 0x3E000000; ss_size = 0x100000; } } //MSM6242B layout static void send_rtc(int type) { //printf("Update RTC\n"); time_t t = time(NULL); if (type & 1) { struct tm tm = *localtime(&t); uint8_t rtc[8]; rtc[0] = (tm.tm_sec % 10) | ((tm.tm_sec / 10) << 4); rtc[1] = (tm.tm_min % 10) | ((tm.tm_min / 10) << 4); rtc[2] = (tm.tm_hour % 10) | ((tm.tm_hour / 10) << 4); rtc[3] = (tm.tm_mday % 10) | ((tm.tm_mday / 10) << 4); rtc[4] = ((tm.tm_mon + 1) % 10) | (((tm.tm_mon + 1) / 10) << 4); rtc[5] = (tm.tm_year % 10) | (((tm.tm_year / 10) % 10) << 4); rtc[6] = tm.tm_wday; rtc[7] = 0x40; spi_uio_cmd_cont(UIO_RTC); spi_w((rtc[1] << 8) | rtc[0]); spi_w((rtc[3] << 8) | rtc[2]); spi_w((rtc[5] << 8) | rtc[4]); spi_w((rtc[7] << 8) | rtc[6]); DisableIO(); } if (type & 2) { t += t - mktime(gmtime(&t)); spi_uio_cmd_cont(UIO_TIMESTAMP); spi_w(t); spi_w(t >> 16); DisableIO(); } } const char* get_rbf_dir() { static char str[1024]; const char *root = getRootDir(); int len = strlen(root); if (!strlen(core_path) || strncmp(root, core_path, len)) return ""; strcpy(str, core_path + len + 1); char *p = strrchr(str, '/'); if (!p) return ""; *p = 0; return str; } const char* get_rbf_name() { if (!strlen(core_path)) return ""; char *p = strrchr(core_path, '/'); if (!p) return core_path; return p+1; } const char* get_rbf_path() { return core_path; } void MakeFile(const char *filename, const char *data) { FILE * file; file = fopen(filename, "w"); fwrite(data, strlen(data), 1, file); fclose(file); } int GetUARTMode() { struct stat filestat; if (!stat("/tmp/uartmode1", &filestat)) return 1; if (!stat("/tmp/uartmode2", &filestat)) return 2; if (!stat("/tmp/uartmode3", &filestat)) return 3; if (!stat("/tmp/uartmode4", &filestat)) return 4; if (!stat("/tmp/uartmode5", &filestat)) return 5; return 0; } void SetUARTMode(int mode) { mode &= 0xF; uint32_t baud = GetUARTbaud(mode); spi_uio_cmd_cont(UIO_SET_UART); spi_w((mode == 4 || mode == 5) ? 1 : mode); spi_w(baud); spi_w(baud >> 16); DisableIO(); MakeFile("/tmp/CORENAME", user_io_get_core_name()); MakeFile("/tmp/RBFNAME", user_io_get_core_name(1)); char data[20]; sprintf(data, "%d", baud); MakeFile("/tmp/UART_SPEED", data); char cmd[32]; sprintf(cmd, "uartmode %d", mode); system(cmd); } static int uart_speed_idx = 0; static int midi_speed_idx = 0; static int mlink_speed_idx = 0; const uint32_t* GetUARTbauds(int mode) { return (mode == 3) ? midi_speeds : (mode > 3) ? mlink_speeds : uart_speeds; } uint32_t GetUARTbaud(int mode) { return (mode == 3) ? midi_speeds[midi_speed_idx] : (mode > 3) ? mlink_speeds[mlink_speed_idx] : uart_speeds[uart_speed_idx]; } const char* GetUARTbaud_label(int mode) { return (mode == 3) ? midi_speed_labels[midi_speed_idx] : (mode > 3) ? mlink_speed_labels[mlink_speed_idx] : uart_speed_labels[uart_speed_idx]; } const char* GetUARTbaud_label(int mode, int idx) { return (mode == 3) ? midi_speed_labels[idx] : (mode > 3) ? mlink_speed_labels[idx] : uart_speed_labels[idx]; } int GetUARTbaud_idx(int mode) { return (mode == 3) ? midi_speed_idx : (mode > 3) ? mlink_speed_idx : uart_speed_idx; } char * GetMidiLinkSoundfont() { FILE * file; static char mLinkSoundfont[255]; char fileName[] = "/tmp/ML_SOUNDFONT"; char strip[] = "/media/fat/"; file = fopen(fileName, "r"); if (file) { fgets((char *) &mLinkSoundfont, sizeof(mLinkSoundfont), file); fclose(file); if(0 == strncmp(strip, mLinkSoundfont, sizeof(strip)-1)) return &mLinkSoundfont[sizeof(strip)-1]; } else { printf("ERROR: GetMidiLinkSoundfont : Unable to open --> '%s'\n", fileName); sprintf(mLinkSoundfont, "linux/soundfonts"); } return mLinkSoundfont; } uint32_t ValidateUARTbaud(int mode, uint32_t baud) { const uint32_t *bauds = GetUARTbauds(mode); int idx = 0; for (int i = 0; i < 13; i++) { if (!bauds[i]) break; if (bauds[i] == baud) { idx = i; break; } } if (mode == 3) midi_speed_idx = idx; else if (mode > 3) mlink_speed_idx = idx; else uart_speed_idx = idx; return bauds[idx]; } int GetMidiLinkMode() { struct stat filestat; if (!stat("/tmp/ML_FSYNTH", &filestat)) return 0; if (!stat("/tmp/ML_MUNT", &filestat)) return 1; if (!stat("/tmp/ML_USBMIDI", &filestat)) return 2; if (!stat("/tmp/ML_UDP", &filestat)) return 3; if (!stat("/tmp/ML_TCP", &filestat)) return 4; if (!stat("/tmp/ML_UDP_ALT", &filestat)) return 5; if (!stat("/tmp/ML_USBSER", &filestat)) return 6; return 0; } void SetMidiLinkMode(int mode) { remove("/tmp/ML_FSYNTH"); remove("/tmp/ML_MUNT"); remove("/tmp/ML_UDP"); remove("/tmp/ML_USBMIDI"); remove("/tmp/ML_UDP_ALT"); remove("/tmp/ML_TCP_ALT"); remove("/tmp/ML_SERMIDI"); remove("/tmp/ML_USBSER"); remove("/tmp/ML_TCP"); struct stat filestat; if (mode == 6 && stat("/dev/ttyUSB0", &filestat)) mode = 4; switch (mode) { case 0: MakeFile("/tmp/ML_FSYNTH", ""); break; case 1: MakeFile("/tmp/ML_MUNT", ""); break; case 2: MakeFile("/tmp/ML_USBMIDI", ""); break; case 3: MakeFile("/tmp/ML_UDP", ""); break; case 4: MakeFile("/tmp/ML_TCP", ""); break; case 5: MakeFile("/tmp/ML_UDP_ALT", ""); break; case 6: MakeFile("/tmp/ML_USBSER", ""); break; } } void ResetUART() { if (uart_mode) { int mode = GetUARTMode(); if (mode != 0) { SetUARTMode(0); SetUARTMode(mode); } } } uint16_t sdram_sz(int sz) { int res = 0; void* buf = shmem_map(0x1FFFF000, 0x1000); if (!buf) return 0; volatile uint8_t* par = (volatile uint8_t*)buf; par += 0xF00; if (sz >= 0) { *par++ = 0x12; *par++ = 0x57; *par++ = (uint8_t)(sz >> 8); *par++ = (uint8_t)sz; } else { if ((par[0] == 0x12) && (par[1] == 0x57)) { res = 0x8000 | (par[2] << 8) | par[3]; if(res & 0x4000) printf("*** Debug phase: %d\n", (res & 0x100) ? (res & 0xFF) : -(res & 0xFF)); else printf("*** Found SDRAM config: %d\n", res & 7); } else if(!is_menu()) { printf("*** SDRAM config not found\n"); } } shmem_unmap(buf, 0x1000); return res; } uint16_t altcfg(int alt) { int res = 0; void* buf = shmem_map(0x1FFFF000, 0x1000); if (!buf) return 0; volatile uint8_t* par = (volatile uint8_t*)buf; par += 0xF04; if (alt >= 0) { *par++ = 0x34; *par++ = 0x99; *par++ = 0xBA; *par++ = (uint8_t)alt; printf("** altcfg(%d)\n", alt); } else { if ((par[0] == 0x34) && (par[1] == 0x99) && (par[2] == 0xBA)) { res = par[3]; printf("** altcfg: got cfg %d\n", res); } else { printf("** altcfg: no cfg\n"); } } shmem_unmap(buf, 0x1000); return res; } int user_io_is_dualsdr() { return dual_sdr; } int user_io_get_width() { return fio_size; } void user_io_init(const char *path, const char *xml) { char *name; static char mainpath[512]; core_name[0] = 0; disable_osd = 0; // Clean up old game ID when loading a new core unlink("/tmp/GAMEID"); // we need to set the directory to where the XML file (MRA) is // not the RBF. The RBF will be in arcade, which the user shouldn't // browse strcpy(core_path, xml ? xml : path); strcpy(rbf_path, path); memset(sd_image, 0, sizeof(sd_image)); core_type = (fpga_core_id() & 0xFF); fio_size = fpga_get_fio_size(); io_ver = fpga_get_io_version(); printf("I/O Board type: %s\n", fpga_get_io_type() ? "digital" : "analogue"); if (core_type == CORE_TYPE_8BIT2) { dual_sdr = 1; core_type = CORE_TYPE_8BIT; } if ((core_type != CORE_TYPE_8BIT) && (core_type != CORE_TYPE_SHARPMZ)) { core_type = CORE_TYPE_UNKNOWN; fio_size = 0; io_ver = 0; } OsdSetSize(8); if (xml) { if (isXmlName(xml) == 1) is_arcade_type = 1; arcade_pre_parse(xml); } if (core_type == CORE_TYPE_8BIT) { printf("Identified 8BIT core"); spi_uio_cmd16(UIO_SET_MEMSZ, sdram_sz(-1)); // send a reset user_io_status_set("[0]", 1); } else if (core_type == CORE_TYPE_SHARPMZ) { user_io_set_core_name("sharpmz"); } user_io_read_confstr(); user_io_read_core_name(); if ((fpga_get_buttons() & BUTTON_OSD) && is_menu()) { altcfg(0); SelectINI(); } cfg_parse(); cfg_print(); while (cfg.waitmount[0] && !is_menu()) { printf("> > > wait for %s mount < < <\n", cfg.waitmount); static char str[256]; snprintf(str, sizeof(str), "exit $(mount | grep \"%s\" | wc -c)", cfg.waitmount); int ret = system(str); if (!(ret & 0xFF) && ret) break; sleep(1); } const char *main = getFullPath(cfg.main); if (strcasecmp(main, getappname()) && FileExists(main)) { printf("Current exec is %s, core requires exec %s\n", getappname(), main); app_restart(path, xml, main); } uint8_t hotswap[4] = {}; ide_reset(hotswap); parse_config(); if (!xml && defmra[0] && FileExists(defmra)) { // attn: FC option won't use name from defmra! // attn: cfg is parsed before defmra, no defmra-name specifics possible in INI! xml = (const char*)defmra; strcpy(core_path, xml); is_arcade_type = 1; arcade_pre_parse(xml); user_io_read_core_name(); printf("Using default MRA: %s\n", xml); } if (cfg.log_file_entry) MakeFile("/tmp/STARTPATH", core_path); if (cfg.bootcore[0] != '\0') { bootcore_init(xml ? xml : path); } video_init(); if (strlen(cfg.font)) LoadFont(cfg.font); load_volume(); user_io_send_buttons(1); if (xml && isXmlName(xml) == 2) mgl_parse(xml); switch (core_type) { case CORE_TYPE_UNKNOWN: printf("Unable to identify core (%x)!\n", core_type); break; case CORE_TYPE_SHARPMZ: printf("Identified Sharp MZ Series core"); user_io_set_core_name("sharpmz"); sharpmz_init(); parse_buttons(); break; case CORE_TYPE_8BIT: // try to load config name = user_io_create_config_name(1); if (strlen(name) > 0) { if (!is_st() && !is_minimig()) { printf("Loading config %s\n", name); memset(cur_status, 0, sizeof(cur_status)); if (FileLoadConfig(name, cur_status, sizeof(cur_status))) { printf("Found config:\n"); hexdump(cur_status, sizeof(cur_status)); } else { memset(cur_status, 0, sizeof(cur_status)); } user_io_status_set("[0]", 1); } name = user_io_create_config_name(); if (is_st()) { tos_config_load(0); tos_upload(NULL); } else if (is_menu()) { user_io_status_set("[4]", (cfg.menu_pal) ? 1 : 0); if (cfg.fb_terminal) video_menu_bg(user_io_status_get("[3:1]")); else user_io_status_set("[3:1]", 0); } else { if (xml && isXmlName(xml) == 1) { arcade_send_rom(xml); if (ss_base) process_ss(xml); } else if (is_minimig()) { printf("Identified Minimig V2 core"); BootInit(); } else if (is_x86() || is_pcxt()) { x86_config_load(); x86_init(); } else if (is_archie()) { printf("Identified Archimedes core"); archie_init(); } else { const char *home = HomeDir(); if (is_uneon()) x86_ide_set(); if (!strlen(path) || !user_io_file_tx(path, 0, 0, 0, 1)) { if (!is_cpc()) { // check for multipart rom for (char i = (boot0_loaded ? 1 : 0); i < 4; i++) { sprintf(mainpath, "%s/boot%d.rom", home, i); user_io_file_tx(mainpath, i << 6); } } // legacy style of rom if (!boot0_loaded) { sprintf(mainpath, "%s/boot.rom", home); if (!user_io_file_tx(mainpath)) { strcpy(name + strlen(name) - 3, "ROM"); sprintf(mainpath, "%s/%s", get_rbf_dir(), name); if (!get_rbf_dir()[0] || !user_io_file_tx(mainpath)) { if (!user_io_file_tx(name)) { sprintf(mainpath, "bootrom/%s", name); user_io_file_tx(mainpath); } } } } // cheats for boot file if (user_io_use_cheats()) cheats_init("", user_io_get_file_crc()); } if (is_cpc()) { for (int m = 0; m < 3; m++) { const char *model = !m ? "" : (m == 1) ? "0" : "1"; sprintf(mainpath, "%s/boot%s.eZZ", home, model); user_io_file_tx(mainpath, 0x40 * (m + 1), 0, 1); sprintf(mainpath, "%s/boot%s.eZ0", home, model); user_io_file_tx(mainpath, 0x40 * (m + 1), 0, 1); for (int i = 0; i < 256; i++) { sprintf(mainpath, "%s/boot%s.e%02X", home, model, i); user_io_file_tx(mainpath, 0x40 * (m + 1), 0, 1); } } } // check if vhd present for (char i = (boot0_mounted ? 1 : 0); i < 4; i++) { sprintf(mainpath, "%s/boot%d.vhd", home, i); if (FileExists(mainpath)) { user_io_set_index(i << 6); user_io_file_mount(mainpath, i); } } if (!boot0_mounted) { sprintf(mainpath, "%s/boot.vhd", home); if (FileExists(mainpath)) { user_io_set_index(0); user_io_file_mount(mainpath, 0); } else { strcpy(name + strlen(name) - 3, "VHD"); sprintf(mainpath, "%s/%s", get_rbf_dir(), name); if (FileExists(mainpath)) { user_io_set_index(0); user_io_file_mount(mainpath, 0); } else if (FileExists(name)) { user_io_set_index(0); user_io_file_mount(name, 0); } } } } } parse_buttons(); } send_rtc(3); // release reset if (!is_minimig() && !is_st()) user_io_status_set("[0]", 0); if (xml && isXmlName(xml) == 1) arcade_check_error(); char cfg_errs[512]; if (cfg_check_errors(cfg_errs, sizeof(cfg_errs))) { Info(cfg_errs, 5000); sleep(5); } break; } OsdRotation((cfg.osd_rotate == 1) ? 3 : (cfg.osd_rotate == 2) ? 1 : 0); uart_mode = spi_uio_cmd16(UIO_GETUARTFLG, 0) || uart_speeds[0]; uint32_t mode = 0; if (uart_mode) { if (!uart_speeds[0]) { uart_speeds[0] = is_st() ? 19200 : 115200; sprintf(uart_speed_labels[0], "%d", uart_speeds[0]); if (!midi_speeds[0]) { midi_speeds[0] = 31250; sprintf(midi_speed_labels[0], "%d", midi_speeds[0]); } } sprintf(mainpath, "uartmode.%s", user_io_get_core_name()); FileLoadConfig(mainpath, &mode, 4); uint32_t speeds[3] = {}; sprintf(mainpath, "uartspeed.%s", user_io_get_core_name()); FileLoadConfig(mainpath, speeds, sizeof(speeds)); ValidateUARTbaud(1, speeds[0]); ValidateUARTbaud(3, speeds[1]); ValidateUARTbaud(4, speeds[2] ? speeds[2] : uart_speeds[0]); printf("UART bauds: %d/%d/%d\n", GetUARTbaud(1), GetUARTbaud(3), GetUARTbaud(4)); } SetUARTMode(0); int midilink = (mode >> 8) & 0xFF; int uartmode = mode & 0xFF; if (uartmode == 4 && (midilink < 4 || midilink>6)) midilink = 4; if (uartmode == 3 && midilink > 3) midilink = 0; if (uartmode < 3 || uartmode > 4) midilink = 0; SetMidiLinkMode(midilink); SetUARTMode(uartmode); if (!mgl_get()->count || is_menu() || is_st() || is_archie() || user_io_core_type() == CORE_TYPE_SHARPMZ) { mgl_get()->done = 1; } else { mgl_get()->timer = GetTimer(mgl_get()->item[0].delay * 1000); } } static int joyswap = 0; void user_io_set_joyswap(int swap) { joyswap = swap; } int user_io_get_joyswap() { return joyswap; } void user_io_l_analog_joystick(unsigned char joystick, char valueX, char valueY) { uint8_t joy = (joystick > 1 || !joyswap) ? joystick : (joystick >= 15) ? (joystick ^ 16) : (joystick ^ 1); if (core_type == CORE_TYPE_8BIT) { spi_uio_cmd8_cont(UIO_ASTICK, joy); if(io_ver) spi_w((valueY << 8) | (uint8_t)(valueX)); else { spi8(valueX); spi8(valueY); } DisableIO(); } } void user_io_r_analog_joystick(unsigned char joystick, char valueX, char valueY) { uint8_t joy = (joystick > 1 || !joyswap) ? joystick : (joystick ^ 1); if (core_type == CORE_TYPE_8BIT) { spi_uio_cmd8_cont(UIO_ASTICK_2, joy); if (io_ver) spi_w((valueY << 8) | (uint8_t)(valueX)); else { spi8(valueX); spi8(valueY); } DisableIO(); } } void user_io_digital_joystick(unsigned char joystick, uint64_t map, int newdir) { uint8_t joy = (joystick>1 || !joyswap) ? joystick : joystick ^ 1; static int use32 = 0; // primary button mappings are in 31:0, alternate mappings are in 64:32. // take the logical OR to ensure a held button isn't overriden // by other mapping being pressed uint32_t bitmask = (uint32_t)(map) | (uint32_t)(map >> 32); use32 |= bitmask >> 16; spi_uio_cmd_cont((joy < 2) ? (UIO_JOYSTICK0 + joy) : (UIO_JOYSTICK2 + joy - 2)); spi_w(bitmask); if(use32) spi_w(bitmask >> 16); DisableIO(); if (!is_minimig() && joy_transl == 1 && newdir) { user_io_l_analog_joystick(joystick, (bitmask & 2) ? 128 : (bitmask & 1) ? 127 : 0, (bitmask & 8) ? 128 : (bitmask & 4) ? 127 : 0); } } static uint8_t CSD[16] = { 0xf1, 0x40, 0x40, 0x0a, 0x80, 0x7f, 0xe5, 0xe9, 0x00, 0x00, 0x59, 0x5b, 0x32, 0x00, 0x0e, 0x40 }; static uint8_t CID[16] = { 0x3e, 0x00, 0x00, 0x34, 0x38, 0x32, 0x44, 0x00, 0x00, 0x73, 0x2f, 0x6f, 0x93, 0x00, 0xc7, 0xcd }; // set SD card info in FPGA (CSD, CID) void user_io_sd_set_config(void) { CSD[6] = (uint8_t)(sd_image[0].size >> 9); CSD[7] = (uint8_t)(sd_image[0].size >> 17); CSD[8] = (uint8_t)(sd_image[0].size >> 25); // forward it to the FPGA spi_uio_cmd_cont(UIO_SET_SDCONF); spi_write(CID, sizeof(CID), fio_size); spi_write(CSD, sizeof(CSD), fio_size); spi8(1); //SDHC permanently DisableIO(); /* printf("SD CID\n"); hexdump(CID, sizeof(CID), 0); printf("SD CSD\n"); hexdump(CSD, sizeof(CSD), 0); */ } // read 8 bit keyboard LEDs status from FPGA uint16_t user_io_kbdled_get_status(void) { uint16_t c; spi_uio_cmd_cont(UIO_GET_KBD_LED); c = spi_w(0); DisableIO(); return c; } uint8_t user_io_ps2_ctl(uint8_t *kbd_ctl, uint8_t *mouse_ctl) { uint16_t c; uint8_t res = 0; spi_uio_cmd_cont(UIO_PS2_CTL); c = spi_w(0); if (kbd_ctl) *kbd_ctl = (uint8_t)c; res |= ((c >> 8) & 1); c = spi_w(0); if (mouse_ctl) *mouse_ctl = (uint8_t)c; res |= ((c >> 7) & 2); DisableIO(); return res; } // 16 byte fifo for amiga key codes to limit max key rate sent into the core #define KBD_FIFO_SIZE 16 // must be power of 2 static unsigned short kbd_fifo[KBD_FIFO_SIZE]; static unsigned char kbd_fifo_r = 0, kbd_fifo_w = 0; static long kbd_timer = 0; static void kbd_fifo_minimig_send(uint32_t code) { spi_uio_cmd8((code&OSD) ? UIO_KBD_OSD : UIO_KEYBOARD, code & 0xff); kbd_timer = GetTimer(10); // next key after 10ms earliest } static void kbd_fifo_enqueue(unsigned short code) { // if fifo full just drop the value. This should never happen if (((kbd_fifo_w + 1)&(KBD_FIFO_SIZE - 1)) == kbd_fifo_r) return; // store in queue kbd_fifo[kbd_fifo_w] = code; kbd_fifo_w = (kbd_fifo_w + 1)&(KBD_FIFO_SIZE - 1); } // send pending bytes if timer has run up static void kbd_fifo_poll() { // timer enabled and runnig? if (kbd_timer && !CheckTimer(kbd_timer)) return; kbd_timer = 0; // timer == 0 means timer is not running anymore if (kbd_fifo_w == kbd_fifo_r) return; kbd_fifo_minimig_send(kbd_fifo[kbd_fifo_r]); kbd_fifo_r = (kbd_fifo_r + 1)&(KBD_FIFO_SIZE - 1); } int process_ss(const char *rom_name, int enable) { static char ss_name[1024] = {}; static char *ss_sufx = 0; static uint32_t ss_cnt[4] = {}; static void *base[4] = {}; static int enabled = 0; if (!ss_base) return 0; if (rom_name) { enabled = enable; if (!enabled) return 0; uint32_t len = ss_size; uint32_t map_addr = ss_base; fileTYPE f = {}; for (int i = 0; i < 4; i++) { if (!base[i]) base[i] = shmem_map(map_addr, len); if (!base[i]) { printf("Unable to mmap (0x%X, %d)!\n", map_addr, len); } else { ss_cnt[i] = 0xFFFFFFFF; memset(base[i], 0, len); if (!i) { FileGenerateSavestatePath(rom_name, ss_name, 1); printf("Base SavestatePath=%s\n", ss_name); if (!FileExists(ss_name)) FileGenerateSavestatePath(rom_name, ss_name, 0); } else { FileGenerateSavestatePath(rom_name, ss_name, i + 1); } if (FileExists(ss_name)) { if (!FileOpen(&f, ss_name)) { printf("Unable to open file: %s\n", ss_name); } else { int ret = FileReadAdv(&f, base[i], len); FileClose(&f); printf("process_ss: read %d bytes from file: %s\n", ret, ss_name); } } *(uint32_t*)(base[i]) = 0xFFFFFFFF; } map_addr += len; } FileGenerateSavestatePath(rom_name, ss_name, 1); ss_sufx = ss_name + strlen(ss_name) - 4; return 1; } if (!enabled) return 0; static unsigned long ss_timer = 0; if (ss_timer && !CheckTimer(ss_timer)) return 0; ss_timer = GetTimer(1000); fileTYPE f = {}; for (int i = 0; i < 4; i++) { if (base[i]) { uint32_t curcnt = ((uint32_t*)(base[i]))[0]; uint32_t size = ((uint32_t*)(base[i]))[1]; if (curcnt != ss_cnt[i]) { ss_cnt[i] = curcnt; if (size) size = (size + 2) * 4; if (size > 0 && size <= ss_size) { MenuHide(); Info("Saving the state", 500); *ss_sufx = i + '1'; if (FileOpenEx(&f, ss_name, O_CREAT | O_TRUNC | O_RDWR | O_SYNC)) { int ret = FileWriteAdv(&f, base[i], size); FileClose(&f); printf("Wrote %d bytes to file: %s\n", ret, ss_name); } else { printf("Unable to create file: %s\n", ss_name); } } } } } return 1; } void user_io_set_index(unsigned char index) { EnableFpga(); spi8(FIO_FILE_INDEX); spi8(index); DisableFpga(); } void user_io_set_aindex(uint16_t index) { EnableFpga(); spi8(FIO_FILE_INDEX); spi_w(index); DisableFpga(); } void user_io_set_download(unsigned char enable, int addr) { EnableFpga(); spi8(FIO_FILE_TX); spi8(enable ? 0xff : 0); if (enable && addr) { spi_w(addr); spi_w(addr >> 16); } DisableFpga(); } void user_io_file_tx_data(const uint8_t *addr, uint32_t len) { EnableFpga(); spi8(FIO_FILE_TX_DAT); spi_write(addr, len, fio_size); DisableFpga(); } void user_io_set_upload(unsigned char enable, int addr) { EnableFpga(); spi8(FIO_FILE_TX); spi8(enable ? 0xaa : 0); if (enable && addr) { spi_w(addr); spi_w(addr >> 16); } DisableFpga(); } void user_io_file_rx_data(uint8_t *addr, uint32_t len) { EnableFpga(); spi8(FIO_FILE_TX_DAT); spi_read(addr, len, fio_size); DisableFpga(); } void user_io_file_info(const char *ext) { EnableFpga(); spi8(FIO_FILE_INFO); char c1 = *ext ? toupper(*ext++) : 0; char c2 = *ext ? toupper(*ext++) : 0; char c3 = *ext ? toupper(*ext++) : 0; char c4 = *ext ? toupper(*ext++) : 0; spi_w(c1 << 8 | c2); spi_w(c3 << 8 | c4); DisableFpga(); } int user_io_file_mount(const char *name, unsigned char index, char pre, int pre_size) { int writable = 0; int ret = 0; int len = strlen(name); int img_type = 0; // disk image type (for C128 core): bit 0=dual sided, 1=raw GCR supported, 2=raw MFM supported, 3=high density sd_image_cangrow[index] = (pre != 0); sd_type[index] = SD_TYPE_DEFAULT ; if (len) { if (!ret) { if (x2trd_ext_supp(name)) { ret = x2trd(name, sd_image + index); } else if (len > 4 && !strcasecmp(name + len - 4, ".t64")) { writable = 0; ret = c64_openT64(name, sd_image + index); if (ret) { img_type = c64_openGCR(name, sd_image + index, index); ret = img_type < 0 ? 0 : 1; sd_type[index] = SD_TYPE_C64; if (!ret) FileClose(&sd_image[index]); if (ret && is_c128()) { printf("Disk image type: %d\n", img_type); user_io_set_aindex(img_type << 6 | index); } } } else { writable = FileCanWrite(name); ret = FileOpenEx(&sd_image[index], name, writable ? (O_RDWR | O_SYNC) : O_RDONLY); if (ret && len > 4) { if (!strcasecmp(name + len - 4, ".d64") || !strcasecmp(name + len - 4, ".g64") || !strcasecmp(name + len - 4, ".d71") || !strcasecmp(name + len - 4, ".g71")) { img_type = c64_openGCR(name, sd_image + index, index); ret = img_type < 0 ? 0 : 1; sd_type[index] = SD_TYPE_C64; if(!ret) FileClose(&sd_image[index]); } else if (!strcasecmp(name + len - 4, ".d81")) { img_type = G64_SUPPORT_HD | G64_SUPPORT_DS; } else if (!strcasecmp(name + len - 4, ".dsk") && ((!strcasecmp(user_io_get_core_name(), "apple-ii") || (!strcasecmp(user_io_get_core_name(), "TK2000") )) )) { printf("FOUND A2 DSK type\n"); sd_type[index] = SD_TYPE_A2; } } if (ret && is_c128()) { printf("Disk image type: %d\n", img_type); user_io_set_aindex(img_type << 6 | index); } } } if (!ret) { printf("Failed to open file %s\n", name); } } else { FileClose(&sd_image[index]); c64_closeGCR(index); } buffer_lba[index] = -1; if (!index || is_cdi() || (is_saturn() && index==1)) use_save = pre; if (!ret) { sd_image[index].size = 0; if (pre) { writable = 1; printf("Will be created upon write\n"); } else { writable = 0; printf("Eject image from %d slot\n", index); } } else { printf("Mount %s as %s on %d slot\n", name, writable ? "read-write" : "read-only", index); } user_io_sd_set_config(); // send mounted image size first then notify about mounting EnableIO(); spi8(UIO_SET_SDINFO); __off64_t size = sd_image[index].size; if (!ret && pre) { sd_image[index].type = 2; strcpy(sd_image[index].path, name); size = pre_size; } if (io_ver) { spi32_w(size); spi32_w(size >> 32); } else { spi32_b(size); spi32_b(size >> 32); } DisableIO(); // notify core of possible sd image change spi_uio_cmd8(UIO_SET_SDSTAT, (1 << index) | (writable ? 0 : 0x80)); return ret ? 1 : 0; } void user_io_bufferinvalidate(unsigned char index) { buffer_lba[index] = -1; } static unsigned char col_attr[1025]; static int col_parse(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, const int n, SAX_Data* sd) { (void)sd; static int in_border = 0; static int in_color = 0; static int in_bright = 0; static int in_entry = 0; static int in_line = 0; static int in_paper = 0; static int in_ink = 0; static int end = 0; static int start = 0; static int line = 0; static char tmp[8]; switch (evt) { case XML_EVENT_START_NODE: if (!strcasecmp(node->tag, "colourisation")) { in_border = 0; in_color = 0; in_bright = 0; in_entry = 0; in_line = 0; in_paper = 0; in_ink = 0; } if (!strcasecmp(node->tag, "border")) in_border = 1; if (!strcasecmp(node->tag, "colour")) in_color = 1; if (!strcasecmp(node->tag, "bright")) in_bright = 1; if (!strcasecmp(node->tag, "entry")) { int ncode = -1; int ncnt = -1; for (int i = 0; i < node->n_attributes; i++) { if (!strcasecmp(node->attributes[i].name, "code")) ncode = atoi(node->attributes[i].value); if (!strcasecmp(node->attributes[i].name, "quantity")) ncnt = atoi(node->attributes[i].value); } in_entry = 0; if (ncode >= 0 && ncode <= 127) { start = ncode; if (ncnt < 1) ncnt = 1; end = start + ncnt; if (end > 128) end = 128; memset(tmp, 0, sizeof(tmp)); in_entry = 1; } } if (!strcasecmp(node->tag, "line")) { int nline = -1; for (int i = 0; i < node->n_attributes; i++) { if (!strcasecmp(node->attributes[i].name, "index")) nline = atoi(node->attributes[i].value); } in_line = 0; if (nline >= 0 && nline <= 7) { line = nline; if (in_entry) tmp[line] = 0; in_line = 1; } } if (!strcasecmp(node->tag, "paper")) in_paper = 1; if (!strcasecmp(node->tag, "ink")) in_ink = 1; break; case XML_EVENT_END_NODE: if (!strcasecmp(node->tag, "border")) in_border = 0; if (!strcasecmp(node->tag, "colour")) in_color = 0; if (!strcasecmp(node->tag, "bright")) in_bright = 0; if (!strcasecmp(node->tag, "line")) in_line = 0; if (!strcasecmp(node->tag, "paper")) in_paper = 0; if (!strcasecmp(node->tag, "ink")) in_ink = 0; if (!strcasecmp(node->tag, "entry")) { if (in_entry) { for (int i = start; i < end; i++) memcpy(&col_attr[i * 8], tmp, 8); } in_entry = 0; } break; case XML_EVENT_TEXT: if (in_border && in_color) col_attr[1024] = (char)((col_attr[1024] & 8) | (atoi(text) & 7)); if (in_border && in_bright) col_attr[1024] = (char)((col_attr[1024] & 7) | ((atoi(text) & 1) << 3)); if (in_entry && in_line && in_ink && in_color) tmp[line] = (char)((tmp[line] & 0xF8) | (atoi(text) & 7)); if (in_entry && in_line && in_ink && in_bright) tmp[line] = (char)((tmp[line] & 0xF7) | ((atoi(text) & 1) << 3)); if (in_entry && in_line && in_paper && in_color) tmp[line] = (char)((tmp[line] & 0x8F) | ((atoi(text) & 7) << 4)); if (in_entry && in_line && in_paper && in_bright) tmp[line] = (char)((tmp[line] & 0x7F) | ((atoi(text) & 1) << 7)); break; case XML_EVENT_ERROR: printf("XML parse: %s: ERROR %d\n", text, n); break; default: break; } return true; } static const unsigned char defchars[512] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x78, 0x20, 0x20, 0x7E, 0x00, 0x00, 0x08, 0x3E, 0x28, 0x3E, 0x0A, 0x3E, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x3C, 0x42, 0x04, 0x08, 0x00, 0x08, 0x00, 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x00, 0x00, 0x20, 0x10, 0x10, 0x10, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10, 0x08, 0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, 0x00, 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x3C, 0x46, 0x4A, 0x52, 0x62, 0x3C, 0x00, 0x00, 0x18, 0x28, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, 0x3C, 0x42, 0x02, 0x3C, 0x40, 0x7E, 0x00, 0x00, 0x3C, 0x42, 0x0C, 0x02, 0x42, 0x3C, 0x00, 0x00, 0x08, 0x18, 0x28, 0x48, 0x7E, 0x08, 0x00, 0x00, 0x7E, 0x40, 0x7C, 0x02, 0x42, 0x3C, 0x00, 0x00, 0x3C, 0x40, 0x7C, 0x42, 0x42, 0x3C, 0x00, 0x00, 0x7E, 0x02, 0x04, 0x08, 0x10, 0x10, 0x00, 0x00, 0x3C, 0x42, 0x3C, 0x42, 0x42, 0x3C, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x3E, 0x02, 0x3C, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x00, 0x00, 0x7C, 0x42, 0x7C, 0x42, 0x42, 0x7C, 0x00, 0x00, 0x3C, 0x42, 0x40, 0x40, 0x42, 0x3C, 0x00, 0x00, 0x78, 0x44, 0x42, 0x42, 0x44, 0x78, 0x00, 0x00, 0x7E, 0x40, 0x7C, 0x40, 0x40, 0x7E, 0x00, 0x00, 0x7E, 0x40, 0x7C, 0x40, 0x40, 0x40, 0x00, 0x00, 0x3C, 0x42, 0x40, 0x4E, 0x42, 0x3C, 0x00, 0x00, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x00, 0x00, 0x3E, 0x08, 0x08, 0x08, 0x08, 0x3E, 0x00, 0x00, 0x02, 0x02, 0x02, 0x42, 0x42, 0x3C, 0x00, 0x00, 0x44, 0x48, 0x70, 0x48, 0x44, 0x42, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7E, 0x00, 0x00, 0x42, 0x66, 0x5A, 0x42, 0x42, 0x42, 0x00, 0x00, 0x42, 0x62, 0x52, 0x4A, 0x46, 0x42, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x00, 0x7C, 0x42, 0x42, 0x7C, 0x40, 0x40, 0x00, 0x00, 0x3C, 0x42, 0x42, 0x52, 0x4A, 0x3C, 0x00, 0x00, 0x7C, 0x42, 0x42, 0x7C, 0x44, 0x42, 0x00, 0x00, 0x3C, 0x40, 0x3C, 0x02, 0x42, 0x3C, 0x00, 0x00, 0xFE, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x5A, 0x24, 0x00, 0x00, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x00, 0x00, 0x82, 0x44, 0x28, 0x10, 0x10, 0x10, 0x00, 0x00, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x00 }; static int chr_parse(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, const int n, SAX_Data* sd) { (void)sd; static int in_entry = 0; static int in_line = 0; static int code = 0; static int line = 0; switch (evt) { case XML_EVENT_START_NODE: if (!strcasecmp(node->tag, "definition")) { in_entry = 0; in_line = 0; code = 0; line = 0; } if (!strcasecmp(node->tag, "entry")) { int ncode = -1; for (int i = 0; i < node->n_attributes; i++) { if (!strcasecmp(node->attributes[i].name, "code")) ncode = atoi(node->attributes[i].value); } in_entry = 0; if (ncode >= 0 && ncode <= 63) { code = ncode; in_entry = 1; } if (ncode >= 128 && ncode <= 191) { code = ncode - 64; in_entry = 1; } } if (!strcasecmp(node->tag, "line")) { int nline = -1; for (int i = 0; i < node->n_attributes; i++) { if (!strcasecmp(node->attributes[i].name, "index")) nline = atoi(node->attributes[i].value); } in_line = 0; if (nline >= 0 && nline <= 7) { line = nline; in_line = 1; } } break; case XML_EVENT_END_NODE: if (!strcasecmp(node->tag, "line")) in_line = 0; if (!strcasecmp(node->tag, "entry")) in_entry = 0; break; case XML_EVENT_TEXT: if (in_entry && in_line) { unsigned char tmp = 0; if (strlen(text) >= 8) { for (int i = 0; i < 8; i++) tmp = (tmp << 1) | ((text[i] == '1') ? 1 : 0); if (code >= 64) tmp = ~tmp; } col_attr[code * 8 + line] = tmp; in_line = 0; } break; case XML_EVENT_ERROR: printf("XML parse: %s: ERROR %d\n", text, n); break; default: break; } return true; } static void send_pcolchr(const char* name, unsigned char index, int type) { static char full_path[1024]; sprintf(full_path, "%s/%s", getRootDir(), name); char *p = strrchr(full_path, '.'); if (!p) p = full_path + strlen(full_path); strcpy(p, type ? ".chr" : ".col"); if (type) { memcpy(col_attr, defchars, sizeof(defchars)); memcpy(col_attr+sizeof(defchars), defchars, sizeof(defchars)); } else memset(col_attr, 0, sizeof(col_attr)); SAX_Callbacks sax; SAX_Callbacks_init(&sax); sax.all_event = type ? chr_parse : col_parse; if (XMLDoc_parse_file_SAX(full_path, &sax, 0)) { printf("Send additional file %s\n", full_path); //hexdump(col_attr, sizeof(col_attr)); user_io_set_index(index); user_io_set_download(1); user_io_file_tx_data(col_attr, type ? 1024 : 1025); user_io_set_download(0); } } static uint32_t file_crc; uint32_t user_io_get_file_crc() { return file_crc; } void user_io_write_gameid(const char *filename, uint32_t crc32_val, const char *serial) { if (!cfg.log_file_entry) return; // Extract basename from filename const char *fname = strrchr(filename, '/'); if (!fname) fname = filename; else fname++; // Skip BIOS files if (strncasecmp(fname, "boot", 4) == 0 || strcasestr(fname, "bios")) { return; } FILE *f = fopen("/tmp/GAMEID", "w"); if (!f) { printf("Failed to write /tmp/GAMEID\n"); return; } int wrote_something = 0; printf("Game ID: %s", fname); if (crc32_val) { fprintf(f, "CRC32: %08X\n", crc32_val); printf(" [CRC32: %08X]", crc32_val); wrote_something = 1; } if (serial && serial[0]) { fprintf(f, "Serial: %s\n", serial); printf(" [%s]", serial); wrote_something = 1; } // Ensure we always write something to the file if (!wrote_something) { fprintf(f, "# No game ID available\n"); } printf("\n"); fflush(f); fclose(f); } int user_io_use_cheats() { return use_cheats; } static void check_status_change() { static u_int8_t last_status_change = 0; char stchg = spi_uio_cmd_cont(UIO_GET_STATUS); if ((stchg & 0xF0) == 0xA0 && last_status_change != (stchg & 0xF)) { last_status_change = (stchg & 0xF); for (uint i = 0; i < sizeof(cur_status); i += 2) { uint16_t x = spi_w(0); cur_status[i] = (char)x; cur_status[i + 1] = (char)(x >> 8); } DisableIO(); user_io_status_set("[0]", 0); } else { DisableIO(); } } static void show_core_info(int info_n) { int i = 2; user_io_read_confstr(); while (1) { char *p = user_io_get_confstr(i++); if (!p) break; if (p[0] == 'I') { static char str[256]; substrcpy(str, p, info_n); if (strlen(str)) Info(str); break; } } } int user_io_file_tx_a(const char* name, uint16_t index) { fileTYPE f = {}; static uint8_t buf[4096]; if (!FileOpen(&f, name, 1)) return 0; unsigned long bytes2send = f.size; /* transmit the entire file using one transfer */ printf("Addon file %s with %lu bytes to send for index %04X\n", name, bytes2send, index); // set index byte (0=bios rom, 1-n=OSD entry index) user_io_set_aindex(index); // prepare transmission of new file user_io_set_download(1); int use_progress = 1; int size = bytes2send; if (use_progress) ProgressMessage(0, 0, 0, 0); while (bytes2send) { uint32_t chunk = (bytes2send > sizeof(buf)) ? sizeof(buf) : bytes2send; FileReadAdv(&f, buf, chunk); user_io_file_tx_data(buf, chunk); if (use_progress) ProgressMessage("Loading", f.name, size - bytes2send, size); bytes2send -= chunk; } // check if core requests some change while downloading check_status_change(); printf("Done.\n"); FileClose(&f); // signal end of transmission user_io_set_download(0); ProgressMessage(0, 0, 0, 0); return 1; } int user_io_file_tx(const char* name, unsigned char index, char opensave, char mute, char composite, uint32_t load_addr) { fileTYPE f = {}; static uint8_t buf[4096]; if (!FileOpen(&f, name, mute)) return 0; uint32_t bytes2send = f.size; if (composite) { if (!FileReadSec(&f, buf)) return 0; if (memcmp(buf, "MiSTer", 6)) return 0; uint32_t off = 16 + *(uint32_t*)(((uint8_t*)buf) + 12); bytes2send -= off; FileSeek(&f, off, SEEK_SET); } /* transmit the entire file using one transfer */ printf("Selected file %s with %u bytes to send for index %d.%d\n", name, bytes2send, index & 0x3F, index >> 6); if(load_addr) printf("Load to address 0x%X\n", load_addr); // set index byte (0=bios rom, 1-n=OSD entry index) user_io_set_index(index); int len = strlen(f.name); char *p = strrchr(f.name, '.'); if (p == 0) { // In case a '.' is not found, send all `NUL` characters. p = f.name + len; } user_io_file_info(p); // prepare transmission of new file user_io_set_download(1, load_addr ? bytes2send : 0); int dosend = 1; int snes_file = SNES_FILE_RAW; if (is_snes() && bytes2send && !load_addr) { const char *ext = strrchr(f.name, '.'); if (ext) { if (index == 0 || !strcasecmp(ext, ".SMC") || !strcasecmp(ext, ".SFC") || !strcasecmp(ext, ".BIN")) { snes_file = SNES_FILE_ROM; } else if (!strcasecmp(ext, ".BS")) { snes_file = SNES_FILE_BS; } else if (!strcasecmp(ext, ".SPC")) { snes_file = SNES_FILE_SPC; } } if (snes_file == SNES_FILE_BS) { char *rom_path = (char*)buf; strcpy(rom_path, name); char *offs = strrchr(rom_path, '/'); if (offs) *offs = 0; else *rom_path = 0; fileTYPE fb = {}; if (FileOpen(&fb, user_io_make_filepath(rom_path, "bsx_bios.rom")) || FileOpen(&fb, user_io_make_filepath(HomeDir(), "bsx_bios.rom"))) { printf("Load BSX bios ROM.\n"); uint8_t* buf = snes_get_header(&fb); hexdump(buf, 16, 0); user_io_file_tx_data(buf, 512); //strip original SNES ROM header if present (not used) if ((bytes2send % 1024) == 512) { bytes2send -= 512; FileReadSec(&f, buf); } uint32_t sz = fb.size; while (sz) { uint32_t chunk = (sz > sizeof(buf)) ? sizeof(buf) : sz; FileReadAdv(&fb, buf, chunk); user_io_file_tx_data(buf, chunk); sz -= chunk; } FileClose(&fb); } else { dosend = 0; Info("Cannot open bsx_bios.rom!"); sleep(1); } } else if (snes_file == SNES_FILE_SPC) { printf("Load SPC ROM.\n"); FileReadSec(&f, buf); user_io_file_tx_data(buf, 256); FileSeek(&f, (64*1024)+256, SEEK_SET); FileReadSec(&f, buf); user_io_file_tx_data(buf, 256); FileSeek(&f, 256, SEEK_SET); bytes2send = 64 * 1024; } else if (snes_file == SNES_FILE_ROM) { printf("Load SNES ROM.\n"); uint8_t* buf = snes_get_header(&f); hexdump(buf, 16, 0); user_io_file_tx_data(buf, 512); //strip original SNES ROM header if present (not used) if ((bytes2send % 1024) == 512) { bytes2send -= 512; FileReadSec(&f, buf); } } } file_crc = 0; uint32_t skip = bytes2send & 0x3FF; // skip possible header up to 1023 bytes int use_progress = 1; // (bytes2send > (1024 * 1024)) ? 1 : 0; int size = bytes2send; if (use_progress) ProgressMessage(0, 0, 0, 0); if(ss_base && opensave) process_ss(name); if (is_gba()) { if ((index >> 6) == 1 || (index >> 6) == 2) { fileTYPE fg = {}; if (!FileOpen(&fg, user_io_make_filepath(HomeDir(), "goomba.rom"))) { dosend = 0; Info("Cannot open goomba.rom!"); sleep(1); } else { uint32_t sz = fg.size; while (sz) { uint32_t chunk = (sz > sizeof(buf)) ? sizeof(buf) : sz; FileReadAdv(&fg, buf, chunk); user_io_file_tx_data(buf, chunk); sz -= chunk; } FileClose(&fg); } } } if (is_electron() && bytes2send) { const char *ext = strrchr(f.name, '.'); if (ext && !strcasecmp(ext, ".UEF")) { UEF_FileSend(&f,use_progress); dosend=0; } } if (dosend && load_addr >= 0x20000000 && (load_addr + bytes2send) <= 0x40000000) { uint32_t map_size = bytes2send + ((is_snes() && load_addr < 0x22000000) ? 0x800000 : 0); uint8_t *mem = (uint8_t *)shmem_map(fpga_mem(load_addr), map_size); if (mem) { while (bytes2send) { uint32_t gap = (is_snes() && (load_addr < 0x22000000) && (load_addr + size - bytes2send) >= 0x22000000) ? 0x800000 : 0; uint32_t chunk = (bytes2send > (256 * 1024)) ? (256 * 1024) : bytes2send; FileReadAdv(&f, mem + size - bytes2send + gap, chunk); if(!is_snes() && use_cheats) file_crc = crc32(file_crc, mem + skip + size - bytes2send, chunk - skip); skip = 0; if (use_progress) ProgressMessage("Loading", f.name, size - bytes2send, size); bytes2send -= chunk; } shmem_unmap(mem, map_size); } } else { while (dosend && bytes2send) { uint32_t chunk = (bytes2send > sizeof(buf)) ? sizeof(buf) : bytes2send; FileReadAdv(&f, buf, chunk); if (is_snes() && (snes_file == SNES_FILE_BS)) snes_patch_bs_header(&f, buf); user_io_file_tx_data(buf, chunk); if (use_progress) ProgressMessage("Loading", f.name, size - bytes2send, size); bytes2send -= chunk; if (skip >= chunk) skip -= chunk; else { file_crc = crc32(file_crc, buf + skip, chunk - skip); skip = 0; } } } // check if core requests some change while downloading check_status_change(); printf("Done.\n"); printf("CRC32: %08X\n", file_crc); user_io_write_gameid(name, file_crc); FileClose(&f); if (opensave) { FileGenerateSavePath(name, (char*)buf); user_io_file_mount((char*)buf, 0, 1); } // signal end of transmission user_io_set_download(0); printf("\n"); if (is_zx81() && index) { send_pcolchr(name, (index & 0x1F) | 0x20, 0); send_pcolchr(name, (index & 0x1F) | 0x60, 1); } ProgressMessage(0, 0, 0, 0); if ((is_snes() || is_sgb()) && !load_addr) { // Setup MSU snes_msu_init(name); } return 1; } static char cfgstr[1024 * 10] = {}; void user_io_read_confstr() { spi_uio_cmd_cont(UIO_GET_STRING); uint32_t j = 0; while (j < sizeof(cfgstr) - 1) { char i = spi_in(); if (!i) break; cfgstr[j++] = i; } cfgstr[j++] = 0; DisableIO(); } char *user_io_get_confstr(int index) { int lidx = 0; static char buffer[(1024*2) + 1]; // max bytes per config item char *start = cfgstr; while (lidx < index) { start = strchr(start, ';'); if (!start) return NULL; start++; lidx++; } char *end = strchr(start, ';'); int len = end ? end - start : strlen(start); if (!len) return NULL; if ((uint32_t)len > sizeof(buffer) - 1) len = sizeof(buffer) - 1; memcpy(buffer, start, len); buffer[len] = 0; return buffer; } static char cur_btn = 0; char user_io_menu_button() { return (cur_btn & BUTTON_OSD) ? 1 : 0; } char user_io_user_button() { return (cur_btn & BUTTON_USR) ? 1 : 0; } static int vga_fb = 0; void set_vga_fb(int enable) { vga_fb = enable; user_io_send_buttons(1); } int get_vga_fb() { return vga_fb; } static char kbd_reset = 0; static char kbd_reset_ovr = 0; void user_io_send_buttons(char force) { static unsigned short key_map = 0; unsigned short map = 0; cur_btn = fpga_get_buttons(); if (user_io_menu_button()) map |= BUTTON1; if (user_io_user_button()) map |= BUTTON2; if (kbd_reset || kbd_reset_ovr) map |= BUTTON2; if (cfg.vga_scaler) map |= CONF_VGA_SCALER; if (cfg.vga_sog) map |= CONF_VGA_SOG; if (cfg.csync) map |= CONF_CSYNC; if (cfg.vga_mode_int == 1) map |= CONF_YPBPR; if (cfg.forced_scandoubler) map |= CONF_FORCED_SCANDOUBLER; if (cfg.hdmi_audio_96k) map |= CONF_AUDIO_96K; if (cfg.dvi_mode == 1) map |= CONF_DVI; if (cfg.hdmi_limited & 1) map |= CONF_HDMI_LIMITED1; if (cfg.hdmi_limited & 2) map |= CONF_HDMI_LIMITED2; if (cfg.direct_video) map |= CONF_DIRECT_VIDEO; if (cfg.direct_video == 2) map |= CONF_DIRECT_VIDEO2; if (vga_fb) map |= CONF_VGA_FB; if ((map != key_map) || force) { const char *name = get_rbf_path(); if (name[0] && (get_key_mod() & (LGUI | LSHIFT)) == (LGUI | LSHIFT) && (key_map & BUTTON2) && !(map & BUTTON2)) { uint16_t sz = sdram_sz(-1); if (sz & 0x4000) sz++; else sz = 0x4000; sdram_sz(sz); fpga_load_rbf(name); } //special reset for some cores if (!user_io_osd_is_visible() && (key_map & BUTTON2) && !(map & BUTTON2)) { if (is_minimig()) minimig_reset(); if (is_megacd()) mcd_reset(); if (is_neogeo_cd()) neocd_reset(); if (is_pce()) pcecd_reset(); if (is_saturn()) saturn_reset(); if (is_x86() || is_pcxt()) x86_init(); if (is_uneon()) x86_ide_set(); if (is_st()) tos_reset(0); ResetUART(); } key_map = map; if (user_io_osd_is_visible()) map &= ~BUTTON2; spi_uio_cmd16(UIO_BUT_SW, map); printf("sending keymap: %X\n", map); } } int user_io_get_kbd_reset() { return kbd_reset || kbd_reset_ovr; } void user_io_set_kbd_reset(int reset) { kbd_reset_ovr = reset; } void user_io_set_ini(int ini_num) { const char *name = rbf_path; const char *xml = strcasecmp(rbf_path, core_path) ? core_path : NULL; if (!name[0]) { name = "menu.rbf"; xml = NULL; } if (FileExists(cfg_get_name(ini_num))) { altcfg(ini_num); fpga_load_rbf(name, NULL, xml); } } static uint32_t diskled_timer = 0; static uint32_t diskled_is_on = 0; void diskled_on() { fpga_set_led(1); diskled_timer = GetTimer(50); diskled_is_on = 1; } static void kbd_reply(char code) { printf("kbd_reply = 0x%02X\n", code); spi_uio_cmd16(UIO_KEYBOARD, 0xFF00 | code); } static void mouse_reply(char code) { printf("mouse_reply = 0x%02X\n", code); spi_uio_cmd16(UIO_MOUSE, 0xFF00 | code); } static uint8_t use_ps2ctl = 0; static unsigned long rtc_timer = 0; void user_io_rtc_reset() { rtc_timer = 0; } static int coldreset_req = 0; static uint32_t res_timer = 0; void user_io_poll() { PROFILE_FUNCTION(); if ((core_type != CORE_TYPE_SHARPMZ) && (core_type != CORE_TYPE_8BIT)) { return; // no user io for the installed core } user_io_send_buttons(0); if (is_minimig()) { //HDD & FDD query unsigned char c1, c2; EnableFpga(); uint16_t tmp = spi_w(0); c1 = (uint8_t)(tmp >> 8); // cmd request and drive number c2 = (uint8_t)tmp; // track number spi_w(0); spi_w(0); DisableFpga(); sysled_enable(0); HandleFDD(c1, c2); sysled_enable(1); uint16_t sd_req = ide_check(); ide_io(0, sd_req & 7); ide_io(1, (sd_req >> 3) & 7); if (sd_req & 0x0100) ide_cdda_send_sector(); UpdateDriveStatus(); kbd_fifo_poll(); if (!rtc_timer || CheckTimer(rtc_timer)) { // Update once per minute should be enough rtc_timer = GetTimer(60000); send_rtc(1); } minimig_share_poll(); } if (core_type == CORE_TYPE_8BIT && !is_menu()) { check_status_change(); } // sd card emulation if (is_x86() || is_pcxt()) { x86_poll(0); } else if ((core_type == CORE_TYPE_8BIT) && !is_menu() && !is_minimig()) { if (is_st()) tos_poll(); if (is_snes() || is_sgb()) snes_poll(); for (int i = 0; i < 4; i++) { int disk = -1; int ack = 0; int op = 0; static uint8_t buffer[16][16384]; uint64_t lba = 0; uint32_t blksz, blks, sz; if (is_uneon() && i == 3) { x86_poll(1); break; } uint16_t c = spi_uio_cmd_cont(UIO_GET_SDSTAT); if (c & 0x8000) { disk = (c >> 2) & 0xF; op = c & 3; ack = disk << 8; spi_w(0); lba = spi_w(0); lba = (lba & 0xFFFF) | (((uint32_t)spi_w(0)) << 16); blks = ((c >> 9) & 0x3F) + 1; if (disk == 1 && is_psx()) blksz = 2352; else if (disk == 0 && is_cdi()) blksz = (2352 + 24); else blksz = 128 << ((c >> 6) & 7); sz = blksz * blks; if (sz > sizeof(buffer[0])) { blks = sizeof(buffer[0]) / blksz; sz = blksz * blks; } //if (op) printf("c=%X, op=%d, blkpow=%d, sz=%d, lba=%llu, disk=%d\n", c, op, blkpow, sz, lba, disk); } else { c = spi_w(0); if ((c & 0xf0) == 0x50 && (c & 0x3F03)) { lba = spi_w(0); lba = (lba & 0xFFFF) | (((uint32_t)spi_w(0)) << 16); // check if core requests configuration if ((c & 0xC) == 0xC) { printf("core requests SD config\n"); user_io_sd_set_config(); } if (c & 0x0003) { disk = 0; op = (c & 1) ? 1 : 2; } else if (c & 0x0900) { disk = 1; op = (c & 0x100) ? 1 : 2; } else if (c & 0x1200) { disk = 2; op = (c & 0x200) ? 1 : 2; } else if (c & 0x2400) { disk = 3; op = (c & 0x400) ? 1 : 2; } ack = ((c & 4) ? 0 : ((disk + 1) << 8)); } sz = 512; blksz = 512; blks = 1; } DisableIO(); if ( sd_type[disk] == SD_TYPE_A2) { //if (op) printf("A2 %x %llu on %d\n", op,lba, disk); if (op == 2) a2_writeDSK(&sd_image[disk], lba, ack); else if (op & 1) a2_readDSK(&sd_image[disk], lba, ack); else break; } else if ((blks == G64_BLOCK_COUNT_1541+1 || blks == G64_BLOCK_COUNT_1571+1) && sd_type[disk]==SD_TYPE_C64) { if (op == 2) c64_writeGCR(disk, lba, blks-1); else if (op & 1) c64_readGCR(disk, lba, blks-1); else break; } else if (is_n64() && n64_process_save(use_save, op, lba, blksz, ack, buffer_lba[disk], buffer[disk], sizeof(*buffer), sz)) { // Handled by N64 core logic. // If n64_process_save returns false (e.g. use_save is off, or unsupported op), // it will fall through to the generic handler below. } else if (op == 2) { //printf("SD WR %llu on %d\n", lba, disk); if (use_save) menu_process_save(); buffer_lba[disk] = -1; // Fetch sector data from FPGA ... EnableIO(); spi_w(UIO_SECTOR_WR | ack); spi_block_read(buffer[disk], fio_size, sz); DisableIO(); if (sd_image[disk].type == 2 && !lba) { //Create the file if (FileOpenEx(&sd_image[disk], sd_image[disk].path, O_CREAT | O_RDWR | O_SYNC)) { diskled_on(); if (FileWriteAdv(&sd_image[disk], buffer[disk], sz)) { sd_image[disk].size = sz; } } else { printf("Error in creating file: %s\n", sd_image[disk].path); } } else { // ... and write it to disk uint64_t size = sd_image[disk].size / blksz; if (sz && lba <= size) { diskled_on(); if (FileSeek(&sd_image[disk], lba * blksz, SEEK_SET)) { if (!sd_image_cangrow[disk]) { __off64_t rem = sd_image[disk].size - sd_image[disk].offset; sz = (rem >= sz) ? sz : (int)rem; } if (sz) FileWriteAdv(&sd_image[disk], buffer[disk], sz); } } } } else if (op & 1) { uint32_t buf_n = sizeof(buffer[0]) / blksz; if (is_psx() && blksz == 2352) { //returns 0 if the mounted disk is not a chd, otherwise returns the chd hunksize in bytes unsigned int psx_blksz = psx_chd_hunksize(); if (psx_blksz && psx_blksz <= sizeof(buffer[0])) buf_n = psx_blksz / blksz; } else if (is_cdi() && blksz == (2352 + 24)) { //returns 0 if the mounted disk is not a chd, otherwise returns the chd hunksize in bytes unsigned int psx_blksz = cdi_chd_hunksize(); if (psx_blksz && psx_blksz <= sizeof(buffer[0])) buf_n = psx_blksz / blksz; } //printf("SD RD (%llu,%d) on %d, WIDE=%d\n", lba, blksz, disk, fio_size); int done = 0; uint32_t offset; if ((buffer_lba[disk] == -1LLU) || lba < buffer_lba[disk] || (lba + blks - buffer_lba[disk]) > buf_n) { buffer_lba[disk] = -1; if (blksz == 2352 && is_psx()) { diskled_on(); psx_read_cd(buffer[disk], lba, buf_n); done = 1; buffer_lba[disk] = lba; } else if (blksz == (2352 + 24) && is_cdi()) { diskled_on(); cdi_read_cd(buffer[disk], lba, buf_n); done = 1; buffer_lba[disk] = lba; } else if (sd_image[disk].size) { diskled_on(); if (FileSeek(&sd_image[disk], lba * blksz, SEEK_SET)) { if (FileReadAdv(&sd_image[disk], buffer[disk], sizeof(buffer[disk]))) { done = 1; buffer_lba[disk] = lba; } } } //Even after error we have to provide the block to the core //Give an empty block. if (!done) { if (sd_image[disk].type == 2) { if (is_megacd()) { mcd_fill_blanksave(buffer[disk], lba); } else if (is_pce()) { memset(buffer[disk], 0, sizeof(buffer[disk])); if (!lba) { memcpy(buffer[disk], "HUBM\x00\x88\x10\x80", 8); } } else if (is_psx()) { psx_fill_blanksave(buffer[disk], lba, blks); } else if (is_saturn()) { saturn_fill_blanksave(buffer[disk], lba); } else { memset(buffer[disk], -1, sizeof(buffer[disk])); } } else { memset(buffer[disk], 0, sizeof(buffer[disk])); } } offset = 0; } else { offset = (lba - buffer_lba[disk]) * blksz; done = 1; } // data is now stored in buffer. send it to fpga EnableIO(); spi_w(UIO_SECTOR_RD | ack); spi_block_write(buffer[disk] + offset, fio_size, sz); DisableIO(); if (sd_image[disk].type == 2) { buffer_lba[disk] = -1; } else if (done && (lba + blks - buffer_lba[disk]) == buf_n) { diskled_on(); lba += blks; if (blksz == 2352 && is_psx()) { psx_read_cd(buffer[disk], lba, buf_n); buffer_lba[disk] = lba; } else if (blksz == (2352 + 24) && is_cdi()) { cdi_read_cd(buffer[disk], lba, buf_n); buffer_lba[disk] = lba; } else if (FileSeek(&sd_image[disk], lba * blksz, SEEK_SET) && FileReadAdv(&sd_image[disk], buffer[disk], sizeof(buffer[disk]))) { buffer_lba[disk] = lba; } else { memset(buffer[disk], 0, sizeof(buffer[disk])); buffer_lba[disk] = -1; } } } else break; } } if (is_neogeo() && (!rtc_timer || CheckTimer(rtc_timer))) { // Update once per minute should be enough rtc_timer = GetTimer(60000); send_rtc(1); } if (is_archie()) archie_poll(); if (core_type == CORE_TYPE_SHARPMZ) sharpmz_poll(); static uint8_t leds = 0; if (use_ps2ctl && !is_minimig() && !is_archie()) { leds |= (KBD_LED_FLAG_STATUS | KBD_LED_CAPS_CONTROL); uint8_t kbd_ctl, mouse_ctl; uint8_t ps2ctl = user_io_ps2_ctl(&kbd_ctl, &mouse_ctl); if (ps2ctl & 1) { static uint8_t cmd = 0; static uint8_t byte = 0; printf("kbd_ctl = 0x%02X\n", kbd_ctl); if (!byte) { cmd = kbd_ctl; switch (cmd) { case 0xff: ps2_kbd_scan_set = 2; kbd_reply(0xFA); kbd_reply(0xAA); break; case 0xf2: kbd_reply(0xFA); kbd_reply(0xAB); kbd_reply(0x83); break; case 0xf0: // scan get/set kbd_reply(0xFA); byte++; break; case 0xf6: // set default parameters kbd_reply(0xFA); ps2_kbd_scan_set = 2; break; case 0xf3: // set type rate kbd_reply(0xFA); byte++; break; case 0xf4: case 0xf5: case 0xfa: kbd_reply(0xFA); break; case 0xed: kbd_reply(0xFA); byte++; break; case 0xee: kbd_reply(0xEE); break; default: kbd_reply(0xFE); break; } } else { switch (cmd) { case 0xed: kbd_reply(0xFA); byte = 0; if (kbd_ctl & 4) leds |= KBD_LED_CAPS_STATUS; else leds &= ~KBD_LED_CAPS_STATUS; break; case 0xf0: byte = 0; if (kbd_ctl <= 3) { kbd_reply(0xFA); if (!kbd_ctl) kbd_reply(ps2_kbd_scan_set); // get else ps2_kbd_scan_set = kbd_ctl; // set } else { kbd_reply(0xFE); // RESEND } break; case 0xf3: // set type rate kbd_reply(0xFA); byte = 0; break; default: byte = 0; break; } } } if (ps2ctl & 2) { static uint8_t cmd = 0; static uint8_t byte = 0; printf("mouse_ctl = 0x%02X\n", mouse_ctl); if (!byte) { cmd = mouse_ctl; switch (cmd) { case 0xe8: case 0xf3: mouse_reply(0xFA); byte++; break; case 0xf2: mouse_reply(0xFA); mouse_reply(0x00); break; case 0xe6: case 0xea: case 0xf0: case 0xf4: case 0xf5: case 0xf6: mouse_reply(0xFA); break; case 0xe9: mouse_reply(0xFA); mouse_reply(0x00); mouse_reply(0x00); mouse_reply(0x00); break; case 0xff: mouse_reply(0xFA); mouse_reply(0xAA); mouse_reply(0x00); break; default: mouse_reply(0xFE); break; } } else { switch (cmd) { case 0xf3: case 0xe8: mouse_reply(0xFA); byte = 0; break; default: byte = 0; break; } } } } if (CheckTimer(led_timer) && !is_menu()) { led_timer = GetTimer(LED_FREQ); if (!use_ps2ctl) { uint16_t s = user_io_kbdled_get_status(); if (s & 0x100) use_ps2ctl = 1; if (!use_ps2ctl) leds = (uint8_t)s; } if ((leds & KBD_LED_FLAG_MASK) != KBD_LED_FLAG_STATUS) leds = 0; if ((keyboard_leds & KBD_LED_CAPS_MASK) != (leds & KBD_LED_CAPS_MASK)) set_kbdled(HID_LED_CAPS_LOCK, (leds & KBD_LED_CAPS_CONTROL) ? leds & KBD_LED_CAPS_STATUS : caps_status); if ((keyboard_leds & KBD_LED_NUM_MASK) != (leds & KBD_LED_NUM_MASK)) set_kbdled(HID_LED_NUM_LOCK, (leds & KBD_LED_NUM_CONTROL) ? leds & KBD_LED_NUM_STATUS : num_status); if ((keyboard_leds & KBD_LED_SCRL_MASK) != (leds & KBD_LED_SCRL_MASK)) set_kbdled(HID_LED_SCROLL_LOCK, (leds & KBD_LED_SCRL_CONTROL) ? leds & KBD_LED_SCRL_STATUS : scrl_status); keyboard_leds = leds; uint8_t info_n = spi_uio_cmd(UIO_INFO_GET); if (info_n) show_core_info(info_n); } if (!res_timer) { res_timer = GetTimer(1000); } else if (CheckTimer(res_timer)) { if (is_menu()) { static int got_cfg = 0; if (!got_cfg) { spi_uio_cmd_cont(UIO_GET_OSDMASK); sdram_cfg = spi_w(0); DisableIO(); if (sdram_cfg & 0x8000) { got_cfg = 1; printf("*** Got SDRAM module type: %d\n", sdram_cfg & 7); switch (user_io_get_sdram_cfg() & 7) { case 7: sdram_sz(3); break; case 3: sdram_sz(2); break; case 1: sdram_sz(1); break; default: sdram_sz(0); } } } } res_timer = GetTimer(500); if (!minimig_get_adjust()) { if (is_minimig()) minimig_adjust_vsize(0); video_mode_adjust(); } /* uint32_t frcnt = spi_uio_cmd(UIO_GET_FR_CNT); if (frcnt & 0x100) { printf("frames:%d\n", frcnt & 0xFF); } */ } static int prev_coldreset_req = 0; static uint32_t reset_timer = 0; if (!prev_coldreset_req && coldreset_req) { reset_timer = GetTimer(1000); } if (!coldreset_req && prev_coldreset_req) { fpga_load_rbf("menu.rbf"); } prev_coldreset_req = coldreset_req; if (reset_timer && CheckTimer(reset_timer)) { reboot(1); } save_volume(); if (diskled_is_on && CheckTimer(diskled_timer)) { fpga_set_led(0); diskled_is_on = 0; } if (is_megacd()) mcd_poll(); if (is_pce()) pcecd_poll(); if (is_saturn()) saturn_poll(); if (is_cdi()) cdi_poll(); if (is_psx()) psx_poll(); if (is_neogeo_cd()) neocd_poll(); if (is_n64()) n64_poll(); if (is_c64() || is_c128()) { uint16_t save_req = spi_uio_cmd(UIO_CHK_UPLOAD); if (save_req) c64_save_cart(save_req >> 8); } process_ss(0); } static void send_keycode(unsigned short key, int press) { if (is_pcxt()) { //WIN+... we override this hotkey in the core. if (key == 125 || key == 126) { winkey_pressed = press; return; } if (winkey_pressed) { return; } } if (is_minimig()) { if (press > 1) return; uint32_t code = get_amiga_code(key); if (code == NONE) return; if (code & CAPS_TOGGLE) { if (press) { // send alternating make and break codes for caps lock if(caps_lock_toggle) code |= 0x80; caps_lock_toggle ^= HID_LED_CAPS_LOCK; set_kbd_led(HID_LED_CAPS_LOCK, caps_lock_toggle); } else { return; } } else { // amiga has "break" marker in msb if (!press) code |= 0x80; } code &= 0xff; if (minimig_get_adjust()) { if (code == 0x44) { minimig_set_adjust(0); res_timer = 0; return; } if (code == 0x45) { Info("Canceled"); res_timer = 0; minimig_set_adjust(2); return; } code |= OSD; } // send immediately if possible if (CheckTimer(kbd_timer) && (kbd_fifo_w == kbd_fifo_r)) { kbd_fifo_minimig_send(code); } else { kbd_fifo_enqueue(code); } return; } if (is_archie()) { if (press > 1) return; uint32_t code = get_archie_code(key); if (code == NONE) return; //WIN+... if (get_key_mod() & (RGUI | LGUI)) { switch (code) { case 0x00: code = 0xf; //ESC = BRAKE break; case 0x11: code = 0x73; // 1 = Mouse extra 1 break; case 0x12: code = 0x74; // 2 = Mouse extra 2 break; case 0x13: code = 0x25; // 3 = KP# break; } } if (code == 0 && (get_key_mod() & (RGUI | LGUI))) { code = 0xF; } if (!press) code |= 0x8000; archie_kbd(code); return; } if (core_type == CORE_TYPE_8BIT) { uint32_t code = get_ps2_code(key); if (code == NONE) return; //pause if ((code & 0xff) == 0xE1) { // pause does not have a break code if (press != 1) { // Pause key sends E11477E1F014E077 static const unsigned char c[] = { 0xe1, 0x14, 0x77, 0xe1, 0xf0, 0x14, 0xf0, 0x77, 0x00 }; static const unsigned char c_set1[] = { 0xe1, 0x1d, 0x45, 0xe1, 0x9d, 0xc5, 0x00 }; const unsigned char *p = (ps2_kbd_scan_set == 1) ? c_set1 : c; spi_uio_cmd_cont(UIO_KEYBOARD); printf("PS2 PAUSE CODE: "); while (*p) { printf("%x ", *p); spi8(*p++); } printf("\n"); DisableIO(); } } // print screen else if ((code & 0xff) == 0xE2) { if (press <= 1) { static const unsigned char c[2][8] = { { 0xE0, 0xF0, 0x7C, 0xE0, 0xF0, 0x12, 0x00, 0x00 }, { 0xE0, 0x12, 0xE0, 0x7C, 0x00, 0x00, 0x00, 0x00 } }; static const unsigned char c_set1[2][8] = { { 0xE0, 0xB7, 0xE0, 0xAA, 0x00, 0x00, 0x00, 0x00 }, { 0xE0, 0x2A, 0xE0, 0x37, 0x00, 0x00, 0x00, 0x00 } }; const unsigned char *p = (ps2_kbd_scan_set == 1) ? c_set1[press] : c[press]; spi_uio_cmd_cont(UIO_KEYBOARD); printf("PS2 PRINT CODE: "); while (*p) { printf("%x ", *p); spi8(*p++); } printf("\n"); DisableIO(); } } else { if (press > 1 && !use_ps2ctl) return; spi_uio_cmd_cont(UIO_KEYBOARD); // prepend extended code flag if required if (code & EXT) spi8(0xe0); // prepend break code if required if (!press) { if (ps2_kbd_scan_set == 1) code |= 0x80; else spi8(0xf0); } // send code itself spi8(code & 0xff); DisableIO(); } } if (core_type == CORE_TYPE_SHARPMZ) { uint32_t code = get_ps2_code(key); if (code == NONE) return; { if (press > 1 && !use_ps2ctl) return; spi_uio_cmd_cont(UIO_KEYBOARD); // prepend extended code flag if required if (code & EXT) spi8(0xe0); // prepend break code if required if (!press) spi8(0xf0); // send code itself spi8(code & 0xff); DisableIO(); } } } void user_io_mouse(unsigned char b, int16_t x, int16_t y, int16_t w) { if (osd_is_visible && !is_menu()) return; switch (core_type) { case CORE_TYPE_8BIT: if (is_minimig()) { spi_uio_cmd_cont(UIO_MOUSE); spi8((x < -127) ? -127 : (x > 127) ? 127 : x); spi8((y < -127) ? -127 : (y > 127) ? 127 : y); spi8(b & 0x07); spi8((w < -127) ? -127 : (w > 127) ? 127 : w); DisableIO(); } else if (is_archie()) { archie_mouse(b, x, y); } else { unsigned char ps2_mouse[3]; // PS2 format: // YOvfl, XOvfl, dy8, dx8, 1, mbtn, rbtn, lbtn // dx[7:0] // dy[7:0] ps2_mouse[0] = (b & 7) | 8; // ------ X axis ----------- // store sign bit in first byte ps2_mouse[0] |= (x < 0) ? 0x10 : 0x00; if (x < -255) { // min possible value + overflow flag ps2_mouse[0] |= 0x40; ps2_mouse[1] = 1; // -255 } else if (x > 255) { // max possible value + overflow flag ps2_mouse[0] |= 0x40; ps2_mouse[1] = 255; } else { ps2_mouse[1] = (char)x; } // ------ Y axis ----------- // store sign bit in first byte y = -y; ps2_mouse[0] |= (y < 0) ? 0x20 : 0x00; if (y < -255) { // min possible value + overflow flag ps2_mouse[0] |= 0x80; ps2_mouse[2] = 1; // -255; } else if (y > 255) { // max possible value + overflow flag ps2_mouse[0] |= 0x80; ps2_mouse[2] = 255; } else { ps2_mouse[2] = (char)y; } if (w > 63) w = 63; else if (w < -63) w = -63; // collect movement info and send at predefined rate if (is_menu() && !video_fb_state()) printf("PS2 MOUSE: %x %d %d %d\n", ps2_mouse[0], ps2_mouse[1], ps2_mouse[2], w); if (!osd_is_visible) { spi_uio_cmd_cont(UIO_MOUSE); spi_w(ps2_mouse[0] | ((w & 0x7f) << 8)); spi_w(ps2_mouse[1] | ((((uint16_t)b) << 5) & 0xF00)); spi_w(ps2_mouse[2] | ((((uint16_t)b) << 1) & 0x100)); DisableIO(); } } return; } } /* usb modifer bits: 0 1 2 3 4 5 6 7 LCTRL LSHIFT LALT LGUI RCTRL RSHIFT RALT RGUI */ #define EMU_BTN1 (0+(keyrah*4)) // left control #define EMU_BTN2 (1+(keyrah*4)) // left shift #define EMU_BTN3 (2+(keyrah*4)) // left alt #define EMU_BTN4 (3+(keyrah*4)) // left gui (usually windows key) void user_io_check_reset(unsigned short modifiers, char useKeys) { unsigned short combo[] = { 0x45, // lctrl+lalt+ralt 0x89, // lctrl+lgui+rgui 0x105, // lctrl+lalt+del }; if (useKeys >= (sizeof(combo) / sizeof(combo[0]))) useKeys = 0; if ((modifiers & ~2) == combo[(uint)useKeys]) { if (modifiers & 2) // with lshift - cold reset { coldreset_req = 1; } else switch (core_type) { case CORE_TYPE_8BIT: if(is_minimig()) minimig_reset(); else kbd_reset = 1; break; } } else { coldreset_req = 0; kbd_reset = 0; } } void user_io_osd_key_enable(char on) { //printf("OSD is now %s\n", on ? "visible" : "invisible"); osd_is_visible = on; input_switch(-1); } void user_io_kbd(uint16_t key, int press) { static int block_F12 = 0; if(is_menu()) spi_uio_cmd(UIO_KEYBOARD); //ping the Menu core to wakeup // Win+PrnScr or Alt/Win+ScrLk - screen shot bool key_WinPrnScr = (key == KEY_SYSRQ && (get_key_mod() & (RGUI | LGUI))); // Excluding scroll lock for PS/2 so Win+ScrLk can be used to change the emu mode. bool key_AltWinScrLk = (key == KEY_SCROLLLOCK && (get_key_mod() & (LALT | RALT | RGUI | LGUI))) && !use_ps2ctl; if (key_WinPrnScr || key_AltWinScrLk) { int shift = (get_key_mod() & LSHIFT); if (press == 1) { printf("print key pressed - do screen shot\n"); user_io_screenshot(nullptr,!shift); } } else if (key == KEY_MUTE) { if (press == 1 && hasAPI1_5()) set_volume(0); } else if (key == KEY_VOLUMEDOWN) { if (press && hasAPI1_5()) set_volume(-1); } else if (key == KEY_VOLUMEUP) { if (press && hasAPI1_5()) set_volume(1); } else if (key == 0xBE) { if (press) setBrightness(BRIGHTNESS_DOWN, 0); } else if (key == 0xBF) { if (press) setBrightness(BRIGHTNESS_UP, 0); } else if (key == KEY_F2 && osd_is_visible) { if (press == 1) cfg.rbf_hide_datecode = !cfg.rbf_hide_datecode; PrintDirectory(); } else { if (key) { uint32_t code = get_ps2_code(key); if (!press) { if (is_menu() && !video_fb_state()) printf("PS2 code(break)%s for core: %d(0x%X)\n", (code & EXT) ? "(ext)" : "", code & 255, code & 255); if (key == KEY_MENU) key = KEY_F12; if (key != KEY_F12 || !block_F12) { if (osd_is_visible) menu_key_set(UPSTROKE | key); // these modifiers should be passed to core even if OSD is open or they will get stuck! if (!osd_is_visible || key == KEY_LEFTALT || key == KEY_RIGHTALT || key == KEY_LEFTMETA || key == KEY_RIGHTMETA) send_keycode(key, press); } if (key == KEY_F12) block_F12 = 0; } else { if (is_menu() && !video_fb_state()) printf("PS2 code(make)%s for core: %d(0x%X)\n", (code & EXT) ? "(ext)" : "", code & 255, code & 255); if (!osd_is_visible && !is_menu() && key == KEY_MENU && press == 3) open_joystick_setup(); else if ((has_menu() || osd_is_visible || (get_key_mod() & (LALT | RALT | RGUI | LGUI))) && (((key == KEY_F12) && ((!is_x86() && !is_pcxt() && !is_archie()) || (get_key_mod() & (RGUI | LGUI)))) || key == KEY_MENU)) { block_F12 = 1; if (press == 1) menu_key_set(KEY_F12); } else if (osd_is_visible) { if (key == KEY_MENU) key = KEY_F12; if (key == KEY_F12) block_F12 = 1; if (press == 1) menu_key_set(key); } else { // When ps2ctl is set then the RGUI or LGUI key must be held in addition // to the EMU_SWITCH_1 or EMU_SWITCH_2. This allows for cores such as AO486 // to pass through the Scroll Lock and Num Lock keys. bool ps2ctl_modifier = (get_key_mod() & (RGUI | LGUI)) || !use_ps2ctl; bool key_EMU_SWITCH_1 = (code & EMU_SWITCH_1) && ps2ctl_modifier; bool key_EMU_SWITCH_2 = (code & EMU_SWITCH_2) && ps2ctl_modifier && !is_archie(); if (( key_EMU_SWITCH_1 || key_EMU_SWITCH_2 ) && !is_menu()) { if (press == 1) { int mode = emu_mode; // all off: normal // num lock on, scroll lock on: mouse emu // num lock on, scroll lock off: joy0 emu // num lock off, scroll lock on: joy1 emu switch (code & 0xff) { case 1: if (!joy_force) mode = EMU_MOUSE; break; case 2: mode = EMU_JOY0; break; case 3: mode = EMU_JOY1; break; case 4: if (!joy_force) mode = EMU_NONE; break; default: if (joy_force) mode = (mode == EMU_JOY0) ? EMU_JOY1 : EMU_JOY0; else { mode = (mode + 1) & 3; if(cfg.kbd_nomouse && mode == EMU_MOUSE) mode = (mode + 1) & 3; } break; } set_emu_mode(mode); } } else { if(key == KEY_MENU) key = KEY_F12; if(input_state()) send_keycode(key, press); } } } } } } unsigned char user_io_ext_idx(char *name, char* ext) { unsigned char idx = 0; printf("Subindex of \"%s\" in \"%s\": ", name, ext); char *p = strrchr(name, '.'); if (p) { p++; char e[4] = " "; for (int i = 0; i < 3; i++) { if (!*p) break; e[i] = *p++; } while (*ext) { int found = 1; for (int i = 0; i < 3; i++) { if (ext[i] == '*') break; if (ext[i] != '?' && (toupper(ext[i]) != toupper(e[i]))) found = 0; } if (found) { printf("%d\n", idx); return idx; } if (strlen(ext) <= 3) break; idx++; ext += 3; } } printf("not found! use 0\n"); return 0; } uint16_t user_io_get_sdram_cfg() { return sdram_cfg; } static struct { const char *fmtstr; Imlib_Load_Error errno; } err_strings[] = { {"file '%s' does not exist", IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST}, {"file '%s' is a directory", IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY}, {"permission denied to read file '%s'", IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ}, {"no loader for the file format used in file '%s'", IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT}, {"path for file '%s' is too long", IMLIB_LOAD_ERROR_PATH_TOO_LONG}, {"a component of path '%s' does not exist", IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT}, {"a component of path '%s' is not a directory", IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY}, {"path '%s' has too many symbolic links", IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS}, {"ran out of file descriptors trying to access file '%s'", IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS}, {"denied write permission for file '%s'", IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE}, {"out of disk space writing to file '%s'", IMLIB_LOAD_ERROR_OUT_OF_DISK_SPACE}, {(const char *)NULL, (Imlib_Load_Error) 0} }; static void print_imlib_load_error (Imlib_Load_Error err, const char *filepath) { int i; for (i = 0; err_strings[i].fmtstr != NULL; i++) { if (err == err_strings[i].errno) { printf("Screenshot Error (%d): ",err); printf(err_strings[i].fmtstr,filepath); printf("\n"); return ; } } /* Unrecognised error */ printf("Screenshot Error (%d): unrecognized error accessing file '%s'\n",err,filepath); return ; } bool user_io_screenshot(const char *pngname, int rescale) { mister_scaler *ms = mister_scaler_init(); if (ms == NULL) { printf("problem with scaler, maybe not a new enough version\n"); Info("Scaler not compatible"); return false; } else { int scwidth = ms->output_width; int scheight = ms->output_height; if (video_get_rotated()) { //If the video is rotated, the scaled output resolution results in a squished image. //Calculate the scaled output res using the original AR scwidth = scheight * ((float)ms->width/ms->height); } const char *basename = last_filename; if( pngname && *pngname ) basename = pngname; unsigned char *outputbuf = (unsigned char *)calloc(ms->width*ms->height * 4, 1); // read the image into the outpubuf - RGBA format mister_scaler_read_32(ms,outputbuf); // using_data will keep a pointer and dispose of the outbuf Imlib_Image im = imlib_create_image_using_data(ms->width,ms->height,(unsigned int *)outputbuf); imlib_context_set_image(im); static char filename[1024]; FileGenerateScreenshotName(basename, filename, 1024); /* do we want to save a rescaled image? */ if (rescale) { Imlib_Image im_scaled=imlib_create_cropped_scaled_image(0,0,ms->width,ms->height,scwidth,scheight); imlib_free_image_and_decache(); imlib_context_set_image(im_scaled); } Imlib_Load_Error error; imlib_save_image_with_error_return(getFullPath(filename),&error); if (error != IMLIB_LOAD_ERROR_NONE) { print_imlib_load_error (error, filename); Info("error in saving png"); return false; } imlib_free_image_and_decache(); mister_scaler_free(ms); free(outputbuf); char msg[1024]; snprintf(msg, 1024, "Screen saved to\n%s", filename + strlen(SCREENSHOT_DIR"/")); Info(msg); } return true; } void user_io_screenshot_cmd(const char *cmd) { if( strncmp( cmd, "screenshot", 10 )) { return; } cmd += 10; while( *cmd != '\0' && ( *cmd == '\t' || *cmd == ' ' || *cmd == '\n' ) ) cmd++; user_io_screenshot(cmd,0); }