0%

LC-3

动手写个虚拟机

​ 写这个东西首先要知道大概的分区,刚开始是头文件,然后是定义的一些东西(包括函数),然后是主函数,在主函数中我们需要用switch来对一些东西进行操作,我们不可能直接写什么vm虚拟机那种东西,所以先从LC-3这个虚拟机开始。不过https://justinmeiners.github.io/lc3-vm/文章是有些乱的,所以我再将文章整理一遍.

虚拟机:虚拟机就是像一台电脑那样运行的程序。它主要模拟了CPU以及其他一小部分硬件,让被模拟的CPU就做算术运算,读写内存,与I/O设备交互,就像一台真正的电脑一样。我们所写的虚拟机是LC-3,能够模仿一些简单的操作,在c语言编成的虚拟机的基础上运行一些程序。

1、读取文件

我们要在一台机器上加载文件,从而运行,那就必须开辟空间

代码1-1

1
2
#define UINT16_MAX 65536
uint16_t memory[UINT16_MAX];//创建2的16次方内存即128kb

代码1-2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main(int argc, const char* argv[])
{
if (argc < 2)
{
printf("lc3 [image-file1]...\n");
exit(2);
}
for (int j = 1; j < argc; ++j)
{
if (!read_image(argv[j]))
{
printf("failed to load image: %s\n",argv[j]);
exit(1);
}
}/*{Load Arguments,5}*/
...

}

这个主函数中最前面一段是牵扯到了内存,argc 和 argv 是 main 函数参数,第一个代表main参数个数,第二个代表为地址的参数;

该if函数是加载VM映像来使这台VM能够运行。如果命令行参数没有给出镜像路径的话就打印一条错误信息。

也就是运行程序的时候的路径要对:

1
.\16VM.exe C:/Users/86159/Downloads/2048.obj

用 read_image() 函数计算参数并判断,若返回值是0,就会说:装载映像失败;那么这个函数是什么呢:

