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