0

I'm trying to implement a version of Bresenham's line-drawing algorithm in 16-bit assembly by passing the coordinates through registers (deliberately not using the stack or writing to video memory right now). It plots the very first pixel correctly (x1 and y1), but nothing else happens.

The following is how the coordinates of the line is defined:

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

And this is the main code

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 sy 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

This is the data portion:

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

pixel on screen

I've read and reread the code, but as there are no errors, I cannot find where I've messed up. I followed the implementation of this pseudocode line by line, and it seems to check out.

 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
    `bl` is the low byte of `bx`. You have overlapping usage. – Jester Dec 07 '22 at 18:26
  • If you single-step your code with a debugger, you should see the BX value change when your code writes BL. (e.g. the debugger built-in to Bochs, or have QEMU act as a GDB remote, or use a debugger inside the 16-bit machine.) – Peter Cordes Dec 07 '22 at 18:54
  • It's easy to make this mistake if you're used to RISC CPUs, but x86 and z80 work differently. As Jester pointed out, `bl` is the low byte of `bx`, meaning that (for example) `mov bx, 1234h` will not only set `bx` to `0x1234`, but also `bh` is set to `0x12` and `bl` to `0x34`. Where you did `movb $0, %ah movb $0x13, %al` you could have done a single `movw $0x13, $ax` and gotten the same result a bit faster (the $0x13 is padded to $0x0013) – puppydrum64 Dec 08 '22 at 15:09

0 Answers0