简体   繁体   English

C 语言中的 MIPS 仿真器 - 跳转和分支指令故障排除

[英]MIPS Emulator in C - Troubleshooting jump and branch instructions

I've been tasked with programming an emulator to translate and execute MIPS machine code in C.我的任务是编写一个模拟器来翻译和执行 C 中的 MIPS 机器代码。

Currently, the program takes the MIPS code, translates it into binary, and stores it in an array that all currently works.目前,该程序采用 MIPS 代码,将其转换为二进制文件,并将其存储在当前所有工作的数组中。 The issue lies within the execute function of the code.问题在于代码的执行功能。

Most of the instructions execute but JR, JAL, BNE and BLEZ are not functional.大多数指令执行,但 JR、JAL、BNE 和 BLEZ 不起作用。 I believe this is because when I attempt to change the PC counter to execute the instruction it causes an error.我相信这是因为当我尝试更改 PC 计数器以执行指令时会导致错误。 The registry output must remain the same as when it is returned on MARS and the program counter must increment correctly.注册表输出必须与在 MARS 上返回时保持相同,并且程序计数器必须正确递增。

  // JR
  else if((text[TEXT_POS(pc)] & 0x0000003F) == 0x8)
  {
    printf("| Executing JR:\n");
    int s = (text[TEXT_POS(pc)] & 0x03E00000);
    pc = registers[s]; // <-- I believe the issue lies here.
  }



// JAL
else if((text[TEXT_POS(pc)] & 0xFC000000) == 0x0C000000)
{
  printf("| Executing JAL:");
  int address = (text[TEXT_POS(pc)] & 0x03FFFFFF);
  registers[/*$*/31] = pc + 8; // <-- And here
  pc = address;
}

I am attempting to set the PC counter = to the variable s Since this is how the MIPS Green sheet says it should be executed.我正在尝试将 PC 计数器 = 设置为变量 s 因为这是 MIPS 绿表所说的应该执行的方式。 However, it is not returning the expected value.但是,它没有返回预期值。

// BNE
else if((text[TEXT_POS(pc)] & 0xFC000000) == 0x14000000)
{
  printf("| EXECUTING BNE:\n");
  int s = (text[TEXT_POS(pc)] & 0x03E00000) >> 21;
  int t = (text[TEXT_POS(pc)] & 0x001F0000) >> 16;
  int imm = (text[TEXT_POS(pc)] & 0xFFFF)/*>> 2*/;
  if(registers[s] != registers[t])
  {
    printf("| EXECUTING PC BRANCH - BNE:\n");
    printf("PC (START) = %x\n",pc);
    printf("imm = %d\n", imm);
    pc = pc + 4 + imm; // <-- Unsure about '<< 2' (BLEZ Too)
    printf("PC (END) = %x\n",pc);
  }
}

// BLEZ
else if ((text[TEXT_POS(pc)] & 0xFC000000) == 0x18000000)
{
  printf("| EXECUTING BLEZ:\n");
  int s = (text[TEXT_POS(pc)] & 0x03E00000) >> 21;
  int tempJump = (text[TEXT_POS(pc)] & 0xFFFF) >> 2;
  if(registers[s] <= 0)
  {
    printf("| EXECUTING PC BRANCH - BLEZ :\n");
    pc = pc + 4 + tempJump; // <-- 'Label'? (MIPS REFERENCE SHEET)
  }
}

All this code is doing is executing the operations and this has proved successful for the other types like ADD, ADDI and SLL.所有这些代码所做的就是执行操作,这已证明对于其他类型(如 ADD、ADDI 和 SLL)是成功的。 However as soon as I try to change the program counter (PC) everything breaks and it no longer works.但是,一旦我尝试更改程序计数器(PC),一切都会中断并且不再起作用。

