views:

124

answers:

3

Hi, I have an old C++ project I made a while back. Well, it is a CPU emulator. Whenever a CPU fault happens(such as divide by zero, or debug breakpoint interrupt, etc) in my code it just does a throw and in my main loop I have something like this:

try{
    *(uint32_t*)&op_cache=ReadDword(cCS,eip);
    (this->*Opcodes[op_cache[0]])();
    eip=(uint16_t)eip+1;
}
catch(CpuInt_excp err){
    err.code&=0x00FF;
    switch(err.code){
        case 0:
        case 1: //.....
        Int16(err.code);
        break;
        default:
        throw CpuPanic_excp("16bit Faults",(err.code|0xF000)|TRIPLE_FAULT_EXCP);
        break;
    }
}

And a simple opcode example(pulled out of thin air)

if(**regs16[AX]==0){
  throw CpuInt_excp(0); //throw divide by zero error
}

What this code basically does is just reads an opcode and if an exception occurred, then call the appropriate interrupt(in the CPU, which just changes the EIP)

Well, this being in the main loop, the try{}catch{} overhead really adds up. This isn't a premature optimization, I profiled it and gcc's exception helper functions(without even doing any faults and thus no throws) and the helper functions took up over 10% of the total execution time of a long running emulated program.

So! What would be the best way of replacing exceptions in this case? I would prefer not to have to keep track of return values because I already have a ton of code written, and because keeping track of them is really difficult when functions get really deep.

+1  A: 

How about moving the try/catch out of your inner loop? It would take more bookkeeping in the catch, and some additional overhead getting back into your innerloop without starting from the top, but you would pay only when an exception actually fired rather than all of the time.

John Knoeller
Adding outer loop that will restart inner one in case of exception and make sure all the variables of inner one actually defined in outer one will take care of bookkeeping and 'starting from the top' problem.
vava
@vava: Yeah, that's what I was thinking too. But only earlz knows how practical that is.
John Knoeller
Yea, this is actually in the form of a library where the main application does something like `cpu->exec()` to execute a single opcode in a main loop. So this will require some rethinking on how that works...
Earlz
If you can keep your state in a struct (or class) that might do the trick.
John Knoeller
+3  A: 

You haven't shown your loop, but I'm guessing in pseudocode it's:

while (some_condition) {
    // Your try..catch block:
    try {
        // Do an op
    }
    catch (CpuInt_excp err) {
        // Handle the exception
    }
}

You could move the try..catch out a level:

done = false;
while (!done) {
    try {
        while (some_condition) {
            // Do an op
        }
        done = true;
    }
    catch (CpuInt_excp err) {
        // Handle the exception
    }
}

There I've included two loops, because I assume that if an exception occurs you want to be able to carry on (hard to tell without knowing what you're doing in Int16, but I think you're allowing carrying on after non-panic exceptions). Naturally if you don't need to carry on, you only need one loop.

The outer loop just restarts things after the exception. It can check the same condition as the inner loop if that condition is not expensive to check (e.g., it's a program counter or something), or it can have a flag as in the above if the condition is expensive to check.

T.J. Crowder
A: 

It looks like you're recording the CPU state in member variables, since your opcode methods don't take any parameters. At the risk of stating the obvious, why not do the same for the exception state? Then you won't have to deal with a return value. Instead, you would just need the opcode functions to set the exception state, return, and let the main loop check the exception state.

bk1e