Search code examples
assemblymipsmips32mars-simulator

Trying to draw Bresenham's Circle, cannot get visuals MARS IDE


So I am attempting to draw a circle using Bresenham's algorithm but I can't seem to get any output on the bitmap display. Perhaps my draw is not working properly as it does not seem to be saving the colour to the target address.

This is the article I was following to help guide me through this process. It is written in c++ Bresenham's Circle

I had troubles finding a reliable source explaining how to modify the display, I based my display memory writes on this article. drawing a 8x8 unit in BitMap Display Mips

I have comments explaining the process and use the $a registers as the designated parameter registers

.text

main:
    li   $a0, 50      # x
    li   $a1, 50      # y
    li   $a2, 30      # r2
    
    jal circleBres 
    
    li   $v0, 10
    syscall


# $a0: passed as xc
# $a1: passed as yc
# $a2: passed as the radius - r
circleBres:
    li $s0, 0x00FF0000  #loads the color red into the register $a2
    
    add  $sp, $sp, 4
    sw   $ra, 0($sp)
    
    move $s1, $a0      # this is xc
    move $s2, $a1      # this is yc 
    move $s3, $a2      # this is r - this is y
    li   $s4, 0
    li   $a0, 3
    li   $a1, 2
    sub  $s5, $a0, $a1 # d = 3 - 2
    mul  $s5, $s5, $s3 # d = d * r
    
    move $a0, $s1      # Set parameter registers
    move $a1, $s2      # to proper values and call
    move $a2, $s4      # the first draw
    move $a3, $s3
    jal drawCircle  
circleBresLoop:
    bge  $s3, $s4, end # Break if y >= x
    add  $s4, $s4, 1   # x++
    blt  $s5, 0, else  # if (d > 0) otherwise else
    sub  $s3, $s3, 1   # y--
    # d = d + 4 * (x - y) + 10
    sub  $t0, $s4, $s3 # $t0 = (x - y)
    mul  $t0, $t0, 4   # $t0 = $t0 * 4
    add  $t0, $t0, $s5 # $t0 = $t0 + d
    add  $t0, $t0, 10  # $t0 = $t0 + 10
    j skip
else:
    # d = d + 4 * x + 6
    mul  $t0, $s4, 4   # $t0 = x * 4
    add  $t0, $t0, $s5 # $t0 = $t0 + d
    add  $t0, $t0, 6
skip:
    move $a0, $s1      # Set parameter registers
    move $a1, $s2      # to proper values and call
    move $a2, $s4      # draw
    move $a3, $s3
    jal drawCircle  
    
    j circleBresLoop
    
end:
    lw   $ra, 0($sp)
    add  $sp, $sp, 4    

    jr $ra

# $a0: passed as xc
# $a1: passed as yc
# $a2: passed as x
# $a3: passed as y
drawCircle:
    add  $sp, $sp, -4
    sw   $ra, 0($sp)
    
    #1
    add  $s1, $a0, $a2  # xc + x
    add  $s2, $a1, $a3  # yc + y
    move $a0, $s1
    move $a1, $s2
    jal   drawPixel    
    #2
    sub  $s1, $a0, $a2  # xc - x
    add  $s2, $a1, $a3  # yc + y
    move $a0, $s1
    move $a1, $s2
    jal   drawPixel
    #3
    add  $s1, $a0, $a2  # xc + x
    sub  $s2, $a1, $a3  # yc - y
    move $a0, $s1
    move $a1, $s2
    jal   drawPixel
    #4
    sub  $s1, $a0, $a2  # xc - x
    sub  $s2, $a1, $a3  # yc - y
    move $a0, $s1
    move $a1, $s2
    jal   drawPixel
    #5
    add  $s1, $a0, $a3  # xc + y
    add  $s2, $a1, $a2  # xc + x
    move $a0, $s1
    move $a1, $s2
    jal   drawPixel
    #6
    sub  $s1, $a0, $a3  # xc - y
    add  $s2, $a1, $a2  # yc + x
    move $a0, $s1
    move $a1, $s2
    jal   drawPixel
    #7
    add  $s1, $a0, $a3  # xc + y
    sub  $s2, $a1, $a2  # yc - x
    move $a0, $s1
    move $a1, $s2
    jal   drawPixel
    #8
    sub  $s1, $a0, $a3  # xc - y
    sub  $s2, $a1, $a2  # yc - x
    move $a0, $s1
    move $a1, $s2
    jal   drawPixel
    
    lw   $ra, 0($sp)
    add  $sp, $sp, 4
    
    jr   $ra    
        
                
    
# $a0: passed as x
# $a1: passed as y
drawPixel:

    move $t3, 0x10000100 #t3 = first Pixel of the screen
    
    sll  $t0, $a1, 9    # y = y * 512
    addu $t0, $a0, $a1  # $t0 = x + y
    sll  $t0, $t0, 2    # $t0 = xy * 4
    addu $t0, $t3, $t0  # adds xy to first pixel ($t3)
    sw   $s0, 0($t0)    # Put RED ($s0) in $t0 memory spot
    
    jr   $ra

