etv_term vector

GFA, ASM, STOS, ...

Moderators: simonsunnyboy, Mug UK, Zorro 2, Moderator Team

mikro
Hardware Guru
Hardware Guru
Posts: 3005
Joined: Sat Sep 10, 2005 11:11 am
Location: Kosice, Slovakia
Contact:

Re: etv_term vector

Post by mikro »

ThorstenOtto wrote:BTW your example my_trap1_c_handler for the gemdos trap only works by accident. If you would do anything useful there, and the compiler chooses to save some registers to the stack first, you cannot just call the old vector, because the original saved parameters to that function are at the wrong offset.
What parameters? It's a void function, by definition.
It would also not work when compiled without optimization, because then the call to oldvec is done by a jsr.
Oh really. So instead of jmp old_vec + RTS in old_vec I would have jsr old_vec + RTS in old_vec + RTS in my_trap1_c_handler. Care to explain how this wouldn't work?

EDIT: Got this, too. ;) I guess you meant that if the system trap vector is called by JSR it would return with RTE what is wrong, of course. The updated code fixes this as well so thanks for spotting this!
Last edited by mikro on Sun Apr 21, 2019 5:20 pm, edited 2 times in total.
mikro
Hardware Guru
Hardware Guru
Posts: 3005
Joined: Sat Sep 10, 2005 11:11 am
Location: Kosice, Slovakia
Contact:

Re: etv_term vector

Post by mikro »

mikro wrote:
ThorstenOtto wrote:BTW your example my_trap1_c_handler for the gemdos trap only works by accident. If you would do anything useful there, and the compiler chooses to save some registers to the stack first, you cannot just call the old vector, because the original saved parameters to that function are at the wrong offset.
What parameters? It's a void function, by definition.
OK, now it hit me -- you mean trap call parameters. Even if this wasn't the purpose of this code sample (imagine VBL or IKBD handler as a better use case), it is still good practice to make it as bullet proof as possible, so fixed.
ThorstenOtto
Fuji Shaped Bastard
Fuji Shaped Bastard
Posts: 2372
Joined: Sun Aug 03, 2014 5:54 pm

Re: etv_term vector

Post by ThorstenOtto »

mikro wrote: OK, now it hit me -- you mean trap call parameters.
Yes, exactly. If it was called from supervisor mode (like the desktop does for example), then they are on the current stack.
joska
Hardware Guru
Hardware Guru
Posts: 5342
Joined: Tue Oct 30, 2007 2:55 pm
Location: Florø, Norway
Contact:

Re: etv_term vector

Post by joska »

ThorstenOtto wrote: Fri Nov 17, 2017 4:14 am Never tried in practice, but i think it should. Just make sure you to use Setexc() to install the vector, not just switching to Super() and install the vector by hand.
Out of curiosity - why must Setexc be used? What does it do that just setting 0x408 by hand does not?
Jo Even

VanillaMiNT - Falcon060 - Milan060 - Falcon040 - MIST - Mega STE - Mega ST - STM - STE - Amiga 600 - Sharp MZ700 - MSX - Amstrad CPC - C64
joska
Hardware Guru
Hardware Guru
Posts: 5342
Joined: Tue Oct 30, 2007 2:55 pm
Location: Florø, Norway
Contact:

Re: etv_term vector

Post by joska »

mikro wrote: Sun Apr 21, 2019 1:34 pm To properly test this I had to actually fix unhook_xbra() described in The Atari Compendium to make it multitasking-aware and voila!
I know it's been a long time but do you remember what you did to make it multitasking-aware?

Your unhook:

Code: Select all

static void restore_trap1(void)
{
	XBRA *rx;
	LONG vecadr, *stepadr;

	vecadr = (LONG)Setexc(VEC_GEMDOS, VEC_INQUIRE);
	rx = (XBRA*)(vecadr - sizeof(XBRA));

	/* Special Case: Vector to remove is first in chain. */
	if(rx->xbra_id == XBRA_ID && rx->app_id == my_trap1.app_id
		&& vecadr == (LONG)my_trap1_asm_handler)
	{
		Setexc(VEC_GEMDOS, rx->oldvec);
		return;
	}

	stepadr = (LONG*)&rx->oldvec;
	rx = (XBRA*)((LONG)rx->oldvec - sizeof(XBRA));
	while(rx->xbra_id == XBRA_ID)
	{
		if(rx->app_id == my_trap1.app_id
			&& *stepadr == (LONG)my_trap1_asm_handler)
		{
			*stepadr = (LONG)rx->oldvec;
			break;
		}

		stepadr = (LONG*)&rx->oldvec;
		rx = (XBRA*)((LONG)rx->oldvec - sizeof(XBRA));
	}
}
unhook from The Atari Compendium:

