/* Fast printf routine for use with sdcc/mcs51 * Copyright (c) 2001, Paul Stoffregen, paul@pjrc.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // include support for 32 bit base 10 integers (%ld and %lu) #define LONG_INT // include support for minimum field widths (%8d, %20s) #define FIELD_WIDTH /* extern void putchar(char ); */ static bit long_flag, short_flag, print_zero_flag, negative_flag; #ifdef FIELD_WIDTH static bit field_width_flag; static data unsigned char field_width; #endif void printf_fast(code char *fmt, ...) reentrant { fmt; /* supress unreferenced variable warning */ _asm printf_begin: mov a, _bp // r0 will point to va_args (stack) add a, #253 mov r0, a // r0 points to MSB of fmt mov dph, @r0 dec r0 mov dpl, @r0 // dptr has address of fmt dec r0 printf_main_loop: clr a movc a, @a+dptr // get next byte of fmt string inc dptr //cjne a, #'%', printf_normal cjne a, #37, printf_normal printf_format: clr _long_flag clr _short_flag clr _print_zero_flag clr _negative_flag #ifdef FIELD_WIDTH clr _field_width_flag mov _field_width, #0 #endif printf_format_loop: clr a movc a, @a+dptr // get next byte of data format inc dptr /* parse and consume the field width digits, even if */ /* we don't build the code to make use of them */ add a, #198 jc printf_nondigit1 add a, #10 jnc printf_nondigit2 #ifdef FIELD_WIDTH printf_digit: setb _field_width_flag mov r1, a mov a, _field_width mov b, #10 mul ab add a, r1 mov _field_width, a #endif sjmp printf_format_loop printf_nondigit1: add a, #10 printf_nondigit2: add a, #48 printf_format_l: //cjne a, #'l', printf_format_h cjne a, #108, printf_format_h setb _long_flag sjmp printf_format_loop printf_format_h: //cjne a, #'h', printf_format_s cjne a, #104, printf_format_s setb _short_flag sjmp printf_format_loop printf_format_s: //cjne a, #'s', printf_format_d cjne a, #115, printf_format_d ljmp printf_string printf_format_d: //cjne a, #'d', printf_format_u cjne a, #100, printf_format_u lcall printf_get_int ljmp printf_int printf_format_u: //cjne a, #'u', printf_format_c cjne a, #117, printf_format_c lcall printf_get_int ljmp printf_uint printf_format_c: //cjne a, #'c', printf_format_x cjne a, #99, printf_format_x mov a, @r0 // Acc has the character to print dec r0 sjmp printf_char printf_format_x: //cjne a, #'x', printf_normal cjne a, #120, printf_normal ljmp printf_hex printf_normal: jz printf_eot printf_char: lcall printf_putchar sjmp printf_main_loop printf_eot: ljmp printf_end /* print a string... just grab each byte with __gptrget */ /* the user much pass a 24 bit generic pointer */ printf_string: push dph // save addr in fmt onto stack push dpl mov b, @r0 // b has type of address (generic *) dec r0 mov dph, @r0 dec r0 mov dpl, @r0 // dptr has address of user's string dec r0 #ifdef FIELD_WIDTH jnb _field_width_flag, printf_str_loop push dpl push dph printf_str_fw_loop: lcall __gptrget jz printf_str_space inc dptr dec _field_width mov a, _field_width jnz printf_str_fw_loop printf_str_space: lcall printf_space pop dph pop dpl #endif // FIELD_WIDTH printf_str_loop: lcall __gptrget jz printf_str_done inc dptr lcall printf_putchar sjmp printf_str_loop printf_str_done: pop dpl // restore addr withing fmt pop dph ljmp printf_main_loop /* printing in hex is easy because sdcc pushes the LSB first */ printf_hex: lcall printf_hex8 jb _short_flag, printf_hex_end lcall printf_hex8 jnb _long_flag, printf_hex_end lcall printf_hex8 lcall printf_hex8 printf_hex_end: lcall printf_zero ljmp printf_main_loop printf_hex8: mov a, @r0 lcall printf_phex_msn mov a, @r0 dec r0 ljmp printf_phex_lsn #ifndef LONG_INT printf_ld_in_hex: //mov a, #'0' mov a, #48 lcall printf_putchar //mov a, #'x' mov a, #120 lcall printf_putchar mov a, r0 add a, #4 mov r0, a sjmp printf_hex #endif /* printing an integer is not so easy. For a signed int */ /* check if it is negative and print the minus sign and */ /* invert it to a positive integer */ printf_int: mov a, r5 jnb acc.7, printf_uint /* check if negative */ setb _negative_flag mov a, r1 /* invert integer */ cpl a addc a, #1 mov r1, a jb _short_flag, printf_uint mov a, r2 cpl a addc a, #0 mov r2, a jnb _long_flag, printf_uint mov a, r3 cpl a addc a, #0 mov r3, a mov a, r4 cpl a addc a, #0 mov r4, a /* printing integers is a lot of work... because it takes so */ /* long, the first thing to do is make sure we're doing as */ /* little work as possible, then convert the binary int to */ /* packed BCD, and finally print each digit of the BCD number */ printf_uint: jb _short_flag, printf_uint_ck8 jnb _long_flag, printf_uint_ck16 printf_uint_ck32: /* it's a 32 bit int... but if the upper 16 bits are zero */ /* we can treat it like a 16 bit integer and convert much faster */ #ifdef LONG_INT mov a, r3 jnz printf_uint_begin mov a, r4 jnz printf_uint_begin #else mov a, r3 jnz printf_ld_in_hex ;print long integer as hex mov a, r4 ;rather than just the low 16 bits jnz printf_ld_in_hex #endif clr _long_flag printf_uint_ck16: /* it's a 16 bit int... but if the upper 8 bits are zero */ /* we can treat it like a 8 bit integer and convert much faster */ mov a, r2 jnz printf_uint_begin setb _short_flag printf_uint_ck8: /* it's an 8 bit int... if it's zero, it's a lot faster to just */ /* print the digit zero and skip all the hard work! */ mov a, r1 jnz printf_uint_begin #ifdef FIELD_WIDTH jnb _field_width_flag, printf_uint_zero dec _field_width lcall printf_space #endif printf_uint_zero: //mov a, #'0' mov a, #48 lcall printf_putchar ljmp printf_main_loop printf_uint_begin: push dpl push dph lcall printf_int2bcd // bcd number in r3/r2/r7/r6/r5 #ifdef FIELD_WIDTH jnb _field_width_flag, printf_uifw_end #ifdef LONG_INT printf_uifw_32: mov r1, #10 jnb _long_flag, printf_uifw_16 mov a, r3 anl a, #0xF0 jnz printf_uifw_sub dec r1 mov a, r3 anl a, #0x0F jnz printf_uifw_sub dec r1 mov a, r2 anl a, #0xF0 jnz printf_uifw_sub dec r1 mov a, r2 anl a, #0x0F jnz printf_uifw_sub dec r1 mov a, r7 anl a, #0xF0 jnz printf_uifw_sub #endif printf_uifw_16: mov r1, #5 jb _short_flag, printf_uifw_8 mov a, r7 anl a, #0x0F jnz printf_uifw_sub dec r1 mov a, r6 anl a, #0xF0 jnz printf_uifw_sub printf_uifw_8: mov r1, #3 mov a, r6 anl a, #0x0F jnz printf_uifw_sub dec r1 mov a, r5 anl a, #0xF0 jnz printf_uifw_sub dec r1 printf_uifw_sub: ;r1 has the number of digits for the number mov a, _field_width mov c, _negative_flag subb a, r1 jc printf_uifw_end mov _field_width, a #ifdef LONG_INT push ar3 push ar2 #endif push ar7 push ar6 push ar5 lcall printf_space pop ar5 pop ar6 pop ar7 #ifdef LONG_INT pop ar2 pop ar3 #endif printf_uifw_end: #endif printf_uint_doit: jnb _negative_flag, printf_uint_pos //mov a, #"-" mov a, #45 lcall printf_putchar printf_uint_pos: jb _short_flag, printf_uint8 #ifdef LONG_INT jnb _long_flag, printf_uint16 printf_uint32: push ar5 push ar6 push ar7 mov dpl, r2 mov a, r3 mov dph, a lcall printf_phex_msn mov a, dph lcall printf_phex_lsn mov a, dpl lcall printf_phex_msn mov a, dpl lcall printf_phex_lsn pop acc mov dpl, a lcall printf_phex_msn mov a, dpl pop dph pop dpl sjmp printf_uint16a #endif printf_uint16: mov dpl, r5 mov dph, r6 mov a, r7 printf_uint16a: lcall printf_phex_lsn mov a, dph lcall printf_phex_msn mov a, dph sjmp printf_uint8a printf_uint8: mov dpl, r5 mov a, r6 printf_uint8a: lcall printf_phex_lsn mov a, dpl lcall printf_phex_msn mov a, dpl lcall printf_phex_lsn lcall printf_zero pop dph pop dpl ljmp printf_main_loop /* read an integer into r1/r2/r3/r4, and msb into r5 */ printf_get_int: mov a, @r0 mov r1, a mov r5, a dec r0 jb _short_flag, printf_get_done mov r2, ar1 mov a, @r0 mov r1, a dec r0 jnb _long_flag, printf_get_done mov r4, ar2 mov r3, ar1 mov a, @r0 mov r2, a dec r0 mov a, @r0 mov r1, a dec r0 printf_get_done: ret /* convert binary number in r4/r3/r2/r1 into bcd packed number * in r3/r2/r7/r6/r5. The input number is destroyed in the * process, to avoid needing extra memory for the result (and * r1 gets used for temporary storage). dptr is overwritten, * but r0 is not changed. */ printf_int2bcd: mov a, r1 anl a, #0x0F mov dptr, #_int2bcd_0 movc a, @a+dptr mov r5, a mov a, r1 swap a anl a, #0x0F mov r1, a // recycle r1 for holding nibble mov dptr, #_int2bcd_1 movc a, @a+dptr add a, r5 da a mov r5, a mov a, r1 orl a, #16 movc a, @a+dptr addc a, #0 da a mov r6, a jnb _short_flag, printf_i2bcd_16 // if 8 bit int, we're done ret printf_i2bcd_16: mov a, r2 anl a, #0x0F mov r1, a mov dptr, #_int2bcd_2 movc a, @a+dptr add a, r5 da a mov r5, a mov a, r1 orl a, #16 movc a, @a+dptr addc a, r6 da a mov r6, a mov a, r2 swap a anl a, #0x0F mov r1, a mov dptr, #_int2bcd_3 movc a, @a+dptr add a, r5 da a mov r5, a mov a, r1 orl a, #16 movc a, @a+dptr addc a, r6 da a mov r6, a mov a, r1 orl a, #32 movc a, @a+dptr addc a, #0 da a mov r7, a jb _long_flag, printf_i2bcd_32 // if 16 bit int, we're done ret printf_i2bcd_32: #ifdef LONG_INT mov a, r3 anl a, #0x0F mov r1, a mov dptr, #_int2bcd_4 movc a, @a+dptr add a, r5 da a mov r5, a mov a, r1 orl a, #16 movc a, @a+dptr addc a, r6 da a mov r6, a mov a, r1 orl a, #32 movc a, @a+dptr addc a, r7 da a mov r7, a clr a addc a, #0 mov r2, a mov a, r3 swap a anl a, #0x0F mov r1, a mov dptr, #_int2bcd_5 movc a, @a+dptr add a, r5 da a mov r5, a mov a, r1 orl a, #16 movc a, @a+dptr addc a, r6 da a mov r6, a mov a, r1 orl a, #32 movc a, @a+dptr addc a, r7 da a mov r7, a mov a, r1 orl a, #48 movc a, @a+dptr addc a, r2 da a mov r2, a mov a, r4 anl a, #0x0F mov r1, a mov dptr, #_int2bcd_6 mov r3, #0 lcall printf_bcd_add10 // saves 27 bytes, costs 5 cycles mov a, r4 swap a anl a, #0x0F mov r1, a mov dptr, #_int2bcd_7 printf_bcd_add10: movc a, @a+dptr add a, r5 da a mov r5, a mov a, r1 orl a, #16 movc a, @a+dptr addc a, r6 da a mov r6, a mov a, r1 orl a, #32 movc a, @a+dptr addc a, r7 da a mov r7, a mov a, r1 orl a, #48 movc a, @a+dptr addc a, r2 da a mov r2, a mov a, r1 orl a, #64 movc a, @a+dptr addc a, r3 da a mov r3, a #endif ret #ifdef FIELD_WIDTH printf_space_loop: //mov a, #' ' mov a, #32 lcall printf_putchar dec _field_width printf_space: mov a, _field_width jnz printf_space_loop ret #endif /* print a hex digit, either upper 4 bit (msn) or lower 4 bits (lsn) */ printf_phex_msn: swap a printf_phex_lsn: anl a, #15 jnz printf_phex_ok jnb _print_zero_flag, printf_ret printf_phex_ok: setb _print_zero_flag add a, #0x90 da a addc a, #0x40 da a printf_putchar: push dph push dpl push ar0 mov dpl, a lcall _putchar pop ar0 pop dpl pop dph printf_ret: ret /* print a zero if all the calls to print the digits ended up */ /* being leading zeros */ printf_zero: jb _print_zero_flag, printf_ret //mov a, #'0' mov a, #48 ljmp printf_putchar printf_end: _endasm; } /* * #! /usr/bin/perl * for ($d=0; $d < 8; $d++) { * $n = 16 ** $d; * for ($p=0; $p < 5; $p++) { * last unless (((16 ** $d) * 15) / (10 ** ($p * 2))) % 100; * printf "code unsigned char int2bcd_%d_%d[15] = {", $d, $p; * for ($i=0; $i < 16; $i++) { * printf "0x%02d", * (((16 ** $d) * $i) / (10 ** ($p * 2))) % 100; * print ", " if $i < 15; * } * print "};\n"; * } * } */ code unsigned char int2bcd_0[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15}; code unsigned char int2bcd_1[] = { 0x00, 0x16, 0x32, 0x48, 0x64, 0x80, 0x96, 0x12, 0x28, 0x44, 0x60, 0x76, 0x92, 0x08, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02}; code unsigned char int2bcd_2[] = { 0x00, 0x56, 0x12, 0x68, 0x24, 0x80, 0x36, 0x92, 0x48, 0x04, 0x60, 0x16, 0x72, 0x28, 0x84, 0x40, 0x00, 0x02, 0x05, 0x07, 0x10, 0x12, 0x15, 0x17, 0x20, 0x23, 0x25, 0x28, 0x30, 0x33, 0x35, 0x38}; code unsigned char int2bcd_3[] = { 0x00, 0x96, 0x92, 0x88, 0x84, 0x80, 0x76, 0x72, 0x68, 0x64, 0x60, 0x56, 0x52, 0x48, 0x44, 0x40, 0x00, 0x40, 0x81, 0x22, 0x63, 0x04, 0x45, 0x86, 0x27, 0x68, 0x09, 0x50, 0x91, 0x32, 0x73, 0x14, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x06}; #ifdef LONG_INT code unsigned char int2bcd_4[] = { 0x00, 0x36, 0x72, 0x08, 0x44, 0x80, 0x16, 0x52, 0x88, 0x24, 0x60, 0x96, 0x32, 0x68, 0x04, 0x40, 0x00, 0x55, 0x10, 0x66, 0x21, 0x76, 0x32, 0x87, 0x42, 0x98, 0x53, 0x08, 0x64, 0x19, 0x75, 0x30, 0x00, 0x06, 0x13, 0x19, 0x26, 0x32, 0x39, 0x45, 0x52, 0x58, 0x65, 0x72, 0x78, 0x85, 0x91, 0x98}; code unsigned char int2bcd_5[] = { 0x00, 0x76, 0x52, 0x28, 0x04, 0x80, 0x56, 0x32, 0x08, 0x84, 0x60, 0x36, 0x12, 0x88, 0x64, 0x40, 0x00, 0x85, 0x71, 0x57, 0x43, 0x28, 0x14, 0x00, 0x86, 0x71, 0x57, 0x43, 0x29, 0x14, 0x00, 0x86, 0x00, 0x04, 0x09, 0x14, 0x19, 0x24, 0x29, 0x34, 0x38, 0x43, 0x48, 0x53, 0x58, 0x63, 0x68, 0x72, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15}; code unsigned char int2bcd_6[] = { 0x00, 0x16, 0x32, 0x48, 0x64, 0x80, 0x96, 0x12, 0x28, 0x44, 0x60, 0x76, 0x92, 0x08, 0x24, 0x40, 0x00, 0x72, 0x44, 0x16, 0x88, 0x60, 0x32, 0x05, 0x77, 0x49, 0x21, 0x93, 0x65, 0x38, 0x10, 0x82, 0x00, 0x77, 0x55, 0x33, 0x10, 0x88, 0x66, 0x44, 0x21, 0x99, 0x77, 0x54, 0x32, 0x10, 0x88, 0x65, 0x00, 0x16, 0x33, 0x50, 0x67, 0x83, 0x00, 0x17, 0x34, 0x50, 0x67, 0x84, 0x01, 0x18, 0x34, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02}; code unsigned char int2bcd_7[] = { 0x00, 0x56, 0x12, 0x68, 0x24, 0x80, 0x36, 0x92, 0x48, 0x04, 0x60, 0x16, 0x72, 0x28, 0x84, 0x40, 0x00, 0x54, 0x09, 0x63, 0x18, 0x72, 0x27, 0x81, 0x36, 0x91, 0x45, 0x00, 0x54, 0x09, 0x63, 0x18, 0x00, 0x43, 0x87, 0x30, 0x74, 0x17, 0x61, 0x04, 0x48, 0x91, 0x35, 0x79, 0x22, 0x66, 0x09, 0x53, 0x00, 0x68, 0x36, 0x05, 0x73, 0x42, 0x10, 0x79, 0x47, 0x15, 0x84, 0x52, 0x21, 0x89, 0x58, 0x26, 0x00, 0x02, 0x05, 0x08, 0x10, 0x13, 0x16, 0x18, 0x21, 0x24, 0x26, 0x29, 0x32, 0x34, 0x37, 0x40}; #endif