gbdk-releases/sdcc/device/lib/printf_fast.c
2015-01-10 16:25:09 +01:00

802 lines
15 KiB
C

/* 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