Quickly, what was done this time and let's jump to conclusions right after that:
- Implementation of BFINS Dx,Da{y,z}, BFFFO Dx{y,z},Da, BFEXTS Dx{y,z},Da, BFEXTU Dx{y,z},Da, BFCLR Dx{y,z}, BFTST Dx{y,z}, BFSET Dx{y,z}, BFCHG Dx{y,z}, ROXR.x Dy,Dz, ROXR.W mem, ROXR.x #imm,Dy, ROXL.x Dy,Dz, ROXL.W mem, ROXL.x #imm,Dy, ASR.W mem, ASL.W mem, LSR.W mem, LSL.W mem, ROR.W mem, ROL.W mem instructions.
- Opcode compiler fucntions for different operation sizes are unified, unnecessary helper functions were removed.
- Macroblock protos are generated from the opcode table file rather than used the manually prepared file.
- Fixed wrong function name for the CNTLZW PowerPC instruction emitter.
- Fixed missing input register in C and X flag extraction macroblock which is used in some shift instructions.
- Fixed register dependency in ASR.x Dy,Dz and LSR.x Dy,Dz instructions.
Now, we are getting dangerously close to the beta stage. It is really hard not to give you guys any promises what I cannot keep later on... (Summer is coming, you know... ;)
Anyway, my plans for the near future (read: when it is done) are:
- After I have finished with the implementation of all instructions which were anticipated earlier I am going to stabilize the emulator a bit and clean up the code if needed.
- I will try to release a compiled beta version and put together some documentation for using/testing it.
- There are some outstanding issues, one of the most critical one is fixing the problems with the macroblock optimizer.
Something completely different
I have to admit that my posts were not that interesting to read recently. Earlier I invested more effort into the posts and it was probably more fun to read, especially to the developers.I would like to bring back that tradition, so for this update I came up with a few interesting thoughts on:
How to avoid the decisions
I know that many developers love to procrastinate anything, including (but not limited to) decisions. But what I am about to write is not how lazy my fellow developers are, but rather how to get around a situation when it is not ideal to do a comparison and branch according to the result.
Why would that be important at all?
There are a number of reasons why it is better to avoid branching, this article lists many examples for that and also explains the reasons. It basically boils down to the following reasons:
- branching can cause cache misses;
- the branching instruction is a useless overhead and it can be even slow on certain architectures;
- conditional branching disrupts the pipelining of the execution (although there are pretty sophisticated techniques available to deal with that).
To put it simply: each macroblock is depending on the results of the previously executed macroblocks, if we skip ahead then it is impossible to tell whether those required macroblocks were executed or not before the dependent macroblocks.
Why does this cause any trouble? Because sometimes it is pretty hard to avoid conditions inside the instruction implementations.
I already faced this issue earlier, when I started to work on the conditional branching and setting instructions (Bcc and Scc). There was no possible way to avoid the conditional branching for these instructions due to its very nature of the instructions.
I ended up creating a very specific macroblock which embeds the condition checking and branching, so the "inter-macroblock" flow analysis remains intact.
In the recent update you can find many bit field instructions which were complicated enough already and then the very same issue came up again:
For bit field instructions 0 (zero) bit field width means 32 bit actually. This doesn't sound that bad, however sometimes the bit field width is coming from an emulated data register instead of a statically encoded constant. In this case the decision must be done in the emulated code instead of in compiling time.
Not in every case, but quite often it is possible to calculate the result instead of making a comparison and branching.
For the bit field instructions the solution was (written in C code, just because it is easier to understand):
temp = width - 1;
width = width | (temp & 32);
(There is one condition, though: the width must be between 0 (zero) and 31 before this operation starts.)
Now, think about it a little bit and figure out on your own why does it work. I am not going to explain it. :)
See you soon.