Code: Select all

LONG unhook_xbra( WORD vecnum, LONG app_id )
{
    XBRA *rx;
    LONG vecadr, *stepadr, lret = 0L;
    char *savessp;

    vecadr = Setexc( vecnum, VEC_INQUIRE );
    rx = (XBRA *)(vecadr - sizeof( XBRA ));

    /* Set supervisor mode for search just in case. */
    savessp = Super( SUP_SET );

    /* Special Case: Vector to remove is first in chain. */
    if( rx->xbra_id == 'XBRA' && rx->app_id == app_id )
    {
        Setexc( vecnum, rx->oldvec );
        return vecadr;
    }

    stepadr = (LONG *)&rx->oldvec;
    rx = (XBRA *)((LONG)rx->oldvec - sizeof( XBRA ));
    while( rx->xbra_id == 'XBRA' )
    {
        if( rx->app_id == app_id )
        {
            *stepadr = lret = (LONG)rx->oldvec;
            break;
        }

        stepadr = (LONG *)&rx->oldvec;
        rx = (XBRA *)((LONG)rx->oldvec - sizeof( XBRA ));
    }

    Super( savessp );
    return lret;
}
I see the additional checks of the vector pointer, but why?
Jo Even

VanillaMiNT - Falcon060 - Milan060 - Falcon040 - MIST - Mega STE - Mega ST - STM - STE - Amiga 600 - Sharp MZ700 - MSX - Amstrad CPC - C64
joska
Hardware Guru
Hardware Guru
Posts: 5342
Joined: Tue Oct 30, 2007 2:55 pm
Location: Florø, Norway
Contact:

Re: etv_term vector

Post by joska »

I'm trying to figure out how to use this vector, and while it seems clear from mikro's example I'm not sure if I understand how and when this vector is actually called. I have this very simple test under MiNT using PureC:

I have a simple asm function:

Code: Select all

etv_term_test:
   move.w #7,-(sp) ; Bconout(2,7)
   move.w #2,-(sp)
   move.w #3,-(sp)
   trap #13
   addq.l #6,sp
   rts
Then I have a very simple C program that does...

Code: Select all

void main(void)
{
   Setexc(0x102, etv_term_test);
   while (1);
}
I start this program from a shell, and then kill it with SIGTERM. The bell sounds, as expected. However, if I kill it with SIGKILL the bell does *not* sound, so it appears that this vector is not called in this case. Am I doing something wrong here or is this by design? If by design - why? I think one can assume that the process has installed a handler here for a reason, *not* calling it would almost certainly not improve on the situation that demanded a SIGKILL?
Jo Even

VanillaMiNT - Falcon060 - Milan060 - Falcon040 - MIST - Mega STE - Mega ST - STM - STE - Amiga 600 - Sharp MZ700 - MSX - Amstrad CPC - C64
ThorstenOtto
Fuji Shaped Bastard
Fuji Shaped Bastard
Posts: 2372
Joined: Sun Aug 03, 2014 5:54 pm

Re: etv_term vector

Post by ThorstenOtto »

joska wrote: Thu Feb 03, 2022 7:44 pm Out of curiosity - why must Setexc be used? What does it do that just setting 0x408 by hand does not?
Because MinT (and i think also Magic) handles that specially when you replace the etv_term vector using Setexc. And its always safer and shorter.
mikro
Hardware Guru
Hardware Guru
Posts: 3005
Joined: Sat Sep 10, 2005 11:11 am
Location: Kosice, Slovakia
Contact:

Re: etv_term vector

Post by mikro »

joska wrote: Thu Feb 03, 2022 8:15 pm I know it's been a long time but do you remember what you did to make it multitasking-aware?
[...]
I see the additional checks of the vector pointer, but why?
Yeah, that's the key. In a multitasking environment, you would start two applications (same xbra app id) and then terminate the second one, it could easily destroy the first app's XBRA vector (because it is found as first).

So what I did was to implement also a check for the vector's address. So if app id matches and the vector matches, it must be the vector which this application had installed.

