Here is my solution. I will admit it is fairly heavily compiler dependent but the initialization functions and some of the setup is as well so meh - it will have to do ^_^
Firstly, I will explain the structure I have taken in my code which may help to explain the way I went about this.
I am structuring my code in a way not unlike the java libraries. As such, all the code for interrupt and exception handling is in namespace called ISR.
Specifically, I have a function called Ex_Div0 that is nominally the int0 interrupt handler.
However, due to the stack problems that I illustrated in my first post, and the repeated problems I discovered after, I have had to implement this strategy to solve the problem. The good news that I believe I have fully tested this now so I don't foresee any more problems (is only but its nice to hope)
Here is the relevant snippets of my code:
isr.cpp
Code: Select all
namespace ISR {
struct IntStk
{
unsigned int eip;
unsigned int cs;
unsigned int eflags;
};
struct EIntStk
{
unsigned int error;
unsigned int eip;
unsigned int cs;
unsigned int eflags;
};
void Ex_Div0Stub();
void Ex_Div0(void *ptr);
void Initialize(){
...
Install_ISR(0,(ISR_FUNCTION)Ex_Div0Stub,0x08,flags);
...
}
void Ex_Div0(void *ptr){
IntStk * data = (IntStk*)ptr;
Print("\n\nDiv0 Exception occurred - Addr: 0x");
Hex2Str(data->cs);
Print(":0x");
Hex2Str(data->eip);
Print(", EF: 0x");
Hex2Str(data->eflags);
Print('\n');
unsigned char ins = *((unsigned char*)data->eip);
if(ins == 0xF7 || ins == 0xF6)
{
PrintLn("Skipping exceptional opcode");
data->eip += 2;
}
else
PrintLn("No exceptional opcode found - returning");
}
};
2 functions, Ex_Div0 and Ex_Div0Stub are declared but only Ex_Div0 is defined. It is a simple function that prints out the offending address, then checks to see wether the offending opcode is a div/idiv instruction (as opposed to an int0) and if so, adds 2 to the instruction pointer to skip it (not ideal but best until I have C++ language exception handling sorted). Then this function returns normally. Notice that the Ex_Div0Stub function is the one set as the interrupt handler, not Ex_Div0 itself.
isrstub.asm
Code: Select all
TITLE isrstub.asm
.686P
.model flat
?EX_Div0@ISR@@YAXPAX@Z PROTO SYSCALL
_TEXT SEGMENT
?Ex_Div0Stub@ISR@@YAXXZ PROC
push eax
lea eax,[esp+4]
push eax
call ?EX_Div0@ISR@@YAXPAX@Z
add esp,4
pop eax
iretd
?Ex_Div0Stub@ISR@@YAXXZ ENDP
_TEXT ENDS
END
This is a .asm file which, when included into an MSCV project, automatically get assembled (instead of compiled) then linked into the final program. The top 3 lines are setup parameters for the file. I used the same settings that the compiler generates and spits out in the code listings, in an attempt to preemptively prevent any bugs due to code of mis-matched versions.
The line "?EX_Div0@ISR@@YAXPAX@Z PROTO SYSCALL" declares the name mangled "void Ex_Div0(void *ptr)" as a function. Similarly, "?Ex_Div0Stub@ISR@@YAXXZ PROC" defines the name mangled "void Ex_Div0Stub()". By using these names, the linker links them together and all is fine as far function pointers are concerned.
The ?Ex_Div0Stub@ISR@@YAXXZ function itself is the interrupt handler. It stored the value eax, the only register which the compiler wont maintain in the Ex_Div0 function. It then pushes a pointer to the start of the stack information from the interrupt as the void* parameter for Ex_Div0. Then it calls the function itself. After the function returns, it restores eax and calls iretd.
By passing the address of the interrupt stack information as a parameter into the Ex_Div0 function, it allows that function to alter things like the return address if needs be. By the parameter being a void pointer, it allows it to determine whether or not an error code was pushed on the stack (important for some handlers) rather than assuming one will be present or not depending on the way its coded.
I hope this is clear but feel free to ask questions about any confusion/misunderstandings.
~Andrew