porting over pcboot codebase
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sun, 1 Oct 2023 23:56:00 +0000 (02:56 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sun, 1 Oct 2023 23:56:00 +0000 (02:56 +0300)
49 files changed:
.gitignore
Makefile
src/asmops.h [new file with mode: 0644]
src/config.h [new file with mode: 0644]
src/contty.c [new file with mode: 0644]
src/contty.h [new file with mode: 0644]
src/desc.h [new file with mode: 0644]
src/intr.c [new file with mode: 0644]
src/intr.h [new file with mode: 0644]
src/intr_s.asm [new file with mode: 0644]
src/intrtab.h [new file with mode: 0644]
src/libc/alloca.h [new file with mode: 0644]
src/libc/assert.h [new file with mode: 0644]
src/libc/ctype.c [new file with mode: 0644]
src/libc/ctype.h [new file with mode: 0644]
src/libc/errno.h [new file with mode: 0644]
src/libc/float.h [new file with mode: 0644]
src/libc/inttypes.h [new file with mode: 0644]
src/libc/limits.h [new file with mode: 0644]
src/libc/malloc.c [new file with mode: 0644]
src/libc/math.c [new file with mode: 0644]
src/libc/math.h [new file with mode: 0644]
src/libc/rand.c [new file with mode: 0644]
src/libc/setjmp.c [new file with mode: 0644]
src/libc/setjmp.h [new file with mode: 0644]
src/libc/stdarg.h [new file with mode: 0644]
src/libc/stddef.h [new file with mode: 0644]
src/libc/stdio.c [new file with mode: 0644]
src/libc/stdio.h [new file with mode: 0644]
src/libc/stdlib.c [new file with mode: 0644]
src/libc/stdlib.h [new file with mode: 0644]
src/libc/string.c [new file with mode: 0644]
src/libc/string.h [new file with mode: 0644]
src/libc/string_s.asm [new file with mode: 0644]
src/loader.asm
src/macros.inc [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/mem.c [new file with mode: 0644]
src/mem.h [new file with mode: 0644]
src/panic.c [new file with mode: 0644]
src/panic.h [new file with mode: 0644]
src/regs.asm [new file with mode: 0644]
src/segm.c [new file with mode: 0644]
src/segm.h [new file with mode: 0644]
src/segm_s.asm [new file with mode: 0644]
src/serial.c [new file with mode: 0644]
src/serial.h [new file with mode: 0644]
src/startup.asm
src/tss.h [new file with mode: 0644]

index dc5c1f6..5300cea 100644 (file)
@@ -6,3 +6,5 @@
 RUN
 disasm
 dosbox.conf
+*.log
+tofloppy
index a3411a3..1872bbf 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -7,6 +7,9 @@ bin = test.com
 warn = -pedantic -Wall
 inc = -Isrc -Isrc/libc
 
+AS = nasm
+
+ASFLAGS = -Isrc/
 CFLAGS = -m32 -march=i386 $(warn) $(opt) $(dbg) -fno-pic -ffreestanding -nostdinc $(inc) $(def)
 LDFLAGS = -m elf_i386 -nostdlib -T com32.ld -Map test.map
 
@@ -16,7 +19,7 @@ $(bin): $(obj)
 -include $(dep)
 
 %.o: %.asm
-       nasm -o $@ -f elf $<
+       $(AS) -o $@ -f elf $(ASFLAGS) $<
 
 %.s: %.c
        $(CC) $(CFLAGS) -S $< -o $@
diff --git a/src/asmops.h b/src/asmops.h
new file mode 100644 (file)
index 0000000..6a25f15
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+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 (%%esp), %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)
+
+
+#endif /* ASMOPS_H_ */
diff --git a/src/config.h b/src/config.h
new file mode 100644 (file)
index 0000000..c83bb90
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+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 PCBOOT_CONFIG_H_
+#define PCBOOT_CONFIG_H_
+
+/* frequency of generated timer ticks in hertz */
+#define TICK_FREQ_HZ           250
+
+#define CON_TEXTMODE
+#define CON_SERIAL
+
+#endif /* PCBOOT_CONFIG_H_ */
diff --git a/src/contty.c b/src/contty.c
new file mode 100644 (file)
index 0000000..ce1115c
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+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 <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_setstart(int y);
+static inline unsigned char crtc_read(int reg);
+static inline void crtc_write(int reg, unsigned char val);
+
+extern 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;
+
+       con_show_cursor(1);
+       crtc_setstart(0);
+       crtc_cursor(cursor_x, cursor_y);
+       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_setstart(int y)
+{
+       unsigned int addr = y * NCOLS;
+
+       crtc_write(CRTC_REG_START_L, addr);
+       crtc_write(CRTC_REG_START_H, addr >> 8);
+}
+
+static inline unsigned char crtc_read(int reg)
+{
+       outp(CRTC_ADDR, reg);
+       return inp(CRTC_DATA);
+}
+
+static inline void crtc_write(int reg, unsigned char val)
+{
+       outp(CRTC_ADDR, reg);
+       outp(CRTC_DATA, val);
+}
diff --git a/src/contty.h b/src/contty.h
new file mode 100644 (file)
index 0000000..fcc6e37
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+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_ */
diff --git a/src/desc.h b/src/desc.h
new file mode 100644 (file)
index 0000000..d581f94
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+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_ */
diff --git a/src/intr.c b/src/intr.c
new file mode 100644 (file)
index 0000000..5a351c8
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+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)
+
+
+void init_pic(void);
+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]
+        */
+       init_pic();
+       eoi_pending = 0;
+}
+
+/* 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 %d, error code: %d\n", frm.inum, frm.err);
+               }
+               printf("unhandled interrupt %d\n", frm.inum);
+       }
+
+       disable_intr();
+       if(eoi_pending) {
+               end_of_irq(INTR_TO_IRQ(eoi_pending));
+       }
+}
+
+void init_pic(void)
+{
+       prog_pic(IRQ_OFFSET);
+}
+
+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_KCODE, 0), (uint32_t)handler, dpl, type);
+}
+
+void set_pic_mask(int pic, unsigned char mask)
+{
+       outp(pic > 0 ? PIC2_DATA : PIC1_DATA, mask);
+}
+
+unsigned char get_pic_mask(int pic)
+{
+       return inp(pic > 0 ? PIC2_DATA : PIC1_DATA);
+}
+
+void mask_irq(int irq)
+{
+       int port;
+       unsigned char mask;
+
+       if(irq < 8) {
+               port = PIC1_DATA;
+       } else {
+               port = PIC2_DATA;
+               irq -= 8;
+       }
+
+       mask = inp(port) | (1 << irq);
+       outp(port, mask);
+}
+
+void unmask_irq(int irq)
+{
+       int port;
+       unsigned char mask;
+
+       if(irq < 8) {
+               port = PIC1_DATA;
+       } else {
+               port = PIC2_DATA;
+               irq -= 8;
+       }
+
+       mask = inp(port) & ~(1 << irq);
+       outp(port, mask);
+}
+
+
+void end_of_irq(int irq)
+{
+       int intr_state = get_intr_flag();
+       disable_intr();
+
+       if(!eoi_pending) {
+               set_intr_flag(intr_state);
+               return;
+       }
+       eoi_pending = 0;
+
+       if(irq > 7) {
+               outp(PIC2_CMD, OCW2_EOI);
+       }
+       outp(PIC1_CMD, OCW2_EOI);
+
+       set_intr_flag(intr_state);
+}
+
+#ifdef ENABLE_GDB_STUB
+void exceptionHandler(int id, void (*func)())
+{
+       set_intr_entry(id, func);
+}
+#endif
diff --git a/src/intr.h b/src/intr.h
new file mode 100644 (file)
index 0000000..f33ec23
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+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);
+
+struct intr_frame *get_intr_frame(void);
+
+/* install high level interrupt callback */
+void interrupt(int intr_num, intr_func_t func);
+
+/* install low-level interrupt vector in IDT
+ * must be able to handle EOI and return with iret
+ */
+void set_intr_entry(int num, void (*handler)(void));
+
+void prog_pic(int offs);
+void set_pic_mask(int pic, unsigned char mask);
+unsigned char get_pic_mask(int pic);
+void mask_irq(int irq);
+void unmask_irq(int irq);
+
+/* defined in intr_asm.S */
+int get_intr_flag(void);
+void set_intr_flag(int onoff);
+
+void intr_ret(struct intr_frame ifrm);
+
+void end_of_irq(int irq);
+
+#endif /* INTR_H_ */
diff --git a/src/intr_s.asm b/src/intr_s.asm
new file mode 100644 (file)
index 0000000..cdae52a
--- /dev/null
@@ -0,0 +1,161 @@
+; 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:
diff --git a/src/intrtab.h b/src/intrtab.h
new file mode 100644 (file)
index 0000000..34af6fa
--- /dev/null
@@ -0,0 +1,38 @@
+INTR_ENTRY_NOEC(0, div)
+INTR_ENTRY_NOEC(1, debug)
+INTR_ENTRY_NOEC(2, nmi)
+INTR_ENTRY_NOEC(3, bp)
+INTR_ENTRY_NOEC(4, overflow)
+INTR_ENTRY_NOEC(5, bound)
+INTR_ENTRY_NOEC(6, ill)
+INTR_ENTRY_NOEC(7, nodev)
+INTR_ENTRY_EC(8, dfault)
+INTR_ENTRY_NOEC(9, copseg)
+INTR_ENTRY_EC(10, tss)
+INTR_ENTRY_EC(11, segpres)
+INTR_ENTRY_EC(12, stack)
+INTR_ENTRY_EC(13, prot)
+INTR_ENTRY_EC(14, page)
+INTR_ENTRY_NOEC(15, reserved)
+INTR_ENTRY_NOEC(16, fpu)
+INTR_ENTRY_EC(17, align)
+INTR_ENTRY_NOEC(18, mce)
+INTR_ENTRY_NOEC(19, sse)
+INTR_ENTRY_NOEC(32, irq0)
+INTR_ENTRY_NOEC(33, irq1)
+INTR_ENTRY_NOEC(34, irq2)
+INTR_ENTRY_NOEC(35, irq3)
+INTR_ENTRY_NOEC(36, irq4)
+INTR_ENTRY_NOEC(37, irq5)
+INTR_ENTRY_NOEC(38, irq6)
+INTR_ENTRY_NOEC(39, irq7)
+INTR_ENTRY_NOEC(40, irq8)
+INTR_ENTRY_NOEC(41, irq9)
+INTR_ENTRY_NOEC(42, irq10)
+INTR_ENTRY_NOEC(43, irq11)
+INTR_ENTRY_NOEC(44, irq12)
+INTR_ENTRY_NOEC(45, irq13)
+INTR_ENTRY_NOEC(46, irq14)
+INTR_ENTRY_NOEC(47, irq15)
+INTR_ENTRY_NOEC(128, syscall)
+INTR_ENTRY_NOEC(255, default)
diff --git a/src/libc/alloca.h b/src/libc/alloca.h
new file mode 100644 (file)
index 0000000..ae27a71
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+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_ */
diff --git a/src/libc/assert.h b/src/libc/assert.h
new file mode 100644 (file)
index 0000000..7180c97
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+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_ */
diff --git a/src/libc/ctype.c b/src/libc/ctype.c
new file mode 100644 (file)
index 0000000..89e7b93
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+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;
+}
diff --git a/src/libc/ctype.h b/src/libc/ctype.h
new file mode 100644 (file)
index 0000000..79595c8
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+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_ */
diff --git a/src/libc/errno.h b/src/libc/errno.h
new file mode 100644 (file)
index 0000000..8c152de
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+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_ */
diff --git a/src/libc/float.h b/src/libc/float.h
new file mode 100644 (file)
index 0000000..073bf6d
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+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_ */
diff --git a/src/libc/inttypes.h b/src/libc/inttypes.h
new file mode 100644 (file)
index 0000000..39d948a
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+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_ */
diff --git a/src/libc/limits.h b/src/libc/limits.h
new file mode 100644 (file)
index 0000000..b3abdad
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+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_ */
diff --git a/src/libc/malloc.c b/src/libc/malloc.c
new file mode 100644 (file)
index 0000000..c064915
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+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 */
diff --git a/src/libc/math.c b/src/libc/math.c
new file mode 100644 (file)
index 0000000..3f3e5de
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+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));
+}
diff --git a/src/libc/math.h b/src/libc/math.h
new file mode 100644 (file)
index 0000000..e3b0f23
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+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_ */
diff --git a/src/libc/rand.c b/src/libc/rand.c
new file mode 100644 (file)
index 0000000..de159c2
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+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();
+       }
+}
diff --git a/src/libc/setjmp.c b/src/libc/setjmp.c
new file mode 100644 (file)
index 0000000..517563b
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+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);
+}
diff --git a/src/libc/setjmp.h b/src/libc/setjmp.h
new file mode 100644 (file)
index 0000000..c92e93d
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+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_ */
diff --git a/src/libc/stdarg.h b/src/libc/stdarg.h
new file mode 100644 (file)
index 0000000..2d0bb92
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+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_ */
diff --git a/src/libc/stddef.h b/src/libc/stddef.h
new file mode 100644 (file)
index 0000000..9ec6bda
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+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_ */
diff --git a/src/libc/stdio.c b/src/libc/stdio.c
new file mode 100644 (file)
index 0000000..1f12fd6
--- /dev/null
@@ -0,0 +1,407 @@
+/*
+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;
+}
+*/
diff --git a/src/libc/stdio.h b/src/libc/stdio.h
new file mode 100644 (file)
index 0000000..19abc10
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+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_ */
diff --git a/src/libc/stdlib.c b/src/libc/stdlib.c
new file mode 100644 (file)
index 0000000..6b130ed
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+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);
+}
diff --git a/src/libc/stdlib.h b/src/libc/stdlib.h
new file mode 100644 (file)
index 0000000..981cf69
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+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_ */
diff --git a/src/libc/string.c b/src/libc/string.c
new file mode 100644 (file)
index 0000000..5afe839
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+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];
+}
diff --git a/src/libc/string.h b/src/libc/string.h
new file mode 100644 (file)
index 0000000..85490b2
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+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_ */
diff --git a/src/libc/string_s.asm b/src/libc/string_s.asm
new file mode 100644 (file)
index 0000000..4545d16
--- /dev/null
@@ -0,0 +1,174 @@
+; 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:
index b0c13d6..68504f8 100644 (file)
@@ -1,11 +1,12 @@
        section .loader
 
        extern startup
