how do i connect dots in midpoint algorithm in mip

2019-01-29 12:22发布

问题:

So I am trying to draw a circle but only 4 dots appear on the screen. How do I generate more dots and connect them? I have included a picture so you can see how it is displayed in mars.

midpoint circle algorithm

.data
#midpoint circle algorithm variables

radius: .word 10
err: .word -10
#yvalue = radius
colour: .word 0x00FFFFFF

bmp: .space 0x80000
height: .word 64
width: .word 64
base: .word 0x10040000


.text

lw $a0, radius # x
li $a1, 0 # y

drawn:
bge $a0, $a1, loading

loading:

lw $t1, err
jal plot8points

add $t1, $t1, $a1 #err += y
addi $a1, $a1, 1 #y++
add $t1, $t1, $a1 #err + = y

bltz $a0, drawn


sub $t1, $t1, $a0 # err -= x
addi $a0, $a0, -1 # x--
sub $t1, $t1, $a0 # err-= x


plot8points:


lw $t3, radius # xcenter
li $t4, 0 # ycenter

move $t7, $a0 # x
move $t8, $a1 # y


jal plot4points 

blt $a0, $a1, end_loading

jal plot4points

jal plot4morepoints 

#jal exit


end_loading:

jr $ra


plot4points:


add $a0, $t7, $t3
add $a1, $t8, $t4
jal setpixel

sub $a0, $t3, $t7
add $a1, $t4, $t8
jal setpixel

add $a0, $t3, $t7
sub $a1, $t4, $t8
jal setpixel

sub $a0, $t3, $7
sub $a1, $t4, $t8
jal setpixel


plot4morepoints:

add $a0, $t3, $t8
add $a1, $t4, $t7
jal setpixel

sub $a0, $t3, $t8
add $a1, $t4, $t7
jal setpixel

add $a0, $t3, $t8
sub $a1, $t4, $t7
jal setpixel

sub $a0, $t3, $t8
sub $a1, $t4, $t7
jal setpixel


setpixel:
lw $t0, colour
lw $s4, width
lw $s2, base
mul $t6, $a1, $s4
add $t6, $t6, $a0
sll $t6, $t6, 2
add $t6, $t6, $s2
sw $t0, ($t6)
jr $ra



#exit:
li $v0, 10
syscall

回答1:

Your pixel addressing appears to be wrong.

width is [your variable for] the display width which is 64. But, in your example, the bitmap has a width of 512 and height of 256 [which are the default values in mars].

So, you either need to change the display geometry in mars or set your width variable to match. Same thing for height.

Note: mars does not retain the values you set, so if you want to use a non-default geometry, you'll have to set it every time. So, maybe the easy way is to use the mars default values.


UPDATE:

I changed the values to match the default but still errors. I'm thinking maybe I need to change something in the setpixel code n also add a counter somewhere

I'm afraid there were a number of bugs. Mostly, your functions did not save/restore $ra on the stack and do a jr $ra at the end.

Also, it seemed [to me] that your octant reflection code was much more complex [and therefore error prone] than it needed to be.

I created two versions. The first is your original code with annotations about [some of] the bugs. The second one is a full rework that works.


Here's the annotated version [please pardon the gratuitous style cleanup]:

    .data
    # midpoint circle algorithm variables

radius:     .word       10
err:        .word       -10
colour:     .word       0x00FFFFFF
    # yvalue = radius

bmp:        .space      0x80000
dpy_width:  .word       512
dpy_height: .word       256
dpy_base:   .word       0x10040000

    .text

    lw      $a0,radius              # x
    li      $a1,0                   # y

# BUG: this bge has no meaning since, either way, it goes to "loading"
# probably should be "end_loading"
drawn:
    bge     $a0,$a1,loading

# BUG: this falls through into the plot8points function
loading:
    lw      $t1,err
    jal     plot8points

    add     $t1,$t1,$a1             # err += y
    addi    $a1,$a1,1               # y++
    add     $t1,$t1,$a1             # err + = y

    bltz    $a0,drawn

    sub     $t1,$t1,$a0             # err -= x
    addi    $a0,$a0,-1              # x--
    sub     $t1,$t1,$a0             # err-= x

# BUG: this is a function but has no return and does _not_ save $ra
plot8points:
    lw      $t3,radius              # xcenter
    li      $t4,0                   # ycenter

    move    $t7,$a0                 # x
    move    $t8,$a1                 # y

    jal     plot4points
# BUG: we're in a function but this jumps to a label outside the function
    blt     $a0,$a1,end_loading

    jal     plot4points
    jal     plot4morepoints

    # jal exit

end_loading:
    jr      $ra

# BUG: this is a function but has no return and does _not_ save $ra
plot4points:
    add     $a0,$t7,$t3
    add     $a1,$t8,$t4
    jal     setpixel

    sub     $a0,$t3,$t7
    add     $a1,$t4,$t8
    jal     setpixel

    add     $a0,$t3,$t7
    sub     $a1,$t4,$t8
    jal     setpixel

    sub     $a0,$t3,$7
    sub     $a1,$t4,$t8
    jal     setpixel

