
_printf		movem.l	d0-d3/a0-a4/a6,-(sp)	save regs
			movea.l	44(sp),a3		a3 points to string
			lea.l	48(sp),a4		a4 points to args
			lea.l	-256(sp),sp		make room for output string
			movea.l	sp,a2			a2 points to output string

; The main loop, copy characters to the output buffer until a % or \ is
; encountered in which case interpret the characters following in the same
; manner as C's printf function and put the results into the output stream
; instead.  A null byte terminates the output.
_printloop	move.b	(a3)+,d0		get a copy of the byte
_possibleend	move.b	d0,(a2)		move to output string
			beq	_printdone			terminating byte
			cmpi.b	#'\',d0			special character byte ?
			beq.s	_specialchar	yes, go see what it is
			cmpi.b	#'%',d0			number or string byte ?
			beq.s	_introchar		yes, go process that
_bumpptr	addq.l	#1,a2			nothing special, bump ptr...
			bra.s	_printloop		...and go back for the next

; Found a backslash, replace character after with tab or newline (\t or \n)
_specialchar	move.b	(a3)+,d0	get the next character
			beq.s	_possibleend	that could have been nasty!!
			cmpi.b	#'\',d0			did caller want a backslash
			beq.s	_bumpptr		yes, so leave the other
			cmpi.b	#'t',d0			did user want a tab ?
			bne.s	_checkcr		nope, maybe a newline
			move.b	#9,(a2)+		yes, store tab and bump
			bra.s	_printloop		and go for the next char
_checkcr	cmpi.b	#'n',d0			did user want a newline ?
			bne.s	_printloop		nope, ignore all others
			move.b	#13,(a2)+		yes, store cr and bump
			move.b	#10,(a2)+
			bra.s	_printloop		and go for the next char

; Found a percent sign.  Must be introducing a number, string or character
_introchar	move.b	(a3)+,d0		get the next character
			beq.s	_possibleend	another possible oops!
			cmpi.b	#'%',d0			did caller want a percent?
			beq.s	_bumpptr		yes, so leave the last one
			cmpi.b	#'c',d0			print a character ?
			bne.s	_checkstring	nope!
			move.l	(a4)+,d0		get char from arg stack
			move.b	d0,(a2)+		store it and bump ptr
			bra.s	_printloop		and go for the next char
_checkstring	cmpi.b	#'s',d0		print a string ?
			bne.s	_checknumber	nope!
			movea.l	(a4)+,a0		get ptr from arg stack
10$			move.b	(a0)+,(a2)+		and copy the string
			bne.s	10$
			subq.l	#1,a2			went 1 byte too far
			bra.s	_printloop		go for the next char

; wasn't a string or character code so it has to be a number code here
_checknumber	moveq.l	#0,d2		flag, short (word) number
			cmpi.b	#'l',d0			long version of a number ?
			bne.s	_whatnumber		no, it was a short
			moveq.l	#1,d2			flag, longword number
			move.b	(a3)+,d0		and get the next letter
			beq.s	_possibleend	yet another nasty spot!
_whatnumber	cmpi.b	#'x',d0			hex character ?
			bne.s	_decnumber		nope, must be a decimal
			move.l	(a4)+,d0		get hex value to print
			movea.l	a2,a0			buffer to store to
			tst.w	d2				see what size
			bne.s	_dolonghex		it's a longword
			bsr		bin2hex16		it's a word
			bra.s	_findend		find where number ends
_dolonghex	bsr		bin2hex32
			bra.s	_findend		find where number ends

_decnumber	cmpi.b	#'d',d0			really want a decimal ?
			bne		_printloop		nope, illegal character!
			move.l	(a4)+,d0		get decimal value to print
			movea.l	a2,a0			and buffer to store to
			tst.w	d2				see what size
			bne.s	_dolongdec		it's a longword
			bsr		bin2asc16		it's a word
			bra.s	_findend		see what size
_dolongdec	bsr		bin2asc32

