/*-------------------------------------------------------------------------
 
   printf_tiny.c - Tiny printf routine for use with sdcc/mcs51
 
 
 
   Copyright (C) 2004, Paul Stoffregen, paul@pjrc.com
 
 
 
   This library is free software; you can redistribute it and/or modify it
 
   under the terms of the GNU General Public License as published by the
 
   Free Software Foundation; either version 2, 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 library; see the file COPYING. If not, write to the
 
   Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
 
   MA 02110-1301, USA.
 
 
 
   As a special exception, if you link this library with other files,
 
   some of which are compiled with SDCC, to produce an executable,
 
   this library does not by itself cause the resulting executable to
 
   be covered by the GNU General Public License. This exception does
 
   not however invalidate any other reasons why the executable file
 
   might be covered by the GNU General Public License.
 
-------------------------------------------------------------------------*/
 
 
 
/*
 
 * 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
 
 
 
 
 
#if !defined(__SDCC_mcs51) || defined(__SDCC_USE_XSTACK) || defined(_SDCC_NO_ASM_LIB_FUNCS)
 
/* Does printf_tiny really work on ds390 and ds400?
 
   If it does, enable them in the line above */
 
#if defined(_SDCC_BUILD_LIB)
 
/* Disable all warnings if building a library */
 
#pragma disable_warning 190
 
#elif defined(__SDCC_USE_XSTACK)
 
#warning "printf_tiny not built, does not support --xstack"
 
#elif defined(_SDCC_NO_ASM_LIB_FUNCS)
 
#warning "printf_tiny not built, _SDCC_NO_ASM_LIB_FUNCS defined"
 
#else
 
/* Disable "ISO C forbids an empty source file" warning message */
 
#pragma disable_warning 190
 
#endif
 
#else /* defines are compatible with printf_tiny */
 
 
 
 
 
 
 
void printf_tiny(__code const char *fmt, ...) __reentrant
 
{
 
        fmt;    /* suppress 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
 
        dec     r0
 
        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_x
 
#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
 
        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;
 
}
 
 
 
 
 
#endif /* defines compatible with printf_tiny */