--- /dev/null
+*.o
+*.d
+*.swp
+*.com
+*.map
+RUN
+disasm
+dosbox.conf
+*.log
+tofloppy
+*.s
--- /dev/null
+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 $< >$@
--- /dev/null
+OUTPUT_FORMAT(binary)
+ENTRY(_start)
+
+SECTIONS {
+ /* loader starts at <seg>: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);
+ }
+}
--- /dev/null
+#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_ */
--- /dev/null
+#include <string.h>
+#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)
+{
+}
--- /dev/null
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#ifndef ASMOPS_H_
+#define ASMOPS_H_
+
+#include <inttypes.h>
+
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#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);
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#ifndef DESC_H_
+#define DESC_H_
+
+#include <inttypes.h>
+
+typedef struct {
+ uint16_t d[4];
+} desc_t;
+
+#endif /* DESC_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#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
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#ifndef INTR_H_
+#define INTR_H_
+
+#include <inttypes.h>
+#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_ */
--- /dev/null
+; pcboot - bootable PC demo/game kernel
+; Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+;
+; 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 <https://www.gnu.org/licenses/>.
+
+ 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:
--- /dev/null
+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)
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#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;
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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);
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#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<boot_mem_map_size; i++) {
+ printf(" start: %08x - size: %08x\n", (unsigned int)boot_mem_map[i].start,
+ (unsigned int)boot_mem_map[i].size);
+
+ start = boot_mem_map[i].start;
+ sz = boot_mem_map[i].size & 0xfffffffc;
+ end = start + sz;
+ if(end < sz) {
+ end = 0xfffffffc;
+ sz = end - start;
+ }
+
+ /* ignore any memory ranges which end before the end of the kernel image */
+ if(end < MEM_START) continue;
+ if(start < MEM_START) {
+ start = MEM_START;
+ }
+
+ add_memory(start, sz);
+ total += sz;
+
+ pg = ADDR_TO_PAGE(end);
+ if(end_pg < pg) {
+ end_pg = pg;
+ }
+ }
+
+ i = 0;
+ while(total > 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<count; i++) {
+ free_ppage(pg0++);
+ }
+}
+
+int alloc_ppage_range(int start, int size)
+{
+ int i, pg = start;
+ int intr_state;
+
+ intr_state = get_intr_flag();
+ disable_intr();
+
+ /* first validate that no page in the requested range is allocated */
+ for(i=0; i<size; i++) {
+ if(!IS_FREE(pg)) {
+ return -1;
+ }
+ ++pg;
+ }
+
+ /* all is well, mark them as used */
+ pg = start;
+ for(i=0; i<size; i++) {
+ mark_page(pg++, USED);
+ }
+
+ set_intr_flag(intr_state);
+ return 0;
+}
+
+int free_ppage_range(int start, int size)
+{
+ int i, pg = start;
+
+ for(i=0; i<size; i++) {
+ free_ppage(pg++);
+ }
+ return 0;
+}
+
+/* adds a range of physical memory to the available pool. used during init_mem
+ * when traversing the memory map.
+ */
+static void add_memory(uint32_t start, size_t sz)
+{
+ int i, szpg, pg;
+
+ szpg = ADDR_TO_PAGE(sz + 4095);
+ pg = ADDR_TO_PAGE(start);
+
+ for(i=0; i<szpg; i++) {
+ mark_page(pg++, FREE);
+ }
+}
+
+/* maps a page as used or free in the allocation bitmap */
+static void mark_page(int pg, int used)
+{
+ int idx = BM_IDX(pg);
+ int bit = BM_BIT(pg);
+
+ if(used) {
+ bitmap[idx] |= 1 << bit;
+ } else {
+ bitmap[idx] &= ~(1 << bit);
+ }
+}
+
+void print_page_bitmap(void)
+{
+ int i;
+
+ for(i=0; i<bmsize/4; i++) {
+ if((i & 3) == 0) {
+ uint32_t pg = i * 32;
+ uint32_t addr = PAGE_TO_ADDR(pg);
+ printf("\n%5d [%08x]:", (int)pg, (unsigned long)addr);
+ }
+ printf(" %08x", bitmap[i]);
+ }
+ printf("\n");
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#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;
+ }
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#ifndef PANIC_H_
+#define PANIC_H_
+
+void panic(const char *fmt, ...) __attribute__((noreturn));
+void backtrace(void);
+
+#endif /* PANIC_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#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!");
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#ifndef POWER_H_
+#define POWER_H_
+
+void reboot(void);
+
+#endif /* POWER_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#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);
+ }
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+ 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:
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#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; i<NUM_SEGMENTS; i++) {
+ printf("%d: ", i);
+ print_desc(gdt + i);
+ }
+}
+
+static void print_desc(desc_t *desc)
+{
+ uint32_t base, limit;
+ int dpl, g;
+
+ if((desc->d[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
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#ifndef SEGM_H_
+#define SEGM_H_
+
+#include <inttypes.h>
+
+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_ */
--- /dev/null
+; pcboot - bootable PC demo/game kernel
+; Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+;
+; 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 <https://www.gnu.org/licenses/>.
+
+ 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:
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#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<num_open; i++) {
+ if(ports[i].base == uart_base[pidx]) {
+ /* the port is already open, return the same fd and increment ref */
+ ports[i].ref++;
+ return i;
+ }
+ }
+
+ fd = num_open++;
+ memset(ports + fd, 0, sizeof ports[pidx]);
+
+ base = uart_base[pidx];
+ intr = uart_irq[pidx];
+
+ if(mode & SER_8N2) {
+ fmt = COM_FMT_8N2;
+ } else {
+ fmt = COM_FMT_8N1;
+ }
+
+ interrupt(IRQ_TO_INTR(uart_irq[pidx]), recv_intr);
+
+ outp(base + UART_LCTL, LCTL_DLAB);
+ outp(base + UART_DIVLO, div & 0xff);
+ outp(base + UART_DIVHI, (div >> 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<widx; i++) {
+ if(linebuf[i] == '\r' || linebuf[i] == '\n') {
+ size = 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
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#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);
+ }
+ }
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#ifndef TSS_H_
+#define TSS_H_
+
+#include <inttypes.h>
+
+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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#ifndef ALLOCA_H_
+#define ALLOCA_H_
+
+#define alloca(x) __builtin_alloca(x)
+
+#endif /* ALLOCA_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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;
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#ifndef FLOAT_H_
+#define FLOAT_H_
+
+#define FLT_MIN __FLT_MIN__
+#define FLT_MAX __FLT_MAX__
+
+#endif /* FLOAT_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#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 */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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));
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+/* random number generator, based on this description of the algorithm
+ * used by the GNU libc: https://www.mathstat.dal.ca/~selinger/random
+ */
+#include <stdlib.h>
+#include <inttypes.h>
+
+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();
+ }
+}
--- /dev/null
+/*
+256boss - bootable launcher for 256byte intros
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <setjmp.h>
+
+int setjmp(jmp_buf buf)
+{
+ return __builtin_setjmp(buf);
+}
+
+void longjmp(jmp_buf buf, int val)
+{
+ __builtin_longjmp(buf, 1);
+}
--- /dev/null
+/*
+256boss - bootable launcher for 256byte intros
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#ifndef STDDEF_H_
+#define STDDEF_H_
+
+#include <inttypes.h>
+
+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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#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<fwidth; i++) {
+ bwrite(out, BUF(buf), SZ(sz), (char*)&padc, 1);
+ cnum++;
+ }
+ 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;
+ }
+ 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<fwidth; i++) {
+ bwrite(out, BUF(buf), SZ(sz), (char*)&padc, 1);
+ cnum++;
+ }
+ if(!left_align) {
+ bwrite(out, BUF(buf), SZ(sz), str, slen);
+ cnum += slen;
+ }
+ break;
+
+ case 'n':
+ *va_arg(ap, int*) = cnum;
+ break;
+
+ default:
+ break;
+ }
+
+ /* restore default conversion state */
+ base = 10;
+ alt = 0;
+ fwidth = 0;
+ padc = ' ';
+ hex_caps = 0;
+
+ fstart = 0;
+ fmt++;
+ } else {
+ switch(*fmt) {
+ case '#':
+ alt = 1;
+ break;
+
+ case '+':
+ sign = 1;
+ break;
+
+ case '-':
+ left_align = 1;
+ break;
+
+ case 'l':
+ case 'L':
+ break;
+
+ case '0':
+ padc = '0';
+ break;
+
+ default:
+ if(isdigit(*fmt)) {
+ const char *fw = fmt;
+ while(*fmt && isdigit(*fmt)) fmt++;
+
+ fwidth = atoi(fw);
+ continue;
+ }
+ }
+ fmt++;
+ }
+ } else {
+ bwrite(out, BUF(buf), SZ(sz), (char*)fmt++, 1);
+ cnum++;
+ }
+ }
+
+ return cnum;
+}
+
+
+#if 0
+static char *sconvc = "diouxcsefg%";
+
+#define IS_SCONV(c) strchr(sconvc, c)
+
+static int intern_scanf(const char *instr, FILE *infile, const char *fmt, va_list ap)
+{
+ return -1; /* TODO */
+}
+#endif
+
+
+/* bwrite is called by intern_printf to transparently handle writing into a
+ * buffer or to the terminal
+ */
+static void bwrite(int out, char *buf, size_t buf_sz, char *str, int sz)
+{
+ int i;
+
+ if(out == OUT_BUF) {
+ if(buf_sz && buf_sz <= sz) sz = buf_sz;
+ buf[sz] = 0;
+ memcpy(buf, str, sz);
+ } else {
+ switch(out) {
+ case OUT_DEF:
+ for(i=0; i<sz; i++) {
+ putchar(*str++);
+ }
+ break;
+
+ case OUT_SER:
+ for(i=0; i<sz; i++) {
+ ser_putchar(*str++);
+ }
+ break;
+
+ default:
+ /* TODO: OUT_SCR */
+ break;
+ }
+ }
+}
+
+/*
+static int readchar(const char *str, FILE *fp)
+{
+ static const char *orig_str;
+ static const char *sptr;
+
+ if(str) {
+ if(str == orig_str) {
+ if(!*sptr) return -1;
+ return *sptr++;
+ } else {
+ orig_str = sptr = str;
+ return readchar(str, fp);
+ }
+ } else {
+ return fgetc(fp);
+ }
+
+ return -1;
+}
+*/
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#ifndef STDIO_H_
+#define STDIO_H_
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <assert.h>
+#include <alloca.h>
+
+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<count; i++) {
+ a = it;
+ it += itemsz;
+ while(a > (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);
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#ifndef STDLIB_H_
+#define STDLIB_H_
+
+#include <stddef.h>
+
+#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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#include <string.h>
+#include <ctype.h>
+#include <stddef.h>
+
+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<n; i++) {
+ *dptr++ = *sptr++;
+ }
+ } else {
+ /* backwards copy */
+ dptr = (char*)dest + n - 1;
+ sptr = (const char*)src + n - 1;
+ for(i=0; i<n; i++) {
+ *dptr-- = *sptr--;
+ }
+ }
+
+ return dest;
+}
+
+int memcmp(void *aptr, void *bptr, size_t n)
+{
+ int i, startoffs, diff;
+ uint32_t *a32, *b32;
+ unsigned char *a = aptr;
+ unsigned char *b = bptr;
+
+ a32 = (uint32_t*)((intptr_t)(a + 3) & 0xfffffffc);
+ b32 = (uint32_t*)((intptr_t)(b + 3) & 0xfffffffc);
+
+ /* if both are aligned the same way... */
+ if((startoffs = (unsigned char*)a32 - a) == (unsigned char*)b32 - b) {
+ /* catch-up to the 32bit alignment */
+ for(i=0; i<startoffs; i++) {
+ if((diff = *a++ - *b++) != 0 || --n <= 0) {
+ return diff;
+ }
+ }
+
+ /* compare 32bit at once */
+ while(n >= 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];
+}
+*/
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+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 <https://www.gnu.org/licenses/>.
+*/
+#ifndef STRING_H_
+#define STRING_H_
+
+#include <stdlib.h>
+
+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_ */
--- /dev/null
+; pcboot - bootable PC demo/game kernel
+; Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+;
+; 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 <https://www.gnu.org/licenses/>.
+
+ ; 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:
--- /dev/null
+ 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:
--- /dev/null
+%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:
--- /dev/null
+ 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: