home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
filutl
/
ascify13.arc
/
ASCIFY.ASM
next >
Wrap
Assembly Source File
|
1989-02-09
|
41KB
|
1,244 lines
page 60,132
; Copyright (C) 1987 Mark Adler Pasadena, CA
; All rights reserved.
title ASCIFY -- convert binary files to/from an ascii encoded form
name ASCIFY
comment #
Version History -
1.0 11 Apr 87 First distrbuted version.
1.1 14 Apr 87 Write failures caught, encode slightly sped up.
1.2 16 Apr 87 Archive bit set on output, check DOS version.
1.3 21 Apr 87 Added hidden key input.
ASCIFY converts one or more files into a single standard text file with
error checking and optional encryption. It can also read such a file
and recreate the original files. It's purpose is to allow the sending
of arbitrary PC binary files over text only links, such as nationwide
mail networks like BITNET and ARPANET.
To convert to a single text file, use ASCIFY with two arguments:
ascify *.* util.asc
This will create the text file UTIL.ASC which contains all the files in
the current directory.
To convert an ASCIFY file back to its original files, use ASCIFY with
just one argument:
ascify util.asc
This will recreate the files to the current directory, including the
original date and time stamps. ASCIFY also checks that the file came
across faithfully using check characters (CRC's) after each 1/2 K.
The data can be encrypted as well. Use a slash followed by a one to
five letter key. Upper and lower case are significant in the key:
ascify *.* util.enc/zztop
and to decode it:
ascify util.enc/zztop
If you enter the key "?", then you will be prompted for the key and when
you type it, it will not be echoed. For example:
ascify util.enc/?
and you will be prompted:
Enter key: _
If you want help on the ASCIFY command, simply enter it with no
arguments:
ascify
Note that ASCIFY is, in a sense, the opposite of compaction utilities
(like PKARC) since its output is larger than its input (about 30%
larger). It is recommended that you use a compaction utility on the
files to be sent, and then use ASCIFY on the result to reduce the size
of final file to be sent over the network. For example:
pkarc a util *.*
ascify util.arc util.enc/zztop
will create a (hopefully) smaller file than "ascify *.* util.asc". Then
use:
ascify util.enc/zztop
pkxarc util.arc
to reconstruct the files sent. Using a compaction utility also improves
the security of encryption.
Whoever is getting the file at the other end will need ASCIFY, whatever
compaction utility you used, if any, and the key you used, if any, to
reconstruct the files contained in the text file. To get ASCIFY over
the network, simply send the ASCIFY.ASM file to your network buddy and
hope that she can find MASM, the Microsoft Macro Assembler, and run it
as follows:
masm ascify;
link ascify; (ignore the no stack segment message)
exe2bin ascify ascify.com
del ascify.exe
Alternatively, you can mail ASCIFY.COM on a diskette. Once you get
ASCIFY over, you can use ASCIFY to send the compaction utility you are
using. If you find the need to use encryption, you should not send the
keys over the network in any way---even in an encrypted file. Keys are
only secure if sent by another medium, such as the telephone or the U.S.
mail.
#
RECSZ = 512 ;Bytes in a record.
LINSZ = 72 ;Characters per line.
RECL1 = (RECSZ+5) and not 3 ;Plus CRC and padding.
RECL2 = RECL1 + (RECL1/4) ;In base 85.
RECL3 = RECL2 + (RECL2/LINSZ) * 2 + 4 ;Plus CR,LF's.
data struc ;Structure for data area.
stk dw 128 dup(?) ;Room for stack.
stkptr dw ? ;Stack pointer (wasted word).
argv db 190 dup(?) ;Argument pointers.
find db 21 dup(?) ;Find information (incl next 4 items).
attr db ? ;File attributes.
time dw ? ;Time stamp.
date dw ? ;Date stamp.
fsiz dd ? ;File size.
fname db 13 dup(?) ;File name (end of find information).
todig db 256 dup(?) ;Inverse of set85 table.
mix85 db 85 dup(?) ;Digit scramble table.
unmix db 256 dup(?) ;Inverse of mix85 table.
rec db RECL1 dup(?) ;Record with room for CRC and padding.
in85 db RECL2 dup(?) ;Output record in base 85.
buf db ? ;Big buffer to end of 64K.
data ends
;
; Program segment.
;
ascify segment
assume CS:ascify,DS:ascify,ES:ascify,SS:ascify
org 5Ch ;General data area
rseed dw ?,? ;32 bit random number seed.
key dw ? ;Pointer to key or zero.
bptr dw ? ;Buffer pointer.
bcnt dw ? ;Buffer byte count (used for reading).
rem dw ? ;Characters remaining in line.
asc dw ? ;ASCIFY file name pointer.
ncat dw ? ;Where to append name to path.
hasc dw ? ;Handle for ASCIFY file.
hbin dw ? ;Handle for binary files.
;
; Start of .COM code.
org 100h
start:
jmp begin
db 13
help equ $
db 'ASCIFY version 1.3 '
db 'Copyright (C) 1987 Mark Adler '
db 'All rights reserved.',13,10,13,10
db ' Converts binary files to/from a text file format. Used for '
db 'transferring files',13,10
db ' across links that only allow "text" information. Command format:'
db 13,10
db 13,10
db ' ascify FILES ASCFILE[/KEY or /?]',13,10
db ' ascify ASCFILE[/KEY or /?]',13,10
db 13,10
db ' where the first form converts binary files to the ASCIFY text '
db 'format, and the',13,10
db ' second form restores a file made by ASCIFY into the original '
db 'binary files.',13,10
db ' [/KEY] is an optional encryption key preceded by a slash. If the '
db 'key is a ?,',13,10
db ' then you are prompted for a key and your response is not echoed. '
db 'FILES is an',13,10
db " ambiguous file name (*'s and ?'s allowed) specifying the files to "
db "convert and",13,10
db ' ASCFILE is the name of the file in the ASCIFY text format. '
db 'Several files (and',13,10
db ' their names and dates) can be stored in one ASCIFY format file. '
db 13,10,'$','Z'-64
errk db 'Can only specify one key$'
erra db 'Too many arguments$'
errf db "Can't find file$"
errg db '--use just ASCIFY for help.',13,10,'$'
errv db 'ASCIFY requires DOS 2.0 or later.',13,10,'$'
errw db '??Error writing output file.',13,10,'$'
errr db '??Error reading file.',13,10,'$'
errc db '??CRC error in decoding file'
db '--possibly not an ASCIFY file or wrong key.',13,10,'$'
errs db '??Premature end of file'
db '--possibly not an ASCIFY file.',13,10,'$'
kask db 'Enter key: ',0
tmsg1 db 'Encoding ',0
msg2 db 13,10,0
fmsg1 db 'Decoding ',0
fin db 'Done.'
crlf db 13,10,'$'
; These characters, and carriage-return and line-feed, are the only
; characters that can appear in an ascified file.
set85 equ $
db '!"#$%&',"'",'()*+,-./0123456789:;<=>?'
db '@ABCDEFGHIJKLMNOPQRSTUVWXYZ'
db '_abcdefghijklmnopqrstuvwxyz'
; All the code assumes direction flag is cleared and ES = DS.
; The phrase "all registers" refers to AX,BX,CX,DX,SI,DI,BP.
begin:
mov SP,offset d.stkptr ;Put stack where it belongs.
cld
; see if DOS 2.0 or later.
mov AH,30h ;Get DOS version.
int 21h
cmp AL,2
mov DX,offset errv
jb bye ;Return if before 2.0.
; process arguments.
mov DI,offset d.argv ;Where to put pointers.
call args ;Process args.
mov SI,offset d.argv ;Point to pointers.
mov CL,2 ;Set counter for normal args.
sub BX,BX ;Clear option pointer.
ascn:
lodsb ;Get delimiter.
cmp AL,-1 ;See if no more args.
je afin
cmp AL,' ' ;See if normal arg.
lodsw ;Get pointer.
je anrm
test BX,BX ;See if already have key.
mov DX,offset errk ;Too many keys error.
jnz errx ;If two keys, then error.
xchg AX,BX ;Save pointer to keys.
jmp short ascn ;Get next arg.
anrm:
test CL,CL ;See if already have two.
mov DX,offset erra ;Too many args error.
jz errx ;If three args, then error.
xchg AX,DI ;Save pointer in DI.
dec CX ;Decrement argument count.
jnz a1st ;If not zero, then is first arg.
mov BP,AX ;Is second arg, put first in BP.
a1st:
jmp short ascn ;Get next arg.
afin:
; now if CL is two, there were no normal args. If CL is one,
; have one arg pointer in DI. If CL is zero, then have first
; arg pointer in BP, second in DI. If BX is not zero, then it
; points to the key.
cmp CL,2 ;See if no args.
mov DX,offset help ;If so, print help.
je bye
mov asc,DI ;Save pointer to ASCIFY file name.
mov key,BX ;Save pointer to key, if any.
; determine direction.
test CL,CL ;See if one arg.
jnz from ;If so, convert from ASCIFY format.
; convert to ASCIFY format.
call toasc ;Convert.
jmp short bye ;Done--leave.
; convert from ASCIFY format.
from:
call frmasc ;Convert.
jmp short bye ;Done--leave.
errx: ;Error--print message at DX, return.
mov AH,9
int 21h
mov DX,offset errg
bye: ;Leave with message.
mov AH,9
int 21h
int 20h
toasc proc near
;
; Convert from binary files to an ASCIFY format text file.
; BP -> source, DI -> destination.
;
; see if any matches on source.
mov DX,offset d.find ;Set DTA to the find area.
mov AH,1Ah
int 21h
mov DX,BP ;Point to name.
sub CX,CX ;Search only for normal files.
mov AH,4Eh ;Do find first.
int 21h
mov DX,offset errf ;Can't find file error.
jb errx
; create output file, hide it, open it for write.
mov DX,DI ;Point to output name.
mov AH,3Ch ;Create it.
int 21h
mov DX,offset errw ;Write error.
jb bye
mov BX,AX ;Close it.
mov AH,3Eh
int 21h
mov DX,DI ;Point to name again.
mov CX,22h ;Hide file so it is not found by find next.
mov AX,4301h ;Set attribute.
int 21h
mov DX,DI ;Open file for write.
mov AX,3D01h
int 21h
mov hasc,AX ;Save handle.
; get drive and path (if any) for source, put it over argv.
mov SI,BP ;Point to name.
mov DI,offset d.argv ;Point to where to put path.
mov BX,DI ;Where to put file name (initially).
tplp:
lodsb ;Get char.
stosb ;Store in result.
test AL,AL ;See if end of string.
jz tpfin ;If so, done.
cmp AL,':' ;See if drive designator.
je tpnam ;If so, name is after.
cmp AL,'\' ;See if path delimiter.
jne tplp ;If not, get next char.
tpnam: ;Last char was drive or path delim,
mov BX,DI ; name starts just after.
jmp short tplp
tpfin:
mov ncat,BX ;Save where to concatenate name.
; initialize output buffer, make key seed.
mov bptr,offset d.buf ;Start buffer at beginning.
mov rem,LINSZ ;LINSZ characters left on line.
call keyseed ;Set up translation.
mov SI,offset set85 ;Copy translate table to mix85.
mov DI,offset d.mix85
mov CX,85
rep movsb
; encode and write each matching name.
wfile:
; copy file information to rec, display name.
mov SI,offset d.attr ;Start of file information.
mov DI,offset d.rec
mov CX,11 ;22 bytes.
rep movsw
mov SI,offset tmsg1 ;Display name of file being encoded.
call puts
mov SI,offset d.fname
call puts
mov SI,offset msg2
call puts
; encode it and write it.
mov CX,22 ;22 bytes.
call encode ;Write header (will be 30 characters).
; copy name after path.
mov SI,offset d.fname ;Point to name.
mov DI,ncat ;Where to put it.
tcop:
lodsb ;Copy name.
stosb
test AL,AL
jnz tcop
; open the file, save handle in hbin.
mov DX,offset d.argv ;Point to path and name.
mov AX,3D00h ;Open it for read.
int 21h
mov DX,offset errr ;Read error message.
jb tret
mov hbin,AX ;Save handle.
; read file, encode and write it.
rdlp:
mov BX,hbin ;Get file handle.
mov DX,offset d.rec ;Record buffer.
mov CX,RECSZ ;Read up to RECSZ bytes.
mov AH,3Fh ;Read from file.
int 21h
mov DX,offset errr ;Read error message.
jb tret
test AX,AX ;See if end of file.
jz rdone ;If so, then done.
mov CX,AX ;Number of bytes to do.
call encode ;Encode and write it.
jmp short rdlp ;Read rest of file.
rdone:
mov BX,hbin ;Get file handle.
mov AH,3Eh ;Close file.
int 21h
; look for next match.
mov DX,offset d.find
mov AH,4Fh ;Find next.
int 21h
jnb wfile ;If another match, then do it.
; done--flush output buffer and leave.
call flush
mov DX,offset fin ;Successful completion message.
; return with message pointer in DX.
tret:
mov DI,DX ;Save message pointer.
mov DX,asc ;Point to output name.
mov CX,20h ;Un-hide file.
mov AX,4301h ;Set attribute.
int 21h
mov DX,DI ;Restore message pointer.
ret
;
toasc endp
frmasc proc near
;
; Recreate the original binary files from an ASCIFY format text file.
; DI -> source.
;
; open input ASCIFY file.
mov DX,DI ;Point DX to file name.
mov AX,3D00h ;Open file for read.
int 21h
mov DX,offset errf ;Find error.
jnb fok
jmp errx
fok:
mov hasc,AX ;Save file handle.
mov bcnt,0 ;Clear input buffer.
call keyseed ;Set up translation.
mov DI,offset d.mix85 ;Put 0..84 in scramble table.
mov CX,85
mov AL,0
fmix:
stosb
inc AX
loop fmix
; do next binary file in input file.
flp:
; get header, create file.
mov AX,22 ;Get next header.
call decode
mov DX,offset fin
jnz fretz ;If end of file, then done--return OK.
mov SI,offset d.rec ;Copy header to find area.
mov DI,offset d.attr
mov CX,22
rep movsb
mov SI,offset fmsg1 ;Display name of file being decoded.
call puts
mov SI,offset d.fname
call puts
mov SI,offset msg2
call puts
mov DX,offset d.fname ;Point to file name.
mov AH,3Ch ;Create file.
int 21h
mov DX,offset errw
jb fret ;Abort if can't create.
mov hbin,AX ;Save file handle.
; read file and decode it.
mov SI,word ptr d.fsiz ;Get size into DI:SI.
mov DI,word ptr d.fsiz+2
frlp:
; compute size of next block.
mov AX,RECSZ ;Convert up to RECSZ decoded bytes.
test DI,DI ;See if size >= RECSZ.
jnz fdec ;If so, decode RECSZ bytes.
cmp SI,AX
jae fdec
mov AX,SI ;If size < RECSZ, decode size bytes.
fdec:
; decode it.
push DI ;Save bytes left to read.
push SI
push AX ;Save bytes requested.
call decode ;Decode the block.
pop CX ;Get bytes of data.
pop SI ;Restore bytes left to read.
pop DI
mov DX,offset errs
fretz: ;(extend jump)
jnz fret ;If end of file, abort with error.
; write data to file.
sub SI,CX ;Subtract bytes from size.
sbb DI,0
mov BX,hbin ;Get handle of file.
mov DX,offset d.rec ;Point to decoded data.
mov AH,40h ;Write to file.
int 21h
mov DX,offset errw
jb fret ;Abort on error.
cmp AX,CX ;Also check for enough bytes written.
jne fret
; see if got it all yet.
mov AX,SI ;See if any bytes left.
or AX,DI
jnz frlp ;If so, get more.
; set date and time and close file.
mov BX,hbin ;Put handle in BX.
mov CX,d.time ;Get time.
mov DX,d.date ;Get date.
mov AX,5701h ;Set date and time.
int 21h
mov AH,3Eh ;Close.
int 21h
mov DX,offset errw
jb fret ;Abort on error.
; look for next header.
jmp flp
; done--return.
fret:
ret
;
frmasc endp
args proc near
;
; process args--delimited by slashes and/or blanks, put delimiters
; and pointers at DI (three bytes per arg). Uses SI,DI,CX,AX.
;
; set up.
mov SI,80h ;Point to args.
lodsb ;Get length.
cbw
xchg AX,CX ;Put length in CX.
; scan arguments.
mov AL,' ' ;Initialize delimiter as a space.
arglp:
jcxz argfin
; look for non-delimiter.
argdlp:
mov AH,AL ;Save delimiter.
lodsb
cmp AL,' ' ;If delimiter, keep looking.
je argd
cmp AL,'/'
argd:
loope argdlp
je argfin ;If ran out of line, then done.
; save info on arg.
mov AL,AH ;Get delimiter.
stosb ;Save it.
mov AX,SI ;Point to arg.
dec AX
stosw ;Save pointer.
; look for delimiter.
jcxz argend ;If no more chars, that is a delimiter.
argalp:
lodsb
cmp AL,' ' ;Wait for delimiter.
je arga
cmp AL,'/'
arga:
loopne argalp
jne argend
; terminate argument, look for more.
mov byte ptr [SI-1],0 ;End arg.
jmp short arglp ;(AL has delimiter.)
argend:
mov byte ptr [SI],0 ;End arg.
argfin:
mov byte ptr [DI],-1 ;End arg pointer list.
ret
;
args endp
encode proc near
;
; Encode CX bytes at rec, put result in buf and write if necessary.
;
; compute CRC, append it, and pad out to mulitple of four.
mov SI,offset d.rec ;Point to record.
sub DX,DX ;Start with CRC=0.
call crc16 ;Compute CRC-16.
mov DI,SI
mov AX,DX ;Put CRC in AX.
stosw ;Append CRC.
plp:
mov AX,DI ;See if multiple of four.
sub AL,low offset d.rec ;(Change to "low (offset d.rec)" for
test AL,3 ; Borland Turbo Assembler.)
jz is4
mov AL,0 ;Pad byte.
stosb ;Pad until it is.
jmp short plp
is4:
; convert to base 85.
mov SI,offset d.rec ;Point to record.
sub DI,SI ;Compute bytes with CRC and padded.
mov CX,DI
shr CX,1 ;Compute doublewords to do.
shr CX,1
add DI,CX ;Compute size of result in bytes.
push DI ;Save size of result.
mov DI,offset d.in85 ;Put result in in85.
call to85 ;Convert it.
; do scramble if requested.
call scramble ;Shuffle mix85.
; convert digits into characters.
mov BX,offset d.mix85 ;Point to digit->character table.
mov SI,offset d.in85 ;Point to digits.
pop CX ;Get size of in85.
push CX ;Save it again.
call xlt ;Translate it.
; put characters into buffer with CR,LF's.
mov SI,offset d.in85 ;Point to characters.
pop DX ;Get bytes to do.
mov DI,bptr ;Get pointer into buffer.
ldlp:
mov CX,rem ;Get bytes left in line.
cmp CX,DX ;See if can fill it.
jg ldlst ;If not, put in rest.
sub DX,CX ;Subtract off what is to be moved.
rep movsb ;Move it.
mov AX,0a0dh ;CR,LF.
stosw ;End line.
mov rem,LINSZ ;Reset character count.
test DX,DX ;See if more to do.
jnz ldlp ;Do rest of in85.
jmp short ldfin ;Else done.
ldlst:
sub CX,DX ;Compute remaining characters.
mov rem,CX ;Save in rem for next time.
mov CX,DX ;Put remaining characters in buffer.
rep movsb
ldfin:
; see if buffer needs to be written.
cmp DI,-RECL3 ;See if enough room for one more.
jb nowrt ;If so, don't need to write buffer.
mov DX,offset d.buf ;Point to buffer to write.
sub DI,DX ;Compute bytes in buffer.
mov CX,DI ;Put in CX.
mov BX,hasc ;Get ASCIFY file handle.
mov AH,40h ;Write buffer.
int 21h
jb enerr ;If error on write, give up.
cmp AX,CX ;Also check for enough bytes written.
jne enerr
mov DI,offset d.buf ;Start buffer over.
nowrt:
; update buffer pointer and return.
mov bptr,DI ;Update buffer pointer.
ret
; error on write.
enerr:
mov DX,offset errw
call tret ;Unhide file.
jmp bye
;
encode endp
flush proc near
;
; Flush output buffer, close file.
;
; compute bytes left in buffer.
mov DX,offset d.buf ;Point to buffer.
mov DI,bptr ;Get buffer pointer.
mov CX,DI ;Compute bytes in buffer.
sub CX,DX
jz empty ;If empty, return.
; finish off last line.
mov AL,byte ptr rem
cmp AL,LINSZ ;See if unfinished line.
je flok ;If not, skip.
mov AX,0a0dh ;Else, finish line with CR,LF.
stosw
inc CX ;Update bytes in buffer.
inc CX
flok:
; write buffer.
mov BX,hasc ;Get output handle.
mov AH,40h ;Write buffer.
int 21h
jb enerr ;If error, exit (in encode proc).
cmp AX,CX ;Also check for enough bytes written.
jne enerr
empty:
mov BX,hasc ;Close file.
mov AH,3Eh
int 21h
jb enerr ;If error, exit.
; done.
ret
;
flush endp
decode proc near
;
; Get AX decoded bytes from the coded ASCIFY file into rec. Return
; Z on success, NZ on end of file.
;
; compute number of digits to get and numbers to convert.
push AX ;Save bytes to get.
add AX,5 ;Compute digits needed for it.
and AX,not 3
mov DX,AX
shr AX,1
shr AX,1
add DX,AX ;Now DX is the number of digits.
push AX ;Save number of 5 digit base 85 numbers.
push DX ;Save number of digits.
; get DX digits from the ASCIFY file.
; find next digit (0..84 after conversion).
mov BP,offset d.in85 ;Put them in d.in85.
mov DI,bptr ;Next bytes in buffer.
mov CX,bcnt ;Bytes left in buffer.
dgetlp:
jcxz dld ;If no bytes in buffer, reload it.
dnew:
mov AL,-1 ;Look for next digit (not -1).
repe scasb
jne dgot ;Found a digit.
dld: ;No digits, ran out of buffer--reload.
mov SI,DX ;Save number of digits still needed.
mov BX,hasc ;Get handle for input file.
mov DX,offset d.buf ;Buffer start.
mov CX,DX ;Compute bytes in buffer (to end of 64K).
neg CX
mov AH,3Fh ;Read from file.
int 21h
mov DX,offset errr
jnb dok ;Abort on error.
jmp bye
dok:
mov DX,SI ;Restore digits left to read.
mov CX,AX ;Put bytes read in CX.
jcxz deof ;If end of file, give up.
mov BX,offset d.todig ;Translate table.
mov SI,offset d.buf ;Convert characters to digits.
push CX ;Save bytes read.
call xlt ;Translate characters to digits.
pop CX ;Restore bytes read.
mov DI,offset d.buf ;Point DI to start of buffer.
jmp short dnew ;Try again with new buffer.
; find up to DX contiguous digits.
dgot:
lea SI,[DI-1] ;Point SI to the first digit.
mov BX,CX ;Put bytes remaining in buffer in BX.
inc BX
cmp BX,DX ;See if more than DX left.
jbe dfew ;If not, scan up to end.
mov CX,DX ;If more than DX, scan only up to DX.
dec CX
dfew:
jcxz dmov ;If just one digit, move it.
repne scasb ;Look for next non-digit.
jne dmov ;If hit end, move the rest of buffer.
dec DI ;Else, back up to first non-digit.
dmov:
; move contiguous digits found to BP.
sub DI,SI ;Compute number found.
mov CX,DI ;Put in CX for move.
mov DI,BP ;Point DI to destination.
sub DX,CX ;Subtract number to move from DX.
sub BX,CX ;Subtract number to move from bytes left.
rep movsb ;Move the digits.
mov BP,DI ;Update destination address.
mov CX,BX ;Put bytes left in CX.
mov DI,SI ;Put address of bytes left in DI.
test DX,DX ;See if any more digits needed.
jnz dgetlp ;If so, get them.
; got the digits, update pointer and count.
mov bptr,DI ;Update buffer pointer.
deof:
mov bcnt,CX ;Update buffer count.
test DX,DX ;See if successful.
jz dsuc ;If so, go on.
add SP,6 ;Trash stuff on stack.
dret:
ret ;Return NZ for end of file.
dsuc:
; do scramble if requested.
call scramble
pop DX ;Get number of digits to unscramble.
jz deno
mov SI,offset d.mix85 ;Make unscramble table.
mov DI,offset d.unmix
call invert
mov BX,offset d.unmix ;Point to unscramble table.
mov SI,offset d.in85
mov CX,DX ;Number to unscramble.
call xlt ;Unscramble.
deno:
; convert digits into bytes.
pop CX ;Get number of 5 digit numbers.
mov SI,offset d.in85 ;Point to base 85 digits.
mov DI,offset d.rec ;Where to put bytes.
call frm85 ;Convert.
pop CX ;Get number of bytes of data.
mov SI,offset d.rec ;Point to data.
sub DX,DX ;Initialize CRC to zero.
call crc16 ;Compute CRC-16.
lodsw ;Get CRC from data.
cmp AX,DX ;See if correct.
mov DX,offset errc
je dret ;Done, return Z on success.
jmp bye ;Abort on CRC error.
;
decode endp
keyseed proc near
;
; make d.todig table from set85.
mov SI,offset set85
mov DI,offset d.todig
call invert
; if there is a key, make five characters out of it.
mov SI,key ;Get pointer to key.
test SI,SI ;See if there is a key.
jz nokey ;If not, then skip.
mov DI,offset d.rec ;Point to an area to use.
mov CX,5 ;Move up to five characters.
klp:
lodsb
test AL,AL ;See if end of key.
jz kend
stosb
loop klp
kend:
jcxz kgen ;If got five characters, use them.
rep stosb ;Else, fill out with zeros.
kgen:
; see if got request for hidden key input.
mov DI,offset d.rec ;Point to first character of key.
cmp byte ptr [DI],'?'
jne kgot ;If not ?, then have key.
mov SI,offset kask ;Ask for key.
mov CX,5 ;Just five characters.
call hide ;Get key with no echo.
kgot:
; convert five characters to a deterministic seed for rnd.
mov BX,offset d.todig ;Convert to base 85 digits.
mov SI,offset d.rec ;Point to characters.
mov CX,5
call xlt
mov SI,offset d.rec ;Convert to base 256.
mov DI,offset rseed
mov CX,1
call frm85
; done.
nokey:
ret
;
keyseed endp
hide proc near
;
; Get CX characters at DI from console with no echo.
; SI points to the prompt.
;
; set up.
mov BX,DI ;Save address of start.
add CX,DI ;Save address of end.
; prompt for input.
hagain:
push SI ;Save prompt address.
call puts ;Give prompt at SI.
pop SI ;Restore address.
; read a character.
hrd:
mov AH,8 ;Get input with no echo.
int 21h
test AL,AL ;See if extended code.
jnz hnrm
mov AH,8 ;If so, ignore it.
int 21h
jmp short hrd
hnrm:
; check for return or backspace.
cmp AL,13 ;See if return.
je hrend ;If so, then done.
cmp AL,8 ;See if backspace.
je hback ;If so, process.
cmp AL,' ' ;See if other control character.
jb hrd ;If so, ignore.
; got what appears to be a normal character.
cmp DI,CX ;See if already have enough characters.
jae hrd ;If so, ignore it.
stosb ;Else, save character and echo space.
mov DL,' '
mov AH,2
int 21h
jmp short hrd ;Get more.
; process backspace.
hback:
cmp DI,BX ;See if at start.
je hrd ;If so, ignore backspace.
dec DI ;Else, erase character and echo backspace.
mov DL,AL
mov AH,2
int 21h
jmp short hrd
; process return.
hrend:
mov DX,offset crlf ;Send carriage return, line feed.
mov AH,9
int 21h
; if got no input, prompt again.
cmp DI,BX ;See if no key.
je hagain ;If so, ask again.
; fill out buffer with zeros.
sub CX,DI ;Fill out with zeros.
jz hret ;If full, then skip.
mov AL,0 ;Else append zeros.
rep stosb
hret:
; done.
ret
;
hide endp
invert proc near
;
; Invert 85 byte table at SI to a 256 byte table at DI.
;
; clear inverse table.
mov BX,DI ;Save address of inverse table.
mov CX,128
mov AX,-1
rep stosw
; make inverse table.
mov DI,BX
mov CX,85
inc AX ;Set digit (AH) to zero.
mov BH,0 ;Set high byte of table offset to zero.
invlp:
lodsb ;Get character.
mov BL,AL ;Put offset in BX.
mov [DI+BX],AH ;Store index at computed position.
inc AH ;Increment index.
loop invlp
ret
;
invert endp
scramble proc near
;
cmp key,0 ;See if encryption/decryption requested.
je noscrm
; shuffle mix85.
mov BP,offset d.mix85
mov CX,85
call shuffle
; return NZ to scramble.
stc
sbb AX,AX
noscrm:
ret
;
scramble endp
to85 proc near
;
; Convert CX doublewords at SI to CX five-digit base 85 numbers at DI.
; In the comments below, the notation "p:q" is "p*65536 + q".
; Approximate execution time on an IBM PC is 252 uS per four bytes.
; All registers used.
;
mov BX,85 ;Convert to base 85.
tolp:
lodsw ;Get low word (q0) into AX.
mov BP,AX ;Put in BP.
lodsw ;Get high word (p0) into AX.
; AX:BP = p0:q0
sub DX,DX ;DX:AX = p0
div BX ;DX = p0%85, AX = p0/85
xchg AX,BP ;DX:AX = (p0%85):q0, BP = p0/85
div BX ;DX = ((p0%85):q0)%85, AX = ((p0%85):q0)/85
xchg AX,BP ;DX = (p0:q0)%85, AX:BP = (p0:q0)/85
; Store remainder (in DL) in result.
mov [DI],DL ;Store base 85 digit 0.
inc DI
; AX:BP = (p0:q0)/85 = p1:q1
sub DX,DX ;DX:AX = p1
div BX ;DX = p1%85, AX = p1/85
xchg AX,BP ;DX:AX = (p1%85):q1, BP = p1/85
div BX ;DX = (p1:q1)%85, BP:AX = (p1:q1)/85
; Store remainder (in DL) in result.
mov [DI],DL ;Store base 85 digit 1.
inc DI
; BP:AX = (p1:q1)/85 = p2:q2 (note: p2 < 85)
mov DX,BP ;DX:AX = p2:q2
div BX ;DX = (p2:q2)%85, AX = (p2:q2)/85
; Store remainder (in DL) in result.
mov [DI],DL ;Store base 85 digit 2.
inc DI
; AX = (p2:q2)/85 = q3 (note: q3 < 85*85 < 64K)
div BL ;AH = q3%85, AL = q3/85
; Store remainder (in AH) in result.
mov [DI],AH ;Store base 85 digit 3.
inc DI
; AL = q3/85 = q4 (note: q4 < 85)
; Store remainder (in AL) in result.
stosb ;Store base 85 digit 4.
; Do CX conversions.
loop tolp
ret ;Done.
;
to85 endp
frm85 proc near
;
; Convert CX five-digit base 85 numbers at SI to CX doublewords at DI.
; Approximate execution time on an IBM PC is 248 uS per conversion.
; All registers used.
;
mov BP,SP ;Point to auxilary area.
sub SP,4 ;Allocate two words.
mov [BP-2],DI ;Save destination pointer.
mov word ptr [BP-4],0 ;Initialize overflow flag.
frmlp:
mov BX,85 ;Convert from base 85, BH used as zero.
; process digit 4.
mov AL,[SI+4] ;AL = d4
; process digit 3.
mul BL ;AX = d4*85
add AL,[SI+3]
adc AH,BH ;AX = d4*85 + d3
; process digit 2.
mul BX ;DX:AX = d4*85^2 + d3*85
add AL,[SI+2]
adc AH,BH
adc DX,0 ;DX:AX = d4*85^2 + d3*85 + d2
; process digit 1.
mov DI,AX ;Multiply DX:AX by 85.
mov AX,DX
mul BX
xchg AX,DI
mul BX
add DX,DI ;DX:AX = d4*85^3 + d3*85^2 + d2*85
add AL,[SI+1] ;Add digit 1 to DX:AX.
adc AH,BH
adc DX,0 ;DX:AX = d4*85^3 + d3*85^2 + d2*85 + d1
; process digit 0.
mov DI,AX ;Multiply DX:AX by 85.
mov AX,DX
mul BX
xchg AX,DI
push DX ;Save overflow, if any.
mul BX
pop BX ;Get overflow.
add DX,DI ;DX:AX = d4*85^4 + d3*85^3 + d2*85^2 + d1*85
adc BX,0 ;Update overflow.
add AL,[SI] ;Add digit 0 to DX:AX.
adc AH,0
adc DX,0 ;DX:AX = d4*85^4 + d3*85^3 + d2*85^2 + d1*85 + d0
adc BX,0 ;Update overflow.
;Store results and update pointers.
add SI,5 ;Increment source pointer.
mov DI,[BP-2] ;Get address for four byte results.
stosw ;Store low word.
xchg AX,DX
stosw ;Store high word.
mov [BP-2],DI ;Save address for four byte results.
or [BP-4],BX ;If overflow, set overflow flag.
;Do CX conversions.
loop frmlp
mov AX,[BP-4] ;Return overflow flag.
mov SP,BP ;Restore stack pointer.
ret ;Done.
;
frm85 endp
crc16 proc near
;
; Compute CRC-16 on CX bytes at SI with DX as the initial CRC. The 16
; bit CRC is returned in DX. The CRC-16 polynomial is: X^16 + X^15 +
; X^2 + 1. The time required to compute the CRC on an IBM PC is
; roughly 42 uS per byte. All registers except BP and DI used.
;
; This routine was shamelessly copied (and translated into 8086 code)
; from "Byte-wise CRC Calculations" by Aram Perez in the June 1983
; issue of IEEE Micro.
;
jcxz crcfn ;If no data, CRC is unchanged.
crclp:
lodsb ;Get next byte.
xor AL,DL ;Exclusive or data byte with low byte of CRC.
mov BL,AL ;Copy it.
shl AL,1 ;Shift one bit left, CY=X8, P=XX7.
mov AH,AL ;Save it.
mov AL,0 ;Make XX7=0 and XX8=XX7.
jpe crc1 ;If XX7=1 then,
mov AL,00000011b ; make XX7 and XX8=1.
crc1:
jnc crc2 ;If X8=1 then,
xor AL,00000010b ; complement XX8.
crc2:
mov BH,AL ;Save XX8 and XX7 (BX bits 9..0 = R16..R7).
xor BL,AH ;Get R14 through R7.
shr AL,1 ;Get XX8 in bit 0.
ror BX,1 ;Shift BX left 6 bits (by shifting right 2).
ror BX,1 ;(Actually BX is backwards after this.)
and BH,11000000b
or AL,BH ;Put XX8 in BH.
xor AL,DH
mov DL,AL
mov DH,BL
loop crclp
crcfn:
ret ;Done--return CRC in DX.
;
crc16 endp
xlt proc near
;
; Translate CX bytes at SI using table at BX.
;
mov DI,SI ;Translate in place.
xlp:
lodsb
xlatb
stosb
loop xlp
ret
;
xlt endp
shuffle proc near
;
; Shuffle n bytes (algorithm from Knuth).
; Address of bytes in BP, number in CX.
; All registers used.
;
dec CX ;j = n - 1.
jz shfend ;If 1 item to shuffle, then done.
shflp:
push CX ;Save j.
call rnd ;k = random number in 0..j.
pop CX ;Get j back.
cmp AX,CX ;See if swap needed (k != j).
je shno ;If not, skip swap.
mov SI,AX ;Put offsets in SI, DI.
mov DI,CX
mov AL,[BP+SI] ;Get a[k].
xchg AL,[BP+DI] ;Exchange with a[j].
mov [BP+SI],AL ;Set new a[k].
shno:
loop shflp ;Do n-1 swaps.
shfend:
ret
;
shuffle endp
rmul: dd 1664525 ;Good multiplier (Knuth Vol. 2, 2nd Ed,
; pg. 102, line 26).
rnd proc near
;
; Return random number in [0..n] using linear congruential method.
; n is in CX. All registers except BP used.
;
push CX ;Save bound.
; multiply seed by multiplier modulo 2^32.
mov SI,offset rmul ;Point SI to multiplier.
mov DI,offset rseed ;Point DI to seed.
lodsw ;Get low word of multiplier.
mov BX,AX ;Save that.
mul word ptr [DI+2] ;Multiply by high word of seed.
mov CX,AX ;Save low word of that for high word of result.
lodsw ;Get high word of multiplier.
mul word ptr [DI] ;Multiply by low word of seed.
add CX,AX ;Add low word into high word of result.
xchg AX,BX ;Get low word of multiplier.
mul word ptr [DI] ;Multiply by low word of seed.
add DX,CX ;Add other high word components.
; add constant modulo 2^32.
add AX,1 ;Constant = 1 (relatively prime to
adc DX,0 ; everything).
; store seed back.
stosw ;Store low word.
mov [DI],DX ;Store high word.
; get random number in [0..n] (as per Knuth's recommendation).
pop CX ;Get n.
inc CX ;Use n+1 to multiply "fraction" in [0..1).
jz rndf ;If n is 65535, then return high part of seed.
mov BX,DX ;Save high part of seed.
mul CX ;Multiply low part of fraction by n+1.
xchg AX,BX ;Get high part of fraction.
mov BX,DX ;Save high part of product.
mul CX ;Multiply high part of fraction by n+1.
add AX,BX ;Add component from low part of fraction.
adc DX,0
rndf:
xchg AX,DX ;Truncate to integer.
ret
;
rnd endp
puts proc near
;
; Output string at SI to standard output.
;
putslp:
lodsb ;Get next character.
test AL,AL ;See if end of string.
jz putsdn ;If so, done.
mov DL,AL ;Send to standard output.
mov AH,2
int 21h
jmp short putslp ;Get next character.
putsdn:
ret
;
puts endp
;
; Data area.
;
even ;Move to word boundary.
d equ $
ascify ends
end start