计算机组成原理实验---PA实验PA2.1

目录

思考题

由于md图片的显示问题,具体可以联系qq 邮箱1528882125@qq.com获取本实验全套的代码和报告

1. 增加了多少(10分)

增加了存储地址,操作数地址,指令地址和opcode

2. 是什么类型(10分)

opcode_entry 类型,set_width 表示操作数长度,make_DHelper表示译码函数 ,make_EHelper 表示执行函数

3.操作数结构体的实现(10分)

结构体/共同体中存储:操作数的类型,操作数的宽度,操作数的地址,寄存器操作数,立即操作数,寄存器和操作数指令

4.复现宏定义(30分)

展开结果如下:

make_EHelper(mov) //mov 指令的执行函数 

\#define make_EHelper(name) void concat(exec_, name) (vaddr_t *eip) 

\#define concat(x, y) concat_temp(x, y) 

\#define concat_temp(x, y) 

make_EHelper(push) //push 指令的执行函数 

\#define make_EHelper(name) void concat(exec_, name) (vaddr_t *eip) 

\#define concat(x, y) concat_temp(x, y) 

\#define concat_temp(x, y) 

make_DHelper(I2r) //I2r 类型操作数的译码函数 

\#define make_DHelper(name) void concat(decode_, name) (vaddr_t *eip)* 

IDEX(I2a, cmp) //cmp 指令的 opcode_table 表项 #define IDEXW(id, ex, w) 

{concat(decode_, id), concat(exec_, ex), w} 

EX(nop) //nop 指令的 opcode_table 表项 

\#define EX(ex) EXW(ex, 0) 

make_rtl_arith_logic(and) //and 运算的 RTL 指令 

\#define make_rtl_arith_logic(name) \ 

static inline void concat(rtl_, name) (rtlreg_t* dest, const rtlreg_t* src1, 

const rtlreg_t* src2) { \ 

*dest = concat(c_, name) (*src1, *src2); \ } 

\ static inline void concat3(rtl_, name, i) (rtlreg_t* dest, const rtlreg_t* 

src1, int imm) 

{ \ *dest = concat(c_, name) (*src1, imm); \ }

5.立即数背后的故事(10分)

注意不同的计算机读取方式可能不同,有大端和小端的区别,读取数据时需要格外注意

6.神奇的 eflags (20分)

当超出表示范围时产生溢出,CF不能代替OF,并不是所有的进位情况都会超出表示范围。两个数相加

结果异号,OF为1,反之为0,两数相减,结果与减数相同,OF为1,反之为0。

7.git branchgit log 截图(10分)

git branch:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tmtxfVth-1649584585743)(C:\Users\86178\Documents\计组\PA2.1\picture\git branch.JPG)]

git log:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z8F5wAln-1649584585745)(C:\Users\86178\Documents\计组\PA2.1\picture\gitlog.JPG)]

操作题

1.实现标志寄存器( 10 分)

cpu/reg.h 中的寄存器结构体中定义eflags 寄存器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iPpIAxUi-1649584585745)(C:\Users\86178\Documents\计组\PA2.1\picture\1.1.png)]

nemu/src/monitor/monitor.c 中的static inline void restart() 中设定eflags 寄存器的初值为0x2 实现初值的设置。

2.实现所有 RTL 指令( 30 分)

mov 指令和not 指令
  • 思路较为简单直接在变量基础上记性赋值或者按为取反再赋值即可。
static inline void rtl_mv(rtlreg_t* dest, const rtlreg_t *src1){ 
    // dest <- src1
    //TODO(); 
    *dest=*src1; 
} rtl_not 按位取反再赋给原变量。 
static inline void rtl_not(rtlreg_t* dest) { 
    // dest <- ~dest
    //TODO();
    *dest=~*dest; 
}
sext 指令
  • 实现符号扩展,因为寄存器是无符号整型数,所以先将寄存器强制转换为带符号整型数据,再通过长度判断移位数。
