Hello!

I am trying to make it so that when you open my addin, it restarts from the beginning of the program.

I found this routine on casiopeia:

Code:

short*APP_EnableRestart(){
  unsigned int ea;
  unsigned int j;
  short*pEnableRestartFlag;
  ea = *(unsigned int*)0x8002007C;
  ea += 0x1e6e*4;
   
  ea = *(unsigned int*)( ea );
  j = *(unsigned char*)( ea + 1 );
  j *= 4;
  j = ( ea + j + 4 ) & 0xFFFFFFFC;
  pEnableRestartFlag = (short*)(*( unsigned int*)( j ) + 8 );

  if ( pEnableRestartFlag ) *pEnableRestartFlag = 1;
  return pEnableRestartFlag;
}


It was designed for OS 2/2.02, but it seems to work on the fx-CG50 Emulator.
I was just wondering if this is safe to run on hardware (OS 3.50), or if anyone has experience with this of a similar function?
Does this function do what you need? It's designed so that if you quit by returning from main() the user can re-select the add-in and it restarts (instead of flickering for a second and staying on the main menu). You can achieve this by just putting a loop around main and invoking the main menu instead of returning.

I don't understand exactly what you're trying to do, more details would be welcome.

Anyway, so this function here is disassembling SetQuitHandler(), assuming the code has some particular shape, and extracts the address of a certain flag from it. One way to check whether it would work is to disassemble a recent version of the syscall (OS 3.60 below) and see if the procedure makes sense:

Code:
cg_3.60 @ 0x80000000> d %1e6e

<%1e6e SetQuitHandler>
 80365190:  d643   mov.l   0x8c0a3040, r6
 80365192:  6260   mov.b   @r6, r2
 80365194:  2228   tst     r2, r2
 80365196:  8902   bt      <8036519e>
 80365198:  d242   mov.l   0x8c0cbd28, r2
 8036519a:  000b   rts
 8036519c:  2242   mov.l   r4, @r2
 8036519e:  d642   mov.l   0x8c0cbd24, r6
 803651a0:  000b   rts
 803651a2:  2642   mov.l   r4, @r6

This might be hard to see, but the APP_EnableRestart() function computes the target of the first mov.l, which my disassembler prints as 0x8c0a3040, and then says the return flag is 8 bytes further, which here would be r6+8.

The flag isn't used by SetQuitHandler(), but it is reasonable to believe that the disassembly step still works; hopefully the flag is still at the same distance from whatever r6 is. The address is never mentioned directly:

Code:
cg_3.60 @ 0x80000000> af4 0x8c0a3048
No occurrence found.

But it could be accessed from a nearby pointer; I don't know how SimLo found the flag so more research would be done to properly check that it still works.

My personal opinion is you can run it without worries; I've corrupted more RAM than I care to count and never bricked anything, this is fairly safe in comparison. I believe there's a very good chance it will still work. (Whether it does what you need is a different question!)
Lephe wrote:
Does this function do what you need? It's designed so that if you quit by returning from main() the user can re-select the add-in and it restarts (instead of flickering for a second and staying on the main menu). You can achieve this by just putting a loop around main and invoking the main menu instead of returning.

I don't understand exactly what you're trying to do, more details would be welcome.

Anyway, so this function here is disassembling SetQuitHandler(), assuming the code has some particular shape, and extracts the address of a certain flag from it. One way to check whether it would work is to disassemble a recent version of the syscall (OS 3.60 below) and see if the procedure makes sense:

Code:
cg_3.60 @ 0x80000000> d %1e6e

<%1e6e SetQuitHandler>
 80365190:  d643   mov.l   0x8c0a3040, r6
 80365192:  6260   mov.b   @r6, r2
 80365194:  2228   tst     r2, r2
 80365196:  8902   bt      <8036519e>
 80365198:  d242   mov.l   0x8c0cbd28, r2
 8036519a:  000b   rts
 8036519c:  2242   mov.l   r4, @r2
 8036519e:  d642   mov.l   0x8c0cbd24, r6
 803651a0:  000b   rts
 803651a2:  2642   mov.l   r4, @r6

