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 data unsigned char field_width;
38 void printf_fast(code char *fmt, ...) reentrant
40 fmt; /* supress unreferenced variable warning */
45 mov a, _bp // r0 will point to va_args (stack)
47 mov r0, a // r0 points to MSB of fmt
50 mov dpl, @r0 // dptr has address of fmt
55 movc a, @a+dptr // get next byte of fmt string
57 //cjne a, #'%', printf_normal
58 cjne a, #37, printf_normal
72 movc a, @a+dptr // get next byte of data format
75 /* parse and consume the field width digits, even if */
76 /* we don't build the code to make use of them */
83 setb _field_width_flag
91 sjmp printf_format_loop
99 //cjne a, #'l', printf_format_h
100 cjne a, #108, printf_format_h
102 sjmp printf_format_loop
105 //cjne a, #'h', printf_format_s
106 cjne a, #104, printf_format_s
108 sjmp printf_format_loop
111 //cjne a, #'s', printf_format_d
112 cjne a, #115, printf_format_d
116 //cjne a, #'d', printf_format_u
117 cjne a, #100, printf_format_u
122 //cjne a, #'u', printf_format_c
123 cjne a, #117, printf_format_c
128 //cjne a, #'c', printf_format_x
129 cjne a, #99, printf_format_x
130 mov a, @r0 // Acc has the character to print
135 //cjne a, #'x', printf_normal
136 cjne a, #120, printf_normal
143 sjmp printf_main_loop
151 /* print a string... just grab each byte with __gptrget */
152 /* the user much pass a 24 bit generic pointer */
155 push dph // save addr in fmt onto stack
157 mov b, @r0 // b has type of address (generic *)
161 mov dpl, @r0 // dptr has address of user's string
165 jnb _field_width_flag, printf_str_loop
174 jnz printf_str_fw_loop
179 #endif // FIELD_WIDTH
188 pop dpl // restore addr withing fmt
190 ljmp printf_main_loop
199 /* printing in hex is easy because sdcc pushes the LSB first */
203 jb _short_flag, printf_hex_end
205 jnb _long_flag, printf_hex_end
210 ljmp printf_main_loop
213 lcall printf_phex_msn
234 /* printing an integer is not so easy. For a signed int */
235 /* check if it is negative and print the minus sign and */
236 /* invert it to a positive integer */
240 jnb acc.7, printf_uint /* check if negative */
242 mov a, r1 /* invert integer */
246 jb _short_flag, printf_uint
251 jnb _long_flag, printf_uint
262 /* printing integers is a lot of work... because it takes so */
263 /* long, the first thing to do is make sure we're doing as */
264 /* little work as possible, then convert the binary int to */
265 /* packed BCD, and finally print each digit of the BCD number */
269 jb _short_flag, printf_uint_ck8
270 jnb _long_flag, printf_uint_ck16
272 /* it's a 32 bit int... but if the upper 16 bits are zero */
273 /* we can treat it like a 16 bit integer and convert much faster */
276 jnz printf_uint_begin
278 jnz printf_uint_begin
281 jnz printf_ld_in_hex ;print long integer as hex
282 mov a, r4 ;rather than just the low 16 bits
287 /* it's a 16 bit int... but if the upper 8 bits are zero */
288 /* we can treat it like a 8 bit integer and convert much faster */
290 jnz printf_uint_begin
293 /* it's an 8 bit int... if it's zero, it's a lot faster to just */
294 /* print the digit zero and skip all the hard work! */
296 jnz printf_uint_begin
298 jnb _field_width_flag, printf_uint_zero
306 ljmp printf_main_loop
312 lcall printf_int2bcd // bcd number in r3/r2/r7/r6/r5
315 jnb _field_width_flag, printf_uifw_end
319 jnb _long_flag, printf_uifw_16
342 jb _short_flag, printf_uifw_8
361 ;r1 has the number of digits for the number
363 mov c, _negative_flag
388 jnb _negative_flag, printf_uint_pos
393 jb _short_flag, printf_uint8
395 jnb _long_flag, printf_uint16
403 lcall printf_phex_msn
405 lcall printf_phex_lsn
407 lcall printf_phex_msn
409 lcall printf_phex_lsn
412 lcall printf_phex_msn
424 lcall printf_phex_lsn
426 lcall printf_phex_msn
434 lcall printf_phex_lsn
436 lcall printf_phex_msn
438 lcall printf_phex_lsn
442 ljmp printf_main_loop
454 /* read an integer into r1/r2/r3/r4, and msb into r5 */
460 jb _short_flag, printf_get_done
465 jnb _long_flag, printf_get_done
483 /* convert binary number in r4/r3/r2/r1 into bcd packed number
484 * in r3/r2/r7/r6/r5. The input number is destroyed in the
485 * process, to avoid needing extra memory for the result (and
486 * r1 gets used for temporary storage). dptr is overwritten,
487 * but r0 is not changed.
493 mov dptr, #_int2bcd_0
500 mov r1, a // recycle r1 for holding nibble
501 mov dptr, #_int2bcd_1
513 jnb _short_flag, printf_i2bcd_16 // if 8 bit int, we're done
520 mov dptr, #_int2bcd_2
536 mov dptr, #_int2bcd_3
554 jb _long_flag, printf_i2bcd_32 // if 16 bit int, we're done
563 mov dptr, #_int2bcd_4
588 mov dptr, #_int2bcd_5
615 mov dptr, #_int2bcd_6
617 lcall printf_bcd_add10 // saves 27 bytes, costs 5 cycles
623 mov dptr, #_int2bcd_7
667 jnz printf_space_loop
675 /* print a hex digit, either upper 4 bit (msn) or lower 4 bits (lsn) */
682 jnb _print_zero_flag, printf_ret
684 setb _print_zero_flag
701 /* print a zero if all the calls to print the digits ended up */
702 /* being leading zeros */
705 jb _print_zero_flag, printf_ret
717 * for ($d=0; $d < 8; $d++) {
719 * for ($p=0; $p < 5; $p++) {
720 * last unless (((16 ** $d) * 15) / (10 ** ($p * 2))) % 100;
721 * printf "code unsigned char int2bcd_%d_%d[15] = {", $d, $p;
722 * for ($i=0; $i < 16; $i++) {
724 * (((16 ** $d) * $i) / (10 ** ($p * 2))) % 100;
725 * print ", " if $i < 15;
733 code unsigned char int2bcd_0[] = {
734 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
735 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15};
737 code unsigned char int2bcd_1[] = {
738 0x00, 0x16, 0x32, 0x48, 0x64, 0x80, 0x96, 0x12,
739 0x28, 0x44, 0x60, 0x76, 0x92, 0x08, 0x24, 0x40,
740 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
741 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02};
743 code unsigned char int2bcd_2[] = {
744 0x00, 0x56, 0x12, 0x68, 0x24, 0x80, 0x36, 0x92,
745 0x48, 0x04, 0x60, 0x16, 0x72, 0x28, 0x84, 0x40,
746 0x00, 0x02, 0x05, 0x07, 0x10, 0x12, 0x15, 0x17,
747 0x20, 0x23, 0x25, 0x28, 0x30, 0x33, 0x35, 0x38};
749 code unsigned char int2bcd_3[] = {
750 0x00, 0x96, 0x92, 0x88, 0x84, 0x80, 0x76, 0x72,
751 0x68, 0x64, 0x60, 0x56, 0x52, 0x48, 0x44, 0x40,
752 0x00, 0x40, 0x81, 0x22, 0x63, 0x04, 0x45, 0x86,
753 0x27, 0x68, 0x09, 0x50, 0x91, 0x32, 0x73, 0x14,
754 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02,
755 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x06};
758 code unsigned char int2bcd_4[] = {
759 0x00, 0x36, 0x72, 0x08, 0x44, 0x80, 0x16, 0x52,
760 0x88, 0x24, 0x60, 0x96, 0x32, 0x68, 0x04, 0x40,
761 0x00, 0x55, 0x10, 0x66, 0x21, 0x76, 0x32, 0x87,
762 0x42, 0x98, 0x53, 0x08, 0x64, 0x19, 0x75, 0x30,
763 0x00, 0x06, 0x13, 0x19, 0x26, 0x32, 0x39, 0x45,
764 0x52, 0x58, 0x65, 0x72, 0x78, 0x85, 0x91, 0x98};
766 code unsigned char int2bcd_5[] = {
767 0x00, 0x76, 0x52, 0x28, 0x04, 0x80, 0x56, 0x32,
768 0x08, 0x84, 0x60, 0x36, 0x12, 0x88, 0x64, 0x40,
769 0x00, 0x85, 0x71, 0x57, 0x43, 0x28, 0x14, 0x00,
770 0x86, 0x71, 0x57, 0x43, 0x29, 0x14, 0x00, 0x86,
771 0x00, 0x04, 0x09, 0x14, 0x19, 0x24, 0x29, 0x34,
772 0x38, 0x43, 0x48, 0x53, 0x58, 0x63, 0x68, 0x72,
773 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
774 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15};
776 code unsigned char int2bcd_6[] = {
777 0x00, 0x16, 0x32, 0x48, 0x64, 0x80, 0x96, 0x12,
778 0x28, 0x44, 0x60, 0x76, 0x92, 0x08, 0x24, 0x40,
779 0x00, 0x72, 0x44, 0x16, 0x88, 0x60, 0x32, 0x05,
780 0x77, 0x49, 0x21, 0x93, 0x65, 0x38, 0x10, 0x82,
781 0x00, 0x77, 0x55, 0x33, 0x10, 0x88, 0x66, 0x44,
782 0x21, 0x99, 0x77, 0x54, 0x32, 0x10, 0x88, 0x65,
783 0x00, 0x16, 0x33, 0x50, 0x67, 0x83, 0x00, 0x17,
784 0x34, 0x50, 0x67, 0x84, 0x01, 0x18, 0x34, 0x51,
785 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
786 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02};
788 code unsigned char int2bcd_7[] = {
789 0x00, 0x56, 0x12, 0x68, 0x24, 0x80, 0x36, 0x92,
790 0x48, 0x04, 0x60, 0x16, 0x72, 0x28, 0x84, 0x40,
791 0x00, 0x54, 0x09, 0x63, 0x18, 0x72, 0x27, 0x81,
792 0x36, 0x91, 0x45, 0x00, 0x54, 0x09, 0x63, 0x18,
793 0x00, 0x43, 0x87, 0x30, 0x74, 0x17, 0x61, 0x04,
794 0x48, 0x91, 0x35, 0x79, 0x22, 0x66, 0x09, 0x53,
795 0x00, 0x68, 0x36, 0x05, 0x73, 0x42, 0x10, 0x79,
796 0x47, 0x15, 0x84, 0x52, 0x21, 0x89, 0x58, 0x26,
797 0x00, 0x02, 0x05, 0x08, 0x10, 0x13, 0x16, 0x18,
798 0x21, 0x24, 0x26, 0x29, 0x32, 0x34, 0x37, 0x40};