/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley Software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#if	!defined(lint) && defined(DOSCCS)
	.ascii	"@(#)doprnt.c	5.2 (Berkeley) 6/6/85"
#endif

	# C library -- conversions

.globl	__doprnt
.globl	__strout

#define flags r10
#define literb 0
#define liter 1
#define ndfndb 0
#define ndfnd 1
#define ljustb 1
#define ljust 2
#define zfillb 2
#define zfill 4
#define precb 3
#define prec 8
#define psignb 4
#define psign 16
#define gflagb 5
#define gflag 32
#define width r9
#define ndigit r8
#define fdesc -4(fp)
#define exp -8(fp)
#define sign -9(fp)
	.set one,010			# 1.0 in floating immediate
	.set ch.zer,'0			# cpp doesn't like single appostrophes

	.align	1
__doprnt:
	.word	0xfc0			# uses r11-r6
	subl2 $128,sp
	movl 4(ap),r11			# addr of format string
	movl 12(ap),fdesc		# output FILE ptr
	movl 8(ap),ap			# addr of first arg
loop:
	movl r11,r0			# current point in format
	bicl2 $liter,flags		# no literal characters yet
L1:	movb (r11)+,width		# next character of format
	beql L2				# end of format string
	cmpb width,$'%
	beql L2				# warning character
	bisl2 $liter,flags		# literal character
	jbr L1
L2:	blbc flags,L3			# bbc $literb,flags,L3 # no literals in format
	pushl fdesc			# file pointer
	pushl $0			# no left/right adjust
	pushl r0			# addr
	subl3 r0,r11,r1			# length
	subl3 $1,r1,-(sp)		# % or null not part of literal
	calls $4,__strout		# dump the literal
L3:
	blbs width,L4			# % is odd; end of format?
	ret				# yes

	# htab overlaps last 16 characters of ftab
ftab:	.byte	 0, 0, 0,'c,'d,'e,'f,'g, 0, 0, 0,'+,'l,'-,'.,'o
htab:	.byte	'0,'1,'2,'3,'4,'5,'6,'7,'8,'9,'a,'b,'c,'d,'e,'f

L4:	movl sp,r5			# reset output buffer pointer
	clrq r9				# width; flags ljustb,ndfndb,zfillb
L4a:	movzbl (r11)+,r0		# supposed format
	extzv $0,$5,r0,r1		 # bottom 5 bits
L4b:	cmpb r0,ftab[r1]		# good enough?
	jneq L6				# no
L4c:	casel r1,$3,$22			# yes
L5:	.word charac-L5			# c
	.word decimal-L5		# d
	.word scien-L5			# e
	.word float-L5			# f
	.word general-L5		# g
	.word L6-L5			# h
	.word L6-L5			# i
	.word L6-L5			# j
	.word plus-L5			# +
	.word longorunsg-L5		# l
	.word minus-L5			# -
	.word dot-L5			# .
	.word octal-L5			# o
	.word gnum0-L5			# 0
	.word gnum-L5			# 1
	.word gnum-L5			# 2
	.word gnum-L5			# 3
	.word gnum-L5			# 4
	.word gnum-L5			# 5
	.word gnum-L5			# 6
	.word gnum-L5			# 7
	.word gnum-L5			# 8
	.word gnum-L5			# 9

L6:	jbcs $5,r0,L4b			# capitals same as small
	cmpb r0,$'s
	jeql string
	cmpb r0,$'x
	jeql hex
	cmpb r0,$'u
	jeql unsigned
	cmpb r0,$'r
	jeql remote
	movzbl -1(r11),r0		# orginal "format" character
	cmpb r0,$'*
	jeql indir
L9:	movb r0,(r5)+			# print the unfound character
	jbr prbuf

nulstr:
	.byte '(,'n,'u,'l,'l,'),0

string:
	movl ndigit,r0
	jbs $precb,flags,L20		# max length was specified
	mnegl $1,r0			# default max length
L20:	movl (ap)+,r2			# addr first byte
	bneq L21
	movab nulstr,r2
L21:	locc $0,r0,(r2)			# find the zero at the end
	movl r1,r5			# addr last byte +1
	movl r2,r1			# addr first byte
	jbr prstr


longorunsg:
	movb (r11)+,r0
	cmpb r0,$'o
	jeql loct
	cmpb r0,$'x
	jeql lhex
	cmpb r0,$'d
	jeql long
	cmpb r0,$'u
	jeql lunsigned
	decl r11
	jbr unsigned

loct:
octal:
	movl $30,r2			# init position
	movl $3,r3			# field width
	movl $10,r4			# result length -1
	jbr L10

