VOGONS


First post, by Rav

User metadata
Rank Member
Rank
Member

Hi, I did made an option rom for my 486 not so long ago and slowly improve it to add features.

So far it does :
* Setup the ALI1429 chipset properly (memory timing, cache stuff, etc, etc)
* Setup the Cyrix 5x86 register to optimal one
* Add Keyboard hook so I can press ctrl-alt-pageup/down to enable or disable cyrix BTB

Everything work everywhere! Except....

The keyboard hook when I boot with QEMM instead of himem/emm386, in that case QEMM bark with a Exception #6 error.
Keyboard hook code attached, I don't know if someone can help me with that. The crash does not append when dos&qemm load, it append at the moment I press any key after qemm is loaded.

The initial 0x09 IVT pointed to 0xF000E987, crash is as F000E991, really not far after.

Crash :

The attachment crash.png is no longer available
newInt9:
cli

push ax ; Save registers
push bx
push es

mov ax, 0x40
mov es, ax

mov al, byte [es:0x17] ; Check if Ctrl+Alt are pressed
and al, 0x0C
cmp al, 0x0C
jne .exit_int9h_hook

in al, 0x60 ; Read keyboard scancode
cmp al, 0x49 ; PgUp check
je .enable_btb
cmp al, 0x51 ; PgDn check
je .disable_btb
jmp .exit_int9h_hook

.enable_btb:
; Enable registers map trap mode MASK = 0F, Register = 0xC3, 0x00 = disabled, 0x10 = enabled
; We have to enable that to write in the CPU register instead of the chipset registers
mov bl,0x0F
mov al,0xC3
mov ah,0x10
call setRegister_from_hook
; Now we disable LOOP_EN and RTSK_EN
mov bl,0xFA
mov al,0x20
mov ah,0x00
call setRegister_from_hook
; We disabled BWRT
mov bl,0xBF
mov al,0xC2
mov ah,0x00
call setRegister_from_hook
; We enable BTB_EN
mov bl,0xFD
mov al,0x20
mov ah,0x02
call setRegister_from_hook
; Enable registers map trap mode MASK = 0F, Register = 0xC3, 0x00 = disabled, 0x10 = enabled
; We have to DISABLE that because we are done and put it back to default
mov bl,0x0F
mov al,0xC3
mov ah,0x00
call setRegister_from_hook
jmp .exit_int9h_hook

.disable_btb:
; Enable registers map trap mode MASK = 0F, Register = 0xC3, 0x00 = disabled, 0x10 = enabled
; We have to enable that to write in the CPU register instead of the chipset registers
mov bl,0x0F
mov al,0xC3
mov ah,0x10
call setRegister_from_hook
; We disable BTB_EN
Show last 29 lines
       mov bl,0xFD
mov al,0x20
mov ah,0x00
call setRegister_from_hook
; Now we enable LOOP_EN and RTSK_EN
mov bl,0xFA
mov al,0x20
mov ah,0x05
call setRegister_from_hook
; We enable BWRT
mov bl,0xBF
mov al,0xC2
mov ah,0x40
call setRegister_from_hook
; Enable registers map trap mode MASK = 0F, Register = 0xC3, 0x00 = disabled, 0x10 = enabled
; We have to DISABLE that because we are done and put it back to default
mov bl,0x0F
mov al,0xC3
mov ah,0x00
call setRegister_from_hook

.exit_int9h_hook:
pop es
pop bx
pop ax
sti
pushf
call far [cs:OldInt9]
iret

Reply 1 of 11, by kingcake

User metadata
Rank Oldbie
Rank
Oldbie

My first guess is that it's because QEMM hooks the keyboard to intercept ctrl-alt-del for the quickboot feature.

Try adding "BE:N" to your QEMM386.SYS line to disable that feature.

Reply 2 of 11, by Deunan

User metadata
Rank l33t
Rank
l33t
Rav wrote on 2024-06-11, 14:22:

The initial 0x09 IVT pointed to 0xF000E987, crash is as F000E991, really not far after.

If the crash happens outside your code, and no matter what key is pressed, I would conclude you are either jumping to wrong place or there is some code that you've upset.