This might be hard to see, but the APP_EnableRestart() function computes the target of the first mov.l, which my disassembler prints as 0x8c0a3040, and then says the return flag is 8 bytes further, which here would be r6+8.

The flag isn't used by SetQuitHandler(), but it is reasonable to believe that the disassembly step still works; hopefully the flag is still at the same distance from whatever r6 is. The address is never mentioned directly:

Code:
cg_3.60 @ 0x80000000> af4 0x8c0a3048
No occurrence found.

But it could be accessed from a nearby pointer; I don't know how SimLo found the flag so more research would be done to properly check that it still works.

My personal opinion is you can run it without worries; I've corrupted more RAM than I care to count and never bricked anything, this is fairly safe in comparison. I believe there's a very good chance it will still work. (Whether it does what you need is a different question!)


Thanks for the explanation.

Basically, I want the user to be able to re-select the addin and it restarts it from the first line of code instead of continuing where it left off (i.e. flickering and returning).
A loop would work, but if you want to force the user to exit the application (i.e. return), you can't then start the loop again when it opens.
So, this function does do what I want - I was thinking of putting it on WikiPrizm in case others need it.
Quote:
Basically, I want the user to be able to re-select the addin and it restarts it from the first line of code instead of continuing where it left off (i.e. flickering and returning).
A loop would work, but if you want to force the user to exit the application (i.e. return), you can't then start the loop again when it opens.

I'm not sure how this is different from replacing exit() with a longjmp() and doing the loop trick. I do agree longjmp() is less appealing, but at the same time this disassembling magic is not secure enough in my opinion. Anyway that's up to preference, I guess - feel free to add it on WikiPrizm.
Lephe wrote:
Quote:
Basically, I want the user to be able to re-select the addin and it restarts it from the first line of code instead of continuing where it left off (i.e. flickering and returning).
A loop would work, but if you want to force the user to exit the application (i.e. return), you can't then start the loop again when it opens.

I'm not sure how this is different from replacing exit() with a longjmp() and doing the loop trick. I do agree longjmp() is less appealing, but at the same time this disassembling magic is not secure enough in my opinion. Anyway that's up to preference, I guess - feel free to add it on WikiPrizm.

Sorry - I had never heard of longjmp()? I assumed you meant literally putting my main() function in a while (1) loop.
Could you give an example of how to use this function?
If you only exit by returning through main(), you indeed only need to use a loop :

Code:
int main(void)
{
    while(1) {
        your_current_main();
        invoke_main_menu();
    }
}

... although it's a bit unfair on my end to say this restarts your add-in because it doesn't re-initialize global variables, you might want to watch out for that.

Invoking the main menu basically consists in starting GetKeyWait() and injecting the MENU key from a timer, here's most of the code for this.

However if you're used to leaving from any point of the add-in with exit(), then you can't do that anymore, and so you need to find a way to jump from anywhere to the main() function above. A long jump can do that:

Code:
#include <setjmp.h>
jmp_buf main_env;

int main(void)
{
    int rc;

    while(1) {
        rc = setjmp(&main_env);

        if(rc == 0)
            your_current_main();

        invoke_main_menu();
    }
}

And then you would replace exit() with longjmp(&main_env, 1).

If you've never seen them, long jumps (which are a part of standard C) is basically a way to teleport through control flow; it's an interprocedural goto of sorts. Not something to abuse, obviously.

I use that technique in gint; it's not the prettiest but on the upside it's not specific to the fx-CG and it has zero risk of changing through OS updates.
Lephe wrote:
If you only exit by returning through main(), you indeed only need to use a loop :

