Category : Files from Magazines
Archive   : PJ-VGA.ZIP
Filename : EVGALINE.ASM

 
Output of file : EVGALINE.ASM contained in archive : PJ-VGA.ZIP
;
; *** Listing 3 ***
;
; Fast assembler implementation of Bresenham's line drawing algorithm
; for the EGA and VGA. Works in modes 0Dh, 0Eh, 0Fh, 10h, and 12h.
; Turbo C version 2.0 near-callable.
; Bit mask accumulation technique when |DeltaX| >= |DeltaY|
; suggested by Jim Mackraz.
;
; Assembled with TASM 1.0.
;
; By Michael Abrash. 2/4/89.
;
;****************************************************************
; C-compatible line-drawing entry point at _EVGALine. *
; Called from Turbo C with: *
; EVGALine(X0, Y0, X1, Y1, Color); *
;****************************************************************
;

_TEXT segment byte public 'CODE'
_TEXT ends
CONST segment word public 'CONST'
CONST ends
_BSS segment word public 'BSS'
_BSS ends
_DATA segment word public 'DATA'
_DATA ends
DGROUP group CONST, _BSS, _DATA
assume cs: _TEXT, ds: DGROUP, ss: DGROUP, es: DGROUP
_TEXT segment

;
; Equates.
;
EVGA_SCREEN_WIDTH_IN_BYTES equ 80 ;memory offset from start of
; one row to start of next
; in display memory
EVGA_SCREEN_SEGMENT equ 0a000h ;display memory segment
GC_INDEX equ 3ceh ;Graphics Controller
; Index register port
SET_RESET_INDEX equ 0 ;indexes of needed
ENABLE_SET_RESET_INDEX equ 1 ; Graphics Controller
BIT_MASK_INDEX equ 8 ; registers

;
; Stack frame.
;
EVGALineParms struc
dw ? ;pushed BP
dw ? ;pushed return address (make double
; word for far call)
X0 dw ? ;starting X coordinate of line
Y0 dw ? ;starting Y coordinate of line
X1 dw ? ;ending X coordinate of line
Y1 dw ? ;ending Y coordinate of line
Color db ? ;color of line
db ? ;dummy to pad to word size
EVGALineParms ends

;****************************************************************
; Line drawing macros. *
;****************************************************************

;
; Macro to loop through length of line, drawing each pixel in turn.
; Used for case of |DeltaX| >= |DeltaY|.
; |DeltaX|+1 points are drawn.
;
; Input:
; MOVE_LEFT: 1 if DeltaX < 0, 0 else
; AL: pixel mask for initial pixel
; BX: |DeltaX| (X distance between start and end points)
; DX: address of GC data register, with index register set to
; index of Bit Mask register
; SI: DeltaY (Y distance between start and end points)
; ES:DI: display memory address of byte containing initial
; pixel
;
; Output: none
;
LINE1 macro MOVE_LEFT
local LineLoop, MoveXCoord, NextPixel, Line1End
local MoveToNextByte, ResetBitMaskAccumulator
mov cx,bx ;# of pixels in line
jcxz Line1End ;done if there are no more pixels
; (there's always at least the one pixel
; at the start location)
shl si,1 ;DeltaY * 2
mov bp,si ;error term
sub bp,bx ;error term starts at DeltaY * 2 - DeltaX
shl bx,1 ;DeltaX * 2
sub si,bx ;DeltaY * 2 - DeltaX * 2 (used in loop)
add bx,si ;DeltaY * 2 (used in loop)
mov ah,al ;set aside pixel mask for initial pixel
; with AL (the pixel mask accumulator) set
; for the initial pixel
LineLoop:
;
; See if it's time to advance the Y coordinate yet.
;
and bp,bp ;see if error term is negative
js MoveXCoord ;yes, stay at the same Y coordinate
;
; Advance the Y coordinate, first writing all pixels in the current
; byte, then move the pixel mask either left or right, depending
; on MOVE_LEFT.
;
out dx,al ;set up bit mask for pixels in this byte
xchg byte ptr [di],al
;load latches and write pixels, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
add di,EVGA_SCREEN_WIDTH_IN_BYTES ;increment Y coordinate
add bp,si ;adjust error term back down
;
; Move pixel mask one pixel (either right or left, depending
; on MOVE_LEFT), adjusting display memory address when pixel mask wraps.
;
if MOVE_LEFT
rol ah,1 ;move pixel mask 1 pixel to the left
else
ror ah,1 ;move pixel mask 1 pixel to the right
endif
jnc ResetBitMaskAccumulator ;didn't wrap to next byte
jmp short MoveToNextByte ;did wrap to next byte
;
; Move pixel mask one pixel (either right or left, depending
; on MOVE_LEFT), adjusting display memory address and writing pixels
; in this byte when pixel mask wraps.
;
MoveXCoord:
add bp,bx ;increment error term & keep same
if MOVE_LEFT
rol ah,1 ;move pixel mask 1 pixel to the left
else
ror ah,1 ;move pixel mask 1 pixel to the right
endif
jnc NextPixel ;if still in same byte, no need to
; modify display memory yet
out dx,al ;set up bit mask for pixels in this byte.
xchg byte ptr [di],al
;load latches and write pixels, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
MoveToNextByte:
if MOVE_LEFT
dec di ;next pixel is in byte to left
else
inc di ;next pixel is in byte to right
endif
ResetBitMaskAccumulator:
sub al,al ;reset pixel mask accumulator
NextPixel:
or al,ah ;add the next pixel to the pixel mask
; accumulator
loop LineLoop
;
; Write the pixels in the final byte.
;
Line1End:
out dx,al ;set up bit mask for pixels in this byte.
xchg byte ptr [di],al
;load latches and write pixels, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
endm