代码1-3

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
uint16_t swap16(uint16_t x)
{
return (x << 8) | (x >> 8);/*改成小端序,交换前8位和后8位*/


void read_image_file(FILE* file)
{
uint16_t origin;
fread(&origin,sizeof(origin), 1, file);
origin = swap16(origin);
uint16_t max_read = UINT16_MAX - origin;
uint16_t* p = memory + origin;
size_t read = fread(p,sizeof(uint16_t), max_read,file);
while(read-- > 0)
{
*p = swap16(*p);
++p;
}
}



int read_image(const char* image_path)
{
FILE* file = fopen(image_path, "rb");
if(!file){return 0;};
read_image_file(file);
fclose(file);
return 1;
}

后者代码就是来判断是否读取到正确路径,没有就返回0,然后进入前者代码。

read_image_file() 函数的作用是将要在该虚拟机上运行的程序内存装入之前设定好的memory数组中;首先fread读取起始地址:origin,然后使用swap16函数进行大小端序的转换,在LC-3上采用大端序!!之后计算最大可容纳地址,并用p指针标记,最后使用循环不断缩小范围;

2、寄存器

我们需要模拟cpu中的寄存器,我们用枚举的方法来创建寄存器:

代码2-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum
{
R_R0 = 0,
R_R1,
R_R2,
R_R3,
R_R4,
R_R5,
R_R6,
R_R7,
R_PC, /*程序计数器,相当于rip*/
R_COND,
R_COUNT/*寄存器数量*/
};/*前10个为寄存器,前八个为通用寄存器,cond为条件标志寄存器*/

然后将其存放在数组里:

代码2-2

1
uint16_t reg[R_COUNT];

接下来还有flag的表示方法:

代码2-3

1
2
3
4
5
6
enum
{
FL_POS = 1 << 0,/*P*/
FL_ZRO = 1 << 1,/*Z*/
FL_NEG = 1 << 2,/*N*/
};

移位的值就是他们的意义!

3、指令集

指令集是对cpu中的寄存器发出命令的东西

代码3-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum
{
OP_BR = 0,/*分支*/
OP_ADD,/*添加*/
OP_LD,/*加载*/
OP_ST,/*存储*/
OP_JSR,/*跳转寄存器*/
OP_AND,/*按位和*/
OP_LDR,/*加载寄存器*/
OP_STR,/*存储寄存器*/
OP_RTI,/*未使用*/
OP_NOT,/*按位非*/
OP_LDI,/*加载间接*/
OP_STI,/*存储间接*/


OP_JMP,/*跳转*/
OP_RES,/*保留(未使用)*/
OP_LEA,/*加载有效地址*/
OP_TRAP/*执行陷阱*/
};

我们简单模拟了几个操作,要注意的是LC-3只有16种操作码。CPU所能做的只是执行一系列指令而已。每条指令都是16位长,高4位用来存放操作码,剩下的12位存放参数。所以在我们眼中只有前4位是有用的!

代码3-2

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
uint16_t sign_extend(uint16_t x, int bit_count)
{
if((x >> (bit_count - 1)) & 1)//判断输入数少了右边bit-1位后是否等于0
{
x |= (0xffff << bit_count);
}
return x;
}


void update_flags(uint16_t r)/*更新标志位*/
{
if (reg[r] == 0)
{
reg[R_COND] = FL_ZRO;
}
else if (reg[r] >> 15)/*最左侧1为负数*/
{
reg[R_COND] = FL_NEG;
}
else
{
reg[R_COND] = FL_POS;
}
}

第一个是符号拓展,符号拓展将正数前面补上0,负数前面补上1,这样得到的16位数字依然和我们最初赋的值相等。这样能使数全是16位,且值不变,很巧妙。下面的函数是对寄存器进行flags的赋值,即更新标志位

4、内存访问

代码4-1

1
2
3
4
5
enum
{
MR_KBSR = 0XFE00,/*键盘状态*/
MR_KBDR = 0XFE02/*键盘数据*/
};

上面是模拟LC-3 的两个内存寄存器;第一个为状态管理,第二个是数据管理;

代码4-2

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
uint16_t check_key()
{
return WaitForSingleObject(hStdin, 1000) == WAIT_OBJECT_0 && _kbhit();
}

void mem_write(uint16_t address, uint16_t val)
{
memory[address] = val;
}

uint16_t mem_read(uint16_t address)
{
if (address == MR_KBSR)
{
if (check_key())
{
memory[MR_KBSR] = (1 << 15);
memory[MR_KBDR] = getchar();
}
else
{
memory[MR_KBSR] = 0;
}
}
return memory[address];
}

这里有三个函数,这个属于调用了windows的api,第一个是设置windows终端输入的代码,第二个是写入内存的代码,第三个是读入内存的代码。

5、c语言模拟指令

ADD指令

代码5-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
uint16_t r0 = (instr >> 9) & 0x7;/*目标寄存器*/
uint16_t r1 = (instr >> 6) & 0x7;/*第一个操作数SR1*/
uint16_t imm_flag = (instr >> 5) & 0x1;/*是否是立即模式*/
if(imm_flag)
{
uint16_t imm5 =sign_extend(instr & 0x1f,5);
reg[r0] = reg[r1] + imm5; //立即模式
}
else
{
uint16_t r2 = instr & 0x7;
reg[r0] = reg[r1] + reg[r2]; //寄存器模式
}
update_flags(r0);
}

这是两种形式,其实从csapp里看是有三种形式的,这时两行开始出现不同,第一行第5位为0,而第二行为1。这个位表示ADD指令是立即模式还是寄存器模式,这就是判断是否用立即模式的标准!

1
ADD R2, R0, R1  ; R2 = R0 + R1   //寄存器模式
1
ADD R0, R0, 1 ; R0 = R0 + 1   //立即模式

AND指令:

代码5-2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t r1 = (instr >> 6) & 0x7;
uint16_t imm_flag = (instr >> 5) & 0x1;

if (imm_flag)
{
uint16_t imm5 = sign_extend(instr & 0x1F, 5);
reg[r0] = reg[r1] & imm5;
}
else
{
uint16_t r2 = instr & 0x7;
reg[r0] = reg[r1] & reg[r2];
}
update_flags(r0);
}

按位和也是分两种模式,立即和寄存器。

