3037 lines
98 KiB
C
3037 lines
98 KiB
C
/*
|
|
Hatari - ikbd.c
|
|
|
|
This file is distributed under the GNU General Public License, version 2
|
|
or at your option any later version. Read the file gpl.txt for details.
|
|
|
|
The keyboard processor(6301) handles any joystick/mouse/keyboard task
|
|
and sends bytes to the ACIA(6850).
|
|
The IKBD has a small ROM which is used to process various commands send
|
|
by the main CPU to the THE IKBD.
|
|
Due to lack of real HD6301 emulation, those commands are handled by
|
|
functionnaly equivalent code that tries to be as close as possible
|
|
to a real HD6301.
|
|
|
|
For program using their own HD6301 code, we also use some custom
|
|
handlers to emulate the expected result.
|
|
*/
|
|
|
|
const char IKBD_fileid[] = "Hatari ikbd.c : " __DATE__ " " __TIME__;
|
|
|
|
/* 2007/09/29 [NP] Use the new int.c to add interrupts with INT_CPU_CYCLE / INT_MFP_CYCLE. */
|
|
/* 2007/12/09 [NP] If reset is written to ACIA control register, we must call ACIA_Reset to reset */
|
|
/* RX/TX status. Reading the control register fffc00 just after a reset should */
|
|
/* return the value 0x02 (used in Transbeauce 2 demo loader). */
|
|
/* 2008/07/06 [NP] Add support for executing 8 bit code sent to the 6301 processor. */
|
|
/* Instead of implementing a full 6301 emulator, we compute a checksum for each */
|
|
/* program sent to the 6301 RAM. If the checksum is recognized, we call some */
|
|
/* functions to emulate the behaviour of the 6301 in that case. */
|
|
/* When the 6301 is in 'Execute' mode (command 0x22), we must stop the normal */
|
|
/* reporting of key/mouse/joystick and use our custom handlers for each read or */
|
|
/* write to $fffc02. */
|
|
/* After a reset command, returns $F1 after $F0 (needed by Dragonnels Demo). */
|
|
/* This fixes the Transbeauce 2 demo menu, the Dragonnels demo menu and the */
|
|
/* Froggies Over The Fence demo menu (yeah ! enjoy this master piece of demo !). */
|
|
/* 2011/05/11 [NP] Add proper support for emulating TX buffer empty/full in status register bit 1 */
|
|
/* when writing to $fffc02 (using an internal timer). */
|
|
/* 2011/07/14 [NP] Don't clear bytes in transit when ACIA_Reset is called ; if a byte is sent to */
|
|
/* the ikbd it should not be cancelled ? FIXME : this would need more tests on a */
|
|
/* real ST (fix Froggies Over The Fence's menu when selecting a demo). */
|
|
/* 2011/07/31 [NP] Don't clear bytes in transit in the ACIA when the IKBD is reset (fix Overdrive */
|
|
/* by Phalanx). */
|
|
/* 2011/12/27 [NP] When sending new bytes while a byte is already in transfer from ACIA to IKBD, */
|
|
/* don't restart the internal TX timer (fix 'Pandemonium Demos' Intro). */
|
|
/* eg : .loop : move.b d0,$fc02.w btst #1,$fc00.w beq.s .loop */
|
|
/* 2012/01/22 [NP] Enable both mouse and joystick reporting when commands 0x12 and 0x14 are */
|
|
/* received during the IKBD reset. */
|
|
/* 2012/02/26 [NP] Handle TX interrupt in the ACIA (eg by sending 0xb6 instead of 0x96 after */
|
|
/* resetting the ACIA) (fix the game 'Hades Nebula'). */
|
|
/* 2012/10/10 [NP] Use the new ACIA emulation in acia.c ; add support for the IKBD's SCI, which */
|
|
/* is similar to the ACIA, with fixed 8 data bits, 1 stop bit and no parity bit. */
|
|
/* 2012/12/23 [NP] Fix timings for the commands $16, $1C, $87-$9A. The first byte is returned */
|
|
/* between 'min' and 'max' cycles after receiving the full command. The delay */
|
|
/* is not fixed to simulate the slight variations measured on a real ST. */
|
|
/* 2012/12/24 [NP] Rewrite SetClock and ReadClock commands to behave like the real IKBD. */
|
|
/* Instead of using time()/localtime() to handle the clock, we now increment it */
|
|
/* on each VBL, taking care of the BCD data (overflows and such) like in the IKBD. */
|
|
/* (this new code is based on the HD6301 disassembly of the IKBD's ROM) */
|
|
/* 2013/01/02 [NP] - Use IKBD_OutputBuffer_CheckFreeCount to ensure there's enough room in the */
|
|
/* output buffer before sending an IKBD packet. If there's not enough bytes to */
|
|
/* transfer the whole packet, then the packet must be discarded. */
|
|
/* - Don't ignore a new RDR byte if the output buffer is not empty yet. The IKBD */
|
|
/* can handle new bytes asynchronously using some interrupt while still processing */
|
|
/* another command. New RDR is discarded only if the input buffer is full. */
|
|
/* 2013/01/13 [NP] For hardware and software reset, share the common code in IKBD_Boot_ROM(). */
|
|
/* 2014/07/06 [NP] Ignore command 0x13 IKBD_Cmd_StopKeyboardTransfer during ikbd's reset. This is */
|
|
/* required for the loader of 'Just Bugging' by ACF which sends 0x11 and 0x13 just */
|
|
/* after 0x80 0x01 (temporary fix, would need to be measured on a real STF to see */
|
|
/* if it's always ignored or just during a specific delay) */
|
|
|
|
|
|
|
|
#include "main.h"
|
|
#include "ikbd.h"
|
|
#include "cycInt.h"
|
|
#include "ioMem.h"
|
|
#include "joy.h"
|
|
#include "m68000.h"
|
|
#include "memorySnapShot.h"
|
|
#include "mfp.h"
|
|
#include "screen.h"
|
|
#include "video.h"
|
|
#include "utils.h"
|
|
#include "acia.h"
|
|
#include "configuration.h"
|
|
#include "clocks_timings.h"
|
|
|
|
|
|
#define DBL_CLICK_HISTORY 0x07 /* Number of frames since last click to see if need to send one or two clicks */
|
|
#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) */
|
|
|
|
#define ABS_X_ONRESET 0 /* Initial XY for absolute mouse position after RESET command */
|
|
#define ABS_Y_ONRESET 0
|
|
#define ABS_MAX_X_ONRESET 320 /* Initial absolute mouse limits after RESET command */
|
|
#define ABS_MAY_Y_ONRESET 200 /* These values are never actually used as user MUST call 'IKBD_Cmd_AbsMouseMode' before ever using them */
|
|
|
|
#define ABS_PREVBUTTONS (0x02|0x8) /* Don't report any buttons up on first call to 'IKBD_Cmd_ReadAbsMousePos' */
|
|
|
|
#define IKBD_RESET_CYCLES 502000 /* Number of cycles (for a 68000 at 8 MHz) between sending the reset command and receiving $F1 */
|
|
|
|
#define IKBD_ROM_VERSION 0xF1 /* On reset, the IKBD will return either 0xF0 or 0xF1, depending on the IKBD's ROM */
|
|
/* version. Only very early ST returned 0xF0, so we use 0xF1 which is the most common case.*/
|
|
/* Beside, some programs explicitly wait for 0xF1 after a reset (Dragonnels demo) */
|
|
|
|
|
|
/* Keyboard state */
|
|
KEYBOARD Keyboard;
|
|
|
|
/* Keyboard processor */
|
|
KEYBOARD_PROCESSOR KeyboardProcessor; /* Keyboard processor details */
|
|
|
|
/* Pattern of mouse button up/down in ST frames (run off a double-click message) */
|
|
static const Uint8 DoubleClickPattern[] =
|
|
{
|
|
BUTTON_MOUSE,BUTTON_MOUSE,BUTTON_MOUSE,BUTTON_MOUSE,
|
|
0,0,0,0,BUTTON_MOUSE,BUTTON_MOUSE,BUTTON_MOUSE,BUTTON_MOUSE
|
|
};
|
|
|
|
static bool bMouseDisabled, bJoystickDisabled;
|
|
static bool bDuringResetCriticalTime, bBothMouseAndJoy;
|
|
static bool bMouseEnabledDuringReset;
|
|
|
|
|
|
|
|
|
|
/*
|
|
HD6301 processor by Hitachi
|
|
|
|
References :
|
|
- HD6301V1, HD63A01V1, HD63B01V1 CMOS MCU datasheet by Hitachi
|
|
|
|
The HD6301 is connected to the ACIA through TX and RX pins.
|
|
Serial transfers are made with 8 bit word, 1 stop bit, no parity and 7812.5 baud
|
|
|
|
The IKBD's ROM is using 2 buffers to handle input/output on the serial line
|
|
in an asynchronous way, by using the SCI's interrupt at address $FEE2. This means
|
|
the IKBD can execute a new command as soon as the current one is completed, as it is
|
|
the interrupt function that will handle sending bytes to the ACIA.
|
|
|
|
Input buffer : 8 bytes, located at $CD-$D4 in the IKBD's RAM.
|
|
New bytes received in RDR are added to this buffer, until we have
|
|
enough bytes to obtain a valid command (with its potential parameters)
|
|
If the buffer already contains 8 bytes, new bytes are ignored (lost).
|
|
This buffer is emptied if a valid command was processed or if the first
|
|
byte in the buffer is not a valid command.
|
|
|
|
Output buffer : 20 bytes as a ring buffer, located at $D9-$ED in the IKBD's RAM.
|
|
When the IKBD automatically reports events or when a command returns some bytes,
|
|
those 'n' bytes are added to the ring buffer.
|
|
If the ring buffer doesn't have enough space to store 'n' new bytes, the 'n' bytes
|
|
are ignored (lost).
|
|
Each time a byte is correctly sent in TDR, a new byte is processed, until the ring
|
|
buffer becomes empty.
|
|
|
|
|
|
Special behaviours during the IKBD reset :
|
|
If the following commands are received during the reset of the IKBD,
|
|
the IKBD will go in a special mode and report both mouse and joystick at the same time :
|
|
0x08 0x14 relative mouse on , joysticks auto
|
|
0x08 0x0b 0x14 relative mouse on , mouse threshold , joysticks auto (eg Barbarian 1 by Psygnosis)
|
|
0x12 0x14 disable mouse , joysticks auto (eg Hammerfist)
|
|
0x12 0x1a disable mouse , disable joysticks
|
|
|
|
In that case mouse and joystick buttons will be reported in a "mouse report" packet
|
|
and joystick actions (except buttons) will be reported in a "joystick report" packet.
|
|
|
|
*/
|
|
|
|
|
|
static void IKBD_RunKeyboardCommand(Uint8 aciabyte);
|
|
|
|
|
|
/* List of possible keyboard commands, others are seen as NOPs by keyboard processor */
|
|
static void IKBD_Cmd_Reset(void);
|
|
static void IKBD_Cmd_MouseAction(void);
|
|
static void IKBD_Cmd_RelMouseMode(void);
|
|
static void IKBD_Cmd_AbsMouseMode(void);
|
|
static void IKBD_Cmd_MouseCursorKeycodes(void);
|
|
static void IKBD_Cmd_SetMouseThreshold(void);
|
|
static void IKBD_Cmd_SetMouseScale(void);
|
|
static void IKBD_Cmd_ReadAbsMousePos(void);
|
|
static void IKBD_Cmd_SetInternalMousePos(void);
|
|
static void IKBD_Cmd_SetYAxisDown(void);
|
|
static void IKBD_Cmd_SetYAxisUp(void);
|
|
static void IKBD_Cmd_StartKeyboardTransfer(void);
|
|
static void IKBD_Cmd_TurnMouseOff(void);
|
|
static void IKBD_Cmd_StopKeyboardTransfer(void);
|
|
static void IKBD_Cmd_ReturnJoystickAuto(void);
|
|
static void IKBD_Cmd_StopJoystick(void);
|
|
static void IKBD_Cmd_ReturnJoystick(void);
|
|
static void IKBD_Cmd_SetJoystickMonitoring(void);
|
|
static void IKBD_Cmd_SetJoystickFireDuration(void);
|
|
static void IKBD_Cmd_SetCursorForJoystick(void);
|
|
static void IKBD_Cmd_DisableJoysticks(void);
|
|
static void IKBD_Cmd_SetClock(void);
|
|
static void IKBD_Cmd_ReadClock(void);
|
|
static void IKBD_Cmd_LoadMemory(void);
|
|
static void IKBD_Cmd_ReadMemory(void);
|
|
static void IKBD_Cmd_Execute(void);
|
|
static void IKBD_Cmd_ReportMouseAction(void);
|
|
static void IKBD_Cmd_ReportMouseMode(void);
|
|
static void IKBD_Cmd_ReportMouseThreshold(void);
|
|
static void IKBD_Cmd_ReportMouseScale(void);
|
|
static void IKBD_Cmd_ReportMouseVertical(void);
|
|
static void IKBD_Cmd_ReportMouseAvailability(void);
|
|
static void IKBD_Cmd_ReportJoystickMode(void);
|
|
static void IKBD_Cmd_ReportJoystickAvailability(void);
|
|
|
|
/* Keyboard Command */
|
|
static const struct {
|
|
Uint8 Command;
|
|
Uint8 NumParameters;
|
|
void (*pCallFunction)(void);
|
|
} KeyboardCommands[] =
|
|
{
|
|
/* Known messages, counts include command byte */
|
|
{ 0x80,2, IKBD_Cmd_Reset },
|
|
{ 0x07,2, IKBD_Cmd_MouseAction },
|
|
{ 0x08,1, IKBD_Cmd_RelMouseMode },
|
|
{ 0x09,5, IKBD_Cmd_AbsMouseMode },
|
|
{ 0x0A,3, IKBD_Cmd_MouseCursorKeycodes },
|
|
{ 0x0B,3, IKBD_Cmd_SetMouseThreshold },
|
|
{ 0x0C,3, IKBD_Cmd_SetMouseScale },
|
|
{ 0x0D,1, IKBD_Cmd_ReadAbsMousePos },
|
|
{ 0x0E,6, IKBD_Cmd_SetInternalMousePos },
|
|
{ 0x0F,1, IKBD_Cmd_SetYAxisDown },
|
|
{ 0x10,1, IKBD_Cmd_SetYAxisUp },
|
|
{ 0x11,1, IKBD_Cmd_StartKeyboardTransfer },
|
|
{ 0x12,1, IKBD_Cmd_TurnMouseOff },
|
|
{ 0x13,1, IKBD_Cmd_StopKeyboardTransfer },
|
|
{ 0x14,1, IKBD_Cmd_ReturnJoystickAuto },
|
|
{ 0x15,1, IKBD_Cmd_StopJoystick },
|
|
{ 0x16,1, IKBD_Cmd_ReturnJoystick },
|
|
{ 0x17,2, IKBD_Cmd_SetJoystickMonitoring },
|
|
{ 0x18,1, IKBD_Cmd_SetJoystickFireDuration },
|
|
{ 0x19,7, IKBD_Cmd_SetCursorForJoystick },
|
|
{ 0x1A,1, IKBD_Cmd_DisableJoysticks },
|
|
{ 0x1B,7, IKBD_Cmd_SetClock },
|
|
{ 0x1C,1, IKBD_Cmd_ReadClock },
|
|
{ 0x20,4, IKBD_Cmd_LoadMemory },
|
|
{ 0x21,3, IKBD_Cmd_ReadMemory },
|
|
{ 0x22,3, IKBD_Cmd_Execute },
|
|
|
|
/* Report message (top bit set) */
|
|
{ 0x87,1, IKBD_Cmd_ReportMouseAction },
|
|
{ 0x88,1, IKBD_Cmd_ReportMouseMode },
|
|
{ 0x89,1, IKBD_Cmd_ReportMouseMode },
|
|
{ 0x8A,1, IKBD_Cmd_ReportMouseMode },
|
|
{ 0x8B,1, IKBD_Cmd_ReportMouseThreshold },
|
|
{ 0x8C,1, IKBD_Cmd_ReportMouseScale },
|
|
{ 0x8F,1, IKBD_Cmd_ReportMouseVertical },
|
|
{ 0x90,1, IKBD_Cmd_ReportMouseVertical },
|
|
{ 0x92,1, IKBD_Cmd_ReportMouseAvailability },
|
|
{ 0x94,1, IKBD_Cmd_ReportJoystickMode },
|
|
{ 0x95,1, IKBD_Cmd_ReportJoystickMode },
|
|
{ 0x99,1, IKBD_Cmd_ReportJoystickMode },
|
|
{ 0x9A,1, IKBD_Cmd_ReportJoystickAvailability },
|
|
|
|
{ 0xFF,0, NULL } /* Term */
|
|
};
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Variables/defines/functions used to transfer data between the */
|
|
/* IKBD's SCI and the ACIA. */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
#define IKBD_TRCSR_BIT_WU 0x01 /* Wake Up */
|
|
#define IKBD_TRCSR_BIT_TE 0x02 /* Transmit Enable */
|
|
#define IKBD_TRCSR_BIT_TIE 0x04 /* Transmit Interrupt Enable */
|
|
#define IKBD_TRCSR_BIT_RE 0x08 /* Receive Enable */
|
|
#define IKBD_TRCSR_BIT_RIE 0x10 /* Receive Interrupt Enable */
|
|
#define IKBD_TRCSR_BIT_TDRE 0x20 /* Transmit Data Register Empty */
|
|
#define IKBD_TRCSR_BIT_ORFE 0x40 /* Over Run Framing Error */
|
|
#define IKBD_TRCSR_BIT_RDRF 0x80 /* Receive Data Register Full */
|
|
|
|
|
|
|
|
/* Possible states when handling TX/RX in the IKBD's Serial Communication Interface */
|
|
enum
|
|
{
|
|
IKBD_SCI_STATE_IDLE = 0,
|
|
IKBD_SCI_STATE_DATA_BIT,
|
|
IKBD_SCI_STATE_STOP_BIT
|
|
};
|
|
|
|
|
|
typedef struct {
|
|
/* IKBD's SCI internal registers */
|
|
Uint8 RMCR; /* reg 0x10 : Rate and Mode Control Register */
|
|
Uint8 TRCSR; /* reg 0x11 : Transmit/Receive Control and Status Register */
|
|
Uint8 TDR; /* reg 0x12 : Transmit Data Register */
|
|
Uint8 RDR; /* reg 0x13 : Receive Data Register */
|
|
|
|
int SCI_TX_State;
|
|
Uint8 TSR; /* Transmit Shift Register */
|
|
Uint8 SCI_TX_Size; /* How many data bits left to transmit in TSR (8 .. 0) */
|
|
int SCI_TX_Delay; /* If >0, wait SCI_TX_Delay calls of IKBD_SCI_Set_Line_TX before */
|
|
/* transferring a new byte in TDR (to simulate the time needed by */
|
|
/* the IKBD to process a command and return the result) */
|
|
|
|
int SCI_RX_State;
|
|
Uint8 RSR; /* Receive Shift Register */
|
|
Uint8 SCI_RX_Size; /* How many bits left to receive in RSR (8 .. 0) */
|
|
|
|
|
|
/* Date/Time is stored in the IKBD using 6 bytes in BCD format */
|
|
/* Clock is cleared on cold reset, but keeps its values on warm reset */
|
|
/* Original RAM location : $82=year $83=month $84=day $85=hour $86=minute $87=second */
|
|
Uint8 Clock[ 6 ];
|
|
Sint64 Clock_micro; /* Incremented every VBL to update Clock[] every second */
|
|
|
|
} IKBD_STRUCT;
|
|
|
|
|
|
static IKBD_STRUCT IKBD;
|
|
static IKBD_STRUCT *pIKBD = &IKBD;
|
|
|
|
|
|
|
|
|
|
static void IKBD_Init_Pointers ( ACIA_STRUCT *pACIA_IKBD );
|
|
static void IKBD_Boot_ROM ( bool ClearAllRAM );
|
|
|
|
static void IKBD_SCI_Get_Line_RX ( int rx_bit );
|
|
static Uint8 IKBD_SCI_Set_Line_TX ( void );
|
|
|
|
static void IKBD_Process_RDR ( Uint8 RDR );
|
|
static void IKBD_Check_New_TDR ( void );
|
|
|
|
static bool IKBD_OutputBuffer_CheckFreeCount ( int Nb );
|
|
static int IKBD_Delay_Random ( int min , int max );
|
|
static void IKBD_Cmd_Return_Byte ( Uint8 Data );
|
|
static void IKBD_Cmd_Return_Byte_Delay ( Uint8 Data , int Delay_Cycles );
|
|
static void IKBD_Send_Byte_Delay ( Uint8 Data , int Delay_Cycles );
|
|
|
|
static bool IKBD_BCD_Check ( Uint8 val );
|
|
static Uint8 IKBD_BCD_Adjust ( Uint8 val );
|
|
void IKBD_UpdateClockOnVBL ( void );
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/* Belows part is used to emulate the behaviour of custom 6301 programs */
|
|
/* sent to the IKBD's RAM. */
|
|
/*-----------------------------------------------------------------------*/
|
|
|
|
static void IKBD_LoadMemoryByte ( Uint8 aciabyte );
|
|
|
|
static void IKBD_CustomCodeHandler_CommonBoot ( Uint8 aciabyte );
|
|
|
|
static void IKBD_CustomCodeHandler_FroggiesMenu_Read ( void );
|
|
static void IKBD_CustomCodeHandler_FroggiesMenu_Write ( Uint8 aciabyte );
|
|
static void IKBD_CustomCodeHandler_Transbeauce2Menu_Read ( void );
|
|
static void IKBD_CustomCodeHandler_Transbeauce2Menu_Write ( Uint8 aciabyte );
|
|
static void IKBD_CustomCodeHandler_DragonnelsMenu_Read ( void );
|
|
static void IKBD_CustomCodeHandler_DragonnelsMenu_Write ( Uint8 aciabyte );
|
|
static void IKBD_CustomCodeHandler_ChaosAD_Read ( void );
|
|
static void IKBD_CustomCodeHandler_ChaosAD_Write ( Uint8 aciabyte );
|
|
|
|
|
|
static int MemoryLoadNbBytesTotal = 0; /* total number of bytes to send with the command 0x20 */
|
|
static int MemoryLoadNbBytesLeft = 0; /* number of bytes that remain to be sent */
|
|
static Uint32 MemoryLoadCrc = 0xffffffff; /* CRC of the bytes sent to the IKBD */
|
|
static int MemoryExeNbBytes = 0; /* current number of bytes sent to the IKBD when IKBD_ExeMode is true */
|
|
|
|
static void (*pIKBD_CustomCodeHandler_Read) ( void );
|
|
static void (*pIKBD_CustomCodeHandler_Write) ( Uint8 );
|
|
static bool IKBD_ExeMode = false;
|
|
|
|
static Uint8 ScanCodeState[ 128 ]; /* state of each key : 0=released 1=pressed */
|
|
|
|
/* This array contains all known custom 6301 programs, with their CRC */
|
|
static const struct
|
|
{
|
|
Uint32 LoadMemCrc; /* CRC of the bytes sent using the command 0x20 */
|
|
void (*ExeBootHandler) ( Uint8 ); /* function handling write to $fffc02 during the 'boot' mode */
|
|
int MainProgNbBytes; /* number of bytes of the main 6301 program */
|
|
Uint32 MainProgCrc; /* CRC of the main 6301 program */
|
|
void (*ExeMainHandler_Read) ( void );/* function handling read to $fffc02 in the main 6301 program */
|
|
void (*ExeMainHandler_Write) ( Uint8 ); /* function handling write to $fffc02 in the main 6301 program */
|
|
const char *Name;
|
|
}
|
|
CustomCodeDefinitions[] =
|
|
{
|
|
{
|
|
0x2efb11b1 ,
|
|
IKBD_CustomCodeHandler_CommonBoot ,
|
|
167,
|
|
0xe7110b6d ,
|
|
IKBD_CustomCodeHandler_FroggiesMenu_Read ,
|
|
IKBD_CustomCodeHandler_FroggiesMenu_Write ,
|
|
"Froggies Over The Fence Main Menu"
|
|
} ,
|
|
{
|
|
0xadb6b503 ,
|
|
IKBD_CustomCodeHandler_CommonBoot ,
|
|
165,
|
|
0x5617c33c ,
|
|
IKBD_CustomCodeHandler_Transbeauce2Menu_Read ,
|
|
IKBD_CustomCodeHandler_Transbeauce2Menu_Write ,
|
|
"Transbeauce 2 Main Menu"
|
|
} ,
|
|
{
|
|
0x33c23cdf ,
|
|
IKBD_CustomCodeHandler_CommonBoot ,
|
|
83 ,
|
|
0xdf3e5a88 ,
|
|
IKBD_CustomCodeHandler_DragonnelsMenu_Read ,
|
|
IKBD_CustomCodeHandler_DragonnelsMenu_Write ,
|
|
"Dragonnels Main Menu"
|
|
},
|
|
{
|
|
0x9ad7fcdf ,
|
|
IKBD_CustomCodeHandler_CommonBoot ,
|
|
109 ,
|
|
0xa11d8be5 ,
|
|
IKBD_CustomCodeHandler_ChaosAD_Read ,
|
|
IKBD_CustomCodeHandler_ChaosAD_Write ,
|
|
"Chaos A.D."
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Init the IKBD processor.
|
|
* Connect the IKBD RX/TX callback functions to the ACIA.
|
|
* This is called only once, when the emulator starts.
|
|
*/
|
|
void IKBD_Init ( void )
|
|
{
|
|
LOG_TRACE ( TRACE_IKBD_ALL, "ikbd init\n" );
|
|
|
|
/* Set the callback functions for RX/TX line */
|
|
IKBD_Init_Pointers ( pACIA_IKBD );
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Init some functions/memory pointers for the IKBD.
|
|
* This is called at Init and when restoring a memory snapshot.
|
|
*/
|
|
static void IKBD_Init_Pointers ( ACIA_STRUCT *pACIA_IKBD )
|
|
{
|
|
pACIA_IKBD->Get_Line_RX = IKBD_SCI_Set_Line_TX; /* Connect ACIA's RX to IKBD SCI's TX */
|
|
pACIA_IKBD->Set_Line_TX = IKBD_SCI_Get_Line_RX; /* Connect ACIA's TX to IKBD SCI's RX */
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Reset the IKBD processor
|
|
*/
|
|
|
|
/* This function is called after a hardware reset of the IKBD.
|
|
* Cold reset is when the computer is turned off/on.
|
|
* Warm reset is when the reset button is pressed or the 68000
|
|
* RESET instruction is used.
|
|
* We clear the serial interface and we execute the function
|
|
* that emulates booting the ROM at 0xF000.
|
|
*/
|
|
void IKBD_Reset ( bool bCold )
|
|
{
|
|
LOG_TRACE ( TRACE_IKBD_ALL , "ikbd reset mode=%s\n" , bCold?"cold":"warm" );
|
|
|
|
/* Reset the SCI */
|
|
pIKBD->TRCSR = IKBD_TRCSR_BIT_TDRE;
|
|
|
|
pIKBD->SCI_TX_State = IKBD_SCI_STATE_IDLE;
|
|
pIKBD->TSR = 0;
|
|
pIKBD->SCI_TX_Size = 0;
|
|
pIKBD->SCI_TX_Delay = 0;
|
|
|
|
pIKBD->SCI_RX_State = IKBD_SCI_STATE_IDLE;
|
|
pIKBD->RSR = 0;
|
|
pIKBD->SCI_RX_Size = 0;
|
|
|
|
|
|
/* On cold reset, clear the whole RAM (including clock data) */
|
|
/* On warm reset, the clock data should be kept */
|
|
if ( bCold )
|
|
IKBD_Boot_ROM ( true );
|
|
else
|
|
IKBD_Boot_ROM ( false );
|
|
}
|
|
|
|
|
|
|
|
/* This function emulates the boot code stored in the ROM at address $F000.
|
|
* This boot code is called either after a hardware reset, or when the
|
|
* reset command ($80 $01) is received.
|
|
* Depending on the conditions, we should clear the clock data or not (the
|
|
* real IKBD will test+clear RAM either in range $80-$FF or in range $89-$FF)
|
|
*/
|
|
static void IKBD_Boot_ROM ( bool ClearAllRAM )
|
|
{
|
|
int i;
|
|
|
|
|
|
LOG_TRACE ( TRACE_IKBD_ALL , "ikbd boot rom clear_all=%s\n" , ClearAllRAM?"yes":"no" );
|
|
|
|
/* Clear clock data when the 128 bytes of RAM are cleared */
|
|
if ( ClearAllRAM )
|
|
{
|
|
/* Clear clock data on cold reset */
|
|
for ( i=0 ; i<6 ; i++ )
|
|
pIKBD->Clock[ i ] = 0;
|
|
pIKBD->Clock_micro = 0;
|
|
}
|
|
|
|
// pIKBD->Clock[ 0 ] = 0x99;
|
|
// pIKBD->Clock[ 1 ] = 0x12;
|
|
// pIKBD->Clock[ 2 ] = 0x31;
|
|
// pIKBD->Clock[ 3 ] = 0x23;
|
|
// pIKBD->Clock[ 4 ] = 0x59;
|
|
// pIKBD->Clock[ 5 ] = 0x57;
|
|
|
|
/* Set default reporting mode for mouse/joysticks */
|
|
KeyboardProcessor.MouseMode = AUTOMODE_MOUSEREL;
|
|
KeyboardProcessor.JoystickMode = AUTOMODE_JOYSTICK;
|
|
|
|
KeyboardProcessor.Abs.X = ABS_X_ONRESET;
|
|
KeyboardProcessor.Abs.Y = ABS_Y_ONRESET;
|
|
KeyboardProcessor.Abs.MaxX = ABS_MAX_X_ONRESET;
|
|
KeyboardProcessor.Abs.MaxY = ABS_MAY_Y_ONRESET;
|
|
KeyboardProcessor.Abs.PrevReadAbsMouseButtons = ABS_PREVBUTTONS;
|
|
|
|
KeyboardProcessor.Mouse.DeltaX = KeyboardProcessor.Mouse.DeltaY = 0;
|
|
KeyboardProcessor.Mouse.XScale = KeyboardProcessor.Mouse.YScale = 0;
|
|
KeyboardProcessor.Mouse.XThreshold = KeyboardProcessor.Mouse.YThreshold = 1;
|
|
KeyboardProcessor.Mouse.YAxis = 1; /* Y origin at top */
|
|
KeyboardProcessor.Mouse.Action = 0;
|
|
|
|
KeyboardProcessor.Joy.PrevJoyData[0] = KeyboardProcessor.Joy.PrevJoyData[1] = 0;
|
|
|
|
for ( i=0 ; i<128 ; i++ )
|
|
ScanCodeState[ i ] = 0; /* key is released */
|
|
|
|
|
|
/* Reset our keyboard states and clear key state table */
|
|
Keyboard.BufferHead = Keyboard.BufferTail = 0;
|
|
Keyboard.NbBytesInOutputBuffer = 0;
|
|
Keyboard.nBytesInInputBuffer = 0;
|
|
Keyboard.PauseOutput = false;
|
|
|
|
memset(Keyboard.KeyStates, 0, sizeof(Keyboard.KeyStates));
|
|
Keyboard.bLButtonDown = BUTTON_NULL;
|
|
Keyboard.bRButtonDown = BUTTON_NULL;
|
|
Keyboard.bOldLButtonDown = Keyboard.bOldRButtonDown = BUTTON_NULL;
|
|
Keyboard.LButtonDblClk = Keyboard.RButtonDblClk = 0;
|
|
Keyboard.LButtonHistory = Keyboard.RButtonHistory = 0;
|
|
|
|
/* Store bool for when disable mouse or joystick */
|
|
bMouseDisabled = bJoystickDisabled = false;
|
|
/* do emulate hardware 'quirk' where if disable both with 'x' time
|
|
* of a RESET command they are ignored! */
|
|
bDuringResetCriticalTime = true;
|
|
bBothMouseAndJoy = false;
|
|
bMouseEnabledDuringReset = false;
|
|
|
|
|
|
/* Remove any custom handlers used to emulate code loaded to the 6301's RAM */
|
|
if ( IKBD_ExeMode == true )
|
|
{
|
|
LOG_TRACE ( TRACE_IKBD_ALL , "ikbd custom exe off\n" );
|
|
|
|
MemoryLoadNbBytesLeft = 0;
|
|
pIKBD_CustomCodeHandler_Read = NULL;
|
|
pIKBD_CustomCodeHandler_Write = NULL;
|
|
IKBD_ExeMode = false;
|
|
}
|
|
|
|
|
|
/* During the boot, the IKBD will test all the keys to ensure no key */
|
|
/* is stuck. We use a timer to emulate the time needed for this part */
|
|
/* (eg Lotus Turbo Esprit 2 requires at least a delay of 50000 cycles */
|
|
/* or it will crash during start up) */
|
|
CycInt_AddRelativeInterrupt( IKBD_RESET_CYCLES , INT_CPU_CYCLE , INTERRUPT_IKBD_RESETTIMER );
|
|
|
|
|
|
/* Add auto-update function to the queue */
|
|
Keyboard.AutoSendCycles = 150000; /* approx every VBL */
|
|
CycInt_AddRelativeInterrupt ( Keyboard.AutoSendCycles, INT_CPU_CYCLE, INTERRUPT_IKBD_AUTOSEND );
|
|
LOG_TRACE ( TRACE_IKBD_ALL , "ikbd reset done, starting reset timer\n" );
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* This timer is started by IKBD_Boot_ROM to emulate the time needed
|
|
* to setup the IKBD in its default state after a reset.
|
|
* If some IKBD commands are received during the boot phase they may be ignored.
|
|
*/
|
|
void IKBD_InterruptHandler_ResetTimer(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_ALL, "ikbd reset timer completed, resuming ikbd processing VBLs=%i framecyc=%i\n",
|
|
nVBLs, Cycles_GetCounter(CYCLES_COUNTER_VIDEO));
|
|
|
|
/* Remove this interrupt from list and re-order */
|
|
CycInt_AcknowledgeInterrupt();
|
|
|
|
/* Reset timer is over */
|
|
bDuringResetCriticalTime = false;
|
|
bMouseEnabledDuringReset = false;
|
|
|
|
/* Return $F1 when IKBD's boot is complete */
|
|
IKBD_Cmd_Return_Byte_Delay ( IKBD_ROM_VERSION , IKBD_Delay_Random ( 0 , 3000 ) );
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Save/Restore snapshot of local variables
|
|
* ('MemorySnapShot_Store' handles type)
|
|
*/
|
|
void IKBD_MemorySnapShot_Capture(bool bSave)
|
|
{
|
|
unsigned int i;
|
|
|
|
/* Save/Restore details */
|
|
MemorySnapShot_Store(&Keyboard, sizeof(Keyboard));
|
|
MemorySnapShot_Store(&KeyboardProcessor, sizeof(KeyboardProcessor));
|
|
MemorySnapShot_Store(&bMouseDisabled, sizeof(bMouseDisabled));
|
|
MemorySnapShot_Store(&bJoystickDisabled, sizeof(bJoystickDisabled));
|
|
MemorySnapShot_Store(&bDuringResetCriticalTime, sizeof(bDuringResetCriticalTime));
|
|
MemorySnapShot_Store(&bBothMouseAndJoy, sizeof(bBothMouseAndJoy));
|
|
MemorySnapShot_Store(&bMouseEnabledDuringReset, sizeof(bMouseEnabledDuringReset));
|
|
|
|
/* restore custom 6301 program if needed */
|
|
MemorySnapShot_Store(&IKBD_ExeMode, sizeof(IKBD_ExeMode));
|
|
MemorySnapShot_Store(&MemoryLoadCrc, sizeof(MemoryLoadCrc));
|
|
if ((bSave == false) && (IKBD_ExeMode == true)) /* restoring a snapshot with active 6301 emulation */
|
|
{
|
|
for ( i = 0 ; i < sizeof ( CustomCodeDefinitions ) / sizeof ( CustomCodeDefinitions[0] ); i++ )
|
|
if ( CustomCodeDefinitions[ i ].MainProgCrc == MemoryLoadCrc )
|
|
{
|
|
pIKBD_CustomCodeHandler_Read = CustomCodeDefinitions[ i ].ExeMainHandler_Read;
|
|
pIKBD_CustomCodeHandler_Write = CustomCodeDefinitions[ i ].ExeMainHandler_Write;
|
|
Keyboard.BufferHead = Keyboard.BufferTail = 0; /* flush all queued bytes that would be read in $fffc02 */
|
|
Keyboard.NbBytesInOutputBuffer = 0;
|
|
break;
|
|
}
|
|
|
|
if ( i >= sizeof ( CustomCodeDefinitions ) / sizeof ( CustomCodeDefinitions[0] ) ) /* not found (should not happen) */
|
|
IKBD_ExeMode = false; /* turn off exe mode */
|
|
}
|
|
|
|
|
|
/* Save the IKBD's SCI part and restore the callback functions for RX/TX lines with the ACIA */
|
|
MemorySnapShot_Store(&IKBD, sizeof(IKBD));
|
|
if (!bSave) /* Restoring a snapshot */
|
|
{
|
|
IKBD_Init_Pointers ( pACIA_IKBD );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* This part emulates the IKBD's Serial Communication Interface. */
|
|
/* This is a simplified implementation that ignores the RMCR content, */
|
|
/* as we assume the IKBD and the ACIA will be using the same baud rate. */
|
|
/* The TX/RX baud rate is chosen at the ACIA level, and the IKBD will */
|
|
/* use the same rate. */
|
|
/* The SCI only supports 8 bits of data, with 1 start bit, 1 stop bit */
|
|
/* and no parity bit. */
|
|
/************************************************************************/
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Prepare a new transfer. Copy TDR to TSR and initialize data size.
|
|
* Transfer will then start at the next call of IKBD_SCI_Set_Line_TX.
|
|
*/
|
|
static void IKBD_SCI_Prepare_TX ( IKBD_STRUCT *pIKBD )
|
|
{
|
|
pIKBD->TSR = pIKBD->TDR;
|
|
pIKBD->SCI_TX_Size = 8;
|
|
|
|
pIKBD->TRCSR |= IKBD_TRCSR_BIT_TDRE; /* TDR was copied to TSR. TDR is now empty */
|
|
|
|
LOG_TRACE ( TRACE_IKBD_ACIA, "ikbd acia prepare tx tsr=0x%02x size=%d VBL=%d HBL=%d\n" , pIKBD->TSR , pIKBD->SCI_TX_Size , nVBLs , nHBL );
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Prepare a new reception. Initialize RSR and data size.
|
|
*/
|
|
static void IKBD_SCI_Prepare_RX ( IKBD_STRUCT *pIKBD )
|
|
{
|
|
pIKBD->RSR = 0;
|
|
pIKBD->SCI_RX_Size = 8;
|
|
|
|
LOG_TRACE ( TRACE_IKBD_ACIA, "ikbd acia prepare rx size=%d VBL=%d HBL=%d\n" , pIKBD->SCI_RX_Size , nVBLs , nHBL );
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Receive a bit on the IKBD SCI's RX line (this is connected to the ACIA's TX)
|
|
* This will fill RDR with bits received from the serial line, using RSR.
|
|
* Incoming bits are stored in bit 7 of RSR, then RSR is shifted to the right.
|
|
* This is similar to the ACIA's RX, but with fixed parameters : 8 data bits,
|
|
* no parity bit and 1 stop bit.
|
|
*/
|
|
static void IKBD_SCI_Get_Line_RX ( int rx_bit )
|
|
{
|
|
int StateNext;
|
|
|
|
|
|
LOG_TRACE ( TRACE_IKBD_ACIA, "ikbd acia rx_state=%d bit=%d VBL=%d HBL=%d\n" , pIKBD->SCI_RX_State , rx_bit , nVBLs , nHBL );
|
|
|
|
StateNext = -1;
|
|
switch ( pIKBD->SCI_RX_State )
|
|
{
|
|
case IKBD_SCI_STATE_IDLE :
|
|
if ( rx_bit == 0 ) /* Receive one "0" start bit */
|
|
{
|
|
IKBD_SCI_Prepare_RX ( pIKBD );
|
|
StateNext = IKBD_SCI_STATE_DATA_BIT;
|
|
}
|
|
break; /* If no start bit, we stay in idle state */
|
|
|
|
case IKBD_SCI_STATE_DATA_BIT :
|
|
if ( rx_bit )
|
|
pIKBD->RSR |= 0x80;
|
|
pIKBD->SCI_RX_Size--;
|
|
|
|
if ( pIKBD->SCI_RX_Size > 0 ) /* All bits were not received yet */
|
|
pIKBD->RSR >>= 1;
|
|
else
|
|
StateNext = IKBD_SCI_STATE_STOP_BIT;
|
|
break;
|
|
|
|
case IKBD_SCI_STATE_STOP_BIT :
|
|
if ( rx_bit == 1 ) /* Wait for one "1" stop bit */
|
|
{
|
|
pIKBD->TRCSR &= ~IKBD_TRCSR_BIT_ORFE;
|
|
|
|
if ( ( pIKBD->TRCSR & IKBD_TRCSR_BIT_RDRF ) == 0 )
|
|
{
|
|
pIKBD->RDR = pIKBD->RSR;
|
|
pIKBD->TRCSR |= IKBD_TRCSR_BIT_RDRF;
|
|
LOG_TRACE ( TRACE_IKBD_ACIA, "ikbd acia get_rx received rsr=0x%02x VBL=%d HBL=%d\n" ,
|
|
pIKBD->RDR , nVBLs , nHBL );
|
|
|
|
IKBD_Process_RDR ( pIKBD->RDR ); /* Process this new byte */
|
|
}
|
|
else
|
|
{
|
|
pIKBD->TRCSR |= IKBD_TRCSR_BIT_ORFE; /* Overrun Error */
|
|
LOG_TRACE ( TRACE_IKBD_ACIA, "ikbd acia get_rx received rsr=0x%02x : ignored, rdr=0x%02x and rdrf already set VBL=%d HBL=%d\n" ,
|
|
pIKBD->RSR , pIKBD->RDR , nVBLs , nHBL );
|
|
|
|
IKBD_Process_RDR ( pIKBD->RDR ); /* RSR is lost, try to process the current RDR which was not read yet */
|
|
}
|
|
StateNext = IKBD_SCI_STATE_IDLE; /* Go to idle state and wait for start bit */
|
|
}
|
|
else /* Not a valid stop bit */
|
|
{
|
|
LOG_TRACE ( TRACE_IKBD_ACIA, "ikbd acia get_rx framing error VBL=%d HBL=%d\n" , nVBLs , nHBL );
|
|
pIKBD->TRCSR |= IKBD_TRCSR_BIT_ORFE; /* Framing Error */
|
|
StateNext = IKBD_SCI_STATE_IDLE; /* Go to idle state and wait for start bit */
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ( StateNext >= 0 )
|
|
pIKBD->SCI_RX_State = StateNext; /* Go to a new state */
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Send a bit on the IKBD SCI's TX line (this is connected to the ACIA's RX)
|
|
* When the SCI is idle, we send '1' stop bits.
|
|
*/
|
|
static Uint8 IKBD_SCI_Set_Line_TX ( void )
|
|
{
|
|
int StateNext;
|
|
Uint8 tx_bit = 1;
|
|
|
|
|
|
LOG_TRACE ( TRACE_IKBD_ACIA, "ikbd acia tx_state=%d tx_delay=%d VBL=%d HBL=%d\n" , pIKBD->SCI_TX_State , pIKBD->SCI_TX_Delay ,
|
|
nVBLs , nHBL );
|
|
|
|
StateNext = -1;
|
|
switch ( pIKBD->SCI_TX_State )
|
|
{
|
|
case IKBD_SCI_STATE_IDLE :
|
|
tx_bit = 1; /* In idle state, default is to send '1' stop bits */
|
|
|
|
if ( pIKBD->SCI_TX_Delay > 0 ) /* Should we delay the next TDR ? */
|
|
{
|
|
pIKBD->SCI_TX_Delay--; /* Don't do anything for now, send a stop bit */
|
|
break;
|
|
}
|
|
|
|
IKBD_Check_New_TDR (); /* Do we have a byte to load in TDR ? */
|
|
|
|
if ( ( pIKBD->TRCSR & IKBD_TRCSR_BIT_TDRE ) == 0 ) /* We have a new byte in TDR */
|
|
{
|
|
IKBD_SCI_Prepare_TX ( pIKBD );
|
|
tx_bit = 0; /* Send one '0' start bit */
|
|
StateNext = IKBD_SCI_STATE_DATA_BIT;
|
|
}
|
|
break;
|
|
|
|
case IKBD_SCI_STATE_DATA_BIT :
|
|
tx_bit = pIKBD->TSR & 1; /* New bit to send */
|
|
pIKBD->TSR >>= 1;
|
|
pIKBD->SCI_TX_Size--;
|
|
|
|
if ( pIKBD->SCI_TX_Size == 0 )
|
|
StateNext = IKBD_SCI_STATE_STOP_BIT;
|
|
break;
|
|
|
|
|
|
case IKBD_SCI_STATE_STOP_BIT :
|
|
tx_bit = 1; /* Send 1 stop bit */
|
|
StateNext = IKBD_SCI_STATE_IDLE; /* Go to idle state to see if a new TDR need to be sent */
|
|
break;
|
|
}
|
|
|
|
if ( StateNext >= 0 )
|
|
pIKBD->SCI_TX_State = StateNext; /* Go to a new state */
|
|
|
|
return tx_bit;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Handle the byte that was received in the RDR from the ACIA.
|
|
* Depending on the IKBD's emulation mode, we either pass it to the standard
|
|
* ROM's emulation layer, or we pass it to the custom handlers.
|
|
*/
|
|
static void IKBD_Process_RDR ( Uint8 RDR )
|
|
{
|
|
pIKBD->TRCSR &= ~IKBD_TRCSR_BIT_RDRF; /* RDR was read */
|
|
|
|
|
|
/* If IKBD is executing custom code, send the byte to the function handling this code */
|
|
if ( IKBD_ExeMode && pIKBD_CustomCodeHandler_Write )
|
|
{
|
|
(*pIKBD_CustomCodeHandler_Write) ( RDR );
|
|
return;
|
|
}
|
|
|
|
if ( MemoryLoadNbBytesLeft == 0 ) /* No pending MemoryLoad command */
|
|
IKBD_RunKeyboardCommand ( RDR ); /* Check for known commands */
|
|
|
|
else /* MemoryLoad command is not finished yet */
|
|
IKBD_LoadMemoryByte ( RDR ); /* Process bytes sent to the IKBD's RAM */
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Check if we have a byte to copy to the IKBD's TDR, to send it to the ACIA.
|
|
* We get new bytes from the buffer filled by IKBD_Send_Byte_Delay
|
|
*/
|
|
static void IKBD_Check_New_TDR ( void )
|
|
{
|
|
// fprintf(stderr , "check new tdr %d %d\n", Keyboard.BufferHead , Keyboard.BufferTail );
|
|
|
|
if ( ( Keyboard.NbBytesInOutputBuffer > 0 )
|
|
&& ( Keyboard.PauseOutput == false ) )
|
|
{
|
|
pIKBD->TDR = Keyboard.Buffer[ Keyboard.BufferHead++ ];
|
|
Keyboard.BufferHead &= KEYBOARD_BUFFER_MASK;
|
|
Keyboard.NbBytesInOutputBuffer--;
|
|
pIKBD->TRCSR &= ~IKBD_TRCSR_BIT_TDRE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Return true if the output buffer can store 'Nb' new bytes,
|
|
* else return false.
|
|
* Some games like 'Downfall' or 'Fokker' are continually issuing the same
|
|
* IKBD_Cmd_ReturnJoystick command without waiting for the returned bytes,
|
|
* which will fill the output buffer faster than the CPU can empty it.
|
|
* In that case, new messages must be discarded until the buffer has some room
|
|
* again for a whole packet.
|
|
*/
|
|
static bool IKBD_OutputBuffer_CheckFreeCount ( int Nb )
|
|
{
|
|
// fprintf ( stderr , "check %d %d head %d tail %d\n" , Nb , SIZE_KEYBOARD_BUFFER - Keyboard.NbBytesInOutputBuffer ,
|
|
// Keyboard.BufferHead , Keyboard.BufferTail );
|
|
|
|
if ( SIZE_KEYBOARD_BUFFER - Keyboard.NbBytesInOutputBuffer >= Nb )
|
|
return true;
|
|
|
|
else
|
|
{
|
|
LOG_TRACE ( TRACE_IKBD_ACIA, "ikbd acia output buffer is full, can't send %d bytes VBL=%d HBL=%d\n" ,
|
|
Nb, nVBLs , nHBL );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Return a random number between 'min' and 'max'.
|
|
* This is used when the IKBD send bytes to the ACIA, to add some
|
|
* randomness to the delay (on real hardware, the delay is not constant
|
|
* when a command return some bytes).
|
|
*/
|
|
static int IKBD_Delay_Random ( int min , int max )
|
|
{
|
|
return min + rand() % ( max - min );
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* This function will buffer all the bytes returned by a specific
|
|
* IKBD_Cmd_xxx command. If we're using a custom handler, we should filter
|
|
* these bytes (keyboard, mouse, joystick) as they don't come from the custom handler.
|
|
*/
|
|
static void IKBD_Cmd_Return_Byte ( Uint8 Data )
|
|
{
|
|
if ( IKBD_ExeMode ) /* If IKBD is executing custom code, don't add */
|
|
return; /* anything to the buffer that comes from an IKBD's command */
|
|
|
|
IKBD_Send_Byte_Delay ( Data , 0 );
|
|
}
|
|
|
|
|
|
/**
|
|
* Same as IKBD_Cmd_Return_Byte, but with a delay before transmitting
|
|
* the byte.
|
|
*/
|
|
static void IKBD_Cmd_Return_Byte_Delay ( Uint8 Data , int Delay_Cycles )
|
|
{
|
|
if ( IKBD_ExeMode ) /* If IKBD is executing custom code, don't add */
|
|
return; /* anything to the buffer that comes from an IKBD's command */
|
|
|
|
IKBD_Send_Byte_Delay ( Data , Delay_Cycles );
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Send bytes from the IKBD to the ACIA. We store the bytes in a buffer
|
|
* and we pull a new byte each time TDR needs to be re-filled.
|
|
*
|
|
* A possible delay can be specified to simulate the fact that some IKBD's
|
|
* commands don't return immediately the first byte. This delay is given
|
|
* in 68000 cycles at 8 MHz and should be converted to a number of bits
|
|
* at the chosen baud rate.
|
|
*/
|
|
static void IKBD_Send_Byte_Delay ( Uint8 Data , int Delay_Cycles )
|
|
{
|
|
//fprintf ( stderr , "send byte=0x%02x delay=%d\n" , Data , Delay_Cycles );
|
|
/* Is keyboard initialised yet ? Ignore any bytes until it is */
|
|
if ( bDuringResetCriticalTime )
|
|
{
|
|
LOG_TRACE ( TRACE_IKBD_ACIA, "ikbd is resetting, can't send byte=0x%02x VBL=%d HBL=%d\n" , Data, nVBLs , nHBL );
|
|
return;
|
|
}
|
|
|
|
/* Is ACIA's serial line initialised yet ? Ignore any bytes until it is */
|
|
if ( pACIA_IKBD->Clock_Divider == 0 )
|
|
{
|
|
LOG_TRACE ( TRACE_IKBD_ACIA, "ikbd acia not initialized, can't send byte=0x%02x VBL=%d HBL=%d\n" , Data, nVBLs , nHBL );
|
|
return;
|
|
}
|
|
|
|
if ( Delay_Cycles > 0 )
|
|
pIKBD->SCI_TX_Delay = Delay_Cycles / 1024; /* 1 bit at 7812.5 baud = 1024 cpu cycles at 8 MHz */
|
|
|
|
|
|
/* Check we have space to add one byte */
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 1 ) )
|
|
{
|
|
/* Add byte to our buffer */
|
|
Keyboard.Buffer[Keyboard.BufferTail++] = Data;
|
|
Keyboard.BufferTail &= KEYBOARD_BUFFER_MASK;
|
|
Keyboard.NbBytesInOutputBuffer++;
|
|
}
|
|
else
|
|
{
|
|
Log_Printf(LOG_ERROR, "IKBD buffer is full, can't send 0x%02x!\n" , Data );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* End of the Serial Communication Interface */
|
|
/************************************************************************/
|
|
|
|
|
|
/**
|
|
* Check that the value is a correctly encoded BCD number
|
|
*/
|
|
static bool IKBD_BCD_Check ( Uint8 val )
|
|
{
|
|
if ( ( ( val & 0x0f ) > 0x09 )
|
|
|| ( ( val & 0xf0 ) > 0x90 ) )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* After adding an integer number to a BCD number, the result is no more
|
|
* in BCD format. This function adjusts the value to be a valid BCD number again.
|
|
* In the HD6301, this is done using the 'DAA' instruction (Decimal Adjust)
|
|
* to "propagate" values 10-15 to the next 4 bits and keep each nibble
|
|
* in the 0-9 range.
|
|
*/
|
|
|
|
static Uint8 IKBD_BCD_Adjust ( Uint8 val )
|
|
{
|
|
if ( ( val & 0x0f ) > 0x09 ) /* low nibble no more in BCD */
|
|
val += 0x06; /* clear bit 4 and add 1 to high nibble */
|
|
if ( ( val & 0xf0 ) > 0x90 ) /* high nibble no more in BCD */
|
|
val += 0x60; /* propagate carry (but bits>7 will be lost) */
|
|
|
|
return val;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Update the IKBD's internal clock.
|
|
*
|
|
* This function is called on every VBL and we add the number of microseconds
|
|
* per VBL. When we reach 1000000 microseconds (1 sec), we update the Clock[]
|
|
* array by incrementing the 'second' byte.
|
|
*
|
|
* This code uses the same logic as the ROM version in the IKBD,
|
|
* don't try to optimise/rewrite it in a different way, as the TOS
|
|
* expects data to be handled this way.
|
|
* This works directly with BCD numbers and propagates the increment
|
|
* to the next byte each time the current byte reaches its maximum
|
|
* value.
|
|
* - when SetClock is used, the IKBD doesn't check the range of each byte,
|
|
* just that it's BCD encoded. So it's possible to set month/day/... to
|
|
* invalid values beyond the maximum allowed. These values will not correctly
|
|
* propagate to the next byte until they reach 0x99 and start again at 0x00.
|
|
* - check leap year for the number of days in february if ( year & 3 == 0 )
|
|
* - there's no explicit max for year : if year is 99 and increments,
|
|
* next year will be 00 (due to the BCD overflow)
|
|
* (used in the game 'Captain Blood' which sets clock to "99 12 31 00 00 00"
|
|
* and ends the game when clock reaches "00 01 01 00 00 00")
|
|
*/
|
|
void IKBD_UpdateClockOnVBL ( void )
|
|
{
|
|
Sint64 FrameDuration_micro;
|
|
int i;
|
|
Uint8 val;
|
|
Uint8 max;
|
|
Uint8 year;
|
|
Uint8 month;
|
|
|
|
/* Max value for year/month/day/hour/minute/second */
|
|
Uint8 val_max[ 6 ] = { 0xFF , 0x13 , 0x00 , 0x24 , 0x60 , 0x60 };
|
|
/* Max number of days per month ; 18 entries, because the index for this array is a BCD coded month */
|
|
Uint8 day_max[ 18 ] = { 0x32, 0x29, 0x32, 0x31, 0x32, 0x31, 0x32, 0x32, 0x31, 0,0,0,0,0,0, 0x32, 0x31, 0x32 };
|
|
|
|
|
|
/* Check if more than 1 second passed since last increment of date/time */
|
|
FrameDuration_micro = ClocksTimings_GetVBLDuration_micro ( ConfigureParams.System.nMachineType , nScreenRefreshRate );
|
|
pIKBD->Clock_micro += FrameDuration_micro;
|
|
if ( pIKBD->Clock_micro < 1000000 )
|
|
return; /* Less than 1 second, don't increment date/time yet */
|
|
pIKBD->Clock_micro -= 1000000;
|
|
|
|
|
|
/* 1 second passed, we can increment the clock data */
|
|
// LOG_TRACE(TRACE_IKBD_CMDS,
|
|
// "IKBD_UpdateClock: %02x %02x %02x %02x %02x %02x -> ", pIKBD->Clock[ 0 ] ,pIKBD->Clock[ 1 ] , pIKBD->Clock[ 2 ] ,
|
|
// pIKBD->Clock[ 3 ] , pIKBD->Clock[ 4 ] , pIKBD->Clock[ 5 ] );
|
|
|
|
for ( i=5 ; i>=0 ; i-- )
|
|
{
|
|
val = pIKBD->Clock[ i ] + 1; /* Increment current value */
|
|
val = IKBD_BCD_Adjust ( val ); /* Convert to BCD */
|
|
|
|
if ( i != 2 )
|
|
max = val_max[ i ];
|
|
|
|
else /* Special case for days per month */
|
|
{
|
|
/* WARNING : it's possible to set the IKBD with month > 0x12, but in that case */
|
|
/* we would access day_max[] out of range. So, if month > 0x12, we limit to 31 days */
|
|
/* (this test is not done in the IKBD, but results would not be correct anyway) */
|
|
month = pIKBD->Clock[ 1 ];
|
|
if ( month > 0x12 ) /* Hatari specific, check range */
|
|
month = 0x12;
|
|
max = day_max[ month - 1 ]; /* Number of days for current month */
|
|
if ( pIKBD->Clock[ 1 ] == 2 ) /* For february, check leap year */
|
|
{
|
|
year = pIKBD->Clock[ 0 ];
|
|
/* Leap year test comes from the IKBD's ROM */
|
|
if ( year & 0x10 )
|
|
year += 0x0a;
|
|
if ( ( year & 0x03 ) == 0 )
|
|
max = 0x30; /* This is a leap year, 29 days */
|
|
}
|
|
}
|
|
|
|
if ( val != max )
|
|
{
|
|
pIKBD->Clock[ i ] = val; /* Max not reached, stop here */
|
|
break;
|
|
}
|
|
else if ( ( i == 1 ) || ( i == 2 ) )
|
|
pIKBD->Clock[ i ] = 1; /* day/month start at 1 */
|
|
else
|
|
pIKBD->Clock[ i ] = 0; /* hour/minute/second start at 0 */
|
|
}
|
|
|
|
// LOG_TRACE(TRACE_IKBD_CMDS,
|
|
// "%02x %02x %02x %02x %02x %02x\n", pIKBD->Clock[ 0 ] ,pIKBD->Clock[ 1 ] , pIKBD->Clock[ 2 ] ,
|
|
// pIKBD->Clock[ 3 ] , pIKBD->Clock[ 4 ] , pIKBD->Clock[ 5 ] );
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Calculate out 'delta' that mouse has moved by each frame, and add this to our internal keyboard position
|
|
*/
|
|
static void IKBD_UpdateInternalMousePosition(void)
|
|
{
|
|
|
|
KeyboardProcessor.Mouse.DeltaX = KeyboardProcessor.Mouse.dx;
|
|
KeyboardProcessor.Mouse.DeltaY = KeyboardProcessor.Mouse.dy;
|
|
KeyboardProcessor.Mouse.dx = 0;
|
|
KeyboardProcessor.Mouse.dy = 0;
|
|
|
|
/* Update internal mouse coords - Y axis moves according to YAxis setting(up/down) */
|
|
/* Limit to Max X/Y(inclusive) */
|
|
if ( KeyboardProcessor.Mouse.XScale > 1 )
|
|
KeyboardProcessor.Abs.X += KeyboardProcessor.Mouse.DeltaX * KeyboardProcessor.Mouse.XScale;
|
|
else
|
|
KeyboardProcessor.Abs.X += KeyboardProcessor.Mouse.DeltaX;
|
|
if (KeyboardProcessor.Abs.X < 0)
|
|
KeyboardProcessor.Abs.X = 0;
|
|
if (KeyboardProcessor.Abs.X > KeyboardProcessor.Abs.MaxX)
|
|
KeyboardProcessor.Abs.X = KeyboardProcessor.Abs.MaxX;
|
|
|
|
if ( KeyboardProcessor.Mouse.YScale > 1 )
|
|
KeyboardProcessor.Abs.Y += KeyboardProcessor.Mouse.DeltaY*KeyboardProcessor.Mouse.YAxis * KeyboardProcessor.Mouse.YScale;
|
|
else
|
|
KeyboardProcessor.Abs.Y += KeyboardProcessor.Mouse.DeltaY*KeyboardProcessor.Mouse.YAxis;
|
|
if (KeyboardProcessor.Abs.Y < 0)
|
|
KeyboardProcessor.Abs.Y = 0;
|
|
if (KeyboardProcessor.Abs.Y > KeyboardProcessor.Abs.MaxY)
|
|
KeyboardProcessor.Abs.Y = KeyboardProcessor.Abs.MaxY;
|
|
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* When running in maximum speed the emulation will not see 'double-clicks'
|
|
* of the mouse as it is running so fast. In this case, we check for a
|
|
* double-click and pass the 'up'/'down' messages in emulation time to
|
|
* simulate the double-click effect!
|
|
*/
|
|
static void IKBD_CheckForDoubleClicks(void)
|
|
{
|
|
/*
|
|
Things get a little complicated when running max speed as a normal
|
|
double-click is a load of 1's, followed by 0's, 1's and 0's - But the
|
|
ST does not see this as a double click as the space in 'ST' time
|
|
between changes is so great.
|
|
Now, when we see a real double-click in max speed we actually send
|
|
the down/up/down/up in ST time. To get this correct (and not send
|
|
three clicks) we look in a history buffer and start at an index which
|
|
gives the correct number of clicks! Phew!
|
|
*/
|
|
|
|
/* Handle double clicks!!! */
|
|
if (Keyboard.LButtonDblClk)
|
|
{
|
|
if (Keyboard.LButtonDblClk == 1) /* First pressed! */
|
|
{
|
|
if ((Keyboard.LButtonHistory&0x3f) == 0) /* If not pressed button in long time do full dbl-click pattern */
|
|
Keyboard.LButtonDblClk = 1;
|
|
else
|
|
{
|
|
Keyboard.LButtonDblClk = 4; /* Otherwise, check where to begin to give 1111000011110000 pattern */
|
|
if ((Keyboard.LButtonHistory&0x7) == 0)
|
|
Keyboard.LButtonDblClk = 8;
|
|
else if ((Keyboard.LButtonHistory&0x3) == 0)
|
|
Keyboard.LButtonDblClk = 7;
|
|
else if ((Keyboard.LButtonHistory&0x1) == 0)
|
|
Keyboard.LButtonDblClk = 6;
|
|
}
|
|
}
|
|
|
|
Keyboard.bLButtonDown = DoubleClickPattern[Keyboard.LButtonDblClk];
|
|
Keyboard.LButtonDblClk++;
|
|
if (Keyboard.LButtonDblClk >= 13) /* Check for end of sequence */
|
|
{
|
|
Keyboard.LButtonDblClk = 0;
|
|
Keyboard.bLButtonDown = false;
|
|
}
|
|
}
|
|
if (Keyboard.RButtonDblClk)
|
|
{
|
|
if (Keyboard.RButtonDblClk == 1) /* First pressed! */
|
|
{
|
|
if ((Keyboard.RButtonHistory&0x3f) == 0) /* If not pressed button in long time do full dbl-click pattern */
|
|
Keyboard.RButtonDblClk = 1;
|
|
else
|
|
{
|
|
Keyboard.RButtonDblClk = 4; /* Otherwise, check where to begin to give 1111000011110000 pattern */
|
|
if ((Keyboard.RButtonHistory&0x7) == 0)
|
|
Keyboard.RButtonDblClk = 8;
|
|
else if ((Keyboard.RButtonHistory&0x3) == 0)
|
|
Keyboard.RButtonDblClk = 7;
|
|
else if ((Keyboard.RButtonHistory&0x1) == 0)
|
|
Keyboard.RButtonDblClk = 6;
|
|
}
|
|
}
|
|
|
|
Keyboard.bRButtonDown = DoubleClickPattern[Keyboard.RButtonDblClk];
|
|
Keyboard.RButtonDblClk++;
|
|
if (Keyboard.RButtonDblClk >= 13) /* Check for end of sequence */
|
|
{
|
|
Keyboard.RButtonDblClk = 0;
|
|
Keyboard.bRButtonDown = false;
|
|
}
|
|
}
|
|
|
|
/* Store presses into history */
|
|
Keyboard.LButtonHistory = (Keyboard.LButtonHistory<<1);
|
|
if (Keyboard.bLButtonDown)
|
|
Keyboard.LButtonHistory |= 0x1;
|
|
Keyboard.RButtonHistory = (Keyboard.RButtonHistory<<1);
|
|
if (Keyboard.bRButtonDown)
|
|
Keyboard.RButtonHistory |= 0x1;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Convert button to bool value
|
|
*/
|
|
static bool IKBD_ButtonBool(int Button)
|
|
{
|
|
/* Button pressed? */
|
|
if (Button)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Return true if buttons match, use this as buttons are a mask and not boolean
|
|
*/
|
|
static bool IKBD_ButtonsEqual(int Button1,int Button2)
|
|
{
|
|
/* Return bool compare */
|
|
return (IKBD_ButtonBool(Button1) == IKBD_ButtonBool(Button2));
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* According to if the mouse is enabled or not the joystick 1 fire
|
|
* button/right mouse button will become the same button. That means
|
|
* pressing one will also press the other and vice-versa.
|
|
* If both mouse and joystick are enabled, report it as a mouse button
|
|
* (needed by the game Big Run for example).
|
|
*/
|
|
static void IKBD_DuplicateMouseFireButtons(void)
|
|
{
|
|
/* If mouse is off then joystick fire button goes to joystick */
|
|
if (KeyboardProcessor.MouseMode == AUTOMODE_OFF)
|
|
{
|
|
/* If pressed right mouse button, should go to joystick 1 */
|
|
if (Keyboard.bRButtonDown&BUTTON_MOUSE)
|
|
KeyboardProcessor.Joy.JoyData[1] |= 0x80;
|
|
/* And left mouse button, should go to joystick 0 */
|
|
if (Keyboard.bLButtonDown&BUTTON_MOUSE)
|
|
KeyboardProcessor.Joy.JoyData[0] |= 0x80;
|
|
}
|
|
/* If mouse is on, joystick 1 fire button goes to the mouse instead */
|
|
else
|
|
{
|
|
/* Is fire button pressed? */
|
|
if (KeyboardProcessor.Joy.JoyData[1]&0x80)
|
|
{
|
|
KeyboardProcessor.Joy.JoyData[1] &= 0x7f; /* Clear fire button bit */
|
|
Keyboard.bRButtonDown |= BUTTON_JOYSTICK; /* Mimick on mouse right button */
|
|
}
|
|
else
|
|
Keyboard.bRButtonDown &= ~BUTTON_JOYSTICK;
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Send 'relative' mouse position
|
|
*/
|
|
static void IKBD_SendRelMousePacket(void)
|
|
{
|
|
int ByteRelX,ByteRelY;
|
|
Uint8 Header;
|
|
|
|
if ( (KeyboardProcessor.Mouse.DeltaX!=0) || (KeyboardProcessor.Mouse.DeltaY!=0)
|
|
|| (!IKBD_ButtonsEqual(Keyboard.bOldLButtonDown,Keyboard.bLButtonDown)) || (!IKBD_ButtonsEqual(Keyboard.bOldRButtonDown,Keyboard.bRButtonDown)) )
|
|
{
|
|
/* Send packet to keyboard process */
|
|
while (true)
|
|
{
|
|
ByteRelX = KeyboardProcessor.Mouse.DeltaX;
|
|
if (ByteRelX>127) ByteRelX = 127;
|
|
if (ByteRelX<-128) ByteRelX = -128;
|
|
ByteRelY = KeyboardProcessor.Mouse.DeltaY;
|
|
if (ByteRelY>127) ByteRelY = 127;
|
|
if (ByteRelY<-128) ByteRelY = -128;
|
|
|
|
Header = 0xf8;
|
|
if (Keyboard.bLButtonDown)
|
|
Header |= 0x02;
|
|
if (Keyboard.bRButtonDown)
|
|
Header |= 0x01;
|
|
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 3 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte (Header);
|
|
IKBD_Cmd_Return_Byte (ByteRelX);
|
|
IKBD_Cmd_Return_Byte (ByteRelY*KeyboardProcessor.Mouse.YAxis);
|
|
}
|
|
|
|
KeyboardProcessor.Mouse.DeltaX -= ByteRelX;
|
|
KeyboardProcessor.Mouse.DeltaY -= ByteRelY;
|
|
|
|
if ( (KeyboardProcessor.Mouse.DeltaX==0) && (KeyboardProcessor.Mouse.DeltaY==0) )
|
|
break;
|
|
|
|
/* Store buttons for next time around */
|
|
Keyboard.bOldLButtonDown = Keyboard.bLButtonDown;
|
|
Keyboard.bOldRButtonDown = Keyboard.bRButtonDown;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Get joystick data
|
|
*/
|
|
static void IKBD_GetJoystickData(void)
|
|
{
|
|
/* Joystick 1 */
|
|
KeyboardProcessor.Joy.JoyData[1] = Joy_GetStickData(1);
|
|
|
|
/* If mouse is on, joystick 0 is not connected */
|
|
if (KeyboardProcessor.MouseMode==AUTOMODE_OFF
|
|
|| (bBothMouseAndJoy && KeyboardProcessor.MouseMode==AUTOMODE_MOUSEREL))
|
|
KeyboardProcessor.Joy.JoyData[0] = Joy_GetStickData(0);
|
|
else
|
|
KeyboardProcessor.Joy.JoyData[0] = 0x00;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Send 'joysticks' bit masks
|
|
*/
|
|
static void IKBD_SendAutoJoysticks(void)
|
|
{
|
|
Uint8 JoyData;
|
|
|
|
/* Did joystick 0/mouse change? */
|
|
JoyData = KeyboardProcessor.Joy.JoyData[0];
|
|
if (JoyData!=KeyboardProcessor.Joy.PrevJoyData[0])
|
|
{
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 2 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte (0xFE); /* Joystick 0 / Mouse */
|
|
IKBD_Cmd_Return_Byte (JoyData);
|
|
}
|
|
KeyboardProcessor.Joy.PrevJoyData[0] = JoyData;
|
|
}
|
|
|
|
/* Did joystick 1(default) change? */
|
|
JoyData = KeyboardProcessor.Joy.JoyData[1];
|
|
if (JoyData!=KeyboardProcessor.Joy.PrevJoyData[1])
|
|
{
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 2 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte (0xFF); /* Joystick 1 */
|
|
IKBD_Cmd_Return_Byte (JoyData);
|
|
}
|
|
KeyboardProcessor.Joy.PrevJoyData[1] = JoyData;
|
|
}
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Send 'joysticks' bit masks when in monitoring mode
|
|
* %000000xy ; where y is JOYSTICK1 Fire button
|
|
* ; and x is JOYSTICK0 Fire button
|
|
* %nnnnmmmm ; where m is JOYSTICK1 state
|
|
* ; and n is JOYSTICK0 state
|
|
*/
|
|
static void IKBD_SendAutoJoysticksMonitoring(void)
|
|
{
|
|
Uint8 Byte1;
|
|
Uint8 Byte2;
|
|
|
|
Byte1 = ( ( KeyboardProcessor.Joy.JoyData[0] & 0x80 ) >> 6 )
|
|
| ( ( KeyboardProcessor.Joy.JoyData[1] & 0x80 ) >> 7 );
|
|
|
|
Byte2 = ( ( KeyboardProcessor.Joy.JoyData[0] & 0x0f ) << 4 )
|
|
| ( KeyboardProcessor.Joy.JoyData[1] & 0x0f );
|
|
|
|
IKBD_Cmd_Return_Byte (Byte1);
|
|
IKBD_Cmd_Return_Byte (Byte2);
|
|
//fprintf ( stderr , "joystick monitoring %x %x VBL=%d HBL=%d\n" , Byte1 , Byte2 , nVBLs , nHBL );
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Send packets which are generated from the mouse action settings
|
|
* If relative mode is on, still generate these packets
|
|
*/
|
|
static void IKBD_SendOnMouseAction(void)
|
|
{
|
|
bool bReportPosition = false;
|
|
|
|
/* Report buttons as keys? Do in relative/absolute mode */
|
|
if (KeyboardProcessor.Mouse.Action&0x4)
|
|
{
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 2 ) )
|
|
{
|
|
/* Left button? */
|
|
if ( (IKBD_ButtonBool(Keyboard.bLButtonDown) && (!IKBD_ButtonBool(Keyboard.bOldLButtonDown))) )
|
|
IKBD_Cmd_Return_Byte (0x74); /* Left */
|
|
else if ( (IKBD_ButtonBool(Keyboard.bOldLButtonDown) && (!IKBD_ButtonBool(Keyboard.bLButtonDown))) )
|
|
IKBD_Cmd_Return_Byte (0x74|0x80);
|
|
/* Right button? */
|
|
if ( (IKBD_ButtonBool(Keyboard.bRButtonDown) && (!IKBD_ButtonBool(Keyboard.bOldRButtonDown))) )
|
|
IKBD_Cmd_Return_Byte (0x75); /* Right */
|
|
else if ( (IKBD_ButtonBool(Keyboard.bOldRButtonDown) && (!IKBD_ButtonBool(Keyboard.bRButtonDown))) )
|
|
IKBD_Cmd_Return_Byte (0x75|0x80);
|
|
}
|
|
/* Ignore bottom two bits, so return now */
|
|
return;
|
|
}
|
|
|
|
/* Check MouseAction - report position on press/release */
|
|
/* MUST do this before update relative positions as buttons get reset */
|
|
if (KeyboardProcessor.Mouse.Action&0x3)
|
|
{
|
|
/* Check for 'press'? */
|
|
if (KeyboardProcessor.Mouse.Action&0x1)
|
|
{
|
|
/* Did 'press' mouse buttons? */
|
|
if ( (IKBD_ButtonBool(Keyboard.bLButtonDown) && (!IKBD_ButtonBool(Keyboard.bOldLButtonDown))) )
|
|
{
|
|
bReportPosition = true;
|
|
KeyboardProcessor.Abs.PrevReadAbsMouseButtons &= ~0x04;
|
|
KeyboardProcessor.Abs.PrevReadAbsMouseButtons |= 0x02;
|
|
}
|
|
if ( (IKBD_ButtonBool(Keyboard.bRButtonDown) && (!IKBD_ButtonBool(Keyboard.bOldRButtonDown))) )
|
|
{
|
|
bReportPosition = true;
|
|
KeyboardProcessor.Abs.PrevReadAbsMouseButtons &= ~0x01;
|
|
KeyboardProcessor.Abs.PrevReadAbsMouseButtons |= 0x08;
|
|
}
|
|
}
|
|
/* Check for 'release'? */
|
|
if (KeyboardProcessor.Mouse.Action&0x2)
|
|
{
|
|
/* Did 'release' mouse buttons? */
|
|
if ( (IKBD_ButtonBool(Keyboard.bOldLButtonDown) && (!IKBD_ButtonBool(Keyboard.bLButtonDown))) )
|
|
{
|
|
bReportPosition = true;
|
|
KeyboardProcessor.Abs.PrevReadAbsMouseButtons &= ~0x08;
|
|
KeyboardProcessor.Abs.PrevReadAbsMouseButtons |= 0x01;
|
|
}
|
|
if ( (IKBD_ButtonBool(Keyboard.bOldRButtonDown) && (!IKBD_ButtonBool(Keyboard.bRButtonDown))) )
|
|
{
|
|
bReportPosition = true;
|
|
KeyboardProcessor.Abs.PrevReadAbsMouseButtons &= ~0x02;
|
|
KeyboardProcessor.Abs.PrevReadAbsMouseButtons |= 0x04;
|
|
}
|
|
}
|
|
|
|
/* Do need to report? */
|
|
if (bReportPosition)
|
|
{
|
|
/* Only report if mouse in absolute mode */
|
|
if (KeyboardProcessor.MouseMode==AUTOMODE_MOUSEABS)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "Report ABS on MouseAction\n");
|
|
IKBD_Cmd_ReadAbsMousePos();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Send mouse movements as cursor keys
|
|
*/
|
|
static void IKBD_SendCursorMousePacket(void)
|
|
{
|
|
int i=0;
|
|
|
|
/* Run each 'Delta' as cursor presses */
|
|
/* Limit to '10' loops as host mouse cursor might have a VERY poor quality. */
|
|
/* Eg, a single mouse movement on and ST gives delta's of '1', mostly, */
|
|
/* but host mouse might go as high as 20+! */
|
|
//fprintf ( stderr , "key %d %d %d %d %d %d\n" , KeyboardProcessor.Mouse.DeltaX , KeyboardProcessor.Mouse.DeltaY, Keyboard.bOldLButtonDown,Keyboard.bLButtonDown, Keyboard.bOldRButtonDown,Keyboard.bRButtonDown );
|
|
while ( (i<10) && ((KeyboardProcessor.Mouse.DeltaX!=0) || (KeyboardProcessor.Mouse.DeltaY!=0)
|
|
|| (!IKBD_ButtonsEqual(Keyboard.bOldLButtonDown,Keyboard.bLButtonDown)) || (!IKBD_ButtonsEqual(Keyboard.bOldRButtonDown,Keyboard.bRButtonDown))) )
|
|
{
|
|
/* Left? */
|
|
if (KeyboardProcessor.Mouse.DeltaX<0)
|
|
{
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 2 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte (75); /* Left cursor */
|
|
IKBD_Cmd_Return_Byte (75|0x80);
|
|
}
|
|
KeyboardProcessor.Mouse.DeltaX++;
|
|
}
|
|
/* Right? */
|
|
if (KeyboardProcessor.Mouse.DeltaX>0)
|
|
{
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 2 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte (77); /* Right cursor */
|
|
IKBD_Cmd_Return_Byte (77|0x80);
|
|
}
|
|
KeyboardProcessor.Mouse.DeltaX--;
|
|
}
|
|
/* Up? */
|
|
if (KeyboardProcessor.Mouse.DeltaY<0)
|
|
{
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 2 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte (72); /* Up cursor */
|
|
IKBD_Cmd_Return_Byte (72|0x80);
|
|
}
|
|
KeyboardProcessor.Mouse.DeltaY++;
|
|
}
|
|
/* Down? */
|
|
if (KeyboardProcessor.Mouse.DeltaY>0)
|
|
{
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 2 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte (80); /* Down cursor */
|
|
IKBD_Cmd_Return_Byte (80|0x80);
|
|
}
|
|
KeyboardProcessor.Mouse.DeltaY--;
|
|
}
|
|
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 2 ) )
|
|
{
|
|
/* Left button? */
|
|
if ( (IKBD_ButtonBool(Keyboard.bLButtonDown) && (!IKBD_ButtonBool(Keyboard.bOldLButtonDown))) )
|
|
IKBD_Cmd_Return_Byte (0x74); /* Left */
|
|
else if ( (IKBD_ButtonBool(Keyboard.bOldLButtonDown) && (!IKBD_ButtonBool(Keyboard.bLButtonDown))) )
|
|
IKBD_Cmd_Return_Byte (0x74|0x80);
|
|
/* Right button? */
|
|
if ( (IKBD_ButtonBool(Keyboard.bRButtonDown) && (!IKBD_ButtonBool(Keyboard.bOldRButtonDown))) )
|
|
IKBD_Cmd_Return_Byte (0x75); /* Right */
|
|
else if ( (IKBD_ButtonBool(Keyboard.bOldRButtonDown) && (!IKBD_ButtonBool(Keyboard.bRButtonDown))) )
|
|
IKBD_Cmd_Return_Byte (0x75|0x80);
|
|
}
|
|
Keyboard.bOldLButtonDown = Keyboard.bLButtonDown;
|
|
Keyboard.bOldRButtonDown = Keyboard.bRButtonDown;
|
|
|
|
/* Count, so exit if try too many times! */
|
|
i++;
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Return packets from keyboard for auto, rel mouse, joystick etc...
|
|
*/
|
|
static void IKBD_SendAutoKeyboardCommands(void)
|
|
{
|
|
/* Don't do anything until processor is first reset */
|
|
if ( bDuringResetCriticalTime )
|
|
return;
|
|
|
|
/* Read joysticks for this frame */
|
|
IKBD_GetJoystickData();
|
|
|
|
/* Check for double-clicks in maximum speed mode */
|
|
IKBD_CheckForDoubleClicks();
|
|
|
|
/* Handle Joystick/Mouse fire buttons */
|
|
IKBD_DuplicateMouseFireButtons();
|
|
|
|
/* Send any packets which are to be reported by mouse action */
|
|
IKBD_SendOnMouseAction();
|
|
|
|
/* Update internal mouse absolute position by find 'delta' of mouse movement */
|
|
IKBD_UpdateInternalMousePosition();
|
|
|
|
/* If IKBD is monitoring only joysticks, don't report other events */
|
|
if ( KeyboardProcessor.JoystickMode == AUTOMODE_JOYSTICK_MONITORING )
|
|
{
|
|
IKBD_SendAutoJoysticksMonitoring();
|
|
return;
|
|
}
|
|
|
|
/* Send automatic joystick packets */
|
|
if (KeyboardProcessor.JoystickMode==AUTOMODE_JOYSTICK)
|
|
IKBD_SendAutoJoysticks();
|
|
/* Send automatic relative mouse positions(absolute are not send automatically) */
|
|
if (KeyboardProcessor.MouseMode==AUTOMODE_MOUSEREL)
|
|
IKBD_SendRelMousePacket();
|
|
/* Send cursor key directions for movements */
|
|
else if (KeyboardProcessor.MouseMode==AUTOMODE_MOUSECURSOR)
|
|
IKBD_SendCursorMousePacket();
|
|
|
|
/* Store buttons for next time around */
|
|
Keyboard.bOldLButtonDown = Keyboard.bLButtonDown;
|
|
Keyboard.bOldRButtonDown = Keyboard.bRButtonDown;
|
|
|
|
/* Send joystick button '2' as 'Space bar' key - MUST do here so does not get mixed up in middle of joystick packets! */
|
|
if (JoystickSpaceBar)
|
|
{
|
|
/* As we simulating space bar? */
|
|
if (JoystickSpaceBar==JOYSTICK_SPACE_DOWN)
|
|
{
|
|
IKBD_PressSTKey(57, true); /* Press */
|
|
JoystickSpaceBar = JOYSTICK_SPACE_UP;
|
|
}
|
|
else //if (JoystickSpaceBar==JOYSTICK_SPACE_UP) {
|
|
{
|
|
IKBD_PressSTKey(57, false); /* Release */
|
|
JoystickSpaceBar = false; /* Complete */
|
|
}
|
|
}
|
|
|
|
|
|
/* If we're executing a custom IKBD program, call it to process the key/mouse/joystick event */
|
|
if ( IKBD_ExeMode && pIKBD_CustomCodeHandler_Read )
|
|
(*pIKBD_CustomCodeHandler_Read) ();
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* When press/release key under host OS, execute this function.
|
|
*/
|
|
void IKBD_PressSTKey(Uint8 ScanCode, bool bPress)
|
|
{
|
|
/* If IKBD is monitoring only joysticks, don't report key */
|
|
if ( KeyboardProcessor.JoystickMode == AUTOMODE_JOYSTICK_MONITORING )
|
|
return;
|
|
|
|
/* Store the state of each ST scancode : 1=pressed 0=released */
|
|
if ( bPress ) ScanCodeState[ ScanCode & 0x7f ] = 1;
|
|
else ScanCodeState[ ScanCode & 0x7f ] = 0;
|
|
|
|
if (!bPress)
|
|
ScanCode |= 0x80; /* Set top bit if released key */
|
|
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 1 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte (ScanCode); /* Add to the IKBD's output buffer */
|
|
}
|
|
|
|
/* If we're executing a custom IKBD program, call it to process the key event */
|
|
if ( IKBD_ExeMode && pIKBD_CustomCodeHandler_Read )
|
|
(*pIKBD_CustomCodeHandler_Read) ();
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* This function is called regularly to automatically send keyboard, mouse
|
|
* and joystick updates.
|
|
*/
|
|
void IKBD_InterruptHandler_AutoSend(void)
|
|
{
|
|
/* Handle user events and other messages, (like quit message) */
|
|
Main_EventHandler();
|
|
|
|
/* Remove this interrupt from list and re-order.
|
|
* (needs to be done after UI event handling so
|
|
* that snapshots saved from UI and restored from
|
|
* command line don't miss the AUTOSEND interrupt)
|
|
*/
|
|
CycInt_AcknowledgeInterrupt();
|
|
|
|
/* Did user try to quit? */
|
|
if (bQuitProgram)
|
|
{
|
|
/* Pass NULL interrupt function to quit cleanly */
|
|
CycInt_AddAbsoluteInterrupt(4, INT_CPU_CYCLE, INTERRUPT_NULL);
|
|
/* Assure that CPU core shuts down */
|
|
M68000_SetSpecial(SPCFLAG_BRK);
|
|
return;
|
|
}
|
|
|
|
/* Trigger this auto-update function again after a while */
|
|
CycInt_AddRelativeInterrupt(Keyboard.AutoSendCycles, INT_CPU_CYCLE, INTERRUPT_IKBD_AUTOSEND);
|
|
|
|
/* We don't send keyboard data automatically within the first few
|
|
* VBLs to avoid that TOS gets confused during its boot time */
|
|
if (nVBLs > 20)
|
|
{
|
|
/* Send automatic keyboard packets for mouse, joysticks etc... */
|
|
IKBD_SendAutoKeyboardCommands();
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* On ST if disable Mouse AND Joystick with a set time of a RESET command they are
|
|
* actually turned back on! (A number of games do this so can get mouse and joystick
|
|
* packets at the same time)
|
|
*/
|
|
static void IKBD_CheckResetDisableBug(void)
|
|
{
|
|
/* Have disabled BOTH mouse and joystick? */
|
|
if (bMouseDisabled && bJoystickDisabled)
|
|
{
|
|
/* And in critical time? */
|
|
if (bDuringResetCriticalTime)
|
|
{
|
|
/* Emulate relative mouse and joystick reports being turned back on */
|
|
KeyboardProcessor.MouseMode = AUTOMODE_MOUSEREL;
|
|
KeyboardProcessor.JoystickMode = AUTOMODE_JOYSTICK;
|
|
bBothMouseAndJoy = true;
|
|
|
|
LOG_TRACE(TRACE_IKBD_ALL, "ikbd cancel commands 0x12 and 0x1a received during reset,"
|
|
" enabling joystick and mouse reporting at the same time\n" );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* When a byte is received by the IKBD, it is added to a small 8 byte buffer.
|
|
* If the first byte is a valid command, we wait for additionnal bytes if needed
|
|
* and then we execute the command's handler.
|
|
* If the first byte is not a valid command or after a successful command, we
|
|
* empty the input buffer (extra bytes, if any, are lost)
|
|
* If the input buffer is full when a new byte is received, the new byte is lost.
|
|
*/
|
|
static void IKBD_RunKeyboardCommand(Uint8 aciabyte)
|
|
{
|
|
int i=0;
|
|
|
|
/* Write into our keyboard input buffer if it's not full yet */
|
|
if ( Keyboard.nBytesInInputBuffer < SIZE_KEYBOARDINPUT_BUFFER )
|
|
Keyboard.InputBuffer[Keyboard.nBytesInInputBuffer++] = aciabyte;
|
|
|
|
/* Now check bytes to see if we have a valid/in-valid command string set */
|
|
while (KeyboardCommands[i].Command!=0xff)
|
|
{
|
|
/* Found command? */
|
|
if (KeyboardCommands[i].Command==Keyboard.InputBuffer[0])
|
|
{
|
|
/* If the command is complete (with its possible parameters) we can execute it */
|
|
/* Else, we wait for the next bytes until the command is complete */
|
|
if (KeyboardCommands[i].NumParameters==Keyboard.nBytesInInputBuffer)
|
|
{
|
|
/* Any new valid command will unpause the output (if command 0x13 was used) */
|
|
Keyboard.PauseOutput = false;
|
|
|
|
CALL_VAR(KeyboardCommands[i].pCallFunction);
|
|
Keyboard.nBytesInInputBuffer = 0; /* Clear input buffer after processing a command */
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
/* Command not known, reset buffer(IKBD assumes a NOP) */
|
|
Keyboard.nBytesInInputBuffer = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* List of keyboard commands handled by the standard IKBD's ROM. */
|
|
/* Each IKBD's command is emulated to get the same result as if we were */
|
|
/* running a full HD6301 emulation. */
|
|
/************************************************************************/
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* RESET
|
|
*
|
|
* 0x80
|
|
* 0x01
|
|
*
|
|
* Performs self test and checks for stuck (closed) keys, if OK returns
|
|
* IKBD_ROM_VERSION (0xF1). Otherwise returns break codes for keys (not emulated).
|
|
*/
|
|
static void IKBD_Cmd_Reset(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_Reset VBLs=%i framecyc=%i\n",
|
|
nVBLs, Cycles_GetCounter(CYCLES_COUNTER_VIDEO));
|
|
|
|
/* Check that 0x01 was received after 0x80 */
|
|
if (Keyboard.InputBuffer[1] == 0x01)
|
|
{
|
|
IKBD_Boot_ROM ( false );
|
|
}
|
|
/* else if not 0x80,0x01 just ignore */
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* SET MOUSE BUTTON ACTION
|
|
*
|
|
* 0x07
|
|
* %00000mss ; mouse button action
|
|
* ; (m is presumed =1 when in MOUSE KEYCODE mode)
|
|
* ; mss=0xy, mouse button press or release causes mouse
|
|
* ; position report
|
|
* ; where y=1, mouse key press causes absolute report
|
|
* ; and x=1, mouse key release causes absolute report
|
|
* ; mss=100, mouse buttons act like keys
|
|
*/
|
|
static void IKBD_Cmd_MouseAction(void)
|
|
{
|
|
KeyboardProcessor.Mouse.Action = Keyboard.InputBuffer[1];
|
|
KeyboardProcessor.Abs.PrevReadAbsMouseButtons = ABS_PREVBUTTONS;
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_MouseAction %d\n",
|
|
(unsigned int)KeyboardProcessor.Mouse.Action);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* SET RELATIVE MOUSE POSITION REPORTING
|
|
*
|
|
* 0x08
|
|
*/
|
|
static void IKBD_Cmd_RelMouseMode(void)
|
|
{
|
|
KeyboardProcessor.MouseMode = AUTOMODE_MOUSEREL;
|
|
|
|
/* Some games (like Barbarian by Psygnosis) enable both, mouse and
|
|
* joystick directly after a reset. This causes the IKBD to send both
|
|
* type of packets. To emulate this feature, we've got to remember
|
|
* that the mouse has been enabled during reset. */
|
|
if (bDuringResetCriticalTime)
|
|
bMouseEnabledDuringReset = true;
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_RelMouseMode\n");
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* SET ABSOLUTE MOUSE POSITIONING
|
|
*
|
|
* 0x09
|
|
* XMSB ;X maximum (in scaled mouse clicks)
|
|
* XLSB
|
|
* YMSB ;Y maximum (in scaled mouse clicks)
|
|
* YLSB
|
|
*/
|
|
static void IKBD_Cmd_AbsMouseMode(void)
|
|
{
|
|
/* These maximums are 'inclusive' */
|
|
KeyboardProcessor.MouseMode = AUTOMODE_MOUSEABS;
|
|
KeyboardProcessor.Abs.MaxX = (((unsigned int)Keyboard.InputBuffer[1])<<8) | (unsigned int)Keyboard.InputBuffer[2];
|
|
KeyboardProcessor.Abs.MaxY = (((unsigned int)Keyboard.InputBuffer[3])<<8) | (unsigned int)Keyboard.InputBuffer[4];
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_AbsMouseMode %d,%d\n",
|
|
KeyboardProcessor.Abs.MaxX, KeyboardProcessor.Abs.MaxY);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* SET MOUSE KEYCODE MODE
|
|
*
|
|
* 0x0A
|
|
* deltax ; distance in X clicks to return (LEFT) or (RIGHT)
|
|
* deltay ; distance in Y clicks to return (UP) or (DOWN)
|
|
*/
|
|
static void IKBD_Cmd_MouseCursorKeycodes(void)
|
|
{
|
|
KeyboardProcessor.MouseMode = AUTOMODE_MOUSECURSOR;
|
|
KeyboardProcessor.Mouse.KeyCodeDeltaX = Keyboard.InputBuffer[1];
|
|
KeyboardProcessor.Mouse.KeyCodeDeltaY = Keyboard.InputBuffer[2];
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_MouseCursorKeycodes %d,%d\n",
|
|
(int)KeyboardProcessor.Mouse.KeyCodeDeltaX,
|
|
(int)KeyboardProcessor.Mouse.KeyCodeDeltaY);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* SET MOUSE THRESHOLD
|
|
*
|
|
* 0x0B
|
|
* X ; x threshold in mouse ticks (positive integers)
|
|
* Y ; y threshold in mouse ticks (positive integers)
|
|
*/
|
|
static void IKBD_Cmd_SetMouseThreshold(void)
|
|
{
|
|
KeyboardProcessor.Mouse.XThreshold = (unsigned int)Keyboard.InputBuffer[1];
|
|
KeyboardProcessor.Mouse.YThreshold = (unsigned int)Keyboard.InputBuffer[2];
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetMouseThreshold %d,%d\n",
|
|
KeyboardProcessor.Mouse.XThreshold, KeyboardProcessor.Mouse.YThreshold);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* SET MOUSE SCALE
|
|
*
|
|
* 0x0C
|
|
* X ; horizontal mouse ticks per internel X
|
|
* Y ; vertical mouse ticks per internel Y
|
|
*/
|
|
static void IKBD_Cmd_SetMouseScale(void)
|
|
{
|
|
KeyboardProcessor.Mouse.XScale = (unsigned int)Keyboard.InputBuffer[1];
|
|
KeyboardProcessor.Mouse.YScale = (unsigned int)Keyboard.InputBuffer[2];
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetMouseScale %d,%d\n",
|
|
KeyboardProcessor.Mouse.XScale, KeyboardProcessor.Mouse.YScale);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* INTERROGATE MOUSE POSITION
|
|
*
|
|
* 0x0D
|
|
* Returns: 0xF7 ; absolute mouse position header
|
|
* BUTTONS
|
|
* 0000dcba
|
|
* where a is right button down since last interrogation
|
|
* b is right button up since last
|
|
* c is left button down since last
|
|
* d is left button up since last
|
|
* XMSB ; X coordinate
|
|
* XLSB
|
|
* YMSB ; Y coordinate
|
|
* YLSB
|
|
*/
|
|
static void IKBD_Cmd_ReadAbsMousePos(void)
|
|
{
|
|
Uint8 Buttons,PrevButtons;
|
|
|
|
/* Test buttons */
|
|
Buttons = 0;
|
|
/* Set buttons to show if up/down */
|
|
if (Keyboard.bRButtonDown)
|
|
Buttons |= 0x01;
|
|
else
|
|
Buttons |= 0x02;
|
|
if (Keyboard.bLButtonDown)
|
|
Buttons |= 0x04;
|
|
else
|
|
Buttons |= 0x08;
|
|
/* Mask off it didn't send last time */
|
|
PrevButtons = KeyboardProcessor.Abs.PrevReadAbsMouseButtons;
|
|
KeyboardProcessor.Abs.PrevReadAbsMouseButtons = Buttons;
|
|
Buttons &= ~PrevButtons;
|
|
|
|
/* And send packet */
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 6 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte_Delay (0xf7, 18000-ACIA_CYCLES);
|
|
IKBD_Cmd_Return_Byte (Buttons);
|
|
IKBD_Cmd_Return_Byte ((unsigned int)KeyboardProcessor.Abs.X>>8);
|
|
IKBD_Cmd_Return_Byte ((unsigned int)KeyboardProcessor.Abs.X&0xff);
|
|
IKBD_Cmd_Return_Byte ((unsigned int)KeyboardProcessor.Abs.Y>>8);
|
|
IKBD_Cmd_Return_Byte ((unsigned int)KeyboardProcessor.Abs.Y&0xff);
|
|
}
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReadAbsMousePos %d,%d 0x%X\n",
|
|
KeyboardProcessor.Abs.X, KeyboardProcessor.Abs.Y, Buttons);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* LOAD MOUSE POSITION
|
|
*
|
|
* 0x0E
|
|
* 0x00 ; filler
|
|
* XMSB ; X coordinate
|
|
* XLSB ; (in scaled coordinate system)
|
|
* YMSB ; Y coordinate
|
|
* YLSB
|
|
*/
|
|
static void IKBD_Cmd_SetInternalMousePos(void)
|
|
{
|
|
/* Setting these do not clip internal position(this happens on next update) */
|
|
KeyboardProcessor.Abs.X = (((unsigned int)Keyboard.InputBuffer[2])<<8) | (unsigned int)Keyboard.InputBuffer[3];
|
|
KeyboardProcessor.Abs.Y = (((unsigned int)Keyboard.InputBuffer[4])<<8) | (unsigned int)Keyboard.InputBuffer[5];
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetInternalMousePos %d,%d\n",
|
|
KeyboardProcessor.Abs.X, KeyboardProcessor.Abs.Y);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* SET Y=0 AT BOTTOM
|
|
*
|
|
* 0x0F
|
|
*/
|
|
static void IKBD_Cmd_SetYAxisDown(void)
|
|
{
|
|
KeyboardProcessor.Mouse.YAxis = -1;
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetYAxisDown\n");
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* SET Y=0 AT TOP
|
|
*
|
|
* 0x10
|
|
*/
|
|
static void IKBD_Cmd_SetYAxisUp(void)
|
|
{
|
|
KeyboardProcessor.Mouse.YAxis = 1;
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetYAxisUp\n");
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* RESUME
|
|
*
|
|
* Any command received by the IKBD will also resume the output if it was
|
|
* paused by command 0x13, so this command is redundant.
|
|
*
|
|
* 0x11
|
|
*/
|
|
static void IKBD_Cmd_StartKeyboardTransfer(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_StartKeyboardTransfer\n");
|
|
Keyboard.PauseOutput = false;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* DISABLE MOUSE
|
|
*
|
|
* 0x12
|
|
*/
|
|
static void IKBD_Cmd_TurnMouseOff(void)
|
|
{
|
|
KeyboardProcessor.MouseMode = AUTOMODE_OFF;
|
|
bMouseDisabled = true;
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_TurnMouseOff\n");
|
|
|
|
IKBD_CheckResetDisableBug();
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* PAUSE OUTPUT
|
|
*
|
|
* 0x13
|
|
*/
|
|
static void IKBD_Cmd_StopKeyboardTransfer(void)
|
|
{
|
|
if (bDuringResetCriticalTime)
|
|
{
|
|
/* Required for the loader of 'Just Bugging' by ACF */
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_StopKeyboardTransfer ignored during ikbd reset\n");
|
|
return;
|
|
}
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_StopKeyboardTransfer\n");
|
|
Keyboard.PauseOutput = true;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* SET JOYSTICK EVENT REPORTING
|
|
*
|
|
* 0x14
|
|
*/
|
|
static void IKBD_Cmd_ReturnJoystickAuto(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReturnJoystickAuto\n");
|
|
|
|
KeyboardProcessor.JoystickMode = AUTOMODE_JOYSTICK;
|
|
KeyboardProcessor.MouseMode = AUTOMODE_OFF;
|
|
|
|
/* If mouse was also enabled within time of a reset (0x08 command) it isn't disabled now!
|
|
* (used by the game Barbarian 1 by Psygnosis for example) */
|
|
if ( bDuringResetCriticalTime && bMouseEnabledDuringReset )
|
|
{
|
|
KeyboardProcessor.MouseMode = AUTOMODE_MOUSEREL;
|
|
bBothMouseAndJoy = true;
|
|
LOG_TRACE(TRACE_IKBD_ALL, "ikbd commands 0x08 and 0x14 received during reset,"
|
|
" enabling joystick and mouse reporting at the same time\n" );
|
|
}
|
|
/* If mouse was disabled during the reset (0x12 command) it is enabled again
|
|
* (used by the game Hammerfist for example) */
|
|
else if ( bDuringResetCriticalTime && bMouseDisabled )
|
|
{
|
|
KeyboardProcessor.MouseMode = AUTOMODE_MOUSEREL;
|
|
bBothMouseAndJoy = true;
|
|
LOG_TRACE(TRACE_IKBD_ALL, "ikbd commands 0x12 and 0x14 received during reset,"
|
|
" enabling joystick and mouse reporting at the same time\n" );
|
|
}
|
|
|
|
/* This command resets the internally previously stored joystick states */
|
|
KeyboardProcessor.Joy.PrevJoyData[0] = KeyboardProcessor.Joy.PrevJoyData[1] = 0;
|
|
|
|
/* This is a hack for the STE Utopos (=> v1.50) and Falcon Double Bubble
|
|
* 2000 games. They expect the joystick data to be sent within a certain
|
|
* amount of time after this command, without checking the ACIA control
|
|
* register first.
|
|
*/
|
|
IKBD_GetJoystickData();
|
|
IKBD_SendAutoJoysticks();
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* SET JOYSTICK INTERROGATION MODE
|
|
*
|
|
* 0x15
|
|
*/
|
|
static void IKBD_Cmd_StopJoystick(void)
|
|
{
|
|
KeyboardProcessor.JoystickMode = AUTOMODE_OFF;
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_StopJoystick\n");
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* JOYSTICK INTERROGATE
|
|
*
|
|
* 0x16
|
|
*/
|
|
static void IKBD_Cmd_ReturnJoystick(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReturnJoystick\n");
|
|
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 3 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte_Delay ( 0xFD , IKBD_Delay_Random ( 7500 , 10000 ) );
|
|
IKBD_Cmd_Return_Byte (Joy_GetStickData(0));
|
|
IKBD_Cmd_Return_Byte (Joy_GetStickData(1));
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* SET JOYSTICK MONITORING
|
|
*
|
|
* 0x17
|
|
* rate ; time between samples in hundreths of a second
|
|
* Returns: (in packets of two as long as in mode)
|
|
* %000000xy where y is JOYSTICK1 Fire button
|
|
* and x is JOYSTICK0 Fire button
|
|
* %nnnnmmmm where m is JOYSTICK1 state
|
|
* and n is JOYSTICK0 state
|
|
*
|
|
* TODO : we use a fixed 8 MHz clock to convert rate in 1/100th of sec into cycles.
|
|
* This should be replaced by using MachineClocks.CPU_Freq.
|
|
*/
|
|
static void IKBD_Cmd_SetJoystickMonitoring(void)
|
|
{
|
|
int Rate;
|
|
int Cycles;
|
|
|
|
Rate = (unsigned int)Keyboard.InputBuffer[1];
|
|
|
|
KeyboardProcessor.JoystickMode = AUTOMODE_JOYSTICK_MONITORING;
|
|
KeyboardProcessor.MouseMode = AUTOMODE_OFF;
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetJoystickMonitoring %d\n" , Rate );
|
|
|
|
if ( Rate == 0 )
|
|
Rate = 1;
|
|
|
|
Cycles = 8021247 * Rate / 100;
|
|
Cycles <<= nCpuFreqShift; /* Compensate for x2 or x4 cpu speed */
|
|
CycInt_AddRelativeInterrupt ( Cycles, INT_CPU_CYCLE, INTERRUPT_IKBD_AUTOSEND );
|
|
Keyboard.AutoSendCycles = Cycles;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* SET FIRE BUTTON MONITORING
|
|
*
|
|
* 0x18
|
|
* Returns: (as long as in mode)
|
|
* %bbbbbbbb ; state of the JOYSTICK1 fire button packed
|
|
* ; 8 bits per byte, the first sample if the MSB
|
|
*/
|
|
static void IKBD_Cmd_SetJoystickFireDuration(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetJoystickFireDuration (not implemented)\n");
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* SET JOYSTICK KEYCODE MODE
|
|
*
|
|
* 0x19
|
|
* RX ; length of time (in tenths of seconds) until
|
|
* ; horizontal velocity breakpoint is reached
|
|
* RY ; length of time (in tenths of seconds) until
|
|
* ; vertical velocity breakpoint is reached
|
|
* TX ; length (in tenths of seconds) of joystick closure
|
|
* ; until horizontal cursor key is generated before RX
|
|
* ; has elapsed
|
|
* TY ; length (in tenths of seconds) of joystick closure
|
|
* ; until vertical cursor key is generated before RY
|
|
* ; has elapsed
|
|
* VX ; length (in tenths of seconds) of joystick closure
|
|
* ; until horizontal cursor keystokes are generated after RX
|
|
* ; has elapsed
|
|
* VY ; length (in tenths of seconds) of joystick closure
|
|
* ; until vertical cursor keystokes are generated after RY
|
|
* ; has elapsed
|
|
*/
|
|
static void IKBD_Cmd_SetCursorForJoystick(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_SetCursorForJoystick (not implemented)\n");
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* DISABLE JOYSTICKS
|
|
*
|
|
* 0x1A
|
|
*/
|
|
static void IKBD_Cmd_DisableJoysticks(void)
|
|
{
|
|
KeyboardProcessor.JoystickMode = AUTOMODE_OFF;
|
|
bJoystickDisabled = true;
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_DisableJoysticks\n");
|
|
|
|
IKBD_CheckResetDisableBug();
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* TIME-OF-DAY CLOCK SET
|
|
*
|
|
* 0x1B
|
|
* YY ; year (2 least significant digits)
|
|
* MM ; month
|
|
* DD ; day
|
|
* hh ; hour
|
|
* mm ; minute
|
|
* ss ; second
|
|
*
|
|
* All bytes are stored in BCD format. If a byte is not in BCD, we ignore it
|
|
* but we process the rest of the bytes.
|
|
* Note that the IKBD doesn't check that month/day/hour/second/minute are in
|
|
* their correct range, just that they're BCD encoded (so you can store 0x30 in hour
|
|
* for example, see IKBD_UpdateClockOnVBL())
|
|
*/
|
|
static void IKBD_Cmd_SetClock(void)
|
|
{
|
|
int i;
|
|
Uint8 val;
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS,
|
|
"IKBD_Cmd_SetClock: %02x %02x %02x %02x %02x %02x\n",
|
|
Keyboard.InputBuffer[1], Keyboard.InputBuffer[2],
|
|
Keyboard.InputBuffer[3], Keyboard.InputBuffer[4],
|
|
Keyboard.InputBuffer[5], Keyboard.InputBuffer[6]);
|
|
|
|
for ( i=1 ; i<=6 ; i++ )
|
|
{
|
|
val = Keyboard.InputBuffer[ i ];
|
|
if ( IKBD_BCD_Check ( val ) ) /* Check if valid BCD, else ignore */
|
|
pIKBD->Clock[ i-1 ] = val; /* Store new value */
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* INTERROGATE TIME-OF-DAY CLOCK
|
|
*
|
|
* 0x1C
|
|
* Returns:
|
|
* 0xFC ; time-of-day event header
|
|
* YY ; year (2 least significant digits)
|
|
* MM ; month
|
|
* DD ; day
|
|
* hh ; hour
|
|
* mm ; minute
|
|
* ss ; second
|
|
*
|
|
* All bytes are stored/returned in BCD format.
|
|
* Date/Time is updated in IKBD_UpdateClockOnVBL()
|
|
*/
|
|
static void IKBD_Cmd_ReadClock(void)
|
|
{
|
|
int i;
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS,
|
|
"IKBD_Cmd_ReadClock: %02x %02x %02x %02x %02x %02x\n",
|
|
pIKBD->Clock[ 0 ] ,pIKBD->Clock[ 1 ] , pIKBD->Clock[ 2 ] ,
|
|
pIKBD->Clock[ 3 ] , pIKBD->Clock[ 4 ] , pIKBD->Clock[ 5 ] );
|
|
|
|
/* Return packet header */
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 7 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte_Delay ( 0xFC , IKBD_Delay_Random ( 7000 , 7500 ) );
|
|
|
|
/* Return the 6 clock bytes */
|
|
for ( i=0 ; i<6 ; i++ )
|
|
IKBD_Cmd_Return_Byte ( pIKBD->Clock[ i ] );
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* MEMORY LOAD
|
|
*
|
|
* 0x20
|
|
* ADRMSB ; address in controller
|
|
* ADRLSB ; memory to be loaded
|
|
* NUM ; number of bytes (0-128)
|
|
* { data }
|
|
*/
|
|
static void IKBD_Cmd_LoadMemory(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_LoadMemory addr 0x%x count %d\n",
|
|
(Keyboard.InputBuffer[1] << 8) + Keyboard.InputBuffer[2], Keyboard.InputBuffer[3]);
|
|
|
|
MemoryLoadNbBytesTotal = Keyboard.InputBuffer[3];
|
|
MemoryLoadNbBytesLeft = MemoryLoadNbBytesTotal;
|
|
crc32_reset ( &MemoryLoadCrc );
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* MEMORY READ
|
|
*
|
|
* 0x21
|
|
* ADRMSB ; address in controller
|
|
* ADRLSB ; memory to be read
|
|
* Returns:
|
|
* 0xF6 ; status header
|
|
* 0x20 ; memory access
|
|
* { data } ; 6 data bytes starting at ADR
|
|
*
|
|
* NOTE : This function requires to handle the IKBD's RAM, which is only
|
|
* possible when emulating a real HD6301 CPU. For now, we only return
|
|
* the correct header and 6 empty bytes.
|
|
*/
|
|
static void IKBD_Cmd_ReadMemory(void)
|
|
{
|
|
int i;
|
|
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReadMemory (not implemented)\n");
|
|
|
|
/* Return packet header */
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 8 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte_Delay ( 0xF6 , IKBD_Delay_Random ( 7000 , 7500 ) );
|
|
IKBD_Cmd_Return_Byte ( 0x20 );
|
|
|
|
/* Return 6 empty bytes */
|
|
for ( i=0 ; i<6 ; i++ )
|
|
IKBD_Cmd_Return_Byte ( 0x00 );
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* CONTROLLER EXECUTE
|
|
*
|
|
* 0x22
|
|
* ADRMSB ; address of subroutine in
|
|
* ADRLSB ; controller memory to be called
|
|
*/
|
|
static void IKBD_Cmd_Execute(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_Execute addr 0x%x\n",
|
|
(Keyboard.InputBuffer[1] << 8) + Keyboard.InputBuffer[2]);
|
|
|
|
if ( pIKBD_CustomCodeHandler_Write )
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_EXEC, "ikbd execute addr 0x%x using custom handler\n",
|
|
(Keyboard.InputBuffer[1] << 8) + Keyboard.InputBuffer[2]);
|
|
|
|
IKBD_ExeMode = true; /* turn 6301's custom mode ON */
|
|
}
|
|
else /* unknown code uploaded to ikbd RAM */
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_EXEC, "ikbd execute addr 0x%x ignored, no custom handler found\n",
|
|
(Keyboard.InputBuffer[1] << 8) + Keyboard.InputBuffer[2]);
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* REPORT MOUSE BUTTON ACTION
|
|
*
|
|
* 0x87
|
|
*/
|
|
static void IKBD_Cmd_ReportMouseAction(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportMouseAction\n");
|
|
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 8 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte_Delay ( 0xF6 , IKBD_Delay_Random ( 7000 , 7500 ) );
|
|
IKBD_Cmd_Return_Byte (7);
|
|
IKBD_Cmd_Return_Byte (KeyboardProcessor.Mouse.Action);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* REPORT MOUSE MODE
|
|
*
|
|
* 0x88 or 0x89 or 0x8A
|
|
*/
|
|
static void IKBD_Cmd_ReportMouseMode(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportMouseMode\n");
|
|
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 8 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte_Delay ( 0xF6 , IKBD_Delay_Random ( 7000 , 7500 ) );
|
|
switch (KeyboardProcessor.MouseMode)
|
|
{
|
|
case AUTOMODE_MOUSEREL:
|
|
IKBD_Cmd_Return_Byte (8);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
break;
|
|
case AUTOMODE_MOUSEABS:
|
|
IKBD_Cmd_Return_Byte (9);
|
|
IKBD_Cmd_Return_Byte (KeyboardProcessor.Abs.MaxX >> 8);
|
|
IKBD_Cmd_Return_Byte (KeyboardProcessor.Abs.MaxX);
|
|
IKBD_Cmd_Return_Byte (KeyboardProcessor.Abs.MaxY >> 8);
|
|
IKBD_Cmd_Return_Byte (KeyboardProcessor.Abs.MaxY);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
break;
|
|
case AUTOMODE_MOUSECURSOR:
|
|
IKBD_Cmd_Return_Byte (10);
|
|
IKBD_Cmd_Return_Byte (KeyboardProcessor.Mouse.KeyCodeDeltaX);
|
|
IKBD_Cmd_Return_Byte (KeyboardProcessor.Mouse.KeyCodeDeltaY);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* REPORT MOUSE THRESHOLD
|
|
*
|
|
* 0x8B
|
|
*/
|
|
static void IKBD_Cmd_ReportMouseThreshold(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportMouseThreshold\n");
|
|
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 8 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte_Delay ( 0xF6 , IKBD_Delay_Random ( 7000 , 7500 ) );
|
|
IKBD_Cmd_Return_Byte (0x0B);
|
|
IKBD_Cmd_Return_Byte (KeyboardProcessor.Mouse.XThreshold);
|
|
IKBD_Cmd_Return_Byte (KeyboardProcessor.Mouse.YThreshold);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* REPORT MOUSE SCALE
|
|
*
|
|
* 0x8C
|
|
*/
|
|
static void IKBD_Cmd_ReportMouseScale(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportMouseScale\n");
|
|
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 8 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte_Delay ( 0xF6 , IKBD_Delay_Random ( 7000 , 7500 ) );
|
|
IKBD_Cmd_Return_Byte (0x0C);
|
|
IKBD_Cmd_Return_Byte (KeyboardProcessor.Mouse.XScale);
|
|
IKBD_Cmd_Return_Byte (KeyboardProcessor.Mouse.YScale);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* REPORT MOUSE VERTICAL COORDINATES
|
|
*
|
|
* 0x8F and 0x90
|
|
*/
|
|
static void IKBD_Cmd_ReportMouseVertical(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportMouseVertical\n");
|
|
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 8 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte_Delay ( 0xF6 , IKBD_Delay_Random ( 7000 , 7500 ) );
|
|
if (KeyboardProcessor.Mouse.YAxis == -1)
|
|
IKBD_Cmd_Return_Byte (0x0F);
|
|
else
|
|
IKBD_Cmd_Return_Byte (0x10);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* REPORT MOUSE AVAILABILITY
|
|
*
|
|
* 0x92
|
|
*/
|
|
static void IKBD_Cmd_ReportMouseAvailability(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportMouseAvailability\n");
|
|
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 8 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte_Delay ( 0xF6 , IKBD_Delay_Random ( 7000 , 7500 ) );
|
|
if (KeyboardProcessor.MouseMode == AUTOMODE_OFF)
|
|
IKBD_Cmd_Return_Byte (0x12);
|
|
else
|
|
IKBD_Cmd_Return_Byte (0x00);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* REPORT JOYSTICK MODE
|
|
*
|
|
* 0x94 or 0x95 or 0x99
|
|
*/
|
|
static void IKBD_Cmd_ReportJoystickMode(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportJoystickMode\n");
|
|
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 8 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte_Delay ( 0xF6 , IKBD_Delay_Random ( 7000 , 7500 ) );
|
|
switch (KeyboardProcessor.JoystickMode)
|
|
{
|
|
case AUTOMODE_JOYSTICK:
|
|
IKBD_Cmd_Return_Byte (0x14);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
break;
|
|
default: /* TODO: Joystick keycodes mode not supported yet! */
|
|
IKBD_Cmd_Return_Byte (0x15);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* REPORT JOYSTICK AVAILABILITY
|
|
*
|
|
* 0x9A
|
|
*/
|
|
static void IKBD_Cmd_ReportJoystickAvailability(void)
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_CMDS, "IKBD_Cmd_ReportJoystickAvailability\n");
|
|
|
|
if ( IKBD_OutputBuffer_CheckFreeCount ( 8 ) )
|
|
{
|
|
IKBD_Cmd_Return_Byte_Delay ( 0xF6 , IKBD_Delay_Random ( 7000 , 7500 ) );
|
|
if (KeyboardProcessor.JoystickMode == AUTOMODE_OFF)
|
|
IKBD_Cmd_Return_Byte (0x1A);
|
|
else
|
|
IKBD_Cmd_Return_Byte (0x00);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
IKBD_Cmd_Return_Byte (0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/************************************************************************/
|
|
/* End of the IKBD's commands emulation. */
|
|
/************************************************************************/
|
|
|
|
|
|
|
|
|
|
/*************************************************************************/
|
|
/**
|
|
* Below part is for emulating custom 6301 program sent to the IKBD's RAM
|
|
* Specific read/write functions for each demo/game should be added here,
|
|
* after being defined in the CustomCodeDefinitions[] array.
|
|
*
|
|
* The 6301 has 256 bytes of RAM, but only 128 bytes are available to
|
|
* put a program (from $80 to $ff).
|
|
*
|
|
* Executing a program in the 6301 is a 2 steps process :
|
|
* 1) a very small program is sent to the RAM using the 0x20 command.
|
|
* This is often loaded at address $b0.
|
|
* This program will stop interruptions in the 6301 and will accept
|
|
* a second small program that will relocate itself to $80.
|
|
* 2) the relocated program at address $80 will accept a third (main)
|
|
* program and will execute it once reception is complete.
|
|
*
|
|
* Writes during step 1 are handled with the ExeBootHandler matching the
|
|
* LoadMemory CRC.
|
|
* ExeBootHandler will compute a 2nd CRC for the writes corresponding to
|
|
* the 2nd and 3rd programs sent to the 6301's RAM.
|
|
*
|
|
* If a match is found for this 2nd CRC, we will override default IKBD's behaviour
|
|
* for reading/writing to $fffc02 with ExeMainHandler_Read / ExeMainHandler_Write
|
|
* (once the Execute command 0x22 is received).
|
|
*
|
|
* When using custom program (ExeMode==true), we must ignore all keyboard/mouse/joystick
|
|
* events sent to IKBD_Cmd_Return_Byte . Only our functions can add bytes
|
|
* to the keyboard buffer.
|
|
*
|
|
* To exit 6301's execution mode, we can use the 68000 'reset' instruction.
|
|
* Some 6301's programs also handle a write to $fffc02 as an exit signal.
|
|
*/
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Handle writes to $fffc02 when loading bytes in the IKBD's RAM.
|
|
* We compute a CRC of the bytes that are sent until MemoryLoadNbBytesLeft
|
|
* reaches 0.
|
|
* When all bytes are loaded, we look for a matching CRC ; if found, we
|
|
* use the ExeBootHandler defined for this CRC to process the next writes
|
|
* that will occur in $fffc02.
|
|
* LoadMemory is often used to load a small boot code into the 6301's RAM.
|
|
* This small program will be executed later using the command 0x22.
|
|
*/
|
|
|
|
static void IKBD_LoadMemoryByte ( Uint8 aciabyte )
|
|
{
|
|
unsigned int i;
|
|
|
|
/* Write received bytes to a file for debug */
|
|
// FILE *f = fopen ( "/tmp/ikbd_loadmemory.dump" , "ab" ) ; fprintf ( f , "%c" , aciabyte ) ; fclose ( f );
|
|
|
|
crc32_add_byte ( &MemoryLoadCrc , aciabyte );
|
|
|
|
MemoryLoadNbBytesLeft--;
|
|
if ( MemoryLoadNbBytesLeft == 0 ) /* all bytes were received */
|
|
{
|
|
/* Search for a match amongst the known custom routines */
|
|
for ( i = 0 ; i < sizeof ( CustomCodeDefinitions ) / sizeof ( CustomCodeDefinitions[0] ) ; i++ )
|
|
if ( CustomCodeDefinitions[ i ].LoadMemCrc == MemoryLoadCrc )
|
|
break;
|
|
|
|
if ( i < sizeof ( CustomCodeDefinitions ) / sizeof ( CustomCodeDefinitions[0] ) ) /* found */
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_EXEC, "ikbd loadmemory %d bytes crc=0x%x matches <%s>\n",
|
|
MemoryLoadNbBytesTotal, MemoryLoadCrc, CustomCodeDefinitions[ i ].Name);
|
|
|
|
crc32_reset ( &MemoryLoadCrc );
|
|
MemoryExeNbBytes = 0;
|
|
pIKBD_CustomCodeHandler_Read = NULL;
|
|
pIKBD_CustomCodeHandler_Write = CustomCodeDefinitions[ i ].ExeBootHandler;
|
|
}
|
|
|
|
else /* unknown code uploaded to IKBD's RAM */
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_EXEC, "ikbd loadmemory %d bytes crc=0x%x : unknown code\n",
|
|
MemoryLoadNbBytesTotal, MemoryLoadCrc);
|
|
|
|
pIKBD_CustomCodeHandler_Read = NULL;
|
|
pIKBD_CustomCodeHandler_Write = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Handle writes to $fffc02 when executing custom code in the IKBD's RAM.
|
|
* This is used to send the small IKBD program that will handle
|
|
* keyboard/mouse/joystick input.
|
|
* We compute a CRC of the bytes that are sent until we found a match
|
|
* with a known custom IKBD program.
|
|
*/
|
|
|
|
static void IKBD_CustomCodeHandler_CommonBoot ( Uint8 aciabyte )
|
|
{
|
|
unsigned int i;
|
|
|
|
/* Write received bytes to a file for debug */
|
|
// FILE *f = fopen ( "/tmp/ikbd_custom_program.dump" , "ab" ) ; fprintf ( f , "%c" , aciabyte ) ; fclose ( f );
|
|
|
|
crc32_add_byte ( &MemoryLoadCrc , aciabyte );
|
|
MemoryExeNbBytes++;
|
|
|
|
LOG_TRACE(TRACE_IKBD_EXEC, "ikbd custom exe common boot write 0x%02x count %d crc=0x%x\n",
|
|
aciabyte, MemoryExeNbBytes, MemoryLoadCrc);
|
|
|
|
/* Search for a match amongst the known custom routines */
|
|
for ( i = 0 ; i < sizeof ( CustomCodeDefinitions ) / sizeof ( CustomCodeDefinitions[0] ) ; i++ )
|
|
if ( ( CustomCodeDefinitions[ i ].MainProgNbBytes == MemoryExeNbBytes )
|
|
&& ( CustomCodeDefinitions[ i ].MainProgCrc == MemoryLoadCrc ) )
|
|
break;
|
|
|
|
if ( i < sizeof ( CustomCodeDefinitions ) / sizeof ( CustomCodeDefinitions[0] ) ) /* found */
|
|
{
|
|
LOG_TRACE(TRACE_IKBD_EXEC, "ikbd custom exe common boot, uploaded code matches <%s>\n",
|
|
CustomCodeDefinitions[i].Name);
|
|
|
|
pIKBD_CustomCodeHandler_Read = CustomCodeDefinitions[ i ].ExeMainHandler_Read;
|
|
pIKBD_CustomCodeHandler_Write = CustomCodeDefinitions[ i ].ExeMainHandler_Write;
|
|
|
|
Keyboard.BufferHead = Keyboard.BufferTail = 0; /* flush all queued bytes that would be read in $fffc02 */
|
|
Keyboard.NbBytesInOutputBuffer = 0;
|
|
}
|
|
|
|
/* If not found, we keep on accumulating bytes until we find a matching crc */
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Froggies Over The Fence menu. */
|
|
/* Returns 'n' bytes with the mouse position, keyboard can be used too. */
|
|
/* Writing a <0 byte to $fffc02 will cause the 6301 to exit custom exe */
|
|
/* mode (jmp $f000). */
|
|
/* When writing byte 'n' >0 to $fffc02, the 6301 will return the content*/
|
|
/* of RAM $7f+n to $7f+1. */
|
|
/* $80/$81 contains deltaY/deltaX + left mouse button in bit 7, $82 */
|
|
/* contains LMB in bit 7 and $83 contains a fixed value 0xfc. */
|
|
/* On each VBL, the demo will ask for 1 byte, then for 4 bytes ; only */
|
|
/* the last 2 bytes ($81/$80) will be used, $83/$82 are ignored. */
|
|
/* IKBD's $81 will be stored in $600 (CPU RAM), and $80 in $601. */
|
|
/* */
|
|
/* TODO : an extra delay of 7000 cycles is necessary to have $81 and $80*/
|
|
/* received after the overrun condition was cleared at the 68000 level. */
|
|
/* Does it mean some timings are wrong with acia/ikbd ? */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
static void IKBD_CustomCodeHandler_FroggiesMenu_Read ( void )
|
|
{
|
|
/* Ignore read */
|
|
}
|
|
|
|
static void IKBD_CustomCodeHandler_FroggiesMenu_Write ( Uint8 aciabyte )
|
|
{
|
|
Uint8 res80 = 0;
|
|
Uint8 res81 = 0;
|
|
Uint8 res82 = 0;
|
|
Uint8 res83 = 0xfc; /* fixed value, not used */
|
|
|
|
/* When writing a <0 byte to $fffc02, Froggies ikbd's program will terminate itself */
|
|
/* and leave Execution mode (jmp $f000) */
|
|
if ( aciabyte & 0x80 )
|
|
{
|
|
IKBD_Boot_ROM ( false );
|
|
return;
|
|
}
|
|
|
|
if ( KeyboardProcessor.Mouse.DeltaY < 0 ) res80 = 0x7a; /* mouse up */
|
|
if ( KeyboardProcessor.Mouse.DeltaY > 0 ) res80 = 0x06; /* mouse down */
|
|
if ( KeyboardProcessor.Mouse.DeltaX < 0 ) res81 = 0x7a; /* mouse left */
|
|
if ( KeyboardProcessor.Mouse.DeltaX > 0 ) res81 = 0x06; /* mouse right */
|
|
if ( Keyboard.bLButtonDown & BUTTON_MOUSE ) res82 |= 0x80; /* left mouse button */
|
|
|
|
if ( ScanCodeState[ 0x48 ] ) res80 |= 0x7a; /* up */
|
|
if ( ScanCodeState[ 0x50 ] ) res80 |= 0x06; /* down */
|
|
if ( ScanCodeState[ 0x4b ] ) res81 |= 0x7a; /* left */
|
|
if ( ScanCodeState[ 0x4d ] ) res81 |= 0x06; /* right */
|
|
if ( ScanCodeState[ 0x70 ] ) res82 |= 0x80; /* keypad 0 */
|
|
|
|
res80 |= res82; /* bit 7 is left mouse button */
|
|
res81 |= res82;
|
|
|
|
// res80 = 0x10 ; res81 = 0x11 ; res82 = 0x12 ; res83 = 0x13 ; /* force some discernible values to debug */
|
|
|
|
if ( aciabyte == 1 ) /* Send 1 byte */
|
|
IKBD_Send_Byte_Delay ( res80 , 0 ); /* $80 in IKBD's RAM */
|
|
|
|
else if ( aciabyte == 4 ) /* Send 4 bytes */
|
|
{
|
|
IKBD_Send_Byte_Delay ( res83 , 7000 ); /* $83 in IKBD's RAM */
|
|
IKBD_Send_Byte_Delay ( res82 , 0 ); /* $82 in IKBD's RAM */
|
|
IKBD_Send_Byte_Delay ( res81 , 0 ); /* $81 in IKBD's RAM */
|
|
IKBD_Send_Byte_Delay ( res80 , 0 ); /* $80 in IKBD's RAM */
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Transbeauce II menu. */
|
|
/* Returns 1 byte with the joystick position, keyboard can be used too. */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
static void IKBD_CustomCodeHandler_Transbeauce2Menu_Read ( void )
|
|
{
|
|
Uint8 res = 0;
|
|
|
|
/* keyboard emulation */
|
|
if ( ScanCodeState[ 0x48 ] ) res |= 0x01; /* up */
|
|
if ( ScanCodeState[ 0x50 ] ) res |= 0x02; /* down */
|
|
if ( ScanCodeState[ 0x4b ] ) res |= 0x04; /* left */
|
|
if ( ScanCodeState[ 0x4d ] ) res |= 0x08; /* right */
|
|
if ( ScanCodeState[ 0x62 ] ) res |= 0x40; /* help */
|
|
if ( ScanCodeState[ 0x39 ] ) res |= 0x80; /* space */
|
|
|
|
/* joystick emulation (bit mapping is same as cursor above, with bit 7 = fire button */
|
|
res |= ( Joy_GetStickData(1) & 0x8f ) ; /* keep bits 0-3 and 7 */
|
|
|
|
IKBD_Send_Byte_Delay ( res , 0 );
|
|
}
|
|
|
|
static void IKBD_CustomCodeHandler_Transbeauce2Menu_Write ( Uint8 aciabyte )
|
|
{
|
|
/* Ignore write */
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Dragonnels demo menu. */
|
|
/* When any byte is written in $fffc02, returns one byte with the */
|
|
/* Y position of the mouse and the state of the left button. */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
static void IKBD_CustomCodeHandler_DragonnelsMenu_Read ( void )
|
|
{
|
|
/* Ignore read */
|
|
}
|
|
|
|
static void IKBD_CustomCodeHandler_DragonnelsMenu_Write ( Uint8 aciabyte )
|
|
{
|
|
Uint8 res = 0;
|
|
|
|
if ( KeyboardProcessor.Mouse.DeltaY < 0 ) res = 0xfc; /* mouse up */
|
|
if ( KeyboardProcessor.Mouse.DeltaY > 0 ) res = 0x04; /* mouse down */
|
|
|
|
if ( Keyboard.bLButtonDown & BUTTON_MOUSE ) res = 0x80; /* left mouse button */
|
|
|
|
IKBD_Send_Byte_Delay ( res , 0 );
|
|
}
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Chaos A.D. protection's decoder */
|
|
/* This custom program reads bytes, decode them and send back the result*/
|
|
/* to the 68000. */
|
|
/* The program first returns $fe to indicate it's ready to receive the */
|
|
/* encoded bytes. */
|
|
/* The program then receives the 8 bytes used to decode the data and */
|
|
/* store them in $f0 - $f7 (KeyBuffer is already initialized, so we */
|
|
/* ignore those 8 bytes). */
|
|
/* Then for any received byte a XOR is made with one of the byte in the */
|
|
/* 8 bytes buffer, by incrementing an index in this buffer. */
|
|
/* The decoded byte is written to addr $13 (TDR) to be received by ACIA */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
static void IKBD_CustomCodeHandler_ChaosAD_Read ( void )
|
|
{
|
|
static bool FirstCall = true;
|
|
|
|
if ( FirstCall == true )
|
|
IKBD_Send_Byte_Delay ( 0xfe , 0 );
|
|
|
|
FirstCall = false;
|
|
}
|
|
|
|
static void IKBD_CustomCodeHandler_ChaosAD_Write ( Uint8 aciabyte )
|
|
{
|
|
static int IgnoreNb = 8;
|
|
Uint8 KeyBuffer[] = { 0xca , 0x0a , 0xbc , 0x00 , 0xde , 0xde , 0xfe , 0xca };
|
|
static int Index = 0;
|
|
static int Count = 0;
|
|
|
|
/* We ignore the first 8 bytes we received (they're already in KeyBuffer) */
|
|
if ( IgnoreNb > 0 )
|
|
{
|
|
IgnoreNb--;
|
|
return;
|
|
}
|
|
|
|
if ( Count <= 6080 ) /* there're 6081 bytes to decode */
|
|
{
|
|
Count++;
|
|
|
|
aciabyte ^= KeyBuffer[ Index ];
|
|
Index++;
|
|
Index &= 0x07;
|
|
|
|
IKBD_Send_Byte_Delay ( aciabyte , 0 );
|
|
}
|
|
|
|
else
|
|
{
|
|
/* When all bytes were decoded if 0x08 is written to $fffc02 */
|
|
/* the program will terminate itself and leave Execution mode */
|
|
if ( aciabyte == 0x08 )
|
|
IKBD_Boot_ROM ( false );
|
|
}
|
|
}
|
|
|