/* * VBMouse - printf & logging routines * Copyright (C) 2022 Javier S. Pedro * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DLOG_H #define DLOG_H #include #include #include #include #include // Customizable defines /** If 0, these routines become nops */ #ifndef ENABLE_DLOG #define ENABLE_DLOG 0 #endif /** Target of logging messages. See DLOG_TARGET_* below. */ #ifndef DLOG_TARGET #define DLOG_TARGET DLOG_TARGET_UNDEF #endif /** When using DLOG_TARGET_IOPORT, which IO port to target. * VirtualBox uses 0x504, Bochs, DOSBox and descendants use 0xE9. * When using DLOG_TARGET_SERIAL, the desired UART IO base port. (e.g. COM1 = 0x3F8). */ #ifndef DLOG_TARGET_PORT #define DLOG_TARGET_PORT 0x504 #endif // End of customizable defines /** You provide your own dlog_init and dputc routines. */ #define DLOG_TARGET_UNDEF 0 /** Output to the screen using BIOS TTY interface. */ #define DLOG_TARGET_BIOSTTY 1 /** Output to a serial port. UART io base defined in DLOG_TARGET_PORT. */ #define DLOG_TARGET_SERIAL 2 /** Output to a raw IO port defined in DLOG_TARGET_PORT.*/ #define DLOG_TARGET_IOPORT 3 #if ENABLE_DLOG /** Initializes the debug log port. */ static void dlog_init(); /** Logs a single character to the debug message IO port. */ static inline void dputc(char c); #if DLOG_TARGET == DLOG_TARGET_BIOSTTY static inline void dlog_init() { // No initialization required } static inline void dconputc(char c); #pragma aux dconputc = \ "mov ah, 0x0E" \ "xor bx, bx" \ "int 0x10" \ __parm [AL] \ __modify __exact [AH AL BH BL]; static inline void dputc(char c) { // Need to convert CR into CRLF... if (c == '\n') { dconputc('\n'); dconputc('\r'); } else { dconputc(c); } } #elif DLOG_TARGET == DLOG_TARGET_SERIAL static void dlog_init() { // Comes from https://wiki.osdev.org/Serial_Ports#Initialization outp(DLOG_TARGET_PORT + 1, 0x00); // Disable all interrupts outp(DLOG_TARGET_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) outp(DLOG_TARGET_PORT + 0, 0x01); // Set divisor to 1 (lo byte) 115200 baud outp(DLOG_TARGET_PORT + 1, 0x00); // (hi byte) outp(DLOG_TARGET_PORT + 3, 0x03); // 8 bits, no parity, one stop bit outp(DLOG_TARGET_PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold outp(DLOG_TARGET_PORT + 4, 0x03); // RTS/DSR set, IRQs disabled } static inline void dputc(char c) { while (!(inp(DLOG_TARGET_PORT + 5) & 0x20)); outp(DLOG_TARGET_PORT, c); } #elif DLOG_TARGET == DLOG_TARGET_IOPORT static inline void dlog_init() { // No initialization required } static inline void dputc(char c) { outp(DLOG_TARGET_PORT, c); } #endif /* DLOG_TARGET */ static void d_utoa(unsigned num, unsigned base) { #if UINT_MAX > 99999 char buf[12]; #else char buf[6]; #endif int i = 0; do { unsigned digit = num % base; if (digit < 10) { buf[i] = '0' + digit; } else { buf[i] = 'a' + (digit - 10); } i++; num /= base; } while (num > 0 && i < sizeof(buf)); while (i--) { dputc(buf[i]); } } static void d_ultoa(unsigned long num, unsigned base) { char buf[12]; int i = 0; do { unsigned digit = 0; // Do unsigned long division with remained on whichever platform we are static inline void uldiv(void); #if M_I86 // 16-bit code #if _M_IX86 >= 300 // 16-bit compiler, but for running on 386+: preserve 32-bit regs and use them. #pragma aux uldiv = \ "push eax" /* save 32-bit regs, compiler does not know about them*/ \ "push ecx" \ "push edx" \ "mov eax, [num]" \ "movzx ecx, [base]" \ "xor edx, edx" \ \ "div ecx" \ \ "mov [num], eax" \ "mov [digit], dx" \ \ "pop edx" \ "pop ecx" \ "pop eax" \ __modify __exact []; #else // 16-bit compiler, 16-bit code: 16-bit division #pragma aux uldiv = \ "mov cx, word ptr [num]" \ "mov ax, word ptr [num+2]" \ "mov bx, [base]" \ "xor dx, dx" \ \ "div bx" /* ax = HI(num)/base -> HI quotient, dx = HI(num)%base -> HI remainder */ \ "xchg ax,cx" /* cx = HI(num)/base, ax = LO(num) */ \ "div bx" /* ax = (HI(num)%base)/base -> true quotient , dx = (HI(num)%base)%base -> true remainder */ \ \ "mov word ptr [num], ax" \ "mov word ptr [num+2], cx" \ "mov [digit], dx" \ __modify __exact []; #endif #elif M_I386 // 32-bit compiler, 32-bit code #pragma aux uldiv = \ "mov eax, [num]" \ "mov ecx, [base]" \ "xor edx, edx" \ \ "div ecx" \ \ "mov [num], eax" \ "mov [digit], edx" \ __modify __exact [eax ecx edx]; #else #error unknown platform #endif uldiv(); if (digit < 10) { buf[i] = '0' + digit; } else { buf[i] = 'a' + (digit - 10); } i++; } while (num > 0 && i < sizeof(buf)); while (i--) { dputc(buf[i]); } } static void d_itoa(int num, unsigned base) { unsigned unum; if (num < 0) { dputc('-'); unum = -num; } else { unum = num; } d_utoa(unum, base); } static void d_ltoa(long num, unsigned base) { unsigned long unum; if (num < 0) { dputc('-'); unum = -num; } else { unum = num; } d_ultoa(unum, base); } static unsigned d_strtou(const char * __far *str) { unsigned i = 0; const char *s = *str; char c = *s; while (c >= '0' && c <= '9') { i = (i * 10) + (c - '0'); c = *(++s); } *str = s; return i; } static void d_printstr(const char __far *s, unsigned l) { while (l > 0 && *s) { dputc(*s++); l--; } } static void dprintf(const char *fmt, ...) { va_list va; char c; va_start(va, fmt); while ((c = *(fmt++))) { if (c == '%') { unsigned width, precision, size; bool is_far = false; width = d_strtou(&fmt); if ((c = *fmt) == '.') { fmt++; precision = d_strtou(&fmt); } else { precision = UINT_MAX; } switch ((c = *fmt)) { case 'l': size = sizeof(long); fmt++; break; case 'h': size = sizeof(short); fmt++; break; case 'F': is_far = true; fmt++; break; default: size = sizeof(int); break; } switch ((c = *fmt)) { case 'd': switch (size) { #if INT_MAX != SHRT_MAX case sizeof(short): d_itoa(va_arg(va, short), 10); break; #endif case sizeof(int): d_itoa(va_arg(va, int), 10); break; #if INT_MAX != LONG_MAX case sizeof(long): d_ltoa(va_arg(va, long), 10); break; #endif } break; case 'u': switch (size) { #if INT_MAX != SHRT_MAX case sizeof(short): d_utoa(va_arg(va, unsigned short), 10); break; #endif case sizeof(int): d_utoa(va_arg(va, unsigned int), 10); break; #if INT_MAX != LONG_MAX case sizeof(long): d_ultoa(va_arg(va, unsigned long), 10); break; #endif } break; case 'x': switch (size) { #if INT_MAX != SHRT_MAX case sizeof(short): d_utoa(va_arg(va, unsigned short), 16); break; #endif case sizeof(int): d_utoa(va_arg(va, unsigned int), 16); break; #if INT_MAX != LONG_MAX case sizeof(long): d_ultoa(va_arg(va, unsigned long), 16); break; #endif } break; case 'b': switch (size) { #if INT_MAX != SHRT_MAX case sizeof(short): d_utoa(va_arg(va, unsigned short), 2); break; #endif case sizeof(int): d_utoa(va_arg(va, unsigned int), 2); break; #if INT_MAX != LONG_MAX case sizeof(long): d_ultoa(va_arg(va, long), 2); break; #endif } break; case 'p': if (is_far) { void __far *p = va_arg(va, void __far *); d_utoa(FP_SEG(p), 16); dputc(':'); d_utoa(FP_OFF(p), 16); } else { d_utoa((unsigned) va_arg(va, void*), 16); } break; case 's': d_printstr(is_far ? va_arg(va, const char __far *) : va_arg(va, const char *), precision); break; case 'c': dputc(va_arg(va, char)); break; case '%': dputc('%'); break; } fmt++; } else { dputc(c); } } va_end(va); } static void dputs(const char *s) { char c; while ((c = *(s++))) { dputc(c); } dputc('\n'); } #else /* ENABLE_DLOG */ #define dlog_nop() do { } while(0) #define dlog_init() dlog_nop() #define dputc(c) dlog_nop() #define dputs(s) dlog_nop() #define dprintf(...) dlog_nop() #endif /* ENABLE_DLOG */ #endif /* DLOG_H */