int exec_bytecode()
{ 
  printf("EXECUTING PROGRAM ...\n");
  pc = ADDR_TEXT; // Set program counter to the start of our program.
  int count = 1; // <-- Stops while loop running forever. (REMOVE WHEN DONE!)

  // Debugging for checking value of program counter.
  printf("----- TESTING THE OPERATOR -----\n");
  printf("FIRST ADDRESS = %x\n",pc);
  for(int c1=0; c1<3; c1++)
  {
    printf("index[%d] = %x\n", c1,TEXT_POS(pc));
    pc=pc+4;
    printf("PC (inside the loop) = %x\n",pc);

  }

  // Debugging for checking value of text array.
  printf("----- TEST TEXT ARRAY -----\n");
  for(int c=0; c<3; c++)
  {
    printf("text[%d] = %x\n", c,text[c]);
  }

  pc = ADDR_TEXT; // Set program counter to the start of our program.

  // While loop to execute bytecode until text array is null.
  while(text[TEXT_POS(pc)] != 0)
  {   
    printf("----- WHILE LOOP EXECUTED -----\n"); 
    printf("text array = %x\n", text[TEXT_POS(pc)]);
    printf("PC = %x\n",pc);

    // ADDI - Finished
    if((text[TEXT_POS(pc)] & 0xFC000000) == 0x20000000)
    {
      printf("| EXECUTING ADDI:\n"); // <-- Printf for debuggin purposes
      int s = (text[TEXT_POS(pc)] & 0x03E00000) >> 21;
      int t = (text[TEXT_POS(pc)] & 0x001F0000) >> 16;
      short int imm = text[TEXT_POS(pc)] & 0xFFFF; // Last 16 bits
      registers[t] = registers[s] + imm;
      pc = pc + 4;
    }

    // ANDI - Finished
    else if((text[TEXT_POS(pc)] & 0xFC000000) == 0x30000000)
    {
      printf("| EXECUTING ANDI:\n"); // <-- Printf for debuggin purposes
      int s = (text[TEXT_POS(pc)] & 0x03E00000) >> 21;
      int t = (text[TEXT_POS(pc)] & 0x001F0000) >> 16;
      short int imm = text[TEXT_POS(pc)] & 0xFFFF;
      registers[t] = registers[s] + imm;
      pc = pc + 4;
    }

    // BNE
    else if((text[TEXT_POS(pc)] & 0xFC000000) == 0x14000000)
    {
      printf("| EXECUTING BNE:\n");
      int s = (text[TEXT_POS(pc)] & 0x03E00000) >> 21;
      int t = (text[TEXT_POS(pc)] & 0x001F0000) >> 16;
      int imm = (text[TEXT_POS(pc)] & 0xFFFF)/*>> 2*/;
      if(registers[s] != registers[t])
      {
        printf("| EXECUTING PC BRANCH - BNE:\n");
        printf("PC (START) = %x\n",pc);
        printf("imm = %d\n", imm);
        pc = pc + 4 + imm; // <-- Unsure about '<< 2' (BLEZ Too)
        printf("PC (END) = %x\n",pc);
      }
    }

    // BLEZ
    else if ((text[TEXT_POS(pc)] & 0xFC000000) == 0x18000000)
    {
      printf("| EXECUTING BLEZ:\n");
      int s = (text[TEXT_POS(pc)] & 0x03E00000) >> 21;
      int tempJump = (text[TEXT_POS(pc)] & 0xFFFF) >> 2;
      if(registers[s] <= 0)
      {
        printf("| EXECUTING PC BRANCH - BLEZ :\n");
        pc = pc + 4 + tempJump; // <-- 'Label'? (MIPS REFERENCE SHEET)
      }
    }
    

    else if((text[TEXT_POS(pc)] & 0xFC000000) == 0)
    {
      printf("| R-TYPE IF STATEMENT |\n");
      // ADD - Finished
      if((text[TEXT_POS(pc)] & 0x0000003F) == 0x20)
      {
        printf("| Executing ADD:\n");
        int d = (text[TEXT_POS(pc)] & 0x0000F800) >> 11;
        int s = (text[TEXT_POS(pc)] & 0x03E00000) >> 21;
        int t = (text[TEXT_POS(pc)] & 0x001F0000) >> 16;
        registers[d] = registers[s] + registers[t];
        pc = pc + 4;
      }

      // SLL - Finished
      else if((text[TEXT_POS(pc)] & 0x0000003F) == 0x00)
      {
        printf("| Executing SLL:\n");
        int t = (text[TEXT_POS(pc)] & 0x001F0000) >> 16;
        int d = (text[TEXT_POS(pc)] & 0x0000F800) >> 11;
        short int shamt = (text[TEXT_POS(pc)] & 0x000007C0);
        registers[d] = registers[t] << shamt;
        pc = pc + 4;
      }

      // SRL - Finished
      else if((text[TEXT_POS(pc)] & 0x0000003F) == 0x2)
      {
        printf("| Executing SRL:\n");
        int t = (text[TEXT_POS(pc)] & 0x001F0000) >> 16;
        int d = (text[TEXT_POS(pc)] & 0x0000F800) >> 11;
        short int shamt = (text[TEXT_POS(pc)] & 0x000007C0);
        registers[d] = registers[t] >> shamt;
        pc = pc + 4;
      }

      // JR
      else if((text[TEXT_POS(pc)] & 0x0000003F) == 0x8)
      {
        printf("| Executing JR:\n");
        int s = (text[TEXT_POS(pc)] & 0x03E00000);
        pc = registers[s]; // <-- I believe the issue lies here.
      }
    }

    // JAL
    else if((text[TEXT_POS(pc)] & 0xFC000000) == 0x0C000000)
    {
      printf("| Executing JAL:");
      int address = (text[TEXT_POS(pc)] & 0x03FFFFFF);
      registers[/*$*/31] = pc + 8; // <-- And here
      pc = address;
    }
    
    if(count == 10)
    {
      break;
    }
    count++;
  }

  printf("----- PRINTING REGISTERS -----\n");
  print_registers(); // print out the state of registers at the end of execution

  printf("... DONE!\n");
  return (0);
}

