; Date: 27 Aug 86 14:14 EDT
; From: junod@dtrc.ARPA (John Junod)
; Subject: MSBPCT.ASM
; To: info-kermit@cu20b
;
; I have just completed a reconstruction of Kermit on a DEC Rainbow
; using the MSVRB1.BOO file. The target machine had a Vanilla
; MS-DOS disk w/o BASIC or a Terminal Emulator w/file transfer.
; It did have MASM and LINK. I was able to capture the BOO file
; by using "copy aux mskermit.boo" (and you could capture the
; msbpct.asm file by using "copy aux msbpct.asm") and sending
; a control-z at the end from another MS-DOS machine connected
; to the Rainbow's communication port. Since I was unable to
; find an assembly version of MSBPCT I wrote the following
; to reconstruct MSKERMIT.EXE. (Note: you must have the source
; BOO file on the default drive as MSKERMIT.BOO and the
; output will always be MSKERMIT.EXE on the default drive.
;
; If necessary do a "copy" or "rename" of your capture file
;  before running the assembled MSBPCT.ASM)
;
;		   Regards,
;
;		   John Junod
;		   Code 1821
;		   David Taylor Naval Ship R and D Center (DTNSRDC)
;		   Bethesda, Md 20084-5000
;
;		   DDN: junod@dtrc
; Edit history:
; [v1.0]
; MSBPCT.ASM
; August 27,1986
; L. John Junod    DDN:junod@dtrc
; Code 1821
; David Taylor Naval Ship R & D Center
; Bethesda, MD 20084-5000
;
; Note Source BOO file must be on default drive as MSKERMIT.BOO
; Result will be MSKERMIT.EXE on the default drive
;

; Macro Definitions

set_dta		macro buffer		;set the data transfer
		mov dx,offset buffer	;address to the proper
		mov ah,1AH		;buffer
		int 21H
		endm

read_seq 	macro FCB		; read buffer full of data
		mov dx,offset FCB	; from via FCB
		mov ah,14H
		int 21H
		endm

display_char 	macro character		;display character on console
		mov dl,character
		mov ah,02H
		int 21H
		endm

put_char	macro character		;write a character to output
					; file
		mov FCB2[14],1		; one char
		mov buffer2[0],character
		set_dta buffer2		;change to output buffer
		mov dx,offset FCB2
		mov ah,15H
		int 21H
		set_dta buffer1		;change back to input buffer
		endm

open		macro FCB		;open a file via the FCB
		mov dx,offset FCB
		mov ah,0FH
		int 21H
		endm

create 		macro FCB		;overwrite or create a file
		mov dx,offset FCB	;via the FCB
		mov ah,16H
		int 21H
		endm

close		macro FCB		;close a file via the FCB
		mov dx,offset FCB
		mov ah,10H
		int 21H
		endm

STKSEG SEGMENT STACK			;setup our stack
	db 100H dup (?)
STKSEG ENDS

 	public unpack,read_char	

code segment
	assume cs:code,ss:stkseg,ds:datas,es:nothing

; Main routine to process MSKERMIT.BOO and produce MSKERMIT.EXE
; on the default drive

START:
		mov ax,datas	; save the data segment
		mov ds,ax
 		set_dta buffer1 ; set dta to input buffer
		open FCB1	; file to read
 		set_dta buffer2 ; set the dta to the output buffer
 		create FCB2	; file to write/overwrite
		mov cx,1	; fake out the dec cx in read_char
		xor si,si	; clear the current input index
skipname:	call read_char	; skip default BOO output name
		cmp al,10	; Linefeed? 
		jne skipname	; No: read until we have it
process_char:	call read_char	; get an input character if avail
		cmp al,10	; skip newlines
		je process_char	
		cmp al,13	; skip carriage returns
		je process_char
		call unpack	; go unpack a quad or null rep
		jmp process_char
; read_char
; 	this routine returns a character in al if available
;	if the end-of-file is encountered, the input file
;	is closed and the output file is closed after
;	appending a final control-Z (26 decimal)

read_char proc near
		dec cx			; read another buffer full?
		cmp cx,00H
		je read_line		; yes
		mov al,buffer1[si] 	; no: put char in al
		cmp al,26		; end of file?
		je all_done		; yes: do wrap up
		inc si			; bump input index
		ret			