# BUG: this is a function but has no return and does _not_ save $ra
plot4morepoints:
    add     $a0,$t3,$t8
    add     $a1,$t4,$t7
    jal     setpixel

    sub     $a0,$t3,$t8
    add     $a1,$t4,$t7
    jal     setpixel

    add     $a0,$t3,$t8
    sub     $a1,$t4,$t7
    jal     setpixel

    sub     $a0,$t3,$t8
    sub     $a1,$t4,$t7
    jal     setpixel

# setpixel -- draw pixel on display
#
# arguments:
#   a0 -- X coord
#   a1 -- Y coord
setpixel:
    lw      $t0,colour              # color
    lw      $s4,dpy_width           # display width
    lw      $s2,dpy_base            # display base address

    mul     $t6,$a1,$s4             # get y * width
    add     $t6,$t6,$a0             # get (y * width) + x
    sll     $t6,$t6,2               # convert to offset
    add     $t6,$t6,$s2             # add in base address

    sw      $t0,($t6)               # store pixel
    jr      $ra

    # exit:
    li      $v0,10
    syscall

Here's the cleaned up, refactored, working version.

When I did the rework, I tried using the algorithm from wikipedia's page for the circle algorithm, but either their version is broken, or I broke it. It doesn't produce a circle, but a diamond/hexagon pattern. So, I left it in as an option.

So, I added John Kennedy's [Santa Monica College] version [from the OSU website]. It works.

I also added some options to autosize to the display and automatically calculate the centroid. Check the display values as it assumes 512x256 and the "static" display base address.

I also added some options to allow concentric circles to be drawn, just for amusement.

# breshenham circle algorithm

    .data
radius:     .word       10
center_x:   .word       0
center_y:   .word       0
dpy_color:  .word       0x00FFFFFF
    # midpoint circle algorithm variables

bmp:        .space      0x80000
dpy_width:  .word       512
dpy_height: .word       256
dpy_base:   .word       0x10010000
    .eqv    dpy_margin      8

ask_diamond:    .word   0
ask_radmin: .word       0
ask_radinc: .word       16

msg_nl:     .asciiz     "\n"
msg_comma:  .asciiz     ","

    .text

    .globl  main

main:
    .eqv    dflg            $fp
    li      dflg,0                  # clear debug flag

    # prompt user for ask_diamond value
    la      $a0,_S_000              # prompt user
    li      $v0,4                   # print string
    syscall
    li      $v0,5
    syscall
    sw      $v0,ask_diamond

    # prompt user for ask_radmin value
    la      $a0,_S_001              # prompt user
    li      $v0,4                   # print string
    syscall
    li      $v0,5
    syscall
    sw      $v0,ask_radmin

    lw      $t0,ask_radmin
    beqz    $t0,main_skipinc

    # prompt user for ask_radinc value
    la      $a0,_S_002              # prompt user
    li      $v0,4                   # print string
    syscall
    li      $v0,5
    syscall
    sw      $v0,ask_radinc

main_skipinc:
    lw      $t0,ask_radmin

    # compute circle center from display geometry
    lw      $s6,dpy_width
    srl     $s6,$s6,1
    sw      $s6,center_x

    lw      $s5,dpy_height
    srl     $s5,$s5,1
    sw      $s5,center_y

    # set radius to min((width / 2) - 16,(height / 2) - 16)
    move    $s0,$s6
    blt     $s6,$s5,main_gotradius
    move    $s0,$s5

main_gotradius:
    subi    $s0,$s0,dpy_margin      # give us some margin
    sw      $s0,radius

main_loop:
    # output circle
    jal     kdraw
    jal     radbump

    # output diamond/hexagon
    lw      $t0,ask_diamond         # is it enabled?
    beqz    $t0,main_next           # if no, skip
    jal     wdraw
    jal     radbump

main_next:
    bnez    $v0,main_loop           # done with concentric circles? if no, loop

main_done:
    li      $v0,10
    syscall

# wdraw -- draw circle (wikipedia)
#
# NOTES:
#   (1) this is wikipedia's algorithm for a circle, but it is more like a
#       diamond or polygon
#   (2) https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
#   (2) either it's "broken" or _I_ broke it
#
# registers:
#   s0 -- x
#   s1 -- y
#   s2 -- decision/error term (err)
#
# * void
# * DrawCircle(int x0,int y0,int radius)
# * {
# *     int x = radius;
# *     int y = 0;
# *
# *     // Decision criterion divided by 2 evaluated at x=r, y=0
# *     int decisionOver2 = 1 - x;
# *
# *     while (y <= x) {
# *         DrawPixel(x + x0,y + y0);       // Octant 1
# *         DrawPixel(y + x0,x + y0);       // Octant 2
# *         DrawPixel(-x + x0,y + y0);      // Octant 4
# *         DrawPixel(-y + x0,x + y0);      // Octant 3
# *         DrawPixel(-x + x0,-y + y0);     // Octant 5
# *         DrawPixel(-y + x0,-x + y0);     // Octant 6
# *         DrawPixel(x + x0,-y + y0);      // Octant 7
# *         DrawPixel(y + x0,-x + y0);      // Octant 8
# *
# *         y++;
# *
# *         // Change in decision criterion for y -> y+1
# *         if (decisionOver2 <= 0) {
# *             decisionOver2 += 2 * y + 1;
# *         }
# *
# *         // Change for y -> y+1, x -> x-1
# *         else {
# *             x--;
# *             decisionOver2 += 2 * (y - x) + 1;
# *         }
# *     }
# * }
wdraw:
    subi    $sp,$sp,4
    sw      $ra,0($sp)

    lw      $s0,radius              # x = radius
    li      $s1,0                   # y = 0

    # get initial decision (err = 1 - x)
    li      $s2,1                   # err = 1
    sub     $s2,$s2,$s0             # err = 1 - x