Dump the memory fragment and disassemble it (or use DOS debug to disassemble it directly). See what code is located at F000E987, how do you end up at F000E991?
In general doing STI is a bad idea, because you don't know what code is being executed before and after yours. You should not be re-enabling interrupts inside a handler until you've cleared the flag that caused it in the first place. In other words, rather than doing CLI, code here, STI do PUSHF, CLI, code here, POPF. This will restore the interrupt flag as it was, while making sure your code runs with interrupts disabled.

Reply 3 of 11, by jakethompson1

User metadata
Rank l33t
Rank
l33t

F000:E991: FF FF FF FF FF FF is obviously not a valid instruction thus the exception 6 (invalid opcode)
maybe use debug to dump F000:E987 with/without qemm and with/without your option ROM.

Reply 4 of 11, by mkarcher

User metadata
Rank l33t
Rank
l33t

Probably you use the "stealth" feature of QEMM, most likely in its more aggressive variant "ST:M". The stealth feature reclaims nearly the whole ROM space as UMBs, and only maps the ROM content into that address space when a BIOS interrupt is called. While no interrupt handler is running, F000:E991 contains UMB data, not the BIOS. I suppose QEMM might just map your option ROM, but not the original mainboard BIOS ROM while the int 9 / IRQ1 handler is running. While QEMM can catch interrupt invocations, QEMM can not catch your FAR call into the BIOS ROM address space.

Try running the "full analysis" option offered in OPTIMIZE (a utility shipped with QEMM) while your option ROM is installed. This option is meant to analyze your system and automatically configure the stealth feature and some other QEMM options in the best way possible that does not crash your system. You might also be able to find the technote archive containing technical documentation about QEMM. It has a file called STEALTH.TEC that is dedicated to explain how the stealth feature works and how to deal with systems that don't work out of the box with QEMM.

Reply 5 of 11, by Rav

User metadata
Rank Member
Rank
Member
mkarcher wrote on 2024-06-11, 20:48:

Probably you use the "stealth" feature of QEMM, most likely in its more aggressive variant "ST:M". The stealth feature reclaims nearly the whole ROM space as UMBs, and only maps the ROM content into that address space when a BIOS interrupt is called. While no interrupt handler is running, F000:E991 contains UMB data, not the BIOS. I suppose QEMM might just map your option ROM, but not the original mainboard BIOS ROM while the int 9 / IRQ1 handler is running. While QEMM can catch interrupt invocations, QEMM can not catch your FAR call into the BIOS ROM address space.

Try running the "full analysis" option offered in OPTIMIZE (a utility shipped with QEMM) while your option ROM is installed. This option is meant to analyze your system and automatically configure the stealth feature and some other QEMM options in the best way possible that does not crash your system. You might also be able to find the technote archive containing technical documentation about QEMM. It has a file called STEALTH.TEC that is dedicated to explain how the stealth feature works and how to deal with systems that don't work out of the box with QEMM.

Yes, you are right

I disassembled the F000:E987 region with debug as recommended and noticed that with qemm, it was totally different.
Then I remembered about that stealth mode (I have ST:M parameter set), reading the manual right now.
Will try to do the optimize, maybe it will just detect it, thanks!

Edit : Optimize don't like it, from what I understand I can exclude memory zone affected by that feature

Reply 6 of 11, by Rav

User metadata
Rank Member
Rank
Member

Got it working

I initially had exclusion for FB00-FBFF and FF00-FFFF
I changed that for F800-FFFF and the problem is gone

(The instruction located at F000-E987 is JMP 80C0)
Now just running optimize to reconfigure where it place the TSR.
I will still get 634KB of free conventional memory!

Reply 7 of 11, by mkarcher

User metadata
Rank l33t
Rank
l33t

Citing the STLTECH.TEC, the more low-level technote about stealth technology:


Q. Which Stealth strategy is preferable?
----------------------------------------
Since ST:M is capable of mapping almost all ROMs out of DOS's address space,
and thus leaves you with much more High RAM, it is the better of the two
options. ST:F should only be needed on a very small number of systems; its
object is to ensure compatibility with machines that have ROMs that jump to
each other without using an interrupt to do so.

