AT&T语法16位汇编VGA13模式Bresenham画线算法循环异常
Bresenham直线绘制算法16位AT&T汇编实现问题排查
我正在用16位AT&T汇编实现Bresenham直线绘制算法,通过寄存器传递坐标(刻意不使用栈或直接写入显存)。目前仅能正确绘制第一个像素(x1和y1),后续无任何像素生成。
直线坐标定义代码
real_start: movw $boot_message, %si # Display our boot message call cons_writeline # Switch to VGA mode 13 movb $0, %ah movb $0x13, %al int $0x10 # lines are defined in the registers from this point movw $5, %cx # x coordinate movw $10, %dx # y coordinate movw $100, %bx # x2 coordinate movw $100, %ax # y2 coordinate movb $2, %bl # colour green call bresenham_start
主算法代码
draw_pixel: movb (colour), %al # move colour to al movb $0x0c, %ah movb $0, %bh int $0x10 jmp bresenham_main bresenham_start: movw %cx, (x1) movw %dx, (y1) movw %bx, (x2) movw %ax, (y2) movb %bl, (colour) sub %cx, %bx # delta x = x2 - x1 movw %bx, (delta_x) # store result in delta_x sub %dx, %ax # delta y = y2 - y1 movw %ax, (delta_y) # store result in delta_y sub %ax, %bx # err = delta x - delta y movw %bx, (err) movw (x1), %cx # movw (x2), %dx # Reset the coordinates to their original registers movw (x1), %cx movw (x2), %dx movw $1, (sx) cmp %dx, %cx jl sx_end # if x1 is less than x2, it jumps to sx_end and keeps sx as 1 movw $-1, (sx) # if x1 is more than x2, it sets sx to -1 and plods along sx_end: movw (y1), %cx movw (y2), %dx movw $1, (sx) cmp %dx, %cx jl sy_end # if y1 is less than y2, it jumps to sy_end and keeps sy as 1 movw $-1, (sx) # if y1 is more than y2, it sets sx to -1 and plods along sy_end: jmp bresenham bresenham: movw (x1), %cx # movw (x2), %bx # Reset the coordinates to their original registers movw (y1), %dx # ^^ movw (y2), %ax # # jump to draw_pixel jmp draw_pixel bresenham_main: # check if x1 = x2 and y1 = y2 cmp %cx, %bx # Check if x1 is less than x2 jne carry_on # Condition in pseudocode uses and, meaning we don't break from the loop if x1 is not equal to x2 cmp %dx, %ax # Check if y1 equals y2 je break # break from the loop if y1 = y2, since to reach this part of the code, x1 is equal to x2 carry_on: # Else: # calculate e2 movw (err), %ax # move err into ax movw $2, %dx # set bx to 2 mul %dx # when operand is a word: (DX AX) = AX * operand movw %ax, (e2) # put the result into e2 # Check if e2 = -dy # e2 is already in ax movw (delta_y), %bx neg %bx # flip delta_y to negative cmp (e2), %bx jg carry_on_two # skips this block of code if e2 > -dy movw (err), %cx # move err into cx add %bx, %cx # add -dy to err, which is same as err - dy movw (x1), %dx # move x1 to dx add (sx), %dx # add sx to x1 and store it in dx carry_on_two: # code resumes here from the if statement # e2 is currently in ax movw (delta_x), %bx # move delta_x to bx cmp %ax, %bx jne inc_y # add delta_x to err and increment y1 by sy, then continue loop jmp bresenham # continue the loop inc_y: # if e2 < dx movw (y1), %ax # move y1 to ax movw (sy), %bx # move sy to bx add %ax, %bx # add y1 to sy and store it in bx movw %bx, (y1) # set y1 to the new result jmp bresenham # jump back to beginning of loop break: ret
数据段内容
x1: .word 0 # x1 coordinate, i.e. the starting coordinate of x x2: .word 0 # x2 coodinate, i.e. the ending coordinate of x y1: .word 0 # y1 coordinate, i.e. the starting coordinate of y y2: .word 0 # y2 coordinate, i.e. the ending coordinate of y sx: .word 0 sy: .word 0 delta_x: .word 0 # Delta x variable delta_y: .word 0 # Delta y variable err: .word 0 # error variable e2: .word 0 colour: .byte 0
参考伪代码
function drawline(x1, y1, x2, y2, colour) dx := abs(x2 - x1) dy := abs(y2 - y1) if x1 < x2 then sx := 1 else sx := -1 if y1 < y2 then sy := 1 else sy := -1 err := dx - dy loop setPixel(x1, y1, colour) if x1 = x2 and y1 = y2 exit loop e2 := 2 * err if e2 > -dy then err := err - dy x1 := x1 + sx end if if e2 < dx then err := err + dx y1 := y1 + sy end if end loop
屏幕仅显示起始像素,反复检查代码未发现错误,恳请协助排查循环无法正常执行的原因。
错误排查与修正方案
以下是导致循环无法推进的关键问题:
sy变量赋值错误
在bresenham_start的sy设置段,错误地将值写入sx而非sy,导致y坐标步进始终为0,同时覆盖了sx的正确值:; 错误代码 movw $1, (sx) cmp %dx, %cx jl sy_end movw $-1, (sx) ; 修正为 movw $1, (sy) cmp %dx, %cx jl sy_end movw $-1, (sy)delta_x和delta_y未取绝对值
伪代码中dx、dy是坐标差的绝对值,但当前代码直接用减法结果,若终点坐标小于起点,delta值会为负,导致后续err计算和判断完全错误,需添加取绝对值处理:; 处理delta_x取绝对值 sub %cx, %bx jge dx_abs_ok neg %bx dx_abs_ok: movw %bx, (delta_x) ; 处理delta_y取绝对值 sub %dx, %ax jge dy_abs_ok neg %ax dy_abs_ok: movw %ax, (delta_y)x1和err更新后未写回内存
在bresenham_main中,计算出新的x1和err值后,没有写回内存单元,导致下一次循环仍使用初始值:; 修正x1更新部分 movw (x1), %dx add (sx), %dx movw %dx, (x1) ; 新增写回操作 ; 修正err更新部分 movw (err), %cx add %bx, %cx movw %cx, (err) ; 新增写回操作inc_y段缺失err更新逻辑
伪代码中当e2 < dx时,需要执行err := err + dx,但当前代码只更新了y1,完全漏掉err的更新,同时y1的加法顺序也错误:inc_y: # if e2 < dx ; 新增err更新 movw (err), %cx movw (delta_x), %bx add %bx, %cx movw %cx, (err) ; 修正y1更新顺序 movw (y1), %ax movw (sy), %bx add %bx, %ax ; 是sy加到y1,而非y1加到sy movw %ax, (y1) jmp bresenham循环终止条件判断逻辑错误
bresenham_main开头的寄存器值并非当前x1、x2、y1、y2,需重新从内存加载后再判断终止条件:bresenham_main: ; 重新加载当前坐标值 movw (x1), %cx movw (x2), %bx movw (y1), %dx movw (y2), %ax ; 正确判断终止条件:x1等于x2且y1等于y2才退出 cmp %cx, %bx jne carry_on cmp %dx, %ax je break carry_on:
修正以上问题后,循环即可正确推进,逐步绘制完整直线。
内容的提问来源于stack exchange,提问作者user0164
相关产品推荐
相关产品推荐

