home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 December / simtel1292_SIMTEL_1292_Walnut_Creek.iso / msdos / filutl / ascify13.arc / ASCIFY.ASM next >
Assembly Source File  |  1989-02-09  |  41KB  |  1,244 lines

  1.  
  2. page 60,132
  3.  
  4. ; Copyright (C) 1987  Mark Adler  Pasadena, CA
  5. ; All rights reserved.
  6.  
  7.  title   ASCIFY -- convert binary files to/from an ascii encoded form
  8.  name    ASCIFY
  9.  
  10. comment #
  11.  
  12. Version History -
  13.  
  14. 1.0     11 Apr 87       First distrbuted version.
  15. 1.1     14 Apr 87       Write failures caught, encode slightly sped up.
  16. 1.2     16 Apr 87       Archive bit set on output, check DOS version.
  17. 1.3     21 Apr 87       Added hidden key input.
  18.  
  19. ASCIFY converts one or more files into a single standard text file with
  20. error checking and optional encryption.  It can also read such a file
  21. and recreate the original files.  It's purpose is to allow the sending
  22. of arbitrary PC binary files over text only links, such as nationwide
  23. mail networks like BITNET and ARPANET.
  24.  
  25. To convert to a single text file, use ASCIFY with two arguments:
  26.  
  27.      ascify *.* util.asc
  28.  
  29. This will create the text file UTIL.ASC which contains all the files in
  30. the current directory.
  31.  
  32. To convert an ASCIFY file back to its original files, use ASCIFY with
  33. just one argument:
  34.  
  35.      ascify util.asc
  36.  
  37. This will recreate the files to the current directory, including the
  38. original date and time stamps.  ASCIFY also checks that the file came
  39. across faithfully using check characters (CRC's) after each 1/2 K.
  40.  
  41. The data can be encrypted as well.  Use a slash followed by a one to
  42. five letter key.  Upper and lower case are significant in the key:
  43.  
  44.      ascify *.* util.enc/zztop
  45.  
  46. and to decode it:
  47.  
  48.      ascify util.enc/zztop
  49.  
  50. If you enter the key "?", then you will be prompted for the key and when
  51. you type it, it will not be echoed.  For example:
  52.  
  53.      ascify util.enc/?
  54.  
  55. and you will be prompted:
  56.  
  57.      Enter key: _
  58.  
  59. If you want help on the ASCIFY command, simply enter it with no
  60. arguments:
  61.  
  62.      ascify
  63.  
  64. Note that ASCIFY is, in a sense, the opposite of compaction utilities
  65. (like PKARC) since its output is larger than its input (about 30%
  66. larger).  It is recommended that you use a compaction utility on the
  67. files to be sent, and then use ASCIFY on the result to reduce the size
  68. of final file to be sent over the network.  For example:
  69.  
  70.      pkarc a util *.*
  71.      ascify util.arc util.enc/zztop
  72.  
  73. will create a (hopefully) smaller file than "ascify *.* util.asc".  Then
  74. use:
  75.  
  76.      ascify util.enc/zztop
  77.      pkxarc util.arc
  78.  
  79. to reconstruct the files sent.  Using a compaction utility also improves
  80. the security of encryption.
  81.  
  82. Whoever is getting the file at the other end will need ASCIFY, whatever
  83. compaction utility you used, if any, and the key you used, if any, to
  84. reconstruct the files contained in the text file.  To get ASCIFY over
  85. the network, simply send the ASCIFY.ASM file to your network buddy and
  86. hope that she can find MASM, the Microsoft Macro Assembler, and run it
  87. as follows:
  88.  
  89.      masm ascify;
  90.      link ascify;               (ignore the no stack segment message)
  91.      exe2bin ascify ascify.com
  92.      del ascify.exe
  93.  
  94. Alternatively, you can mail ASCIFY.COM on a diskette.  Once you get
  95. ASCIFY over, you can use ASCIFY to send the compaction utility you are
  96. using.  If you find the need to use encryption, you should not send the
  97. keys over the network in any way---even in an encrypted file.  Keys are
  98. only secure if sent by another medium, such as the telephone or the U.S.
  99. mail.
  100.  
  101. #
  102.  
  103. RECSZ = 512             ;Bytes in a record.
  104. LINSZ = 72              ;Characters per line.
  105.  
  106. RECL1 = (RECSZ+5) and not 3             ;Plus CRC and padding.
  107. RECL2 = RECL1 + (RECL1/4)               ;In base 85.
  108. RECL3 = RECL2 + (RECL2/LINSZ) * 2 + 4   ;Plus CR,LF's.
  109.  
  110.  
  111. data struc              ;Structure for data area.
  112.  stk dw 128 dup(?)              ;Room for stack.
  113.  stkptr dw ?                    ;Stack pointer (wasted word).
  114.  argv db 190 dup(?)             ;Argument pointers.
  115.  find db 21 dup(?)              ;Find information (incl next 4 items).
  116.  attr db ?                      ;File attributes.
  117.  time dw ?                      ;Time stamp.
  118.  date dw ?                      ;Date stamp.
  119.  fsiz dd ?                      ;File size.
  120.  fname db 13 dup(?)             ;File name (end of find information).
  121.  todig db 256 dup(?)            ;Inverse of set85 table.
  122.  mix85 db 85 dup(?)             ;Digit scramble table.
  123.  unmix db 256 dup(?)            ;Inverse of mix85 table.
  124.  rec db RECL1 dup(?)            ;Record with room for CRC and padding.
  125.  in85 db RECL2 dup(?)           ;Output record in base 85.
  126.  buf db ?                       ;Big buffer to end of 64K.
  127. data ends
  128.  
  129.  
  130. ;
  131. ; Program segment.
  132. ;
  133. ascify segment
  134.  assume CS:ascify,DS:ascify,ES:ascify,SS:ascify
  135.  
  136.   org 5Ch                       ;General data area
  137.   rseed dw ?,?                  ;32 bit random number seed.
  138.   key dw ?                      ;Pointer to key or zero.
  139.   bptr dw ?                     ;Buffer pointer.
  140.   bcnt dw ?                     ;Buffer byte count (used for reading).
  141.   rem dw ?                      ;Characters remaining in line.
  142.   asc dw ?                      ;ASCIFY file name pointer.
  143.   ncat dw ?                     ;Where to append name to path.
  144.   hasc dw ?                     ;Handle for ASCIFY file.
  145.   hbin dw ?                     ;Handle for binary files.
  146.  
  147.  
  148. ;
  149. ; Start of .COM code.
  150.  org 100h
  151. start:
  152.   jmp begin
  153. db 13
  154.  
  155. help equ $
  156. db 'ASCIFY version 1.3 '
  157. db 'Copyright (C) 1987  Mark Adler  '
  158. db 'All rights reserved.',13,10,13,10
  159. db ' Converts binary files to/from a text file format.  Used for '
  160. db 'transferring files',13,10
  161. db ' across links that only allow "text" information.  Command format:'
  162. db 13,10
  163. db 13,10
  164. db '    ascify FILES ASCFILE[/KEY or /?]',13,10
  165. db '    ascify ASCFILE[/KEY or /?]',13,10
  166. db 13,10
  167. db ' where the first form converts binary files to the ASCIFY text '
  168. db 'format, and the',13,10
  169. db ' second form restores a file made by ASCIFY into the original '
  170. db 'binary files.',13,10
  171. db ' [/KEY] is an optional encryption key preceded by a slash.  If the '
  172. db 'key is a ?,',13,10
  173. db ' then you are prompted for a key and your response is not echoed.  '
  174. db 'FILES is an',13,10
  175. db " ambiguous file name (*'s and ?'s allowed) specifying the files to "
  176. db "convert and",13,10
  177. db ' ASCFILE is the name of the file in the ASCIFY text format.  '
  178. db 'Several files (and',13,10
  179. db ' their names and dates) can be stored in one ASCIFY format file.  '
  180. db 13,10,'$','Z'-64
  181.  
  182. errk db 'Can only specify one key$'
  183. erra db 'Too many arguments$'
  184. errf db "Can't find file$"
  185. errg db '--use just ASCIFY for help.',13,10,'$'
  186. errv db 'ASCIFY requires DOS 2.0 or later.',13,10,'$'
  187. errw db '??Error writing output file.',13,10,'$'
  188. errr db '??Error reading file.',13,10,'$'
  189. errc db '??CRC error in decoding file'
  190.      db '--possibly not an ASCIFY file or wrong key.',13,10,'$'
  191. errs db '??Premature end of file'
  192.      db '--possibly not an ASCIFY file.',13,10,'$'
  193.  
  194. kask db 'Enter key: ',0
  195. tmsg1 db 'Encoding ',0
  196. msg2 db 13,10,0
  197. fmsg1 db 'Decoding ',0
  198. fin db 'Done.'
  199. crlf db 13,10,'$'
  200.  
  201. ; These characters, and carriage-return and line-feed, are the only
  202. ; characters that can appear in an ascified file.
  203. set85 equ $
  204.  db '!"#$%&',"'",'()*+,-./0123456789:;<=>?'
  205.  db '@ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  206.  db '_abcdefghijklmnopqrstuvwxyz'
  207.  
  208.  
  209. ; All the code assumes direction flag is cleared and ES = DS.
  210. ; The phrase "all registers" refers to AX,BX,CX,DX,SI,DI,BP.
  211.  
  212. begin:
  213.   mov SP,offset d.stkptr        ;Put stack where it belongs.
  214.   cld
  215.         ; see if DOS 2.0 or later.
  216.   mov AH,30h            ;Get DOS version.
  217.   int 21h
  218.   cmp AL,2
  219.   mov DX,offset errv
  220.   jb bye                ;Return if before 2.0.
  221.         ; process arguments.
  222.   mov DI,offset d.argv  ;Where to put pointers.
  223.   call args             ;Process args.
  224.   mov SI,offset d.argv  ;Point to pointers.
  225.   mov CL,2              ;Set counter for normal args.
  226.   sub BX,BX             ;Clear option pointer.
  227.  ascn:
  228.    lodsb                ;Get delimiter.
  229.    cmp AL,-1            ;See if no more args.
  230.    je afin
  231.    cmp AL,' '           ;See if normal arg.
  232.    lodsw                ;Get pointer.
  233.    je anrm
  234.     test BX,BX          ;See if already have key.
  235.     mov DX,offset errk  ;Too many keys error.
  236.     jnz errx            ;If two keys, then error.
  237.     xchg AX,BX          ;Save pointer to keys.
  238.     jmp short ascn      ;Get next arg.
  239.   anrm:
  240.     test CL,CL          ;See if already have two.
  241.     mov DX,offset erra  ;Too many args error.
  242.     jz errx             ;If three args, then error.
  243.     xchg AX,DI          ;Save pointer in DI.
  244.     dec CX              ;Decrement argument count.
  245.     jnz a1st            ;If not zero, then is first arg.
  246.      mov BP,AX          ;Is second arg, put first in BP.
  247.    a1st:
  248.     jmp short ascn      ;Get next arg.
  249.  afin:
  250.         ; now if CL is two, there were no normal args.  If CL is one,
  251.         ; have one arg pointer in DI.  If CL is zero, then have first
  252.         ; arg pointer in BP, second in DI.  If BX is not zero, then it
  253.         ; points to the key.
  254.   cmp CL,2              ;See if no args.
  255.   mov DX,offset help    ;If so, print help.
  256.   je bye
  257.   mov asc,DI            ;Save pointer to ASCIFY file name.
  258.   mov key,BX            ;Save pointer to key, if any.
  259.         ; determine direction.
  260.   test CL,CL            ;See if one arg.
  261.   jnz from              ;If so, convert from ASCIFY format.
  262.         ; convert to ASCIFY format.
  263.    call toasc           ;Convert.
  264.    jmp short bye        ;Done--leave.
  265.         ; convert from ASCIFY format.
  266.  from:
  267.    call frmasc          ;Convert.
  268.    jmp short bye        ;Done--leave.
  269.  
  270.  errx:                  ;Error--print message at DX, return.
  271.    mov AH,9
  272.    int 21h
  273.    mov DX,offset errg
  274.  bye:                   ;Leave with message.
  275.    mov AH,9
  276.    int 21h
  277.    int 20h
  278.  
  279.  
  280.  
  281. toasc proc near
  282.  ;
  283.  ; Convert from binary files to an ASCIFY format text file.
  284.  ; BP -> source, DI -> destination.
  285.  ;
  286.         ; see if any matches on source.
  287.   mov DX,offset d.find  ;Set DTA to the find area.
  288.   mov AH,1Ah
  289.   int 21h
  290.   mov DX,BP             ;Point to name.
  291.   sub CX,CX             ;Search only for normal files.
  292.   mov AH,4Eh            ;Do find first.
  293.   int 21h
  294.   mov DX,offset errf    ;Can't find file error.
  295.   jb errx
  296.         ; create output file, hide it, open it for write.
  297.   mov DX,DI             ;Point to output name.
  298.   mov AH,3Ch            ;Create it.
  299.   int 21h
  300.   mov DX,offset errw    ;Write error.
  301.   jb bye
  302.   mov BX,AX             ;Close it.
  303.   mov AH,3Eh
  304.   int 21h
  305.   mov DX,DI             ;Point to name again.
  306.   mov CX,22h            ;Hide file so it is not found by find next.
  307.   mov AX,4301h          ;Set attribute.
  308.   int 21h
  309.   mov DX,DI             ;Open file for write.
  310.   mov AX,3D01h
  311.   int 21h
  312.   mov hasc,AX           ;Save handle.
  313.         ; get drive and path (if any) for source, put it over argv.
  314.   mov SI,BP             ;Point to name.
  315.   mov DI,offset d.argv  ;Point to where to put path.
  316.   mov BX,DI             ;Where to put file name (initially).
  317.  tplp:
  318.    lodsb                ;Get char.
  319.    stosb                ;Store in result.
  320.    test AL,AL           ;See if end of string.
  321.    jz tpfin             ;If so, done.
  322.    cmp AL,':'           ;See if drive designator.
  323.    je tpnam             ;If so, name is after.
  324.    cmp AL,'\'           ;See if path delimiter.
  325.    jne tplp             ;If not, get next char.
  326.   tpnam:                ;Last char was drive or path delim,
  327.    mov BX,DI            ; name starts just after.
  328.    jmp short tplp
  329.  tpfin:
  330.   mov ncat,BX           ;Save where to concatenate name.
  331.         ; initialize output buffer, make key seed.
  332.   mov bptr,offset d.buf ;Start buffer at beginning.
  333.   mov rem,LINSZ         ;LINSZ characters left on line.
  334.   call keyseed          ;Set up translation.
  335.   mov SI,offset set85   ;Copy translate table to mix85.
  336.   mov DI,offset d.mix85
  337.   mov CX,85
  338.   rep movsb
  339.         ; encode and write each matching name.
  340.  wfile:
  341.         ; copy file information to rec, display name.
  342.    mov SI,offset d.attr ;Start of file information.
  343.    mov DI,offset d.rec
  344.    mov CX,11            ;22 bytes.
  345.    rep movsw
  346.    mov SI,offset tmsg1  ;Display name of file being encoded.
  347.    call puts
  348.    mov SI,offset d.fname
  349.    call puts
  350.    mov SI,offset msg2
  351.    call puts
  352.         ; encode it and write it.
  353.    mov CX,22            ;22 bytes.
  354.    call encode          ;Write header (will be 30 characters).
  355.         ; copy name after path.
  356.    mov SI,offset d.fname        ;Point to name.
  357.    mov DI,ncat          ;Where to put it.
  358.   tcop:
  359.     lodsb               ;Copy name.
  360.     stosb
  361.     test AL,AL
  362.     jnz tcop
  363.         ; open the file, save handle in hbin.
  364.    mov DX,offset d.argv ;Point to path and name.
  365.    mov AX,3D00h         ;Open it for read.
  366.    int 21h
  367.    mov DX,offset errr   ;Read error message.
  368.    jb tret
  369.    mov hbin,AX          ;Save handle.
  370.         ; read file, encode and write it.
  371.   rdlp:
  372.     mov BX,hbin         ;Get file handle.
  373.     mov DX,offset d.rec ;Record buffer.
  374.     mov CX,RECSZ        ;Read up to RECSZ bytes.
  375.     mov AH,3Fh          ;Read from file.
  376.     int 21h
  377.     mov DX,offset errr  ;Read error message.
  378.     jb tret
  379.     test AX,AX          ;See if end of file.
  380.     jz rdone            ;If so, then done.
  381.     mov CX,AX           ;Number of bytes to do.
  382.     call encode         ;Encode and write it.
  383.     jmp short rdlp      ;Read rest of file.
  384.   rdone:
  385.    mov BX,hbin          ;Get file handle.
  386.    mov AH,3Eh           ;Close file.
  387.    int 21h
  388.         ; look for next match.
  389.    mov DX,offset d.find
  390.    mov AH,4Fh           ;Find next.
  391.    int 21h
  392.    jnb wfile            ;If another match, then do it.
  393.         ; done--flush output buffer and leave.
  394.   call flush
  395.   mov DX,offset fin     ;Successful completion message.
  396.         ; return with message pointer in DX.
  397.  tret:
  398.   mov DI,DX             ;Save message pointer.
  399.   mov DX,asc            ;Point to output name.
  400.   mov CX,20h            ;Un-hide file.
  401.   mov AX,4301h          ;Set attribute.
  402.   int 21h
  403.   mov DX,DI             ;Restore message pointer.
  404.   ret
  405.  ;
  406. toasc endp
  407.  
  408.  
  409.  
  410. frmasc proc near
  411.  ;
  412.  ; Recreate the original binary files from an ASCIFY format text file.
  413.  ; DI -> source.
  414.  ;
  415.         ; open input ASCIFY file.
  416.   mov DX,DI             ;Point DX to file name.
  417.   mov AX,3D00h          ;Open file for read.
  418.   int 21h
  419.   mov DX,offset errf    ;Find error.
  420.   jnb fok
  421.    jmp errx
  422.  fok:
  423.   mov hasc,AX           ;Save file handle.
  424.   mov bcnt,0            ;Clear input buffer.
  425.   call keyseed          ;Set up translation.
  426.   mov DI,offset d.mix85 ;Put 0..84 in scramble table.
  427.   mov CX,85
  428.   mov AL,0
  429.  fmix:
  430.    stosb
  431.    inc AX
  432.    loop fmix
  433.         ; do next binary file in input file.
  434.  flp:
  435.         ; get header, create file.
  436.    mov AX,22            ;Get next header.
  437.    call decode
  438.    mov DX,offset fin
  439.    jnz fretz            ;If end of file, then done--return OK.
  440.    mov SI,offset d.rec  ;Copy header to find area.
  441.    mov DI,offset d.attr
  442.    mov CX,22
  443.    rep movsb
  444.    mov SI,offset fmsg1  ;Display name of file being decoded.
  445.    call puts
  446.    mov SI,offset d.fname
  447.    call puts
  448.    mov SI,offset msg2
  449.    call puts
  450.    mov DX,offset d.fname        ;Point to file name.
  451.    mov AH,3Ch           ;Create file.
  452.    int 21h
  453.    mov DX,offset errw
  454.    jb fret              ;Abort if can't create.
  455.    mov hbin,AX          ;Save file handle.
  456.         ; read file and decode it.
  457.    mov SI,word ptr d.fsiz       ;Get size into DI:SI.
  458.    mov DI,word ptr d.fsiz+2
  459.   frlp:
  460.         ; compute size of next block.
  461.     mov AX,RECSZ        ;Convert up to RECSZ decoded bytes.
  462.     test DI,DI          ;See if size >= RECSZ.
  463.     jnz fdec            ;If so, decode RECSZ bytes.
  464.      cmp SI,AX
  465.      jae fdec
  466.       mov AX,SI         ;If size < RECSZ, decode size bytes.
  467.    fdec:
  468.         ; decode it.
  469.     push DI             ;Save bytes left to read.
  470.     push SI
  471.     push AX             ;Save bytes requested.
  472.     call decode         ;Decode the block.
  473.     pop CX              ;Get bytes of data.
  474.     pop SI              ;Restore bytes left to read.
  475.     pop DI
  476.     mov DX,offset errs
  477.    fretz:               ;(extend jump)
  478.     jnz fret            ;If end of file, abort with error.
  479.         ; write data to file.
  480.     sub SI,CX           ;Subtract bytes from size.
  481.     sbb DI,0
  482.     mov BX,hbin         ;Get handle of file.
  483.     mov DX,offset d.rec ;Point to decoded data.
  484.     mov AH,40h          ;Write to file.
  485.     int 21h
  486.     mov DX,offset errw
  487.     jb fret             ;Abort on error.
  488.     cmp AX,CX           ;Also check for enough bytes written.
  489.     jne fret
  490.         ; see if got it all yet.
  491.     mov AX,SI           ;See if any bytes left.
  492.     or AX,DI
  493.     jnz frlp            ;If so, get more.
  494.         ; set date and time and close file.
  495.    mov BX,hbin          ;Put handle in BX.
  496.    mov CX,d.time        ;Get time.
  497.    mov DX,d.date        ;Get date.
  498.    mov AX,5701h         ;Set date and time.
  499.    int 21h
  500.    mov AH,3Eh           ;Close.
  501.    int 21h
  502.    mov DX,offset errw
  503.    jb fret              ;Abort on error.
  504.         ; look for next header.
  505.   jmp flp
  506.         ; done--return.
  507.  fret:
  508.   ret
  509.  ;
  510. frmasc endp
  511.  
  512.  
  513.  
  514. args proc near
  515. ;
  516. ; process args--delimited by slashes and/or blanks, put delimiters
  517. ; and pointers at DI (three bytes per arg).  Uses SI,DI,CX,AX.
  518. ;
  519.         ; set up.
  520.   mov SI,80h            ;Point to args.
  521.   lodsb                 ;Get length.
  522.   cbw
  523.   xchg AX,CX            ;Put length in CX.
  524.         ; scan arguments.
  525.   mov AL,' '            ;Initialize delimiter as a space.
  526.  arglp:
  527.    jcxz argfin
  528.         ; look for non-delimiter.
  529.   argdlp:
  530.     mov AH,AL           ;Save delimiter.
  531.     lodsb
  532.     cmp AL,' '          ;If delimiter, keep looking.
  533.     je argd
  534.     cmp AL,'/'
  535.    argd:
  536.     loope argdlp
  537.    je argfin            ;If ran out of line, then done.
  538.         ; save info on arg.
  539.    mov AL,AH            ;Get delimiter.
  540.    stosb                ;Save it.
  541.    mov AX,SI            ;Point to arg.
  542.    dec AX
  543.    stosw                ;Save pointer.
  544.         ; look for delimiter.
  545.    jcxz argend          ;If no more chars, that is a delimiter.
  546.   argalp:
  547.     lodsb
  548.     cmp AL,' '          ;Wait for delimiter.
  549.     je arga
  550.     cmp AL,'/'
  551.    arga:
  552.     loopne argalp
  553.    jne argend
  554.         ; terminate argument, look for more.
  555.    mov byte ptr [SI-1],0        ;End arg.
  556.    jmp short arglp      ;(AL has delimiter.)
  557.  
  558.  argend:
  559.   mov byte ptr [SI],0   ;End arg.
  560.  argfin:
  561.   mov byte ptr [DI],-1  ;End arg pointer list.
  562.   ret
  563.  ;
  564. args endp
  565.  
  566.  
  567.  
  568. encode proc near
  569.  ;
  570.  ; Encode CX bytes at rec, put result in buf and write if necessary.
  571.  ;
  572.         ; compute CRC, append it, and pad out to mulitple of four.
  573.   mov SI,offset d.rec   ;Point to record.
  574.   sub DX,DX             ;Start with CRC=0.
  575.   call crc16            ;Compute CRC-16.
  576.   mov DI,SI
  577.   mov AX,DX             ;Put CRC in AX.
  578.   stosw                 ;Append CRC.
  579.  plp:
  580.    mov AX,DI            ;See if multiple of four.
  581.    sub AL,low offset d.rec      ;(Change to "low (offset d.rec)" for
  582.    test AL,3                    ; Borland Turbo Assembler.)
  583.    jz is4
  584.    mov AL,0             ;Pad byte.
  585.    stosb                ;Pad until it is.
  586.    jmp short plp
  587.  is4:
  588.         ; convert to base 85.
  589.   mov SI,offset d.rec   ;Point to record.
  590.   sub DI,SI             ;Compute bytes with CRC and padded.
  591.   mov CX,DI
  592.   shr CX,1              ;Compute doublewords to do.
  593.   shr CX,1
  594.   add DI,CX             ;Compute size of result in bytes.
  595.   push DI               ;Save size of result.
  596.   mov DI,offset d.in85  ;Put result in in85.
  597.   call to85             ;Convert it.
  598.         ; do scramble if requested.
  599.   call scramble         ;Shuffle mix85.
  600.         ; convert digits into characters.
  601.   mov BX,offset d.mix85 ;Point to digit->character table.
  602.   mov SI,offset d.in85  ;Point to digits.
  603.   pop CX                ;Get size of in85.
  604.   push CX               ;Save it again.
  605.   call xlt              ;Translate it.
  606.         ; put characters into buffer with CR,LF's.
  607.   mov SI,offset d.in85  ;Point to characters.
  608.   pop DX                ;Get bytes to do.
  609.   mov DI,bptr           ;Get pointer into buffer.
  610.  ldlp:
  611.    mov CX,rem           ;Get bytes left in line.
  612.    cmp CX,DX            ;See if can fill it.
  613.    jg ldlst             ;If not, put in rest.
  614.     sub DX,CX           ;Subtract off what is to be moved.
  615.     rep movsb           ;Move it.
  616.     mov AX,0a0dh        ;CR,LF.
  617.     stosw               ;End line.
  618.     mov rem,LINSZ       ;Reset character count.
  619.     test DX,DX          ;See if more to do.
  620.     jnz ldlp            ;Do rest of in85.
  621.     jmp short ldfin     ;Else done.
  622.   ldlst:
  623.    sub CX,DX            ;Compute remaining characters.
  624.    mov rem,CX           ;Save in rem for next time.
  625.    mov CX,DX            ;Put remaining characters in buffer.
  626.    rep movsb
  627.  ldfin:
  628.         ; see if buffer needs to be written.
  629.   cmp DI,-RECL3         ;See if enough room for one more.
  630.   jb nowrt              ;If so, don't need to write buffer.
  631.    mov DX,offset d.buf  ;Point to buffer to write.
  632.    sub DI,DX            ;Compute bytes in buffer.
  633.    mov CX,DI            ;Put in CX.
  634.    mov BX,hasc          ;Get ASCIFY file handle.
  635.    mov AH,40h           ;Write buffer.
  636.    int 21h
  637.    jb enerr             ;If error on write, give up.
  638.    cmp AX,CX            ;Also check for enough bytes written.
  639.    jne enerr
  640.    mov DI,offset d.buf  ;Start buffer over.
  641.  nowrt:
  642.         ; update buffer pointer and return.
  643.   mov bptr,DI           ;Update buffer pointer.
  644.   ret
  645.         ; error on write.
  646.  enerr:
  647.    mov DX,offset errw
  648.    call tret            ;Unhide file.
  649.    jmp bye
  650.  ;
  651. encode endp
  652.  
  653.  
  654.  
  655. flush proc near
  656.  ;
  657.  ; Flush output buffer, close file.
  658.  ;
  659.         ; compute bytes left in buffer.
  660.   mov DX,offset d.buf   ;Point to buffer.
  661.   mov DI,bptr           ;Get buffer pointer.
  662.   mov CX,DI             ;Compute bytes in buffer.
  663.   sub CX,DX
  664.   jz empty              ;If empty, return.
  665.         ; finish off last line.
  666.    mov AL,byte ptr rem
  667.    cmp AL,LINSZ         ;See if unfinished line.
  668.    je flok              ;If not, skip.
  669.     mov AX,0a0dh        ;Else, finish line with CR,LF.
  670.     stosw
  671.     inc CX              ;Update bytes in buffer.
  672.     inc CX
  673.   flok:
  674.         ; write buffer.
  675.    mov BX,hasc          ;Get output handle.
  676.    mov AH,40h           ;Write buffer.
  677.    int 21h
  678.    jb enerr             ;If error, exit (in encode proc).
  679.    cmp AX,CX            ;Also check for enough bytes written.
  680.    jne enerr
  681.  empty:
  682.   mov BX,hasc           ;Close file.
  683.   mov AH,3Eh
  684.   int 21h
  685.   jb enerr              ;If error, exit.
  686.         ; done.
  687.   ret
  688.  ;
  689. flush endp
  690.  
  691.  
  692.  
  693. decode proc near
  694.  ;
  695.  ; Get AX decoded bytes from the coded ASCIFY file into rec.  Return
  696.  ; Z on success, NZ on end of file.
  697.  ;
  698.         ; compute number of digits to get and numbers to convert.
  699.   push AX               ;Save bytes to get.
  700.   add AX,5              ;Compute digits needed for it.
  701.   and AX,not 3
  702.   mov DX,AX
  703.   shr AX,1
  704.   shr AX,1
  705.   add DX,AX             ;Now DX is the number of digits.
  706.   push AX               ;Save number of 5 digit base 85 numbers.
  707.   push DX               ;Save number of digits.
  708.         ; get DX digits from the ASCIFY file.
  709.         ; find next digit (0..84 after conversion).
  710.   mov BP,offset d.in85  ;Put them in d.in85.
  711.   mov DI,bptr           ;Next bytes in buffer.
  712.   mov CX,bcnt           ;Bytes left in buffer.
  713.  dgetlp:
  714.    jcxz dld             ;If no bytes in buffer, reload it.
  715.   dnew:
  716.    mov AL,-1            ;Look for next digit (not -1).
  717.    repe scasb
  718.    jne dgot             ;Found a digit.
  719.   dld:                  ;No digits, ran out of buffer--reload.
  720.     mov SI,DX           ;Save number of digits still needed.
  721.     mov BX,hasc         ;Get handle for input file.
  722.     mov DX,offset d.buf ;Buffer start.
  723.     mov CX,DX           ;Compute bytes in buffer (to end of 64K).
  724.     neg CX
  725.     mov AH,3Fh          ;Read from file.
  726.     int 21h
  727.     mov DX,offset errr
  728.     jnb dok             ;Abort on error.
  729.      jmp bye
  730.    dok:
  731.     mov DX,SI           ;Restore digits left to read.
  732.     mov CX,AX           ;Put bytes read in CX.
  733.     jcxz deof           ;If end of file, give up.
  734.     mov BX,offset d.todig       ;Translate table.
  735.     mov SI,offset d.buf ;Convert characters to digits.
  736.     push CX             ;Save bytes read.
  737.     call xlt            ;Translate characters to digits.
  738.     pop CX              ;Restore bytes read.
  739.     mov DI,offset d.buf ;Point DI to start of buffer.
  740.     jmp short dnew      ;Try again with new buffer.
  741.         ; find up to DX contiguous digits.
  742.   dgot:
  743.    lea SI,[DI-1]        ;Point SI to the first digit.
  744.    mov BX,CX            ;Put bytes remaining in buffer in BX.
  745.    inc BX
  746.    cmp BX,DX            ;See if more than DX left.
  747.    jbe dfew             ;If not, scan up to end.
  748.     mov CX,DX           ;If more than DX, scan only up to DX.
  749.     dec CX
  750.   dfew:
  751.    jcxz dmov            ;If just one digit, move it.
  752.     repne scasb         ;Look for next non-digit.
  753.     jne dmov            ;If hit end, move the rest of buffer.
  754.      dec DI             ;Else, back up to first non-digit.
  755.   dmov:
  756.         ; move contiguous digits found to BP.
  757.    sub DI,SI            ;Compute number found.
  758.    mov CX,DI            ;Put in CX for move.
  759.    mov DI,BP            ;Point DI to destination.
  760.    sub DX,CX            ;Subtract number to move from DX.
  761.    sub BX,CX            ;Subtract number to move from bytes left.
  762.    rep movsb            ;Move the digits.
  763.    mov BP,DI            ;Update destination address.
  764.    mov CX,BX            ;Put bytes left in CX.
  765.    mov DI,SI            ;Put address of bytes left in DI.
  766.    test DX,DX           ;See if any more digits needed.
  767.    jnz dgetlp           ;If so, get them.
  768.         ; got the digits, update pointer and count.
  769.   mov bptr,DI           ;Update buffer pointer.
  770.  deof:
  771.   mov bcnt,CX           ;Update buffer count.
  772.   test DX,DX            ;See if successful.
  773.   jz dsuc               ;If so, go on.
  774.    add SP,6             ;Trash stuff on stack.
  775.  dret:
  776.    ret                  ;Return NZ for end of file.
  777.  dsuc:
  778.         ; do scramble if requested.
  779.   call scramble
  780.   pop DX                ;Get number of digits to unscramble.
  781.   jz deno
  782.    mov SI,offset d.mix85        ;Make unscramble table.
  783.    mov DI,offset d.unmix
  784.    call invert
  785.    mov BX,offset d.unmix        ;Point to unscramble table.
  786.    mov SI,offset d.in85
  787.    mov CX,DX            ;Number to unscramble.
  788.    call xlt             ;Unscramble.
  789.  deno:
  790.         ; convert digits into bytes.
  791.   pop CX                ;Get number of 5 digit numbers.
  792.   mov SI,offset d.in85  ;Point to base 85 digits.
  793.   mov DI,offset d.rec   ;Where to put bytes.
  794.   call frm85            ;Convert.
  795.   pop CX                ;Get number of bytes of data.
  796.   mov SI,offset d.rec   ;Point to data.
  797.   sub DX,DX             ;Initialize CRC to zero.
  798.   call crc16            ;Compute CRC-16.
  799.   lodsw                 ;Get CRC from data.
  800.   cmp AX,DX             ;See if correct.
  801.   mov DX,offset errc
  802.   je dret               ;Done, return Z on success.
  803.   jmp bye               ;Abort on CRC error.
  804.  ;
  805. decode endp
  806.  
  807.  
  808.  
  809. keyseed proc near
  810.  ;
  811.         ; make d.todig table from set85.
  812.   mov SI,offset set85
  813.   mov DI,offset d.todig
  814.   call invert
  815.         ; if there is a key, make five characters out of it.
  816.   mov SI,key            ;Get pointer to key.
  817.   test SI,SI            ;See if there is a key.
  818.   jz nokey              ;If not, then skip.
  819.    mov DI,offset d.rec  ;Point to an area to use.
  820.    mov CX,5             ;Move up to five characters.
  821.   klp:
  822.     lodsb
  823.     test AL,AL          ;See if end of key.
  824.     jz kend
  825.     stosb
  826.     loop klp
  827.   kend:
  828.    jcxz kgen            ;If got five characters, use them.
  829.     rep stosb           ;Else, fill out with zeros.
  830.   kgen:
  831.         ; see if got request for hidden key input.
  832.    mov DI,offset d.rec  ;Point to first character of key.
  833.    cmp byte ptr [DI],'?'
  834.    jne kgot             ;If not ?, then have key.
  835.     mov SI,offset kask  ;Ask for key.
  836.     mov CX,5            ;Just five characters.
  837.     call hide           ;Get key with no echo.
  838.   kgot:
  839.         ; convert five characters to a deterministic seed for rnd.
  840.    mov BX,offset d.todig        ;Convert to base 85 digits.
  841.    mov SI,offset d.rec  ;Point to characters.
  842.    mov CX,5
  843.    call xlt
  844.    mov SI,offset d.rec  ;Convert to base 256.
  845.    mov DI,offset rseed
  846.    mov CX,1
  847.    call frm85
  848.         ; done.
  849.  nokey:
  850.   ret
  851.  ;
  852. keyseed endp
  853.  
  854.  
  855.  
  856. hide proc near
  857.  ;
  858.  ; Get CX characters at DI from console with no echo.
  859.  ; SI points to the prompt.
  860.  ;
  861.         ; set up.
  862.   mov BX,DI             ;Save address of start.
  863.   add CX,DI             ;Save address of end.
  864.         ; prompt for input.
  865.  hagain:
  866.   push SI               ;Save prompt address.
  867.   call puts             ;Give prompt at SI.
  868.   pop SI                ;Restore address.
  869.         ; read a character.
  870.  hrd:
  871.    mov AH,8             ;Get input with no echo.
  872.    int 21h
  873.    test AL,AL           ;See if extended code.
  874.    jnz hnrm
  875.     mov AH,8            ;If so, ignore it.
  876.     int 21h
  877.     jmp short hrd
  878.   hnrm:
  879.         ; check for return or backspace.
  880.    cmp AL,13            ;See if return.
  881.    je hrend             ;If so, then done.
  882.    cmp AL,8             ;See if backspace.
  883.    je hback             ;If so, process.
  884.    cmp AL,' '           ;See if other control character.
  885.    jb hrd               ;If so, ignore.
  886.         ; got what appears to be a normal character.
  887.     cmp DI,CX           ;See if already have enough characters.
  888.     jae hrd             ;If so, ignore it.
  889.     stosb               ;Else, save character and echo space.
  890.     mov DL,' '
  891.     mov AH,2
  892.     int 21h
  893.     jmp short hrd       ;Get more.
  894.         ; process backspace.
  895.   hback:
  896.     cmp DI,BX           ;See if at start.
  897.     je hrd              ;If so, ignore backspace.
  898.     dec DI              ;Else, erase character and echo backspace.
  899.     mov DL,AL
  900.     mov AH,2
  901.     int 21h
  902.     jmp short hrd
  903.         ; process return.
  904.  hrend:
  905.   mov DX,offset crlf    ;Send carriage return, line feed.
  906.   mov AH,9
  907.   int 21h
  908.         ; if got no input, prompt again.
  909.   cmp DI,BX             ;See if no key.
  910.   je hagain             ;If so, ask again.
  911.         ; fill out buffer with zeros.
  912.   sub CX,DI             ;Fill out with zeros.
  913.   jz hret               ;If full, then skip.
  914.    mov AL,0             ;Else append zeros.
  915.    rep stosb
  916.  hret:
  917.         ; done.
  918.   ret
  919.  ;
  920. hide endp
  921.  
  922.  
  923.  
  924. invert proc near
  925.  ;
  926.  ; Invert 85 byte table at SI to a 256 byte table at DI.
  927.  ;
  928.         ; clear inverse table.
  929.   mov BX,DI             ;Save address of inverse table.
  930.   mov CX,128
  931.   mov AX,-1
  932.   rep stosw
  933.         ; make inverse table.
  934.   mov DI,BX
  935.   mov CX,85
  936.   inc AX                ;Set digit (AH) to zero.
  937.   mov BH,0              ;Set high byte of table offset to zero.
  938.  invlp:
  939.    lodsb                ;Get character.
  940.    mov BL,AL            ;Put offset in BX.
  941.    mov [DI+BX],AH       ;Store index at computed position.
  942.    inc AH               ;Increment index.
  943.    loop invlp
  944.   ret
  945.  ;
  946. invert endp
  947.  
  948.  
  949.  
  950. scramble proc near
  951.  ;
  952.   cmp key,0             ;See if encryption/decryption requested.
  953.   je noscrm
  954.         ; shuffle mix85.
  955.    mov BP,offset d.mix85
  956.    mov CX,85
  957.    call shuffle
  958.         ; return NZ to scramble.
  959.    stc
  960.    sbb AX,AX
  961.  noscrm:
  962.   ret
  963.  ;
  964. scramble endp
  965.  
  966.  
  967.  
  968. to85 proc near
  969.  ;
  970.  ; Convert CX doublewords at SI to CX five-digit base 85 numbers at DI.
  971.  ; In the comments below, the notation "p:q" is "p*65536 + q".
  972.  ; Approximate execution time on an IBM PC is 252 uS per four bytes.
  973.  ; All registers used.
  974.  ;
  975.   mov BX,85             ;Convert to base 85.
  976.  tolp:
  977.    lodsw                ;Get low word (q0) into AX.
  978.    mov BP,AX            ;Put in BP.
  979.    lodsw                ;Get high word (p0) into AX.
  980.          ; AX:BP = p0:q0
  981.    sub DX,DX            ;DX:AX = p0
  982.    div BX               ;DX = p0%85, AX = p0/85
  983.    xchg AX,BP           ;DX:AX = (p0%85):q0, BP = p0/85
  984.    div BX               ;DX = ((p0%85):q0)%85, AX = ((p0%85):q0)/85
  985.    xchg AX,BP           ;DX = (p0:q0)%85, AX:BP = (p0:q0)/85
  986.         ; Store remainder (in DL) in result.
  987.    mov [DI],DL          ;Store base 85 digit 0.
  988.    inc DI
  989.         ; AX:BP = (p0:q0)/85 = p1:q1
  990.    sub DX,DX            ;DX:AX = p1
  991.    div BX               ;DX = p1%85, AX = p1/85
  992.    xchg AX,BP           ;DX:AX = (p1%85):q1, BP = p1/85
  993.    div BX               ;DX = (p1:q1)%85, BP:AX = (p1:q1)/85
  994.         ; Store remainder (in DL) in result.
  995.    mov [DI],DL          ;Store base 85 digit 1.
  996.    inc DI
  997.         ; BP:AX = (p1:q1)/85 = p2:q2 (note: p2 < 85)
  998.    mov DX,BP            ;DX:AX = p2:q2
  999.    div BX               ;DX = (p2:q2)%85, AX = (p2:q2)/85
  1000.         ; Store remainder (in DL) in result.
  1001.    mov [DI],DL          ;Store base 85 digit 2.
  1002.    inc DI
  1003.         ; AX = (p2:q2)/85 = q3 (note: q3 < 85*85 < 64K)
  1004.    div BL               ;AH = q3%85, AL = q3/85
  1005.         ; Store remainder (in AH) in result.
  1006.    mov [DI],AH          ;Store base 85 digit 3.
  1007.    inc DI
  1008.         ; AL = q3/85 = q4 (note: q4 < 85)
  1009.         ; Store remainder (in AL) in result.
  1010.    stosb                ;Store base 85 digit 4.
  1011.         ; Do CX conversions.
  1012.    loop tolp
  1013.   ret                   ;Done.
  1014.  ;
  1015. to85 endp
  1016.  
  1017.  
  1018.  
  1019. frm85 proc near
  1020.  ;
  1021.  ; Convert CX five-digit base 85 numbers at SI to CX doublewords at DI.
  1022.  ; Approximate execution time on an IBM PC is 248 uS per conversion.
  1023.  ; All registers used.
  1024.  ;
  1025.   mov BP,SP             ;Point to auxilary area.
  1026.   sub SP,4              ;Allocate two words.
  1027.   mov [BP-2],DI         ;Save destination pointer.
  1028.   mov word ptr [BP-4],0 ;Initialize overflow flag.
  1029.  frmlp:
  1030.    mov BX,85            ;Convert from base 85, BH used as zero.
  1031.         ; process digit 4.
  1032.    mov AL,[SI+4]        ;AL = d4
  1033.         ; process digit 3.
  1034.    mul BL               ;AX = d4*85
  1035.    add AL,[SI+3]
  1036.    adc AH,BH            ;AX = d4*85 + d3
  1037.         ; process digit 2.
  1038.    mul BX               ;DX:AX = d4*85^2 + d3*85
  1039.    add AL,[SI+2]
  1040.    adc AH,BH
  1041.    adc DX,0             ;DX:AX = d4*85^2 + d3*85 + d2
  1042.         ; process digit 1.
  1043.    mov DI,AX            ;Multiply DX:AX by 85.
  1044.    mov AX,DX
  1045.    mul BX
  1046.    xchg AX,DI
  1047.    mul BX
  1048.    add DX,DI            ;DX:AX = d4*85^3 + d3*85^2 + d2*85
  1049.    add AL,[SI+1]        ;Add digit 1 to DX:AX.
  1050.    adc AH,BH
  1051.    adc DX,0             ;DX:AX = d4*85^3 + d3*85^2 + d2*85 + d1
  1052.         ; process digit 0.
  1053.    mov DI,AX            ;Multiply DX:AX by 85.
  1054.    mov AX,DX
  1055.    mul BX
  1056.    xchg AX,DI
  1057.    push DX              ;Save overflow, if any.
  1058.    mul BX
  1059.    pop BX               ;Get overflow.
  1060.    add DX,DI            ;DX:AX = d4*85^4 + d3*85^3 + d2*85^2 + d1*85
  1061.    adc BX,0             ;Update overflow.
  1062.    add AL,[SI]          ;Add digit 0 to DX:AX.
  1063.    adc AH,0
  1064.    adc DX,0             ;DX:AX = d4*85^4 + d3*85^3 + d2*85^2 + d1*85 + d0
  1065.    adc BX,0             ;Update overflow.
  1066.          ;Store results and update pointers.
  1067.    add SI,5             ;Increment source pointer.
  1068.    mov DI,[BP-2]        ;Get address for four byte results.
  1069.    stosw                ;Store low word.
  1070.    xchg AX,DX
  1071.    stosw                ;Store high word.
  1072.    mov [BP-2],DI        ;Save address for four byte results.
  1073.    or [BP-4],BX         ;If overflow, set overflow flag.
  1074.         ;Do CX conversions.
  1075.    loop frmlp
  1076.   mov AX,[BP-4]         ;Return overflow flag.
  1077.   mov SP,BP             ;Restore stack pointer.
  1078.   ret                   ;Done.
  1079.  ;
  1080. frm85 endp
  1081.  
  1082.  
  1083.  
  1084. crc16 proc near
  1085.  ;
  1086.  ; Compute CRC-16 on CX bytes at SI with DX as the initial CRC.  The 16
  1087.  ; bit CRC is returned in DX.  The CRC-16 polynomial is: X^16 + X^15 +
  1088.  ; X^2 + 1.  The time required to compute the CRC on an IBM PC is
  1089.  ; roughly 42 uS per byte.  All registers except BP and DI used.
  1090.  ;
  1091.  ; This routine was shamelessly copied (and translated into 8086 code)
  1092.  ; from "Byte-wise CRC Calculations" by Aram Perez in the June 1983
  1093.  ; issue of IEEE Micro.
  1094.  ;
  1095.   jcxz crcfn            ;If no data, CRC is unchanged.
  1096.   crclp:
  1097.     lodsb               ;Get next byte.
  1098.     xor AL,DL           ;Exclusive or data byte with low byte of CRC.
  1099.     mov BL,AL           ;Copy it.
  1100.     shl AL,1            ;Shift one bit left, CY=X8, P=XX7.
  1101.     mov AH,AL           ;Save it.
  1102.     mov AL,0            ;Make XX7=0 and XX8=XX7.
  1103.     jpe crc1            ;If XX7=1 then,
  1104.      mov AL,00000011b   ; make XX7 and XX8=1.
  1105.    crc1:
  1106.     jnc crc2            ;If X8=1 then,
  1107.      xor AL,00000010b   ; complement XX8.
  1108.    crc2:
  1109.     mov BH,AL           ;Save XX8 and XX7 (BX bits 9..0 = R16..R7).
  1110.     xor BL,AH           ;Get R14 through R7.
  1111.     shr AL,1            ;Get XX8 in bit 0.
  1112.     ror BX,1            ;Shift BX left 6 bits (by shifting right 2).
  1113.     ror BX,1            ;(Actually BX is backwards after this.)
  1114.     and BH,11000000b
  1115.     or AL,BH            ;Put XX8 in BH.
  1116.     xor AL,DH
  1117.     mov DL,AL
  1118.     mov DH,BL
  1119.     loop crclp
  1120.  crcfn:
  1121.   ret                   ;Done--return CRC in DX.
  1122.  ;
  1123. crc16 endp
  1124.  
  1125.  
  1126.  
  1127. xlt proc near
  1128.  ;
  1129.  ; Translate CX bytes at SI using table at BX.
  1130.  ;
  1131.   mov DI,SI             ;Translate in place.
  1132.  xlp:
  1133.    lodsb
  1134.    xlatb
  1135.    stosb
  1136.    loop xlp
  1137.   ret
  1138.  ;
  1139. xlt endp
  1140.  
  1141.  
  1142.  
  1143. shuffle proc near
  1144.  ;
  1145.  ; Shuffle n bytes (algorithm from Knuth).
  1146.  ; Address of bytes in BP, number in CX.
  1147.  ; All registers used.
  1148.  ;
  1149.   dec CX                ;j = n - 1.
  1150.   jz shfend             ;If 1 item to shuffle, then done.
  1151.  shflp:
  1152.    push CX              ;Save j.
  1153.    call rnd             ;k = random number in 0..j.
  1154.    pop CX               ;Get j back.
  1155.    cmp AX,CX            ;See if swap needed (k != j).
  1156.    je shno              ;If not, skip swap.
  1157.     mov SI,AX           ;Put offsets in SI, DI.
  1158.     mov DI,CX
  1159.     mov AL,[BP+SI]      ;Get a[k].
  1160.     xchg AL,[BP+DI]     ;Exchange with a[j].
  1161.     mov [BP+SI],AL      ;Set new a[k].
  1162.   shno:
  1163.    loop shflp           ;Do n-1 swaps.
  1164.  shfend:
  1165.   ret
  1166.  ;
  1167. shuffle endp
  1168.  
  1169.  
  1170.  
  1171. rmul: dd 1664525        ;Good multiplier (Knuth Vol. 2, 2nd Ed,
  1172.                         ;                 pg. 102, line 26).
  1173. rnd proc near
  1174.  ;
  1175.  ; Return random number in [0..n] using linear congruential method.
  1176.  ; n is in CX.  All registers except BP used.
  1177.  ;
  1178.   push CX               ;Save bound.
  1179.         ; multiply seed by multiplier modulo 2^32.
  1180.   mov SI,offset rmul    ;Point SI to multiplier.
  1181.   mov DI,offset rseed   ;Point DI to seed.
  1182.   lodsw                 ;Get low word of multiplier.
  1183.   mov BX,AX             ;Save that.
  1184.   mul word ptr [DI+2]   ;Multiply by high word of seed.
  1185.   mov CX,AX             ;Save low word of that for high word of result.
  1186.   lodsw                 ;Get high word of multiplier.
  1187.   mul word ptr [DI]     ;Multiply by low word of seed.
  1188.   add CX,AX             ;Add low word into high word of result.
  1189.   xchg AX,BX            ;Get low word of multiplier.
  1190.   mul word ptr [DI]     ;Multiply by low word of seed.
  1191.   add DX,CX             ;Add other high word components.
  1192.         ; add constant modulo 2^32.
  1193.   add AX,1              ;Constant = 1 (relatively prime to
  1194.   adc DX,0              ; everything).
  1195.         ; store seed back.
  1196.   stosw                 ;Store low word.
  1197.   mov [DI],DX           ;Store high word.
  1198.         ; get random number in [0..n] (as per Knuth's recommendation).
  1199.   pop CX                ;Get n.
  1200.   inc CX                ;Use n+1 to multiply "fraction" in [0..1).
  1201.   jz rndf               ;If n is 65535, then return high part of seed.
  1202.    mov BX,DX            ;Save high part of seed.
  1203.    mul CX               ;Multiply low part of fraction by n+1.
  1204.    xchg AX,BX           ;Get high part of fraction.
  1205.    mov BX,DX            ;Save high part of product.
  1206.    mul CX               ;Multiply high part of fraction by n+1.
  1207.    add AX,BX            ;Add component from low part of fraction.
  1208.    adc DX,0
  1209.  rndf:
  1210.   xchg AX,DX            ;Truncate to integer.
  1211.   ret
  1212.  ;
  1213. rnd endp
  1214.  
  1215.  
  1216.  
  1217. puts proc near
  1218.  ;
  1219.  ; Output string at SI to standard output.
  1220.  ;
  1221.  putslp:
  1222.    lodsb                ;Get next character.
  1223.    test AL,AL           ;See if end of string.
  1224.    jz putsdn            ;If so, done.
  1225.    mov DL,AL            ;Send to standard output.
  1226.    mov AH,2
  1227.    int 21h
  1228.    jmp short putslp     ;Get next character.
  1229.  putsdn:
  1230.   ret
  1231.  ;
  1232. puts endp
  1233.  
  1234.  
  1235. ;
  1236. ; Data area.
  1237. ;
  1238.  even                   ;Move to word boundary.
  1239. d equ $
  1240.  
  1241. ascify ends
  1242.  
  1243. end start
  1244.