Category : Recently Uploaded Files
Archive   : HPTINY20.ZIP
Filename : CONCAT.ASM

 
Output of file : CONCAT.ASM contained in archive : HPTINY20.ZIP
; File: CONCAT.ASM System: Utility Version: 1.0 Date: 07-22-95 ;

;-----------------------------------------------------------------------------
; Utility concatenates text files, with directory entry as header for each.
; Allows multiple file specs, line numbering, and file ordering. Assembles to
; about 0.7K code, with syntax display filling out to 1K.
;
; A batch file (with FOR, SHIFT, and FIND) will do most of this, but opted for
; COM utility to include line numbering and file ordering.
;-----------------------------------------------------------------------------
MOVE EQU xchg ; Saves byte on some AX moves
STDOUT EQU 1 ; Redirectable display handle
STDERR EQU 2 ; Non-redirectable display handle
JBEOP EQU 076h ; Opcode for descending sort
HUNMILHI EQU 1525 ; Hundred million high word
HUNMILLO EQU 57600 ; Hundred million low word

SLACK EQU LOW (256 - LOW (OFFSET EndData - OFFSET ConCat))

NUMFLAGS EQU 6 ; Switch count--see ParmList
SHIFTBIT EQU 1 SHL (16-NUMFLAGS) ; Bit for GetFlags test
FILEMAX EQU 1024 ; Maximum number of files
LOWSIZE EQU OFFSET EndData + SLACK; PSP/program/fixed data 1.25K
LISTSIZE EQU 16*FILEMAX ; File list size 16K
INSIZE EQU 4000h ; Input buffer size 16K
OUTSIZE EQU 4000h ; Output buffer size 16K
MINSTACK EQU 300h ; Minimum stack size 0.75K
MINSIZE EQU LOWSIZE + LISTSIZE + INSIZE + OUTSIZE + MINSTACK ; 50K

Code_Seg SEGMENT
ASSUME CS:Code_Seg,DS:Code_Seg,ES:Code_Seg

ORG 100h
;-----------------------------------------------------------------------------
; Set DTA buffer, display syntax, and check RAM. Assume DOS 2.0+.
;-----------------------------------------------------------------------------
ConCat: cld ; Fixed--could assume from DOS
mov dx,OFFSET TempBuf ; Also syntax offset
mov ah,1Ah
int 21h ; Move DTA buffer away from 80h
mov cx,OFFSET EndData - OFFSET TempBuf
call DispInfo ; Display syntax, non-redirectable
cmp sp,MINSIZE ; Check space requirements
jnc Start ; Enough room? Ahead, else abort
;-----------------------------------------------------------------------------
; Beep/DispInfo are non-redirectable. Display is redirectable.
;-----------------------------------------------------------------------------
Beep: mov cx,1
beepmax: mov dx,OFFSET BeepMsg
DispInfo: mov bx,STDERR ; To console, non-redirectable
write: mov ah,40h ; Write CX bytes at DX
int 21h
wout: ret ; Return or exit via PSP

Display: mov bx,STDOUT ; To console, redirectable
jmp SHORT write
;-----------------------------------------------------------------------------
; Populate file list, setting file count BP. Uses BH flags to set sort key in
; DTAtoList. List entry is key/name/extension/spec number (4/8/3/1). Need
; BP/BL zero initially.
;-----------------------------------------------------------------------------
slloop: mov ah,4Eh ; DOS Find First
int 21h ; CX zero and DX set from PrepName
jc FillList ; No files? To next spec

slfile: call DTAtoList ; Construct list entry, preserving zero
; CH and pointing DI past key
mov [di+11],bl ; Place spec number at end of entry
inc bp ; Bump file count
cmp di,OFFSET FileList+LISTSIZE-12
jae Beep ; Reached limit? Issue beep warning
; and ignore rest of files
mov ah,4Fh ; DOS Find Next
int 21h
jnc slfile ; Another file? Loop

FillList: inc bx ; Point BL to next spec
mov dx,bx ; Spec number to DL (1 initially)
call PrepName ; If inequality, points DX to TempBuf
jne slloop ; and zeroes CX for Find First

