; Tenie Remmel's Graphics library

Ideal
Jumps
Warn ICG
NoWarn ALN

Model Tiny
P186
CodeSeg
Org 0100h

Start:

MinX        dw 0
MinY        dw 0
MaxX        dw 319
MaxY        dw 199
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

;**************************** Setup -- Sets up 320x200x256x1 mode

ALIGN 16

Proc        Setup

            pusha                  ;Save registers
            push es
            mov ax,13h             ;Video mode 13h
            int 10h                ;Set video mode
            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
            pop es                 ;Restore registers
            popa
Dummy:      ret                    ;Return

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
            push cx di ds          ;Save registers
            push 0A000h            ;DS = video memory
            pop ds
            mov di,dx              ;DI = DX * 320...
            shl di,2
            add di,dx
            shl di,6
            add di,cx              ;Add in column
            mov [di],al            ;Store byte
            pop ds di cx           ;Restore registers
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 si es             ;Save registers
            push 0A000h            ;ES = video memory
            pop es
            mov si,dx              ;SI = DX * 320...
            shl si,2
            add si,dx
            shl si,6
            add si,cx              ;Add in column
            seges lodsb            ;Read byte
            pop es si              ;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
            push 0A000h            ;DS = video memory
            pop ds
            xor ax,ax              ;Clear AX
            xor di,di              ;Clear DI
            mov si,10              ;SI = 10
            mov cx,3200            ;3200 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
            push 0A000h            ;ES = video memory
            pop es
            mov ax,[cs:MinX]       ;Get corners of viewport
            mov bx,[cs:MinY]       ;in AX, BX, CX, DX
            mov di,[cs:MaxX]
            mov si,[cs:MaxY]
            sub di,ax              ;Get distances in SI, DI
            sub si,bx

            inc di                 ;Add one for ends
            mov dx,di              ;X distance in DX

            add ah,bl              ;Calculate offset
            shl bx,6
            add ax,bx
            mov di,ax              ;DI = offset

            xor ax,ax              ;AX = 0 (black)
            mov bp,320             ;Auxiliary increment in BP
            sub bp,dx
            xor bx,bx              ;Clear BX
            shr dx,1               ;DX = X distance in words
            adc bx,0               ;BX = odd byte
ALIGN 16                           ;Force paragraph alignment
cv_loop:    mov cx,dx              ;CX = words
            rep stosw              ;Store by words
            mov cx,bx              ;CX = odd byte
            rep stosb              ;Store odd byte
            add di,bp              ;Next line
            dec si                 ;Dec line counter
            jns cv_loop            ;Loop back

            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
            push 0A000h            ;ES = video memory
            pop es
            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:     mov bx,cx              ;BX = x
            add bh,dl              ;BX = x + y * 256
            shl dx,6               ;BX = x + y * 256 + y * 64
            add bx,dx              ;so BX = x + y * 320
            shr dx,6               ;Restore DX
            mov al,[Color]         ;Get color
            mov [es:bx],al         ;Write pixel

            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:    sub cx,ax              ;Get X distance
            jge lh_xplus           ;Negative?
            add ax,cx              ;AX = previous CX
            neg cx                 ;Make it positive

lh_xplus:   inc cx                 ;Adjust
            add ah,bl              ;AX = x + 256 * y
            shl bx,6               ;multiply BX by 64
            add ax,bx              ;AX = x + 320 * y
            mov di,ax              ;Offset in DI
            mov al,[cs:Color]      ;Get color in AL, AH
            mov ah,al
            xor bx,bx              ;Clear BX
            shr cx,1               ;CX = width / 2
            adc bx,0               ;Odd byte in BX
EVEN                               ;Force word alignment
            rep stosw              ;Store by words
            mov cx,bx              ;Odd byte?
            rep stosb              ;Store odd 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:   add ah,bl              ;AX = x + 256 * y
            shl bx,6               ;multiply BX by 64
            add ax,bx              ;AX = x + 320 * y
            mov di,ax              ;Offset in DI
            mov al,[cs:Color]      ;AL = color
ALIGN 16                           ;Force paragraph alignment
lv_loop:    stosb                  ;Write pixel
            add di,319             ;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
            push 0A000h            ;ES = video memory
            pop es

            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:   inc di                 ;Add one for ends
            mov dx,di              ;X distance in DX

            add ah,bl              ;Calculate offset
            shl bx,6
            add ax,bx
            mov di,ax              ;DI = offset

            mov al,[cs:Color]      ;Color in AL, AH
            mov ah,al
            mov bp,320             ;Auxiliary increment in BP
            sub bp,dx
            xor bx,bx              ;Clear BX
            shr dx,1               ;DX = X distance in words
            adc bx,0               ;BX = odd byte
