;**********************************************************;
;* ------< VGA GRAPHICS LIBRARY - BY TENIE REMMEL >------ *;
;* *;
;* L13X.ASM - This is a highly optimized graphics library *;
;* for the 360-width ModeX family of modes. It is quite *;
;* complete, and is also small (2« K). This is intended *;
;* for Tiny and Small model ASM programs. *;
;* *;
;* This library is public domain. However, if you intend *;
;* to distribute this source code, you MUST retain this *;
;* header in its original form. *;
;**********************************************************;
;See lines 37 and 116 for instructions on how to modify
;this library for a vertical height other than 240.
Ideal
Jumps
Public InitGraph,SetPixel,GetPixel
Public ClearScreen,ClearViewport
Public Line,Rectangle
Public FillRectangle,OutChar
Public OutStr,GetImage,PutImage
Public MinX,MinY,MaxX,MaxY
Public FontPtr,FontWidth
Public FontHeight,Color
Model Tiny
P186
CodeSeg
MinX dw 0
MinY dw 0
MaxX dw 359
MaxY dw 239 ;This value must be Lines - 1
FontPtr dd 0
FontWidth db 8
FontHeight db 8
Color db 15
CurVidSeg dw 0A000h
;**************************** InitGraph -- Sets up 360x240x256 mode
ALIGN 16
Proc InitGraph
pusha ;Save registers
push es
mov ax,13h ;Set 320x200x256x1 mode
int 10h
mov ax,1130h ;Get pointer to
mov bh,3 ;ROM 8x8 font
int 10h
mov [word cs:FontPtr],bp ;Save this pointer
mov [word cs:FontPtr+2],es
cli ;Disable interrupts
mov dx,03C4h ;Sequencer port
mov ax,0100h ;Synchronous reset
out dx,ax
mov ax,0604h ;Disable Chain-4 Mode
out dx,ax
mov dx,03C2h ;Misc Output port
lea si,[CRTdata] ;Point to data
segcs outsb ;Output byte
mov dx,03C4h ;Sequencer port
mov ax,0300h ;Restart Sequencer
out dx,ax
mov ax,0F02h ;Enable all planes
out dx,ax ;(for clearscreen)
mov dx,03D4h ;CRTC port
segcs lodsw ;Load count
mov cx,ax ;Put in CX
segcs rep outsw ;Output data
mov es,[cs:CurVidSeg] ;ES = video memory
xor di,di ;Zero DI, AX
xor ax,ax
mov cx,8000h ;Clear the screen
rep stosw
sti ;Enable interrupts
pop es ;Restore registers
popa
Dummy: ret ;Return
CRTdata db 0E7h ;480 scanlines, 28 MHz
dw 18
dw 02C11h ;De-protect regs
dw 06B00h ;Horizontal Total
dw 05901h ;Horizontal Display End
dw 05A02h ;Horizontal Start Blanking
dw 08E03h ;Horizontal End Blanking
dw 05E04h ;Horizontal Start Retrace
dw 08A05h ;Horizontal End Retrace
dw 03E07h ;Vertical Overflow
dw 00D06h ;Vertical Total
dw 0DF12h ;Vertical Display End
dw 0EA10h ;Vertical Start Blanking
dw 0AC11h ;Vertical End Blanking
dw 0E715h ;Vertical Start Retrace
dw 00616h ;Vertical End Retrace
dw 04109h ;Pixel Height
dw 02D13h ;Memory Allocation
dw 00014h ;Disable Doubleword Mode
dw 0E317h ;Enable Byte Mode
;The preceding CRTC parameters must be changed
;for a different vertical height. Inquire about
;heights 160, 175, 200, 240, 270, 282, 350, 400,
;480, 540, 564 if you wish.
EndP InitGraph
;**************************** SetPixel -- plots a pixel
ALIGN 16
Proc SetPixel
;Supply CX=x, DX=y, AL=color
cmp cx,[cs:MinX] ;X < MinX?
jl sp_done
cmp dx,[cs:MinY] ;Y < MinY?
jl sp_done
cmp cx,[cs:MaxX] ;X > MaxX?
jg sp_done
cmp dx,[cs:MaxY] ;Y > MaxY?
jg sp_done
pusha ;Save registers
push ds
mov ds,[cs:CurVidSeg] ;DS = video memory
mov si,dx ;SI = y, BX = CX = x
mov bx,cx
and cl,3 ;CL = bit plane
mov ch,al ;Save color in CH
mov ah,1 ;AH = plane mask
shl ah,cl
shr bx,2 ;BX = X offset
mov dx,03C4h ;Sequencer port
mov al,2 ;Set Map Mask
out dx,ax
mov ax,90 ;Multiply Y by 90
mul si
add bx,ax ;BX = offset
mov [bx],ch ;Put byte in memory
pop ds ;Restore registers
popa
sp_done: ret ;Return
EndP SetPixel
;**************************** GetPixel -- reads a pixel
ALIGN 16
Proc GetPixel
;Supply CX=x, DX=y, returns AL=color
cmp cx,[cs:MinX] ;X < MinX?
jl gp_bad
cmp dx,[cs:MinY] ;Y < MinY?
jl gp_bad
cmp cx,[cs:MaxX] ;X > MaxX?
jg gp_bad
cmp dx,[cs:MaxY] ;Y > MaxY?
jg gp_bad
push bx cx dx si ;Save registers
push ds ax
mov ds,[cs:CurVidSeg] ;DS = video memory
mov si,dx ;SI = y, BX = CX = x
mov bx,cx
mov ah,bl
and ah,3 ;AH = bit plane
shr bx,2 ;BX = X offset
mov dx,03CEh ;Graphics port
mov al,4 ;Set Read Plane
out dx,ax
mov ax,90 ;Multiply Y by 90
mul si
add bx,ax ;BX = offset
pop ax ;Restore AX
mov al,[bx] ;Get byte
pop ds si dx cx bx ;Restore registers
ret ;Return
gp_bad: mov al,0 ;Bad value, return 0
ret
EndP GetPixel
;**************************** ClearScreen -- clears the screen
ALIGN 16
Proc ClearScreen
pusha ;Save registers
push ds
mov ds,[cs:CurVidSeg] ;DS = video memory
mov dx,03C4h ;Sequencer port
mov ax,0F02h ;Set Map Mask
out dx,ax
xor ax,ax ;Clear AX
xor di,di ;Clear DI
mov si,10 ;SI = 10
mov cx,1080 ;1080 iterations
ALIGN 16 ;Force paragraph alignment
clear_loop: mov [di],ax ;Clear 10 bytes with DI
mov [di+2],ax
mov [di+4],ax
mov [di+6],ax
mov [di+8],ax
add di,20
mov [si],ax ;Clear 10 bytes with SI
mov [si+2],ax
mov [si+4],ax
mov [si+6],ax
mov [si+8],ax
add si,20
dec cx ;Loop back
jnz clear_loop
pop ds ;Restore registers
popa
ret ;Return
EndP ClearScreen
;**************************** ClearViewport -- clears the viewport
ALIGN 16
Proc ClearViewport
pusha ;Push registers
push es
mov es,[cs:CurVidSeg] ;ES = video memory
mov ax,[cs:MinX] ;Get corners of viewport
mov bx,[cs:MinY] ;in AX, BX, CX, DX
mov cx,[cs:MaxX]
mov dx,[cs:MaxY]
sub dx,bx ;DX = height
mov di,ax ;Put X1, X2 in DI, SI
mov si,cx
shr di,2 ;Divide them by 4
shr si,2
push ax ;Save AX
mov al,90 ;Multiply Y by 90
mul bl
add di,ax ;Add to X1
add si,ax ;Add to X2
pop ax ;Restore AX
and ax,3 ;AX = X1 mod 4
and cx,3 ;CX = Y1 mod 4
mov bx,0101h ;BL = 1, BH = 1
inc cl ;Adjust shift count
shl bh,cl ;Calculate map mask
dec bh ;for the right side
xchg ax,cx ;Switch AX, CX
shl bl,cl ;for the left side
neg bl ;which is a little
add bl,16 ;more complicated
cmp si,di ;Thin rectangle?
je cv_thin ;Jump if yes
cv_loop: pusha ;Save all registers
mov dx,03C4h ;Sequencer port
mov al,2 ;Set Map Mask
mov ah,bl ;Left side
out dx,ax ;Send command
xor al,al ;AL = 0 (black)
stosb ;Store byte
mov al,2 ;Set Map Mask
mov ah,bh ;Right side
out dx,ax ;Send command
mov [byte es:si],0 ;Store byte
mov ax,0F02h ;Enable all planes
out dx,ax ;Send command
mov al,ch ;AL = color
mov ah,al ;AH = color
mov cx,si ;CX = distance
sub cx,di
xor dx,dx ;Clear DX
shr cx,1 ;CX = words
adc dx,0 ;DX = odd byte
rep stosw ;Store by words
mov cx,dx ;Get odd byte
rep stosb ;Store odd byte
popa ;Restore registers
add di,90 ;Next line
add si,90
dec dx ;Dec line counter
jns cv_loop ;Loop back
jmp cv_done ;Return
cv_thin: and bl,bh ;AND masks together
push dx ;Save DX
mov dx,03C4h ;Sequencer port
mov al,2 ;Set Map Mask
mov ah,bl ;Short line
out dx,ax ;Send command
pop dx ;Restore DX
xor al,al ;AL = 0 (black)
cvt_loop: stosb ;Store byte
add di,89 ;Next line
dec dx ;Dec line counter
jns cvt_loop ;Loop back
cv_done: pop es ;Restore registers
popa
ret ;Exit
EndP ClearViewport
;**************************** Line -- draws a line
ALIGN 16
Proc Line
;Supply AX=x1, BX=y1, CX=x2, DX=y2
pusha ;Push registers
push ds es
mov es,[cs:CurVidSeg] ;ES = video memory
push cs ;DS = CS
pop ds
cmp bx,dx ;Is it horizontal?
je HorizLine
cmp ax,cx ;Vertical?
je VertLine
cmp ax,[MaxX] ;X1 > MaxX?
jg l_x1g
cmp ax,[MinX] ;X1 < MinX?
jl l_x1l
l_cx2g: cmp cx,[MaxX] ;X2 > MaxX?
jg l_x2g
l_cx2l: cmp cx,[MinX] ;X2 < MinX?
jl l_x2l
l_checky: cmp bx,[MaxY] ;Y1 > MaxY?
jg l_y1g
cmp bx,[MinY] ;Y1 < MinY?
jl l_y1l
l_cy2g: cmp dx,[MaxY] ;Y2 > MaxY?
jg l_y2g
l_cy2l: cmp dx,[MinY] ;Y2 < MinY?
jl l_y2l
jmp l_cont1
l_x1g: cmp cx,[MaxX] ;X1 > MaxX.
jg l_done ;Both > MaxX?
push cx dx ;Save x2, y2
sub cx,ax
sub dx,bx ;Calculate the intersection:
sub ax,[MaxX]
neg ax ; (MaxX - x1) * (y2 - y1)
imul dx ;y1 + -----------------------
idiv cx ; (x2 - x1)
add bx,ax
pop dx cx ;Restore x2, y2
mov ax,[MaxX] ;x1 = MaxX
jmp l_cx2l
l_x1l: cmp cx,[MinX] ;X1 < MinX.
jl l_done ;Both < MinX?
push cx dx ;Save x2, y2
sub cx,ax
sub dx,bx ;Calculate the intersection:
sub ax,[MinX]
neg ax ; (MinX - x1) * (y2 - y1)
imul dx ;y1 + -----------------------
idiv cx ; (x2 - x1)
add bx,ax
pop dx cx ;Restore x2, y2
mov ax,[MinX] ;x1 = MinX
jmp l_cx2g
l_x2g: push ax bx ;Save x1, y1
sub cx,ax
sub dx,bx ;Calculate the intersection:
sub ax,[MaxX]
neg ax ; (MaxX - x1) * (y2 - y1)
imul dx ;y1 + -----------------------
idiv cx ; (x2 - x1)
add bx,ax
mov dx,bx
pop bx ax ;Restore x1, y1
mov cx,[MaxX] ;x2 = MaxX
jmp l_checky
l_x2l: push ax bx ;Save x1, y1
sub cx,ax
sub dx,bx ;Calculate the intersection:
sub ax,[MinX]
neg ax ; (MinX - x1) * (y2 - y1)
imul dx ;y1 + -----------------------
idiv cx ; (x2 - x1)
add bx,ax
mov dx,bx
pop bx ax ;Restore x1, y1
mov cx,[MinX] ;x2 = MinX
jmp l_checky
l_y1g: cmp dx,[MaxY] ;Y1 > MaxY.
jg l_done ;Both > MaxY?
push cx dx ;Save x2, y2
xchg ax,bx
xchg cx,dx
sub cx,ax ;Calculate the intersection:
sub dx,bx
sub ax,[MaxY] ; (MaxY - y1) * (x2 - x1)
neg ax ;x1 + -----------------------
imul dx ; (y2 - y1)
idiv cx
add ax,bx
pop dx cx ;Restore x2, y2
mov bx,[MaxY] ;y1 = MaxX
jmp l_cy2l
l_y1l: cmp dx,[MinY] ;Y1 < MinY.
jl l_done ;Both < MinY?
push cx dx ;Save x2, y2
xchg ax,bx
xchg cx,dx
sub cx,ax ;Calculate the intersection:
sub dx,bx
sub ax,[MinY] ; (MinY - y1) * (x2 - x1)
neg ax ;x1 + -----------------------
imul dx ; (y2 - y1)
idiv cx
add ax,bx
pop dx cx ;Restore x2, y2
mov bx,[MinY] ;y1 = MinY
jmp l_cy2g
l_y2g: push ax bx ;Save x1, y1
xchg ax,bx
xchg cx,dx
sub cx,ax ;Calculate the intersection:
sub dx,bx
sub ax,[MaxY] ; (MaxY - y1) * (x2 - x1)
neg ax ;x1 + -----------------------
imul dx ; (y2 - y1)
idiv cx
add ax,bx
mov cx,ax
pop bx ax ;Restore x1, y1
mov dx,[MaxY] ;y2 = MaxY
jmp l_cont1
l_y2l: push ax bx ;Save x1, y1
xchg ax,bx
xchg cx,dx
sub cx,ax ;Calculate the intersection:
sub dx,bx
sub ax,[MinY] ; (MinY - y1) * (x2 - x1)
neg ax ;x1 + -----------------------
imul dx ; (y2 - y1)
idiv cx
add ax,bx
mov cx,ax
pop bx ax ;Restore x1, y1
mov dx,[MinY] ;y2 = MinY
l_cont1: push ax bx ;Save starting position
sub cx,ax ;get X and Y distance
mov [xinc_str],1 ;Increments are 1
mov [yinc_str],1
jge l_xplus ;X positive?
neg [xinc_str] ;If not, negate
neg cx
l_xplus: sub dx,bx ;get Y distance
jge l_yplus ;Y positive?
neg [yinc_str] ;If not, negate
neg dx
l_yplus: mov ax,[xinc_str] ;Copy into the
mov [xinc_diag],ax ;diagonal values
mov ax,[yinc_str]
mov [yinc_diag],ax
cmp cx,dx ;X < Y ?
jle l_ystr ;If so, go vertical
mov [yinc_str],0 ;Otherwise go horizontal
jmp l_cont2 ;At l_cont2: xinc, yinc_str are
l_ystr: mov [xinc_str],0 ;the straight increments and xinc,
xchg cx,dx ;yinc_diag are the diag. increments
l_cont2: mov di,dx ;cx, dx are straight, perpendicular
mov [deltav_str],di ;Get dev. increments and start value
sub di,cx ;Str. incr = perp_dist
mov [deltav_diag],di ;Perp. incr = perp_dist - str_dist
add di,dx ;Starting value =
sar di,1 ;(2 * perp_dist - str_dist) / 2
mov si,di ;Put starting dev. in SI
mov di,cx ;Put length in DI
inc di ;One more point (ends)
pop dx cx ;Get starting point in (cx,dx)
ALIGN 16 ;Force paragraph alignment
l_loop: push cx dx ;Save registers
mov bp,si
mov si,dx ;SI = y, BX = CX = x
mov bx,cx
and cl,3 ;CL = bit plane
mov ah,1 ;AH = plane mask
shl ah,cl
shr bx,2 ;BX = X offset
mov dx,03C4h ;Sequencer port
mov al,2 ;Set Map Mask
out dx,ax
mov ax,90 ;Multiply Y by 90
mul si
add bx,ax ;BX = offset
mov ch,[Color] ;CH = color
mov [es:bx],ch ;Store byte
pop dx cx ;Restore registers
mov si,bp
test si,si ;Decide whether to go straight
jg l_godiag ;or diagonal
l_gostr: add cx,[xinc_str] ;Go straight
add dx,[yinc_str]
add si,[deltav_str]
jmp l_loopback
l_godiag: add cx,[xinc_diag] ;Go diagonal
add dx,[yinc_diag]
add si,[deltav_diag]
l_loopback: dec di ;Decrement counter
jnz l_loop ;Loop back
l_done: pop es ds ;Restore registers
popa
ret ;Return
ALIGN 16 ;Force paragraph alignment
HorizLine: cmp bx,[cs:MaxY] ;Y > MaxY?
jg l_done
cmp bx,[cs:MinY] ;Y < MinY?
jl l_done
cmp ax,[cs:MaxX] ;X1 > MaxX?
jg lh_x1g
cmp ax,[cs:MinX] ;X1 < MinX?
jl lh_x1l
lh_cx2g: cmp cx,[cs:MaxX] ;X2 > MaxX?
jg lh_x2g
lh_cx2l: cmp cx,[cs:MinX] ;X2 < MinX?
jl lh_x2l
jmp lh_cont ;Continue
lh_x1g: cmp cx,[cs:MaxX] ;X1 > MaxX.
jg l_done ;Both > MaxX?
mov ax,[cs:MaxX] ;No, fix X1
jmp lh_cx2l
lh_x1l: cmp cx,[cs:MinX] ;X1 < MinX.
jl l_done ;Both < MinX?
mov ax,[cs:MinX] ;No, fix X1
jmp lh_cx2g
lh_x2g: mov cx,[cs:MaxX] ;X2 > MaxX, fix X2
jmp lh_cont
lh_x2l: mov cx,[cs:MinX] ;X2 < MinX, fix X2
lh_cont: cmp cx,ax ;X1 > X2?
jge lh_xplus
xchg cx,ax ;Switch them
lh_xplus: mov di,ax ;Put X1, X2 in DI, SI
mov si,cx
shr di,2 ;Divide them by 4
shr si,2
push ax ;Save AX
mov al,90 ;Multiply Y by 90
mul bl
add di,ax ;Add to X1
add si,ax ;Add to X2
pop ax ;Restore AX
and ax,3 ;AX = X1 mod 4
and cx,3 ;CX = Y1 mod 4
mov bx,0101h ;BL = 1, BH = 1
inc cl ;Adjust shift count
shl bh,cl ;Calculate map mask
dec bh ;for the right side
xchg ax,cx ;Switch AX, CX
shl bl,cl ;for the left side
neg bl ;which is a little
add bl,16 ;more complicated
cmp si,di ;Short line?
je lh_short ;Jump if yes
mov dx,03C4h ;Sequencer port
mov al,2 ;Set Map Mask
mov ah,bl ;Left side
out dx,ax ;Send command
mov al,[Color] ;AL = color
mov ch,al ;CH = color
stosb ;Store byte
mov al,2 ;Set Map Mask
mov ah,bh ;Right side
out dx,ax ;Send command
mov [es:si],ch ;Store byte
mov ax,0F02h ;Enable all planes
out dx,ax ;Send command
mov al,ch ;AL = color
mov ah,al ;AH = color
mov cx,si ;CX = distance
sub cx,di
xor dx,dx ;Clear DX
shr cx,1 ;CX = words
adc dx,0 ;DX = odd byte
rep stosw ;Store by words
mov cx,dx ;Get odd byte
rep stosb ;Store odd byte
jmp l_done ;Return
lh_short: and bl,bh ;AND masks together
mov dx,03C4h ;Sequencer port
mov al,2 ;Set Map Mask
mov ah,bl ;Short line
out dx,ax ;Send command
mov al,[Color] ;AL = color
stosb ;Store byte
jmp l_done ;Return
ALIGN 16 ;Force paragraph alignment
VertLine: cmp ax,[cs:MaxX] ;X > MaxX?
jg l_done
cmp ax,[cs:MinX] ;X < MinX?
jl l_done
cmp bx,[cs:MaxY] ;Y1 > MaxY?
jg lv_y1g
cmp bx,[cs:MinY] ;Y1 < MinY?
jl lv_y1l
lv_cy2g: cmp dx,[cs:MaxY] ;Y2 > MaxY?
jg lv_y2g
lv_cy2l: cmp dx,[cs:MinY] ;Y2 < MinY?
jl lv_y2l
jmp lv_cont ;Continue
lv_y1g: cmp dx,[cs:MaxY] ;Y1 > MaxY.
jg l_done ;Both > MaxY?
mov bx,[cs:MaxY] ;No, fix Y1
jmp lv_cy2l
lv_y1l: cmp dx,[cs:MinY] ;Y1 < MinY.
jl l_done ;Both < MinY?
mov bx,[cs:MinY] ;No, fix Y1
jmp lv_cy2g
lv_y2g: mov dx,[cs:MaxY] ;Y2 > MaxY, fix Y2
jmp lv_cont
lv_y2l: mov dx,[cs:MinY] ;Y2 < MinY, fix Y2
lv_cont: sub dx,bx ;Get Y distance
jge lv_yplus ;Negative?
add bx,dx ;BX = previous DX
neg dx ;Make it positive
lv_yplus: push ax ;Save X
mov al,90 ;AX = Y * 90
mul bl
pop bx ;BX = X
mov cl,bl ;CL = low 2 bits of X
and cl,3
shr bx,2 ;X = X / 4
add ax,bx ;AX = X + Y * 90
mov di,ax ;Offset in DI
mov ah,1 ;AH = 1
shl ah,cl ;AH = bit plane mask
mov al,2 ;AL = 2: Set Map Mask
push dx ;Save DX
mov dx,03C4h ;Sequencer Port
out dx,ax ;Send command
pop dx ;Restore DX
mov al,[Color] ;AL = color
ALIGN 16 ;Force paragraph alignment
lv_loop: stosb ;Write pixel
add di,89 ;Next line
dec dx ;Done?
jns lv_loop ;Loop back
jmp l_done ;Return
EVEN ;Force word alignment
deltav_str dw 0 ;Deviation change straight
deltav_diag dw 0 ;Deviation change diagonal
xinc_str dw 0 ;X increment straight
xinc_diag dw 0 ;X increment diagonal
yinc_str dw 0 ;Y increment straight
yinc_diag dw 0 ;Y increment diagonal
EndP Line
;**************************** Rectangle -- draws a rectangle
ALIGN 16
Proc Rectangle
;Supply AX=x1, BX=y1, CX=x2, DX=y2
push si ;Save SI
mov si,cx ;Save X2
mov cx,ax ;X2 = X1
call Line ;Draw top side
mov cx,si ;Restore X2
mov si,ax ;Save X1
mov ax,cx ;X1 = X2
call Line ;Draw bottom side
mov ax,si ;Restore X1
mov si,dx ;Save Y2
mov dx,bx ;Y2 = Y1
call Line ;Draw left side
mov dx,si ;Restore Y2
mov si,bx ;Save Y1
mov bx,dx ;Y1 = Y2
call Line ;Draw right side
mov bx,si ;Restore Y2
pop si ;Restore SI
ret ;Return
EndP Rectangle
;**************************** FillRectangle -- draws a filled rectangle
ALIGN 16
Proc FillRectangle
;Supply AX=x1, BX=y1, CX=x2, DX=y2
pusha ;Push registers
push es
mov es,[cs:CurVidSeg] ;ES = video memory
cmp ax,[cs:MaxX] ;X1 > MaxX?
jg fr_x1g
cmp ax,[cs:MinX] ;X1 < MinX?
jl fr_x1l
fr_cx2g: cmp cx,[cs:MaxX] ;X2 > MaxX?
jg fr_x2g
fr_cx2l: cmp cx,[cs:MinX] ;X2 < MinX?
jl fr_x2l
fr_checky: cmp bx,[cs:MaxY] ;Y1 > MaxY?
jg fr_y1g
cmp bx,[cs:MinY] ;Y1 < MinY?
jl fr_y1l
fr_cy2g: cmp dx,[cs:MaxY] ;Y2 > MaxY?
jg fr_y2g
fr_cy2l: cmp dx,[cs:MinY] ;Y2 < MinY?
jl fr_y2l
jmp fr_cont ;Continue
fr_x1g: cmp cx,[cs:MaxX] ;X1 > MaxX.
jg fr_done ;Both > MaxX?
mov ax,[cs:MaxX] ;No, fix X1
jmp fr_cx2l
fr_x1l: cmp cx,[cs:MinX] ;X1 < MinX.
jl fr_done ;Both < MinX?
mov ax,[cs:MinX] ;No, fix X1
jmp fr_cx2g
fr_x2g: mov cx,[cs:MaxX] ;X2 > MaxX, fix X2
jmp fr_checky
fr_x2l: mov cx,[cs:MinX] ;X2 < MinX, fix X2
jmp fr_checky
fr_y1g: cmp dx,[cs:MaxY] ;Y1 > MaxY.
jg fr_done ;Both > MaxY?
mov bx,[cs:MaxY] ;No, fix Y1
jmp fr_cy2l
fr_y1l: cmp dx,[cs:MinY] ;Y1 < MinY.
jl fr_done ;Both < MinY?
mov bx,[cs:MinY] ;No, fix Y1
jmp fr_cy2g
fr_y2g: mov dx,[cs:MaxY] ;Y2 > MaxY, fix Y2
jmp fr_cont
fr_y2l: mov dx,[cs:MinY] ;Y2 < MinY, fix Y2
fr_cont: mov si,dx ;Get distances in SI, DI
mov di,cx
sub di,ax ;Is the X distance negative?
jge fr_xplus ;Switch points and negate
neg di
xchg cx,ax
fr_xplus: sub si,bx ;Same thing for Y
jge fr_yplus
neg si
xchg dx,bx
fr_yplus: sub dx,bx ;DX = height
mov di,ax ;Put X1, X2 in DI, SI
mov si,cx
shr di,2 ;Divide them by 4
shr si,2
push ax ;Save AX
mov al,90 ;Multiply Y by 90
mul bl
add di,ax ;Add to X1
add si,ax ;Add to X2
pop ax ;Restore AX
and ax,3 ;AX = X1 mod 4
and cx,3 ;CX = Y1 mod 4
mov bx,0101h ;BL = 1, BH = 1
inc cl ;Adjust shift count
shl bh,cl ;Calculate map mask
dec bh ;for the right side
xchg ax,cx ;Switch AX, CX
shl bl,cl ;for the left side
neg bl ;which is a little
add bl,16 ;more complicated
cmp si,di ;Thin rectangle?
je fr_thin ;Jump if yes
fr_loop: pusha ;Save all registers
mov dx,03C4h ;Sequencer port
mov al,2 ;Set Map Mask
mov ah,bl ;Left side
out dx,ax ;Send command
mov al,[Color] ;AL = color
mov ch,al ;CH = color
stosb ;Store byte
mov al,2 ;Set Map Mask
mov ah,bh ;Right side
out dx,ax ;Send command
mov [es:si],ch ;Store byte
mov ax,0F02h ;Enable all planes
out dx,ax ;Send command
mov al,ch ;AL = color
mov ah,al ;AH = color
mov cx,si ;CX = distance
sub cx,di
xor dx,dx ;Clear DX
shr cx,1 ;CX = words
adc dx,0 ;DX = odd byte
rep stosw ;Store by words
mov cx,dx ;Get odd byte
rep stosb ;Store odd byte
popa ;Restore registers
add di,90 ;Next line
add si,90
dec dx ;Dec line counter
jns fr_loop ;Loop back
jmp fr_done ;Return
fr_thin: and bl,bh ;AND masks together
push dx ;Save DX
mov dx,03C4h ;Sequencer port
mov al,2 ;Set Map Mask
mov ah,bl ;Short line
out dx,ax ;Send command
pop dx ;Restore DX
mov al,[Color] ;AL = color
frt_loop: stosb ;Store byte
add di,89 ;Next line
dec dx ;Dec line counter
jns frt_loop ;Loop back
fr_done: pop es ;Restore registers
popa
ret ;Exit
EndP FillRectangle
;**************************** OutChar -- outputs a character
ALIGN 16
Proc OutChar
;Supply AL=char, CX=x, DX=y, returns CX=x+width
pusha ;Save registers
push ds es
mov es,[cs:CurVidSeg] ;ES = video memory
lds si,[cs:FontPtr] ;DS:SI = font
cmp cx,[cs:MinX] ;X < MinX?
jl oc_bad
cmp dx,[cs:MinY] ;Y < MinY?
jl oc_bad
add cl,[cs:FontWidth] ;Lower right corner
adc ch,0
add dl,[cs:FontHeight]
adc dh,0
cmp cx,[cs:MaxX] ;X > MaxX?
jg oc_bad
cmp dx,[cs:MaxY] ;Y > MaxY?
jg oc_bad
sub cl,[cs:FontWidth] ;Back to upper left
sbb ch,0
sub dl,[cs:FontHeight]
sbb dh,0
push ax cx ;Save AX, CX
and cl,3 ;Calculate map mask
mov ch,1
shl ch,cl
mov [cs:init_mask],ch ;Save mask
pop cx ;Restore CX
mov ax,90 ;Get offset, which is
mul dx ;x / 4 + 90 * y
shr cx,2
add cx,ax
mov di,cx ;Offset in DI
pop ax ;Restore AX
cmp [cs:FontWidth],6 ;6 wide?
je oc_6wide
cmp [cs:FontWidth],8 ;8 wide?
jne oc_bad ;Invalid width
oc_8wide: mov cl,[cs:FontHeight] ;CX = font height
xor ch,ch
mul cl ;Point to specific char
add si,ax
ALIGN 16 ;Force paragraph alignment
oc_8loop: mov bl,[si] ;Load byte
mov bh,80h
inc si
push cx di ;Save CX, DI
mov ah,[cs:init_mask] ;Get initial mask
mov al,2 ;Set Map Mask command
mov dx,03C4h ;Sequencer port
mov cx,8 ;8 wide
oc_8pxloop: test bl,bh ;Is bit set?
jz oc_8px1 ;Jump if not
out dx,ax ;Set sequencer
push ax ;Save AX
mov al,[cs:Color] ;Put color byte
mov [es:di],al ;on screen
pop ax
oc_8px1: shl ah,1 ;Next pixel
shr bh,1
test ah,16 ;Byte boundary?
jz oc_8pxlb ;Jump if not
mov ah,1 ;Next byte,
inc di ;mask = 0001
oc_8pxlb: dec cx ;Loop back
jnz oc_8pxloop
pop di cx ;Restore CX, DI
add di,90 ;Next line
dec cx ;Loop back
jnz oc_8loop
jmp oc_done
oc_6wide: mov cl,[cs:FontHeight] ;CX = font height
xor ch,ch
mul cl ;Point to specific char
add si,ax
ALIGN 16 ;Force paragraph alignment
oc_6loop: mov bl,[si] ;Load byte
mov bh,80h
inc si
push cx di ;Save CX, DI
mov ah,[cs:init_mask] ;Get initial mask
mov al,2 ;Set Map Mask command
mov dx,03C4h ;Sequencer port
mov cx,6 ;6 wide
oc_6pxloop: test bl,bh ;Is bit set?
jz oc_6px1 ;Jump if not
out dx,ax ;Set sequencer
push ax ;Save AX
mov al,[cs:Color] ;Put color byte
mov [es:di],al ;on screen
pop ax
oc_6px1: shl ah,1 ;Next pixel
shr bh,1
test ah,16 ;Byte boundary?
jz oc_6pxlb ;Jump if not
mov ah,1 ;Next byte,
inc di ;mask = 0001
oc_6pxlb: dec cx ;Loop back
jnz oc_6pxloop
pop di cx ;Restore CX, DI
add di,90 ;Next line
dec cx ;Loop back
jnz oc_6loop
oc_done: pop es ds ;Restore registers
popa
add cl,[cs:FontWidth] ;Advance X
adc ch,0
ret ;Return
oc_bad: pop es ds ;Restore registers
popa
ret ;Return
init_mask db 0
EndP OutChar
;**************************** OutStr -- outputs a string
ALIGN 16
Proc OutStr
;Supply DS:SI=string, CX=x, DX=y
pusha ;Save registers
os_loop: lodsb ;Load byte
test al,al ;Zero = done
jz os_done
mov bx,cx ;Save old X
call OutChar ;Output character
cmp cx,bx ;Did it work?
jne os_loop ;If yes, loop back
os_done: popa ;Restore registers
ret ;Return
EndP OutStr
;**************************** GetImage -- gets a rectangular image
ALIGN 16
Proc GetImage
;Supply DS:SI=buffer, AX=x1, BX=y1, CX=x2, DX=y2
pusha ;Save registers
push ds es
push ds ;ES:DI = DS:SI
pop es
mov di,si
mov ds,[cs:CurVidSeg] ;DS = video memory
xchg ax,cx ;CX = x, DX = y,
xchg bx,dx ;AX = width,
sub ax,cx ;BX = height
sub bx,dx
inc ax ;Adjust AX, BX
inc bx
push ax ;SI = offset
mov ax,90 ;(x + y * 90)
mul dx
mov bp,cx ;BP = horiz. position
shr cx,2
add cx,ax
pop ax
mov si,cx
stosw ;Store width
mov cx,ax ;CX = width
mov ax,bx ;AX = height
stosw ;Store height
mov dx,03CEh ;Graphics Controller Port
mov ax,bp ;Horiz. position in AH
mov ah,al
mov al,4 ;Set Read Plane command
and ah,3
mov [cs:gi_plane],ah ;gi_plane = starting plane
mov [cs:gi_cplane],0 ;gi_cplane = 0
gi_ploop: out dx,ax ;Send command
push ax bx cx si ;Save AX, BX, CX, SI
mov bp,si ;BP = SI
add cx,3 ; (CX + 3 - Plane)
sub cx,[cs:gi_cplane] ;CX = ----------------
shr cx,2 ; 4
mov [cs:gi_width],cx ;Save CX
gi_loop: mov cx,[cs:gi_width] ;Get width
shr cx,1 ;Divide by 2
rep movsw ;Move by words
test [cs:gi_width],1 ;Odd byte?
jz $+3 ;Jump if not
movsb ;Move odd byte
add bp,90 ;Next line
mov si,bp ;Offset in SI
dec bx ;Decrement counter
jnz gi_loop ;Loop back
pop si cx bx ax ;Restore registers
inc ah ;Next plane
cmp ah,4 ;Next byte too?
jne $+5 ;Jump if not
inc si ;Increment offset
xor ah,ah ;Plane 0
inc [cs:gi_cplane] ;Next plane
cmp ah,[cs:gi_plane] ;Back to start?
jne gi_ploop ;Jump if not
pop es ds ;Restore registers
popa
ret ;Return
EVEN ;Force word alignment
gi_width dw 0 ;Total width
gi_cplane dw 0 ;Current plane
gi_plane db 0 ;Starting plane
EndP GetImage
;**************************** PutImage -- puts a rectangular image
ALIGN 16
Proc PutImage
;Supply DS:SI=buffer, CX=x, DX=y
pusha ;Save registers
push ds es
mov es,[cs:CurVidSeg] ;ES = video memory
lodsw ;Load width
mov bx,ax ;Put it in CX
lodsw ;Load height
cmp cx,[cs:MinX] ;X < MinX?
jl pi_done
cmp dx,[cs:MinY] ;Y < MinY?
jl pi_done
add cx,bx ;Lower right corner
add dx,ax
cmp cx,[cs:MaxX] ;X > MaxX?
jg pi_done
cmp dx,[cs:MaxY] ;Y > MaxY?
jg pi_done
sub cx,bx ;Back to upper left
sub dx,ax
push ax ;DI = offset
mov ax,90 ;(x + y * 90)
mul dx
mov bp,cx ;BP = horiz. position
shr cx,2
add cx,ax
pop ax
mov di,cx
mov cx,bx ;CX = width
mov bx,ax ;BX = height
mov dx,03C4h ;Sequencer Port
mov al,2 ;Set Write Plane command
mov ah,11h ;AH = 11h
xchg cx,bp ;Switch with CX
and cl,3 ;CX = CX and 3
shl ah,cl ;AH = AH shl CX
mov cx,bp ;Restore CX
mov [cs:pi_plane],ah ;pi_plane = starting plane
mov [cs:pi_cplane],0 ;pi_cplane = 0
pi_ploop: out dx,ax ;Send command
push ax bx cx di ;Save AX, BX, CX, DI
mov bp,di ;BP = DI
add cx,3 ; (CX + 3 - Plane)
sub cx,[cs:pi_cplane] ;CX = ----------------
shr cx,2 ; 4
mov [cs:pi_width],cx ;Save CX
pi_loop: mov cx,[cs:pi_width] ;Get width
shr cx,1 ;Divide by 2
rep movsw ;Move by words
test [cs:pi_width],1 ;Odd byte?
jz $+3 ;Jump if not
movsb ;Move odd byte
add bp,90 ;Next line
mov di,bp ;Offset in SI
dec bx ;Decrement counter
jnz pi_loop ;Loop back
pop di cx bx ax ;Restore registers
shl ah,1 ;Next plane
jnc $+5 ;Next byte too?
inc di ;Increment offset
mov ah,11h ;Plane 0
inc [cs:pi_cplane] ;Next plane
cmp ah,[cs:pi_plane] ;Back to start?
jne pi_ploop ;Jump if not
pi_done: pop es ds ;Restore registers
popa
ret ;Return
EVEN ;Force word alignment
pi_width dw 0 ;Total width
pi_cplane dw 0 ;Current plane
pi_plane db 0 ;Starting plane
EndP PutImage
End