NOT指令:

代码5-3

1
2
3
4
5
6
7
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t r1 = (instr >> 6) & 0x7;

reg[r0] = ~reg[r1];
update_flags(r0);
}

按位非不存在有立即数这一种说法。

BR指令:

代码5-4

1
2
3
4
5
6
7
8
{
uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
uint16_t cond_flag = (instr >> 9) & 0x7;
if (cond_flag & reg[R_COND])
{
reg[R_PC] += pc_offset;
}
}

JMP指令:

代码5-5

1
2
3
4
5
{
/* Also handles RET */
uint16_t r1 = (instr >> 6) & 0x7;
reg[R_PC] = reg[r1];
}

JSR指令:

代码5-6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
uint16_t long_flag = (instr >> 11) & 1;
reg[R_R7] = reg[R_PC];
if (long_flag)
{
uint16_t long_pc_offset = sign_extend(instr & 0x7FF, 11);
reg[R_PC] += long_pc_offset; /* JSR */
}
else
{
uint16_t r1 = (instr >> 6) & 0x7;
reg[R_PC] = reg[r1]; /* JSRR */
}
break;
}

这是寄存器跳转指令

LD指令:

代码5-7

1
2
3
4
5
6
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
reg[r0] = mem_read(reg[R_PC] + pc_offset);
update_flags(r0);
}

加载是在内存中进行,所以要算地址。

LDI指令:

代码5-8

1
2
3
4
5
6
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t pc_offset = sign_extend(instr &0x1ff,9);
reg[r0] = mem_read(mem_read(reg[R_PC] + pc_offset));
update_flags(r0);
}

LDR指令:

代码5-9

1
2
3
4
5
6
7
8
9
{
/* destination register (DR) */
uint16_t r0 = (instr >> 9) & 0x7;
/* PCoffset 9*/
uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
/* add pc_offset to the current PC, look at that memory location to get the final address */
reg[r0] = mem_read(mem_read(reg[R_PC] + pc_offset));
update_flags(r0);
}

LEA指令:

代码5-10

1
2
3
4
5
6
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
reg[r0] = reg[R_PC] + pc_offset;
update_flags(r0);
}

ST指令:

代码5-11

1
2
3
4
5
6
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
mem_write(reg[R_PC] + pc_offset, reg[r0]);
update_flags(r0);
}

加载寄存器

STI指令:

代码5-12

1
2
3
4
5
6
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t pc_offset = sign_extend(instr & 0x1ff,9);
mem_write(mem_read(reg[R_PC] + pc_offset),reg[r0]);
update_flags(r0);
}

加载地址

STR指令:

代码5-13

1
2
3
4
5
6
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t r1 = (instr >> 6) & 0x7;
uint16_t offset = sign_extend(instr & 0x3F, 6);
mem_write(reg[r1] + offset, reg[r0]);
}

6、I/O交互

LC-3提供了少量的预定义好的过程来完成一般的任务以及与I/O设备交互。比如有的过程可以读取键盘输入,有的可以在控制台上显示字符串。这些过程叫Trap过程,你可以理解为LC-3的操作系统或者API。每个Trap过程都有一个自己的编码(跟操作码类似)。为了执行Trap过程,需要用到TRAP指令,参数为需要的TRAP号。

代码6-1

1
2
3
4
5
6
7
8
enum {
TRAP_GETC = 0x20, /* get character from keyboard, not echoed onto the terminal */
TRAP_OUT = 0x21, /* output a character */
TRAP_PUTS = 0x22, /* output a word string */
TRAP_IN = 0x23, /* get character from keyboard, echoed onto the terminal */
TRAP_PUTSP = 0x24, /* output a byte string */
TRAP_HALT = 0x25 /* halt the program */
};

