From 28d5d8d6f0b9da979030465c304157a4919ca6de Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Fri, 6 Oct 2023 08:08:35 +0300 Subject: [PATCH] initial commit --- .gitignore | 11 + Makefile | 39 ++++ com32.ld | 41 ++++ src/config.h | 10 + src/game.c | 31 +++ src/game.h | 22 ++ src/kern/asmops.h | 85 ++++++++ src/kern/contty.c | 310 +++++++++++++++++++++++++++ src/kern/contty.h | 87 ++++++++ src/kern/desc.h | 27 +++ src/kern/intr.c | 279 ++++++++++++++++++++++++ src/kern/intr.h | 90 ++++++++ src/kern/intr_s.asm | 161 ++++++++++++++ src/kern/intrtab.h | 38 ++++ src/kern/kbregs.h | 81 +++++++ src/kern/kbscan.h | 52 +++++ src/kern/keyb.c | 317 +++++++++++++++++++++++++++ src/kern/keyb.h | 79 +++++++ src/kern/main.c | 63 ++++++ src/kern/mem.c | 314 +++++++++++++++++++++++++++ src/kern/mem.h | 48 +++++ src/kern/panic.c | 81 +++++++ src/kern/panic.h | 24 +++ src/kern/power.c | 45 ++++ src/kern/power.h | 23 ++ src/kern/psaux.c | 208 ++++++++++++++++++ src/kern/psaux.h | 34 +++ src/kern/regs.asm | 57 +++++ src/kern/segm.c | 173 +++++++++++++++ src/kern/segm.h | 41 ++++ src/kern/segm_s.asm | 64 ++++++ src/kern/serial.c | 361 +++++++++++++++++++++++++++++++ src/kern/serial.h | 46 ++++ src/kern/timer.c | 192 +++++++++++++++++ src/kern/timer.h | 43 ++++ src/kern/tss.h | 38 ++++ src/libc/alloca.h | 23 ++ src/libc/assert.h | 28 +++ src/libc/ctype.c | 73 +++++++ src/libc/ctype.h | 34 +++ src/libc/errno.h | 41 ++++ src/libc/float.h | 24 +++ src/libc/inttypes.h | 31 +++ src/libc/limits.h | 36 ++++ src/libc/malloc.c | 429 +++++++++++++++++++++++++++++++++++++ src/libc/math.c | 55 +++++ src/libc/math.h | 36 ++++ src/libc/rand.c | 65 ++++++ src/libc/setjmp.c | 28 +++ src/libc/setjmp.h | 26 +++ src/libc/stdarg.h | 29 +++ src/libc/stddef.h | 32 +++ src/libc/stdio.c | 409 +++++++++++++++++++++++++++++++++++ src/libc/stdio.h | 90 ++++++++ src/libc/stdlib.c | 272 +++++++++++++++++++++++ src/libc/stdlib.h | 53 +++++ src/libc/string.c | 260 ++++++++++++++++++++++ src/libc/string.h | 52 +++++ src/libc/string_s.asm | 174 +++++++++++++++ src/loader.asm | 570 +++++++++++++++++++++++++++++++++++++++++++++++++ src/macros.inc | 33 +++ src/startup.asm | 47 ++++ 62 files changed, 6565 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 com32.ld create mode 100644 src/config.h create mode 100644 src/game.c create mode 100644 src/game.h create mode 100644 src/kern/asmops.h create mode 100644 src/kern/contty.c create mode 100644 src/kern/contty.h create mode 100644 src/kern/desc.h create mode 100644 src/kern/intr.c create mode 100644 src/kern/intr.h create mode 100644 src/kern/intr_s.asm create mode 100644 src/kern/intrtab.h create mode 100644 src/kern/kbregs.h create mode 100644 src/kern/kbscan.h create mode 100644 src/kern/keyb.c create mode 100644 src/kern/keyb.h create mode 100644 src/kern/main.c create mode 100644 src/kern/mem.c create mode 100644 src/kern/mem.h create mode 100644 src/kern/panic.c create mode 100644 src/kern/panic.h create mode 100644 src/kern/power.c create mode 100644 src/kern/power.h create mode 100644 src/kern/psaux.c create mode 100644 src/kern/psaux.h create mode 100644 src/kern/regs.asm create mode 100644 src/kern/segm.c create mode 100644 src/kern/segm.h create mode 100644 src/kern/segm_s.asm create mode 100644 src/kern/serial.c create mode 100644 src/kern/serial.h create mode 100644 src/kern/timer.c create mode 100644 src/kern/timer.h create mode 100644 src/kern/tss.h create mode 100644 src/libc/alloca.h create mode 100644 src/libc/assert.h create mode 100644 src/libc/ctype.c create mode 100644 src/libc/ctype.h create mode 100644 src/libc/errno.h create mode 100644 src/libc/float.h create mode 100644 src/libc/inttypes.h create mode 100644 src/libc/limits.h create mode 100644 src/libc/malloc.c create mode 100644 src/libc/math.c create mode 100644 src/libc/math.h create mode 100644 src/libc/rand.c create mode 100644 src/libc/setjmp.c create mode 100644 src/libc/setjmp.h create mode 100644 src/libc/stdarg.h create mode 100644 src/libc/stddef.h create mode 100644 src/libc/stdio.c create mode 100644 src/libc/stdio.h create mode 100644 src/libc/stdlib.c create mode 100644 src/libc/stdlib.h create mode 100644 src/libc/string.c create mode 100644 src/libc/string.h create mode 100644 src/libc/string_s.asm create mode 100644 src/loader.asm create mode 100644 src/macros.inc create mode 100644 src/startup.asm diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cec07fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +*.o +*.d +*.swp +*.com +*.map +RUN +disasm +dosbox.conf +*.log +tofloppy +*.s diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9a8942f --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +csrc = $(wildcard src/*.c) $(wildcard src/kern/*.c) $(wildcard src/libc/*.c) +ssrc = $(wildcard src/*.asm) $(wildcard src/kern/*.asm) $(wildcard src/libc/*.asm) +obj = $(csrc:.c=.o) $(ssrc:.asm=.o) +dep = $(csrc:.c=.d) +bin = game.com + +warn = -pedantic -Wall -Wno-unused-function +opt = -O2 +inc = -Isrc -Isrc/kern -Isrc/libc + +AS = nasm + +ASFLAGS = -Isrc/ -Isrc/kern/ +CFLAGS = -m32 -march=i386 $(warn) $(opt) $(dbg) -fno-pic -ffreestanding \ + -fno-stack-protector -mpreferred-stack-boundary=2 -nostdinc -ffast-math \ + -fno-asynchronous-unwind-tables $(inc) $(def) -MMD +LDFLAGS = -m elf_i386 -nostdlib -T com32.ld -Map game.map + +$(bin): $(obj) + $(LD) -o $@ $(obj) $(LDFLAGS) + +-include $(dep) + +%.o: %.asm + $(AS) -o $@ -f elf $(ASFLAGS) $< + +%.s: %.c + $(CC) $(CFLAGS) -masm=intel -S $< -o $@ + +.PHONY: clean +clean: + rm -f $(obj) $(bin) + +.PHONY: cleandep +cleandep: + rm -f $(dep) + +disasm: $(bin) + ndisasm -o 0x100 -b 16 $< >$@ diff --git a/com32.ld b/com32.ld new file mode 100644 index 0000000..213b4e2 --- /dev/null +++ b/com32.ld @@ -0,0 +1,41 @@ +OUTPUT_FORMAT(binary) +ENTRY(_start) + +SECTIONS { + /* loader starts at :100h */ + . = 0x100; + .loader : { * (.loader); } + + . = ALIGN(4); + _ldr_main_start = .; + _ldr_main_end = _ldr_main_start + _main_size; + + /* main program will be moved to 2MB by the loader */ + . = 2M; + .main ALIGN(4): AT (_ldr_main_start) { + _main_start = .; + * (.startup); + * (.text*); + * (.rodata*); + * (.data*); + . = ALIGN(4); + } + + .bss ALIGN(4): AT (_ldr_main_start + SIZEOF(.main)) { + . = ALIGN(4); + _bss_start = .; + * (.bss*); + * (COMMON); + . = ALIGN(4); + _bss_end = .; + } + + . = ALIGN(4); + _main_size = . - _main_start; + _mem_start = .; + + /DISCARD/ : { + *(.note.GNU-stack); + *(.comment); + } +} diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..ba9415d --- /dev/null +++ b/src/config.h @@ -0,0 +1,10 @@ +#ifndef PCBOOT_CONFIG_H_ +#define PCBOOT_CONFIG_H_ + +/* frequency of generated timer ticks in hertz */ +#define TICK_FREQ_HZ 250 + +#undef CON_TEXTMODE +#define CON_SERIAL + +#endif /* PCBOOT_CONFIG_H_ */ diff --git a/src/game.c b/src/game.c new file mode 100644 index 0000000..f04984c --- /dev/null +++ b/src/game.c @@ -0,0 +1,31 @@ +#include +#include "game.h" + +int game_init(void) +{ + return 0; +} + +void game_shutdown(void) +{ +} + +void game_draw(void) +{ + memset(framebuf, 2, 64000); + + game_swap_buffers(); +} + +void game_keyboard(int key, int press) +{ + if(key == 27) game_quit(); +} + +void game_mouse(int bn, int press, int x, int y) +{ +} + +void game_motion(int x, int y) +{ +} diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000..9eb4cc9 --- /dev/null +++ b/src/game.h @@ -0,0 +1,22 @@ +#ifndef GAME_H_ +#define GAME_H_ + +#define FB_WIDTH 320 +#define FB_HEIGHT 200 + +extern unsigned char *framebuf, *vmem; + +int game_init(void); +void game_shutdown(void); + +void game_draw(void); + +void game_reshape(int x, int y); +void game_keyboard(int key, int press); +void game_mouse(int bn, int press, int x, int y); +void game_motion(int x, int y); + +void game_quit(void); +void game_swap_buffers(void); + +#endif /* GAME_H_ */ diff --git a/src/kern/asmops.h b/src/kern/asmops.h new file mode 100644 index 0000000..5d8ec31 --- /dev/null +++ b/src/kern/asmops.h @@ -0,0 +1,85 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2023 John Tsiombikas + +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 3 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, see . +*/ +#ifndef ASMOPS_H_ +#define ASMOPS_H_ + +#include + +#define enable_intr() asm volatile("sti") +#define disable_intr() asm volatile("cli") +#define halt_cpu() asm volatile("hlt") + +#define CALLER_EIP(eip) \ + asm volatile( \ + "mov 4(%%ebp), %0\n\t" \ + : "=a" ((uint32_t)eip)) + +static inline uint8_t inp(uint16_t port) +{ + uint8_t res; + asm volatile ( + "inb %1, %0\n\t" + : "=a" (res) + : "dN" (port)); + return res; +} + +static inline uint16_t inpw(uint16_t port) +{ + uint16_t res; + asm volatile ( + "inw %1, %0\n\t" + : "=a" (res) + : "dN" (port)); + return res; +} + +static inline uint32_t inpd(uint16_t port) +{ + uint32_t res; + asm volatile ( + "inl %1, %0\n\t" + : "=a" (res) + : "dN" (port)); + return res; +} + +#define outp(port, src) \ + asm volatile( \ + "outb %0, %1\n\t" \ + :: "a" ((uint8_t)(src)), "dN" ((uint16_t)(port))) + +#define outpw(port, src) \ + asm volatile( \ + "outw %0, %1\n\t" \ + :: "a" ((uint16_t)(src)), "dN" ((uint16_t)(port))) + +#define outpd(port, src) \ + asm volatile( \ + "outl %0, %1\n\t" \ + :: "a" ((uint32_t)(src)), "dN" ((uint16_t)(port))) + +/* delay for about 1us */ +#define iodelay() outp(0x80, 0) + +#define get_ebp(ebp) \ + asm volatile( \ + "mov %%ebp, %0\n\t" \ + : "=g"((uint32_t)(ebp))) + +#endif /* ASMOPS_H_ */ diff --git a/src/kern/contty.c b/src/kern/contty.c new file mode 100644 index 0000000..2ed22e7 --- /dev/null +++ b/src/kern/contty.c @@ -0,0 +1,310 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2023 John Tsiombikas + +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 3 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, see . +*/ +#include +#include +#include +#include "contty.h" +#include "serial.h" +#include "asmops.h" +#include "config.h" + +#define VIRT_ROWS 200 + +#define NCOLS 80 +#define NROWS 25 +#define TEXT_ADDR ((char*)0xb8000) + +#define CRTC_ADDR 0x3d4 +#define CRTC_DATA 0x3d5 + +#define CRTC_REG_CURSTART 0x0a +#define CRTC_REG_CUREND 0x0b +#define CRTC_REG_START_H 0x0c +#define CRTC_REG_START_L 0x0d +#define CRTC_REG_CURLOC_H 0x0e +#define CRTC_REG_CURLOC_L 0x0f + +#define VMEM_CHAR(c, attr) \ + (((uint16_t)(c) & 0xff) | ((uint16_t)(attr) << 8)) + +static void scroll(void); +static void crtc_cursor(int x, int y); +static void crtc_getcursor(int *x, int *y); +static void crtc_setstart(int y); +static int crtc_getstart(void); +static inline unsigned char crtc_read(int reg); +static inline void crtc_write(int reg, unsigned char val); + +static int cursor_x, cursor_y; +static unsigned char txattr = 0x07; +static int start_line; +static unsigned char cy0, cy1; +static int curvis; +static int scr_on = 1; + + +int con_init(void) +{ +#ifdef CON_SERIAL + ser_open(1, 9600, SER_8N1); +#endif + +#ifdef CON_TEXTMODE + cy0 = crtc_read(CRTC_REG_CURSTART); + curvis = cy0 & 0x20 ? 1 : 0; + cy0 &= 0x1f; + cy1 = crtc_read(CRTC_REG_CUREND) & 0x1f; + + start_line = crtc_getstart(); + crtc_getcursor(&cursor_x, &cursor_y); + con_show_cursor(1); + scr_on = 1; +#endif + + return 0; +} + +void con_scr_enable(void) +{ + scr_on = 1; +} + +void con_scr_disable(void) +{ + scr_on = 0; +} + +void con_show_cursor(int show) +{ +#ifdef CON_TEXTMODE + unsigned char val = cy0 & 0x1f; + if(!show) { + val |= 0x20; + } + crtc_write(CRTC_REG_CURSTART, val); + curvis = show; +#endif +} + +void con_cursor(int x, int y) +{ +#ifdef CON_TEXTMODE + cursor_x = x; + cursor_y = y; + crtc_cursor(x, y); +#endif +} + +void con_curattr(int shape, int blink) +{ +#ifdef CON_TEXTMODE + unsigned char start; + cy0 = (shape == CON_CURSOR_LINE) ? 0xd : 0; + cy1 = 0xe; + + start = cy0; + if(curvis) { + start |= 0x20; + } + + crtc_write(CRTC_REG_CURSTART, start); + crtc_write(CRTC_REG_CUREND, cy0); +#endif +} + +void con_fgcolor(int c) +{ + txattr = (txattr & 0xf0) | c; +} + +void con_bgcolor(int c) +{ + txattr = (txattr & 0x0f) | (c << 4); +} + +void con_setattr(unsigned char attr) +{ + txattr = attr; +} + +unsigned char con_getattr(void) +{ + return txattr; +} + +void con_clear(void) +{ +#ifdef CON_TEXTMODE + memset16(TEXT_ADDR, VMEM_CHAR(' ', txattr), NCOLS * NROWS); + + start_line = 0; + crtc_setstart(0); + + cursor_x = cursor_y = 0; + crtc_cursor(0, 0); +#endif +} + +static inline void linefeed(void) +{ + if(++cursor_y >= NROWS) { + scroll(); + --cursor_y; + } +} + +void con_putchar(int c) +{ +#ifdef CON_TEXTMODE + if(scr_on) { + switch(c) { + case '\n': + linefeed(); + case '\r': + cursor_x = 0; + crtc_cursor(cursor_x, cursor_y); + break; + + case '\t': + cursor_x = (cursor_x & 0x7) + 8; + if(cursor_x >= NCOLS) { + linefeed(); + cursor_x = 0; + } + crtc_cursor(cursor_x, cursor_y); + break; + + case '\b': + if(cursor_x > 0) cursor_x--; + con_putchar_scr(cursor_x, cursor_y, ' '); + crtc_cursor(cursor_x, cursor_y); + break; + + default: + con_putchar_scr(cursor_x, cursor_y, c); + + if(++cursor_x >= NCOLS) { + linefeed(); + cursor_x = 0; + } + crtc_cursor(cursor_x, cursor_y); + } + } +#endif + +#ifdef CON_SERIAL + ser_putchar(c); +#endif +} + +void con_putchar_scr(int x, int y, int c) +{ +#ifdef CON_TEXTMODE + uint16_t *ptr = (uint16_t*)TEXT_ADDR; + ptr[(y + start_line) * NCOLS + x] = VMEM_CHAR(c, txattr); +#endif +} + +int con_printf(int x, int y, const char *fmt, ...) +{ +#ifdef CON_TEXTMODE + va_list ap; + char buf[81]; + char *ptr = buf; + + va_start(ap, fmt); + vsnprintf(buf, 80, fmt, ap); + va_end(ap); + + while(*ptr && x < 80) { + con_putchar_scr(x++, y, *ptr++); + } + return ptr - buf; +#else + return 0; +#endif +} + +static void scroll(void) +{ + int new_line; + + if(++start_line > VIRT_ROWS - NROWS) { + /* The bottom of the visible range reached the end of our text buffer. + * Copy the rest of the lines to the top and reset start_line. + */ + memcpy(TEXT_ADDR, TEXT_ADDR + start_line * NCOLS, (NROWS - 1) * NCOLS * 2); + start_line = 0; + } + + /* clear the next line that will be revealed by scrolling */ + new_line = start_line + NROWS - 1; + memset16(TEXT_ADDR + new_line * NCOLS * 2, VMEM_CHAR(' ', txattr), NCOLS); + crtc_setstart(start_line); +} + +static void crtc_cursor(int x, int y) +{ + unsigned int addr; + + addr = (y + start_line) * NCOLS + x; + + crtc_write(CRTC_REG_CURLOC_L, addr); + crtc_write(CRTC_REG_CURLOC_H, addr >> 8); +} + +static void crtc_getcursor(int *x, int *y) +{ + unsigned int addr; + + addr = crtc_read(CRTC_REG_CURLOC_L); + addr |= (unsigned int)crtc_read(CRTC_REG_CURLOC_H) << 8; + + *y = addr / NCOLS - start_line; + *x = addr % NCOLS; +} + +static void crtc_setstart(int y) +{ + unsigned int addr = y * NCOLS; + + crtc_write(CRTC_REG_START_L, addr); + crtc_write(CRTC_REG_START_H, addr >> 8); +} + +static int crtc_getstart(void) +{ + unsigned int addr; + + addr = crtc_read(CRTC_REG_START_L); + addr |= (unsigned int)crtc_read(CRTC_REG_START_H) << 8; + + return addr / NCOLS; +} + +static inline unsigned char crtc_read(int reg) +{ + outp(CRTC_ADDR, reg); + return inp(CRTC_DATA); +} + +static inline void crtc_write(int reg, unsigned char val) +{ + outp(CRTC_ADDR, reg); + outp(CRTC_DATA, val); +} diff --git a/src/kern/contty.h b/src/kern/contty.h new file mode 100644 index 0000000..fcc6e37 --- /dev/null +++ b/src/kern/contty.h @@ -0,0 +1,87 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef CONTTY_H_ +#define CONTTY_H_ + +enum { + CON_CURSOR_LINE, + CON_CURSOR_BLOCK +}; + +enum { + BLACK, + BLUE, + GREEN, + CYAN, + RED, + MAGENTA, + BROWN, + LTGREY, + GREY, + LTBLUE, + LTGREEN, + LTCYAN, + LTRED, + LTMAGENTA, + YELLOW, + WHITE +}; +#define BRIGHT 8 +#define FG_BRIGHT 0x08 +#define BG_BRIGHT 0x80 + +enum { + G_DIAMOND = 0x04, + G_CHECKER = 0xb1, + G_LR_CORNER = 0xd9, + G_UR_CORNER = 0xbf, + G_UL_CORNER = 0xda, + G_LL_CORNER = 0xc0, + G_CROSS = 0xc5, + G_HLINE = 0xc4, + G_L_TEE = 0xc3, + G_R_TEE = 0xb4, + G_B_TEE = 0xc1, + G_T_TEE = 0xc2, + G_VLINE = 0xb3, + G_CDOT = 0xf8, + + G_HDBL = 0xcd, + G_UL_HDBL = 0xd5, + G_UR_HDBL = 0xb8, + G_T_HDBL_TEE = 0xd1 +}; + + +int con_init(void); +void con_scr_enable(void); +void con_scr_disable(void); +void con_show_cursor(int show); +void con_cursor(int x, int y); +void con_curattr(int shape, int blink); +void con_fgcolor(int c); +void con_bgcolor(int c); +void con_setattr(unsigned char attr); +unsigned char con_getattr(void); +void con_clear(void); +void con_putchar(int c); + +void con_putchar_scr(int x, int y, int c); +int con_printf(int x, int y, const char *fmt, ...); + +#endif /* CONTTY_H_ */ diff --git a/src/kern/desc.h b/src/kern/desc.h new file mode 100644 index 0000000..d581f94 --- /dev/null +++ b/src/kern/desc.h @@ -0,0 +1,27 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef DESC_H_ +#define DESC_H_ + +#include + +typedef struct { + uint16_t d[4]; +} desc_t; + +#endif /* DESC_H_ */ diff --git a/src/kern/intr.c b/src/kern/intr.c new file mode 100644 index 0000000..616189e --- /dev/null +++ b/src/kern/intr.c @@ -0,0 +1,279 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#include +#include "config.h" +#include "intr.h" +#include "desc.h" +#include "segm.h" +#include "asmops.h" +#include "panic.h" + +#define SYSCALL_INT 0x80 + +/* IDT gate descriptor bits */ +#define GATE_TASK (5 << 8) +#define GATE_INTR (6 << 8) +#define GATE_TRAP (7 << 8) +#define GATE_DEFAULT (1 << 11) +#define GATE_PRESENT (1 << 15) + +/* PIC command and data ports */ +#define PIC1_CMD 0x20 +#define PIC1_DATA 0x21 +#define PIC2_CMD 0xa0 +#define PIC2_DATA 0xa1 + +/* PIC initialization command word 1 bits */ +#define ICW1_ICW4_NEEDED (1 << 0) +#define ICW1_SINGLE (1 << 1) +#define ICW1_INTERVAL4 (1 << 2) +#define ICW1_LEVEL (1 << 3) +#define ICW1_INIT (1 << 4) +/* PIC initialization command word 4 bits */ +#define ICW4_8086 (1 << 0) +#define ICW4_AUTO_EOI (1 << 1) +#define ICW4_BUF_SLAVE (1 << 3) /* 1000 */ +#define ICW4_BUF_MASTER (3 << 2) /* 1100 */ +#define ICW4_SPECIAL (1 << 4) + +/* PIC operation command word 2 bits */ +#define OCW2_EOI (1 << 5) + + +static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type); + +/* defined in intr_asm.S */ +void set_idt(uint32_t addr, uint16_t limit); +void intr_entry_default(void); +void irq7_entry_check_spurious(void); +void irq15_entry_check_spurious(void); + +/* the IDT (interrupt descriptor table) */ +static desc_t idt[256] __attribute__((aligned(8))); + +/* table of handler functions for all interrupts */ +static intr_func_t intr_func[256]; + +static struct intr_frame *cur_intr_frame; +static int eoi_pending; + +#define INTR_ENTRY_EC(n, name) \ + void intr_entry_##name(void); \ + set_intr_entry(n, intr_entry_##name); +#define INTR_ENTRY_NOEC(n, name) INTR_ENTRY_EC(n, name) + +void init_intr(void) +{ + int i; + + set_idt((uint32_t)idt, sizeof idt - 1); + + /* initialize all entry points and interrupt handlers */ + for(i=0; i<256; i++) { + set_intr_entry(i, intr_entry_default); + interrupt(i, 0); + } + + /* by including intrtab.h here the series of INTR_ENTRY_* macros will be + * expanded to a series of function prototypes for all interrupt entry + * points and the corresponding calls to set_intr_entry to set up the IDT + * slots + */ +#include "intrtab.h" + + /* change irq7 and irq15 to special entry points which first + * make sure we didn't get a spurious interrupt before proceeding + */ + set_intr_entry(IRQ_TO_INTR(7), irq7_entry_check_spurious); + set_intr_entry(IRQ_TO_INTR(15), irq15_entry_check_spurious); + + /* initialize the programmable interrupt controller + * setting up the maping of IRQs [0, 15] to interrupts [32, 47] + */ + prog_pic(IRQ_OFFSET); + eoi_pending = 0; +} + +void cleanup_intr(void) +{ + disable_intr(); + /* reprogram the PIC to the default BIOS offset (8) */ + prog_pic(8); +} + +/* retrieve the current interrupt frame. + * returns 0 when called during init. + */ +struct intr_frame *get_intr_frame(void) +{ + return cur_intr_frame; +} + +/* set an interrupt handler function for a particular interrupt */ +void interrupt(int intr_num, intr_func_t func) +{ + int iflag = get_intr_flag(); + disable_intr(); + intr_func[intr_num] = func; + set_intr_flag(iflag); +} + +/* this function is called from all interrupt entry points + * it calls the appropriate interrupt handlers if available and handles + * sending an end-of-interrupt command to the PICs when finished. + */ +void dispatch_intr(struct intr_frame frm) +{ + cur_intr_frame = &frm; + + if(IS_IRQ(frm.inum)) { + eoi_pending = frm.inum; + } + + if(intr_func[frm.inum]) { + intr_func[frm.inum](frm.inum); + } else { + if(frm.inum < 32) { + panic("unhandled exception %u, error code: %u, at cs:eip=%x:%x\n", + frm.inum, frm.err, frm.cs, frm.eip); + } + /*printf("unhandled interrupt %d\n", frm.inum);*/ + } + + disable_intr(); + if(eoi_pending) { + end_of_irq(INTR_TO_IRQ(eoi_pending)); + } +} + +void prog_pic(int offs) +{ + /* send ICW1 saying we'll follow with ICW4 later on */ + outp(PIC1_CMD, ICW1_INIT | ICW1_ICW4_NEEDED); + outp(PIC2_CMD, ICW1_INIT | ICW1_ICW4_NEEDED); + /* send ICW2 with IRQ remapping */ + outp(PIC1_DATA, offs); + outp(PIC2_DATA, offs + 8); + /* send ICW3 to setup the master/slave relationship */ + /* ... set bit3 = 3rd interrupt input has a slave */ + outp(PIC1_DATA, 4); + /* ... set slave ID to 2 */ + outp(PIC2_DATA, 2); + /* send ICW4 to set 8086 mode (no calls generated) */ + outp(PIC1_DATA, ICW4_8086); + outp(PIC2_DATA, ICW4_8086); + /* done, just reset the data port to 0 */ + outp(PIC1_DATA, 0); + outp(PIC2_DATA, 0); +} + +static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type) +{ + /* first 16bit part is the low 16bits of the entry address */ + desc->d[0] = addr & 0xffff; + /* second 16bit part is the segment selector for the entry code */ + desc->d[1] = sel; + /* third 16bit part has the privilege level, type, and present bit */ + desc->d[2] = ((dpl & 3) << 13) | type | GATE_DEFAULT | GATE_PRESENT; + /* last 16bit part is the high 16bits of the entry address */ + desc->d[3] = (addr & 0xffff0000) >> 16; +} + +#define IS_TRAP(n) ((n) >= 32 && !IS_IRQ(n)) +void set_intr_entry(int num, void (*handler)(void)) +{ + int type = IS_TRAP(num) ? GATE_TRAP : GATE_INTR; + + /* the syscall interrupt has to have a dpl of 3 otherwise calling it from + * user space will raise a general protection exception. All the rest should + * have a dpl of 0 to disallow user programs to execute critical interrupt + * handlers and possibly crashing the system. + */ + int dpl = (num == SYSCALL_INT) ? 3 : 0; + + gate_desc(idt + num, selector(SEGM_CODE, 0), (uint32_t)handler, dpl, type); +} + +void set_pic_mask(int pic, unsigned char mask) +{ + outp(pic > 0 ? PIC2_DATA : PIC1_DATA, mask); +} + +unsigned char get_pic_mask(int pic) +{ + return inp(pic > 0 ? PIC2_DATA : PIC1_DATA); +} + +void mask_irq(int irq) +{ + int port; + unsigned char mask; + + if(irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + irq -= 8; + } + + mask = inp(port) | (1 << irq); + outp(port, mask); +} + +void unmask_irq(int irq) +{ + int port; + unsigned char mask; + + if(irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + irq -= 8; + } + + mask = inp(port) & ~(1 << irq); + outp(port, mask); +} + + +void end_of_irq(int irq) +{ + int intr_state = get_intr_flag(); + disable_intr(); + + if(!eoi_pending) { + set_intr_flag(intr_state); + return; + } + eoi_pending = 0; + + if(irq > 7) { + outp(PIC2_CMD, OCW2_EOI); + } + outp(PIC1_CMD, OCW2_EOI); + + set_intr_flag(intr_state); +} + +#ifdef ENABLE_GDB_STUB +void exceptionHandler(int id, void (*func)()) +{ + set_intr_entry(id, func); +} +#endif diff --git a/src/kern/intr.h b/src/kern/intr.h new file mode 100644 index 0000000..3cfbe8b --- /dev/null +++ b/src/kern/intr.h @@ -0,0 +1,90 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef INTR_H_ +#define INTR_H_ + +#include +#include "asmops.h" + +/* offset used to remap IRQ numbers (+32) */ +#define IRQ_OFFSET 32 +/* conversion macros between IRQ and interrupt numbers */ +#define IRQ_TO_INTR(x) ((x) + IRQ_OFFSET) +#define INTR_TO_IRQ(x) ((x) - IRQ_OFFSET) +/* checks whether a particular interrupt is an remapped IRQ */ +#define IS_IRQ(n) ((n) >= IRQ_OFFSET && (n) < IRQ_OFFSET + 16) + +/* general purpose registers as they are pushed by pusha */ +struct registers { + uint32_t edi, esi, ebp, esp; + uint32_t ebx, edx, ecx, eax; +} __attribute__ ((packed)); + +/* structure used to pass the interrupt stack frame from the + * entry points to the C dispatch function. + */ +struct intr_frame { + /* registers pushed by pusha in intr_entry_* */ + struct registers regs; + /* data segment selectors */ + /* XXX removed: not needed unless we do dpl3 transitions + uint32_t ds, es, fs, gs; + */ + /* interrupt number and error code pushed in intr_entry_* */ + uint32_t inum, err; + /* pushed by CPU during interrupt entry */ + uint32_t eip, cs, eflags; + /* pushed by CPU during interrupt entry from user space */ + /* XXX removed: again, not doing user space currently + uint32_t esp, ss; + */ +} __attribute__ ((packed)); + + + +typedef void (*intr_func_t)(int); + + +void init_intr(void); +void cleanup_intr(void); + +struct intr_frame *get_intr_frame(void); + +/* install high level interrupt callback */ +void interrupt(int intr_num, intr_func_t func); + +/* install low-level interrupt vector in IDT + * must be able to handle EOI and return with iret + */ +void set_intr_entry(int num, void (*handler)(void)); + +void prog_pic(int offs); +void set_pic_mask(int pic, unsigned char mask); +unsigned char get_pic_mask(int pic); +void mask_irq(int irq); +void unmask_irq(int irq); + +/* defined in intr_asm.S */ +int get_intr_flag(void); +void set_intr_flag(int onoff); + +void intr_ret(struct intr_frame ifrm); + +void end_of_irq(int irq); + +#endif /* INTR_H_ */ diff --git a/src/kern/intr_s.asm b/src/kern/intr_s.asm new file mode 100644 index 0000000..58c31f6 --- /dev/null +++ b/src/kern/intr_s.asm @@ -0,0 +1,161 @@ +; pcboot - bootable PC demo/game kernel +; Copyright (C) 2018-2023 John Tsiombikas +; +; 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 3 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, see . + + section .data + align 4 + dw 0 +idtr_desc: +lim: dw 0 +addr: dd 0 + + section .text +; void set_idt(uint32_t addr, uint16_t limit) + global set_idt +set_idt: + mov eax, [esp + 4] + mov [addr], eax + mov ax, [esp + 8] + mov [lim], ax + lidt [idtr_desc] + ret + +; int get_intr_flag() + global get_intr_flag +get_intr_flag: + pushf + pop eax + ; bit 9 of eflags is IF + shr eax, 9 + and eax, 1 + ret + +; void set_intr_flag(int onoff) + global set_intr_flag +set_intr_flag: + cmp dword [esp + 4], 0 + jz .off + sti + ret +.off: cli + ret + +; interrupt entry with error code macro +; this macro generates an interrupt entry point for the +; exceptions which include error codes in the stack frame +%macro ientry_err 2 + global intr_entry_%2 +intr_entry_%2: + push dword %1 + jmp intr_entry_common +%endmacro + +; interrupt entry without error code macro +; this macro generates an interrupt entry point for the interrupts +; and exceptions which do not include error codes in the stack frame +; it pushes a dummy error code (0), to make the stack frame identical +%macro ientry_noerr 2 + global intr_entry_%2 +intr_entry_%2: + push dword 0 + push dword %1 + jmp intr_entry_common +%endmacro + +; common code used by all entry points. calls dispatch_intr() +; defined in intr.c + extern dispatch_intr +intr_entry_common: + ; save general purpose registers + pusha + call dispatch_intr +intr_ret_local: + ; restore general purpose registers + popa + ; remove error code and intr num from stack + add esp, 8 + iret + +; special case for the timer interrupt, to avoid all the overhead of +; going through the C interrupt dispatcher 250 times each second +; extern nticks +; global intr_entry_fast_timer +;intr_entry_fast_timer: +; inc dword [nticks] +; ; signal end of interrupt +; push eax +; mov al, 20h +; out 20h, al +; pop eax +; iret + +; special case for IRQ 7 and IRQ 15, to catch spurious interrupts +PIC1_CMD equ 0x20 +PIC2_CMD equ 0xa0 +OCW2_EOI equ 0x20 +OCW3_ISR equ 0x0b + + global irq7_entry_check_spurious +irq7_entry_check_spurious: + push eax + mov al, OCW3_ISR + out PIC1_CMD, al + in al, PIC1_CMD + and al, 80h + pop eax + jnz intr_entry_irq7 + iret + + global irq15_entry_check_spurious +irq15_entry_check_spurious: + push eax + mov al, OCW3_ISR + out PIC2_CMD, al + in al, PIC2_CMD + and al, 80h + jnz .legit + ; it was spurious, send EOI to master PIC and iret + mov al, OCW2_EOI + out PIC1_CMD, al + pop eax + iret +.legit: pop eax + jmp intr_entry_irq15 + + +; XXX not necessary for now, just leaving it in in case it's useful +; down the road. +; +; intr_ret is called by context_switch to return from the kernel +; to userspace. The argument is a properly formed intr_frame +; structure with the saved context of the new task. +; +; First thing to do is remove the return address pointing back +; to context_switch, which then leaves us with a proper interrupt +; stack frame, so we can jump right in the middle of the regular +; interrupt return code above. + global intr_ret +intr_ret: + add esp, $4 + jmp intr_ret_local + +; by including interrupts.h with ASM defined, the macros above +; are expanded to generate all required interrupt entry points +%define INTR_ENTRY_EC(n, name) ientry_err n, name +%define INTR_ENTRY_NOEC(n, name) ientry_noerr n, name + +%include 'intrtab.h' + +; vi:set ts=8 sts=8 sw=8 ft=nasm: diff --git a/src/kern/intrtab.h b/src/kern/intrtab.h new file mode 100644 index 0000000..34af6fa --- /dev/null +++ b/src/kern/intrtab.h @@ -0,0 +1,38 @@ +INTR_ENTRY_NOEC(0, div) +INTR_ENTRY_NOEC(1, debug) +INTR_ENTRY_NOEC(2, nmi) +INTR_ENTRY_NOEC(3, bp) +INTR_ENTRY_NOEC(4, overflow) +INTR_ENTRY_NOEC(5, bound) +INTR_ENTRY_NOEC(6, ill) +INTR_ENTRY_NOEC(7, nodev) +INTR_ENTRY_EC(8, dfault) +INTR_ENTRY_NOEC(9, copseg) +INTR_ENTRY_EC(10, tss) +INTR_ENTRY_EC(11, segpres) +INTR_ENTRY_EC(12, stack) +INTR_ENTRY_EC(13, prot) +INTR_ENTRY_EC(14, page) +INTR_ENTRY_NOEC(15, reserved) +INTR_ENTRY_NOEC(16, fpu) +INTR_ENTRY_EC(17, align) +INTR_ENTRY_NOEC(18, mce) +INTR_ENTRY_NOEC(19, sse) +INTR_ENTRY_NOEC(32, irq0) +INTR_ENTRY_NOEC(33, irq1) +INTR_ENTRY_NOEC(34, irq2) +INTR_ENTRY_NOEC(35, irq3) +INTR_ENTRY_NOEC(36, irq4) +INTR_ENTRY_NOEC(37, irq5) +INTR_ENTRY_NOEC(38, irq6) +INTR_ENTRY_NOEC(39, irq7) +INTR_ENTRY_NOEC(40, irq8) +INTR_ENTRY_NOEC(41, irq9) +INTR_ENTRY_NOEC(42, irq10) +INTR_ENTRY_NOEC(43, irq11) +INTR_ENTRY_NOEC(44, irq12) +INTR_ENTRY_NOEC(45, irq13) +INTR_ENTRY_NOEC(46, irq14) +INTR_ENTRY_NOEC(47, irq15) +INTR_ENTRY_NOEC(128, syscall) +INTR_ENTRY_NOEC(255, default) diff --git a/src/kern/kbregs.h b/src/kern/kbregs.h new file mode 100644 index 0000000..813fa1a --- /dev/null +++ b/src/kern/kbregs.h @@ -0,0 +1,81 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef KBREGS_H_ +#define KBREGS_H_ + +#define KB_IRQ 1 +#define PSAUX_IRQ 12 + +#define KB_DATA_PORT 0x60 +#define KB_CMD_PORT 0x64 +#define KB_STATUS_PORT 0x64 + +#define KB_ACK 0xfa +#define KB_NACK 0xfe +#define KB_TEST_PASSED 0x55 +#define KB_TEST_FAILED 0xfc + +#define KB_STAT_OUTBUF_FULL 0x01 +#define KB_STAT_INBUF_FULL 0x02 +#define KB_STAT_SYSFLAG 0x04 +#define KB_STAT_CMD 0x08 +#define KB_STAT_ACTIVE 0x10 +#define KB_STAT_AUX 0x20 +#define KB_STAT_TIMEOUT 0x40 +#define KB_STAT_PAR_ERROR 0x80 + +#define KB_CMD_TEST 0xaa + +/* keyboard commands */ +#define KB_CMD_GET_CMDBYTE 0x20 +#define KB_CMD_SET_CMDBYTE 0x60 +#define KB_CMD_AUX_ENABLE 0xa8 +#define KB_CMD_READ_OUTPORT 0xd0 +#define KB_CMD_WRITE_OUTPORT 0xd1 +#define KB_CMD_PSAUX 0xd4 +#define KB_CMD_PULSE_RESET 0xfe + +/* controller command byte bits */ +#define KB_CCB_KB_INTREN 0x01 +#define KB_CCB_AUX_INTREN 0x02 +#define KB_CCB_SYSFLAG 0x04 +#define KB_CCB_KB_DISABLE 0x10 +#define KB_CCB_AUX_DISABLE 0x20 +#define KB_CCB_KB_XLAT 0x40 + +/* psaux commands (use with d4 prefix) */ +#define AUX_CMD_STREAM_MODE 0xea +#define AUX_CMD_READ_MOUSE 0xeb +#define AUX_CMD_REMOTE_MODE 0xf0 +#define AUX_CMD_ENABLE 0xf4 +#define AUX_CMD_DEFAULTS 0xf6 + +#define AUX_PKT0_LEFTBN 0x01 +#define AUX_PKT0_RIGHTBN 0x02 +#define AUX_PKT0_MIDDLEBN 0x04 +#define AUX_PKT0_ALWAYS1 0x08 +#define AUX_PKT0_XSIGN 0x10 +#define AUX_PKT0_YSIGN 0x20 +#define AUX_PKT0_XOVF 0x40 +#define AUX_PKT0_YOVF 0x80 + +#define AUX_PKT0_OVF_BITS (AUX_PKT0_XOVF | AUX_PKT0_YOVF) +#define AUX_PKT0_BUTTON_BITS \ + (AUX_PKT0_LEFTBN | AUX_PKT0_RIGHTBN | AUX_PKT0_MIDDLEBN) + +#endif /* KBREGS_H_ */ diff --git a/src/kern/kbscan.h b/src/kern/kbscan.h new file mode 100644 index 0000000..0c1cdee --- /dev/null +++ b/src/kern/kbscan.h @@ -0,0 +1,52 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#ifndef KBSCAN_H_ +#define KBSCAN_H_ + +/* table with rough translations from set 1 scancodes to ASCII-ish */ +static int scantbl_set1[] = { + 0, KB_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', /* 0 - e */ + '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', /* f - 1c */ + KB_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', /* 1d - 29 */ + KB_LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KB_RSHIFT, /* 2a - 36 */ + KB_NUM_MUL, KB_LALT, ' ', KB_CAPSLK, KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, KB_F7, KB_F8, KB_F9, KB_F10, /* 37 - 44 */ + KB_NUMLK, KB_SCRLK, KB_NUM_7, KB_NUM_8, KB_NUM_9, KB_NUM_MINUS, KB_NUM_4, KB_NUM_5, KB_NUM_6, KB_NUM_PLUS, /* 45 - 4e */ + KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_0, KB_NUM_DOT, KB_SYSRQ, 0, 0, KB_F11, KB_F12, /* 4d - 58 */ +#if 0 + 0, 0, 0, 0, 0, 0, 0, /* 59 - 5f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 70 - 7f */ +#endif +}; + +/* extended scancodes, after the 0xe0 prefix */ +#if 0 +static int scantbl_set1_ext[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\r', KB_RCTRL, 0, 0, /* 10 - 1f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2f */ + 0, 0, 0, 0, 0, KB_NUM_MINUS, 0, KB_SYSRQ, KB_RALT, 0, 0, 0, 0, 0, 0, 0, /* 30 - 3f */ + 0, 0, 0, 0, 0, 0, 0, KB_HOME, KB_UP, KB_PGUP, 0, KB_LEFT, 0, KB_RIGHT, 0, KB_END, /* 40 - 4f */ + KB_DOWN, KB_PGDN, KB_INSERT, KB_DEL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7f */ +}; +#endif + + +#endif /* KBSCAN_H_ */ diff --git a/src/kern/keyb.c b/src/kern/keyb.c new file mode 100644 index 0000000..ea32945 --- /dev/null +++ b/src/kern/keyb.c @@ -0,0 +1,317 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#include +#include +#include "keyb.h" +#include "intr.h" +#include "asmops.h" +#include "kbregs.h" +#include "kbscan.h" +#include "power.h" +#include "panic.h" + +#define delay7us() \ + do { \ + iodelay(); iodelay(); iodelay(); iodelay(); \ + iodelay(); iodelay(); iodelay(); \ + } while(0) + +static void set_ccb(unsigned char ccb); +static unsigned char get_ccb(void); + +static void kbintr(); + +#define BUFSZ 64 +#define ADVANCE(x) ((x) = ((x) + 1) & (BUFSZ - 1)) + +static int buffer[BUFSZ]; +static int buf_ridx, buf_widx; + +static unsigned int num_pressed; +static unsigned char keystate[256]; + + +void kb_init(void) +{ + buf_ridx = buf_widx = 0; + num_pressed = 0; + memset(keystate, 0, sizeof keystate); + + /* make sure set1 translation is enabled */ + kb_set_translate(1); + + interrupt(IRQ_TO_INTR(KB_IRQ), kbintr); + kb_intr_enable(); + unmask_irq(KB_IRQ); +} + +void kb_intr_enable(void) +{ + unsigned char ccb = get_ccb(); + ccb |= KB_CCB_KB_INTREN; + set_ccb(ccb); +} + +void kb_intr_disable(void) +{ + unsigned char ccb = get_ccb(); + ccb &= ~KB_CCB_KB_INTREN; + set_ccb(ccb); +} + +int kb_setmode(int mode) +{ + kb_send_data(0xf0); + if(!kb_wait_read() || kb_read_data() != KB_ACK) { + return -1; + } + kb_send_data(mode); + if(!kb_wait_read() || kb_read_data() != KB_ACK) { + return -1; + } + return 0; +} + +int kb_getmode(void) +{ + int mode; + + kb_send_data(0xf0); + if(!kb_wait_read() || kb_read_data() != KB_ACK) { + return -1; + } + kb_send_data(0); + if(!kb_wait_read() || kb_read_data() != KB_ACK) { + return -1; + } + mode = kb_read_data(); + + switch(mode) { + case 0x43: return 1; + case 0x41: return 2; + case 0x3f: return 3; + default: + break; + } + return mode; +} + +void kb_set_translate(int xlat) +{ + unsigned char ccb = get_ccb(); + if(xlat) { + ccb |= KB_CCB_KB_XLAT; + } else { + ccb &= ~KB_CCB_KB_XLAT; + } + set_ccb(ccb); +} + +int kb_get_translate(void) +{ + return get_ccb() & KB_CCB_KB_XLAT; +} + +int kb_isdown(int key) +{ + switch(key) { + case KB_ANY: + return num_pressed; + + case KB_ALT: + return keystate[KB_LALT] + keystate[KB_RALT]; + + case KB_CTRL: + return keystate[KB_LCTRL] + keystate[KB_RCTRL]; + } + return keystate[key]; +} + +void kb_wait(void) +{ + int key; + while((key = kb_getkey()) == -1) { + /* put the processor to sleep while waiting for keypresses, but first + * make sure interrupts are enabled, or we'll sleep forever + */ + enable_intr(); + halt_cpu(); + } + kb_putback(key); +} + +int kb_getkey(void) +{ + int res; + + if(buf_ridx == buf_widx) { + return -1; + } + res = buffer[buf_ridx]; + ADVANCE(buf_ridx); + return res; +} + +void kb_putback(int key) +{ + /* go back a place */ + if(--buf_ridx < 0) { + buf_ridx += BUFSZ; + } + + /* if the write end hasn't caught up with us, go back one place + * and put it there, otherwise just overwrite the oldest key which + * is right where we were. + */ + if(buf_ridx == buf_widx) { + ADVANCE(buf_ridx); + } + + buffer[buf_ridx] = key; +} + +int kb_wait_write(void) +{ + int i; + for(i=0; i<32768; i++) { + if(!(inp(KB_STATUS_PORT) & KB_STAT_INBUF_FULL)) { + return 1; + } + iodelay(); + } + /*printf("kb_wait_write timeout\n");*/ + return 0; +} + +int kb_wait_read(void) +{ + int i; + for(i=0; i<32768; i++) { + if((inp(KB_STATUS_PORT) & KB_STAT_OUTBUF_FULL)) { + return 1; + } + iodelay(); + } + /*printf("kb_wait_read timeout\n");*/ + return 0; +} + +void kb_send_cmd(unsigned char cmd) +{ + kb_wait_write(); + outp(KB_CMD_PORT, cmd); +} + +void kb_send_data(unsigned char data) +{ + kb_wait_write(); + outp(KB_DATA_PORT, data); +} + +unsigned char kb_read_data(void) +{ + kb_wait_read(); + delay7us(); + return inp(KB_DATA_PORT); +} + +static void set_ccb(unsigned char ccb) +{ + kb_send_cmd(KB_CMD_SET_CMDBYTE); + kb_send_data(ccb); + + if(kb_wait_read()) { + kb_read_data(); + } +} + +static unsigned char get_ccb(void) +{ + kb_send_cmd(KB_CMD_GET_CMDBYTE); + return kb_read_data(); +} + +static void kbintr() +{ + unsigned char code; + int key, press; + static int ext = 0; + + code = inp(KB_DATA_PORT); + + if(code == 0xe0) { + ext = 1; + return; + } + + if(code & 0x80) { + press = 0; + code &= 0x7f; + + if(num_pressed > 0) { + num_pressed--; + } + } else { + press = 1; + + num_pressed++; + } + + if(!ext) { + key = code < 0x59 ? scantbl_set1[code] : 0; + } else { + switch(code) { + case 0x1c: key = '\r'; break; + case 0x1d: key = KB_RCTRL; break; + //case 0x35: key = KB_NUM_MINUS; break; + //case 0x37: key = KB_SYSRQ; break; + case 0x38: key = KB_RALT; break; + case 0x47: key = KB_HOME; break; + case 0x48: key = KB_UP; break; + case 0x49: key = KB_PGUP; break; + case 0x4b: key = KB_LEFT; break; + case 0x4d: key = KB_RIGHT; break; + case 0x4f: key = KB_END; break; + case 0x50: key = KB_DOWN; break; + case 0x51: key = KB_PGDN; break; + case 0x52: key = KB_INSERT; break; + case 0x53: key = KB_DEL; break; + default: + key = 0; + } + ext = 0; + } + + if(press) { + if(key == KB_DEL && (keystate[KB_LALT] || keystate[KB_RALT]) && (keystate[KB_LCTRL] || keystate[KB_RCTRL])) { + reboot(); + } + /* append to buffer */ + buffer[buf_widx] = key; + ADVANCE(buf_widx); + /* if the write end overtook the read end, advance the read end + * too, to discard the oldest keypress from the buffer + */ + if(buf_widx == buf_ridx) { + ADVANCE(buf_ridx); + } + } + + /* and update keystate table */ + keystate[key] = press; +} diff --git a/src/kern/keyb.h b/src/kern/keyb.h new file mode 100644 index 0000000..dc92f62 --- /dev/null +++ b/src/kern/keyb.h @@ -0,0 +1,79 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef KEYB_H_ +#define KEYB_H_ + +#define KB_ANY (-1) +#define KB_ALT (-2) +#define KB_CTRL (-3) +#define KB_SHIFT (-4) + +/* special keys */ +enum { + KB_ESC = 27, + KB_BACKSP = 127, + + KB_INSERT, KB_DEL, KB_HOME, KB_END, KB_PGUP, KB_PGDN, + KB_LEFT, KB_RIGHT, KB_UP, KB_DOWN, + KB_NUM_DOT, KB_NUM_ENTER, KB_NUM_PLUS, KB_NUM_MINUS, KB_NUM_MUL, KB_NUM_DIV, + KB_NUM_0, KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_4, + KB_NUM_5, KB_NUM_6, KB_NUM_7, KB_NUM_8, KB_NUM_9, + + KB_LALT, KB_RALT, + KB_LCTRL, KB_RCTRL, + KB_LSHIFT, KB_RSHIFT, + KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, + KB_F7, KB_F8, KB_F9, KB_F10, KB_F11, KB_F12, + KB_CAPSLK, KB_NUMLK, KB_SCRLK, KB_SYSRQ +}; + +void kb_init(void); + +void kb_intr_enable(void); +void kb_intr_disable(void); + +int kb_setmode(int mode); +int kb_getmode(void); + +void kb_set_translate(int xlat); +int kb_get_translate(void); + +/* Boolean predicate for testing the current state of a particular key. + * You may also pass KB_ANY to test if any key is held down. + */ +int kb_isdown(int key); + +/* waits for any keypress */ +void kb_wait(void); + +/* removes and returns a single key from the input buffer. */ +int kb_getkey(void); + +void kb_putback(int key); + +/* returns 1 if the keyboard controller is ready to read/write + * returns 0 if the wait times out + */ +int kb_wait_write(void); +int kb_wait_read(void); + +void kb_send_cmd(unsigned char cmd); +void kb_send_data(unsigned char data); +unsigned char kb_read_data(void); + +#endif /* KEYB_H_ */ diff --git a/src/kern/main.c b/src/kern/main.c new file mode 100644 index 0000000..6cf5de3 --- /dev/null +++ b/src/kern/main.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include "segm.h" +#include "intr.h" +#include "mem.h" +#include "contty.h" +#include "keyb.h" +#include "psaux.h" +#include "timer.h" +#include "game.h" + +unsigned char *framebuf, *vmem = (unsigned char*)0xa0000; + +static int quit; + + +int main(void) +{ + int key; + + init_segm(); + init_intr(); + con_init(); + kb_init(); + init_psaux(); + init_mem(); + init_timer(); + enable_intr(); + + if(!(framebuf = malloc(320 * 200))) { + printf("failed to allocate framebuffer\n"); + goto end; + } + + if(game_init() == -1) { + goto end; + } + + for(;;) { + if((key = kb_getkey()) >= 0) { + game_keyboard(key, 1); + } + if(quit) break; + game_draw(); + } + game_shutdown(); + +end: + cleanup_intr(); /* also disables interrupts */ + cleanup_timer(); + return 0; +} + +void game_quit(void) +{ + quit = 1; +} + +void game_swap_buffers(void) +{ + memcpy(vmem, framebuf, 64000); +} diff --git a/src/kern/mem.c b/src/kern/mem.c new file mode 100644 index 0000000..a013607 --- /dev/null +++ b/src/kern/mem.c @@ -0,0 +1,314 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#include +#include +#include "config.h" +#include "panic.h" +#include "mem.h" +#include "intr.h" + +#define FREE 0 +#define USED 1 + +#define BM_IDX(pg) ((pg) / 32) +#define BM_BIT(pg) ((pg) & 0x1f) + +#define IS_FREE(pg) ((bitmap[BM_IDX(pg)] & (1 << BM_BIT(pg))) == 0) + +#define MEM_START ((uint32_t)&_mem_start) + + +struct mem_range { + uint32_t start; + uint32_t size; +}; + +void move_stack(uint32_t newaddr); /* defined in startup.s */ + +static void mark_page(int pg, int used); +static void add_memory(uint32_t start, size_t size); + +#define MAX_MAP_SIZE 16 +extern struct mem_range boot_mem_map[MAX_MAP_SIZE]; +extern int boot_mem_map_size; + +/* linker supplied symbol, points to the end of the kernel image */ +extern uint32_t _mem_start; + + +/* A bitmap is used to track which physical memory pages are used, and which + * are available for allocation by alloc_ppage. + * + * last_alloc_idx keeps track of the last 32bit element in the bitmap array + * where a free page was found. It's guaranteed that all the elements before + * this have no free pages, but it doesn't imply that there will be another + * free page there. So it's used as a starting point for the search. + */ +static uint32_t *bitmap; +static int bmsize, last_alloc_idx; + + + +void init_mem(void) +{ + int i, pg, max_used_pg, end_pg = 0; + uint32_t used_end, start, end, sz, total = 0, rem; + const char *suffix[] = {"bytes", "KB", "MB", "GB"}; + + last_alloc_idx = 0; + + /* the allocation bitmap starts right at the end of the kernel image */ + bitmap = (uint32_t*)&_mem_start; + + /* start by marking all posible pages (2**20) as used. We do not "reserve" + * all this space. Pages beyond the end of the useful bitmap area + * ((char*)bitmap + bmsize), which will be determined after we traverse the + * memory map, are going to be marked as available for allocation. + */ + memset(bitmap, 0xff, 1024 * 1024 / 8); + + + if(boot_mem_map_size <= 0 || boot_mem_map_size > MAX_MAP_SIZE) { + panic("invalid memory map size reported by the boot loader: %d\n", boot_mem_map_size); + } + + printf("Memory map:\n"); + for(i=0; i 1024) { + rem = total & 0x3ff; + total >>= 10; + ++i; + } + printf("Total usable RAM: %u.%u %s\n", total, 100 * rem / 1024, suffix[i]); + + /* size of the useful part of the bitmap in bytes padded to 4-byte + * boundaries to allow 32bit at a time operations. + */ + bmsize = (end_pg / 32) * 4; + + /* mark all pages occupied by the bitmap as used */ + used_end = (uint32_t)bitmap + bmsize - 1; + + max_used_pg = ADDR_TO_PAGE(used_end); + printf("marking pages up to %x (page: %d) as used\n", used_end, max_used_pg); + for(i=0; i<=max_used_pg; i++) { + mark_page(i, USED); + } + +#ifdef MOVE_STACK_RAMTOP + /* allocate space for the stack at the top of RAM and move it there */ + if((pg = alloc_ppages(STACK_PAGES, MEM_STACK)) != -1) { + printf("moving stack-top to: %x (%d pages)\n", PAGE_TO_ADDR(end_pg) - 4, STACK_PAGES); + move_stack(PAGE_TO_ADDR(pg + STACK_PAGES) - 4); + } +#endif +} + +int alloc_ppage(int area) +{ + return alloc_ppages(1, area); +} + +/* free_ppage marks the physical page, free in the allocation bitmap. + * + * CAUTION: no checks are done that this page should actually be freed or not. + * If you call free_ppage with the address of some part of memory that was + * originally reserved due to it being in a memory hole or part of the kernel + * image or whatever, it will be subsequently allocatable by alloc_ppage. + */ +void free_ppage(int pg) +{ + int bmidx = BM_IDX(pg); + + int intr_state = get_intr_flag(); + disable_intr(); + + if(IS_FREE(pg)) { + panic("free_ppage(%d): I thought that was already free!\n", pg); + } + + mark_page(pg, FREE); + if(bmidx < last_alloc_idx) { + last_alloc_idx = bmidx; + } + + set_intr_flag(intr_state); +} + + +int alloc_ppages(int count, int area) +{ + int i, dir, pg, idx, max, intr_state, found_free = 0; + + intr_state = get_intr_flag(); + disable_intr(); + + if(area == MEM_STACK) { + idx = (bmsize - 1) / 4; + max = -1; + dir = -1; + } else { + idx = last_alloc_idx; + max = bmsize / 4; + dir = 1; + } + + while(idx != max) { + /* if at least one bit is 0 then we have at least + * one free page. find it and try to allocate a range starting from there + */ + if(bitmap[idx] != 0xffffffff) { + pg = idx * 32; + if(dir < 0) pg += 31; + + for(i=0; i<32; i++) { + if(IS_FREE(pg)) { + if(!found_free && dir > 0) { + last_alloc_idx = idx; + found_free = 1; + } + + if(alloc_ppage_range(pg, count) != -1) { + set_intr_flag(intr_state); + return pg; + } + } + pg += dir; + } + } + idx += dir; + } + + set_intr_flag(intr_state); + return -1; +} + +void free_ppages(int pg0, int count) +{ + int i; + + for(i=0; i + +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 3 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, see . +*/ +#ifndef MEM_H_ +#define MEM_H_ + +#define ADDR_TO_PAGE(x) ((uint32_t)(x) >> 12) +#define PAGE_TO_ADDR(x) ((uint32_t)(x) << 12) +#define PAGE_TO_PTR(x) ((void*)PAGE_TO_ADDR(x)) + +#define BYTES_TO_PAGES(x) (((uint32_t)(x) + 4095) >> 12) + +/* for alloc_ppage/alloc_ppages */ +enum { + MEM_HEAP = 0, /* start searching from the bottom */ + MEM_STACK = 1 /* start searching from the top */ +}; + +void init_mem(void); + +int alloc_ppage(int area); +void free_ppage(int pg); + +/* allocate a number of consecutive pages */ +int alloc_ppages(int count, int area); +void free_ppages(int pg0, int count); + +/* allocate a specific range of pages. + * Fails (and returns -1) if any page in the requested range is not free. + */ +int alloc_ppage_range(int start, int size); +int free_ppage_range(int start, int size); + +#endif /* MEM_H_ */ diff --git a/src/kern/panic.c b/src/kern/panic.c new file mode 100644 index 0000000..788900e --- /dev/null +++ b/src/kern/panic.c @@ -0,0 +1,81 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2023 John Tsiombikas + +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 3 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, see . +*/ +#include +#include +#include +#include "intr.h" +#include "asmops.h" + +struct all_registers { + uint32_t eax, ebx, ecx, edx; + uint32_t esp, ebp, esi, edi; + uint32_t eflags; + uint32_t cs, ss, ds, es, fs, gs; + uint32_t cr0, cr1, cr2, cr3; +}; + +/* defined in regs.S */ +void get_regs(struct all_registers *regs); + +void panic(const char *fmt, ...) +{ + va_list ap; + struct all_registers regs; + uint32_t eip; + + disable_intr(); + + memset(®s, 0, sizeof regs); + get_regs(®s); + + CALLER_EIP(eip); + + printf("~~~~~ pcboot panic ~~~~~\n"); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + + printf("\nRegisters:\n"); + printf("eax: %x, ebx: %x, ecx: %x, edx: %x\n", regs.eax, regs.ebx, regs.ecx, regs.edx); + printf("esp: %x, ebp: %x, esi: %x, edi: %x\n", regs.esp, regs.ebp, regs.esi, regs.edi); + printf("eip: %x, eflags: %x\n", eip, regs.eflags); + printf("cr0: %x, cr1: %x, cr2: %x, cr3: %x\n", regs.cr0, regs.cr1, regs.cr2, regs.cr3); + printf("cs: %x (%d|%d)\n", regs.cs, regs.cs >> 3, regs.cs & 3); + printf("ss: %x (%d|%d)\n", regs.ss, regs.ss >> 3, regs.ss & 3); + printf("ds: %x (%d|%d)\n", regs.ds, regs.ds >> 3, regs.ds & 3); + printf("es: %x (%d|%d)\n", regs.es, regs.es >> 3, regs.es & 3); + printf("fs: %x (%d|%d)\n", regs.fs, regs.fs >> 3, regs.fs & 3); + printf("gs: %x (%d|%d)\n", regs.gs, regs.gs >> 3, regs.gs & 3); + + for(;;) halt_cpu(); +} + +void backtrace(void) +{ + int lvl = 0; + uint32_t *frmptr; + int ien = get_intr_flag(); + disable_intr(); + get_ebp(frmptr); + set_intr_flag(ien); + + while(frmptr) { + printf("%d: %p\n", lvl++, (void*)frmptr[1]); + frmptr = (uint32_t*)*frmptr; + } +} diff --git a/src/kern/panic.h b/src/kern/panic.h new file mode 100644 index 0000000..0785059 --- /dev/null +++ b/src/kern/panic.h @@ -0,0 +1,24 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef PANIC_H_ +#define PANIC_H_ + +void panic(const char *fmt, ...) __attribute__((noreturn)); +void backtrace(void); + +#endif /* PANIC_H_ */ diff --git a/src/kern/power.c b/src/kern/power.c new file mode 100644 index 0000000..8a677d0 --- /dev/null +++ b/src/kern/power.c @@ -0,0 +1,45 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#include +#include "power.h" +#include "keyb.h" +#include "kbregs.h" +#include "timer.h" +#include "panic.h" +#include "asmops.h" + +/* defined in intr_asm.S */ +void set_idt(uint32_t addr, uint16_t limit); + +void reboot(void) +{ + int i; + + printf("reboot: keyboard controller\n"); + kb_send_cmd(KB_CMD_PULSE_RESET); + + for(i=0; i<32768; i++) { + iodelay(); + } + + printf("reboot: triple-fault\n"); + set_idt(0, 0); + asm volatile("int $3"); + + panic("can't reboot!"); +} diff --git a/src/kern/power.h b/src/kern/power.h new file mode 100644 index 0000000..5e0c9fb --- /dev/null +++ b/src/kern/power.h @@ -0,0 +1,23 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#ifndef POWER_H_ +#define POWER_H_ + +void reboot(void); + +#endif /* POWER_H_ */ diff --git a/src/kern/psaux.c b/src/kern/psaux.c new file mode 100644 index 0000000..a0fb58d --- /dev/null +++ b/src/kern/psaux.c @@ -0,0 +1,208 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#include +#include "psaux.h" +#include "intr.h" +#include "asmops.h" +#include "keyb.h" +#include "kbregs.h" + +static void init_mouse(void); +static void proc_mouse_data(unsigned char *data); +static void psaux_intr(); + +static int mx, my; +static unsigned int bnstate; +static int present; +static int bounds[4]; +static int intr_mode; + +void init_psaux(void) +{ + interrupt(IRQ_TO_INTR(12), psaux_intr); + unmask_irq(12); + + init_mouse(); + set_mouse_bounds(0, 0, 319, 199); +} + +static void init_mouse(void) +{ + unsigned char val; + + kb_send_cmd(KB_CMD_AUX_ENABLE); + if(kb_wait_read()) { + val = kb_read_data(); + printf("aux enable: %02x\n", (unsigned int)val); + } + + /* + kb_send_cmd(KB_CMD_PSAUX); + kb_send_data(AUX_CMD_REMOTE_MODE); + val = kb_read_data(); + */ + + kb_send_cmd(KB_CMD_GET_CMDBYTE); + val = kb_read_data(); + val &= ~KB_CCB_AUX_DISABLE; + val |= KB_CCB_AUX_INTREN; + kb_send_cmd(KB_CMD_SET_CMDBYTE); + kb_send_data(val); + + if(kb_wait_read()) { + val = kb_read_data(); + printf("set cmdbyte: %02x\n", (unsigned int)val); + } + intr_mode = 1; + + kb_send_cmd(KB_CMD_PSAUX); + kb_send_data(AUX_CMD_DEFAULTS); + val = kb_read_data(); + + kb_send_cmd(KB_CMD_PSAUX); + kb_send_data(AUX_CMD_ENABLE); + val = kb_read_data(); + + present = (val == KB_ACK) ? 1 : 0; + printf("init_mouse: %spresent\n", present ? "" : "not "); +} + +int have_mouse(void) +{ + return present; +} + +void set_mouse_bounds(int x0, int y0, int x1, int y1) +{ + bounds[0] = x0; + bounds[1] = y0; + bounds[2] = x1; + bounds[3] = y1; +} + +unsigned int mouse_state(int *xp, int *yp) +{ + if(!intr_mode) { + poll_mouse(); + } + + *xp = mx; + *yp = my; + return bnstate; +} + +/* TODO: poll_mouse doesn't work (enable remote mode and disable interrupt on init to test) */ +#define STAT_AUX_PENDING (KB_STAT_OUTBUF_FULL | KB_STAT_AUX) +static inline int aux_pending(void) +{ + return (inp(KB_STATUS_PORT) & STAT_AUX_PENDING) == STAT_AUX_PENDING ? 1 : 0; +} + +void poll_mouse(void) +{ + static int poll_state; + static unsigned char pkt[3]; + unsigned char rd; + + ser_printf("poll_mouse(%d)\n", poll_state); + + switch(poll_state) { + case 0: /* send read mouse command */ + kb_send_cmd(KB_CMD_PSAUX); + kb_send_data(AUX_CMD_READ_MOUSE); + ++poll_state; + break; + + case 1: /* wait for ACK */ + if(kb_wait_read()) {// && aux_pending()) { + if((rd = kb_read_data()) == KB_ACK) { + ++poll_state; + } else { + ser_printf("poll_mouse state 1: expected ack: %02x\n", (unsigned int)rd); + } + } + break; + + case 2: /* read packet data */ + case 3: + case 4: + if(kb_wait_read() && aux_pending()) { + int i = poll_state++ - 2; + pkt[i] = kb_read_data(); + } + if(poll_state == 5) { + ser_printf("proc_mouse_data(%02x %02x %02x)\n", (unsigned int)pkt[0], + (unsigned int)pkt[1], (unsigned int)pkt[2]); + proc_mouse_data(pkt); + poll_state = 0; + } + break; + + default: + ser_printf("poll_mouse reached state: %d\n", poll_state); + } +} + +static void proc_mouse_data(unsigned char *data) +{ + int dx, dy; + + if(data[0] & AUX_PKT0_OVF_BITS) { + /* consensus seems to be that if overflow bits are set, something is + * fucked, and it's best to re-initialize the mouse + */ + /*init_mouse();*/ + } else { + bnstate = data[0] & AUX_PKT0_BUTTON_BITS; + dx = data[1]; + dy = data[2]; + + if(data[0] & AUX_PKT0_XSIGN) { + dx |= 0xffffff00; + } + if(data[0] & AUX_PKT0_YSIGN) { + dy |= 0xffffff00; + } + + mx += dx; + my -= dy; + + if(mx < bounds[0]) mx = bounds[0]; + if(mx > bounds[2]) mx = bounds[2]; + if(my < bounds[1]) my = bounds[1]; + if(my > bounds[3]) my = bounds[3]; + } +} + +static void psaux_intr() +{ + static unsigned char data[3]; + static int idx; + + if(!(inp(KB_STATUS_PORT) & KB_STAT_AUX)) { + /* no mouse data pending, ignore interrupt */ + return; + } + + data[idx] = kb_read_data(); + if(++idx >= 3) { + idx = 0; + + proc_mouse_data(data); + } +} diff --git a/src/kern/psaux.h b/src/kern/psaux.h new file mode 100644 index 0000000..7acc3f6 --- /dev/null +++ b/src/kern/psaux.h @@ -0,0 +1,34 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef PSAUX_H_ +#define PSAUX_H_ + +#define MOUSE_LBN_BIT 1 +#define MOUSE_RBN_BIT 2 +#define MOUSE_MBN_BIT 4 + +void init_psaux(void); + +int have_mouse(void); + +void set_mouse_bounds(int x0, int y0, int x1, int y1); +unsigned int mouse_state(int *xp, int *yp); + +void poll_mouse(void); + +#endif /* PSAUX_H_ */ diff --git a/src/kern/regs.asm b/src/kern/regs.asm new file mode 100644 index 0000000..5fb93d1 --- /dev/null +++ b/src/kern/regs.asm @@ -0,0 +1,57 @@ + section .text + align 4 + + global get_regs +get_regs: + push ebp + mov ebp, esp + + push edx + mov edx, [ebp + 8] + + mov [edx], eax + mov [edx + 4], ebx + mov [edx + 8], ecx + + ; juggle edx + mov eax, edx + pop edx + mov [eax + 12], edx + push edx + mov edx, eax + + ; those two are pointless in a function + mov [edx + 16], esp + mov [edx + 20], ebp + + mov [edx + 24], esi + mov [edx + 28], edx + + pushf + pop eax + mov [edx + 32], eax + + mov [edx + 36], cs + mov [edx + 40], ss + mov [edx + 44], ds + mov [edx + 48], es + mov [edx + 52], fs + mov [edx + 56], gs + + push ebx + mov ebx, cr0 + mov [edx + 60], ebx + ;mov ebx, cr1 + ;mov [edx + 64], ebx + mov ebx, cr2 + mov [edx + 68], ebx + mov ebx, cr3 + mov [edx + 72], ebx + pop ebx + + pop edx + pop ebp + ret + + +; vi:set ts=8 sts=8 sw=8 ft=nasm: diff --git a/src/kern/segm.c b/src/kern/segm.c new file mode 100644 index 0000000..2a461a6 --- /dev/null +++ b/src/kern/segm.c @@ -0,0 +1,173 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#include +#include +#include "segm.h" +#include "desc.h" +#include "tss.h" + +/* bits for the 3rd 16bt part of the descriptor */ +#define BIT_ACCESSED (1 << 8) +#define BIT_WR (1 << 9) +#define BIT_RD (1 << 9) +#define BIT_EXP_DOWN (1 << 10) +#define BIT_CONFORMING (1 << 10) +#define BIT_CODE (1 << 11) +#define BIT_NOSYS (1 << 12) +#define BIT_PRESENT (1 << 15) +/* TSS busy bit */ +#define BIT_BUSY (1 << 9) + +/* bits for the last 16bit part of the descriptor */ +#define BIT_BIG (1 << 6) +#define BIT_DEFAULT (1 << 6) +#define BIT_GRAN (1 << 7) + +enum {TYPE_DATA, TYPE_CODE}; + +/* we need the following bit pattern at the 8th bit excluding the busy bit: 1001 */ +#define TSS_TYPE_BITS (9 << 8) + +static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type); +static void segm_desc16(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type); +/*static void task_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl);*/ + +/* these functions are implemented in segm-asm.S */ +void setup_selectors(uint16_t code, uint16_t data); +void set_gdt(uint32_t addr, uint16_t limit); +void set_task_reg(uint16_t tss_selector); + +/* +static void dbg_print_gdt(void); +static void print_desc(desc_t *desc); +*/ + +/* our global descriptor table */ +static desc_t gdt[NUM_SEGMENTS] __attribute__((aligned(8))); +uint16_t orig_seg; + +void init_segm(void) +{ + asm volatile ( + "mov %%fs, %0\n\t" + : "=a"(orig_seg)); + + memset(gdt, 0, sizeof gdt); + segm_desc(gdt + SEGM_CODE, 0, 0xffffffff, 0, TYPE_CODE); + segm_desc(gdt + SEGM_DATA, 0, 0xffffffff, 0, TYPE_DATA); + segm_desc(gdt + SEGM_XCODE, orig_seg << 4, 0xffffffff, 0, TYPE_CODE); + segm_desc(gdt + SEGM_XDATA, orig_seg << 4, 0xffffffff, 0, TYPE_DATA); + segm_desc16(gdt + SEGM_CODE16, orig_seg << 4, 0xffff, 0, TYPE_CODE); + + set_gdt((uint32_t)gdt, sizeof gdt - 1); + + setup_selectors(selector(SEGM_CODE, 0), selector(SEGM_DATA, 0)); +} + +/* constructs a GDT selector based on index and priviledge level */ +uint16_t selector(int idx, int rpl) +{ + return (idx << 3) | (rpl & 3); +} + +/* +void set_tss(uint32_t addr) +{ + task_desc(gdt + SEGM_TASK, addr, sizeof(struct task_state) - 1, 3); + set_task_reg(selector(SEGM_TASK, 0)); +} +*/ + +static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type) +{ + desc->d[0] = limit & 0xffff; /* low order 16bits of limit */ + desc->d[1] = base & 0xffff; /* low order 16bits of base */ + + /* third 16bit part contains the last 8 bits of base, the 2 priviledge + * level bits starting on bit 13, present flag on bit 15, and type bits + * starting from bit 8 + */ + desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT | + BIT_NOSYS | (type == TYPE_DATA ? BIT_WR : (BIT_RD | BIT_CODE)); + + /* last 16bit part contains the last nibble of limit, the last byte of + * base, and the granularity and deafult/big flags in bits 23 and 22 resp. + */ + desc->d[3] = ((limit >> 16) & 0xf) | ((base >> 16) & 0xff00) | BIT_GRAN | BIT_BIG; +} + +static void segm_desc16(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type) +{ + desc->d[0] = limit & 0xffff; /* low order 16bits of limit */ + desc->d[1] = base & 0xffff; /* low order 16bits of base */ + + /* third 16bit part contains the last 8 bits of base, the 2 priviledge + * level bits starting on bit 13, present flag on bit 15, and type bits + * starting from bit 8 + */ + desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT | + BIT_NOSYS | (type == TYPE_DATA ? BIT_WR : (BIT_RD | BIT_CODE)); + + /* 16bit descriptors have the upper word 0 */ + desc->d[3] = 0; +} + +#if 0 +static void task_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl) +{ + desc->d[0] = limit & 0xffff; + desc->d[1] = base & 0xffff; + + desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT | + TSS_TYPE_BITS; /* XXX busy ? */ + desc->d[3] = ((limit >> 16) & 0xf) | ((base >> 16) & 0xff00) | BIT_GRAN; +} + +static void dbg_print_gdt(void) +{ + int i; + + printf("Global Descriptor Table\n"); + printf("-----------------------\n"); + + for(i=0; id[0] | desc->d[1] | desc->d[2] | desc->d[3]) == 0) { + printf("null\n"); + } + + base = (uint32_t)desc->d[1] | ((uint32_t)(desc->d[2] & 0xff) << 16) | ((uint32_t)(desc->d[3] >> 8) << 24); + limit = (uint32_t)desc->d[0] | ((uint32_t)(desc->d[3] & 0xf) << 16); + dpl = (desc->d[2] >> 13) & 3; + g = (desc->d[3] >> 7) & 1; + + if(g) limit = ((limit + 1) << 12) - 1; + + printf("base:%x lim:%x dpl:%d type:%s %dbit\n", base, limit, dpl, + desc->d[2] & BIT_CODE ? "code" : "data", desc->d[3] & BIT_BIG ? 32 : 16); +} +#endif diff --git a/src/kern/segm.h b/src/kern/segm.h new file mode 100644 index 0000000..f3b083c --- /dev/null +++ b/src/kern/segm.h @@ -0,0 +1,41 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef SEGM_H_ +#define SEGM_H_ + +#include + +enum { + SEGM_NULL, + SEGM_CODE, + SEGM_DATA, + SEGM_XCODE, /* transition 32bit code with base matching the RM segment */ + SEGM_XDATA, /* same for data */ + SEGM_CODE16, + + NUM_SEGMENTS +}; + +void init_segm(void); + +uint16_t selector(int idx, int rpl); + +void set_tss(uint32_t addr); + + +#endif /* SEGM_H_ */ diff --git a/src/kern/segm_s.asm b/src/kern/segm_s.asm new file mode 100644 index 0000000..6448c04 --- /dev/null +++ b/src/kern/segm_s.asm @@ -0,0 +1,64 @@ +; pcboot - bootable PC demo/game kernel +; Copyright (C) 2018-2023 John Tsiombikas +; +; 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 3 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, see . + + section .data + align 4 +; memory reserved for setup_selectors +off: dd 0 +segm: dw 0 +; memory reserved for set_gdt +lim: dw 0 +addr: dd 0 + + section .text +; setup_selectors(uint16_t code, uint16_t data) +; loads the requested selectors to all the selector registers + global setup_selectors +setup_selectors: + ; set data selectors directly + mov eax, [esp + 8] + mov ss, ax + mov es, ax + mov ds, ax + mov gs, ax + ;mov fs, ax ; XXX don't touch fs, we use it to store initial seg + ; set cs using a long jump + mov eax, [esp + 4] + mov [segm], ax + mov dword [off], .ldcs + jmp [off] +.ldcs: ret + +; set_gdt(uint32_t addr, uint16_t limit) +; loads the GDTR with the new address and limit for the GDT + global set_gdt +set_gdt: + mov eax, [esp + 4] + mov [addr], eax + mov ax, [esp + 8] + mov [lim], ax + lgdt [lim] + ret + +; set_task_reg(uint16_t tss_selector) +; loads the TSS selector in the task register + global set_task_reg +set_task_reg: + mov eax, [esp + 4] + ltr [esp + 4] + ret + +; vi:set ts=8 sts=8 sw=8 ft=nasm: diff --git a/src/kern/serial.c b/src/kern/serial.c new file mode 100644 index 0000000..84953c8 --- /dev/null +++ b/src/kern/serial.c @@ -0,0 +1,361 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#include +#include +#include +#include "config.h" +#include "serial.h" +#include "asmops.h" +#include "intr.h" +#include "panic.h" + +#define UART1_BASE 0x3f8 +#define UART2_BASE 0x2f8 +#define UART1_IRQ 4 +#define UART2_IRQ 3 + +#define UART_DATA 0 +#define UART_INTR 1 +#define UART_DIVLO 0 +#define UART_DIVHI 1 +#define UART_FIFO 2 +#define UART_IID 2 +#define UART_LCTL 3 +#define UART_MCTL 4 +#define UART_LSTAT 5 +#define UART_MSTAT 6 + +/* interrupt enable register bits */ +#define INTR_RECV 1 +#define INTR_SEND 2 +#define INTR_LSTAT 4 +#define INTR_DELTA 8 + +/* fifo control register bits */ +#define FIFO_ENABLE 0x01 +#define FIFO_RECV_CLEAR 0x02 +#define FIFO_SEND_CLEAR 0x04 +#define FIFO_DMA 0x08 +#define FIFO_TRIG_4 0x40 +#define FIFO_TRIG_8 0x80 +#define FIFO_TRIG_14 0xc0 + +/* interrupt id register bits */ +#define IID_PENDING 0x01 +#define IID_ID0 0x02 +#define IID_ID1 0x04 +#define IID_ID2 0x08 +#define IID_FIFO_EN 0xc0 + +#define IID_SOURCE 0xe + +#define IID_DELTA 0 +#define IID_SEND 0x2 +#define IID_RECV 0x4 +#define IID_FIFO 0xc +#define IID_STATUS 0x6 + +/* line control register bits */ +#define LCTL_BITS_8 0x03 +#define LCTL_STOP_2 0x04 +#define LCTL_DLAB 0x80 +#define LCTL_8N1 LCTL_BITS_8 +#define LCTL_8N2 (LCTL_BITS_8 | LCTL_STOP_2) + +/* modem control register bits */ +#define MCTL_DTR 0x01 +#define MCTL_RTS 0x02 +#define MCTL_OUT1 0x04 +#define MCTL_OUT2 0x08 +#define MCTL_LOOP 0x10 + +/* line status register bits */ +#define LST_DRDY 0x01 +#define LST_ERR_OVER 0x02 +#define LST_ERR_PARITY 0x04 +#define LST_ERR_FRAME 0x08 +#define LST_ERR_BRK 0x10 +#define LST_TREG_EMPTY 0x20 +#define LST_TIDLE 0x40 +#define LST_ERROR 0x80 + +/* modem status register bits */ +#define MST_DELTA_CTS 0x01 +#define MST_DELTA_DSR 0x02 +#define MST_TERI 0x04 +#define MST_DELTA_DCD 0x08 +#define MST_CTS 0x10 +#define MST_DSR 0x20 +#define MST_RING 0x40 +#define MST_DCD 0x80 + +/* interrupt controller stuff */ +#define PIC1_CMD_PORT 0x20 +#define PIC1_DATA_PORT 0x21 +#define PIC2_CMD_PORT 0xa0 +#define PIC2_DATA_PORT 0xa1 +#define OCW2_EOI 0x20 + +#define COM_FMT_8N1 LCTL_8N1 +#define COM_FMT_8N2 LCTL_8N2 + +#define INBUF_SIZE 16 +#define BNEXT(x) (((x) + 1) & (INBUF_SIZE - 1)) +#define BEMPTY(b) (b##_ridx == b##_widx) + +struct serial_port { + int base, intr; + int blocking; + int ref; + + char inbuf[INBUF_SIZE]; + int inbuf_ridx, inbuf_widx; +}; + + +static int have_recv(int base); +static void recv_intr(); + +static struct serial_port ports[2]; +static int num_open; + +static int uart_base[] = {UART1_BASE, UART2_BASE}; +static int uart_irq[] = {UART1_IRQ, UART2_IRQ}; + +int ser_open(int pidx, int baud, unsigned int mode) +{ + unsigned short div = 115200 / baud; + int i, fd, base, intr; + unsigned int fmt; + + if(pidx < 0 || pidx > 1) { + printf("ser_open: invalid serial port: %d\n", pidx); + return -1; + } + + for(i=0; i> 8) & 0xff); + outp(base + UART_LCTL, fmt); /* fmt should be LCTL_8N1, LCTL_8N2 etc */ + outp(base + UART_FIFO, FIFO_ENABLE | FIFO_SEND_CLEAR | FIFO_RECV_CLEAR); + outp(base + UART_MCTL, MCTL_DTR | MCTL_RTS | MCTL_OUT2); + outp(base + UART_INTR, INTR_RECV); + + ports[fd].base = base; + ports[fd].intr = intr; + ports[fd].blocking = 1; + ports[fd].ref = 1; + return fd; +} + +void ser_close(int fd) +{ + if(!ports[fd].ref) return; + + if(--ports[fd].ref == 0) { + outp(ports[fd].base + UART_INTR, 0); + outp(ports[fd].base + UART_MCTL, 0); + ports[fd].base = 0; + } +} + +int ser_block(int fd) +{ + ports[fd].blocking = 1; + return 0; +} + +int ser_nonblock(int fd) +{ + ports[fd].blocking = 0; + return 0; +} + +int ser_pending(int fd) +{ + return !BEMPTY(ports[fd].inbuf); +} + +/* if msec < 0: wait for ever */ +int ser_wait(int fd, long msec) +{ + int res; + while(!(res = ser_pending(fd))) { + /* TODO timeout */ + } + return res; +} + +static int can_send(int fd) +{ + int base = ports[fd].base; + return inp(base + UART_LSTAT) & LST_TREG_EMPTY; +} + +void ser_putc(int fd, char c) +{ + int base = ports[fd].base; + + if(c == '\n') { + ser_putc(fd, '\r'); + } + + while(!can_send(fd)); + /*while((inp(base + UART_MSTAT) & MST_CTS) == 0);*/ + outp(base + UART_DATA, c); +} + +int ser_getc(int fd) +{ + struct serial_port *p = ports + fd; + int have, c = -1; + + if(p->blocking) { + while(!(have = ser_pending(fd))); + } else { + have = ser_pending(fd); + } + + if(have) { + c = p->inbuf[p->inbuf_ridx]; + p->inbuf_ridx = BNEXT(p->inbuf_ridx); + } + return c; +} + +int ser_write(int fd, const char *buf, int count) +{ + int n = count; + while(n--) { + ser_putc(fd, *buf++); + } + return count; +} + +int ser_read(int fd, char *buf, int count) +{ + int c, n = 0; + while(n < count && (c = ser_getc(fd)) != -1) { + *buf++ = c; + ++n; + } + return n; +} + +char *ser_getline(int fd, char *buf, int bsz) +{ + static int widx; + char linebuf[512]; + int i, rd, size, offs; + + size = sizeof linebuf - widx - 1; + while(size && (rd = ser_read(fd, linebuf + widx, size)) > 0) { + widx += rd; + size -= rd; + } + + linebuf[widx] = 0; + + for(i=0; i= bsz ? bsz - 1 : i; + memcpy(buf, linebuf, size); + buf[size] = 0; + + offs = i + 1; + memmove(linebuf, linebuf + offs, widx - offs); + widx -= offs; + return buf; + } + } + return 0; +} + +static int have_recv(int base) +{ + unsigned short stat = inp(base + UART_LSTAT); + if(stat & LST_ERROR) { + panic("serial receive error\n"); + } + return stat & LST_DRDY; +} + +static void recv_intr() +{ + int i, idreg, c; + + for(i=0; i<2; i++) { + int base = uart_base[i]; + struct serial_port *p = ports + i; + + while(((idreg = inp(base + UART_IID)) & IID_PENDING) == 0) { + while(have_recv(base)) { + c = inp(base + UART_DATA); + +#ifdef ENABLE_GDB_STUB + if(c == 3 && i == GDB_SERIAL_PORT) { + asm("int $3"); + continue; + } +#endif + + p->inbuf[p->inbuf_widx] = c; + p->inbuf_widx = BNEXT(p->inbuf_widx); + + if(p->inbuf_widx == p->inbuf_ridx) { + /* we overflowed, drop the oldest */ + p->inbuf_ridx = BNEXT(p->inbuf_ridx); + } + } + } + } +} + +#ifdef ENABLE_GDB_STUB +void putDebugChar(int c) +{ + ser_putc(GDB_SERIAL_PORT, c); +} + +int getDebugChar(void) +{ + return ser_getc(GDB_SERIAL_PORT); +} +#endif diff --git a/src/kern/serial.h b/src/kern/serial.h new file mode 100644 index 0000000..fb2c17e --- /dev/null +++ b/src/kern/serial.h @@ -0,0 +1,46 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef SERIAL_H_ +#define SERIAL_H_ + +#define SER_8N1 0 +#define SER_8N2 1 +#define SER_HWFLOW 2 + +int ser_open(int pidx, int baud, unsigned int mode); +void ser_close(int fd); + +int ser_block(int fd); +int ser_nonblock(int fd); + +int ser_pending(int fd); +/* if msec < 0: wait for ever */ +int ser_wait(int fd, long msec); + +void ser_putc(int fd, char c); +int ser_getc(int fd); + +int ser_write(int fd, const char *buf, int count); +int ser_read(int fd, char *buf, int count); + +#define ser_putchar(c) ser_putc(0, c) + +char *ser_getline(int fd, char *buf, int bsz); + + +#endif /* SERIAL_H_ */ diff --git a/src/kern/timer.c b/src/kern/timer.c new file mode 100644 index 0000000..c050b84 --- /dev/null +++ b/src/kern/timer.c @@ -0,0 +1,192 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2023 John Tsiombikas + +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 3 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, see . +*/ +#include +#include "intr.h" +#include "asmops.h" +#include "timer.h" +#include "panic.h" +#include "config.h" + +/* frequency of the oscillator driving the 8254 timer */ +#define OSC_FREQ_HZ 1193182 + +/* macro to divide and round to the nearest integer */ +#define DIV_ROUND(a, b) ((a) / (b) + ((a) % (b)) / ((b) / 2)) + +/* I/O ports connected to the 8254 */ +#define PORT_DATA0 0x40 +#define PORT_DATA1 0x41 +#define PORT_DATA2 0x42 +#define PORT_CMD 0x43 + +/* command bits */ +#define CMD_CHAN0 0 +#define CMD_CHAN1 (1 << 6) +#define CMD_CHAN2 (2 << 6) +#define CMD_RDBACK (3 << 6) + +#define CMD_LATCH 0 +#define CMD_ACCESS_LOW (1 << 4) +#define CMD_ACCESS_HIGH (2 << 4) +#define CMD_ACCESS_BOTH (3 << 4) + +#define CMD_OP_INT_TERM 0 +#define CMD_OP_ONESHOT (1 << 1) +#define CMD_OP_RATE (2 << 1) +#define CMD_OP_SQWAVE (3 << 1) +#define CMD_OP_SOFT_STROBE (4 << 1) +#define CMD_OP_HW_STROBE (5 << 1) + +#define CMD_MODE_BIN 0 +#define CMD_MODE_BCD 1 + +volatile unsigned long nticks; + +struct timer_event { + int dt; /* remaining ticks delta from the previous event */ + void (*func)(void); + struct timer_event *next; +}; + +static void timer_handler(int inum); + +static struct timer_event *evlist; + + +void init_timer(void) +{ + /* calculate the reload count: round(osc / freq) */ + int reload_count = DIV_ROUND(OSC_FREQ_HZ, TICK_FREQ_HZ); + + /* set the mode to square wave for channel 0, both low + * and high reload count bytes will follow... + */ + outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE); + + /* write the low and high bytes of the reload count to the + * port for channel 0 + */ + outp(PORT_DATA0, reload_count & 0xff); + outp(PORT_DATA0, (reload_count >> 8) & 0xff); + + /* set the timer interrupt handler */ + interrupt(IRQ_TO_INTR(0), timer_handler); + unmask_irq(0); +} + +void cleanup_timer(void) +{ + /* return the timer to the original rate */ + outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE); + outp(PORT_DATA0, 0); + outp(PORT_DATA0, 0); +} + +void set_alarm(unsigned long msec, void (*func)(void)) +{ + int ticks, tsum, iflag; + struct timer_event *ev, *node; + + if((ticks = MSEC_TO_TICKS(msec)) <= 0) { + return; + } + + if(!(ev = malloc(sizeof *ev))) { + panic("failed to allocate timer event"); + return; + } + ev->func = func; + + iflag = get_intr_flag(); + disable_intr(); + + if(!evlist || ticks < evlist->dt) { + /* insert at the begining */ + ev->next = evlist; + evlist = ev; + + ev->dt = ticks; + if(ev->next) { + ev->next->dt -= ticks; + } + } else { + tsum = evlist->dt; + node = evlist; + + while(node->next && ticks > tsum + node->next->dt) { + tsum += node->next->dt; + node = node->next; + } + + ev->next = node->next; + node->next = ev; + + /* fix the relative times */ + ev->dt = ticks - tsum; + if(ev->next) { + ev->next->dt -= ev->dt; + } + } + + set_intr_flag(iflag); +} + +void cancel_alarm(void (*func)(void)) +{ + int iflag; + struct timer_event *ev, *node; + struct timer_event dummy; + + iflag = get_intr_flag(); + disable_intr(); + + dummy.next = evlist; + node = &dummy; + while(node->next) { + ev = node->next; + if(ev->func == func) { + /* found it */ + if(ev->next) { + ev->next->dt += ev->dt; + } + node->next = ev->next; + free(ev); + break; + } + node = node->next; + } + + set_intr_flag(iflag); +} + +static void timer_handler(int inum) +{ + nticks++; + + if(evlist) { + evlist->dt--; + + while(evlist && evlist->dt <= 0) { + struct timer_event *ev = evlist; + evlist = evlist->next; + + ev->func(); + free(ev); + } + } +} diff --git a/src/kern/timer.h b/src/kern/timer.h new file mode 100644 index 0000000..c057801 --- /dev/null +++ b/src/kern/timer.h @@ -0,0 +1,43 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2023 John Tsiombikas + +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 3 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, see . +*/ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#include "config.h" + +#define MSEC_TO_TICKS(ms) ((ms) * TICK_FREQ_HZ / 1000) +#define TICKS_TO_MSEC(tk) ((tk) * 1000 / TICK_FREQ_HZ) + +extern volatile unsigned long nticks; + +void init_timer(void); +void cleanup_timer(void); + +/* +int sys_sleep(int sec); +void sleep(unsigned long msec); +*/ + +/* schedule a function to be called 'msec' milliseconds into the future + * warning: this function will be called directly from the timer interrupt, and + * must be extremely quick. + */ +void set_alarm(unsigned long msec, void (*func)(void)); +void cancel_alarm(void (*func)(void)); + +#endif /* _TIMER_H_ */ diff --git a/src/kern/tss.h b/src/kern/tss.h new file mode 100644 index 0000000..e9c6943 --- /dev/null +++ b/src/kern/tss.h @@ -0,0 +1,38 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef TSS_H_ +#define TSS_H_ + +#include + +struct task_state { + uint32_t prev_task; + uint32_t esp0, ss0; /* we only ever set these two values */ + uint32_t esp1, ss1; + uint32_t esp2, ss2; + uint32_t cr3; + uint32_t eip; + uint32_t eflags; + uint32_t eax, ecx, edx, ebx; + uint32_t esp, ebp, esi, edi; + uint32_t es, cs, ss, ds, fs, gs; + uint32_t ldt_sel; + uint16_t trap, iomap_addr; +} __attribute__((packed)); + +#endif /* TSS_H_ */ diff --git a/src/libc/alloca.h b/src/libc/alloca.h new file mode 100644 index 0000000..ae27a71 --- /dev/null +++ b/src/libc/alloca.h @@ -0,0 +1,23 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef ALLOCA_H_ +#define ALLOCA_H_ + +#define alloca(x) __builtin_alloca(x) + +#endif /* ALLOCA_H_ */ diff --git a/src/libc/assert.h b/src/libc/assert.h new file mode 100644 index 0000000..7180c97 --- /dev/null +++ b/src/libc/assert.h @@ -0,0 +1,28 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef ASSERT_H_ +#define ASSERT_H_ + +#include "panic.h" + +#define assert(x) \ + if(!(x)) { \ + panic("Kernel assertion failed at " __FILE__ ":%d: " #x "\n", __LINE__); \ + } + +#endif /* ASSERT_H_ */ diff --git a/src/libc/ctype.c b/src/libc/ctype.c new file mode 100644 index 0000000..89e7b93 --- /dev/null +++ b/src/libc/ctype.c @@ -0,0 +1,73 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#include "ctype.h" + +int isalnum(int c) +{ + return isalpha(c) || isdigit(c); +} + +int isalpha(int c) +{ + return isupper(c) || islower(c); +} + +int isblank(int c) +{ + return c == ' ' || c == '\t'; +} + +int isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +int isupper(int c) +{ + return c >= 'A' && c <= 'Z'; +} + +int islower(int c) +{ + return c >= 'a' && c <= 'z'; +} + +int isgraph(int c) +{ + return c > ' ' && c <= '~'; +} + +int isprint(int c) +{ + return isgraph(c) || c == ' '; +} + +int isspace(int c) +{ + return isblank(c) || c == '\f' || c == '\n' || c == '\r' || c == '\v'; +} + +int toupper(int c) +{ + return islower(c) ? (c + ('A' - 'a')) : c; +} + +int tolower(int c) +{ + return isupper(c) ? (c - ('A' - 'a')) : c; +} diff --git a/src/libc/ctype.h b/src/libc/ctype.h new file mode 100644 index 0000000..79595c8 --- /dev/null +++ b/src/libc/ctype.h @@ -0,0 +1,34 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef CTYPE_H_ +#define CTYPE_H_ + +int isalnum(int c); +int isalpha(int c); +#define isascii(c) ((c) < 128) +int isblank(int c); +int isdigit(int c); +int isupper(int c); +int islower(int c); +int isprint(int c); +int isspace(int c); + +int toupper(int c); +int tolower(int c); + +#endif /* CTYPE_H_ */ diff --git a/src/libc/errno.h b/src/libc/errno.h new file mode 100644 index 0000000..8c152de --- /dev/null +++ b/src/libc/errno.h @@ -0,0 +1,41 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef ERRNO_H_ +#define ERRNO_H_ + +#define EFOO 1 +#define EAGAIN 2 +#define EINVAL 3 +#define ECHILD 4 +#define EBUSY 5 +#define ENOMEM 6 +#define EIO 7 +#define ENOENT 8 +#define ENAMETOOLONG 9 +#define ENOSPC 10 +#define EPERM 11 +#define ENOTDIR 12 +#define EISDIR 13 +#define EEXIST 14 +#define ERANGE 34 + +#define EBUG 127 /* for missing features and known bugs */ + +extern int errno; + +#endif /* ERRNO_H_ */ diff --git a/src/libc/float.h b/src/libc/float.h new file mode 100644 index 0000000..073bf6d --- /dev/null +++ b/src/libc/float.h @@ -0,0 +1,24 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#ifndef FLOAT_H_ +#define FLOAT_H_ + +#define FLT_MIN __FLT_MIN__ +#define FLT_MAX __FLT_MAX__ + +#endif /* FLOAT_H_ */ diff --git a/src/libc/inttypes.h b/src/libc/inttypes.h new file mode 100644 index 0000000..39d948a --- /dev/null +++ b/src/libc/inttypes.h @@ -0,0 +1,31 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef INTTYPES_H_ +#define INTTYPES_H_ + +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long long int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +#endif /* INTTYPES_H_ */ diff --git a/src/libc/limits.h b/src/libc/limits.h new file mode 100644 index 0000000..b3abdad --- /dev/null +++ b/src/libc/limits.h @@ -0,0 +1,36 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#ifndef LIMITS_H_ +#define LIMITS_H_ + +#define CHAR_BIT 8 + +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 +#define INT_MIN (-2147483648) +#define INT_MAX 2147483647 +#define LONG_MIN (-2147483648) +#define LONG_MAX 2147483647 + +#define USHRT_MAX 65535 +#define UINT_MAX 0xffffffff +#define ULONG_MAX 0xffffffff + +#define PATH_MAX 256 + +#endif /* LIMITS_H_ */ diff --git a/src/libc/malloc.c b/src/libc/malloc.c new file mode 100644 index 0000000..c064915 --- /dev/null +++ b/src/libc/malloc.c @@ -0,0 +1,429 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#include +#include +#include +#include +#include +#include "mem.h" +#include "panic.h" + +#define SINGLE_POOL + +struct mem_desc { + size_t size; + uint32_t magic; +#ifdef MALLOC_DEBUG + uint32_t dbg; +#endif + struct mem_desc *next; +}; + +#ifdef MALLOC_DEBUG +static void check_cycles(struct mem_desc *mem); +static void print_pool(void); +#endif + +#define MAGIC_USED 0xdeadf00d +#define MAGIC_FREE 0x1ee7d00d + +#define DESC_PTR(b) ((void*)((struct mem_desc*)(b) + 1)) +#define PTR_DESC(p) ((struct mem_desc*)(p) - 1) + +#ifdef SINGLE_POOL +static struct mem_desc *pool; +#else + +#define NUM_POOLS 16 +#define FIRST_POOL_POW2 5 +/* 2**(x+5) size pools: 0->32, 1->64, 2->128 .. 15->1048576 */ +#define POOL_SIZE(x) (1 << ((x) + FIRST_POOL_POW2)) +#define MAX_POOL_SIZE (1 << ((NUM_POOLS - 1) + FIRST_POOL_POW2)) +#define MAX_POOL_PAGES BYTES_TO_PAGES(MAX_POOL_SIZE) +static struct mem_desc *pools[NUM_POOLS]; + +static int add_to_pool(struct mem_desc *b); + +static int pool_index(int sz) +{ + int x = 0; + --sz; + while(sz) { + sz >>= 1; + ++x; + } + return x - FIRST_POOL_POW2; +} +#endif /* !SINGLE_POOL */ + + +#ifdef SINGLE_POOL +#define MIN_BLOCK_SIZE (sizeof(struct mem_desc) * 2) + +void *malloc(size_t sz) +{ + int pg0, npages; + size_t total_sz, rest_sz; + struct mem_desc *mem, *rest, *prev, dummy; + int found = 0; + + total_sz = (sz + sizeof(struct mem_desc) + 3) & 0xfffffffc; + + dummy.next = pool; + prev = &dummy; + while(prev->next) { + mem = prev->next; + /* give the whole block to the allocation if mem->size == total_sz or + * if it's larger, but not large enough to fit another mem_desc in there + * for the new block that we're going to split off + some reasonable + * amount of memory for the new block. + */ + if(mem->size >= total_sz && mem->size < total_sz + MIN_BLOCK_SIZE) { + prev->next = mem->next; + found = 1; + break; + } + /* if we have enough space, split the block and give the upper part + * to the allocation + */ + if(mem->size > total_sz) { + void *ptr = (char*)mem + mem->size - total_sz; + mem->size -= total_sz; + mem = ptr; + found = 1; + break; + } + prev = prev->next; + } + pool = dummy.next; + + if(found) { + mem->size = total_sz; + mem->magic = MAGIC_USED; + mem->next = 0; + return DESC_PTR(mem); + } + + /* did not find a free block, grab a new one */ + npages = BYTES_TO_PAGES(total_sz); + if((pg0 = alloc_ppages(npages, 0)) == -1) { + errno = ENOMEM; + return 0; + } + mem = PAGE_TO_PTR(pg0); + mem->size = total_sz; + mem->next = 0; + mem->magic = MAGIC_USED; + + /* add the rest of the block to pool */ + rest_sz = npages * 4096 - total_sz; + if(rest_sz > 0) { + rest = (struct mem_desc*)((char*)mem + total_sz); + rest->size = rest_sz; + rest->next = 0; + rest->magic = MAGIC_USED; + free(DESC_PTR(rest)); + } + + return DESC_PTR(mem); +} + +void free(void *p) +{ + struct mem_desc *mem, *prev, *cur, dummy; + char *end; + + if(!p) return; + mem = PTR_DESC(p); + + if(mem->magic != MAGIC_USED) { + if(mem->magic == MAGIC_FREE) { + panic("free(%p): double-free\n", p); + } else { + panic("free(%p): corrupted magic (%x)!\n", p, mem->magic); + } + } + mem->magic = MAGIC_FREE; + mem->next = 0; + + /* nothing in the pool, just add this one */ + if(!pool) { + pool = mem; + return; + } + + end = (char*)mem + mem->size; + + dummy.next = pool; + prev = &dummy; + + while(prev->next) { + cur = prev->next; + + /* block starts right at the end of mem: coalesce */ + if((char*)cur == end) { + mem->size += cur->size; + mem->next = cur->next; + cur->magic = 0; + prev->next = mem; + goto done; + } + + /* block starts *after* the end of mem: add in front */ + if((char*)cur > end) { + mem->next = cur; + prev->next = mem; + goto done; + } + + prev = prev->next; + } + + /* our block starts at the end of the last block in the pool: coalesce */ + if((char*)prev + prev->size == (char*)mem) { + mem->magic = 0; + prev->size += mem->size; + goto done; + } + + /* so our block starts after the end of the last block: append */ + prev->next = mem; + +done: + pool = dummy.next; + +#ifdef MALLOC_DEBUG + print_pool(); +#endif +} + +#else /* !SINGLE_POOL */ + +void *malloc(size_t sz) +{ + int pidx, pg0; + size_t total_sz, halfsz; + struct mem_desc *mem, *other; + + total_sz = sz + sizeof(struct mem_desc); + + if(total_sz > MAX_POOL_SIZE) { + /*printf(" allocation too big, hitting sys_alloc directly\n");*/ + if((pg0 = alloc_ppages(BYTES_TO_PAGES(total_sz), 0)) == -1) { + errno = ENOMEM; + return 0; + } + mem = PAGE_TO_PTR(pg0); + mem->magic = MAGIC_USED; + mem->size = total_sz; + mem->next = 0; + return DESC_PTR(mem); + } + + pidx = pool_index(total_sz); + while(pidx < NUM_POOLS) { + if(pools[pidx]) { + /* found a pool with a free block that fits */ + mem = pools[pidx]; + pools[pidx] = mem->next; + mem->next = 0; + + /*printf(" using pool %d (size %ld)\n", pidx, (unsigned long)mem->size);*/ + + /* while the mem size is larger than twice the allocation, split it */ + while(pidx-- > 0 && total_sz <= (halfsz = mem->size / 2)) { + /*printf(" splitting %ld block in half and ", (unsigned long)mem->size);*/ + other = (struct mem_desc*)((char*)mem + halfsz); + mem->size = other->size = halfsz; + + add_to_pool(other); + } + + if(mem->magic != MAGIC_FREE) { + panic("Trying to allocate range surrounded by an aura of wrong MAGIC\n"); + } + mem->magic = MAGIC_USED; + return DESC_PTR(mem); + } + + ++pidx; + } + + /* did not find a free block, add a new one */ + pidx = NUM_POOLS - 1; + if((pg0 = alloc_ppages(MAX_POOL_PAGES, 0)) == -1) { + errno = ENOMEM; + return 0; + } + mem = PAGE_TO_PTR(pg0); + mem->size = MAX_POOL_SIZE; + mem->next = pools[pidx]; + mem->magic = MAGIC_FREE; + pools[pidx] = mem; + + /* try again now that there is a free block */ + return malloc(sz); +} + + +void free(void *p) +{ + int pg0; + struct mem_desc *mem; + + if(!p) return; + + mem = PTR_DESC(p); + if(mem->magic != MAGIC_USED) { + if(mem->magic == MAGIC_FREE) { + panic("free(%p): double-free\n", p); + } else { + panic("free(%p): corrupted magic (%x)!\n", p, mem->magic); + } + } + + if(mem->size > MAX_POOL_SIZE) { + /*printf("foo_free(%p): %ld bytes. block too large for pools, returning to system\n", + (void*)p, (unsigned long)mem->size);*/ + pg0 = ADDR_TO_PAGE(mem); + free_ppages(pg0, BYTES_TO_PAGES(mem->size)); + return; + } + + /*printf("foo_free(%p): block of %ld bytes and ", (void*)p, (unsigned long)mem->size);*/ + add_to_pool(mem); +} + +#endif /* !def SINGLE_POOL */ + + +void *calloc(size_t num, size_t size) +{ + void *ptr = malloc(num * size); + if(ptr) { + memset(ptr, 0, num * size); + } + return ptr; +} + +void *realloc(void *ptr, size_t size) +{ + struct mem_desc *mem; + void *newp; + + if(!ptr) { + return malloc(size); + } + + mem = PTR_DESC(ptr); + if(mem->size >= size) { + return ptr; /* TODO: shrink */ + } + + if(!(newp = malloc(size))) { + return 0; + } + memcpy(newp, ptr, mem->size); + free(ptr); + return newp; +} + +#ifdef MALLOC_DEBUG +static void check_cycles(struct mem_desc *mem) +{ + static uint32_t dbg = 42; + uint32_t cur_dbg = dbg++; + + while(mem) { + if(mem->magic != MAGIC_FREE) { + panic("check_cycles: NON-FREE MAGIC!\n"); + } + if(mem->dbg == cur_dbg) { + panic("CYCLE DETECTED\n"); + } + mem->dbg = cur_dbg; + mem = mem->next; + } +} + +static void print_pool(void) +{ + struct mem_desc *mem = pool; + + printf("DBG: malloc pool:\n"); + while(mem) { + printf(" %p (%ld) [%x]\n", mem, mem->size, mem->magic); + mem = mem->next; + + assert((uint32_t)mem != MAGIC_USED); + } +} +#endif /* MALLOC_DEBUG */ + +#ifndef SINGLE_POOL +static int add_to_pool(struct mem_desc *mem) +{ + int pidx; + struct mem_desc head; + struct mem_desc *iter, *pnode; + + pidx = pool_index(mem->size); + + /*printf("adding %ld block to pool %d\n", (unsigned long)mem->size, pidx);*/ + + iter = &head; + head.next = pools[pidx]; + + while(iter->next) { + pnode = iter->next; + if(mem->size == pnode->size) { /* only coalesce same-sized blocks */ + size_t size = mem->size; + + if((char*)mem == (char*)pnode - size) { + iter->next = pnode->next; /* unlink pnode */ + pools[pidx] = head.next; + mem->next = 0; + mem->size += size; + + /*printf(" coalescing %p with %p and ", (void*)mem, (void*)pnode);*/ + return add_to_pool(mem); + } + if((char*)mem == (char*)pnode + size) { + iter->next = pnode->next; /* unlink pnode */ + pools[pidx] = head.next; + pnode->next = 0; + pnode->size += size; + + /*printf(" coalescing %p with %p and ", (void*)mem, (void*)pnode);*/ + return add_to_pool(pnode); + } + } + iter = iter->next; + } + + /* otherwise just add it to the pool */ + mem->magic = MAGIC_FREE; + mem->next = pools[pidx]; + pools[pidx] = mem; + +#ifdef MALLOC_DEBUG + check_cycles(pools[pidx]); +#endif + return 0; +} +#endif /* !def SINGLE_POOL */ diff --git a/src/libc/math.c b/src/libc/math.c new file mode 100644 index 0000000..3f3e5de --- /dev/null +++ b/src/libc/math.c @@ -0,0 +1,55 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#include "math.h" + +static double calc_pow(double x, double y, double precision); + +double pow(double x, double y) +{ + if(y == 0.0 || y == -0.0) { + return 1.0; + } + if(y == 1.0) { + return x; + } + if(y == -INFINITY) { + return fabs(x) < 1.0 ? INFINITY : 0.0; + } + if(y == INFINITY) { + return fabs(x) < 1.0 ? 0.0 : INFINITY; + } + return calc_pow(x, y, 1e-6); +} + +static double calc_pow(double x, double y, double precision) +{ + if(y < 0.0) { + return 1.0 / calc_pow(x, -y, precision); + } + if(y >= 10.0) { + double p = calc_pow(x, y / 2.0, precision / 2.0); + return p * p; + } + if(y >= 1.0) { + return x * calc_pow(x, y - 1.0, precision); + } + if(precision >= 1) { + return __builtin_sqrt(x); + } + return __builtin_sqrt(calc_pow(x, y * 2.0, precision * 2.0)); +} diff --git a/src/libc/math.h b/src/libc/math.h new file mode 100644 index 0000000..e3b0f23 --- /dev/null +++ b/src/libc/math.h @@ -0,0 +1,36 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#ifndef MATH_H_ +#define MATH_H_ + +#define INFINITY __builtin_inff() +#define NAN __builtin_nanf + +#define M_PI 3.141592653589793 + +#define sin(x) __builtin_sin(x) +#define cos(x) __builtin_cos(x) +#define tan(x) __builtin_tan(x) +#define fabs(x) __builtin_fabs(x) +#define fmod(x, y) __builtin_fmod(x, y) +#define sqrt(x) __builtin_sqrt(x) +#define atan2(y, x) __builtin_atan2(y, x) + +double pow(double x, double y); + +#endif /* MATH_H_ */ diff --git a/src/libc/rand.c b/src/libc/rand.c new file mode 100644 index 0000000..de159c2 --- /dev/null +++ b/src/libc/rand.c @@ -0,0 +1,65 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +/* random number generator, based on this description of the algorithm + * used by the GNU libc: https://www.mathstat.dal.ca/~selinger/random + */ +#include +#include + +static int init_done; +static int32_t rng[34]; +static int32_t *ptr0, *ptr1; + +int rand(void) +{ + int res; + + if(!init_done) { + srand(1); + } + + *ptr1 += *ptr0; + res = (uint32_t)*ptr1 >> 1; + if(++ptr0 >= rng + 34) ptr0 = rng; + if(++ptr1 >= rng + 34) ptr1 = rng; + + return res; +} + +void srand(unsigned int seed) +{ + int i; + + init_done = 1; + if(seed == 0) seed = 1; + + rng[0] = seed; + for(i=1; i<31; i++) { + rng[i] = (16807 * rng[i - 1]) % RAND_MAX; + if(rng[i] < 0) rng[i] += RAND_MAX; + } + for(i=31; i<34; i++) { + rng[i] = rng[i - 31]; + } + ptr0 = rng + 3; + ptr1 = rng + 31; + + for(i=34; i<344; i++) { + rand(); + } +} diff --git a/src/libc/setjmp.c b/src/libc/setjmp.c new file mode 100644 index 0000000..517563b --- /dev/null +++ b/src/libc/setjmp.c @@ -0,0 +1,28 @@ +/* +256boss - bootable launcher for 256byte intros +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#include + +int setjmp(jmp_buf buf) +{ + return __builtin_setjmp(buf); +} + +void longjmp(jmp_buf buf, int val) +{ + __builtin_longjmp(buf, 1); +} diff --git a/src/libc/setjmp.h b/src/libc/setjmp.h new file mode 100644 index 0000000..c92e93d --- /dev/null +++ b/src/libc/setjmp.h @@ -0,0 +1,26 @@ +/* +256boss - bootable launcher for 256byte intros +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#ifndef SETJMP_H_ +#define SETJMP_H_ + +typedef unsigned long jmp_buf[5]; + +int setjmp(jmp_buf buf); +void longjmp(jmp_buf buf, int val); + +#endif /* SETJMP_H_ */ diff --git a/src/libc/stdarg.h b/src/libc/stdarg.h new file mode 100644 index 0000000..2d0bb92 --- /dev/null +++ b/src/libc/stdarg.h @@ -0,0 +1,29 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +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 3 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, see . +*/ +#ifndef STDARG_H_ +#define STDARG_H_ + +/* Assumes that arguments are passed on the stack 4-byte aligned */ + +typedef int* va_list; + +#define va_start(ap, last) ((ap) = (int*)&(last) + 1) +#define va_arg(ap, type) (*(type*)(ap)++) +#define va_end(ap) + +#endif /* STDARG_H_ */ diff --git a/src/libc/stddef.h b/src/libc/stddef.h new file mode 100644 index 0000000..9ec6bda --- /dev/null +++ b/src/libc/stddef.h @@ -0,0 +1,32 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#ifndef STDDEF_H_ +#define STDDEF_H_ + +#include + +typedef int32_t ssize_t; +typedef uint32_t size_t; +typedef int wchar_t; + +typedef int32_t ptrdiff_t; +typedef uint32_t intptr_t; + +#define NULL 0 + +#endif /* STDDEF_H_ */ diff --git a/src/libc/stdio.c b/src/libc/stdio.c new file mode 100644 index 0000000..a0e39b1 --- /dev/null +++ b/src/libc/stdio.c @@ -0,0 +1,409 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#include +#include +#include +#include +#include "contty.h" +#include "serial.h" +#include "panic.h" + +enum { + OUT_DEF, + OUT_BUF, + OUT_SCR, + OUT_SER +}; + +extern void pcboot_putchar(int c); + +static int intern_printf(int out, char *buf, size_t sz, const char *fmt, va_list ap); +static int intern_scanf(const char *instr, FILE *infile, const char *fmt, va_list ap); +static void bwrite(int out, char *buf, size_t buf_sz, char *str, int sz); +/*static int readchar(const char *str, FILE *fp);*/ + +int putchar(int c) +{ + con_putchar(c); + return c; +} + +int puts(const char *s) +{ + while(*s) { + putchar(*s++); + } + putchar('\n'); + return 0; +} + +/* -- printf and friends -- */ + +int printf(const char *fmt, ...) +{ + int res; + va_list ap; + + va_start(ap, fmt); + res = intern_printf(OUT_DEF, 0, 0, fmt, ap); + va_end(ap); + return res; +} + +int vprintf(const char *fmt, va_list ap) +{ + return intern_printf(OUT_DEF, 0, 0, fmt, ap); +} + +int sprintf(char *buf, const char *fmt, ...) +{ + int res; + va_list ap; + + va_start(ap, fmt); + res = intern_printf(OUT_BUF, buf, 0, fmt, ap); + va_end(ap); + return res; +} + +int vsprintf(char *buf, const char *fmt, va_list ap) +{ + return intern_printf(OUT_BUF, buf, 0, fmt, ap); +} + +int snprintf(char *buf, size_t sz, const char *fmt, ...) +{ + int res; + va_list ap; + + va_start(ap, fmt); + res = intern_printf(OUT_BUF, buf, sz, fmt, ap); + va_end(ap); + return res; +} + +int vsnprintf(char *buf, size_t sz, const char *fmt, va_list ap) +{ + return intern_printf(OUT_BUF, buf, sz, fmt, ap); +} + +int fprintf(FILE *fp, const char *fmt, ...) +{ + int res; + va_list ap; + + va_start(ap, fmt); + res = vfprintf(fp, fmt, ap); + va_end(ap); + return res; +} + +int vfprintf(FILE *fp, const char *fmt, va_list ap) +{ + if(fp == stdout || fp == stderr) { + return vprintf(fmt, ap); + } + + panic("*fprintf for anything other than stdout/stderr, not implemented yet\n"); + return 0; +} + +int ser_printf(const char *fmt, ...) +{ + int res; + va_list ap; + + va_start(ap, fmt); + res = intern_printf(OUT_SER, 0, 0, fmt, ap); + va_end(ap); + return res; +} + +int ser_vprintf(const char *fmt, va_list ap) +{ + return intern_printf(OUT_SER, 0, 0, fmt, ap); +} + +/* +void perror(const char *s) +{ + printf("%s: %s\n", s, strerror(errno)); +} +*/ + +/* intern_printf provides all the functionality needed by all the printf + * variants. + * - buf: optional buffer onto which the formatted results are written. If null + * then the output goes to the terminal through putchar calls. This is used + * by the (v)sprintf variants which write to an array of char. + * - sz: optional maximum size of the output, 0 means unlimited. This is used + * by the (v)snprintf variants to avoid buffer overflows. + * The rest are obvious, format string and variable argument list. + */ +static char *convc = "dioxXucsfeEgGpn%"; + +#define IS_CONV(c) strchr(convc, c) + +#define BUF(x) ((x) ? (x) + cnum : (x)) +#define SZ(x) ((x) ? (x) - cnum : (x)) + +static int intern_printf(int out, char *buf, size_t sz, const char *fmt, va_list ap) +{ + char conv_buf[32]; + char *str; + int i, slen; + const char *fstart = 0; + + /* state */ + int cnum = 0; + int base = 10; + int alt = 0; + int fwidth = 0; + int padc = ' '; + int sign = 0; + int left_align = 0; + int hex_caps = 0; + int unsig = 0; + int num, unum; + + while(*fmt) { + if(*fmt == '%') { + fstart = fmt++; + continue; + } + + if(fstart) { + if(IS_CONV(*fmt)) { + switch(*fmt) { + case 'X': + hex_caps = 1; + + case 'x': + case 'p': + base = 16; + + if(alt) { + bwrite(out, BUF(buf), SZ(sz), "0x", 2); + cnum += 2; + } + + case 'u': + unsig = 1; + + if(0) { + case 'o': + base = 8; + + if(alt) { + bwrite(out, BUF(buf), SZ(sz), "0", 1); + cnum++; + } + } + + case 'd': + case 'i': + if(unsig) { + unum = va_arg(ap, unsigned int); + utoa(unum, conv_buf, base); + } else { + num = va_arg(ap, int); + itoa(num, conv_buf, base); + } + if(hex_caps) { + for(i=0; conv_buf[i]; i++) { + conv_buf[i] = toupper(conv_buf[i]); + } + } + + slen = strlen(conv_buf); + + if(left_align) { + if(!unsig && sign && num >= 0) { + bwrite(out, BUF(buf), SZ(sz), "+", 1); + cnum++; + } + bwrite(out, BUF(buf), SZ(sz), conv_buf, slen); + cnum += slen; + padc = ' '; + } + for(i=slen; i= 0) { + bwrite(out, BUF(buf), SZ(sz), "+", 1); + cnum++; + } + bwrite(out, BUF(buf), SZ(sz), conv_buf, slen); + cnum += slen; + } + break; + + case 'c': + { + char c = va_arg(ap, int); + bwrite(out, BUF(buf), SZ(sz), &c, 1); + cnum++; + } + break; + + case 's': + str = va_arg(ap, char*); + slen = strlen(str); + + if(left_align) { + bwrite(out, BUF(buf), SZ(sz), str, slen); + cnum += slen; + padc = ' '; + } + for(i=slen; i + +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 3 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, see . +*/ +#ifndef STDIO_H_ +#define STDIO_H_ + +#include +#include + +typedef struct FILE FILE; + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#define EOF (-1) + +#define stdin ((FILE*)0) +#define stdout ((FILE*)1) +#define stderr ((FILE*)2) + +int putchar(int c); +int puts(const char *s); + +int printf(const char *fmt, ...); +int vprintf(const char *fmt, va_list ap); + +int sprintf(char *buf, const char *fmt, ...); +int vsprintf(char *buf, const char *fmt, va_list ap); + +int snprintf(char *buf, size_t sz, const char *fmt, ...); +int vsnprintf(char *buf, size_t sz, const char *fmt, va_list ap); + +/* TODO */ +int fprintf(FILE *fp, const char *fmt, ...); +int vfprintf(FILE *fp, const char *fmt, va_list ap); + +/* TODO +int fscanf(FILE *fp, const char *fmt, ...); +int vfscanf(FILE *fp, const char *fmt, va_list ap); + +int sscanf(const char *str, const char *fmt, ...); +int vsscanf(const char *ptr, const char *fmt, va_list ap); +*/ + +/* printf to the serial port */ +int ser_printf(const char *fmt, ...); +int ser_vprintf(const char *fmt, va_list ap); + +void perror(const char *s); + + +/* FILE I/O */ +FILE *fopen(const char *path, const char *mode); +int fclose(FILE *fp); + +long filesize(FILE *fp); +int fseek(FILE *fp, long offset, int from); +void rewind(FILE *fp); +long ftell(FILE *fp); + +size_t fread(void *buf, size_t size, size_t count, FILE *fp); +size_t fwrite(const void *buf, size_t size, size_t count, FILE *fp); + +int fgetc(FILE *fp); +char *fgets(char *buf, int size, FILE *fp); + +int fputc(int c, FILE *fp); + +int fflush(FILE *fp); + +int feof(FILE *fp); +int ferror(FILE *fp); +void clearerr(FILE *fp); + +#endif /* STDIO_H_ */ diff --git a/src/libc/stdlib.c b/src/libc/stdlib.c new file mode 100644 index 0000000..6b130ed --- /dev/null +++ b/src/libc/stdlib.c @@ -0,0 +1,272 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#include +#include +#include +#include +#include +#include + +int atoi(const char *str) +{ + return strtol(str, 0, 10); +} + +long atol(const char *str) +{ + return strtol(str, 0, 10); +} + +long strtol(const char *str, char **endp, int base) +{ + long acc = 0; + int sign = 1; + int valid = 0; + const char *start = str; + + while(isspace(*str)) str++; + + if(base == 0) { + if(str[0] == '0') { + if(str[1] == 'x' || str[1] == 'X') { + base = 16; + } else { + base = 8; + } + } else { + base = 10; + } + } + + if(*str == '+') { + str++; + } else if(*str == '-') { + sign = -1; + str++; + } + + while(*str) { + long val = LONG_MAX; + char c = tolower(*str); + + if(isdigit(c)) { + val = *str - '0'; + } else if(c >= 'a' && c <= 'f') { + val = 10 + c - 'a'; + } else { + break; + } + if(val >= base) { + break; + } + valid = 1; + + acc = acc * base + val; + str++; + } + + if(endp) { + *endp = (char*)(valid ? str : start); + } + + return sign > 0 ? acc : -acc; +} + +void itoa(int val, char *buf, int base) +{ + static char rbuf[16]; + char *ptr = rbuf; + int neg = 0; + + if(val < 0) { + neg = 1; + val = -val; + } + + if(val == 0) { + *ptr++ = '0'; + } + + while(val) { + int digit = val % base; + *ptr++ = digit < 10 ? (digit + '0') : (digit - 10 + 'a'); + val /= base; + } + + if(neg) { + *ptr++ = '-'; + } + + ptr--; + + while(ptr >= rbuf) { + *buf++ = *ptr--; + } + *buf = 0; +} + +void utoa(unsigned int val, char *buf, int base) +{ + static char rbuf[16]; + char *ptr = rbuf; + + if(val == 0) { + *ptr++ = '0'; + } + + while(val) { + unsigned int digit = val % base; + *ptr++ = digit < 10 ? (digit + '0') : (digit - 10 + 'a'); + val /= base; + } + + ptr--; + + while(ptr >= rbuf) { + *buf++ = *ptr--; + } + *buf = 0; +} + +double atof(const char *str) +{ + return strtod(str, 0); +} + + +double strtod(const char *str, char **endp) +{ + char *ep; + const char *start = str; + int valid = 0; + long ival = 0, dval = 0; + int ddig = 0; + double res; + + /* integer part */ + ival = strtol(str, &ep, 10); + if(ep == str && *str != '.') { + if(endp) *endp = (char*)str; + return 0.0; + } + if(ep != str) valid = 1; + str = *ep == '.' ? ep + 1 : ep; + if(!isdigit(*str)) { + goto done; + } + valid = 1; + + dval = strtol(str, &ep, 10); + assert(dval >= 0); + ddig = ep - str; + str = ep; + +done: + if(*endp) { + *endp = (char*)(valid ? str : start); + } + + res = (double)ival; + if(ddig) { + double d = (double)dval; + while(ddig-- > 0) { + d /= 10.0; + } + res += d; + } + return res; +} + +int atexit(void (*func)(void)) +{ + /* there's no concept of exiting at the moment, so this does nothing */ + return 0; +} + +void abort(void) +{ + panic("Aborted\n"); +} + +#define QSORT_THRESHOLD 4 +#define ITEM(idx) ((char*)arr + (idx) * itemsz) + +#define SWAP(p, q) \ + do { \ + int nn = itemsz; \ + char *pp = (p); \ + char *qq = (q); \ + do { \ + char tmp = *pp; \ + *pp++ = *qq; \ + *qq++ = tmp; \ + } while(--nn > 0); \ + } while(0) + +static void ins_sort(void *arr, size_t count, size_t itemsz, int (*cmp)(const void*, const void*)) +{ + int i; + char *it, *a, *b; + + if(count <= 1) return; + + it = (char*)arr + itemsz; + for(i=1; i (char*)arr && cmp(a, (b = a - itemsz)) < 0) { + SWAP(a, b); + a -= itemsz; + } + } +} + +void qsort(void *arr, size_t count, size_t itemsz, int (*cmp)(const void*, const void*)) +{ + char *ma, *mb, *mc, *left, *right; + size_t sepidx, nleft, nright; + + if(count <= 1) return; + + if(count < QSORT_THRESHOLD) { + ins_sort(arr, count, itemsz, cmp); + return; + } + + ma = arr; + mb = ITEM(count / 2); + mc = ITEM(count - 1); + if(cmp(ma, mb) < 0) SWAP(ma, mb); + if(cmp(mc, ma) < 0) SWAP(mc, ma); + + left = ma + itemsz; + right = mc - itemsz; + for(;;) { + while(cmp(left, ma) < 0) left += itemsz; + while(cmp(ma, right) < 0) right -= itemsz; + if(left >= right) break; + SWAP(left, right); + } + SWAP(ma, right); + sepidx = (right - (char*)arr) / itemsz; + nleft = sepidx; + nright = count - nleft - 1; + + qsort(ma, nleft, itemsz, cmp); + qsort(right + itemsz, nright, itemsz, cmp); +} diff --git a/src/libc/stdlib.h b/src/libc/stdlib.h new file mode 100644 index 0000000..981cf69 --- /dev/null +++ b/src/libc/stdlib.h @@ -0,0 +1,53 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#ifndef STDLIB_H_ +#define STDLIB_H_ + +#include + +#define RAND_MAX 2147483647 + +#define abs(x) __builtin_abs(x) + +int atoi(const char *str); +long atol(const char *str); +long strtol(const char *str, char **endp, int base); + +void itoa(int val, char *buf, int base); +void utoa(unsigned int val, char *buf, int base); + +double atof(const char *str); +double strtod(const char *str, char **endp); + +int atexit(void (*func)(void)); + +void abort(void); + +void qsort(void *arr, size_t count, size_t size, int (*cmp)(const void*, const void*)); + +int rand(void); +int rand_r(unsigned int *seedp); +void srand(unsigned int seed); + +/* defined in malloc.c */ +void *malloc(size_t sz); +void *calloc(size_t num, size_t sz); +void *realloc(void *ptr, size_t sz); +void free(void *ptr); + +#endif /* STDLIB_H_ */ diff --git a/src/libc/string.c b/src/libc/string.c new file mode 100644 index 0000000..06d8f87 --- /dev/null +++ b/src/libc/string.c @@ -0,0 +1,260 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#include +#include +#include + +int errno; + +void *memmove(void *dest, const void *src, size_t n) +{ + int i; + char *dptr; + const char *sptr; + + if(dest <= src) { + /* forward copy */ + dptr = dest; + sptr = src; + for(i=0; i= 4) { + if(*a32 != *b32) break; + a32++; + b32++; + n -= 4; + } + + /* update byte pointers to contine with the tail */ + a = (unsigned char*)a32; + b = (unsigned char*)b32; + } + + /* we're here both for the tail-end of same-alignment buffers, or for the + * whole length of mis-aligned buffers. + */ + while(n-- > 0) { + if((diff = *a++ - *b++) != 0) { + return diff; + } + } + return 0; +} + +size_t strlen(const char *s) +{ + size_t len = 0; + while(*s++) len++; + return len; +} + +char *strchr(const char *s, int c) +{ + while(*s) { + if(*s == c) { + return (char*)s; + } + s++; + } + return 0; +} + +char *strrchr(const char *s, int c) +{ + const char *ptr = s; + + /* find the end */ + while(*ptr) ptr++; + + /* go back checking for c */ + while(--ptr >= s) { + if(*ptr == c) { + return (char*)ptr; + } + } + return 0; +} + +char *strstr(const char *str, const char *substr) +{ + while(*str) { + const char *s1 = str; + const char *s2 = substr; + + while(*s1 && *s1 == *s2) { + s1++; + s2++; + } + if(!*s2) { + return (char*)str; + } + str++; + } + return 0; +} + +char *strcasestr(const char *str, const char *substr) +{ + while(*str) { + const char *s1 = str; + const char *s2 = substr; + + while(*s1 && tolower(*s1) == tolower(*s2)) { + s1++; + s2++; + } + if(!*s2) { + return (char*)str; + } + str++; + } + return 0; +} + +int strcmp(const char *s1, const char *s2) +{ + while(*s1 && *s1 == *s2) { + s1++; + s2++; + } + return *s1 - *s2; +} + +int strcasecmp(const char *s1, const char *s2) +{ + while(*s1 && tolower(*s1) == tolower(*s2)) { + s1++; + s2++; + } + return tolower(*s1) - tolower(*s2); +} + +int strncmp(const char *s1, const char *s2, int n) +{ + if(n <= 0) return 0; + + while(n-- > 0 && *s1 && *s2 && *s1 == *s2) { + s1++; + s2++; + } + + if(n <= 0) return 0; + return *s1 - *s2; +} + +int strncasecmp(const char *s1, const char *s2, int n) +{ + if(n <= 0) return 0; + + while(n-- > 0 && *s1 && *s2 && tolower(*s1) == tolower(*s2)) { + s1++; + s2++; + } + + if(n <= 0) return 0; + return tolower(*s1) - tolower(*s2); +} + +char *strcpy(char *dest, const char *src) +{ + char *dptr = dest; + while((*dptr++ = *src++)); + return dest; +} + +char *strcat(char *dest, const char *src) +{ + strcpy(dest + strlen(dest), src); + return dest; +} + +char *strncpy(char *dest, const char *src, int n) +{ + char *dptr = dest; + while(n-- > 0 && (*dptr++ = *src++)); + return dest; +} + +/* +static const char *errstr[] = { + "Success", + "Foo", + "Interrupted", + "Invalid", + "Child", + "Timeout", + "Out of memory", + "I/O error", + "Not found", + "Name too long", + "No space left on device", + "Permission denied", + "Not a directory", + "Is a directory", + "Does not exist", + 0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0, + "Bug" +}; + +char *strerror(int err) +{ + if(err < 0 || err > sizeof errstr / sizeof *errstr || !errstr[err]) { + return "Unknown"; + } + return (char*)errstr[err]; +} +*/ diff --git a/src/libc/string.h b/src/libc/string.h new file mode 100644 index 0000000..85490b2 --- /dev/null +++ b/src/libc/string.h @@ -0,0 +1,52 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +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 3 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, see . +*/ +#ifndef STRING_H_ +#define STRING_H_ + +#include + +void *memset(void *s, int c, size_t n); +void *memset16(void *s, int c, size_t n); + +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); + +int memcmp(void *aptr, void *bptr, size_t n); + +size_t strlen(const char *s); + +char *strchr(const char *s, int c); +char *strrchr(const char *s, int c); + +char *strstr(const char *str, const char *substr); +char *strcasestr(const char *str, const char *substr); + +int strcmp(const char *s1, const char *s2); +int strcasecmp(const char *s1, const char *s2); + +int strncmp(const char *s1, const char *s2, int n); +int strncasecmp(const char *s1, const char *s2, int n); + +char *strcpy(char *dest, const char *src); +char *strcat(char *dest, const char *src); + +char *strncpy(char *dest, const char *src, int n); + +char *strerror(int err); + +#endif /* STRING_H_ */ diff --git a/src/libc/string_s.asm b/src/libc/string_s.asm new file mode 100644 index 0000000..4545d16 --- /dev/null +++ b/src/libc/string_s.asm @@ -0,0 +1,174 @@ +; pcboot - bootable PC demo/game kernel +; Copyright (C) 2018 John Tsiombikas +; +; 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 3 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, see . + + ; standard C memset + global memset +memset: + push ebp + mov ebp, esp + push edi + + mov edi, [ebp + 8] + push edi + mov al, [ebp + 12] + mov ah, al + mov cx, ax + shl eax, 16 + mov ax, cx + mov ecx, [ebp + 16] + + test ecx, ecx + jz .done + + ; write 1, 2, or 3 times until we reache a 32bit-aligned dest address + mov edx, edi + and edx, 3 + jz .main + jmp [.pre_tab + ecx * 4] + +.pre_tab dd .main, .pre1, .pre2, .pre3 +.pre3: stosb + dec ecx +.pre2: stosb + dec ecx +.pre1: stosb + dec ecx + jz .done + + ; edi is 32bit-aligned here, write ecx>>2 32bit values +.main: + push ecx + shr ecx, 2 + rep stosd + pop ecx + + ; write any trailing bytes + and ecx, 3 + jmp [.post_tab + ecx * 4] + +.post_tab dd .done, .post1, .post2, .post3 +.post3: stosb +.post2: stosb +.post1: stosb + +.done: pop eax + pop edi + pop ebp + ret + + + ; same as memset but copies 16bit values, and the count is the number + ; of 16bit values to copy, not bytes. + global memset16 +memset16: + push ebp + mov ebp, esp + push edi + + mov edi, [ebp + 8] + push edi + mov ax, [ebp + 12] + shl eax, 16 + mov ax, [ebp + 12] + mov ecx, [ebp + 16] + + test ecx, ecx + jz .done + + mov edx, edi + and edx, 3 + jz .main + jmp [.pre_tab + edx * 4] + +.pre_tab dd .main, .pre1, .pre2, .pre3 +.pre3: + ; unaligned by +3: + ; same as next one, but jump to ms16main instead of falling through + mov [edi], al + mov [edi + ecx * 2 - 1], ah + rol eax, 8 + inc edi + dec ecx + jz .done + jmp .main +.pre1: + ; unaligned by +1: + ; - write low byte at edi + ; - write high byte at the end + ; - rotate by 8 for the rest + ; - decrement ecx + mov [edi], al + mov [edi + ecx * 2 - 1], ah + rol eax, 8 + inc edi + dec ecx + jz .done +.pre2: + ; unaligned by +2 + stosw + dec ecx + jz .done + +.main: + push ecx + shr ecx, 1 + rep stosd + pop ecx + + and ecx, 1 + jz .done + stosw +.done: + pop eax + pop edi + pop ebp + ret + + + ; standard C memcpy + global memcpy +memcpy: + push ebp + mov ebp, esp + push edi + push esi + + mov edi, [ebp + 8] + mov esi, [ebp + 12] + mov ecx, [ebp + 16] + + test ecx, ecx + jz .done + + mov edx, ecx + shr ecx, 2 + rep movsd + + and edx, 3 + jmp [.post_tab + edx * 4] + +.post_tab dd .done, .post1, .post2, .post3 +.post3: movsb +.post2: movsb +.post1: movsb + +.done: + pop esi + pop edi + pop ebp + ret + +; vi:set ts=8 sts=8 sw=8 ft=nasm: diff --git a/src/loader.asm b/src/loader.asm new file mode 100644 index 0000000..41d9452 --- /dev/null +++ b/src/loader.asm @@ -0,0 +1,570 @@ + section .loader + + extern startup + extern _ldr_main_start + extern _main_start + extern _main_size + extern boot_mem_map + extern boot_mem_map_size + +%include 'macros.inc' + + [bits 16] + global _start +_start: + cli + mov ax, cs + mov ds, ax + mov es, ax + mov fs, ax ; this will store the original real mode segment + mov ss, ax + ; modify the return to real mode jump segment + mov [.jmpcs16 + 3], ax + + xor ax, ax + mov sp, ax + + ; check for VM86 and abort + mov eax, cr0 + test ax, 1 + jz .notvm86 + + mov si, str_errvm86 + call printstr + jmp exit + +.notvm86: +%ifdef BUG_WARNING + call warning ; issue a warning and allow the user to abort +%endif + +%ifdef CON_SERIAL + call setup_serial +%endif + call enable_a20 + call detect_memory + + ; calculate GDT linear address + xor eax, eax + mov ax, cs + shl eax, 4 + add eax, gdt + mov [gdt_base], eax + + ; set tmp segment bases to match the linear address of our current seg + xor eax, eax + mov ax, cs + shl eax, 4 + mov word [gdt + 18h + 2], ax ; tmp pm code base + mov word [gdt + 20h + 2], ax ; tmp pm data base + mov word [gdt + 28h + 2], ax ; ret-to-realmode code + shr eax, 16 + mov byte [gdt + 18h + 4], al + mov byte [gdt + 20h + 4], al + mov byte [gdt + 28h + 4], al + + mov si, str_enterpm + call printstr + + ; change video mode now, to avoid having to bring in all the int86 code + mov ax, 13h + int 10h + + lgdt [gdt_lim] + + mov eax, cr0 + or eax, 1 + mov cr0, eax + + jmp 18h:.pm + + [bits 32] +.pm: mov ax, 20h ; tmp data selector + mov ds, ax + mov ax, 10h ; dest data selector + mov es, ax + + ; copy main program high + cld + mov esi, _ldr_main_start + mov edi, _main_start + mov ecx, _main_size + 3 + shr ecx, 2 + rep movsd + + ; copy memory map + mov eax, [mem_map_size] + mov [es:boot_mem_map_size], eax + mov esi, mem_map + mov edi, boot_mem_map + mov ecx, 32 ; 128 bytes + rep movsd + + mov ax, 10h + mov ds, ax + mov ss, ax + mov esp, _main_start + + call 8:startup + cli + + ; return to real mode + jmp 28h:.rm + + [bits 16] +.rm: mov eax, cr0 + and ax, 0xfffe + mov cr0, eax +.jmpcs16: + jmp 42h:.loadcs16 ; 42 seg is modifed at the start +.loadcs16: + mov ax, fs + mov ds, ax + mov es, ax + mov ss, ax + + ; restore real-mode IVT + lidt [rmidt] + sti + + mov ax, 3 + int 10h +exit: mov ax, 4c00h + int 21h + +str_errvm86 db 'Error: memory manager detected! Stop it and try again (e.g. emm386 off)',10,0 +str_enterpm db 'Entering 32bit protected mode ...',10,0 + + +%ifdef BUG_WARNING + ; warns the user about the experimental and buggy state of this + ; protected mode system, and allows aborting if having to reboot later + ; is not acceptable. +warning: + mov si, str_warnmsg + call printstr + WAITKEY + dec al + jz exit + ret + +str_warnmsg: + db 'WARNING: this program uses an experimental protected mode kernel, which is ',10 + db 'still in a very early stage of development, and will probably not be able to ',10 + db 'return cleanly to DOS on exit. You might be forced to reboot after quitting!',10 + db 'If this is not acceptable, press ESC now to abort.',10,10 + db 'Press ESC to abort and return to DOS, any other key to continue...',10,0 +%endif ; BUG_WARNING + + +; ---------------------- A20 address line enable ----------------------- + +enable_a20: + call test_a20 + jnc .ret + + mov si, .infomsg + call printstr ; print "Enable A20 line ... " + + call enable_a20_kbd + call test_a20 + jnc .done + call enable_a20_fast + call test_a20 + jnc .done + mov si, .failstr + call printstr + mov ax, 4c00h + int 21h +.done: mov si, .okstr + call printstr +.ret: ret + +.infomsg db 'Enable A20 line:',0 +.failstr db ' failed.',10,0 +.okstr db ' success.',10,0 + + ; CF = 1 if A20 test fails (not enabled) +test_a20: + push ds + push es + xor ax, ax + mov ds, ax + not ax + mov es, ax + mov si, 420h + mov di, 430h + mov dl, [ds:si] + mov dh, [es:di] + mov [ds:si], al + not ax + mov [es:di], al + cmp al, [ds:si] + clc + jnz .done + stc +.done: mov [ds:si], dl + mov [es:di], dh + pop es + pop ds + ret + +enable_a20_fast: + mov si, .info + call printstr + in al, 92h + or al, 2 + out 92h, al + ret +.info db ' fast ...',0 + +KBC_DATA_PORT equ 0x60 +KBC_CMD_PORT equ 0x64 +KBC_STATUS_PORT equ 0x64 +KBC_CMD_WR_OUTPORT equ 0xd1 + +KBC_STAT_IN_FULL equ 2 + +enable_a20_kbd: + mov si, .info + call printstr + call kbc_wait_write + mov al, KBC_CMD_WR_OUTPORT + out KBC_CMD_PORT, al + call kbc_wait_write + mov al, 0xdf + out KBC_DATA_PORT, al + ret +.info db ' kbd ...',0 + +kbc_wait_write: + in al, KBC_STATUS_PORT + and al, KBC_STAT_IN_FULL + jnz kbc_wait_write + ret + + +; ---------------------- memory detection ----------------------- + +detect_memory: + mov si, memdet_detram + call printstr + mov si, memdet_e820_msg + call printstr + call detect_mem_e820 + jnc .done + mov si, str_fail + call printstr + + mov si, memdet_detram + call printstr + mov si, memdet_e801_msg + call printstr + call detect_mem_e801 + jnc .done + mov si, str_fail + call printstr + + mov si, memdet_detram + call printstr + mov esi, memdet_88_msg + call printstr + call detect_mem_88 + jnc .done + mov esi, str_fail + call printstr + + mov si, memdet_detram + call printstr + mov esi, memdet_cmos_msg + call printstr + call detect_mem_cmos + jnc .done + mov esi, str_fail + call printstr + + mov si, memdet_fail_msg + call printstr + jmp exit +.done: + mov si, str_ok + call printstr + ret + +str_ok db 'OK',10,0 +str_fail db 'failed',10,0 +memdet_fail_msg db 'Failed to detect available memory!',10,0 +memdet_detram db 'Detecting RAM ' +memdet_e820_msg db '(BIOS 15h/0xe820)... ',0 +memdet_e801_msg db '(BIOS 15h/0xe801)... ',0 +memdet_88_msg db '(BIOS 15h/0x88, max 64mb)... ',0 +memdet_cmos_msg db '(CMOS)...',0 + + ; detect extended memory using BIOS call 15h/e820 +detect_mem_e820: + mov dword [mem_map_size], 0 + + mov edi, .buffer + xor ebx, ebx + mov edx, 534d4150h + +.looptop: + mov eax, 0xe820 + mov ecx, 24 + int 15h + jc .fail + cmp eax, 534d4150h + jnz .fail + + ; skip areas starting above 4GB as we won't be able to use them + cmp dword [di + 4], 0 + jnz .skip + + ; only care for type 1 (usable ram), otherwise ignore + cmp dword [di + 16], 1 + jnz .skip + + mov eax, [.buffer] + mov esi, mem_map + mov ebp, [mem_map_size] + mov [ebp * 8 + esi], eax + + ; skip areas with 0 size (also clamp size to 4gb) + ; test high 32bits + cmp dword [edi + 12], 0 + jz .highzero + ; high part is non-zero, make low part ffffffff + xor eax, eax + not eax + jmp .skiph0 + +.highzero: + ; if both high and low parts are zero, ignore + mov eax, [di + 8] + test eax, eax + jz .skip + +.skiph0:mov [ebp * 8 + esi + 4], eax + inc dword [mem_map_size] + +.skip: + ; terminate the loop if ebx was reset to 0 + test ebx, ebx + jz .done + jmp .looptop +.done: + clc + ret + +.fail: ; if size > 0, then it's not a failure, just the end + cmp dword [mem_map_size], 0 + jnz .done + stc + ret + +.buffer times 32 db 0 + + ; detect extended memory using BIOS call 15h/e801 +detect_mem_e801: + mov si, mem_map + mov ebp, [mem_map_size] + mov dword [ebp], 0 + + xor cx, cx + xor dx, dx + mov ax, 0xe801 + int 15h + jc .fail + + test cx, cx + jnz .foo1 + test ax, ax + jz .fail + mov cx, ax + mov dx, bx + +.foo1: mov dword [si], 100000h + movzx eax, cx + ; first size is in KB, convert to bytes + shl eax, 10 + jnc .foo2 + ; overflow means it's >4GB, clamp to 4GB + mov eax, 0xffffffff +.foo2: mov [si + 4], eax + inc dword [mem_map_size] + test dx, dx + jz .done + mov dword [si + 8], 1000000h + movzx eax, dx + ; second size is in 64kb blocks, convert to bytes + shl eax, 16 + jnc .foo3 + ; overflow means it's >4GB, clamp to 4GB + mov eax, 0xffffffff +.foo3: mov [si + 12], eax + inc dword [mem_map_size] +.done: + clc + ret +.fail: + stc + ret + +detect_mem_88: + ; reportedly some BIOS implementations fail to clear CF on success + clc + mov ah, 88h + int 15h + jc .fail + + test ax, ax + jz .fail + + ; ax has size in KB, convert to bytes in eax + and eax, 0xffff + shl eax, 10 + + mov esi, mem_map + mov dword [si], 100000h + mov [si + 4], eax + + mov dword [mem_map_size], 1 + clc + ret +.fail: stc + ret + +detect_mem_cmos: + mov al, 31h + out 70h, al + in al, 71h + mov ah, al + mov al, 30h + out 70h, al + in al, 71h + + test ax, ax + jz .fail + + ; ax has size in KB, convert to bytes in eax + and eax, 0xffff + shl eax, 10 + + mov esi, mem_map + mov dword [si], 100000h + mov [si + 4], eax + mov dword [mem_map_size], 1 + clc + ret +.fail: stc + ret + + + align 4 +mem_map_size dd 0 +mem_map times 128 db 0 + + +; ----------------------- serial console ------------------------ + +%ifdef CON_SERIAL +setup_serial: + ; set clock divisor + mov dx, UART_BASE + 3 ; LCTL + mov al, 80h ; DLAB + out dx, al + mov dx, UART_BASE ; DIVLO + mov al, UART_DIVISOR & 0xff + out dx, al + inc dx ; DIVHI + mov al, UART_DIVISOR >> 8 + out dx, al + mov dx, UART_BASE + 3 ; LCTL + mov al, 3 ; 8n1 + out dx, al + inc dx ; MCTL + mov al, 0xb ; DTR/RTS/OUT2 + out dx, al + ret + +ser_putchar: + SER_PUTCHAR + ret + +ser_printstr: + lodsb + test al, al + jz .end + cmp al, 10 + jnz .nolf + push ax + mov al, 13 + call ser_putchar + pop ax +.nolf: call ser_putchar + jmp ser_printstr +.end: ret + +%endif ; def CON_SERIAL + + +printstr: + lodsb + test al, al + jz .end + cmp al, 10 ; check for line-feed and insert CR before it + jnz .nolf + push ax +%ifdef CON_SERIAL + mov al, 13 + call ser_putchar +%endif + mov dl, 13 + mov ah, 2 + int 21h + pop ax +.nolf: +%ifdef CON_SERIAL + call ser_putchar +%endif + mov ah, 2 + mov dl, al + int 21h + jmp printstr +.end: ret + + + align 4 +enterpm dd 0xbad00d ; space for linear address for far jump to pmode +enterpm_sel dw 8 ; selector for far jump to protected mode + align 4 +gdt_lim dw 47 ; GDT limit +gdt_base dd 0xbadf00d ; space for GDT linear address + + align 8 +gdt: ; 0: null segment + dd 0 + dd 0 + ; 1: code - 0/lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:code/non-conf/rd (sel: 8) + dd 0000ffffh + dd 00cf9a00h + ; 2: data - 0/lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:data/rw (sel: 10h) + dd 0000ffffh + dd 00cf9200h + ; 3: tmp code (will set base before entering pmode) (sel: 18h) + dd 0000ffffh + dd 00cf9a00h + ; 4: tmp data (will set base before entering pmode) (sel: 20h) + dd 0000ffffh + dd 00cf9200h + ; 5: return to real-mode 16bit code segment (sel: 28h) + dd 0000ffffh + dd 00009a00h + + ; pseudo IDTR descriptor for real-mode IVT at address 0 + align 4 + dw 0 +rmidt: dw 3ffh ; IVT limit (1kb / 256 entries) + dd 0 ; IVT base 0 + +; vi:set ts=8 sts=8 sw=8 ft=nasm: diff --git a/src/macros.inc b/src/macros.inc new file mode 100644 index 0000000..ce5250a --- /dev/null +++ b/src/macros.inc @@ -0,0 +1,33 @@ +%define BUG_WARNING +%define CON_SERIAL + +UART_BASE equ 2f8h ; COM1: 3f8, COM2: 2f8 +UART_DIVISOR equ 115200 / 9600 ; 9600 baud + +%macro WAITKEY 0 +%%waitkey: + in al, 64h + test al, 1 + jz %%waitkey + in al, 60h +%endmacro + +%macro SER_PUTCHAR 0 + push dx + push ax + mov dx, UART_BASE + 5 ; LSTAT +%%wait: in al, dx + test al, 20h ; TREG_EMPTY + jz %%wait + mov dx, UART_BASE + pop ax + out dx, al + pop dx +%endmacro + +%macro PMPRINT 1 + mov al, %1 + SER_PUTCHAR +%endmacro + +; vi:set ts=8 sts=8 sw=8 ft=nasm: diff --git a/src/startup.asm b/src/startup.asm new file mode 100644 index 0000000..c1d85e5 --- /dev/null +++ b/src/startup.asm @@ -0,0 +1,47 @@ + bits 32 + section .text + +%include 'macros.inc' + + extern main + extern _bss_start + extern _bss_end + + global startup +startup: + ; clear .bss + mov eax, _bss_end + sub eax, _bss_start + test eax, eax + jz .nobss + mov ecx, eax + mov edi, _bss_start + xor eax, eax + shr ecx, 2 + rep stosd +.nobss: + + xor ebp, ebp ; terminate backtraces + call main + retf + +; global putchar +;putchar: +; mov eax, [esp + 4] +; cmp al, 10 +; jnz .nonl +; push eax +; mov al, 13 +; SER_PUTCHAR +; pop eax +;.nonl: SER_PUTCHAR +; ret + + section .data + global boot_mem_map_size + global boot_mem_map + align 4 +boot_mem_map_size dd 0 +boot_mem_map times 128 db 0 + +; vi:set ts=8 sts=8 sw=8 ft=nasm: -- 1.7.10.4