xref: /original-bsd/lib/libc/vax/stdio.old/doprnt.c (revision 542201aa)
1 #ifdef LIBC_SCCS
2 	.data
3 _sccsid:
4 	.asciz	"@(#)doprnt.c	5.4 (Berkeley) 03/09/86"
5 	.text
6 #endif LIBC_SCCS
7 
8 	# C library -- conversions
9 
10 #include "DEFS.h"
11 
12 .globl	__doprnt
13 .globl	__flsbuf
14 
15 #define vbit 1
16 #define flags r10
17 #define ndfnd 0
18 #define prec 1
19 #define zfill 2
20 #define minsgn 3
21 #define plssgn 4
22 #define numsgn 5
23 #define caps 6
24 #define blank 7
25 #define gflag 8
26 #define dpflag 9
27 #define width r9
28 #define ndigit r8
29 #define llafx r7
30 #define lrafx r6
31 #define fdesc -4(fp)
32 #define exp -8(fp)
33 #define sexp -12(fp)
34 #define nchar -16(fp)
35 #define sign -17(fp)
36 	.set ch.zer,'0			# cpp doesn't like single appostrophes
37 
38 	.align 2
39 strtab:		# translate table for detecting null and percent
40 	.byte	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
41 	.byte	16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
42 	.byte	' ,'!,'",'#,'$, 0,'&,'','(,'),'*,'+,',,'-,'.,'/
43 	.byte	'0,'1,'2,'3,'4,'5,'6,'7,'8,'9,':,';,'<,'=,'>,'?
44 	.byte	'@,'A,'B,'C,'D,'E,'F,'G,'H,'I,'J,'K,'L,'M,'N,'O
45 	.byte	'P,'Q,'R,'S,'T,'U,'V,'W,'X,'Y,'Z,'[,'\,'],'^,'_
46 	.byte	'`,'a,'b,'c,'d,'e,'f,'g,'h,'i,'j,'k,'l,'m,'n,'o
47 	.byte	'p,'q,'r,'s,'t,'u,'v,'w,'x,'y,'z,'{,'|,'},'~,127
48 	.byte	128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143
49 	.byte	144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159
50 	.byte	160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175
51 	.byte	176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191
52 	.byte	192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207
53 	.byte	208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223
54 	.byte	224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239
55 	.byte	240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
56 
57 ENTRY(_doprnt, R6|R7|R8|R9|R10|R11)
58 	jbr	doit
59 
60 strfoo:
61 	clrl r4					# fix interrupt race
62 	jbr strok				# and try again
63 strout2:		# enter here to force out r2; r0,r1 must be set
64 	# do some tricks with line buffering (_IOLBF) first
65 	movl	fdesc,r3
66 	jbc	$7,16(r3),0f		# not line buffered (unbuffered)
67 	addl3	12(r3),8(r3),r4		# fdesc->_base+fdesc->_bufsiz
68 	cmpl	4(r3),r4		# buffer full?
69 	jgeq	0f			#  yes
70 	cmpl	r2,$10			# c == '\n'?
71 	jeql	0f			#  yes
72 	movb	r2,*4(r3)		# line buffered and not buffer full
73 	incl	4(r3)			#  and not newline
74 	clrl	(r3)			#  just stuff it and fix _cnt
75 	incl	nchar			# count the char
76 	jbr	strout			# skip __flsbuf
77 0:	pushr	$3				# save input descriptor
78 	pushl fdesc				# FILE
79 	pushl r2				# the char
80 	calls $2,__flsbuf		# please empty the buffer and handle 1 char
81 	tstl r0					# successful?
82 	jgeq strm1				# yes
83 	jbcs $31,nchar,strm1	# turn on sign bit of nchar to signify error
84 strm1:
85 	incl nchar				# count the char
86 	popr $3					# get input descriptor back
87 strout:			# enter via bsb with (r0,r1)=input descriptor
88 	movab strtab,r3			# table address
89 	movq *fdesc,r4			# output descriptor
90 	jbs $31,r4,strfoo		# negative count is a no no
91 strok:
92 	addl2 r0,nchar			# we intend to move this many chars
93 /******* Start bogus movtuc workaround  *****/
94 	clrl r2
95 	tstl    r0
96 	bleq    movdon
97 movlp:
98 	tstl    r4
99 	bleq    movdon
100 	movzbl  (r1)+,r3
101 	tstb    strtab[r3]
102 	bneq    1f
103 	mnegl   $1,r2
104 	decl    r1
105 	brb     movdon
106 1:
107 	movb    r3,(r5)+
108 	decl    r4
109 	sobgtr  r0,movlp
110   /******* End bogus movtuc workaround ***
111 	movtuc r0,(r1),$0,(r3),r4,(r5)
112 	movpsl r2                       /*  squirrel away condition codes */
113   /******* End equally bogus movtuc ****/
114 movdon: movq r4,*fdesc                  /*  update output descriptor */
115 	subl2 r0,nchar			# some chars not moved
116 	jbs $vbit,r2,stresc		# terminated by escape?
117 	sobgeq r0,strmore		# no; but out buffer might be full
118 stresc:
119 	rsb
120 strmore:
121 	movzbl (r1)+,r2			# one char
122 	tstb strtab[r2]			# translate
123 	jneq strout2			# bad guy in disguise (outbuf is full)
124 	incl r0				# fix the length
125 	decl r1				# and the addr
126 	movl $1<vbit,r2			# fake condition codes
127 	rsb
128 
129 errdone:
130 	jbcs $31,nchar,prdone	# set error bit
131 prdone:
132 	movl nchar,r0
133 	ret
134 
135 doit:
136 	movab -256(sp),sp		# work space
137 	movl 4(ap),r11			# addr of format string
138 	movl 12(ap),fdesc		# output FILE ptr
139 	movl 8(ap),ap			# addr of first arg
140 	clrl nchar				# number of chars transferred
141 loop:
142 	movzwl $65535,r0		# pseudo length
143 	movl r11,r1				# fmt addr
144 		# comet sucks.
145 	movq *fdesc,r4
146 	subl3 r1,r5,r2
147 	jlss lp1
148 	cmpl r0,r2
149 	jleq lp1
150 	movl r2,r0
151 lp1:
152 		#
153 	bsbw strout				# copy to output, stop at null or percent
154 	movl r1,r11				# new fmt
155 	jbc $vbit,r2,loop		# if no escape, then very long fmt
156 	tstb (r11)+				# escape; null or percent?
157 	jeql prdone				# null means end of fmt
158 
159 	movl sp,r5			# reset output buffer pointer
160 	clrq r9				# width; flags
161 	clrq r6				# lrafx,llafx
162 longorunsg:				# we can ignore both of these distinctions
163 short:
164 L4a:
165 	movzbl (r11)+,r0		# so capital letters can tail merge
166 L4:	caseb r0,$' ,$'x-' 		# format char
167 L5:
168 	.word space-L5			# space
169 	.word fmtbad-L5			# !
170 	.word fmtbad-L5			# "
171 	.word sharp-L5			# #
172 	.word fmtbad-L5			# $
173 	.word fmtbad-L5			# %
174 	.word fmtbad-L5			# &
175 	.word fmtbad-L5			# '
176 	.word fmtbad-L5			# (
177 	.word fmtbad-L5			# )
178 	.word indir-L5			# *
179 	.word plus-L5			# +
180 	.word fmtbad-L5			# ,
181 	.word minus-L5			# -
182 	.word dot-L5			# .
183 	.word fmtbad-L5			# /
184 	.word gnum0-L5			# 0
185 	.word gnum-L5			# 1
186 	.word gnum-L5			# 2
187 	.word gnum-L5			# 3
188 	.word gnum-L5			# 4
189 	.word gnum-L5			# 5
190 	.word gnum-L5			# 6
191 	.word gnum-L5			# 7
192 	.word gnum-L5			# 8
193 	.word gnum-L5			# 9
194 	.word fmtbad-L5			# :
195 	.word fmtbad-L5			# ;
196 	.word fmtbad-L5			# <
197 	.word fmtbad-L5			# =
198 	.word fmtbad-L5			# >
199 	.word fmtbad-L5			# ?
200 	.word fmtbad-L5			# @
201 	.word fmtbad-L5			# A
202 	.word fmtbad-L5			# B
203 	.word fmtbad-L5			# C
204 	.word decimal-L5		# D
205 	.word capital-L5		# E
206 	.word fmtbad-L5			# F
207 	.word capital-L5		# G
208 	.word fmtbad-L5			# H
209 	.word fmtbad-L5			# I
210 	.word fmtbad-L5			# J
211 	.word fmtbad-L5			# K
212 	.word fmtbad-L5			# L
213 	.word fmtbad-L5			# M
214 	.word fmtbad-L5			# N
215 	.word octal-L5			# O
216 	.word fmtbad-L5			# P
217 	.word fmtbad-L5			# Q
218 	.word fmtbad-L5			# R
219 	.word fmtbad-L5			# S
220 	.word fmtbad-L5			# T
221 	.word unsigned-L5		# U
222 	.word fmtbad-L5			# V
223 	.word fmtbad-L5			# W
224 	.word capital-L5		# X
225 	.word fmtbad-L5			# Y
226 	.word fmtbad-L5			# Z
227 	.word fmtbad-L5			# [
228 	.word fmtbad-L5			# \
229 	.word fmtbad-L5			# ]
230 	.word fmtbad-L5			# ^
231 	.word fmtbad-L5			# _
232 	.word fmtbad-L5			# `
233 	.word fmtbad-L5			# a
234 	.word fmtbad-L5			# b
235 	.word charac-L5			# c
236 	.word decimal-L5		# d
237 	.word scien-L5			# e
238 	.word float-L5			# f
239 	.word general-L5		# g
240 	.word short-L5			# h
241 	.word fmtbad-L5			# i
242 	.word fmtbad-L5			# j
243 	.word fmtbad-L5			# k
244 	.word longorunsg-L5		# l
245 	.word fmtbad-L5			# m
246 	.word fmtbad-L5			# n
247 	.word octal-L5			# o
248 	.word fmtbad-L5			# p
249 	.word fmtbad-L5			# q
250 	.word fmtbad-L5			# r
251 	.word string-L5			# s
252 	.word fmtbad-L5			# t
253 	.word unsigned-L5		# u
254 	.word fmtbad-L5			# v
255 	.word fmtbad-L5			# w
256 	.word hex-L5			# x
257 fmtbad:
258 	movb r0,(r5)+			# print the unfound character
259 	jeql errdone			# dumb users who end the format with a %
260 	jbr prbuf
261 capital:
262 	bisl2 $1<caps,flags		# note that it was capitalized
263 	xorb2 $'a^'A,r0			# make it small
264 	jbr L4					# and try again
265 
266 string:
267 	movl ndigit,r0
268 	jbs $prec,flags,L20		# max length was specified
269 	mnegl $1,r0			# default max length
270 L20:	movl (ap)+,r2			# addr first byte
271 	locc $0,r0,(r2)			# find the zero at the end
272 	movl r1,r5			# addr last byte +1
273 	movl r2,r1			# addr first byte
274 	jbr prstr
275 
276 htab:	.byte	'0,'1,'2,'3,'4,'5,'6,'7,'8,'9,'a,'b,'c,'d,'e,'f
277 Htab:	.byte	'0,'1,'2,'3,'4,'5,'6,'7,'8,'9,'A,'B,'C,'D,'E,'F
278 
279 octal:
280 	movl $30,r2			# init position
281 	movl $3,r3			# field width
282 	movab htab,llafx	# translate table
283 	jbr L10
284 
285 hex:
286 	movl $28,r2			# init position
287 	movl $4,r3			# field width
288 	movab htab,llafx	# translate table
289 	jbc $caps,flags,L10
290 	movab Htab,llafx
291 L10:	mnegl r3,r6			# increment
292 	clrl r1
293 	addl2 $4,r5			# room for left affix (2) and slop [forced sign?]
294 	movl (ap)+,r0			# fetch arg
295 L11:	extzv r2,r3,r0,r1		# pull out a digit
296 	movb (llafx)[r1],(r5)+		# convert to character
297 L12:	acbl $0,r6,r2,L11		# continue until done
298 	clrq r6				# lrafx, llafx
299 	clrb (r5)			# flag end
300 	skpc $'0,$11,4(sp)		# skip over leading zeroes
301 	jbc $numsgn,flags,prn3	# easy if no left affix
302 	tstl -4(ap)				# original value
303 	jeql prn3			# no affix on 0, for some reason
304 	cmpl r3,$4			# were we doing hex or octal?
305 	jneq L12a			# octal
306 	movb $'x,r0
307 	jbc $caps,flags,L12b
308 	movb $'X,r0
309 L12b:	movb r0,-(r1)
310 	movl $2,llafx		# leading 0x for hex is an affix
311 L12a:	movb $'0,-(r1)	# leading zero for octal is a digit, not an affix
312 	jbr prn3			# omit sign (plus, blank) massaging
313 
314 unsigned:
315 lunsigned:
316 	bicl2 $1<plssgn|1<blank,flags	# omit sign (plus, blank) massaging
317 	extzv $1,$31,(ap),r0		# right shift logical 1 bit
318 	cvtlp r0,$10,(sp)		# convert [n/2] to packed
319 	movp $10,(sp),8(sp)		# copy packed
320 	addp4 $10,8(sp),$10,(sp)	# 2*[n/2] in packed, at (sp)
321 	blbc (ap)+,L14			# n was even
322 	addp4 $1,pone,$10,(sp)		# n was odd
323 	jbr L14
324 
325 patdec:					# editpc pattern for decimal printing
326 	.byte 0xAA			# eo$float 10
327 	.byte 0x01			# eo$end_float
328 	.byte 0				# eo$end
329 
330 decimal:
331 	cvtlp (ap)+,$10,(sp)		# 10 digits max
332 	jgeq L14
333 	incl llafx			# minus sign is a left affix
334 L14:	editpc $10,(sp),patdec,8(sp)	# ascii at 8(sp); r5=end+1
335 	skpc $' ,$11,8(sp)		# skip leading blanks; r1=first
336 
337 prnum:			# r1=addr first byte, r5=addr last byte +1, llafx=size of signs
338 				# -1(r1) vacant, for forced sign
339 	tstl llafx
340 	jneq prn3			# already some left affix, dont fuss
341 	jbc $plssgn,flags,prn2
342 	movb $'+,-(r1)		# needs a plus sign
343 	jbr prn4
344 prn2:	jbc $blank,flags,prn3
345 	movb $' ,-(r1)		# needs a blank sign
346 prn4:	incl llafx
347 prn3:	jbs $prec,flags,prn1
348 	movl $1,ndigit		# default precision is 1
349 prn1:	subl3 r1,r5,lrafx	# raw width
350 	subl2 llafx,lrafx	# number of digits
351 	subl2 lrafx,ndigit	# number of leading zeroes needed
352 	jleq prstr			# none
353 	addl2 llafx,r1		# where current digits start
354 	pushl r1			# movcx gobbles registers
355 		# check bounds on users who say %.300d
356 	movab 32(r5)[ndigit],r2
357 	subl2 fp,r2
358 	jlss prn5
359 	subl2 r2,ndigit
360 prn5:
361 		#
362 	movc3 lrafx,(r1),(r1)[ndigit]	# make room in middle
363 	movc5 $0,(r1),$ch.zer,ndigit,*(sp)	# '0 fill
364 	subl3 llafx,(sp)+,r1	# first byte addr
365 	addl3 lrafx,r3,r5	# last byte addr +1
366 
367 prstr:			# r1=addr first byte; r5=addr last byte +1
368 				# width=minimum width; llafx=len. left affix
369 				# ndigit=<avail>
370 	subl3 r1,r5,ndigit		# raw width
371 	subl3 ndigit,width,r0	# pad length
372 	jleq padlno				# in particular, no left padding
373 	jbs $minsgn,flags,padlno
374 			# extension for %0 flag causing left zero padding to field width
375 	jbs $zfill,flags,padlz
376 			# this bsbb needed even if %0 flag extension is removed
377 	bsbb padb				# blank pad on left
378 	jbr padnlz
379 padlz:
380 	movl llafx,r0
381 	jleq padnlx				# left zero pad requires left affix first
382 	subl2 r0,ndigit			# part of total length will be transferred
383 	subl2 r0,width			# and will account for part of minimum width
384 	bsbw strout				# left affix
385 padnlx:
386 	subl3 ndigit,width,r0	# pad length
387 	bsbb padz				# zero pad on left
388 padnlz:
389 			# end of extension for left zero padding
390 padlno:			# remaining: root, possible right padding
391 	subl2 ndigit,width		# root reduces minimum width
392 	movl ndigit,r0			# root length
393 p1:	bsbw strout				# transfer to output buffer
394 p3:	jbc $vbit,r2,padnpct	# percent sign (or null byte via %c) ?
395 	decl r0					# yes; adjust count
396 	movzbl (r1)+,r2			# fetch byte
397 	movq *fdesc,r4			# output buffer descriptor
398 	sobgeq r4,p2			# room at the out [inn] ?
399 	bsbw strout2			# no; force it, then try rest
400 	jbr p3					# here we go 'round the mullberry bush, ...
401 p2:	movb r2,(r5)+			# hand-deposit the percent or null
402 	incl nchar				# count it
403 	movq r4,*fdesc			# store output descriptor
404 	jbr p1					# what an expensive hiccup!
405 padnpct:
406 	movl width,r0	# size of pad
407 	jleq loop
408 	bsbb padb
409 	jbr loop
410 
411 padz:
412 	movb $'0,r2
413 	jbr pad
414 padb:
415 	movb $' ,r2
416 pad:
417 	subl2 r0,width			# pad width decreases minimum width
418 	pushl r1				# save non-pad addr
419 	movl r0,llafx			# remember width of pad
420 	subl2 r0,sp				# allocate
421 	movc5 $0,(r0),r2,llafx,(sp)	# create pad string
422 	movl llafx,r0			# length
423 	movl sp,r1				# addr
424 	bsbw strout
425 	addl2 llafx,sp			# deallocate
426 	movl (sp)+,r1			# recover non-pad addr
427 	rsb
428 
429 pone:	.byte	0x1C			# packed 1
430 
431 charac:
432 	movl (ap)+,r0		# word containing the char
433 	movb r0,(r5)+		# one byte, that's all
434 
435 prbuf:
436 	movl sp,r1			# addr first byte
437 	jbr prstr
438 
439 space:	bisl2 $1<blank,flags		# constant width e fmt, no plus sign
440 	jbr L4a
441 sharp:	bisl2 $1<numsgn,flags		# 'self identifying', please
442 	jbr L4a
443 plus:	bisl2 $1<plssgn,flags		# always print sign for floats
444 	jbr L4a
445 minus:	bisl2 $1<minsgn,flags		# left justification, please
446 	jbr L4a
447 gnum0:	jbs $ndfnd,flags,gnum
448 	jbs $prec,flags,gnump		# ignore when reading precision
449 	bisl2 $1<zfill,flags		# leading zero fill, please
450 gnum:	jbs $prec,flags,gnump
451 	moval (width)[width],width	# width *= 5;
452 	movaw -ch.zer(r0)[width],width	# width = 2*witdh + r0 - '0';
453 	jbr gnumd
454 gnump:	moval (ndigit)[ndigit],ndigit	# ndigit *= 5;
455 	movaw -ch.zer(r0)[ndigit],ndigit # ndigit = 2*ndigit + r0 - '0';
456 gnumd:	bisl2 $1<ndfnd,flags		# digit seen
457 	jbr L4a
458 dot:	clrl ndigit			# start on the precision
459 	bisl2 $1<prec,flags
460 	bicl2 $1<ndfnd,flags
461 	jbr L4a
462 indir:
463 	jbs $prec,flags,in1
464 	movl (ap)+,width		# width specified by parameter
465 	jgeq gnumd
466 	xorl2 $1<minsgn,flags		# parameterized left adjustment
467 	mnegl width,width
468 	jbr gnumd
469 in1:
470 	movl (ap)+,ndigit		# precision specified by paratmeter
471 	jgeq gnumd
472 	mnegl ndigit,ndigit
473 	jbr gnumd
474 
475 float:
476 	jbs $prec,flags,float1
477 	movl $6,ndigit			# default # digits to right of decpt.
478 float1:	bsbw fltcvt
479 	addl3 exp,ndigit,r7
480 	movl r7,r6			# for later "underflow" checking
481 	bgeq fxplrd
482 	clrl r7				# poor programmer planning
483 fxplrd:	cmpl r7,$31			# expressible in packed decimal?
484 	bleq fnarro			# yes
485 	movl $31,r7
486 fnarro:	subl3 $17,r7,r0			# where to round
487 	ashp r0,$17,(sp),$5,r7,16(sp)	# do it
488 	bvc fnovfl
489 		# band-aid for microcode error (spurious overflow)
490 	#	clrl r0				# assume even length result
491 	#	jlbc r7,fleven			# right
492 	#	movl $4,r0			# odd length result
493 	#fleven:	cmpv r0,$4,16(sp),$0		# top digit zero iff true overflow
494 	#	bneq fnovfl
495 		# end band-aid
496 	aobleq $0,r6,fnovfl		# if "underflow" then jump
497 	movl r7,r0
498 	incl exp
499 	incl r7
500 	ashp r0,$1,pone,$0,r7,16(sp)
501 	ashl $-1,r7,r0			# displ to last byte
502 	bisb2 sign,16(sp)[r0]		# insert sign
503 fnovfl:
504 	movab 16(sp),r1		# packed source
505 	movl r7,r6		# packed length
506 	pushab prnum	# goto prnum after fall-through call to fedit
507 
508 
509 	# enter via bsb
510 	#	r1=addr of packed source
511 	#	   16(r1) used to unpack source
512 	#	   48(r1) used to construct pattern to unpack source
513 	#	   48(r1) used to hold result
514 	#	r6=length of packed source (destroyed)
515 	#	exp=# digits to left of decimal point (destroyed)
516 	#	ndigit=# digits to right of decimal point (destroyed)
517 	#	sign=1 if negative, 0 otherwise
518 	# stack will be used for work space for pattern and unpacked source
519 	# exits with
520 	#	r1=addr of punctuated result
521 	#	r5=addr of last byte +1
522 	#	llafx=1 if minus sign inserted, 0 otherwise
523 fedit:
524 	pushab 48(r1)			# save result addr
525 	movab 48(r1),r3			# pattern addr
526 	movb $0x03,(r3)+		# eo$set_signif
527 	movc5 $0,(r1),$0x91,r6,(r3)	# eo$move 1
528 	clrb (r3)				# eo$end
529 	editpc r6,(r1),48(r1),16(r1)	# unpack 'em all
530 	subl3 r6,r5,r1			# addr unpacked source
531 	movl (sp),r3			# punctuated output placed here
532 	clrl llafx
533 	jlbc sign,f1
534 	movb $'-,(r3)+		# negative
535 	incl llafx
536 f1:	movl exp,r0
537 	jgtr f2
538 	movb $'0,(r3)+		# must have digit before decimal point
539 	jbr f3
540 f2:	cmpl r0,r6			# limit on packed length
541 	jleq f4
542 	movl r6,r0
543 f4:	subl2 r0,r6			# eat some digits
544 	subl2 r0,exp		# from the exponent
545 	movc3 r0,(r1),(r3)	# (most of the) digits to left of decimal point
546 	movl exp,r0			# need any more?
547 	jleq f3
548 	movc5 $0,(r1),$'0,r0,(r3)	# '0 fill
549 f3:	movl ndigit,r0		# # digits to right of decimal point
550 	jgtr f5
551 	jbs $numsgn,flags,f5	# no decimal point unless forced
552 	jbcs $dpflag,flags,f6	# no decimal point
553 f5:	movb $'.,(r3)+		# the decimal point
554 f6:	mnegl exp,r0		# "leading" zeroes to right of decimal point
555 	jleq f9
556 	cmpl r0,ndigit		# cant exceed this many
557 	jleq fa
558 	movl ndigit,r0
559 fa:	subl2 r0,ndigit
560 	movc5 $0,(r1),$'0,r0,(r3)
561 f9:	movl ndigit,r0
562 	cmpl r0,r6			# limit on packed length
563 	jleq f7
564 	movl r6,r0
565 f7:	subl2 r0,ndigit		# eat some digits from the fraction
566 	movc3 r0,(r1),(r3)	# (most of the) digits to right of decimal point
567 	movl ndigit,r0			# need any more?
568 	jleq f8
569 		# check bounds on users who say %.300f
570 	movab 32(r3)[r0],r2
571 	subl2 fp,r2
572 	jlss fb
573 	subl2 r2,r0			# truncate, willy-nilly
574 	movl r0,ndigit		# and no more digits later, either
575 fb:
576 		#
577 	subl2 r0,ndigit		# eat some digits from the fraction
578 	movc5 $0,(r1),$'0,r0,(r3)	# '0 fill
579 f8:	movl r3,r5			# addr last byte +1
580 	popr $1<1			# [movl (sp)+,r1] addr first byte
581 	rsb
582 
583 patexp:	.byte	0x03			# eo$set_signif
584 	.byte	0x44,'e			# eo$insert 'e
585 	.byte	0x42,'+			# eo$load_plus '+
586 	.byte	0x04			# eo$store_sign
587 	.byte	0x92			# eo$move 2
588 	.byte	0			# eo$end
589 
590 scien:
591 	incl ndigit
592 	jbs $prec,flags,L23
593 	movl $7,ndigit
594 L23:	bsbw fltcvt			# get packed digits
595 	movl ndigit,r7
596 	cmpl r7,$31				# expressible in packed decimal?
597 	jleq snarro				# yes
598 	movl $31,r7
599 snarro:	subl3 $17,r7,r0		# rounding position
600 	ashp r0,$17,(sp),$5,r7,16(sp) # shift and round
601 	bvc snovfl
602 		# band-aid for microcode error (spurious overflow)
603 	#	clrl r0				# assume even length result
604 	#	jlbc ndigit,sceven		# right
605 	#	movl $4,r0			# odd length result
606 	#sceven:	cmpv r0,$4,16(sp),$0		# top digit zero iff true overflow
607 	#	bneq snovfl
608 		# end band-aid
609 	incl exp			# rounding overflowed to 100...
610 	subl3 $1,r7,r0
611 	ashp r0,$1,pone,$0,r7,16(sp)
612 	ashl $-1,r7,r0		# displ to last byte
613 	bisb2 sign,16(sp)[r0]		# insert sign
614 snovfl:
615 	jbs $gflag,flags,gfmt		# %g format
616 	movab 16(sp),r1
617 	bsbb eedit
618 eexp:
619 	movl r1,r6		# save fwa from destruction by cvtlp
620 	subl3 $1,sexp,r0	# 1P exponent
621 	cvtlp r0,$2,(sp)	# packed
622 	editpc $2,(sp),patexp,(r5)
623 	movl r6,r1		# fwa
624 	jbc $caps,flags,prnum
625 	xorb2 $'e^'E,-4(r5)
626 	jbr prnum
627 
628 eedit:
629 	movl r7,r6		# packed length
630 	decl ndigit		# 1 digit before decimal point
631 	movl exp,sexp	# save from destruction
632 	movl $1,exp		# and pretend
633 	jbr fedit
634 
635 gfmt:
636 	addl3 $3,exp,r0		# exp is 1 more than e
637 	jlss gfmte		# (e+1)+3<0, e+4<=-1, e<=-5
638 	subl2 $3,r0		# exp [==(e+1)]
639 	cmpl r0,ndigit
640 	jgtr gfmte		# e+1>n, e>=n
641 gfmtf:
642 	movl r7,r6
643 	subl2 r0,ndigit		# n-e-1
644 	movab 16(sp),r1
645 	bsbw fedit
646 g1:	jbs $numsgn,flags,g2
647 	jbs $dpflag,flags,g2	# dont strip if no decimal point
648 g3:	cmpb -(r5),$'0		# strip trailing zeroes
649 	jeql g3
650 	cmpb (r5),$'.		# and trailing decimal point
651 	jeql g2
652 	incl r5
653 g2:	jbc $gflag,flags,eexp
654 	jbr prnum
655 gfmte:
656 	movab 16(sp),r1		# packed source
657 	bsbw eedit
658 	jbsc $gflag,flags,g1	# gflag now means "use %f" [hence no exponent]
659 
660 general:
661 	jbs $prec,flags,gn1
662 	movl $6,ndigit		# default precision is 6 significant digits
663 gn1:	tstl ndigit		# cannot allow precision of 0
664 	jgtr gn2
665 	movl $1,ndigit		# change 0 to 1, willy-nilly
666 gn2:	jbcs $gflag,flags,L23
667 	jbr L23			# safety net
668 
669 	# convert double-floating at (ap) to 17-digit packed at (sp),
670 	# set 'sign' and 'exp', advance ap.
671 fltcvt:
672 	clrb sign
673 	movd (ap)+,r5
674 	jeql fzero
675 	bgtr fpos
676 	mnegd r5,r5
677 	incb sign
678 fpos:
679 	extzv $7,$8,r5,r2		# exponent of 2
680 	movab -0200(r2),r2		# unbias
681 	mull2 $59,r2			# 59/196: 3rd convergent continued frac of log10(2)
682 	jlss eneg
683 	movab 196(r2),r2
684 eneg:
685 	movab -98(r2),r2
686 	divl2 $196,r2
687 	bsbw expten
688 	cmpd r0,r5
689 	bgtr ceil
690 	incl r2
691 ceil:	movl r2,exp
692 	mnegl r2,r2
693 	cmpl r2,$29			# 10^(29+9) is all we can handle
694 	bleq getman
695 	muld2 ten16,r5
696 	subl2 $16,r2
697 getman:	addl2 $9,r2			# -ceil(log10(x)) + 9
698 	jsb expten
699 	emodd r0,r4,r5,r0,r5		# (r0+r4)*r5; r0=int, r5=frac
700 fz1:	cvtlp r0,$9,16(sp)		# leading 9 digits
701 	ashp $8,$9,16(sp),$0,$17,4(sp)	# as top 9 of 17
702 	emodd ten8,$0,r5,r0,r5
703 	cvtlp r0,$8,16(sp)		# trailing 8 digits
704 		# if precision >= 17, must round here
705 	movl ndigit,r7			# so figure out what precision is
706 	pushab scien
707 	cmpl (sp)+,(sp)
708 	jleq gm1			# who called us?
709 	addl2 exp,r7			# float; adjust for exponent
710 gm1:	cmpl r7,$17
711 	jlss gm2
712 	cmpd r5,$0d0.5			# must round here; check fraction
713 	jlss gm2
714 	bisb2 $0x10,8+4(sp)		# increment l.s. digit
715 gm2:		# end of "round here" code
716 	addp4 $8,16(sp),$17,4(sp)	# combine leading and trailing
717 	bisb2 sign,12(sp)		# and insert sign
718 	rsb
719 fzero:	clrl r0
720 	movl $1,exp		# 0.000e+00 and 0.000 rather than 0.000e-01 and .000
721 	jbr fz1
722 
723 	.align 2
724 lsb: .long 0x00010000		# lsb in the crazy floating-point format
725 
726 	# return 10^r2 as a double float in r0||r1 and 8 extra bits of precision in r4
727 	# preserve r2, r5||r6
728 expten:
729 	movd $0d1.0,r0			# begin computing 10^exp10
730 	clrl r4				# bit counter
731 	movad ten1,r3			# table address
732 	tstl r2
733 	bgeq e10lp
734 	mnegl r2,r2			# get absolute value
735 	jbss $6,r2,e10lp		# flag as negative
736 e10lp:	jbc r4,r2,el1			# want this power?
737 	muld2 (r3),r0			# yes
738 el1:	addl2 $8,r3			# advance to next power
739 	aobleq $5,r4,e10lp		# through 10^32
740 	jbcc $6,r2,el2			# correct for negative exponent
741 	divd3 r0,$0d1.0,r0		# by taking reciprocal
742 	cmpl $28,r2
743 	jneq enm28
744 	addl2 lsb,r1			# 10**-28 needs lsb incremented
745 enm28:	mnegl r2,r2			# original exponent of 10
746 el2:	addl3 $5*8,r2,r3		# negative bit positions are illegal?
747 	jbc r3,xlsbh-5,eoklsb
748 	subl2 lsb,r1			# lsb was too high
749 eoklsb:
750 	movzbl xprec[r2],r4		# 8 extra bits
751 	rsb
752 
753 	# powers of ten
754 	.align	2
755 ten1:	.word	0x4220,0,0,0
756 ten2:	.word	0x43c8,0,0,0
757 ten4:	.word	0x471c,0x4000,0,0
758 ten8:	.word	0x4dbe,0xbc20,0,0
759 ten16:	.word	0x5b0e,0x1bc9,0xbf04,0
760 ten32:	.word	0x759d,0xc5ad,0xa82b,0x70b6
761 
762 	# whether lsb is too high or not
763 	.byte 1:0,1:0,1:0,1:0,1:1,1:0,1:1,1:0	# -40 thru -33
764 	.byte 1:0,1:1,1:0,1:0,1:0,1:0,1:1,1:0	# -32 thru -25
765 	.byte 1:0,1:0,1:1,1:1,1:1,1:1,1:0,1:0	# -24 thru -17
766 	.byte 1:0,1:1,1:0,1:0,1:1,1:1,1:1,1:1	# -16 thru -9
767 	.byte 1:1,1:1,1:1,1:0,1:0,1:0,1:0,1:1	# -8  thru -1
768 xlsbh:
769 	.byte 1:0,1:0,1:0,1:0,1:0,1:0,1:0,1:0	# 0 thru 7
770 	.byte 1:0,1:0,1:0,1:0,1:0,1:0,1:0,1:0	# 8 thru 15
771 	.byte 1:0,1:0,1:0,1:0,1:0,1:0,1:0,1:0	# 16 thru 23
772 	.byte 1:0,1:1,1:1,1:0,1:1,1:1,1:1,1:1	# 24 thru 31
773 	.byte 1:1,1:1,1:1,1:1,1:1,1:1,1:1    	# 32 thru 38
774 
775 	# bytes of extra precision
776 	.byte           0x56,0x76,0xd3,0x88,0xb5,0x62	# -38 thru -33
777 	.byte 0xba,0xf5,0x32,0x3e,0x0e,0x48,0xdb,0x51	# -32 thru -25
778 	.byte 0x53,0x27,0xb1,0xef,0xeb,0xa5,0x07,0x49	# -24 thru -17
779 	.byte 0x5b,0xd9,0x0f,0x13,0xcd,0xff,0xbf,0x97	# -16 thru -9
780 	.byte 0xfd,0xbc,0xb6,0x23,0x2c,0x3b,0x0a,0xcd	# -8  thru -1
781 xprec:
782 	.byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00	# 0  thru 7
783 	.byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00	# 8  thru 15
784 	.byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00	# 16 thru 23
785 	.byte 0x00,0xa0,0xc8,0x3a,0x84,0xe4,0xdc,0x92	# 24 thru 31
786 	.byte 0x9b,0x00,0xc0,0x58,0xae,0x18,0xef     	# 32 thru 38
787