-
        extern _ldr_main_start
        extern _main_start
        extern _main_size
 
+%include 'macros.inc'
+
        [bits 16]
        global _start
 _start:
@@ -31,6 +32,7 @@ _start:
        jmp exit
 
 .notvm86:
+       call setup_serial
        call enable_a20
 
        ; calculate GDT linear address
@@ -84,6 +86,14 @@ _start:
 
        call 8:startup
 
+       PMPRINT 'X'
+       PMPRINT 'Y'
+       PMPRINT 'Z'
+       PMPRINT 'Z'
+       PMPRINT 'Y'
+       PMPRINT 13
+       PMPRINT 10
+
        ; return to real mode
        jmp 28h:.rm
 
@@ -102,6 +112,9 @@ _start:
 exit:  mov ax, 4c00h
        int 21h
 
+dbg_inpm db 'DBG: in pmode',10,0
+dbg_retrm db 'DBG: returning to real mode...',10,0
+
 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
 
@@ -189,6 +202,45 @@ kbc_wait_write:
        jnz kbc_wait_write
        ret
 
+UART_BASE      equ 2f8h                ; COM1: 3f8, COM2: 2f8
+UART_DIVISOR   equ 115200 / 9600       ; 9600 baud
+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
+
 printstr:
        lodsb
        test al, al
