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