Code:
int main(void)
{
    while(1) {
        your_current_main();
        invoke_main_menu();
    }
}

... although it's a bit unfair on my end to say this restarts your add-in because it doesn't re-initialize global variables, you might want to watch out for that.

Invoking the main menu basically consists in starting GetKeyWait() and injecting the MENU key from a timer, here's most of the code for this.

However if you're used to leaving from any point of the add-in with exit(), then you can't do that anymore, and so you need to find a way to jump from anywhere to the main() function above. A long jump can do that:

Code:
#include <setjmp.h>
jmp_buf main_env;

int main(void)
{
    int rc;

    while(1) {
        rc = setjmp(&main_env);

        if(rc == 0)
            your_current_main();

        invoke_main_menu();
    }
}

And then you would replace exit() with longjmp(&main_env, 1).

If you've never seen them, long jumps (which are a part of standard C) is basically a way to teleport through control flow; it's an interprocedural goto of sorts. Not something to abuse, obviously.

I use that technique in gint; it's not the prettiest but on the upside it's not specific to the fx-CG and it has zero risk of changing through OS updates.

Okay, so in this example I only need to put it in a loop.

However, it doesn't seem to be working for me.
I exit to menu, but when I open the add-in again (without opening anything else), it shows the last screen it was on. Example:


Code:

int main(void) {
  while (1) {
    Bdisp_AllClr_VRAM();

    PrintXY(1, 1, "  Text 1", 0, 0);

    int key;
    GetKey(&key);

    PrintXY(1, 2, "  Text 2", 0, 0);

    GetKey(&key);

    int timer_num = Timer_Install(
        0, []() { Keyboard_PutKeycode(4, 9, 0); }, 1);
    Timer_Start(timer_num);
    GetKey(&key);
    Timer_Deinstall(timer_num);
  }
}


This shows text 1, then text 2 (after a keypress), then exits after a keypress.
After re-opening the addin it shows both text 1 and text 2 and quickly exits to the menu again.

Is there any way I can do this without the APP_EnableRestart function?
You can't trick the timer logic like that, you need to actually stop the timer and deinstall it from the callback. Here is a working example (in C).


Code:
#include <fxcg/display.h>
#include <fxcg/keyboard.h>

int timer_num;

void func(void)
{
   Keyboard_PutKeycode(4, 9, 0);
   Timer_Stop(timer_num);
   Timer_Deinstall(timer_num);
}

int main(void) {
  while (1) {
    Bdisp_AllClr_VRAM();

    PrintXY(1, 1, "  Text 1", 0, 0);

    int key;
    GetKey(&key);

    PrintXY(1, 2, "  Text 2", 0, 0);

    GetKey(&key);

    timer_num = Timer_Install(0, func, 1);
    Timer_Start(timer_num);
    GetKey(&key);
  }
}

Quote:
Is there any way I can do this without the APP_EnableRestart function?

None that I am aware of, unfortunately! This same problem was known on the fx-9860G series, and I don't believe I've ever heard of a reliable/properly interfaced way of accessing the flag.

Just to be clear, I don't think APP_EnableRestart() is bad, just that I think disassembly-based methods should have more validation/safety built-in to at least try to detect when something goes wrong ^^
Lephe wrote:
You can't trick the timer logic like that, you need to actually stop the timer and deinstall it from the callback. Here is a working example (in C).


Code:
#include <fxcg/display.h>
#include <fxcg/keyboard.h>

int timer_num;

void func(void)
{
   Keyboard_PutKeycode(4, 9, 0);
   Timer_Stop(timer_num);
   Timer_Deinstall(timer_num);
}

int main(void) {
  while (1) {
    Bdisp_AllClr_VRAM();

    PrintXY(1, 1, "  Text 1", 0, 0);

    int key;
    GetKey(&key);

    PrintXY(1, 2, "  Text 2", 0, 0);

    GetKey(&key);

    timer_num = Timer_Install(0, func, 1);
    Timer_Start(timer_num);
    GetKey(&key);
  }
}