wdraw_loop:
    bgt     $s1,$s0,wdraw_done      # y <= x? if no, fly (we're done)

    # draw pixels in all 8 octants
    jal     draw8

    addi    $s1,$s1,1               # y += 1

    bgtz    $s2,wdraw_case2         # err <= 0? if no, fly

# change in decision criterion for y -> y+1
#   err += (2 * y) + 1
wdraw_case1:
    sll     $t0,$s2,1               # get 2 * y
    addu    $s2,$s2,$t0             # err += 2 * y (NOTE: this can overflow)
    add     $s2,$s2,1               # err += 1
    j       wdraw_loop

# change for y -> y+1, x -> x-1
#   x -= 1
#   err += (2 * (y - x)) + 1
wdraw_case2:
    subi    $s0,$s0,1               # x -= 1
    sub     $t0,$s1,$s0             # get y - x
    sll     $t0,$t0,1               # get 2 * (y - x)
    addi    $t0,$t0,1               # get 2 * (y - x) + 1
    add     $s2,$s2,$t0             # add it to err
    j       wdraw_loop

wdraw_done:
    lw      $ra,0($sp)
    addi    $sp,$sp,4
    jr      $ra

# kdraw -- draw circle (john kennedy)
#
# NOTES:
# (1) this is John Kennedy's algorithm from:
#     http://web.engr.oregonstate.edu/~sllu/bcircle.pdf
#
# registers:
#   s0 -- x
#   s1 -- y
#   s2 -- raderr
#   s3 -- xchg
#   s4 -- ychg
#
# * void
# * PlotCircle(int CX, int CY, int r)
# * {
# *     int x;
# *     int y;
# *     int xchg;
# *     int ychg;
# *     int raderr;
# *
# *     x = r;
# *     y = 0;
# *
# *     xchg = 1 - (2 * r);
# *     ychg = 1;
# *
# *     raderr = 0;
# *
# *     while (x >= y) {
# *         draw8(x,y);
# *         y += 1;
# *
# *         raderr += ychg;
# *         ychg += 2;
# *
# *         if (((2 * raderr) + xchg) > 0) {
# *             x -= 1;
# *             raderr += xchg;
# *             xchg += 2;
# *         }
# *     }
# * }
kdraw:
    subi    $sp,$sp,4
    sw      $ra,0($sp)

    lw      $s0,radius              # x = radius
    li      $s1,0                   # y = 0

    # initialize: xchg = 1 - (2 * r)
    li      $s3,1                   # xchg = 1
    sll     $t0,$s0,1               # get 2 * r
    sub     $s3,$s3,$t0             # xchg -= (2 * r)

    li      $s4,1                   # ychg = 1
    li      $s2,0                   # raderr = 0

kdraw_loop:
    blt     $s0,$s1,kdraw_done      # x >= y? if no, fly (we're done)

    # draw pixels in all 8 octants
    jal     draw8

    addi    $s1,$s1,1               # y += 1
    add     $s2,$s2,$s4             # raderr += ychg
    addi    $s4,$s4,2               # ychg += 2

    sll     $t0,$s2,1               # get 2 * raderr
    add     $t0,$t0,$s3             # get (2 * raderr) + xchg
    blez    $s2,kdraw_loop          # >0? if no, loop

    subi    $s0,$s0,1               # x -= 1
    add     $s2,$s2,$s3             # raderr += xchg
    addi    $s3,$s3,2               # xchg += 2
    j       kdraw_loop

kdraw_done:
    lw      $ra,0($sp)
    addi    $sp,$sp,4
    jr      $ra

# draw8 -- draw single point in all 8 octants
#
# arguments:
#   s0 -- X coord
#   s1 -- Y coord
#
# registers:
#   t8 -- center_x
#   t9 -- center_y
draw8:
    subi    $sp,$sp,4
    sw      $ra,0($sp)

    #+drawctr $t8,$t9
    lw      $t8,center_x            #+
    lw      $t9,center_y            #+
    #+

    # draw [+x,+y]
    add     $a0,$t8,$s0
    add     $a1,$t9,$s1
    jal     setpixel

    # draw [+y,+x]
    add     $a0,$t8,$s1
    add     $a1,$t9,$s0
    jal     setpixel

    # draw [-x,+y]
    sub     $a0,$t8,$s0
    add     $a1,$t9,$s1
    jal     setpixel

    # draw [-y,+x]
    add     $a0,$t8,$s1
    sub     $a1,$t9,$s0
    jal     setpixel

    # draw [-x,-y]
    sub     $a0,$t8,$s0
    sub     $a1,$t9,$s1
    jal     setpixel

    # draw [-y,-x]
    sub     $a0,$t8,$s1
    sub     $a1,$t9,$s0
    jal     setpixel

    # draw [+x,-y]
    add     $a0,$t8,$s0
    sub     $a1,$t9,$s1
    jal     setpixel

    # draw [+y,-x]
    sub     $a0,$t8,$s1
    add     $a1,$t9,$s0
    jal     setpixel

    lw      $ra,0($sp)
    addi    $sp,$sp,4
    jr      $ra

