	TITLE	MSBMKB MS-DOS System EXE to BOO Utility Program
 
; (c) 1988 W.C. Parke, WGCP0086@GWUVM
;
; This program converts an EXE to a BOO file 
;
;  BOO files are ASCII files constructed from binary files for the purpose
;  of transferring the files over networks or tape which do not accept binary.
;  The translation is by the following algorithm (by Bill Catchings): 
;    1. The name of the file is placed on the first line, in plain text,
;       followed by CR,LF.
;    2. If a series of nulls occurs, a '~' (7EH) is used followed by
;       an byte number for the null count from 2 to 78 plus '0' bias.
;    3. Three byte sequences are coded as 4 bytes, taking lower 6 bits from
;       each byte and adding a '0' bias.
;    4. Carriage return/linefeeds are inserted to make lines breaks
;       at column 76 unless a split of 4 byte code would occur.  If so,
;       line ends at column 74.
;
; MSBMKB compiled with MASM 5.10.  Use LINK and EXE2BIN to make COM file.
;
; May be used with any file on any MS-DOS path.
; BOO file is created in current directory.
;
; MSBMKB uses the file name on the first line of the BOO file
; for its output file.
 
LF	EQU	0AH
CR	EQU	0DH
BUFSIZ	EQU	6000H
 
CODE	SEGMENT
	ASSUME	CS:CODE,DS:CODE,ES:CODE,SS:CODE
 
	ORG	100H
 
START:	JMP	BOO
FHAND1	DW	0
	DW	0
FNAM1	DB	80 DUP(0)
FHAND2	DW	0
	DW	0
FNAM2	DB	16 DUP(0)
	DW	0
FNAMT	DB	16 DUP(0)
INVOC	DB	CR,LF,'MSMKB Version 1.1'
	DB	CR,LF,'EXE to BOO file converter.'
	DB	CR,LF,'(c) 1988 W.C. Parke',CR,LF,CR,LF,'$'
DHPERR	DB	'Syntax:',CR,LF,LF
	DB	'  MSBMKB filename',CR,LF,CR,LF,'$'
DCONV	DB	'Converting $'
DTO	DB	' to $'
DDOT	DB	' ... $'
DEXIST	DB	' already exists. Overwrite it? $'
DERROR	DB	CR,LF,'Error in file $'
DOPERR	DB	'open$'
DCRERR	DB	'creation$'
DCLERR	DB	'close$'
DRDERR	DB	'read$'
DWRERR	DB	'write$'
DERRF	DB	'.',CR,LF,'$'
FBOO	DB	'.BOO',0,0
DONE	DB	CR,LF,'Done. ',CR,LF,'$'
FEOF	DB	0
FEND	DW	0
FEND2	DW	0
 
BOO:	MOV	AH,9
	MOV	DX,OFFSET INVOC
	INT	21H			; give our invocation
	MOV	SI,80H
	LODSB
	XOR	AH,AH
	AND	AX,AX
	JNZ	BOF			; got command line tail
	JMP	ABORTHP			; give help screen
BOF:	MOV	DI,OFFSET FNAM1
	INC	AX
	MOV	CX,AX
BO0:	LODSB				; get byte of command line tail
	CMP	AL,CR			; check for cr termination
	JZ	BO1
	CMP	AL,20H
	JZ	BOL			; check for spaces
	CMP	AL,'a'
	JC	BOLL
	AND	AL,5FH
BOLL:	STOSB				; save in file name buffer
BOL:	LOOP	BO0			; continue
BO1:	XOR	AL,AL
	STOSB				; make asciiz string
	DEC	DI			; point to 0
	MOV	AX,OFFSET FNAM1
	MOV	CX,DI
	SUB	CX,AX
	MOV	FEND,CX			; size of file and path name to 0
	INC	CX
	INC	CX
	STD
	MOV	AL,'\'
	REPNZ	SCASB
	CLD
	INC	DI
	INC	DI		; pointer to fnam1 without path
	MOV	SI,DI
	MOV	DI,OFFSET FNAM2
	XOR	CX,CX
BON:	LODSB
	INC	CX
	AND	AL,AL
	STOSB			; move copy to fnam2
	JZ	BOM
	JMP	SHORT BON
BOM:	DEC	CX
	MOV	FEND2,CX	; size of fnam2
	MOV	SI,OFFSET FNAM2
	MOV	DI,OFFSET FNAMT
	MOV	CX,8
	REP	MOVSW		; copy file name for header of BOO file
	MOV	DX,OFFSET FNAM1
	MOV	AH,3DH		; open file 1
	INT	21H
	JNC	BO2
	JMP	ABORTOP		; give error on open message
BO2:	MOV	FHAND1,AX	; save handle
	MOV	DI,OFFSET FNAM2
	MOV	CX,FEND2
	ADD	DI,CX
	PUSH	DI		; save ptr to 0
	STD
	INC	CX
	MOV	AL,'.'
	REPNZ	SCASB		; look for '.' in string from rear
	CLD
	JCXZ	BO3		; not found
	INC	DI		; point to '.'
	POP	AX		; drop old 0 pointer
	PUSH	DI		; use new pointer
