/************************ *** Team Kitty, 2019 *** *** Sync *** ***********************/ /* This file contains all of the xprintx functions. * The most commonly used will be printf */ #include static inline int Max(int a, int b) { return (a > b ? a : b); } static void printchar(int Char, void* Args); static char* ksprintn(char* Buffer, size_t Number, int Base, int* Size, int Upper); static void snprintfFunc(int Char, void* Args); static void Newline(void* AP); #define BPP 8 const char HexAsciiData[] = "0123456789abcdefhijklmnopqrstuvwxyz"; #define HexToASCII(hex) (HexAsciiData[hex]) #define ToUpper(c) ((c) - 0x20 * (((c) >= 'a') && ((c) <= 'z'))) #define MaxConvertBufferLength (sizeof(size_t) * BPP + 1) typedef struct { char* String; size_t Remain; } snprintf_args; static char* ksprintn(char* Buffer, size_t Number, int Base, int* Size, int Upper) { char* PrintBuffer; char CharBuffer; PrintBuffer = Buffer; *PrintBuffer = '\0'; // Initialize just in case. do { CharBuffer = HexToASCII( Number % Base ); *++PrintBuffer = Upper ? ToUpper(CharBuffer) : CharBuffer; } while (Number /= Base); if(Size) { *Size = PrintBuffer - Buffer; } return (PrintBuffer); } // This function is taken from the Linux kernel. // TODO: make readable. int kvprintf(char const *fmt, void (*func)(int, void*), void *arg, int radix, va_list ap) { #define PCHAR(c) {int cc=(c); if (func) (*func)(cc,arg); else *d++ = cc; retval++; } char nbuf[MaxConvertBufferLength]; char *d; const char *p, *percent, *q; unsigned char *up; int ch, n; uintmax_t num; int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot; int cflag, hflag, jflag, tflag, zflag; int bconv, dwidth, upper; char padc; int stop = 0, retval = 0; num = 0; q = NULL; if (!func) d = (char *) arg; else d = NULL; if (fmt == NULL) fmt = "(fmt null)\n"; if (radix < 2 || radix > 36) radix = 10; for (;;) { padc = ' '; width = 0; while ((ch = (unsigned char)*fmt++) != '%' || stop) { if (ch == '\0') return (retval); PCHAR(ch); } percent = fmt - 1; qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; sign = 0; dot = 0; bconv = 0; dwidth = 0; upper = 0; cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0; reswitch: switch (ch = (unsigned char)*fmt++) { case '.': dot = 1; goto reswitch; case '#': sharpflag = 1; goto reswitch; case '+': sign = 1; goto reswitch; case '-': ladjust = 1; goto reswitch; case '%': PCHAR(ch); break; case '*': if (!dot) { width = va_arg(ap, int); if (width < 0) { ladjust = !ladjust; width = -width; } } else { dwidth = va_arg(ap, int); } goto reswitch; case '0': if (!dot) { padc = '0'; goto reswitch; } /* FALLTHROUGH */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (n = 0;; ++fmt) { n = n * 10 + ch - '0'; ch = *fmt; if (ch < '0' || ch > '9') break; } if (dot) dwidth = n; else width = n; goto reswitch; case 'b': ladjust = 1; bconv = 1; goto handle_nosign; case 'c': width -= 1; if (!ladjust && width > 0) while (width--) PCHAR(padc); PCHAR(va_arg(ap, int)); if (ladjust && width > 0) while (width--) PCHAR(padc); break; case 'D': up = va_arg(ap, unsigned char *); p = va_arg(ap, char *); if (!width) width = 16; while(width--) { PCHAR(HexToASCII(*up >> 4)); PCHAR(HexToASCII(*up & 0x0f)); up++; if (width) for (q=p;*q;q++) PCHAR(*q); } break; case 'd': case 'i': base = 10; sign = 1; goto handle_sign; case 'h': if (hflag) { hflag = 0; cflag = 1; } else hflag = 1; goto reswitch; case 'j': jflag = 1; goto reswitch; case 'l': if (lflag) { lflag = 0; qflag = 1; } else lflag = 1; goto reswitch; case 'n': if (jflag) *(va_arg(ap, intmax_t *)) = retval; else if (qflag) *(va_arg(ap, long long *)) = retval; else if (lflag) *(va_arg(ap, long *)) = retval; else if (zflag) *(va_arg(ap, size_t *)) = retval; else if (hflag) *(va_arg(ap, short *)) = retval; else if (cflag) *(va_arg(ap, char *)) = retval; else *(va_arg(ap, int *)) = retval; break; case 'o': base = 8; goto handle_nosign; case 'p': base = 16; sharpflag = (width == 0); sign = 0; num = (uintptr_t)va_arg(ap, void *); goto number; case 'q': qflag = 1; goto reswitch; case 'r': base = radix; if (sign) goto handle_sign; goto handle_nosign; case 's': p = va_arg(ap, char *); if (p == NULL) p = "(null)"; if (!dot) n = strlen (p); else for (n = 0; n < dwidth && p[n]; n++) continue; width -= n; if (!ladjust && width > 0) while (width--) PCHAR(padc); while (n--) PCHAR(*p++); if (ladjust && width > 0) while (width--) PCHAR(padc); break; case 't': tflag = 1; goto reswitch; case 'u': base = 10; goto handle_nosign; case 'X': upper = 1; __attribute__ ((fallthrough)); // For GCC to stop warning about a fallthrough here case 'x': base = 16; goto handle_nosign; case 'y': base = 16; sign = 1; goto handle_sign; case 'z': zflag = 1; goto reswitch; handle_nosign: sign = 0; if (jflag) num = va_arg(ap, uintmax_t); else if (qflag) num = va_arg(ap, unsigned long long); else if (tflag) num = va_arg(ap, ptrdiff_t); else if (lflag) num = va_arg(ap, unsigned long); else if (zflag) num = va_arg(ap, size_t); else if (hflag) num = (unsigned short)va_arg(ap, int); else if (cflag) num = (unsigned char)va_arg(ap, int); else num = va_arg(ap, unsigned int); if (bconv) { q = va_arg(ap, char *); base = *q++; } goto number; handle_sign: if (jflag) num = va_arg(ap, intmax_t); else if (qflag) num = va_arg(ap, long long); else if (tflag) num = va_arg(ap, ptrdiff_t); else if (lflag) num = va_arg(ap, long); else if (zflag) num = va_arg(ap, size_t); else if (hflag) num = (short)va_arg(ap, int); else if (cflag) num = (char)va_arg(ap, int); else num = va_arg(ap, int); number: if (sign && (intmax_t)num < 0) { neg = 1; num = -(intmax_t)num; } p = ksprintn(nbuf, num, base, &n, upper); tmp = 0; // There's weird behavior here with #. Don't use # to get 0x with zero-padding // (e.g. use 0x%016qx instead, not %#016qx or %#018qx, the latter of which will pad // 16 characters for nonzero numbers but zeros will have 18 characters). // Same with octal: use a leading zero and don't rely on # if you want zero-padding. // # works if you don't need zero padding, though. if (sharpflag && num != 0) { if (base == 8) tmp++; else if (base == 16) tmp += 2; } if (neg) tmp++; if (!ladjust && padc == '0') dwidth = width - tmp; width -= tmp + Max(dwidth, n); dwidth -= n; if (!ladjust) while (width-- > 0) PCHAR(' '); if (neg) PCHAR('-'); if (sharpflag && num != 0) { if (base == 8) { PCHAR('0'); } else if (base == 16) { PCHAR('0'); PCHAR('x'); } } while (dwidth-- > 0) PCHAR('0'); while (*p) PCHAR(*p--); if (bconv && num != 0) { /* %b conversion flag format. */ tmp = retval; while (*q) { n = *q++; if (num & (1 << (n - 1))) { PCHAR(retval != tmp ? ',' : '<'); for (; (n = *q) > ' '; ++q) PCHAR(n); } else for (; *q > ' '; ++q) continue; } if (retval != tmp) { PCHAR('>'); width -= retval - tmp; } } if (ladjust) while (width-- > 0) PCHAR(' '); break; default: while (percent < fmt) PCHAR(*percent++); /* * Since we ignore a formatting argument it is no * longer safe to obey the remaining formatting * arguments as the arguments will no longer match * the format specs. */ stop = 1; break; } } #undef PCHAR } int snprintf(char* String, size_t Length, const char* Format, ...) { int ReturnVal; va_list AP; va_start(AP, Format); ReturnVal = vsnprintf(String, Length, Format, AP); va_end(AP); return (ReturnVal); } int vsnprintf(char* String, size_t Length, const char* Format, va_list AP) { snprintf_args Info; int ReturnVal; Info.String = Format; Info.Remain = Length; ReturnVal = kvprintf(Format, snprintfFunc, &Info, 10, AP); if (Info.Remain >= 1) { *Info.String++ = '\0'; } return ReturnVal; } static void snprintfFunc(int Char, void* Args) { snprintf_args* Info = Args; if(Info->Remain >= 2) { *Info->String++ = Char; Info->Remain--; } } int vsnrprintf(char* String, size_t Length, int Radix, const char* Format, va_list AP) { snprintf_args Info; int ReturnVal; Info.String = Format; Info.Remain = Length; ReturnVal = kvprintf(Format, snprintfFunc, &Info, Radix, AP); if(Info.Remain >= 1) { *Info.String++ = '\0'; } return ReturnVal; } static void printchar(int Char, void* Args) { PRINT_INFO* Arg = (PRINT_INFO*)Args; switch(Char) { case '\033': // TODO: Escape codes! break; case '\x7F': // TODO: Decide on DEL functionality? break; case '\x85': // Next Line (not Newline, this does some hijinks.) Arg->cursorPos = 0; Newline(Arg); break; case '\014': // Form Feed (Clear Screen) ResetScreen(); break; case '\a': // Alert // TODO: Audio alert. break; case '\b': // Backspace if(Arg->cursorPos != 0) { Arg->cursorPos--; } break; case '\r': // Carriage Return Arg->cursorPos = 0; break; case '\v': // Vertical Tab - 6 line breaks. for(int Line = 0; Line < 6; Line++) { Newline(Arg); } break; case '\n': // Newline. Newline(Arg); break; case '\t': // Tab for(int Spaces = 0; Spaces < 8; Spaces++) { // That's right. There are 8 spaces in a tab. RenderText(Arg->defaultGPU, ' ', Arg->charHeight, Arg->charWidth, Arg->charFGColor, Arg->charHLColor, Arg->screenMinX, Arg->screenMinY, Arg->charScale, Arg->cursorPos); Arg->cursorPos++; // Move our cursor one space along. if(Arg->cursorPos * Arg->charWidth * Arg->charScale > (Arg->defaultGPU.Info->HorizontalResolution - Arg->charWidth * Arg->charScale)) { // Check for wraparound Arg->cursorPos = 0; Newline(Arg); } } break; // TODO: More codes! default: RenderText(Arg->defaultGPU, Char, Arg->charHeight, Arg->charWidth, Arg->charFGColor, Arg->charHLColor, Arg->screenMinX, Arg->screenMinY, Arg->charScale, Arg->cursorPos); if(Arg->cursorPos * Arg->charWidth * Arg->charScale > (Arg->defaultGPU.Info->HorizontalResolution - Arg->charWidth * Arg->charScale)) { // Check for wraparound Arg->cursorPos = 0; Newline(Arg); } break; } } static void Newline(void* Args) { PRINT_INFO* Arg = (PRINT_INFO* )Args; if ((Arg->screenMinY + Arg->charHeight * Arg->charScale) > (Arg->defaultGPU.Info->VerticalResolution - Arg->charHeight * Arg->charScale)) { if(!Arg->scrollMode) { // Scrollmode isn't set, so just overwrite the top. Arg->screenMinY = 0; } else if (Arg->scrollMode == Arg->charHeight * Arg->charScale) { // Scroll mode is set to "smooth". This leaves no empty space at the bottom of the screen. size_t ScrollLine = Arg->screenMinY + 2 * Arg->charHeight * Arg->charScale - Arg->defaultGPU.Info->VerticalResolution; Arg->screenMinY = Arg->defaultGPU.Info->VerticalResolution - Arg->charHeight * Arg->charScale; memmoveAVX( (EFI_PHYSICAL_ADDRESS*) Arg->defaultGPU.FrameBufferBase, (EFI_PHYSICAL_ADDRESS*)(Arg->defaultGPU.FrameBufferBase + Arg->defaultGPU.Info->PixelsPerScanline * 4 * ScrollLine), (Arg->defaultGPU.Info->VerticalResolution - ScrollLine) * Arg->defaultGPU.Info->PixelsPerScanline * 4); if(Arg->charBGColor != 0xFF000000) { memsetAVX_By4Bytes( (EFI_PHYSICAL_ADDRESS*)(Arg->defaultGPU.FrameBufferBase + (Arg->defaultGPU.Info->VerticalResolution - ScrollLine) * Arg->defaultGPU.Info->PixelsPerScanline * 4), Arg->charBGColor, (Arg->defaultGPU.Info->VerticalResolution - ScrollLine) * Arg->defaultGPU.Info->PixelsPerScanline); } } // TODO: Implement "quick" scroll. else if (Arg->scrollMode == Arg->defaultGPU.Info->VerticalResolution) { // Wipe screen. if(Arg->charBGColor != 0xFF000000) { memsetAVX_By4Bytes( (EFI_PHYSICAL_ADDRESS*)Arg->defaultGPU.FrameBufferBase, Arg->charBGColor, Arg->defaultGPU.Info->VerticalResolution * Arg->defaultGPU.Info->PixelsPerScanline); } } else { size_t ScrollLine = Arg->screenMinY + 2 * Arg->charHeight * Arg->charScale - Arg->defaultGPU.Info->VerticalResolution; Arg->screenMinY = Arg->defaultGPU.Info->VerticalResolution - Arg->charHeight * Arg->charScale; // This offset correction is needed in case a font size/scale combination is not an integer multiple of the vertical resolution. // Even if it is, changing scales or arg->y could cause a variable offset and that needs to be accounted for. for(size_t SmoothScroll = 0; SmoothScroll < ScrollLine; SmoothScroll += Arg->scrollMode) { memmoveAVX((EFI_PHYSICAL_ADDRESS* )Arg->defaultGPU.FrameBufferBase, (EFI_PHYSICAL_ADDRESS* )(Arg->defaultGPU.FrameBufferBase + Arg->defaultGPU.Info->PixelsPerScanline * 4 * Arg->scrollMode), (Arg->defaultGPU.Info->VerticalResolution - Arg->scrollMode - SmoothScroll) * Arg->defaultGPU.Info->PixelsPerScanline * 4); if(Arg->charBGColor != 0xFF000000) { memsetAVX_By4Bytes((EFI_PHYSICAL_ADDRESS* )(Arg->defaultGPU.FrameBufferBase + (Arg->defaultGPU.Info->VerticalResolution - Arg->scrollMode - SmoothScroll) * Arg->defaultGPU.Info->PixelsPerScanline * 4), Arg->charBGColor, Arg->scrollMode * Arg->defaultGPU.Info->PixelsPerScanline); } } } } else { // No wraparound needed. Just move along. Arg->screenMinY += Arg->charHeight * Arg->charScale; } } int printf(const char* Format, ...) { va_list AP; int ReturnVal; va_start(AP, Format); ReturnVal = kvprintf(Format, printchar, &Print_Info, 10, AP); va_end(AP); return ReturnVal; } int vprintf(const char* Format, va_list AP) { return kvprintf(Format, printchar, &Print_Info, 10, AP); } int sprintf(char* Buffer, const char* Format, ...) { va_list AP; va_start(AP, Format); int ReturnVal = kvprintf(Format, NULL, (void*) Buffer, 10, AP); Buffer[ReturnVal] = '\0'; va_end(AP); return ReturnVal; } int vsprintf(char* Buffer, const char* Format, va_list AP) { int ReturnVal = kvprintf(Format, NULL, (void*) Buffer, 10, AP); Buffer[ReturnVal] = '\0'; return ReturnVal; } void printUnicode(CHAR16* String, size_t Size) { for(size_t Char = 0; Char < Size; Char++) { if( ((char*)String)[Char] != 0) { printf("%c", ((char*)String)[Char]); } } }