Page 1 of 3
Why does my subroutine crash when called from an ISR?
Posted: Tue Aug 11, 2015 3:14 pm
by onlyonemac
I've got a subroutine which, when called normally, executes fine and does not crash, but when I call it from an ISR, the system has some fault or another (not sure which kind, but it causes the machine to reset itself).
The subroutine is called "beep":
Code: Select all
beep:
inb al, #0x0061
or al, #0x03
outb #0x0061, al
mov cx, #0xFFFF
mov dx, #0x0FFF
mov bx, #0x0000
loopba:
mov ax, #0x0000
loopaa:
inc ax
cmp ax, cx
jne loopaa
inc bx
cmp bx, dx
jne loopba
inb al, #0x0061
and al, #0xFC
out #0x0061, al
ret
When called from this ISR, the machine completes the beep (as in, the beep is continued for the same duration so it is not crashing part of the way through the beep) and then immediately resets itself:
Code: Select all
call #0x0008:#beep+0x00007C00
push ax
mov al, #0x20
outb 0x20, al
pop ax
iretd
This is in protected mode, without paging. The segments and addresses are all correct, as the "beep" subroutine is executed. I should maybe check if the crash is before the subroutine returns or after (back in the ISR), however I do not have time to check that now so will report back if/when I do.
Re: Why does my subroutine crash when called from an ISR?
Posted: Tue Aug 11, 2015 3:29 pm
by kzinti
To which IRQ are you hooking your ISR? Some processor interrupts will push a status code that you will need to pop before executing iretq.
Also, you aren't saving any registers and trashing a bunch of them...
Re: Why does my subroutine crash when called from an ISR?
Posted: Tue Aug 11, 2015 4:17 pm
by Brendan
Hi,
The main reason this crashes is because you're using a "far call" (which pushes "return CS:EIP" on the stack) and then a "near ret" (which only pops a "return EIP" from the stack). This leaves a spare "return CS" to be left on the stack, which causes the IRETD to fail. Either change the call from "call #0x0008:#beep+0x00007C00" to "call #beep+0x00007C00"; or change the return from "ret" to "retf".
Also note that kiznit is correct - there are at least 2 more reasons why this could/would cause a crash.
Cheers,
Brendan
Re: Why does my subroutine crash when called from an ISR?
Posted: Wed Aug 12, 2015 3:49 am
by onlyonemac
@kiznit: The ISR is hooked to the keyboard interrupt. Furthermore, if I remove the call to the subroutine, the ISR is (I assume) called when I press a key but does not do anything - the machine does not reset then. Thus the problem is not with returning from the ISR. Also the trashing of registers is currently not a problem, as I have the processor sitting in an inifinite jmp loop for now while I debug the ISR. Thus the value of the registers does not matter.
@Brenden: I use the same calling style from "normal" code and there is no problem. Furthermore, I just tested calling the subroutine twice before returning from the ISR and it beeps twice as it should. Thus the problem is not with the subroutine in any way, because it is able to return and be re-called without a crash.
Posted: Wed Aug 12, 2015 3:56 am
by onlyonemac
I just tried changing the ret to a retf and now it beeps twice when I press a key, does not reset, but does not beep if I then press another key. (To me this sounds like I am not acknowledging the interrupt correctly, but I was sure that I was.) I also don't understand why the calls were returnning properly even with the "wrong" ret statement, but causing the ISR to crash. In that case, i would have expected the machine to crash on returnning from the subroutine, not somewhere in the ISR.
Re: Why does my subroutine crash when called from an ISR?
Posted: Wed Aug 12, 2015 4:10 am
by iansjack
Brendan explained why the "ret" call would return successfully but the subsequent "iret" wouldn't; the stack is left misaligned. A second subroutine call increases the misalignment, but will still return OK. It is only when the enclosing interrupt routine tries to return that the problem becomes apparent.
Re: Why does my subroutine crash when called from an ISR?
Posted: Wed Aug 12, 2015 4:14 am
by Octocontrabass
onlyonemac wrote:I also don't understand why the calls were returnning properly even with the "wrong" ret statement, but causing the ISR to crash. In that case, i would have expected the machine to crash on returnning from the subroutine, not somewhere in the ISR.
The interrupt pushes CS, then IP.
Stack: [ return CS ][ return IP ]
Your CALLF pushes CS, then IP.
Stack: [ return CS ][ return IP ][ ISR CS ][ ISR IP ]
Your RETN pops IP.
Stack: [ return CS ][ return IP ][ ISR CS ]
Your IRET pops the ISR CS into IP, and the return IP into CS. The return IP is not a valid code segment, so it crashes.
It probably works elsewhere because you never try to pop anything from the stack after calling the subroutine.
Why are you using CALLF anyway?
Re: Why does my subroutine crash when called from an ISR?
Posted: Wed Aug 12, 2015 5:30 am
by onlyonemac
@octocontrabass: Thanks for the explanation. So essentially it doesn't matter, if I just call and return, call and return, etc. that it is returnning wrong because the CS doesn't actually change. But I get it now. The reason why I'm using a far call is because I'm still trying to figure out how to get my assembler to do an absolute near call, not a relative near call, because it does a relative call not to the address specified but rather jumping by the value specified, interpreting the address as an offset, resulting in the wrong address being executed.
The real question now though is why it works once, but not again.
Re: Why does my subroutine crash when called from an ISR?
Posted: Wed Aug 12, 2015 5:53 am
by iansjack
You're not reading the keycode from the keyboard controller. Until you do that you will get no further interrupts from it.
Re: Why does my subroutine crash when called from an ISR?
Posted: Wed Aug 12, 2015 5:55 am
by onlyonemac
LOL yeah I think I knew that at one time.
Re: Why does my subroutine crash when called from an ISR?
Posted: Wed Aug 12, 2015 5:56 am
by Octocontrabass
onlyonemac wrote:The reason why I'm using a far call is because I'm still trying to figure out how to get my assembler to do an absolute near call, not a relative near call, because it does a relative call not to the address specified but rather jumping by the value specified, interpreting the address as an offset, resulting in the wrong address being executed.
Your linker should be smart enough to generate the correct offset for a near CALL. What assembler/linker are you using?
Re: Why does my subroutine crash when called from an ISR?
Posted: Wed Aug 12, 2015 6:04 am
by onlyonemac
According to
http://x86.renejeschke.de/html/file_mod ... id_26.html, there doesn't seem to be a way to do an absolute near call.
Opcode Mnemonic Description
E8 cw CALL rel16 Call near, relative, displacement relative to next instruction
E8 cd CALL rel32 Call near, relative, displacement relative to next instruction
FF /2 CALL r/m16 Call near, absolute indirect, address given in r/m16
FF /2 CALL r/m32 Call near, absolute indirect, address given in r/m32
9A cd CALL ptr16:16 Call far, absolute, address given in operand
9A cp CALL ptr16:32 Call far, absolute, address given in operand
FF /3 CALL m16:16 Call far, absolute indirect, address given in m16:16
FF /3 CALL m16:32 Call far, absolute indirect, address given in m16:32
The only options are a relative near call, or an indirect absolute near call, or an absolute far call. This means that my functions which are at static addresses such that they can be called from any code even if it does not know the offset of the address need to use far calls (and thus far returns).
Re: Why does my subroutine crash when called from an ISR?
Posted: Wed Aug 12, 2015 6:31 am
by Octocontrabass
onlyonemac wrote:indirect absolute near call
That's still absolute. "Indirect" just means the destination address comes from a register or memory location instead of the operand.
onlyonemac wrote:This means that my functions which are at static addresses such that they can be called from any code even if it does not know the offset of the address need to use far calls (and thus far returns).
It sounds like you're writing position-independent code. You should look up some examples for an idea of how to make it work without far calls.
Re: Why does my subroutine crash when called from an ISR?
Posted: Wed Aug 12, 2015 9:01 am
by onlyonemac
Octocontrabass wrote:Your linker should be smart enough to generate the correct offset for a near CALL. What assembler/linker are you using?
I'm using as86. Don't know about linkers - told it to make a raw binary. I don't think there's a linker involved. The problem is that I tell the assembler "ORG 0" even though the code's executing at 0x7C00, otherwise I get a file that's got 31744 zeros at the front. Thus when I give it an address to jump to, I have to add 0x7C00 to the address - unfortunately it interprets it that I want to add 0x7C00 to the offset. Besides, the point is not if it's doing a near or a far call; the point is that I need to call an absolute address.
Octocontrabass wrote:That's still absolute. "Indirect" just means the destination address comes from a register or memory location instead of the operand.
Yeah I know that, but I don't want to waste memory/registers with having to specify an address. It's easier to just keep all absolute calls as far calls.
Octocontrabass wrote:It sounds like you're writing position-independent code. You should look up some examples for an idea of how to make it work without far calls.
No it's not position-independent code. It's a few low-level utility routines (such as "beep") which need to be located at known addresses such that they can be called from anywhere wihtout having to calculate an offset. Thus I can call them simply by calling their address - except that that's proving more complicated than it should be.
I think I'll just stick with the far calls.
Re: Why does my subroutine crash when called from an ISR?
Posted: Wed Aug 12, 2015 9:23 am
by iansjack
onlyonemac wrote:Octocontrabass wrote:That's still absolute. "Indirect" just means the destination address comes from a register or memory location instead of the operand.
Yeah I know that, but I don't want to waste memory/registers with having to specify an address. It's easier to just keep all absolute calls as far calls
You are wasting more memory by making far calls than if you used indirect near calls.
The real answer real is to use a proper assembler/linker so that you can make all calls relative and save even more memory.