# setpixel -- draw pixel on display
#
# arguments:
#   a0 -- X coord
#   a1 -- Y coord
#
# clobbers:
#   v0 -- bitmap offset/index
#   v1 -- bitmap address
# trace:
#   v0,a0
setpixel:
    bnez    dflg,setpixel_show      # debug output? if yes, fly

setpixel_go:
    lw      $v0,dpy_width           # off = display width

    mul     $v0,$a1,$v0             # off = y * width
    add     $v0,$v0,$a0             # off += x
    sll     $v0,$v0,2               # convert to offset

    lw      $v1,dpy_base            # ptr = display base address
    add     $v1,$v1,$v0             # ptr += off

    lw      $v0,dpy_color           # color
    sw      $v0,($v1)               # store pixel
    jr      $ra

setpixel_show:
    move    $a2,$a0
    move    $a3,$a1

    # print x
    li      $v0,1
    move    $a0,$a2
    syscall

    # print comma
    li      $v0,4
    la      $a0,msg_comma
    syscall

    # print y
    li      $v0,1
    move    $a0,$a3
    syscall

    # print newline
    li      $v0,4
    la      $a0,msg_nl
    syscall

    move    $a0,$a2
    move    $a1,$a3
    j       setpixel_go

# radbump -- bump down radius
#
# RETURNS:
#   v0 -- 1=more to do, 0=done
#
# registers:
#   t0 -- radius value
radbump:
    lw      $t0,radius
    lw      $t1,ask_radinc
    sub     $t0,$t0,$t1

    lw      $v0,ask_radmin          # do multiple rings?
    beqz    $v0,radbump_store       # if no, fly

    slt     $v0,$v0,$t0             # radius < ask_radmin?

radbump_store:
    beqz    $t0,radbump_safe
    sw      $t0,radius

radbump_safe:
    jr      $ra

    #+dfnc
    #+

    .data
_S_000:     .asciiz     "output diamond pattern? "
_S_001:     .asciiz     "minimum radius (0=single) > "
_S_002:     .asciiz     "radius decrement > "
_S_003:     .asciiz     "dpy_width"
_S_004:     .asciiz     "dpy_height"
_S_005:     .asciiz     "radius"

I recently did a MIPS answer for struct/linked list. I also added a lot of suggestions about how to write MIPS code well. It may be of some help to you in understanding what I did here. See: MIPS linked list



回答2:

Note: This answer is a continuation of my previous one, which would not have fit, for space reasons, into the first answer.

After experimentation, the only viable display mapping is the "heap" mapping with the geometry shown below. It's a limitation of mars.

Here's an updated/improved version that does color:

# mipscirc/fix2.asm -- breshenham circle algorithm

# this _must_ be first
    .data
    .eqv    dpy_max         4194304 # maximum display area
dpy_width:  .word       512
dpy_height: .word       256
dpy_base:   .word       0x10040000      # heap
dpy_now:    .word       0               # current base [for debug]
dpy_blit:   .word       0               # 1=do offscreen blit (currently broken)
    .eqv    dpy_stride      9       # left shift
    .eqv    dpy_size        524288  # display size (bytes)

    .eqv    dpy_margin      8       # radius margin

    .data

sdata:
radius:     .word       10
center_x:   .word       0
center_y:   .word       0
    # midpoint circle algorithm variables

color_octant:   .space  32

dpy_radius: .word       10
dpy_color:  .word       0x00FFFFFF

ask_diamond:    .word   0               # 1=do diamond pattern
ask_radmin: .word       0               # minimum radius (0=single circle)
ask_radinc: .word       16              # radius decrement amount
ask_colorinc:   .word   0               # color increment (0=single color)

bitmap:     .space      dpy_size

    .text

    .globl  main

main:
    # this _must_ be first
    li      $a0,dpy_size            # maximum size
    li      $v0,9                   # sbrk
    syscall
    lw      $v1,dpy_base            # get what we expect
    beq     $v0,$v1,dpyinit_done
    la      $a0,_S_000
    li      $v0,4                   # puts
    syscall