@@ -196,17 +248,39 @@ printstr:
        cmp al, 10      ; check for line-feed and insert CR before it
        jnz .nolf
        push ax
+       mov al, 13
+       call ser_putchar
        mov dl, 13
        mov ah, 2
        int 21h
        pop ax
-.nolf: mov ah, 2
+.nolf: call ser_putchar
+       mov ah, 2
        mov dl, al
        int 21h
        jmp printstr
 .end:  ret
 
+       [bits 32]
+ser_putchar_pmode:
+       SER_PUTCHAR
+       ret
+
+ser_putstr_pmode:
+       lodsb
+       test al, al
+       jz .end
+       cmp al, 10
+       jnz .nolf
+       push ax
+       mov al, 13
+       call ser_putchar_pmode
+       pop ax
+.nolf: call ser_putchar_pmode
+       jmp ser_putstr_pmode
+.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
diff --git a/src/macros.inc b/src/macros.inc
new file mode 100644 (file)
index 0000000..03613dc
--- /dev/null
@@ -0,0 +1,30 @@
+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            ; TRIG_EMPTY
+       jz %%wait
+       mov dx, UART_BASE
+       pop ax
+       out dx, al
+       pop dx
+%endmacro
+
+%macro PMPRINT 1
+       mov al, %1
+       SER_PUTCHAR
+%endmacro
+
+; vi:set ts=8 sts=8 sw=8 ft=nasm:
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..93cbfd5
--- /dev/null
@@ -0,0 +1,14 @@
+void putchar(int c);   /* in startup.asm */
+
+static void printstr(const char *s)
+{
+       while(*s) {
+               putchar(*s++);
+       }
+}
+
+int main(void)
+{
+       printstr("hello from C\n");
+       return 0;
+}
diff --git a/src/mem.c b/src/mem.c
new file mode 100644 (file)
index 0000000..a013607
--- /dev/null
+++ b/src/mem.c
@@ -0,0 +1,314 @@
+/*
+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");
+}
diff --git a/src/mem.h b/src/mem.h
new file mode 100644 (file)
index 0000000..40de366
--- /dev/null
+++ b/src/mem.h
@@ -0,0 +1,48 @@
+/*
+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_ */
diff --git a/src/panic.c b/src/panic.c
new file mode 100644 (file)
index 0000000..3daff29
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+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 <stdarg.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(&regs, 0, sizeof regs);
+       get_regs(&regs);
+
+       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();
+}
diff --git a/src/panic.h b/src/panic.h
new file mode 100644 (file)
index 0000000..012eecb
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+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));
+
+#endif /* PANIC_H_ */
diff --git a/src/regs.asm b/src/regs.asm
new file mode 100644 (file)
index 0000000..5fb93d1
--- /dev/null
@@ -0,0 +1,57 @@
+       section .text
+       align 4
+
+       global get_regs
+get_regs:
+       push ebp
+       mov ebp, esp
+
+       push edx
+       mov edx, [ebp + 8]
+
+       mov [edx], eax
+       mov [edx + 4], ebx
+       mov [edx + 8], ecx
+       
+       ; juggle edx
+       mov eax, edx
+       pop edx
+       mov [eax + 12], edx
+       push edx
+       mov edx, eax
+       
+       ; those two are pointless in a function
+       mov [edx + 16], esp
+       mov [edx + 20], ebp
+
+       mov [edx + 24], esi
+       mov [edx + 28], edx
+
+       pushf
+       pop eax
+       mov [edx + 32], eax
+
+       mov [edx + 36], cs
+       mov [edx + 40], ss
+       mov [edx + 44], ds
+       mov [edx + 48], es
+       mov [edx + 52], fs
+       mov [edx + 56], gs
+
+       push ebx
+       mov ebx, cr0
+       mov [edx + 60], ebx
+       ;mov ebx, cr1
+       ;mov [edx + 64], ebx
+       mov ebx, cr2
+       mov [edx + 68], ebx
+       mov ebx, cr3
+       mov [edx + 72], ebx
+       pop ebx
+
+       pop edx
+       pop ebp
+       ret
+
+
+; vi:set ts=8 sts=8 sw=8 ft=nasm:
diff --git a/src/segm.c b/src/segm.c
new file mode 100644 (file)
index 0000000..c6baffd
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+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 <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);
+
+
+/* our global descriptor table */
+static desc_t gdt[NUM_SEGMENTS] __attribute__((aligned(8)));
+
+
+void init_segm(void)
+{
+       memset(gdt, 0, sizeof gdt);
+       segm_desc(gdt + SEGM_KCODE, 0, 0xffffffff, 0, TYPE_CODE);
+       segm_desc(gdt + SEGM_KDATA, 0, 0xffffffff, 0, TYPE_DATA);
+       segm_desc(gdt + SEGM_UCODE, 0, 0xffffffff, 3, TYPE_CODE);
+       segm_desc(gdt + SEGM_UDATA, 0, 0xffffffff, 3, TYPE_DATA);
+       segm_desc16(gdt + SEGM_CODE16, 0, 0xffff, 0, TYPE_CODE);
+
+       set_gdt((uint32_t)gdt, sizeof gdt - 1);
+
+       setup_selectors(selector(SEGM_KCODE, 0), selector(SEGM_KDATA, 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;
+}
+
+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<6; i++) {
+               print_desc(gdt + i);
+       }
+}
+
+static void print_desc(desc_t *desc)
+{
+       uint32_t base, limit;
+       int dpl, g, db, l, avl, p, s, type;
+       char *type_str;
+
+       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;
+       type = (desc->d[2] >> 8) & 0xf;
+       g = (desc->d[3] >> 23) & 1;
+       db = (desc->d[3] >> 22) & 1;
+       l = (desc->d[3] >> 21) & 1;
+       avl = (desc->d[3] >> 20) & 1;
+
+       p = (desc->d[2] >> 15) & 1;
+       s = (desc->d[2] >> 12) & 1;
+}
+*/
diff --git a/src/segm.h b/src/segm.h
new file mode 100644 (file)
index 0000000..b57375b
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+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_
+
+enum {
+       SEGM_KCODE = 1,
+       SEGM_KDATA = 2,
+       SEGM_UCODE,
+       SEGM_UDATA,
+       SEGM_TASK,
+       SEGM_CODE16,
+
+       NUM_SEGMENTS
+};
+
+void init_segm(void);
+
+uint16_t selector(int idx, int rpl);
+
+void set_tss(uint32_t addr);
+
+
+#endif /* SEGM_H_ */
diff --git a/src/segm_s.asm b/src/segm_s.asm
new file mode 100644 (file)
index 0000000..6448c04
--- /dev/null
@@ -0,0 +1,64 @@
+; 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:
diff --git a/src/serial.c b/src/serial.c
new file mode 100644 (file)
index 0000000..f9ee14c
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+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 "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
+
+struct serial_port {
+       int base, intr;
+       int blocking;
+
+       char inbuf[256];
+       int inbuf_ridx, inbuf_widx;
+};
+
+#define BNEXT(x)       (((x) + 1) & 0xff)
+#define BEMPTY(b)      (b##_ridx == b##_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 base, intr;
+       unsigned int fmt;
+
+       if(pidx < 0 || pidx > 1) {
+               printf("ser_open: invalid serial port: %d\n", pidx);
+               return -1;
+       }
+
+       if(ports[pidx].base) {
+               printf("ser_open: port %d already open!\n", pidx);
+               return -1;
+       }
+       memset(ports + pidx, 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[pidx].base = base;
+       ports[pidx].intr = intr;
+       ports[pidx].blocking = 1;
+       ++num_open;
+       return pidx;
+}
+
+void ser_close(int fd)
+{
+       if(--num_open == 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 char linebuf[512];
+       static int widx;
+       int i, rd, size, offs;
+
+       size = sizeof linebuf - widx;
+       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
diff --git a/src/serial.h b/src/serial.h
new file mode 100644 (file)
index 0000000..fb2c17e
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+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_ */
index 56e25b4..16e08fa 100644 (file)
@@ -1,10 +1,20 @@
        bits 32
        section .text
 
+%include 'macros.inc'
+
+       extern main
+
        global startup
 startup:
-       mov ebx, 0xb8000
+       mov ebx, 0xb8000 + 156
        mov byte [ebx], '@'
+       mov byte [ebx + 2], '@'
+       mov byte [ebx + 160], '@'
+       mov byte [ebx + 162], '@'
+
+       call main
+
 .waitkey:
        in al, 64h
        test al, 1
@@ -12,4 +22,16 @@ startup:
        in al, 60h
        ret
 
+       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
+
 ; vi:set ts=8 sts=8 sw=8 ft=nasm:
diff --git a/src/tss.h b/src/tss.h
new file mode 100644 (file)
index 0000000..e9c6943
--- /dev/null
+++ b/src/tss.h
@@ -0,0 +1,38 @@
+/*
+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_ */