Introduction

Emulators are cool pieces of technology that allow users to run a totally different system on top of another one. Here, we’ll explore how a CPU works and how its software can emulate one by implementing a simple machinethat will run programs written for a fantasy CPU.

Emulators have a wide range of applications, like running x86 programs on ARM devices or running ARM Android applications on x86 Windows Desktops and even running your favorite retro game on an emulated computer/console on a Raspberry Pi.

When a full system is emulated, the emulator software needs to handle all the hardware devices of those systems. This may include not only the CPU but also the video system, input devices, etc. However, the core concept of an emulator is nevertheless emulation of the CPU.

Programs are just a series of bytes

It’s well known that CPUs are the core of a machine. Their main job is to execute programs. And programs are nothing more than a series of instructions in the computer’s memory.

At this point, you may be tempted to believe that your CPU knows JavaScript. Although a tempting concept, this is not the case. Imagine changing a CPU if a new version of JavaScript appeared!

In reality, a CPU understands only a finite set of instructions; the instructions the CPU engineering team designed the CPU to understand.

Users can create programs by using these instructions directly or by coding in a higher-level language and then using a compiler/system to translate the program into the set of instructions understood by the CPU.

Regardless of how the user created the program, the instructions end up in memory as a series of bytes like these:

11,0,10,42,6,255,30,0,11,0,0,11,1,1,11,3,1,60,1,10,2,0,20,
2,1,60,2,10,0,1,10,1,2,11,2,1,20,3,2,31,2,30,2,41,3,2,19,31,0,50

The CPU role is to fetch these instructions from memory and to execute them.

Using mnemonics makes the task of writing programs much easier for humans. If one writes programs using the instructions mnemonics it is said that he codes in assembly language. It only takes a simple step to transform these programs written in assembly language into binary instructions (e.g. machine language).

Instructions and mnemonics for our fantasy CPU, as described below, are quite intuitive. But we need to know the CPU instructions if we want to emulate that CPU into software.

Note: Under each instruction is specified the number used to encode that particular instruction. Also registers R0R1R2, **R3 **are encoded as numbers 0, 1, 2, 3):

Loads the value from regsrc into regdst. E.g. regdst = regsrc

MOVR reg_dst, reg_src
MOVR = 10

Loads the numeric value into register regdst. E.g. regdst = value

MOVV reg_dst, value
MOVV = 11

Adds the value from regsrc to the value of regdst and store the result in reg_dst

ADD reg_dst, reg_src
ADD = 20

Subtracts the value of regsrc from the value of regdst and store the result in reg_dst

SUB reg_dst, reg_src
SUB = 21

Pushes the value of reg_src on the stack

PUSH reg_src
PUSH = 30

Pops the last value from stack and loads it into register reg_dst

POP reg_dst
POP = 31

Jumps the execution to address addr. Similar to a GOTO!

JP addr
JP = 40

Jump to the address addr only if the value from reg1 < reg2 (IF reg1 < reg2 THEN JP addr)

JL reg_1, reg_2, addr
JL = 41

Pushes onto the stack the address of instruction that follows CALL and then jumps to address addr

CALL addr
CALL = 42

Pops from the stack the last number, assumes is an address and jump to that address

RET
RET = 50

Print on the screen the value contained in the register reg

PRINT reg
PRINT = 60

Stops our VM. The virtual CPU doesn’t execute instructions once HALT is encountered.

HALT
HALT = 255

#javascript #emulator #cpu #virtual-machine #coding #x86 #raspberry-pi #bytecode

How to Build an Emulator for a Fantasy CPU in JavaScript
4.70 GEEK