static inline void rtl_sext(rtlreg_t* dest, const rtlreg_t* src1, int width) { 
    // dest <- signext(src1[(width * 8 - 1) .. 0])
    //TODO(); 
    int32_t t=(int32_t)* src1;
    width=width<<3; 
    t=t>>width;
    t=t<<width; 
    *dest=t;
}
pushpop指令
  • 读取寄存器实现进栈和出栈操作
static inline void rtl_push(const rtlreg_t* src1) { 
    // esp <- esp - 4 
    // M[esp] <- src1 
    //TODO(); cpu.esp=cpu.esp-4;
    rtl_sm(&cpu.esp,4,src1);
}
static inline void rtl_pop(rtlreg_t* dest) { 
    // dest <- M[esp] 
    // esp <- esp + 4 
    //TODO(); 
    rtl_lm(dest,&cpu.esp,4); 
    cpu.esp=cpu.esp+4; 
}
eq0指令
  • 判断 src1 是否为0,为0则 *dest1,反之 *dest0
static inline void rtl_eq0(rtlreg_t* dest, const rtlreg_t* src1) { 
    // dest <- (src1 == 0 ? 1 : 0) 
    //TODO();
    * dest=*src1==0?1:0; 
}
eqi指令
  • 判断 src1 是否等与 imm ,相等 *dest1,不等 *dest0
static inline void rtl_eqi(rtlreg_t* dest, const rtlreg_t* src1, int imm) {
    // dest <- (src1 == imm ? 1 : 0) 
    //TODO();
    * dest=*src1==imm?1:0;
}
neq0指令
  • 判断 src1 是否为1,为1*dest1,反之 *dest0
static inline void rtl_neq0(rtlreg_t* dest, const rtlreg_t* src1) { 
    // dest <- (src1 != 0 ? 1 : 0) 
    //TODO();
    *dest=*src1!=0?1:0; 
}
msb指令
  • *src1 指向的数的符号位赋给 *dest