slout: ret
;-----------------------------------------------------------------------------
; Initialization then main loop. In main loop and in DispFile, BP is LF flag.
;-----------------------------------------------------------------------------
Start: call CmdCXDI ; Ready CX/DI
inc cx ; Include CR in scan, CX non-zero now
xor bx,bx ; Clear BH flags, zero BL spec number
call GetFlags ; Sets flags in BH
xor bp,bp ; File pointer BP (count)
call FillList ; Populates file list, bumping BP
mov cx,bp ; Count to CX for sort
jcxz wout ; No files? Done

call SortList ; Sorts list, zeroing CX
xchg cx,bp ; Zero pointer BP and set CX for loop
mainloop: push cx ; Open next file and place header info
call OpenNext ; in TempBuf, positioning DI after
mov ax,0A0Dh
stosw ; Append CrLf to header info
mov dx,di ; Start of bar line for display below
push ax
mov al,'Ä' ; Construct bar line
mov cx,36
rep stosb ; Also zeroes CH for display calls
pop ax
push di ; Save offset for after file display
stosw ; Append pair of CrLfs to bar
stosw
mov cl,36 + 2 ; Display first line bar
call Display
sub dx,cx ; Postpone LF so that if no redirection,
dec cx ; then next Display overwrites
call DispInfo ; Non-redirectable display of header
mov cl,36 + 2 + 36 + 4 ; Prepare redirectable display
call DispFile ; Display header/bar then file
pop dx ; Offset of CrLf pair in TempBuf
mov cl,2 ; CH is zero from DispFile
shl bp,1 ; Test high bit for trailing LF in file
jnc onecrlf ; Yes? Then one CrLf will do

call Display ; Else terminate last line first
onecrlf: call Display
call DispInfo ; Conclude with non-redirectable LF
mov ah,3Eh ; Close file
call Read21h
shr bp,1 ; Clear high bit for next file
inc bp ; Point to next file
pop cx
loop mainloop ; Loop, else fall to PSP exit...
;-----------------------------------------------------------------------------
; Set CX/DI to length/81h for command-line scan.
;-----------------------------------------------------------------------------
CmdCXDI: mov di,80h
mov al,[di] ; Command-line size 0-127
inc di
cbw ; Zero AH
MOVE cx,ax
ret
;-----------------------------------------------------------------------------
; Copy from SI to DI until dot or null, assumed within 9 bytes. Then space
; pad to 8 bytes. If input equality, just pad. Return equality if null
; terminated copy (for second call). Assume input CH zero.
;-----------------------------------------------------------------------------
Copy8: mov cl,9 ; Assume CH zero
je cspad ; Input equality forces pad only (for
; second call with no extension)
csloop: lodsb
cmp al,"."
je cspad
cmp al,0
je cspad
stosb
loop csloop

cspad: dec cx ; Assume null or dot reached
cmp al,0 ; Exit condition, then fall...
;-----------------------------------------------------------------------------
; Store CX spaces to DI.
;-----------------------------------------------------------------------------
Blanks: mov al,' '
rep stosb
ret
;-----------------------------------------------------------------------------
; Copy DTA info to list entry pointed to by BP. Use BH flags to set sort key.
; On exit, leave DI pointing past key and preserve input zero CH.
;-----------------------------------------------------------------------------
DTAtoList: call SetAX ; Point AX to entry
push ax ; Save as key offset
add al,4 ; Ahead 4 to name, setting inequality
push ax ; Save
MOVE di,ax ; CH zero and inequality set
mov si,OFFSET TempBuf+30 ; ASCIIZ name offset (up to 13 bytes)
call Copy8 ; Copy space-padded name to entry
call Copy8 ; Copy space-padded extension to entry,
; with 4-byte overrun of entry ok
pop si ; Points to name in list entry
pop di ; Entry start (will be 4-byte sort key)
mov ax,bx ; Switches N-E-D-S are high 4 bits, with
shl ax,1 ; that order of precedence
jc dtmove ; /N name? Ahead--replicating first
; four name characters is ok
add si,8 ; Extension currently space-padded to
shl ax,1 ; 4 bytes--spec number inserted later
jc dtmove ; /E extension? Ahead

mov si,OFFSET TempBuf+22 ; Time and date offset--4 bytes
shl ax,1
jc dtswap ; /D date? Ahead