dpyinit_done:

    .eqv    mrzhow          $fp
    li      $v1,0                   # clear mask

    .eqv    MRZDBGPRT       0x00000001  # output numbers
    .eqv    _MRZDBGPRT      0       # output numbers

    .eqv    MRZDPYSHOW      0x00000002  # show display coordinates
    .eqv    _MRZDPYSHOW     1       # show display coordinates

    .eqv    MRZDPYCHK       0x00000004  # check display area bounds
    .eqv    _MRZDPYCHK      2       # check display area bounds

    move    mrzhow,$v1              # set final register value

    # prompt user for ask_diamond value
    la      $a0,_S_001              # prompt user
    li      $a1,0                   # set default
    jal     qask
    sw      $v0,ask_diamond         # place to store

    # prompt user for ask_radmin value
    la      $a0,_S_002              # prompt user
    li      $a1,4                   # set default
    jal     qask
    sw      $v0,ask_radmin          # place to store

    lw      $t0,ask_radmin
    beqz    $t0,main_skipinc

    # prompt user for ask_radinc value
    la      $a0,_S_003              # prompt user
    li      $a1,6                   # set default
    jal     qask
    sw      $v0,ask_radinc          # place to store

main_skipinc:

    # prompt user for ask_colorinc value
    la      $a0,_S_004              # prompt user
    li      $a1,-2                  # set default
    jal     qask
    sw      $v0,ask_colorinc        # place to store

    lw      $t0,ask_radmin

    # compute circle center from display geometry
    lw      $s6,dpy_width
    srl     $s6,$s6,1
    sw      $s6,center_x

    lw      $s5,dpy_height
    srl     $s5,$s5,1
    sw      $s5,center_y

    # set radius to min((width / 2) - 16,(height / 2) - 16)
    move    $s0,$s6
    blt     $s6,$s5,main_gotradius
    move    $s0,$s5

main_gotradius:
    subi    $s0,$s0,dpy_margin      # give us some margin
    sw      $s0,dpy_radius

main_loop:
    jal     colorgo

    jal     colorinc
    bnez    $v0,main_loop

main_done:
    li      $v0,10
    syscall

# colorgo -- draw all items with single color
colorgo:
    subi    $sp,$sp,4
    sw      $ra,0($sp)

    jal     dpybase

# reset the radius
colorgo_noblit:
    lw      $t0,dpy_radius
    sw      $t0,radius

colorgo_loop:
    # output circle
    jal     kdraw
    jal     radbump

    # output diamond/hexagon
    lw      $t0,ask_diamond         # is it enabled?
    beqz    $t0,colorgo_next        # if no, skip
    jal     wdraw
    jal     radbump

colorgo_next:
    bnez    $v0,colorgo_loop        # done with concentric circles? if no, loop

    jal     dpyblit                 # blit to screen (if mode applicable)

colorgo_done:
    lw      $ra,0($sp)
    addi    $sp,$sp,4
    jr      $ra

# colorinc -- increment to next color
#
# RETURNS:
#   v0 -- 1=more to do, 0=done
#
# registers:
#   t0 -- color value
colorinc:
    subi    $sp,$sp,4
    sw      $ra,0($sp)

    lw      $v0,ask_colorinc        # get option that controls increment
    beqz    $v0,colorinc_done       # do increment? if no, fly

    bltz    $v0,colorinc_rand       # random increment? if yes, fly

    lw      $t0,dpy_color           # get current color
    addi    $t0,$t0,1               # increment it
    andi    $t0,$t0,0x00FFFFFF      # keep it clean
    sw      $t0,dpy_color           # save it back

    li      $v0,1
    j       colorinc_done

colorinc_rand:
    jal     colorany                # get random colors
    lw      $t0,color_octant        # the first one
    sw      $t0,dpy_color           # save to the single color

    # without a delay, the colors blast by too fast to be enjoyed
    ###li       $t0,1000000
    li      $t0,500000

colorinc_loop:
    subi    $t0,$t0,1
    bgtz    $t0,colorinc_loop

    li      $v0,1

colorinc_done:
    lw      $ra,0($sp)
    addi    $sp,$sp,4
    jr      $ra

# colorany -- get random colors
colorany:
    lw      $t0,ask_colorinc        # get option
    li      $t1,-3                  # get value for the "spin" option
    bne     $t0,$t1,colorany_noslide    # slide mode? if no, fly

    li      $t0,7
    la      $t1,color_octant
    subi    $t1,$t1,4

# slide all colors to make room for single new one
colorany_slide:
    addi    $t1,$t1,4               # advance
    lw      $a0,4($t1)              # get ptr[1]
    sw      $a0,0($t1)              # set ptr[0]
    subi    $t0,$t0,1               # more to do?
    bnez    $t0,colorany_slide      # if yes, loop

    # set new random first element
    li      $v0,41                  # randint
    syscall
    andi    $a0,$a0,0x00FFFFFF      # clean the value
    sw      $a0,4($t1)
    j       colorany_done

colorany_noslide:
    li      $t0,8
    la      $t1,color_octant

colorany_loop:
    li      $v0,41                  # randint
    syscall
    andi    $a0,$a0,0x00FFFFFF      # clean the value

    sw      $a0,0($t1)              # store it
    subi    $t0,$t0,1               # decrement remaining count
    addi    $t1,$t1,4               # advance pointer
    bnez    $t0,colorany_loop       # done? if no, loop

colorany_done:
    jr      $ra

