Metropoli BBS
VIEWER: l13h.asm MODE: TEXT (LATIN1)
;**********************************************************;
;* ------< VGA GRAPHICS LIBRARY - BY TENIE REMMEL >------ *;
;*                                                        *;
;* L13H.ASM - This is a highly optimized graphics library *;
;* for the 320x200x256 VGA Mode 13h (19d).  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.                           *;
;**********************************************************;

Ideal
Jumps
Warn ICG
NoWarn ALN

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 319
MaxY        dw 199
FontPtr     dd 0
FontWidth   db 8
FontHeight  db 8
Color       db 15

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

ALIGN 16

Proc        InitGraph

            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        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
            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
[ RETURN TO DIRECTORY ]