mov si,OFFSET TempBuf+26 ; File size offset--4 bytes
shl ax,1
mov ax,bp ; Anticipate natural order--2 bytes
jnc natural ; Not /S size? Ahead

dtswap: lodsw ; For date-time and size, must reverse
MOVE dx,ax ; order of 4 bytes
lodsw
xchg dl,dh
natural: xchg al,ah ; For natural order, reverse file
stosw ; count bytes--DX irrelevant
MOVE ax,dx
stosw
ret

dtmove: movsw ; For name and extension, direct
movsw ; copy is correct
ret
;-----------------------------------------------------------------------------
; Sort file list with CX entries. If default natural order, key assures no
; swaps. CX zeroed on exit.
;-----------------------------------------------------------------------------
SortList: dec cx
je nosort ; Only one file? Out

mov di,OFFSET FileList
mov dx,16 ; Fixed--size of entry
sloutlp: push cx ; Simple order ný sort--first pass
mov si,di ; places max or min element in
add si,dx ; first position, next pass sets
slinlp: push cx ; second position, etc.
mov cx,dx
push di
push si
repe cmpsb ; Compare entries at DI/SI
pop si
pop di
SortLoc = $ ; /R changes to jbe
jae noswap

mov cx,dx
push di
push si
swaploop: mov al,[di] ; Swap entries at DI/SI
xchg al,[si]
stosb
inc si
loop swaploop

pop si
pop di
noswap: pop cx
add si,dx
loop slinlp

pop cx
add di,dx
loop sloutlp

nosort: ret
;-----------------------------------------------------------------------------
; Copy DLth file specification to TempBuf, make ASCIIZ, set DX to offset of
; TempBuf, and set DI to short name offset in TempBuf. Return equality if no
; ALth specification found. If inequality on exit, CX zeroed.
;-----------------------------------------------------------------------------
PrepName: call CmdCXDI ; Set CX/DI to length/81h
mov ax,'/ ' ; Space low, slash high
pnloop: xor dh,dh ; Force equality too
repe scasb ; If CX was zero, equality passes
je pnout ; End of command-line? Out

dec di ; Point back to name start
inc cx ; Adjust CX (still excludes CR)
cmp [di],ah ; Check if hit first switch
je pnout ; Switch? Then file specs done

mov si,di ; Ready SI for copy
repne scasb ; Scan past name, looking for space
dec dx ; Decrement input count
jne pnloop ; Not done? Loop

mov dx,OFFSET TempBuf ; File spec destination--DX free now
mov cx,di
sub cx,si ; CX now copy length
mov di,dx ; TempBuf offset
rep movsb ; Copy file spec, zeroing CX
mov [di],cl ; Make spec ASCIIZ (ok if after space)
pnbacklp: mov al,[di] ; Back up to find start of short name
cmp al,'\'
je pngotloc ; Backslash? Exit loop
cmp al,':'
je pngotloc ; Colon? Exit loop
dec di
cmp di,dx ; DX still TempBuf offset
jnb pnbacklp ; Not before start of full name? Loop

pngotloc: inc di ; Short name start, setting inequality
pnout: ret ; CX also zero if inequality
;-----------------------------------------------------------------------------
; Open next file for read and fill header name/size/date/time info.
;-----------------------------------------------------------------------------
OpenNext: call SetAX ; Point AX to 16-byte list entry
add al,4 ; Point past key to name
MOVE si,ax
mov dl,[si+11] ; Fetch spec pointer from last position
push si
call PrepName ; Also sets DI for copy and DX for open,
pop si ; and zeroes CX for post-open
movsw ; Copy 4 word name
movsw
movsw
movsw
mov al,'.'
stosb
movsw ; Copy extension
movsb
mov ax,3D00h ; Open read-only (DX is TempBuf offset)
stosb ; Make name ASCIIZ
call Int21h ; May abort internally
mov WORD PTR InHandle,ax ; Save file handle for reads/etc.
dec di ; Back to null--CH zero from PrepName
mov cl,9 ; Blank separator and 8 digit positions
call Blanks ; Points DI to right of rightmost digit