ALIGN 16                           ;Force paragraph alignment
fr_loop:    mov cx,dx              ;CX = words
            rep stosw              ;Store by words
            mov cx,bx              ;CX = odd byte
            rep stosb              ;Store odd byte
            add di,bp              ;Next line
            dec si                 ;Dec line counter
            jns fr_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
            push 0A000h            ;ES = video memory
            pop es
            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

            add ch,dl              ;Get offset, which is
            shl dx,6               ;x + 320 * y
            add cx,dx
            mov di,cx

            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
            mov ah,[cs:Color]      ;AH = color
ALIGN 16                           ;Force paragraph alignment
oc_8loop:   lodsb                  ;Load byte

            test al,80h            ;Pixel in column 0?
            jz oc_8_1              ;No, jump
            mov [es:di],ah         ;Yes, store byte

oc_8_1:     test al,40h            ;Pixel in column 1?
            jz oc_8_2              ;No, jump
            mov [es:di+1],ah       ;Yes, store byte

oc_8_2:     test al,20h            ;Pixel in column 2?
            jz oc_8_3              ;No, jump
            mov [es:di+2],ah       ;Yes, store byte

oc_8_3:     test al,10h            ;Pixel in column 3?
            jz oc_8_4              ;No, jump
            mov [es:di+3],ah       ;Yes, store byte

oc_8_4:     test al,08h            ;Pixel in column 4?
            jz oc_8_5              ;No, jump
            mov [es:di+4],ah       ;Yes, store byte

oc_8_5:     test al,04h            ;Pixel in column 5?
            jz oc_8_6              ;No, jump
            mov [es:di+5],ah       ;Yes, store byte

oc_8_6:     test al,02h            ;Pixel in column 6?
            jz oc_8_7              ;No, jump
            mov [es:di+6],ah       ;Yes, store byte

oc_8_7:     test al,01h            ;Pixel in column 7?
            jz oc_8_8              ;No, jump
            mov [es:di+7],ah       ;Yes, store byte

oc_8_8:     add di,320             ;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
            mov ah,[cs:Color]      ;AH = color
ALIGN 16                           ;Force paragraph alignment
oc_6loop:   lodsb                  ;Load byte

            test al,80h            ;Pixel in column 0?
            jz oc_6_1              ;No, jump
            mov [es:di],ah         ;Yes, store byte

oc_6_1:     test al,40h            ;Pixel in column 1?
            jz oc_6_2              ;No, jump
            mov [es:di+1],ah       ;Yes, store byte

oc_6_2:     test al,20h            ;Pixel in column 2?
            jz oc_6_3              ;No, jump
            mov [es:di+2],ah       ;Yes, store byte

oc_6_3:     test al,10h            ;Pixel in column 3?
            jz oc_6_4              ;No, jump
            mov [es:di+3],ah       ;Yes, store byte

oc_6_4:     test al,08h            ;Pixel in column 4?
            jz oc_6_5              ;No, jump
            mov [es:di+4],ah       ;Yes, store byte

oc_6_5:     test al,04h            ;Pixel in column 5?
            jz oc_6_6              ;No, jump
            mov [es:di+5],ah       ;Yes, store byte

oc_6_6:     add di,320             ;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

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
            push 0A000h            ;DS = video memory
            pop ds

            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

            add ch,dl              ;SI = offset
            shl dx,6               ;(x + y * 320)
            add cx,dx
            mov si,cx

            stosw                  ;Store width
            xchg ax,bx             ;Switch AX, BX
            stosw                  ;Store height

            xor dx,dx              ;Clear DX
            shr bx,1               ;BX = width in words
            adc dx,0               ;DX = odd byte
            mov bp,si              ;BP = SI

GetLoop:    mov cx,bx              ;CX = width in words
            rep movsw              ;Move by words
            mov cx,dx              ;CX = odd byte
            rep movsb              ;Move odd byte
            add bp,320             ;Next line
            mov si,bp              ;offset in SI
            dec ax                 ;Dec line counter
            jnz GetLoop            ;Loop back

            pop es ds              ;Restore registers
            popa
            ret                    ;Return

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
            push 0A000h            ;ES = video memory
            pop es

            lodsw                  ;Load width
            mov bx,ax              ;Put it in BX
            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

            add ch,dl              ;DI = offset
            shl dx,6               ;(x + y * 320)
            add cx,dx
            mov di,cx

            xor dx,dx              ;Clear DX
            shr bx,1               ;BX = width in words
            adc dx,0               ;DX = odd byte
            mov bp,di              ;BP = DI

PutLoop:    mov cx,bx              ;CX = width in words
            rep movsw              ;Move by words
            mov cx,dx              ;CX = odd byte
            rep movsb              ;Move odd byte
            add bp,320             ;Next line
            mov di,bp              ;offset in DI
            dec ax                 ;Dec line counter
            jnz PutLoop            ;Loop back

pi_done:    pop es ds              ;Restore registers
            popa
            ret                    ;Return

EndP        PutImage

End Start
