Use backtick code blocks for syntax highlighting

This commit is contained in:
James Gregory 2013-12-31 11:38:48 +11:00
commit f658bfbb40
203 changed files with 26546 additions and 25801 deletions

672
37-01.md
View file

@ -67,351 +67,353 @@ plug-compatible with the C code from the previous chapter.
**LISTING 37.1 L37-1.ASM**
; Fast run-length slice line drawing implementation for mode 0x13, the VGA's
; 320x200 256-color mode.
; Draws a line between the specified endpoints in color Color.
; C near-callable as:
; void LineDraw(int XStart, int YStart, int XEnd, int YEnd, int Color)
; Tested with TASM
```nasm
; Fast run-length slice line drawing implementation for mode 0x13, the VGA's
; 320x200 256-color mode.
; Draws a line between the specified endpoints in color Color.
; C near-callable as:
; void LineDraw(int XStart, int YStart, int XEnd, int YEnd, int Color)
; Tested with TASM
SCREEN_WIDTH equ 320
SCREEN_SEGMENT equ 0a000h
.model small
.code
SCREEN_WIDTH equ 320
SCREEN_SEGMENT equ 0a000h
.model small
.code
; Parameters to call.
parms struc
dw ? ;pushed BP
dw ? ;pushed return address
XStart dw ? ;X start coordinate of line
YStart dw ? ;Y start coordinate of line
XEnd dw ? ;X end coordinate of line
YEnd dw ? ;Y end coordinate of line
Color db ? ;color in which to draw line
db ? ;dummy byte because Color is really a word
parms ends
; Parameters to call.
parms struc
dw ? ;pushed BP
dw ? ;pushed return address
XStart dw ? ;X start coordinate of line
YStart dw ? ;Y start coordinate of line
XEnd dw ? ;X end coordinate of line
YEnd dw ? ;Y end coordinate of line
Color db ? ;color in which to draw line
db ? ;dummy byte because Color is really a word
parms ends
; Local variables.
AdjUp equ -2 ;error term adjust up on each advance
AdjDown equ -4 ;error term adjust down when error term turns over
WholeStep equ -6 ;minimum run length
XAdvance equ -8 ;1 or -1, for direction in which X advances
LOCAL_SIZE equ 8
public _LineDraw
_LineDraw proc near
cld
push bp ;preserve caller's stack frame
mov bp,sp ;point to our stack frame
sub sp, LOCAL_SIZE ;allocate space for local variables
push si ;preserve C register variables
push di
push ds ;preserve caller's DS
; We'll draw top to bottom, to reduce the number of cases we have to handle,
; and to make lines between the same endpoints always draw the same pixels.
mov ax,[bp].YStart
cmp ax,[bp].YEnd
jle LineIsTopToBottom
xchg [bp].YEnd,ax; swap endpoints
mov [bp].YStart,ax
movbx, [bp].XStart
xchg [bp].XEnd,bx
mov [bp].XStart,bx
LineIsTopToBottom:
; Point DI to the first pixel to draw.
mov dx,SCREEN_WIDTH
mul dx ;YStart * SCREEN_WIDTH
mov si,[bp].XStart
mov di,si
add di,ax ;DI = YStart * SCREEN_WIDTH + XStart
; = offset of initial pixel
; Figure out how far we're going vertically (guaranteed to be positive).
mov cx,[bp].YEnd
sub cx,[bp].YStart ;CX = YDelta
; Figure out whether we're going left or right, and how far we're going
; horizontally. In the process, special-case vertical lines, for speed and
; to avoid nasty boundary conditions and division by 0.
mov dx,[bp].XEnd
sub dx,si ;XDelta
jnz NotVerticalLine ;XDelta == 0 means vertical line
;it is a vertical line
;yes, special case vertical line
mov ax,SCREEN_SEGMENT
mov ds,ax ;point DS:DI to the first byte to draw
mov al,[bp].Color
VLoop:
mov [di],al
add di,SCREEN_WIDTH
dec cx
jns VLoop
jmp Done
; Special-case code for horizontal lines.
align 2
IsHorizontalLine:
mov ax,SCREEN_SEGMENT
mov es,ax ;point ES:DI to the first byte to draw
mov al,[bp].Color
mov ah,al ;duplicate in high byte for word access
and bx,bx ;left to right?
jns DirSet ;yes
sub di,dx ;currently right to left, point to left
; end so we can go left to right
; (avoids unpleasantness withright to
; left REP STOSW)
DirSet:
mov cx,dx
inc cx ;# of pixels to draw
shr cx,1 ;# of words to draw
rep stosw ;do as many words as possible
adc cx,cx
rep stosb ;do the odd byte, if there is one
jmp Done
; Special-case code for diagonal lines.
align 2
IsDiagonalLine:
mov ax,SCREEN_SEGMENT
mov ds,ax ;point DS:DI to the first byte to draw
mov al,[bp].Color
add bx,SCREEN_WIDTH ;advance distance from one pixel to next
DLoop:
mov [di],al
add di,bx
dec cx
jns DLoop
jmp Done
; Local variables.
AdjUp equ -2 ;error term adjust up on each advance
AdjDown equ -4 ;error term adjust down when error term turns over
WholeStep equ -6 ;minimum run length
XAdvance equ -8 ;1 or -1, for direction in which X advances
LOCAL_SIZE equ 8
public _LineDraw
_LineDraw proc near
cld
push bp ;preserve caller's stack frame
mov bp,sp ;point to our stack frame
sub sp, LOCAL_SIZE ;allocate space for local variables
push si ;preserve C register variables
push di
push ds ;preserve caller's DS
; We'll draw top to bottom, to reduce the number of cases we have to handle,
; and to make lines between the same endpoints always draw the same pixels.
mov ax,[bp].YStart
cmp ax,[bp].YEnd
jle LineIsTopToBottom
xchg [bp].YEnd,ax; swap endpoints
mov [bp].YStart,ax
movbx, [bp].XStart
xchg [bp].XEnd,bx
mov [bp].XStart,bx
LineIsTopToBottom:
; Point DI to the first pixel to draw.
mov dx,SCREEN_WIDTH
mul dx ;YStart * SCREEN_WIDTH
mov si,[bp].XStart
mov di,si
add di,ax ;DI = YStart * SCREEN_WIDTH + XStart
; = offset of initial pixel
; Figure out how far we're going vertically (guaranteed to be positive).
mov cx,[bp].YEnd
sub cx,[bp].YStart ;CX = YDelta
; Figure out whether we're going left or right, and how far we're going
; horizontally. In the process, special-case vertical lines, for speed and
; to avoid nasty boundary conditions and division by 0.
mov dx,[bp].XEnd
sub dx,si ;XDelta
jnz NotVerticalLine ;XDelta == 0 means vertical line
;it is a vertical line
;yes, special case vertical line
mov ax,SCREEN_SEGMENT
mov ds,ax ;point DS:DI to the first byte to draw
mov al,[bp].Color
VLoop:
mov [di],al
add di,SCREEN_WIDTH
dec cx
jns VLoop
jmp Done
; Special-case code for horizontal lines.
align 2
IsHorizontalLine:
mov ax,SCREEN_SEGMENT
mov es,ax ;point ES:DI to the first byte to draw
mov al,[bp].Color
mov ah,al ;duplicate in high byte for word access
and bx,bx ;left to right?
jns DirSet ;yes
sub di,dx ;currently right to left, point to left
; end so we can go left to right
; (avoids unpleasantness withright to
; left REP STOSW)
DirSet:
mov cx,dx
inc cx ;# of pixels to draw
shr cx,1 ;# of words to draw
rep stosw ;do as many words as possible
adc cx,cx
rep stosb ;do the odd byte, if there is one
jmp Done
; Special-case code for diagonal lines.
align 2
IsDiagonalLine:
mov ax,SCREEN_SEGMENT
mov ds,ax ;point DS:DI to the first byte to draw
mov al,[bp].Color
add bx,SCREEN_WIDTH ;advance distance from one pixel to next
DLoop:
mov [di],al
add di,bx
dec cx
jns DLoop
jmp Done
align 2
NotVerticalLine:
mov bx,1 ;assume left to right, so XAdvance = 1
;***leaves flags unchanged***
jns LeftToRight ;left to right, all set
neg bx ;right to left, so XAdvance = -1
neg dx ;|XDelta|
LeftToRight:
; Special-case horizontal lines.
and cx,cx ;YDelta == 0?
jz IsHorizontalLine ;yes
align 2
NotVerticalLine:
mov bx,1 ;assume left to right, so XAdvance = 1
;***leaves flags unchanged***
jns LeftToRight ;left to right, all set
neg bx ;right to left, so XAdvance = -1
neg dx ;|XDelta|
LeftToRight:
; Special-case horizontal lines.
and cx,cx ;YDelta == 0?
jz IsHorizontalLine ;yes
; Special-case diagonal lines.
cmp cx,dx ;YDelta == XDelta?
jz IsDiagonalLine ;yes
; Determine whether the line is X or Y major, and handle accordingly.
cmp dx,cx
jae XMajor
jmp YMajor
; X-major (more horizontal than vertical) line.
align 2
XMajor:
mov ax,SCREEN_SEGMENT
mov es,ax ;point ES:DI to the first byte to draw
and bx,bx ;left to right?
jns DFSet ;yes, CLD is already set
std ;right to left, so draw backwards
DFSet:
mov ax,dx ;XDelta
sub dx,dx ;prepare for division
div cx ;AX = XDelta/YDelta
; (minimum # of pixels in a run in this line)
;DX = XDelta % YDelta
mov bx,dx ;error term adjust each time Y steps by 1;
add bx,bx ; used to tell when one extra pixel should be
mov [bp].AdjUp,bx ; drawn as part of a run, to account for
; fractional steps along the X axis per
; 1-pixel steps along Y
mov si,cx ;error term adjust when the error term turns
add si,si ; over, used to factor out the X step made at
mov [bp].AdjDown,si ; that time
; Initial error term; reflects an initial step of 0.5 along the Y axis.
sub dx,si ;(XDelta % YDelta) - (YDelta * 2)
;DX = initial error term
; The initial and last runs are partial, because Y advances only 0.5 for
; these runs, rather than 1. Divide one full run, plus the initial pixel,
; between the initial and last runs.
mov si,cx ;SI = YDelta
mov cx,ax ;whole step (minimum run length)
shr cx,1
inc cx ;initial pixel count = (whole step / 2) + 1;
; (may be adjusted later). This is also the
; final run pixel count
push cx ;remember final run pixel count for later
; If the basic run length is even and there's no fractional advance, we have
; one pixel that could go to either the initial or last partial run, which
; we'll arbitrarily allocate to the last run.
; If there is an odd number of pixels per run, we have one pixel that can't
; be allocated to either the initial or last partial run, so we'll add 0.5 to
; the error term so this pixel will be handled by the normal full-run loop.
add dx,si ;assume odd length, add YDelta to error term
; (add 0.5 of a pixel to the error term)
test al,1 ;is run length even?
jnz XMajorAdjustDone ;no, already did work for odd case, all set
sub dx,si ;length is even, undo odd stuff we just did
and bx,bx ;is the adjust up equal to 0?
jnz XMajorAdjustDone ;no (don't need to check for odd length,
; because of the above test)
dec cx ;both conditions met; make initial run 1
; shorter
XMajorAdjustDone:
mov [bp].WholeStep,ax ;whole step (minimum run length)
mov al,[bp].Color ;AL = drawing color
; Draw the first, partial run of pixels.
rep stosb ;draw the final run
add di,SCREEN_WIDTH ;advance along the minor axis (Y)
; Draw all full runs.
cmp si,1 ;are there more than 2 scans, so there are
; some full runs? (SI = # scans - 1)
jna XMajorDrawLast ;no, no full runs
dec dx ;adjust error term by -1 so we can use
; carry test
shr si,1 ;convert from scan to scan-pair count
jnc XMajorFullRunsOddEntry ;if there is an odd umber of scans,
; do the odd scan now
XMajorFullRunsLoop:
mov cx,[bp].WholeStep ;run is at least this long
add dx,bx ;advance the error term and add an extra
jnc XMajorNoExtra ; pixel if the error term so indicates
inc cx ;one extra pixel in run
sub dx,[bp].AdjDown ;reset the error term
XMajorNoExtra:
rep stosb ;draw this scan line's run
add di,SCREEN_WIDTH ;advance along the minor axis (Y)
XMajorFullRunsOddEntry: ;enter loop here if there is an odd number
; of full runs
mov cx,[bp].WholeStep ;run is at least this long
add dx,bx ;advance the error term and add an extra
jnc XMajorNoExtra2 ; pixel if the error term so indicates
inc cx ;one extra pixel in run
sub dx,[bp].AdjDown ;reset the error term
XMajorNoExtra2:
rep stosb ;draw this scan line's run
add di,SCREEN_WIDTH ;advance along the minor axis (Y)
; Special-case diagonal lines.
cmp cx,dx ;YDelta == XDelta?
jz IsDiagonalLine ;yes
; Determine whether the line is X or Y major, and handle accordingly.
cmp dx,cx
jae XMajor
jmp YMajor
; X-major (more horizontal than vertical) line.
align 2
XMajor:
mov ax,SCREEN_SEGMENT
mov es,ax ;point ES:DI to the first byte to draw
and bx,bx ;left to right?
jns DFSet ;yes, CLD is already set
std ;right to left, so draw backwards
DFSet:
mov ax,dx ;XDelta
sub dx,dx ;prepare for division
div cx ;AX = XDelta/YDelta
; (minimum # of pixels in a run in this line)
;DX = XDelta % YDelta
mov bx,dx ;error term adjust each time Y steps by 1;
add bx,bx ; used to tell when one extra pixel should be
mov [bp].AdjUp,bx ; drawn as part of a run, to account for
; fractional steps along the X axis per
; 1-pixel steps along Y
mov si,cx ;error term adjust when the error term turns
add si,si ; over, used to factor out the X step made at
mov [bp].AdjDown,si ; that time
; Initial error term; reflects an initial step of 0.5 along the Y axis.
sub dx,si ;(XDelta % YDelta) - (YDelta * 2)
;DX = initial error term
; The initial and last runs are partial, because Y advances only 0.5 for
; these runs, rather than 1. Divide one full run, plus the initial pixel,
; between the initial and last runs.
mov si,cx ;SI = YDelta
mov cx,ax ;whole step (minimum run length)
shr cx,1
inc cx ;initial pixel count = (whole step / 2) + 1;
; (may be adjusted later). This is also the
; final run pixel count
push cx ;remember final run pixel count for later
; If the basic run length is even and there's no fractional advance, we have
; one pixel that could go to either the initial or last partial run, which
; we'll arbitrarily allocate to the last run.
; If there is an odd number of pixels per run, we have one pixel that can't
; be allocated to either the initial or last partial run, so we'll add 0.5 to
; the error term so this pixel will be handled by the normal full-run loop.
add dx,si ;assume odd length, add YDelta to error term
; (add 0.5 of a pixel to the error term)
test al,1 ;is run length even?
jnz XMajorAdjustDone ;no, already did work for odd case, all set
sub dx,si ;length is even, undo odd stuff we just did
and bx,bx ;is the adjust up equal to 0?
jnz XMajorAdjustDone ;no (don't need to check for odd length,
; because of the above test)
dec cx ;both conditions met; make initial run 1
; shorter
XMajorAdjustDone:
mov [bp].WholeStep,ax ;whole step (minimum run length)
mov al,[bp].Color ;AL = drawing color
; Draw the first, partial run of pixels.
rep stosb ;draw the final run
add di,SCREEN_WIDTH ;advance along the minor axis (Y)
; Draw all full runs.
cmp si,1 ;are there more than 2 scans, so there are
; some full runs? (SI = # scans - 1)
jna XMajorDrawLast ;no, no full runs
dec dx ;adjust error term by -1 so we can use
; carry test
shr si,1 ;convert from scan to scan-pair count
jnc XMajorFullRunsOddEntry ;if there is an odd umber of scans,
; do the odd scan now
XMajorFullRunsLoop:
mov cx,[bp].WholeStep ;run is at least this long
add dx,bx ;advance the error term and add an extra
jnc XMajorNoExtra ; pixel if the error term so indicates
inc cx ;one extra pixel in run
sub dx,[bp].AdjDown ;reset the error term
XMajorNoExtra:
rep stosb ;draw this scan line's run
add di,SCREEN_WIDTH ;advance along the minor axis (Y)
XMajorFullRunsOddEntry: ;enter loop here if there is an odd number
; of full runs
mov cx,[bp].WholeStep ;run is at least this long
add dx,bx ;advance the error term and add an extra
jnc XMajorNoExtra2 ; pixel if the error term so indicates
inc cx ;one extra pixel in run
sub dx,[bp].AdjDown ;reset the error term
XMajorNoExtra2:
rep stosb ;draw this scan line's run
add di,SCREEN_WIDTH ;advance along the minor axis (Y)
dec si
jnz XMajorFullRunsLoop
; Draw the final run of pixels.
XMajorDrawLast:
pop cx ;get back the final run pixel length
rep stosb ;draw the final run
dec si
jnz XMajorFullRunsLoop
; Draw the final run of pixels.
XMajorDrawLast:
pop cx ;get back the final run pixel length
rep stosb ;draw the final run
cld ;restore normal direction flag
jmp Done
; Y-major (more vertical than horizontal) line.
align 2
YMajor:
mov [bp].XAdvance,bx ;remember which way X advances
mov ax,SCREEN_SEGMENT
mov ds,ax ;point DS:DI to the first byte to draw
mov ax,cx ;YDelta
mov cx,dx ;XDelta
sub dx,dx ;prepare for division
div cx ;AX = YDelta/XDelta
; (minimum # of pixels in a run in this line)
;DX = YDelta % XDelta
mov bx,dx ;error term adjust each time X steps by 1;
add bx,bx ; used to tell when one extra pixel should be
mov [bp].AdjUp,bx ; drawn as part of a run, to account for
; fractional steps along the Y axis per
; 1-pixel steps along X
mov si,cx ;error term adjust when the error term turns
add si,si ; over, used to factor out the Y step made at
mov [bp].AdjDown,si ; that time
cld ;restore normal direction flag
jmp Done
; Y-major (more vertical than horizontal) line.
align 2
YMajor:
mov [bp].XAdvance,bx ;remember which way X advances
mov ax,SCREEN_SEGMENT
mov ds,ax ;point DS:DI to the first byte to draw
mov ax,cx ;YDelta
mov cx,dx ;XDelta
sub dx,dx ;prepare for division
div cx ;AX = YDelta/XDelta
; (minimum # of pixels in a run in this line)
;DX = YDelta % XDelta
mov bx,dx ;error term adjust each time X steps by 1;
add bx,bx ; used to tell when one extra pixel should be
mov [bp].AdjUp,bx ; drawn as part of a run, to account for
; fractional steps along the Y axis per
; 1-pixel steps along X
mov si,cx ;error term adjust when the error term turns
add si,si ; over, used to factor out the Y step made at
mov [bp].AdjDown,si ; that time
; Initial error term; reflects an initial step of 0.5 along the X axis.
sub dx,si ;(YDelta % XDelta) - (XDelta * 2)
;DX = initial error term
; The initial and last runs are partial, because X advances only 0.5 for
; these runs, rather than 1. Divide one full run, plus the initial pixel,
; between the initial and last runs.
mov si,cx ;SI = XDelta
mov cx,ax ;whole step (minimum run length)
shr cx,1
inc cx ;initial pixel count = (whole step / 2) + 1;
; (may be adjusted later)
push cx ;remember final run pixel count for later
; Initial error term; reflects an initial step of 0.5 along the X axis.
sub dx,si ;(YDelta % XDelta) - (XDelta * 2)
;DX = initial error term
; The initial and last runs are partial, because X advances only 0.5 for
; these runs, rather than 1. Divide one full run, plus the initial pixel,
; between the initial and last runs.
mov si,cx ;SI = XDelta
mov cx,ax ;whole step (minimum run length)
shr cx,1
inc cx ;initial pixel count = (whole step / 2) + 1;
; (may be adjusted later)
push cx ;remember final run pixel count for later
; If the basic run length is even and there's no fractional advance, we have
; one pixel that could go to either the initial or last partial run, which
; we'll arbitrarily allocate to the last run.
; If there is an odd number of pixels per run, we have one pixel that can't
; be allocated to either the initial or last partial run, so we'll add 0.5 to
; the error term so this pixel will be handled by the normal full-run loop.
add dx,si ;assume odd length, add XDelta to error term
test al,1 ;is run length even?
jnz YMajorAdjustDone ;no, already did work for odd case, all set
sub dx,si ;length is even, undo odd stuff we just did
and bx,bx ;is the adjust up equal to 0?
jnz YMajorAdjustDone ;no (don't need to check for odd length,
; because of the above test)
dec cx ;both conditions met; make initial run 1
; shorter
YMajorAdjustDone:
mov [bp].WholeStep,ax ;whole step (minimum run length)
mov al,[bp].Color ;AL = drawing color
mov bx,[bp].XAdvance ;which way X advances
; Draw the first, partial run of pixels.
YMajorFirstLoop:
mov [di],al ;draw the pixel
add di,SCREEN_WIDTH ;advance along the major axis (Y)
dec cx
jnz YMajorFirstLoop
add di,bx ;advance along the minor axis (X)
; Draw all full runs.
cmp si,1 ;# of full runs. Are there more than 2
; columns, so there are some full runs?
; (SI = # columns - 1)
jna YMajorDrawLast ;no, no full runs
dec dx ;adjust error term by -1 so we can use
; carry test
shr si,1 ;convert from column to column-pair count
jnc YMajorFullRunsOddEntry ;if there is an odd number of
; columns, do the odd column now
YMajorFullRunsLoop:
mov cx,[bp].WholeStep ;run is at least this long
add dx,[bp].AdjUp ;advance the error term and add an extra
jnc YMajorNoExtra ; pixel if the error term so indicates
inc cx ;one extra pixel in run
sub dx,[bp].AdjDown ;reset the error term
YMajorNoExtra:
;draw the run
YMajorRunLoop:
mov [di],al ;draw the pixel
add di,SCREEN_WIDTH ;advance along the major axis (Y)
dec cx
jnz YMajorRunLoop
add di,bx ;advance along the minor axis (X)
YMajorFullRunsOddEntry: ;enter loop here if there is an odd number
; of full runs
mov cx,[bp].WholeStep ;run is at least this long
add dx,[bp].AdjUp ;advance the error term and add an extra
jnc YMajorNoExtra2 ; pixel if the error term so indicates
inc cx ;one extra pixel in run
sub dx,[bp].AdjDown ;reset the error term
YMajorNoExtra2:
;draw the run
YMajorRunLoop2:
mov [di],al ;draw the pixel
add di,SCREEN_WIDTH ;advance along the major axis (Y)
dec cx
jnz YMajorRunLoop2
add di,bx ;advance along the minor axis (X)
; If the basic run length is even and there's no fractional advance, we have
; one pixel that could go to either the initial or last partial run, which
; we'll arbitrarily allocate to the last run.
; If there is an odd number of pixels per run, we have one pixel that can't
; be allocated to either the initial or last partial run, so we'll add 0.5 to
; the error term so this pixel will be handled by the normal full-run loop.
add dx,si ;assume odd length, add XDelta to error term
test al,1 ;is run length even?
jnz YMajorAdjustDone ;no, already did work for odd case, all set
sub dx,si ;length is even, undo odd stuff we just did
and bx,bx ;is the adjust up equal to 0?
jnz YMajorAdjustDone ;no (don't need to check for odd length,
; because of the above test)
dec cx ;both conditions met; make initial run 1
; shorter
YMajorAdjustDone:
mov [bp].WholeStep,ax ;whole step (minimum run length)
mov al,[bp].Color ;AL = drawing color
mov bx,[bp].XAdvance ;which way X advances
; Draw the first, partial run of pixels.
YMajorFirstLoop:
mov [di],al ;draw the pixel
add di,SCREEN_WIDTH ;advance along the major axis (Y)
dec cx
jnz YMajorFirstLoop
add di,bx ;advance along the minor axis (X)
; Draw all full runs.
cmp si,1 ;# of full runs. Are there more than 2
; columns, so there are some full runs?
; (SI = # columns - 1)
jna YMajorDrawLast ;no, no full runs
dec dx ;adjust error term by -1 so we can use
; carry test
shr si,1 ;convert from column to column-pair count
jnc YMajorFullRunsOddEntry ;if there is an odd number of
; columns, do the odd column now
YMajorFullRunsLoop:
mov cx,[bp].WholeStep ;run is at least this long
add dx,[bp].AdjUp ;advance the error term and add an extra
jnc YMajorNoExtra ; pixel if the error term so indicates
inc cx ;one extra pixel in run
sub dx,[bp].AdjDown ;reset the error term
YMajorNoExtra:
;draw the run
YMajorRunLoop:
mov [di],al ;draw the pixel
add di,SCREEN_WIDTH ;advance along the major axis (Y)
dec cx
jnz YMajorRunLoop
add di,bx ;advance along the minor axis (X)
YMajorFullRunsOddEntry: ;enter loop here if there is an odd number
; of full runs
mov cx,[bp].WholeStep ;run is at least this long
add dx,[bp].AdjUp ;advance the error term and add an extra
jnc YMajorNoExtra2 ; pixel if the error term so indicates
inc cx ;one extra pixel in run
sub dx,[bp].AdjDown ;reset the error term
YMajorNoExtra2:
;draw the run
YMajorRunLoop2:
mov [di],al ;draw the pixel
add di,SCREEN_WIDTH ;advance along the major axis (Y)
dec cx
jnz YMajorRunLoop2
add di,bx ;advance along the minor axis (X)
dec si
jnz YMajorFullRunsLoop
; Draw the final run of pixels.
YMajorDrawLast:
pop cx ;get back the final run pixel length
YMajorLastLoop:
mov [di],al ;draw the pixel
add di,SCREEN_WIDTH ;advance along the major axis (Y)
dec cx
jnz YMajorLastLoop
Done:
pop ds ;restore caller's DS
pop di
pop si ;restore C register variables
mov sp,bp ;deallocate local variables
pop bp ;restore caller's stack frame
ret
_LineDraw endp
end
dec si
jnz YMajorFullRunsLoop
; Draw the final run of pixels.
YMajorDrawLast:
pop cx ;get back the final run pixel length
YMajorLastLoop:
mov [di],al ;draw the pixel
add di,SCREEN_WIDTH ;advance along the major axis (Y)
dec cx
jnz YMajorLastLoop
Done:
pop ds ;restore caller's DS
pop di
pop si ;restore C register variables
mov sp,bp ;deallocate local variables
pop bp ;restore caller's stack frame
ret
_LineDraw endp
end
```