I also tried this as just a proof of concept to see that I could get a visual but I do not entirely understand the positioning or why

.text
    li   $a2, 0x00FF0000     #loads the color red into the register $a2

    li   $s3, 50             #y1 = y position of the tail
    li   $s0, 50             #x2 = x position of the head

DrawPixel:

    li   $t3, 0x10000100     #t3 = first Pixel of the screen

    sll   $t0, $s3, 9        #y = y * 512
    addu  $t0, $t0, $s0      # (xy) t0 = x + y
    sll   $t0, $t0, 2        # (xy) t0 = xy * 4
    addu  $t0, $t3, $t0      # adds xy to the first pixel ( t3 )
    sw    $a2, ($t0)         # put the color red ($a2) in $t0

Solution

  • The first add to $sp has the wrong sign.

    Did you notice in single stepping, perhaps, that after calling of drawCircle from circleBres, your local variables in s registers are clobbered?

    You are taking care to preserve the $ra register for later use, but not the s registers.

    The s registers aren't magic — if you're going to use them their original values must be preserved.  Here, drawCircle repurposes some s registers, while they are simultaneously in use by circleBres, so that is a conflict, and last to modify wins.  The calling convention says that those need to be preserved, which is typically done by saving their values on entry in prologue and restoring those original values upon exit it epilogue.  By the calling convention, drawCircle should do the same, but main doesn't place any values into the s registers, so it doesn't matter here.


    Whether you realize it or not, you're confronted with the dilemma of how to share the limited number of CPU registers across multiple functions.  There are two ways to approach this:

    1. Use completely different registers in different functions — this may be done as an optimization in limited circumstances, but you can imagine that you'll run out of registers if you have more than 4-6 functions or so.

    2. Use a calling convention: most such conventions partition the registers into two sets: call clobbered, and call preserved.  Registers in the former set can be used with impunity with the caveat that they are not expected to survive a function call with the same values as before the call.  Registers in the latter set can be used, but only if they are restored to their original values before returning to the caller.  These two sets serve two common usage patterns (temporaries/scratch, and variables needed across function calls).

    The call clobbered set is useful for functions that need to do some work internally without involving function calling.  These registers are free to be used at will and by their nature require no overhead of save/restore.

    The call preserved set is useful for functions that need to do some work surrounding function calling — for example, if you have some variables you want in registers and those variables are established (set) before a function call and used after.  These registers will survive a function call — though only if all the functions obey the rules, which is that if they also want to use these registers, they must restore the original values prior to returning to the caller.

    I already stated this: The calling convention says that call preserved registers (here, the s) need to be preserved, which is typically done by saving their values on entry in prologue and restoring those original values upon exit it epilogue, if the are used by a function.

    So, free free to use the s registers, just follow the rules of preservation.  Such preservation is very similar to how you are already handling the $ra register, so expand on that.  (There are also sample programs you can find that will further demonstrate how the s registers are properly used.)

    The t registers don't necessarily survive a function call as is also the case with a registers.

    To repeat: there is no magic — the only reason s registers survive a function call is that functions obey the usage rules.  The reason that the t and the a registers don't necessarily survive a function call is also by agreement (the calling convention).


    With the above in mind, let's look at the following snippet:

    #1
    add  $s1, $a0, $a2  # xc + x
    add  $s2, $a1, $a3  # yc + y
    move $a0, $s1    <-------- what do you think happens to xc here?? (hint: $a0 holding xc is overwritten, not by drawPixel but by this very instruction
    move $a1, $s2
    jal   drawPixel    
    #2
    sub  $s1, $a0, $a2  # xc - x  <--- what do you think is in $a0 here, after you've already clobbered it above? (hint: nothing useful)
    

    You need to consider values held in variables and where those variables are stored in assembly/machine code, rather than just individual instructions.  There is no magic: if you overwrite a register holding an important value, then it's gone.  The processor is going to blindly do what you say; it doesn't know your intent.

    Since you need xc and x both, after the call but they are in a registers, you'll need to move them somewhere that will survive a function call — that could be s registers, or memory.

    There's no point in doing

    add  $s1, $a0, $a2  # xc + x
    ...
    move $a0, $s1   <--- should target $a0 directly, above, instead of putting into $s1 then moving here to $a0
    ...
    jal   drawPixel
    ...
    sub  $s1, $a0, $a2 <--- overwriting $s1 proves that we didn't need xc+x to be preserved from above
    

    You're using s registers here as temporaries (which is inappropriate) and also expecting the a registers to survive the first call to drawPixel (which as you can see even the act of passing one parameter destroys $a0).


    Oh, and one more thing: the bit mapped display is normally located at 0x10010000 (not 0x10000100).