How the YEMU Emulator Works

Engineering

Posted by Bruce Lee on 2024-05-08

About Me

Welcome to my blog! This is where I collect my observations and notes on programming and technology. The main subjects range from implementation details to broader ideas about programming.

Main Topics

  • Engineering Projects: Exploring implementation details and how technical systems work.
  • C/C++: Notes on language features and programming techniques.
  • The Programmer’s Perspective: Ideas about developing a career and a way of thinking as a programmer.

For more, visit the categories page.

Contact

If you have questions or would like to discuss something, please get in touch through the About page.

Thank you for reading and for your support. I hope these notes help you on your own technical journey!


How the YEMU Emulator Works

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <stdint.h>
#include <stdio.h>

//------------4个寄存器,16字节地址
#define NREG 4
#define NMEM 16

// define the instruction format
//-----------------联合体用法,总长是8bit,可以引用不同的位域
typedef union {
struct { uint8_t rs : 2, rt : 2, op : 4; } rtype;
struct { uint8_t addr : 4 , op : 4; } mtype;
uint8_t inst;
} inst_t;
//-------------------宏,解码声明定义
#define DECODE_R(inst) uint8_t rt = (inst).rtype.rt, rs = (inst).rtype.rs
#define DECODE_M(inst) uint8_t addr = (inst).mtype.addr

uint8_t pc = 0; // PC, There is no 4-bit data type in C, we use 8-bit type to represent it
uint8_t R[NREG] = {}; // registers
uint8_t M[NMEM] = { // memory, which contains a program that calculates z = x + y
0b11100110, // load 6# | R[0] <- M[y]//加载33
0b00000100, // mov r1, r0 | R[1] <- R[0]//移动到r1寄存器
0b11100101, // load 5# | R[0] <- M[x]//加载16
0b00010001, // add r0, r1 | R[0] <- R[0] + R[1]//相加存储在r0
0b11110111, // store 7# | M[z] <- R[0]//存放内存中
0b00010000, // x = 16
0b00100001, // y = 33
0b00000000, // z = 0
};

int halt = 0; // end marker

// Execute an instruction
void exec_once() {
inst_t this;
this.inst = M[pc]; // instruction fetch
switch (this.rtype.op) {
// op decode operand decode execution
//局部作用域,声明定义解码中的不同域
case 0b0000: { DECODE_R(this); R[rt] = R[rs]; break; }
case 0b0001: { DECODE_R(this); R[rt] += R[rs]; break; }
case 0b1110: { DECODE_M(this); R[0] = M[addr]; break; }
case 0b1111: { DECODE_M(this); M[addr] = R[0]; break; }
default:
printf("Invalid instruction with opcode = %x, halting...\n", this.rtype.op);
halt = 1;
break;
}
pc ++; // Update PC
}

int main() {
while (1) {
exec_once();
if (halt) break;
}
printf("The result of 16 + 33 is %d\n", M[7]);
return 0;
}

The State Model

For this program, the relevant state consists of PC, R0, R1, and M[7]. We can represent each step as the tuple (PC, R0, R1, M[7]):

1
2
3
4
5
6
7
8
(0, 0, 0, 0)
-> (1, 33, 0, 0)
-> (2, 33, 33, 0)
-> (3, 16, 33, 0)
-> (4, 49, 33, 0)
-> (5, 49, 33, 49)
-> (6, 98, 33, 49)
-> RETURN

The stored result is 49. Execution then reaches data that is interpreted as an instruction: 0b00010000 doubles R0, producing 98, before the following unsupported opcode halts execution. In the displayed source, that 0b00010000 byte is at M[5]; the original note called it M[6].

Defining Instruction Formats With a Union

This small emulator uses PC to fetch one byte from the memory array and decodes the opcode. Either this.rtype.op or this.mtype.op accesses the opcode in this example, because both formats place it in the upper four bits.

A switch selects the operation. The decoding macros declare the operand fields appropriate to the instruction format, after which the emulator performs the arithmetic or assignment. An unsupported opcode sets halt to 1 so the outer loop can terminate. Otherwise, the function returns and the loop executes the next instruction.

Connecting the Two Views

The implementation makes the fetch-and-execute process explicit: read an instruction from memory, then access registers, memory, or arithmetic operations as required.

The state model hides the decoding details and focuses on stored values. The implementation view instead explains the sequence of actions that produces each state transition.


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. All the images used in the blog are my original works or AI works, if you want to take it,don't hesitate. Thank you !