;
; Macro to loop through length of line, drawing each pixel in turn.
; Used for case of DeltaX < DeltaY.
; |DeltaY|+1 points are drawn.
;
; Input:
; MOVE_LEFT: 1 if DeltaX < 0, 0 else
; AL: pixel mask for initial pixel
; BX: |DeltaX| (X distance between start and end points)
; DX: address of GC data register, with index register set to
; index of Bit Mask register
; SI: DeltaY (Y distance between start and end points)
; ES:DI: display memory address of byte containing initial
; pixel
;
; Output: none
;
LINE2 macro MOVE_LEFT
local LineLoop, MoveYCoord, ETermAction, Line2End
mov cx,si ;# of pixels in line
shl bx,1 ;DeltaX * 2
mov bp,bx ;error term
sub bp,si ;error term starts at DeltaX * 2 - DeltaY
shl si,1 ;DeltaY * 2
sub bx,si ;DeltaX * 2 - DeltaY * 2 (used in loop)
add si,bx ;DeltaX * 2 (used in loop)
;
; Set up initial bit mask & draw initial pixel.
;
out dx,al
xchg byte ptr [di],ah
;load latches and write pixel, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
jcxz Line2End ;done if there are no more pixels
; (there's always at least the one pixel
; at the start location)
LineLoop:
;
; See if it's time to advance the X coordinate yet.
;
and bp,bp ;see if error term is negative
jns ETermAction ;no, advance X coordinate
add bp,si ;increment error term & keep same
jmp short MoveYCoord ; X coordinate
ETermAction:
;
; Move pixel mask one pixel (either right or left, depending
; on MOVE_LEFT), adjusting display memory address when pixel mask wraps.
;
if MOVE_LEFT
rol al,1
sbb di,0
else
ror al,1
adc di,0
endif
out dx,al ;set new bit mask
add bp,bx ;adjust error term back down
;
; Advance Y coordinate.
;
MoveYCoord:
add di,EVGA_SCREEN_WIDTH_IN_BYTES
;
; Write the next pixel.
;
xchg byte ptr [di],ah
;load latches and write pixel, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
;
loop LineLoop
Line2End:
endm

;****************************************************************
; Line drawing routine. *
;****************************************************************

public _EVGALine
_EVGALine proc near
push bp
mov bp,sp
push si ;preserve register variables
push di
push ds
;
; Point DS to display memory.
;
mov ax,EVGA_SCREEN_SEGMENT
mov ds,ax
;
; Set the Set/Reset and Set/Reset Enable registers for
; the selected color.
;
mov dx,GC_INDEX
mov al,SET_RESET_INDEX
out dx,al
inc dx
mov al,[bp+Color]
out dx,al
dec dx
mov al,ENABLE_SET_RESET_INDEX
out dx,al
inc dx
mov al,0ffh
out dx,al
;
; Get DeltaY.
;
mov si,[bp+Y1] ;line Y start
mov ax,[bp+Y0] ;line Y end, used later in
;calculating the start address
sub si,ax ;calculate DeltaY
jns CalcStartAddress ;if positive, we're set
;
; DeltaY is negative -- swap coordinates so we're always working
; with a positive DeltaY.
;
mov ax,[bp+Y1] ;set line start to Y1, for use
; in calculating the start address
mov dx,[bp+X0]
xchg dx,[bp+X1]
mov [bp+X0],dx ;swap X coordinates
neg si ;convert to positive DeltaY
;
; Calculate the starting address in display memory of the line.
; Hardwired for a screen width of 80 bytes.
;
CalcStartAddress:
shl ax,1 ;Y0 * 2 ;Y0 is already in AX
shl ax,1 ;Y0 * 4
shl ax,1 ;Y0 * 8
shl ax,1 ;Y0 * 16
mov di,ax
shl ax,1 ;Y0 * 32
shl ax,1 ;Y0 * 64
add di,ax ;Y0 * 80
mov dx,[bp+X0]
mov cl,dl ;set aside lower 3 bits of column for
and cl,7 ; pixel masking
shr dx,1
shr dx,1
shr dx,1 ;get byte address of column (X0/8)
add di,dx ;offset of line start in display segment
;
; Set up GC Index register to point to the Bit Mask register.
;
mov dx,GC_INDEX
mov al,BIT_MASK_INDEX
out dx,al
inc dx ;leave DX pointing to the GC Data register
;
; Set up pixel mask (in-byte pixel address).
;
mov al,80h
shr al,cl
;
; Calculate DeltaX.
;
mov bx,[bp+X1]
sub bx,[bp+X0]
;
; Handle correct one of four octants.
;
js NegDeltaX
cmp bx,si
jb Octant1
;
; DeltaX >= DeltaY >= 0.
;
LINE1 0
jmp EVGALineDone
;
; DeltaY > DeltaX >= 0.
;
Octant1:
LINE2 0
jmp short EVGALineDone
;
NegDeltaX:
neg bx ;|DeltaX|
cmp bx,si
jb Octant2
;
; |DeltaX| >= DeltaY and DeltaX < 0.
;
LINE1 1
jmp short EVGALineDone
;
; |DeltaX| < DeltaY and DeltaX < 0.
;
Octant2:
LINE2 1
;
EVGALineDone:
;
; Restore EVGA state.
;
mov al,0ffh
out dx,al ;set Bit Mask register to 0ffh
dec dx
mov al,ENABLE_SET_RESET_INDEX
out dx,al
inc dx
sub al,al
out dx,al ;set Enable Set/Reset register to 0
;
pop ds
pop di
pop si
pop bp
ret
_EVGALine endp

_TEXT ends
end


  3 Responses to “Category : Files from Magazines
Archive   : PJ-VGA.ZIP
Filename : EVGALINE.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/