1 /* Fast printf routine for use with sdcc/mcs51
2 * Copyright (c) 2001, Paul Stoffregen, paul@pjrc.com
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // include support for 32 bit base 10 integers (%ld and %lu)
23 // include support for minimum field widths (%8d, %20s)
27 /* extern void putchar(char ); */
30 static bit long_flag, short_flag, print_zero_flag, negative_flag;
33 static bit field_width_flag;
34 static bit leading_zero_flag;
35 static data unsigned char field_width;
39 void printf_fast(code char *fmt, ...) reentrant
41 fmt; /* supress unreferenced variable warning */
46 mov a, _bp // r0 will point to va_args (stack)
48 mov r0, a // r0 points to MSB of fmt
51 mov dpl, @r0 // dptr has address of fmt
56 movc a, @a+dptr // get next byte of fmt string
58 //cjne a, #'%', printf_normal
59 cjne a, #37, printf_normal
68 clr _leading_zero_flag
74 movc a, @a+dptr // get next byte of data format
77 /* parse and consume the field width digits, even if */
78 /* we don't build the code to make use of them */
86 cjne a, _field_width, printf_digit_2
87 setb _leading_zero_flag
89 setb _field_width_flag
97 sjmp printf_format_loop
105 //cjne a, #'l', printf_format_h
106 cjne a, #108, printf_format_h
108 sjmp printf_format_loop
111 //cjne a, #'h', printf_format_s
112 cjne a, #104, printf_format_s
114 sjmp printf_format_loop
117 //cjne a, #'s', printf_format_d
118 cjne a, #115, printf_format_d
122 //cjne a, #'d', printf_format_u
123 cjne a, #100, printf_format_u
128 //cjne a, #'u', printf_format_c
129 cjne a, #117, printf_format_c
134 //cjne a, #'c', printf_format_x
135 cjne a, #99, printf_format_x
136 mov a, @r0 // Acc has the character to print
141 //cjne a, #'x', printf_normal
142 cjne a, #120, printf_normal
149 sjmp printf_main_loop
157 /* print a string... just grab each byte with __gptrget */
158 /* the user much pass a 24 bit generic pointer */
161 push dph // save addr in fmt onto stack
163 mov b, @r0 // b has type of address (generic *)
167 mov dpl, @r0 // dptr has address of user's string
171 jnb _field_width_flag, printf_str_loop
172 clr _leading_zero_flag // never leading zeros for strings
181 jnz printf_str_fw_loop
186 #endif // FIELD_WIDTH
195 pop dpl // restore addr withing fmt
197 ljmp printf_main_loop
206 /* printing in hex is easy because sdcc pushes the LSB first */
210 jb _short_flag, printf_hex_end
212 jnb _long_flag, printf_hex_end
217 ljmp printf_main_loop
220 lcall printf_phex_msn
241 /* printing an integer is not so easy. For a signed int */
242 /* check if it is negative and print the minus sign and */
243 /* invert it to a positive integer */
247 jnb acc.7, printf_uint /* check if negative */
249 mov a, r1 /* invert integer */
253 jb _short_flag, printf_uint
258 jnb _long_flag, printf_uint
269 /* printing integers is a lot of work... because it takes so */
270 /* long, the first thing to do is make sure we're doing as */
271 /* little work as possible, then convert the binary int to */
272 /* packed BCD, and finally print each digit of the BCD number */
276 jb _short_flag, printf_uint_ck8
277 jnb _long_flag, printf_uint_ck16
279 /* it's a 32 bit int... but if the upper 16 bits are zero */
280 /* we can treat it like a 16 bit integer and convert much faster */
283 jnz printf_uint_begin
285 jnz printf_uint_begin
288 jnz printf_ld_in_hex ;print long integer as hex
289 mov a, r4 ;rather than just the low 16 bits
294 /* it's a 16 bit int... but if the upper 8 bits are zero */
295 /* we can treat it like a 8 bit integer and convert much faster */
297 jnz printf_uint_begin
300 /* it's an 8 bit int... if it's zero, it's a lot faster to just */
301 /* print the digit zero and skip all the hard work! */
303 jnz printf_uint_begin
305 jnb _field_width_flag, printf_uint_zero
313 ljmp printf_main_loop
319 lcall printf_int2bcd // bcd number in r3/r2/r7/r6/r5
322 jnb _field_width_flag, printf_uifw_end
326 jnb _long_flag, printf_uifw_16
349 jb _short_flag, printf_uifw_8
368 ;r1 has the number of digits for the number
370 mov c, _negative_flag
395 jnb _negative_flag, printf_uint_pos
400 jb _short_flag, printf_uint8
402 jnb _long_flag, printf_uint16
410 lcall printf_phex_msn
412 lcall printf_phex_lsn
414 lcall printf_phex_msn
416 lcall printf_phex_lsn
419 lcall printf_phex_msn
431 lcall printf_phex_lsn
433 lcall printf_phex_msn
441 lcall printf_phex_lsn
443 lcall printf_phex_msn
445 lcall printf_phex_lsn
449 ljmp printf_main_loop
461 /* read an integer into r1/r2/r3/r4, and msb into r5 */
467 jb _short_flag, printf_get_done
472 jnb _long_flag, printf_get_done
490 /* convert binary number in r4/r3/r2/r1 into bcd packed number
491 * in r3/r2/r7/r6/r5. The input number is destroyed in the
492 * process, to avoid needing extra memory for the result (and
493 * r1 gets used for temporary storage). dptr is overwritten,
494 * but r0 is not changed.
500 mov dptr, #_int2bcd_0
507 mov r1, a // recycle r1 for holding nibble
508 mov dptr, #_int2bcd_1
520 jnb _short_flag, printf_i2bcd_16 // if 8 bit int, we're done
527 mov dptr, #_int2bcd_2
543 mov dptr, #_int2bcd_3
561 jb _long_flag, printf_i2bcd_32 // if 16 bit int, we're done
570 mov dptr, #_int2bcd_4
595 mov dptr, #_int2bcd_5
622 mov dptr, #_int2bcd_6
624 lcall printf_bcd_add10 // saves 27 bytes, costs 5 cycles
630 mov dptr, #_int2bcd_7
670 jnb _leading_zero_flag, printf_space_output
678 jnz printf_space_loop
686 /* print a hex digit, either upper 4 bit (msn) or lower 4 bits (lsn) */
693 jnb _print_zero_flag, printf_ret
695 setb _print_zero_flag
712 /* print a zero if all the calls to print the digits ended up */
713 /* being leading zeros */
716 jb _print_zero_flag, printf_ret
728 * for ($d=0; $d < 8; $d++) {
730 * for ($p=0; $p < 5; $p++) {
731 * last unless (((16 ** $d) * 15) / (10 ** ($p * 2))) % 100;
732 * printf "code unsigned char int2bcd_%d_%d[15] = {", $d, $p;
733 * for ($i=0; $i < 16; $i++) {
735 * (((16 ** $d) * $i) / (10 ** ($p * 2))) % 100;
736 * print ", " if $i < 15;
744 code unsigned char int2bcd_0[] = {
745 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
746 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15};
748 code unsigned char int2bcd_1[] = {
749 0x00, 0x16, 0x32, 0x48, 0x64, 0x80, 0x96, 0x12,
750 0x28, 0x44, 0x60, 0x76, 0x92, 0x08, 0x24, 0x40,
751 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
752 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02};
754 code unsigned char int2bcd_2[] = {
755 0x00, 0x56, 0x12, 0x68, 0x24, 0x80, 0x36, 0x92,
756 0x48, 0x04, 0x60, 0x16, 0x72, 0x28, 0x84, 0x40,
757 0x00, 0x02, 0x05, 0x07, 0x10, 0x12, 0x15, 0x17,
758 0x20, 0x23, 0x25, 0x28, 0x30, 0x33, 0x35, 0x38};
760 code unsigned char int2bcd_3[] = {
761 0x00, 0x96, 0x92, 0x88, 0x84, 0x80, 0x76, 0x72,
762 0x68, 0x64, 0x60, 0x56, 0x52, 0x48, 0x44, 0x40,
763 0x00, 0x40, 0x81, 0x22, 0x63, 0x04, 0x45, 0x86,
764 0x27, 0x68, 0x09, 0x50, 0x91, 0x32, 0x73, 0x14,
765 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02,
766 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x06};
769 code unsigned char int2bcd_4[] = {
770 0x00, 0x36, 0x72, 0x08, 0x44, 0x80, 0x16, 0x52,
771 0x88, 0x24, 0x60, 0x96, 0x32, 0x68, 0x04, 0x40,
772 0x00, 0x55, 0x10, 0x66, 0x21, 0x76, 0x32, 0x87,
773 0x42, 0x98, 0x53, 0x08, 0x64, 0x19, 0x75, 0x30,
774 0x00, 0x06, 0x13, 0x19, 0x26, 0x32, 0x39, 0x45,
775 0x52, 0x58, 0x65, 0x72, 0x78, 0x85, 0x91, 0x98};
777 code unsigned char int2bcd_5[] = {
778 0x00, 0x76, 0x52, 0x28, 0x04, 0x80, 0x56, 0x32,
779 0x08, 0x84, 0x60, 0x36, 0x12, 0x88, 0x64, 0x40,
780 0x00, 0x85, 0x71, 0x57, 0x43, 0x28, 0x14, 0x00,
781 0x86, 0x71, 0x57, 0x43, 0x29, 0x14, 0x00, 0x86,
782 0x00, 0x04, 0x09, 0x14, 0x19, 0x24, 0x29, 0x34,
783 0x38, 0x43, 0x48, 0x53, 0x58, 0x63, 0x68, 0x72,
784 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
785 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15};
787 code unsigned char int2bcd_6[] = {
788 0x00, 0x16, 0x32, 0x48, 0x64, 0x80, 0x96, 0x12,
789 0x28, 0x44, 0x60, 0x76, 0x92, 0x08, 0x24, 0x40,
790 0x00, 0x72, 0x44, 0x16, 0x88, 0x60, 0x32, 0x05,
791 0x77, 0x49, 0x21, 0x93, 0x65, 0x38, 0x10, 0x82,
792 0x00, 0x77, 0x55, 0x33, 0x10, 0x88, 0x66, 0x44,
793 0x21, 0x99, 0x77, 0x54, 0x32, 0x10, 0x88, 0x65,
794 0x00, 0x16, 0x33, 0x50, 0x67, 0x83, 0x00, 0x17,
795 0x34, 0x50, 0x67, 0x84, 0x01, 0x18, 0x34, 0x51,
796 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
797 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02};
799 code unsigned char int2bcd_7[] = {
800 0x00, 0x56, 0x12, 0x68, 0x24, 0x80, 0x36, 0x92,
801 0x48, 0x04, 0x60, 0x16, 0x72, 0x28, 0x84, 0x40,
802 0x00, 0x54, 0x09, 0x63, 0x18, 0x72, 0x27, 0x81,
803 0x36, 0x91, 0x45, 0x00, 0x54, 0x09, 0x63, 0x18,
804 0x00, 0x43, 0x87, 0x30, 0x74, 0x17, 0x61, 0x04,
805 0x48, 0x91, 0x35, 0x79, 0x22, 0x66, 0x09, 0x53,
806 0x00, 0x68, 0x36, 0x05, 0x73, 0x42, 0x10, 0x79,
807 0x47, 0x15, 0x84, 0x52, 0x21, 0x89, 0x58, 0x26,
808 0x00, 0x02, 0x05, 0x08, 0x10, 0x13, 0x16, 0x18,
809 0x21, 0x24, 0x26, 0x29, 0x32, 0x34, 0x37, 0x40};