You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

屏幕仅显示起始像素,反复检查代码未发现错误,恳请协助排查循环无法正常执行的原因。


错误排查与修正方案

以下是导致循环无法推进的关键问题:

  1. 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)
    
  2. 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)
    
  3. 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) ; 新增写回操作
    
  4. 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
    
  5. 循环终止条件判断逻辑错误
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 05:40:41