代码6-2

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
case OP_TRAP:
{
switch(instr & 0xff)
{
case TRAP_GETC:
{
reg[R_R0] = (uint16_t)getchar(); //输入字符
update_flags(R_R0);
}
break;
case TRAP_OUT:
{
putc((char)reg[R_R0],stdout); //输出字符
fflush(stdout);
}
break;
case TRAP_PUTS:
{
uint16_t* c = memory + reg[R_R0];
while(*c)
{
putc((char)*c,stdout);
++c;
}
fflush(stdout);
}
break;
case TRAP_IN:
{
printf("Enter a character: "); //准备输入字符
char c = getchar();
putc(c,stdout);
fflush(stdout);
reg[R_R0] = (uint16_t)c;
update_flags(R_R0);
}
break;
case TRAP_PUTSP: //输出字符串
{
uint16_t* c = memory + reg[R_R0];
while(*c)
{
char char1 = (*c) & 0xff;
putc(char1, stdout);
char char2 = (*c) >> 8;
if (char2) putc(char2,stdout);
++c;
}
fflush(stdout);
}
break;
case TRAP_HALT: //终止程序
{
puts("HALT");
fflush(stdout);
running = 0;
break;
}
}
}

上面是在trap中嵌套了switch

7、补全代码

代码7-1

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdint.h> // uint16_t
#include <stdio.h> // FILE
#include <signal.h> // SIGINT
/* windows only */
#include <Windows.h>
#include <conio.h> // _kbhit

HANDLE hStdin = INVALID_HANDLE_VALUE;

#define UINT16_MAX 65536

加入api:

代码7-2

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
DWORD fdwMode, fdwOldMode;

void disable_input_buffering()
{
hStdin = GetStdHandle(STD_INPUT_HANDLE);
GetConsoleMode(hStdin, &fdwOldMode); /* save old mode */
fdwMode = fdwOldMode
^ ENABLE_ECHO_INPUT /* no input echo */
^ ENABLE_LINE_INPUT; /* return when one or
more characters are available */
SetConsoleMode(hStdin, fdwMode); /* set new mode */
FlushConsoleInputBuffer(hStdin); /* clear buffer */
}

void restore_input_buffering()
{
SetConsoleMode(hStdin, fdwOldMode);
}

void handle_interrupt(int signal)
{
restore_input_buffering();
printf("\n");
exit(-2);
}

初始化

代码7-3

1
2
signal(SIGINT, handle_interrupt);
disable_input_buffering();

释放

代码7-4

1
restore_input_buffering();

我直接给出脚本:

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<string.h>
#include<signal.h>
#include <Windows.h>
#include <conio.h>

HANDLE hStdin = INVALID_HANDLE_VALUE;

#define UINT16_MAX 65536

uint16_t memory[UINT16_MAX];//创建2的16次方内存即128kb
enum
{
R_R0 = 0,
R_R1,
R_R2,
R_R3,
R_R4,
R_R5,
R_R6,
R_R7,
R_PC, /*程序计数器,相当于rip*/
R_COND,
R_COUNT/*寄存器数量*/
};/*前10个为寄存器,前八个为通用寄存器,cond为条件标志寄存器*/

uint16_t reg[R_COUNT];

enum
{
OP_BR = 0,/*分支*/
OP_ADD,/*添加*/
OP_LD,/*加载*/
OP_ST,/*存储*/
OP_JSR,/*跳转寄存器*/
OP_AND,/*按位和*/
OP_LDR,/*加载寄存器*/
OP_STR,/*存储寄存器*/
OP_RTI,/*未使用*/
OP_NOT,/*按位非*/
OP_LDI,/*加载间接*/
OP_STI,/*存储间接*/

OP_JMP,/*跳转*/
OP_RES,/*保留(未使用)*/
OP_LEA,/*加载有效地址*/
OP_TRAP/*执行陷阱*/
};

enum
{
FL_POS = 1 << 0,/*P*/
FL_ZRO = 1 << 1,/*Z*/
FL_NEG = 1 << 2,/*N*/
};

enum
{
TRAP_GETC = 0X20,/*从键盘获取字符,不回显到终端*/
TRAP_OUT = 0X21,/*输出一个字符*/
TRAP_PUTS = 0X22,/*输出一个单词字符串*/
TRAP_IN = 0X23,/*获取来自键盘的字符,回显到终端*/
TRAP_PUTSP = 0X24,/*输出一个字符串*/
TRAP_HALT =0X25/*停止程序*/
};

