add relative mouse handling in mouse.asm
[retroray] / src / logger.c
1 /*
2 RetroRay - integrated standalone vintage modeller/renderer
3 Copyright (C) 2023  John Tsiombikas <nuclear@mutantstargoat.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stdarg.h>
21 #include "logger.h"
22
23 #if defined(__MSDOS__) || defined(MSDOS)
24 static int setup_serial(int sdev);
25
26 void ser_putchar(int c);
27 void ser_puts(const char *s);
28 void ser_printf(const char *fmt, ...);
29 #else
30 #define USE_STD
31 #endif
32
33 enum { LOG_FILE, LOG_STREAM, LOG_CON, LOG_CB };
34 enum { LOG_DBG, LOG_INFO, LOG_WARN, LOG_ERR };
35
36 struct log_callback {
37         void (*func)(const char*, void*);
38         void *cls;
39 };
40
41 struct log_output {
42         int type, level;
43         union {
44                 FILE *fp;
45                 int con;
46                 struct log_callback cb;
47         } out;
48 };
49
50 #define MAX_OUTPUTS     8
51 static struct log_output outputs[MAX_OUTPUTS];
52 static int num_outputs;
53
54 void init_logger(void)
55 {
56         num_outputs = 0;
57 }
58
59 void cleanup_logger(void)
60 {
61         int i;
62
63         for(i=0; i<num_outputs; i++) {
64                 if(outputs[i].type == LOG_FILE) {
65                         fclose(outputs[i].out.fp);
66                 }
67         }
68         num_outputs = 0;
69 }
70
71 int add_log_file(const char *fname)
72 {
73         FILE *fp;
74         int idx;
75
76         if(num_outputs >= MAX_OUTPUTS) {
77                 return -1;
78         }
79         if(!(fp = fopen(fname, "w"))) {
80                 return -1;
81         }
82         idx = num_outputs++;
83
84         outputs[idx].type = LOG_FILE;
85         outputs[idx].out.fp = fp;
86         return 0;
87 }
88
89 int add_log_stream(FILE *fp)
90 {
91         int idx;
92
93         if(num_outputs >= MAX_OUTPUTS) {
94                 return -1;
95         }
96         idx = num_outputs++;
97
98         outputs[idx].type = LOG_STREAM;
99         outputs[idx].out.fp = fp;
100         return 0;
101 }
102
103 int add_log_console(const char *devname)
104 {
105 #if defined(MSDOS) || defined(__MSDOS__)
106         int i, comport;
107         if(sscanf(devname, "COM%d", &comport) != 1 || comport < 1 || comport > 2) {
108                 return -1;
109         }
110         comport--;
111
112         if(num_outputs >= MAX_OUTPUTS) {
113                 return -1;
114         }
115         for(i=0; i<num_outputs; i++) {
116                 if(outputs[i].type == LOG_CON && outputs[i].out.con == comport) {
117                         return -1;
118                 }
119         }
120         if(setup_serial(comport) == -1) {
121                 return -1;
122         }
123
124         i = num_outputs++;
125         outputs[i].type = LOG_CON;
126         outputs[i].out.con = comport;
127         return 0;
128
129 #elif defined(unix) || defined(__unix__)
130         /* TODO? */
131         return -1;
132 #endif
133 }
134
135 int add_log_callback(void (*cbfunc)(const char*, void*), void *cls)
136 {
137         int idx;
138
139         if(num_outputs >= MAX_OUTPUTS) {
140                 return -1;
141         }
142         idx = num_outputs++;
143
144         outputs[idx].type = LOG_CB;
145         outputs[idx].out.cb.func = cbfunc;
146         outputs[idx].out.cb.cls = cls;
147         return 0;
148 }
149
150 #if defined(__WATCOMC__)
151 #ifndef vsnprintf
152 #define vsnprintf _vsnprintf
153 #endif
154 #endif
155
156 static void logmsg(int type, const char *fmt, va_list ap)
157 {
158         static char *buf;
159         static int bufsz;
160         int i, ret, newsz;
161         char *tmp;
162
163         while((ret = vsnprintf(buf, bufsz, fmt, ap)) > bufsz || ret < 0) {
164                 if(ret > bufsz) {
165                         newsz = ret + 1;
166                 } else {
167                         newsz = bufsz ? bufsz * 2 : 256;
168                 }
169                 if(!(tmp = realloc(buf, newsz))) {
170                         if(buf) {
171                                 buf[bufsz - 1] = 0;
172                         }
173                         break;
174                 }
175                 buf = tmp;
176                 bufsz = newsz;
177         }
178
179         if(!buf) return;
180
181         for(i=0; i<num_outputs; i++) {
182                 switch(outputs[i].type) {
183                 case LOG_FILE:
184                 case LOG_STREAM:
185                         fputs(buf, outputs[i].out.fp);
186                         fflush(outputs[i].out.fp);
187                         break;
188
189 #if defined(MSDOS) || defined(__MSDOS__)
190                 case LOG_CON:
191                         ser_puts(buf);
192                         break;
193 #endif
194                 case LOG_CB:
195                         outputs[i].out.cb.func(buf, outputs[i].out.cb.cls);
196                         break;
197
198                 default:
199                         break;
200                 }
201         }
202 }
203
204 void errormsg(const char *fmt, ...)
205 {
206         va_list ap;
207         va_start(ap, fmt);
208         logmsg(LOG_ERR, fmt, ap);
209         va_end(ap);
210 }
211
212 void warnmsg(const char *fmt, ...)
213 {
214         va_list ap;
215         va_start(ap, fmt);
216         logmsg(LOG_WARN, fmt, ap);
217         va_end(ap);
218 }
219
220 void infomsg(const char *fmt, ...)
221 {
222         va_list ap;
223         va_start(ap, fmt);
224         logmsg(LOG_INFO, fmt, ap);
225         va_end(ap);
226 }
227
228 void dbgmsg(const char *fmt, ...)
229 {
230         va_list ap;
231         va_start(ap, fmt);
232         logmsg(LOG_DBG, fmt, ap);
233         va_end(ap);
234 }
235
236 void verrormsg(const char *fmt, va_list ap)
237 {
238         logmsg(LOG_ERR, fmt, ap);
239 }
240
241 void vwarnmsg(const char *fmt, va_list ap)
242 {
243         logmsg(LOG_ERR, fmt, ap);
244 }
245
246 void vinfomsg(const char *fmt, va_list ap)
247 {
248         logmsg(LOG_ERR, fmt, ap);
249 }
250
251 void vdbgmsg(const char *fmt, va_list ap)
252 {
253         logmsg(LOG_ERR, fmt, ap);
254 }
255
256
257 #if defined(MSDOS) || defined(__MSDOS__)
258 #include <conio.h>
259
260 #define UART1_BASE      0x3f8
261 #define UART2_BASE      0x2f8
262
263 #define UART_DATA       0
264 #define UART_DIVLO      0
265 #define UART_DIVHI      1
266 #define UART_FIFO       2
267 #define UART_LCTL       3
268 #define UART_MCTL       4
269 #define UART_LSTAT      5
270
271 #define DIV_9600                        (115200 / 9600)
272 #define DIV_38400                       (115200 / 38400)
273 #define LCTL_8N1                        0x03
274 #define LCTL_DLAB                       0x80
275 #define FIFO_ENABLE_CLEAR       0x07
276 #define MCTL_DTR_RTS_OUT2       0x0b
277 #define LST_TRIG_EMPTY          0x20
278
279 static unsigned int iobase;
280
281 static int setup_serial(int sdev)
282 {
283         if(sdev < 0 || sdev > 1) {
284                 return -1;
285         }
286         iobase = sdev == 0 ? UART1_BASE : UART2_BASE;
287
288         /* set clock divisor */
289         outp(iobase | UART_LCTL, LCTL_DLAB);
290         outp(iobase | UART_DIVLO, DIV_9600 & 0xff);
291         outp(iobase | UART_DIVHI, DIV_9600 >> 8);
292         /* set format 8n1 */
293         outp(iobase | UART_LCTL, LCTL_8N1);
294         /* assert RTS and DTR */
295         outp(iobase | UART_MCTL, MCTL_DTR_RTS_OUT2);
296         return 0;
297 }
298
299 void ser_putchar(int c)
300 {
301         if(c == '\n') {
302                 ser_putchar('\r');
303         }
304
305         while((inp(iobase | UART_LSTAT) & LST_TRIG_EMPTY) == 0);
306         outp(iobase | UART_DATA, c);
307 }
308
309 void ser_puts(const char *s)
310 {
311         while(*s) {
312                 ser_putchar(*s++);
313         }
314 }
315
316 void ser_printf(const char *fmt, ...)
317 {
318         va_list ap;
319         char buf[512];
320
321         va_start(ap, fmt);
322         vsprintf(buf, fmt, ap);
323         va_end(ap);
324
325         ser_puts(buf);
326 }
327 #endif