#include #include #include #include "hardware.h" #include "osd.h" #include "state.h" #include "state.h" #include "user_io.h" #include "data_io.h" #include "archie.h" #include "pcecd.h" #include "neocd.h" #include "psx.h" #include "hdd.h" #include "cdc_control.h" #include "usb.h" #include "debug.h" #include "keycodes.h" #include "ikbd.h" #include "idxfile.h" #include "spi.h" #include "mist_cfg.h" #include "mmc.h" #include "tos.h" #include "errors.h" #include "arc_file.h" #include "cue_parser.h" #include "utils.h" #include "settings.h" #include "usb/joymapping.h" #include "usb/joystick.h" #include "FatFs/diskio.h" #include "menu.h" #ifdef HAVE_HDMI #include "it6613/HDMI_TX.h" #endif // up to 16 key can be remapped #define MAX_REMAP 16 unsigned char key_remap_table[MAX_REMAP][2]; #define BREAK 0x8000 static char umounted; // 1st image is file or direct SD? static char cache_buffer[1024]; static uint8_t buffer_drive_index = 0; static uint32_t buffer_lba = 0xffffffff; extern char s[FF_LFN_BUF + 1]; // mouse and keyboard emulation state typedef enum { EMU_NONE, EMU_MOUSE, EMU_JOY0, EMU_JOY1 } emu_mode_t; static emu_mode_t emu_mode = EMU_NONE; static unsigned char emu_state = 0; static unsigned long emu_timer = 0; #define EMU_MOUSE_FREQ 5 // keep state over core type and its capabilities static unsigned char core_type = CORE_TYPE_UNKNOWN; static char core_type_8bit_with_config_string = 0; // core supports direct ROM upload via SS4 extern char rom_direct_upload; // extra features in the firmware requested by the core static uint32_t core_features = 0; // core variant (mostly for arcades) static int64_t core_mod = 0; // keep state of caps lock static char caps_lock_toggle = 0; // avoid multiple keyboard/controllers to interfere static uint8_t latest_keyb_priority = 0; // keyboard=0, joypad with key mappings=1 // mouse position storage for ps2 and minimig rate limitation #define X 0 #define Y 1 #define Z 2 #define MOUSE_FREQ 20 // 20 ms -> 50hz static int16_t mouse_pos[2][3] = { {0, 0, 0}, {0, 0, 0} }; static uint8_t mouse_flags[2] = { 0, 0 }; static unsigned long mouse_timer; #define LED_FREQ 100 // 100 ms static unsigned long led_timer; char keyboard_leds = 0; bool caps_status = 0; bool num_status = 0; bool scrl_status = 0; #define RTC_FREQ 1000 // 1 s static unsigned long rtc_timer; static unsigned char modifier = 0, pressed[6] = { 0,0,0,0,0,0 }; static unsigned char ps2_typematic_rate = 0x80; static unsigned long ps2_typematic_timer; typedef enum { PS2_KBD_IDLE, PS2_KBD_SCAN_GETSET, PS2_KBD_TYPEMATIC_SET, PS2_KBD_LED_SET } ps2_kbd_state_t; static ps2_kbd_state_t ps2_kbd_state; static char ps2_kbd_scan_set = 2; typedef enum { PS2_MOUSE_IDLE, PS2_MOUSE_SETRESOLUTION, PS2_MOUSE_SETSAMPLERATE } ps2_mouse_state_t; static ps2_mouse_state_t ps2_mouse_state; static unsigned char ps2_mouse_status; static unsigned char ps2_mouse_resolution; static unsigned char ps2_mouse_samplerate; // 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 = false; static char autofire; static unsigned long autofire_timer; static uint32_t autofire_map; static uint32_t autofire_mask; static char autofire_joy; // ATA drives hardfileTYPE hardfiles[4]; static uint8_t i2c_flags; #ifdef HAVE_HDMI static unsigned long hdmi_timer; static bool hdmi_detected = 0; static uint8_t hdmi_hiclk = 0; #define HDMI_FREQ 1000 #endif #define CONF_TBL_MAX 64 static uint16_t conf_idx[CONF_TBL_MAX]; static int conf_items = 0; char user_io_osd_is_visible() { return osd_is_visible; } void user_io_reset() { // no sd card image selected, SD card accesses will go directly // to the card (first slot, and only until the first unmount) umounted = 0; toc.valid = 0; sd_image[0].valid = 0; sd_image[1].valid = 0; sd_image[2].valid = 0; sd_image[3].valid = 0; for (int i=0; i 0-6 spi8(0x40); // flag DisableIO(); } } uint32_t user_io_get_core_features() { return core_features; } static void user_io_read_core_features() { core_features = 0; spi_uio_cmd_cont(UIO_GET_FEATS); if (spi_in() == 0x80) { core_features = spi_in(); core_features = (core_features<<8) | spi_in(); core_features = (core_features<<8) | spi_in(); core_features = (core_features<<8) | spi_in(); } DisableIO(); if (core_features & FEAT_PS2REP) ps2_typematic_rate = 0x08; } void user_io_detect_core_type() { core_name[0] = 0; EnableIO(); core_type = SPI(0xff); DisableIO(); #ifdef SD_NO_DIRECT_MODE rom_direct_upload = 0; #else rom_direct_upload = (core_type & 0x10) >> 4; // bit 4 - direct upload support #endif core_type &= 0xef; switch(core_type) { case CORE_TYPE_DUMB: puts("Identified core without user interface"); break; case CORE_TYPE_MINIMIG: strcpy(core_name, "MINIMIG"); puts("Identified Minimig V1 core"); break; case CORE_TYPE_MINIMIG2: strcpy(core_name, "MINIMIG"); puts("Identified Minimig V2 core"); break; case CORE_TYPE_PACE: puts("Identified PACE core"); break; case CORE_TYPE_MIST: case CORE_TYPE_MIST2: strcpy(core_name, "ST"); puts("Identified MiST core"); break; case CORE_TYPE_ARCHIE: puts("Identified Archimedes core"); strcpy(core_name, "ARCHIE"); archie_init(); break; case CORE_TYPE_8BIT: puts("Identified 8BIT core"); // send core variant first to allow the FPGA choosing the config string user_io_send_core_mod(); // forward SD card config to core in case it uses the local // SD card implementation user_io_sd_set_config(); // check if core has a config string core_type_8bit_with_config_string = (user_io_8bit_get_string(0) != NULL); // set core name. This currently only sets a name for the 8 bit cores user_io_read_core_name(); // get requested features user_io_read_core_features(); break; default: iprintf("Unable to identify core (%x)!\n", core_type); core_type = CORE_TYPE_UNKNOWN; } } void user_io_init_core() { if(core_type == CORE_TYPE_8BIT) { // send a reset user_io_8bit_set_status(UIO_STATUS_RESET, ~0); FIL file; UINT br; // try to load config if(!user_io_create_config_name(s, "CFG", CONFIG_ROOT)) { iprintf("Loading config %s\n", s); if (f_open(&file, s, FA_READ) == FR_OK) { iprintf("Found config\n"); if(f_size(&file) <= 8) { ((unsigned long long*)sector_buffer)[0] = 0; f_read(&file, sector_buffer, f_size(&file), &br); user_io_8bit_set_status(((unsigned long long*)sector_buffer)[0], ~1); } else { settings_load(false); } f_close(&file); } else { user_io_8bit_set_status(arc_get_default(), ~1); } } // check if there's a .rom or .r0[1-6] present, send it via index 0-6 for (int i = 0; i < 7; i++) { char ext[4]; if (!i) { strcpy(ext, "ROM"); } else { strcpy(ext, "R01"); ext[2] = '0'+i; } for (char root = 0; root <= 1; root++) { if (!user_io_create_config_name(s, ext, root)) { iprintf("Looking for %s\n", s); if (f_open(&file, s, FA_READ) == FR_OK) { data_io_file_tx(&file, i, ext); f_close(&file); break; } } } } if(!user_io_create_config_name(s, "RAM", CONFIG_ROOT)) { iprintf("Looking for %s\n", s); // check if there's a .ram present, send it via index -1 if (f_open(&file, s, FA_READ) == FR_OK) { data_io_file_tx(&file, -1, "RAM"); f_close(&file); } } for (int i = 0; i < SD_IMAGES; i++) { hardfile[i] = &hardfiles[i]; if ((core_features & (FEAT_IDE0 << (2*i))) == (FEAT_IDE0_CDROM << (2*i))) { iprintf("IDE %d: ATAPI CDROM\n", i); hardfiles[i].enabled = HDF_CDROM; OpenHardfile(i, false); } } // check if there's a .vhd present if(!user_io_create_config_name(s, "VHD", CONFIG_ROOT | CONFIG_VHD)) { iprintf("Looking for %s\n", s); if (!(core_features & FEAT_IDE0)) user_io_file_mount(s, 0); if (!user_io_is_mounted(0)) { // check for .HD0/1 files if(!user_io_create_config_name(s, "HD0", CONFIG_ROOT | CONFIG_VHD)) { for (int i = 0; i < SD_IMAGES; i++) { s[strlen(s)-1] = '0'+i; iprintf("Looking for %s\n", s); if ((core_features & (FEAT_IDE0 << (2*i))) == (FEAT_IDE0_ATA << (2*i))) { iprintf("IDE %d: ATA Hard Disk\n", i); hardfiles[i].enabled = HDF_FILE; strncpy(hardfiles[i].name, s, sizeof(hardfiles[0].name)); hardfiles[i].name[sizeof(hardfiles[0].name)-1] = 0; OpenHardfile(i, false); } else { user_io_file_mount(s, i); } } } } } if (core_features & FEAT_IDE_MASK) SendHDFCfg(); // release reset user_io_8bit_set_status(0, UIO_STATUS_RESET); } #ifdef HAVE_HDMI hdmi_detected = false; hdmi_hiclk = 0; if ((core_type == CORE_TYPE_8BIT && core_features & FEAT_HDMI) || core_type == CORE_TYPE_MIST2 || core_type == CORE_TYPE_MINIMIG2 || core_type == CORE_TYPE_ARCHIE) { hdmi_detected = HDMITX_Init(); if (hdmi_detected) HDMITX_ChangeVideoTiming(1); } #endif } static unsigned short usb2amiga( unsigned char k ) { // replace MENU key by RGUI to allow using Right Amiga on reduced keyboards // (it also disables the use of Menu for OSD) if (mist_cfg.key_menu_as_rgui && k==0x65) { return 0x67; } return usb2ami[k]; } static unsigned short usb2ps2code( unsigned char k) { // replace MENU key by RGUI e.g. to allow using RGUI on reduced keyboards without physical key // (it also disables the use of Menu for OSD) if (mist_cfg.key_menu_as_rgui && k==0x65) { return EXT | 0x27; } return (ps2_kbd_scan_set == 1) ? usb2ps2_set1[k] : usb2ps2[k]; } void user_io_analog_joystick(unsigned char joystick, char valueX, char valueY, char valueX2, char valueY2) { if(osd_is_visible) return; if(core_type == CORE_TYPE_8BIT || core_type == CORE_TYPE_MINIMIG2) { int16_t valueXX = valueX*mist_cfg.joystick_analog_mult/128 + mist_cfg.joystick_analog_offset; int16_t valueYY = valueY*mist_cfg.joystick_analog_mult/128 + mist_cfg.joystick_analog_offset; int16_t valueXX2 = valueX2*mist_cfg.joystick_analog_mult/128 + mist_cfg.joystick_analog_offset; int16_t valueYY2 = valueY2*mist_cfg.joystick_analog_mult/128 + mist_cfg.joystick_analog_offset; //iprintf("analog: x=%d, y=%d, xx=%d, yy=%d, mult=%d, offs=%d\n", valueX, valueY, valueXX, valueYY, mist_cfg.joystick_analog_mult, mist_cfg.joystick_analog_offset); spi_uio_cmd8_cont(UIO_ASTICK, joystick); spi8(valueXX); spi8(valueYY); spi8(valueXX2); spi8(valueYY2); DisableIO(); } } void user_io_digital_joystick(unsigned char joystick, unsigned char map) { uint8_t state = map; // "only" 6 joysticks are supported if(joystick > 5) return; // if osd is open, control it via joystick if(osd_is_visible && map) return; //iprintf("j%d: %x\n", joystick, map); // atari ST handles joystick 0 and 1 through the ikbd emulated by the io controller // but only for joystick 1 and 2 if((core_type == CORE_TYPE_MIST) && (joystick < 2)) { ikbd_joystick(joystick, map); return; } // every other core else uses this // (even MIST, joystick 3 and 4 were introduced later) spi_uio_cmd8((joystick < 2)?(UIO_JOYSTICK0 + joystick):((UIO_JOYSTICK2 + joystick - 2)), map); } void user_io_digital_joystick_ext(unsigned char joystick, uint32_t map) { // "only" 6 joysticks are supported if(joystick > 5) return; if(osd_is_visible && map) return; //iprintf("ext j%d: %x\n", joystick, map); spi_uio_cmd32(UIO_JOYSTICK0_EXT + joystick, 0x000fffff & map); if (autofire && (map & 0x30)) { autofire_mask = map & 0x30; autofire_map = (autofire_map & autofire_mask) | (map & ~autofire_mask); if (autofire_joy != joystick) { autofire_joy = joystick; autofire_timer = GetTimer(autofire*50); } } else { autofire_joy = -1; } } static char dig2ana(char min, char max) { if(min && !max) return -128; if(max && !min) return 127; return 0; } void user_io_joystick(unsigned char joystick, uint16_t map) { // digital joysticks also send analog signals user_io_digital_joystick(joystick, map); user_io_digital_joystick_ext(joystick, map); user_io_analog_joystick(joystick, dig2ana(map&JOY_LEFT, map&JOY_RIGHT), dig2ana(map&JOY_UP, map&JOY_DOWN), 0 ,0); } // transmit serial/rs232 data into core void user_io_serial_tx(char *chr, uint16_t cnt) { if (core_type == CORE_TYPE_MIST) spi_uio_cmd_cont(UIO_SERIAL_OUT); else spi_uio_cmd_cont(UIO_SIO_OUT); while(cnt--) spi8(*chr++); DisableIO(); } char user_io_serial_status(serial_status_t *status_in, uint8_t status_out) { uint8_t i, *p = (uint8_t*)status_in; spi_uio_cmd_cont(UIO_SERIAL_STAT); // first byte returned by core must be "magic". otherwise the // core doesn't support this request if(SPI(status_out) != 0xa5) { DisableIO(); return 0; } // read the whole structure for(i=0;i> 26) & 0xff; data[16+8] = (capacity >> 18) & 0xff; data[16+9] = (capacity >> 10) & 0xff; data[16+10] = 0x5f; data[16+11] = 0xc0; data[32] = 1; // SDHC } // and forward it to the FPGA spi_uio_cmd_cont(UIO_SET_SDCONF); spi_write(data, sizeof(data)); DisableIO(); // hexdump(data, sizeof(data), 0); } void user_io_sd_ack(char drive_index) { spi_uio_cmd_cont(UIO_SD_ACK); spi8(drive_index); DisableIO(); } // read 8+32 bit sd card status word from FPGA uint8_t user_io_sd_get_status(uint32_t *lba, uint8_t *drive_index, uint8_t *blksz) { uint32_t s; uint8_t c; *drive_index = 0; *blksz = 0; spi_uio_cmd_cont(UIO_GET_SDSTAT); c = spi_in(); if ((c & 0xf0) == 0x60) { uint8_t tmp = spi_in(); *drive_index = tmp & 0x03; *blksz = (tmp >> 4) & 0x01; } s = spi_in(); s = (s<<8) | spi_in(); s = (s<<8) | spi_in(); s = (s<<8) | spi_in(); DisableIO(); if(lba) *lba = s; return c; } // read 8 bit keyboard LEDs status from FPGA static uint8_t user_io_kbdled_get_status(void) { uint8_t c; spi_uio_cmd_cont(UIO_GET_KBD_LED); c = spi_in(); DisableIO(); return c; } // read 32 bit ethernet status word from FPGA uint32_t user_io_eth_get_status(void) { uint32_t s; spi_uio_cmd_cont(UIO_ETH_STATUS); s = spi_in(); s = (s<<8) | spi_in(); s = (s<<8) | spi_in(); s = (s<<8) | spi_in(); DisableIO(); return s; } // read ethernet frame from FPGAs ethernet tx buffer void user_io_eth_receive_tx_frame(uint8_t *d, uint16_t len) { spi_uio_cmd_cont(UIO_ETH_FRM_IN); while(len--) *d++=spi_in(); DisableIO(); } // write ethernet frame to FPGAs rx buffer void user_io_eth_send_rx_frame(uint8_t *s, uint16_t len) { spi_uio_cmd_cont(UIO_ETH_FRM_OUT); while(len--) SPI(*s++); //spi_write(s, len); spi8(0); // one additional byte to allow fpga to store the previous one DisableIO(); } // the physical joysticks (db9 ports at the right device side) // as well as the joystick emulation are renumbered if usb joysticks // are present in the system. The USB joystick(s) replace joystick 1 // and 0 and the physical joysticks are "shifted up". // // Since the primary joystick is in port 1 the first usb joystick // becomes joystick 1 and only the second one becomes joystick 0 // (mouse port) static uint8_t joystick_renumber(uint8_t j) { uint8_t usb_sticks = joystick_count(); // no usb sticks present: no changes are being made if(!usb_sticks) return j; // Keep DB9 joysticks as joystick 0 and joystick 1 // USB joysticks will be 2,3,... if(mist_cfg.joystick_db9_fixed_index) return j; if(j == 0) { // if usb joysticks are present, then physical joystick 0 (mouse port) // becomes becomes 2,3,... j = mist_cfg.joystick0_prefer_db9 ? 0 : usb_sticks + 1; } else { // if one usb joystick is present, then physical joystick 1 (joystick port) // becomes physical joystick 0 (mouse) port. If more than 1 usb joystick // is present it becomes 2,3,... if(usb_sticks == 1) j = mist_cfg.joystick_disable_swap? 1 : 0; else j = usb_sticks; } return j; } static void user_io_joystick_emu() { // iprintf("joystick_emu_fixed_index: %d\n", mist_cfg.joystick_emu_fixed_index); // joystick emulation also follows renumbering if requested (default) if(emu_mode == EMU_JOY0) user_io_joystick(mist_cfg.joystick_emu_fixed_index ? 0 : joystick_renumber(0), emu_state); if(emu_mode == EMU_JOY1) user_io_joystick(mist_cfg.joystick_emu_fixed_index ? 1 : joystick_renumber(1), emu_state); } // 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(unsigned short 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 running? 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); } char user_io_is_cue_mounted() { return toc.valid; } char user_io_cue_mount(const unsigned char *name, unsigned char index) { char res = CUE_RES_OK; toc.valid = 0; if (name) { res = cue_parse(name, &sd_image[index]); } #ifdef HAVE_PSX if (core_features & FEAT_PSX) psx_mount_cd(name); #endif // send mounted image size first then notify about mounting EnableIO(); SPI(UIO_SET_SDINFO); // use LE version, so following BYTE(s) may be used for size extension in the future. spi32le(toc.valid ? f_size(&toc.file->file) : 0); spi32le(toc.valid ? f_size(&toc.file->file) >> 32 : 0); spi32le(0); // reserved for future expansion spi32le(0); // reserved for future expansion DisableIO(); // notify core of possible sd image change spi_uio_cmd8(UIO_SET_SDSTAT, 1); return res; } static inline char sd_index(unsigned char index) { unsigned char retval = index; if (core_type == CORE_TYPE_ARCHIE) return (index + 2); else return index; } char user_io_is_mounted(unsigned char index) { return sd_image[sd_index(index)].valid; } void user_io_file_mount(const unsigned char *name, unsigned char index) { FRESULT res; buffer_lba = 0xffffffff; // invalidate cache if (name) { if (sd_image[sd_index(index)].valid) f_close(&sd_image[sd_index(index)].file); res = IDXOpen(&sd_image[sd_index(index)], name, FA_READ | FA_WRITE); if (res != FR_OK) res = IDXOpen(&sd_image[sd_index(index)], name, FA_READ); if (res == FR_OK) { iprintf("selected %llu bytes to slot %d\n", f_size(&sd_image[sd_index(index)].file), index); sd_image[sd_index(index)].valid = 1; // build index for fast random access IDXIndex(&sd_image[sd_index(index)]); } else { iprintf("error mounting %s (%d)\n", name, res); return; } } else { iprintf("unmounting file in slot %d\n", index); if (sd_image[sd_index(index)].valid) f_close(&sd_image[sd_index(index)].file); sd_image[sd_index(index)].valid = 0; if (!index) umounted = 1; } // send mounted image size first then notify about mounting EnableIO(); SPI(UIO_SET_SDINFO); // use LE version, so following BYTE(s) may be used for size extension in the future. spi32le(sd_image[sd_index(index)].valid ? f_size(&sd_image[sd_index(index)].file) : 0); spi32le(sd_image[sd_index(index)].valid ? f_size(&sd_image[sd_index(index)].file) >> 32: 0); spi32le(0); // reserved for future expansion spi32le(0); // reserved for future expansion DisableIO(); // notify core of possible sd image change spi_uio_cmd8(UIO_SET_SDSTAT, index); } // 8 bit cores have a config string telling the firmware how // to treat it char *user_io_8bit_get_string(unsigned char index) { unsigned char i, lidx = 0, j = 0, d = 0, arc = 0; int arc_ptr = 0; char dip[3]; static char buffer[128+1]; // max 128 bytes per config item uint16_t start_chr; // clear buffer buffer[0] = 0; // use the config index table to get where to start // conf_idx stores the starting position of every 4th item // if the index is in a DIP setting, it has 0 uint16_t pos = 0, lastpos = 0; i = index>>2; while (i > 0 && (i > conf_items || conf_idx[i] == 0)) i--; pos = lastpos = conf_idx[i]; lidx = i<<2; //iprintf("index=%d cached pos=%d lidx=%d\n", index, pos, lidx); spi_uio_cmd_cont(UIO_GET_STR_EXT); i = SPI(pos & 0xff); if (i == 0xaa) { SPI(pos >> 8); i = spi_in(); // dummy byte to prepare to apply the offset in the core i = spi_in(); } else { DisableIO(); lidx = 0; pos = lastpos = 0; //iprintf("UIO_GET_STRING_EXT not supported\n"); spi_uio_cmd_cont(UIO_GET_STRING); i = spi_in(); // the first char returned will be 0xff if the core doesn't support // config strings. atari 800 returns 0xa4 which is the status byte if((i == 0xff) || (i == 0xa4)) { DisableIO(); return NULL; } } // iprintf("String: "); while ((i != 0) && (i!=0xff) && (j> 2) < CONF_TBL_MAX) { conf_idx[lidx >> 2] = arc ? 0 : lastpos; if (conf_items < (lidx >> 2)) conf_items = (lidx >> 2); } lastpos = pos+1; if(!arc && d==3 && !strncmp(dip, "DIP", 3)) { // found "DIP", continue with config snippet from ARC if(lidx == index) { // skip the DIP line j = 0; buffer[0] = 0; } arc = 1; } else { if(lidx == index) { buffer[j++] = 0; break; } lidx++; } d = 0; } else { if(lidx == index) buffer[j++] = i; if (d<3) dip[d++] = i; } //iprintf("%c", i); if (arc) { i = arc_get_conf()[arc_ptr++]; if (!i) arc = 0; } if (!arc) { i = spi_in(); pos++; } } DisableIO(); // iprintf("\n"); // if this was the last string in the config string list, then it still // needs to be terminated if(lidx == index) buffer[j] = 0; // also return NULL for empty strings if(!buffer[0]) return NULL; return buffer; } unsigned long long user_io_8bit_set_status(unsigned long long new_status, unsigned long long mask) { static unsigned long long status = 0; // if mask is 0 just return the current status if(mask) { // keep everything not masked status &= ~mask; // updated masked bits status |= new_status & mask; spi_uio_cmd8(UIO_SET_STATUS, status); spi_uio_cmd64(UIO_SET_STATUS2, status); } return status; } char kbd_reset = 0; void user_io_send_buttons(char force) { static unsigned char key_map = 0; // frequently poll the adc the switches // and buttons are connected to PollADC(); unsigned char map = 0; if(Buttons() & 1) map |= SWITCH2; if(Buttons() & 2) map |= SWITCH1; if(Buttons() & 4) map |= BUTTON1; else if(Buttons() & 8) map |= BUTTON2; if(kbd_reset) map |= BUTTON2; if(!mist_cfg.keep_video_mode) VIDEO_ALTERED_VAR = 0; if(VIDEO_ALTERED_VAR & 1) { if(VIDEO_SD_DISABLE_VAR) map |= CONF_SCANDOUBLER_DISABLE; } else { if(mist_cfg.scandoubler_disable) map |= CONF_SCANDOUBLER_DISABLE; } if(VIDEO_ALTERED_VAR & 2) { if(VIDEO_YPBPR_VAR) map |= CONF_YPBPR; } else { if(mist_cfg.ypbpr) map |= CONF_YPBPR; } if(mist_cfg.csync_disable) map |= CONF_CSYNC_DISABLE; if(mist_cfg.sdram64) map |= CONF_SDRAM64; if((map != key_map) || force) { key_map = map; spi_uio_cmd8(UIO_BUT_SW, map); iprintf("sending keymap\n"); } } static void set_kbd_led(unsigned char led, bool on) { if(led & HID_LED_CAPS_LOCK) { if(!(keyboard_leds & KBD_LED_CAPS_CONTROL)) hid_set_kbd_led(led, on); caps_status = on; } if(led & HID_LED_NUM_LOCK) { if(!(keyboard_leds & KBD_LED_NUM_CONTROL)) hid_set_kbd_led(led, on); num_status = on; } if(led & HID_LED_SCROLL_LOCK) { if(!(keyboard_leds & KBD_LED_SCRL_CONTROL)) hid_set_kbd_led(led, on); scrl_status = on; } } static void handle_ps2_kbd_commands() { unsigned char c, cmd; spi_uio_cmd_cont(UIO_KEYBOARD_IN); c = spi_in(); cmd = spi_in(); DisableIO(); if (c == UIO_KEYBOARD_IN) { // receiving echo of the command code shows the core supports this message iprintf("PS2 keyboard cmd: %02x\n", cmd); switch (ps2_kbd_state) { case PS2_KBD_IDLE: switch (cmd) { case 0xFF: // reset ps2_kbd_scan_set = 2; spi_uio_cmd8(UIO_KEYBOARD, 0xFA); // ACK spi_uio_cmd8(UIO_KEYBOARD, 0xAA); // BAT successful break; case 0xF2: // read ID spi_uio_cmd8(UIO_KEYBOARD, 0xFA); // ACK spi_uio_cmd8(UIO_KEYBOARD, 0xAB); // ID1 spi_uio_cmd8(UIO_KEYBOARD, 0x83); // ID2 break; case 0xF0: // scan get/set spi_uio_cmd8(UIO_KEYBOARD, 0xFA); // ACK ps2_kbd_state = PS2_KBD_SCAN_GETSET; break; case 0xF3: // typematic set spi_uio_cmd8(UIO_KEYBOARD, 0xFA); // ACK ps2_kbd_state = PS2_KBD_TYPEMATIC_SET; break; case 0xED: // set LEDs spi_uio_cmd8(UIO_KEYBOARD, 0xFA); // ACK ps2_kbd_state = PS2_KBD_LED_SET; break; case 0xEE: // echo spi_uio_cmd8(UIO_KEYBOARD, 0xEE); // ACK break; case 0xF4: // enable scanning // TODO: handle the message spi_uio_cmd8(UIO_KEYBOARD, 0xFA); // ACK break; case 0xF5: // disable scanning // TODO: handle the message spi_uio_cmd8(UIO_KEYBOARD, 0xFA); // ACK break; case 0xF6: // set default parameters ps2_kbd_scan_set = 2; spi_uio_cmd8(UIO_KEYBOARD, 0xFA); // ACK break; } break; case PS2_KBD_SCAN_GETSET: if (cmd<=3) { spi_uio_cmd8(UIO_KEYBOARD, 0xFA); // ACK if (!cmd) // get spi_uio_cmd8(UIO_KEYBOARD, ps2_kbd_scan_set); else // set ps2_kbd_scan_set = cmd; ps2_kbd_state = PS2_KBD_IDLE; } else { spi_uio_cmd8(UIO_KEYBOARD, 0xFE); // RESEND } break; case PS2_KBD_TYPEMATIC_SET: ps2_typematic_rate = cmd; spi_uio_cmd8(UIO_KEYBOARD, 0xFA); // ACK ps2_kbd_state = PS2_KBD_IDLE; break; case PS2_KBD_LED_SET: // TODO: handle the message spi_uio_cmd8(UIO_KEYBOARD, 0xFA); // ACK ps2_kbd_state = PS2_KBD_IDLE; break; } } } static void send_keycode(unsigned short code); static unsigned short keycode(unsigned char in); // 1000/(2^(39-rate)^(1/8)) static int ps2_typematic_rates[] = {34, 37, 40, 44, 48, 52, 57, 62, 68, 74, 81, 88, 96, 105, 114, 125, 136, 148, 162, 176, 192, 210, 229, 250, 272, 297, 324, 353, 385, 420, 458, 500}; static void handle_ps2_typematic_repeat() { if (ps2_typematic_rate & 0x80) return; if (ps2_kbd_state != PS2_KBD_IDLE) return; if (CheckTimer(ps2_typematic_timer)) { ps2_typematic_timer = GetTimer(ps2_typematic_rates[ps2_typematic_rate & 0x1f]); for (char i=5; i>=0; i--) { if (pressed[i]) { unsigned short code = keycode(pressed[i]); if (!osd_is_visible && !(code & CAPS_LOCK_TOGGLE)&& !(code & NUM_LOCK_TOGGLE)) { send_keycode(code); } break; } } } } static void handle_ps2_mouse_commands() { unsigned char c, cmd; spi_uio_cmd_cont(UIO_MOUSE_IN); c = spi_in(); cmd = spi_in(); DisableIO(); if (c == UIO_MOUSE_IN) { // receiving echo of the command code shows the core supports this message iprintf("PS2 mouse cmd: %02x\n", cmd); switch (ps2_mouse_state) { case PS2_MOUSE_IDLE: switch (cmd) { case 0xFF: // reset spi_uio_cmd8(UIO_MOUSE0_EXT, 0xFA); // ACK spi_uio_cmd8(UIO_MOUSE0_EXT, 0xAA); // BAT successful spi_uio_cmd8(UIO_MOUSE0_EXT, 0); break; case 0xF6: // set defaults; spi_uio_cmd8(UIO_MOUSE0_EXT, 0xFA); // ACK break; case 0xE6: // set mouse scaling to 1:1 spi_uio_cmd8(UIO_MOUSE0_EXT, 0xFA); // ACK ps2_mouse_status &= ~0x10; break; case 0xE7: // set mouse scaling to 1:2 spi_uio_cmd8(UIO_MOUSE0_EXT, 0xFA); // ACK ps2_mouse_status |= 0x10; break; case 0xE8: // set resolution spi_uio_cmd8(UIO_MOUSE0_EXT, 0xFA); // ACK ps2_mouse_state = PS2_MOUSE_SETRESOLUTION; break; case 0xE9: // status request spi_uio_cmd8(UIO_MOUSE0_EXT, 0xFA); // ACK spi_uio_cmd8(UIO_MOUSE0_EXT, ps2_mouse_status); spi_uio_cmd8(UIO_MOUSE0_EXT, ps2_mouse_resolution); spi_uio_cmd8(UIO_MOUSE0_EXT, ps2_mouse_samplerate); break; case 0xF2: // get device ID spi_uio_cmd8(UIO_MOUSE0_EXT, 0xFA); // ACK spi_uio_cmd8(UIO_MOUSE0_EXT, 0x00); // Normal PS2 mouse break; case 0xF4: // enable data reporting spi_uio_cmd8(UIO_MOUSE0_EXT, 0xFA); // ACK ps2_mouse_status |= 0x20; break; case 0xF5: // disable data reporting spi_uio_cmd8(UIO_MOUSE0_EXT, 0xFA); // ACK ps2_mouse_status &= ~0x20; break; case 0xF3: // set sample rate spi_uio_cmd8(UIO_MOUSE0_EXT, 0xFA); // ACK ps2_mouse_state = PS2_MOUSE_SETSAMPLERATE; break; } break; case PS2_MOUSE_SETRESOLUTION: spi_uio_cmd8(UIO_MOUSE0_EXT, 0xFA); // ACK ps2_mouse_resolution = cmd; ps2_mouse_state = PS2_MOUSE_IDLE; break; case PS2_MOUSE_SETSAMPLERATE: spi_uio_cmd8(UIO_MOUSE0_EXT, 0xFA); // ACK ps2_mouse_samplerate = cmd; ps2_mouse_state = PS2_MOUSE_IDLE; break; } } } void user_io_poll() { // check of core has changed from a good one to a not supported on // as this likely means that the user is reloading the core via jtag unsigned char ct; static unsigned char ct_cnt = 0; EnableIO(); ct = SPI(0xff); DisableIO(); SPI_MINIMIGV1_HACK if((ct&0xef) == core_type) ct_cnt = 0; // same core type, everything is fine else { // core type has changed if(++ct_cnt == 255) { USB_LOAD_VAR = USB_LOAD_VALUE; // wait for a new valid core id to appear while((ct & 0xe0) != 0xa0) { EnableIO(); ct = SPI(0xff); DisableIO(); SPI_MINIMIGV1_HACK } // reset io controller to cope with new core MCUReset(); // restart for(;;); } } if((core_type != CORE_TYPE_MINIMIG) && (core_type != CORE_TYPE_MINIMIG2) && (core_type != CORE_TYPE_PACE) && (core_type != CORE_TYPE_MIST) && (core_type != CORE_TYPE_MIST2) && (core_type != CORE_TYPE_ARCHIE) && (core_type != CORE_TYPE_8BIT)) { return; // no user io for the installed core } if((core_type == CORE_TYPE_MIST) || (core_type == CORE_TYPE_MIST2)) { char redirect = tos_get_cdc_control_redirect(); if (core_type == CORE_TYPE_MIST) ikbd_poll(); // check for input data on usart USART_Poll(); unsigned char c = 0; // check for incoming serial data. this is directly forwarded to the // arm rs232 and mixes with debug output. Useful for debugging only of // e.g. the diagnostic cartridge if(!pl2303_is_blocked()) { if (core_type == CORE_TYPE_MIST) spi_uio_cmd_cont(UIO_SERIAL_IN); else spi_uio_cmd_cont(UIO_SIO_IN); while(spi_in() && !pl2303_is_blocked()) { c = spi_in(); // if a serial/usb adapter is connected it has precesence over // any other sink if(pl2303_present()) pl2303_tx_byte(c); else { if(c != 0xff) putchar(c); // forward to USB if redirection via USB/CDC enabled if(redirect == CDC_REDIRECT_RS232) cdc_control_tx(c); } } DisableIO(); } // check for incoming parallel/midi data if((redirect == CDC_REDIRECT_PARALLEL) || (redirect == CDC_REDIRECT_MIDI)) { spi_uio_cmd_cont((redirect == CDC_REDIRECT_PARALLEL)?UIO_PARALLEL_IN:UIO_MIDI_IN); // character 0xff is returned if FPGA isn't configured c = 0; while(spi_in() && (c!= 0xff)) { c = spi_in(); cdc_control_tx(c); } DisableIO(); // always flush when doing midi to reduce latencies if(redirect == CDC_REDIRECT_MIDI) cdc_control_flush(); } } // poll db9 joysticks uint16_t joy_state = 0, joy_map = 0; if(GetDB9(0, &joy_state)) { joy_map = virtual_joystick_mapping(0x00db, 0x0000, joy_state); uint8_t idx = joystick_renumber(0); uint8_t id = mist_cfg.joystick_db9_fixed_index ? idx : joystick_count(); if (!user_io_osd_is_visible()) user_io_joystick(idx, joy_map); StateUsbIdSet(0x00db, 0x0000, 2, id); StateJoySet(joy_map, id); // send to OSD StateJoySetExtra(joy_map >> 8, id); // send to OSD StateUsbJoySet(joy_state, joy_state >> 8, id); virtual_joystick_keyboard(joy_map); } if(GetDB9(1, &joy_state)) { joy_map = virtual_joystick_mapping(0x00db, 0x0001, joy_state); uint8_t idx = joystick_renumber(1); uint8_t id = mist_cfg.joystick_db9_fixed_index ? idx : joystick_count() + 1; if (!user_io_osd_is_visible()) user_io_joystick(idx, joy_map); StateUsbIdSet(0x00db, 0x0001, 2, id); StateJoySet(joy_map, id); // send to OSD StateJoySetExtra(joy_map >> 8, id); // send to OSD StateUsbJoySet(joy_state, joy_state >> 8, id); virtual_joystick_keyboard(joy_map); } if (autofire && autofire_joy >= 0 && autofire_joy <= 5 && CheckTimer(autofire_timer)) { autofire_map ^= autofire_mask; //iprintf("06x\n", autofire_map); spi_uio_cmd32(UIO_JOYSTICK0_EXT + autofire_joy, 0x000fffff & autofire_map); autofire_timer = GetTimer(autofire*50); } user_io_send_buttons(0); // mouse movement emulation is continous if(emu_mode == EMU_MOUSE) { if(CheckTimer(emu_timer)) { emu_timer = GetTimer(EMU_MOUSE_FREQ); if(emu_state & JOY_MOVE) { unsigned char b = 0; char x = 0, y = 0; if((emu_state & (JOY_LEFT | JOY_RIGHT)) == JOY_LEFT) x = -1; if((emu_state & (JOY_LEFT | JOY_RIGHT)) == JOY_RIGHT) x = +1; if((emu_state & (JOY_UP | JOY_DOWN)) == JOY_UP) y = -1; if((emu_state & (JOY_UP | JOY_DOWN)) == JOY_DOWN) y = +1; if(emu_state & JOY_BTN1) b |= 1; if(emu_state & JOY_BTN2) b |= 2; user_io_mouse(0, b, x, y, 0); } } } if((core_type == CORE_TYPE_MINIMIG) || (core_type == CORE_TYPE_MINIMIG2)) { kbd_fifo_poll(); // frequently check mouse for events if(CheckTimer(mouse_timer)) { mouse_timer = GetTimer(MOUSE_FREQ); // has ps2 mouse data been updated in the meantime for (char idx = 0; idx < 2; idx ++) { if(mouse_flags[idx] & 0x80) { char x, y, z; // ----- X axis ------- if(mouse_pos[idx][X] < -128) { x = -128; mouse_pos[idx][X] += 128; } else if(mouse_pos[idx][X] > 127) { x = 127; mouse_pos[idx][X] -= 127; } else { x = mouse_pos[idx][X]; mouse_pos[idx][X] = 0; } // ----- Y axis ------- if(mouse_pos[idx][Y] < -128) { y = (-128); mouse_pos[idx][Y] += 128; } else if(mouse_pos[idx][Y] > 127) { y = 127; mouse_pos[idx][Y] -= 127; } else { y = mouse_pos[idx][Y]; mouse_pos[idx][Y] = 0; } // ----- Z axis ------- if(mouse_pos[idx][Z] < -128) { z = (-128); mouse_pos[idx][Z] += 128; } else if(mouse_pos[idx][Z] > 127) { z = 127; mouse_pos[idx][Z] -= 127; } else { z = mouse_pos[idx][Z]; mouse_pos[idx][Z] = 0; } if (!idx) { // send the first mouse only with the old message spi_uio_cmd_cont(UIO_MOUSE); spi8(x); spi8(y); spi8(mouse_flags[idx] & 0x07); DisableIO(); } spi_uio_cmd_cont(UIO_MOUSE0_EXT + idx); spi8(x); spi8(y); spi8(mouse_flags[idx] & 0x07); spi8(z); DisableIO(); // reset flags mouse_flags[idx] = 0; } } } } if((core_type == CORE_TYPE_MIST) || (core_type == CORE_TYPE_MIST2)) { // do some tos specific monitoring here tos_poll(); } // serial IO - TODO: merge with MiST2 if(core_type == CORE_TYPE_8BIT) { unsigned char c = 1, f, p=0; // check for input data on usart USART_Poll(); // TODO: currently doesn't send anything for 8BIT // check for serial data to be sent // check for incoming serial data. this is directly forwarded to the // arm rs232 and mixes with debug output. spi_uio_cmd_cont(UIO_SIO_IN); // status byte is 1000000A with A=1 if data is available if((f = spi_in(0)) == 0x81) { iprintf("\033[1;36m"); // character 0xff is returned if FPGA isn't configured while((f == 0x81) && (c!= 0xff) && (c != 0x00) && (p < 8)) { c = spi_in(); if(c != 0xff && c != 0x00) iprintf("%c", c); f = spi_in(); p++; } iprintf("\033[0m"); } DisableIO(); } if((core_type == CORE_TYPE_8BIT) && (!strcmp(user_io_get_core_name(), "TGFX16") || (core_features & FEAT_PCECD))) pcecd_poll(); if((core_type == CORE_TYPE_8BIT) && (core_features & FEAT_NEOCD)) neocd_poll(); // sd card emulation if((core_type == CORE_TYPE_8BIT) || (core_type == CORE_TYPE_MIST2) || (core_type == CORE_TYPE_ARCHIE)) { uint32_t lba; uint8_t drive_index; uint8_t blksz; uint8_t c = user_io_sd_get_status(&lba, &drive_index, &blksz); // valid sd commands start with "5x" (old API), or "6x" (new API) // to avoid problems with cores that don't implement this command if((c & 0xf0) == 0x50 || (c & 0xf0) == 0x60) { #if 0 // debug: If the io controller reports and non-sdhc card, then // the core should never set the sdhc flag if((c & 3) && !MMC_IsSDHC() && (c & 0x04)) iprintf("WARNING: SDHC access to non-sdhc card\n"); #endif // check if core requests configuration if(c & 0x08) { iprintf("core requests SD config\n"); user_io_sd_set_config(); } // check if system is trying to access a sdhc card from // a sd/mmc setup // check if an SDHC card is inserted if(MMC_IsSDHC()) { static char using_sdhc = 1; // SD request and if(c & 0x03){ if (!(c & 0x04)) { if(using_sdhc) { // we have not been using sdhc so far? // -> complain! ErrorMessage(" This core does not support\n" " SDHC cards. Using them may\n" " lead to data corruption.\n\n" " Please use an SD card <2GB!", 0); using_sdhc = 0; } } else // SDHC request from core is always ok using_sdhc = 1; } } // Write to file/SD Card if((c & 0x03) == 0x02) { // only write if the inserted card is not sdhc or // if the core uses sdhc if((!MMC_IsSDHC()) || (c & 0x04)) { if(user_io_dip_switch1()) iprintf("SD WR (%d) %d/%d\n", drive_index, lba, 512<> (9+blksz)) >= lba) { IDXSeek(&sd_image[sd_index(drive_index)], (lba<> (9+blksz)) >= lba) { IDXSeek(&sd_image[sd_index(drive_index)], lba<> (9+blksz)) > lba) { IDXSeek(&sd_image[sd_index(drive_index)], (lba+1)< 255) { // max possible value + overflow flag ps2_mouse[0] |= 0x40; ps2_mouse[1] = 255; } else ps2_mouse[1] = mouse_pos[idx][X]; // ------ Y axis ----------- // store sign bit in first byte ps2_mouse[0] |= (mouse_pos[idx][Y] < 0)?0x20:0x00; if(mouse_pos[idx][Y] < -255) { // min possible value + overflow flag ps2_mouse[0] |= 0x80; ps2_mouse[2] = -128; } else if(mouse_pos[idx][Y] > 255) { // max possible value + overflow flag ps2_mouse[0] |= 0x80; ps2_mouse[2] = 255; } else ps2_mouse[2] = mouse_pos[idx][Y]; // ------ Z axis ----------- ps2_mouse[3] = 0; if(mouse_pos[idx][Z] < -8) { // min possible value ps2_mouse[3] = -8; } else if(mouse_pos[idx][Z] > 7) { // max possible value ps2_mouse[3] = 7; } else ps2_mouse[3] = mouse_pos[idx][Z]; // collect movement info and send at predefined rate if(!(ps2_mouse[0]==0x08 && ps2_mouse[1]==0 && ps2_mouse[2]==0 && ps2_mouse[3]==0) && user_io_dip_switch1()) iprintf("PS2 MOUSE(%d): %x %d %d %d\n", idx, ps2_mouse[0], ps2_mouse[1], ps2_mouse[2], ps2_mouse[3]); // old message sends the movements for all mice spi_uio_cmd_cont(UIO_MOUSE); spi8(ps2_mouse[0]); spi8(ps2_mouse[1]); spi8(ps2_mouse[2]); DisableIO(); // new message with Intellimouse PS2 message spi_uio_cmd_cont(UIO_MOUSE0_EXT+idx); spi8(ps2_mouse[0]); spi8(ps2_mouse[1]); spi8(ps2_mouse[2]); spi8(ps2_mouse[3]); DisableIO(); // reset counters mouse_flags[idx] = 0; mouse_pos[idx][X] = mouse_pos[idx][Y] = mouse_pos[idx][Z] = 0; } } } } if(core_type == CORE_TYPE_8BIT) { handle_ps2_typematic_repeat(); handle_ps2_kbd_commands(); handle_ps2_mouse_commands(); } if(core_type == CORE_TYPE_ARCHIE) archie_poll(); if(core_features & FEAT_IDE_MASK) { unsigned char c1; EnableFpga(); c1 = SPI(0); // cmd request SPI(0); SPI(0); SPI(0); SPI(0); SPI(0); DisableFpga(); HandleHDD(c1, 0, 1); } if((core_type == CORE_TYPE_MINIMIG2) || (core_type == CORE_TYPE_MIST2) || (core_type == CORE_TYPE_ARCHIE) || (core_type == CORE_TYPE_8BIT)) { if(CheckTimer(rtc_timer)) { rtc_timer = GetTimer(RTC_FREQ); user_io_send_rtc(); } } if(CheckTimer(led_timer)) { led_timer = GetTimer(LED_FREQ); uint8_t leds = user_io_kbdled_get_status(); if((leds & KBD_LED_FLAG_MASK) != KBD_LED_FLAG_STATUS) leds = 0; if((keyboard_leds & KBD_LED_CAPS_MASK) != (leds & KBD_LED_CAPS_MASK)) hid_set_kbd_led(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)) hid_set_kbd_led(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)) hid_set_kbd_led(HID_LED_SCROLL_LOCK, (leds & KBD_LED_SCRL_CONTROL) ? leds & KBD_LED_SCRL_STATUS : scrl_status); keyboard_leds = leds; } // check for long press > 1 sec on menu button // and toggle scandoubler on/off then static unsigned long timer = 1; static unsigned char ypbpr_toggle = 0; if(MenuButton()) { if(timer == 1) timer = GetTimer(1000); else if(timer != 2) { if(CheckTimer(timer)) { // toggle video mode bit mist_cfg.scandoubler_disable = !mist_cfg.scandoubler_disable; timer = 2; user_io_send_buttons(1); OsdDisableMenuButton(1); VIDEO_ALTERED_VAR |= 1; VIDEO_SD_DISABLE_VAR = mist_cfg.scandoubler_disable; } } if(UserButton()) { if(!ypbpr_toggle) { // toggle video mode bit mist_cfg.ypbpr = !mist_cfg.ypbpr; timer = 2; ypbpr_toggle = 1; user_io_send_buttons(1); OsdDisableMenuButton(1); VIDEO_ALTERED_VAR |= 2; VIDEO_YPBPR_VAR = mist_cfg.ypbpr; } } else { ypbpr_toggle = 0; } } else { timer = 1; OsdDisableMenuButton(0); ypbpr_toggle = 0; } #ifdef HAVE_HDMI if (hdmi_detected) { if(CheckTimer(hdmi_timer)) { hdmi_timer = GetTimer(HDMI_FREQ); HDMITX_DevLoopProc(); if ((i2c_flags & 1) != hdmi_hiclk) { hdmi_hiclk = i2c_flags & 1; if (hdmi_detected) HDMITX_ChangeVideoTiming(hdmi_hiclk ? 16 : 1); } } } #endif } char user_io_dip_switch1() { return(((Buttons() & 2)?1:0) || DEBUG_MODE); } static void send_keycode(unsigned short code) { if((core_type == CORE_TYPE_MINIMIG) || (core_type == CORE_TYPE_MINIMIG2)) { // amiga has "break" marker in msb if(code & BREAK) code = (code & 0xff) | 0x80; // send immediately if possible if(CheckTimer(kbd_timer) &&(kbd_fifo_w == kbd_fifo_r) ) kbd_fifo_minimig_send(code); else kbd_fifo_enqueue(code); } if(core_type == CORE_TYPE_MIST) { // atari has "break" marker in msb ikbd_keyboard((code & BREAK) ? ((code & 0xff) | 0x80) : code); } if((core_type == CORE_TYPE_8BIT) || (core_type == CORE_TYPE_MIST2)) { // send ps2 keycodes for those cores that prefer ps2 spi_uio_cmd_cont(UIO_KEYBOARD); // "pause" has a complex code if((code&0xff) == 0x77) { // pause does not have a break code if(!(code & BREAK)) { // Pause key sends E11477E1F014E077 static const unsigned char c[] = { 0xe1, 0x14, 0x77, 0xe1, 0xf0, 0x14, 0xf0, 0x77, 0x00 }; const unsigned char *p = c; iprintf("PS2 KBD "); while(*p) { iprintf("%x ", *p); spi8(*p++); } iprintf("\n"); } } else { if (user_io_dip_switch1()) { iprintf("PS2 KBD "); if(code & EXT) iprintf("e0 "); if(code & BREAK) iprintf("f0 "); iprintf("%x\n", code & 0xff); } if(code & EXT) // prepend extended code flag if required spi8(0xe0); if(code & BREAK) // prepend break code if required if (ps2_kbd_scan_set == 1) code |= 0x80; else spi8(0xf0); spi8(code & 0xff); // send code itself } DisableIO(); } if(core_type == CORE_TYPE_ARCHIE) archie_kbd(code); } void user_io_mouse(unsigned char idx, unsigned char b, char x, char y, char z) { // send mouse data as minimig expects it if((core_type == CORE_TYPE_MINIMIG) || (core_type == CORE_TYPE_MINIMIG2)) { mouse_pos[idx][X] += x; mouse_pos[idx][Y] += y; mouse_pos[idx][Z] += z; mouse_flags[idx] |= 0x80 | (b&7); } // 8 bit core expects ps2 like data if((core_type == CORE_TYPE_8BIT) || (core_type == CORE_TYPE_MIST2)) { mouse_pos[idx][X] += x; mouse_pos[idx][Y] -= y; // ps2 y axis is reversed over usb mouse_pos[idx][Z] += z; mouse_flags[idx] |= 0x08 | (b&7); } // send mouse data as mist expects it if(core_type == CORE_TYPE_MIST) ikbd_mouse(b, x, y); if(core_type == CORE_TYPE_ARCHIE) archie_mouse(b, x, y); } // check if this is a key that's supposed to be suppressed // when emulation is active static unsigned char is_emu_key(unsigned char c, unsigned alt) { static const unsigned char m[] = { JOY_RIGHT, JOY_LEFT, JOY_DOWN, JOY_UP }; static const unsigned char m2[] = { 0x5A, JOY_DOWN, 0x5C, JOY_LEFT, 0x5D, JOY_DOWN, 0x5E, JOY_RIGHT, 0x60, JOY_UP, 0x5F, JOY_BTN1, 0x61, JOY_BTN2 }; if(emu_mode == EMU_NONE) return 0; if(alt) { for(int i=0; i<(sizeof(m2)/sizeof(m2[0])); i +=2) if(c == m2[i]) return m2[i+1]; } else { // direction keys R/L/D/U if(c >= 0x4f && c <= 0x52) return m[c-0x4f]; } return 0; } /* 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) static unsigned short keycode(unsigned char in) { if((core_type == CORE_TYPE_MINIMIG) || (core_type == CORE_TYPE_MINIMIG2)) return usb2amiga(in); if(core_type == CORE_TYPE_MIST) return usb2atari[in]; if(core_type == CORE_TYPE_ARCHIE) return usb2archie[in]; if((core_type == CORE_TYPE_8BIT) || (core_type == CORE_TYPE_MIST2)) return usb2ps2code(in); return MISS; } static void check_reset(unsigned short modifiers, char useKeys) { unsigned short combo[] = { 0x45, // lctrl+lalt+ralt 0x89, // lctrl+lgui+rgui 0x105, // lctrl+lalt+del }; if((modifiers & ~2)==combo[useKeys]) { if(modifiers & 2) // with lshift - MiST reset { if(mist_cfg.keep_video_mode) VIDEO_KEEP_VAR = VIDEO_KEEP_VALUE; MCUReset(); // HW reset for(;;); } switch(core_type) { case CORE_TYPE_MINIMIG: case CORE_TYPE_MINIMIG2: OsdReset(RESET_NORMAL); break; case CORE_TYPE_8BIT: kbd_reset = 1; break; } } else { kbd_reset = 0; } } unsigned short modifier_keycode(unsigned char index) { /* usb modifer bits: 0 1 2 3 4 5 6 7 LCTRL LSHIFT LALT LGUI RCTRL RSHIFT RALT RGUI */ if((core_type == CORE_TYPE_MINIMIG) || (core_type == CORE_TYPE_MINIMIG2)) { static const unsigned short amiga_modifier[] = { 0x63, 0x60, 0x64, 0x66, 0x63, 0x61, 0x65, 0x67 }; return amiga_modifier[index]; } if(core_type == CORE_TYPE_MIST) { static const unsigned short atari_modifier[] = { 0x1d, 0x2a, 0x38, MISS, 0x1d, 0x36, 0x38, MISS }; return atari_modifier[index]; } if((core_type == CORE_TYPE_8BIT) || (core_type == CORE_TYPE_MIST2)) { static const unsigned short ps2_modifier[] = { 0x14, 0x12, 0x11, EXT|0x1f, EXT|0x14, 0x59, EXT|0x11, EXT|0x27 }; static const unsigned short ps2_modifier_set1[] = { 0x1d, 0x2a, 0x38, MISS, EXT|0x1d, 0x36, EXT|0x38, MISS }; return (ps2_kbd_scan_set == 1) ? ps2_modifier_set1[index] : ps2_modifier[index]; } if(core_type == CORE_TYPE_ARCHIE) { static const unsigned short archie_modifier[] = { 0x36, 0x4c, 0x5e, MISS, 0x61, 0x58, 0x60, MISS }; return archie_modifier[index]; } return MISS; } void user_io_osd_key_enable(char on) { iprintf("OSD is now %s\n", on?"visible":"invisible"); osd_is_visible = on; } static char key_used_by_osd(unsigned short s) { // this key is only used to open the OSD and has no keycode if((s & OSD_OPEN) && !(s & 0xff)) return true; // no keys are suppressed if the OSD is inactive if(!osd_is_visible) return false; // in atari mode eat all keys if the OSD is online, // else none as it's up to the core to forward keys // to the OSD return((core_type == CORE_TYPE_MIST) || (core_type == CORE_TYPE_MIST2) || (core_type == CORE_TYPE_ARCHIE) || (core_type == CORE_TYPE_8BIT)); } static char kr_fn_table[] = { 0x54, 0x48, // pause/break 0x55, 0x46, // prnscr 0x50, 0x4a, // home 0x4f, 0x4d, // end 0x52, 0x4b, // pgup 0x51, 0x4e, // pgdown 0x3a, 0x44, // f11 0x3b, 0x45, // f12 0x3c, 0x6c, // EMU_MOUSE 0x3d, 0x6d, // EMU_JOY0 0x3e, 0x6e, // EMU_JOY1 0x3f, 0x6f, // EMU_NONE //Emulate keypad for A600 0x1E, 0x59, //KP1 0x1F, 0x5A, //KP2 0x20, 0x5B, //KP3 0x21, 0x5C, //KP4 0x22, 0x5D, //KP5 0x23, 0x5E, //KP6 0x24, 0x5F, //KP7 0x25, 0x60, //KP8 0x26, 0x61, //KP9 0x27, 0x62, //KP0 0x2D, 0x56, //KP- 0x2E, 0x57, //KP+ 0x31, 0x55, //KP* 0x2F, 0x68, //KP( 0x30, 0x69, //KP) 0x37, 0x63, //KP. 0x28, 0x58 //KP Enter }; static void keyrah_trans(unsigned char *m, unsigned char *k) { static char keyrah_fn_state = 0; char fn = 0; char empty = 1; char rctrl = 0; int i = 0; while(i<6) { if((k[i] == 0x64) || (k[i] == 0x32)) { if(k[i] == 0x64) fn = 1; if(k[i] == 0x32) rctrl = 1; for(int n = i; n<5; n++) k[n] = k[n+1]; k[5] = 0; } else { if(k[i]) empty = 0; i++; } } if(fn) { for(i=0; i<6; i++) { for(int n = 0; n<(sizeof(kr_fn_table)/(2*sizeof(kr_fn_table[0]))); n++) { if(k[i] == kr_fn_table[n*2]) k[i] = kr_fn_table[(n*2)+1]; } } } else { // free these keys for core usage for(i=0; i<6; i++) { if(k[i] == 0x53) k[i] = 0x68; if(k[i] == 0x47) k[i] = 0x69; if(k[i] == 0x49) k[i] = 0x6b; // workaround! } } *m = rctrl ? (*m) | 0x10 : (*m) & ~0x10; if(fn) { keyrah_fn_state |= 1; if(*m || !empty) keyrah_fn_state |= 2; } else { if(keyrah_fn_state == 1) { if((core_type == CORE_TYPE_MINIMIG) || (core_type == CORE_TYPE_MINIMIG2)) { send_keycode(KEY_MENU); send_keycode(BREAK | KEY_MENU); } else { OsdKeySet(KEY_MENU); } } keyrah_fn_state = 0; } } //Keyrah v2: USB\VID_18D8&PID_0002\A600/A1200_MULTIMEDIA_EXTENSION_VERSION #define KEYRAH_ID (mist_cfg.keyrah_mode && (((((uint32_t)vid)<<16) | pid) == mist_cfg.keyrah_mode)) void user_io_kbd(unsigned char m, unsigned char *k, uint8_t priority, unsigned short vid, unsigned short pid) { static char caps=0; // ignore lower priority clears if higher priority key was pressed if(m==0 && (k[0] + k[1] + k[2] + k[3] + k[4] + k[5])==0) { if (priority > latest_keyb_priority) return; // lower number = higher priority } latest_keyb_priority = priority; // set for next call char keyrah = KEYRAH_ID ? 1 : 0; if(emu_mode == EMU_MOUSE) keyrah <<= 1; if(keyrah) keyrah_trans(&m, k); if(mist_cfg.amiga_mod_keys) { // bit 0 1 2 3 4 5 6 7 // key LCTRL LSHIFT LALT LGUI RCTRL RSHIFT RALT RGUI // 1 2 4 8 10 20 40 80 unsigned char m_in = m; switch(mist_cfg.amiga_mod_keys) { case 1: // swap RALT/RGUI & LALT/LGUI m = ((m & 0x40) << 1) | ((m & 0x80) >> 1) | m & 0x20 | m & 0x10 | ((m & 0x8) >> 1) | ((m & 0x4) << 1) | m & 0x2; break; case 2: // swap RGUI/RCTRL & LGUI/CTRL m = ((m & 0x10) << 3) | ((m & 0x80) >> 3) | m & 0x20 | m & 0x40 | ((m & 0x8) >> 3) | ((m & 0x1) << 3) | m & 0x2 | m & 0x4; break; case 3: // Map Alt to GUI, Ctrl to Alt, GUI to Ctrl m = ((m & 0x10) << 2) | ((m & 0x80) >> 3) | (m & 0x20) | ((m & 0x40) << 1) | ((m & 0x8) >> 3) | ((m & 0x1) << 2) | (m & 0x2) | ((m & 0x4) << 1) ; break; default: break; } // CAPSLOCK/LCTRL mapping // First map Caps Lock to L Ctrl for(char i=0;i<6;i++) { if(k[i] == 0x39) { m |= 0x1; k[i] = 0; caps|=0x07; } else if (k[i]) /* any other (non-qualifier) key pressed? */ caps|=0x80; } switch(mist_cfg.amiga_mod_keys) { case 1: // Map L Ctrl to Caps Lock if(m_in & 0x1) { for(char i=0;i<6;i++) { if(k[i] == 0) { k[i] = 0x39; break; } } } break; case 2: case 3: // If Caps Lock is pressed and released with no other key events in between, generate a Caps Lock keypress. // (In modern keyboard firmware parlance, the Caps Lock key has "mod-tap") if(!(m & 0x01)) { // is Caps Lock (afer mapping to L Ctrl) no longer pressed? for(char i=0;i<6;++i) { if(k[i] == 0) { // We have an empty slot in the key report if(caps&0x80) // Were other (non-modfier) keys were pressed before capslock was released? caps=0; else if(caps) { k[i]=0x39; --caps; } break; } } } break; default: break; } } unsigned short reset_m = m; for(char i=0;i<6;i++) if(k[i] == 0x4c) reset_m |= 0x100; check_reset(reset_m, KEYRAH_ID ? 1 : mist_cfg.reset_combo); if( (core_type == CORE_TYPE_MINIMIG) || (core_type == CORE_TYPE_MINIMIG2) || (core_type == CORE_TYPE_MIST) || (core_type == CORE_TYPE_MIST2) || (core_type == CORE_TYPE_ARCHIE) || (core_type == CORE_TYPE_8BIT)) { //iprintf("KBD: %d\n", m); //hexdump(k, 6, 0); char keycodes[6] = { 0,0,0,0,0,0 }; uint16_t keycodes_ps2[6] = { 0,0,0,0,0,0 }; char i, j; // remap keycodes if requested for(i=0;(i<6) && k[i];i++) { for(j=0;j>5)+1)*250); } } /* translates a USB modifiers into scancodes */ void add_modifiers(uint8_t mod, uint16_t* keys_ps2) { uint8_t i; uint8_t offset = 1; uint8_t index = 0; while(offset) { if(mod&offset) { uint16_t ps2_value = modifier_keycode(index); if(ps2_value != MISS) { if(ps2_value & EXT) ps2_value = (0xE000 | (ps2_value & 0xFF)); for(i=0; i<4; i++) { if(keys_ps2[i]==0) { keys_ps2[i] = ps2_value; break; } } } } offset <<= 1; index++; } } char user_io_key_remap(char *s, char action, int tag) { if (action == INI_SAVE) return 0; // s is a string containing two comma separated hex numbers if((strlen(s) != 5) && (s[2]!=',')) { ini_parser_debugf("malformed entry %s", s); return 0; } char i; for(i=0;i3) && *ext) { if(!_strnicmp(ext3,ext,3)) return idx; if(strlen(ext)<=3) break; idx++; ext +=3; } return 0; } void user_io_change_into_core_dir(void) { if (arc_get_dirname()[0]) { strcpy(s, "/"); strcat(s, arc_get_dirname()); } else { user_io_create_config_name(s, 0, CONFIG_ROOT); } // try to change into subdir named after the core iprintf("Trying to open work dir \"%s\"\n", s); ChangeDirectoryName(s); } static char user_io_i2c_stat(unsigned char *data) { unsigned char c, d; while(1) { spi_uio_cmd_cont(UIO_I2C_GET); c = SPI(0xff); d = SPI(0xff); DisableIO(); if (c & 1) { // end flag if (data) *data = d; i2c_flags = c >> 2; return (c & 2); // ack flag } } } char user_io_i2c_write(unsigned char addr, unsigned char subaddr, unsigned char data) { spi_uio_cmd_cont(UIO_I2C_SEND); spi8(addr << 1); spi8(subaddr); spi8(data); DisableIO(); return user_io_i2c_stat(0); } char user_io_i2c_read(unsigned char addr, unsigned char subaddr, unsigned char *data) { spi_uio_cmd_cont(UIO_I2C_SEND); spi8(addr << 1 | 1); // read request spi8(subaddr); spi8(0xff); DisableIO(); return user_io_i2c_stat(data); } bool user_io_hdmi_detected() { #ifdef HAVE_HDMI return hdmi_detected; #else return false; #endif }