Search code examples
mipsspim

MIPS Branch Addressing Algorithm and Opcode isolation from instruction binary?


I just want to check my understanding of these two concepts is correct, as I have been trying to finish a project and while everything works to my expectations, it keeps narrowly failing the test cases and introducing a random value...

Basically, the objective of the project is to write out a branch instruction to console in this form:

BranchName $S, [$t, if applicable] 0xAbsoluteAddressOfBranchTargetInstruction

Edit: Clarification: I'm writing this in MIPS. The idea is I get a memory address in $a0 given to the program by my instructor's code (I write the function). The address is for the word containing a MIPS instruction. I'm to do the following:

  1. Get instruction
  2. Isolate instruction opcode and output its name to register (ie: opcode 5, output BNE), do nothing if it isn't a branch instruction.
  3. Isolate $s, $t, and output as applicable (ie: no $t for bgez)
  4. Use offset in the branch instruction to calculate its absolute address (the address of the target instruction following branch) and output in hex. For the purposes of this calculation, the address of the branch instruction ($a0) is assumed to be $pc.

IE:

BEQ $6, $9, 0x00100008

Firstly, is my understanding of branch calculation correct?

  1. PC -> PC + 4
  2. Lower 16 bits of instruction
  3. << 2 these lower bits
  4. Add PC+4 and the left shifted lower 16 bits (only the lower 16 though).

Secondly, could somebody tell me which bits I need to isolate to know what kind of branch I'm dealing with? I think I have them (first 6 for BEQ/BNE, first 16 with $s masked out for others) but I wanted to double check.

Oh, and finally... should I expect deviation on SPIM from running it on an Intel x86 Windows system and an Intel x86 Linux system? I'm getting a stupid glitch and I cannot seem to isolate it from my hand-worked address calculations, but it only shows up when I run the test scripts my prof gave us on Linux (.sh); running directly in spim on either OS seems to work... provided my understanding of how to do the hand calculations (as listed above) is correct.


Solution

  • This is prefaced by my various comments.

    Here is a sample program that does the address calculation correctly. It does not do the branch instruction type decode, so you'll have to combine parts of this and your version together.

    Note that it uses the mars syscall 34 to print values in hex. This isn't available under spim, so you may need to output in decimal using syscall 1 or write your own hex value output function [if you haven't already]

        .data
    msg_best:   .asciiz     "correct target address: "
    msg_tgt:    .asciiz     "current target address: "
    msg_nl:     .asciiz     "\n"
    
        .text
        .globl  main
    main:
        la      $s0,inst                # pointer to branch instruction
        la      $s1,einst               # get end of instructions
        subu    $s1,$s1,$s0             # get number of bytes
        srl     $s1,$s1,2               # get number of instruction words
        la      $s2,loop                # the correct target address
    
        la      $a0,msg_best
        move    $a1,$s2
        jal     printaddr
    
    loop:
        move    $a0,$s0
        jal     showme                  # decode and print instruction
        addiu   $s0,$s0,4
        sub     $s1,$s1,1
        bnez    $s1,loop                # more to do? yes, loop
    
        li      $v0,10
        syscall
    
        # branch instructions to decode
    inst:
        bne     $s0,$s1,loop
        beq     $s0,$s1,loop
        beqz    $s1,loop
        bnez    $s1,loop
        bgtz    $s1,loop
        bgez    $s1,loop
        bltz    $s1,loop
        blez    $s1,loop
    einst:
    
    # showme -- decode and print data about instruction
    #
    # NOTE: this does _not_ decode the instruction type
    #
    # arguments:
    #   a0 -- instruction address
    #
    # registers:
    #   t5 -- raw instruction word
    #   t4 -- branch offset
    #   t3 -- absolute address of branch target
    showme:
        subu    $sp,$sp,4
        sw      $ra,0($sp)
    
        lw      $t5,0($a0)              # get inst word
        addiu   $t3,$a0,4               # get PC + 4
    
        sll     $t4,$t5,16              # shift offset left
        sra     $t4,$t4,16              # shift offset right (sign extend)
        sll     $t4,$t4,2               # get byte offset
    
        addu    $t3,$t3,$t4             # add in offset
    
        # NOTE: as a diagnostic, we could compare t3 against s2 -- it should
        # always match
    
        la      $a0,msg_tgt
        move    $a1,$t3
        jal     printaddr
    
        lw      $ra,0($sp)
        addu    $sp,$sp,4
        jr      $ra
    
    # printaddr -- print address
    #
    # arguments:
    #   a0 -- message
    #   a1 -- address value
    printaddr:
        li      $v0,4
        syscall
    
        # NOTE: only mars supports this syscall
        # to use spim, use a syscall number of 1, which outputs in decimal and
        # then hand convert
        # or write your own hex output function
        move    $a0,$a1
        li      $v0,34                  # output number in hex (mars _only_)
        syscall
    
        la      $a0,msg_nl
        li      $v0,4
        syscall
    
        jr      $ra