Source to display .zip file contents
#1
Code:
    page    74,132
        title   ZIPVIEW - Verbose ARC directory listing

; usage:
;
;       CALL ARCV (Workname$,"filename[.PAK]", RETCD%)

;       This program source PD 2014.


STDOUT  equ     1                       ;Standard Output
STDERR  equ     2                       ;Std Error (console)
FALSE    equ    0
TRUE    equ    NOT FALSE
DEBUG    equ    FALSE

Print    macro    name            ; display a field
    mov    dx,offset name
    call    PrintS
    endm

header  struc                ; archive header
aMbrflag        db      1AH             ;unique ARC/PAK flag
aCmpMeth    db    0        ;  compression code
aMbrName    db    13 dup (0)    ;  file name
aCmpSiz        dw    0,0        ;  file size in archive
aModDate    dw    0        ;  creation date
aModTime    dw    0        ;  creation time
aCrc16        dw    0        ;  cyclic redundancy check
aUncmpSiz    dw    0,0        ;  true file size, bytes
header  ends

ARCHDRLEN       equ     29              ;size of ARC/PAK header.

;ZIP Local file header structure:

zLocalEntry    STRUC
 
zdig0    db    50H,4BH,03H,04H    ;local file header signature    4 bytes
                ;(0x04034b50)
zVerMade    dw    ?    ;version needed to extract    2 bytes
zBitflag    dw    ?    ;general purpose bit flag    2 bytes
zCmpMeth    dw    ?    ;compression method        2 bytes
zModTime    dw    ?    ;last mod file time         2 bytes
zModDate    dw    ?    ;last mod file date        2 bytes
zCrc32        dw    ?,?    ;crc-32               4 bytes
zCmpSiz        dw    ?,?    ;compressed size        4 bytes
zUncmpSiz    dw    ?,?    ;uncompressed size        4 bytes
zNameLen    dw    ?    ;filename length        2 bytes
zExtraLen    dw    ?    ;extra field length        2 bytes
zMbrName    db    ?    ;filename (variable size)
                ;extra field (variable size)
ZLocalEntry    ENDS

ZIPHDRLEN       equ     30              ;length of initial ZIP hdr read

;LZH header structure

lzhlfh    STRUC            ;Local file header
lUnk1        db    ?,?    ;char unknown1[2];    ;?
lCmpMeth    db    5 dup(?) ;char method[5];    ;compression method
lCmpSiz        dw    ?,?    ;long csize;    ;compressed size
lUncmpSiz    dw    ?,?    ;long fsize;    ;uncompressed size
lModTime    dw    ?    ;int ftime;    ;last mod file time
lModDate    dw    ?    ;int fdate;    ;last mod file date
lFAttr        db    ?    ;char fattr;    ;file attributes
lUnk2        db    ?    ;char unknown2;    ;?
lNameLen    db    ?    ;char namelen;    ;filename length
lMbrName    db    ?    ;char *fname;    ;filename
;lCrc16        dw    ?        ;int crc;    ;crc-16
lzhlfh    ENDS

LZHHDRLEN    equ    22    ;not including lMbrName or lCrc16


CSEG    segment public para 'CODE'
    assume    CS:CSEG,DS:CSEG,ES:CSEG

    public  ArcV

ArcV    proc    far
    push    bp            ; save BASIC reg
    mov    bp,sp            ; get parameter list pointer
    mov    CS:stkptr,sp        ; save stack ptr
    mov    CS:saveds,DS        ; save QB seg reg
    mov    CS:savees,ES        ; save QB seg reg
        call    Start                   ; do our thing