BO3:	POP	SI	; pointer to extension in FNAM1
	MOV	DI,SI
	MOV	SI,OFFSET FBOO
	MOV	CX,3
	REP	MOVSW		; add BOO extension to fnam2
	MOV	DX,OFFSET FNAM2
	XOR	CX,CX
	MOV	AH,4EH
	INT	21H		; search file
	JC	BOZ
	MOV	SI,OFFSET FNAM2
	CALL	SHOW
	MOV	AH,9
	MOV	DX,OFFSET DEXIST	; already exists
	INT	21H
	MOV	AX,0C01H
	INT	21H			; look for user response
	PUSH	AX
	MOV	AH,2
	MOV	DL,CR
	INT	21H
	MOV	DL,LF
	INT	21H
	POP	AX
	AND	AL,5FH
	CMP	AL,'Y'
	JZ	BOZ			; got overwrite permission
	JMP	EXIT
BOZ:	MOV	DX,OFFSET FNAM2
	MOV	AH,3CH
	XOR	CX,CX
	INT	21H		; create output file
	JC	ABORTCR
	MOV	FHAND2,AX	; save handle
	MOV	AH,9
	MOV	DX,OFFSET DCONV
	INT	21H		; say converting
	MOV	SI,OFFSET FNAM1
	CALL	SHOW
	MOV	AH,9
	MOV	DX,OFFSET DTO
	INT	21H
	MOV	SI,OFFSET FNAM2
	CALL	SHOW
	MOV	AH,9
	MOV	DX,OFFSET DDOT
	INT	21H
	MOV	SI,OFFSET FNAMT
	XOR	AX,AX
	MOV	BP,AX			; line column count
	MOV	CNT1,AX
	MOV	CNT2,AX			; zero buffer counts
	MOV	DI,OFFSET FBUF2
	MOV	CX,16			; do no more than 16
BO4:	LODSB
	AND	AL,AL			; end of asciiz string
	JZ	BO5
	SUB	AL,'0'			; anticipate bias
	CALL	OUTB			; send to file2 buffer
	LOOP	BO4
BO5:	CALL	OUTCR			; send lf,cr
	MOV	SI,OFFSET FBUF1
	JMP	NEXT			; start exe file conversion
ABORTOP:
	MOV	DX,OFFSET DOPERR	; file open error
	MOV	AL,2
	JMP	ABORT
ABORTCR:
	MOV	DX,OFFSET DCRERR	; file creation error
	MOV	AL,3
	JMP	ABORT
ABORTCL:
	MOV	DX,OFFSET DCLERR	; file close error
	MOV	AL,7
	JMP	ABORT
PUBLIC NULLS
NUL1:	MOV	BL,AL			; put byte-2 after null
	XOR	BH,BH			; put null for byte-1
	JMP	SHORT NEX3	; process next byte for byte-3
NULLS:	CALL	GETB			; process multiple nulls
	AND	AL,AL
	JNZ	NUL1			; only one null
	CMP	BP,75		; columns count - 2
	JC	NULG
	CALL	OUTCR
NULG:	MOV	AL,'~'-'0'		; ascii null flag
	CALL	OUTB
	MOV	CX,77			; lowest count is 2
	MOV	BX,78			; largest count allowed
NULSN:	CALL	GETB
	AND	AL,AL
	LOOPZ	NULSN
NULF:	PUSH	AX			; save non-null for byte-1
	SUB	BX,CX			; find null count
	MOV	AX,BX
	CALL	OUTB
	POP	AX
	JMP	SHORT NEX1
PUBLIC NEXT
NEXT:	CALL	GETB		; get byte-1 xxxxxxxx
NEX1:	AND	AL,AL
	JZ	NULLS		; got a null
	MOV	AH,AL
	CALL	GETB		; get byte-2 yyyyyyyy
	MOV	BX,AX
NEX3:	CALL	GETB		; get byte-3 zzzzzzzz
	MOV	CH,AL
	MOV	AX,BX
	SHR	AX,1
	SHR	AX,1
	XCHG	AH,AL
	CMP	BP,73	; columns count - 4
	JC	NEXG
	CALL	OUTCR
NEXG:	CALL	OUTB	; send 00xxxxxx
	XCHG	AL,AH
	SHR	AL,1
	SHR	AL,1
	CALL	OUTB	; send 00xxyyyy
	MOV	AH,BL	
	AND	AH,0FH
	MOV	AL,CH
	SHL	AX,1
	SHL	AX,1
	XCHG	AH,AL
	CALL	OUTB	; send 00yyyyzz
	MOV	AL,AH
	SHR	AL,1
	SHR	AL,1
	CALL	OUTB	; send 00zzzzzz, fourth of quarted
	JMP	NEXT
 
