问题描述
我需要使用 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_to
和line_to
调用set_next_pixel
,set_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
) 比使用乘法更容易加倍 -
您的调用约定是非标准的。这没问题,但会使其他人更难阅读代码,并且更容易出错。