diff --git a/BaS_gcc/Makefile b/BaS_gcc/Makefile index db739db..af9569d 100644 --- a/BaS_gcc/Makefile +++ b/BaS_gcc/Makefile @@ -90,6 +90,8 @@ CSRCS= \ $(SRCDIR)/ohci-hcd.c \ $(SRCDIR)/ehci-hcd.c \ \ + $(SRCDIR)/ikbd.c \ + \ $(SRCDIR)/basflash.c \ $(SRCDIR)/basflash_start.c diff --git a/BaS_gcc/include/ikbd.h b/BaS_gcc/include/ikbd.h new file mode 100644 index 0000000..2f6927b --- /dev/null +++ b/BaS_gcc/include/ikbd.h @@ -0,0 +1,11 @@ +#ifndef _IKBD_H_ +#define _IKBD_H_ + +extern void ikbd_init(void); +extern void ikbd_poll(void); +extern void ikbd_joystick(uint8_t joy, uint8_t map); +extern void ikbd_mouse(uint8_t buttons, int8_t x, int8_t y); +extern void ikbd_keyboard(uint8_t code); + +#endif /* _IKBD_H_ */ + diff --git a/BaS_gcc/include/user_io.h b/BaS_gcc/include/user_io.h new file mode 100644 index 0000000..cc3ba04 --- /dev/null +++ b/BaS_gcc/include/user_io.h @@ -0,0 +1,57 @@ +/* +* user_io.h +* +*/ + +#ifndef _USER_IO_H_ +#define _USER_IO_H_ + +#define UIO_STATUS 0x00 +#define UIO_BUT_SW 0x01 + +// codes as used by minimig (amiga) +#define UIO_JOYSTICK0 0x02 +#define UIO_JOYSTICK1 0x03 +#define UIO_MOUSE 0x04 +#define UIO_KEYBOARD 0x05 +#define UIO_KBD_OSD 0x06 // keycodes used by OSD only + +// codes as used by MiST (atari) +#define UIO_IKBD_OUT 0x02 +#define UIO_IKBD_IN 0x03 +#define UIO_SERIAL_OUT 0x04 +#define UIO_SERIAL_IN 0x05 + +#define JOY_RIGHT 0x01 +#define JOY_LEFT 0x02 +#define JOY_DOWN 0x04 +#define JOY_UP 0x08 +#define JOY_BTN1 0x10 +#define JOY_BTN2 0x20 +#define JOY_MOVE (JOY_RIGHT|JOY_LEFT|JOY_UP|JOY_DOWN) + +#define BUTTON1 0x01 +#define BUTTON2 0x02 +#define SWITCH1 0x04 +#define SWITCH2 0x08 + +// core type value should be unlikely to be returned by broken cores +#define CORE_TYPE_UNKNOWN 0x55 +#define CORE_TYPE_DUMB 0xa0 +#define CORE_TYPE_MINIMIG 0xa1 +#define CORE_TYPE_PACE 0xa2 +#define CORE_TYPE_MIST 0xa3 + +void user_io_init(); +void user_io_detect_core_type(); +unsigned char user_io_core_type(); +void user_io_poll(); +int user_io_button_pressed(); +void user_io_osd_key_enable(char); + +// hooks from the usb layer +void user_io_mouse(unsigned char b, char x, char y); +void user_io_kbd(unsigned char m, unsigned char *k); + +#endif /* _USER_IO_H_ */ + diff --git a/BaS_gcc/sources/ikbd.c b/BaS_gcc/sources/ikbd.c new file mode 100644 index 0000000..ae743b1 --- /dev/null +++ b/BaS_gcc/sources/ikbd.c @@ -0,0 +1,315 @@ +/* + + 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 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); + } +} +