push di ; Save for date-time stores
call SeekEOF ; To EOF, setting DX:AX to file size
hugeloop: sub ax,HUNMILLO ; Truncate display size to 8 decimal
sbb dx,HUNMILHI ; digits
jnc hugeloop ; About 20+ iterations max (if 2GB file)

add ax,HUNMILLO ; Undo last subtraction
adc dx,HUNMILHI
div TenThou ; High 4 digits in AX, low 4 in DX
MOVE bx,ax ; Save in BX (also flag to first call)
MOVE ax,dx ; Remainder to AX as low 4 digits
call AXtoASC ; Zeroes AX too
xchg ax,bx ; Restore AX, zeroing BX flag for
test ax,ax ; possible second call
je notbig ; Under 10,000 bytes? Ahead

call AXtoASC ; Zeroes AX for next
notbig: call SeekZero ; Rewind (AX zero in/out), setting BX
pop di ; One left of date position

mov WORD PTR LineCnt,ax ; Rezero line count, AL zero for next
mov ah,57h ; DOS get file date-time, BX handle
int 21h ; Output CX/DX is time/date
push cx ; Save time
mov cx,0F05h
mov bl,' '
call StoDgts ; Month
mov cx,1F00h
mov bl,'/'
call StoDgts ; Day
mov al,dh
shr al,1 ; AL now year offset from 1980
add al,80
subloop: sub al,100 ; Modulo 100 loop
jnc subloop

add al,100
call StoAam ; BL still '/'
pop dx ; Restore time
mov cx,1F0Bh
mov bl,' '
call StoDgts ; Hour 0-23
mov cx,3F05h ; Fall, storing minutes
mov bl,':'
StoDgts: mov ax,dx ; Not MOVE
shr ax,cl
and al,ch
StoAam: xchg ax,bx
stosb ; Prefix separator
xchg ax,bx
aam
or ax,3030h ; To ASCII
xchg al,ah
stosw ; Store two digits
onout: ret
;-----------------------------------------------------------------------------
; Display DX/CX header then file, updating LF flag in BP high bit. Flag has
; dual use--for line numbering here and for post-exit check. CX zero on exit.
;
; Could omit back seek and overrun test if OutBuf 6 times larger than InBuf.
;-----------------------------------------------------------------------------
endseek: call SeekEOF ; DX:AX return ignored
bufloop: mov cx,di
pop dx ; Start of OutBuf to DX
sub cx,dx ; OutBuf byte count to CX
DispFile: call Display ; Display buffer (or header initially)
mov dx,OFFSET InBuf
mov cx,INSIZE
mov ah,3Fh
call Read21h ; Read to InBuf, returning size AX
MOVE cx,ax
jcxz onout ; No data? EOF, so done

mov si,dx ; Start of InBuf
mov di,OFFSET OutBuf ; Start of OutBuf
push di ; Save till display
byteloop: cmp di,OFFSET OutBuf + OUTSIZE
ja backseek ; Past output buffer? Out (occurs
; only if /L)
lodsb
cmp al,26 ; Test for EOF character
je endseek ; Yes? Mimic COPY /A and quit file

shl bp,1 ; Extract LF flag bit
jc chklf ; Previous character non-LF? Ahead

LineLoc = $ + 1 ; /L changes to effective nop
jmp SHORT chklf ; To chklf (or to next instruction)

push cx ; Insert line number
push ax
scasw
scasw ; Point DI right of rightmost digit
LineCnt = $ + 1
mov ax,0
inc ax ; Bump and save line count
mov WORD PTR LineCnt,ax ; BX is non-zero (file handle)
push di
call AXtoAsc ; Store AX as 4 zero-padded digits
pop di
mov al,' '
stosb ; Space after number, advancing DI
pop ax
pop cx
chklf: cmp al,10
je stobit ; Line feed? Ahead with clear carry

stc ; Set flags non-LF as previous byte
stobit: rcr bp,1 ; Reinsert LF flag bit, also restoring
stosb ; file pointer
ignore: loop byteloop ; Loop if more bytes

