Original Post
🔒 Locked
optimizing MCU emulator code
Advertisement
If the total code inside the switch statement isn't very much (several hundred lines of C++, say), switch is the fastest way to take a number and perform some operation based on the number. Otherwise, it might be better to use a table of function pointers with each opcode represented by a single function (possibly with the functions taking as one parameter the number of the opcode so the same function could handle multiple similar opcodes) so that more will fit into the cache (presuming some opcodes are used more often than others).
The problem with speed could be due to the details of the implementation, but we can't know that for sure without the code.
The problem with speed could be due to the details of the implementation, but we can't know that for sure without the code.
Actually I using switch with small parts of code.
I forgot to say very important thing. All code was written in C#. I thinking about rewrite MCU code in C++ with inline asm. I planing to use call vectors and minimize opcode resolving code to minimum. It is critical part of whole implementation.
I am interesting about miscallaneous techniques. Which probably could be faster. Currently simulation is ten time slower than real-time.
Why others emulators (more complicated than my) working with efficiency excedding 1000% and my have only 10%-15%? C# is slower than other languages? Maybe using .NET is not good idea?
switch (opcode){ // (...) // CLR A case 0xE4: ACC = 0; cycles = 1; m_pc++; break; // CPL A case 0xF4: ACC = (byte)~ACC; cycles = 1; m_pc++; break; // RL A case 0x23: ACC = (byte)((ACC << 1) | (ACC >> 7)); cycles = 1; m_pc++; break; // RLC A case 0x33: tmp_b = (byte)(((PSW & PSW_Flags.CY) != 0) ? 1 : 0); if ((ACC & 0x80) != 0) PSW |= PSW_Flags.CY; else PSW &= ~PSW_Flags.CY; ACC = (byte)((ACC << 1) | tmp_b); cycles = 1; m_pc++; break; // (...)}I forgot to say very important thing. All code was written in C#. I thinking about rewrite MCU code in C++ with inline asm. I planing to use call vectors and minimize opcode resolving code to minimum. It is critical part of whole implementation.
I am interesting about miscallaneous techniques. Which probably could be faster. Currently simulation is ten time slower than real-time.
Why others emulators (more complicated than my) working with efficiency excedding 1000% and my have only 10%-15%? C# is slower than other languages? Maybe using .NET is not good idea?
AFAIK C# is slower language than C++. C# is similar to Java language when the code is executed. C# compiler doesn't create native code for the computer but bytecode (which of course can be optimized and compiled to native code). It could be that the C# program is actually running on a certain kind of virtual machine too which cannot reach the speed of native code.
With the switch, on C++ for example, if correctly coded (find a faq about it), the cpu makes only one jump and doesn't test all the cases, so it is pretty efficient.
If you have a random collection of different values to test, you could perhaps implement some sort of binary tree to reduce the amount of tests required to find the correct code.
With the switch, on C++ for example, if correctly coded (find a faq about it), the cpu makes only one jump and doesn't test all the cases, so it is pretty efficient.
If you have a random collection of different values to test, you could perhaps implement some sort of binary tree to reduce the amount of tests required to find the correct code.
Hm. I know IL is language working on virtual machine. I attempt to compile it to native code. Well, it fail ;)
I realy must learn more about .NET programming techniques.
A word about 'switch'. If I arrange all cases in raising order that will be enought for compiler to generator optimized code? I trying to write my own switch-like command in inline asm. I use a list of jmp instructionts which move execution point to correct operation. It will be great if compiler do this same thing (or similar) without using inline asm.
Exacly switch contain all values of 8-bit opcode (excluding one). So, I have 255 cases. Now they are unordered. I think any binary tree is not needed.
I realy must learn more about .NET programming techniques.
A word about 'switch'. If I arrange all cases in raising order that will be enought for compiler to generator optimized code? I trying to write my own switch-like command in inline asm. I use a list of jmp instructionts which move execution point to correct operation. It will be great if compiler do this same thing (or similar) without using inline asm.
Exacly switch contain all values of 8-bit opcode (excluding one). So, I have 255 cases. Now they are unordered. I think any binary tree is not needed.
If I'm not mistaken, bit-level operations are one of the slower points of C# so doing things like instruction decoding, bit packing/unpacking, etc may be the bottleneck. A language like C or C++ will likely be much faster here.
Which micro-controller are you emulating? what is the instruction-set encoding scheme like?
Which micro-controller are you emulating? what is the instruction-set encoding scheme like?
If you are looking for a high-level arch change that would drastically improve performance, you could re-compile the uC binary into an x86 assembly/binary.
It wouldn't really be an emulator anymore, but a translator.
Sticking with C# and emulation, I would first check and make sure you have all the C# optimizations enabled. Looking at the code I see some repition that could be eliminated. For example, I assume you always have to increase the PC (and actually the PC is usually incremented after the instruction fetched is initiated but before that instruction is actually executed). But a good optimizer should do that for you.
You mentioned that compiling the code to a native .exe failed? See if you can resolved what went wrong there. The Windows CLR implementation is quite sophisticated, IIRC it does JIT compliation and produces native code - but I do not know any of the caveat's to that.
To make the switch statement most effective, the numbers switched on need to be contiguous, then the compiler can make an efficent jump-table. Or you can make a jump-table your self. If you vector to a function for each op code you're going to add tremendous overhead to each op code. If you use a function table, you gaurantee a function call as well, since the compiler must load the addresd then jump to it - there's no possibility of inlining them.
Typically the op code are designed with some method to thier madness. You probably shouldn't switch on the entire op code, but part of it. Maybe you can do some gross-culling which decides which type of op code it is and switch to an (inlined) function that handles that famility of op codes.
It wouldn't really be an emulator anymore, but a translator.
Sticking with C# and emulation, I would first check and make sure you have all the C# optimizations enabled. Looking at the code I see some repition that could be eliminated. For example, I assume you always have to increase the PC (and actually the PC is usually incremented after the instruction fetched is initiated but before that instruction is actually executed). But a good optimizer should do that for you.
You mentioned that compiling the code to a native .exe failed? See if you can resolved what went wrong there. The Windows CLR implementation is quite sophisticated, IIRC it does JIT compliation and produces native code - but I do not know any of the caveat's to that.
To make the switch statement most effective, the numbers switched on need to be contiguous, then the compiler can make an efficent jump-table. Or you can make a jump-table your self. If you vector to a function for each op code you're going to add tremendous overhead to each op code. If you use a function table, you gaurantee a function call as well, since the compiler must load the addresd then jump to it - there's no possibility of inlining them.
Typically the op code are designed with some method to thier madness. You probably shouldn't switch on the entire op code, but part of it. Maybe you can do some gross-culling which decides which type of op code it is and switch to an (inlined) function that handles that famility of op codes.
if(opcode & 0x0100) mem_op();else if (opcode & 0x8000) alu_op();else if (opcode & 0xf000) fpu_op();else whatevers_left();
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement