gint: avoid annoying banner on return from menu on fx-CG

Instead of using GetKeyWait(), we directly call into an internal OS
function, which avoids the need to press a key before we can redraw over
the OS banner and VRAM when coming back. The disassembly to locate the
function was developed by Dr-Carlos.

We still have to redraw when coming back; getkey() will do it by
dupdate() by default, and it that's not a possibility for the
application an option to receive a special event and handle it manually
is provided.
This commit is contained in:
Lephe 2023-08-19 22:52:53 +02:00
parent 6d86c54507
commit 2bb5294578
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
4 changed files with 114 additions and 20 deletions

View file

@ -70,7 +70,7 @@ extern "C" {
* keyreleased() checks if the specified key is currently up and was down at * keyreleased() checks if the specified key is currently up and was down at
the time of the last cleareventflips(). the time of the last cleareventflips().
A typical game loop might look like. cleareventflisps() must be called A typical game loop might look like. cleareventflips() must be called
_before_ clearevents(). _before_ clearevents().
cleareventflips(); cleareventflips();
@ -116,13 +116,13 @@ typedef struct
{ {
uint time :16; /* Time of event, unique over short periods */ uint time :16; /* Time of event, unique over short periods */
uint :3; /* Reserved for future use */ uint :2; /* Reserved for future use */
uint mod :1; /* Whether modifiers are used */ uint mod :1; /* Whether modifiers are used */
uint shift :1; /* If mod=1, whether SHIFT was pressed */ uint shift :1; /* If mod=1, whether SHIFT was pressed */
uint alpha :1; /* If mod=1, whether ALPHA was pressed */ uint alpha :1; /* If mod=1, whether ALPHA was pressed */
uint type :2; /* Type of key event */ uint type :3; /* Type of key event */
uint key :8; /* Hit key */ uint key :8; /* Hit key */
} GPACKED(4) key_event_t; } GPACKED(4) key_event_t;
@ -130,10 +130,11 @@ typedef struct
/* Keyboard event types, as in the [type] field of key_event_t */ /* Keyboard event types, as in the [type] field of key_event_t */
enum enum
{ {
KEYEV_NONE = 0, /* No event available (poll() only) */ KEYEV_NONE = 0, /* No event available (poll() only) */
KEYEV_DOWN = 1, /* Key was pressed */ KEYEV_DOWN = 1, /* Key was pressed */
KEYEV_UP = 2, /* Key was released */ KEYEV_UP = 2, /* Key was released */
KEYEV_HOLD = 3, /* A key that was pressed has been held down */ KEYEV_HOLD = 3, /* A key that was pressed has been held down */
KEYEV_OSMENU = 4, /* We went to the main menu and back */
}; };
/* Keyboard frequency analysis is a runtime setting since gint 2.4. This macro /* Keyboard frequency analysis is a runtime setting since gint 2.4. This macro
@ -239,24 +240,29 @@ key_event_t getkey(void);
/* The following are the option bits for getkey_opt(). */ /* The following are the option bits for getkey_opt(). */
enum { enum {
/* Enable modifiers keys */ /* Enable modifiers keys */
GETKEY_MOD_SHIFT = 0x01, GETKEY_MOD_SHIFT = 0x0001,
GETKEY_MOD_ALPHA = 0x02, GETKEY_MOD_ALPHA = 0x0002,
/* SHIFT + OPTN (requires GETKEY_MOD_SHIFT) or LIGHT toggles backlight */ /* SHIFT + OPTN (requires GETKEY_MOD_SHIFT) or LIGHT toggles backlight */
GETKEY_BACKLIGHT = 0x04, GETKEY_BACKLIGHT = 0x0004,
/* MENU triggers a task switch and displays the main menu */ /* MENU triggers a task switch and displays the main menu */
GETKEY_MENU = 0x08, GETKEY_MENU = 0x0008,
/* Repeat arrow keys, or even all keys */ /* Repeat arrow keys, or even all keys */
GETKEY_REP_ARROWS = 0x10, GETKEY_REP_ARROWS = 0x0010,
GETKEY_REP_ALL = 0x20, GETKEY_REP_ALL = 0x0020,
/* Enable custom repeat profiles; see getkey_set_repeat_profile() */ /* Enable custom repeat profiles; see getkey_set_repeat_profile() */
GETKEY_REP_PROFILE = 0x40, GETKEY_REP_PROFILE = 0x0040,
/* Enable application shortcuts; see getkey_set_feature_function() */ /* Enable application shortcuts; see getkey_set_feature_function() */
GETKEY_FEATURES = 0x80, GETKEY_FEATURES = 0x0080,
/* After coming back from the main menu, redraw the screen with dupdate
(has an effect on fx-CG 50 only) */
GETKEY_MENU_DUPDATE = 0x0100,
/* After coming back from the main menu, send a KEYEV_OSMENU event */
GETKEY_MENU_EVENT = 0x0200,
/* No modifiers */ /* No modifiers */
GETKEY_NONE = 0x00, GETKEY_NONE = 0x0000,
/* Default settings of getkey() */ /* Default settings of getkey() */
GETKEY_DEFAULT = 0xdf, GETKEY_DEFAULT = 0x01df,
}; };
/* getkey_feature_t: Custom feature function /* getkey_feature_t: Custom feature function

View file

@ -1,6 +1,7 @@
#include <gint/gint.h> #include <gint/gint.h>
#include <gint/display.h> #include <gint/display.h>
#include <gint/hardware.h> #include <gint/hardware.h>
#include <gint/keyboard.h>
#include <string.h> #include <string.h>
@ -28,6 +29,69 @@ static void __osmenu_handler(void)
__Timer_Deinstall(__osmenu_id); __Timer_Deinstall(__osmenu_id);
} }
#ifdef FXCG50
typedef void (os_menu_function_t)(void);
/* This method is possible thanks to reverse-engineering by Dr-Carlos.
<https://www.cemetech.net/forum/viewtopic.php?t=18944> */
static os_menu_function_t *find_os_menu_function(void)
{
/* Get syscall table address */
uint32_t addr = *(uint32_t *)0x8002007c;
if(addr < 0x80020070 || addr >= 0x81000000 - 0x1e58 * 4)
return NULL;
/* Get pointer to %1e58 SwitchToMainMenu() */
uint16_t const *insns = *(uint16_t const **)(addr + 0x1e58 * 4);
if(addr < 0x80020070 || addr >= 0x81000000)
return NULL;
/* Check up to 150 instructions to find the call to the internal function
SaveAndOpenMainMenu(). This call is in a widget of the shape
mov.l GetkeyToMainFunctionReturnFlag, rX
mov #3, rY
bsr SaveAndOpenMainMenu
mov.b rY, @rX
bra <start of widget>
nop */
for(int i = 0; i < 150; i++) {
/* Match: mov.l @(disp, pc), rX */
if((insns[i] & 0xf000) != 0xd000)
continue;
int rX = (insns[i] >> 8) & 0x0f;
/* Match: mov #3, rY */
if((insns[i+1] & 0xf0ff) != 0xe003)
continue;
int rY = (insns[i+1] >> 8) & 0x0f;
/* Match: bsr @(disp, pc) */
if((insns[i+2] & 0xf000) != 0xb000)
continue;
int disp = (insns[i+2] & 0x0fff);
/* Match: mov.b rX, @rY */
if((insns[i+3] != 0x2000 + (rX << 8) + (rY << 4)))
continue;
/* Match: bra @(_, pc) */
if((insns[i+4] & 0xf000) != 0xa000)
continue;
/* Match: nop */
if(insns[i+5] != 0x0009)
continue;
/* Return the target of the bsr instruction */
uint32_t fun_addr = (uint32_t)&insns[i+2] + 4 + disp * 2;
return (os_menu_function_t *)fun_addr;
}
return NULL;
}
#endif
void gint_osmenu_native(void) void gint_osmenu_native(void)
{ {
__ClearKeyBuffer(); __ClearKeyBuffer();
@ -52,6 +116,22 @@ void gint_osmenu_native(void)
{ {
dst[x] = src[x]; dst[x] = src[x];
} }
/* Try to use the internal function directly if we could figure out its
address by dynamically disassembling */
os_menu_function_t *fun = find_os_menu_function();
if(fun) {
fun();
/* Run an immediate keyboard update, and clear the events so that the
key pressed in order to re-enter the add-in is not also processed in
the application */
extern int keysc_tick(void);
keysc_tick();
clearevents();
return;
}
#endif #endif
/* Mysteriously crashes when coming back; might be useful another time /* Mysteriously crashes when coming back; might be useful another time

View file

@ -4,6 +4,7 @@
#include <gint/keyboard.h> #include <gint/keyboard.h>
#include <gint/drivers/keydev.h> #include <gint/drivers/keydev.h>
#include <gint/display.h>
#include <gint/gint.h> #include <gint/gint.h>
#include <gint/defs/types.h> #include <gint/defs/types.h>
@ -64,10 +65,17 @@ key_event_t getkey_opt(int opt, volatile int *timeout)
e.key == KEY_MENU && !e.shift && !e.alpha) e.key == KEY_MENU && !e.shift && !e.alpha)
{ {
gint_osmenu(); gint_osmenu();
continue; #ifdef FXCG50
if(opt & GETKEY_MENU_DUPDATE)
dupdate();
#endif
if(!(opt & GETKEY_MENU_EVENT))
continue;
e.type = KEYEV_OSMENU;
} }
if(e.type == KEYEV_DOWN || e.type == KEYEV_HOLD) if(e.type != KEYEV_NONE || e.type != KEYEV_UP)
{ {
/* Custom global features */ /* Custom global features */
bool accepted = false; bool accepted = false;

View file

@ -56,7 +56,7 @@ static void keysc_scan(uint8_t *scan)
} }
/* keysc_tick(): Update the keyboard to the next state */ /* keysc_tick(): Update the keyboard to the next state */
static int keysc_tick(void) int keysc_tick(void)
{ {
uint8_t scan[12] = { 0 }; uint8_t scan[12] = { 0 };
keysc_scan(scan); keysc_scan(scan);