lhex:
hex:
	movl $28,r2			# init position
	movl $4,r3			# field width
	movl $7,r4			# result length -1
L10:	mnegl r3,r6			# increment
	clrl r1
	movl (ap)+,r0			# fetch arg
L11:	extzv r2,r3,r0,r1		# pull out a digit
	movb htab[r1],(r5)+		# convert to character
L12:	acbl $0,r6,r2,L11		# continue until done
	clrb (r5)			# flag end
	skpc $'0,r4,(sp)		# skip over leading zeroes
	jbr prstr

patdec:					# editpc pattern for decimal printing
	.byte 0xA9			# eo$float 9
	.byte 0x01			# eo$end_float
	.byte 0x91			# eo$move 1
	.byte 0				# eo$end

long:
decimal:
	cvtlp (ap)+,$10,(sp)		# 10 digits max
L14:	editpc $10,(sp),patdec,8(sp)	# ascii at 8(sp); r5=end+1
	skpc $' ,$10,8(sp)		# skip leading blanks; r1=first

prstr:			# r1=addr first byte; r5=addr last byte +1
	cvtbl $' ,-(sp)			# blank fill
	jbc $zfillb,flags,L15
	cvtbl $'0,(sp)			# zero fill
L15:	pushl fdesc			# FILE
	subl2 r1,r5			# r5=actual length=end+1-first
	subl3 r5,width,r0		# if >0, how much to fill
	bgeq L24
	clrl r0				# no fill
L24:	jbs $ljustb,flags,L25
	mnegl r0,r0
L25:	pushl r0			# fill count
	pushl r1			# addr first byte
	pushl r5			# length
	calls $5,__strout
	jbr	loop

pone:	.byte	0x1C			# packed 1
	
unsigned:
lunsigned:
	extzv $1,$31,(ap),r0		# right shift logical 1 bit
	cvtlp r0,$10,(sp)		# convert [n/2] to packed
	movp $10,(sp),8(sp)		# copy packed
	addp4 $10,8(sp),$10,(sp)	# 2*[n/2] in packed, at (sp)
	blbc (ap)+,L14			# n was even
	addp4 $1,pone,$10,(sp)		# n was odd
	jbr L14

charac:
	movl $4,r0			# chars per word
L18:	movb (ap)+,(r5)+		# transfer char
	bneq L19
	decl r5				# omit null characters
L19:	sobgtr r0,L18

prbuf:
	movl sp,r1			# addr first byte
	jbr prstr

plus:	bisl2 $psign,flags		# always print sign for floats
	jbr L4a
minus:	bisl2 $ljust,flags		# left justification, please
	jbr L4a
gnum0:	jbs $ndfndb,flags,gnum
	jbs $precb,flags,gnump		# ignore when reading precision
	bisl2 $zfill,flags		# leading zero fill, please
gnum:	jbs $precb,flags,gnump
	moval (width)[width],width	# width *= 5;
	movaw -ch.zer(r0)[width],width	# width = 2*witdh + r0 - '0';
	jbr gnumd
gnump:	moval (ndigit)[ndigit],ndigit	# ndigit *= 5;
	movaw -ch.zer(r0)[ndigit],ndigit # ndigit = 2*ndigit + r0 - '0';
gnumd:	bisl2 $ndfnd,flags		# digit seen
	jbr L4a
dot:	clrl ndigit			# start on the precision
	bisl2 $prec,flags
	bicl2 $ndfnd,flags
	jbr L4a
indir:	movl (ap)+,ndigit		# width specified by parameter
	jbr gnumd
remote:	movl (ap)+,ap
	movl (ap)+,r11
	jbr loop

float:
	bsbw fltcvt
fltg:	jbs $ndfndb,flags,float1
	movl $6,ndigit			# default # digits to right of decpt.
float1:	addl3 exp,ndigit,r7
	movl r7,r6			# for later "underflow" checking
	bgeq fxplrd
	clrl r7				# poor programmer planning
fxplrd:	cmpl r7,$31			# expressible in packed decimal?
	bleq fnarro			# yes
	movl $31,r7
fnarro:	subl3 $17,r7,r0			# where to round
	ashp r0,$17,(sp),$5,r7,16(sp)	# do it
	bvc fnovfl
	# band-aid for microcode error (spurious overflow)
	clrl r0				# assume even length result
	jlbc r7,fleven			# right
	movl $4,r0			# odd length result
