How to create an emulator for a single instruction processor using openGL

Using OpenGL to code an emulator with only one instruction, maximum up to seven days

Victor Misael Brito F. Carneiro
TotalCross Community
7 min readJan 11, 2021

--

For a long time, I was fascinated with the idea of ​​running one platform using another and I always tried to study emulators. That is why I have always been researching and aware of this topic and that is how I found it in the emulator community at Discord, a project for beginners of an emulator with only one instruction, plus a challenge: to be able to develop this in 7 days. I was captivated by the idea, especially by the fact it was a 2010 project, that is, I have help and material on the internet!

I completed the challenge and was extremely happy with the result and so I decided to share here what I learned from the development of emulators and about the technology used, openGL. So, let’s discover how to create an emulator for a single instruction processor using openGL. But first of all:

What is OpenGL?

OpenGL, which stands for Open Graphics Library, is an API (Application Programming Interface) used in computer graphics, which exposes the resources of Video Hardware to programmers to put their creativity to 100%. This feature is super used in the development of 3D environments, games (like Counter Strike) and other technologies.

Knowing what this tool is, it is interesting to understand some points of it to be able to follow the development of the emulator before putting your hand in the code or to prepare for the market, because openGL is a widely used tool (including TotalCross — an open source framework for developing graphical interfaces for embedded and mobile systems — uses openGL and Skia in the components).

Understanding bytePusher

bytePusher is a virtual machine, with a CPU (The ByteByteJump) with just one instruction: copy a byte to location x and then jump to location y (yes, exactly as the name says). Basically, this is how the CPU works. And because of this simplicity, which allows it to be easy to use so that, depending on your skills, it takes up to 2 days to create a simple emulator (it was my first time, so I made it in 7 days)!

Coding the emulator

Starting at the heart of the emulator, the CPU, as stated above, is simple. It’s just an instruction: Copy this byte to that address, and jump to that, and that’s it.

To help me with the task of removing and putting data in memory, I created a function that reads the number of bytes you want (with a limit of 64 bits, or 8 bytes):

Function to read data from memory

The variable PC, initialized with 0, when entering the loop will push the byte (8 bits) previously inserted to the left (the shift <<) and will do the OR logical operation with the value, to add the other byte.

Imagine this “push” as if I add zeros to the right of a number and then add it to another number. For example, 5, I put a zero (I push 5 to the left) and it becomes 50. So I take another value from memory (let’s assume 9), and I add it to 5 (doing the logical OR), and it becomes 59.

Specifications virtual machine (VM)

The CPU does not have a defined specification which address should start, and how many cycles should run before leaving for other tasks, however, the virtual machine (the bytepusher itself, which uses the CPU), has these well-defined aspects, which are:

  • 65535 cycles for each rendered frame, 16MB of RAM (already allocated);
  • Big-Endian;
  • 256x256 screen, 1 byte per pixel, 216 colors;
  • 16-key keyboard;
  • 8 bit sound, mono, 256 samples per frame (15360 per second assuming 60 frames per second)

According to the specification, the first 2 bytes of memory are reserved for the keys, at the beginning of each frame the variable PC (which points to which address the CPU should read the instructions) must be read from the third byte (that is, the address 2 of the memory, since it starts with 0) until byte 5 (address 4), and the remaining two bytes are. The starting address of the segment where the bytes describing the screen will be (a pointer in practice), and the other two last bytes, the audio segment

Implementing the CPU

Knowing the details above, you can implement the CPU in this way:

Take the PC byte (the address you want to go to), copy the data pointed by PC (3 bytes in size) to the address 3 bytes after it (since it is the size of the data), and then place PC for the value that is 6 bytes ahead. Do this 65535 times to facilitate the implementation, I made a macro with the number of cycles

So, from that, we basically have the processor implemented, so we can go to the other part of the emulator (the part that involves the cute things on the screen)

This program was developed to receive the name of the executable via the command line, and after that, load into an array. By the end, load in the emulator’s allocated memory, (being the object of the CPU).

CPU c = CPU();

The code to load data into memory is very simple:

After placing the ROM in memory, we initialize:

We created the SDL window, put its measurements (600 by 600) and its position, (100 on the x axis, and 100 on the y axis). use the header assert.h, in case this variable is not started correctly, the program does not continue. initialize the events, and the audio, and then load the system’s color palette.

According to the emulator specification (available in the references link), there are 216 colors, each RGB color component can have 6 different intensities, (6 * 6 * 6), these intensities varying from 0 to 5, the value color is stored in an 8-bit variable (after all, there are only 216 colors, no larger values ​​are needed).

Such a value of 216 colors is achieved as follows: Red * 36 + Green * 6 + Blue. As we know, each value can have a value from 0 to 5, to extract the blue color, we only need to remove the module (% 6) from the operation color. As the green value is multiplied by 6, divided by 6, and we remove the module again and red the same way.

After that, we will save these values ​​in a 32-bit variable, as we want this variable to hold 3 values ​​of 8 bits, we use the magic of C ++ pointers and references!

Simply convert caught the reference _color32val, reinterpreted as a pointer to an 8-bit variable, and access to an array that does not cause problems because these places are already allocated. And after that, save in an array with the color palette

From this, let’s go to the coolest part of the emulator

This first part of the code, I initialize the emulator audio with the parameters above:

From there, I leave the initial color of the system black, using the glclearcolor. Then, load the identity matrix, and use GlOrtho to map the space I’m going to render in 2D, maps the 256 by 256 of the screen, to the -1 space ,1.

With that we have the main loop, in the first part of the code, using SDL_QueueAudio, it is possible to reproduce the audio, as there are 256 samples per frame, fill in the last argument with this value, and take the pointer of the section where the audio is located using the getAudioSection() method;

Notice that I convert this pointer from uint8_t * to int8_t *, because I want such values ​​with a negative or positive sign. From that, I call the CPU, so that it runs its number of necessary cycles. After that, we call glClear, to clean the screen and draw the new frame, and we take the memory section with the color bytes usingpointer arithmetic.

With that, we go through the 256 * 256 pixels that are in memory, and draw it:

As the color palette has already been loaded into memory, we only convert it to a vector and access it in the same way it was saved. After that, we draw a small square with the desired color, for each of the pixels.

So, just use SwapWindow to change the frame:

SDL_GL_SwapWindow(Window);

After that, using SDL, it is very simple to be able to see the button input, as we know that the keys are 16, from 0 to 10, from A to F, simply map these keys from the keyboard to the emulator.

I used the key down to find the key pressed, and then I did a logical OR with the value, to fill the 2 bytes of the 16 bits

In an analogous way:

I made an exclusive to remove the key pressed. After that, put it in memory, the CPU already has a function ready for it.

As I am compiling this code for x86, and x86 is little endian, the MSB is the last byte, we make this inversion. With this, we created an emulator for a single instruction processor using openGL

Result

Result on simulator

Conclusion

I hope you enjoyed it and any questions just leave in the comments or enter there in the emulator development community group or TotalCross group, on Discord, and I promise I’m going to help. The TotalCross community is very open to helping in learning and exchanging experience at any stage of development.

I am interning there now and I have learned a lot about graphics component development, which helps me with emulators since they use the same technology.

And if you finished, send the printout of the result to me =)

References

--

--