enum
{
MR_KBSR = 0XFE00,/*键盘状态*/
MR_KBDR = 0XFE02/*键盘数据*/
};

uint16_t swap16(uint16_t x)
{
return (x << 8) | (x >> 8);/*改成小端序,交换前8位和后8位*/
}
uint16_t sign_extend(uint16_t x, int bit_count)
{
if((x >> (bit_count - 1)) & 1)
{
x |= (0xffff << bit_count);
}
return x;
}

void update_flags(uint16_t r)/*更新标志位*/
{
if (reg[r] == 0)
{
reg[R_COND] = FL_ZRO;
}
else if (reg[r] >> 15)/*最左侧1为负数*/
{
reg[R_COND] = FL_NEG;
}
else
{
reg[R_COND] = FL_POS;
}
}

void read_image_file(FILE* file)
{
uint16_t origin;
fread(&origin,sizeof(origin), 1, file);
origin = swap16(origin);

uint16_t max_read = UINT16_MAX - origin;
uint16_t* p = memory + origin;
size_t read = fread(p,sizeof(uint16_t), max_read,file);

while(read-- > 0)
{
*p = swap16(*p);
++p;
}
}

int read_image(const char* image_path)
{
FILE* file = fopen(image_path, "rb");
if(!file){return 0;};
read_image_file(file);
fclose(file);
return 1;
}

uint16_t check_key()
{
return WaitForSingleObject(hStdin, 1000) == WAIT_OBJECT_0 && _kbhit();
}

void mem_write(uint16_t address,uint16_t val)
{
memory[address] = val;
}

uint16_t mem_read(uint16_t address)
{
if(address == MR_KBSR)
{
if (check_key())
{
memory[MR_KBSR] = (1 << 15);
memory[MR_KBDR] = getchar();
}
else
{
memory[MR_KBSR] = 0;
}
}
return memory[address];
}

DWORD fdwMode, fdwOldMode;

void disable_input_buffering()
{
hStdin = GetStdHandle(STD_INPUT_HANDLE);
GetConsoleMode(hStdin, &fdwOldMode); /* save old mode */
fdwMode = fdwOldMode
^ ENABLE_ECHO_INPUT /* no input echo */
^ ENABLE_LINE_INPUT; /* return when one or
more characters are available */
SetConsoleMode(hStdin, fdwMode); /* set new mode */
FlushConsoleInputBuffer(hStdin); /* clear buffer */
}

void restore_input_buffering()
{
SetConsoleMode(hStdin, fdwOldMode);
}

void handle_interrupt(int signal)
{
restore_input_buffering();
printf("\n");
exit(-2);
}

