0
votes

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.

2
I don't think there will be a difference between Windows and Linux since SPIM uses the underlying "endiness" of the hardware.Zack
Try using MARS instead of SPIM and see if you get the same answer. (courses.missouristate.edu/KenVollmar/mars)Zack
@Zack Do you think it has something to do with my pure argorithmic considerations of this? I read somewhere that Spim doesn't bother with PC+4, which might be leading to the mistake?smitty_werbermanjensen
spim obeys PC+4 [as does mars]. But, mips has [can have] "branch delay slots". Normally, emulation for these is defaulted off for spim/mars but you may have enabled it somehow (e.g. registry entry, etc.). See my answer: stackoverflow.com/questions/36994841/… If enabled but not compensated by you, you'd experience an "off-by-4" discrepancyCraig Estey
To sign extend in C, just do word_offset = *(short *) .... To do it in mips, assuming $t5 has the whole [branch] instruction value, do: sll $t4,$t5,16--this puts the [16 bit] sign bit into bit 31, followed by sra $t4,$t4,16 [shift right arithmetic which does the sign extension when shifting]. Now, $t4 has the sign extended word offset. To get the byte offset, now do sll $t4,$t4,2Craig Estey

2 Answers

1
votes

The 16 bit immediate value is sign-extended to 32 bits, then shifted. I don't know if that would affect your program; but, that's the only potential "mistake" I noticed.

1
votes

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