PA2.1 运行dumy

PA2.1实现dumy

    • 实现思路
    • 实验过程
      • 修改opcode_table数组
      • 修改rtl.h文件
      • 实现对应的操作
    • 实验结果

实现思路

在进行代码编写之前,最重要的就是阅读实验手册,确保自己真的理解了实验手册中的内容,再下手也不迟,正所谓磨刀不误砍柴工。PA2实验对我来说还是有一点点的新奇,因为自己并没有接触过编译,所幸自己对计算机组成原理有一点的学习,知道指令的执行和操作过程,理解起来也就变的容易很多啦。
首先,我先根据实验手册中的指示,阅读dumy-x86-nemu.txt文件,弄清楚自己需要实现的指令及指令代码。

00100000 <_start>:                                                                                                     
  100000:   bd 00 00 00 00          mov    $0x0,%ebp
  100005:   bc 00 7c 00 00          mov    $0x7c00,%esp
  10000a:   e8 01 00 00 00          call   100010 <_trm_init>
  10000f:   90                      nop 
 
00100010 <_trm_init>:
  100010:   55                      push   %ebp
  100011:   89 e5                   mov    %esp,%ebp
  100013:   83 ec 08                sub    $0x8,%esp
  100016:   e8 05 00 00 00          call   100020 
10001b: d6 (bad) 10001c: eb fe jmp 10001c <_trm_init+0xc> 10001e: 66 90 xchg %ax,%ax 00100020
: 100020: 55 push %ebp 100021: 89 e5 mov %esp,%ebp 100023: 31 c0 xor %eax,%eax 100025: 5d pop %ebp 100026: c3 ret

根据这个文件列出所需要实现的操作指令:

指令 编码 Description
call e8 Call near, displacement relativeto next instruction
push 50 + rw /rb Push register word/dword
sub 83 Subtract sign-extended immediate byte from r/m word
xor 31 Exclusive-OR dword register to r/m word/dword
pop 58+rw/rb Pop top of stack into word/dword register
ret c3 Return (near) to caller

这里我介绍下我的debug之路

Created with Raphaël 2.2.0 Start 阅读i386手册 修改ics-pa/nemu/src/cpu/exec/exec.c中的opcode_able 修改ics-pa/nemu/include/cpu/rtl.h中的操作函数 实现ics-pa/nemu/src/cpu/* 中的操作函数 测试 测试是否通过 End yes no

(第一次用markdown画流程图。。有点丑,以后会慢慢改进的。。)

实验过程

修改opcode_table数组

首先在all-insert中添加所需要的指令名字:

make_EHelper(call);			// control.c
make_EHelper(push); 		//data-mov.c
make_EHelper(sub); 			//arith.c
make_EHelper(xor); 			//logic.c
make_EHelper(pop);			//data-mov.c
make_EHelper(ret);  		//control.c

修改opcode_table

/* 0x80, 0x81, 0x83 */                                                                                               
	make_group(gp1,   
    EX(add), EX(or), EX(adc), EX(sbb),
    EX(and), EX(sub),EX(xor), EX(cmp))
    
/* 0xe8 */    IDEX(J,call), IDEX(J,jmp), IDEX(I,jmp_rm), IDEXW(J,jmp,1),
/* 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),
/* 0x58 */    IDEX(r,pop), IDEX(r,pop), IDEX(r,pop), IDEX(r,pop),
/* 0x5c */    IDEX(r,pop), IDEX(r,pop), IDEX(r,pop), IDEX(r,pop),
/* 0x30 */    IDEXW(G2E,xor,1), IDEX(G2E,xor), IDEXW(E2G,xor,1),IDEX(E2G,xor),
/* 0x34 */    IDEXW(I2a,xor,1), IDEX(I2a,xor), EMPTY, EMPTY,
/* 0xc0 */  IDEXW(gp2_Ib2E, gp2, 1), IDEX(gp2_Ib2E, gp2), IDEXW(I,ret,2), EX(ret),

修改rtl.h文件

  1. push和pop操作
    按i386手册和框架中给出的提示完成入栈和出栈的操作。
static inline void rtl_push(const rtlreg_t* src1) {
 // esp <- esp - 4
   rtl_subi(&cpu.esp,&cpu.esp,4);
 // M[esp] <- src1
   rtl_sm(&cpu.esp,src1,4);
}   
   
static inline void rtl_pop(rtlreg_t* dest) {
 // dest <- M[esp]
   //*dest = vaddr_read(cpu.esp,4);
   rtl_lm(dest,&cpu.esp,4);
 // esp <- esp + 4
   //cpu.esp += 4;
   rtl_addi(&cpu.esp,&cpu.esp,4);                                                                                     
} 
  1. 更新标志符操作
static inline void rtl_setrelopi(uint32_t relop, rtlreg_t *dest,
   const rtlreg_t *src1, int imm) {
 // dest <- (src1 relop imm ? 1 : 0)
   rtl_li(&at, imm);
 *dest = interpret_relop(relop, *src1, at);
}  
  
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);
} 
static inline void rtl_update_ZF(const rtlreg_t* result, int width) {
 // eflags.ZF <- is_zero(result[width * 8 - 1 .. 0])
   rtl_shli(&at, result, 32 - width * 8);
   if(at == 0) cpu.eflags.ZF = 1;
   else cpu.eflags.ZF = 0;
}  
  
static inline void rtl_update_SF(const rtlreg_t* result, int width) {
 // eflags.SF <- is_sign(result[width * 8 - 1 .. 0])
   rtl_msb(&at, result, width);
   cpu.eflags.SF = at; 
}

实现对应的操作

  1. call指令
    CALL:读取要压栈的eip值,然后用rtl_push压栈,然后设置跳转。
make_EHelper(call) {               
  // the target address is calculated at the decode stage
    // Don't forget to add push eip
  rtl_push(eip);                   
  rtl_j(decoding.jmp_eip);         
  print_asm("call %x", decoding.jmp_eip);                                                                              
}  
  1. push和pop指令
make_EHelper(push) {                    
    rtl_push(&id_dest->val);            
    print_asm_template1(push);          
}   
    
make_EHelper(pop) {                     
    rtl_pop(&id_src->val);              
    operand_write(id_dest,&id_src->val);
    print_asm_template1(pop);                                                                                          
}  
  1. sub指令(参考sbb指令)
    在实现sub指令前需要写eflags结构体(这个根据i386进行编写即可。)
make_EHelper(sub) {          
  rtl_sub(&t2, &id_dest->val, &id_src->val);
  rtl_setrelop(RELOP_LTU, &t3, &id_dest->val, &t2);
  operand_write(id_dest, &t2);
  rtl_update_ZFSF(&t2, id_dest->width);
 
  rtl_setrelop(RELOP_LTU, &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);
}

注意,还需要实现**static inline make_DopHelper(SI)**内的功能,这个按照注释实现就可以了。
4. xor指令

make_EHelper(xor) {
    rtl_xor(&id_dest->val, &id_dest->val, &id_src->val);
    operand_write(id_dest,&id_dest->val);
  
    rtl_li(&t0,0);
    rtl_set_CF(&t0);
    rtl_set_OF(&t0);
    //CF = OF = 0
  
    rtl_update_ZFSF(&id_dest->val, id_dest->width);
  
  print_asm_template2(xor);
}  
  1. ret指令
make_EHelper(ret) {         
    rtl_pop(&decoding.jmp_eip);
    rtl_j(decoding.jmp_eip);
    print_asm("ret");                                                                                                  
}   

实验结果

你可能感兴趣的:(PA实验)