static inline void rtl_msb(rtlreg_t dest, const rtlreg_t* src1, int width) { 
    `// dest <- src1[width * 8 - 1] 
        //TODO(); 
        rtl_shr(dest,src1,width*8-1);
}
update_ZF指令
  • 更新ZF位,先移位,再判断是否为0,为0ZF位为1,反之ZF位为0
static inline void rtl_update_ZF(const rtlreg_t* result, int width) { 
    // eflags.ZF <- is_zero(result[width * 8 - 1 .. 0]) 
    //TODO(); 
    rtlreg_t t; 
    rtl_shli(&t,result,32-width*8); 
    if(t==0) cpu.eflags.ZF=1; 
    else 
        cpu.eflags.ZF=0; 
}
update_SF指令
  • 更新SF位,调用 rtl_msb*result 指向数的符号位赋给SF位。
*static inline void rtl_update_SF(const rtlreg_t* result, int width) { 
    // eflags.SF <- is_sign(result[width * 8 - 1 .. 0]) 
    //TODO(); 
    rtlreg_t t; 
    rtl_msb(&t,result,width); 
    cpu.eflags.SF=t; 
}

3.实现 6 条 x86 指令( 30 分)

首先需要在all-instr.h中声明所有的指令

如下:

#include "cpu/exec.h"

make_EHelper(mov);

make_EHelper(operand_size);

make_EHelper(inv);
make_EHelper(nemu_trap);
make_EHelper(call);
make_EHelper(push);
make_EHelper(pop);
make_EHelper(sub);
make_EHelper(xor);
make_EHelper(ret);
make_EHelper(nop);
make_EHelper(lea);
make_EHelper(and);
make_EHelper(jmp);
make_EHelper(add);
make_EHelper(cmp);
make_EHelper(leave);
make_EHelper(setcc);
make_EHelper(movzx);
make_EHelper(call_rm);
make_EHelper(jmp_rm);
make_EHelper(inc);
make_EHelper(dec);
make_EHelper(jcc);
make_EHelper(adc);
make_EHelper(or);
make_EHelper(test);
make_EHelper(shl);
make_EHelper(shr);
make_EHelper(sar);
make_EHelper(not);
make_EHelper(div);
make_EHelper(idiv);
make_EHelper(mul);
make_EHelper(imul1);
make_EHelper(imul2);
make_EHelper(cwtl);
make_EHelper(cltd);
make_EHelper(movsx);
make_EHelper(sbb);
make_EHelper(in);
make_EHelper(out);
make_EHelper(neg);
make_EHelper(rol);
make_EHelper(lidt);
make_EHelper(int);
make_EHelper(pusha);
make_EHelper(popa);
make_EHelper(iret);
make_EHelper(mov_r2cr);
make_EHelper(mov_cr2r);

call指令

根据视频里面的提示,可以很快完成 call。先填表,转译函数选择 make_DHelper(I),根据 i386 手 册的代码框架,补充opcode 数组

/* 0xe8 */	IDEX(I,call), IDEX(J,jmp), EMPTY, IDEXW(J,jmp,1),

编写在 control.c中的make_EHelper(call)


make_EHelper(call) {
  // the target address is calculated at the decode stage
 // TODO();
	rtl_push(eip);//将eip压栈
	decoding.is_jmp=1;//设置跳转标志
	rtl_add(&decoding.jmp_eip,eip,&id_dest->val);//将两个相加赋给
 	print_asm("call %x", decoding.jmp_eip);
}

运行结果:

10000a:   e8 0f 00 00 00                        call 10001e
push指令

到指令55 停止,查找对应push 指令填写opcode 数组

 /* 0x50 */	IDEX(r,push),IDEX(r,push),IDEX(r,push),IDEX(r,push),
 /* 0x54 */	IDEX(r,push),IDEX(r,push),IDEX(r,push),IDEX(r,push),
  

之后在data-mov.c 中补充指令

make_EHelper(push) {
 // TODO();
	rtl_push(&id_dest->val);//将值压栈 
  	print_asm_template1(push);
}

运行结果:

10001e:   55                                    pushl %ebp

#### sub指令

运行到83停止,对应sub操作,修改grp1数组,sub指令在第六个的位置

	/* 0x80, 0x81, 0x83 */
	make_group(gp1,
    EX(add), EX(or), EX(adc),EX(sbb),
    EX(and), EX(sub), EX(xor), EX(cmp))

arith.c 中补充sub指令

make_EHelper(sub) {
  //TODO();
  //参考sbb
	rtl_sub(&t2, &id_dest->val, &id_src->val);
  	rtl_sltu(&t3, &id_dest->val, &t2);
  	operand_write(id_dest, &t2);
	//设置ZF,SF标志位
  	rtl_update_ZFSF(&t2, id_dest->width);
	//设置CF标志位
  	rtl_sltu(&t0, &id_dest->val, &t2);
  	rtl_or(&t0, &t3, &t0);
  	rtl_set_CF(&t0);
	//设置OF标志位
  	rtl_xor(&t0, &id_dest->val, &id_src->val);
  	rtl_xor(&t1, &id_dest->val, &t2);
  	rtl_and(&t0, &t0, &t1);
  	rtl_msb(&t0, &t0, id_dest->width);
  	rtl_set_OF(&t0);
  	print_asm_template2(sub);
}

运行结果:

100021:   83 ec 18                              subl $0x18,%esp
nop指令

对应90,填写opcode表格即可

/* 0x90 */	EX(nop), EMPTY, EMPTY, EMPTY,

运行结果:

100012:   90                                    nop
pop指令

再运行到5d停止,参考反汇编,对应 pop ,参考 push,填表

编写在data-mov.c中的 make_EHelper(pop)

make_EHelper(pop) {
  //TODO();
	rtl_pop(&t0);//寄存器出栈
  	if(id_dest->type==OP_TYPE_REG)//寄存器操作数,写入寄存器
	{
		rtl_sr(id_dest->reg,id_dest->width,&t0);
	}
  	else if(id_dest->type==OP_TYPE_MEM)//内存,写入内存
	{
		rtl_sm(&id_dest->addr,id_dest->width,&t0);
	}
  	else {assert(0);}
  	print_asm_template1(pop);
}

运行结果:

 100013:   5d                                    popl %ebp
ret指令

再运行到c3停止,参考反汇编,对应ret 操作,无译码函数,直接执行

/* 0xc0 */	IDEXW(gp2_Ib2E, gp2, 1), IDEX(gp2_Ib2E, gp2), IDEXW(I,ret,2), EX(ret),

编写在control.c中的 make_EHelper(ret),参考 make_EHelper(call),将 eip退栈,并设置跳转标志。

make_EHelper(ret) {
  //TODO();
	rtl_pop(&decoding.jmp_eip);//将eip出栈
	if(decoding.opcode==0xc2)//0xc2处,esp+dest
	{
		cpu.esp+=id_dest->val;
	}
	decoding.is_jmp=1;//设置跳转标志
  	print_asm("ret");
}

运行结果:

 100014:   c3                                    ret

4.成功运行 dummy(10 分)

nexus-am/tests/cputest 目录下输入指令

make ARCH=x86-nemu ALL=dummy run

输入si30 输出后出现HIT GOOD TRAP 的信息,说明功能实现成功,如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8r8crIhI-1649584585747)(C:\Users\86178\Documents\计组\PA2.1\picture\dummy.JPG)]

5.实现 Diff-test( 20 分)

首先需要在在 nemu/include/common.h 中定义宏 DIFF_TEST ,取消注释即可

然后在 nemu/src/monitor/difftest/diff-test.c 中编写difftest_step() 函数比较寄存器的值,若不一样设为ture一样的话设为false

代码如图所示

	if(r.eip!=cpu.eip||r.eax!=cpu.eax||r.ebx!=cpu.ebx||r.ecx!=cpu.ecx||r.edx!=cpu.edx||r.ebp!=cpu.ebp||r.esp!=cpu.esp||r.edi!=cpu.edi||r.esi!=cpu.esi)
	{
		diff=true;
		printf("r.eip:%#x,cpu.eip:%#x\n",r.eip,cpu.eip);
	    printf("r.eax:%#x,cpu.eax:%#x\n",r.eax,cpu.eax);
 		printf("r.ebx:%#x,cpu.ebx:%#x\n",r.ebx,cpu.ebx);
		printf("r.ecx:%#x,cpu.ecx:%#x\n",r.ecx,cpu.ecx);
		printf("r.edx:%#x,cpu.edx:%#x\n",r.edx,cpu.edx);
		printf("r.ebp:%#x,cpu.ebp:%#x\n",r.ebp,cpu.ebp);
		printf("r.esp:%#x,cpu.esp:%#x\n",r.esp,cpu.esp);
		printf("r.edi:%#x,cpu.edi:%#x\n",r.edi,cpu.edi);
		printf("r.esi:%#x,cpu.esi:%#x\n",r.esi,cpu.esi);
	}	

遇到的问题及解决方法

1.遇到问题:在把每条x86 指令实现完成之后运行nemu 仍然显示没有定义函数

​ 解决方法:首先排除函数没有实现的缘故,因为绝大部分TODO 部分都已经实现完成了,经过排查是没有在all-instr.h中声明所有的指令造成的错误,之后全 部进行定义就解决了问题。

实验心得

个人感觉本次实验难度较大,对之前的寄存器的知识也有一部分的考察,对指令的内容,汇编反汇编的内容都融合在一起进行综合的应用因而提升了实验的难度但是在结束PA2.1 的内容之后,可以对指令进行更加深入的了解,对理论课的学习复习都有帮助。

备注

助教真帅,解决了我实验中遇到的问题

明天会更好,实验继续加油

遇到的问题及解决方法

1.遇到问题:在把每条x86 指令实现完成之后运行nemu 仍然显示没有定义函数

​ 解决方法:首先排除函数没有实现的缘故,因为绝大部分TODO 部分都已经实现完成了,经过排查是没有在all-instr.h中声明所有的指令造成的错误,之后全 部进行定义就解决了问题。

实验心得

个人感觉本次实验难度较大,对之前的寄存器的知识也有一部分的考察,对指令的内容,汇编反汇编的内容都融合在一起进行综合的应用因而提升了实验的难度但是在结束PA2.1 的内容之后,可以对指令进行更加深入的了解,对理论课的学习复习都有帮助。

备注

助教真帅,解决了我实验中遇到的问题

明天会更好,实验继续加油

  • 8
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值