SDL-1.2: atari:events: hook on ikbdsys instead of ACIA

From 0dfd096c71cc30f28ef6d904968f68a80e55a57e Mon Sep 17 00:00:00 2001
From: Miro Kropacek <[EMAIL REDACTED]>
Date: Wed, 30 Sep 2026 22:39:09 +1000
Subject: [PATCH] atari:events: hook on ikbdsys instead of ACIA

The routine replaces ikbdsys in the Kbdvbase() table rather than the
ACIA interrupt vector at 0x118. Both ACIAs share the edge triggered MFP
GPIP4 line; the OS interrupt handler calls midisys and ikbdsys in a loop
until the line is released, so an interrupt raised by either ACIA while
the handler runs is not lost.
---
 README.MiNT                               |  63 +++---
 src/video/ataricommon/SDL_atarievents.c   |   5 +
 src/video/ataricommon/SDL_ikbdinterrupt.S | 256 +++++-----------------
 3 files changed, 95 insertions(+), 229 deletions(-)

diff --git a/README.MiNT b/README.MiNT
index a175edef..b30c0fd2 100644
--- a/README.MiNT
+++ b/README.MiNT
@@ -72,12 +72,13 @@ to report this type event.
 V.  Environment variables:
 
 SDL_VIDEODRIVER:
-	Set to 'xbios' to force xbios video driver. Uses XBIOS/Ikbd events driver.
+	Set to 'xbios' to force xbios video driver. Uses XBIOS/Ikbd events
+	driver.
 	Set to 'gem' to force gem video driver. Uses an internal events driver.
 
 SDL_VIDEO_ALIGNED_WINDOWS:
-	Set to force aligning of the window's x coordinate to a 16-pixel boundary
-	(e.g. for speed reasons)
+	Set to force aligning of the window's x coordinate to a 16-pixel
+	boundary (e.g. for speed reasons)
 
 SDL_AUDIODRIVER:
 	Set to 'xbios' to force Atari Xbios audio driver
@@ -133,9 +134,9 @@ VI.  More informations about drivers:
 
 OpenGL:
 	OpenGL is provided by a statically linked OSMesa (libOSMesa.a), the
-	OpenGL context is taken care of by SDL itself, you just have to use gl*
-	functions. Programs not calling any gl* function don't link OSMesa at all.
-	SDL_GL_LoadLibrary() accepts only NULL, i.e. the linked OSMesa.
+	OpenGL context is taken care of by SDL itself, you just have to use
+	gl* functions. Programs not calling any gl* function don't link OSMesa
+	at all. SDL_GL_LoadLibrary() accepts only NULL, i.e. the linked OSMesa.
 
 Xbios video:
 	Video chip is detected using the _VDO cookie.
@@ -143,8 +144,8 @@ Xbios video:
 	them.
 
 	ST, STE, Mega ST, Mega STE:
-		320x200x4 bits, shades of grey, available only for the purpose of
-		testing SDL.
+		320x200x4 bits, shades of grey, available only for the purpose
+		of testing SDL.
 	TT:
 		320x480x8 and 320x240x8 (software double-lined mode).
 	Falcon:
@@ -152,11 +153,12 @@ Xbios video:
 		BlowUp and Centscreen: Extended modes.
 		ScreenBlaster 3: Only current mode.
 		Supervidel: Extended modes.
-		CTPCI: Only the modes supported by XBIOS (640x480, 800x600, 1024x768,
-			   1280x960, 1600x1200 in 8/16/32 bpp).
+		CTPCI: Only the modes supported by XBIOS (640x480, 800x600,
+			   1024x768, 1280x960, 1600x1200 in 8/16/32 bpp).
 	Milan:
-		Nearly identical with CTPCI driver. Works only on Milan with S3 video
-		card driver (which should be newer than 12.1.2000). To be verified.
+		Nearly identical with CTPCI driver. Works only on Milan with S3
+		video card driver (which should be newer than 12.1.2000). To be
+		verified.
 	Any machine with NOVA video card:
 		All modes with 8 bits or higher depth.
 	Clones and any machine with monochrome monitor:
@@ -171,9 +173,10 @@ Xbios video:
 	called.
 
 	The decision should depend on how the framebuffer is used. If the SDL
-	application already maintains its own framebuffer and SDL is used merely as
-	a display device, use SDL_HWSURFACE. If the framebuffer is accessed on a
-	per-pixel basis —or if pixels are read multiple times— prefer SDL_SWSURFACE.
+	application already maintains its own framebuffer and SDL is used
+	merely as a display device, use SDL_HWSURFACE. If the framebuffer is
+	accessed on a per-pixel basis —or if pixels are read multiple times—
+	prefer SDL_SWSURFACE.
 
 Gem video:
 	Automatically used if xbios not available.
@@ -185,25 +188,27 @@ Keyboard, mouse and joystick driver:
 	When unset, XBIOS video uses XBIOS where available (TOS >= 2.x and/or in
 	MagiC) and IKBD otherwise, GEM video uses GEM.
 
+	The IKBD driver hooks the ikbdsys vector from Kbdvbase() instead of the
+	ACIA interrupt vector at 0x118, so MIDI interrupts keep being serviced.
+	It is not available on FireTOS, whose ACIA handler never calls ikbdsys.
+
 	In ideal circumstances, XBIOS video supports selection of XBIOS and IKBD
-	events while GEM video supports selection of XBIOS and GEM events. Otherwise
-	XBIOS video is stuck with IKBD events and GEM video with GEM events.
+	events while GEM video supports selection of XBIOS and GEM events.
+	Otherwise XBIOS video is stuck with IKBD events and GEM video with GEM
+	events.
 
 	With GEM video the choice only decides where keyboard events come from:
-	AES message loop or XBIOS keyboard vector (which reports key press/release
-	events). Mouse buttons, relative motion and the joystick always come from the
-	XBIOS vectors (which are available on every machine).
-
-	The AES reports key presses and their repeats but no releases, so the GEM
-	driver considers a key released once its next repeat is overdue, using
-	the delay and rate set in the Control Panel. Only the last pressed key
-	repeats, so keys held together are released early; use
+	AES message loop or XBIOS keyboard vector (which reports key
+	press/release events). Mouse buttons, relative motion and the joystick
+	always come from the XBIOS vectors (which are available on every
+	machine).
+
+	The AES reports key presses and their repeats but no releases, so the
+	GEM driver considers a key released once its next repeat is overdue,
+	using the delay and rate set in the Control Panel. Only the last
+	pressed key repeats, so keys held together are released early; use
 	SDL_ATARI_EVENTSDRIVER=xbios where available.
 
-	As XBIOS video driver is not available on Hades, it is forced to use the
-	implicit GEM driver despite the fact that it has both IKBD and XBIOS
-	features available.
-
 Joypad driver:
 	Available if _MCH cookie is STE or Falcon. Supports teamtap.
 
