From 705b7357c506ef25bf9e84dea2d1a9b4eefea251 Mon Sep 17 00:00:00 2001 From: pjs Date: Sat, 25 Dec 2004 17:11:53 +0000 Subject: [PATCH] Added printf_tiny (minimal but useful printing in only 267 bytes) Added float support and other options (not on by default) to printf_fast git-svn-id: https://sdcc.svn.sourceforge.net/svnroot/sdcc/trunk/sdcc@3611 4a8a32a2-be11-0410-ad9d-d568d2c75423 --- device/lib/printf_fast.c | 1038 +++++++++++++++++++++++++++++++++++--- device/lib/printf_tiny.c | 275 ++++++++++ sim/ucsim/libtool | 4 +- 3 files changed, 1254 insertions(+), 63 deletions(-) create mode 100644 device/lib/printf_tiny.c diff --git a/device/lib/printf_fast.c b/device/lib/printf_fast.c index 6f492c9d..e1f0aa03 100644 --- a/device/lib/printf_fast.c +++ b/device/lib/printf_fast.c @@ -1,5 +1,5 @@ /* Fast printf routine for use with sdcc/mcs51 - * Copyright (c) 2001, Paul Stoffregen, paul@pjrc.com + * Copyright (c) 2004, 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 @@ -16,13 +16,75 @@ * 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) +/******************************************************************/ +/** **/ +/** Major features. These determine what capabilities your **/ +/** comiled printf_fast will have. **/ +/** **/ +/******************************************************************/ + +// Include support for 32 bit base 10 integers (%ld and %lu). Without +// this, you won't be able to print 32 bit integers as base 10. They +// will appear in hexidecimal. +#define LONG + +// Include support for floating point numbers (%f). Don't forget to +// enable LONG above, if you want to print floats greater than +// 65535.997. You can have 6 good digits after the decimal point, +// or an 8th if a small error is ok. +/- 2^32 to 1/10^8 isn't the +// full dynamic range of 32 bit floats, but it covers the most +// commonly used range. Adds about 500-600 bytes of code. +//#define FLOAT + +// Include support for minimum field widths (%8d, %20s, %12.5f) #define FIELD_WIDTH +// Include fast integer conversion. Without this, a compact but slower +// algorithm is used to convert integers (%d, %u, int part of %f). +// Even the slow algorithm is much faster than a typical C implementation +// based on repetitive division by 10. If you enable this, you get an +// extremely fast version (only 8 table lookups and 8 adds to convert a +// 32 bit integer), but it costs extra code space for larger lookup +// tables and optimized non-looping code. +#define FAST_INTEGER + + +/******************************************************************/ +/** **/ +/** Minor tweaks. These provide small code savings, with **/ +/** a partial loss of functionality. **/ +/** **/ +/******************************************************************/ + + +// If you enabled FLOAT, enabling this replaces the normal %f float +// output with a very compact version that always prints 4 fractional +// digits and does not have round off. Zero will print as "0.0000", +// and 1.999997 will print as "1.9999" (not rounded up to 2). The +// 4th digit is not accurate (+/- 2). This simpler version also +// avoids using 5 bytes of internal data memory. Code size is about +// 240 bytes less. +//#define FLOAT_FIXED4 + +// If you used FLOAT (not FLOAT_FIXED4), this will remove the smart +// default number of digits code. When you use "%f" without a field +// width, normally the smart default width code chooses a good number +// of digits based on size of the number. If you enabled FIELD_WIDTH +// and use a number, like "%.5f", this smart default code is never +// used anyway. Saves about 40 bytes of code. +//#define FLOAT_DEFAULT_FRAC_DIGITS 6 + +// If you used FLOAT (not FLOAT_FIXED4) and you do not specify a +// field width, normally trailing zeros are trimmed. Using this +// removes that feature (saves only a few bytes). +//#define DO_NOT_TRIM_TRAILING_ZEROS + +// Omit saving and restoring registers when calling putchar(). If you +// are desparate for a little more code space, this will give you a +// small savings. You MUST define putchar() with #pragma callee_saves, +// or implement it in assembly and avoid changing the registers. +//#define PUTCHAR_CALLEE_SAVES + /* extern void putchar(char ); */ @@ -31,10 +93,26 @@ static bit long_flag, short_flag, print_zero_flag, negative_flag; #ifdef FIELD_WIDTH static bit field_width_flag; -static bit leading_zero_flag; static data unsigned char field_width; #endif +#ifdef FLOAT +#include +static bit continue_float; +#ifndef FLOAT_FIXED4 +static data unsigned char frac_field_width; +static data unsigned char float_frac_bcd[4]; +// TODO: can float_frac_bcd be overlaid with temps used by trig functions +#endif +#endif + +#ifndef FAST_INTEGER +#ifdef LONG +static data unsigned int i2bcd_tmp; // slow 32 int conversion needs temp space +#endif +#endif + + void printf_fast(code char *fmt, ...) reentrant { @@ -51,6 +129,7 @@ printf_begin: 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 @@ -65,8 +144,11 @@ printf_format: clr _negative_flag #ifdef FIELD_WIDTH clr _field_width_flag - clr _leading_zero_flag - mov _field_width, #0 + mov r1, #_field_width + mov @r1, #0 +#endif +#ifdef FLOAT + clr _continue_float #endif printf_format_loop: @@ -82,17 +164,13 @@ printf_format_loop: jnc printf_nondigit2 #ifdef FIELD_WIDTH printf_digit: - jnz printf_digit_2 - cjne a, _field_width, printf_digit_2 - setb _leading_zero_flag -printf_digit_2: setb _field_width_flag - mov r1, a - mov a, _field_width + mov r2, a + mov a, @r1 mov b, #10 mul ab - add a, r1 - mov _field_width, a + add a, r2 + mov @r1, a #endif sjmp printf_format_loop printf_nondigit1: @@ -138,15 +216,35 @@ printf_format_c: sjmp printf_char printf_format_x: - //cjne a, #'x', printf_normal - cjne a, #120, printf_normal + //cjne a, #'x', printf_format_f + cjne a, #120, printf_format_f ljmp printf_hex +printf_format_f: +#ifdef FLOAT + //cjne a, #'f', printf_format_dot + cjne a, #102, printf_format_dot + ljmp print_float +#endif + +printf_format_dot: + //cjne a, #'.', printf_normal + cjne a, #46, printf_normal +#ifdef FLOAT +#ifdef FLOAT_FIXED4 + mov r1, #ar3 // parse frac field, but discard if FIXED4 +#else + mov r1, #_frac_field_width + mov @r1, #0 +#endif +#endif + sjmp printf_format_loop + printf_normal: jz printf_eot printf_char: lcall printf_putchar - sjmp printf_main_loop + ljmp printf_main_loop printf_eot: ljmp printf_end @@ -154,6 +252,8 @@ printf_eot: + + /* print a string... just grab each byte with __gptrget */ /* the user much pass a 24 bit generic pointer */ @@ -169,7 +269,6 @@ printf_string: #ifdef FIELD_WIDTH jnb _field_width_flag, printf_str_loop - clr _leading_zero_flag // never leading zeros for strings push dpl push dph printf_str_fw_loop: @@ -223,7 +322,7 @@ printf_hex8: ljmp printf_phex_lsn -#ifndef LONG_INT +#ifndef LONG printf_ld_in_hex: //mov a, #'0' mov a, #48 @@ -248,7 +347,7 @@ printf_int: setb _negative_flag mov a, r1 /* invert integer */ cpl a - addc a, #1 + add a, #1 mov r1, a jb _short_flag, printf_uint mov a, r2 @@ -278,15 +377,15 @@ printf_uint: 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 +#ifdef LONG 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 // print long integer as hex + mov a, r4 // rather than just the low 16 bits jnz printf_ld_in_hex #endif clr _long_flag @@ -303,6 +402,8 @@ printf_uint_ck8: jnz printf_uint_begin #ifdef FIELD_WIDTH jnb _field_width_flag, printf_uint_zero + mov a, _field_width + jz printf_uint_zero dec _field_width lcall printf_space #endif @@ -310,17 +411,24 @@ printf_uint_zero: //mov a, #'0' mov a, #48 lcall printf_putchar +#ifdef FLOAT + jnb _continue_float, 0001$ + ret +0001$: +#endif ljmp printf_main_loop + printf_uint_begin: push dpl push dph lcall printf_int2bcd // bcd number in r3/r2/r7/r6/r5 +printf_uint_2: #ifdef FIELD_WIDTH jnb _field_width_flag, printf_uifw_end -#ifdef LONG_INT +#ifdef LONG printf_uifw_32: mov r1, #10 jnb _long_flag, printf_uifw_16 @@ -343,7 +451,7 @@ printf_uifw_32: mov a, r7 anl a, #0xF0 jnz printf_uifw_sub -#endif +#endif // LONG printf_uifw_16: mov r1, #5 jb _short_flag, printf_uifw_8 @@ -372,33 +480,60 @@ printf_uifw_sub: jc printf_uifw_end mov _field_width, a -#ifdef LONG_INT +#ifndef PUTCHAR_CALLEE_SAVES +#ifdef LONG push ar3 push ar2 #endif push ar7 push ar6 push ar5 +#endif lcall printf_space +#ifndef PUTCHAR_CALLEE_SAVES pop ar5 pop ar6 pop ar7 -#ifdef LONG_INT +#ifdef LONG pop ar2 pop ar3 #endif -printf_uifw_end: #endif +printf_uifw_end: +#endif // FIELD_WIDTH + + printf_uint_doit: jnb _negative_flag, printf_uint_pos - //mov a, #"-" +#ifdef PUTCHAR_CALLEE_SAVES + //mov a, #'-' + mov a, #45 + lcall printf_putchar +#else +#ifdef LONG + push ar3 + push ar2 +#endif + push ar7 + push ar6 + push ar5 + //mov a, #'-' mov a, #45 lcall printf_putchar + pop ar5 + pop ar6 + pop ar7 +#ifdef LONG + pop ar2 + pop ar3 +#endif +#endif // PUTCHAR_CALLEE_SAVES + printf_uint_pos: jb _short_flag, printf_uint8 -#ifdef LONG_INT +#ifdef LONG jnb _long_flag, printf_uint16 printf_uint32: push ar5 @@ -421,7 +556,7 @@ printf_uint32: pop dph pop dpl sjmp printf_uint16a -#endif +#endif // LONG printf_uint16: mov dpl, r5 @@ -446,10 +581,471 @@ printf_uint8a: lcall printf_zero pop dph pop dpl +#ifdef FLOAT + jnb _continue_float, 0002$ + ret +0002$: +#endif + ljmp printf_main_loop + + + + + + + + +#ifdef FLOAT +#ifdef FLOAT_FIXED4 + // Print a float the easy way. First, extract the integer part and + // use the integer printing code. Then extract the fractional part, + // convert each bit to 4 digit BCD, and print the BCD sum. Absolutely + // no field width control, always 4 digits printed past the decimal + // point. No round off. 1.9999987 prints as 1.9999, not 2.0000. +print_float: +#ifdef FIELD_WIDTH + jnb _field_width_flag, print_float_begin + mov a, _field_width + add a, #251 + mov _field_width, a + jc print_float_begin + mov _field_width, #0 +#endif +print_float_begin: + mov exp_b, r0 // keep r0 safe, will need it again + lcall printf_get_float + clr c + mov a, #158 // check for large float we can't print + subb a, exp_a + jnc print_float_size_ok +printf_float_too_big: + // TODO: should print some sort of overflow error?? + ljmp printf_format_loop +print_float_size_ok: + lcall __fs_rshift_a + setb _continue_float +#ifndef LONG + mov a, r3 + orl a, r4 + jnz printf_float_too_big +#endif + lcall printf_uint // print the integer portion + //mov a, #'.' + mov a, #0x2E + lcall printf_putchar + // now that the integer part is printed, we need to refetch the + // float from the va_args and extract the fractional part + mov r0, exp_b + lcall printf_get_float + push ar0 + mov a, exp_a + cjne a, #126, print_float_frac_lshift + sjmp print_float_frac // input between 0.5 to 0.9999 +print_float_frac_lshift: + jc print_float_frac_rshift + //Acc (exponent) is greater than 126 (input >= 1.0) + add a, #130 + mov r5, a +print_float_lshift_loop: + clr c + mov a, r2 + rlc a + mov r2, a + mov a, r3 + rlc a + mov r3, a + mov a, r4 + rlc a + mov r4, a + djnz r5, print_float_lshift_loop + sjmp print_float_frac +print_float_frac_rshift: + //Acc (exponent) is less than 126 (input < 0.5) + cpl a + add a, #127 + lcall __fs_rshift_a +print_float_frac: + // now we've got the fractional part, so now is the time to + // convert to BCD... just convert each bit to BCD using a + // lookup table and BCD sum them together + mov r7, #14 + clr a + mov r6, a + mov r5, a + push dpl + push dph + mov dptr, #_frac2bcd // FLOAT_FIXED4 version (14 entries) +print_float_frac_loop: + mov a, r3 + rlc a + mov r3, a + mov a, r4 + rlc a + mov r4, a + jnc print_float_frac_skip + clr a + movc a, @a+dptr + add a, r5 + da a + mov r5, a + mov a, #1 + movc a, @a+dptr + addc a, r6 + da a + mov r6, a +print_float_frac_skip: + inc dptr + inc dptr + djnz r7, print_float_frac_loop + // the BCD sum is in dptr, so all we've got to do is output + // all 4 digits. No trailing zero suppression, no nice round + // off (impossible to change the integer part since we already + // printed it). + mov dph, r6 + mov dpl, r5 + setb _print_zero_flag + mov a, dph + 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 dph + pop dpl + pop ar0 + ljmp printf_main_loop + +#else // not FLOAT_FIXED4 + + + + +print_float: + // Print a float the not-as-easy way, with a configurable number of + // fractional digits (up to 8) and proper round-off (up to 7 digits). + // First, extract the fractional part, convert to BCD, and then add + // the scaled round-off. Store the rounded fractional digits and + // their carry. Then extract the integer portion, increment it if + // the rounding caused a carry. Use the integer printing to output + // the integer, and then output the stored fractional digits. This + // approach requires 5 bytes of internal RAM to store the 8 fractional + // digits and the number of them we'll actually print. This code is + // a couple hundred bytes larger and a bit slower than the FIXED4 + // version, but it gives very nice results. +print_float_1: +#ifdef FIELD_WIDTH + jnb _field_width_flag, print_float_default_width + // The caller specified exact field width, so use it. Need to + // convert the whole float digits into the integer portion only. + mov a, _field_width + setb c + subb a, _frac_field_width + mov _field_width, a + jnc print_float_begin + mov _field_width, #0 + sjmp print_float_begin +#endif +print_float_default_width: + // The caller didn't specify field width (or FIELD_WIDTH is + // not defined so it's ignored). We've still got to know + // how many fractional digits are going to print, so we can + // round off properly. +#ifdef FLOAT_DEFAULT_FRAC_DIGITS + mov _frac_field_width, #FLOAT_DEFAULT_FRAC_DIGITS +#else + // default fractional field width (between 0 to 7) + // attempt to scale the default number of fractional digits + // based on the magnitude of the float + mov ar1, r0 // r0 points to first byte of float + dec r1 // r1 points to second byte of float + mov r6, dpl + mov r7, dph + mov dptr, #_float_range_table + mov r5, #7 +print_float_default_loop: + clr a + movc a, @a+dptr + add a, @r1 + inc dptr + clr a + movc a, @a+dptr + addc a, @r0 + jnc print_float_default_done + inc dptr + djnz r5, print_float_default_loop +print_float_default_done: + mov _frac_field_width, r5 + mov dpl, r6 + mov dph, r7 +#endif // not FLOAT_DEFAULT_FRAC_DIGITS + +print_float_begin: + push ar0 // keep r0 safe, will need it again + lcall printf_get_float + mov a, exp_a + cjne a, #126, print_float_frac_lshift + sjmp print_float_frac // input between 0.5 to 0.9999 + +print_float_frac_lshift: + jc print_float_frac_rshift + //Acc (exponent) is greater than 126 (input >= 1.0) + add a, #130 + mov r5, a +print_float_lshift_loop: + clr c + mov a, r2 + rlc a + mov r2, a + mov a, r3 + rlc a + mov r3, a + mov a, r4 + rlc a + mov r4, a + djnz r5, print_float_lshift_loop + sjmp print_float_frac +print_float_frac_rshift: + //Acc (exponent) is less than 126 (input < 0.5) + cpl a + add a, #127 + lcall __fs_rshift_a +print_float_frac: + // Convert the fraction in r4/r3/r2/r1 into 8 BCD digits in exb_b/r7/r6/r5 + mov b, #27 + clr a + mov exp_b, a + mov r7, a + mov r6, a + mov r5, a + push dpl + push dph + mov dptr, #_frac2bcd // FLOAT version (27 entries) +print_float_frac_loop: + mov a, r1 + rlc a + mov r1, a + mov a, r2 + rlc a + mov r2, a + mov a, r3 + rlc a + mov r3, a + mov a, r4 + rlc a + mov r4, a + jnc print_float_frac_skip + clr a + movc a, @a+dptr + add a, r5 + da a + mov r5, a + mov a, #1 + movc a, @a+dptr + addc a, r6 + da a + mov r6, a + mov a, #2 + movc a, @a+dptr + addc a, r7 + da a + mov r7, a + mov a, #3 + movc a, @a+dptr + addc a, exp_b + da a + mov exp_b, a +print_float_frac_skip: + inc dptr + inc dptr + inc dptr + inc dptr + djnz b, print_float_frac_loop + pop dph + pop dpl +print_float_frac_roundoff: + // Now it's time to round-off the BCD digits to the desired precision. + clr a + mov r4, #0x50 // r4/r3/r2/r1 = 0.5 (bcd rounding) + mov r3, a + mov r2, a + mov r1, a + mov a, _frac_field_width + rl a + rl a + anl a, #0xFC + lcall __fs_rshift_a // divide r4/r3/r2/r1 by 10^frac_field_width + mov a, r5 + add a, r1 // add rounding to fractional part + da a + mov _float_frac_bcd+3, a // and store it for later use + mov a, r6 + addc a, r2 + da a + mov _float_frac_bcd+2, a + mov a, r7 + addc a, r3 + da a + mov _float_frac_bcd+1, a + mov a, exp_b + addc a, r4 + da a + mov _float_frac_bcd+0, a + mov sign_b, c // keep fractional carry in sign_b +print_float_int: + // Time to work on the integer portion... fetch the float again, check + // size (exponent), scale to integer, add the fraction's carry, and + // let the integer printing code do all the work. + pop ar0 + lcall printf_get_float + push ar0 + clr c + mov a, #158 // check for large float we can't print + subb a, exp_a + jnc print_float_size_ok +printf_float_too_big: + // TODO: should print some sort of overflow error?? + ljmp printf_format_loop +print_float_size_ok: + lcall __fs_rshift_a + jnb sign_b, print_float_do_int + // if we get here, the fractional round off caused the + // integer part to increment. Add 1 for a proper result + mov a, r1 + add a, #1 + mov r1, a + clr a + addc a, r2 + mov r2, a +#ifdef LONG + clr a + addc a, r3 + mov r3, a + clr a + addc a, r4 + mov r4, a +#endif + jc printf_float_too_big +print_float_do_int: +#ifndef LONG + mov a, r3 + orl a, r4 + jnz printf_float_too_big +#endif + setb _continue_float + lcall printf_uint // print the integer portion + + +print_float_frac_width: + // Now all we have to do is output the fractional digits that + // were previous computed and stored in memory. +#ifdef FIELD_WIDTH + jb _field_width_flag, print_float_do_frac +#endif +#ifndef DO_NOT_TRIM_TRAILING_ZEROS + // if the user did not explicitly set a + // field width, trim off trailing zeros +print_float_frac_trim: + mov a, _frac_field_width + jz print_float_do_frac + lcall get_float_frac_digit + jnz print_float_do_frac + djnz _frac_field_width, print_float_frac_trim +#endif + +print_float_do_frac: + mov a, _frac_field_width + jz print_float_done + //mov a, #'.' + mov a, #0x2E + lcall printf_putchar + mov r0, #0 + setb _print_zero_flag +print_float_do_frac_loop: + inc r0 + mov a, r0 + lcall get_float_frac_digit + lcall printf_phex_lsn + mov a, r0 + cjne a, _frac_field_width, print_float_do_frac_loop + +print_float_done: + pop ar0 ljmp printf_main_loop + // acc=1 for tenths, acc=2 for hundredths, etc +get_float_frac_digit: + dec a + clr c + rrc a + mov psw.5, c + add a, #_float_frac_bcd + mov r1, a + mov a, @r1 + jb psw.5, get_float_frac_digit_done + swap a +get_float_frac_digit_done: + anl a, #15 + ret + + + + +#endif // end of normal FLOAT code (not FLOAT_FIXED4) + + +// These helper functions are used, regardless of which type of +// FLOAT code is used. + +#if 0 +pm2_print_float: + mov a, exp_a + lcall pm2_entry_phex + mov a, #0x20 + lcall pm2_entry_cout + lcall _print_r4321 + mov a, #0x20 + lcall pm2_entry_cout + ret +#endif + + // Fetch a float from the va_args and put it into + // exp_a/r4/r3/r2 and also clear r1 and preset + // the flags +printf_get_float: + mov a, @r0 + dec r0 + mov r1, a + mov a, @r0 + dec r0 + mov r4, a + rlc a + mov a, r1 + rlc a + mov _negative_flag, c + mov exp_a, a + jz printf_get_float_2 + orl ar4, #0x80 +printf_get_float_2: + mov a, @r0 + dec r0 + mov r3, a + mov a, @r0 + dec r0 + mov r2, a + mov r1, #0 + clr _short_flag + setb _long_flag + ret +#endif // FLOAT + + + + + @@ -485,7 +1081,7 @@ printf_get_done: - +#ifdef FAST_INTEGER /* 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 @@ -496,26 +1092,15 @@ printf_get_done: printf_int2bcd: mov a, r1 - anl a, #0x0F - mov dptr, #_int2bcd_0 - movc a, @a+dptr - mov r5, a - - mov a, r1 + mov b, #100 + div ab + mov r6, a + mov a, #10 + xch a, b + div ab 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 + orl a, b 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 @@ -563,7 +1148,7 @@ printf_i2bcd_16: printf_i2bcd_32: -#ifdef LONG_INT +#ifdef LONG mov a, r3 anl a, #0x0F mov r1, a @@ -657,9 +1242,143 @@ printf_bcd_add10: addc a, r3 da a mov r3, a -#endif +#endif // LONG + ret + + +#else // not FAST_INTEGER + + /* 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. + */ + +#ifdef LONG + +printf_int2bcd: + mov a, #8 + jb _short_flag, printf_int2bcd_begin + mov a, #16 + jnb _long_flag, printf_int2bcd_begin + mov a, #32 +printf_int2bcd_begin: + mov b, a + clr a + mov r5, a + mov r6, a + mov r7, a + mov (_i2bcd_tmp + 0), a + mov (_i2bcd_tmp + 1), a + mov dptr, #_int2bcd +printf_i2bcd_loop: + mov a, r4 + rrc a + mov r4, a + mov a, r3 + rrc a + mov r3, a + mov a, r2 + rrc a + mov r2, a + mov a, r1 + rrc a + mov r1, a + jnc print_i2bcd_skip + clr a + movc a, @a+dptr + add a, r5 + da a + mov r5, a + mov a, #1 + movc a, @a+dptr + addc a, r6 + da a + mov r6, a + mov a, #2 + movc a, @a+dptr + addc a, r7 + da a + mov r7, a + mov a, #3 + movc a, @a+dptr + addc a, (_i2bcd_tmp + 0) + da a + mov (_i2bcd_tmp + 0), a + mov a, #4 + movc a, @a+dptr + addc a, (_i2bcd_tmp + 1) + da a + mov (_i2bcd_tmp + 1), a +print_i2bcd_skip: + inc dptr + inc dptr + inc dptr + inc dptr + inc dptr + djnz b, printf_i2bcd_loop + mov r2, (_i2bcd_tmp + 0) + mov r3, (_i2bcd_tmp + 1) ret +#else // not LONG + +printf_int2bcd: + mov a, #8 + jb _short_flag, printf_int2bcd_begin + mov a, #16 +printf_int2bcd_begin: + mov b, a + clr a + mov r5, a + mov r6, a + mov r7, a + mov dptr, #_int2bcd +printf_i2bcd_loop: + mov a, r2 + rrc a + mov r2, a + mov a, r1 + rrc a + mov r1, a + jnc printf_i2bcd_add_skip + clr a + movc a, @a+dptr + add a, r5 + da a + mov r5, a + mov a, #1 + movc a, @a+dptr + addc a, r6 + da a + mov r6, a + mov a, #2 + movc a, @a+dptr + addc a, r7 + da a + mov r7, a +printf_i2bcd_add_skip: + inc dptr + inc dptr + inc dptr + djnz b, printf_i2bcd_loop + ret + +#endif // not LONG + + +#endif // not FAST_INTEGER + + + + + + + + + + @@ -667,10 +1386,6 @@ printf_bcd_add10: printf_space_loop: //mov a, #' ' mov a, #32 - jnb _leading_zero_flag, printf_space_output - //mov a, #'0' - mov a, #48 -printf_space_output: lcall printf_putchar dec _field_width printf_space: @@ -698,6 +1413,14 @@ printf_phex_ok: addc a, #0x40 da a printf_putchar: +#ifdef PUTCHAR_CALLEE_SAVES + push dph + push dpl + mov dpl, a + lcall _putchar + pop dpl + pop dph +#else push dph push dpl push ar0 @@ -706,6 +1429,7 @@ printf_putchar: pop ar0 pop dpl pop dph +#endif printf_ret: ret @@ -723,6 +1447,7 @@ printf_end: } +#ifdef FAST_INTEGER /* * #! /usr/bin/perl * for ($d=0; $d < 8; $d++) { @@ -740,7 +1465,7 @@ printf_end: * } */ - +#if 0 code unsigned char int2bcd_0[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15}; @@ -750,6 +1475,7 @@ code unsigned char int2bcd_1[] = { 0x28, 0x44, 0x60, 0x76, 0x92, 0x08, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02}; +#endif code unsigned char int2bcd_2[] = { 0x00, 0x56, 0x12, 0x68, 0x24, 0x80, 0x36, 0x92, @@ -765,7 +1491,7 @@ code unsigned char int2bcd_3[] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x06}; -#ifdef LONG_INT +#ifdef LONG code unsigned char int2bcd_4[] = { 0x00, 0x36, 0x72, 0x08, 0x44, 0x80, 0x16, 0x52, 0x88, 0x24, 0x60, 0x96, 0x32, 0x68, 0x04, 0x40, @@ -807,6 +1533,196 @@ code unsigned char int2bcd_7[] = { 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 // LONG + +#else // not FAST_INTEGER + +/* + * #! /usr/bin/perl + * print "code unsigned char int2bcd[] = {\n"; + * for ($i=0, $n=1; $i<32; $i++, $n*=2) { + * $r = sprintf "%010u", $n; + * $r =~ /([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])/; + * printf "0x%02d, 0x%02d, 0x%02d, 0x%02d, 0x%02d", $5, $4, $3, $2, $1; + * print ',' if $i < 31; + * printf "\t\t// %10u\n", $n; + * } + * print "}\ncode unsigned char int2bcd[] = {\n"; + * for ($i=0, $n=1; $i<16; $i++, $n*=2) { + * $r = sprintf "%06u", $n; + * $r =~ /([0-9][0-9])([0-9][0-9])([0-9][0-9])/; + * printf "0x%02d, 0x%02d, 0x%02d", $3, $2, $1; + * print ',' if $i < 15; + * printf "\t\t// %10u\n", $n; + * } + * print "};\n"; +*/ + +#ifdef LONG +code unsigned char int2bcd[] = { +0x01, 0x00, 0x00, 0x00, 0x00, // 1 +0x02, 0x00, 0x00, 0x00, 0x00, // 2 +0x04, 0x00, 0x00, 0x00, 0x00, // 4 +0x08, 0x00, 0x00, 0x00, 0x00, // 8 +0x16, 0x00, 0x00, 0x00, 0x00, // 16 +0x32, 0x00, 0x00, 0x00, 0x00, // 32 +0x64, 0x00, 0x00, 0x00, 0x00, // 64 +0x28, 0x01, 0x00, 0x00, 0x00, // 128 +0x56, 0x02, 0x00, 0x00, 0x00, // 256 +0x12, 0x05, 0x00, 0x00, 0x00, // 512 +0x24, 0x10, 0x00, 0x00, 0x00, // 1024 +0x48, 0x20, 0x00, 0x00, 0x00, // 2048 +0x96, 0x40, 0x00, 0x00, 0x00, // 4096 +0x92, 0x81, 0x00, 0x00, 0x00, // 8192 +0x84, 0x63, 0x01, 0x00, 0x00, // 16384 +0x68, 0x27, 0x03, 0x00, 0x00, // 32768 +0x36, 0x55, 0x06, 0x00, 0x00, // 65536 +0x72, 0x10, 0x13, 0x00, 0x00, // 131072 +0x44, 0x21, 0x26, 0x00, 0x00, // 262144 +0x88, 0x42, 0x52, 0x00, 0x00, // 524288 +0x76, 0x85, 0x04, 0x01, 0x00, // 1048576 +0x52, 0x71, 0x09, 0x02, 0x00, // 2097152 +0x04, 0x43, 0x19, 0x04, 0x00, // 4194304 +0x08, 0x86, 0x38, 0x08, 0x00, // 8388608 +0x16, 0x72, 0x77, 0x16, 0x00, // 16777216 +0x32, 0x44, 0x55, 0x33, 0x00, // 33554432 +0x64, 0x88, 0x10, 0x67, 0x00, // 67108864 +0x28, 0x77, 0x21, 0x34, 0x01, // 134217728 +0x56, 0x54, 0x43, 0x68, 0x02, // 268435456 +0x12, 0x09, 0x87, 0x36, 0x05, // 536870912 +0x24, 0x18, 0x74, 0x73, 0x10, // 1073741824 +0x48, 0x36, 0x48, 0x47, 0x21 // 2147483648 +}; +#else // not LONG +code unsigned char int2bcd[] = { +0x01, 0x00, 0x00, // 1 +0x02, 0x00, 0x00, // 2 +0x04, 0x00, 0x00, // 4 +0x08, 0x00, 0x00, // 8 +0x16, 0x00, 0x00, // 16 +0x32, 0x00, 0x00, // 32 +0x64, 0x00, 0x00, // 64 +0x28, 0x01, 0x00, // 128 +0x56, 0x02, 0x00, // 256 +0x12, 0x05, 0x00, // 512 +0x24, 0x10, 0x00, // 1024 +0x48, 0x20, 0x00, // 2048 +0x96, 0x40, 0x00, // 4096 +0x92, 0x81, 0x00, // 8192 +0x84, 0x63, 0x01, // 16384 +0x68, 0x27, 0x03 // 32768 +}; +#endif // not LONG + +#endif // not FAST_INTEGER + + + + +#ifdef FLOAT +#ifndef FLOAT_FIXED4 + +/* + * #! /usr/bin/perl + * for ($i=0, $f=0.5; $i<24; $i++) { + * $r = sprintf "%.8f", $f; + * $r =~ /0\.([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])/; + * printf "0x%02d, 0x%02d, 0x%02d, 0x%02d", $4, $3, $2, $1; + * print ',' if $i < 23; + * $sum += $r; + * printf "\t\t// %.15f %.8f\n", $f, $sum; + * $f /= 2; + * } + */ + +code unsigned char frac2bcd[] = { +0x00, 0x00, 0x00, 0x50, // 0.500000000000000 0.50000000 +0x00, 0x00, 0x00, 0x25, // 0.250000000000000 0.75000000 +0x00, 0x00, 0x50, 0x12, // 0.125000000000000 0.87500000 +0x00, 0x00, 0x25, 0x06, // 0.062500000000000 0.93750000 +0x00, 0x50, 0x12, 0x03, // 0.031250000000000 0.96875000 +0x00, 0x25, 0x56, 0x01, // 0.015625000000000 0.98437500 +0x50, 0x12, 0x78, 0x00, // 0.007812500000000 0.99218750 +0x25, 0x06, 0x39, 0x00, // 0.003906250000000 0.99609375 +0x12, 0x53, 0x19, 0x00, // 0.001953125000000 0.99804687 +0x56, 0x76, 0x09, 0x00, // 0.000976562500000 0.99902343 +0x28, 0x88, 0x04, 0x00, // 0.000488281250000 0.99951171 +0x14, 0x44, 0x02, 0x00, // 0.000244140625000 0.99975585 +0x07, 0x22, 0x01, 0x00, // 0.000122070312500 0.99987792 +0x04, 0x61, 0x00, 0x00, // 0.000061035156250 0.99993896 +0x52, 0x30, 0x00, 0x00, // 0.000030517578125 0.99996948 +0x26, 0x15, 0x00, 0x00, // 0.000015258789062 0.99998474 +0x63, 0x07, 0x00, 0x00, // 0.000007629394531 0.99999237 +0x81, 0x03, 0x00, 0x00, // 0.000003814697266 0.99999618 +0x91, 0x01, 0x00, 0x00, // 0.000001907348633 0.99999809 +0x95, 0x00, 0x00, 0x00, // 0.000000953674316 0.99999904 +0x48, 0x00, 0x00, 0x00, // 0.000000476837158 0.99999952 +0x24, 0x00, 0x00, 0x00, // 0.000000238418579 0.99999976 +0x12, 0x00, 0x00, 0x00, // 0.000000119209290 0.99999988 +0x06, 0x00, 0x00, 0x00, // 0.000000059604645 0.99999994 +0x03, 0x00, 0x00, 0x00, // 0.000000029802322 0.99999997 +0x01, 0x00, 0x00, 0x00, // 0.000000014901161 0.99999998 +0x01, 0x00, 0x00, 0x00 // 0.000000007450581 0.99999999 +}; + +#ifndef FLOAT_DEFAULT_FRAC_DIGITS +// TODO: Perhaps these should be tweaked a bit to take round up +// effects into account... or maybe give more default digits?? +// Range #digits +// 0.0001 - 0.0009999 7 +// 0.001 - 0.009999 6 0.001 = 0x3A83126F 3A83 +// 0.01 - 0.09999 5 0.01 = 0x3C23D70A 3C23 +// 0.1 - 9.9999 4 0.1 = 0x3DCCCCCD, 3DCC +// 10.0 - 99.99 3 10.0 = 0x41200000 4120 +// 100.0 - 999.99 2 100.0 = 0x42C80000 42C8 +// 1000 - 9999.9 1 1000 = 0x447A0000 447A +// 10000+ 0 10000 = 0x461C4000 461C +code unsigned int float_range_table[] = { +65536 - 0x3A83, +65536 - 0x3C23, +65536 - 0x3DCC, +65536 - 0x4120, +65536 - 0x42C8, +65536 - 0x447A, +65536 - 0x461C +}; #endif +#else // using FLOAT_FIXED4 + +/* +* #! /usr/bin/perl +* for ($i=0, $f=0.5; $i<14; $i++) { +* $r = sprintf "%.4f", $f; +* $r =~ /0\.([0-9][0-9])([0-9][0-9])/; +* printf "0x%02d, 0x%02d", $2, $1; +* print ',' if $i < 13; +* $sum += $r; +* printf "\t\t// %.15f %.4f\n", $f, $sum; +* $f /= 2; +* } +*/ + +code unsigned char frac2bcd[] = { +0x00, 0x50, // 0.500000000000000 0.5000 +0x00, 0x25, // 0.250000000000000 0.7500 +0x50, 0x12, // 0.125000000000000 0.8750 +0x25, 0x06, // 0.062500000000000 0.9375 +0x12, 0x03, // 0.031250000000000 0.9687 +0x56, 0x01, // 0.015625000000000 0.9843 +0x78, 0x00, // 0.007812500000000 0.9921 +0x39, 0x00, // 0.003906250000000 0.9960 +0x20, 0x00, // 0.001953125000000 0.9980 +0x10, 0x00, // 0.000976562500000 0.9990 +0x05, 0x00, // 0.000488281250000 0.9995 +0x02, 0x00, // 0.000244140625000 0.9997 +0x01, 0x00, // 0.000122070312500 0.9998 +0x01, 0x00 // 0.000061035156250 0.9999 +}; + +#endif // FLOAT_FIXED4 +#endif // FLOAT + + + diff --git a/device/lib/printf_tiny.c b/device/lib/printf_tiny.c new file mode 100644 index 00000000..4a6dfd4d --- /dev/null +++ b/device/lib/printf_tiny.c @@ -0,0 +1,275 @@ +/* Tiny printf routine for use with sdcc/mcs51 + * Copyright (c) 2004, 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. + */ + +/* + * This tiny printf uses minimal code space, and it is fully reentrant + * and register bank neutral (usually safe to call from within an + * interrupt routine). Code size is under 270 bytes. Only one library + * function is called (_gptrget, 41 bytes), in addition to calls to + * putchar(). + * + * Five simple formats are supported + * + * %d signed 16 bit integer decimal (-32768 to 32767) + * %u unsigned 16 bit integer decimal (0 to 65535) + * %s string, takes a 24 bit generic pointer + * %c character. You must explicitly cast to char in SDCC + * %x 16 bit integer in hex (0 to FFFF) + * + * For a more complete printf that supports longs, floating point and + * field width, try using printf_fast() or printf_large(). + */ + + +// This removes the negative number code, causing "%d" to be the same +// as "%u". If you don't care about printing negative numbers, this +// will save 21 bytes of code. +//#define ALWAYS_PRINT_UNSIGNED + +// Directly output characters to the serial port using simple polling, +// rather than calling putchar(). This saves 14 bytes, plus the size +// of putchar. +//#define DIRECT_SERIAL_OUTPUT + + + +/* extern void putchar(char ); */ + + +#define print_zero_flag PSW.5 + + +void printf_tiny(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 + add a, #256 - 37 + jz printf_format // check for '%' + add a, #37 + jz printf_end_h + lcall printf_putchar + sjmp printf_main_loop +printf_end_h: + ljmp printf_end + + +printf_format: + setb print_zero_flag + clr a + movc a, @a+dptr // get next byte of data format + inc dptr + push dph + push dpl + + +printf_format_s: + //cjne a, #'s', printf_format_c + cjne a, #115, printf_format_c +printf_string: + /* print a string... just grab each byte with __gptrget */ + /* the user much pass a 24 bit generic pointer */ + 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 +printf_str_loop: + lcall __gptrget + jz printf_format_done + inc dptr + lcall printf_putchar + sjmp printf_str_loop + + +printf_format_c: + //cjne a, #'c', printf_format_d + cjne a, #99, printf_format_d + mov a, @r0 // Acc has the character to print + dec r0 + lcall printf_putchar + sjmp printf_format_done + + +printf_format_d: + //cjne a, #'d', printf_format_u + cjne a, #100, printf_format_u +#ifndef ALWAYS_PRINT_UNSIGNED + mov a, @r0 + jnb acc.7, printf_uint + dec r0 + mov a, @r0 + cpl a + add a, #1 + mov @r0, a + inc r0 + mov a, @r0 + cpl a + addc a, #0 + mov @r0, a + //mov a, #'-' + mov a, #45 + lcall printf_putchar +#endif + sjmp printf_uint + + +printf_format_x: + //cjne a, #'x', printf_format_u + cjne a, #120, printf_format_u + mov dph, @r0 + dec r0 + mov dpl, @r0 + dec r0 + clr a +printf_hex: + lcall printf_phex_lsn + mov a, dph + 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 + jnb print_zero_flag, printf_format_done + //mov a, #'0' + mov a, #48 + lcall printf_putchar +printf_format_done: + pop dpl + pop dph + ljmp printf_main_loop + + +printf_format_u: + //cjne a, #'u', printf_format_done + cjne a, #117, printf_format_done +printf_uint: + mov a, @r0 + mov r2, a + dec r0 + mov a, @r0 + mov r1, a + dec r0 +printf_int2bcd: + mov r4, #16 + mov r5, #39 + lcall div_by_sub + mov r7, a + mov r4, #232 + mov r5, #3 + lcall div_by_sub + swap a + mov dph, a + mov r4, #100 + mov r5, #0 + lcall div_by_sub + orl dph, a + mov a, r1 + mov b, #10 + div ab + swap a + orl a, b + mov dpl, a + mov a, r7 + sjmp printf_hex + + + // Divide r2/r1 by r5/r4 using successive subtraction + // returns quotient in r2/r1 and remainder in acc. +div_by_sub: + mov r3, #0 +div_by_sub_loop: + inc r3 + clr c + mov a, r1 + subb a, r4 + mov r1, a + mov a, r2 + subb a, r5 + mov r2, a + jnc div_by_sub_loop + dec r3 + mov a, r1 + add a, r4 + mov r1, a + mov a, r2 + addc a, r5 + mov r2, a + mov a, r3 + ret + + + /* 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 + jb print_zero_flag, printf_ret +printf_phex_ok: + clr print_zero_flag + add a, #0x90 + da a + addc a, #0x40 + da a +printf_putchar: +#ifdef DIRECT_SERIAL_OUTPUT + jnb ti, printf_putchar + clr ti + mov sbuf, a +#else + push dph + push dpl + push b + mov dpl, a + mov a, r0 + push acc + lcall _putchar + pop acc + mov r0, a + pop b + pop dpl + pop dph +#endif +printf_ret: + ret + + +printf_end: + _endasm; +} + + diff --git a/sim/ucsim/libtool b/sim/ucsim/libtool index fe1e5a3d..cde8c246 100755 --- a/sim/ucsim/libtool +++ b/sim/ucsim/libtool @@ -1,7 +1,7 @@ #! /bin/sh # libtool - Provide generalized library-building support services. -# Generated automatically by (GNU 0.5.3) +# Generated automatically by (GNU 0.5.4) # NOTE: Changes made to this file will be lost: look at ltmain.sh. # # Copyright (C) 1996-2000 Free Software Foundation, Inc. @@ -35,7 +35,7 @@ if test "X${CDPATH+set}" = Xset; then CDPATH=:; export CDPATH; fi # ### BEGIN LIBTOOL CONFIG -# Libtool was configured on host p4: +# Libtool was configured on host preston: # Shell to use when invoking shell scripts. SHELL="/bin/sh" -- 2.30.2