backseek: MOVE ax,cx ; Seek back CX bytes (usually zero)
neg ax
cwd ; 0 or -1 to DX
xchg dx,ax ; AX:DX now relative seek offset
MOVE cx,ax ; Now CX:DX, zero or small negative
mov ax,4201h ; Seek from current offset
call Read21h ; Returned DX:AX ignored
jmp SHORT bufloop ; Display, then read next buffer
;-----------------------------------------------------------------------------
; Store AX leftward from DI-1 as 4 ASCII digits, zero-padding if BX non-zero.
;-----------------------------------------------------------------------------
AXtoAsc: mov cx,4
ascloop: xor dx,dx
div WORD PTR Ten ; Divide by 10
or dl,'0' ; Make remainder ASCII digit
dec di ; Move left
mov [di],dl ; Store
test ax,ax
jne asccont ; Still non-zero? Continue

test bx,bx ; Check if zero-padding wanted
asccont: loopne ascloop ; Loop up to CX digits

ret ; AX/CH zero on exit
;-----------------------------------------------------------------------------
; Seek to BOF (AL = 0) or EOF (AL = 2). Also entries for open/read/close.
;-----------------------------------------------------------------------------
SeekEOF: mov al,2
SeekZero: mov ah,42h ; Seek
cwd ; Zero CX:DX
xor cx,cx
InHandle = $ + 1 ; Set by OpenNext
Read21h: mov bx,0
Int21h: int 21h
jc abort ; Problem? Beep-abort

ret
;-----------------------------------------------------------------------------
; Set BX flags with command-line scan. Input DI as 81h, CX as length with
; trailing CR included, and BX as zero. At exit, /L and /R are handled.
;-----------------------------------------------------------------------------
flagloop: mov si,OFFSET ParmList
swloop: lodsb
cmp [di],al ; AL is upper case switch
je match ; Match? Save flag in BH

or al,20h ; To lower case
cmp [di],al
je match ; Match? Save flag in BH

shl ah,1
jne swloop ; More? Loop, else invalid switch

abort: call Beep ; Beep and abort, with DOS
int 20h ; closing any open files

match: or bh,ah ; Save flag
GetFlags: mov ax,'/' OR SHIFTBIT ; Switch low, test bit high
repne scasb ; Get inequality eventually due to CR
je flagloop ; Switch? Back

shl bx,1 ; Test for /R
jnc chknum ; No? Ahead, else replace jae with jbe
; in SortList for reverse sort
mov al,JBEOP
mov BYTE PTR SortLoc,al
chknum: shl bx,1 ; Test for /L
jnc saout ; No? Out, else enable line numbers
; with short jmp 0 in DispFile
mov BYTE PTR LineLoc,cl ; Fall harmlessly...
;-----------------------------------------------------------------------------
; Set AX to offset of entry in FileList pointed to by BP.
;-----------------------------------------------------------------------------
SetAX: mov ax,bp ; File pointer (high bit may be flag)
mov cl,4
shl ax,cl ; Entries are 16 bytes
add ax,OFFSET FileList
saout: ret
;-----------------------------------------------------------------------------
; Program data, besides LineCnt/InHandle embedded in code operamds. Syntax
; message overwritten. TempBuf holds file specifications and 43-byte DTA info
; in FillList, then holds full filenames followed by header data (up to 128+78
; bytes) in main display loop.
;-----------------------------------------------------------------------------
ParmList DB "SDENLR" ; Upper case
Ten DW 10 ; Divisor in AXtoAsc
TenThou DW 10000 ; Divisor in NextOpen
BeepMsg DB 7 ; Bell character for warning/abort

TempBuf = $ ; Filespecs/DTA info/filenames/header

DB 13,10
DB "Syntax: CONCAT files [/Linenums][/Reverse][/Name|Ext|Date|Size]" ,13,10
DB "Example: CONCAT *.C *.ASM \INC\*.* /L/E >SOURCE.TXT" ,13,10
DB 10
DB "Concatenates text files with directory entry as header for each." ,13,10
DB "Line numbers/file sorting optional. Intended as paper saver with" ,13,10
DB "HPTINY print. Need 50K RAM. Limit 1024 files--CRH." ,13,10
DB 10

EndData = $

FileList = $ + SLACK
InBuf = $ + SLACK + LISTSIZE
OutBuf = $ + SLACK + LISTSIZE + INSIZE

Code_Seg ENDS
END ConCat


  3 Responses to “Category : Recently Uploaded Files
Archive   : HPTINY20.ZIP
Filename : CONCAT.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. 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/