However there's one issue, as Thorsten pointed out, if FreeMiNT ever gets a true virtual memory support, this may no longer be true -- every application's handler may end up at the same (logical) address, making this code again multitasking-unfriendly. I liked the idea of using getpid() instead of the vector address but I must think it through whether it isn't possible to have an issue in TOS with this, i.e. an ACC + an APP could still install the same XBRA ID (user can rename it and use both) so getpid() must have an alternative (in theory app_id but then again, one can have an app which runs as an .ACC and .TTP at the same time; without AES initialisation).
mikro
Hardware Guru
Hardware Guru
Posts: 3005
Joined: Sat Sep 10, 2005 11:11 am
Location: Kosice, Slovakia
Contact:

Re: etv_term vector

Post by mikro »

ThorstenOtto wrote: Thu Feb 03, 2022 9:24 pm
joska wrote: Thu Feb 03, 2022 7:44 pm Out of curiosity - why must Setexc be used? What does it do that just setting 0x408 by hand does not?
Because MinT (and i think also Magic) handles that specially when you replace the etv_term vector using Setexc. And its always safer and shorter.
Yes, MagiC as well. It makes etv_vector much more useful as it is handled on a per-application basis.
mikro
Hardware Guru
Hardware Guru
Posts: 3005
Joined: Sat Sep 10, 2005 11:11 am
Location: Kosice, Slovakia
Contact:

Re: etv_term vector

Post by mikro »

joska wrote: Thu Feb 03, 2022 8:46 pmIf by design - why?
I can't tell you why precisely but it seems it was at least recognised as a problem... 27 years ago. ;)

Another instance of stating this fact:
Er wird vom GEMDOS immer dann durchsprungen, wenn ein GEMDOS-Prozeß beendet wird (Ausnahme unter MiNT: bei SIGKILL).
Interestingly, at least MiNT made this "feature" intentional, an excerpt from an old changelog:
Kill programs with p_term() instead of terminate() (except for signal SIGKILL) so as to allow them a chance to clean up via the term_vec vector.
However the good news is that SIGKILL is not used that much. I tested etv_term's behaviour in Atari800 (SDL to be precise) in a way that FreeMiNT was supposed to kill Atari800 because of missing global memory flag and Atari800 nicely cleaned up its mess via etv_term.
czietz
Hardware Guru
Hardware Guru
Posts: 2090
Joined: Tue May 24, 2016 6:47 pm

Re: etv_term vector

Post by czietz »

mikro wrote: Fri Feb 04, 2022 7:57 am Interestingly, at least MiNT made this "feature" intentional, an excerpt from an old changelog:
Kill programs with p_term() instead of terminate() (except for signal SIGKILL) so as to allow them a chance to clean up via the term_vec vector.
I would argue this is the correct behavior. Recall that SIGKILL is the most drastic method to terminate a process. It is fully intended that the process has no way to react to this. (Imagine, e.g., the etv_term handler of the process ending stuck in an endless loop.)
joska
Hardware Guru
Hardware Guru
Posts: 5342
Joined: Tue Oct 30, 2007 2:55 pm
Location: Florø, Norway
Contact:

Re: etv_term vector

Post by joska »

mikro wrote: Fri Feb 04, 2022 7:37 am Yeah, that's the key. In a multitasking environment, you would start two applications (same xbra app id) and then terminate the second one, it could easily destroy the first app's XBRA vector (because it is found as first).
Yes of course, I did not think of that :) So in reality the XBRA id is worthless as a way of identifying the vector, the vector address itself is enough (and safer).
mikro wrote: Fri Feb 04, 2022 7:37 am However there's one issue, as Thorsten pointed out, if FreeMiNT ever gets a true virtual memory support, this may no longer be true -- every application's handler may end up at the same (logical) address, making this code again multitasking-unfriendly. I liked the idea of using getpid() instead of the vector address but I must think it through whether it isn't possible to have an issue in
If MiNT ever gets true virtual memory support this will be the least of our worries ;)
mikro wrote: Fri Feb 04, 2022 7:57 am Interestingly, at least MiNT made this "feature" intentional, an excerpt from an old changelog:
Ok, so atleast it's not something I'm doing wrong :) I have to decide on what to do here - either split the program into a TSR containing the vector handler and an AES app doing the rest, or letting the AES app do it all. I'm not too fond of the TSR+app solution, as it means that either the TSR or the app must be globally writeable in order for these to communicate. I'm leaning towards the all-on-one solution, as that atleast limits any sources for trouble to my own code.
czietz wrote: Fri Feb 04, 2022 8:31 am I would argue this is the correct behavior. Recall that SIGKILL is the most drastic method to terminate a process. It is fully intended that the process has no way to react to this. (Imagine, e.g., the etv_term handler of the process ending stuck in an endless loop.)
To be honest I don't think not calling etv_term makes it any better. Especially with MP enabled - in that case the memory region containing any vector handlers will be inaccessible immediately when the process is terminated, which almost certainly will leave you with a crashed system. If etv_term is called there is atleast a chance that the process will clean up after itself.
Jo Even