diff --git a/src/video/ataricommon/SDL_atarievents.c b/src/video/ataricommon/SDL_atarievents.c
index 5b707c00..f39bbd06 100644
--- a/src/video/ataricommon/SDL_atarievents.c
+++ b/src/video/ataricommon/SDL_atarievents.c
@@ -117,6 +117,11 @@ static SDL_bool IsIkbdSupported(void)
 {
 	long cookie_mch = SDL_Atari_GetMch();
 
+	/* FireTOS reports a Falcon but its ACIA interrupt handler never calls
+	   kb_kbdsys, which the IKBD driver replaces */
+	if (Getcookie(C__CF_, NULL) == C_FOUND)
+		return SDL_FALSE;
+
 	/* The IKBD driver talks to the keyboard chip directly, which only
 	   Atari hardware and its emulators provide */
 	return (cookie_mch == MCH_ST<<16) || ((cookie_mch>>16) == MCH_STE) ||
diff --git a/src/video/ataricommon/SDL_ikbdinterrupt.S b/src/video/ataricommon/SDL_ikbdinterrupt.S
index a4f15f10..be30a806 100644
--- a/src/video/ataricommon/SDL_ikbdinterrupt.S
+++ b/src/video/ataricommon/SDL_ikbdinterrupt.S
@@ -41,7 +41,7 @@
 
 	.globl	SYM(Atari_UnhookXbra)
 
-/*--- Install our IKBD vector ---*/
+/*--- Install our kb_kbdsys vector ---*/
 
 SYM(SDL_AtariIkbd_Install):
 	| KBDVBASE *Kbdvbase(void);
@@ -62,6 +62,7 @@ SYM(SDL_AtariIkbd_Install):
 	moveml	sp@+,d2/a2
 #endif
 	movel	d0,a1
+	movel	a1,ikbd_kbdvecs
 
 processing_ikbd_packet_install:
 
@@ -78,59 +79,28 @@ processing_ikbd_packet_install:
 	bras	processing_ikbd_packet_install
 no_ikbd_packet_install:
 
-	| Save MFP registers used for keyboard
-
-	lea	0xfffffa00:w,a0
-	btst	#6,a0@(0x09)
-#if defined(__mcoldfire__)
-	sne	d0
-	move.b	d0,ikbd_ierb
-#else
-	sne	ikbd_ierb
-#endif
-	btst	#6,a0@(0x15)
-#if defined(__mcoldfire__)
-	sne	d0
-	move.b	d0,ikbd_imrb
-#else
-	sne	ikbd_imrb
-#endif
-
 	| Set our routine
 
-#if defined(__mcoldfire__)
-	movel	0x118:w,d0
+	lea	ikbd_packet_start,a0
+	movel	a0,ikbd_state
+
+	movel	a1@(32),d0
 	movel	d0,old_ikbd
 	lea	ikbd,a0
-	movel	a0,0x118:w
-	moveql	#6,d0
-	bset	d0,0xfffffa09:w	| IERB
-	bset	d0,0xfffffa15:w	| IMRB
-#else
-	movel	0x118:w,old_ikbd
-	movel	#ikbd,0x118:w
-	bset	#6,0xfffffa09:w	| IERB
-	bset	#6,0xfffffa15:w	| IMRB
-#endif
+	movel	a0,a1@(32)
 
 	| Set mouse relative mode
 
-#if defined(__mcoldfire__)
 	moveql	#8,d0
 	moveb	d0,0xfffffc02:w
-#else
-	moveb	#8,0xfffffc02:w
-#endif
 
 	| Reenable interrupts
 
 	movew	d1,sr
 
-	| Interrupts done
-
 	rts
 
-/*--- Uninstall our IKBD vector ---*/
+/*--- Uninstall our kb_kbdsys vector ---*/
 
 SYM(SDL_AtariIkbd_Restore):
 	tstl	old_ikbd
@@ -143,34 +113,12 @@ ikbd_installed:
 	movew	sr,d1
 	movew	#0x2700,sr
 
-	| Restore previous MFP registers
-
-	lea	0xfffffa00:w,a0
-
-	bclr	#6,a0@(0x09)
-	tstb	ikbd_ierb
-	beqs	ikbd_restoreierb
-	bset	#6,a0@(0x09)
-ikbd_restoreierb:
-
-	bclr	#6,a0@(0x15)
-	tstb	ikbd_imrb
-	beqs	ikbd_restoreimrb
-	bset	#6,a0@(0x15)
-ikbd_restoreimrb:
-
-#if defined(__mcoldfire__)
-	movel	#ikbd,d0
-	movel	d0,0x118:w
-#else
-	movel	#ikbd,0x118:w
-#endif
-
 	movew	d1,sp@-
 
-	pea	ikbd(pc)
+	pea	ikbd
 	movel	#0x4c53444c,sp@-	/* "LSDL" */
-	pea	0x118/4:w
+	movel	ikbd_kbdvecs,a0
+	pea	a0@(32)
 	jsr	SYM(Atari_UnhookXbra)
 	lea	sp@(12),sp
 
@@ -190,20 +138,20 @@ ikbd_finbuffer:
 
 	movew	d1,sr
 
-	| Interrupts done
-
 	clrl	old_ikbd
 	rts
 
 	.bss
 
 	.even
-ikbd_ierb:
-	.ds.b	1
-ikbd_imrb:
-	.ds.b	1
+ikbd_kbdvecs:
+	.ds.l	1
+ikbd_state:
+	.ds.l	1
+ikbd_mouse_entry:
+	.ds.l	1
 
-/*--- Our custom IKBD vector ---*/
+/*--- Our custom kb_kbdsys vector ---*/
 
 	.text
 	.even
@@ -219,16 +167,26 @@ ikbd:
 	moveml	d0-d1/a0,sp@-
 #endif
 
-	| Check if source is IKBD or MIDI
-#if defined(__mcoldfire__)
-	moveql	#0,d0
-	btst	d0,0xfffffc00.w
-#else
-	btst	#0,0xfffffc00.w
-#endif
-	beq	ikbd_oldmidi
+	| Interrupt request from the IKBD ACIA?
+
+	moveb	0xfffffc00:w,d1
+	bpl	ikbd_end
+
+	| Reading the data register clears both a received byte and an
+	| overrun, releasing the ACIA's interrupt line
 
 	moveb	0xfffffc02:w,d0
+	btst	#0,d1
+	beq	ikbd_end
+
+	| Continue the packet in progress
+
+	movel	ikbd_state,a0
+	jmp	a0@
+
+	| First byte of a packet
+
+ikbd_packet_start:
 
 	| Joystick packet ?
 
@@ -248,7 +206,7 @@ ikbd_yes_mouse:
 	andl	#3,d0
 	movew	d0,SYM(SDL_Atari_mouseb)
 
-	| Start the queue entry, dx and dy follow in the next two packets
+	| Start the queue entry, dx and dy follow in the next two bytes
 
 	moveql	#ATARI_QUEUE_MOUSE,d1
 	lsll	#8,d1
@@ -256,24 +214,14 @@ ikbd_yes_mouse:
 	swap	d1
 	movel	d1,ikbd_mouse_entry
 
-#if defined(__mcoldfire__)
-	movel	#ikbd_mousex,d0
-	movel	d0,0x118:w
-#else
-	movel	#ikbd_mousex,0x118:w
-#endif
-	bras	ikbd_endit_stack
+	lea	ikbd_mousex,a0
+	bras	ikbd_next_state
 
 	| Joystick packet, byte #1
 
 ikbd_yes_joystick:
-#if defined(__mcoldfire__)
-	movel	#ikbd_joystick,d0
-	movel	d0,0x118:w
-#else
-	movel	#ikbd_joystick,0x118:w
-#endif
-	bras	ikbd_endit_stack
+	lea	ikbd_joystick,a0
+	bras	ikbd_next_state
 
 	| Keyboard press/release
 
@@ -284,132 +232,40 @@ ikbd_no_mouse:
 	moveb	d1,d0
 	swap	d0
 	ATARI_QUEUE_PUSH
-
-	| End of interrupt
-
-ikbd_endit_stack:
-#if defined(__mcoldfire__)
-	moveql	#6,d0
-	bclr	d0,0xfffffa11:w
-
-	moveml	sp@,d0-d1/a0
-	lea	sp@(12),sp
-#else
-	moveml	sp@+,d0-d1/a0
-
-	bclr	#6,0xfffffa11:w
-#endif
-	rte
-
-	| Call old MIDI interrupt
-
-ikbd_oldmidi:
-#if defined(__mcoldfire__)
-	moveml	sp@,d0-d1/a0
-	lea	sp@(12),sp
-#else
-	moveml	sp@+,d0-d1/a0
-#endif
-
-	movel	old_ikbd,sp@-
-	rts
+	bras	ikbd_end
 
 	| Mouse packet, byte #2
 
 ikbd_mousex:
-#if defined(__mcoldfire__)
-	lea	sp@(-12),sp
-	moveml	d0-d1/a0,sp@
-#else
-	moveml	d0-d1/a0,sp@-
-#endif
-
-	| Check if source is IKBD or MIDI
-#if defined(__mcoldfire__)
-	moveql	#0,d0
-	btst	d0,0xfffffc00.w
-#else
-	btst	#0,0xfffffc00.w
-#endif
-	beq	ikbd_oldmidi
-
-	moveb	0xfffffc02:w,d0
 	moveb	d0,ikbd_mouse_entry+2
-#if defined(__mcoldfire__)
-	movel	#ikbd_mousey,d0
-	movel	d0,0x118:w
-#else
-	movel	#ikbd_mousey,0x118:w
-#endif
-	bra	ikbd_endit_stack
+	lea	ikbd_mousey,a0
+	bras	ikbd_next_state
 
 	| Mouse packet, byte #3
 
 ikbd_mousey:
-#if defined(__mcoldfire__)
-	lea	sp@(-12),sp
-	moveml	d0-d1/a0,sp@
-#else
-	moveml	d0-d1/a0,sp@-
-#endif
-
-	| Check if source is IKBD or MIDI
-#if defined(__mcoldfire__)
-	moveql	#0,d0
-	btst	d0,0xfffffc00.w
-#else
-	btst	#0,0xfffffc00.w
-#endif
-	beq	ikbd_oldmidi
-
-	moveb	0xfffffc02:w,d0
 	moveb	d0,ikbd_mouse_entry+3
 
 	movel	ikbd_mouse_entry,d0
 	ATARI_QUEUE_PUSH
-
-#if defined(__mcoldfire__)
-	movel	#ikbd,d0
-	movel	d0,0x118:w
-#else
-	movel	#ikbd,0x118:w
-#endif
-	bra	ikbd_endit_stack
+	bras	ikbd_packet_done
 
 	| Joystick packet, byte #2
 
 ikbd_joystick:
-#if defined(__mcoldfire__)
-	lea	sp@(-12),sp
-	moveml	d0-d1/a0,sp@
-#else
-	moveml	d0-d1/a0,sp@-
-#endif
+	moveb	d0,SYM(SDL_Atari_joystick)
 
-	| Check if source is IKBD or MIDI
-#if defined(__mcoldfire__)
-	moveql	#0,d0
-	btst	d0,0xfffffc00.w
-#else
-	btst	#0,0xfffffc00.w
-#endif
-	beq	ikbd_oldmidi
+ikbd_packet_done:
+	lea	ikbd_packet_start,a0
 
-#if defined(__mcoldfire__)
-	moveb	0xfffffc02:w,d0
-	moveb	d0,SYM(SDL_Atari_joystick)
+ikbd_next_state:
+	movel	a0,ikbd_state
 
-	movel	#ikbd,d0
-	movel	d0,0x118:w
+ikbd_end:
+#if defined(__mcoldfire__)
+	moveml	sp@,d0-d1/a0
+	lea	sp@(12),sp
 #else
-	moveb	0xfffffc02:w,SYM(SDL_Atari_joystick)
-
-	movel	#ikbd,0x118:w
+	moveml	sp@+,d0-d1/a0
 #endif
-	bra	ikbd_endit_stack
-
-	.bss
-
-	.even
-ikbd_mouse_entry:
-	.ds.l	1
+	rts