Added printf_tiny (minimal but useful printing in only 267 bytes)
[fw/sdcc] / device / lib / printf_tiny.c
1 /* Tiny printf routine for use with sdcc/mcs51
2  * Copyright (c) 2004, Paul Stoffregen, paul@pjrc.com
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 /*
20  * This tiny printf uses minimal code space, and it is fully reentrant
21  * and register bank neutral (usually safe to call from within an
22  * interrupt routine).  Code size is under 270 bytes.  Only one library
23  * function is called (_gptrget, 41 bytes), in addition to calls to
24  * putchar().
25  * 
26  * Five simple formats are supported
27  *
28  *      %d      signed 16 bit integer decimal (-32768 to 32767)
29  *      %u      unsigned 16 bit integer decimal (0 to 65535)
30  *      %s      string, takes a 24 bit generic pointer
31  *      %c      character.  You must explicitly cast to char in SDCC
32  *      %x      16 bit integer in hex (0 to FFFF)
33  *
34  * For a more complete printf that supports longs, floating point and
35  * field width, try using printf_fast() or printf_large().
36  */     
37
38
39 // This removes the negative number code, causing "%d" to be the same
40 // as "%u".  If you don't care about printing negative numbers, this
41 // will save 21 bytes of code.
42 //#define ALWAYS_PRINT_UNSIGNED
43
44 // Directly output characters to the serial port using simple polling,
45 // rather than calling putchar().  This saves 14 bytes, plus the size
46 // of putchar.
47 //#define DIRECT_SERIAL_OUTPUT
48
49
50
51 /* extern void putchar(char ); */
52
53
54 #define print_zero_flag PSW.5
55
56
57 void printf_tiny(code char *fmt, ...) reentrant
58 {
59         fmt;    /* supress unreferenced variable warning */
60
61         _asm
62
63 printf_begin:
64         mov     a, _bp          // r0 will point to va_args (stack)
65         add     a, #253
66         mov     r0, a           // r0 points to MSB of fmt
67         mov     dph, @r0
68         dec     r0
69         mov     dpl, @r0        // dptr has address of fmt
70         dec     r0
71
72
73 printf_main_loop:
74         clr     a
75         movc    a, @a+dptr      // get next byte of fmt string
76         inc     dptr
77         add     a, #256 - 37
78         jz      printf_format   // check for '%'
79         add     a, #37
80         jz      printf_end_h
81         lcall   printf_putchar
82         sjmp    printf_main_loop
83 printf_end_h:
84         ljmp    printf_end
85
86
87 printf_format:
88         setb    print_zero_flag
89         clr     a
90         movc    a, @a+dptr      // get next byte of data format
91         inc     dptr
92         push    dph
93         push    dpl
94
95
96 printf_format_s:
97         //cjne  a, #'s', printf_format_c
98         cjne    a, #115, printf_format_c
99 printf_string:
100         /* print a string... just grab each byte with __gptrget */
101         /* the user much pass a 24 bit generic pointer */
102         mov     b, @r0          // b has type of address (generic *)
103         dec     r0
104         mov     dph, @r0
105         dec     r0
106         mov     dpl, @r0        // dptr has address of user's string
107         dec     r0
108 printf_str_loop:
109         lcall   __gptrget
110         jz      printf_format_done
111         inc     dptr
112         lcall   printf_putchar
113         sjmp    printf_str_loop
114
115
116 printf_format_c:
117         //cjne  a, #'c', printf_format_d
118         cjne    a, #99, printf_format_d
119         mov     a, @r0          // Acc has the character to print
120         dec     r0
121         lcall   printf_putchar
122         sjmp    printf_format_done
123
124
125 printf_format_d:
126         //cjne  a, #'d', printf_format_u
127         cjne    a, #100, printf_format_u
128 #ifndef ALWAYS_PRINT_UNSIGNED
129         mov     a, @r0
130         jnb     acc.7, printf_uint
131         dec     r0
132         mov     a, @r0
133         cpl     a
134         add     a, #1
135         mov     @r0, a
136         inc     r0
137         mov     a, @r0
138         cpl     a
139         addc    a, #0
140         mov     @r0, a
141         //mov   a, #'-'
142         mov     a, #45
143         lcall   printf_putchar
144 #endif
145         sjmp    printf_uint
146
147
148 printf_format_x:
149         //cjne  a, #'x', printf_format_u
150         cjne    a, #120, printf_format_u
151         mov     dph, @r0
152         dec     r0
153         mov     dpl, @r0
154         dec     r0
155         clr     a
156 printf_hex:
157         lcall   printf_phex_lsn
158         mov     a, dph
159         lcall   printf_phex_msn
160         mov     a, dph
161         lcall   printf_phex_lsn
162         mov     a, dpl
163         lcall   printf_phex_msn
164         mov     a, dpl
165         lcall   printf_phex_lsn
166         jnb     print_zero_flag, printf_format_done
167         //mov   a, #'0'
168         mov     a, #48
169         lcall   printf_putchar
170 printf_format_done:
171         pop     dpl
172         pop     dph
173         ljmp    printf_main_loop
174
175
176 printf_format_u:
177         //cjne  a, #'u', printf_format_done
178         cjne    a, #117, printf_format_done
179 printf_uint:
180         mov     a, @r0
181         mov     r2, a
182         dec     r0
183         mov     a, @r0
184         mov     r1, a
185         dec     r0
186 printf_int2bcd:
187         mov     r4, #16
188         mov     r5, #39
189         lcall   div_by_sub
190         mov     r7, a
191         mov     r4, #232
192         mov     r5, #3
193         lcall   div_by_sub
194         swap    a
195         mov     dph, a
196         mov     r4, #100
197         mov     r5, #0
198         lcall   div_by_sub
199         orl     dph, a
200         mov     a, r1
201         mov     b, #10
202         div     ab
203         swap    a
204         orl     a, b
205         mov     dpl, a
206         mov     a, r7
207         sjmp    printf_hex
208
209
210         // Divide r2/r1 by r5/r4 using successive subtraction
211         // returns quotient in r2/r1 and remainder in acc.
212 div_by_sub:
213         mov     r3, #0
214 div_by_sub_loop:
215         inc     r3
216         clr     c
217         mov     a, r1
218         subb    a, r4
219         mov     r1, a
220         mov     a, r2
221         subb    a, r5
222         mov     r2, a
223         jnc     div_by_sub_loop
224         dec     r3
225         mov     a, r1
226         add     a, r4
227         mov     r1, a
228         mov     a, r2
229         addc    a, r5
230         mov     r2, a
231         mov     a, r3
232         ret
233
234
235         /* print a hex digit, either upper 4 bit (msn) or lower 4 bits (lsn) */
236 printf_phex_msn:
237         swap    a
238 printf_phex_lsn:
239         anl     a, #15
240         jnz     printf_phex_ok
241         jb      print_zero_flag, printf_ret
242 printf_phex_ok:
243         clr     print_zero_flag
244         add     a, #0x90
245         da      a
246         addc    a, #0x40
247         da      a
248 printf_putchar:
249 #ifdef DIRECT_SERIAL_OUTPUT
250         jnb     ti, printf_putchar
251         clr     ti
252         mov     sbuf, a
253 #else
254         push    dph
255         push    dpl
256         push    b
257         mov     dpl, a
258         mov     a, r0
259         push    acc
260         lcall   _putchar
261         pop     acc
262         mov     r0, a
263         pop     b
264         pop     dpl
265         pop     dph
266 #endif
267 printf_ret:
268         ret
269
270
271 printf_end:
272         _endasm;
273 }
274
275