PUBLIC GETB
CNT1	DW	0		; byte count in buffer 1
EXTRA	DB	3		; extra byte count to finish ascii quartet
GETB:	CMP	WORD PTR CNT1,0
	JZ	GETBF		; get more from file
GETBG:	LODSB
	DEC	WORD PTR CNT1	; on less in buffer
	RET
GETBF:	CALL	GETMF		; file get
	JNC	GETBG		; got more file bytes
	TEST	BYTE PTR FEOF,0FFH
	JZ	GETBEC		; error if not end of file
	DEC	EXTRA
	CMP	EXTRA,0		; any extra bytes to add?
	JZ	FIN		; no
	MOV	AL,90H		; add extra 'nop' to file
	RET
GETBEC:	POP	AX		; drop return address
	JMP	ABORTWR
PUBLIC FIN
FIN:	POP	AX		; drop return address
	CALL	OUTLST			; save last of converted file
	JNC	EXIT
	JMP	ABORTCL		; close error
EXIT:	MOV	AH,9
	MOV	DX,OFFSET DONE
	INT	21H
	MOV	AX,4C00H
	INT	21H			; good exit
 
GETMFF:	STC			; got eof
	RET
GETMF:	TEST	BYTE PTR FEOF,0FFH
	JNZ	GETMFF
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	MOV	BX,FHAND1
	MOV	DX,OFFSET FBUF1
	MOV	AH,3FH
	MOV	CX,BUFSIZ	; file buffer size
	INT	21H		; get bytes
	MOV	SI,OFFSET FBUF1
	POP	DX
	POP	CX
	POP	BX
	JC	GETBC		; read error
	MOV	CNT1,AX		; new byte count
	AND	AX,AX
	JZ	GETBE		; no more bytes
GETBC:	POP	AX
	RET
GETBE:	INC	BYTE PTR FEOF	; set eof
	POP	AX
	STC
	RET
 
OUTCR:	XOR	BP,BP		; new column
	PUSH	AX
	MOV	AL,0DDH	; cr-'0'
	CALL	OUTB		; send cr
	MOV	AL,0DAH	; lf-'0'
	CALL	OUTB		; send lf
	POP	AX
	XOR	BP,BP		; reset column position
	RET
 
PUBLIC OUTB
CNT2	DW	0		; buffer 2 character count
OUTBB:	CALL	OUTBF		; write buffer contents
	JC	OUTBCA		; write error
	JMP	SHORT OUTBS	; start filling new buffer
OUTB:	CMP	CNT2,BUFSIZ	; any space in buffer?
	JNC	OUTBB		; no, write buffer
OUTBS:	ADD	AL,'0'		; add bias to byte 
	STOSB			; save character
	INC	BP		; increment column position
	INC	WORD PTR CNT2	; and characters in buffer
	CLC
	RET
OUTBCA:	POP	AX
	JMP	ABORTWR		; write error
 
OUTBF:	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	MOV	BX,WORD PTR FHAND2
	MOV	AH,40H
	MOV	DX,OFFSET FBUF2
	MOV	CX,BUFSIZ
	INT	21H		; write file 2 buffer
	POP	DX
	POP	CX
	POP	BX
	JC	OUTBC		; write error
	CMP	AX,BUFSIZ
	JNZ	OUTBC		; carry set if incomplete write
	MOV	WORD PTR CNT2,0	; reset character count
	MOV	DI,OFFSET FBUF2	; and buffer pointer
OUTBC:	POP	AX
	RET
OUTLST:	CALL	OUTCR		; send final cr,lf
	MOV	BX,WORD PTR FHAND2
	MOV	AH,40H
	MOV	DX,OFFSET FBUF2
	MOV	CX,CNT2
	INT	21H		; write
	MOV	AH,3EH
	INT	21H		; and close
	RET
ABORTHP:
	MOV	DX,OFFSET DHPERR
	MOV	AH,9
	INT	21H		; show help screen
	MOV	AX,4C01H
	INT	21H
ABORTRD:
	MOV	DX,OFFSET DRDERR	; read error
	MOV	AL,4
	JMP	SHORT ABORT
ABORTWR:
	MOV	DX,OFFSET DWRERR	; write error
	MOV	AL,6
ABORT:	CALL	ABERR			; error in file ...
	PUSH	AX
	MOV	AH,9
	INT	21H			; ...(error type)
	MOV	DX,OFFSET DERRF		; period
	INT	21H
	POP	AX			; errorlevel in AL
	MOV	AH,4CH
	INT	21H
ABERR:	PUSH	AX
	PUSH	DX
	MOV	DX,OFFSET DERROR
	MOV	AH,9
	INT	21H			; show error in file
	POP	DX
	POP	AX
	RET
	EVEN
SHOW:	MOV	AH,2			; show asciiz string
SHOM:	LODSB
	AND	AL,AL
	JZ	SHOE
	MOV	DL,AL
	INT	21H
	JMP	SHORT SHOM
SHOE:	RET	
 
FBUF1	EQU	$
FBUF2	EQU	$+BUFSIZ
 
CODE	ENDS
 
	END	START
