Category : Assembly Language Source Code
Archive   : MISC_ASM.ZIP
Filename : UUE12.ASM

 
Output of file : UUE12.ASM contained in archive : MISC_ASM.ZIP
name UUENCODE
page 55,132
title 'UUENCODE.ASM'
;
; UUENCODE.ASM -- UUEncodes a Binary File
;
; Cmd line processing, general layout from UU.ASM by Theodore A. Kaldis
;
;Author: David Kirschbaum
; Toad Hall
; [email protected]
;Released to Public Domain
;
; To assemble and link this program into the executable UUENCODE.COM:
; (It will NOT run compiled as an .EXE program!)

; MASM UUE;
; LINK UUE;
; (If you just have EXE2BIN:
; EXE2BIN UUE
; REN UUE.BIN UUENCODE.COM (or whatever)
; (If you have Public Domain EXE2COM or equivalent:
; EXE2COM UUE
; REN UUE.COM UUENCODE.COM (or whatever)
; (Delete the bogus .OBJ file.)

;v1.3, 9 Nov 88
; - Tightening again.
; BP holds binary character count throughout each line uuencoding.
; Tightened uuencoding algorithm itself (fewer shifts & register shuffling).
; - Bumping prompted input filename input to 80 chars.
; - Different (faster) way of testing:
; (1) if we've hit end of the binary read buffer.
; (2) if we've completed 60 uuencoded output characters.
; - Changing READSIZE to a MOD 3 so we lessen problems from reading
; non-MOD 3 numbers of binary characters (until the LAST read).
; This (and other fixes) finally fixed the problem with 'non-MOD 3'
; sized binary files adding bogus uuencoded bytes to the end.
; - Found (and fixed) new bug: one junk byte sneaking in after a binary
; buffer refill from file read. (Problem was uuencoded buffer overlying
; binary buffer.)
;
;v1.1, 7 Sep 88
; - Provisional compilation .. Set STDOUT to 1 to enable redirection.
; Else it'll use the default "filename.uue" format.
; - Fixed bug in filename parsing (need to find the SECOND '\' when
; stripping paths!

;v1.0, 6 Sep 88
;Hacked together from Kaldis' UU.ASM, the public domain UUENCODE.C,
; UUENCODE.PAS, etc.
;Uses same algorithm (kinda) as the Turbo Pascal UUENCODE.PAS (not the
;C version which does too much bit fiddling).
;
; - The last few uuencoded characters are coming out different from the
; products of UUENCODE.PAS and my Unix mainframe's uuencode. (Actually,
; they're ALL different!) Doesn't seem to matter: .ARC files, when
; uuencoded and then uudecoded again, still check out. Might bite us
; somewhere, but haven't had any problems in lots of tests.
;
; David Kirschbaum
; Toad Hall
; [email protected]
;-------------------------------------------
LF EQU 10
CR EQU 13
CMD_TAIL EQU 80H ;PSP cmd line offset
READSIZE EQU 45000 ;max bytes for a binary file read.
;Small enough to fit within our
;64Kb code/data space. Size must
;be an even divisor of 3.
LINELEN EQU 60 ;nr chars in a uuencoded line

STDOUT EQU 0 ;change to non-0 to enable redirection

Cseg SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:Cseg,DS:Cseg, ES:Cseg
org 100H

UuEncode PROC near
jmp Start ;jump over data

IF STDOUT
usage db 'UUENCODE [d:][\path\]binary.fil [>output] '
db CR,LF,'(Uses redirection)'
ELSE
usage db 'UUENCODE [d:][\path\]binary.fil ',CR,LF
db 'produces binary.UUE on current drive\path.'
ENDIF
db CR,LF,'$'

msg_v1 DB 'This program requires DOS V2.0 or higher.',CR,LF,'$'

pr_inp DB CR,LF,'Input path/file: '
PR_INP_LEN EQU $-pr_inp

err_inp DB 'Input file error.',CR,LF
ERR_INP_LEN EQU $-err_inp

err_out DB 'Output file error.',CR,LF
ERR_OUT_LEN EQU $-err_out

end_msg DB '`',CR,LF,'end',CR,LF
END_MSG_LEN EQU $-end_msg

no_action db 'No action',CR,LF,'$'

inp_handle DW 0 ;input file handle
out_handle DW 1 ;output file handle (default StdOut)
read_count DW data_buf ;nr binary bytes read v1.2
last_flag db 0 ;set true when partial read
;-------------------------------------------

Start:
;v1.2 Insure we're DOS 2.0 or above (or handles won't work!)
MOV AH,30h ;get DOS version
INT 21h
CMP AL,2 ;2.0 or above?
JAE Chk_Cmd ;yep, ok v1.2
MOV DX,OFFSET msg_v1 ;'DOS 2.0 or above'
Msg_Die:
MOV AH,9 ;display string
INT 21h
mov ax,4C01H ;terminate, ERRORLEVEL 1
int 21H

;-------------------------------------------
;Check our PSP command line for a target filename.
Chk_Cmd:
MOV SI,CMD_TAIL ;move cmd line parm
MOV DI,offset inp_fil ;to our filename buffer
CLD ;insure fwd
LODSB ;cmd line length byte
or al,al ;nothing there?
jz Prompt_User ;yep, nothing there v1.2

mov ah,20H ;get a handy space
Strip_Ct:
LODSB ;next cmd line char
CMP AL,ah ;gobble spaces,tabs, etc.
JBE Strip_Ct
Ct_Char:
CMP AL,ah ;ctrl char? (e.g., CR)
JBE Prog_Go ;yep, done v1.2
STOSB ;stuff filename byte
LODSB ;snarf next cmdline char
JMP SHORT Ct_Char ;and loop
;-------------------------------------------
;Protect DI .. it points to
; (1) the filename buffer byte just beyond our file name
; (so we can AsciiZ) or
; (2) to filename buffer start (indicating no input!).

Prog_Go:
cmp di,offset inp_fil ;did we get a cmd line filename?
ja Open_Inp_Fil ;yep

;Ok, let's prompt our user:
Prompt_User:
MOV DX,OFFSET pr_inp ;'Input/file name:' prompt
MOV CX,PR_INP_LEN ;nr chars to display
MOV BX,1 ;default output handle
MOV AH,40h ;write to file or device
INT 21h

;Get user's kbd input
mov dx,di ;DI points to filename buff start
MOV CX,80 ;up to 80 chars v1.2
xor bx,bx ;standard input handle
MOV AH,3Fh ;read from file or device
INT 21h
add di,AX ;nr bytes read
inc di ;adjust for add v1.2
inc di ;..and CR v1.2

Open_Inp_Fil:
MOV DX,offset inp_fil ;input filename buffer
cmp di,dx ;no name at all?
ja Open1 ;ok, continue
mov dx,offset usage ;'Usage..'
mov ah,9 ;display str
int 21H
mov dx,offset no_action ;'No action'
jmp short Msg_Die ;display, terminate

Open1:
MOV AX,3D00h ;open file
mov [di],al ;make input filename AsciiZ
INT 21h
JNC Inp_Open ;went ok
JMP Inp_Err ;input file open error, die

;-------------------------------------------
Inp_Open:
MOV inp_handle,AX ;save input file handle

;Take input file name (up to file separator) (no paths)
;move "filename.typ" into our uuencoded buffer and write to file.
;First scan for any paths, drives, etc.
;DI points to the last filename char+1, so we can compute length.

mov si,offset inp_fil ;remember input filename buff start
mov cx,di ;last char+1
sub cx,si ;-start = nr chars+1
dec cx ;adjust
;We'll start at the end and scan back toward the front.
;Remember, scasb decrements DI even if it finds the scan char,
;so we'll have to adjust after the find.
mov al,'\' ;first scan for paths
std ;going backwards
repne scasb
cld ;set back forward again
jz Found_Path ;DI points to char before '\'
mov di,si ;back to start
cmp byte ptr [di+1],':' ;how about a drive?
jne Name_Start ;nope, use buffer start
;else fall thru and bump di past 'd:'

;DI's now pointing at the REAL target file name starting char
;(past the drive, paths, etc.)
;We first move the original target file name into our uue buffer
;(which is initialized with the "start 644 " characters).
;This uue buffer will be written as the first line of our uuencoded file.

Found_Path:
inc di ;adjust for scasb or 'd:'
inc di
Name_Start:
mov si,di ;move from input name start
mov dx,si ;save starting point a sec
mov di,offset uue_filename ;move to within uue buffer
OutName_Loop:
lodsb ;snarf each char
; stosb ;and stuff to output file name buff
; or al,al ;0 means filename end
; jnz OutName_Loop ;move the whole thing
; dec di ;back up from last stosb

or al,al ;0 means filename end v1.2
jz OutName_Done ;done v1.2
stosb ;stuff filename char v1.2
jmp OutName_Loop ;keep going v1.2

OutName_Done: ;v1.2
mov ax,0A0DH ;get CR/LF v1.2
stosw ;stuff it in uuencode buffer

;target file name has now been moved into a starting uuencoded file
;text line (to include CR/LF).

IF STDOUT ;use StdOut redirection
mov cx,di ;ptr to last filename char +1
ELSE ;create 'filename.uue'

push di ;remember that ending psn

;Now to create our output file name: filename.uue

mov si,dx ;SI is PSP target filename start
mov di,offset out_fil ;move to output file name buffer
mov dx,di ;we'll need it here also
Uue_Name_Loop:
lodsb ;snarf each char
or al,al ;0 means filename end
jne Check_Dot ;nope
mov al,'.' ;no file type, so fake it
Check_Dot:
stosb ;stuff name char into output name
cmp al,'.' ;go up to separator
jne Uue_Name_Loop ;not yet
;We've now moved the file name (plus the '.') into our output buffer.
;Time for the type
mov ax,'uu' ;'uue'
stosw ;stuff
mov ax,'e' ;'e', DOS AsciiZ terminator v1.2
mov [di],ax ;stuff v1.2

;DX has output filename starting ofs.
;ptr to last byte in uue buffer is on the stack.
xor cx,cx ;normal file attrib (R/W)
mov ah,3CH ;create file
int 21H
pop cx ;restore uue ptr into CX
jnb Create_Ok ;ok
jmp Out_Err ;'Output file error', die

Create_Ok:
mov out_handle,ax ;save output handle

ENDIF ;StdOut or filename.uue

mov dx,offset uue_out ;'start 644 filename.typ', CR/LF
sub cx,dx ;last char-buff start = bytes to write
call Write_File ;write that record
;Write_File set DI to offset uue_out+1,BP=0

Read_Loop:
CALL Read_File ;do the initial binary read
jz Write_Uue_End ;nothing read, done with input v1.2

;Read_File set SI to offset data_buf, didn't touch DI output buffer ptr,
;or BP binary byte counter.

Uue_Loop:
;SI and BP are incrementing as we uuencode 45 bytes of binary data
;into 60 bytes of 'ready to Asciify' data.
mov cx,0604H ;handy constant v1.2
;CL=4,CH=6 v1.2

lodsb ;hunk[1]
mov ah,al ;AH, AL=hunk[1]
shr al,1 ;quad[1] = hunk[1] SHR 2 v1.2
shr al,1 ;(faster this way) v1.2
stosb ;= quad[1], stuff

lodsb ;AL=hunk[2]
mov dl,al ;save hunk[2] a sec
shl ah,cl ;hunk[1] SHL 4
shr al,cl ;hunk[2] SHR 4
add al,ah ;shifted hunk[1]+shifted hunk[2]
stosb ;= quad[2], stuff

mov ah,dl ;AH=orig hunk[2]
lodsb ;AL=hunk[3]
mov dl,al ;save hunk[3] in DL a sec
shl ah,1 ;hunk[2] SHL 2 v1.2
shl ah,1 ;(faster this way) v1.2
mov cl,ch ;CL now 6
shr al,cl ;hunk[3] SHR 6
add al,ah ;shifted hunk[2]+shifted hunk[3]
stosb ;= quad[3], stuff

mov al,dl ;AL=orig hunk[3]
stosb ;= quad[4], stuff

;That 3-byte hunk is done. See if our binary buffer's empty.
;Notice we ALWAYS assume we did all 3 binary bytes.
;If we didn't (e.g., had a non-MOD 3 nr of binary bytes in our file),
;we'll correct that later with an adjustment to the binary counter
;character in the uuencoded line.

add bp,3 ;+3 v1.2
cmp si,read_count ;hit data end yet? v1.2
jnb Chk_Eof ;yep, see if file is done v1.2

;Binary file is not done, so see if the line is ready to be finished up
;and written out to uuencoded file.
cmp bp,45 ;done a line of binary data yet? v1.2
;(45 binary bytes = 60 uuencoded)
jb Uue_Loop ;not yet v1.2
call Write_Uue ;stuff binary count in record,
;Asciify entire record,
;append CR/LF, write to file
;Reset BP binary counter=0,
;DI back to output buffer start +1
jmp Uue_Loop ;Keep working through binary buffer.

Chk_Eof:
cmp last_flag,1 ;Was last read the binary file EOF?
jne Read_Loop ;nope, do another read, maybe end.

;-------------------------------------------
Write_Uue_End:
or bp,bp ;any bytes uuencoded? v1.2
jz No_Partial_Write ;none v1.2

;In converting 3 binary bytes to 4 quad chars, we may have bumped SI
;beyond the actual number of binary bytes read.
;By subtracting the original count of bytes read from SI,
;we'll get the number of 'bogus' binary bytes in that last quad.
;Subtract that from the BP binary counter, and we'll get the TRUE
;number of binary bytes in that uuencoded line.
;It's up to the uudecoding program to catch that difference
;and ignore the extra quads. (The ones I've tested seem to.)

sub si,read_count ;check for overrun v1.2
sub bp,si ;subtract any bogus bytes v1.2
call Write_Uue ;write partial line

No_Partial_Write:
mov dx,offset end_msg ;'end',CR/LF
mov cx,END_MSG_LEN ;nr bytes to move
call Write_File ;do the last write

;Funny .. this program runs just fine without any file closing
;at all! DOS must take care of it all at the termination.!
;Still, just to be neat...

IF NOT STDOUT ;no StdOut
mov bx,out_handle ;output file handle
mov ah,3EH ;close the file
int 21H
ENDIF

File_End_X:
MOV AH,4Ch ;terminate, ERRORLEVEL ?
INT 21h
UuEncode endp

;-------------------------------------------

Write_Uue PROC NEAR
;Enter with DI pointing to char beyond last uuencoded char.
;Stuff CR/LF, compute line length, write to file.
push si ;save input ptr a sec
MOV DX,offset uue_out ;output line start (length byte)
mov cx,di ;current output pointer
sub cx,dx ;- buffer start = nr bytes in line
;+1, but that's ok since we're adding
;the line_len byte
push cx ;save full line length for later
;Do the last masking of the line of quads
mov si,dx ;point to line start for 'from'
mov di,dx ;moving to same place
mov ax,bp ;binary bytes in this line v1.2
mov [si],al ;stuff binary length byte v1.2
; (uuencode later)
mov ah,20H ;get a handy constant

;Gotta process every byte, masking, checking for spaces, etc.
;This includes that length byte.
mov bx,(3FH SHL 8) + 96 ;get another handy constant v1.2
;BH=3FH, BL=96 v1.2
Mask_Loop:
lodsb ;get output line char
and al,bh ;3FH ;six-bit mask v1.2
add al,ah ;plus asciifying offset
cmp al,ah ;end up with a space
jne Not_Space ;nope
mov al,bl ;96 ;use space substitute "`" v1.2
Not_Space:
stosb ;stuff it back in line buffer
loop Mask_Loop ;do them all
pop cx ;restore char count for bytes to write
;DI now points at char just beyond uuencoded char line
mov ax,0A0DH ;Get CR/LF v1.2
mov [di],ax ;stuff them in buffer
inc cx ;add in CR/LF to length v1.2
inc cx ; v1.2
pop si ;restore SI
Write_File:
MOV BX,out_handle ;output file handle
MOV AH,40h ;write to file/device
INT 21h
jb Out_Err ;write error
mov di,dx ;point DI back to uue_out start
inc di ;bump past length byte
xor bp,bp ;reset byte ctr v1.2
RET ;write done

;Output file write error
Out_Err:
MOV DX,OFFSET err_out ;'Output file error'
MOV CX,ERR_OUT_LEN ;msg length
jmp short Fatal_Error ;common code

Write_Uue ENDP

;-------------------------------------------
;Read a chunk of raw binary data
Read_File PROC NEAR
MOV DX,offset data_buf ;into binary input buffer
mov cx,READSIZE ;nr bytes to read
MOV BX,inp_handle ;input file handle
MOV AH,3Fh ;read from file/device
INT 21h
jb Inp_Err ;failed

;AX has nr of bytes read
mov si,dx ;SI needs offset data_buf v1.2
mov bx,dx ;buffer start v1.2
add bx,ax ;+bytes read = data end v1.2
;BX points to the next 'empty' byte (data_buf start + bytes read)

cmp ax,cx ;read a full buffer?
je Read_Full ;yep, no fiddling required

;We've read less than a buffer full. Let's make sure the last bytes
;are nulls if bytes read are not MOD 3.
mov word ptr [bx],0 ;stuff 2 nulls there v1.2
mov last_flag,1 ;turn EOF flag on

Read_Full:
mov read_count,bx ;point to data end v1.2
or ax,ax ;set flags for return v1.2
RET ;read done

;-------------------------------------------
;Input file read error. Error value in AL
Inp_Err:
MOV DX,OFFSET err_inp ;'Input file error'
MOV CX,ERR_INP_LEN ;msg length
;Common code added here
Fatal_Error:
push ax ;save error in AL
call Say_Error ;common code
POP AX ;restore error in AL
JMP File_End_X ;terminate
Read_File ENDP

;-------------------------------------------
Say_Error proc near
MOV BX,2 ;Std ErrOut handle
MOV AH,40h ;write to file/device
INT 21h
RET
Say_Error ENDP

;using pointers here at code end for various buffers.
;the uue_out buffer is normally 60 uuencoded chars, plus CR/LF
;It's initialized with the default uuencode file header.
;No, I don't know the magic in '644'.

EVEN ;v1.2
uue_out db 'begin 644 ' ;first write contains this + name

;The rest of these buffers don't take any code space.

uue_filename equ $ ;where we move filename.uue
;Leave room for LINELEN+2 chars for uue_out buffer.
inp_fil equ uue_out + LINELEN +2 ;80 chars long v1.2

;Leave room for 80 chars for inp_fil filename buffer.
out_fil equ inp_fil + 80 ;15 bytes long v1.2

data_buf equ out_fil ;leave room for uue_out v1.2

Cseg ENDS
END UuEncode


  3 Responses to “Category : Assembly Language Source Code
Archive   : MISC_ASM.ZIP
Filename : UUE12.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/