315 lines
8.2 KiB
C
315 lines
8.2 KiB
C
|
#include <stdio.h>
|
||
|
#include <arch/i686/io.h>
|
||
|
|
||
|
#include <stdarg.h>
|
||
|
#include <stdbool.h>
|
||
|
|
||
|
const unsigned SCREEN_WIDTH = 80;
|
||
|
const unsigned SCREEN_HEIGHT = 25;
|
||
|
const uint8_t DEFAULT_COLOR = 0x7;
|
||
|
|
||
|
uint8_t* g_ScreenBuffer = (uint8_t*)0xB8000;
|
||
|
int g_ScreenX = 0, g_ScreenY = 0;
|
||
|
|
||
|
void putchr(int x, int y, char c)
|
||
|
{
|
||
|
g_ScreenBuffer[2 * (y * SCREEN_WIDTH + x)] = c;
|
||
|
}
|
||
|
|
||
|
void putcolor(int x, int y, uint8_t color)
|
||
|
{
|
||
|
g_ScreenBuffer[2 * (y * SCREEN_WIDTH + x) + 1] = color;
|
||
|
}
|
||
|
|
||
|
char getchr(int x, int y)
|
||
|
{
|
||
|
return g_ScreenBuffer[2 * (y * SCREEN_WIDTH + x)];
|
||
|
}
|
||
|
|
||
|
uint8_t getcolor(int x, int y)
|
||
|
{
|
||
|
return g_ScreenBuffer[2 * (y * SCREEN_WIDTH + x) + 1];
|
||
|
}
|
||
|
|
||
|
void setcursor(int x, int y)
|
||
|
{
|
||
|
int pos = y * SCREEN_WIDTH + x;
|
||
|
|
||
|
i686_outb(0x3D4, 0x0F);
|
||
|
i686_outb(0x3D5, (uint8_t)(pos & 0xFF));
|
||
|
i686_outb(0x3D4, 0x0E);
|
||
|
i686_outb(0x3D5, (uint8_t)((pos >> 8) & 0xFF));
|
||
|
}
|
||
|
|
||
|
void clrscr()
|
||
|
{
|
||
|
for (int y = 0; y < SCREEN_HEIGHT; y++)
|
||
|
for (int x = 0; x < SCREEN_WIDTH; x++)
|
||
|
{
|
||
|
putchr(x, y, '\0');
|
||
|
putcolor(x, y, DEFAULT_COLOR);
|
||
|
}
|
||
|
|
||
|
g_ScreenX = 0;
|
||
|
g_ScreenY = 0;
|
||
|
setcursor(g_ScreenX, g_ScreenY);
|
||
|
}
|
||
|
|
||
|
void scrollback(int lines)
|
||
|
{
|
||
|
for (int y = lines; y < SCREEN_HEIGHT; y++)
|
||
|
for (int x = 0; x < SCREEN_WIDTH; x++)
|
||
|
{
|
||
|
putchr(x, y - lines, getchr(x, y));
|
||
|
putcolor(x, y - lines, getcolor(x, y));
|
||
|
}
|
||
|
|
||
|
for (int y = SCREEN_HEIGHT - lines; y < SCREEN_HEIGHT; y++)
|
||
|
for (int x = 0; x < SCREEN_WIDTH; x++)
|
||
|
{
|
||
|
putchr(x, y, '\0');
|
||
|
putcolor(x, y, DEFAULT_COLOR);
|
||
|
}
|
||
|
|
||
|
g_ScreenY -= lines;
|
||
|
}
|
||
|
|
||
|
void putc(char c)
|
||
|
{
|
||
|
switch (c)
|
||
|
{
|
||
|
case '\n':
|
||
|
g_ScreenX = 0;
|
||
|
g_ScreenY++;
|
||
|
break;
|
||
|
|
||
|
case '\t':
|
||
|
for (int i = 0; i < 4 - (g_ScreenX % 4); i++)
|
||
|
putc(' ');
|
||
|
break;
|
||
|
|
||
|
case '\r':
|
||
|
g_ScreenX = 0;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
putchr(g_ScreenX, g_ScreenY, c);
|
||
|
g_ScreenX++;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (g_ScreenX >= SCREEN_WIDTH)
|
||
|
{
|
||
|
g_ScreenY++;
|
||
|
g_ScreenX = 0;
|
||
|
}
|
||
|
if (g_ScreenY >= SCREEN_HEIGHT)
|
||
|
scrollback(1);
|
||
|
|
||
|
setcursor(g_ScreenX, g_ScreenY);
|
||
|
}
|
||
|
|
||
|
void puts(const char* str)
|
||
|
{
|
||
|
while(*str)
|
||
|
{
|
||
|
putc(*str);
|
||
|
str++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const char g_HexChars[] = "0123456789abcdef";
|
||
|
|
||
|
void printf_unsigned(unsigned long long number, int radix)
|
||
|
{
|
||
|
char buffer[32];
|
||
|
int pos = 0;
|
||
|
|
||
|
// convert number to ASCII
|
||
|
do
|
||
|
{
|
||
|
unsigned long long rem = number % radix;
|
||
|
number /= radix;
|
||
|
buffer[pos++] = g_HexChars[rem];
|
||
|
} while (number > 0);
|
||
|
|
||
|
// print number in reverse order
|
||
|
while (--pos >= 0)
|
||
|
putc(buffer[pos]);
|
||
|
}
|
||
|
|
||
|
void printf_signed(long long number, int radix)
|
||
|
{
|
||
|
if (number < 0)
|
||
|
{
|
||
|
putc('-');
|
||
|
printf_unsigned(-number, radix);
|
||
|
}
|
||
|
else printf_unsigned(number, radix);
|
||
|
}
|
||
|
|
||
|
#define PRINTF_STATE_NORMAL 0
|
||
|
#define PRINTF_STATE_LENGTH 1
|
||
|
#define PRINTF_STATE_LENGTH_SHORT 2
|
||
|
#define PRINTF_STATE_LENGTH_LONG 3
|
||
|
#define PRINTF_STATE_SPEC 4
|
||
|
|
||
|
#define PRINTF_LENGTH_DEFAULT 0
|
||
|
#define PRINTF_LENGTH_SHORT_SHORT 1
|
||
|
#define PRINTF_LENGTH_SHORT 2
|
||
|
#define PRINTF_LENGTH_LONG 3
|
||
|
#define PRINTF_LENGTH_LONG_LONG 4
|
||
|
|
||
|
void printf(const char* fmt, ...)
|
||
|
{
|
||
|
va_list args;
|
||
|
va_start(args, fmt);
|
||
|
|
||
|
int state = PRINTF_STATE_NORMAL;
|
||
|
int length = PRINTF_LENGTH_DEFAULT;
|
||
|
int radix = 10;
|
||
|
bool sign = false;
|
||
|
bool number = false;
|
||
|
|
||
|
while (*fmt)
|
||
|
{
|
||
|
switch (state)
|
||
|
{
|
||
|
case PRINTF_STATE_NORMAL:
|
||
|
switch (*fmt)
|
||
|
{
|
||
|
case '%': state = PRINTF_STATE_LENGTH;
|
||
|
break;
|
||
|
default: putc(*fmt);
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case PRINTF_STATE_LENGTH:
|
||
|
switch (*fmt)
|
||
|
{
|
||
|
case 'h': length = PRINTF_LENGTH_SHORT;
|
||
|
state = PRINTF_STATE_LENGTH_SHORT;
|
||
|
break;
|
||
|
case 'l': length = PRINTF_LENGTH_LONG;
|
||
|
state = PRINTF_STATE_LENGTH_LONG;
|
||
|
break;
|
||
|
default: goto PRINTF_STATE_SPEC_;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case PRINTF_STATE_LENGTH_SHORT:
|
||
|
if (*fmt == 'h')
|
||
|
{
|
||
|
length = PRINTF_LENGTH_SHORT_SHORT;
|
||
|
state = PRINTF_STATE_SPEC;
|
||
|
}
|
||
|
else goto PRINTF_STATE_SPEC_;
|
||
|
break;
|
||
|
|
||
|
case PRINTF_STATE_LENGTH_LONG:
|
||
|
if (*fmt == 'l')
|
||
|
{
|
||
|
length = PRINTF_LENGTH_LONG_LONG;
|
||
|
state = PRINTF_STATE_SPEC;
|
||
|
}
|
||
|
else goto PRINTF_STATE_SPEC_;
|
||
|
break;
|
||
|
|
||
|
case PRINTF_STATE_SPEC:
|
||
|
PRINTF_STATE_SPEC_:
|
||
|
switch (*fmt)
|
||
|
{
|
||
|
case 'c': putc((char)va_arg(args, int));
|
||
|
break;
|
||
|
|
||
|
case 's':
|
||
|
puts(va_arg(args, const char*));
|
||
|
break;
|
||
|
|
||
|
case '%': putc('%');
|
||
|
break;
|
||
|
|
||
|
case 'd':
|
||
|
case 'i': radix = 10; sign = true; number = true;
|
||
|
break;
|
||
|
|
||
|
case 'u': radix = 10; sign = false; number = true;
|
||
|
break;
|
||
|
|
||
|
case 'X':
|
||
|
case 'x':
|
||
|
case 'p': radix = 16; sign = false; number = true;
|
||
|
break;
|
||
|
|
||
|
case 'o': radix = 8; sign = false; number = true;
|
||
|
break;
|
||
|
|
||
|
// ignore invalid spec
|
||
|
default: break;
|
||
|
}
|
||
|
|
||
|
if (number)
|
||
|
{
|
||
|
if (sign)
|
||
|
{
|
||
|
switch (length)
|
||
|
{
|
||
|
case PRINTF_LENGTH_SHORT_SHORT:
|
||
|
case PRINTF_LENGTH_SHORT:
|
||
|
case PRINTF_LENGTH_DEFAULT: printf_signed(va_arg(args, int), radix);
|
||
|
break;
|
||
|
|
||
|
case PRINTF_LENGTH_LONG: printf_signed(va_arg(args, long), radix);
|
||
|
break;
|
||
|
|
||
|
case PRINTF_LENGTH_LONG_LONG: printf_signed(va_arg(args, long long), radix);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch (length)
|
||
|
{
|
||
|
case PRINTF_LENGTH_SHORT_SHORT:
|
||
|
case PRINTF_LENGTH_SHORT:
|
||
|
case PRINTF_LENGTH_DEFAULT: printf_unsigned(va_arg(args, unsigned int), radix);
|
||
|
break;
|
||
|
|
||
|
case PRINTF_LENGTH_LONG: printf_unsigned(va_arg(args, unsigned long), radix);
|
||
|
break;
|
||
|
|
||
|
case PRINTF_LENGTH_LONG_LONG: printf_unsigned(va_arg(args, unsigned long long), radix);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// reset state
|
||
|
state = PRINTF_STATE_NORMAL;
|
||
|
length = PRINTF_LENGTH_DEFAULT;
|
||
|
radix = 10;
|
||
|
sign = false;
|
||
|
number = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
fmt++;
|
||
|
}
|
||
|
|
||
|
va_end(args);
|
||
|
}
|
||
|
|
||
|
void print_buffer(const char* msg, const void* buffer, uint32_t count)
|
||
|
{
|
||
|
const uint8_t* u8Buffer = (const uint8_t*)buffer;
|
||
|
|
||
|
puts(msg);
|
||
|
for (uint16_t i = 0; i < count; i++)
|
||
|
{
|
||
|
putc(g_HexChars[u8Buffer[i] >> 4]);
|
||
|
putc(g_HexChars[u8Buffer[i] & 0xF]);
|
||
|
}
|
||
|
puts("\n");
|
||
|
}
|