fleven:	cmpv r0,$4,16(sp),$0		# top digit zero iff true overflow
	bneq fnovfl
	# end band-aid
	aobleq $0,r6,fnovfl		# if "underflow" then jump
	movl r7,r0
	incl exp
	incl r7
	ashp r0,$1,pone,$0,r7,16(sp)
	ashl $-1,r7,r0			# displ to last byte
	bisb2 sign,16(sp)[r0]		# insert sign
fnovfl:
	movc3 $4,patsci,(sp)
	clrl r6				# # digits moved so far
	movl exp,r0
	bleq fexpng
	bsbb patmov			# digits to left of decpt.
fexpng:	tstl ndigit
	jeql fnodp
	movc3 $2,fpatdp,(r3)
	tstl exp
	bgeq fxppos
	addl3 exp,ndigit,r6
	bgeq  flfakl
	clrl r6				# it's all fill
flfakl:	subl3 r6,$31,r6			# fake length for patmov
flfill:	movc3 $2,fpatzf,(r3)		# zero fill to right of dec.pt
fxppos:	movl ndigit,r0
	bsbb patmov
fnodp:	sobgeq r6,fledit		# must move at least 1 digit
	movl $31,r6			# none moved; fake it
	aobleq $1,ndigit,flfill		# with a one-character zero fill
fledit:	editpc r7,16(sp),(sp),32(sp)
	jbr prflt

patexp:	.byte	0x03			# eo$set_signif
	.byte	0x44,'e			# eo$insert 'e
	.byte	0x42,'+			# eo$load_plus '+
	.byte	0x04			# eo$store_sign
	.byte	0x92			# eo$move 2
	.byte	0			# eo$end
patsci:	.byte	0x42,'+			# eo$load_plus '+
	.byte	0x03			# eo$set_signif
	.byte	0x04			# eo$store_sign
	.byte	0x91			# eo$move 1
fpatdp:	.byte	0x44,'.			# eo$insert '.
fpatzf:	.byte	0x40,'0			# eo$load_fill '0

	# construct pattern at (r3) to move  r0  digits in  editpc;
	#  r6  digits already moved for this number
patmov:
	movb $0x90,r2			# eo$move
	subl3 r6,$31,r1			# # digits remaining in packed
	addl2 r0,r6
	cmpl r0,r1			# enough digits remaining?
	bleq patsml			# yes
	tstl exp			# zero 'fill'; before or after rest?
	bgeq pataft			# after
	pushl r1			# # digits remaining
	movb $0x80,r2			# eo$fill
	subl3 $31,r6,r0			# number of fill bytes
	bsbb patsml			# recursion!
	movl (sp)+,r0
	movb $0x90,r2			# eo$move
	jbr patsml
pataft:	movl r1,r0			# last of the 31
	bsbb patsml			# recursion!
	subl3 $31,r6,r0			# number of fill bytes
	movb $0x80,r2			# eo$fill
patsml:	tstl r0
	bleq patzer			# DEC doesn't like repetition counts of 0
	mnegl $15,r1			# 15 digits at a time
	subl2 r1,r0			# counteract acbl
	jbr pattst
patmlp:	bisb3 r2,$15,(r3)+		# 15
pattst:	acbl $16,r1,r0,patmlp		# until <= 15 left
	bisb3 r2,r0,(r3)+		# rest
patzer:	clrb (r3)			# eo$end
	rsb

scien:
	bsbw fltcvt			# get packed digits
scig:	incl ndigit
	jbs $ndfndb,flags,L23
	movl $7,ndigit
L23:	subl3 $17,ndigit,r0		# rounding position
	ashp r0,$17,(sp),$5,ndigit,16(sp) # shift and round
	bvc snovfl
	# band-aid for microcode error (spurious overflow)
	clrl r0				# assume even length result
	jlbc ndigit,sceven		# right
	movl $4,r0			# odd length result
sceven:	cmpv r0,$4,16(sp),$0		# top digit zero iff true overflow
	bneq snovfl
	# end band-aid
	incl exp			# rounding overflowed to 100...
	subl3 $1,ndigit,r0
	ashp r0,$1,pone,$0,ndigit,16(sp)
	ashl $-1,ndigit,r0		# displ to last byte
	bisb2 sign,16(sp)[r0]		# insert sign
snovfl:
	jbc $gflagb,flags,enotg		# not %g format
 # find trailing zeroes in packed number
	ashl $-1,ndigit,r0
	addl2 r3,r0			# addr of l.s.digit and sign
	movl $4,r1			# bit position of digit
	movl ndigit,r7			# current length of packed
	jbr gtz
gtz1:	xorl2 $4,r1			# position of next digit
	bneq gtz			# same byte
	decl r0				# different byte
gtz:	cmpv r1,$4,(r0),$0		# a trailing zero?
	jneq gntz
	sobgtr r7,gtz1
	incl r7
gntz:					# r7: minimum width of fraction
	cmpl exp,$-4
	jleq eg				# small exponents use %e
	subl3 r7,exp,r0
	cmpl $5,r0
	jleq eg				# so do (w+5) <= exp
	tstl r0				# rest use %f
	jleq fg				# did we trim too many trailing zeroes?
	movl exp,r7			# yes
fg:	subl3 ndigit,r7,r0
	ashp r0,ndigit,16(sp),$0,r7,(sp)
	movp r7,(sp),16(sp)
	subl3 exp,r7,ndigit		# correct ndigit for %f
	jbr fnovfl
eg:	subl3 ndigit,r7,r0
	ashp r0,ndigit,16(sp),$0,r7,(sp)
	movp r7,(sp),16(sp)
	movl r7,ndigit			# packed number has been trimmed
enotg:
	movc3 $7,patsci,(sp)
	movl $1,r6			# 1P
	subl3 $1,ndigit,r0		# digits after dec.pt
	bsbw patmov
	editpc ndigit,16(sp),(sp),32(sp)	# 32(sp)->result, r5->(end+1)
	decl exp			# compensate: 1 digit left of dec.pt
	cvtlp exp,$2,(sp)		# exponent
	editpc $2,(sp),patexp,(r5)
prflt:	movab 32(sp),r1
	jbs $psignb,flags,prflt1
	cmpb (r1)+,$'+
	beql prflt1
	decl r1
prflt1:	skpc $' ,$63,(r1)
	jbr prstr

general:
	jbcs $gflagb,flags,scien
	jbr scien			# safety net

	# convert double-floating at (ap) to 17-digit packed at (sp),
	# set 'sign' and 'exp', advance ap.
fltcvt:
	clrb sign
	movd (ap)+,r5
	jeql fzero
	bgtr fpos
	mnegd r5,r5
	incb sign
fpos:
	extzv $7,$8,r5,r2		# exponent of 2
	movaw -0600(r2)[r2],r2		# unbias and mult by 3
	bgeq epos
	subl2 $9,r2
epos:	divl2 $10,r2
	bsbb expten
	cmpd r0,r5
	bgtr ceil
	incl r2
ceil:	movl r2,exp
	mnegl r2,r2
	cmpl r2,$29			# 10^(29+9) is all we can handle
	bleq getman
	muld2 ten16,r5
	subl2 $16,r2
getman:	addl2 $9,r2			# -ceil(log10(x)) + 9
	bsbb expten
	emodd r0,r4,r5,r0,r5		# (r0+r4)*r5; r0=int, r5=frac
fz1:	cvtlp r0,$9,16(sp)		# leading 9 digits
	ashp $8,$9,16(sp),$0,$17,4(sp)	# as top 9 of 17
	emodd ten8,$0,r5,r0,r5
	cvtlp r0,$8,16(sp)		# trailing 8 digits
	addp4 $8,16(sp),$17,4(sp)	# combine leading and trailing
	bisb2 sign,12(sp)		# and insert sign
	rsb
fzero:	clrl r0
	movl $1,exp		# 0.000e+00 and 0.000 rather than 0.000e-01 and .000
	jbr fz1

	# return 10^r2 as a double float in r0||r1 and 8 extra bits of precision in r4
	# preserve r2, r5||r6
expten:
	movd $one,r0			# begin computing 10^exp10
	clrl r4				# bit counter
	movad ten1,r3			# table address
	tstl r2
	bgeq e10lp
	mnegl r2,r2			# get absolute value
	jbss $6,r2,e10lp		# flag as negative
e10lp:	jbc r4,r2,el1			# want this power?
	muld2 (r3),r0			# yes
el1:	addl2 $8,r3			# advance to next power
	aobleq $5,r4,e10lp		# through 10^32
	jbcc $6,r2,el2			# correct for negative exponent
	divd3 r0,$one,r0		# by taking reciprocal
	mnegl r2,r2
el2:	clrl r4				# 8 extra bits of precision
	rsb

	# powers of ten
	.align	2
ten1:	.word	0x4220,0,0,0
ten2:	.word	0x43c8,0,0,0
ten4:	.word	0x471c,0x4000,0,0
ten8:	.word	0x4dbe,0xbc20,0,0
ten16:	.word	0x5b0e,0x1bc9,0xbf04,0
ten32:	.word	0x759d,0xc5ad,0xa82b,0x70b6
