* configure.in: added missing mcs51 in status output
[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         mov     a, @r0          // Acc has the character to print
135         dec     r0
136         lcall   printf_putchar
137         sjmp    printf_format_done
138
139
140 printf_format_d:
141         //cjne  a, #'d', printf_format_u
142         cjne    a, #100, printf_format_x
143 #ifndef ALWAYS_PRINT_UNSIGNED
144         mov     a, @r0
145         jnb     acc.7, printf_uint
146         dec     r0
147         mov     a, @r0
148         cpl     a
149         add     a, #1
150         mov     @r0, a
151         inc     r0
152         mov     a, @r0
153         cpl     a
154         addc    a, #0
155         mov     @r0, a
156         //mov   a, #'-'
157         mov     a, #45
158         lcall   printf_putchar
159 #endif
160         sjmp    printf_uint
161
162
163 printf_format_x:
164         //cjne  a, #'x', printf_format_u
165         cjne    a, #120, printf_format_u
166         mov     dph, @r0
167         dec     r0
168         mov     dpl, @r0
169         dec     r0
170         clr     a
171 printf_hex:
172         lcall   printf_phex_lsn
173         mov     a, dph
174         lcall   printf_phex_msn
175         mov     a, dph
176         lcall   printf_phex_lsn
177         mov     a, dpl
178         lcall   printf_phex_msn
179         mov     a, dpl
180         lcall   printf_phex_lsn
181         jnb     print_zero_flag, printf_format_done
182         //mov   a, #'0'
183         mov     a, #48
184         lcall   printf_putchar
185 printf_format_done:
186         pop     dpl
187         pop     dph
188         ljmp    printf_main_loop
189
190
191 printf_format_u:
192         //cjne  a, #'u', printf_format_done
193         cjne    a, #117, printf_format_done
194 printf_uint:
195         mov     a, @r0
196         mov     r2, a
197         dec     r0
198         mov     a, @r0
199         mov     r1, a
200         dec     r0
201 printf_int2bcd:
202         mov     r4, #16
203         mov     r5, #39
204         lcall   div_by_sub
205         mov     r7, a
206         mov     r4, #232
207         mov     r5, #3
208         lcall   div_by_sub
209         swap    a
210         mov     dph, a
211         mov     r4, #100
212         mov     r5, #0
213         lcall   div_by_sub
214         orl     dph, a
215         mov     a, r1
216         mov     b, #10
217         div     ab
218         swap    a
219         orl     a, b
220         mov     dpl, a
221         mov     a, r7
222         sjmp    printf_hex
223
224
225         // Divide r2/r1 by r5/r4 using successive subtraction
226         // returns quotient in r2/r1 and remainder in acc.
227 div_by_sub:
228         mov     r3, #0
229 div_by_sub_loop:
230         inc     r3
231         clr     c
232         mov     a, r1
233         subb    a, r4
234         mov     r1, a
235         mov     a, r2
236         subb    a, r5
237         mov     r2, a
238         jnc     div_by_sub_loop
239         dec     r3
240         mov     a, r1
241         add     a, r4
242         mov     r1, a
243         mov     a, r2
244         addc    a, r5
245         mov     r2, a
246         mov     a, r3
247         ret
248
249
250         /* print a hex digit, either upper 4 bit (msn) or lower 4 bits (lsn) */
251 printf_phex_msn:
252         swap    a
253 printf_phex_lsn:
254         anl     a, #15
255         jnz     printf_phex_ok
256         jb      print_zero_flag, printf_ret
257 printf_phex_ok:
258         clr     print_zero_flag
259         add     a, #0x90
260         da      a
261         addc    a, #0x40
262         da      a
263 printf_putchar:
264 #ifdef DIRECT_SERIAL_OUTPUT
265         jnb     ti, printf_putchar
266         clr     ti
267         mov     sbuf, a
268 #else
269         push    dph
270         push    dpl
271         push    b
272         mov     dpl, a
273         mov     a, r0
274         push    acc
275         lcall   _putchar
276         pop     acc
277         mov     r0, a
278         pop     b
279         pop     dpl
280         pop     dph
281 #endif
282 printf_ret:
283         ret
284
285
286 printf_end:
287         _endasm;
288 }
289
290
291 #endif // defines compatible with printf_tiny
292