Metropoli BBS
VIEWER: l13x.asm MODE: TEXT (LATIN1)
;**********************************************************;
;* ------< 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
[ RETURN TO DIRECTORY ]