Quote:
Is there any way I can do this without the APP_EnableRestart function?

None that I am aware of, unfortunately! This same problem was known on the fx-9860G series, and I don't believe I've ever heard of a reliable/properly interfaced way of accessing the flag.

Just to be clear, I don't think APP_EnableRestart() is bad, just that I think disassembly-based methods should have more validation/safety built-in to at least try to detect when something goes wrong ^^

Ah, I didn't realise that was how timers worked - thanks.
My program is working now, thanks for helping out.
Lephe wrote:
You can't trick the timer logic like that, you need to actually stop the timer and deinstall it from the callback. Here is a working example (in C).


Code:
#include <fxcg/display.h>
#include <fxcg/keyboard.h>

int timer_num;

void func(void)
{
   Keyboard_PutKeycode(4, 9, 0);
   Timer_Stop(timer_num);
   Timer_Deinstall(timer_num);
}

int main(void) {
  while (1) {
    Bdisp_AllClr_VRAM();

    PrintXY(1, 1, "  Text 1", 0, 0);

    int key;
    GetKey(&key);

    PrintXY(1, 2, "  Text 2", 0, 0);

    GetKey(&key);

    timer_num = Timer_Install(0, func, 1);
    Timer_Start(timer_num);
    GetKey(&key);
  }
}

Quote:
Is there any way I can do this without the APP_EnableRestart function?

None that I am aware of, unfortunately! This same problem was known on the fx-9860G series, and I don't believe I've ever heard of a reliable/properly interfaced way of accessing the flag.

Just to be clear, I don't think APP_EnableRestart() is bad, just that I think disassembly-based methods should have more validation/safety built-in to at least try to detect when something goes wrong ^^

It seems that using this approach forces the user to press a key after re-entering the application before it restarts. I presume this is because of the GetKey(&key) call at the end, but I may also be doing it wrong somehow.
Ah ! No, that's correct. I tend to forget about this problem because gint unknowingly works around it. I think you could cheese that using the timer, by injecting another key once you detect that you came back. It's a bit edgy though, sorry about that; I remembered this method being more consistent.
Lephe wrote:
Ah ! No, that's correct. I tend to forget about this problem because gint unknowingly works around it. I think you could cheese that using the timer, by injecting another key once you detect that you came back. It's a bit edgy though, sorry about that; I remembered this method being more consistent.

I tried injecting a key after the MENU one, but this just pressed that key in the menu.
The only way I found to do it was to use GetKeyWait_OS instead of GetKey:

Code:

int column, row;
unsigned short keycode;
GetKeyWait_OS(&column, &row, 2, 0, 0, &keycode);

It handles the MENU key, opening the menu straight away, but when you go back in it times out (the timeout is 0), and powers off.

Thanks for all your help.
Quote:
It handles the MENU key, opening the menu straight away, but when you go back in it times out (the timeout is 0), and powers off.

That's handy! I should try it in gint. I think it's not supposed to work because without a timeout it would return before you're able to inject the MENU key. I'm glad to see it does though. Smile
Lephe wrote:
Quote:
It handles the MENU key, opening the menu straight away, but when you go back in it times out (the timeout is 0), and powers off.

That's handy! I should try it in gint. I think it's not supposed to work because without a timeout it would return before you're able to inject the MENU key. I'm glad to see it does though. Smile

Yes, I thought that would happen originally, but I presume it works because it is run just after the timer is started.
SimLo's original version on the fx-9860G does indicate that you need GetKeyWait() to start before injecting, otherwise it doesn't work. Well, admittedly, I've never seen it fail in that situation; maybe it just became viable after OS 2 or something. This would be convenient for me as that extra key problem is still half-present in my add-ins (the application gets the first key press as if GetKeyWait() had finished, but you still can't update the display before a key is pressed).
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement