要求与规范:
基础组件定义:
寄存器:实现至少8个通用寄存器(如AX, BX, CX, DX, SP, BP, SI, DI)和一个指令寄存器(IR)、一个程序计数器(PC/IP,Instruction Pointer)。寄存器大小应设定为16位。
数据总线:定义一条16位的数据总线,用于CPU内部及CPU与外部存储器之间的数据传输。
控制单元:设计逻辑以解析指令,控制指令的执行流程,包括从内存中读取指令、解码指令、执行指令以及更新PC等。
内存:实现一个简单的内存模型,能够存储指令和数据,至少包含足够空间以加载和执行示例程序。
根据c语言特点,用结构体实现
simple8086
的类,也需要手动考虑一下用什么数据类型表示各个组件。内存这里实现的也不太理想,c里的字符串不是一个单独的数据类型,是字符数组,所以这里用的二维数组表示的。dalao说string不单独作为一个数据类型更好,我还不是dalao只想直接用string8个通用寄存器就是一个int型数组,为了方便起见我也不知道有没有真的方便到我,我想到宏定义各个数组的名称作为索引?其实总线的表示我觉得是很难模拟的,因为实际上并没有数据在传输。。。会在总线更新的函数里完善打印内容的。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#define MEMORY_SIZE 256
#define INSTRUCTION_SIZE 25
// 16位 8个通用寄存器
#define AX 0
#define BX 1
#define CX 2
#define DX 3
#define SP 4
#define BP 5
#define SI 6
#define DI 7
typedef struct{
unsigned int ip;//指令指针
char memory[MEMORY_SIZE][INSTRUCTION_SIZE];
int registers[8];
int data_bus;
bool control_bus_read;
bool control_bus_write;
}simple8086;
指令集实现:
MOV指令:实现数据在寄存器之间的移动。例如,MOV AX, BX
会将BX寄存器的值复制到AX寄存器中。
ADD指令:实现两个寄存器之间的加法操作,并将结果存回其中一个寄存器。例如,ADD AX, BX
会将AX和BX的值相加,并将结果存回AX。
HLT指令:实现停机指令,当CPU执行到HLT指令时,应停止执行后续指令,模拟系统挂起或等待外部中断的状态。程序加载与执行:设计一种方式(如文本文件或硬编码)来加载和执行包含MOV、ADD、HLT等指令的示例程序。程序应能够连续执行指令,直到遇到HLT指令或程序结束。
这里就体现了宏定义寄存器和指令集有点蠢,此乃我觉得写一堆
ifelse
不好看就想写成switch的结果。但容我辩解,一直strcmp
感觉也挺烦的。退一步讲其实应该都是机器码,不过我不知道怎么表示解释器这一步。
// 指令集
#define MOV 0
#define ADD 1
#define HLT 2
void mov(simple8086* cpu,int* reg,int val){
printf("将 %d 移动到寄存器AX",val);
switch(*reg){
case AX:
cpu->registers[AX]=val;
printf("将 %d 移动到寄存器AX\n",val);
break;
case BX:
cpu->registers[BX]=val;
printf("将 %d 移动到寄存器BX\n",val);
break;
case CX:
cpu->registers[CX]=val;
printf("将 %d 移动到寄存器CX\n",val);
break;
case DX:
cpu->registers[DX]=val;
printf("将 %d 移动到寄存器DX\n",val);
break;
}
}
void add(simple8086* cpu,int* reg,int val){
switch (*reg) {
case AX:
cpu->registers[AX]+=val;
printf("给寄存器AX加上%d\n",val);
break;
case BX:
cpu->registers[BX]+=val;
printf("给寄存器BX加上%d\n",val);
break;
case CX:
cpu->registers[CX]+=val;
printf("给寄存器CX加上%d\n",val);
break;
case DX:
cpu->registers[DX]+=val;
printf("给寄存器DX加上%d\n",val);
break;
}
}
void hlt(){
printf("停止执行\n");
exit(0);
}
其实后面调试的时候发现一种我觉得更好的写法,非常滴简洁,我非常喜欢。
void mov(simple8086* cpu,int* reg,int* val){
printf("将 %d 移动到寄存器%s\n",
*val, *reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" : "DX");
cpu->registers[*reg] = *val;
}
void add(simple8086* cpu,int* reg,int* val){
printf("给寄存器%s加上 %d\n",
*reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" : "DX", *val);
cpu->registers[*reg] += *val;
}
void hlt(){
printf("...停止执行\n");
exit(0);
}
调试与输出:
实现一种机制(如控制台输出)来显示每一步执行后的寄存器状态、内存内容以及当前执行的指令,以便于调试和观察CPU的工作过程。
这里就是简单的打印寄存器状态和总线状态的代码
// 显示寄存器状态
void print_reg(simple8086* cpu){
printf("寄存器状态:\n");
printf("\tAX: %d BX: %d CX: %d DX: %d SP: %d BP: %d SI: %d DI: %d\n\n",
cpu->registers[AX],
cpu->registers[BX],
cpu->registers[CX],
cpu->registers[DX],
cpu->registers[SP],
cpu->registers[BP],
cpu->registers[SI],
cpu->registers[DI]);
}
// 更新并显示总线状态
void update_buses(simple8086* cpu,int* op,int* reg,int* val){
printf("总线状态:\n");
printf("地址总线:\n");
switch(*op){
case MOV:
cpu->data_bus = *val;
cpu->control_bus_read=true;
cpu->control_bus_write=false;
printf("\tSOURCE: %s DESTINATION: memory[%d]\n",
*reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" : "DX",cpu->ip);
break;
case ADD:
cpu->data_bus = *val;
cpu->control_bus_read=false;
cpu->control_bus_write=true;
printf("\tSOURCE: %s DESTINATION: %s\n",
*reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" :"DX",
*reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" :"DX");
break;
case HLT:
cpu->data_bus = 0;
cpu->control_bus_read=false;
cpu->control_bus_write=false;
printf("HLT指令:停止执行\n");
break;
}
printf("\t控制总线:\n");
printf("\t\tREAD:%s WRITE:%s\n",cpu->control_bus_read? "TRUE" : "FALSE",cpu->control_bus_write? "TRUE" : "FALSE");
printf("\t数据总线:\n");
printf("\t\tDATA:%d\n",cpu->data_bus);
}
关于指令的执行的打印输出感觉逻辑并不是很清楚,但姑且完成了。
void mov(simple8086* cpu,int* reg,int* val){
printf("将 %d 移动到寄存器%s\n", *val, *reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" :"DX");
cpu->registers[*reg] = *val;
}
void add(simple8086* cpu,int* reg,int* val){
printf("给寄存器%s加上 %d\n", *reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" : "DX", *val);
cpu->registers[*reg] += *val;
}
void hlt(){
printf("...停止执行\n");
exit(0);
}
static char instruction_buffer[INSTRUCTION_SIZE];
char* fetch(simple8086* cpu){
strcpy(instruction_buffer,cpu->memory[cpu->ip]);
printf("正在从内存地址 %u 获取指令:%s\n", cpu->ip, instruction_buffer);
cpu->ip++;
return instruction_buffer;
}
void decode(simple8086* cpu,char* instruction,int* op,int* reg,int* val){
printf("正在解码指令...");
/* 执行部分代码 */
printf("操作码%s 寄存器%s 值%d\n",parts[0],parts[1],*val);
/* 执行部分代码 */
}
// 显示寄存器状态
void print_reg(simple8086* cpu){
printf("寄存器状态:\n");
printf("\tAX: %d BX: %d CX: %d DX: %d SP: %d BP: %d SI: %d DI: %d\n\n",
cpu->registers[AX],
cpu->registers[BX],
cpu->registers[CX],
cpu->registers[DX],
cpu->registers[SP],
cpu->registers[BP],
cpu->registers[SI],
cpu->registers[DI]);
}
// 更新并显示总线状态
void update_buses(simple8086* cpu,int* op,int* reg,int* val){
printf("总线状态:\n");
printf("地址总线:\n");
switch(*op){
case MOV:
/* 执行部分代码 */
printf("\tSOURCE: %s DESTINATION: memory[%d]\n",
*reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" : "DX",cpu->ip);
break;
case ADD:
/* 执行部分代码 */
printf("\tSOURCE: %s DESTINATION: %s\n",
*reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" :"DX",
*reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" :"DX");
break;
case HLT:
/* 执行部分代码 */
printf("HLT指令:停止执行\n");
break;
}
printf("\t控制总线:\n");
printf("\t\tREAD:%s WRITE:%s\n",cpu->control_bus_read? "TRUE" : "FALSE",cpu->control_bus_write? "TRUE" : "FALSE");
printf("\t数据总线:\n");
printf("\t\tDATA:%d\n",cpu->data_bus);
}
// 执行
void execute(simple8086* cpu,int* op,int* reg,int* val) {
printf("正在执行指令: %s...\n",
*op == MOV ? "MOV" : *op == ADD ? "ADD" : *op == HLT ? "HLT" : "未知指令");
/* 执行部分代码 */
update_buses(cpu,op,reg,val);
print_reg(cpu);
}
本次作业大量参考老师给出的python源码。
此乃参考代码的运行结果。
此乃本次完成的代码结果。
从结果上来看已经基本完成需求了。不过操作码,寄存器和值是三个指针感觉好像不是很好,不过暂时没想到更好的办法,就先这样吧。
执行流程图
完整源码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
//内存大小及指令字长
#define MEMORY_SIZE 256
#define INSTRUCTION_SIZE 25
// 16位 8个通用寄存器
#define AX 0
#define BX 1
#define CX 2
#define DX 3
#define SP 4
#define BP 5
#define SI 6
#define DI 7
// 指令集
#define MOV 0
#define ADD 1
#define HLT 2
typedef struct{
unsigned int ip;//指令指针
char memory[MEMORY_SIZE][INSTRUCTION_SIZE];//字符串结尾有多余的“/0”
int registers[8];
int data_bus;
bool control_bus_read;
bool control_bus_write;
}simple8086;
void mov(simple8086* cpu,int* reg,int* val){
printf("将 %d 移动到寄存器%s\n", *val, *reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" :"DX");
cpu->registers[*reg] = *val;
}
void add(simple8086* cpu,int* reg,int* val){
printf("给寄存器%s加上 %d\n", *reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" : "DX", *val);
cpu->registers[*reg] += *val;
}
void hlt(){
printf("...停止执行\n");
exit(0);
}
static char instruction_buffer[INSTRUCTION_SIZE];
char* fetch(simple8086* cpu){
strcpy(instruction_buffer,cpu->memory[cpu->ip]);
printf("正在从内存地址 %u 获取指令:%s\n", cpu->ip, instruction_buffer);
cpu->ip++;
return instruction_buffer;
}
void decode(simple8086* cpu,char* instruction,int* op,int* reg,int* val){
printf("正在解码指令...");
char parts[3][16];
//sscanf从字符串中读取格式化输入的函数
//sscanf返回成功接收到几个参数
int part_count = sscanf(instruction,"%s %s %d",
parts[0],parts[1],val);
printf("操作码%s 寄存器%s 值%d\n",parts[0],parts[1],*val);
//反向译码hhhh 我把字符串翻译成数字怎么不算译码呢
if(strcmp(parts[0],"MOV")==0) {*op = MOV;}
else if(strcmp(parts[0],"ADD")==0){*op = ADD;/*printf("%d",*op);说明解码操作码没问题*/}
else if(strcmp(parts[0],"HLT")==0){*op = HLT;}
//printf("%d",part_count);>>>3
if(part_count>=2){
if(strcmp(parts[1],"AX")==0) {*reg = AX;}
else if(strcmp(parts[1],"BX")==0){*reg = BX;}
else if(strcmp(parts[1],"CX")==0){*reg = CX;/*printf("\n%d\n",*reg);*/}
else if(strcmp(parts[1],"DX")==0){*reg = DX;}
}
}
// 显示寄存器状态
void print_reg(simple8086* cpu){
printf("寄存器状态:\n");
printf("\tAX: %d BX: %d CX: %d DX: %d SP: %d BP: %d SI: %d DI: %d\n\n",
cpu->registers[AX],
cpu->registers[BX],
cpu->registers[CX],
cpu->registers[DX],
cpu->registers[SP],
cpu->registers[BP],
cpu->registers[SI],
cpu->registers[DI]);
}
// 更新并显示总线状态
void update_buses(simple8086* cpu,int* op,int* reg,int* val){
printf("总线状态:\n");
printf("地址总线:\n");
switch(*op){
case MOV:
cpu->data_bus = *val;
cpu->control_bus_read=true;
cpu->control_bus_write=false;
printf("\tSOURCE: %s DESTINATION: memory[%d]\n",
*reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" : "DX",cpu->ip);
break;
case ADD:
cpu->data_bus = *val;
cpu->control_bus_read=false;
cpu->control_bus_write=true;
printf("\tSOURCE: %s DESTINATION: %s\n",
*reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" :"DX",
*reg == AX ? "AX" : *reg == BX ? "BX" : *reg == CX ? "CX" :"DX");
break;
case HLT:
cpu->data_bus = 0;
cpu->control_bus_read=false;
cpu->control_bus_write=false;
printf("HLT指令:停止执行\n");
break;
}
printf("\t控制总线:\n");
printf("\t\tREAD:%s WRITE:%s\n",cpu->control_bus_read? "TRUE" : "FALSE",cpu->control_bus_write? "TRUE" : "FALSE");
printf("\t数据总线:\n");
printf("\t\tDATA:%d\n",cpu->data_bus);
}
// 执行
void execute(simple8086* cpu,int* op,int* reg,int* val) {
printf("正在执行指令: %s...\n",
*op == MOV ? "MOV" :
(*op == ADD ? "ADD" :
(*op == HLT ? "HLT" : "未知指令")));
switch(*op){
case MOV:
mov(cpu,reg,val);
break;
case ADD:
add(cpu,reg,val);
break;
case HLT:
hlt();
break;
default:
printf("未知指令\n");
break;
}
update_buses(cpu,op,reg,val);
print_reg(cpu);
}
void run(simple8086* cpu){
printf("开始执行...\n");
while(1){
char* instruction = fetch(cpu);
int* op = (int*) malloc(sizeof(int));
int* reg = (int*) malloc(sizeof(int));
int* val = (int*) malloc(sizeof(int));
*op = 0;
*reg = 0;
*val = 0;
decode(cpu,instruction,op,reg,val);
execute(cpu,op,reg,val);
free(op);
free(reg);
free(val);
}
}
void Init_simple8086(simple8086 *cpu) {
cpu->ip = 0;
memset(cpu->memory, 0, sizeof(cpu->memory));
memset(cpu->registers, 0, sizeof(cpu->registers));
cpu->data_bus = 0;
cpu->control_bus_read = false;
cpu->control_bus_write = false;
}
int main(){
simple8086 cpu;
Init_simple8086(&cpu);
strcpy(cpu.memory[0], "MOV AX 1");
strcpy(cpu.memory[1], "ADD AX 2");
strcpy(cpu.memory[2], "HLT");
run(&cpu);
return 0;
}