mirror of
https://github.com/MiSTer-devel/Main_MiSTer.git
synced 2026-04-12 03:04:02 +00:00
4283 lines
94 KiB
C++
4283 lines
94 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <limits.h>
|
|
#include <ctype.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/statvfs.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"
|
|
#ifdef PROFILING
|
|
#include "profiling.h"
|
|
#endif
|
|
#include "frame_timer.h"
|
|
#include "scaler.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;
|
|
|
|
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)
|
|
{
|
|
if (!strcasecmp(orig_name, "PCXT") ||
|
|
!strcasecmp(orig_name, "Tandy1000") ||
|
|
!strcasecmp(orig_name, "PCjr")
|
|
)
|
|
is_pcxt_type = 1;
|
|
else
|
|
is_pcxt_type = 2;
|
|
}
|
|
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_atari800_type = 0;
|
|
char is_atari800()
|
|
{
|
|
if (!is_atari800_type) is_atari800_type = strcasecmp(orig_name, "Atari800") ? 2 : 1;
|
|
return (is_atari800_type == 1);
|
|
}
|
|
|
|
static int is_atari5200_type = 0;
|
|
char is_atari5200()
|
|
{
|
|
if (!is_atari5200_type) is_atari5200_type = strcasecmp(orig_name, "Atari5200") ? 2 : 1;
|
|
return (is_atari5200_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_3do_type = 0;
|
|
char is_3do()
|
|
{
|
|
if (!is_3do_type) is_3do_type = strcasecmp(orig_name, "3DO") ? 2 : 1;
|
|
return (is_3do_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_atari800_type = 0;
|
|
is_atari5200_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);
|
|
}
|
|
|
|
uint16_t f12_mod;
|
|
char is_f12_mod_needed()
|
|
{
|
|
return (is_x86() || is_pcxt() || is_archie() || (f12_mod != 0));
|
|
}
|
|
|
|
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;
|
|
if (!stat("/tmp/uartmode6", &filestat)) return 6;
|
|
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 if (is_atari800())
|
|
{
|
|
atari800_init();
|
|
}
|
|
else if (is_atari5200())
|
|
{
|
|
atari5200_init();
|
|
}
|
|
else
|
|
{
|
|
const char *home = HomeDir();
|
|
|
|
if (is_uneon()) x86_ide_set();
|
|
if (is_cdi()) cdi_load_root_nvram();
|
|
|
|
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);
|
|
|
|
f12_mod = spi_uio_cmd(UIO_GET_F12_MOD);
|
|
|
|
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, uint32_t map, int newdir)
|
|
{
|
|
uint8_t joy = (joystick>1 || !joyswap) ? joystick : joystick ^ 1;
|
|
|
|
static int use32 = 0;
|
|
use32 |= map >> 16;
|
|
|
|
spi_uio_cmd_cont((joy < 2) ? (UIO_JOYSTICK0 + joy) : (UIO_JOYSTICK2 + joy - 2));
|
|
spi_w(map);
|
|
if(use32) spi_w(map >> 16);
|
|
DisableIO();
|
|
|
|
if (!is_minimig() && joy_transl == 1 && newdir)
|
|
{
|
|
user_io_l_analog_joystick(joystick, (map & 2) ? 128 : (map & 1) ? 127 : 0, (map & 8) ? 128 : (map & 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);
|
|
|
|
uint32_t rom_size = 0;
|
|
uint8_t *rom = snes_get_mirrored_rom(&f, &rom_size);
|
|
if (rom) {
|
|
uint32_t remaining = rom_size;
|
|
uint32_t sent = 0;
|
|
const uint32_t chunk_size = 4096;
|
|
while (remaining) {
|
|
uint32_t chunk = (remaining > chunk_size) ? chunk_size : remaining;
|
|
ProgressMessage("Loading", f.name, sent, rom_size);
|
|
user_io_file_tx_data(rom + sent, chunk);
|
|
sent += chunk;
|
|
remaining -= chunk;
|
|
}
|
|
free(rom);
|
|
}
|
|
dosend = 0;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
mdplus_init(name); // MD+ CDDA init
|
|
|
|
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);
|
|
if (is_3do()) p3do_reset();
|
|
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()
|
|
{
|
|
#ifdef PROFILING
|
|
PROFILE_FUNCTION();
|
|
#endif
|
|
|
|
// every frame, check if a screenshot has been requested.
|
|
// this is reduce risk of screenshot occurring while the scaler
|
|
// is being updated and getting a corrupted image.
|
|
add_frame_callback(screenshot_cb);
|
|
|
|
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();
|
|
mdplus_poll(); // MD+ CDDA poll
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
int disk = -1;
|
|
int ack = 0;
|
|
int op = 0;
|
|
static uint8_t buffer[16][UIO_BUFFER_SIZE];
|
|
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 = CDI_CDIC_BUFFER_SIZE;
|
|
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 == CDI_CDIC_BUFFER_SIZE)
|
|
{
|
|
//returns 0 if the mounted disk is not a chd, otherwise returns the chd hunksize in bytes
|
|
unsigned int cdi_blksz = cdi_chd_hunksize();
|
|
if (cdi_blksz && cdi_blksz <= sizeof(buffer[0])) buf_n = cdi_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 if (is_3do())
|
|
{
|
|
p3do_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);
|
|
}
|
|
if (is_atari800()) atari800_poll();
|
|
if (is_atari5200()) atari5200_poll();
|
|
if (is_3do()) p3do_poll();
|
|
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 (is_atari800())
|
|
{
|
|
atari800_check_osd_key(key, press);
|
|
}
|
|
|
|
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)
|
|
{
|
|
|
|
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)
|
|
{
|
|
printf("print key pressed - do screen shot\n");
|
|
request_screenshot(NULL, !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);
|
|
bool is_menu_event = ((has_menu() || osd_is_visible || (get_key_mod() & (LALT | RALT | RGUI | LGUI))) && (((key == KEY_F12) && (!is_f12_mod_needed() || (get_key_mod() & (RGUI | LGUI)))) || key == KEY_MENU));
|
|
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 || !is_menu_event)
|
|
{
|
|
|
|
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 (is_menu_event) menu_key_set(KEY_F12 | UPSTROKE);
|
|
}
|
|
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 (is_menu_event)
|
|
{
|
|
if (press == 1) menu_key_set(KEY_F12);
|
|
}
|
|
else if (osd_is_visible)
|
|
{
|
|
if (key == KEY_MENU) key = KEY_F12;
|
|
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;
|
|
}
|