SDL-1.2: atari:events:xbios: add keyboard handler

From 20b97558e4c8ad70165c731dd4a56f3f85a636cf Mon Sep 17 00:00:00 2001
From: Anders Granlund <[EMAIL REDACTED]>
Date: Thu, 27 Jun 2024 22:57:48 +0200
Subject: [PATCH] atari:events:xbios: add keyboard handler

Add support for hooking the keyboard vector on TOS >= 2.x
---
 src/video/ataricommon/SDL_atarievents.c      |  8 ++
 src/video/ataricommon/SDL_xbiosevents.c      | 97 ++++++++++++++++++++
 src/video/ataricommon/SDL_xbiosevents_c.h    |  8 ++
 src/video/ataricommon/SDL_xbiosinterrupt.S   | 86 +++++++++++++++++
 src/video/ataricommon/SDL_xbiosinterrupt_s.h |  3 +
 5 files changed, 202 insertions(+)

diff --git a/src/video/ataricommon/SDL_atarievents.c b/src/video/ataricommon/SDL_atarievents.c
index a5172de3..d913f0bc 100644
--- a/src/video/ataricommon/SDL_atarievents.c
+++ b/src/video/ataricommon/SDL_atarievents.c
@@ -39,6 +39,7 @@
 #include "SDL_atarikeys.h"
 #include "SDL_atarievents_c.h"
 #include "SDL_biosevents_c.h"
+#include "SDL_xbiosevents_c.h"
 #include "SDL_gemdosevents_c.h"
 #include "SDL_ikbdevents_c.h"
 
@@ -117,6 +118,13 @@ static void Atari_InitializeEvents(_THIS)
 		this->PumpEvents=AtariBios_PumpEvents;
 		Atari_ShutdownEvents=AtariBios_ShutdownEvents;
 	}
+
+	if (SDL_strcmp(envr, "xbios") == 0) {
+		this->InitOSKeymap=AtariXbios_InitOSKeymap;
+		this->PumpEvents=AtariXbios_PumpEvents;
+		Atari_ShutdownEvents=AtariXbios_ShutdownEvents;
+	}
+
 }
 
 void Atari_InitOSKeymap(_THIS)
diff --git a/src/video/ataricommon/SDL_xbiosevents.c b/src/video/ataricommon/SDL_xbiosevents.c
index 3dd26a8f..0708cf76 100644
--- a/src/video/ataricommon/SDL_xbiosevents.c
+++ b/src/video/ataricommon/SDL_xbiosevents.c
@@ -29,11 +29,19 @@
 
 #include <mint/osbind.h>
 
+#include "../../events/SDL_sysevents.h"
 #include "../../events/SDL_events_c.h"
+
 #include "SDL_atarisuper.h"
+#include "SDL_atarikeys.h"
+#include "SDL_atarievents_c.h"
 #include "SDL_xbiosevents_c.h"
 #include "SDL_xbiosinterrupt_s.h"
 
+#define KEY_PRESSED		0xff
+#define KEY_UNDEFINED	0x80
+#define KEY_RELEASED	0x00
+
 /* Variables */
 
 SDL_bool SDL_AtariXbios_enabled=SDL_FALSE;
@@ -44,6 +52,44 @@ static Uint16 atari_prevmouseb;	/* buttons */
 
 /* Functions */
 
