ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

南京航空航天大学 PA2.1

2021-05-06 19:30:39  阅读:617  来源: 互联网

标签:rtlreg dest rtl make t0 PA2.1 南京航空航天大学 id


PA 2.1

目录

文章目录

思考题

  1. 增加了多少?

    操作码

    源操作数1 或/和 源操作数2(立即数、寄存器编号、存储地址)

    目的操作数地址(寄存器编号、存储地址)

  2. 是什么类型?

    opcode_table每个表项的类型是opcode_entry

    opcode_entry.width记录了操作数长度信息

    opcode_entry.decode是一个函数指针,记录了译码函数

    opcode_entry.execute是一个函数指针,记录了执行函数

  3. 操作数结构体的实现

    typedef struct {
      uint32_t type; //操作数的类型
      int width; //操作数的宽度
      union {
        uint32_t reg; //寄存器编号
        rtlreg_t addr; //操作数的地址
        uint32_t imm; //立即数
        int32_t simm; //带符号立即数
      };
      rtlreg_t val;//操作数的值
      char str[OP_STR_SIZE];//指令的解释
    } Operand;
    

    一般在译码过程中,都会根据操作码的类型先行标记操作数的类型,再根据操作数类型对共同体内的对应变量赋值。val一般存储该操作数的值,str记录了要显示的指令信息

  4. 复现宏定义

    • void exec_mov (vaddr_t *eip)

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

      concat连接为exec_name的形式

    • void exec_push (vaddr_t *eip)

      同上

    • void decode_I2r (vaddr_t *eip)

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

      concat连接为decode_name的形式

    • {decode_I2a, exec_cmp, 0}

      #define IDEXW(id, ex, w)   {concat(decode_, id), concat(exec_, ex), w}
      #define IDEX(id, ex)       IDEXW(id, ex, 0)
      
    • {((void *)0), exec_nop, 0}

      #define NULL ((void *)0)
      #define EXW(ex, w)  {NULL, concat(exec_, ex), w}
      #define EX(ex)  EXW(ex, 0)
      
    static inline void rtl_and (rtlreg_t* dest, const rtlreg_t* src1, const rtlreg_t* src2) { 	  *dest = ((*src1) & (*src2)); 
    } 
    static inline void rtl_andi (rtlreg_t* dest, const rtlreg_t* src1, int imm) { 
    	*dest = ((*src1) & (imm)); 
    }
    
    #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. ⽴即数背后的故事

    要注意机器是大端机还是小端机。

    因为大小端机字节保存的顺序相反。

    使用前先判断大小端机,根据类型使用不同的字节读取方式。

  6. 神奇的 eflags

    溢出表示超过最大可表示范围。

    不能替换,进位不能代表溢出。

    OF=Cn⊕Cn-1。

  7. git branchgit log 截图(最新的,⼀张即可)

实验内容

实现标志寄存器


1.实现eflags

查询i386手册可知,eflags寄存器结构如下,根据讲义介绍,我们只需实现CFZFSFIFOF

通过位域与无名位域适当组合,来组织出eflags的结构

其中value是用来给eflags赋初值时使用

union {
    uint32_t value;//用于赋初值
    struct  {
      uint32_t CF:1;
      uint32_t :5;//无名位域
      uint32_t ZF:1;
      uint32_t SF:1;
      uint32_t :1;
      uint32_t IF:1;
      uint32_t :1;
      uint32_t OF:1;
    };
  }eflags;

2.eflags设置初值

查询i386手册,发现Chapter 10 Initialization第十章与初始化相关。通过阅读可知需要把eflags初始化为0x2

ics2020\nemu\src\monitor\monitor.c找到restart()函数,并在其中添加

cpu.eflags.value=0x2;//eflags赋初始值0x2

3.实现所有指令对标志位的设置

已有6条指令中,通过查询i386手册,可知subxor需要修改标志位

对于sub,模仿sbb,除了不需要减去CF外,其余的均相同

  //更新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);

对于xor,需要更新SF ZFOF=0CF=0

  rtl_li(&t0,0);  rtl_set_CF(&t0);//CF=0  rtl_set_OF(&t0);//OF=0  rtl_update_ZFSF(&id_dest->val,id_dest->width);//更新ZF、SF

实现所有RTL指令


1.rtl_mv

按照注释,简单赋值即可

