MIPS 中的 Bresenham 线算法 静态调试动态调试其他问题其他观察

问题描述

我需要使用 Bresenham 的线条算法在 32x32 的白/黑图像上画一条线。我读过_bmp 和 save_bmp 函数,以及设置颜色的函数、绘制像素的函数和绘制线条的函数。不幸的是,当我编译我的代码时,result.bmp 文件中只显示像素。

.eqv    pHeader 0
.eqv    filesize 4
.eqv    pImg    8
.eqv    width   12
.eqv    height  16
.eqv    linesbytes 20

.eqv    bi_imgoffset  10
.eqv    bi_imgwidth   18
.eqv    bi_imgheight  22

.eqv    max_file_size 2048

    .data
filename:   .asciiz "white32x32.bmp"
resultfile: .asciiz "result.bmp"
color_err:  .asciiz "Incorrect color - should be 0 or 1"

num_one:      .word 1
num_neg_one:  .word -1
mult2:  .word 2
loop:   .word 32
        .align  4
descriptor: .word   0,0

filebuf:    .space max_file_size

    .text
    
    
    move $t0,$zero 
    lw   $s0,loop 
main:
    la $a0,filename
    la $a1,descriptor
    la $t0,filebuf
    sw $t0,pHeader($a1)
    li $t0,max_file_size
    sw $t0,filesize($a1)
    jal read_bmp_file
    # check for errors
    
    
        la   $a0,descriptor
        li   $a1,6  #cx
        li   $a2,8  #cy
        li   $a3,0  #color
        li $s0,10   #x
        li $s1,12   #y
        jal line_to
        addi $t0,$t0,32
        j main
        
        move $t0,loop 
    li $v0,10
    syscall
    
line_to:
    

# |register|   variable  |
# | t0     |   cx        |   
# | t1     |   cy        |
# | t2     |    dx - dy  |   
# | t3     |   ai        |
# | t4     |   bi        |
# | s0     |   x         |
# | s1     |   y         |
# | s2     | dx = x-cx   |
# | s3     } dy = y-cy   |      
# | s4     |   xi        |
# | s5     |   yi        |


    sub $s3,$s0,$t0 #s3 = x - cx
    sub $s4,$s1,$t1 #s4 = y - cy

#if (dx < 0) { xi = 1 } else { xi = -1}
    slt $t0,$s1   #dx = -dx;
    beq $t0,$zero,if_1_else  
    if_1_then:
     lw $s4,num_one
         j if_1_exit
    if_1_else:
         lw $s4,num_neg_one
    if_1_exit:

#if (dy < 0) { yi = 1 } else { yi = -1}
    slt $t0,$s1   #dy = -dy;
    beq $t0,if_2_else  
    if_2_then:
        lw $s5,num_one
         j if_2_exit
    if_2_else:
         lw $s5,num_neg_one
    if_2_exit:

    sub $t2,$s2,$s3 #   dy - dx

    lw $t0,num_neg_one
    mult $t0,$s3
    mflo $s3 # s3 = -dy

    move $t0,$a0 # t0 = cx
    move $t1,$a1 # t1 = cy
    move $s1,$s0 # s0 = x
    move $s2,$s1 # s1 = y
    jal set_next_pixel

loop_cond:
    

    sub $t3,$t0 
    sub $t4,$t1 
    add $t3,$t3,$t4 
    beqz $t3,loop1_end
    
    lw $t3,mult2
        mult $t3,$t2
        mflo $t3
        
        slt $t4,$s3,$t3 
    beqz $t4,loop1_end
    
    lopp_if_1_then:
        add $t2,$t2,$s3 
        add $t0,$s4 
        loop1_end:
        
        slt $t4,$s2 
        beqz $t4,loop2_end
        loop_if_2_then:
        add $t2,$s2 
        add $t1,$t1,$s5 
        loop2_end:

        j loop_cond 
        
 #loop_cond_end:
         
        
    jr $ra
set_next_pixel:
    lw $t0,linesbytes($a0)
    mul $t0,$a2   # $t0 offset of the beginning of the row
    
    sra $t1,$a1,3     # pixel byte offset within the row
    add $t0,$t1   # pixel byte offset within the image
    
    lw $t1,pImg($a0)
    add $t0,$t1   # address of the pixel byte
    
    lb $t1,0($t0)
    
    and $a1,0x7   # pixel offset within the byte
    li $t2,0x80
    srlv $t2,$a1  # mask on the position of pixel
    
    jal set_color
#   sub $a3,$a3,1
#   bnez $a3,set_next_pixel
    
    la $a0,resultfile
    la $a1,descriptor
    jal save_bmp_file
    
    li $v0,10
    syscall
    
