VOGONS


First post, by clb

User metadata
Rank Oldbie
Rank
Oldbie

When CRT Terminator is used with an ISA VLB graphics card, and the motherboard BIOS does not support configuring the VGA palette snoop option, a TSR "PALTSR" must be used to mirror the VGA palette to CRT Terminator.

Currently the TSR that we use is based on hooking the PC timer interrupt, in order to poll and periodically mirror the palette:

https://github.com/juj/crt_terminator/blob/9e … TSR.CPP#L87-L88

This works well in many games, e.g. Doom.

But some games are programmed to play a bit dirty: they hook the timer interrupt themselves, without chaining. This stops PALTSR interrupts from firing when the game runs, and the palette mirroring no longer takes place.

So the question I have to Vogons PC experts: are there other ways that one might be able to generate a periodically triggering interrupt, that would run at about 20Hz-60Hz or similar, that one might be able to try with these kinds of rogue behaving games? Something that these games might not be stealing for themselves?

Any tips welcome!

Reply 1 of 10, by BloodyCactus

User metadata
Rank Oldbie
Rank
Oldbie

you can hook other interrupts, and periodically check your timer interrupt is still hooked.

I would hook int 0x21, its something that everything is going to use more or less. I would do something like recording the last tick of your timer interrupt, and when int0x21 is called, test the current timer to see time elapsed, if its too much, you know your timer isnt firing and can possible re-hook.

i know when i wrote dos games i would hook timer+keyboard and not chain back.

you will probably have same problems with protected mode stuff not chaining back, or only chaining when they switch from pmode to rmode or something sometimes. (depending how they reflect ints and do vm86 mode).

--/\-[ Stu : Bloody Cactus :: [ https://bloodycactus.com :: http://kråketær.com ]-/\--

Reply 2 of 10, by Grzyb

User metadata
Rank l33t
Rank
l33t

Have a look at the /STEALTH option of PCXDUMP...

Kiełbasa smakuje najlepiej, gdy przysmażysz ją laserem!

Reply 3 of 10, by Tiido

User metadata
Rank l33t
Rank
l33t

You can use IRQ from the RTC, it should not be touched by anything.

EDIT: That stealth way is excellent ~

T-04YBSC, a new YMF71x based sound card & Official VOGONS thread about it
Newly made 4MB 60ns 30pin SIMMs ~
mida sa loed ? nagunii aru ei saa 😜

Reply 4 of 10, by digger

User metadata
Rank Oldbie
Rank
Oldbie

That's indeed a neat trick. However, it does make one wonder if any game developers ever used that same trick themselves, since that would render the stealth solution useless as well in those games.

Reply 5 of 10, by clb

User metadata
Rank Oldbie
Rank
Oldbie

Thanks for the info here - I've been looking into STEALTH mode, and that is definitely something I need to migrate to, although couldn't get it quite working on a first try.

Another question I have had in this area is: is it possible to hook the INT 10h, AH = 00h call "Set Video Mode", and execute some code *after* running the original BIOS code?

I think I can easily hook into INT 10h, AH = 00h and run something before running the original INT 10h vector.. but when I chain to the original vector, it won't return back, so I don't have an obvious way to run code afterwards.

What I would like to do is reprogram some VGA adapter registers after each video mode change, so run some code once after each occurrence of INT 10h, AH = 00h in a game.. but not quite sure if that might be feasible?

Reply 6 of 10, by jmarsh

User metadata
Rank Oldbie
Rank
Oldbie

You would need to set up the stack appropriately (with a return address, segment and flags) so that the original vector's IRET ends up "returning" to your code.

Reply 7 of 10, by DaveDDS

User metadata
Rank Oldbie
Rank
Oldbie
jmarsh wrote on 2025-03-18, 12:06:

You would need to set up the stack appropriately (with a return address, segment and flags) so that the original vector's IRET ends up "returning" to your code.

I think you might do it a bit simpler than that - just save the original return address, and patch it to point
at your code - then jump to the IRQ handler, when it "returns" to you - do your stuff then jump back to the
actual return address

Dave ::: https://dunfield.themindfactory.com ::: "Daves Old Computers"->Personal

Reply 8 of 10, by jmarsh

User metadata
Rank Oldbie
Rank
Oldbie

Then how do you return from your handler? The old handler will have already popped off the needed data so you can't use IRET... You can't return via a branch instead because you need to restore flags and the original segment atomically.

Reply 9 of 10, by DaveDDS

User metadata
Rank Oldbie
Rank
Oldbie
jmarsh wrote on 2025-03-18, 12:37:

Then how do you return from your handler? The old handler will have already popped off the needed data so you can't use IRET... You can't return via a branch instead because you need to restore flags and the original segment atomically.

My idea was a bit overly simplified - when the handler returns to you, it will have restores the flags etc.
You would save them (and any other registers you need) do you stuff, restore and inter-segment jump
back to the original caller - prob easiest way to do that would be to save original-return/flags/registers
to a "fake" IRET stack and just IRET at the end of your code.

I still think it would be better than making the fake stack at the beginning - that would need twice as
much stack space, and you would have to copy all entries for registers you don't actually use.

If you have an unused int vector available that you can point at your code - you could prob make it all
dead easy - patch original stack to point to you, launch original handler, back to you, "Special" INT, patch
stack to point to original caller - do "stuff", IRET - I've prob. missed some details - it has been years since
I've done such low level/tricky x86 code...

Dave ::: https://dunfield.themindfactory.com ::: "Daves Old Computers"->Personal

Reply 10 of 10, by BloodyCactus

User metadata
Rank Oldbie
Rank
Oldbie

trap int 0x10, saving original address

when new int 0x10 comes in, calls your code, cmp ah=0, if so, you do pushf/call d[old_address], which is what the old int10 needs to return, you then do your stuff.

--/\-[ Stu : Bloody Cactus :: [ https://bloodycactus.com :: http://kråketær.com ]-/\--