+SDL_bool SDL_AtariXbios_IsKeyboardVectorSupported_Super()
+{
+	/* TODO: FreeMiNT (MagiC, Geneva?) */
+	uint16_t* oshdr = *((uint16_t**)(0x4f2));
+	return (oshdr[1] >= 0x0200) ? SDL_TRUE : SDL_FALSE;
+}
+
+SDL_bool SDL_AtariXbios_IsKeyboardVectorSupported()
+{
+	return Supexec(SDL_AtariXbios_IsKeyboardVectorSupported_Super);
+}
+
+void AtariXbios_InitOSKeymap(_THIS)
+{
+	int vectors_mask;
+	vectors_mask  = ATARI_XBIOS_JOYSTICKEVENTS;	/* XBIOS joystick events */
+	vectors_mask |= ATARI_XBIOS_MOUSEEVENTS;	/* XBIOS mouse events */
+	if (SDL_AtariXbios_IsKeyboardVectorSupported()) {
+		vectors_mask |= ATARI_XBIOS_KEYBOARDEVENTS;	/* XBIOS keyboard events */
+	} else {
+		SDL_SetError("Xbios keyboard vector is not supported on TOS 1.x");
+        return;
+	}
+
+	SDL_AtariXbios_InstallVectors(vectors_mask);
+}
+
+void AtariXbios_PumpEvents(_THIS)
+{
+	SDL_AtariXbios_PostKeyboardEvents(this);
+	SDL_AtariXbios_PostMouseEvents(this, SDL_TRUE);
+}
+
+void AtariXbios_ShutdownEvents(void)
+{
+	SDL_AtariXbios_RestoreVectors();
+}
+
 void SDL_AtariXbios_InstallVectors(int vectors_mask)
 {
 	/* Clear variables */
@@ -54,6 +100,8 @@ void SDL_AtariXbios_InstallVectors(int vectors_mask)
 		SDL_AtariXbios_joystick =
 		atari_prevmouseb = 0;
 
+	SDL_memset((void*)SDL_AtariXbios_keyboard, KEY_UNDEFINED, 128);
+
 	if (vectors_mask==0) {
 		SDL_AtariXbios_enabled=SDL_FALSE;
 		return;
@@ -62,6 +110,8 @@ void SDL_AtariXbios_InstallVectors(int vectors_mask)
 	/* Install our vectors */
 	SDL_AtariXbios_installmousevector = (vectors_mask & ATARI_XBIOS_MOUSEEVENTS) != 0;
 	SDL_AtariXbios_installjoystickvector = (vectors_mask & ATARI_XBIOS_JOYSTICKEVENTS) != 0;
+	SDL_AtariXbios_installkeyboardvector = (vectors_mask & ATARI_XBIOS_KEYBOARDEVENTS) != 0;
+
 	Supexec(SDL_AtariXbios_Install);
 	/* SDL_AtariXbios_Restore() doesn't need SDL_AtariXbios_enabled */
 	Setexc(VEC_PROCTERM, SDL_AtariXbios_Restore);
@@ -130,3 +180,50 @@ void SDL_AtariXbios_LockMousePosition(SDL_bool lockPosition)
 {
 	SDL_AtariXbios_mouselock = lockPosition;
 }
+
+void SDL_AtariXbios_PostKeyboardEvents(_THIS)
+{
+	short i, kstate;
+	SDL_keysym keysym;
+
+	if (!SDL_AtariXbios_enabled) {
+		return;
+	}
+
+	kstate  = (SDL_AtariXbios_keyboard[SCANCODE_LEFTSHIFT] == KEY_PRESSED) ? K_LSHIFT : 0;
+	kstate |= (SDL_AtariXbios_keyboard[SCANCODE_RIGHTSHIFT] == KEY_PRESSED) ? K_RSHIFT : 0;
+	kstate |= (SDL_AtariXbios_keyboard[SCANCODE_LEFTCONTROL] == KEY_PRESSED) ? K_CTRL : 0;
+	kstate |= (SDL_AtariXbios_keyboard[SCANCODE_LEFTALT] == KEY_PRESSED) ? K_ALT : 0;
+	kstate |= (SDL_AtariXbios_keyboard[SCANCODE_CAPSLOCK] == KEY_PRESSED) ? K_CAPSLOCK : 0;
+	kstate |= (SDL_AtariXbios_keyboard[SCANCODE_ALTGR] == KEY_PRESSED) ? 0x80 : 0;
+
+	for (i=0; i<sizeof(SDL_AtariXbios_keyboard); i++) {
+		/* Key pressed ? */
+		if (SDL_AtariXbios_keyboard[i]==KEY_PRESSED) {
+			SDL_PrivateKeyboard(SDL_PRESSED,
+				SDL_Atari_TranslateKey(i, &keysym, SDL_TRUE, kstate));
+			if (i == SCANCODE_CAPSLOCK) {
+				/* Pressed capslock: generate a release event, too because this
+				 * is what SDL expects; it handles locking by itself.
+				 */
+				SDL_PrivateKeyboard(SDL_RELEASED,
+					SDL_Atari_TranslateKey(i, &keysym, SDL_FALSE, kstate & ~K_CAPSLOCK));
+			}
+			SDL_AtariXbios_keyboard[i]=KEY_UNDEFINED;
+		}
+			
+		/* Key released ? */
+		if (SDL_AtariXbios_keyboard[i]==KEY_RELEASED) {
+			if (i == SCANCODE_CAPSLOCK) {
+				/* Released capslock: generate a pressed event, too because this
+				 * is what SDL expects; it handles locking by itself.
+				 */
+				SDL_PrivateKeyboard(SDL_PRESSED,
+					SDL_Atari_TranslateKey(i, &keysym, SDL_TRUE, kstate | K_CAPSLOCK));
+			}
+			SDL_PrivateKeyboard(SDL_RELEASED,
+				SDL_Atari_TranslateKey(i, &keysym, SDL_FALSE, kstate));
+			SDL_AtariXbios_keyboard[i]=KEY_UNDEFINED;
+		}
+	}
+}
diff --git a/src/video/ataricommon/SDL_xbiosevents_c.h b/src/video/ataricommon/SDL_xbiosevents_c.h
index 40a43308..55482d33 100644
--- a/src/video/ataricommon/SDL_xbiosevents_c.h
+++ b/src/video/ataricommon/SDL_xbiosevents_c.h
@@ -37,12 +37,20 @@
 
 #define ATARI_XBIOS_MOUSEEVENTS (1<<0)
 #define ATARI_XBIOS_JOYSTICKEVENTS (1<<1)
+#define ATARI_XBIOS_KEYBOARDEVENTS (1<<2)
 
 extern SDL_bool SDL_AtariXbios_enabled;
 
+extern void AtariXbios_InitOSKeymap(_THIS);
+extern void AtariXbios_PumpEvents(_THIS);
+extern void AtariXbios_ShutdownEvents(void);
+
 extern void SDL_AtariXbios_InstallVectors(int vectors_mask);
 extern void SDL_AtariXbios_RestoreVectors(void);
 extern void SDL_AtariXbios_PostMouseEvents(_THIS, SDL_bool buttonEvents);
 extern void SDL_AtariXbios_LockMousePosition(SDL_bool lockPosition);
+extern void SDL_AtariXbios_PostKeyboardEvents(_THIS);
+
+extern SDL_bool SDL_AtariXbios_IsKeyboardVectorSupported();
 
 #endif /* _SDL_XBIOSEVENTS_H_ */
diff --git a/src/video/ataricommon/SDL_xbiosinterrupt.S b/src/video/ataricommon/SDL_xbiosinterrupt.S
index d000194f..d03073ea 100644
--- a/src/video/ataricommon/SDL_xbiosinterrupt.S
+++ b/src/video/ataricommon/SDL_xbiosinterrupt.S
@@ -40,6 +40,8 @@
 	.globl	SYM(SDL_AtariXbios_mousey)
 	.globl	SYM(SDL_AtariXbios_installjoystickvector)
 	.globl	SYM(SDL_AtariXbios_joystick)
+	.globl	SYM(SDL_AtariXbios_installkeyboardvector)
+	.globl	SYM(SDL_AtariXbios_keyboard)
 
 	.globl	SYM(Atari_UnhookXbra)
 
@@ -99,6 +101,21 @@ no_new_mouse_vector:
 #endif
 no_new_joystick_vector:
 
+	/* Save old keyboard vector, set our routine */
+
+	tstw	SYM(SDL_AtariXbios_installkeyboardvector)
+	beqs	no_new_keyboard_vector
+#if defined(__mcoldfire__)
+	movel	a0@(-4),a1
+	movel	a1,oldkeyboardvector
+	lea	newkeyboardvector(pc),a1
+	movel	a1,a0@(-4)
+#else
+	movel	a0@(-4),oldkeyboardvector
+	movel	#newkeyboardvector,a0@(-4)
+#endif
+no_new_keyboard_vector:
+
 	/* Restart interrupts */
 
 	movew	d1,sr
@@ -176,6 +193,24 @@ no_restore_mouse:
 
 no_restore_joystick:
 
+	/* Restore keyboard vector */
+
+	tstl	oldkeyboardvector
+	beqs	no_restore_keyboard
+
+	movew	d1,sp@-
+
+	pea	newkeyboardvector(pc)
+	movel	#0x4c53444c,sp@-	/* "LSDL" */
+	pea	a0@(-4)
+	jsr	SYM(Atari_UnhookXbra)
+	lea	sp@(12),sp
+	clrl	oldkeyboardvector
+
+	movew	sp@+,d1
+
+no_restore_keyboard:
+
 	/* Restart interrupts */
 
 	movew	d1,sr
@@ -294,3 +329,54 @@ SYM(SDL_AtariXbios_installjoystickvector):
 	.dc.w	0
 SYM(SDL_AtariXbios_joystick):
 	.dc.w	0
+
+
+/*--- Our keyboard vector ---*/
+
+	.text
+
+	.even
+	.ascii "XBRA"
+	.ascii "LSDL"
+oldkeyboardvector:
+	.dc.l	0
+newkeyboardvector:
+
+#if defined(__mcoldfire__)
+	lea	sp@(-12),sp
+	moveml	d0-d1/a0,sp@
+#else
+	moveml	d0-d1/a0,sp@-
+#endif
+
+	moveb	d0,d1
+	lea		SYM(SDL_AtariXbios_keyboard),a0
+#if defined(__mcoldfire__)
+	andl	#0x7f,d1
+	btst	#7,d0
+	seq	d0
+	moveb	d0,a0@(0,d1:l)
+#else
+	andw	#0x7f,d1
+	btst	#7,d0
+	seq	a0@(0,d1:w)
+#endif
+
+	/* Jump through old vector */
+#if defined(__mcoldfire__)
+	moveml	sp@,d0-d1/a0
+	lea	sp@(12),sp
+#else
+	moveml	sp@+,d0-d1/a0
+#endif
+
+	movel	oldkeyboardvector,sp@-
+	rts
+
+	.data
+
+	.even
+SYM(SDL_AtariXbios_installkeyboardvector):
+	.dc.w	0
+SYM(SDL_AtariXbios_keyboard):
+	.ds.b	128
diff --git a/src/video/ataricommon/SDL_xbiosinterrupt_s.h b/src/video/ataricommon/SDL_xbiosinterrupt_s.h
index efaf85b2..f669498d 100644
--- a/src/video/ataricommon/SDL_xbiosinterrupt_s.h
+++ b/src/video/ataricommon/SDL_xbiosinterrupt_s.h
@@ -41,6 +41,8 @@ extern volatile Sint16 SDL_AtariXbios_mousex;	/* X relative motion */
 extern volatile Sint16 SDL_AtariXbios_mousey;	/* Y relative motion */
 extern Uint16 SDL_AtariXbios_installjoystickvector;	/* flag for SDL_AtariXbios_Install() */
 extern volatile Uint16 SDL_AtariXbios_joystick;	/* Joystick */
+extern Uint16 SDL_AtariXbios_installkeyboardvector;	/* flag for SDL_AtariXbios_Install() */
+extern volatile Uint8 SDL_AtariXbios_keyboard[128];	/* Keyboard table */
 
 /* Functions */ 
 
@@ -48,5 +50,6 @@ extern void SDL_AtariXbios_Install(void);
 extern void SDL_AtariXbios_Restore(void);
 extern void SDL_AtariXbios_MouseVector(void *buf);
 extern void SDL_AtariXbios_JoystickVector(void *buf);
+extern void SDL_AtariXbios_KeyboardVector(void *buf);
 
 #endif /* _SDL_XBIOSINTERRUPT_S_H_ */