static inline void rtl_mv(rtlreg_t* dest, const rtlreg_t *src1) {  // dest <- src1  *dest = *src1;}

2.rtl_not

按照注释,简单取非即可

static inline void rtl_not(rtlreg_t* dest) {  // dest <- ~dest  *dest = ~ (*dest);}

3.rtl_sext

函数参数中有width,考虑要对不同大小的数进行符号扩展。我们可以通过带符号整数的算数右移来实现符号扩展,则先左移一定位数使得要求符号处于最高位,再算数右移回原位置,即可实现符号扩展。

static inline void rtl_sext(rtlreg_t* dest, const rtlreg_t* src1, int width) {  // dest <- signext(src1[(width * 8 - 1) .. 0])  int32_t t = *src1;//转为带符号整数  if (width == 1) {//若为1字节    *dest = (t<<24)>>24;//符号扩展  }  else if (width == 2) {//2字节    *dest = (t<<16)>>16;  }  else if (width ==4) {//4字节    *dest = t;  }  else {    assert(0);  }}

4.rtl_push

根据注释,使用rtl实现取寄存器值、减法、存储等功能

static inline void rtl_push(const rtlreg_t* src1) {  // esp <- esp - 4  // M[esp] <- src1  rtl_lr(&t0,R_ESP,4);//获取寄存器ESP的值  rtl_subi(&t0,&t0,4);//ESP-4  rtl_sr(R_ESP,4,&t0);//存储ESP  rtl_sm(&t0,4,src1);//存储要push的内容}

5.rtl_pop

实现思路与rtl_push基本相同

static inline void rtl_pop(rtlreg_t* dest) {  // dest <- M[esp]  // esp <- esp + 4  rtl_lr(&t0,R_ESP,4);//获取寄存器ESP的值  rtl_lm(dest,&t0,4);//获取ESP指定位置的内存的值  rtl_addi(&t0,&t0,4);//ESP+4  rtl_sr(R_ESP,4,&t0);//存储ESP}

6.rtl_eq0

判断src1是否为0,若是,则给dest赋值1,否则赋值0

static inline void rtl_eq0(rtlreg_t* dest, const rtlreg_t* src1) {  // dest <- (src1 == 0 ? 1 : 0)  *dest = *src1 == 0 ? 1 : 0;}

7.rtl_eqi

判断操作数src1与立即数imm是否相等。若相等,则给dest赋值1,否则赋值0

static inline void rtl_eqi(rtlreg_t* dest, const rtlreg_t* src1, int imm) {  // dest <- (src1 == imm ? 1 : 0)  *dest = *src1 == imm ? 1 : 0;}

8.rtl_neq0

判断操作数是否为0

static inline void rtl_neq0(rtlreg_t* dest, const rtlreg_t* src1) {  // dest <- (src1 != 0 ? 1 : 0)  *dest = *src1 != 0 ? 1 : 0;}

9.rtl_msb

获取最高有效位,因为src1是无符号整数,可以通过逻辑右移获取其最高有效位

static inline void rtl_msb(rtlreg_t* dest, const rtlreg_t* src1, int width) {  // dest <- src1[width * 8 - 1]  rtl_shri(dest, src1, width*8 - 1);//逻辑右移}

10.make_rtl_setget_eflags

此宏定义实现了rtl_set_XF()rtl_get_XF()的功能,我们只需要实现对eflags寄存器相应位置的存取即可

#define make_rtl_setget_eflags(f) \  static inline void concat(rtl_set_, f) (const rtlreg_t* src) { \    cpu.eflags.f = *src; \  } \  static inline void concat(rtl_get_, f) (rtlreg_t* dest) { \    *dest = cpu.eflags.f; \  }

11.rtl_update_ZF

更新ZF,根据注释,我们只需判断从width*8-10这些位是否全为0。我们可以通过逻辑左移把原来width*8-1移到最高位,末尾补0。若移位后的数值为0,则ZF=1否则ZF=0

static inline void rtl_update_ZF(const rtlreg_t* result, int width) {  // eflags.ZF <- is_zero(result[width * 8 - 1 .. 0])  t0 = *result;  rtl_shli(&t0,&t0,32-width*8);//逻辑左移,去掉无用位  if (t0 == 0)//若整体为0,即所有位都是0    t1=1;  else    t1=0;  rtl_set_ZF(&t1);//设置ZF}

12.rtl_update_SF

更新SF,获取result符号即可。

static inline void rtl_update_SF(const rtlreg_t* result, int width) {  // eflags.SF <- is_sign(result[width * 8 - 1 .. 0])  rtl_msb(&t0, result, width);//获取result符号  rtl_set_SF(&t0);//设置SF}

实现6条x86指令


1.call

运行dummy发现e8指令没有实现,查询i386手册可知需要实现callA表明操作数是一个立即数,v表明操作数大小可能为2或4字节

据此,我们可以选定译码函数make_DHelper(J),但在此之前,我们还需要实现make_DopHelper(SI),其功能是译码一个带符号立即数。

op->simm=instr_fetch(eip,op->width);//读取指定长度信息

译码函数make_DHelper(J)功能是获取一个带符号立即数,并decoding.jmp_eip 指向id_dest->simm + *eip的位置

实现执行函数make_EHelper(call),由于译码函数已经完成了跳转位置的设定,这里我们只需标志跳转并入栈eip即可

make_EHelper(call) {  decoding.is_jmp=1;//标识跳转  rtl_push(eip);//入栈eip  print_asm("call %x", decoding.jmp_eip);}

最后填写opcode_table

IDEXW(J,call,4)

执行结果如下

2.push

运行dummy发现55指令没有实现,查询i386手册可知需要实现push

选取译码函数make_DHelper(r),其功能是读取操作码中的寄存器信息。

选取执行函数make_EHelper(push)

使用rtl_push指令把取到的操作数入栈

make_EHelper(push) {  rtl_push(&id_dest->val);  print_asm_template1(push);}

最后填写opcode_table

IDEX(r,push)

执行结果如下

3.pop

运行dummy发现5d指令没有实现,查询i386手册可知需要实现pop

选取译码函数make_DHelper(r),其功能是读取操作码中的寄存器信息。

选取执行函数make_EHelper(pop)

使用rtl_pop指令把栈顶内容存入id_dest

make_EHelper(pop) {  rtl_pop(&id_dest->val);  operand_write(id_dest, &id_dest->val);  print_asm_template1(pop);}

最后填写opcode_table

IDEX(r,pop)

执行结果如下

4.sub

运行dummy发现83指令没有实现,查询反汇编结果可知需要实现sub

发现opcode_table中要使用了grp1,查表可知grp1[5]应填写执行函数make_EHelper(sub)

选取执行函数make_EHelper(sub),若src的位数少于dest,则需要位扩展,相减之后的结果存入dest即可

make_EHelper(sub) {  rtl_sub(&t2, &id_dest->val, &id_src->val);  rtl_sltu(&t3, &id_dest->val, &t2);//t3记录是否借位,0表示借位    operand_write(id_dest, &t2);//最终结果写入对应寄存器或内存  rtl_update_ZFSF(&t2, id_dest->width);//更新ZF,SF  rtl_sltu(&t0, &id_dest->val, &t2);//与减去借位后再比  rtl_or(&t0, &t3, &t0);  rtl_set_CF(&t0);  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);}

填写gpr1

make_group(gp1,    EMPTY, EMPTY, EMPTY, EMPTY,    EMPTY, EX(sub), EMPTY, EMPTY)

执行结果如下

5.xor

运行dummy发现31指令没有实现,查询i386手册可知需要实现xor

选取译码函数make_DHelper(G2E) Ev <- Gv

选取执行函数make_EHelper(xor),只需要把两个源操作数异或后的结果存入目的操作数即可。

make_EHelper(xor) {  rtl_xor(&id_dest->val,&id_src->val,&id_src2->val);//异或  operand_write(id_dest,&id_dest->val);//赋值  rtl_li(&t0,0);  rtl_set_CF(&t0);//CF=0  rtl_set_OF(&t0);//OF=0  rtl_update_ZFSF(&id_dest->val,id_dest->width);//更新ZF、SF  print_asm_template2(xor);}

最后填写opcode_table

IDEX(G2E,xor)

执行结果如下

6.ret

运行dummy发现c3指令没有实现,查询i386手册可知需要实现ret

由上图可知ret无译码函数

选取make_EHelper(ret)为执行函数,由于需要跳转回上次call指令的下条指令处,需要设置跳转标志,并出把要跳转的位置出栈赋给decoding.jmp_eip

make_EHelper(ret) {  decoding.is_jmp=1;//设置跳转  rtl_pop(&decoding.jmp_eip);//获取跳转位置  print_asm("ret");}

最后填写opcode_table

EX(ret)

执行结果如下

成功运行dummy


实现Diff-test


nemu/include/common.h中定义宏

#define DIFF_TEST

nemu/src/monitor/diff-test/diff-test.c中修改difftest_step(),实现寄存器的对比。

先定义一个宏定义,用来判断r.regcpu.reg的内容是否相等,若不相等,则令diff=false

#define test_reg(reg) \	if (r.reg != cpu.reg) { \	    diff = true; \	    Log("reg error NEMU.reg=0x%08x QEMU.reg=0x%08x\n",cpu.reg,r.reg); \  }

difftest_step()添加下面代码,实现对8个通用寄存器以及eip的检测

  test_reg(eax);  test_reg(ecx);  test_reg(edx);  test_reg(ebx);  test_reg(ebp);  test_reg(esp);  test_reg(edi);  test_reg(esi);  test_reg(eip);

开启diff-test,成功运行dummy

标签:rtlreg,dest,rtl,make,t0,PA2.1,南京航空航天大学,id
来源: https://blog.csdn.net/CBrilliant/article/details/116460846

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有