VanillaMiNT - Falcon060 - Milan060 - Falcon040 - MIST - Mega STE - Mega ST - STM - STE - Amiga 600 - Sharp MZ700 - MSX - Amstrad CPC - C64
czietz
Hardware Guru
Hardware Guru
Posts: 2090
Joined: Tue May 24, 2016 6:47 pm

Re: etv_term vector

Post by czietz »

joska wrote: Fri Feb 04, 2022 10:04 am To be honest I don't think not calling etv_term makes it any better. Especially with MP enabled - in that case the memory region containing any vector handlers will be inaccessible immediately when the process is terminated, which almost certainly will leave you with a crashed system. If etv_term is called there is atleast a chance that the process will clean up after itself.
Fortunately, there is an easy fix for that: Just don't kill this kind of process with SIGKILL. :wink:

More seriously: SIGKILL is intended as a method of last resort. You would only use it when a process is so f*cked up that it cannot be assumed to do anything sensible anymore, including cleaning up after itself. Not calling etv_term after SIGKILL might make things worse in your special case, but on the other hand, actually calling it would create a whole new sort of problems. Not only would it contradict the "unix-y" sense of SIGKILL, whereby a process must not get the chance to react to it; it would also leave you without a way to terminate processes that are so broken that they hang in their etv_term handler.
User avatar
mfro
Atari God
Atari God
Posts: 1167
Joined: Thu Aug 02, 2012 10:33 am
Location: SW Germany

Re: etv_term vector

Post by mfro »

Agreed. SIGKILL is the final resort and MiNT shouldn't call anything in the affected process.

Anyway, I think MiNT could (and maybe should) do something else instead. As MiNT knows the process it's killing, it might be able to walk a list of "well known to be a target of redirection" vectors, determine if they are modified (pointing inside the process' memory that just gets killed) and - provided the redirection was done cleanly (using XBRA) - unhook the vectors by resetting them to their previous value?
mikro
Hardware Guru
Hardware Guru
Posts: 3005
Joined: Sat Sep 10, 2005 11:11 am
Location: Kosice, Slovakia
Contact:

Re: etv_term vector

Post by mikro »

