dirkwhoffmann / Moira

A Motorola 68000 emulator written in C++
https://dirkwhoffmann.github.io/Moira
Other
109 stars 13 forks source link

Design decision: Use C++ exceptions to emulate address errors and bus errors #13

Closed dirkwhoffmann closed 2 years ago

dirkwhoffmann commented 2 years ago

Background: In 68030+ CPUs every access to memory will require a call to translate to convert a logical address to a physical one. The problem is that any call to translate may result in a bus error which terminates the execution of the current instruction immediately. The situation is comparable with address errors. When I implemented address error support a while ago I had to blow up the code by passing around a lot of boolean flags and adding many If-statements to check those flags. Adding bus error support in a similar way would make the code much more ugly.

Therefore, I am thinking of using C++ exceptions to emulate address errors and bus errors. But before doing this, I’d like to sum up pros and cons. Here is my point of view (which might be wrong):

  1. Speed: Throwing and catching exceptions is costly. I think we can live with that because address errors and bus errors are very rare events. On the standard execution path, speed could even increase slightly, because functions such as readOp are no longer required to pass back a success flag. Code readability would also increase. I.e., statements such as

    if (!readOp<C, M, Byte>(ax, &ea1, &data1)) return;

    would simply look like this:

    readOp<C, M, Byte>(ax, &ea1, &data1);
  2. Memory: The C++ compiler has to add some amount of exception processing code to each function that might throw an exception. Since we have lots of instruction handlers due to heavy template usage, the resulting overhead might become an issue.

  3. Style: In theory, exceptions should only be used to handle error conditions. They should not be used as part of the normal program control flow. Address errors and bus errors are „virtual error conditions“ and as such part of the functionality of the program. Hence, one could argue that exceptions would be misused in this context. However, we have a special situation here, because address and bus errors are nothing else than exceptions built in hardware which makes them a perfect fit for being emulated by software exceptions.

Item 2 has the potential to be a show stopper. Hence, I think it’s best to postpone MMU stuff at the moment and to evaluate the cost of implementing address error handling via C++ exceptions first. If it doesn’t blow up the code too much, bus errors could be handled the same way.

dirkwhoffmann commented 2 years ago

I've setup an experimental version of vAmiga which declares readOp as a void function. If an address error occurs, an exception is thrown instead of returning false. Here are the results:

% ls -al vAmigaOld/.../vAmiga 
-rwxr-xr-x  1 Dirk  staff  11972496 22 Sep 12:54 vAmigaOld/.../vAmiga
% ls -al vAmigaExceptions/.../vAmiga
-rwxr-xr-x  1 Dirk  staff  11873760 22 Sep 13:24 vAmigaExceptions/.../vAmiga

The new executable is slightly smaller. This means the old executable had all the exception cleanup code already included. The size reduction is due to the removal of many If-statements which are no longer needed in the new code.

Bildschirmfoto 2022-09-22 um 13 26 32

Interestingly, the version using exceptions is slight slower. This is a surprise, because no exceptions are thrown and exception should come with zero-cost in this case. However, the slowdown is negligibly small. Nevertheless, I would have expected it the other ways round, because of many deleted If-statements.

Bottom line: Neither speed nor memory footprint are a show stopper here.

dirkwhoffmann commented 2 years ago

Done. Seems to work well.