#include "stdio.h" #include "string.h" #include "../../hardware.h" #include "../../fpga_io.h" #include "../../menu.h" #include "archie.h" #include "../../debug.h" #include "../../user_io.h" #include "../../input.h" #define CONFIG_FILENAME "ARCHIE.CFG" typedef struct { unsigned long system_ctrl; // system control word char rom_img[1024]; // rom image file name } archie_config_t; static archie_config_t config; #define archie_debugf(a, ...) printf("\033[1;31mARCHIE: " a "\033[0m\n", ##__VA_ARGS__) // #define archie_debugf(a, ...) #define archie_x_debugf(a, ...) printf("\033[1;32mARCHIE: " a "\033[0m\n", ##__VA_ARGS__) enum state { STATE_HRST, STATE_RAK1, STATE_RAK2, STATE_IDLE, STATE_WAIT4ACK1, STATE_WAIT4ACK2, STATE_HOLD_OFF } kbd_state; // archie keyboard controller commands #define HRST 0xff #define RAK1 0xfe #define RAK2 0xfd #define RQPD 0x40 // mask 0xf0 #define PDAT 0xe0 // mask 0xf0 #define RQID 0x20 #define KBID 0x80 // mask 0xc0 #define KDDA 0xc0 // new key down data, mask 0xf0 #define KUDA 0xd0 // new key up data, mask 0xf0 #define RQMP 0x22 // request mouse data #define MDAT 0x00 // mouse data, mask 0x80 #define BACK 0x3f #define NACK 0x30 // disable kbd scan, disable mouse #define SACK 0x31 // enable kbd scan, disable mouse #define MACK 0x32 // disable kbd scan, enable mouse #define SMAK 0x33 // enable kbd scan, enable mouse #define LEDS 0x00 // mask 0xf8 #define PRST 0x21 // nop #define QUEUE_LEN 8 static unsigned char tx_queue[QUEUE_LEN][2]; static unsigned char tx_queue_rptr, tx_queue_wptr; #define QUEUE_NEXT(a) ((a+1)&(QUEUE_LEN-1)) static unsigned long ack_timeout; static short mouse_x, mouse_y; #define FLAG_SCAN_ENABLED 0x01 #define FLAG_MOUSE_ENABLED 0x02 static unsigned char flags; // #define HOLD_OFF_TIME 2 #ifdef HOLD_OFF_TIME static unsigned long hold_off_timer; #endif const char *archie_get_rom_name(void) { char *p = strrchr(config.rom_img, '/'); if (!p) p = config.rom_img; else p++; return p; } void archie_set_ar(char i) { if (i) config.system_ctrl |= 1; else config.system_ctrl &= ~1; user_io_8bit_set_status((i ? -1 : 0), 2); } int archie_get_ar() { return config.system_ctrl & 1; } void archie_set_60(char i) { if (i) config.system_ctrl |= 0b1000; else config.system_ctrl &= ~0b1000; user_io_8bit_set_status((i ? -1 : 0), 0b10000); } int archie_get_60() { return config.system_ctrl & 0b1000; } void archie_set_afix(char i) { if (i) config.system_ctrl |= 0b10000; else config.system_ctrl &= ~0b10000; user_io_8bit_set_status((i ? -1 : 0), 0b100000); } int archie_get_afix() { return config.system_ctrl & 0b10000; } static int mswap = 0; void archie_set_mswap(char i) { mswap = i; } int archie_get_mswap() { return mswap; } void archie_set_amix(char i) { config.system_ctrl = (config.system_ctrl & ~0b110) | ((i & 3)<<1); user_io_8bit_set_status(config.system_ctrl << 1, 0b1100); } int archie_get_amix() { return (config.system_ctrl>>1) & 3; } void archie_save_config(void) { FileSaveConfig(CONFIG_FILENAME, &config, sizeof(config)); } void archie_set_rom(char *name) { if (!name) return; printf("archie_set_rom(%s)\n", name); // save file name strcpy(config.rom_img, name); user_io_file_tx(name, 1); } static void archie_kbd_enqueue(unsigned char state, unsigned char byte) { if (QUEUE_NEXT(tx_queue_wptr) == tx_queue_rptr) { archie_debugf("KBD tx queue overflow"); return; } //archie_debugf("KBD ENQUEUE %x (%x)", byte, state); tx_queue[tx_queue_wptr][0] = state; tx_queue[tx_queue_wptr][1] = byte; tx_queue_wptr = QUEUE_NEXT(tx_queue_wptr); } static void archie_kbd_tx(unsigned char state, unsigned char byte) { //archie_debugf("KBD TX %x (%x)", byte, state); spi_uio_cmd_cont(0x05); spi8(byte); DisableIO(); kbd_state = (enum state)state; ack_timeout = GetTimer(10); // 10ms timeout } static void archie_kbd_send(unsigned char state, unsigned char byte) { // don't send if we are waiting for an ack if ((kbd_state != STATE_WAIT4ACK1) && (kbd_state != STATE_WAIT4ACK2)) archie_kbd_tx(state, byte); else archie_kbd_enqueue(state, byte); } static void archie_kbd_reset(void) { archie_debugf("KBD reset"); tx_queue_rptr = tx_queue_wptr = 0; kbd_state = STATE_HRST; mouse_x = mouse_y = 0; flags = 0; } void archie_init(void) { archie_debugf("init"); user_io_8bit_set_status(1, UIO_STATUS_RESET); // set config defaults config.system_ctrl = 0; strcpy(config.rom_img, "Archie/RISCOS.ROM"); // try to load config from card int size = FileLoadConfig(CONFIG_FILENAME, 0, 0); if (size>0) { if (size == sizeof(archie_config_t)) { FileLoadConfig(CONFIG_FILENAME, &config, sizeof(archie_config_t)); } else archie_debugf("Unexpected config size %d != %d", size, sizeof(archie_config_t)); } else archie_debugf("No %s config found", CONFIG_FILENAME); archie_set_ar(archie_get_ar()); archie_set_amix(archie_get_amix()); archie_set_60(archie_get_60()); archie_set_afix(archie_get_afix()); // upload rom file archie_set_rom(config.rom_img); // upload ext file //user_io_file_tx("Archie/RISCOS.EXT", 2); user_io_file_tx("Archie/CMOS.DAT", 3); user_io_8bit_set_status(0, UIO_STATUS_RESET); /* int i; // try to open default floppies for (i = 0; i> 4)); archie_kbd_send(STATE_WAIT4ACK2, prefix | (code & 0x0f)); } void archie_mouse(unsigned char b, int16_t x, int16_t y) { //archie_debugf("KBD MOUSE X:%d Y:%d B:%d", x, y, b); // max values -64 .. 63 mouse_x += x; if (mouse_x > 63) mouse_x = 63; if (mouse_x < -64) mouse_x = -64; mouse_y -= y; if (mouse_y > 63) mouse_y = 63; if (mouse_y < -64) mouse_y = -64; // don't send anything yet if we are still in reset state if (kbd_state <= STATE_RAK2) { archie_debugf("KBD still in reset"); return; } // ignore any mouse movement if mouse is disabled or if nothing to report if ((flags & FLAG_MOUSE_ENABLED) && (mouse_x || mouse_y)) { // send asap if no pending byte if (kbd_state == STATE_IDLE) { archie_kbd_send(STATE_WAIT4ACK1, mouse_x & 0x7f); archie_kbd_send(STATE_WAIT4ACK2, mouse_y & 0x7f); mouse_x = mouse_y = 0; } } // ignore mouse buttons if key scanning is disabled if (flags & FLAG_SCAN_ENABLED) { static const uint8_t remap[] = { 0, 2, 1 }; static unsigned char buts = 0; uint8_t s; // map all three buttons for (s = 0; s<3; s++) { uint8_t mask = (1 << s); if ((b&mask) != (buts&mask)) { unsigned char prefix = (b&mask) ? KDDA : KUDA; archie_kbd_send(STATE_WAIT4ACK1, prefix | 0x07); archie_kbd_send(STATE_WAIT4ACK2, prefix | ((!mswap ^ !(get_key_mod() & (RGUI|LGUI))) ? s : remap[s])); } } buts = b; } } static void archie_check_queue(void) { if (tx_queue_rptr == tx_queue_wptr) return; archie_kbd_tx(tx_queue[tx_queue_rptr][0], tx_queue[tx_queue_rptr][1]); tx_queue_rptr = QUEUE_NEXT(tx_queue_rptr); } void archie_poll(void) { #ifdef HOLD_OFF_TIME if ((kbd_state == STATE_HOLD_OFF) && CheckTimer(hold_off_timer)) { archie_debugf("KBD resume after hold off"); kbd_state = STATE_IDLE; archie_check_queue(); } #endif // timeout waiting for ack? if ((kbd_state == STATE_WAIT4ACK1) || (kbd_state == STATE_WAIT4ACK2)) { if (CheckTimer(ack_timeout)) { if (kbd_state == STATE_WAIT4ACK1) archie_debugf(">>>> KBD ACK TIMEOUT 1ST BYTE <<<<"); if (kbd_state == STATE_WAIT4ACK2) archie_debugf(">>>> KBD ACK TIMEOUT 2ND BYTE <<<<"); kbd_state = STATE_IDLE; } } // timeout in reset sequence? if (kbd_state <= STATE_RAK2) { if (CheckTimer(ack_timeout)) { //archie_debugf("KBD timeout in reset state"); archie_kbd_send(STATE_RAK1, HRST); ack_timeout = GetTimer(20); // 20ms timeout } } spi_uio_cmd_cont(0x04); if (spi_in() == 0xa1) { unsigned char data = spi_in(); DisableIO(); //archie_debugf("KBD RX %x", data); switch (data) { // arm requests reset case HRST: archie_kbd_reset(); archie_kbd_send(STATE_RAK1, HRST); ack_timeout = GetTimer(20); // 20ms timeout break; // arm sends reset ack 1 case RAK1: if (kbd_state == STATE_RAK1) { archie_kbd_send(STATE_RAK2, RAK1); ack_timeout = GetTimer(20); // 20ms timeout } else kbd_state = STATE_HRST; break; // arm sends reset ack 2 case RAK2: if (kbd_state == STATE_RAK2) { archie_kbd_send(STATE_IDLE, RAK2); ack_timeout = GetTimer(20); // 20ms timeout } else kbd_state = STATE_HRST; break; // arm request keyboard id case RQID: archie_kbd_send(STATE_IDLE, KBID | 1); break; // arm acks first byte case BACK: if (kbd_state != STATE_WAIT4ACK1) { archie_debugf("KBD unexpected BACK, resetting KBD"); kbd_state = STATE_HRST; } else { #ifdef HOLD_OFF_TIME // wait some time before sending next byte archie_debugf("KBD starting hold off"); kbd_state = STATE_HOLD_OFF; hold_off_timer = GetTimer(10); // wait some time before sending next byte archie_debugf("KBD starting hold off"); kbd_state = STATE_HOLD_OFF; hold_off_timer = GetTimer(10); #else kbd_state = STATE_IDLE; archie_check_queue(); kbd_state = STATE_IDLE; archie_check_queue(); #endif } break; // arm acks second byte case NACK: case SACK: case MACK: case SMAK: if (((data == SACK) || (data == SMAK)) && !(flags & FLAG_SCAN_ENABLED)) { archie_debugf("KBD Enabling key scanning"); flags |= FLAG_SCAN_ENABLED; } if (((data == NACK) || (data == MACK)) && (flags & FLAG_SCAN_ENABLED)) { archie_debugf("KBD Disabling key scanning"); flags &= ~FLAG_SCAN_ENABLED; } if (((data == MACK) || (data == SMAK)) && !(flags & FLAG_MOUSE_ENABLED)) { archie_debugf("KBD Enabling mouse"); flags |= FLAG_MOUSE_ENABLED; } if (((data == NACK) || (data == SACK)) && (flags & FLAG_MOUSE_ENABLED)) { archie_debugf("KBD Disabling mouse"); flags &= ~FLAG_MOUSE_ENABLED; } // wait another 10ms before sending next byte #ifdef HOLD_OFF_TIME archie_debugf("KBD starting hold off"); kbd_state = STATE_HOLD_OFF; hold_off_timer = GetTimer(10); #else kbd_state = STATE_IDLE; archie_check_queue(); #endif break; } } else DisableIO(); }