;+
;
; Utility to convert to/from MIME BASE64 encoding.
;
; 04/22/97	JMBW	Created.
;
;-
	.radix	8
;
lf=	12
cr=	15
;
ibufl=	16384d			;size of input buf
obufl=	16384d			;size of output buf
;
code	segment
	assume	cs:code
	org	100h
;
start:	cld			;DF=0
	call	from
	int	20h
;+
;
; Convert from BASE64 to binary.
;
;-
from:	mov	ax,-1		;init prev chars
	mov	dx,ax
	mov	di,offset obuf	;point at output buf
from1:	call	load		;get more
	jcxz	from10		;EOF
	xor	bh,bh		;clear high half
from2:	mov	bl,[si]		;get a byte
	inc	si		;skip it
	mov	bl,ds:from64[bx] ;translate
	test	bl,bl		;negative?
	js	from5		;yes
from3:	mov	dh,dl		;ripple up
	mov	dl,ah
	mov	ah,al
	mov	al,bl
	cmp	dh,-1		;is that 4 chars?
	jne	from6		;yes, store
from4:	loop	from2		;handle rest of buffer
	jmp	short from1	;go refill buffer
from5:	inc	bl		;-1 or -2?
	jz	from4		;-1, ignore
	jmp	short from3
from6:	; store 4 chars as 3 bytes
	cmp	dh,-2		;can't have padding except as last 1-2 chars
	je	from9
	cmp	dl,-2
	je	from9
	sal	dl,2		;squish high order together
	sal	dx,2
	mov	[di],dh		;save 1st char
	inc	di
	cmp	ah,-2		;padding OK as 3rd char if 4th char is also
	je	from8
	sal	dx,2		;make space
	or	dl,ah
	sal	dx,6		;left-justify
	mov	[di],dh		;save 2nd char
	inc	di
	cmp	al,-2		;padding as last char?
	je	from7
	or	al,dl		;compose 3rd char
	mov	[di],al		;save
	inc	di
from7:	mov	ax,-1		;reinit prev chars
	mov	dx,ax
	cmp	di,offset obuf+obufl-3 ;almost full?
	jb	from4		;loop if not
	call	flush		;flush output buffer
	jmp	short from4	;loop
from8:	cmp	al,-2		;is 4th char also padding?
	je	from7		;yes
from9:	mov	dx,offset fmterr ;format error
	jmp	punt		;die

;;; once we accept a '=' char there should be no more non-padding chars
;;; (set a flag on -2 accepted above)

from10:	; EOF, see if we owe any chars
	cmp	al,-1		;anything stored?
	je	from12		;no
	inc	cx		;pretend we got one char so we'll loop
from11:	cmp	dh,-1		;should be multiple of 4 chars
	jne	from6		;it is, good
	mov	dh,dl		;left-justify
	mov	dl,ah
	mov	ah,al
	mov	al,-2		;pad with "="
	jmp	short from11
from12:	call	flush		;flush final buf
	ret
;+
;
; Load next bufferful of file data.
;
; ax,dx	preserved
; si	returns ptr to data
; cx	returns byte count (0=EOF)
;
;-
load:	push	ax		;save
	push	dx
	mov	dx,offset ibuf	;point at buf
	mov	cx,ibufl	;length
	xor	bx,bx		;STDIN
	mov	ah,3Fh		;func=read
	int	21h
	jc	load1		;failed
	mov	si,dx		;point at it
	mov	cx,ax		;get actual length
	pop	dx		;restore
	pop	ax
	ret
load1:	mov	dx,offset rderr	;read error
	jmp	short punt
;+
;
; Flush output buf.
;
; di	OBUF ptr (reset on return)
; ax,cx,dx preserved
;
;-
flush:	push	ax		;save
	push	cx
	push	dx
	mov	dx,offset obuf	;pt at buf
	sub	di,dx		;find length
	jz	flush1		;0, skip
	mov	cx,di		;copy
	mov	bx,0001h	;STDOUT
	mov	ah,40h		;func=write
	int	21h
	jc	flush2		;error
flush1:	mov	di,dx		;reset ptr
	pop	dx		;restore
	pop	cx
	pop	ax
	ret
flush2:	mov	dx,offset wrerr	;write error
	;jmp	short punt
;+
;
; Fatal error.
;
; dx	ptr to .ASCIZ error message
;
;-
punt:	mov	di,dx		;copy ptr
	xor	al,al		;search for zero
	repz	scasb		;(will always succeed)
	dec	di		;point at it
	sub	di,dx		;find length
	mov	cx,di		;copy
	mov	bx,0002h	;STDERR
	mov	ah,40h		;func=write
	int	21h
	mov	ax,4C01h	;func=punt
	int	21h
;
; Table to translate from BASE64 to binary.  Values:
; 00-77	6-bit binary value
; -1	invalid character
; -2	"=", padding char (fills last 0-2 chars for partial 24-bit sequence)
;
from64	db	-1,-1,-1,-1,-1,-1,-1,-1 ;^@ ^A ^B ^C ^D ^E ^F ^G
	db	-1,-1,-1,-1,-1,-1,-1,-1 ;^H ^I ^J ^K ^L ^M ^N ^O
	db	-1,-1,-1,-1,-1,-1,-1,-1 ;^P ^Q ^R ^S ^T ^U ^V ^W
	db	-1,-1,-1,-1,-1,-1,-1,-1 ;^X ^Y ^Z ^[ ^\ ^] ^^ ^_
	db	-1,-1,-1,-1,-1,-1,-1,-1 ;SP !  "  #  $  %  &  '
	db	-1,-1,-1,76,-1,-1,-1,77 ;(  )  *  +  ,  -  .  /
	db	64,65,66,67,70,71,72,73 ;0  1  2  3  4  5  6  7
	db	74,75,-1,-1,-1,-2,-1,-1 ;8  9  :  ;  <  =  >  ?
	db	-1,00,01,02,03,04,05,06 ;@  A  B  C  D  E  F  G
	db	07,10,11,12,13,14,15,16 ;H  I  J  K  L  M  N  O
	db	17,20,21,22,23,24,25,26 ;P  Q  R  S  T  U  V  W
	db	27,30,31,-1,-1,-1,-1,-1 ;X  Y  Z  [  \  ]  ^  _
	db	-1,32,33,34,35,36,37,40 ;`  a  b  c  d  e  f  g
	db	41,42,43,44,45,46,47,50 ;h  i  j  k  l  m  n  o
	db	51,52,53,54,55,56,57,60 ;p  q  r  s  t  u  v  w
	db	61,62,63,-1,-1,-1,-1,-1 ;x  y  z  {  |  }  ~  RO
	db	128d dup(-1)	;2nd half is all ignored
;
to64	db	'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef'
	db	'ghijklmnopqrstuvwxyz0123456789+/'
;
rderr	db	'?Read error',cr,lf,0
wrerr	db	'?Write error',cr,lf,0
fmterr	db	'?File format error',cr,lf,0
banner	db	'Base64 to binary translator by John Wilson <wilson@dbit.com>'
;
ibuf	db	ibufl dup(?)
obuf	db	obufl dup(?)
;
code	ends
	end	start