# radbump -- bump down radius
#
# RETURNS:
#   v0 -- 1=more to do, 0=done
#
# registers:
#   t0 -- radius value
radbump:
    lw      $t0,radius
    lw      $t1,ask_radinc
    sub     $t0,$t0,$t1

    lw      $v0,ask_radmin          # do multiple rings?
    beqz    $v0,radbump_store       # if no, fly

    slt     $v0,$v0,$t0             # radius < ask_radmin?

radbump_store:
    beqz    $t0,radbump_safe
    sw      $t0,radius

radbump_safe:
    jr      $ra

# wdraw -- draw circle (wikipedia)
#
# NOTES:
#   (1) this is wikipedia's algorithm for a circle, but it is more like a
#       diamond or polygon
#   (2) https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
#   (2) either it's "broken" or _I_ broke it
#
# registers:
#   s0 -- x
#   s1 -- y
#   s2 -- decision/error term (err)
#
# * void
# * DrawCircle(int x0,int y0,int radius)
# * {
# *     int x = radius;
# *     int y = 0;
# *
# *     // Decision criterion divided by 2 evaluated at x=r, y=0
# *     int decisionOver2 = 1 - x;
# *
# *     while (y <= x) {
# *         DrawPixel(x + x0,y + y0);       // Octant 1
# *         DrawPixel(y + x0,x + y0);       // Octant 2
# *         DrawPixel(-x + x0,y + y0);      // Octant 4
# *         DrawPixel(-y + x0,x + y0);      // Octant 3
# *         DrawPixel(-x + x0,-y + y0);     // Octant 5
# *         DrawPixel(-y + x0,-x + y0);     // Octant 6
# *         DrawPixel(x + x0,-y + y0);      // Octant 7
# *         DrawPixel(y + x0,-x + y0);      // Octant 8
# *
# *         y++;
# *
# *         // Change in decision criterion for y -> y+1
# *         if (decisionOver2 <= 0) {
# *             decisionOver2 += 2 * y + 1;
# *         }
# *
# *         // Change for y -> y+1, x -> x-1
# *         else {
# *             x--;
# *             decisionOver2 += 2 * (y - x) + 1;
# *         }
# *     }
# * }
wdraw:
    subi    $sp,$sp,4
    sw      $ra,0($sp)

    lw      $s0,radius              # x = radius
    li      $s1,0                   # y = 0

    # get initial decision (err = 1 - x)
    li      $s2,1                   # err = 1
    sub     $s2,$s2,$s0             # err = 1 - x

wdraw_loop:
    bgt     $s1,$s0,wdraw_done      # y <= x? if no, fly (we're done)

    # draw pixels in all 8 octants
    jal     draw8

    addi    $s1,$s1,1               # y += 1

    bgtz    $s2,wdraw_case2         # err <= 0? if no, fly

# change in decision criterion for y -> y+1
#   err += (2 * y) + 1
wdraw_case1:
    sll     $t0,$s2,1               # get 2 * y
    addu    $s2,$s2,$t0             # err += 2 * y (NOTE: this can overflow)
    add     $s2,$s2,1               # err += 1
    j       wdraw_loop

# change for y -> y+1, x -> x-1
#   x -= 1
#   err += (2 * (y - x)) + 1
wdraw_case2:
    subi    $s0,$s0,1               # x -= 1
    sub     $t0,$s1,$s0             # get y - x
    sll     $t0,$t0,1               # get 2 * (y - x)
    addi    $t0,$t0,1               # get 2 * (y - x) + 1
    add     $s2,$s2,$t0             # add it to err
    j       wdraw_loop

wdraw_done:
    lw      $ra,0($sp)
    addi    $sp,$sp,4
    jr      $ra

# kdraw -- draw circle (john kennedy)
#
# NOTES:
# (1) this is John Kennedy's algorithm from:
#     http://web.engr.oregonstate.edu/~sllu/bcircle.pdf
#
# registers:
#   s0 -- x
#   s1 -- y
#   s2 -- raderr
#   s3 -- xchg
#   s4 -- ychg
#
# * void
# * PlotCircle(int CX, int CY, int r)
# * {
# *     int x;
# *     int y;
# *     int xchg;
# *     int ychg;
# *     int raderr;
# *
# *     x = r;
# *     y = 0;
# *
# *     xchg = 1 - (2 * r);
# *     ychg = 1;
# *
# *     raderr = 0;
# *
# *     while (x >= y) {
# *         draw8(x,y);
# *         y += 1;
# *
# *         raderr += ychg;
# *         ychg += 2;
# *
# *         if (((2 * raderr) + xchg) > 0) {
# *             x -= 1;
# *             raderr += xchg;
# *             xchg += 2;
# *         }
# *     }
# * }
kdraw:
    subi    $sp,$sp,4
    sw      $ra,0($sp)

    lw      $s0,radius              # x = radius
    li      $s1,0                   # y = 0

    # initialize: xchg = 1 - (2 * r)
    li      $s3,1                   # xchg = 1
    sll     $t0,$s0,1               # get 2 * r
    sub     $s3,$s3,$t0             # xchg -= (2 * r)

    li      $s4,1                   # ychg = 1
    li      $s2,0                   # raderr = 0

