/* https://www.kernel.org/doc/Documentation/input/atarikbd.txt ikbd ToDo: Feature Example using/needing it impl. tested --------------------------------------------------------------------- mouse y at bottom Bolo X X mouse button key events Goldrunner/A_008 X X joystick interrogation mode Xevious/A_004 X X Absolute mouse mode Backlash/A_008, A-Ball/A50 disable mouse ? X disable joystick ? X Joysticks also generate Goldrunner X X mouse button events! Pause (cmd 0x13) Wings of Death/A_427 */ #include #include "bas_printf.h" #include "bas_string.h" #include "user_io.h" //#include "hardware.h" #include "ikbd.h" // atari ikbd stuff #define IKBD_STATE_JOYSTICK_EVENT_REPORTING 0x01 #define IKBD_STATE_MOUSE_Y_BOTTOM 0x02 #define IKBD_STATE_MOUSE_BUTTON_AS_KEY 0x04 // mouse buttons act like keys #define IKBD_STATE_MOUSE_DISABLED 0x08 #define IKBD_STATE_MOUSE_ABSOLUTE 0x10 #define IKBD_DEFAULT IKBD_STATE_JOYSTICK_EVENT_REPORTING #define QUEUE_LEN 16 // power of 2! static unsigned char tx_queue[QUEUE_LEN]; static unsigned char wptr = 0, rptr = 0; // structure to keep track of ikbd state static struct { unsigned char cmd; unsigned char state; unsigned char expect; // joystick state unsigned char joystick[2]; // mouse state unsigned short mouse_pos_x, mouse_pos_y; unsigned char mouse_buttons; } ikbd; // #define IKBD_DEBUG void ikbd_init() { // reset ikbd state memset(&ikbd, 0, sizeof(ikbd)); ikbd.state = IKBD_DEFAULT; } static void enqueue(unsigned char b) { if(((wptr + 1)&(QUEUE_LEN-1)) == rptr) { xprintf("IKBD: !!!!!!! tx queue overflow !!!!!!!!!\n"); return; } tx_queue[wptr] = b; wptr = (wptr+1)&(QUEUE_LEN-1); } // convert internal joystick format into atari ikbd format static unsigned char joystick_map2ikbd(unsigned in) { unsigned char out = 0; if(in & JOY_UP) out |= 0x01; if(in & JOY_DOWN) out |= 0x02; if(in & JOY_LEFT) out |= 0x04; if(in & JOY_RIGHT) out |= 0x08; if(in & JOY_BTN1) out |= 0x80; return out; } // process inout from atari core into ikbd void ikbd_handle_input(unsigned char cmd) { // expecting a second byte for command if(ikbd.expect) { ikbd.expect--; // last byte of command received if(!ikbd.expect) { switch(ikbd.cmd) { case 0x07: // set mouse button action xprintf("IKBD: mouse button action = %x\n", cmd); // bit 2: Mouse buttons act like keys (LEFT=0x74 & RIGHT=0x75) if(cmd & 0x04) ikbd.state |= IKBD_STATE_MOUSE_BUTTON_AS_KEY; else ikbd.state &= ~IKBD_STATE_MOUSE_BUTTON_AS_KEY; break; case 0x80: // ibkd reset // reply "everything is ok" enqueue(0xf0); break; default: break; } } return; } ikbd.cmd = cmd; switch(cmd) { case 0x07: xprintf("IKBD: Set mouse button action"); ikbd.expect = 1; break; case 0x08: xprintf("IKBD: Set relative mouse positioning"); ikbd.state &= ~IKBD_STATE_MOUSE_DISABLED; ikbd.state &= ~IKBD_STATE_MOUSE_ABSOLUTE; break; case 0x09: xprintf("IKBD: Set absolute mouse positioning"); ikbd.state &= ~IKBD_STATE_MOUSE_DISABLED; ikbd.state |= IKBD_STATE_MOUSE_ABSOLUTE; ikbd.expect = 4; break; case 0x0b: xprintf("IKBD: Set Mouse threshold"); ikbd.expect = 2; break; case 0x0f: xprintf("IKBD: Set Y at bottom"); ikbd.state |= IKBD_STATE_MOUSE_Y_BOTTOM; break; case 0x10: xprintf("IKBD: Set Y at top"); ikbd.state &= ~IKBD_STATE_MOUSE_Y_BOTTOM; break; case 0x12: xprintf("IKBD: Disable mouse"); ikbd.state |= IKBD_STATE_MOUSE_DISABLED; break; case 0x14: xprintf("IKBD: Set Joystick event reporting"); ikbd.state |= IKBD_STATE_JOYSTICK_EVENT_REPORTING; break; case 0x15: xprintf("IKBD: Set Joystick interrogation mode"); ikbd.state &= ~IKBD_STATE_JOYSTICK_EVENT_REPORTING; break; case 0x16: // interrogate joystick // send reply enqueue(0xfd); enqueue(joystick_map2ikbd(ikbd.joystick[0])); enqueue(joystick_map2ikbd(ikbd.joystick[1])); break; case 0x1a: xprintf("IKBD: Disable joysticks"); ikbd.state &= ~IKBD_STATE_JOYSTICK_EVENT_REPORTING; break; case 0x1c: xprintf("IKBD: Interrogate time of day"); enqueue(0xfc); enqueue(0x13); // year bcd enqueue(0x03); // month bcd enqueue(0x07); // day bcd enqueue(0x20); // hour bcd enqueue(0x58); // minute bcd enqueue(0x00); // second bcd break; case 0x80: xprintf("IKBD: Reset"); ikbd.expect = 1; ikbd.state = IKBD_DEFAULT; break; default: xprintf("IKBD: unknown command: %x\n", cmd); break; } } void ikbd_poll(void) { static int mtimer = 0; if(CheckTimer(mtimer)) { mtimer = GetTimer(10); // check for incoming ikbd data EnableIO(); SPI(UIO_IKBD_IN); while(SPI(0)) ikbd_handle_input(SPI(0)); DisableIO(); } // send data from queue if present if(rptr == wptr) return; // transmit data from queue EnableIO(); SPI(UIO_IKBD_OUT); SPI(tx_queue[rptr]); DisableIO(); rptr = (rptr+1)&(QUEUE_LEN-1); } void ikbd_joystick(unsigned char joystick, unsigned char map) { // todo: suppress events for joystick 0 as long as mouse // is enabled? if(ikbd.state & IKBD_STATE_JOYSTICK_EVENT_REPORTING) { #ifdef IKBD_DEBUG xprintf("IKBD: joy %d %x\n", joystick, map); #endif // only report joystick data for joystick 0 if the mouse is disabled if((ikbd.state & IKBD_STATE_MOUSE_DISABLED) || (joystick == 1)) { enqueue(0xfe + joystick); enqueue(joystick_map2ikbd(map)); } if(!(ikbd.state & IKBD_STATE_MOUSE_DISABLED)) { // the fire button also generates a mouse event if // mouse reporting is enabled if((map & JOY_BTN1) != (ikbd.joystick[joystick] & JOY_BTN1)) { // generate mouse event (ikbd_joystick_buttons is evaluated inside // user_io_mouse) ikbd.joystick[joystick] = map; ikbd_mouse(0, 0, 0); } } } #ifdef IKBD_DEBUG else xprintf("IKBD: no monitor, drop joy %d %x\n", joystick, map); #endif // save state of joystick for interrogation mode ikbd.joystick[joystick] = map; } void ikbd_keyboard(unsigned char code) { #ifdef IKBD_DEBUG xprintf("IKBD: send keycode %x%s\n", code&0x7f, (code&0x80)?" BREAK":""); #endif enqueue(code); } void ikbd_mouse(uint8_t b, int8_t x, int8_t y) { if(ikbd.state & IKBD_STATE_MOUSE_DISABLED) return; // joystick and mouse buttons are wired together in // atari st b |= (ikbd.joystick[0] & JOY_BTN1)?1:0; b |= (ikbd.joystick[1] & JOY_BTN1)?2:0; static unsigned char b_old = 0; // monitor state of two mouse buttons if(b != b_old) { // check if mouse buttons are supposed to be treated like keys if(ikbd.state & IKBD_STATE_MOUSE_BUTTON_AS_KEY) { // Mouse buttons act like keys (LEFT=0x74 & RIGHT=0x75) // handle left mouse button if((b ^ b_old) & 1) ikbd_keyboard(0x74 | ((b&1)?0x00:0x80)); // handle right mouse button if((b ^ b_old) & 2) ikbd_keyboard(0x75 | ((b&2)?0x00:0x80)); } b_old = b; } #if 0 if(ikbd.state & IKBD_STATE_MOUSE_BUTTON_AS_KEY) { b = 0; // if mouse position is 0/0 quit here if(!x && !y) return; } #endif if(ikbd.state & IKBD_STATE_MOUSE_ABSOLUTE) { } else { // atari has mouse button bits swapped enqueue(0xf8|((b&1)?2:0)|((b&2)?1:0)); enqueue(x); enqueue((ikbd.state & IKBD_STATE_MOUSE_Y_BOTTOM)?-y:y); } }