Category : Printer + Display Graphics
Archive   : GX2FILE.ZIP
Filename : GX2FILE.ASM
Output of file : GX2FILE.ASM contained in archive : GX2FILE.ZIP
; GX2File.Asm Oct 19, 1987
;
; This file implements the saving and loading of GX2 graphics
; bitmaps to/from disk.
;
; The following routines are screen mode dependant :
;
; Calc_Loc, PixByte, BytePix, NxtScan macro
; Plus minor usage of GMODE in Read_Gx2
;
; GMODE byte has current screen mode as follows:
; 0 40 column CGA B/W text
; 1 40 column CGA Color text
; 2 80 column CGA B/W text
; 3 80 column CGA Color text
; 4 CGA graphics (B/W treated as 4 color)
; 5 640x480x2 VGA graphics
; 6 640x400x2 Ericson graphics
; 7 640x400x2 ATT 6300 graphics
; 8 720x348x2 Hercules graphics
; 9 640x350x4 EGA Monochrome graphics
; A 320x200x16 EGA graphics
; B 640x200x16 EGA graphics
; C 640x350x4 EGA graphics
; D 640x350x16 EGA graphics
; E 640x480x16 VGA graphics
; F 320x200x256 MCGA graphics
;
; To read a GX2 file :
; 1. Call Open_GX2 to open the file and load
; the header info.
; 2. Find an available screen mode which most
; closely matches the header data.
; 3. Put the mode's ID into GMODE and call Read_Gx2.
; 4. Call Release_Gx2 to close the file.
;
; To save a GX2 file simply set up the header variables and
; the GMODE value and then call the Save_Gx2 routine.
;
; For further instructions on the use of these routines, see
; the doc at the top of each procedure.
;
Gx2DATA Segment para PUBLIC 'data'
; Header information fields
PUBLIC gx2_id,gx2_version,head_size,pixel_bits
PUBLIC line_pixels,file_lines,hor_size,ver_size
PUBLIC size_units,tag_area,palette_type
PUBLIC gx2_blocks,file_pal
PUBLIC gmode,errnum
gmode db 4 ;Mode number (see above)
errnum dw 0 ;For reporting errors
hdwseg dw 0b800h ;Segment of hardware bitmap
picseg dw 0
big_head db 0 ;Flag for huge header!
svg2sp dw 0 ;Recover from write error
gbpl dw 0 ;Generic bitmap bytes per line
verchg db 128 dup(0) ;Vertical compression changes
header label byte
gx2_id db 'GX2' ;GX2 header
gx2_version db 1 ;Format version #
head_size dw 0 ;Size of header
pixel_bits db 0 ;Bits per pixel
line_pixels dw 0 ;Pixels per scan line
file_lines dw 0 ;Scan lines on screen
hor_size dw 0 ;Physical horizontal size
ver_size dw 0 ; " vertical size
size_units db 0 ;[aspect,inches*100,milli*100]
siz_less_tags equ $ - header
scanln label byte
tag_len label word
tag_area db 1024 dup(0) ;Max 1k of app tag data!
changes db 1024 dup(0) ;Buffer for vertical compress.
siz_subhead equ $ - pixel_bits
max_head equ $ - header
palette_type db 2 ;Type of bitmap/palette
gx2_blocks db 0
;
; Boolean bits in palette_type byte.
;
planar equ 80h
;
; RGB Lookup table (palette_type lower nibble equal 2)
;
file_pal db 768 dup(0) ;MAX 256 colors allowed in LUT
;
; Bitmap block header for partial screens (bit D6 of palette type equal 1)
;
blk_pixels dw 0 ;Pixels per block line
blk_lines dw 0 ;Scan lines in block
blk_x dw 0 ;Left ordinate of blk location
blk_y dw 0 ;Top ordinate of blk location
Gx2DATA EndS
DGroup Group Gx2DATA
CSEG Segment para PUBLIC 'code'
ASSUME cs:CSeg,ds:DGroup
PUBLIC Open_Gx2,Read_Gx2,Release_Gx2
PUBLIC Save_Gx2
blkadr dw 0
scndbl db 0
ebpl dw 0
;
; The following macro alters an index register down one scan line
;
NxtScan Macro reg
local x0,x1,x2
cmp gmode,5
je x1
cmp gmode,9
jae x1
cmp gmode,7
jb x0
add reg,2000h
jns x2
sub reg,8000h
jmp short x1
x0: xor reg,2000h
test reg,2000h
jnz x2
x1: add reg,ebpl
x2:
EndM
;
;..............................................................................
;
; SETMODE
;
; This subroutine sets the EBPL (bytes per scan line) variable
; and HDWSEG (segment of hardware bitmap) depending on the contents
; of GMODE.
;
SETMODE Proc Near
mov picseg,es
mov hdwseg,0b800h
mov ebpl,80
cmp gmode,5
je smev
cmp gmode,8
jb smdun
ja sm1
mov ebpl,90
jmp short smdun
sm1: cmp gmode,10
jne sm2
mov ebpl,40
jmp short smev
sm2: cmp gmode,15
jne smev
mov ebpl,320
smev: mov hdwseg,0a000h
smdun: ret
SETMODE EndP
;
;..............................................................................
;
; Calc_Loc (for use with blocked bitmap files)
;
; Return the address of a pixel coordinate (DX,CX) in AX.
;
Calc_Loc Proc Near
;
cmp gmode,4 ;Graphics?
je cloc0
shl dx,1 ;Word per pixel
mov ax,160 ;80 column bytes-per-line
cmp gmode,2
jae cloct
shr ax,1 ;40 column
cloct: mul cl
add ax,dx
ret
;
cloc0: cmp gmode,15 ;Byte per pixel?
je cloc1
shr dx,1
shr dx,1 ;At least 4 pixels per byte
cmp gmode,4 ;CGA graphics?
je cloc1
shr dx,1 ;All other modes 8 pixels/byte
cloc1: cmp gmode,4
je calcga
cmp gmode,6
je caleric
cmp gmode,7
je calherc
cmp gmode,8
je calherc
;
mov ax,cx ;Linear modes!
mov cx,dx
mul ebpl
add ax,cx
ret
;
calcga: mov ax,cx ;CGA odd/even scans
mov cx,dx
shr ax,1
pushf
mul ebpl
popf
jnc ccgax
add ax,2000h
ccgax: add ax,cx
ret
;
caleric: shr cx,1 ;Ercison 400 line mode
pushf
shr cx,1
pushf
mov ax,cx
mov dx,cx
mul ebpl
shl ax,1
shl ax,1
shl ax,1
popf
rcl ax,1
popf
rcl ax,1
shr ax,1
ret
;
calherc: xor ax,ax ;MOD 4 scan bank organization
shr cx,1
jnc chrc1
add ax,2000h
chrc1: shr cx,1
jnc chrc2
add ax,4000h
chrc2: xchg ax,cx
push dx
mul ebpl
pop dx
add ax,cx
add ax,dx
ret
;
dos_write: mov ah,40h
int 21h
jc wrterr
cmp ax,cx
jne wrterr
ret
wrterr: mov ax,DGroup
mov ds,ax
mov errnum,8
mov sp,svg2sp
ret
;
; EGAPAGE gets a plane # in AL and sets the EGA bitmap for read and write.
;
egapage: push dx
push ax
mov dx,3c4h
mov al,2
out dx,al
inc dx
pop ax
push ax
cmp al,2
jae egpg2
or al,al
jnz egpg1
mov al,3
cmp gmode,9
je egpg3
cmp gmode,12
je egpg3
mov al,1
jmp short egpg3
egpg1: mov al,12
cmp gmode,9
je egpg3
cmp gmode,12
je egpg3
mov al,2
jmp short egpg3
egpg2: mov ah,al
mov al,4
cmp ah,3
jne egpg3
shl al,1
egpg3: out dx,al
;
mov dx,3ceh
mov al,4
out dx,al
inc dx
pop ax
push ax
cmp al,1
jne egpg4
cmp gmode,9
je egpg5
cmp gmode,12
je egpg5
egpg4: out dl,al
pop ax
pop dx
ret
egpg5: inc al
jmp short egpg4
;
Calc_Loc EndP
;..............................................................................
;
; PixByte This routine returns the next byte of bitmap data in AL.
; No range checking is done -- the calling routine should
; know how may bytes are involved. ES:SI is used to accumulate
; a screen address and should be initialized and preserved
; during the compression job. SI should be set to 0 for the
; first call to this routine.
;
pbvect dw txt40 ;GMODE jump vector
dw txt40
dw txt80
dw txt80
dw pbcga
dw pbcga
dw pbcga
dw pbcga
dw pbcga
dw pbega2
dw pbega4
dw pbega4
dw pbega2
dw pbega4
dw pbega4
dw pbcga
;
bytcnt dw 0
plncnt db 0
plnsiz dw 0
egabuf db 4 dup(0)
;
PUBLIC pixbyte
;
PixByte Proc Near
;
push bx
mov al,gmode
cbw
shl ax,1
mov bx,ax
call pbvect[bx]
pop bx
ret
;
prepb: test palette_type,planar
jz ppb1
push dx
mov ax,file_lines ;Planar modes want plane bytes
mul ebpl
mov bytcnt,ax
mov plnsiz,ax
mov plncnt,0
xor al,al
call epage
pop dx
ret
ppb1: mov ax,ebpl ;Most modes want bytes per line
mov bytcnt,ax ; in this work variable!
cmp gmode,9
jb pbpx
cmp gmode,15
je pbpx
mov bytcnt,8 ;Ega modes want bits per pixel
call egain
pbpx: ret
;
txt40: mov al,es:[si] ;Get screen byte
add si,2
cmp si,2000
jb t40x
mov si,1 ;Switch to attributes
t40x: ret
;
txt80: mov al,es:[si]
add si,2
cmp si,4000
jb t80x
mov si,1
t80x: ret
;
pbcga: mov al,es:[si]
inc si
dec bytcnt
jnz pbcx
mov bx,ebpl
mov bytcnt,bx
sub si,bx
NxtScan SI
pbcx: ret
;
epage: push bx
mov bx,es
cmp bx,hdwseg
jne epg1
call egapage ;Setup EGA/VGA for plane AL
pop bx
ret
epg1: push dx ;Point ES at DOS memory
xor ah,ah ; for plane AL.
mul ebpl
mul file_lines
add ax,picseg
mov es,ax
pop dx
pop bx
ret
;
egain: push es
xor al,al ;Fill EGA byte buffers with
call epage ; planar data.
mov al,es:[si]
mov egabuf+0,al
pop es
push es
mov al,1
call epage
mov al,es:[si]
mov egabuf+1,al
cmp gmode,9
je efx
cmp gmode,12
je efx
pop es
push es
mov al,2
call epage
mov al,es:[si]
mov egabuf+2,al
pop es
push es
mov al,3
call epage
mov al,es:[si]
mov egabuf+3,al
efx: pop es
inc si
ret
;
pbega2: test palette_type,planar
jz pbe2n
jmp pbe4p
pbe2n: push cx
mov cx,4 ;4 pixels per byte
xor al,al
pbe2a: shl egabuf+1,1
rcl al,1
shl egabuf+0,1
rcl al,1
loop pbe2a
pop cx
sub bytcnt,4
jnz pbe2x
mov bytcnt,8
push ax
call egain
pop ax
pbe2x: ret
;
pbega4: test palette_type,planar
jz pbe4n
pbe4p: mov al,es:[si]
inc si
dec bytcnt
jnz p4plx
xor si,si ;Planar version!
push ax
mov al,plncnt
inc al
cmp al,pixel_bits
je p4pln
mov plncnt,al
call epage
mov ax,plnsiz
mov bytcnt,ax
p4pln: pop ax
p4plx: ret
pbe4n: push cx
mov cx,2 ;4 pixels per byte
xor al,al
pbe4a: shl egabuf+3,1
rcl al,1
shl egabuf+2,1
rcl al,1
shl egabuf+1,1
rcl al,1
shl egabuf+0,1
rcl al,1
loop pbe4a
pop cx
sub bytcnt,2
jnz pbe4x
mov bytcnt,8
push ax
call egain
pop ax
pbe4x: ret
;
PixByte EndP
;
;..............................................................................
;
; BytePix This routine is the mirror of PixByte above. ES:DI accums
; the screen address and should be initted to HDWSEG:0 before
; the first call here. This routine disperses a GX2 byte
; into respective pixel positions on the screen.
;
bpvect dw bpt40 ;GMODE jump vector
dw bpt40
dw bpt80
dw bpt80
dw bpcga
dw bpcga
dw bpcga
dw bpcga
dw bpcga
dw bpega2
dw bpega4
dw bpega4
dw bpega2
dw bpega4
dw bpega4
dw bpcga
;
BytePix Proc Near
;
push bx
mov bl,gmode
xor bh,bh
shl bx,1
mov cx,gbpl
call bpvect[bx]
pop bx
ret
;
prebp: test palette_type,planar
jz bpb0
push dx
mov ax,file_lines ;Planar modes want plane bytes
mul ebpl
mov bytcnt,ax
mov plnsiz,ax
mov plncnt,0
xor al,al
call epage
pop dx
ret
bpb0: mov ax,ebpl ;Most modes want bytes per line
cmp gmode,8
jne bpb1
cmp file_lines,200
jne bpb1
mov scndbl,0 ;Scan doubler counter
mov ax,80 ;Force if Herc Xlat
bpb1: mov bytcnt,ax
cmp gmode,9
jb ppbx
cmp gmode,15
je ppbx
mov bytcnt,8 ;Ega modes want bits per pixel
mov word ptr egabuf,0
mov word ptr egabuf+2,0
ppbx: ret
;
bpt40: push di
bt4a: lodsb
stosb
inc di
loop bt4a
pop di
add di,80
bt4x: ret
;
bpt80: push di
bt8a: lodsb
stosb
inc di
loop bt8a
pop di
add di,160
bt8x: ret
;
bpcga: push di
cmp gmode,8 ;Hercules?
je bpcga0
bprcga: jmp bpcga1
bpcga0: cmp file_lines,200 ;Cga Translation?
jne bprcga
push cx
xor al,al
mov cx,5
rep stosb
pop cx
rep movsb
mov cx,5
rep stosb
inc scndbl
test scndbl,3 ;Double 3 of every 4 lines!
jz bpcga2
pop si
mov di,si
NxtScan DI
push di
mov cx,90
push ds
push es
pop ds
rep movsb
pop ds
jmp short bpcga2
bpcga1: rep movsb
bpcga2: pop di
NxtScan DI
ret
;
bpega2: push di
test palette_type,planar
jz bpe2n
jmp bpe4p
bpe2n: push cx
mov cx,4 ;4 pixels per byte
lodsb
bpe2a: shl al,1
rcl egabuf+1,1
shl al,1
rcl egabuf+0,1
loop bpe2a
pop cx
sub bytcnt,4
jnz bpe2x
mov bytcnt,8
xor al,al
call epage
mov al,egabuf+0
mov es:[di],al
mov al,1
call epage
mov al,egabuf+1
stosb
bpe2x: loop bpe2n
pop di
add di,ebpl
ret
;
bpega4: push di
test palette_type,planar
jz bpe4n
bpe4p: rep movsb
pop di
add di,ebpl
ret
bpe4n: push cx
mov cx,2 ;2 pixels per byte
lodsb
bpe4a: shl al,1
rcl egabuf+3,1
shl al,1
rcl egabuf+2,1
shl al,1
rcl egabuf+1,1
shl al,1
rcl egabuf+0,1
loop bpe4a
pop cx
sub bytcnt,2
jnz bpe4x
mov bytcnt,8
xor al,al
call epage
mov al,egabuf+0
mov es:[di],al
mov al,1
call epage
mov al,egabuf+1
mov es:[di],al
mov al,2
call epage
mov al,egabuf+2
mov es:[di],al
mov al,3
call epage
mov al,egabuf+3
stosb
bpe4x: loop bpe4n
pop di
add di,ebpl
ret
;
BytePix EndP
;
;..............................................................................
;
; Run Length Encoding Engine - These routines encode and decode
; run length encoded data. They can be thought of as input
; and output filters whose purpose is to eliminate run length
; encoding details from higher level routines. Source or
; destination of the run-length encoded data is specified
; by the FHANDLE word. If this word is ZERO, encoded data
; [ is/will be ] in the memory segment addressed by ES.
; Otherwise I/O to a file handle is assummed.
;
; RlIni - Initialize the run length encoding routines before each task
; RlGet - CX bytes are decoded from FHANDLE and moved to DS:DI
; RlPut - CX bytes from DS:SI are encoded and sent to FHANDLE
; RlFin - Finish last packet and send to FHANDLE
;
; NOTE : For files larger than 64k sent to memory (FHANDLE=0), ES
; register will return changed at segment index overflow time.
;
PUBLIC RlIni,RlGet,RlPut,RlFin,fhandle
;
rlsame db 0 ;All run-length stuff is
rlbyte db 0 ; code relative.
rlcount db 0
rlindex dw 0
rlio db 0
tgtoff dw 0
dta db 256 dup(0)
fhandle dw 0
;
ASSUME ds:NOTHING
;
Run_Len Proc Near
;
RlIni: mov rlsame,0
mov rlcount,0
mov rlindex,0
mov tgtoff,0
mov rlio,0
ret
;
RlGet: cmp rlcount,0 ;Is run length zero?
jne rlg1
mov rlsame,0 ;Init SAME flag!
call rlgbyte
shl al,1
rcl rlsame,1
shr al,1
jnz rlg0
mov rlcount,0ffh ;Input error - 0 run-length
mov rlbyte,0 ;Send zeros back
mov rlsame,1
jmp short rlg1
rlg0: mov rlcount,al
test rlsame,1
jz rlg1
call rlgbyte
mov rlbyte,al
rlg1: test rlsame,1
jz rlg2
mov al,rlbyte
jmp short rlg3
rlg2: call rlgbyte
rlg3: mov ds:[di],al
inc di
dec rlcount
loop RlGet
ret
;
rlgbyte: push ds
mov ax,DGroup
mov ds,ax
ASSUME ds:DGroup
cmp fhandle,0
je rlgmem
cmp tgtoff,0 ;1st time thru?
je rlgfil ;Yep!
cmp tgtoff,128 ;Time for another 128?
je rlgfil
push si ;Get from current dta!
mov si,tgtoff
mov al,dta[si]
inc tgtoff
pop si
pop ds
ret
rlgfil: pop ds
push ds ;Fill DTA with file data!
push dx
push cx
push bx
lea dx,dta
mov cx,128
mov bx,fhandle
mov ah,3fh
push cs
pop ds
int 21h
pop bx
pop cx
pop dx
pop ds
mov al,dta
mov tgtoff,1
ret
rlgmem: push si ;Get from mem image!
mov si,rlindex
mov al,es:[si]
inc rlindex
jnz rlgmx
push ax
mov ax,es
add ax,1000h
mov es,ax
pop ax
rlgmx: pop si
pop ds
ret
;
RlPut: or cx,cx
jnz rlp0
ret
rlp0: push di
mov di,tgtoff
or di,di ;First time thru?
jnz rlp1
mov rlio,1 ;Signify rl output in progress
lodsb
mov dta,1
mov dta+1,al
mov di,2
mov rlsame,0
jmp rlpnxt
rlp1: lodsb
test rlsame,1
jz rlp3
cmp al,dta[1] ;Countinue repeating stream?
jne rlp2 ;Nope - byte is different
inc dta ;Else - bump the run length
jnz vrpnxt ;Jmp if not full count
dec dta
call rlpack ;Output full repeat count
mov dta,1
mov rlsame,0 ;Switch to non-repeating
mov di,2
vrpnxt: jmp rlpnxt
rlp2: call rlpack ;Send repeating packet
mov di,2 ; and switch to non-repeat
mov dta,1
mov dta+1,al
mov rlsame,0
jmp short vrpnxt
rlp3: cmp dta,3 ;For non-repeat counts:
jb rlp5
cmp al,dta[di-3] ;Switch to repeating bytes?
jne rlp5
cmp al,dta[di-2]
jne rlp5
cmp al,dta[di-1]
jne rlp5
sub di,4
jz rlp4 ;Jmp if no non-repeat packet!
sub dta,3
call rlpack ;Else - send packet
rlp4: mov di,1 ;Setup for repeating bytes
mov dta,84h
mov dta+1,al
mov rlsame,1
jmp short rlpnxt
rlp5: cmp dta,130 ;2 non-reps in a row?
jb rlp6 ;Not yet anyway
mov dta,7fh
mov di,127 ;Send full count non-repeat
call rlpack
mov dta+4,al ;Copy extras to DTA top
mov al,dta+130
mov dta+3,al
mov al,dta+129
mov dta+2,al
mov al,dta+128
mov dta+1,al
mov dta,4
mov di,5
jmp short rlpnxt
rlp6: inc dta ;Add to existing non-repeat
mov dta[di],al
inc di
rlpnxt: dec cx ;Count 1 byte transferred!
jz rlpxit
jmp rlp1
rlpxit: mov tgtoff,di
pop di
ret
;
RlFin: push di
mov di,tgtoff
test rlsame,1
jnz rlf1
dec di
rlf1: call rlpack
pop di
ret
;
rlpack: push ds ;Run length data router!
push dx
push cx
push bx
push ax
lea dx,dta
mov cx,di
inc cx
cmp fhandle,0
jne rlpfil
push si ;Send to mem image!
push di
lea si,dta
mov di,rlindex
add rlindex,cx
jnc rlpm1
push rlindex
sub cx,rlindex
push ds
push cs
pop ds
rep movsb
pop ds
mov cx,es
add cx,1000h
mov es,cx
pop cx
rlpm1: push cs
pop ds
cld
rep movsb
mov rlindex,di
cmp rlindex,0
jne rlpm2
mov di,es
add di,1000h
mov es,di
rlpm2: pop di
pop si
jmp short rlpkx
rlpfil: mov bx,fhandle ;Send to File!
push cs
pop ds
call dos_write
rlpkx: pop ax
pop bx
pop cx
pop dx
pop ds
ret
;
calgbl: mov ax,line_pixels
mov cl,palette_type
and cl,0fh
cmp cl,2 ;Return line_pix for text modes
jb cgbl1
test palette_type,planar ;Bit Planes?
jz cgbl0 ;Nope!
mov cl,3
shr ax,cl
jmp short cgbl1
cgbl0: mov al,pixel_bits ;Calc Generic bytes per line
xor ah,ah
mul line_pixels
add ax,7
mov cl,3
shr ax,cl
cgbl1: mov cx,ax
mov gbpl,cx
ret
;
;
Run_Len EndP
;
;..............................................................................
;
; Open_GX2 Open a graphics bitmap file for input.
;
; Entry Parameters
; DS:DX pointer to a file name
; DS:BX pointer to an application tag buffer (BX=FFFF means
; null tag).
;
; Exit Parameters
; CY is SET if an error occurred, AL has its code
;
; Once open, all the public GX2 header variables reflect the
; information in the GX2 file. Use should use this information
; to choose a GMODE value and then call the Read_Gx2 function.
;
Open_Gx2 Proc Near
;
push si
push di
push dx
push bx
push es ;Zero the tag area
push ds
pop es
lea di,tag_area
xor ax,ax
mov cx,LENGTH tag_area
cld
rep stosw
pop es
mov ax,3d00h ;Open file.
int 21h
jnc o2a
mserr: pop bx ;File Open error
pop dx
pop di
pop si
ret
o2a: mov bx,ax
mov fhandle,bx
lea dx,header
mov cx,6 ;Read the header length word
mov ah,3fh
int 21h
jc mserr
cmp gx2_id+0,'G'
jne fnferr
cmp gx2_id+1,'X'
jne fnferr
cmp gx2_id+2,'2'
je isgx2
fnferr: mov al,2 ;Not a GX2 file - fake like
stc ; file not found for now!
jmp short mserr
isgx2: mov cx,head_size
sub cx,6
jbe fnferr
mov big_head,0
cmp cx,siz_subhead ;Header size above our max?
jbe ntovr
mov big_head,0ffh
mov cx,siz_subhead
ntovr: lea dx,pixel_bits ;Read rest of header.
mov ah,3fh
int 21h
;
; See if App wants a tag. If so, see if we have a match!
;
pop ax
inc ax
jz notag ;No tag requested.
dec ax
mov di,ax
xor si,si
mov cx,head_size
sub cx,siz_less_tags
cmp tag_len,0 ;NULL tag area?
je notag
tsrch: cmp si,cx ;Tag not found?
jae notag
push bx
mov bx,2
ts1: mov al,tag_area[si+bx]
cmp al,ds:[di+bx]
jne ts3
inc bx
cmp bx,6
jb ts1
pop bx ;Tag is found!
mov cx,ds:[di] ;Get App's max length
cmp cx,tag_len[si] ;If less than file
jbe ts2 ; USE IT
mov cx,tag_len[si] ;Else - use input len
ts2: cld
lea si,tag_len[si]
push es
push ds
pop es
push cx
push di
rep movsb
pop di
pop word ptr es:[di]
pop es
jmp short notag
ts3: pop bx
add di,tag_len[si] ;Check next tag!
add si,tag_len[si]
jmp tsrch
;
notag: test big_head,0ffh ;Position file to start of
jz ntbig ; bitmap data.
lea dx,tag_area
mov cx,head_size
sub cx,max_head
;
filpos: push cx
cmp cx,LENGTH tag_area * 2
jbe fp1
mov cx,LENGTH tag_area * 2
fp1: mov ah,3fh
int 21h
pop cx
sub cx,ax
ja filpos
ntbig: lea dx,palette_type ;Read the palette info!
mov cx,2
mov ah,3fh
int 21h
mov al,palette_type
and al,0fh
mov cx,48
cmp al,1 ;RGB Text?
je fp1b
cmp al,2 ;RGB LUT?
jne fp2 ;Nope - No palette lookup!
mov cl,pixel_bits
mov ax,1
shl ax,cl ;AX has # of colors
mov cx,ax
shl ax,1
add cx,ax ;CX has # of palette bytes
fp1b: lea dx,file_pal
mov ah,3fh
int 21h ;Read RGB color LUT.
;
fp2: call RlIni ;Initialize run-length system!
pop dx ;Restore file name ptr.
pop di
pop si
clc ;No error!
ret
;
Open_Gx2 EndP
;
;..............................................................................
;
; Read_Gx2 After App processes the header and decides on GMODE, this
; routine is called to read the file into the hardware or
; a buffer.
;
; Entry Parameters
; ES segment of bitmap memory
; GMODE screen mode ID
;
; Exit Parameters
; ERRNUM will contain any error code.
; only segment register values are preserved
;
Read_Gx2 Proc Near
;
call setmode
xor di,di
mov blkadr,di
cmp gx2_blocks,0 ;Blocked bitmap?
jne rdall ;Yep - Read em all
jmp rdblock ;Else- Read just one
;
; For blocked bitmaps
;
rdall: push line_pixels
push file_lines
lea dx,blk_pixels ;Read Block header
mov cx,8
mov bx,fhandle
mov ah,3fh
int 21h
rt0: cmp blk_pixels,0 ;End of blocked file?
jne rt1
pop file_lines
pop line_pixels
clc
ret
;
rt1: mov dx,blk_x
mov cx,blk_y
call calc_loc
mov di,ax ;Get starting address
mov blkadr,ax
mov ax,blk_pixels
mov line_pixels,ax
mov ax,blk_lines
mov file_lines,ax
call rdblock
mov blk_pixels,0
dec gx2_blocks
jz rt0
mov si,tgtoff
lea di,blk_pixels
mov cx,8
rt2: cmp si,128
je rt3
mov al,dta[si]
inc si
mov byte ptr [di],al
inc di
loop rt2
push si
call RlIni
pop tgtoff
jmp short rt0
rt3: lea dx,blk_pixels[di]
mov bx,fhandle
mov ah,3fh
int 21h
call RlIni
jmp short rt0
;
rdblock: call prebp
cmp gmode,4 ;Is graphics the target?
jae gx2graf ;Yep!
;
mov ax,line_pixels
mov gbpl,ax
mov dx,file_lines
rtxt1: push di
lea di,scanln ;Read characters
mov cx,gbpl
call RlGet
pop di
lea si,scanln
call BytePix
dec dx
jnz rtxt1
mov di,blkadr
inc di
mov dx,file_lines
rtxt2: push di
lea di,scanln ;Read attributes
mov cx,gbpl
call RlGet
pop di
lea si,scanln
call BytePix
dec dx
jnz rtxt2
clc
ret
;
gx2graf: call calgbl
push di
lea di,scanln
call RlGet ;Get 1st scan line verbatum
pop di
mov dx,file_lines
;
rdgx2: lea si,scanln
call BytePix
dec dx
jnz rdgx3
test palette_type,planar
jz rdg2x
mov al,plncnt
inc al
cmp al,pixel_bits
je rdg2x
mov plncnt,al
call epage
mov di,blkadr
mov dx,file_lines
jmp short rdgx3
rdg2x: clc
ret ;ALL DONE!
;
rdgx3: push di
mov cx,gbpl
add cx,7
shr cx,1
shr cx,1
shr cx,1
lea di,verchg
call RlGet ;Get changes for next line!
xor si,si
xor bx,bx
mov cx,gbpl
rdgx4: shl verchg[bx],1 ;Install byte changes when
jnc rdgx5 ; bit in change map is set
lea di,scanln[si]
push cx
mov cx,1
call RlGet
pop cx
rdgx5: inc si
test si,7
jnz rdgx6
inc bx
rdgx6: loop rdgx4
pop di
jmp short rdgx2
;
Read_Gx2 EndP
;
;..............................................................................
;
; Release_Gx2 Simply closes the file!
;
Release_Gx2 Proc Near
;
mov bx,fhandle
mov ah,3eh
int 21h
ret
;
Release_Gx2 EndP
;
;..............................................................................
;
; Save_Gx2 This routine saves a GX2 file to disk.
;
; Entry Parameters
; DS:DX pointer to the file name to use
; DS:BX pointer at the application tag (if none BX=-1)
; GMODE contains screen mode ID
; ES has segment of bitmap memory
;
; TAG_AREA should contain any old tags to be included in the
; new file -- if all old tags are to be discarded write a zero
; to word TAG_AREA. Before calling this routine, the public
; header variables should all be filled out except for
; HEAD_SIZE which is computed. On return, TAG_AREA is
; destroyed along with ALL registers except the segments.
;
; On Exit
; ERRNUM will be non-zero if an error occurred
;
; Typically when saving a file :
; 1. Check to see if file already exists.
; 2. If yes - Ask whether to overwrite.
; 3. If yes/yes - Ask whether to keep old tags from other apps.
; 4. If yes/yes/yes - Open and Release old file to get tags.
; 5. Init all header info after HEAD_SIZE.
; 6. Set up the above entry parameters and call this routine.
;
Save_Gx2 Proc Near
;
mov svg2sp,sp
call setmode
mov gx2_id+0,'G'
mov gx2_id+1,'X'
mov gx2_id+2,'2'
mov gx2_version,1
mov gx2_blocks,0
xor si,si ;First find the length of
xor cx,cx ; the new tag_area.
mov di,-1
mov head_size,siz_less_tags
cmp bx,-1
je flt4
mov ax,ds:[bx]
add head_size,ax ;Add in the size of new tag
flt1: cmp word ptr tag_area[si],0 ;End of old tags?
je flt4
mov al,tag_area[si+2]
cmp al,ds:[bx+2]
jne flt2
mov al,tag_area[si+3]
cmp al,ds:[bx+3] ;Check if this is an old tag
jne flt2 ; for this App.
mov al,tag_area[si+4]
cmp al,ds:[bx+4]
jne flt2
mov al,tag_area[si+5]
cmp al,ds:[bx+5]
jne flt2
mov di,si
mov ax,word ptr tag_area[di]
jmp short flt3
flt2: mov ax,word ptr tag_area[si]
add head_size,ax
flt3: add si,ax
jmp short flt1
flt4: cmp di,-1 ;Old tag to be replaced?
je flt5b ;Nope!
lea ax,tag_area
add si,ax
add di,ax
mov cx,si
sub cx,di
sub cx,[di]
jz flt5a ;Tag is at end of list
mov si,di
add si,ds:[si]
push es
push ds
pop es
rep movsb
pop es
flt5a: mov word ptr [di],0
mov si,di
lea ax,tag_area
sub si,ax
flt5b: push si ;Push length of old tags!
push bx ;Push App Tag address.
xor cx,cx
mov ah,3ch ;CREATE FILE!
int 21h
jnc flt6
mov errnum,3
pop bx
pop si
stc
ret
;
flt6: mov fhandle,ax
lea dx,header
mov cx,siz_less_tags
mov bx,ax
call dos_write ;Write main header
pop si
cmp si,-1 ;If no tag - skip this
je flt6a
mov cx,ds:[si]
mov dx,si
call dos_write ;Write this App tag.
flt6a: pop cx
or cx,cx
jz flt7
lea dx,tag_area
call dos_write ;Write tags for other apps.
flt7: lea dx,palette_type
mov cx,2
call dos_write ;Write palette type byte.
mov cx,48
cmp palette_type,1 ;Colored text?
je flt7a
mov cl,palette_type
and cl,0fh
cmp cl,2
jne flt8 ;Jmp if no RGB LUT.
mov cl,pixel_bits
mov ax,1
shl ax,cl
mov cx,ax
shl ax,1
add cx,ax
flt7a: lea dx,file_pal
call dos_write ;Write color look-up-table.
;
; Header now done. Time to encode the bitmap!
;
flt8: call RlIni ;Init run-length encoder.
call calgbl ;Calc generic bytes-per-line.
mov ax,file_lines ;DX has scan counter.
cmp gmode,4
jae flt9
;
; Text file compression!
;
shl ax,1
mov dx,ax
xor si,si
call prepb
fltxt: xor di,di
mov cx,gbpl
fltx2: call PixByte
mov scanln[di],al
inc di
loop fltx2
push si
lea si,scanln
mov cx,gbpl
call RlPut
pop si
dec dx
jnz fltxt
jmp sg7
;
flt9: test palette_type,planar
jz sg0
mov dl,pixel_bits
xor dh,dh
mul dx
sg0: mov dx,ax
xor si,si ;Init bitmap pointer.
xor di,di
call prepb
sg1: call PixByte ;Get 1st scan into buffer.
mov scanln[di],al
inc di
loop sg1
push si
lea si,scanln
mov cx,gbpl
call RlPut ;Send to file verbatim.
pop si
dec dx
sg2: xor di,di ;For every remaining scan line!
xor bx,bx
xor bp,bp
mov cx,gbpl
sg3: call PixByte
cmp al,scanln[di] ;Is it different from previous?
je sg4 ;Nope!
xchg si,bp
mov changes[si],al
mov scanln[di],al
inc si
xchg si,bp
stc
sg4: rcl verchg[bx],1
inc di
test di,7
jnz sg5
inc bx
sg5: loop sg3
mov cx,di
dec cl
xor cl,7
and cl,7 ;Any partial change map byte?
jz sg6 ;Nope!
shl verchg[bx],cl
inc bx
sg6: push si
lea si,verchg
mov cx,bx
call RlPut ;Write the change map.
lea si,changes
mov cx,bp
call RlPut ;Write the changed bytes.
pop si
dec dx ;Count 1 scan done.
jz sg7
jmp sg2
sg7: call RlFin ;Finish last run-length packet.
call Release_Gx2 ;Close it up.
clc
ret
;
Save_Gx2 EndP
;
CSeg EndS
;
End
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/