;    set DOS error level and exit
;       We aren't relying on the CF flag anymore to indicate errors.
;    Instead, check AL.
;    0 = success
;    1 = command line parm error
;    2..6 are file-related (not found, etc.)
;    11 = Invalid format (probably didn't find a member header)
;    13 = invalid data (probably a bad file header structure)
;    18 = Unexpected EOF ('no further files to be found')

Exit:    mov    sp,stkptr        ; restore entry stack value

        push    ax                      ;save error value

;Numerous errors could be returned

    or    al,al            ;no errors?
    jz    Exit_NoErr        ;yep, ok

    mov    bx,offset errtbl    ;assume unknown error
    mov    di,bx            ;various error values
    mov    cx,ERRTBLLEN        ;table length
    repne    scasb            ;find the offset
    jnz    Err_TblDone        ;unknown, BX has table start

        dec    di                       ;back up to actual error
        sub    di,bx                    ;current psn - start = relative nr
        mov    bx,di                    ;into BX for msg offset

Err_TblDone:
    shl    bx,1            ;*2 for words
Err_Unk:
    add    bx,offset errmsgtbl    ;table of addresses
    mov    dx,[bx]            ;ptr to string
    call    PrintS            ;output error msg
       
Exit_NoErr:

    mov    bx,word ptr outhdl    ; close listing file
        cmp     bl,STDOUT               ;never opened or STDERR?
        jna     Exit1                   ;not a real handle
        mov     ah,3eh                  ;close file handle
        int     21h
Exit1:
        mov     bx,word ptr archdl      ;close ARC/PAK/ZIP file
        or      bx,bx                   ; if it was opened
        jz      Exit2                   ; nope
        mov     ah,3EH                  ;close file handle
        int     21H
Exit2:

;Adding a test to insure we switched DTAs
;(so we don't blow away the caller's DTA with a vector 0:0!)

    lds    dx,dword ptr savedta    ;get orig DTA vector
    or    dx,dx            ;did we ever get it?
    jz    Exit_NoDTA        ;nope
    mov    ax,DS            ;check out seg
    or    ax,ax
    jz    Exit_NoDTA        ;nope
        mov     ah,1ah                  ;set DTA
        int     21h
Exit_NoDTA:

        les     ax,dword ptr CS:saveds  ;recover calling seg regs
                                        ;(low word is orig DS)
        mov     ds,ax
    ASSUME    DS:NOTHING,ES:NOTHING    ;a reminder

        pop     ax                      ;restore error level
        xor     ah,ah                   ;insure msb clear

    mov    bp,sp            ; parm ptr from entry
        mov     6[bp],ax                ;return retcd variable
    pop    bp
        ret     6                       ; clear parms from stack

    subttl    '--- constants, equates and work areas'
    page

CR    equ    13
LF    equ    10
BEL    equ    7
TAB    equ    9

STOPPER equ    0        ; end of display line indicator
ARCMARK equ    26        ; special archive marker
ARCVER  equ    10        ; highest compression code used

        even

stkptr  dw    0        ; stack pointer upon entry

arctitl db      CR,LF,'Archive:  '      ;keep this even
saveds  dw    0        ; QB seg reg
savees  dw    0        ; QB seg reg

    subttl    '--- i/o control variables'
    page

INBUFSZ equ     128     ;512    ; size of input buffer

;Completely reordered these runtime variables
;so we can purge them with one fell swoop

PURGESTART      equ     $

totsf    dw    0,0        ; average stowage factor
totlen  dw    0,0        ; total of file lengths
totsize dw    0,0        ; total of file sizes
totmbrs dw    0        ; total number of files

archdl  dw    0        ; file handle
fileptr dw    0        ; ptr to filename part of arcname
arclen  dw      0               ;full archive filename length
;260 long filename length
arcname db      260 dup (0)

outhdl  dw      0               ; handle for output listing
templen dw      0               ;output filename length
;260 long filename length
temp    db      260 dup (0)     ; and temporary file name

filelen dw      0,0             ;absolute archive file length
curpsn  dw      0,0             ;remember current file pointer psn

savedta dw    0,0        ; addr of QB dta
dta    db    48 dup (0)    ; data transfer area

        even

PURGELEN        EQU     ($ - PURGESTART) SHR 1  ;amount to purge each run

;    display lines for verbose

vhdr db CR,LF,'Name          Length    Stowage    SF   Size now  Date       Time    CRC '
 db CR,LF,'============  ========  ========  ====  ========  =========  ======  ===='
 db CR,LF
 db STOPPER

vflnm db 'Contents of: ',0

;vline    db    CR,LF
vline   label   byte
vname    db    14 dup (' ')
vlength db      '          '    ; length in archive
vstyle  db    '          '    ; compression method
vfactor db    ' xx%  '    ; compression factor
vsize    db    10 dup (' ')    ; actual file bytes
vdate    db    'dd '        ; creation date
 vmonth db    'mmm '
 vyear  db    'yy  '
 vtime  db    'hh:mm   '    ; creation time
 vcrc    db    'xxxx'        ; crc in hex
        db      CR,LF
    db    STOPPER

hundred dw    100        ; for computing percentages

;    final totals line

vthdr   db '------    --- --------            ----  --------',CR,LF
        db      '*Total    '
 vtmbrs db    '    '
 vtlen  db    8 dup (' '),'  '
    db    10 dup (' ')
 vtsf    db    '   %  '
 vtsize db    8 dup (' ')
    db    CR,LF        ; for tom
    db    STOPPER

 sign    db    ' '

styles  db    '  ----- '    ; 1 = old, no compression
    db    '  ----- '    ; 2 = new, no compression
    db    ' Packed '    ; 3 = dle for repeat chars
    db    'Squeezed'    ; 4 = huffman encoding
    db    'crunched'    ; 5 = lz, no dle
    db    'crunched'    ; 6 = lz with dle
    db    'Crunched'    ; 7 = lz with readjust
    db    'Crunched'    ; 8 = lz with readjust and dle
    db    'Squashed'    ; 9 = 13-bit lz with no dle
    db    ' Crushed'    ;10 = Pak10 file ---------Bob Simoneau

;ZIP compression types:

zstyles    label    byte
    db    '  Stored'    ;0 - The file is stored (no compression)
    db    '  Shrunk'    ;1 - The file is Shrunk
    db    'Reduced1'    ;2 - Reduced with compression factor 1
    db    'Reduced2'    ;3 - Reduced with compression factor 2
    db    'Reduced3'    ;4 - Reduced with compression factor 3
    db    'Reduced4'    ;5 - Reduced with compression factor 4
        db      'Imploded'      ;6 - New format
        db      'Deflate1'      ;7 - New format
        db      'Deflate2'      ;8 - New format

;LZH compression types are already coded as 5 chars of text
;in the compressed file.
;All we need to do is pad them out to the correct width.

months  db    'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec '

ARCPAK  =       0
ZIP     =       1
LZH     =       2
ftype   db      ZIP                     ;flag which type file

;4 types of archive file

ziptype    db    'ZIP'
arctype    db    'ARC'
paktype    db    'PAK'
lzhtype db      'LZH'
larctype db     'LZS'                   ;not enabled for now

;zfilesig db    50H,4BH,03H,04H         ;local file header signature
;zdirsig db     50H,4BH,01H,02H         ;central file header signature

ZSIG    equ     4B50H                   ;unique ZIP signature
ZFILESIG equ    0403H                   ;file member signature
ZDIRSIG equ     0201H                   ;central file header signature

;Centralizing errors at the exit point

;    1 = command line parm error
;    2..6 are file-related (not found, etc.)
;    11 = Invalid format (probably didn't find a member header)
;    12 = Invalid file type (not an ARC, PAK, ZIP)
;    13 = invalid data (probably a bad file header structure)
;    18 = Unexpected EOF ('no further files to be found')

errtbl db       0,1,2,3,4,5,6,11,12,13,18,25,27,29,30
ERRTBLLEN    equ    $ - errtbl

errmsgtbl dw    msg0,msg1,msg2,msg3
    dw    msg4,msg5,msg6,msg11
    dw    msg12,msg13,msg18,msg25
    dw    msg27,msg29,msg30

msg0    db    'Unknown error',0

msg1    db    'Invalid function number',0
msg2    db    'Archive file not found',0
msg3    db    'Path not found',0
msg4    db    'No handle available',0
msg5    db    'Access denied',0
msg6    db    'Invalid handle',0
msg11    db    'Archive header error',0
msg12    db    'Invalid file type',0
msg13    db    'Archive format error',0
msg18    db    'No further files to be found',0
msg25    db    'Disk seek error',0
msg27    db    'Disk sector not found',0
msg29    db    'Write error',0
msg30    db    'Read error',0


    subttl    '--- mainline processing'
    page
;
Start   proc    near

        mov     ax,CS                   ;just set ES for now
    mov    ES,ax
        ASSUME  DS:NOTHING,ES:CSEG      ;a reminder

;Insure all variables are cleared
    cld
    mov    di,offset PURGESTART
        xor     ax,ax                   ;clear all the variables
        mov     cx,PURGELEN             ;nr words to clear
        rep     stosw

;Move first parameter (output filename) into code space

        mov     si,word ptr 10[bp]      ; ptr to parameter vector
        lodsw                           ; get string length
        mov     cx,ax
        jcxz    Copy_Parm2              ;empty, forget it
        mov     di,offset templen       ;str length
        stosw                           ;save length
        mov     si,[si]                 ; get string offset
        rep     movsb                   ;copy in the string

Copy_Parm2:

;Now copy 2d parameter (target archive filename)

    mov    si,word ptr 8[bp]    ; ptr to parameter vector
    lodsw                ; get string length
        mov     cx,ax
        jcxz    Parm2_Done              ;forget it
        mov     di,offset arclen        ;archive name length
        stosw                           ;save length
        mov     si,[si]                 ; get string offset
        mov     ah,'a'                  ;constant for typecasing
Parm2_Upper:
        lodsb                           ;snarf char
        cmp     al,ah                   ;need uppercasing?
        jb      Parm2_NoU               ;nope
        sub     al,20H                  ;uppercase it
Parm2_NoU:
        stosb
        loop    Parm2_Upper

Parm2_Done:

;All done with DS

        mov     ax,CS
        mov     DS,ax
        ASSUME  DS:CSEG,ES:CSEG         ;a reminder

        mov     ax,STDOUT               ;assume no output filename
        cmp     temp,0                  ;any output filename?
        jz      Temp_Opened2            ;nope, use STDERR

;open for append
        mov     si,offset temp          ;open temporary file for output
        mov     bx,41h                  ;write only
        xor     cx,cx                   ;no special attributes
        mov     dx,11h                  ;create\append
        mov     ah,6ch                  ;open file
        mov     al,0
    int    21h
        jnb     Temp_Opened1            ;fine
        ret                             ;back to Exit, AL=error code
                                        ;CF set
Temp_Opened1:
         mov     outhdl,ax              ;save handle
;reset to eof
         mov     bx,outhdl              ;store file handle
         xor     cx,cx                  ;clear file offset
         xor     dx,dx                  ;clear file offset
         mov     ah,42h                 ;seek to file
         mov     al,2h                  ;seek to eof
         int     21h
         mov     ax,outhdl              ;restore file handle
Temp_Opened2:
         mov     outhdl,ax              ;store actual file handle

;Parse the target archive name
;Separate path from name
;Insure it's an ARC, PAK or ZIP type.

        mov     di,offset arclen        ;archive name length
        mov     ax,[di]                 ;snarf length
        inc     di                      ;bump to name proper
        inc     di
        mov     cx,ax                   ;into CX for scans to come
        jcxz    No_ArcName              ;no length, ergo no name

        mov     dx,ax                   ;save in DX for later
        xor     al,al                   ;will scan for AsciiZ terminator
        cmp     [di],al                 ;no name at all?
        jnz     Got_ArcName             ;yep

No_ArcName:
        mov     al,2                    ;'Archive file not found'
        ret                             ;back to Exit

Got_ArcName:

;We have some sort of target name.
;But is it a legal type?
;DX = filename length
;DI -> archive filename (arcname)

        add     di,dx                   ;+ length -> last char+1
        dec     di                      ;back up to last char
        mov     bx,di                   ;BX -> last char

        mov     al,'\'                  ;look for normal path delimiter
        mov     cx,dx                   ;length for scan
        std                             ;backwards scanning now
        repne   scasb
        jz      Got_Start               ;found one

    mov    di,bx            ;back to end
    mov    cx,dx            ;restore length
    mov    al,'/'            ;funny path delimiter
    repne    scasb
    jz    Got_Start        ;found one

    mov    di,bx            ;back to end .. sigh ..
    mov    cx,dx            ;restore length
    mov    al,':'            ;ok, how about a drive?
    repne    scasb
    jnz    No_Paths        ;nope, DI -> name start

Got_Start:
    inc    di            ;bump up to the separator
No_Paths:
    inc    di            ;bump to the first name char
    cld                ;forward again
    mov    fileptr,di        ;remember real filename start

;You MUST specify the type .ARC, .PAK, .ZIP, or .LZH.
;If .ARC or .PAK, we'll use the old code to display ARC-type files.
;Else if ZIP or LZH, it's a totally new format!
;We remember the type archiving format in 'ftype'.

;DS:SI -> filename's first char.

        mov     al,'.'                  ;find the separator
        mov     cx,word ptr 12          ;max of 12 chars
        repne   scasb                   ;find it
        jnz     BadType                 ;forget it

        mov     dx,di                   ;save pointer to file type
                                        ;(just past the separator)
    mov    ax,3            ;3 chars constant

    mov    ftype,ZIP        ;assume ZIP

    mov    si,offset ziptype    ;is it a ZIP?
    mov    di,dx            ;back to filename type
    mov    cx,ax            ;3 chars
    repz    cmpsb            ;compare
    jz    Got_Type        ;a match

        mov     ftype,ARCPAK            ;ok, assume ARC or PAK

        mov     si,offset arctype       ;is it an ARC?
    mov    di,dx            ;back to filename type
    mov    cx,ax            ;3 chars
    repz    cmpsb            ;compare
    jz    Got_Type        ;a match

    mov    si,offset paktype    ;is it a PAK?
    mov    di,dx            ;back to filename type
    mov    cx,ax            ;3 chars
    repz    cmpsb            ;compare
    jz    Got_Type        ;a match

;Adding .LZH types
        mov     ftype,LZH               ;ok, assume .LZH file

    mov    si,offset lzhtype    ;is it an LZH?
    mov    di,dx            ;back to filename type
    mov    cx,ax            ;3 chars
    repz    cmpsb            ;compare
    jz    Got_Type        ;a match

BadType:
         mov    al,12                   ;'Invalid file type'
         ret                            ;back to Exit

Got_Type:

;    find first matching file

    push    ES
    mov    ah,2fh            ; get current dta ptr
    int    21h            ; returned in ES:bx
    mov    savedta,ES
    mov    savedta[2],bx
    pop    ES

    mov    dx,offset dta        ; set local dta for murkers
    mov    ah,1ah
    int    21h

    call    OpenArc            ; see if archive exists
    jnb    ArcV1            ;ok
        jmp     ArcV_X                  ;nope, return, AL=error

;Display archive filename, header,
;then into a loop for each archive member.

ArcV1:  Print   vflnm
        mov     dx,fileptr              ;pointer to filename
        call    PrintS                  ;display, CR/LF
        jb      ArcV_X                  ;output failed

    Print    vhdr
        jb      ArcV_X                  ;output failed, AL = error

ArcVNext:
IF    DEBUG
    Print    debug1
    jmp    short debugj1
debug1    db    'Calling GetHdr',CR,LF,0
debugj1:
ENDIF
    call    GetHdr            ; load next header
        jb      ArcV_NoHdr              ;failed somehow, AL=error
                                        ;(could be EOF, which is ok)
IF    DEBUG
    Print    debug2
    jmp    short debugj2
debug2    db    'Calling ArcVgo',CR,LF,0
debugj2:
ENDIF
    call    ArcVgo            ;format, write out file report
        jb      Arcv_NoHdr              ;something failed, AL=error

IF    DEBUG
    Print    debug3
    jmp    short debugj3
debug3    db    'Calling Bump_ArcPtrs',CR,LF,0
debugj3:
ENDIF
        call    Bump_ArcPtrs            ;bump to next archive file
        jnb     ArcVNext                ;loop if ok, else AL=error
                                        ;(could be EOF)

ArcV_NoHdr:
    cmp    archdr.aCmpMeth,0    ; archive eof?
        jnz     ArcV_X                  ;nope, something else happened

        cmp     totmbrs,0               ;any totals?
        jz      ArcV_X                  ;nope
        push    ax                      ;save previous error value
        call    Format_Totals           ;yep, format and output
        pop     ax                      ;restore prev err value

ArcV_X: ret                             ;AL=error

Start   endp


;Format, display single line for each member
; On success, return:
;    CF clear
;    AL = 0
; On error, return:
;    CF set (because of output write fail)
;    AL = error code

ArcVgo    proc    near
    mov    di,offset vname        ; copy file name
    mov    si,offset archdr.aMbrName
    mov    cx,word ptr 13        ;up to 12 chars long, AsciiZ 0
ArcV3:
    lodsb
        or      al,al                   ; end of name?
    je    ArcV4
        stosb
        loop    ArcV3
        jmp     short ArcV5

ArcV4:
    mov    al,' '            ; pad with blanks
    rep    stosb
ArcV5:
; reduce the size/length to word values

        mov     bx,offset archdr.aCmpSiz        ;-> compressed size
        mov     cx,[bx]                 ;.lo
        mov     dx,2[bx]                ;.hi
        mov     bx,offset archdr.aUncmpSiz      ;-> uncompressed size
        mov     ax,2[bx]                ;.hi
        mov     bx,[bx]                 ;.lo

ArcV51: or    ax,ax            ; big number?
    jz    ArcV52            ; nope, can use it
        shr     ax,1                    ; yup, divide by two
        rcr     bx,1
        shr     dx,1
        rcr     cx,1
        jmp     short ArcV51

ArcV52:
    mov    ax,bx            ; low word of actual size
    mov    sign,' '
    cmp    ax,cx            ; arc member is larger?
    jb    ArcV520
        sub     ax,cx                   ; amount saved
        jmp     short ArcV56

ArcV520:
    sub    ax,cx
    neg    ax
    mov    sign,'-'

ArcV56:
    mul    hundred            ; to percentage
    add    ax,50
    adc    dx,0            ; round up percent
    or    bx,bx            ; empty file?
    jnz    ArcV53
        mov     ax,100
        jmp     short ArcV54

ArcV53: div    bx
ArcV54:
    cmp    ax,100            ; archive fouled?
    jbe    ArcV55
        sub     ax,ax
ArcV55:
        mov     di,offset vfactor-2     ;format stowage factor
    call    Asciify            ;display AX

    mov    al,sign
    mov    vfactor,al

        mov     cx,word ptr 3           ;gonna need it in a sec
        cmp     ftype,LZH               ;LZH type? (compression method
                                        ; is already text)
        jnz     ArcV_GetStyles          ;nope

;The LZH compression method (5 chars) is still in inbuf.

        mov     si,offset inbuf.lCmpMeth        ;-> 5-char compression
                                                ;   method string
    mov    di,si
        add     di,5                    ;point to beyond chars
        mov     ax,'  '                 ;need 3 trailing blanks
    stosw
    stosb
        mov     di,offset vstyle+1      ;indent to be neat
        jmp     short ArcV_GotStyle     ;skip

ArcV_GetStyles:

        mov     si,offset zstyles       ;assume ZIP
        cmp     ftype,ZIP               ;ZIP file?
        jz      ArcV55A                 ;yep
        mov     si,offset styles        ;ARC or PAK
ArcV55A:
    sub    bx,bx            ; determine style
    mov    bl,archdr.aCmpMeth
        dec     bl                      ;adjust for table offset
        shl     bx,cl                   ;multply by 8

        add     si,bx                   ;point into style table
    mov    di,offset vstyle
ArcV_GotStyle:
        inc     cx                      ;CX=4=words to move
        rep     movsw

        mov     bx,offset archdr.aCmpSiz        ;-> compressed size
        mov     ax,[bx]                 ;.lo
        mov     dx,2[bx]                ;.hi
        mov     bx,offset totsize       ;-> accumulated compressed size
        add     [bx],ax                 ;.lo
        adc     2[bx],dx                ;.hi

        mov     di,offset vsize         ;format file size
        call    Asciify_Long

        mov     bx,offset archdr.aUncmpSiz      ;-> uncompressed size
        mov     ax,[bx]                 ;.lo
        mov     dx,2[bx]                ;.hi
        mov     bx,offset totlen        ;-> total length accumulator
        add     [bx],ax                 ;.lo
        adc     2[bx],dx                ;.hi
   
        mov     di,offset vlength       ;format file length
        call    Asciify_Long

    mov    ax,archdr.aModDate    ; format file date
    call    GetDate

    mov    ax,archdr.aModTime    ; format file time
    call    GetTime

    mov    ax,archdr.aCrc16    ; format crc in hex
    mov    di,offset vcrc
    call    Cvh

        inc     totmbrs                 ;NOW bump total count
    Print    vline            ; display this file info
                                        ;(may return error)
    ret

ArcVgo    endp


    subttl    '--- load next archive header'
    page

;Adding ZIP file searching
; For ARC/PAK files, now testing to see if we're at the archive
; file end.  If so (a proper file), return with EOF (CF set but AL=0).
; Archive files may have picked up some garbage on the end
; (from XMODEM xfers, whatever).  We'll see if we at LEAST have
; enough data for an archive header.
; If not, assume EOF, ignoring garbage.
; If there's more than 29 bytes of garbage .. the header will be
; garbage and we're gonna report a format error .. but that's ok for now.
; Zip files have a definite ending (the central directory,
; and they'll look out for their own endings.
;
;Also returning CF and AL per any errors.

GetHdr  proc    near

    xor    ax,ax            ;handy 0
    mov    archdr.aCmpMeth,al    ;assume archive EOF

    cmp    ftype,ZIP        ;doing ZIP files?
        jnz     GH_NotZip               ;nope
        jmp     Get_ZipHdr              ;yep, they look out for themselves

GH_NotZip:
        cmp     ftype,LZH               ;doing an LZH file?
        jnz     GH_ArcPak_Hdr           ;nope
        jmp     Get_LZHHdr              ;yep

GH_ArcPak_Hdr:

;New code
;ARC/PAK headers look like this:
;aMbrFlag    db    1AH        ;unique header flag
;aCmpMeth    db    0        ;  compression code
;aMbrName    db    13 dup (0)    ;  file name
;aCmpSiz    dw    0,0        ;  file size in archive
;aModDate    dw    0        ;  creation date
;aModTime    dw    0        ;  creation time
;aCrc16        dw    0        ;  cyclic redundancy check
;aUncmpSiz    dw    0,0        ;  true file size, bytes

    mov    dx,offset archdr    ;read into here
    mov    cx,ARCHDRLEN        ;nr bytes to read
    mov    bx,archdl        ;archive file handle
    mov    ah,3FH            ;read from file/device
    int    21H
        jnb     GH_ChkHdr               ;read ok
        ret                             ;return CF set, AL=error

GH_ChkHdr:
        mov     bx,dx                   ;DS:BX -> structure start

    cmp    [bx].aMbrFlag,ARCMARK    ;start of header?
    jne    Hdr_InvalFmt        ;'invalid format', exit CF set

    mov    al,[bx].aCmpMeth    ;type compression
    cmp    al,ARCVER        ;reasonable code?
    ja    Hdr_InvalFmt        ;nope, funny stuff

    or    al,al            ; archive eof?
    je    Hdr_RetCF        ;yep, done, return CF set
                                        ;but AL=0 = not a REAL error
    cmp    al,1            ; old format?
    jne    GetHdrX            ; if so, it's short
        mov    si,offset archdr.aCmpSiz
        mov    di,offset archdr.aUncmpSiz
        movsw
        movsw
GetHdrX:
        xor     al,al                   ;return AL=0, success
    clc
    ret

Hdr_InvalFmt:
    mov    al,0BH            ;'invalid format'
Hdr_EarlyEOF:
        mov     [bx].aCmpMeth,al        ;signal EOF or invalid format
Hdr_RetCF:
    stc                ;return CF set, AL=error
    ret

GetHdr    endp


Get_ZipHdr    proc    near
;GetHdr Subroutine for ZIP files
;Reads in ZIP file entry.
;Then scans for the unique file entry signature.
; On success:
;    DS:BX -> file entry directory structure
;    CF clear
; Else CF set for failure

    call    Read_Zip_Entry
    jb    Get_ZHdrX            ;failed, AL=ERRORLEVEL

    mov    bx,offset inbuf            ;use for field base
    mov    di,offset archdr.aCmpMeth    ;moving into this structure

;Remember, the ZIP header we'll be snarfing data from looks like this:
;zVerMade    dw    ?    ;version needed to extract    2 bytes
;zBitflag    dw    ?    ;general purpose bit flag    2 bytes
;zCmpMeth    dw    ?    ;compression method        2 bytes
;zModTime    dw    ?    ;last mod file time         2 bytes
;zModDate    dw    ?    ;last mod file date        2 bytes
;zCrc32        dw    ?,?    ;crc-32               4 bytes
;zCmpSiz    dw    ?,?    ;compressed size        4 bytes
;zUncmpSiz    dw    ?,?    ;uncompressed size        4 bytes
;zNameLen    dw    ?    ;filename length        2 bytes
;zExtraLen    dw    ?    ;extra field length        2 bytes
;zMbrName    db    ?    ;filename (variable size)
                ;extra field (variable size)
;
;    and the ARC/PAK record we'll be formatting to
;    looks like this:
;aMbrFlag db    1AH
;aCmpMeth db    0            ;  compression code
;aMbrName db    13 dup (0)        ;  file name
;aCmpSiz dw    0,0            ;  file size in archive
;aModDate dw    0            ;  creation date
;aModTime dw    0            ;  creation time
;aCrc16  dw    0            ;  cyclic redunancy check
;aUncmpSiz  dw    0,0            ;  true file size, bytes

    mov    ax,[bx].zCmpMeth        ;compression method
    inc    al                ;bump to be non-0
    stosb                    ;->  aCmpMeth

;For now, assuming a normal file name (no paths)

    mov    ax,[bx].zNameLen        ;filename length
    and    ax,15                ;constrain to max 12 chars
    mov    cx,ax                ;into CX for move
    lea    si,[bx].zMbrName        ;pointer to actual filename
    rep    movsb                ;do the move
    xor    al,al                ;terminating 0
    stosb

    mov    di,offset archdr.aCmpSiz    ;bump past name
    mov    si,offset inbuf.zCmpSiz        ;-> compressed size
    movsw                    ;aCmpSiz.lo
    movsw                    ;aCmpSiz.hi

    mov    ax,[bx].zModDate        ;last mod date
    stosw                    ; -> aModDate
    mov    ax,[bx].zModTime        ;last mod time
    stosw                    ; -> aModTime
    mov    ax,[bx].zCrc32            ;CRC-32 value.lo
    stosw                    ; -> aCrc16
    mov    si,offset inbuf.zUncmpSiz    ;-> uncompressed size
    movsw                    ;aUncmpSiz.lo
    movsw                    ;aUncmpSiz.hi

    xor    ax,ax                ;return AX 0
    clc                    ;return CF clear
Get_ZHdrX:
    ret

Get_ZipHdr    endp        ;GetHdr subroutine



Get_LZHHdr    proc    near
;       GetHdr Subroutine for LZH headers
;    LZH file header has already been read in to inbuf.
;
;    If all is ok, we move the appropriate LZH fields into the
;    standard ARC/PAK structure (archdr) (so far as we can).
;
;    Gleaning from the LHARCDOC documentation, the 'laCmpMeth' field
;    (5 characters) can be:
;        '-lh0-'        stored as is (no compression)
;        '-lh1-'        compressed by LZHuf coding
;    There appear to be at least two more possible compression codes
;    that may appear:  "LARC type 4 and type 5" (whatever they may be!).
;
;    Assuming this field will ALWAYS be text, we are NOT gonna try to
;    snarf some magic code number out of the field, but will just
;    protect the field (in inbuf) and move the text directly into our
;    formatted display line later.
;
;    The only way we can test this as an LZH header is to look
;    for a '-%%%-' starting at the 2d header byte (the laCmpMeth
;    field).
;
;    On success:
;     DS:BX -> file entry directory structure
;     CF clear
;    Else CF set for failure

;  LZH files don't have a decent, clean EOF header.
;  We have to test for near-EOF the hard way.

    mov    di,offset archdr.aMbrFlag    ;moving into this structure
    mov    ax,001AH            ;fake ARC/PAK flag
    stosw                    ; and EOF compression code

    xor    ax,ax            ;handy 0
    mov    bx,offset filelen    ;-> file length
    mov    dx,[bx]            ;file length.lo
    mov    cx,2[bx]        ;file length.hi

    mov    bx,offset curpsn    ;for fast access   
    cmp    cx,2[bx]        ;length.hi = psn.hi?
    jnz    GL_AddHdr        ;nope
    cmp    dx,[bx]            ;length.lo = psn.lo?
    jz    GL_TrueEof        ;yep, we're exactly at EOF

GL_AddHdr:
    sub    dx,LZHHDRLEN        ;sub header length
    sbb    cx,ax    ;0        ;handle the borrow
    jb    GL_Eof            ;<0, beyond EOF
    sub    dx,[bx]            ;- file psn.lo
    sbb    cx,2[bx]        ;- file psn.hi, minus any borrows
    jnb    GL_NotEof        ;not near end .. ok

;There must've been junk on the file end.
;However .. there ALWAYS seems to be junk on the end.
; So .. we'll return no message at all (AL=0)
;If we ever figure out how to detect a TRUE LZH EOF,
;we can enable this ERRORLEVEL=18 business.

GL_Eof:
;    mov    al,18            ;'No further files to be found'
GL_TrueEof:
        stc                             ;CF set for EOF
    ret

GL_NotEof:

    push    di            ;save ptr -> archdr.aMbrName
    call    Read_LZH_Entry
    pop    di
    jb    Get_LHdrX            ;failed, AL=ERRORLEVEL

    mov    bx,offset inbuf            ;use for field base

;Remember, the LZH header we'll be snarfing data from looks like this:
;lUnk1    db    ?,?    ;char unknown1[2];    ;?
;lCmpMeth    db    5 dup(?) ;char method[5];    ;compression method
;lCmpSiz    dw    ?,?    ;long csize;    ;compressed size
;lUncmpSiz    dw    ?,?    ;long fsize;    ;uncompressed size
;lModTime    dw    ?    ;int ftime;    ;last mod file time
;                        ; (msdos format)
;lModDate    dw    ?    ;int fdate;    ;last mod file date
;lfAttr        db    ?    ;char fattr;    ;file attributes
;unknown2    db    ?    ;char unknown2;    ;?
;lNameLen    db    ?    ;char namelen;    ;filename length
;
;lMbrName    db    ?    ;char *fname;    ;filename
;;lCrc16    dw    ?    ;int crc;    ;crc-16
;
;    and the ARC/PAK record we'll be formatting to
;    looks like this:
;aMbrFlag db    1AH
;aCmpMeth db    0            ;  compression code
;aMbrName db    13 dup (0)        ;  file name
;aCmpSiz dw    0,0            ;  file size in archive
;aModDate dw    0            ;  creation date
;aModTime dw    0            ;  creation time
;aCrc16  dw    0            ;  cyclic redundancy check
;aUncmpSiz  dw    0,0            ;  true file size, bytes

    mov    al,[bx].lNameLen        ;filename length
    and    ax,15                ;constrain to max 12 chars
    mov    cx,ax                ;into CX for move
    mov    si,offset inbuf.lMbrName    ;-> actual filename
    rep    movsb                ;do the move
    xor    al,al                ;terminating 0
    stosb

;In LZH headers, the 2-byte CRC16 word lies immediately
;after the filename.
;Snarf it now and stuff in the ARC header.

    lodsw                    ;lCrc16
    push    ax                ;save a sec

    mov    di,offset archdr.aCmpSiz    ;bump past name
    mov    si,offset inbuf.lCmpSiz        ;-> compressed size
    movsw                    ;aCmpSiz.lo
    movsw                    ;aCmpSiz.hi

    mov    ax,[bx].lModDate        ;last mod date
    stosw                    ; -> aModDate
    mov    ax,[bx].lModTime        ;last mod time
    stosw                    ; -> aModTime
    pop    ax                ;CRC-16 value
    stosw                    ; -> aCrc16
    mov    si,offset inbuf.lUncmpSiz    ;-> uncompressed size
    movsw                    ;aUncmpSiz.lo
    movsw                    ;aUncmpSiz.hi

    xor    ax,ax                ;return AX 0
    clc                    ;return CF clear
Get_LHdrX:
    ret

Get_LZHHdr      endp                    ;GetHdr Subroutine


Read_LZH_Entry  proc    near            ;GetHdr Subroutine

    mov    dx,offset inbuf            ;read into here
    mov    cx,LZHHDRLEN            ;entry structure size
                        ;(does NOT include variable
                        ; length filename, and the
                        ;two CRC bytes following the
                        ;filename)
    mov    bx,archdl            ;file handle
    call    ReadZ_It            ;try to read in header
                        ;(up to filename)
    jb    ReadL_Eof            ;failed, AL=error

    mov    si,dx                ;structure start
    mov    al,'-'                ;test for '-l%-' or whatever
    cmp    [si].lCmpMeth,al        ;first part of compression
                        ;method string?
    jnz    ReadL_InvalDat            ;bogus, failed
        cmp     [si].lCmpMeth+4,al              ;how about last char?
        jz      ReadL_Ok1                       ;yep, fine
ReadL_InvalDat:
    mov    al,0DH                ;force to 'invalid data'
ReadL_Eof:
    mov    archdr.aCmpMeth,al        ;set per EOF or error
    stc                    ;return CF set
    ret

ReadL_Ok1:
    mov    dx,offset inbuf.lMbrName    ;-> lMbrName psn
    mov    cl,inbuf.lNameLen        ;length of member filename
    xor    ch,ch                ;clear msb
    call    ReadZ_It            ;read in the name
    jb    ReadL_Eof            ;failed
    add    dx,cx                ;bump buff ptr past name
    mov    cx,2                ;LZH CRC is a word
    call    ReadZ_It            ;read in the CRC word
    jb    ReadL_Eof            ;failed
    ret                    ;success

Read_LZH_Entry  endp                            ;GetHdr Subroutine


Read_Zip_Entry  proc    near                    ;GetHdr Subroutine

    mov    dx,offset inbuf            ;read into here
    mov    cx,ZIPHDRLEN            ;entry structure size
                        ;(does NOT include filename or
                        ; Extra fields, which are
                        ;dynamic)
    mov    bx,archdl            ;file handle
    call    ReadZ_It            ;try to read in header
                        ;(up to filename)
        jb      ReadZ_Eof                       ;failed, AL=error

        mov     si,dx                           ;->file signature
        lodsw                                   ;snarf first 2 chars
        cmp     ax,ZSIG                         ;ZIP header?
        jnz     ReadZ_InvalDat                  ;nope, bogus
        lodsw                                   ;file or central sig
        cmp     ax,ZFILESIG                     ;next member?
        jz      ReadZ_Ok1                       ;yep, fine
        cmp     ax,ZDIRSIG                      ;central directory?
                                                ;(means we're done)
        mov     al,0                            ;assume yes, EOF
        jz      ReadZ_Eof                       ;yep

ReadZ_InvalDat:
        mov     al,0DH                          ;'Invalid data'
ReadZ_Eof:
        mov     archdr.aCmpMeth,al              ;set per EOF or error
        stc                                     ;return CF set
    ret

ReadZ_Ok1:
    mov    dx,offset inbuf.zMbrName    ;move to zFilename psn
    mov    cx,inbuf.zNameLen        ;length of member filename
                                                ;fall thru to ...

;Common subroutine for ReadZ and Read_LZH
;   DX -> buffer
;   CX = bytes to read
;   BX MUST have archdl .. so protect BX!

ReadZ_It:
        mov     ah,3FH                          ;read from file/device
        int     21H
        jb      ReadZ_ItX                       ;failed, error in AX

;We'll update our curpsn file pointers later
;when we try to read past compressed file contents.

        cmp     ax,cx                           ;read all we expected?
        mov     ax,0                            ;clear AX
        jz      ReadZ_ItX                       ;yep, return CF clear
        mov     al,0BH                          ;assume unexpected EOF
                        ;('invalid format')
        stc
ReadZ_ItX:
        ret                                     ;CF, AL set per error

Read_Zip_Entry  endp                            ;GetHdr subroutine


;Common subroutine
;Bumps archive file pointers to next entry
; On success, return:
;    CF clear
;    AL = 0
; On failure (e.g., couldn't move ptrs), return:
;    CF set
;    AL = error

Bump_ArcPtrs    proc    near

        cmp     ftype,ZIP               ;ZIP file?
        jz      Next_ZEntry             ;bump file ptr to next entry

;Entirely new code

        mov     bx,offset archdr.aCmpSiz        ;-> encoded file length
        mov     dx,[bx]                 ;.lo
    mov    cx,2[bx]        ;.hi
    jmp    short Bump_Common    ;common code


;Positions ZIP file pointer to next local entry.
;We've already read in the entire header, plus the filename,
;so the file pointer should be just beyond the filename
;(at the Extra field).
;Move file pointers beyond the Extra field, and then past
;the actual entry data (the compressed size).

Next_ZEntry:

    mov    bx,offset inbuf            ;point back to structure
    mov    dx,[bx].zCmpSiz            ;size.lo
    mov    cx,[bx].zCmpSiz[2]        ;size.hi
    add    dx,[bx].zExtraLen        ;add in extra field length
    adc    cx,0                ;in case of carry

Bump_Common:

    mov    bx,archdl            ;file handle
    mov    ax,4201H            ;move pointer from current loc
    int    21H
        jb      Bump_X                          ;seek error
                                                ;return CF set, AL=error

;Updating curpsn variables now
;so the NEXT GetHdr call will have current data.
        mov     bx,offset curpsn
        mov     [bx],ax
        mov     2[bx],dx
        xor     ax,ax                           ;AX,CF clear
Bump_X:
    ret

Bump_ArcPtrs    endp


;Formats, displays totals

Format_Totals    proc    near

        mov     ax,totmbrs              ;total members
        mov     di,offset vtmbrs-2      ;format total members
        call    Asciify

        mov     bx,offset totlen        ;-> total actual file size
        mov     ax,[bx]                 ;.lo
        mov     dx,2[bx]                ;.hi

        push    ax                      ;save totlen.lo
        push    dx                      ; and totlen.hi

        mov     di,offset vtlen         ;format total actual file size
        call    Asciify_Long

        mov     bx,offset totsize       ;-> total compressed file sizes
        mov     ax,[bx]                 ;.lo
        mov     dx,2[bx]                ;.hi

        push    ax                      ;save totsize.lo
        push    dx                      ; and totsize.hi

        mov     di,offset vtsize        ;format total archive file size
        call    Asciify_Long

; reduce the total size/length to word values

        pop     dx                      ;totsize.hi
        pop     cx                      ;totsize.lo
        pop     ax                      ;totlen.hi
        pop     bx                      ;totlen.lo

ArcV2b: or    ax,ax            ; big number?
    jz    ArcV2c            ; nope, can use it
        shr     ax,1                    ; yup, divide by two
        rcr     bx,1
        shr     dx,1
        rcr     cx,1
        jmp     short ArcV2b

ArcV2c:
    mov    ax,bx
    mov    sign,' '        ; whata kludge
    cmp    ax,cx            ; arc is bigger than orig?
    jb    ArcV2c1
        sub     ax,cx                   ; amount saved
        jmp     short ArcV2f

ArcV2c1:
    sub    ax,cx
    neg    ax
    mov    sign,'-'

ArcV2f:
    mul    hundred            ; to percentage
    add    ax,50
    adc    dx,0            ; round up percent
    or    bx,bx            ; empty file?
    jnz    ArcV2d
        mov     ax,100
        jmp     short ArcV2e

ArcV2d: div    bx
ArcV2e:
        mov     di,offset vtsf-2        ;format stowage factor
        call    Asciify                 ;AX

    mov    al,sign
    mov    vtsf,al
    Print    vthdr            ; display totals
    ret

Format_Totals    endp


OpenArc proc    near            ; open new archive

    mov    dx,offset arcname
        mov     ax,3d40h                ; for input
    int    21h
        jnb     Open_GetSize            ;opened ok
        ret                             ;return CF set, AL=error

Open_GetSize:
        mov     bx,ax                   ;handle into BX
    mov    archdl,ax        ; save file handle

;We get the total file size now for later EOF testing.
    xor    dx,dx            ;0 offset
    xor    cx,cx
    mov    ax,4202H        ;from file end
    int    21H
    mov    filelen,ax        ;length.low
    mov    filelen[2],dx        ;length.hi
    xor    cx,cx            ;back to start
    xor    dx,dx
    mov    ax,4200H        ;psn file pointer from start
    int    21H
    ret                ;CF should be clear

OpenArc endp


ClosArc proc    near

    mov    bx,archdl        ; previous handle
    or    bx,bx            ; already open?
    jz    Closed
        mov     ah,3eh                  ; yes, so close it
        int     21H
Closed:    mov    archdl,0        ;flag as closed
    ret

ClosArc endp


;print null-terminated (AsciiZ) string like int 21h function 9
;Enter with DS:DX -> AsciiZ string
; destroys AX
; On success, return:
;    CF clear
;    AL = 0
; On failure (write fail), return:
;    CF set
;    AL = error

PrintS  proc    near

        push    di
    push    bx
    push    cx

        mov     cx,0FFFFH               ;max scan
        xor     al,al                   ;handy 0
        mov     di,dx                   ;string start
        repne   scasb                   ;find the terminator
        inc     cx                      ;adjust
        not     cx                      ;CX=length
        mov     bx,outhdl               ; using std out or temp file
        or      bx,bx                   ;never opened?
        jnz     Print_S1                ;nope, we got a handle
        inc     bx                      ;make it STDERR
Print_S1:
    mov    ah,40h            ; write to file
    int    21h
        jnb     PrintS_Done             ;fine

;What happens if we're trying to write to an output file
;and THAT fails?  Even error msgs can't get out.
;Switch to StdOut instead..

        mov     di,ax                   ;save error level
        mov     bx,STDOUT               ;force to STDERR
        mov     outhdl,bx               ;and for future output
        mov     ah,40H                  ;write to STDOUT
        int     21H                     ;(CX,DX unchanged)
        mov     ax,di                   ;restore orig error
        stc                             ;return CF set
 
PrintS_Done:
    pop    cx            ; recover registers
    pop    bx
    pop    di
    ret

PrintS  endp

    page
;
;    format the time (in AX)

time    record  hour:5,min:6,sec:5    ;packed time

GetTime proc    near            ;format the date
    mov    di,offset vtime
    or    ax,ax            ;it is zero?
    jz    GotTime

    push    ax            ;save date
    and    ax,mask hour        ;get hour part
    mov    cl,hour            ;bits to shift
    shr    ax,cl
    call    Cnvrt1
    stosw
    mov    al,':'
    stosb

GT3:    pop    ax            ;get the time back
    and    ax,mask min        ;get min part
    mov    cl,min            ;bits to shift
    call    Cnvrt
    stosw
GotTime:ret
GetTime endp


Cnvrt2  proc    near            ;convert to ascii
    call    Cnvrt
    cmp    al,'0'            ;suppress leading zero
    jne    Cnvrtd
        mov     al,' '
        ret

Cnvrt:  shr    ax,cl
Cnvrt1: aam                ;make al into bcd
    or    ax,'00'            ; and to ascii
    xchg    al,ah
Cnvrtd: ret
Cnvrt2  endp

    page
;
;    format the date (in AX)

date    record  yr:7,mo:4,dy:5        ;packed date

GetDate proc    near            ;format the date
    or    ax,ax            ;is it zero?
    jz    GotDate

    push    ax            ;save date
    and    ax,mask yr        ;get year part
    mov    cl,yr            ;bits to shift
    call    Cnvrt
    mov    di,offset vyear
    or    al,'8'            ;adjust for base year
    stosw

    pop    bx            ;get the date back
    push    bx            ;save it
    and    bx,mask mo        ;get month part
    mov    cl,mo            ;bits to shift
    shr    bx,cl
    add    bx,bx            ; form month table index
    add    bx,bx
    lea    si,word ptr months-4[bx]
    mov    cx,word ptr 3
    mov    di,offset vmonth
    rep    movsb

    pop    ax            ;get the date back
    and    ax,mask dy        ;get day part
    mov    cl,dy            ;bits to shift
    call    Cnvrt
    mov    di,offset vdate
    stosw
GotDate:ret
GetDate endp

    page

;A severely hacked single/double precision number conversion function.
;Originally from JMODEM, but severely hacked by Toad Hall.
;  ES:DI -> string
;  Destroys everything almost.

;Enter here if integer in AX
Asciify    proc    near

    xor    dx,dx            ; clear fake long.hi
    mov    si,ax            ;move integer into SI
    xor    ah,ah            ;clear msb (flag)
    jmp    short Ascii_Ax        ;jump into the code

;Enter here if long integer in DX:AX.
Asciify_Long:

    mov    si,ax            ;move long.lo into SI
    xor    ah,ah            ;clear msb (flag)
Comment    ~
    MOV    CX,3B9AH        ; Get billions
    MOV    BX,0CA00H
    CALL    Subtr            ; Subtract them out

    MOV    CX,05F5H        ; Get hundred-millions
    MOV    BX,0E100H
    CALL    Subtr            ; Subtract them out
Comment    ends    ~

        and     dx,4FFH                 ;seems likely
    MOV    CX,word ptr 0098H    ; Get ten-millions
    MOV    BX,9680H
    CALL    Subtr            ; Subtract them out

    MOV    CX,word ptr 000FH    ; Get millions
    MOV    BX,4240H
    CALL    Subtr            ; Subtract them out

    MOV    CX,word ptr 1        ; Get hundred-thousands
    MOV    BX,86A0H
    CALL    Subtr            ; Subtract them out

Ascii_Ax:
    xor    cx,cx            ; Get ten-thousands
    MOV    BX,2710H
    CALL    Subtr            ; Subtract them out
    MOV    BX,03E8H
    CALL    Subtr            ; Subtract them out

    MOV    BX,word ptr 0064H
    CALL    Subtr            ; Subtract them out
    MOV    BX,word ptr 10
    CALL    Subtr            ; Subtract them out
    mov    ax,si            ;residual in SI
    add    AL,'0'            ; Add bias to residual
    stosb                ; Put in the string
    RET

;Common subroutine for Asciify

Subtr:    mov    al,'0'-1

Subtr1:    INC    al            ; Bump the digit character
    SUB    si,BX            ; Dword subtraction
    SBB    DX,CX
    JNB    Subtr1            ; Continue until a carry

    ADD    si,BX            ; One too many, add back
    ADC    DX,CX            ;   and the remainder

    cmp    al,'0'
    jnz    Subtr2            ;nope, turn off leading flag, stuff
        or      ah,ah                   ;no more leading spaces?
        jnz     Sub_Stuff               ;right, stuff the '0'
        mov     al,' '                  ;make it neat with leading spaces
Sub_Stuff:
    stosb                ;stuff the char
    RET

Subtr2:    inc    ah            ;turn off leading space flag
    stosb
    ret
Asciify    ENDP


;Convert 16-bit binary word in AX
;to hex ASCII string at ES:DI
;(No need to save any registers)

hexchar db    '0123456789ABCDEF'

Cvh    proc    near

        mov     si,offset hexchar       ;for faster access
    mov    dx,ax            ; save 16-bits

    mov    bl,dh            ; third nibble
        mov     cx,0F04H                ;CL=4 for shifting,
                                        ;CH=0FH for masking
    shr    bl,cl
        mov     al,[si][bx]             ;snarf hex char
    stosb

    mov    bl,dh            ; last nibble
        and     bl,ch                   ;0fh
        mov     al,[si][bx]             ;snarf hex char
    stosb

    mov    bl,dl            ; first nibble
    sub    bh,bh
        shr     bl,cl                   ; isolate (CL still 4)
        mov     al,[si][bx]             ;snarf hex char
    stosb

    mov    bl,dl            ; second nibble
        and     bl,ch   ;0fh            ; isolate
        mov     al,[si][bx]             ;snarf hex char
    stosb
    ret

Cvh    endp

    subttl    '--- i/o data areas'

ArcV    endp

archdr  db      30 dup (0)              ; i/o area for a header

inbuf    db    INBUFSZ dup (0)        ;just big enough for ZIP
                                        ;directories and filenames

CSEG    ENDS
    END
dndbbs project:

Links to my MUD: (strictly 16-bit); AKA XP:

Dndbbs executables
http://www.filegate.net/pdn/pdnbasic/dnd50a1e.zip

Dndbbs source
http://www.filegate.net/pdn/pdnbasic/dnd50a1s.zip

Dndbbs upgrade
http://www.filegate.net/pdn/pdnbasic/dnd50a1u.zip

DNDDOOR - https://bit.ly/EriksDNDDoor DUNGEON - https://bit.ly/EriksDungeon
Interpreter - https://bit.ly/EriksSICK Hex Editor - https://bit.ly/EriksHexEditor Utilities - https://bit.ly/EriksUtils
QB45 files: - https://bit.ly/EriksQB45 QB64shell - https://bit.ly/QB64shell Some old QB64 versions: - https://bit.ly/OldQB64
Reply