#include "AT91SAM7S256.h" #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 "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 "utils.h" #include "usb/joymapping.h" // up to 16 key can be remapped #define MAX_REMAP 16 unsigned char key_remap_table[MAX_REMAP][2]; #define BREAK 0x8000 #define MAX_IMAGES 2 static IDXFile sd_image[MAX_IMAGES]; static char buffer[512]; static uint8_t buffer_drive_index = 0; static uint32_t buffer_lba = 0xffffffff; extern fileTYPE file; extern char s[40]; // 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; // core variant (mostly for arcades) static char core_mod = 0; // permanent state of adc inputs used for dip switches static unsigned char adc_state = 0; AT91PS_ADC a_pADC = AT91C_BASE_ADC; AT91PS_PMC a_pPMC = AT91C_BASE_PMC; // 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; #define VIDEO_KEEP_VALUE 0x87654321 #define video_keep (*(int*)0x0020FF10) #define video_altered (*(uint8_t*)0x0020FF14) #define video_sd_disable (*(uint8_t*)0x0020FF15) #define video_ypbpr (*(uint8_t*)0x0020FF16) // 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; char user_io_osd_is_visible() { return osd_is_visible; } static void PollOneAdc() { static unsigned char adc_cnt = 0xff; // fetch result from previous run if(adc_cnt != 0xff) { unsigned int result; // wait for end of convertion while(!(AT91C_BASE_ADC->ADC_SR & (1 << (4+adc_cnt)))); switch (adc_cnt) { case 0: result = AT91C_BASE_ADC->ADC_CDR4; break; case 1: result = AT91C_BASE_ADC->ADC_CDR5; break; case 2: result = AT91C_BASE_ADC->ADC_CDR6; break; case 3: result = AT91C_BASE_ADC->ADC_CDR7; break; } if(result < 128) adc_state |= (1< 128) adc_state &= ~(1<ADC_CHER = 1 << (4+adc_cnt); // Start conversion AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; } static void InitADC(void) { // Enable clock for interface AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_ADC; // Reset AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST; AT91C_BASE_ADC->ADC_CR = 0x0; // Set maximum startup time and hold time AT91C_BASE_ADC->ADC_MR = 0x0F1F0F00 | AT91C_ADC_LOWRES_8_BIT; // make sure we get the first values immediately PollOneAdc(); PollOneAdc(); PollOneAdc(); PollOneAdc(); } // poll one adc channel every 25ms static void PollAdc() { static long adc_timer = 0; if(CheckTimer(adc_timer)) { adc_timer = GetTimer(25); PollOneAdc(); } } void user_io_init() { // no sd card image selected, SD card accesses will go directly // to the card sd_image[0].file.size = 0; sd_image[1].file.size = 0; if(video_keep != VIDEO_KEEP_VALUE) video_altered = 0; video_keep = 0; // mark remap table as unused memset(key_remap_table, 0, sizeof(key_remap_table)); InitADC(); if(user_io_menu_button()) DEBUG_MODE_VAR = DEBUG_MODE ? 0 : DEBUG_MODE_VALUE; iprintf("debug_mode = %d\n", DEBUG_MODE); ikbd_init(); } unsigned char user_io_core_type() { return core_type; } char minimig_v1() { return(core_type == CORE_TYPE_MINIMIG); } char minimig_v2() { return(core_type == CORE_TYPE_MINIMIG2); } char user_io_create_config_name(char *s) { char *p = user_io_get_core_name(); if(p[0]) { strcpy(s, p); while(strlen(s) < 8) strcat(s, " "); strcat(s, "CFG"); return 0; } return 1; } char user_io_is_8bit_with_config_string() { return core_type_8bit_with_config_string; } static char core_name[16+1]; // max 16 bytes for core name char *user_io_get_core_name() { char *arc_core_name = arc_get_corename(); return *arc_core_name ? arc_core_name : core_name; } static void user_io_read_core_name() { core_name[0] = 0; if(user_io_is_8bit_with_config_string()) { char *p = user_io_8bit_get_string(0); // get core name if(p && p[0]) strcpy(core_name, p); } iprintf("Core name from FPGA is \"%s\"\n", core_name); } void user_io_set_core_mod(char mod) { core_mod = mod; } void user_io_send_core_mod() { iprintf("Sending core mod = %d\n", core_mod); spi_uio_cmd8(UIO_SET_MOD, core_mod); } void user_io_send_rtc(void) { uint8_t date[7]; //year,month,date,hour,min,sec,day uint8_t i; if (usb_rtc_get_time((uint8_t*)&date)) { //iprintf("Sending time of day %u:%02u:%02u %u.%u.%u\n", // date[3], date[4], date[5], date[2], date[1], 1900 + date[0]); spi_uio_cmd_cont(UIO_SET_RTC); spi8(bin2bcd(date[5])); // sec spi8(bin2bcd(date[4])); // min spi8(bin2bcd(date[3])); // hour spi8(bin2bcd(date[2])); // date spi8(bin2bcd(date[1])); // month spi8(bin2bcd(date[0]-100)); // year spi8(bin2bcd(date[6])-1); //day 1-7 -> 0-6 spi8(0x40); // flag DisableIO(); } } extern unsigned long iCurrentDirectory; // cluster number of current directory, 0 for root void user_io_detect_core_type() { core_name[0] = 0; EnableIO(); core_type = SPI(0xff); DisableIO(); rom_direct_upload = (core_type & 0x10) >> 4; // bit 4 - direct upload support core_type &= 0xef; if((core_type != CORE_TYPE_DUMB) && (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)) core_type = CORE_TYPE_UNKNOWN; switch(core_type) { case CORE_TYPE_UNKNOWN: iprintf("Unable to identify core (%x)!\n", core_type); break; 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(); // send a reset user_io_8bit_set_status(UIO_STATUS_RESET, ~0); // try to load config user_io_create_config_name(s); if(strlen(s) > 0) { iprintf("Loading config %.11s\n", s); if (FileOpen(&file, s)) { iprintf("Found config\n"); if(file.size <= 8) { ((unsigned long long*)sector_buffer)[0] = 0; FileRead(&file, sector_buffer); user_io_8bit_set_status(((unsigned long long*)sector_buffer)[0], ~1); } } else { user_io_8bit_set_status(arc_get_default(), ~1); } // check if there's a .rom present, send it via index 0 strcpy(s+8, "ROM"); if (FileOpenDir(&file, s, iCurrentDirectory) || FileOpen(&file, s)) data_io_file_tx(&file, 0); // check if there's a .ram present, send it via index -1 strcpy(s+8, "RAM"); if (FileOpen(&file, s)) data_io_file_tx(&file, -1); // check if there's a .vhd present strcpy(s+8, "VHD"); if (FileOpen(&file, s)) user_io_file_mount(&file, 0); else { // check for .HD0/1 files strcpy(s+8, "HD "); for (int i = 0; i < MAX_IMAGES; i++) { s[10] = '0'+i; if (FileOpen(&file, s)) user_io_file_mount(&file, i); } } } // release reset user_io_8bit_set_status(0, UIO_STATUS_RESET); } break; } } 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]; } 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 usb2ps2[k]; } void user_io_analog_joystick(unsigned char joystick, char valueX, char valueY) { if(core_type == CORE_TYPE_8BIT) { spi_uio_cmd8_cont(UIO_ASTICK, joystick); spi8(valueX); spi8(valueY); 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) 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, uint16_t map) { // "only" 6 joysticks are supported if(joystick > 5) return; //iprintf("ext j%d: %x\n", joystick, map); spi_uio_cmd32(UIO_JOYSTICK0_EXT + joystick, 0x0000ffff & map); } 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, unsigned char 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)); } // 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;iname, file->size, index); memcpy(&sd_image[index].file, file, sizeof(fileTYPE)); // build index for fast random access IDXIndex(&sd_image[index]); } else { iprintf("unmounting file in slot %d\n", index); sd_image[index].file.size = 0; } buffer_lba = 0xffffffff; // 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(file ? file->size : 0); spi32le(0); // reserved for future expansion 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(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 // clear buffer buffer[0] = 0; 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 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] & 0x03); DisableIO(); } spi_uio_cmd_cont(UIO_MOUSE0_EXT + idx); spi8(x); spi8(y); spi8(mouse_flags[idx] & 0x03); 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(); } // 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 c = user_io_sd_get_status(&lba, &drive_index); // 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) && !(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)) { uint8_t wr_buf[512]; if(user_io_dip_switch1()) iprintf("SD WR %d\n", lba); // if we write the sector stored in the read buffer, then // invalidate the cache if(buffer_lba == lba && buffer_drive_index == drive_index) { buffer_lba = 0xffffffff; } // Fetch sector data from FPGA ... spi_uio_cmd_cont(UIO_SECTOR_WR); spi_block_read(wr_buf); DisableIO(); // ... and write it to disk DISKLED_ON; #if 1 if(sd_image[drive_index].file.size) { if(((sd_image[drive_index].file.size-1) >> 9) >= lba) { IDXSeek(&sd_image[drive_index], lba); IDXWrite(&sd_image[drive_index], wr_buf); } } else MMC_Write(lba, wr_buf); #else hexdump(wr_buf, 512, 0); #endif DISKLED_OFF; } } // Read from file/SD Card if((c & 0x03) == 0x01) { if(user_io_dip_switch1()) iprintf("SD RD %d\n", lba); // invalidate cache if it stores data from another drive if (drive_index != buffer_drive_index) buffer_lba = 0xffffffff; // are we using a file as the sd card image? // (C64 floppy does that ...) if(buffer_lba != lba) { DISKLED_ON; if(sd_image[drive_index].file.size) { if(((sd_image[drive_index].file.size-1) >> 9) >= lba) { IDXSeek(&sd_image[drive_index], lba); IDXRead(&sd_image[drive_index], buffer); } } else { // sector read // read sector from sd card if it is not already present in // the buffer MMC_Read(lba, buffer); } buffer_lba = lba; DISKLED_OFF; } if(buffer_lba == lba) { // hexdump(buffer, 32, 0); // data is now stored in buffer. send it to fpga spi_uio_cmd_cont(UIO_SECTOR_RD); spi_block_write(buffer); DisableIO(); // the end of this transfer acknowledges the FPGA internal // sd card emulation } // just load the next sector now, so it may be prefetched // for the next request already DISKLED_ON; if(sd_image[drive_index].file.size) { // but check if it would overrun on the file if(((sd_image[drive_index].file.size-1) >> 9) > lba) { IDXSeek(&sd_image[drive_index], lba+1); IDXRead(&sd_image[drive_index], buffer); buffer_lba = lba + 1; } } else { // sector read // read sector from sd card if it is not already present in // the buffer MMC_Read(lba+1, buffer); buffer_lba = lba+1; } buffer_drive_index = drive_index; DISKLED_OFF; } } } if((core_type == CORE_TYPE_8BIT) || (core_type == CORE_TYPE_MIST2)) { // frequently check ps2 mouse for events if(CheckTimer(mouse_timer)) { mouse_timer = GetTimer(MOUSE_FREQ); for (char idx=0; idx<2; idx++) { // has ps2 mouse data been updated in the meantime if(mouse_flags[idx] & 0x08) { unsigned char ps2_mouse[4]; // PS2 format: // YOvfl, XOvfl, dy8, dx8, 1, mbtn, rbtn, lbtn // dx[7:0] // dy[7:0] // 0,0,btn5,btn,dz[3:0] ps2_mouse[0] = mouse_flags[idx]; // ------ X axis ----------- // store sign bit in first byte ps2_mouse[0] |= (mouse_pos[idx][X] < 0)?0x10:0x00; if(mouse_pos[idx][X] < -255) { // min possible value + overflow flag ps2_mouse[0] |= 0x40; ps2_mouse[1] = -128; } else if(mouse_pos[idx][X] > 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_ARCHIE) archie_poll(); 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(user_io_menu_button()) { 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 |= 1; video_sd_disable = mist_cfg.scandoubler_disable; } } if(adc_state & 8) { 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 |= 2; video_ypbpr = mist_cfg.ypbpr; } } else { ypbpr_toggle = 0; } } else { timer = 1; OsdDisableMenuButton(0); ypbpr_toggle = 0; } } char user_io_dip_switch1() { return(((adc_state & 2)?1:0) || DEBUG_MODE); } char user_io_menu_button() { return((adc_state & 4)?1:0); } char user_io_user_button() { return((!user_io_menu_button() && (adc_state & 8))?1:0); } 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 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&3); } // 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) 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; } 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 = VIDEO_KEEP_VALUE; *AT91C_RSTC_RCR = 0xA5 << 24 | AT91C_RSTC_PERRST | AT91C_RSTC_PROCRST | AT91C_RSTC_EXTRST; // 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 }; return 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) { // 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); 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); static unsigned char modifier = 0, pressed[6] = { 0,0,0,0,0,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;j3) && *ext) { if(!strncmp(file->name+8,ext,3)) return idx; if(strlen(ext)<=3) break; idx++; ext +=3; } return 0; } extern unsigned char nDirEntries; extern DIRENTRY DirEntry[MAXDIRENTRIES]; extern unsigned char iSelectedEntry; // this should be moved into fat.c? void user_io_change_into_core_dir(void) { char s[13]; // 8+3+'\0' user_io_create_config_name(s); // try to change into subdir named after the core strcpy(s+8, " "); iprintf("Trying to open work dir \"%s\"\n", s); ScanDirectory(SCAN_INIT, "", SCAN_DIR | FIND_DIR); unsigned short last_StartCluster = 0; // no return flag :(, so scan 10 times blindly... for(;;) { char res = 0; #if 0 // debug iprintf("new list nDirentries=%d iSelected=%d\n", nDirEntries, iSelectedEntry); for(int i=0;i