mfro wrote: Fri Feb 04, 2022 11:35 amAnyway, I think MiNT could (and maybe should) do something else instead. As MiNT knows the process it's killing, it might be able to walk a list of "well known to be a target of redirection" vectors, determine if they are modified (pointing inside the process' memory that just gets killed) and - provided the redirection was done cleanly (using XBRA) - unhook the vectors by resetting them to their previous value?
Yes, something similar was proposed in that 1995 thread as well. I quite like it (despite the fact it's burdening the OS with something which it really shouldn't be its business but on the other hand, changing system vectors shouldn't be application business either).

Since the kernel knows everything, it could easily supply the xbra unhook function, it could even check whether the application had already called its own unhook function.

This would actually make FreeMiNT quite user friendly in case an app/game/demo freezes (and still adheres to XBRA of course -- but maybe there could be a fallback reset in case it doesn't?); one would get keyboard, screen, sound/dsp lock etc back for every crash... hmm... not bad, not bad at all.

The only problem I see is that someone in the future could misinterpret this as an OS feature and leave out any deinit code entirely, making it crash in TOS and MagiC pretty badly.

EDIT: now I'm thinking about it even more, maybe we could make this really robust. For instance the kernel could supply the XBRA idea as whole, i.e. tracking who's changing which vectors and when the application is unexpectedly terminated (or terminated with faulty deinit code), just fix the chain within the kernel. And for really dirty apps (setting vectors directly, no XBRA, ...) one could have a flag in the PRG header like "save the current state because this app is going to destroy everything and not put it back on exit".
ThorstenOtto
Fuji Shaped Bastard
Fuji Shaped Bastard
Posts: 2372
Joined: Sun Aug 03, 2014 5:54 pm

Re: etv_term vector

Post by ThorstenOtto »

mikro wrote: Fri Feb 04, 2022 11:42 am The only problem I see is that someone in the future could misinterpret this as an OS feature and leave out any deinit code entirely, making it crash in TOS and MagiC pretty badly.
Yes, thats one of the problems proposing such changes now. For clean programs, that would make even more work: detect whether mint handles it, otherwise fall back to code that you would have to write anyway.
User avatar
mfro
Atari God
Atari God
Posts: 1167
Joined: Thu Aug 02, 2012 10:33 am
Location: SW Germany

Re: etv_term vector

Post by mfro »

mikro wrote: Fri Feb 04, 2022 11:42 am... now I'm thinking about it even more, maybe we could make this really robust...
Yes. No. A little more robust, maybe.
MiNT could only reverse the redirects it knows about. That would probably work for trivial programs that e.g. redirect a certain OS call to their own (faster, cleaner, better) code. More complex programs (e.g. think about a screen driver for non-Atari graphics hardware) would probably fail anyway. They would not only redirect the LineA vector to their own routines (which MiNT could reverse) but probably also modify LineA variables (which MiNT had no knowledge about and wouldn't be able to fix), so you most likely end up in a crash anyway.

@ThorstenOtto I don't think clean programs had to do more work - they either get killed by MiNT (then they won't be able to do anything anyway) or not at all (no SIGKILL in TOS, SIGINT equivalent would be CTRL-C in a GEMDOS routine), so the only chance for them to cleanly terminate in TOS would be if they have their own mechanism to do so anyway.
joska
Hardware Guru
Hardware Guru
Posts: 5342
Joined: Tue Oct 30, 2007 2:55 pm
Location: Florø, Norway
Contact:

Re: etv_term vector

Post by joska »

Bringing back this thread... I have successfully used the etv_term vector in a MiNT-only application to restore a system vector. Now I'm working on another project that is supposed to work under both TOS and MiNT (on the Falcon). In this project I'm accessing the Videl directly, and have to restore the Videl state if the program crashes (which is frequently at this stage of development). I'm doing exactly the same as in my MiNT-only application, installs a void C-function with no arguments using Setexc. It works perfectly fine under MiNT, but not under TOS.

The vector is called and it does restore the Videl state (as well as some system variables and the IKBD vector) at exit, so far so good. I can then start another program, but exiting this program results in a bus error. Looks like TOS does not restore the etv_term-vector when the process exits, so I have to do so myself in my etv_term handler but under TOS only. Now the question - restoring the old etv_term vector from the etv_term handler using Setexc appears to work just fine in both TOS and MiNT, but is this safe? I would prefer to do it this way as it means I don't have to do things differently in TOS and MiNT.

Edit: Never mind :) Setexc is a BIOS function, not GEMDOS. So it's perfectly safe to call it from this handler.
Jo Even

VanillaMiNT - Falcon060 - Milan060 - Falcon040 - MIST - Mega STE - Mega ST - STM - STE - Amiga 600 - Sharp MZ700 - MSX - Amstrad CPC - C64
ThorstenOtto
Fuji Shaped Bastard
Fuji Shaped Bastard
Posts: 2372
Joined: Sun Aug 03, 2014 5:54 pm

Re: etv_term vector

Post by ThorstenOtto »

joska wrote: Mon May 23, 2022 7:03 pm restoring the old etv_term vector from the etv_term handler using Setexc appears to work just fine in both TOS and MiNT, but is this safe?
Edit: Never mind :) Setexc is a BIOS function, not GEMDOS. So it's perfectly safe to call it from this handler.
Yes, but it is still not safe to just restore the old value. Consider you have several program that behave this way:
- Program 1 starts and saves etv_term, then sets it to his own routine. Old vector will point to ROM.
- Program 2 starts and saves etv_term, then sets it to his own routine. Old vector will point to Program 1.

- Program 1 exits and restores the vector. But the saved vector of Program 2 still points to Program 1's memory
- Program 2 exits and restores the vector. etv_term will now point to the previously Program 1's memory, which no longer exists.

Any program run after that will most likely crash when exiting.

You can partly avoid that using the XBRA protocol, but that only works when all programs in question use it.
joska
Hardware Guru
Hardware Guru
Posts: 5342
Joined: Tue Oct 30, 2007 2:55 pm
Location: Florø, Norway
Contact:

Re: etv_term vector

Post by joska »

This would only be a problem with Geneva. With MiNT (and MagiC I believe) each process has it's own etv_term vector, which ceases to exist as soon as the process is terminated. Under plain TOS it can't happen - there is only one process running so the process that exits is always the last started as well.

But under Geneva the situation you describe could happen, since there is only one etv_term vector but multiple GEMDOS processes.
Jo Even

VanillaMiNT - Falcon060 - Milan060 - Falcon040 - MIST - Mega STE - Mega ST - STM - STE - Amiga 600 - Sharp MZ700 - MSX - Amstrad CPC - C64
Post Reply

Return to “Coding”