kdraw_loop:
    blt     $s0,$s1,kdraw_done      # x >= y? if no, fly (we're done)

    # draw pixels in all 8 octants
    jal     draw8

    addi    $s1,$s1,1               # y += 1
    add     $s2,$s2,$s4             # raderr += ychg
    addi    $s4,$s4,2               # ychg += 2

    sll     $t0,$s2,1               # get 2 * raderr
    add     $t0,$t0,$s3             # get (2 * raderr) + xchg
    blez    $s2,kdraw_loop          # >0? if no, loop

    subi    $s0,$s0,1               # x -= 1
    add     $s2,$s2,$s3             # raderr += xchg
    addi    $s3,$s3,2               # xchg += 2
    j       kdraw_loop

kdraw_done:
    lw      $ra,0($sp)
    addi    $sp,$sp,4
    jr      $ra

# draw8 -- draw single point in all 8 octants
#
# arguments:
#   s0 -- X coord
#   s1 -- Y coord
#
# registers:
#   t8 -- center_x
#   t9 -- center_y
draw8:
    subi    $sp,$sp,4
    sw      $ra,0($sp)

    lw      $t8,center_x
    lw      $t9,center_y
    lw      $a2,dpy_color

    lw      $t0,ask_colorinc
    li      $t1,-2
    ble     $t0,$t1,draw8_octant

    # draw [+x,+y]
    add     $a0,$t8,$s0
    add     $a1,$t9,$s1
    jal     dpypixel

    # draw [+y,+x]
    add     $a0,$t8,$s1
    add     $a1,$t9,$s0
    jal     dpypixel

    # draw [-y,+x]
    add     $a0,$t8,$s1
    sub     $a1,$t9,$s0
    jal     dpypixel

    # draw [-x,+y]
    sub     $a0,$t8,$s0
    add     $a1,$t9,$s1
    jal     dpypixel

    # draw [-x,-y]
    sub     $a0,$t8,$s0
    sub     $a1,$t9,$s1
    jal     dpypixel

    # draw [-y,-x]
    sub     $a0,$t8,$s1
    sub     $a1,$t9,$s0
    jal     dpypixel

    # draw [+x,-y]
    add     $a0,$t8,$s0
    sub     $a1,$t9,$s1
    jal     dpypixel

    # draw [+y,-x]
    sub     $a0,$t8,$s1
    add     $a1,$t9,$s0
    jal     dpypixel
    j       draw8_done

draw8_octant:
    la      $t7,color_octant

    # draw [+x,+y]
    add     $a0,$t8,$s0
    add     $a1,$t9,$s1
    lw      $a2,0($t7)
    addi    $t7,$t7,4
    jal     dpypixel

    # draw [+y,+x]
    add     $a0,$t8,$s1
    add     $a1,$t9,$s0
    lw      $a2,0($t7)
    addi    $t7,$t7,4
    jal     dpypixel

    # draw [-y,+x]
    add     $a0,$t8,$s1
    sub     $a1,$t9,$s0
    lw      $a2,0($t7)
    addi    $t7,$t7,4
    jal     dpypixel

    # draw [-x,+y]
    sub     $a0,$t8,$s0
    add     $a1,$t9,$s1
    lw      $a2,0($t7)
    addi    $t7,$t7,4
    jal     dpypixel

    # draw [-x,-y]
    sub     $a0,$t8,$s0
    sub     $a1,$t9,$s1
    lw      $a2,0($t7)
    addi    $t7,$t7,4
    jal     dpypixel

    # draw [-y,-x]
    sub     $a0,$t8,$s1
    sub     $a1,$t9,$s0
    lw      $a2,0($t7)
    addi    $t7,$t7,4
    jal     dpypixel

    # draw [+x,-y]
    add     $a0,$t8,$s0
    sub     $a1,$t9,$s1
    lw      $a2,0($t7)
    addi    $t7,$t7,4
    jal     dpypixel

    # draw [+y,-x]
    sub     $a0,$t8,$s1
    add     $a1,$t9,$s0
    lw      $a2,0($t7)
    addi    $t7,$t7,4
    jal     dpypixel

draw8_done:
    lw      $ra,0($sp)
    addi    $sp,$sp,4
    jr      $ra
    # marzmca/marzdpy.inc -- mars display functions

# dpypixel -- draw pixel on display
#
# arguments:
#   a0 -- X coord
#   a1 -- Y coord
#   a2 -- color
#   a3 -- display base address
#
# clobbers:
#   v1 -- bitmap offset/index
# trace:
#   v0,a0
dpypixel:

dpypixel_go:
    lw      $v1,dpy_width           # off = display width
    mul     $v1,$a1,$v1             # off = y * width

    add     $v1,$v1,$a0             # off += x
    sll     $v1,$v1,2               # convert to offset

    add     $v1,$a3,$v1             # ptr = base + off

    sw      $a2,($v1)               # store pixel
    jr      $ra