Your setup is exactly what the last sentence describe: "ROMs that jump to each other without using an interrupt to do so". Your way of excluding the BIOS INT9 handler is a fine way of solving the problem, and most likely the most memory efficient one. Using ST:F instead of ST:M will work too, but that will cost you even more UMBs. Another way of ensuring QEMM compatiblity is not to use a FAR CALL to jump to the original INT9, but to use some interrupt vector like INT49 to store the old INT9 vector, and then call int using "INT 49h". This will be fully stealth compatible, but obviously be incompatible with other software that expects INT 49h to be freely usable.

Finally, I want to point out that hooking INT15, AH=4F is oftentimes a better approach than hooking INT9 to implement special hotkeys. INT15/4F is still supported if MSDOS KEYB.COM is loaded. Hooking INT 15, AH=4F in stead of INT9 will not directly solve your QEMM problem, though. It will likely make it worse, because you would then need to exclude everything related to INT15, which may be sprinkled around the whole F000 segment. You can again ensure stealth compatibility by calling from your ROM into the main BIOS ROM using an interrupt vector.

Reply 8 of 11, by Deunan

User metadata
Rank l33t
Rank
l33t
mkarcher wrote on 2024-06-11, 21:49:

use some interrupt vector like INT49 to store the old INT9 vector, and then call int using "INT 49h"

If you are going that far why not just restore the original vector, call it with int, then install your own handler again when the call returns? It might have a bit of a performance penatly when V86 is enabled (and memory manager traps writes to that page) but shouldn't be too costly. Obviously using direct access to interrupt table, not going through DOS syscall. It'll only trigger on a keypress anyway.

Reply 9 of 11, by jakethompson1

User metadata
Rank l33t
Rank
l33t
Deunan wrote on 2024-06-11, 22:18:
mkarcher wrote on 2024-06-11, 21:49:

use some interrupt vector like INT49 to store the old INT9 vector, and then call int using "INT 49h"

If you are going that far why not just restore the original vector, call it with int, then install your own handler again when the call returns? It might have a bit of a performance penatly when V86 is enabled (and memory manager traps writes to that page) but shouldn't be too costly. Obviously using direct access to interrupt table, not going through DOS syscall. It'll only trigger on a keypress anyway.

I assume because when QEMM starts, it hooks every vector pointing into a stealth ROM area. So as to do the "bank switch" between ROM and UMBs when needed.
If you start unhooking and rehooking things while in the middle of an ISR, QEMM wouldn't see it.
Or, maybe it would, supposing you use INT 21h AH=25h to hook rather than doing it via direct manipulation of the interrupt vectors?

Reply 10 of 11, by Rav

User metadata
Rank Member
Rank
Member

I went with the mkarcher way, using int 49h.
Looking at the IVT there is many other available after that too in the event I need to choose a different one.

It work perfectly!
And it fixed another minor issue, I had "OldInt9 dd 0xF000E987" in the code... Yeah, it was hardcoded. Somehow using mov to copy the int9 value into Oldint9 did leave everything to 0000... I did assume it was because it was in "Rom", Yet initially did hope that it would let me do that because shadow was enabled... Anyway, now that I store the old int9 address in int49, I don't have it hardcoded anymore.

Reply 11 of 11, by Deunan

User metadata
Rank l33t
Rank
l33t
jakethompson1 wrote on 2024-06-11, 22:43:

If you start unhooking and rehooking things while in the middle of an ISR, QEMM wouldn't see it.
Or, maybe it would, supposing you use INT 21h AH=25h to hook rather than doing it via direct manipulation of the interrupt vectors?

I admit I have no idea how QEMM works internally, I've never really used it much so I'm unfamiliar with this "stealth" tech. But since half of the stuff these managers do require paging to be enabled anyway, if it was me writing QEMM I'd just mark the physical page where IVs reside as read-only and thus trap any attempts to change them. But it's a moot point, that code would need RAM of some sort to store the original IV and doing it properly requires stealing some from DOS. If no more RAM is otherwise needed then using some other "free" vector to store that data is also a nice trick, and at that point why bother with swapping the data if the new vector can be called as-is.