0ebded46115da41f7600453ac15a711fa04a74d2
[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 #if !defined(SDCC_mcs51) || defined(SDCC_USE_XSTACK) || defined(_SDCC_NO_ASM_LIB_FUNCS)
58 // Does printf_tiny really work on ds390 and ds400?
59 // If it does, enable them in the line above
60 #if defined(SDCC_USE_XSTACK)
61 #warning "printf_tiny not built, does not support --xstack"
62 #elif defined(_SDCC_NO_ASM_LIB_FUNCS)
63 #warning "printf_tiny not built, _SDCC_NO_ASM_LIB_FUNCS defined"
64 #else
65 /* Disable "ISO C forbids an empty source file" wraning message */
66 #pragma disable_warning 190
67 #endif
68 #else // defines are compatible with printf_tiny
69
70
71
72 void printf_tiny(__code char *fmt, ...) __reentrant
73 {
74         fmt;    /* suppress unreferenced variable warning */
75
76         __asm
77
78 printf_begin:
79         mov     a, _bp          // r0 will point to va_args (stack)
80         add     a, #253
81         mov     r0, a           // r0 points to MSB of fmt
82         mov     dph, @r0
83         dec     r0
84         mov     dpl, @r0        // dptr has address of fmt
85         dec     r0
86
87
88 printf_main_loop:
89         clr     a
90         movc    a, @a+dptr      // get next byte of fmt string
91         inc     dptr
92         add     a, #256 - 37
93         jz      printf_format   // check for '%'
94         add     a, #37
95         jz      printf_end_h
96         lcall   printf_putchar
97         sjmp    printf_main_loop
98 printf_end_h:
99         ljmp    printf_end
100
101
102 printf_format:
103         setb    print_zero_flag
104         clr     a
105         movc    a, @a+dptr      // get next byte of data format
106         inc     dptr
107         push    dph
108         push    dpl
109
110
111 printf_format_s:
112         //cjne  a, #'s', printf_format_c
113         cjne    a, #115, printf_format_c
114 printf_string:
115         /* print a string... just grab each byte with __gptrget */
116         /* the user much pass a 24 bit generic pointer */
117         mov     b, @r0          // b has type of address (generic *)
118         dec     r0
119         mov     dph, @r0
120         dec     r0
121         mov     dpl, @r0        // dptr has address of user's string
122         dec     r0
123 printf_str_loop:
124         lcall   __gptrget
125         jz      printf_format_done
126         inc     dptr
127         lcall   printf_putchar
128         sjmp    printf_str_loop
129
130
131 printf_format_c:
132         //cjne  a, #'c', printf_format_d
133         cjne    a, #99, printf_format_d
134         dec     r0
135         mov     a, @r0          // Acc has the character to print
136         dec     r0
137         lcall   printf_putchar
138         sjmp    printf_format_done
139
140
141 printf_format_d:
142         //cjne  a, #'d', printf_format_u
143         cjne    a, #100, printf_format_x
144 #ifndef ALWAYS_PRINT_UNSIGNED
145         mov     a, @r0
146         jnb     acc.7, printf_uint
147         dec     r0
148         mov     a, @r0
149         cpl     a
150         add     a, #1
151         mov     @r0, a
152         inc     r0
153         mov     a, @r0
154         cpl     a
155         addc    a, #0
156         mov     @r0, a
157         //mov   a, #'-'
158         mov     a, #45
159         lcall   printf_putchar
160 #endif
161         sjmp    printf_uint
162
163
164 printf_format_x:
165         //cjne  a, #'x', printf_format_u
166         cjne    a, #120, printf_format_u
167         mov     dph, @r0
168         dec     r0
169         mov     dpl, @r0
170         dec     r0
171         clr     a
172 printf_hex:
173         lcall   printf_phex_lsn
174         mov     a, dph
175         lcall   printf_phex_msn
176         mov     a, dph
177         lcall   printf_phex_lsn
178         mov     a, dpl
179         lcall   printf_phex_msn
180         mov     a, dpl
181         lcall   printf_phex_lsn
182         jnb     print_zero_flag, printf_format_done
183         //mov   a, #'0'
184         mov     a, #48
185         lcall   printf_putchar
186 printf_format_done:
187         pop     dpl
188         pop     dph
189         ljmp    printf_main_loop
190
191
192 printf_format_u:
193         //cjne  a, #'u', printf_format_done
194         cjne    a, #117, printf_format_done
195 printf_uint:
196         mov     a, @r0
197         mov     r2, a
198         dec     r0
199         mov     a, @r0
200         mov     r1, a
201         dec     r0
202 printf_int2bcd:
203         mov     r4, #16
204         mov     r5, #39
205         lcall   div_by_sub
206         mov     r7, a
207         mov     r4, #232
208         mov     r5, #3
209         lcall   div_by_sub
210         swap    a
211         mov     dph, a
212         mov     r4, #100
213         mov     r5, #0
214         lcall   div_by_sub
215         orl     dph, a
216         mov     a, r1
217         mov     b, #10
218         div     ab
219         swap    a
220         orl     a, b
221         mov     dpl, a
222         mov     a, r7
223         sjmp    printf_hex
224
225
226         // Divide r2/r1 by r5/r4 using successive subtraction
227         // returns quotient in r2/r1 and remainder in acc.
228 div_by_sub:
229         mov     r3, #0
230 div_by_sub_loop:
231         inc     r3
232         clr     c
233         mov     a, r1
234         subb    a, r4
235         mov     r1, a
236         mov     a, r2
237         subb    a, r5
238         mov     r2, a
239         jnc     div_by_sub_loop
240         dec     r3
241         mov     a, r1
242         add     a, r4
243         mov     r1, a
244         mov     a, r2
245         addc    a, r5
246         mov     r2, a
247         mov     a, r3
248         ret
249
250
251         /* print a hex digit, either upper 4 bit (msn) or lower 4 bits (lsn) */
252 printf_phex_msn:
253         swap    a
254 printf_phex_lsn:
255         anl     a, #15
256         jnz     printf_phex_ok
257         jb      print_zero_flag, printf_ret
258 printf_phex_ok:
259         clr     print_zero_flag
260         add     a, #0x90
261         da      a
262         addc    a, #0x40
263         da      a
264 printf_putchar:
265 #ifdef DIRECT_SERIAL_OUTPUT
266         jnb     ti, printf_putchar
267         clr     ti
268         mov     sbuf, a
269 #else
270         push    dph
271         push    dpl
272         push    b
273         mov     dpl, a
274         mov     a, r0
275         push    acc
276         lcall   _putchar
277         pop     acc
278         mov     r0, a
279         pop     b
280         pop     dpl
281         pop     dph
282 #endif
283 printf_ret:
284         ret
285
286
287 printf_end:
288         __endasm;
289 }
290
291
292 #endif // defines compatible with printf_tiny