# dpybase -- get display base address
#
# RETURNS:
#   a3 -- display base address
dpybase:
    lw      $a3,dpy_base            # direct draw to display

    lw      $t0,dpy_blit
    beqz    $t0,dpybase_done

    la      $a3,bitmap              # draw to bitmap

dpybase_done:
    sw      $a3,dpy_now             # remember it [for debug]
    jr      $ra

# dpyblit -- blit bitmap to display
dpyblit:
    lw      $a0,dpy_blit            # blit mode?
    beqz    $a0,dpyblit_done        # if no, fly

    li      $t0,2                   # zap the background first?
    blt     $a0,$t2,dpyblit_init    # if no, fly

    # zero out the display
    lw      $a0,dpy_base            # get the base address
    addi    $a1,$a0,dpy_size        # get the end address

dpyblit_zap:
    sw      $zero,0($a0)            # set black
    addi    $a0,$a0,4               # advance current pointer
    blt     $a0,$a1,dpyblit_zap     # more to do? if yes, loop

# setup for blit
dpyblit_init:
    lw      $a0,dpy_base            # get the base address
    addi    $a1,$a0,dpy_size        # get the end address
    la      $a2,bitmap              # get offscreen address

dpyblit_loop:
    lw      $t0,0($a2)              # fetch from offscreen image
    addi    $a2,$a2,4               # advance offscreen pointer
    sw      $t0,0($a0)              # store to live area
    addi    $a0,$a0,4               # advance current pointer
    blt     $a0,$a1,dpyblit_loop    # more to do? if yes, loop

dpyblit_done:
    jr      $ra
    # marzmca/marzqask.inc -- extra prompting functions

    .eqv    qask_siz        100

# qask -- prompt user for number (possibly hex)
#
# RETURNS:
#   v0 -- value
#
# arguments:
#   a0 -- prompt string
#   a1 -- default value
#
# registers:
#   a2 -- save for a0
#   a3 -- save for a1
#   t0 -- current buffer char
#   t1 -- offset into qask_hex
#   t2 -- current hex string char
#   t3 -- current hex string pointer
#   t6 -- 1=negative
#   t7 -- number base
qask:
    move    $a2,$a0                 # remember for reprompt
    move    $a3,$a1                 # remember for reprompt

qask_retry:
    # output the prompt
    move    $a0,$a2
    li      $v0,4
    syscall

    la      $a0,qask_dft1
    li      $v0,4
    syscall

    # output the default value
    move    $a0,$a3
    li      $v0,1
    syscall

    la      $a0,qask_dft2
    li      $v0,4
    syscall

    # read in string
    li      $v0,8
    la      $a0,qask_buf
    la      $a1,qask_siz
    syscall

    lb      $t0,0($a0)              # get first buffer char
    li      $t1,0x0A                # get newline
    beq     $t0,$t1,qask_dft        # empty line? if yes, use default

    li      $v0,0                   # zap accumulator

    # decide if we have a negative number
    li      $t6,0
    lb      $t0,0($a0)              # get first buffer char
    li      $t1,'-'
    bne     $t0,$t1,qask_tryhex
    li      $t6,1                   # set negative number
    addi    $a0,$a0,1               # skip over '-'

# decide if want hex
qask_tryhex:
    li      $t7,10                  # assume base 10
    li      $t1,'x'
    bne     $t0,$t1,qask_loop
    addi    $a0,$a0,1               # skip over 'x'
    li      $t7,16                  # set base 16

qask_loop:
    lb      $t0,0($a0)              # get character
    addi    $a0,$a0,1               # advance buffer pointer

    # bug out if newline -- we are done
    li      $t1,0x0A
    beq     $t0,$t1,qask_done

    la      $t3,qask_hex
    li      $t1,0

qask_trymatch:
    lb      $t2,0($t3)              # get next hex char
    addi    $t3,$t3,1               # advance hex string pointer

    beq     $t2,$t0,qask_match      # got a match

    addi    $t1,$t1,1               # advance hex offset
    blt     $t1,$t7,qask_trymatch   # too large? if no, loop
    j       qask_retry              # if yes, the input char is unknown

qask_match:
    mul     $v0,$v0,$t7             # acc *= base
    add     $v0,$v0,$t1             # acc += digit
    j       qask_loop

qask_dft:
    move    $v0,$a3
    j       qask_exit

qask_done:
    beqz    $t6,qask_exit
    neg     $v0,$v0                 # set negative number

qask_exit:
    jr      $ra

    .data
qask_dft1:  .asciiz     " ["
qask_dft2:  .asciiz     "] > "
qask_buf:   .space      qask_siz
qask_hex:   .asciiz     "0123456789ABCDEF"

    .text

    .data
_S_000:     .asciiz     "dpyinit: mismatch\n"
_S_001:     .asciiz     "output diamond pattern?"
_S_002:     .asciiz     "minimum radius (0=single)"
_S_003:     .asciiz     "radius decrement"
_S_004:     .asciiz     "color increment (-1=rand, -2=rand/octant, -3=spin)"
_S_005:     .asciiz     "dpy_width"
_S_006:     .asciiz     "dpy_height"
_S_007:     .asciiz     "dpy_radius"
    .data                           #+
#+
edata: