1 /*******************************************************************************
2 Copyright 2001, 2002 Georges Menie (<URL snipped>)
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU Lesser General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU Lesser General Public License for more details.
11 You should have received a copy of the GNU Lesser General Public License
12 along with this program; if not, write to the Free Software
13 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 /*******************************************************************************
17 putchar is the only external dependency for this file,
18 if you have a working putchar, just remove the following
19 define. If the function should be called something else,
20 replace outbyte(c) by your own function call.
22 // *******************************************************************************
23 // Updated by Daniel D Miller. Changes to the original Menie code are
24 // Copyright 2009-2012 Daniel D Miller
25 // All such changes are distributed under the same license as the original,
26 // as described above.
27 // 11/06/09 - adding floating-point support
28 // 03/19/10 - pad fractional portion of floating-point number with 0s
29 // 03/30/10 - document the \% bug
30 // 07/20/10 - Fix a round-off bug in floating-point conversions
31 // ( 0.99 with %.1f did not round to 1.0 )
32 // 10/25/11 - Add support for %+ format (always show + on positive numbers)
33 // 01/19/12 - fix handling of %f with no decimal; it was defaulting to 0
34 // decimal places, rather than printf's 6.
35 // *******************************************************************************
37 // If '%' is included in a format string, in the form \% with the intent
38 // of including the percent sign in the output string, this function will
39 // mis-handle the data entirely!!
40 // Essentially, it will just discard the character following the percent sign.
41 // This bug is not easy to fix in the existing code;
42 // for now, I'll just try to remember to use %% instead of \% ...
43 // *******************************************************************************
45 // lint -esym(752, debug_output)
46 // lint -esym(766, stdio.h)
48 // #define TEST_PRINTF 1
52 static uint use_leading_plus
= 0;
54 /* based on a example-code from Keil for CS G++ */
56 /* for caddr_t (typedef char * caddr_t;) */
57 #include <sys/types.h>
61 #include <sys/unistd.h>
63 #include <sys/times.h>
66 /*==============================================================================
67 * Environment variables.
68 * A pointer to a list of environment variables and their values. For a minimal
69 * environment, this empty list is adequate:
71 char *__env
[1] = { 0 };
72 char * *environ
= __env
;
74 /*==============================================================================
77 int _close(__attribute__((unused
)) int file
)
82 /*==============================================================================
83 * Transfer control to a new process.
85 int _execve(__attribute__((unused
)) char *name
, __attribute__((unused
)) char * *argv
, __attribute__((unused
)) char * *env
)
91 /*==============================================================================
92 * Exit a program without cleaning up files.
94 void _exit(__attribute__((unused
)) int code
)
96 /* Should we force a system reset? */
102 /*==============================================================================
103 * Create a new process.
111 /*==============================================================================
112 * Status of an open file.
114 int _fstat(__attribute__((unused
)) int file
, struct stat
*st
)
116 st
->st_mode
= S_IFCHR
;
120 /*==============================================================================
128 /*==============================================================================
129 * Query whether output stream is a terminal.
131 int _isatty(__attribute__((unused
)) int file
)
136 /*==============================================================================
139 int _kill(__attribute__((unused
)) int pid
, __attribute__((unused
)) int sig
)
145 /*==============================================================================
146 * Establish a new name for an existing file.
148 int _link(__attribute__((unused
)) char *old
, __attribute__((unused
)) char *new)
154 /*==============================================================================
155 * Set position in a file.
157 int _lseek(__attribute__((unused
)) int file
, __attribute__((unused
)) int ptr
, __attribute__((unused
)) int dir
)
162 /*==============================================================================
165 int _open(__attribute__((unused
)) const char *name
, __attribute__((unused
)) int flags
, __attribute__((unused
)) int mode
)
170 /*==============================================================================
173 int _read(__attribute__((unused
)) int file
, __attribute__((unused
)) char *ptr
, __attribute__((unused
)) int len
)
178 /*==============================================================================
179 * Write to a file. libc subroutines will use this system routine for output to
180 * all files, including stdout�so if you need to generate any output, for
181 * example to a serial port for debugging, you should make your minimal write
182 * capable of doing this.
184 int _write_r(__attribute__((unused
)) void *reent
, __attribute__((unused
)) int file
, __attribute__((unused
)) char *ptr
, __attribute__((unused
)) int len
)
189 /*==============================================================================
190 * Increase program data space. As malloc and related functions depend on this,
191 * it is useful to have a working implementation. The following suffices for a
192 * standalone system; it exploits the symbol _end automatically defined by the
195 caddr_t
_sbrk(int incr
)
197 extern char _end
; /* Defined by the linker */
198 static char *heap_end
;
206 prev_heap_end
= heap_end
;
207 asm volatile ("MRS %0, msp" : "=r" (stack_ptr
));
208 if (heap_end
+ incr
> stack_ptr
) {
209 _write_r((void *)0, 1, "Heap and stack collision\n", 25);
214 return (caddr_t
)prev_heap_end
;
217 /*==============================================================================
218 * Status of a file (by name).
220 int _stat(__attribute__((unused
)) char *file
, struct stat
*st
)
222 st
->st_mode
= S_IFCHR
;
226 /*==============================================================================
227 * Timing information for current process.
229 int _times(__attribute__((unused
)) struct tms
*buf
)
234 /*==============================================================================
235 * Remove a file's directory entry.
237 int _unlink(__attribute__((unused
)) char *name
)
243 /*==============================================================================
244 * Wait for a child process.
246 int _wait(__attribute__((unused
)) int *status
)
251 // * NEWLIB STUBS *//
254 // ****************************************************************************
255 static void printchar(char * *str
, int c
)
263 extern int putchar(int c
);
269 // ****************************************************************************
270 static uint
my_strlen(char *str
)
283 // ****************************************************************************
284 // This version returns the length of the output string.
285 // It is more useful when implementing a walking-string function.
286 // ****************************************************************************
287 static const double round_nums
[8] = {
298 static unsigned dbl2stri(char *outbfr
, double dbl
, unsigned dec_digits
)
300 static char local_bfr
[128];
301 char *output
= (outbfr
== 0) ? local_bfr
: outbfr
;
303 // *******************************************
304 // extract negative info
305 // *******************************************
310 if (use_leading_plus
) {
315 // handling rounding by adding .5LSB to the floating-point data
316 if (dec_digits
< 8) {
317 dbl
+= round_nums
[dec_digits
];
320 // **************************************************************************
321 // construct fractional multiplier for specified number of digits.
322 // **************************************************************************
325 for (idx
= 0; idx
< dec_digits
; idx
++) {
329 // printf("mult=%u\n", mult) ;
330 uint wholeNum
= (uint
)dbl
;
331 uint decimalNum
= (uint
)((dbl
- wholeNum
) * mult
);
333 // *******************************************
334 // convert integer portion
335 // *******************************************
338 while (wholeNum
!= 0) {
339 tbfr
[idx
++] = '0' + (wholeNum
% 10);
342 // printf("%.3f: whole=%s, dec=%d\n", dbl, tbfr, decimalNum) ;
347 *output
++ = tbfr
[idx
- 1]; // lint !e771
351 if (dec_digits
> 0) {
354 // *******************************************
355 // convert fractional portion
356 // *******************************************
358 while (decimalNum
!= 0) {
359 tbfr
[idx
++] = '0' + (decimalNum
% 10);
362 // pad the decimal portion with 0s as necessary;
363 // We wouldn't want to report 3.093 as 3.93, would we??
364 while (idx
< dec_digits
) {
367 // printf("decimal=%s\n", tbfr) ;
372 *output
++ = tbfr
[idx
- 1];
380 output
= (outbfr
== 0) ? local_bfr
: outbfr
;
381 return my_strlen(output
);
384 // ****************************************************************************
388 static int prints(char * *out
, const char *string
, int width
, int pad
)
390 register int pc
= 0, padchar
= ' ';
395 for (ptr
= string
; *ptr
; ++ptr
) {
403 if (pad
& PAD_ZERO
) {
407 if (!(pad
& PAD_RIGHT
)) {
408 for (; width
> 0; --width
) {
409 printchar(out
, padchar
);
413 for (; *string
; ++string
) {
414 printchar(out
, *string
);
417 for (; width
> 0; --width
) {
418 printchar(out
, padchar
);
424 // ****************************************************************************
425 /* the following should be enough for 32 bit int */
426 #define PRINT_BUF_LEN 12
427 static int printi(char * *out
, int i
, int b
, int sg
, int width
, int pad
, int letbase
)
429 char print_buf
[PRINT_BUF_LEN
];
431 int t
, neg
= 0, pc
= 0;
432 unsigned u
= (unsigned)i
;
437 return prints(out
, print_buf
, width
, pad
);
439 if (sg
&& b
== 10 && i
< 0) {
443 // make sure print_buf is NULL-term
444 s
= print_buf
+ PRINT_BUF_LEN
- 1;
449 t
= u
% b
; // lint !e573 Warning 573: Signed-unsigned mix with divide
451 t
+= letbase
- '0' - 10;
454 u
/= b
; // lint !e573 Warning 573: Signed-unsigned mix with divide
457 if (width
&& (pad
& PAD_ZERO
)) {
465 if (use_leading_plus
) {
469 return pc
+ prints(out
, s
, width
, pad
);
472 // ****************************************************************************
473 static int print(char * *out
, int *varg
)
477 unsigned dec_width
= 6;
479 char *format
= (char *)(*varg
++);
482 use_leading_plus
= 0; // start out with this clear
483 for (; *format
!= 0; ++format
) {
484 if (*format
== '%') {
488 if (*format
== '\0') {
491 if (*format
== '%') {
494 if (*format
== '-') {
498 if (*format
== '+') {
500 use_leading_plus
= 1;
502 while (*format
== '0') {
507 if (*format
== '.' ||
508 (*format
>= '0' && *format
<= '9')) {
510 if (*format
== '.') {
514 } else if ((*format
>= '0' && *format
<= '9')) {
517 dec_width
+= *format
- '0';
520 width
+= *format
- '0';
528 if (*format
== 'l') {
534 // char *s = *((char **) varg++); //lint !e740
535 char *s
= (char *)*varg
++; // lint !e740 !e826 convert to double pointer
536 pc
+= prints(out
, s
? s
: "(null)", width
, pad
);
537 use_leading_plus
= 0; // reset this flag after printing one value
541 pc
+= printi(out
, *varg
++, 10, 1, width
, pad
, 'a');
542 use_leading_plus
= 0; // reset this flag after printing one value
545 pc
+= printi(out
, *varg
++, 16, 0, width
, pad
, 'a');
546 use_leading_plus
= 0; // reset this flag after printing one value
549 pc
+= printi(out
, *varg
++, 16, 0, width
, pad
, 'A');
550 use_leading_plus
= 0; // reset this flag after printing one value
553 pc
+= printi(out
, *varg
++, 10, 0, width
, pad
, 'a');
554 use_leading_plus
= 0; // reset this flag after printing one value
557 /* char are converted to int then pushed on the stack */
560 pc
+= prints(out
, scr
, width
, pad
);
561 use_leading_plus
= 0; // reset this flag after printing one value
566 // http://wiki.debian.org/ArmEabiPort#Structpackingandalignment
569 // The ARM EABI requires 8-byte stack alignment at public function entry points,
570 // compared to the previous 4-byte alignment.
572 char *cptr
= (char *)varg
; // lint !e740 !e826 convert to double pointer
573 uint caddr
= (uint
)cptr
;
574 if ((caddr
& 0xF) != 0) {
577 double *dblptr
= (double *)cptr
; // lint !e740 !e826 convert to double pointer
579 double *dblptr
= (double *)varg
; // lint !e740 !e826 convert to double pointer
581 double dbl
= *dblptr
++; // increment double pointer
582 varg
= (int *)dblptr
; // lint !e740 copy updated pointer back to base pointer
585 dbl2stri(bfr
, dbl
, dec_width
);
586 // stuff_talkf("[%s], width=%u, dec_width=%u\n", bfr, width, dec_width) ;
587 pc
+= prints(out
, bfr
, width
, pad
);
588 use_leading_plus
= 0; // reset this flag after printing one value
594 printchar(out
, *format
);
595 use_leading_plus
= 0; // reset this flag after printing one value
599 // if (*format == '\\') {
603 printchar(out
, *format
);
606 } // for each char in format string
607 if (out
) { // lint !e850
613 // ****************************************************************************
614 int stringf(char *out
, const char *format
, ...)
616 // create a pointer into the stack.
617 // Thematically this should be a void*, since the actual usage of the
618 // pointer will vary. However, int* works fine too.
619 // Either way, the called function will need to re-cast the pointer
620 // for any type which isn't sizeof(int)
621 int *varg
= (int *)(char *)(&format
);
623 return print(&out
, varg
);
626 int printf(const char *format
, ...)
628 int *varg
= (int *)(char *)(&format
);
630 return print(0, varg
);
633 int sprintf(char *out
, const char *format
, ...)
635 int *varg
= (int *)(char *)(&format
);
637 return print(&out
, varg
);