int main(int argc, const char* argv[])
{
if (argc < 2)
{
printf("lc3 [image-file1]...\n");
exit(2);
}

for (int j = 1; j < argc; ++j)
{
if (!read_image(argv[j]))
{
printf("failed to load image: %s\n",argv[j]);
exit(1);
}
}/*{Load Arguments,5}*/

signal(SIGINT,handle_interrupt);
disable_input_buffering();
reg[R_COND] = FL_ZRO;


enum { PC_START = 0x3000 };
reg[R_PC] = PC_START;

int running = 1;
while (running)
{

uint16_t instr = mem_read(reg[R_PC]++);
uint16_t op = instr >> 12;

switch (op)
{
case OP_ADD:
{
uint16_t r0 = (instr >> 9) & 0x7;/*目标寄存器*/
uint16_t r1 = (instr >> 6) & 0x7;/*第一个操作数SR1*/
uint16_t imm_flag = (instr >> 5) & 0x1;/*是否是立即模式*/

if(imm_flag)
{
uint16_t imm5 =sign_extend(instr & 0x1f,5);
reg[r0] = reg[r1] + imm5;
}
else
{
uint16_t r2 = instr & 0x7;
reg[r0] = reg[r1] + reg[r2];
}
update_flags(r0);
}
break;
case OP_AND:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t r1 = (instr >> 6) & 0x7;/*第一个操作数SR1*/
uint16_t imm_flag = (instr >> 5) & 0x1;/*是否是立即模式*/

if(imm_flag)
{
uint16_t imm5 =sign_extend(instr & 0x1f,5);
reg[r0] = reg[r1] & imm5;
}
else
{
uint16_t r2 = instr & 0x7;
reg[r0] = reg[r1] & reg[r2];
}
update_flags(r0);
}
break;
case OP_NOT:
{

uint16_t r0 = (instr >> 9) & 0x7;
uint16_t r1 = (instr >> 6) & 0x7;
reg[r0] = ~reg[r1];
update_flags(r0);

}
break;
case OP_BR:
{
uint16_t pc_offset = sign_extend(instr & 0x1ff,9);
uint16_t cond_flag = (instr >> 9) & 0x7;
if(cond_flag & reg[R_COND])
{
reg[R_PC] += pc_offset;
}

}
break;
case OP_JMP:
{
uint16_t r1 = (instr >> 6) & 0x7;
reg[R_PC] = reg[r1];
}
break;
case OP_JSR:
{
uint16_t long_flag = (instr >> 11) & 1;
reg[R_R7] = reg[R_PC];
if(long_flag)
{
uint16_t long_pc_offset = sign_extend(instr & 0x7ff,11);
reg[R_PC] += long_pc_offset;
}
else
{
uint16_t r1 = (instr >> 6) & 0x7;
reg[R_PC] = reg[r1];
}
break;
}
break;
case OP_LD:
{
uint16_t r0 =(instr >> 9) & 0x7;
uint16_t pc_offset =sign_extend(instr & 0x1ff,9);
reg[r0] = mem_read(reg[R_PC] + pc_offset);
update_flags(r0);
}
break;
case OP_LDI:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t pc_offset = sign_extend(instr &0x1ff,9);
reg[r0] = mem_read(mem_read(reg[R_PC] + pc_offset));
update_flags(r0);
}
break;
case OP_LDR:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t r1 = (instr >> 6) & 0x7;
uint16_t offset =sign_extend(instr & 0x3f,6);
reg[r0] = mem_read(reg[r1] + offset);
update_flags(r0);
}
break;
case OP_LEA:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t pc_offset = sign_extend(instr & 0x1FF, 9);
reg[r0] = reg[R_PC] + pc_offset;
update_flags(r0);
}
break;
case OP_ST:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t pc_offset = sign_extend(instr & 0x1ff,9);
mem_write(reg[R_PC] + pc_offset,reg[r0]);
}
break;
case OP_STI:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t pc_offset = sign_extend(instr & 0x1ff,9);
mem_write(mem_read(reg[R_PC] + pc_offset),reg[r0]);
}
break;
case OP_STR:
{
uint16_t r0 = (instr >> 9) & 0x7;
uint16_t r1 = (instr >> 6) & 0x7;
uint16_t offset = sign_extend(instr & 0x3F, 6);
mem_write(reg[r1] + offset, reg[r0]);
}
break;
case OP_TRAP:
{
switch(instr & 0xff)
{
case TRAP_GETC:
{
reg[R_R0] = (uint16_t)getchar();
update_flags(R_R0);
}
break;
case TRAP_OUT:
{
putc((char)reg[R_R0],stdout);
fflush(stdout);
}
break;
case TRAP_PUTS:
{
uint16_t* c = memory + reg[R_R0];
while(*c)
{
putc((char)*c,stdout);
++c;
}
fflush(stdout);
}
break;
case TRAP_IN:
{
printf("Enter a character: ");
char c = getchar();
putc(c,stdout);
fflush(stdout);
reg[R_R0] = (uint16_t)c;
update_flags(R_R0);
}
break;
case TRAP_PUTSP:
{
uint16_t* c = memory + reg[R_R0];
while(*c)
{
char char1 = (*c) & 0xff;
putc(char1, stdout);
char char2 = (*c) >> 8;
if (char2) putc(char2,stdout);
++c;
}
fflush(stdout);
}
break;
case TRAP_HALT:
{
puts("HALT");
fflush(stdout);
running = 0;
break;
}
}
}
break;
case OP_RES:
case OP_RTI:
default:
abort();
break;

}
}
restore_input_buffering();
}

我们下载2048软件,来验证是否建好了虚拟机。

成功!!