read_line: 	set_dta buffer1	; make sure we are using input buffer
		read_seq FCB1	; read buffer full from input file
		cmp al,01H	; end of file? no data?
		je all_done	; yes: wrap up
		cmp al,01H	; end-of-file w/partial record?
		jg check_more	; yes: go process partial record
		mov cx,128	; no: normal read
		xor si,si	; zero the input buffer index
		mov al,buffer1[si] ; get 1st char in input buffer
		inc si		; bump input buffer index
		cmp al,26	; end-of-file?
		je all_done	; yes: wrap up
		ret	

check_more:	cmp al,03H	; input buffer full? read cancelled?
		jne all_done    ; yes:exit
		mov cx,128	; no: process parital record
		xor si,si	; zero the input buffer index
		mov al,buffer1[si] ; get 1st char in input buffer
		inc si		; bump input buffer index
		cmp al,26	; end-of-file?
		je all_done	; yes: wrap up
		ret

all_done:	close FCB1	; close input file
 		put_char 26	; append eof (control-z) to output file
 		close FCB2	; close output file
		mov ah,4CH	; exit back to dos
		xor al,al	; return a zero 
		int 21H

read_char 	endp

; unpack
; 	Process a repeat count or quad and return to caller
; 	if char is ~ (7EH) then read next char as repeat count
;		and subtract '0' (30H) to get actual repeat count
;		 and generate nulls
; 	else process a quad (ie 4 characters reduced to 3)
;
;	AL is assumed to contain a valid character on entry
;
unpack proc near
		cmp al,7EH	; null compress char?
		jne quad	; no: do a quad 
P1:		call read_char  ; yes: get repeat count
		cmp al,10	; skip new lines
		je p1
		cmp al,13	; skip carriage returns
		je p1
		sub al,30H	; get actual repeat count 
		push cx	  	; save cx for read_char use
		xor cx,cx	; put count in cx for rep_count loop
		mov cl,al
rep_count: 	put_char 00H 	;output nulls
		loop rep_count
		pop cx		; restore cx for read-char use
		ret		; done with null repeat

quad:		xor bx,bx	  ; clear inbyte buffer index
		sub al,30H	  ; correct character
		mov inbyte[bx],al ; save 1st byte
		inc bx		  ; bump inbyte buffer index
p2:		call read_char	  ; get a character
		cmp al,10	  ; skip newlines
		je p2
		cmp al,13	  ; skip carriage returns
		je p2
		sub al,30H	  ; correct character
		mov inbyte[bx],al ; save 2nd byte
		inc bx		  ; bump inbyte buffer index
p3:		call read_char	  ; get a character
		cmp al,10	  ; skip newlines
		je p3
		cmp al,13	  ; skip carriage returns
		je p3
		sub al,30H	  ; correct char
		mov inbyte[bx],al ; save 3rd byte
		inc bx		  ; bump inbyte buffer index
p4:		call read_char	  ; get a character
		cmp al,10	  ; skip newlines
		je p4
		cmp al,13	  ; skip carriage returns
		je p4
		sub al,30H	  ; correct char
		mov inbyte[bx],al ; save 4th byte - all 4 bytes loaded
	 	xor ax,ax	  ; clear ax
		mov al,inbyte[1]  ; inbyte[0] * 4 + inbyte[1] / 16
		shr ax,1
		shr ax,1
		shr ax,1
		shr ax,1
		xor bx,bx
		mov bl,inbyte[0]
		shl bx,1
		shl bx,1
		add al,bl
		put_char al	   ; output 1st decoded byte
		xor ax,ax	   ; inbyte[1] * 16 + inbyte[2]/4
		mov al,inbyte[2]
		shr ax,1
		shr ax,1
		xor bx,bx
		mov bl,inbyte[1]
		shl bx,1
		shl bx,1
		shl bx,1
		shl bx,1
		add al,bl
		put_char al	   ; output 2nd decoded byte
		xor ax,ax	   ; inbyte[2] * 64 + inbyte[3]
		mov al,inbyte[2]
		shl ax,1
		shl ax,1
		shl ax,1
		shl ax,1
		shl ax,1
		shl ax,1
		add al,inbyte[3]
		put_char al	   ; output 3rd decoded byte
		ret
unpack endp

code ends

; Data Declarations

datas segment

FCB1 db 0,"MSKERMITBOO"		; Input file is MSKERMIT.BOO
	db 25 dup (?)		; on default drive
FCB2 db 0,"MSKERMITEXE"		; Output file is MSKERMIT.EXE
	db 25 dup (?)		; on default drive
buffer1 db 128 dup (?)		; Input buffer
buffer2 db 128 dup (?)		; Output buffer
inbyte 	db 4 dup (?)		; Unpack temporary buffer

datas ends

	end start