Above is the full EXC function以上是完整的EXC函数

ADDI was given as an example to base the rest on!以 ADDI 为例,作为其余部分的基础!

  • JR

    The handling code fails to right justify the register number: it is unshifted, so I don't see how you can use that for an array index into the register file.处理代码无法右对齐寄存器编号:它没有移位,所以我看不出如何将它用于寄存器文件的数组索引。 Have you tried debugging this C code?您是否尝试过调试此 C 代码? You should have noticed the register number s being very large.您应该已经注意到寄存器编号s非常大。

  • JAL

    The handling code doesn't follow the specification, which is to keep the upper 4 bits of the existing PC+4 (or maybe +0 or even +8 in a simulator that uses different machine-code than real MIPS).处理代码不遵循规范,即保留现有 PC+4 的高 4 位(或者在使用与真实 MIPS 不同的机器代码的模拟器中可能是 +0 甚至 +8)。 Only merge in the shifted 26-bit immediate into the lower 28 bits of the new PC.仅将移位后的 26 位立即数合并到新 PC 的低 28 位中。

  • BNE

    The immediate is signed 16 bits, so sign extend to 32 bits and then shift it to quadruple.立即数是有符号的 16 位,因此符号扩展到 32 位,然后将其移位为四位。 Sign extending a 16 bit value to 32 bits is easy in C: just cast to short , we won't even need the mask: (short) text[TEXT_POS(pc)] .将 16 位值扩展为 32 位的符号在 C 中很容易:只需转换为short ,我们甚至不需要掩码: (short) text[TEXT_POS(pc)] This shortening cast will immediately return to full 32 bits when used with anything else, but this time sign extending from 16 bits.当与其他任何东西一起使用时,这种缩短转换将立即返回完整的 32 位,但这次符号从 16 位扩展。 (The sign extended, shifted immediate is relative to PC+4. Or PC+0 on QtSPIM with branch delay slots disabled.) (符号扩展、移位立即数与 PC+4 相关。或者 QtSPIM 上的 PC+0 禁用了分支延迟槽。)

    Most C implementations use a 16-bit short , but int16_t from stdint.h would be reliably 16-bit 2's complement on any C implementation where it exists.大多数 C 实现使用 16 位short ,但stdint.h中的int16_t在任何存在的 C 实现中都是可靠的 16 位 2 补码。 (And fail to build on ones that don't have an int16_t , which is what you want instead of working wrong.) (并且无法建立在没有int16_t的基础上,这是您想要的,而不是错误的工作。)


See How to Calculate Jump Target Address and Branch Target Address?请参阅如何计算跳转目标地址和分支目标地址? for how real MIPS calculates branch targets, in the full ISA with a branch delay slot.了解真正的 MIPS 如何在带有分支延迟槽的完整 ISA 中计算分支目标。 Some simulators differ, even though they don't need to.一些模拟器有所不同,即使它们不需要。 (Return address calculation needs to depend on whether there's a delay slot, but target-calculation for J-type jumps and I-type branches can always work the same.) (返回地址计算需要看是否有延迟槽,但是J型跳转和I型分支的target-calculation总是一样的。)

If you're trying to be compatible with MARS, you should double check whether it is using PC+4 or PC+8, and do that everywhere you use the PC.如果您尝试与 MARS 兼容,您应该仔细检查它使用的是 PC+4 还是 PC+8,并且在您使用 PC 的任何地方都这样做。 In the JAL , for example, you have PC+8 for the return address, but in the BNE you have PC+4 to work with the immediate.例如,在JAL中,您有 PC+8 作为返回地址,但在BNE中,您有 PC+4 来处理立即数。 If there's no branch delay slot, then your JAL emulation will skip one instruction upon function return.如果没有分支延迟槽,那么您的JAL仿真将在函数返回时跳过一条指令。

MARS & QtSpim, the other popular MIPS simulator, have differences in this area, and, differences from official MIPS documentation, so double check what the simulator is doing for each such, if you want to be compatible with it. MARS 和 QtSpim,另一个流行的 MIPS 模拟器,在这方面有所不同,并且与官方 MIPS 文档有所不同,所以如果你想与它兼容,请仔细检查模拟器为每个这样的模拟器做了什么。


We don't generally refer to MIPS machine code as bytecode, but rather as machine code.我们一般不将 MIPS 机器码称为字节码,而是将其称为机器码。 It is a fixed length 32-bit instruction set, and there's nothing "byte" about it, really — we might call it wordcode, but that's not a term.它是一个固定长度的 32 位指令集,实际上并没有关于它的“字节”——我们可以称之为字码,但这不是一个术语。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM