; Tenie Remmel's Graphics library

Ideal
Jumps

Model Tiny
P186
CodeSeg
Org 0100h

Start:

MinX        dw 0
MinY        dw 0
MaxX        dw 359
MaxY        dw 239
FontPtr     dd 0
FontWidth   db 8
FontHeight  db 8
Color       db 15

Org 0110h

ProcTable   dw offset Setup
            dw offset SetPixel
            dw offset GetPixel
            dw offset ClearScreen
            dw offset ClearViewport
            dw offset Line
            dw offset Rectangle
            dw offset FillRectangle
            dw offset OutChar
            dw offset OutStr
            dw offset GetImage
            dw offset PutImage

CurVidSeg   dw 0A000h

;**************************** Setup -- Sets up 360x240x256 mode

ALIGN 16

Proc        Setup

            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

EndP        Setup

;**************************** 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 Start
