You heard correct! I’ve written my HMCU as a virtual machine in C. I’m not actually a fan of the only virtual machine language I can think of off the top of my head – Java though I think thats more at fault to the syntax and way of programming, all that overloading, inheritance and polymorphism gives me a headache. I do however enjoy programming my virtual machine with bytecodes in my good ol’ made up language VSH (Very Simple Harris), which is pretty much assembler.
Why have I done this you ask? I’m hoping eventually to be able to make the very basic of C compilers for the HMCU, load it up onto my STM32 and run programs off an SD card. This however means that I’ll need to make some well needed improvements to the HMCU! And thus, here is what I’ve improved:
- Word size from 16bit to 32bit, improving the addressable memory space to (2^22)-1 bits, or in normal terms, 4MBytes.
- Upgrading the size of the OpCode from 4bit to 6bit, leaving room for 47 device specific instructions (2^6-17)
- At some point, I need to add signed support though currently, we’re still with unsigned
- Improving some of the instructions. Now that I’m not trying to fit the processor into logic gates and an FPGA, I can improve my instructions a bit. I’ve now changed the equal, more than and less than instructions to have comparison register A, comparison register B and destination register. This allows me to do things like if(A==B) C = SET, and allows me to get rid of register swaps that were required in the VHDL version.
- Added two computer specific instructions, PRINT and BREAK. The PRINT instruction prints all of the registers, along with printing the contents of the RAM, if specified, to the debug console (printf on my computer implementation). The BREAK instruction is used to indicate the end of the program.
- I’ve doubled the amount of registers allowing for much more complex programs. There are now 8 general purpose registers vs 4.
- The conditional jump instruction can now jump if a register is SET (Jump if all 1) or RESET (Jump if 0).
- An ADD and SUB instructions! What use is anything without at least an ADD instruction?!
These improvements are actually making the virtual machine much more useful! As ever, I’ve written a few test programs that allow me to see how well the system works. Unfortunately, these tests aren’t anywhere near as interesting as using the hardware version of the microcontroller and seeing things change in real life, they still indicate progress though. The main downside at the moment however is the word by word memory access leading to a slow system. If the programs are small enough, they could be stored in a memory buffer and executed pretty fast. Storing a 4MB RAM and ROM buffer on my STM32 however is not feasible so I’ll be leaving buffered program running out for now! In the future, I’ll be looking to working through program blocks.
Test 1: Incrementing a variable from 0 to RB (Value stored in Register B) then exiting
Here is a very simple program, its pretty much all in the title! A ‘0’ is loaded into register A and a constant value is loaded into register B (4 in this case). A is then incremented and an equal check is done with the result stored in register C. If register C is set, the program jumps to the end. Otherwise, the program will continue to increment A until C is set (A == B). The program also has a PRINT instruction (no RAM) before each conditional jump test.
I’m currently in debug mode where each instruction and the decoded OpCode and OpDat are written to the screen, along with the program counter and running file position. This mode is useful to make sure the system is running properly by looking at the program counter etc. As you can see, RA starts at 1 and increments to 4. Once it gets to 4, the program jumps to the end and exits successfully.
Test 2: Euclids GCD algorithm
Here is an algorithm that computes the greatest common divider of two numbers. I’ve chosen two large numbers at random: 12300 and 999000. Using wolfram alpha, the GCD should be 300. Writing this up in HMCU requires a total of 11 instructions and uses the newly implemented SUB instruction.
As can be seen, RA and RB get smaller and smaller until they’re equal. Once they’re equal, the GCD can be found in RA (or RB since they’re equal anyway…). Comparing this to the wolfram alpha answer proves that its worked!
Ignore the top two comments (A = 50, B=30), I hadn’t changed them from a previous program!
Test 3: Multiplication of two numbers
Here, I’ll be using the decrement and jump if zero instructions to perform the multiplication of two numbers.
How does this work? Multiplication can be computed through repeated additions, for example: 3*20 is equal to 20+20+20 = 60. By counting how many additions have been done, you can multiply two numbers together. In my case, I’ve used 10*20 = 200.
Obviously, a problem with this method is the length of time taken is proportional to the size of the multiplier.
Test 4: Integer division of two numbers
This test is pretty much the same as the above and is merely a subtraction instead of an addition. This time, register A is loaded with the dividend and register B is loaded with the divisor. Register C is used to count how many divisors fit into the dividend. If this is a non integer amount, register A will contain the remainder and register C will contain the dividend.
So far, the system seems great! I’m going to reduce the instruction set to allow for even more device dependent instructions (even if they’re not required!) but even a virtual machine this simple can be useful – even if its written in a much more useful language.