; A number has been stored into the buffer, find the end to update A2 ptr
_findend	tst.b	(a2)+			search for null byte
			bne.s	_findend		not there yet
			subq.l	#1,a2			went one byte too far
			bra		_printloop		go for the next char

; We got to the end of the format string and now have a formatted string on
; the stack that is terminated by a null byte.  Open the dos library to find
; the current output stream and write the string out.
_printdone	movea.l	4.w,a6			need exec library node
			lea.l	DosName(pc),a1
			moveq.l	#0,d0			don't care what version
			jsr		_LVOOpenLibrary(a6)
			tst.l	d0
			beq.s	_cleanup		didn't get the library
			movea.l	d0,a6

			jsr		_LVOOutput(a6)	find output handle
			move.l	d0,d1			where we print to
			move.l	sp,d2			where we print from
			suba.l	sp,a2			find length of string
			move.l	a2,d3
			jsr		_LVOWrite(a6)	and write it out

			movea.l	a6,a1
			movea.l	4.w,a6			finished with dos lib now
			jsr		_LVOCloseLibrary(a6)

_cleanup	lea.l	256(sp),sp		fix up the stack pointer
			movem.l	(sp)+,d0-d3/a0-a4/a6	restore regs
			rts						all done


bin2asc16	EXT.L	D0			make word into a longword
bin2asc32	TST.L	D0			is it negative ?
			BPL.S	bin2asc		nope, call main routine
			NEG.L	D0			make positive
			MOVE.B	#'-',(A0)+	output a minus sign

bin2asc		MOVEM.L	D2-D3/A2-A3,-(SP)
			MOVEA.L	A0,A3			save buffer pointer
			LEA.L	PowersOfTen(PC),A2	powers of ten table
			MOVEQ.L	#0,D1			leading zero flag
			MOVE.L	(A2)+,D2		fetch first power of ten

10$			MOVEQ.L	#-1,D3			current digit value
20$			ADDQ.B	#1,D3			update this digit
			SUB.L	D2,D0			subtract current pwr of 10
			BPL.S	20$				go for another
			ADD.L	D2,D0			fix, we went too far
			TST.B	D3				is this a zero ?
			BNE.S	30$				no, skip leading zero test
			TST.W	D1				have we put in any digits yet?
			BEQ.S	40$				no, so scrap this one
30$			MOVEQ.L	#1,D1			stop suppressing zeros now
			ADDI.B	#'0',D3			make an ascii digit
			MOVE.B	D3,(A0)+		store it
40$			MOVE.L	(A2)+,D2		get next power of 10
			BNE.S	10$				do the next bit

			TST.W	D1				did we put any digits in
			BNE.S	50$				yes
			MOVE.B	#'0',(A0)+		no, put zero as the result
50$			CLR.B	(A0)			terminate the string
			MOVE.L	A3,D0			retrieve pointer to buffer
			MOVEM.L	(SP)+,D2-D3/A2-A3
			RTS

PowersOfTen	DC.L	1000000000,100000000,10000000,1000000
			DC.L	100000,10000,1000,100,10,1,0

bin2hex32	MOVEQ.L	#7,D1			generate a longword
			BRA.S	dohex
bin2hex16	MOVEQ.L	#3,D1			generate a word

dohex		MOVE.L	D2,-(SP)
			MOVE.L	D0,D2			save the value
			MOVEA.L	A0,A1			save buffer address for later
			CLR.B	1(A0,D1.W)		terminate the string
10$			MOVE.B	D2,D0			get next nybble
			ANDI.B	#$0F,D0
			ADDI.B	#'0',D0			make into ascii
			CMPI.B	#'9',D0
			BLE.S	20$				no adjustment for letters
			ADDI.B	#('A'-'9')-1,D0	add offset for letters
20$			MOVE.B	D0,0(A0,D1.W)	store this digit
			LSR.L	#4,D2			move next nybble into place
			DBRA	D1,10$

			MOVE.L	A1,D0			return address of buffer
			MOVE.L	(SP)+,D2
			RTS

DosName		dc.b	'dos.library',0
			even