set_color:
    addi $v1,1
    blt $a3,print_color_err     # if a3 < 0,then error
    bgt $a3,$v1,print_color_err   #if a3 > 1,then error
    beq $a3,white  # if a3 = 1,then white
        beq $a3,black    # if a3 = 0,then black
        jr $ra
black:
    xor $t1,$t2    # set proper pixel to 0 (black)
    sb $t1,0($t0)
    jr $ra
white:
    or $t1,$t2    # set proper pixel to 1 (white)
    sb $t1,0($t0)
    jr $ra
print_color_err:
    li $v0,4
    la $a0,color_err
    syscall


read_bmp_file:
    # $a0 - file name 
    # $a1 - file descriptor
    #   pHeader - contains pointer to file buffer
    #   filesize - maximum file size allowed
    move $t0,$a1
    li $a1,0
    li $a2,0
    li $v0,13 # open file
    syscall
    # check for errors: $v0 < 0
    
    move $a0,$v0
    lw $a1,pHeader($t0)
    lw $a2,filesize($t0)
    li $v0,14
    syscall
    
    sw $v0,filesize($t0)  # actual size of bmp file

    li $v0,16 # close file
    syscall

    lhu $t1,bi_imgoffset($a1)
    add $t1,$a1
    sw $t1,pImg($t0)
    
    lhu $t1,bi_imgwidth($a1)
    sw $t1,width($t0)
    
    lhu $t1,bi_imgheight($a1)
    sw $t1,height($t0)
    
    # number of words in a line: (width + 31) / 32
    # number of bytes in a line: ((width + 31) / 32) * 4
    lw $t1,width($t0)
    add $t1,31
    sra $t1,5 # t1 contains number of words
    sll $t1,2
    sw $t1,linesbytes($t0)
    jr $ra
    


save_bmp_file:
    # $a0 - file name 
    # $a1 - file descriptor
    #   pHeader - contains pointer to file buffer
    #   filesize - maximum file size allowed
    move $t0,1  # write
    li $a2,15
    syscall
    
    li $v0,16 # close file
    syscall

除了 line_to 之外的每个函数都可以正常工作,但我不知道是什么导致 line_to 出现问题。

解决方法

您需要一种调试哲学。

静态调试

  • 确保您的 C/伪代码算法确实有效,否则当您仍在学习汇编时,很难在汇编中调试设计问题。 (最好的方法是用 C 编写伪代码并实际运行它。)

  • 阅读代码并验证指令中命名的每个寄存器对应于算法中的正确变量。例如,

    # | s3     } dy = y-cy   |      
    # | s4     |   xi        |
    # | s5     |   yi        |
    
    sub $s3,$s0,$t0 #s3 = x - cx    # here s3 is dx,but comment above says s3 is dy
    sub $s4,$s1,$t1 #s4 = y - cy    # here s4 is dy,but comment above says s4 is xi
    
    

您可以看到注释和代码之间存在不匹配。

(顺便说一句,+1 用于编写寄存器变量映射。)

动态调试

  • 检查每个函数开头的参数是否正确传递
  • 从最简单的测试用例开始,例如一条水平线和/或一条垂直线——甚至可能是一条只有 1 磅长的线。
  • 单步执行,按照每条指令的效果
    • 在每条指令之后,验证每个寄存器是否具有您期望的值
    • 在每条指令之后,验证分支的控制流是否正确
    • 验证内存是否按照您对存储指令的预期更新

其他问题

  • 您的静态调用链有 main 调用 line_toline_to 调用 set_next_pixelset_next_pixel 调用 set_color。每次使用 jal 时,它都会重新调整 $ra 寄存器的用途,但是这些函数中的每一个都需要自己的 $ra 值才能返回给调用者。通常这是通过在非叶例程中创建堆栈帧并将 $ra 存储在那里,然后在返回调用者之前重新加载 $ra 来处理的。 (叶函数——不调用其他函数的函数——不需要这样做。)

  • set_next_pixel 不完整,没有返回给调用者。

  • print_color_err 处的代码仅属于 read_bmp_file。它应该返回或者结束程序。

其他观察

  • 不要使用带有常量的 lw 来将 +1 或 -1 放入寄存器 - 只需使用 li $t0,-1(或 addi $t0,$0,-1,如果您不这样做)想使用伪指令。)

  • set_color 处的代码正在测试太多条件,应该简化。它正在测试 $a3 for 1,= 1,= 0,并且仍然有一个 else 子句,以防这些测试都不成功。

  • 通过 0-x 而不是使用乘法 -1*i

    更容易完成否定
  • 通过移位 1 (sll $trg,$src,1) 比使用乘法更容易加倍

  • 您的调用约定是非标准的。这没问题,但会使其他人更难阅读代码,并且更容易出错。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...