|
|
1.1 root 1: /*
1.1.1.6 root 2: Hatari - ikbd.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
1.1 root 6:
1.1.1.12 root 7: The keyboard processor(6301) handles any joystick/mouse task and sends bytes
8: to the ACIA(6850). When a byte arrives in the ACIA (which takes just over
9: 7000 CPU cycles) an MFP interrupt is flagged. The CPU can now read the byte
10: from the ACIA by reading address $fffc02.
11: An annoying bug can be found in Dungeon Master. This, when run, turns off the
12: mouse input - but of course then you are unable to play the game! A bodge
13: flag has been added so we need to be told twice to turn off the mouse input
14: (although I think this causes errors in other games...).
15: Also, the ACIA_CYCLES time is very important for games such as Carrier
16: Command. The keyboard handler in this game has a bug in it, which corrupts
17: its own registers if more than one byte is queued up. This value was found by
18: a test program on a real ST and has correctly emulated the behaviour.
19: */
1.1.1.14 root 20: const char IKBD_fileid[] = "Hatari ikbd.c : " __DATE__ " " __TIME__;
1.1.1.12 root 21:
22: /* 2007/09/29 [NP] Use the new int.c to add interrupts with INT_CPU_CYCLE / INT_MFP_CYCLE. */
23: /* 2007/12/09 [NP] If reset is written to ACIA control register, we must call ACIA_Reset to reset */
24: /* RX/TX status. Reading the control register fffc00 just after a reset should */
25: /* return the value 0x02 (used in Transbeauce 2 demo loader). */
1.1.1.13 root 26: /* 2008/07/06 [NP] Add support for executing 8 bit code sent to the 6301 processor. */
27: /* Instead of implementing a full 6301 emulator, we compute a checksum for each */
28: /* program sent to the 6301 RAM. If the checksum is recognized, we call some */
29: /* functions to emulate the behaviour of the 6301 in that case. */
30: /* When the 6301 is in 'Execute' mode (command 0x22), we must stop the normal */
31: /* reporting of key/mouse/joystick and use our custom handlers for each read or */
32: /* write to $fffc02. */
33: /* After a reset command, returns $F1 after $F0 (needed by Dragonnels Demo). */
34: /* This fixes the Transbeauce 2 demo menu, the Dragonnels demo menu and the */
35: /* Froggies Over The Fence demo menu (yeah ! enjoy this master piece of demo !). */
1.1.1.17 root 36: /* 2011/05/11 [NP] Add proper support for emulating TX buffer empty/full in status register bit 1 */
37: /* when writing to $fffc02 (using an internal timer). */
38: /* 2011/07/14 [NP] Don't clear bytes in transit when ACIA_Reset is called ; if a byte is sent to */
39: /* the ikbd it should not be cancelled ? FIXME : this would need more tests on a */
40: /* real ST (fix Froggies Over The Fence's menu when selecting a demo). */
1.1.1.18 root 41: /* 2011/07/31 [NP] Don't clear bytes in transit in the ACIA when the IKBD is reset (fix Overdrive */
42: /* by Phalanx). */
43: /* 2011/12/27 [NP] When sending new bytes while a byte is already in transfer from ACIA to IKBD, */
44: /* don't restart the internal TX timer (fix 'Pandemonium Demos' Intro). */
45: /* eg : .loop : move.b d0,$fc02.w btst #1,$fc00.w beq.s .loop */
1.1.1.19! root 46: /* 2012/01/22 [NP] Enable both mouse and joystick reporting when commands 0x12 and 0x14 are */
! 47: /* received during the IKBD reset. */
! 48: /* 2012/02/26 [NP] Handle TX interrupt in the ACIA (eg by sending 0xb6 instead of 0x96 after */
! 49: /* resetting the ACIA) (fix the game 'Hades Nebula'). */
! 50:
1.1.1.6 root 51:
52: #include <time.h>
1.1 root 53:
54: #include "main.h"
55: #include "ikbd.h"
1.1.1.16 root 56: #include "cycInt.h"
1.1.1.10 root 57: #include "ioMem.h"
1.1 root 58: #include "joy.h"
59: #include "m68000.h"
60: #include "memorySnapShot.h"
61: #include "mfp.h"
1.1.1.17 root 62: #include "screen.h"
1.1 root 63: #include "video.h"
1.1.1.13 root 64: #include "utils.h"
1.1.1.10 root 65:
1.1 root 66:
67: #define DBL_CLICK_HISTORY 0x07 /* Number of frames since last click to see if need to send one or two clicks */
1.1.1.9 root 68: #define ACIA_CYCLES 7200 /* Cycles (Multiple of 4) between sent to ACIA from keyboard along serial line - 500Hz/64, (approx' 6920-7200cycles from test program) */
1.1 root 69:
1.1.1.13 root 70: #define IKBD_RESET_CYCLES 223500 /* Cycles after RESET before complete */
1.1 root 71:
72: #define ABS_X_ONRESET 0 /* Initial XY for absolute mouse position after RESET command */
73: #define ABS_Y_ONRESET 0
74: #define ABS_MAX_X_ONRESET 320 /* Initial absolute mouse limits after RESET command */
75: #define ABS_MAY_Y_ONRESET 200 /* These values are never actually used as user MUST call 'IKBD_Cmd_AbsMouseMode' before ever using them */
76:
1.1.1.10 root 77: #define ABS_PREVBUTTONS (0x02|0x8) /* Don't report any buttons up on first call to 'IKBD_Cmd_ReadAbsMousePos' */
1.1 root 78:
79:
80: /* Keyboard state */
81: KEYBOARD Keyboard;
82:
83: /* Keyboard processor */
84: KEYBOARD_PROCESSOR KeyboardProcessor; /* Keyboard processor details */
1.1.1.11 root 85:
86: /* Pattern of mouse button up/down in ST frames (run off a double-click message) */
1.1.1.17 root 87: static const Uint8 DoubleClickPattern[] =
1.1.1.12 root 88: {
89: BUTTON_MOUSE,BUTTON_MOUSE,BUTTON_MOUSE,BUTTON_MOUSE,
90: 0,0,0,0,BUTTON_MOUSE,BUTTON_MOUSE,BUTTON_MOUSE,BUTTON_MOUSE
91: };
1.1.1.6 root 92:
1.1.1.13 root 93: static bool bMouseDisabled, bJoystickDisabled;
94: static bool bDuringResetCriticalTime, bBothMouseAndJoy;
1.1.1.14 root 95: static bool bMouseEnabledDuringReset;
1.1 root 96:
1.1.1.19! root 97: static time_t nTimeOffset; /* Offset between current time and emulated time */
! 98:
1.1 root 99: /* ACIA */
1.1.1.10 root 100: static Uint8 ACIAControlRegister = 0;
101: static Uint8 ACIAStatusRegister = ACIA_STATUS_REGISTER__TX_BUFFER_EMPTY; /* Pass when read 0xfffc00 */
1.1.1.13 root 102: static Uint8 ACIAByte; /* When a byte has arrived at the ACIA (from the keyboard) it is stored here */
1.1.1.17 root 103: static Uint8 ACIATxDataRegister; /* When a byte must be sent by the ACIA (to the keyboard) it is stored here */
1.1.1.15 root 104: static bool bByteInTransitToACIA = false; /* Is a byte being sent to the ACIA from the keyboard? */
1.1.1.17 root 105: static bool bByteInTransitFromACIA = false; /* Is a byte being sent from the ACIA to the keyboard? */
1.1 root 106:
107: /*
108: 6850 ACIA (Asynchronous Communications Inferface Apdater)
109: Page 41, ST Internals. Also ST Update Magazine, February 1989 (I glad I kept that!)
110:
111: Pins:-
112: Vss
113: RX DATA Receive Data
114: RX CLK Receive Clock
115: TX CLK Transmitter Clock
116: RTS Request To Send
117: TX DATA Transmitter Data
118: IRQ Interrupt Request
119: CS 0,1,2 Chip Select
120: RS Register Select
121: Vcc Voltage
122: R/W Read/Write
123: E Enable
124: D0-D7 Data
125: DCD Data Carrier Detect
126: CTS Clear To Send
127:
128: Registers:-
129: 0xfffc00 Keyboard ACIA Control (write)/Status(read)
130: 0xfffc02 Keyboard ACIA Data
131: 0xfffc04 MIDI ACIA Control (write)/Status(read)
132: 0xfffc06 MIDI ACIA Data
133:
134: Control Register (0xfffc00 write):-
135: Bits 0,1 - These bits determine by which factor the transmitter and receiver
136: clock will be divided. These bits also are joined with a master reset
137: function. The 6850 has no separate reset line, so it must be
138: accomplished though software.
139: 0 0 RXCLK/TXCLK without division
140: 0 1 RXCLK/TXCLK by 16 (MIDI)
141: 1 0 RXCLK/TXCLK by 64 (Keyboard)
142: 1 1 Master RESET
143: Bits 2,3,4 - These so-called Word Select bits tell whether 7 or 8 data-bits are
144: involved; whether 1 or 2 stop-bits are transferred; and the type of parity
145: Bits 5,6 - These Transmitter Control bits set the RTS output pin, and allow or prevent
146: an interrupt through the ACIA when the send register is emptied. Also, BREAK signals
147: can be sent over the serial output by this line. A BREAK signal is nothing more than
148: a long seqence of null bits
149: 0 0 RTS low, transmitter IRQ disabled
150: 0 1 RTS low, transmitter IRQ enabled
151: 1 0 RTS high, transmitter IRQ disabled
152: 1 1 RTS low, transmitter IRQ disabled, BREAK sent
153: Bit 7 - The Receiver Interrupt Enable bit determines whether the receiver interrupt
154: will be on. An interrupt can be caused by the DCD line chaning from low to high, or
155: by the receiver data buffer filling. Besides that, an interrupt can occur from an
156: OVERRUN ( a received character isn't properly read from the processior).
157: 0 Interrupt disabled
158: 1 Interrupt enabled
159:
160: Status Register (0xfffc00 read):-
161: Bit 0 - When this bit is high, the RX data register is full. The byte must be read
162: before a new character is received (otherwise an OVERRUN happens)
163: Bit 1 - This bit reflects the status of the TX data buffer. An empty register
164: set the bit.
165: Bit 2 - A low-high change in pin DCD sets bit 2. If the receiver interrupt is allowable, the IRQ
166: is cancelled. The bit is cleared when the status register and the receiver register are
167: read. This also cancels the IRQ. Bit 2 register remains highis the signal on the DCD pin
168: is still high; Bit 2 register low if DCD becomes low.
169: Bit 3 - This line shows the status of CTS. This signal cannot be altered by a mater reset,
170: or by ACIA programming.
171: Bit 4 - Shows 'Frame Errors'. Frame errors are when no stop-bit is recognized in receiver
172: switching. It can be set with every new character.
173: Bit 5 - This bit display the previously mentioned OVERRUN condition. Bit 5 is reset when the
174: RX buffer is read.
175: Bit 6 - This bit recognizes whether the parity of a received character is correct. The bit is
176: set on an error.
177: Bit 7 - This signals the state of the IRQ pins; this bit make it possible to switch several
178: IRQ lines on one interrupt input. In cases where an interrupt is program-generated, bit 7
179: can tell which IC cut off the interrupt.
1.1.1.7 root 180:
1.1 root 181: ST ACIA:-
182: Note CTS,DCD and RTS are not connected! Phew!
183: The keyboard ACIA are address 0xfffc000 and 0xfffc02.
1.1.1.17 root 184: Default parameters are :- 8-bit word, 1 stopbit, no parity, 7812.5 baud; 500KHz/64 (keyboard clock div)
1.1 root 185: Default MIDI parameters are are above but :- 31250 baud; 500KHz/16 (MIDI clock div)
1.1.1.19! root 186:
! 187:
! 188: Special behaviours during the IKBD reset :
! 189: If the following commands are received during the reset of the IKBD,
! 190: the IKBD will go in a special mode and report both mouse and joystick at the same time :
! 191: 0x08 0x14 relative mouse on , joysticks auto
! 192: 0x08 0x0b 0x14 relative mouse on , mouse threshold , joysticks auto (eg Barbarian 1 by Psygnosis)
! 193: 0x12 0x14 disable mouse , joysticks auto (eg Hammerfist)
! 194: 0x12 0x1a disable mouse , disable joysticks
! 195:
! 196: In that case mouse and joystick buttons will be reported in a "mouse report" packet
! 197: and joystick actions (except buttons) will be reported in a "joystick report" packet.
! 198:
1.1 root 199: */
200:
1.1.1.2 root 201: /* List of possible keyboard commands, others are seen as NOPs by keyboard processor */
1.1.1.13 root 202: static void IKBD_Cmd_Reset(void);
203: static void IKBD_Cmd_MouseAction(void);
204: static void IKBD_Cmd_RelMouseMode(void);
205: static void IKBD_Cmd_AbsMouseMode(void);
206: static void IKBD_Cmd_MouseCursorKeycodes(void);
207: static void IKBD_Cmd_SetMouseThreshold(void);
208: static void IKBD_Cmd_SetMouseScale(void);
209: static void IKBD_Cmd_ReadAbsMousePos(void);
210: static void IKBD_Cmd_SetInternalMousePos(void);
211: static void IKBD_Cmd_SetYAxisDown(void);
212: static void IKBD_Cmd_SetYAxisUp(void);
213: static void IKBD_Cmd_StartKeyboardTransfer(void);
214: static void IKBD_Cmd_TurnMouseOff(void);
215: static void IKBD_Cmd_StopKeyboardTransfer(void);
216: static void IKBD_Cmd_ReturnJoystickAuto(void);
217: static void IKBD_Cmd_StopJoystick(void);
218: static void IKBD_Cmd_ReturnJoystick(void);
219: static void IKBD_Cmd_SetJoystickDuration(void);
220: static void IKBD_Cmd_SetJoystickFireDuration(void);
221: static void IKBD_Cmd_SetCursorForJoystick(void);
222: static void IKBD_Cmd_DisableJoysticks(void);
223: static void IKBD_Cmd_SetClock(void);
224: static void IKBD_Cmd_ReadClock(void);
225: static void IKBD_Cmd_LoadMemory(void);
226: static void IKBD_Cmd_ReadMemory(void);
227: static void IKBD_Cmd_Execute(void);
228: static void IKBD_Cmd_ReportMouseAction(void);
229: static void IKBD_Cmd_ReportMouseMode(void);
230: static void IKBD_Cmd_ReportMouseThreshold(void);
231: static void IKBD_Cmd_ReportMouseScale(void);
232: static void IKBD_Cmd_ReportMouseVertical(void);
233: static void IKBD_Cmd_ReportMouseAvailability(void);
234: static void IKBD_Cmd_ReportJoystickMode(void);
235: static void IKBD_Cmd_ReportJoystickAvailability(void);
236:
1.1.1.17 root 237: /* Keyboard Command */
238: static const struct {
239: Uint8 Command;
240: Uint8 NumParameters;
241: void (*pCallFunction)(void);
242: } KeyboardCommands[] =
1.1.1.12 root 243: {
244: /* Known messages, counts include command byte */
245: { 0x80,2, IKBD_Cmd_Reset },
246: { 0x07,2, IKBD_Cmd_MouseAction },
247: { 0x08,1, IKBD_Cmd_RelMouseMode },
248: { 0x09,5, IKBD_Cmd_AbsMouseMode },
249: { 0x0A,3, IKBD_Cmd_MouseCursorKeycodes },
250: { 0x0B,3, IKBD_Cmd_SetMouseThreshold },
251: { 0x0C,3, IKBD_Cmd_SetMouseScale },
252: { 0x0D,1, IKBD_Cmd_ReadAbsMousePos },
253: { 0x0E,6, IKBD_Cmd_SetInternalMousePos },
254: { 0x0F,1, IKBD_Cmd_SetYAxisDown },
255: { 0x10,1, IKBD_Cmd_SetYAxisUp },
256: { 0x11,1, IKBD_Cmd_StartKeyboardTransfer },
257: { 0x12,1, IKBD_Cmd_TurnMouseOff },
258: { 0x13,1, IKBD_Cmd_StopKeyboardTransfer },
259: { 0x14,1, IKBD_Cmd_ReturnJoystickAuto },
260: { 0x15,1, IKBD_Cmd_StopJoystick },
261: { 0x16,1, IKBD_Cmd_ReturnJoystick },
262: { 0x17,2, IKBD_Cmd_SetJoystickDuration },
263: { 0x18,1, IKBD_Cmd_SetJoystickFireDuration },
264: { 0x19,7, IKBD_Cmd_SetCursorForJoystick },
265: { 0x1A,1, IKBD_Cmd_DisableJoysticks },
266: { 0x1B,7, IKBD_Cmd_SetClock },
267: { 0x1C,1, IKBD_Cmd_ReadClock },
268: { 0x20,4, IKBD_Cmd_LoadMemory },
269: { 0x21,3, IKBD_Cmd_ReadMemory },
270: { 0x22,3, IKBD_Cmd_Execute },
271:
1.1.1.13 root 272: /* Report message (top bit set) */
273: { 0x87,1, IKBD_Cmd_ReportMouseAction },
274: { 0x88,1, IKBD_Cmd_ReportMouseMode },
275: { 0x89,1, IKBD_Cmd_ReportMouseMode },
276: { 0x8A,1, IKBD_Cmd_ReportMouseMode },
277: { 0x8B,1, IKBD_Cmd_ReportMouseThreshold },
278: { 0x8C,1, IKBD_Cmd_ReportMouseScale },
279: { 0x8F,1, IKBD_Cmd_ReportMouseVertical },
280: { 0x90,1, IKBD_Cmd_ReportMouseVertical },
281: { 0x92,1, IKBD_Cmd_ReportMouseAvailability },
282: { 0x94,1, IKBD_Cmd_ReportJoystickMode },
283: { 0x95,1, IKBD_Cmd_ReportJoystickMode },
284: { 0x99,1, IKBD_Cmd_ReportJoystickMode },
285: { 0x9A,1, IKBD_Cmd_ReportJoystickAvailability },
1.1 root 286:
1.1.1.12 root 287: { 0xFF,0, NULL } /* Term */
1.1 root 288: };
289:
1.1.1.2 root 290:
1.1.1.13 root 291: static void IKBD_SendByteToKeyboardProcessor(Uint16 bl);
292: static Uint16 IKBD_GetByteFromACIA(void);
293: static void IKBD_SendByteToACIA(int nAciaCycles);
294: static void IKBD_AddKeyToKeyboardBuffer(Uint8 Data);
295: static void IKBD_AddKeyToKeyboardBufferWithDelay(Uint8 Data, int nAciaCycles);
296: static void IKBD_AddKeyToKeyboardBuffer_Real(Uint8 Data, int nAciaCycles);
297:
298:
299: /* Belows part is used to emulate the behaviour of custom 6301 programs */
300: /* sent to the ikbd RAM. */
301:
302: static void IKBD_LoadMemoryByte ( Uint8 aciabyte );
303:
304: static void IKBD_CustomCodeHandler_CommonBoot ( Uint8 aciabyte );
305:
306: static void IKBD_CustomCodeHandler_FroggiesMenu_Read ( void );
307: static void IKBD_CustomCodeHandler_FroggiesMenu_Write ( Uint8 aciabyte );
308: static void IKBD_CustomCodeHandler_Transbeauce2Menu_Read ( void );
309: static void IKBD_CustomCodeHandler_Transbeauce2Menu_Write ( Uint8 aciabyte );
310: static void IKBD_CustomCodeHandler_DragonnelsMenu_Read ( void );
311: static void IKBD_CustomCodeHandler_DragonnelsMenu_Write ( Uint8 aciabyte );
1.1.1.17 root 312: static void IKBD_CustomCodeHandler_ChaosAD_Read ( void );
313: static void IKBD_CustomCodeHandler_ChaosAD_Write ( Uint8 aciabyte );
1.1.1.13 root 314:
315:
1.1.1.17 root 316: static int MemoryLoadNbBytesTotal = 0; /* total number of bytes to send with the command 0x20 */
317: static int MemoryLoadNbBytesLeft = 0; /* number of bytes that remain to be sent */
318: static Uint32 MemoryLoadCrc = 0xffffffff; /* CRC of the bytes sent to the ikbd */
319: static int MemoryExeNbBytes = 0; /* current number of bytes sent to the ikbd when IKBD_ExeMode is true */
1.1.1.13 root 320:
1.1.1.17 root 321: static void (*pIKBD_CustomCodeHandler_Read) ( void );
322: static void (*pIKBD_CustomCodeHandler_Write) ( Uint8 );
323: static bool IKBD_ExeMode = false;
324:
325: static Uint8 ScanCodeState[ 128 ]; /* state of each key : 0=released 1=pressed */
1.1.1.13 root 326:
327: /* This array contains all known custom 6301 programs, with their CRC */
1.1.1.17 root 328: static const struct
1.1.1.13 root 329: {
330: Uint32 LoadMemCrc; /* CRC of the bytes sent using the command 0x20 */
331: void (*ExeBootHandler) ( Uint8 ); /* function handling write to $fffc02 during the 'boot' mode */
332: int MainProgNbBytes; /* number of bytes of the main 6301 program */
333: Uint32 MainProgCrc; /* CRC of the main 6301 program */
334: void (*ExeMainHandler_Read) ( void );/* function handling read to $fffc02 in the main 6301 program */
335: void (*ExeMainHandler_Write) ( Uint8 ); /* funciton handling write to $fffc02 in the main 6301 program */
336: const char *Name;
337: }
338: CustomCodeDefinitions[] =
339: {
340: {
341: 0x2efb11b1 ,
342: IKBD_CustomCodeHandler_CommonBoot ,
343: 167,
344: 0xe7110b6d ,
345: IKBD_CustomCodeHandler_FroggiesMenu_Read ,
346: IKBD_CustomCodeHandler_FroggiesMenu_Write ,
347: "Froggies Over The Fence Main Menu"
348: } ,
349: {
350: 0xadb6b503 ,
351: IKBD_CustomCodeHandler_CommonBoot ,
352: 165,
353: 0x5617c33c ,
354: IKBD_CustomCodeHandler_Transbeauce2Menu_Read ,
355: IKBD_CustomCodeHandler_Transbeauce2Menu_Write ,
356: "Transbeauce 2 Main Menu"
357: } ,
358: {
359: 0x33c23cdf ,
360: IKBD_CustomCodeHandler_CommonBoot ,
361: 83 ,
362: 0xdf3e5a88 ,
363: IKBD_CustomCodeHandler_DragonnelsMenu_Read ,
364: IKBD_CustomCodeHandler_DragonnelsMenu_Write ,
365: "Dragonnels Main Menu"
1.1.1.17 root 366: },
367: {
368: 0x9ad7fcdf ,
369: IKBD_CustomCodeHandler_CommonBoot ,
370: 109 ,
371: 0xa11d8be5 ,
372: IKBD_CustomCodeHandler_ChaosAD_Read ,
373: IKBD_CustomCodeHandler_ChaosAD_Write ,
374: "Chaos A.D."
1.1.1.13 root 375: }
376: };
377:
378:
379:
1.1.1.2 root 380: /*-----------------------------------------------------------------------*/
1.1.1.12 root 381: /**
382: * Reset the ACIA
383: */
384: void ACIA_Reset(void)
385: {
1.1.1.17 root 386: LOG_TRACE(TRACE_IKBD_EXEC, "ikbd acia reset\n");
387:
388: /* [NP] 2011/07/14 FIXME : Acia reset should not clear bytes in transit ? */
389: /* Else, "Froggies Over The Fence" doesn't exit the custom ikbd mode */
390: #if 0
1.1.1.15 root 391: bByteInTransitToACIA = false;
1.1.1.17 root 392: bByteInTransitFromACIA = false;
393: #endif
1.1.1.12 root 394: ACIAControlRegister = 0;
395: ACIAStatusRegister = ACIA_STATUS_REGISTER__TX_BUFFER_EMPTY;
396: }
397:
398:
399: /*-----------------------------------------------------------------------*/
400: /**
401: * Reset the IKBD processor
402: */
1.1.1.13 root 403:
404: /* Cancel execution of any program that was uploaded to the 6301's RAM */
405: /* This function is also called when performing a 68000 'reset' ; in that */
406: /* case we need to return $F0 and $F1. */
407:
408: void IKBD_Reset_ExeMode ( void )
1.1 root 409: {
1.1.1.15 root 410: LOG_TRACE(TRACE_IKBD_EXEC, "ikbd custom exe off\n");
1.1.1.13 root 411:
412: /* Reset any custom code run with the Execute command 0x22 */
413: MemoryLoadNbBytesLeft = 0;
414: pIKBD_CustomCodeHandler_Read = NULL;
415: pIKBD_CustomCodeHandler_Write = NULL;
1.1.1.15 root 416: IKBD_ExeMode = false;
1.1.1.13 root 417:
418: Keyboard.BufferHead = Keyboard.BufferTail = 0; /* flush all queued bytes that would be read in $fffc02 */
1.1.1.18 root 419: /* [NP] 2011/07/31 FIXME : IKBD reset should not reset acia and clear bytes in transit */
420: /* Else, "Overdrive" lock after doing a 68000 "reset" */
421: #if 0
1.1.1.15 root 422: bByteInTransitToACIA = false;
1.1.1.17 root 423: bByteInTransitFromACIA = false;
1.1.1.18 root 424: #endif
1.1.1.15 root 425: // IKBD_AddKeyToKeyboardBuffer(0xF0); /* Assume OK, return correct code */
1.1.1.13 root 426: IKBD_AddKeyToKeyboardBuffer(0xF1); /* [NP] Dragonnels demo needs this */
427: }
428:
429:
430: void IKBD_Reset(bool bCold)
431: {
432: int i;
433:
434:
1.1.1.12 root 435: /* Reset internal keyboard processor details */
436: if (bCold)
437: {
1.1.1.15 root 438: KeyboardProcessor.bReset = false;
1.1.1.16 root 439: if (CycInt_InterruptActive(INTERRUPT_IKBD_RESETTIMER))
440: CycInt_RemovePendingInterrupt(INTERRUPT_IKBD_RESETTIMER);
1.1.1.19! root 441: nTimeOffset = 0;
1.1.1.12 root 442: }
443:
444: KeyboardProcessor.MouseMode = AUTOMODE_MOUSEREL;
445: KeyboardProcessor.JoystickMode = AUTOMODE_JOYSTICK;
446:
447: KeyboardProcessor.Abs.X = ABS_X_ONRESET;
448: KeyboardProcessor.Abs.Y = ABS_Y_ONRESET;
449: KeyboardProcessor.Abs.MaxX = ABS_MAX_X_ONRESET;
450: KeyboardProcessor.Abs.MaxY = ABS_MAY_Y_ONRESET;
451: KeyboardProcessor.Abs.PrevReadAbsMouseButtons = ABS_PREVBUTTONS;
452:
453: KeyboardProcessor.Mouse.DeltaX = KeyboardProcessor.Mouse.DeltaY = 0;
454: KeyboardProcessor.Mouse.XScale = KeyboardProcessor.Mouse.YScale = 0;
455: KeyboardProcessor.Mouse.XThreshold = KeyboardProcessor.Mouse.YThreshold = 1;
456: KeyboardProcessor.Mouse.YAxis = 1; /* Y origin at top */
457: KeyboardProcessor.Mouse.Action = 0;
458:
459: KeyboardProcessor.Joy.PrevJoyData[0] = KeyboardProcessor.Joy.PrevJoyData[1] = 0;
460:
1.1.1.13 root 461: for ( i=0 ; i<128 ; i++ )
462: ScanCodeState[ i ] = 0; /* key is released */
463:
1.1.1.12 root 464: /* Reset our ACIA status */
465: ACIA_Reset();
466: /* And our keyboard states and clear key state table */
467: Keyboard.BufferHead = Keyboard.BufferTail = 0;
468: Keyboard.nBytesInInputBuffer = 0;
469: memset(Keyboard.KeyStates, 0, sizeof(Keyboard.KeyStates));
470: Keyboard.bLButtonDown = BUTTON_NULL;
471: Keyboard.bRButtonDown = BUTTON_NULL;
472: Keyboard.bOldLButtonDown = Keyboard.bOldRButtonDown = BUTTON_NULL;
473: Keyboard.LButtonDblClk = Keyboard.RButtonDblClk = 0;
474: Keyboard.LButtonHistory = Keyboard.RButtonHistory = 0;
475:
1.1.1.13 root 476: /* Store bool for when disable mouse or joystick */
1.1.1.15 root 477: bMouseDisabled = bJoystickDisabled = false;
1.1.1.12 root 478: /* do emulate hardware 'quirk' where if disable both with 'x' time
479: * of a RESET command they are ignored! */
1.1.1.15 root 480: bDuringResetCriticalTime = bBothMouseAndJoy = false;
481: bMouseEnabledDuringReset = false;
1.1.1.13 root 482:
483: /* Remove any custom handlers used to emulate code loaded to the 6301's RAM */
484: IKBD_Reset_ExeMode ();
1.1.1.14 root 485:
486: /* Add auto-update function to the queue */
1.1.1.16 root 487: CycInt_AddRelativeInterrupt(150000, INT_CPU_CYCLE, INTERRUPT_IKBD_AUTOSEND);
1.1 root 488: }
489:
1.1.1.2 root 490:
491: /*-----------------------------------------------------------------------*/
1.1.1.12 root 492: /**
493: * Save/Restore snapshot of local variables
494: * ('MemorySnapShot_Store' handles type)
495: */
1.1.1.13 root 496: void IKBD_MemorySnapShot_Capture(bool bSave)
1.1 root 497: {
1.1.1.13 root 498: unsigned int i;
1.1.1.19! root 499: time_t nEmuTime;
1.1.1.13 root 500:
1.1.1.12 root 501: /* Save/Restore details */
502: MemorySnapShot_Store(&Keyboard, sizeof(Keyboard));
503: MemorySnapShot_Store(&KeyboardProcessor, sizeof(KeyboardProcessor));
504: MemorySnapShot_Store(&ACIAControlRegister, sizeof(ACIAControlRegister));
505: MemorySnapShot_Store(&ACIAStatusRegister, sizeof(ACIAStatusRegister));
506: MemorySnapShot_Store(&ACIAByte, sizeof(ACIAByte));
1.1.1.17 root 507: MemorySnapShot_Store(&ACIATxDataRegister, sizeof(ACIATxDataRegister));
1.1.1.12 root 508: MemorySnapShot_Store(&bByteInTransitToACIA, sizeof(bByteInTransitToACIA));
1.1.1.17 root 509: MemorySnapShot_Store(&bByteInTransitFromACIA, sizeof(bByteInTransitFromACIA));
1.1.1.12 root 510: MemorySnapShot_Store(&bMouseDisabled, sizeof(bMouseDisabled));
511: MemorySnapShot_Store(&bJoystickDisabled, sizeof(bJoystickDisabled));
512: MemorySnapShot_Store(&bDuringResetCriticalTime, sizeof(bDuringResetCriticalTime));
513: MemorySnapShot_Store(&bBothMouseAndJoy, sizeof(bBothMouseAndJoy));
1.1.1.14 root 514: MemorySnapShot_Store(&bMouseEnabledDuringReset, sizeof(bMouseEnabledDuringReset));
1.1.1.13 root 515:
516: /* restore custom 6301 program if needed */
517: MemorySnapShot_Store(&IKBD_ExeMode, sizeof(IKBD_ExeMode));
518: MemorySnapShot_Store(&MemoryLoadCrc, sizeof(MemoryLoadCrc));
1.1.1.15 root 519: if ((bSave == false) && (IKBD_ExeMode == true)) /* restoring a snapshot with active 6301 emulation */
1.1.1.13 root 520: {
521: for ( i = 0 ; i < sizeof ( CustomCodeDefinitions ) / sizeof ( CustomCodeDefinitions[0] ); i++ )
522: if ( CustomCodeDefinitions[ i ].MainProgCrc == MemoryLoadCrc )
523: {
524: pIKBD_CustomCodeHandler_Read = CustomCodeDefinitions[ i ].ExeMainHandler_Read;
525: pIKBD_CustomCodeHandler_Write = CustomCodeDefinitions[ i ].ExeMainHandler_Write;
526: Keyboard.BufferHead = Keyboard.BufferTail = 0; /* flush all queued bytes that would be read in $fffc02 */
527: // (*pIKBD_CustomCodeHandler_Read) (); /* initialize ACIAByte */
528: ACIAByte = 0; /* initialize ACIAByte, don't call IKBD_AddKeyToKeyboardBuffer_Real now */
529: break;
530: }
531:
532: if ( i >= sizeof ( CustomCodeDefinitions ) / sizeof ( CustomCodeDefinitions[0] ) ) /* not found (should not happen) */
1.1.1.15 root 533: IKBD_ExeMode = false; /* turn off exe mode */
1.1.1.13 root 534: }
1.1.1.19! root 535:
! 536: /* The time offset is special: When 0, the emulated system is running
! 537: * with the current time, so we assume that it will also continue with
! 538: * real time when the memory snapshot is loaded again. When it is not
! 539: * zero, the program (like the game "Zombi") might expect a certain
! 540: * system date. In this case we initialize the nTimeOffset during
! 541: * loading so that it maches this system date again. */
! 542: nEmuTime = (nTimeOffset != 0) ? (time(NULL) - nTimeOffset) : 0;
! 543: MemorySnapShot_Store(&nEmuTime, sizeof(nEmuTime));
! 544: if (!bSave)
! 545: {
! 546: nTimeOffset = (nEmuTime != 0) ? (time(NULL) - nEmuTime) : 0;
! 547: }
1.1 root 548: }
549:
1.1.1.2 root 550:
551: /*-----------------------------------------------------------------------*/
1.1.1.12 root 552: /**
553: * Calculate out 'delta' that mouse has moved by each frame, and add this to our internal keyboard position
554: */
1.1.1.8 root 555: static void IKBD_UpdateInternalMousePosition(void)
1.1 root 556: {
557:
1.1.1.12 root 558: KeyboardProcessor.Mouse.DeltaX = KeyboardProcessor.Mouse.dx;
559: KeyboardProcessor.Mouse.DeltaY = KeyboardProcessor.Mouse.dy;
560: KeyboardProcessor.Mouse.dx = 0;
561: KeyboardProcessor.Mouse.dy = 0;
562:
563: /* Update internal mouse coords - Y axis moves according to YAxis setting(up/down) */
564: /* Limit to Max X/Y(inclusive) */
565: KeyboardProcessor.Abs.X += KeyboardProcessor.Mouse.DeltaX;
566: if (KeyboardProcessor.Abs.X < 0)
567: KeyboardProcessor.Abs.X = 0;
568: if (KeyboardProcessor.Abs.X > KeyboardProcessor.Abs.MaxX)
569: KeyboardProcessor.Abs.X = KeyboardProcessor.Abs.MaxX;
570:
571: KeyboardProcessor.Abs.Y += KeyboardProcessor.Mouse.DeltaY*KeyboardProcessor.Mouse.YAxis; /* Needed '+' for Falcon... */
572: if (KeyboardProcessor.Abs.Y < 0)
573: KeyboardProcessor.Abs.Y = 0;
574: if (KeyboardProcessor.Abs.Y > KeyboardProcessor.Abs.MaxY)
575: KeyboardProcessor.Abs.Y = KeyboardProcessor.Abs.MaxY;
1.1.1.4 root 576:
1.1 root 577: }
578:
1.1.1.2 root 579:
580: /*-----------------------------------------------------------------------*/
1.1.1.12 root 581: /**
582: * When running in maximum speed the emulation will not see 'double-clicks'
583: * of the mouse as it is running so fast. In this case, we check for a
584: * double-click and pass the 'up'/'down' messages in emulation time to
585: * simulate the double-click effect!
586: */
1.1.1.8 root 587: static void IKBD_CheckForDoubleClicks(void)
1.1 root 588: {
1.1.1.12 root 589: /*
590: Things get a little complicated when running max speed as a normal
591: double-click is a load of 1's, followed by 0's, 1's and 0's - But the
592: ST does not see this as a double click as the space in 'ST' time
593: between changes is so great.
594: Now, when we see a real double-click in max speed we actually send
595: the down/up/down/up in ST time. To get this correct (and not send
596: three clicks) we look in a history buffer and start at an index which
597: gives the correct number of clicks! Phew!
598: */
599:
600: /* Handle double clicks!!! */
601: if (Keyboard.LButtonDblClk)
602: {
603: if (Keyboard.LButtonDblClk == 1) /* First pressed! */
604: {
605: if ((Keyboard.LButtonHistory&0x3f) == 0) /* If not pressed button in long time do full dbl-click pattern */
606: Keyboard.LButtonDblClk = 1;
607: else
608: {
609: Keyboard.LButtonDblClk = 4; /* Otherwise, check where to begin to give 1111000011110000 pattern */
610: if ((Keyboard.LButtonHistory&0x7) == 0)
611: Keyboard.LButtonDblClk = 8;
612: else if ((Keyboard.LButtonHistory&0x3) == 0)
613: Keyboard.LButtonDblClk = 7;
614: else if ((Keyboard.LButtonHistory&0x1) == 0)
615: Keyboard.LButtonDblClk = 6;
616: }
617: }
618:
619: Keyboard.bLButtonDown = DoubleClickPattern[Keyboard.LButtonDblClk];
620: Keyboard.LButtonDblClk++;
621: if (Keyboard.LButtonDblClk >= 13) /* Check for end of sequence */
622: {
623: Keyboard.LButtonDblClk = 0;
1.1.1.15 root 624: Keyboard.bLButtonDown = false;
1.1.1.12 root 625: }
626: }
627: if (Keyboard.RButtonDblClk)
628: {
629: if (Keyboard.RButtonDblClk == 1) /* First pressed! */
630: {
631: if ((Keyboard.RButtonHistory&0x3f) == 0) /* If not pressed button in long time do full dbl-click pattern */
632: Keyboard.RButtonDblClk = 1;
633: else
634: {
635: Keyboard.RButtonDblClk = 4; /* Otherwise, check where to begin to give 1111000011110000 pattern */
636: if ((Keyboard.RButtonHistory&0x7) == 0)
637: Keyboard.RButtonDblClk = 8;
638: else if ((Keyboard.RButtonHistory&0x3) == 0)
639: Keyboard.RButtonDblClk = 7;
640: else if ((Keyboard.RButtonHistory&0x1) == 0)
641: Keyboard.RButtonDblClk = 6;
642: }
643: }
644:
645: Keyboard.bRButtonDown = DoubleClickPattern[Keyboard.RButtonDblClk];
646: Keyboard.RButtonDblClk++;
647: if (Keyboard.RButtonDblClk >= 13) /* Check for end of sequence */
648: {
649: Keyboard.RButtonDblClk = 0;
1.1.1.15 root 650: Keyboard.bRButtonDown = false;
1.1.1.12 root 651: }
652: }
653:
654: /* Store presses into history */
655: Keyboard.LButtonHistory = (Keyboard.LButtonHistory<<1);
656: if (Keyboard.bLButtonDown)
657: Keyboard.LButtonHistory |= 0x1;
658: Keyboard.RButtonHistory = (Keyboard.RButtonHistory<<1);
659: if (Keyboard.bRButtonDown)
660: Keyboard.RButtonHistory |= 0x1;
661: }
662:
663:
664: /*-----------------------------------------------------------------------*/
665: /**
1.1.1.13 root 666: * Convert button to bool value
1.1.1.12 root 667: */
1.1.1.13 root 668: static bool IKBD_ButtonBool(int Button)
1.1 root 669: {
1.1.1.12 root 670: /* Button pressed? */
671: if (Button)
1.1.1.15 root 672: return true;
673: return false;
1.1 root 674: }
675:
1.1.1.2 root 676:
677: /*-----------------------------------------------------------------------*/
1.1.1.12 root 678: /**
1.1.1.15 root 679: * Return true if buttons match, use this as buttons are a mask and not boolean
1.1.1.12 root 680: */
1.1.1.13 root 681: static bool IKBD_ButtonsEqual(int Button1,int Button2)
1.1 root 682: {
1.1.1.13 root 683: /* Return bool compare */
1.1.1.12 root 684: return (IKBD_ButtonBool(Button1) == IKBD_ButtonBool(Button2));
1.1 root 685: }
686:
1.1.1.2 root 687:
688: /*-----------------------------------------------------------------------*/
1.1.1.12 root 689: /**
1.1.1.13 root 690: * According to if the mouse is enabled or not the joystick 1 fire
691: * button/right mouse button will become the same button. That means
692: * pressing one will also press the other and vice-versa.
693: * If both mouse and joystick are enabled, report it as a mouse button
694: * (needed by the game Big Run for example).
1.1.1.12 root 695: */
1.1.1.8 root 696: static void IKBD_DuplicateMouseFireButtons(void)
1.1 root 697: {
1.1.1.12 root 698: /* If mouse is off then joystick fire button goes to joystick */
1.1.1.14 root 699: if (KeyboardProcessor.MouseMode == AUTOMODE_OFF)
1.1.1.12 root 700: {
701: /* If pressed right mouse button, should go to joystick 1 */
702: if (Keyboard.bRButtonDown&BUTTON_MOUSE)
703: KeyboardProcessor.Joy.JoyData[1] |= 0x80;
704: /* And left mouse button, should go to joystick 0 */
705: if (Keyboard.bLButtonDown&BUTTON_MOUSE)
706: KeyboardProcessor.Joy.JoyData[0] |= 0x80;
707: }
1.1.1.13 root 708: /* If mouse is on, joystick 1 fire button goes to the mouse instead */
1.1.1.12 root 709: else
710: {
711: /* Is fire button pressed? */
712: if (KeyboardProcessor.Joy.JoyData[1]&0x80)
713: {
714: KeyboardProcessor.Joy.JoyData[1] &= 0x7f; /* Clear fire button bit */
715: Keyboard.bRButtonDown |= BUTTON_JOYSTICK; /* Mimick on mouse right button */
716: }
717: else
718: Keyboard.bRButtonDown &= ~BUTTON_JOYSTICK;
719: }
1.1 root 720: }
721:
1.1.1.2 root 722:
723: /*-----------------------------------------------------------------------*/
1.1.1.12 root 724: /**
725: * Send 'relative' mouse position
726: */
1.1.1.8 root 727: static void IKBD_SendRelMousePacket(void)
1.1 root 728: {
1.1.1.12 root 729: int ByteRelX,ByteRelY;
730: Uint8 Header;
1.1 root 731:
1.1.1.12 root 732: if ( (KeyboardProcessor.Mouse.DeltaX!=0) || (KeyboardProcessor.Mouse.DeltaY!=0)
733: || (!IKBD_ButtonsEqual(Keyboard.bOldLButtonDown,Keyboard.bLButtonDown)) || (!IKBD_ButtonsEqual(Keyboard.bOldRButtonDown,Keyboard.bRButtonDown)) )
734: {
735: /* Send packet to keyboard process */
1.1.1.15 root 736: while (true)
1.1.1.12 root 737: {
738: ByteRelX = KeyboardProcessor.Mouse.DeltaX;
739: if (ByteRelX>127) ByteRelX = 127;
740: if (ByteRelX<-128) ByteRelX = -128;
741: ByteRelY = KeyboardProcessor.Mouse.DeltaY;
742: if (ByteRelY>127) ByteRelY = 127;
743: if (ByteRelY<-128) ByteRelY = -128;
744:
745: Header = 0xf8;
746: if (Keyboard.bLButtonDown)
747: Header |= 0x02;
748: if (Keyboard.bRButtonDown)
749: Header |= 0x01;
750: IKBD_AddKeyToKeyboardBuffer(Header);
751: IKBD_AddKeyToKeyboardBuffer(ByteRelX);
752: IKBD_AddKeyToKeyboardBuffer(ByteRelY*KeyboardProcessor.Mouse.YAxis);
753:
754: KeyboardProcessor.Mouse.DeltaX -= ByteRelX;
755: KeyboardProcessor.Mouse.DeltaY -= ByteRelY;
756:
757: if ( (KeyboardProcessor.Mouse.DeltaX==0) && (KeyboardProcessor.Mouse.DeltaY==0) )
758: break;
759:
760: /* Store buttons for next time around */
761: Keyboard.bOldLButtonDown = Keyboard.bLButtonDown;
762: Keyboard.bOldRButtonDown = Keyboard.bRButtonDown;
763: }
764: }
1.1 root 765: }
766:
1.1.1.2 root 767:
1.1.1.13 root 768: /**
769: * Get joystick data
770: */
771: static void IKBD_GetJoystickData(void)
772: {
773: /* Joystick 1 */
774: KeyboardProcessor.Joy.JoyData[1] = Joy_GetStickData(1);
775:
776: /* If mouse is on, joystick 0 is not connected */
777: if (KeyboardProcessor.MouseMode==AUTOMODE_OFF
778: || (bBothMouseAndJoy && KeyboardProcessor.MouseMode==AUTOMODE_MOUSEREL))
779: KeyboardProcessor.Joy.JoyData[0] = Joy_GetStickData(0);
780: else
781: KeyboardProcessor.Joy.JoyData[0] = 0x00;
782: }
783:
784:
1.1.1.2 root 785: /*-----------------------------------------------------------------------*/
1.1.1.12 root 786: /**
787: * Send 'joysticks' bit masks
788: */
1.1.1.8 root 789: static void IKBD_SelAutoJoysticks(void)
1.1 root 790: {
1.1.1.12 root 791: Uint8 JoyData;
1.1 root 792:
1.1.1.12 root 793: /* Did joystick 0/mouse change? */
794: JoyData = KeyboardProcessor.Joy.JoyData[0];
795: if (JoyData!=KeyboardProcessor.Joy.PrevJoyData[0])
796: {
797: IKBD_AddKeyToKeyboardBuffer(0xFE); /* Joystick 0/Mouse */
798: IKBD_AddKeyToKeyboardBuffer(JoyData);
799:
800: KeyboardProcessor.Joy.PrevJoyData[0] = JoyData;
801: }
802:
803: /* Did joystick 1(default) change? */
804: JoyData = KeyboardProcessor.Joy.JoyData[1];
805: if (JoyData!=KeyboardProcessor.Joy.PrevJoyData[1])
806: {
807: IKBD_AddKeyToKeyboardBuffer(0xFF); /* Joystick 1 */
808: IKBD_AddKeyToKeyboardBuffer(JoyData);
809:
810: KeyboardProcessor.Joy.PrevJoyData[1] = JoyData;
811: }
1.1 root 812: }
813:
814: /*-----------------------------------------------------------------------*/
1.1.1.12 root 815: /**
816: * Send packets which are generated from the mouse action settings
817: * If relative mode is on, still generate these packets
818: */
1.1.1.8 root 819: static void IKBD_SendOnMouseAction(void)
1.1 root 820: {
1.1.1.15 root 821: bool bReportPosition = false;
1.1 root 822:
1.1.1.12 root 823: /* Report buttons as keys? Do in relative/absolute mode */
824: if (KeyboardProcessor.Mouse.Action&0x4)
825: {
826: /* Left button? */
827: if ( (IKBD_ButtonBool(Keyboard.bLButtonDown) && (!IKBD_ButtonBool(Keyboard.bOldLButtonDown))) )
828: IKBD_AddKeyToKeyboardBuffer(0x74); /* Left */
829: else if ( (IKBD_ButtonBool(Keyboard.bOldLButtonDown) && (!IKBD_ButtonBool(Keyboard.bLButtonDown))) )
830: IKBD_AddKeyToKeyboardBuffer(0x74|0x80);
831: /* Right button? */
832: if ( (IKBD_ButtonBool(Keyboard.bRButtonDown) && (!IKBD_ButtonBool(Keyboard.bOldRButtonDown))) )
833: IKBD_AddKeyToKeyboardBuffer(0x75); /* Right */
834: else if ( (IKBD_ButtonBool(Keyboard.bOldRButtonDown) && (!IKBD_ButtonBool(Keyboard.bRButtonDown))) )
835: IKBD_AddKeyToKeyboardBuffer(0x75|0x80);
836:
837: /* Ignore bottom two bits, so return now */
838: return;
839: }
840:
841: /* Check MouseAction - report position on press/release */
842: /* MUST do this before update relative positions as buttons get reset */
843: if (KeyboardProcessor.Mouse.Action&0x3)
844: {
845: /* Check for 'press'? */
846: if (KeyboardProcessor.Mouse.Action&0x1)
847: {
848: /* Did 'press' mouse buttons? */
849: if ( (IKBD_ButtonBool(Keyboard.bLButtonDown) && (!IKBD_ButtonBool(Keyboard.bOldLButtonDown))) )
850: {
1.1.1.15 root 851: bReportPosition = true;
1.1.1.12 root 852: KeyboardProcessor.Abs.PrevReadAbsMouseButtons &= ~0x04;
853: KeyboardProcessor.Abs.PrevReadAbsMouseButtons |= 0x02;
854: }
855: if ( (IKBD_ButtonBool(Keyboard.bRButtonDown) && (!IKBD_ButtonBool(Keyboard.bOldRButtonDown))) )
856: {
1.1.1.15 root 857: bReportPosition = true;
1.1.1.12 root 858: KeyboardProcessor.Abs.PrevReadAbsMouseButtons &= ~0x01;
859: KeyboardProcessor.Abs.PrevReadAbsMouseButtons |= 0x08;
860: }
861: }
862: /* Check for 'release'? */
863: if (KeyboardProcessor.Mouse.Action&0x2)
864: {
865: /* Did 'release' mouse buttons? */
866: if ( (IKBD_ButtonBool(Keyboard.bOldLButtonDown) && (!IKBD_ButtonBool(Keyboard.bLButtonDown))) )
867: {
1.1.1.15 root 868: bReportPosition = true;
1.1.1.12 root 869: KeyboardProcessor.Abs.PrevReadAbsMouseButtons &= ~0x08;
870: KeyboardProcessor.Abs.PrevReadAbsMouseButtons |= 0x01;
871: }
872: if ( (IKBD_ButtonBool(Keyboard.bOldRButtonDown) && (!IKBD_ButtonBool(Keyboard.bRButtonDown))) )
873: {
1.1.1.15 root 874: bReportPosition = true;
1.1.1.12 root 875: KeyboardProcessor.Abs.PrevReadAbsMouseButtons &= ~0x02;
876: KeyboardProcessor.Abs.PrevReadAbsMouseButtons |= 0x04;
877: }
878: }
879:
880: /* Do need to report? */
881: if (bReportPosition)
882: {
883: /* Only report if mouse in absolute mode */
884: if (KeyboardProcessor.MouseMode==AUTOMODE_MOUSEABS)
885: {
1.1.1.15 root 886: LOG_TRACE(TRACE_IKBD_ALL, "Report ABS on MouseAction\n");
1.1.1.12 root 887: IKBD_Cmd_ReadAbsMousePos();
888: }
889: }
890: }
891: }
892:
893:
894: /*-----------------------------------------------------------------------*/
895: /**
896: * Send mouse movements as cursor keys
897: */
1.1.1.8 root 898: static void IKBD_SendCursorMousePacket(void)
1.1 root 899: {
1.1.1.12 root 900: int i=0;
1.1 root 901:
1.1.1.12 root 902: /* Run each 'Delta' as cursor presses */
903: /* Limit to '10' loops as host mouse cursor might have a VERY poor quality. */
904: /* Eg, a single mouse movement on and ST gives delta's of '1', mostly, */
905: /* but host mouse might go as high as 20+! */
1.1.1.18 root 906: //fprintf ( stderr , "key %d %d %d %d %d %d\n" , KeyboardProcessor.Mouse.DeltaX , KeyboardProcessor.Mouse.DeltaY, Keyboard.bOldLButtonDown,Keyboard.bLButtonDown, Keyboard.bOldRButtonDown,Keyboard.bRButtonDown );
1.1.1.12 root 907: while ( (i<10) && ((KeyboardProcessor.Mouse.DeltaX!=0) || (KeyboardProcessor.Mouse.DeltaY!=0)
908: || (!IKBD_ButtonsEqual(Keyboard.bOldLButtonDown,Keyboard.bLButtonDown)) || (!IKBD_ButtonsEqual(Keyboard.bOldRButtonDown,Keyboard.bRButtonDown))) )
909: {
910: /* Left? */
911: if (KeyboardProcessor.Mouse.DeltaX<0)
912: {
913: IKBD_AddKeyToKeyboardBuffer(75); /* Left cursor */
914: IKBD_AddKeyToKeyboardBuffer(75|0x80);
915: KeyboardProcessor.Mouse.DeltaX++;
916: }
917: /* Right? */
918: if (KeyboardProcessor.Mouse.DeltaX>0)
919: {
920: IKBD_AddKeyToKeyboardBuffer(77); /* Right cursor */
921: IKBD_AddKeyToKeyboardBuffer(77|0x80);
922: KeyboardProcessor.Mouse.DeltaX--;
923: }
924: /* Up? */
925: if (KeyboardProcessor.Mouse.DeltaY<0)
926: {
927: IKBD_AddKeyToKeyboardBuffer(72); /* Up cursor */
928: IKBD_AddKeyToKeyboardBuffer(72|0x80);
929: KeyboardProcessor.Mouse.DeltaY++;
930: }
931: /* Down? */
932: if (KeyboardProcessor.Mouse.DeltaY>0)
933: {
934: IKBD_AddKeyToKeyboardBuffer(80); /* Down cursor */
935: IKBD_AddKeyToKeyboardBuffer(80|0x80);
936: KeyboardProcessor.Mouse.DeltaY--;
937: }
938:
939: /* Left button? */
940: if ( (IKBD_ButtonBool(Keyboard.bLButtonDown) && (!IKBD_ButtonBool(Keyboard.bOldLButtonDown))) )
941: IKBD_AddKeyToKeyboardBuffer(0x74); /* Left */
942: else if ( (IKBD_ButtonBool(Keyboard.bOldLButtonDown) && (!IKBD_ButtonBool(Keyboard.bLButtonDown))) )
943: IKBD_AddKeyToKeyboardBuffer(0x74|0x80);
944: /* Right button? */
945: if ( (IKBD_ButtonBool(Keyboard.bRButtonDown) && (!IKBD_ButtonBool(Keyboard.bOldRButtonDown))) )
946: IKBD_AddKeyToKeyboardBuffer(0x75); /* Right */
947: else if ( (IKBD_ButtonBool(Keyboard.bOldRButtonDown) && (!IKBD_ButtonBool(Keyboard.bRButtonDown))) )
948: IKBD_AddKeyToKeyboardBuffer(0x75|0x80);
949:
950: Keyboard.bOldLButtonDown = Keyboard.bLButtonDown;
951: Keyboard.bOldRButtonDown = Keyboard.bRButtonDown;
952:
953: /* Count, so exit if try too many times! */
954: i++;
955: }
1.1 root 956: }
957:
1.1.1.2 root 958:
959: /*-----------------------------------------------------------------------*/
1.1.1.12 root 960: /**
961: * Return packets from keyboard for auto, rel mouse, joystick etc...
962: */
1.1.1.14 root 963: static void IKBD_SendAutoKeyboardCommands(void)
1.1 root 964: {
1.1.1.12 root 965: /* Don't do anything until processor is first reset */
966: if (!KeyboardProcessor.bReset)
967: return;
968:
969: /* Read joysticks for this frame */
1.1.1.13 root 970: IKBD_GetJoystickData();
1.1.1.12 root 971:
972: /* Check for double-clicks in maximum speed mode */
973: IKBD_CheckForDoubleClicks();
974:
975: /* Handle Joystick/Mouse fire buttons */
976: IKBD_DuplicateMouseFireButtons();
977:
978: /* Send any packets which are to be reported by mouse action */
979: IKBD_SendOnMouseAction();
980:
981: /* Update internal mouse absolute position by find 'delta' of mouse movement */
982: IKBD_UpdateInternalMousePosition();
983:
984: /* Send automatic joystick packets */
985: if (KeyboardProcessor.JoystickMode==AUTOMODE_JOYSTICK)
986: IKBD_SelAutoJoysticks();
987: /* Send automatic relative mouse positions(absolute are not send automatically) */
988: if (KeyboardProcessor.MouseMode==AUTOMODE_MOUSEREL)
989: IKBD_SendRelMousePacket();
990: /* Send cursor key directions for movements */
991: else if (KeyboardProcessor.MouseMode==AUTOMODE_MOUSECURSOR)
992: IKBD_SendCursorMousePacket();
993:
994: /* Store buttons for next time around */
995: Keyboard.bOldLButtonDown = Keyboard.bLButtonDown;
996: Keyboard.bOldRButtonDown = Keyboard.bRButtonDown;
997:
998: /* Send joystick button '2' as 'Space bar' key - MUST do here so does not get mixed up in middle of joystick packets! */
999: if (JoystickSpaceBar)
1000: {
1001: /* As we simulating space bar? */
1002: if (JoystickSpaceBar==JOYSTICK_SPACE_DOWN)
1003: {
1.1.1.15 root 1004: IKBD_PressSTKey(57, true); /* Press */
1.1.1.12 root 1005: JoystickSpaceBar = JOYSTICK_SPACE_UP;
1006: }
1007: else //if (JoystickSpaceBar==JOYSTICK_SPACE_UP) {
1008: {
1.1.1.15 root 1009: IKBD_PressSTKey(57, false); /* Release */
1010: JoystickSpaceBar = false; /* Complete */
1.1.1.12 root 1011: }
1012: }
1.1 root 1013: }
1014:
1.1.1.2 root 1015:
1016: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1017: /**
1.1.1.14 root 1018: * This function is called regularly to automatically send keyboard, mouse
1019: * and joystick updates.
1020: */
1021: void IKBD_InterruptHandler_AutoSend(void)
1022: {
1023: /* Handle user events and other messages, (like quit message) */
1024: Main_EventHandler();
1025:
1.1.1.17 root 1026: /* Remove this interrupt from list and re-order.
1027: * (needs to be done after UI event handling so
1028: * that snapshots saved from UI and restored from
1029: * command line don't miss the AUTOSEND interrupt)
1030: */
1031: CycInt_AcknowledgeInterrupt();
1032:
1.1.1.14 root 1033: /* Did user try to quit? */
1034: if (bQuitProgram)
1035: {
1036: /* Pass NULL interrupt function to quit cleanly */
1.1.1.16 root 1037: CycInt_AddAbsoluteInterrupt(4, INT_CPU_CYCLE, INTERRUPT_NULL);
1.1.1.14 root 1038: /* Assure that CPU core shuts down */
1039: M68000_SetSpecial(SPCFLAG_BRK);
1040: return;
1041: }
1042:
1043: /* Trigger this auto-update function again after a while */
1.1.1.16 root 1044: CycInt_AddRelativeInterrupt(150000, INT_CPU_CYCLE, INTERRUPT_IKBD_AUTOSEND);
1.1.1.14 root 1045:
1046: /* We don't send keyboard data automatically within the first few
1047: * VBLs to avoid that TOS gets confused during its boot time */
1048: if (nVBLs > 20)
1049: {
1050: /* Send automatic keyboard packets for mouse, joysticks etc... */
1051: IKBD_SendAutoKeyboardCommands();
1052: }
1053: }
1054:
1055:
1056: /*-----------------------------------------------------------------------*/
1057: /**
1.1.1.12 root 1058: * On ST if disable Mouse AND Joystick with a set time of a RESET command they are
1059: * actually turned back on! (A number of games do this so can get mouse and joystick
1060: * packets at the same time)
1061: */
1.1.1.8 root 1062: static void IKBD_CheckResetDisableBug(void)
1.1 root 1063: {
1.1.1.12 root 1064: /* Have disabled BOTH mouse and joystick? */
1065: if (bMouseDisabled && bJoystickDisabled)
1066: {
1067: /* And in critical time? */
1068: if (bDuringResetCriticalTime)
1069: {
1070: /* Emulate relative mouse and joystick reports being turned back on */
1071: KeyboardProcessor.MouseMode = AUTOMODE_MOUSEREL;
1072: KeyboardProcessor.JoystickMode = AUTOMODE_JOYSTICK;
1.1.1.15 root 1073: bBothMouseAndJoy = true;
1.1.1.6 root 1074:
1.1.1.19! root 1075: LOG_TRACE(TRACE_IKBD_ALL, "ikbd cancel commands 0x12 and 0x1a received during reset,"
! 1076: " enabling joystick and mouse reporting at the same time\n" );
1.1.1.12 root 1077: }
1078: }
1.1 root 1079: }
1080:
1.1.1.2 root 1081:
1082: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1083: /**
1084: * Start timer after keyboard RESET command to emulate 'quirk'
1085: * If some IKBD commands are sent during time after a RESET they may be ignored
1086: */
1.1 root 1087: void IKBD_InterruptHandler_ResetTimer(void)
1088: {
1.1.1.19! root 1089: LOG_TRACE(TRACE_IKBD_CMDS, "ikbd reset timer completed, resuming ikbd processing VBLs=%i framecyc=%i\n",
! 1090: nVBLs, Cycles_GetCounter(CYCLES_COUNTER_VIDEO));
! 1091:
1.1.1.12 root 1092: /* Remove this interrupt from list and re-order */
1.1.1.16 root 1093: CycInt_AcknowledgeInterrupt();
1.1 root 1094:
1.1.1.12 root 1095: /* Turn processor on; can now process commands */
1.1.1.15 root 1096: KeyboardProcessor.bReset = true;
1.1.1.9 root 1097:
1.1.1.12 root 1098: /* Critical timer is over */
1.1.1.15 root 1099: bDuringResetCriticalTime = false;
1100: bMouseEnabledDuringReset = false;
1.1 root 1101: }
1102:
1103:
1104:
1.1.1.2 root 1105: /*-----------------------------------------------------------------------*/
1.1 root 1106: /*
1107: List of keyboard commands
1108: */
1109:
1110:
1.1.1.2 root 1111: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1112: /**
1113: * RESET
1114: *
1115: * 0x80
1116: * 0x01
1117: *
1.1.1.15 root 1118: * Performs self test and checks for stuck (closed) keys, if OK returns 0xF0 or
1119: * 0xF1. Otherwise returns break codes for keys (not emulated).
1.1.1.12 root 1120: */
1.1.1.13 root 1121: static void IKBD_Cmd_Reset(void)
1.1 root 1122: {
1.1.1.19! root 1123: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_Reset VBLs=%i framecyc=%i\n",
1.1.1.15 root 1124: nVBLs, Cycles_GetCounter(CYCLES_COUNTER_VIDEO));
1.1.1.14 root 1125:
1.1.1.12 root 1126: /* Check for error series of bytes, eg 0x80,0x01 */
1127: if (Keyboard.InputBuffer[1] == 0x01)
1128: {
1129: /* Set defaults */
1130: KeyboardProcessor.MouseMode = AUTOMODE_MOUSEREL;
1131: KeyboardProcessor.JoystickMode = AUTOMODE_JOYSTICK;
1132: KeyboardProcessor.Abs.X = ABS_X_ONRESET;
1133: KeyboardProcessor.Abs.Y = ABS_Y_ONRESET;
1134: KeyboardProcessor.Abs.MaxX = ABS_MAX_X_ONRESET;
1135: KeyboardProcessor.Abs.MaxY = ABS_MAY_Y_ONRESET;
1136: KeyboardProcessor.Abs.PrevReadAbsMouseButtons = ABS_PREVBUTTONS;
1137:
1.1.1.15 root 1138: /* flush all queued bytes that would be read in $fffc02 */
1139: Keyboard.BufferHead = Keyboard.BufferTail = 0;
1140:
1141: /* This command returns either the byte 0xf0 or 0xf1 (depending
1142: * on the version of the IKBD ROM) when the reset has been
1143: * successful. Some notes:
1144: * - Dragonnels demo requires 0xf1 so we use only this value
1145: * right now.
1146: * - Lotus Turbo Esprit 2 requires at least a delay of 50000
1147: * cycles or it will crash during start up.
1148: */
1149: IKBD_AddKeyToKeyboardBufferWithDelay(0xf1, 50000);
1.1.1.12 root 1150:
1151: /* Start timer - some commands are send during this time they may be ignored (see real ST!) */
1.1.1.16 root 1152: CycInt_AddRelativeInterrupt(IKBD_RESET_CYCLES, INT_CPU_CYCLE, INTERRUPT_IKBD_RESETTIMER);
1.1.1.12 root 1153:
1154: /* Set this 'critical' flag, gets reset when timer expires */
1.1.1.15 root 1155: bDuringResetCriticalTime = true;
1156: bMouseDisabled = bJoystickDisabled = false;
1157: bBothMouseAndJoy = false;
1158: bMouseEnabledDuringReset = false;
1.1.1.14 root 1159:
1.1.1.19! root 1160: LOG_TRACE(TRACE_IKBD_ALL, "ikbd reset done, starting reset timer\n");
1.1.1.12 root 1161: }
1162: /* else if not 0x80,0x01 just ignore */
1.1 root 1163: }
1164:
1.1.1.2 root 1165:
1166: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1167: /**
1168: * SET MOUSE BUTTON ACTION
1169: *
1170: * 0x07
1171: * %00000mss ; mouse button action
1172: * ; (m is presumed =1 when in MOUSE KEYCODE mode)
1173: * ; mss=0xy, mouse button press or release causes mouse
1174: * ; position report
1175: * ; where y=1, mouse key press causes absolute report
1176: * ; and x=1, mouse key release causes absolute report
1177: * ; mss=100, mouse buttons act like keys
1178: */
1.1.1.13 root 1179: static void IKBD_Cmd_MouseAction(void)
1.1 root 1180: {
1.1.1.12 root 1181: KeyboardProcessor.Mouse.Action = Keyboard.InputBuffer[1];
1182: KeyboardProcessor.Abs.PrevReadAbsMouseButtons = ABS_PREVBUTTONS;
1.1.1.13 root 1183:
1.1.1.15 root 1184: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_MouseAction %d\n",
1185: (unsigned int)KeyboardProcessor.Mouse.Action);
1.1 root 1186: }
1187:
1.1.1.2 root 1188:
1189: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1190: /**
1191: * SET RELATIVE MOUSE POSITION REPORTING
1192: *
1193: * 0x08
1194: */
1.1.1.13 root 1195: static void IKBD_Cmd_RelMouseMode(void)
1.1 root 1196: {
1.1.1.12 root 1197: KeyboardProcessor.MouseMode = AUTOMODE_MOUSEREL;
1.1.1.13 root 1198:
1.1.1.14 root 1199: /* Some games (like Barbarian by Psygnosis) enable both, mouse and
1200: * joystick directly after a reset. This causes the IKBD to send both
1201: * type of packets. To emulate this feature, we've got to remember
1202: * that the mouse has been enabled during reset. */
1203: if (bDuringResetCriticalTime)
1.1.1.15 root 1204: bMouseEnabledDuringReset = true;
1.1.1.14 root 1205:
1.1.1.15 root 1206: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_RelMouseMode\n");
1.1 root 1207: }
1208:
1.1.1.2 root 1209:
1210: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1211: /**
1212: * SET ABSOLUTE MOUSE POSITIONING
1213: *
1214: * 0x09
1215: * XMSB ;X maximum (in scaled mouse clicks)
1216: * XLSB
1217: * YMSB ;Y maximum (in scaled mouse clicks)
1218: * YLSB
1219: */
1.1.1.13 root 1220: static void IKBD_Cmd_AbsMouseMode(void)
1.1 root 1221: {
1.1.1.12 root 1222: /* These maximums are 'inclusive' */
1223: KeyboardProcessor.MouseMode = AUTOMODE_MOUSEABS;
1224: KeyboardProcessor.Abs.MaxX = (((unsigned int)Keyboard.InputBuffer[1])<<8) | (unsigned int)Keyboard.InputBuffer[2];
1225: KeyboardProcessor.Abs.MaxY = (((unsigned int)Keyboard.InputBuffer[3])<<8) | (unsigned int)Keyboard.InputBuffer[4];
1.1.1.13 root 1226:
1.1.1.15 root 1227: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_AbsMouseMode %d,%d\n",
1228: KeyboardProcessor.Abs.MaxX, KeyboardProcessor.Abs.MaxY);
1.1 root 1229: }
1230:
1.1.1.2 root 1231:
1232: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1233: /**
1234: * SET MOUSE KEYCODE MODE
1235: *
1236: * 0x0A
1237: * deltax ; distance in X clicks to return (LEFT) or (RIGHT)
1238: * deltay ; distance in Y clicks to return (UP) or (DOWN)
1239: */
1.1.1.13 root 1240: static void IKBD_Cmd_MouseCursorKeycodes(void)
1.1 root 1241: {
1.1.1.12 root 1242: KeyboardProcessor.MouseMode = AUTOMODE_MOUSECURSOR;
1243: KeyboardProcessor.Mouse.KeyCodeDeltaX = Keyboard.InputBuffer[1];
1244: KeyboardProcessor.Mouse.KeyCodeDeltaY = Keyboard.InputBuffer[2];
1.1.1.13 root 1245:
1.1.1.15 root 1246: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_MouseCursorKeycodes %d,%d\n",
1247: (int)KeyboardProcessor.Mouse.KeyCodeDeltaX,
1248: (int)KeyboardProcessor.Mouse.KeyCodeDeltaY);
1.1 root 1249: }
1250:
1.1.1.2 root 1251:
1252: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1253: /**
1254: * SET MOUSE THRESHOLD
1255: *
1256: * 0x0B
1257: * X ; x threshold in mouse ticks (positive integers)
1258: * Y ; y threshold in mouse ticks (positive integers)
1259: */
1.1.1.13 root 1260: static void IKBD_Cmd_SetMouseThreshold(void)
1.1 root 1261: {
1.1.1.12 root 1262: KeyboardProcessor.Mouse.XThreshold = (unsigned int)Keyboard.InputBuffer[1];
1263: KeyboardProcessor.Mouse.YThreshold = (unsigned int)Keyboard.InputBuffer[2];
1.1.1.13 root 1264:
1.1.1.15 root 1265: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetMouseThreshold %d,%d\n",
1266: KeyboardProcessor.Mouse.XThreshold, KeyboardProcessor.Mouse.YThreshold);
1.1 root 1267: }
1268:
1.1.1.2 root 1269:
1270: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1271: /**
1272: * SET MOUSE SCALE
1273: *
1274: * 0x0C
1275: * X ; horizontal mouse ticks per internel X
1276: * Y ; vertical mouse ticks per internel Y
1277: */
1.1.1.13 root 1278: static void IKBD_Cmd_SetMouseScale(void)
1.1 root 1279: {
1.1.1.12 root 1280: KeyboardProcessor.Mouse.XScale = (unsigned int)Keyboard.InputBuffer[1];
1281: KeyboardProcessor.Mouse.YScale = (unsigned int)Keyboard.InputBuffer[2];
1.1.1.13 root 1282:
1.1.1.15 root 1283: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetMouseScale %d,%d\n",
1284: KeyboardProcessor.Mouse.XScale, KeyboardProcessor.Mouse.YScale);
1.1 root 1285: }
1286:
1287:
1.1.1.12 root 1288: /*-----------------------------------------------------------------------*/
1289: /**
1290: * INTERROGATE MOUSE POSITION
1291: *
1292: * 0x0D
1293: * Returns: 0xF7 ; absolute mouse position header
1294: * BUTTONS
1295: * 0000dcba
1296: * where a is right button down since last interrogation
1297: * b is right button up since last
1298: * c is left button down since last
1299: * d is left button up since last
1300: * XMSB ; X coordinate
1301: * XLSB
1302: * YMSB ; Y coordinate
1303: * YLSB
1304: */
1.1.1.13 root 1305: static void IKBD_Cmd_ReadAbsMousePos(void)
1.1 root 1306: {
1.1.1.12 root 1307: Uint8 Buttons,PrevButtons;
1.1 root 1308:
1.1.1.12 root 1309: /* Test buttons */
1310: Buttons = 0;
1311: /* Set buttons to show if up/down */
1312: if (Keyboard.bRButtonDown)
1313: Buttons |= 0x01;
1314: else
1315: Buttons |= 0x02;
1316: if (Keyboard.bLButtonDown)
1317: Buttons |= 0x04;
1318: else
1319: Buttons |= 0x08;
1320: /* Mask off it didn't send last time */
1321: PrevButtons = KeyboardProcessor.Abs.PrevReadAbsMouseButtons;
1322: KeyboardProcessor.Abs.PrevReadAbsMouseButtons = Buttons;
1323: Buttons &= ~PrevButtons;
1324:
1325: /* And send packet */
1.1.1.13 root 1326: IKBD_AddKeyToKeyboardBufferWithDelay(0xf7, 18000);
1.1.1.12 root 1327: IKBD_AddKeyToKeyboardBuffer(Buttons);
1328: IKBD_AddKeyToKeyboardBuffer((unsigned int)KeyboardProcessor.Abs.X>>8);
1329: IKBD_AddKeyToKeyboardBuffer((unsigned int)KeyboardProcessor.Abs.X&0xff);
1330: IKBD_AddKeyToKeyboardBuffer((unsigned int)KeyboardProcessor.Abs.Y>>8);
1331: IKBD_AddKeyToKeyboardBuffer((unsigned int)KeyboardProcessor.Abs.Y&0xff);
1332:
1.1.1.15 root 1333: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReadAbsMousePos %d,%d 0x%X\n",
1334: KeyboardProcessor.Abs.X, KeyboardProcessor.Abs.Y, Buttons);
1.1 root 1335: }
1336:
1337:
1.1.1.12 root 1338: /*-----------------------------------------------------------------------*/
1339: /**
1340: * LOAD MOUSE POSITION
1341: *
1342: * 0x0E
1343: * 0x00 ; filler
1344: * XMSB ; X coordinate
1345: * XLSB ; (in scaled coordinate system)
1346: * YMSB ; Y coordinate
1347: * YLSB
1348: */
1.1.1.13 root 1349: static void IKBD_Cmd_SetInternalMousePos(void)
1.1.1.7 root 1350: {
1.1.1.12 root 1351: /* Setting these do not clip internal position(this happens on next update) */
1352: KeyboardProcessor.Abs.X = (((unsigned int)Keyboard.InputBuffer[2])<<8) | (unsigned int)Keyboard.InputBuffer[3];
1353: KeyboardProcessor.Abs.Y = (((unsigned int)Keyboard.InputBuffer[4])<<8) | (unsigned int)Keyboard.InputBuffer[5];
1.1.1.13 root 1354:
1.1.1.15 root 1355: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetInternalMousePos %d,%d\n",
1356: KeyboardProcessor.Abs.X, KeyboardProcessor.Abs.Y);
1.1 root 1357: }
1358:
1.1.1.2 root 1359:
1360: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1361: /**
1362: * SET Y=0 AT BOTTOM
1363: *
1364: * 0x0F
1365: */
1.1.1.13 root 1366: static void IKBD_Cmd_SetYAxisDown(void)
1.1 root 1367: {
1.1.1.12 root 1368: KeyboardProcessor.Mouse.YAxis = -1;
1.1.1.13 root 1369:
1.1.1.15 root 1370: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetYAxisDown\n");
1.1 root 1371: }
1372:
1.1.1.2 root 1373:
1374: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1375: /**
1376: * SET Y=0 AT TOP
1377: *
1378: * 0x10
1379: */
1.1.1.13 root 1380: static void IKBD_Cmd_SetYAxisUp(void)
1.1 root 1381: {
1.1.1.12 root 1382: KeyboardProcessor.Mouse.YAxis = 1;
1.1.1.13 root 1383:
1.1.1.15 root 1384: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetYAxisUp\n");
1.1 root 1385: }
1386:
1.1.1.2 root 1387:
1388: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1389: /**
1390: * RESUME
1391: *
1392: * 0x11
1393: */
1.1.1.13 root 1394: static void IKBD_Cmd_StartKeyboardTransfer(void)
1.1 root 1395: {
1.1.1.15 root 1396: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_StartKeyboardTransfer\n");
1.1 root 1397: }
1398:
1.1.1.2 root 1399:
1400: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1401: /**
1402: * DISABLE MOUSE
1403: *
1404: * 0x12
1405: */
1.1.1.13 root 1406: static void IKBD_Cmd_TurnMouseOff(void)
1.1 root 1407: {
1.1.1.12 root 1408: KeyboardProcessor.MouseMode = AUTOMODE_OFF;
1.1.1.15 root 1409: bMouseDisabled = true;
1.1.1.13 root 1410:
1.1.1.15 root 1411: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_TurnMouseOff\n");
1.1 root 1412:
1.1.1.12 root 1413: IKBD_CheckResetDisableBug();
1.1 root 1414: }
1415:
1.1.1.2 root 1416:
1417: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1418: /**
1419: * PAUSE OUTPUT
1420: *
1421: * 0x13
1422: */
1.1.1.13 root 1423: static void IKBD_Cmd_StopKeyboardTransfer(void)
1.1 root 1424: {
1.1.1.15 root 1425: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_StopKeyboardTransfer\n");
1.1 root 1426: }
1427:
1.1.1.2 root 1428:
1429: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1430: /**
1431: * SET JOYSTICK EVENT REPORTING
1432: *
1433: * 0x14
1434: */
1.1.1.13 root 1435: static void IKBD_Cmd_ReturnJoystickAuto(void)
1.1 root 1436: {
1.1.1.15 root 1437: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReturnJoystickAuto\n");
1.1.1.14 root 1438:
1.1.1.12 root 1439: KeyboardProcessor.JoystickMode = AUTOMODE_JOYSTICK;
1440: KeyboardProcessor.MouseMode = AUTOMODE_OFF;
1.1.1.6 root 1441:
1.1.1.19! root 1442: /* If mouse was also enabled within time of a reset (0x08 command) it isn't disabled now!
! 1443: * (used by the game Barbarian 1 by Psygnosis for example) */
! 1444: if ( bDuringResetCriticalTime && bMouseEnabledDuringReset )
! 1445: {
! 1446: KeyboardProcessor.MouseMode = AUTOMODE_MOUSEREL;
! 1447: bBothMouseAndJoy = true;
! 1448: LOG_TRACE(TRACE_IKBD_ALL, "ikbd commands 0x08 and 0x14 received during reset,"
! 1449: " enabling joystick and mouse reporting at the same time\n" );
! 1450: }
! 1451: /* If mouse was disabled during the reset (0x12 command) it is enabled again
! 1452: * (used by the game Hammerfist for example) */
! 1453: else if ( bDuringResetCriticalTime && bMouseDisabled )
1.1.1.12 root 1454: {
1455: KeyboardProcessor.MouseMode = AUTOMODE_MOUSEREL;
1.1.1.15 root 1456: bBothMouseAndJoy = true;
1.1.1.19! root 1457: LOG_TRACE(TRACE_IKBD_ALL, "ikbd commands 0x12 and 0x14 received during reset,"
! 1458: " enabling joystick and mouse reporting at the same time\n" );
1.1.1.12 root 1459: }
1.1.1.6 root 1460:
1.1.1.13 root 1461: /* This command resets the internally previously stored joystick states */
1462: KeyboardProcessor.Joy.PrevJoyData[0] = KeyboardProcessor.Joy.PrevJoyData[1] = 0;
1463:
1464: /* This is a hack for the STE Utopos (=> v1.50) and Falcon Double Bubble
1465: * 2000 games. They expect the joystick data to be sent within a certain
1466: * amount of time after this command, without checking the ACIA control
1467: * register first.
1468: */
1469: IKBD_GetJoystickData();
1470: IKBD_SelAutoJoysticks();
1.1 root 1471: }
1472:
1.1.1.2 root 1473:
1474: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1475: /**
1476: * SET JOYSTICK INTERROGATION MODE
1477: *
1478: * 0x15
1479: */
1.1.1.13 root 1480: static void IKBD_Cmd_StopJoystick(void)
1.1 root 1481: {
1.1.1.12 root 1482: KeyboardProcessor.JoystickMode = AUTOMODE_OFF;
1.1.1.15 root 1483: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_StopJoystick\n");
1.1 root 1484: }
1485:
1.1.1.2 root 1486:
1487: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1488: /**
1489: * JOYSTICK INTERROGATE
1490: *
1491: * 0x16
1492: */
1.1.1.13 root 1493: static void IKBD_Cmd_ReturnJoystick(void)
1.1 root 1494: {
1.1.1.17 root 1495: /* The game "Downfall" continually issues this command during the
1496: * title screen - faster than processing the 3 bytes from the ACIA,
1497: * so it floods our Keyboard.Buffer ... in the end, we can not put the
1498: * whole packet into the buffer anymore, and the game hangs due to
1499: * these incomplete answers. To avoid this situation, only execute
1500: * this command if there is enough space left in our Keyboard.Buffer! */
1501: if (((Keyboard.BufferHead-1-Keyboard.BufferTail)&KEYBOARD_BUFFER_MASK) < 3)
1502: {
1503: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReturnJoystick ignored - buffer is full!\n");
1504: return;
1505: }
1506:
1507: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReturnJoystick\n");
1508:
1.1.1.13 root 1509: IKBD_AddKeyToKeyboardBufferWithDelay(0xFD, 35000);
1.1.1.12 root 1510: IKBD_AddKeyToKeyboardBuffer(Joy_GetStickData(0));
1511: IKBD_AddKeyToKeyboardBuffer(Joy_GetStickData(1));
1.1 root 1512: }
1513:
1.1.1.2 root 1514:
1515: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1516: /**
1517: * SET JOYSTICK MONITORING
1518: *
1519: * 0x17
1520: * rate ; time between samples in hundreths of a second
1521: * Returns: (in packets of two as long as in mode)
1522: * %000000xy where y is JOYSTICK1 Fire button
1523: * and x is JOYSTICK0 Fire button
1524: * %nnnnmmmm where m is JOYSTICK1 state
1525: * and n is JOYSTICK0 state
1526: */
1.1.1.13 root 1527: static void IKBD_Cmd_SetJoystickDuration(void)
1.1 root 1528: {
1.1.1.15 root 1529: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetJoystickDuration\n");
1.1 root 1530: }
1531:
1.1.1.2 root 1532:
1533: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1534: /**
1535: * SET FIRE BUTTON MONITORING
1536: *
1537: * 0x18
1538: * Returns: (as long as in mode)
1539: * %bbbbbbbb ; state of the JOYSTICK1 fire button packed
1540: * ; 8 bits per byte, the first sample if the MSB
1541: */
1.1.1.13 root 1542: static void IKBD_Cmd_SetJoystickFireDuration(void)
1.1 root 1543: {
1.1.1.15 root 1544: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetJoystickFireDuration\n");
1.1 root 1545: }
1546:
1.1.1.2 root 1547:
1548: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1549: /**
1550: * SET JOYSTICK KEYCODE MODE
1551: *
1552: * 0x19
1553: * RX ; length of time (in tenths of seconds) until
1554: * ; horizontal velocity breakpoint is reached
1555: * RY ; length of time (in tenths of seconds) until
1556: * ; vertical velocity breakpoint is reached
1557: * TX ; length (in tenths of seconds) of joystick closure
1558: * ; until horizontal cursor key is generated before RX
1559: * ; has elapsed
1560: * TY ; length (in tenths of seconds) of joystick closure
1561: * ; until vertical cursor key is generated before RY
1562: * ; has elapsed
1563: * VX ; length (in tenths of seconds) of joystick closure
1564: * ; until horizontal cursor keystokes are generated after RX
1565: * ; has elapsed
1566: * VY ; length (in tenths of seconds) of joystick closure
1567: * ; until vertical cursor keystokes are generated after RY
1568: * ; has elapsed
1569: */
1.1.1.13 root 1570: static void IKBD_Cmd_SetCursorForJoystick(void)
1.1 root 1571: {
1.1.1.15 root 1572: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetCursorForJoystick\n");
1.1 root 1573: }
1574:
1.1.1.2 root 1575:
1576: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1577: /**
1578: * DISABLE JOYSTICKS
1579: *
1580: * 0x1A
1581: */
1.1.1.13 root 1582: static void IKBD_Cmd_DisableJoysticks(void)
1.1 root 1583: {
1.1.1.12 root 1584: KeyboardProcessor.JoystickMode = AUTOMODE_OFF;
1.1.1.15 root 1585: bJoystickDisabled = true;
1.1.1.13 root 1586:
1.1.1.15 root 1587: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_DisableJoysticks\n");
1.1 root 1588:
1.1.1.12 root 1589: IKBD_CheckResetDisableBug();
1.1 root 1590: }
1591:
1.1.1.2 root 1592:
1.1.1.19! root 1593: /**
! 1594: * Convert value from 2-digit BCD.
! 1595: */
! 1596: static Uint16 IKBD_FromBCD(Uint8 Value)
! 1597: {
! 1598: return ((Value >> 4) * 10) | (Value & 0x0f);
! 1599: }
! 1600:
! 1601:
! 1602: /**
! 1603: * Convert value to 2-digit BCD.
! 1604: * Note: TOS 2.0x requires BCD conversion with overflow, so the decade
! 1605: * is not calculated modulo 10, or it will end up in the year 2039
! 1606: * instead...
! 1607: */
! 1608: static Uint8 IKBD_ToBCD(Uint16 Value)
! 1609: {
! 1610: return ((Value / 10) << 4) | (Value % 10);
! 1611: }
! 1612:
! 1613:
1.1.1.12 root 1614: /**
1615: * TIME-OF-DAY CLOCK SET
1616: *
1617: * 0x1B
1618: * YY ; year (2 least significant digits)
1619: * MM ; month
1620: * DD ; day
1621: * hh ; hour
1622: * mm ; minute
1623: * ss ; second
1624: */
1.1.1.13 root 1625: static void IKBD_Cmd_SetClock(void)
1.1 root 1626: {
1.1.1.19! root 1627: struct tm NewTime;
1.1.1.13 root 1628:
1.1.1.19! root 1629: LOG_TRACE(TRACE_IKBD_CMDS,
! 1630: "IKBD_Cmd_SetClock: %02x %02x %02x %02x %02x %02x\n",
! 1631: Keyboard.InputBuffer[1], Keyboard.InputBuffer[2],
! 1632: Keyboard.InputBuffer[3], Keyboard.InputBuffer[4],
! 1633: Keyboard.InputBuffer[5], Keyboard.InputBuffer[6]);
! 1634:
! 1635: NewTime.tm_year = IKBD_FromBCD(Keyboard.InputBuffer[1]);
! 1636: NewTime.tm_mon = IKBD_FromBCD(Keyboard.InputBuffer[2]) - 1;
! 1637: NewTime.tm_mday = IKBD_FromBCD(Keyboard.InputBuffer[3]);
! 1638: NewTime.tm_hour = IKBD_FromBCD(Keyboard.InputBuffer[4]);
! 1639: NewTime.tm_min = IKBD_FromBCD(Keyboard.InputBuffer[5]);
! 1640: NewTime.tm_sec = IKBD_FromBCD(Keyboard.InputBuffer[6]);
1.1.1.13 root 1641:
1.1.1.19! root 1642: nTimeOffset = time(NULL) - mktime(&NewTime);
1.1 root 1643: }
1644:
1.1.1.2 root 1645:
1.1.1.12 root 1646: /**
1647: * INTERROGATE TIME-OF-DAT CLOCK
1648: *
1649: * 0x1C
1650: * Returns:
1651: * 0xFC ; time-of-day event header
1652: * YY ; year (2 least significant digits)
1653: * MM ; month
1654: * DD ; day
1655: * hh ; hour
1656: * mm ; minute
1657: * ss ; second
1658: */
1.1.1.13 root 1659: static void IKBD_Cmd_ReadClock(void)
1.1 root 1660: {
1.1.1.12 root 1661: struct tm *SystemTime;
1662: time_t nTimeTicks;
1.1 root 1663:
1.1.1.12 root 1664: /* Get system time */
1.1.1.19! root 1665: nTimeTicks = time(NULL) - nTimeOffset;
1.1.1.12 root 1666: SystemTime = localtime(&nTimeTicks);
1667:
1668: /* Return packet */
1.1.1.13 root 1669: IKBD_AddKeyToKeyboardBufferWithDelay(0xFC, 32000);
1.1.1.12 root 1670: /* Return time-of-day clock as yy-mm-dd-hh-mm-ss as BCD */
1.1.1.19! root 1671: IKBD_AddKeyToKeyboardBuffer(IKBD_ToBCD(SystemTime->tm_year)); /* yy - year (2 least significant digits) */
! 1672: IKBD_AddKeyToKeyboardBuffer(IKBD_ToBCD(SystemTime->tm_mon+1)); /* mm - Month */
! 1673: IKBD_AddKeyToKeyboardBuffer(IKBD_ToBCD(SystemTime->tm_mday)); /* dd - Day */
! 1674: IKBD_AddKeyToKeyboardBuffer(IKBD_ToBCD(SystemTime->tm_hour)); /* hh - Hour */
! 1675: IKBD_AddKeyToKeyboardBuffer(IKBD_ToBCD(SystemTime->tm_min)); /* mm - Minute */
! 1676: IKBD_AddKeyToKeyboardBuffer(IKBD_ToBCD(SystemTime->tm_sec)); /* ss - Second */
1.1.1.13 root 1677:
1.1.1.15 root 1678: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReadClock\n");
1.1 root 1679: }
1680:
1.1.1.2 root 1681:
1682: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1683: /**
1684: * MEMORY LOAD
1685: *
1686: * 0x20
1687: * ADRMSB ; address in controller
1688: * ADRLSB ; memory to be loaded
1689: * NUM ; number of bytes (0-128)
1690: * { data }
1691: */
1.1.1.13 root 1692: static void IKBD_Cmd_LoadMemory(void)
1.1 root 1693: {
1.1.1.15 root 1694: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_LoadMemory addr 0x%x count %d\n",
1695: (Keyboard.InputBuffer[1] << 8) + Keyboard.InputBuffer[2], Keyboard.InputBuffer[3]);
1.1.1.13 root 1696:
1697: MemoryLoadNbBytesTotal = Keyboard.InputBuffer[3];
1698: MemoryLoadNbBytesLeft = MemoryLoadNbBytesTotal;
1699: crc32_reset ( &MemoryLoadCrc );
1.1 root 1700: }
1701:
1.1.1.2 root 1702:
1703: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1704: /**
1705: * MEMORY READ
1706: *
1707: * 0x21
1708: * ADRMSB ; address in controller
1709: * ADRLSB ; memory to be read
1710: * Returns:
1711: * 0xF6 ; status header
1712: * 0x20 ; memory access
1713: * { data } ; 6 data bytes starting at ADR
1714: */
1.1.1.13 root 1715: static void IKBD_Cmd_ReadMemory(void)
1.1 root 1716: {
1.1.1.15 root 1717: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReadMemory\n");
1.1 root 1718: }
1719:
1.1.1.2 root 1720:
1721: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1722: /**
1723: * CONTROLLER EXECUTE
1724: *
1725: * 0x22
1726: * ADRMSB ; address of subroutine in
1727: * ADRLSB ; controller memory to be called
1728: */
1.1.1.13 root 1729: static void IKBD_Cmd_Execute(void)
1730: {
1.1.1.17 root 1731: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_Execute addr 0x%x\n",
1732: (Keyboard.InputBuffer[1] << 8) + Keyboard.InputBuffer[2]);
1.1.1.13 root 1733:
1734: if ( pIKBD_CustomCodeHandler_Write )
1735: {
1.1.1.15 root 1736: LOG_TRACE(TRACE_IKBD_EXEC, "ikbd execute addr 0x%x using custom handler\n",
1737: (Keyboard.InputBuffer[1] << 8) + Keyboard.InputBuffer[2]);
1.1.1.13 root 1738:
1.1.1.15 root 1739: IKBD_ExeMode = true; /* turn 6301's custom mode ON */
1.1.1.13 root 1740: }
1741: else /* unknown code uploaded to ikbd RAM */
1742: {
1.1.1.15 root 1743: LOG_TRACE(TRACE_IKBD_EXEC, "ikbd execute addr 0x%x ignored, no custom handler found\n",
1744: (Keyboard.InputBuffer[1] << 8) + Keyboard.InputBuffer[2]);
1.1.1.13 root 1745: }
1746: }
1747:
1748:
1749: /*-----------------------------------------------------------------------*/
1750: /**
1751: * REPORT MOUSE BUTTON ACTION
1752: *
1753: * 0x87
1754: */
1755: static void IKBD_Cmd_ReportMouseAction(void)
1756: {
1.1.1.15 root 1757: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportMouseAction\n");
1.1.1.13 root 1758:
1759: IKBD_AddKeyToKeyboardBufferWithDelay(0xf6, 30000);
1760: IKBD_AddKeyToKeyboardBuffer(7);
1761: IKBD_AddKeyToKeyboardBuffer(KeyboardProcessor.Mouse.Action);
1762: IKBD_AddKeyToKeyboardBuffer(0);
1763: IKBD_AddKeyToKeyboardBuffer(0);
1764: IKBD_AddKeyToKeyboardBuffer(0);
1765: IKBD_AddKeyToKeyboardBuffer(0);
1766: IKBD_AddKeyToKeyboardBuffer(0);
1767: }
1768:
1769:
1770: /*-----------------------------------------------------------------------*/
1771: /**
1772: * REPORT MOUSE MODE
1773: *
1774: * 0x88 or 0x89 or 0x8A
1775: */
1776: static void IKBD_Cmd_ReportMouseMode(void)
1777: {
1.1.1.15 root 1778: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportMouseMode\n");
1.1.1.13 root 1779:
1780: IKBD_AddKeyToKeyboardBufferWithDelay(0xf6, 30000);
1781: switch (KeyboardProcessor.MouseMode)
1782: {
1783: case AUTOMODE_MOUSEREL:
1784: IKBD_AddKeyToKeyboardBuffer(8);
1785: IKBD_AddKeyToKeyboardBuffer(0);
1786: IKBD_AddKeyToKeyboardBuffer(0);
1787: IKBD_AddKeyToKeyboardBuffer(0);
1788: IKBD_AddKeyToKeyboardBuffer(0);
1789: IKBD_AddKeyToKeyboardBuffer(0);
1790: IKBD_AddKeyToKeyboardBuffer(0);
1791: break;
1792: case AUTOMODE_MOUSEABS:
1793: IKBD_AddKeyToKeyboardBuffer(9);
1794: IKBD_AddKeyToKeyboardBuffer(KeyboardProcessor.Abs.MaxX >> 8);
1795: IKBD_AddKeyToKeyboardBuffer(KeyboardProcessor.Abs.MaxX);
1796: IKBD_AddKeyToKeyboardBuffer(KeyboardProcessor.Abs.MaxY >> 8);
1797: IKBD_AddKeyToKeyboardBuffer(KeyboardProcessor.Abs.MaxY);
1798: IKBD_AddKeyToKeyboardBuffer(0);
1799: IKBD_AddKeyToKeyboardBuffer(0);
1800: break;
1801: case AUTOMODE_MOUSECURSOR:
1802: IKBD_AddKeyToKeyboardBuffer(10);
1803: IKBD_AddKeyToKeyboardBuffer(KeyboardProcessor.Mouse.KeyCodeDeltaX);
1804: IKBD_AddKeyToKeyboardBuffer(KeyboardProcessor.Mouse.KeyCodeDeltaY);
1805: IKBD_AddKeyToKeyboardBuffer(0);
1806: IKBD_AddKeyToKeyboardBuffer(0);
1807: IKBD_AddKeyToKeyboardBuffer(0);
1808: IKBD_AddKeyToKeyboardBuffer(0);
1809: break;
1810: }
1811: }
1812:
1813:
1814: /*-----------------------------------------------------------------------*/
1815: /**
1816: * REPORT MOUSE THRESHOLD
1817: *
1818: * 0x8B
1819: */
1820: static void IKBD_Cmd_ReportMouseThreshold(void)
1821: {
1.1.1.15 root 1822: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportMouseThreshold\n");
1.1.1.13 root 1823:
1824: IKBD_AddKeyToKeyboardBufferWithDelay(0xf6, 30000);
1825: IKBD_AddKeyToKeyboardBuffer(0x0B);
1826: IKBD_AddKeyToKeyboardBuffer(KeyboardProcessor.Mouse.XThreshold);
1827: IKBD_AddKeyToKeyboardBuffer(KeyboardProcessor.Mouse.YThreshold);
1828: IKBD_AddKeyToKeyboardBuffer(0);
1829: IKBD_AddKeyToKeyboardBuffer(0);
1830: IKBD_AddKeyToKeyboardBuffer(0);
1831: IKBD_AddKeyToKeyboardBuffer(0);
1832: }
1833:
1834:
1835: /*-----------------------------------------------------------------------*/
1836: /**
1837: * REPORT MOUSE SCALE
1838: *
1839: * 0x8C
1840: */
1841: static void IKBD_Cmd_ReportMouseScale(void)
1842: {
1.1.1.15 root 1843: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportMouseScale\n");
1.1.1.13 root 1844:
1845: IKBD_AddKeyToKeyboardBufferWithDelay(0xf6, 30000);
1846: IKBD_AddKeyToKeyboardBuffer(0x0C);
1847: IKBD_AddKeyToKeyboardBuffer(KeyboardProcessor.Mouse.XScale);
1848: IKBD_AddKeyToKeyboardBuffer(KeyboardProcessor.Mouse.YScale);
1849: IKBD_AddKeyToKeyboardBuffer(0);
1850: IKBD_AddKeyToKeyboardBuffer(0);
1851: IKBD_AddKeyToKeyboardBuffer(0);
1852: IKBD_AddKeyToKeyboardBuffer(0);
1853: }
1854:
1855:
1856: /*-----------------------------------------------------------------------*/
1857: /**
1858: * REPORT MOUSE VERTICAL COORDINATES
1859: *
1860: * 0x8F and 0x90
1861: */
1862: static void IKBD_Cmd_ReportMouseVertical(void)
1863: {
1.1.1.15 root 1864: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportMouseVertical\n");
1.1.1.13 root 1865:
1866: IKBD_AddKeyToKeyboardBufferWithDelay(0xf6, 30000);
1867: if (KeyboardProcessor.Mouse.YAxis == -1)
1868: IKBD_AddKeyToKeyboardBuffer(0x0F);
1869: else
1870: IKBD_AddKeyToKeyboardBuffer(0x10);
1871: IKBD_AddKeyToKeyboardBuffer(0);
1872: IKBD_AddKeyToKeyboardBuffer(0);
1873: IKBD_AddKeyToKeyboardBuffer(0);
1874: IKBD_AddKeyToKeyboardBuffer(0);
1875: IKBD_AddKeyToKeyboardBuffer(0);
1876: IKBD_AddKeyToKeyboardBuffer(0);
1877: }
1878:
1879:
1880: /*-----------------------------------------------------------------------*/
1881: /**
1882: * REPORT MOUSE AVAILABILITY
1883: *
1884: * 0x92
1885: */
1886: static void IKBD_Cmd_ReportMouseAvailability(void)
1887: {
1.1.1.15 root 1888: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportMouseAvailability\n");
1.1.1.13 root 1889:
1890: IKBD_AddKeyToKeyboardBufferWithDelay(0xf6, 30000);
1891: if (KeyboardProcessor.MouseMode == AUTOMODE_OFF)
1892: IKBD_AddKeyToKeyboardBuffer(0x12);
1893: else
1894: IKBD_AddKeyToKeyboardBuffer(0x00);
1895: IKBD_AddKeyToKeyboardBuffer(0);
1896: IKBD_AddKeyToKeyboardBuffer(0);
1897: IKBD_AddKeyToKeyboardBuffer(0);
1898: IKBD_AddKeyToKeyboardBuffer(0);
1899: IKBD_AddKeyToKeyboardBuffer(0);
1900: IKBD_AddKeyToKeyboardBuffer(0);
1901: }
1902:
1903:
1904: /*-----------------------------------------------------------------------*/
1905: /**
1906: * REPORT JOYSTICK MODE
1907: *
1908: * 0x94 or 0x95 or 0x99
1909: */
1910: static void IKBD_Cmd_ReportJoystickMode(void)
1.1 root 1911: {
1.1.1.15 root 1912: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportJoystickMode\n");
1.1.1.13 root 1913:
1914: IKBD_AddKeyToKeyboardBufferWithDelay(0xf6, 30000);
1915: switch (KeyboardProcessor.JoystickMode)
1916: {
1917: case AUTOMODE_JOYSTICK:
1918: IKBD_AddKeyToKeyboardBuffer(0x14);
1919: IKBD_AddKeyToKeyboardBuffer(0);
1920: IKBD_AddKeyToKeyboardBuffer(0);
1921: IKBD_AddKeyToKeyboardBuffer(0);
1922: IKBD_AddKeyToKeyboardBuffer(0);
1923: IKBD_AddKeyToKeyboardBuffer(0);
1924: IKBD_AddKeyToKeyboardBuffer(0);
1925: break;
1926: default: /* TODO: Joystick keycodes mode not supported yet! */
1927: IKBD_AddKeyToKeyboardBuffer(0x15);
1928: IKBD_AddKeyToKeyboardBuffer(0);
1929: IKBD_AddKeyToKeyboardBuffer(0);
1930: IKBD_AddKeyToKeyboardBuffer(0);
1931: IKBD_AddKeyToKeyboardBuffer(0);
1932: IKBD_AddKeyToKeyboardBuffer(0);
1933: IKBD_AddKeyToKeyboardBuffer(0);
1934: break;
1935: }
1936: }
1937:
1938:
1939: /*-----------------------------------------------------------------------*/
1940: /**
1941: * REPORT JOYSTICK AVAILABILITY
1942: *
1943: * 0x9A
1944: */
1945: static void IKBD_Cmd_ReportJoystickAvailability(void)
1946: {
1.1.1.15 root 1947: LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportJoystickAvailability\n");
1.1.1.13 root 1948:
1949: IKBD_AddKeyToKeyboardBufferWithDelay(0xf6, 30000);
1950: if (KeyboardProcessor.JoystickMode == AUTOMODE_OFF)
1951: IKBD_AddKeyToKeyboardBuffer(0x1A);
1952: else
1953: IKBD_AddKeyToKeyboardBuffer(0x00);
1954: IKBD_AddKeyToKeyboardBuffer(0);
1955: IKBD_AddKeyToKeyboardBuffer(0);
1956: IKBD_AddKeyToKeyboardBuffer(0);
1957: IKBD_AddKeyToKeyboardBuffer(0);
1958: IKBD_AddKeyToKeyboardBuffer(0);
1959: IKBD_AddKeyToKeyboardBuffer(0);
1.1 root 1960: }
1961:
1962:
1.1.1.2 root 1963: /*-----------------------------------------------------------------------*/
1.1.1.12 root 1964: /**
1965: * Send data to keyboard processor via ACIA by writing to address 0xfffc02.
1966: * For our emulation we bypass the ACIA (I've yet to see anything check for this)
1967: * and add the byte directly into the keyboard input buffer.
1968: */
1.1.1.11 root 1969: static void IKBD_RunKeyboardCommand(Uint16 aciabyte)
1.1 root 1970: {
1.1.1.12 root 1971: int i=0;
1.1 root 1972:
1.1.1.12 root 1973: /* Write into our keyboard input buffer */
1974: Keyboard.InputBuffer[Keyboard.nBytesInInputBuffer++] = aciabyte;
1.1 root 1975:
1.1.1.12 root 1976: /* Now check bytes to see if we have a valid/in-valid command string set */
1977: while (KeyboardCommands[i].Command!=0xff)
1978: {
1979: /* Found command? */
1980: if (KeyboardCommands[i].Command==Keyboard.InputBuffer[0])
1981: {
1982: /* Is string complete, then can execute? */
1983: if (KeyboardCommands[i].NumParameters==Keyboard.nBytesInInputBuffer)
1984: {
1985: CALL_VAR(KeyboardCommands[i].pCallFunction);
1986: Keyboard.nBytesInInputBuffer = 0;
1987: }
1.1 root 1988:
1.1.1.12 root 1989: return;
1990: }
1.1.1.7 root 1991:
1.1.1.12 root 1992: i++;
1993: }
1.1 root 1994:
1.1.1.12 root 1995: /* Command not known, reset buffer(IKBD assumes a NOP) */
1996: Keyboard.nBytesInInputBuffer = 0;
1.1 root 1997: }
1998:
1.1.1.2 root 1999:
2000: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2001: /**
2002: * Send byte to our keyboard processor, and execute
2003: */
1.1.1.13 root 2004: static void IKBD_SendByteToKeyboardProcessor(Uint16 bl)
1.1 root 2005: {
1.1.1.13 root 2006: /* If IKBD is executing custom code, send the byte to the function handling this code */
2007: if ( IKBD_ExeMode && pIKBD_CustomCodeHandler_Write )
2008: {
2009: (*pIKBD_CustomCodeHandler_Write) ( (Uint8) bl );
2010: return;
2011: }
2012:
2013: if ( MemoryLoadNbBytesLeft == 0 ) /* No pending MemoryLoad command */
2014: IKBD_RunKeyboardCommand ( bl ); /* check for known commands */
2015:
2016: else /* MemoryLoad command is not finished yet */
2017: IKBD_LoadMemoryByte ( (Uint8) bl ); /* process bytes sent to the ikbd RAM */
1.1 root 2018: }
2019:
1.1.1.2 root 2020:
2021: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2022: /**
2023: * The byte stored in the ACIA 'ACIAByte' has been read by the CPU by reading from
2024: * address $fffc02. We clear the status flag and set the GPIP register to signal read.
2025: */
1.1.1.11 root 2026: Uint16 IKBD_GetByteFromACIA(void)
1.1 root 2027: {
1.1.1.12 root 2028: /* ACIA is now reset */
2029: ACIAStatusRegister &= ~(ACIA_STATUS_REGISTER__RX_BUFFER_FULL | ACIA_STATUS_REGISTER__INTERRUPT_REQUEST | ACIA_STATUS_REGISTER__OVERRUN_ERROR);
1.1 root 2030:
1.1.1.12 root 2031: /* GPIP I4 - General Purpose Pin Keyboard/MIDI interrupt */
1.1.1.19! root 2032: MFP_GPIP |= 0x10; /* clear IRQ signal */
1.1.1.12 root 2033: return ACIAByte; /* Return byte from keyboard */
1.1 root 2034: }
2035:
1.1.1.2 root 2036:
2037: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2038: /**
1.1.1.18 root 2039: * These interrupt handlers are used to simulate a correct number of CPU cycles
1.1.1.17 root 2040: * when sending a byte from the ACIA to the ikdb or when receiving a byte
2041: * from the ikbd to the ACIA (because ACIA serial transfers are running
2042: * at 7812.5 bps, bytes should not be available immediatly as it would break
2043: * some programs)
1.1.1.18 root 2044: * - Byte received in the ACIA from the keyboard processor (RX) : store byte for read
1.1.1.17 root 2045: * from $fffc02 and schedule MFP interrupt to be triggered just a few cycles after.
1.1.1.18 root 2046: * - Byte sent from the ACIA to the keyboard processor (TX) : pass the byte to
1.1.1.17 root 2047: * IKBD_SendByteToKeyboardProcessor and set TX_BUFFER_EMPTY in status register.
1.1.1.12 root 2048: */
1.1.1.18 root 2049: void IKBD_InterruptHandler_ACIA_RX(void)
1.1 root 2050: {
1.1.1.12 root 2051: /* Remove this interrupt from list and re-order */
1.1.1.16 root 2052: CycInt_AcknowledgeInterrupt();
1.1 root 2053:
1.1.1.18 root 2054: /* Copy keyboard byte, ready for read from $fffc02 */
2055: ACIAByte = Keyboard.Buffer[Keyboard.BufferHead++];
2056: Keyboard.BufferHead &= KEYBOARD_BUFFER_MASK;
2057:
2058: /* Did we get an over-run? Ie byte has arrived from keyboard processor BEFORE CPU has read previous one from ACIA */
2059: if (ACIAStatusRegister&ACIA_STATUS_REGISTER__RX_BUFFER_FULL)
2060: ACIAStatusRegister |= ACIA_STATUS_REGISTER__OVERRUN_ERROR; /* Set over-run */
2061: //fprintf ( stderr , "int acia %x %x\n" , ACIAByte, ACIAStatusRegister );
2062:
2063: /* ACIA buffer is now full */
2064: ACIAStatusRegister |= ACIA_STATUS_REGISTER__RX_BUFFER_FULL;
2065: /* Signal interrupt pending */
2066: ACIAStatusRegister |= ACIA_STATUS_REGISTER__INTERRUPT_REQUEST;
2067:
2068: /* GPIP I4 - General Purpose Pin Keyboard/MIDI interrupt */
2069: /* NOTE: GPIP will remain low(0) until keyboard data is read from $fffc02. */
1.1.1.19! root 2070: MFP_GPIP &= ~0x10; /* set IRQ signal */
1.1.1.18 root 2071:
2072: /* There seems to be a small gap on a real ST between the point in time
2073: * the ACIA_STATUS_REGISTER__RX_BUFFER_FULL bit is set and the MFP
2074: * interrupt is triggered - for example the "V8 music system" demo
2075: * depends on this behaviour. To emulate this, we simply start another
2076: * Int which triggers the MFP interrupt later: */
2077: CycInt_AddRelativeInterrupt(18, INT_CPU_CYCLE, INTERRUPT_IKBD_MFP);
2078: }
2079:
2080:
2081: void IKBD_InterruptHandler_ACIA_TX(void)
2082: {
2083: /* Remove this interrupt from list and re-order */
2084: CycInt_AcknowledgeInterrupt();
2085:
2086: IKBD_SendByteToKeyboardProcessor(ACIATxDataRegister); /* Pass the byte to the keyboard processor */
2087: ACIAStatusRegister |= ACIA_STATUS_REGISTER__TX_BUFFER_EMPTY; /* TX buffer is now empty */
2088: bByteInTransitFromACIA = false; /* ready to send another byte */
1.1.1.19! root 2089:
! 2090: /* If TX interrupt is enabled do an IRQ now */
! 2091: if ( ( ACIAControlRegister & 0x60 ) == 0x20 ) /* CR6+CR5 = 01 -> transmit interrupt disabled */
! 2092: {
! 2093: /* NOTE: GPIP will remain low(0) until byte is written to $fffc02. */
! 2094: MFP_GPIP &= ~0x10; /* set IRQ signal */
! 2095: MFP_InputOnChannel(MFP_ACIA_BIT, MFP_IERB, &MFP_IPRB);
! 2096: }
1.1.1.13 root 2097: }
2098:
2099:
2100: /**
2101: * Start MFP interrupt after byte has been received in the ACIA.
2102: */
2103: void IKBD_InterruptHandler_MFP(void)
2104: {
1.1.1.18 root 2105: //fprintf ( stderr , "int mfp %x %x\n" , ACIAByte, ACIAStatusRegister );
1.1.1.13 root 2106: /* Remove this interrupt from list and re-order */
1.1.1.16 root 2107: CycInt_AcknowledgeInterrupt();
1.1.1.13 root 2108:
1.1.1.12 root 2109: /* Acknowledge in MFP circuit, pass bit,enable,pending */
2110: MFP_InputOnChannel(MFP_ACIA_BIT, MFP_IERB, &MFP_IPRB);
2111:
2112: /* Clear flag so can allow another byte to be sent along serial line */
1.1.1.15 root 2113: bByteInTransitToACIA = false;
1.1.1.13 root 2114:
1.1.1.12 root 2115: /* If another key is waiting, start sending from keyboard processor now */
2116: if (Keyboard.BufferHead!=Keyboard.BufferTail)
1.1.1.13 root 2117: IKBD_SendByteToACIA(ACIA_CYCLES);
1.1 root 2118: }
2119:
2120:
1.1.1.2 root 2121: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2122: /**
2123: * Send a byte from the keyboard buffer to the ACIA. On a real ST this takes some time to send
2124: * so we must be as accurate in the timing as possible - bytes do not appear to the 68000 instantly!
2125: * We do this via an internal interrupt - neat!
2126: */
1.1.1.13 root 2127: static void IKBD_SendByteToACIA(int nAciaCycles)
1.1 root 2128: {
1.1.1.13 root 2129: /* Transmit byte from keyboard processor to ACIA.
2130: * This takes approx ACIA_CYCLES CPU clock cycles to complete */
1.1.1.12 root 2131: if (!bByteInTransitToACIA)
2132: {
2133: /* Send byte to ACIA */
1.1.1.18 root 2134: CycInt_AddRelativeInterrupt(nAciaCycles, INT_CPU_CYCLE, INTERRUPT_IKBD_ACIA_RX);
1.1.1.12 root 2135: /* Set flag so only transmit one byte at a time */
1.1.1.15 root 2136: bByteInTransitToACIA = true;
1.1.1.12 root 2137: }
1.1 root 2138: }
2139:
1.1.1.2 root 2140:
2141: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2142: /**
1.1.1.13 root 2143: * Add character to our internal keyboard buffer, with default ACIA_CYCLES
2144: * timing.
2145: */
2146: static void IKBD_AddKeyToKeyboardBuffer(Uint8 Data)
2147: {
2148: if ( IKBD_ExeMode ) /* if IKBD is executing custom code, don't add */
2149: return; /* anything to the buffer */
2150:
2151: IKBD_AddKeyToKeyboardBuffer_Real(Data, ACIA_CYCLES);
2152: }
2153:
2154:
2155: /**
2156: * Add character to our internal keyboard buffer, with additional delay.
2157: * This is required for some keyboard commands like ReadAbsMousePos (0x0d)
2158: * where it takes a little bit longer than the typical ACIA_CYCLES until
2159: * the first byte arrives from the IKBD (for example the "Unlimited bobs"
2160: * screen in the Dragonnels demo depends on this behaviour.
2161: */
2162: static void IKBD_AddKeyToKeyboardBufferWithDelay(Uint8 Data, int nAciaCycles)
2163: {
2164: if (IKBD_ExeMode) /* if IKBD is executing custom code, */
2165: return; /* don't add anything to the buffer */
2166:
2167: IKBD_AddKeyToKeyboardBuffer_Real(Data, nAciaCycles);
2168: }
2169:
2170:
2171: /**
2172: * Add character to our internal keyboard buffer. These bytes are then sent
2173: * one at a time to the ACIA. This is done via a delay to mimick the STs
2174: * internal workings, as this is needed for games such as Carrier Command.
1.1.1.12 root 2175: */
1.1.1.13 root 2176: static void IKBD_AddKeyToKeyboardBuffer_Real(Uint8 Data, int nAciaCycles)
1.1 root 2177: {
1.1.1.12 root 2178: /* Is keyboard initialised yet? Ignore any bytes until it is */
2179: if (!KeyboardProcessor.bReset)
2180: return;
2181:
2182: /* Check we have space to add byte */
2183: if (Keyboard.BufferHead!=((Keyboard.BufferTail+1)&KEYBOARD_BUFFER_MASK))
2184: {
2185: /* Add byte to our buffer */
2186: Keyboard.Buffer[Keyboard.BufferTail++] = Data;
2187: Keyboard.BufferTail &= KEYBOARD_BUFFER_MASK;
2188:
2189: /* We have character ready to transmit from the ACIA - see if can send it now */
1.1.1.13 root 2190: IKBD_SendByteToACIA(nAciaCycles);
1.1.1.12 root 2191: }
1.1.1.17 root 2192: else
2193: {
2194: Log_Printf(LOG_ERROR, "IKBD buffer is full!\n");
2195: }
1.1 root 2196: }
2197:
1.1.1.2 root 2198:
2199: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2200: /**
2201: * When press/release key under host OS, execute this function.
2202: */
1.1.1.13 root 2203: void IKBD_PressSTKey(Uint8 ScanCode, bool bPress)
1.1 root 2204: {
1.1.1.13 root 2205: /* Store the state of each ST scancode : 1=pressed 0=released */
2206: if ( bPress ) ScanCodeState[ ScanCode & 0x7f ] = 1;
2207: else ScanCodeState[ ScanCode & 0x7f ] = 0;
2208:
1.1.1.12 root 2209: if (!bPress)
2210: ScanCode |= 0x80; /* Set top bit if released key */
2211: IKBD_AddKeyToKeyboardBuffer(ScanCode); /* And send to keyboard processor */
1.1 root 2212: }
1.1.1.10 root 2213:
2214:
2215: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2216: /**
2217: * Handle read from keyboard control ACIA register (0xfffc00)
2218: */
1.1.1.10 root 2219: void IKBD_KeyboardControl_ReadByte(void)
2220: {
1.1.1.11 root 2221: /* ACIA registers need wait states - but the value seems to vary in certain cases */
2222: M68000_WaitState(8);
2223:
1.1.1.13 root 2224: IoMem[0xfffc00] = ACIAStatusRegister;
1.1.1.12 root 2225:
1.1.1.15 root 2226: if (LOG_TRACE_LEVEL(TRACE_IKBD_ACIA))
1.1.1.12 root 2227: {
1.1.1.15 root 2228: int FrameCycles, HblCounterVideo, LineCycles;
2229: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2230: LOG_TRACE_PRINT("ikbd read fffc00 ctrl=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
2231: IoMem[0xfffc00], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
1.1.1.12 root 2232: }
1.1.1.10 root 2233: }
2234:
2235: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2236: /**
2237: * Handle read from keyboard data ACIA register (0xfffc02)
2238: */
1.1.1.10 root 2239: void IKBD_KeyboardData_ReadByte(void)
2240: {
1.1.1.11 root 2241: /* ACIA registers need wait states - but the value seems to vary in certain cases */
2242: M68000_WaitState(8);
2243:
1.1.1.13 root 2244: /* If IKBD is executing custom code, call the function to update the byte read in $fffc02 */
2245: if ( IKBD_ExeMode && pIKBD_CustomCodeHandler_Read )
2246: {
2247: (*pIKBD_CustomCodeHandler_Read) ();
2248: }
2249:
2250:
1.1.1.10 root 2251: IoMem[0xfffc02] = IKBD_GetByteFromACIA(); /* Return our byte from keyboard processor */
1.1.1.12 root 2252:
1.1.1.15 root 2253: if (LOG_TRACE_LEVEL(TRACE_IKBD_ACIA))
1.1.1.12 root 2254: {
1.1.1.15 root 2255: int FrameCycles, HblCounterVideo, LineCycles;
2256: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2257: LOG_TRACE_PRINT("ikbd read fffc02 data=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
2258: IoMem[0xfffc02], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
1.1.1.12 root 2259: }
1.1.1.10 root 2260: }
2261:
2262:
2263: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2264: /**
2265: * Handle write to keyboard control ACIA register (0xfffc00)
2266: */
1.1.1.10 root 2267: void IKBD_KeyboardControl_WriteByte(void)
2268: {
1.1.1.19! root 2269: int FrameCycles, HblCounterVideo, LineCycles;
! 2270:
1.1.1.11 root 2271: /* ACIA registers need wait states - but the value seems to vary in certain cases */
2272: M68000_WaitState(8);
2273:
1.1.1.19! root 2274: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
! 2275: LOG_TRACE(TRACE_IKBD_ACIA, "ikbd write fffc00 ctrl=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
1.1.1.15 root 2276: IoMem[0xfffc00], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
1.1.1.12 root 2277:
2278: /* [NP] We only handle reset of the ACIA */
2279: if ( ( IoMem[0xfffc00] & 0x03 ) == 0x03 )
2280: ACIA_Reset();
2281:
1.1.1.19! root 2282: /* If TX interrupt is enabled (CR6+CR5 go from 00 to 01) and TX buffer is empty, do an IRQ now */
! 2283: if ( ( ( ACIAControlRegister & 0x60 ) == 0x00 ) /* CR6+CR5 = 00 -> transmit interrupt disabled */
! 2284: && ( ( IoMem[0xfffc00] & 0x60 ) == 0x20 ) /* CR6+CR5 = 01 -> transmit interrupt enabled */
! 2285: && ( ACIAStatusRegister & ACIA_STATUS_REGISTER__TX_BUFFER_EMPTY ) )
! 2286: {
! 2287: LOG_TRACE(TRACE_IKBD_ACIA, "ikbd write fffc00 ctrl=0x%x enable ACIA TX IRQ video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
! 2288: IoMem[0xfffc00], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
! 2289: /* NOTE: GPIP will remain low(0) until byte is written to $fffc02. */
! 2290: MFP_GPIP &= ~0x10; /* set IRQ signal */
! 2291: MFP_InputOnChannel(MFP_ACIA_BIT, MFP_IERB, &MFP_IPRB);
! 2292: }
! 2293:
! 2294: ACIAControlRegister = IoMem[0xfffc00];
1.1.1.10 root 2295: }
2296:
2297: /*-----------------------------------------------------------------------*/
1.1.1.12 root 2298: /**
2299: * Handle write to keyboard data ACIA register (0xfffc02)
2300: */
1.1.1.10 root 2301: void IKBD_KeyboardData_WriteByte(void)
2302: {
1.1.1.18 root 2303: int FrameCycles, HblCounterVideo, LineCycles;
2304:
1.1.1.11 root 2305: /* ACIA registers need wait states - but the value seems to vary in certain cases */
2306: M68000_WaitState(8);
2307:
1.1.1.18 root 2308: Video_GetPosition ( &FrameCycles , &HblCounterVideo , &LineCycles );
2309: LOG_TRACE(TRACE_IKBD_ACIA, "ikbd write fffc02 data=0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
1.1.1.15 root 2310: IoMem[0xfffc02], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
1.1.1.12 root 2311:
1.1.1.19! root 2312: if ( bDuringResetCriticalTime ) /* warn when some byte are sent to the IKBD during its reset */
! 2313: {
! 2314: LOG_TRACE(TRACE_IKBD_ACIA, "ikbd write fffc02 data=0x%x during reset might be ignored video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
! 2315: IoMem[0xfffc02], FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
! 2316:
! 2317: }
! 2318:
1.1.1.13 root 2319:
1.1.1.18 root 2320: if ( bByteInTransitFromACIA == true ) /* we're already transfering a byte to the ikbd */
1.1.1.17 root 2321: {
1.1.1.18 root 2322: LOG_TRACE(TRACE_IKBD_ACIA, "ikbd write fffc02 data=0x%x cancels command 0x%x video_cyc=%d %d@%d pc=%x instr_cycle %d\n",
2323: IoMem[0xfffc02], ACIATxDataRegister, FrameCycles, LineCycles, HblCounterVideo, M68000_GetPC(), CurrentInstrCycles);
1.1.1.17 root 2324:
1.1.1.18 root 2325: }
2326:
1.1.1.19! root 2327: ACIATxDataRegister = IoMem[0xfffc02]; /* store the byte that we want to send to the ikbd */
! 2328:
! 2329: /* A write in TDR clears the TX IRQ */
! 2330: if ( ( ACIAControlRegister & 0x60 ) == 0x20 ) /* CR6+CR5 = 01 -> transmit interrupt enabled */
! 2331: MFP_GPIP |= 0x10; /* clear IRQ signal */
! 2332:
1.1.1.18 root 2333:
2334: /* [NP] FIXME 2011/12/27 : when writing constantly in $fffc02, we should replace ACIATxDataRegister */
2335: /* but we should not restart INTERRUPT_IKBD_ACIA_TX from the start each time, nor reset TX_BUFFER bit */
2336: /* ('Pandemonium Demos' by Chaos). This should be measured on a real ST. */
2337: if ( bByteInTransitFromACIA == false )
2338: {
1.1.1.17 root 2339: /* Delay the processing of the byte in IKBD_InterruptHandler_ACIA */
2340: /* The delay doesn't seem to be constant, so we add a small random number of max 40 cycles */
2341: /* (else some programs get stuck in an endless loop ('Pandemonium Demos' by Chaos) */
2342:
2343: // [NP] FIXME 2011/05/20 : if we use a delay of ACIA_CYCLES=7200, tos 1.02 and 1.04 are showing
2344: // a bug where addr $6122/$6124 are overwritten by the stack, preventing the desktop
2345: // to be restored at the correct resolution !
2346: // For now, use a delay of 1000 cycles ; need to do complete measures on a real ST for this
1.1.1.18 root 2347: //CycInt_AddRelativeInterrupt(ACIA_CYCLES+rand()%40, INT_CPU_CYCLE, INTERRUPT_IKBD_ACIA_TX);
1.1.1.17 root 2348: // delay cycles <=1560 ok, > 1570 bad for tos 1.02/1.04
1.1.1.18 root 2349: CycInt_AddRelativeInterrupt(1000+rand()%40, INT_CPU_CYCLE, INTERRUPT_IKBD_ACIA_TX);
1.1.1.17 root 2350:
2351: /* Some games like USS John Young / FOF54 actually check whether the
2352: * transmit-buffer-empty bit is really cleared after writing a data
2353: * byte to the IKBD, so we have to temporarily clear this bit, too,
2354: * although the byte is send immediately to our virtual IKBD. */
2355: ACIAStatusRegister &= ~ACIA_STATUS_REGISTER__TX_BUFFER_EMPTY; /* TX buffer is full */
2356: }
1.1.1.18 root 2357:
2358: bByteInTransitFromACIA = true;
1.1.1.10 root 2359: }
1.1.1.13 root 2360:
2361:
2362: /*************************************************************************/
2363: /**
2364: * Below part is for emulating custom 6301 program sent to the ikbd RAM
2365: * Specific read/write functions for each demo/game should be added here,
2366: * after being defined in the CustomCodeDefinitions[] array.
2367: *
2368: * The 6301 has 256 bytes of RAM, but only 128 bytes are available to
2369: * put a program (from $80 to $ff).
2370: *
2371: * Executing a program in the 6301 is a 2 steps process :
2372: * 1) a very small program is sent to the RAM using the 0x20 command.
2373: * This is often loaded at address $b0.
2374: * This program will stop interruptions in the 6301 and will accept
2375: * a second small program that will relocate itself to $80.
2376: * 2) the relocated program at address $80 will accept a third (main)
2377: * program and will execute it once reception is complete.
2378: *
2379: * Writes during step 1 are handled with the ExeBootHandler matching the
2380: * LoadMemory CRC.
2381: * ExeBootHandler will compute a 2nd CRC for the writes corresponding to
2382: * the 2nd and 3rd programs sent to the 6301's RAM.
2383: *
2384: * If a match is found for this 2nd CRC, we will override default ikbd's behaviour
2385: * for reading/writing to $fffc02 with ExeMainHandler_Read / ExeMainHandler_Write
2386: * (once the Execute command 0x22 is received).
2387: *
1.1.1.15 root 2388: * When using custom program (ExeMode==true), we must ignore all keyboard/mouse/joystick
1.1.1.13 root 2389: * events sent to IKBD_AddKeyToKeyboardBuffer. Only our functions can add bytes
2390: * to the keyboard buffer.
2391: *
2392: * To exit 6301's execution mode, we can use the 68000 'reset' instruction.
2393: * Some 6301's programs also handle a write to $fffc02 as an exit signal.
2394: */
2395:
2396:
2397: /*-----------------------------------------------------------------------*/
2398: /**
2399: * Handle writes to $fffc02 when loading bytes in the ikbd RAM.
2400: * We compute a CRC of the bytes that are sent until MemoryLoadNbBytesLeft
2401: * reaches 0.
2402: * When all bytes are loaded, we look for a matching CRC ; if found, we
2403: * use the ExeBootHandler defined for this CRC to process the next writes
2404: * that will occur in $fffc02.
2405: * LoadMemory is often used to load a small boot code into the 6301's RAM.
2406: * This small program will be executed later using the command 0x22.
2407: */
2408:
2409: static void IKBD_LoadMemoryByte ( Uint8 aciabyte )
2410: {
2411: unsigned int i;
2412:
2413: crc32_add_byte ( &MemoryLoadCrc , aciabyte );
2414:
2415: MemoryLoadNbBytesLeft--;
2416: if ( MemoryLoadNbBytesLeft == 0 ) /* all bytes were received */
2417: {
2418: /* Search for a match amongst the known custom routines */
2419: for ( i = 0 ; i < sizeof ( CustomCodeDefinitions ) / sizeof ( CustomCodeDefinitions[0] ) ; i++ )
2420: if ( CustomCodeDefinitions[ i ].LoadMemCrc == MemoryLoadCrc )
2421: break;
2422:
2423: if ( i < sizeof ( CustomCodeDefinitions ) / sizeof ( CustomCodeDefinitions[0] ) ) /* found */
2424: {
1.1.1.15 root 2425: LOG_TRACE(TRACE_IKBD_EXEC, "ikbd loadmemory %d bytes crc=0x%x matches <%s>\n",
2426: MemoryLoadNbBytesTotal, MemoryLoadCrc, CustomCodeDefinitions[ i ].Name);
1.1.1.13 root 2427:
2428: crc32_reset ( &MemoryLoadCrc );
2429: MemoryExeNbBytes = 0;
2430: pIKBD_CustomCodeHandler_Read = NULL;
2431: pIKBD_CustomCodeHandler_Write = CustomCodeDefinitions[ i ].ExeBootHandler;
2432: }
2433:
2434: else /* unknown code uploaded to ikbd RAM */
2435: {
1.1.1.15 root 2436: LOG_TRACE(TRACE_IKBD_EXEC, "ikbd loadmemory %d bytes crc=0x%x : unknown code\n",
2437: MemoryLoadNbBytesTotal, MemoryLoadCrc);
1.1.1.13 root 2438:
2439: pIKBD_CustomCodeHandler_Read = NULL;
2440: pIKBD_CustomCodeHandler_Write = NULL;
2441: }
2442: }
2443: }
2444:
2445:
2446:
2447: /*-----------------------------------------------------------------------*/
2448: /**
2449: * Handle writes to $fffc02 when executing custom code in the ikbd RAM.
2450: * This is used to send the small ikdb program that will handle keyboard/mouse/joystick
2451: * input.
2452: * We compute a CRC of the bytes that are sent until we found a match
2453: * with a known custom ikbd program.
2454: */
2455:
2456: static void IKBD_CustomCodeHandler_CommonBoot ( Uint8 aciabyte )
2457: {
2458: unsigned int i;
2459:
2460: crc32_add_byte ( &MemoryLoadCrc , aciabyte );
2461: MemoryExeNbBytes++;
2462:
1.1.1.15 root 2463: LOG_TRACE(TRACE_IKBD_EXEC, "ikbd custom exe common boot write 0x%02x count %d crc=0x%x\n",
2464: aciabyte, MemoryExeNbBytes, MemoryLoadCrc);
1.1.1.13 root 2465:
2466: /* Search for a match amongst the known custom routines */
2467: for ( i = 0 ; i < sizeof ( CustomCodeDefinitions ) / sizeof ( CustomCodeDefinitions[0] ) ; i++ )
2468: if ( ( CustomCodeDefinitions[ i ].MainProgNbBytes == MemoryExeNbBytes )
2469: && ( CustomCodeDefinitions[ i ].MainProgCrc == MemoryLoadCrc ) )
2470: break;
2471:
2472: if ( i < sizeof ( CustomCodeDefinitions ) / sizeof ( CustomCodeDefinitions[0] ) ) /* found */
2473: {
1.1.1.15 root 2474: LOG_TRACE(TRACE_IKBD_EXEC, "ikbd custom exe common boot, uploaded code matches <%s>\n",
2475: CustomCodeDefinitions[i].Name);
1.1.1.13 root 2476:
2477: pIKBD_CustomCodeHandler_Read = CustomCodeDefinitions[ i ].ExeMainHandler_Read;
2478: pIKBD_CustomCodeHandler_Write = CustomCodeDefinitions[ i ].ExeMainHandler_Write;
2479:
2480: Keyboard.BufferHead = Keyboard.BufferTail = 0; /* flush all queued bytes that would be read in $fffc02 */
2481: (*pIKBD_CustomCodeHandler_Read) (); /* initialize ACIAByte */
2482: }
2483:
2484: /* If not found, we keep on accumulating bytes until we find a matching crc */
2485: }
2486:
2487:
2488:
2489: /*----------------------------------------------------------------------*/
2490: /* Froggies Over The Fence menu. */
2491: /* Returns 2 bytes with the mouse position, keyboard can be used too. */
2492: /* Writing 0xff to $fffc02 will cause the 6301 to exit custom exe mode. */
2493: /*----------------------------------------------------------------------*/
2494:
2495: static void IKBD_CustomCodeHandler_FroggiesMenu_Read ( void )
2496: {
2497: Uint8 res1 = 0;
2498: Uint8 res2 = 0;
2499:
2500: if ( KeyboardProcessor.Mouse.DeltaX < 0 ) res1 = 0x7a; /* mouse left */
2501: if ( KeyboardProcessor.Mouse.DeltaX > 0 ) res1 = 0x06; /* mouse right */
2502: if ( KeyboardProcessor.Mouse.DeltaY < 0 ) res2 = 0x7a; /* mouse up */
2503: if ( KeyboardProcessor.Mouse.DeltaY > 0 ) res2 = 0x06; /* mouse down */
2504: if ( Keyboard.bLButtonDown & BUTTON_MOUSE ) res1 |= 0x80; /* left mouse button */
2505:
2506: if ( ScanCodeState[ 0x4b ] ) res1 |= 0x7a; /* left */
2507: if ( ScanCodeState[ 0x4d ] ) res1 |= 0x06; /* right */
2508: if ( ScanCodeState[ 0x48 ] ) res2 |= 0x7a; /* up */
2509: if ( ScanCodeState[ 0x50 ] ) res2 |= 0x06; /* down */
2510: if ( ScanCodeState[ 0x70 ] ) res1 |= 0x80; /* keypad 0 */
2511:
2512: IKBD_AddKeyToKeyboardBuffer_Real(res1, ACIA_CYCLES);
2513: IKBD_AddKeyToKeyboardBuffer_Real(res2, ACIA_CYCLES);
2514: }
2515:
2516: static void IKBD_CustomCodeHandler_FroggiesMenu_Write ( Uint8 aciabyte )
2517: {
2518: /* When writing 0xff to $fffc02, Froggies ikbd's program will terminate itself */
2519: /* and leave Execution mode */
2520: if ( aciabyte == 0xff )
2521: IKBD_Reset_ExeMode ();
2522: }
2523:
2524:
2525:
2526: /*----------------------------------------------------------------------*/
2527: /* Transbeauce II menu. */
2528: /* Returns 1 byte with the joystick position, keyboard can be used too. */
2529: /*----------------------------------------------------------------------*/
2530:
2531: static void IKBD_CustomCodeHandler_Transbeauce2Menu_Read ( void )
2532: {
2533: Uint8 res = 0;
2534:
2535: /* keyboard emulation */
2536: if ( ScanCodeState[ 0x48 ] ) res |= 0x01; /* up */
2537: if ( ScanCodeState[ 0x50 ] ) res |= 0x02; /* down */
2538: if ( ScanCodeState[ 0x4b ] ) res |= 0x04; /* left */
2539: if ( ScanCodeState[ 0x4d ] ) res |= 0x08; /* right */
2540: if ( ScanCodeState[ 0x62 ] ) res |= 0x40; /* help */
2541: if ( ScanCodeState[ 0x39 ] ) res |= 0x80; /* space */
2542:
2543: /* joystick emulation (bit mapping is same as cursor above, with bit 7 = fire button */
2544: res |= ( Joy_GetStickData(1) & 0x8f ) ; /* keep bits 0-3 and 7 */
2545:
2546: IKBD_AddKeyToKeyboardBuffer_Real(res, ACIA_CYCLES);
2547: }
2548:
2549: static void IKBD_CustomCodeHandler_Transbeauce2Menu_Write ( Uint8 aciabyte )
2550: {
2551: /* Ignore write */
2552: }
2553:
2554:
2555:
2556: /*----------------------------------------------------------------------*/
2557: /* Dragonnels demo menu. */
2558: /* Returns 1 byte with the Y position of the mouse. */
2559: /*----------------------------------------------------------------------*/
2560:
2561: static void IKBD_CustomCodeHandler_DragonnelsMenu_Read ( void )
2562: {
2563: Uint8 res = 0;
2564:
2565: if ( KeyboardProcessor.Mouse.DeltaY < 0 ) res = 0xfc; /* mouse up */
2566: if ( KeyboardProcessor.Mouse.DeltaY > 0 ) res = 0x04; /* mouse down */
2567:
2568: if ( Keyboard.bLButtonDown & BUTTON_MOUSE ) res = 0x80; /* left mouse button */
2569:
2570: IKBD_AddKeyToKeyboardBuffer_Real(res, ACIA_CYCLES);
2571: }
2572:
2573: static void IKBD_CustomCodeHandler_DragonnelsMenu_Write ( Uint8 aciabyte )
2574: {
2575: /* Ignore write */
2576: }
2577:
1.1.1.17 root 2578:
2579:
2580: /*----------------------------------------------------------------------*/
2581: /* Chaos A.D. protection's decoder */
2582: /* This custom program reads bytes, decode them and send back the result*/
2583: /* to the 68000. */
2584: /* The program first returns $fe to indicate it's ready to receive the */
2585: /* encoded bytes. */
2586: /* The program then receives the 8 bytes used to decode the data and */
2587: /* store them in $f0 - $f7 (KeyBuffer is already initialized, so we */
2588: /* ignore those 8 bytes). */
2589: /* Then for any received byte a XOR is made with one of the byte in the */
2590: /* 8 bytes buffer, by incrementing an index in this buffer. */
2591: /* The decoded byte is written to addr $13 (TDR) to be received by ACIA */
2592: /*----------------------------------------------------------------------*/
2593:
2594: static void IKBD_CustomCodeHandler_ChaosAD_Read ( void )
2595: {
2596: static bool FirstCall = true;
2597:
2598: if ( FirstCall == true )
2599: IKBD_AddKeyToKeyboardBuffer_Real ( 0xfe , ACIA_CYCLES );
2600:
2601: FirstCall = false;
2602: }
2603:
2604: static void IKBD_CustomCodeHandler_ChaosAD_Write ( Uint8 aciabyte )
2605: {
2606: static int IgnoreNb = 8;
2607: Uint8 KeyBuffer[] = { 0xca , 0x0a , 0xbc , 0x00 , 0xde , 0xde , 0xfe , 0xca };
2608: static int Index = 0;
2609: static int Count = 0;
2610:
2611: /* We ignore the first 8 bytes we received (they're already in KeyBuffer) */
2612: if ( IgnoreNb > 0 )
2613: {
2614: IgnoreNb--;
2615: return;
2616: }
2617:
2618: if ( Count <= 6080 ) /* there're 6081 bytes to decode */
2619: {
2620: Count++;
2621:
2622: aciabyte ^= KeyBuffer[ Index ];
2623: Index++;
2624: Index &= 0x07;
2625:
2626: IKBD_AddKeyToKeyboardBuffer_Real ( aciabyte , ACIA_CYCLES );
2627: }
2628:
2629: else
2630: {
2631: /* When all bytes were decoded if 0x08 is written to $fffc02 */
2632: /* the program will terminate itself and leave Execution mode */
2633: if ( aciabyte == 0x08 )
2634: IKBD_Reset_ExeMode ();
2635: }
2636: }
2637:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.