-csrc = $(wildcard src/*.c) $(wildcard src/libc/*.c)
-ssrc = $(wildcard src/*.asm) $(wildcard src/libc/*.asm)
+csrc = $(wildcard src/*.c) $(wildcard src/kern/*.c) $(wildcard src/libc/*.c)
+ssrc = $(wildcard src/*.asm) $(wildcard src/kern/*.asm) $(wildcard src/libc/*.asm)
obj = $(csrc:.c=.o) $(ssrc:.asm=.o)
dep = $(csrc:.c=.d)
bin = test.com
warn = -pedantic -Wall
-inc = -Isrc -Isrc/libc
+opt = -O2
+inc = -Isrc -Isrc/kern -Isrc/libc
AS = nasm
-ASFLAGS = -Isrc/
+ASFLAGS = -Isrc/ -Isrc/kern/
CFLAGS = -m32 -march=i386 $(warn) $(opt) $(dbg) -fno-pic -ffreestanding \
-fno-stack-protector -mpreferred-stack-boundary=2 -nostdinc -ffast-math \
-fno-asynchronous-unwind-tables $(inc) $(def) -MMD
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef ASMOPS_H_
-#define ASMOPS_H_
-
-#include <inttypes.h>
-
-#define enable_intr() asm volatile("sti")
-#define disable_intr() asm volatile("cli")
-#define halt_cpu() asm volatile("hlt")
-
-#define CALLER_EIP(eip) \
- asm volatile( \
- "mov 4(%%ebp), %0\n\t" \
- : "=a" ((uint32_t)eip))
-
-static inline uint8_t inp(uint16_t port)
-{
- uint8_t res;
- asm volatile (
- "inb %1, %0\n\t"
- : "=a" (res)
- : "dN" (port));
- return res;
-}
-
-static inline uint16_t inpw(uint16_t port)
-{
- uint16_t res;
- asm volatile (
- "inw %1, %0\n\t"
- : "=a" (res)
- : "dN" (port));
- return res;
-}
-
-static inline uint32_t inpd(uint16_t port)
-{
- uint32_t res;
- asm volatile (
- "inl %1, %0\n\t"
- : "=a" (res)
- : "dN" (port));
- return res;
-}
-
-#define outp(port, src) \
- asm volatile( \
- "outb %0, %1\n\t" \
- :: "a" ((uint8_t)(src)), "dN" ((uint16_t)(port)))
-
-#define outpw(port, src) \
- asm volatile( \
- "outw %0, %1\n\t" \
- :: "a" ((uint16_t)(src)), "dN" ((uint16_t)(port)))
-
-#define outpd(port, src) \
- asm volatile( \
- "outl %0, %1\n\t" \
- :: "a" ((uint32_t)(src)), "dN" ((uint16_t)(port)))
-
-/* delay for about 1us */
-#define iodelay() outp(0x80, 0)
-
-#define get_ebp(ebp) \
- asm volatile( \
- "mov %%ebp, %0\n\t" \
- : "=g"((uint32_t)(ebp)))
-
-#endif /* ASMOPS_H_ */
-/*
-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_
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include "contty.h"
-#include "serial.h"
-#include "asmops.h"
-#include "config.h"
-
-#define VIRT_ROWS 200
-
-#define NCOLS 80
-#define NROWS 25
-#define TEXT_ADDR ((char*)0xb8000)
-
-#define CRTC_ADDR 0x3d4
-#define CRTC_DATA 0x3d5
-
-#define CRTC_REG_CURSTART 0x0a
-#define CRTC_REG_CUREND 0x0b
-#define CRTC_REG_START_H 0x0c
-#define CRTC_REG_START_L 0x0d
-#define CRTC_REG_CURLOC_H 0x0e
-#define CRTC_REG_CURLOC_L 0x0f
-
-#define VMEM_CHAR(c, attr) \
- (((uint16_t)(c) & 0xff) | ((uint16_t)(attr) << 8))
-
-static void scroll(void);
-static void crtc_cursor(int x, int y);
-static void crtc_getcursor(int *x, int *y);
-static void crtc_setstart(int y);
-static int crtc_getstart(void);
-static inline unsigned char crtc_read(int reg);
-static inline void crtc_write(int reg, unsigned char val);
-
-static int cursor_x, cursor_y;
-static unsigned char txattr = 0x07;
-static int start_line;
-static unsigned char cy0, cy1;
-static int curvis;
-static int scr_on = 1;
-
-
-int con_init(void)
-{
-#ifdef CON_SERIAL
- ser_open(1, 9600, SER_8N1);
-#endif
-
-#ifdef CON_TEXTMODE
- cy0 = crtc_read(CRTC_REG_CURSTART);
- curvis = cy0 & 0x20 ? 1 : 0;
- cy0 &= 0x1f;
- cy1 = crtc_read(CRTC_REG_CUREND) & 0x1f;
-
- start_line = crtc_getstart();
- crtc_getcursor(&cursor_x, &cursor_y);
- con_show_cursor(1);
- scr_on = 1;
-#endif
-
- return 0;
-}
-
-void con_scr_enable(void)
-{
- scr_on = 1;
-}
-
-void con_scr_disable(void)
-{
- scr_on = 0;
-}
-
-void con_show_cursor(int show)
-{
-#ifdef CON_TEXTMODE
- unsigned char val = cy0 & 0x1f;
- if(!show) {
- val |= 0x20;
- }
- crtc_write(CRTC_REG_CURSTART, val);
- curvis = show;
-#endif
-}
-
-void con_cursor(int x, int y)
-{
-#ifdef CON_TEXTMODE
- cursor_x = x;
- cursor_y = y;
- crtc_cursor(x, y);
-#endif
-}
-
-void con_curattr(int shape, int blink)
-{
-#ifdef CON_TEXTMODE
- unsigned char start;
- cy0 = (shape == CON_CURSOR_LINE) ? 0xd : 0;
- cy1 = 0xe;
-
- start = cy0;
- if(curvis) {
- start |= 0x20;
- }
-
- crtc_write(CRTC_REG_CURSTART, start);
- crtc_write(CRTC_REG_CUREND, cy0);
-#endif
-}
-
-void con_fgcolor(int c)
-{
- txattr = (txattr & 0xf0) | c;
-}
-
-void con_bgcolor(int c)
-{
- txattr = (txattr & 0x0f) | (c << 4);
-}
-
-void con_setattr(unsigned char attr)
-{
- txattr = attr;
-}
-
-unsigned char con_getattr(void)
-{
- return txattr;
-}
-
-void con_clear(void)
-{
-#ifdef CON_TEXTMODE
- memset16(TEXT_ADDR, VMEM_CHAR(' ', txattr), NCOLS * NROWS);
-
- start_line = 0;
- crtc_setstart(0);
-
- cursor_x = cursor_y = 0;
- crtc_cursor(0, 0);
-#endif
-}
-
-static inline void linefeed(void)
-{
- if(++cursor_y >= NROWS) {
- scroll();
- --cursor_y;
- }
-}
-
-void con_putchar(int c)
-{
-#ifdef CON_TEXTMODE
- if(scr_on) {
- switch(c) {
- case '\n':
- linefeed();
- case '\r':
- cursor_x = 0;
- crtc_cursor(cursor_x, cursor_y);
- break;
-
- case '\t':
- cursor_x = (cursor_x & 0x7) + 8;
- if(cursor_x >= NCOLS) {
- linefeed();
- cursor_x = 0;
- }
- crtc_cursor(cursor_x, cursor_y);
- break;
-
- case '\b':
- if(cursor_x > 0) cursor_x--;
- con_putchar_scr(cursor_x, cursor_y, ' ');
- crtc_cursor(cursor_x, cursor_y);
- break;
-
- default:
- con_putchar_scr(cursor_x, cursor_y, c);
-
- if(++cursor_x >= NCOLS) {
- linefeed();
- cursor_x = 0;
- }
- crtc_cursor(cursor_x, cursor_y);
- }
- }
-#endif
-
-#ifdef CON_SERIAL
- ser_putchar(c);
-#endif
-}
-
-void con_putchar_scr(int x, int y, int c)
-{
-#ifdef CON_TEXTMODE
- uint16_t *ptr = (uint16_t*)TEXT_ADDR;
- ptr[(y + start_line) * NCOLS + x] = VMEM_CHAR(c, txattr);
-#endif
-}
-
-int con_printf(int x, int y, const char *fmt, ...)
-{
-#ifdef CON_TEXTMODE
- va_list ap;
- char buf[81];
- char *ptr = buf;
-
- va_start(ap, fmt);
- vsnprintf(buf, 80, fmt, ap);
- va_end(ap);
-
- while(*ptr && x < 80) {
- con_putchar_scr(x++, y, *ptr++);
- }
- return ptr - buf;
-#else
- return 0;
-#endif
-}
-
-static void scroll(void)
-{
- int new_line;
-
- if(++start_line > VIRT_ROWS - NROWS) {
- /* The bottom of the visible range reached the end of our text buffer.
- * Copy the rest of the lines to the top and reset start_line.
- */
- memcpy(TEXT_ADDR, TEXT_ADDR + start_line * NCOLS, (NROWS - 1) * NCOLS * 2);
- start_line = 0;
- }
-
- /* clear the next line that will be revealed by scrolling */
- new_line = start_line + NROWS - 1;
- memset16(TEXT_ADDR + new_line * NCOLS * 2, VMEM_CHAR(' ', txattr), NCOLS);
- crtc_setstart(start_line);
-}
-
-static void crtc_cursor(int x, int y)
-{
- unsigned int addr;
-
- addr = (y + start_line) * NCOLS + x;
-
- crtc_write(CRTC_REG_CURLOC_L, addr);
- crtc_write(CRTC_REG_CURLOC_H, addr >> 8);
-}
-
-static void crtc_getcursor(int *x, int *y)
-{
- unsigned int addr;
-
- addr = crtc_read(CRTC_REG_CURLOC_L);
- addr |= (unsigned int)crtc_read(CRTC_REG_CURLOC_H) << 8;
-
- *y = addr / NCOLS - start_line;
- *x = addr % NCOLS;
-}
-
-static void crtc_setstart(int y)
-{
- unsigned int addr = y * NCOLS;
-
- crtc_write(CRTC_REG_START_L, addr);
- crtc_write(CRTC_REG_START_H, addr >> 8);
-}
-
-static int crtc_getstart(void)
-{
- unsigned int addr;
-
- addr = crtc_read(CRTC_REG_START_L);
- addr |= (unsigned int)crtc_read(CRTC_REG_START_H) << 8;
-
- return addr / NCOLS;
-}
-
-static inline unsigned char crtc_read(int reg)
-{
- outp(CRTC_ADDR, reg);
- return inp(CRTC_DATA);
-}
-
-static inline void crtc_write(int reg, unsigned char val)
-{
- outp(CRTC_ADDR, reg);
- outp(CRTC_DATA, val);
-}
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef CONTTY_H_
-#define CONTTY_H_
-
-enum {
- CON_CURSOR_LINE,
- CON_CURSOR_BLOCK
-};
-
-enum {
- BLACK,
- BLUE,
- GREEN,
- CYAN,
- RED,
- MAGENTA,
- BROWN,
- LTGREY,
- GREY,
- LTBLUE,
- LTGREEN,
- LTCYAN,
- LTRED,
- LTMAGENTA,
- YELLOW,
- WHITE
-};
-#define BRIGHT 8
-#define FG_BRIGHT 0x08
-#define BG_BRIGHT 0x80
-
-enum {
- G_DIAMOND = 0x04,
- G_CHECKER = 0xb1,
- G_LR_CORNER = 0xd9,
- G_UR_CORNER = 0xbf,
- G_UL_CORNER = 0xda,
- G_LL_CORNER = 0xc0,
- G_CROSS = 0xc5,
- G_HLINE = 0xc4,
- G_L_TEE = 0xc3,
- G_R_TEE = 0xb4,
- G_B_TEE = 0xc1,
- G_T_TEE = 0xc2,
- G_VLINE = 0xb3,
- G_CDOT = 0xf8,
-
- G_HDBL = 0xcd,
- G_UL_HDBL = 0xd5,
- G_UR_HDBL = 0xb8,
- G_T_HDBL_TEE = 0xd1
-};
-
-
-int con_init(void);
-void con_scr_enable(void);
-void con_scr_disable(void);
-void con_show_cursor(int show);
-void con_cursor(int x, int y);
-void con_curattr(int shape, int blink);
-void con_fgcolor(int c);
-void con_bgcolor(int c);
-void con_setattr(unsigned char attr);
-unsigned char con_getattr(void);
-void con_clear(void);
-void con_putchar(int c);
-
-void con_putchar_scr(int x, int y, int c);
-int con_printf(int x, int y, const char *fmt, ...);
-
-#endif /* CONTTY_H_ */
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef DESC_H_
-#define DESC_H_
-
-#include <inttypes.h>
-
-typedef struct {
- uint16_t d[4];
-} desc_t;
-
-#endif /* DESC_H_ */
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#include <stdio.h>
-#include "config.h"
-#include "intr.h"
-#include "desc.h"
-#include "segm.h"
-#include "asmops.h"
-#include "panic.h"
-
-#define SYSCALL_INT 0x80
-
-/* IDT gate descriptor bits */
-#define GATE_TASK (5 << 8)
-#define GATE_INTR (6 << 8)
-#define GATE_TRAP (7 << 8)
-#define GATE_DEFAULT (1 << 11)
-#define GATE_PRESENT (1 << 15)
-
-/* PIC command and data ports */
-#define PIC1_CMD 0x20
-#define PIC1_DATA 0x21
-#define PIC2_CMD 0xa0
-#define PIC2_DATA 0xa1
-
-/* PIC initialization command word 1 bits */
-#define ICW1_ICW4_NEEDED (1 << 0)
-#define ICW1_SINGLE (1 << 1)
-#define ICW1_INTERVAL4 (1 << 2)
-#define ICW1_LEVEL (1 << 3)
-#define ICW1_INIT (1 << 4)
-/* PIC initialization command word 4 bits */
-#define ICW4_8086 (1 << 0)
-#define ICW4_AUTO_EOI (1 << 1)
-#define ICW4_BUF_SLAVE (1 << 3) /* 1000 */
-#define ICW4_BUF_MASTER (3 << 2) /* 1100 */
-#define ICW4_SPECIAL (1 << 4)
-
-/* PIC operation command word 2 bits */
-#define OCW2_EOI (1 << 5)
-
-
-static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type);
-
-/* defined in intr_asm.S */
-void set_idt(uint32_t addr, uint16_t limit);
-void intr_entry_default(void);
-void irq7_entry_check_spurious(void);
-void irq15_entry_check_spurious(void);
-
-/* the IDT (interrupt descriptor table) */
-static desc_t idt[256] __attribute__((aligned(8)));
-
-/* table of handler functions for all interrupts */
-static intr_func_t intr_func[256];
-
-static struct intr_frame *cur_intr_frame;
-static int eoi_pending;
-
-#define INTR_ENTRY_EC(n, name) \
- void intr_entry_##name(void); \
- set_intr_entry(n, intr_entry_##name);
-#define INTR_ENTRY_NOEC(n, name) INTR_ENTRY_EC(n, name)
-
-void init_intr(void)
-{
- int i;
-
- set_idt((uint32_t)idt, sizeof idt - 1);
-
- /* initialize all entry points and interrupt handlers */
- for(i=0; i<256; i++) {
- set_intr_entry(i, intr_entry_default);
- interrupt(i, 0);
- }
-
- /* by including intrtab.h here the series of INTR_ENTRY_* macros will be
- * expanded to a series of function prototypes for all interrupt entry
- * points and the corresponding calls to set_intr_entry to set up the IDT
- * slots
- */
-#include "intrtab.h"
-
- /* change irq7 and irq15 to special entry points which first
- * make sure we didn't get a spurious interrupt before proceeding
- */
- set_intr_entry(IRQ_TO_INTR(7), irq7_entry_check_spurious);
- set_intr_entry(IRQ_TO_INTR(15), irq15_entry_check_spurious);
-
- /* initialize the programmable interrupt controller
- * setting up the maping of IRQs [0, 15] to interrupts [32, 47]
- */
- prog_pic(IRQ_OFFSET);
- eoi_pending = 0;
-}
-
-void cleanup_intr(void)
-{
- disable_intr();
- /* reprogram the PIC to the default BIOS offset (8) */
- prog_pic(8);
-}
-
-/* retrieve the current interrupt frame.
- * returns 0 when called during init.
- */
-struct intr_frame *get_intr_frame(void)
-{
- return cur_intr_frame;
-}
-
-/* set an interrupt handler function for a particular interrupt */
-void interrupt(int intr_num, intr_func_t func)
-{
- int iflag = get_intr_flag();
- disable_intr();
- intr_func[intr_num] = func;
- set_intr_flag(iflag);
-}
-
-/* this function is called from all interrupt entry points
- * it calls the appropriate interrupt handlers if available and handles
- * sending an end-of-interrupt command to the PICs when finished.
- */
-void dispatch_intr(struct intr_frame frm)
-{
- cur_intr_frame = &frm;
-
- if(IS_IRQ(frm.inum)) {
- eoi_pending = frm.inum;
- }
-
- if(intr_func[frm.inum]) {
- intr_func[frm.inum](frm.inum);
- } else {
- if(frm.inum < 32) {
- panic("unhandled exception %u, error code: %u, at cs:eip=%x:%x\n",
- frm.inum, frm.err, frm.cs, frm.eip);
- }
- /*printf("unhandled interrupt %d\n", frm.inum);*/
- }
-
- disable_intr();
- if(eoi_pending) {
- end_of_irq(INTR_TO_IRQ(eoi_pending));
- }
-}
-
-void prog_pic(int offs)
-{
- /* send ICW1 saying we'll follow with ICW4 later on */
- outp(PIC1_CMD, ICW1_INIT | ICW1_ICW4_NEEDED);
- outp(PIC2_CMD, ICW1_INIT | ICW1_ICW4_NEEDED);
- /* send ICW2 with IRQ remapping */
- outp(PIC1_DATA, offs);
- outp(PIC2_DATA, offs + 8);
- /* send ICW3 to setup the master/slave relationship */
- /* ... set bit3 = 3rd interrupt input has a slave */
- outp(PIC1_DATA, 4);
- /* ... set slave ID to 2 */
- outp(PIC2_DATA, 2);
- /* send ICW4 to set 8086 mode (no calls generated) */
- outp(PIC1_DATA, ICW4_8086);
- outp(PIC2_DATA, ICW4_8086);
- /* done, just reset the data port to 0 */
- outp(PIC1_DATA, 0);
- outp(PIC2_DATA, 0);
-}
-
-static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type)
-{
- /* first 16bit part is the low 16bits of the entry address */
- desc->d[0] = addr & 0xffff;
- /* second 16bit part is the segment selector for the entry code */
- desc->d[1] = sel;
- /* third 16bit part has the privilege level, type, and present bit */
- desc->d[2] = ((dpl & 3) << 13) | type | GATE_DEFAULT | GATE_PRESENT;
- /* last 16bit part is the high 16bits of the entry address */
- desc->d[3] = (addr & 0xffff0000) >> 16;
-}
-
-#define IS_TRAP(n) ((n) >= 32 && !IS_IRQ(n))
-void set_intr_entry(int num, void (*handler)(void))
-{
- int type = IS_TRAP(num) ? GATE_TRAP : GATE_INTR;
-
- /* the syscall interrupt has to have a dpl of 3 otherwise calling it from
- * user space will raise a general protection exception. All the rest should
- * have a dpl of 0 to disallow user programs to execute critical interrupt
- * handlers and possibly crashing the system.
- */
- int dpl = (num == SYSCALL_INT) ? 3 : 0;
-
- gate_desc(idt + num, selector(SEGM_CODE, 0), (uint32_t)handler, dpl, type);
-}
-
-void set_pic_mask(int pic, unsigned char mask)
-{
- outp(pic > 0 ? PIC2_DATA : PIC1_DATA, mask);
-}
-
-unsigned char get_pic_mask(int pic)
-{
- return inp(pic > 0 ? PIC2_DATA : PIC1_DATA);
-}
-
-void mask_irq(int irq)
-{
- int port;
- unsigned char mask;
-
- if(irq < 8) {
- port = PIC1_DATA;
- } else {
- port = PIC2_DATA;
- irq -= 8;
- }
-
- mask = inp(port) | (1 << irq);
- outp(port, mask);
-}
-
-void unmask_irq(int irq)
-{
- int port;
- unsigned char mask;
-
- if(irq < 8) {
- port = PIC1_DATA;
- } else {
- port = PIC2_DATA;
- irq -= 8;
- }
-
- mask = inp(port) & ~(1 << irq);
- outp(port, mask);
-}
-
-
-void end_of_irq(int irq)
-{
- int intr_state = get_intr_flag();
- disable_intr();
-
- if(!eoi_pending) {
- set_intr_flag(intr_state);
- return;
- }
- eoi_pending = 0;
-
- if(irq > 7) {
- outp(PIC2_CMD, OCW2_EOI);
- }
- outp(PIC1_CMD, OCW2_EOI);
-
- set_intr_flag(intr_state);
-}
-
-#ifdef ENABLE_GDB_STUB
-void exceptionHandler(int id, void (*func)())
-{
- set_intr_entry(id, func);
-}
-#endif
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef INTR_H_
-#define INTR_H_
-
-#include <inttypes.h>
-#include "asmops.h"
-
-/* offset used to remap IRQ numbers (+32) */
-#define IRQ_OFFSET 32
-/* conversion macros between IRQ and interrupt numbers */
-#define IRQ_TO_INTR(x) ((x) + IRQ_OFFSET)
-#define INTR_TO_IRQ(x) ((x) - IRQ_OFFSET)
-/* checks whether a particular interrupt is an remapped IRQ */
-#define IS_IRQ(n) ((n) >= IRQ_OFFSET && (n) < IRQ_OFFSET + 16)
-
-/* general purpose registers as they are pushed by pusha */
-struct registers {
- uint32_t edi, esi, ebp, esp;
- uint32_t ebx, edx, ecx, eax;
-} __attribute__ ((packed));
-
-/* structure used to pass the interrupt stack frame from the
- * entry points to the C dispatch function.
- */
-struct intr_frame {
- /* registers pushed by pusha in intr_entry_* */
- struct registers regs;
- /* data segment selectors */
- /* XXX removed: not needed unless we do dpl3 transitions
- uint32_t ds, es, fs, gs;
- */
- /* interrupt number and error code pushed in intr_entry_* */
- uint32_t inum, err;
- /* pushed by CPU during interrupt entry */
- uint32_t eip, cs, eflags;
- /* pushed by CPU during interrupt entry from user space */
- /* XXX removed: again, not doing user space currently
- uint32_t esp, ss;
- */
-} __attribute__ ((packed));
-
-
-
-typedef void (*intr_func_t)(int);
-
-
-void init_intr(void);
-void cleanup_intr(void);
-
-struct intr_frame *get_intr_frame(void);
-
-/* install high level interrupt callback */
-void interrupt(int intr_num, intr_func_t func);
-
-/* install low-level interrupt vector in IDT
- * must be able to handle EOI and return with iret
- */
-void set_intr_entry(int num, void (*handler)(void));
-
-void prog_pic(int offs);
-void set_pic_mask(int pic, unsigned char mask);
-unsigned char get_pic_mask(int pic);
-void mask_irq(int irq);
-void unmask_irq(int irq);
-
-/* defined in intr_asm.S */
-int get_intr_flag(void);
-void set_intr_flag(int onoff);
-
-void intr_ret(struct intr_frame ifrm);
-
-void end_of_irq(int irq);
-
-#endif /* INTR_H_ */
+++ /dev/null
-; pcboot - bootable PC demo/game kernel
-; Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
-;
-; This program is free software: you can redistribute it and/or modify
-; it under the terms of the GNU General Public License as published by
-; the Free Software Foundation, either version 3 of the License, or
-; (at your option) any later version.
-;
-; This program is distributed in the hope that it will be useful,
-; but WITHOUT ANY WARRANTY, without even the implied warranty of
-; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-; GNU General Public License for more details.
-;
-; You should have received a copy of the GNU General Public License
-; along with this program. If not, see <https://www.gnu.org/licenses/>.
-
- section .data
- align 4
- dw 0
-idtr_desc:
-lim: dw 0
-addr: dd 0
-
- section .text
-; void set_idt(uint32_t addr, uint16_t limit)
- global set_idt
-set_idt:
- mov eax, [esp + 4]
- mov [addr], eax
- mov ax, [esp + 8]
- mov [lim], ax
- lidt [idtr_desc]
- ret
-
-; int get_intr_flag()
- global get_intr_flag
-get_intr_flag:
- pushf
- pop eax
- ; bit 9 of eflags is IF
- shr eax, 9
- and eax, 1
- ret
-
-; void set_intr_flag(int onoff)
- global set_intr_flag
-set_intr_flag:
- cmp dword [esp + 4], 0
- jz .off
- sti
- ret
-.off: cli
- ret
-
-; interrupt entry with error code macro
-; this macro generates an interrupt entry point for the
-; exceptions which include error codes in the stack frame
-%macro ientry_err 2
- global intr_entry_%2
-intr_entry_%2:
- push dword %1
- jmp intr_entry_common
-%endmacro
-
-; interrupt entry without error code macro
-; this macro generates an interrupt entry point for the interrupts
-; and exceptions which do not include error codes in the stack frame
-; it pushes a dummy error code (0), to make the stack frame identical
-%macro ientry_noerr 2
- global intr_entry_%2
-intr_entry_%2:
- push dword 0
- push dword %1
- jmp intr_entry_common
-%endmacro
-
-; common code used by all entry points. calls dispatch_intr()
-; defined in intr.c
- extern dispatch_intr
-intr_entry_common:
- ; save general purpose registers
- pusha
- call dispatch_intr
-intr_ret_local:
- ; restore general purpose registers
- popa
- ; remove error code and intr num from stack
- add esp, 8
- iret
-
-; special case for the timer interrupt, to avoid all the overhead of
-; going through the C interrupt dispatcher 250 times each second
-; extern nticks
-; global intr_entry_fast_timer
-;intr_entry_fast_timer:
-; inc dword [nticks]
-; ; signal end of interrupt
-; push eax
-; mov al, 20h
-; out 20h, al
-; pop eax
-; iret
-
-; special case for IRQ 7 and IRQ 15, to catch spurious interrupts
-PIC1_CMD equ 0x20
-PIC2_CMD equ 0xa0
-OCW2_EOI equ 0x20
-OCW3_ISR equ 0x0b
-
- global irq7_entry_check_spurious
-irq7_entry_check_spurious:
- push eax
- mov al, OCW3_ISR
- out PIC1_CMD, al
- in al, PIC1_CMD
- and al, 80h
- pop eax
- jnz intr_entry_irq7
- iret
-
- global irq15_entry_check_spurious
-irq15_entry_check_spurious:
- push eax
- mov al, OCW3_ISR
- out PIC2_CMD, al
- in al, PIC2_CMD
- and al, 80h
- jnz .legit
- ; it was spurious, send EOI to master PIC and iret
- mov al, OCW2_EOI
- out PIC1_CMD, al
- pop eax
- iret
-.legit: pop eax
- jmp intr_entry_irq15
-
-
-; XXX not necessary for now, just leaving it in in case it's useful
-; down the road.
-;
-; intr_ret is called by context_switch to return from the kernel
-; to userspace. The argument is a properly formed intr_frame
-; structure with the saved context of the new task.
-;
-; First thing to do is remove the return address pointing back
-; to context_switch, which then leaves us with a proper interrupt
-; stack frame, so we can jump right in the middle of the regular
-; interrupt return code above.
- global intr_ret
-intr_ret:
- add esp, $4
- jmp intr_ret_local
-
-; by including interrupts.h with ASM defined, the macros above
-; are expanded to generate all required interrupt entry points
-%define INTR_ENTRY_EC(n, name) ientry_err n, name
-%define INTR_ENTRY_NOEC(n, name) ientry_noerr n, name
-
-%include 'intrtab.h'
-
-; vi:set ts=8 sts=8 sw=8 ft=nasm:
+++ /dev/null
-INTR_ENTRY_NOEC(0, div)
-INTR_ENTRY_NOEC(1, debug)
-INTR_ENTRY_NOEC(2, nmi)
-INTR_ENTRY_NOEC(3, bp)
-INTR_ENTRY_NOEC(4, overflow)
-INTR_ENTRY_NOEC(5, bound)
-INTR_ENTRY_NOEC(6, ill)
-INTR_ENTRY_NOEC(7, nodev)
-INTR_ENTRY_EC(8, dfault)
-INTR_ENTRY_NOEC(9, copseg)
-INTR_ENTRY_EC(10, tss)
-INTR_ENTRY_EC(11, segpres)
-INTR_ENTRY_EC(12, stack)
-INTR_ENTRY_EC(13, prot)
-INTR_ENTRY_EC(14, page)
-INTR_ENTRY_NOEC(15, reserved)
-INTR_ENTRY_NOEC(16, fpu)
-INTR_ENTRY_EC(17, align)
-INTR_ENTRY_NOEC(18, mce)
-INTR_ENTRY_NOEC(19, sse)
-INTR_ENTRY_NOEC(32, irq0)
-INTR_ENTRY_NOEC(33, irq1)
-INTR_ENTRY_NOEC(34, irq2)
-INTR_ENTRY_NOEC(35, irq3)
-INTR_ENTRY_NOEC(36, irq4)
-INTR_ENTRY_NOEC(37, irq5)
-INTR_ENTRY_NOEC(38, irq6)
-INTR_ENTRY_NOEC(39, irq7)
-INTR_ENTRY_NOEC(40, irq8)
-INTR_ENTRY_NOEC(41, irq9)
-INTR_ENTRY_NOEC(42, irq10)
-INTR_ENTRY_NOEC(43, irq11)
-INTR_ENTRY_NOEC(44, irq12)
-INTR_ENTRY_NOEC(45, irq13)
-INTR_ENTRY_NOEC(46, irq14)
-INTR_ENTRY_NOEC(47, irq15)
-INTR_ENTRY_NOEC(128, syscall)
-INTR_ENTRY_NOEC(255, default)
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef KBREGS_H_
-#define KBREGS_H_
-
-#define KB_IRQ 1
-#define PSAUX_IRQ 12
-
-#define KB_DATA_PORT 0x60
-#define KB_CMD_PORT 0x64
-#define KB_STATUS_PORT 0x64
-
-#define KB_ACK 0xfa
-#define KB_NACK 0xfe
-#define KB_TEST_PASSED 0x55
-#define KB_TEST_FAILED 0xfc
-
-#define KB_STAT_OUTBUF_FULL 0x01
-#define KB_STAT_INBUF_FULL 0x02
-#define KB_STAT_SYSFLAG 0x04
-#define KB_STAT_CMD 0x08
-#define KB_STAT_ACTIVE 0x10
-#define KB_STAT_AUX 0x20
-#define KB_STAT_TIMEOUT 0x40
-#define KB_STAT_PAR_ERROR 0x80
-
-#define KB_CMD_TEST 0xaa
-
-/* keyboard commands */
-#define KB_CMD_GET_CMDBYTE 0x20
-#define KB_CMD_SET_CMDBYTE 0x60
-#define KB_CMD_AUX_ENABLE 0xa8
-#define KB_CMD_READ_OUTPORT 0xd0
-#define KB_CMD_WRITE_OUTPORT 0xd1
-#define KB_CMD_PSAUX 0xd4
-#define KB_CMD_PULSE_RESET 0xfe
-
-/* controller command byte bits */
-#define KB_CCB_KB_INTREN 0x01
-#define KB_CCB_AUX_INTREN 0x02
-#define KB_CCB_SYSFLAG 0x04
-#define KB_CCB_KB_DISABLE 0x10
-#define KB_CCB_AUX_DISABLE 0x20
-#define KB_CCB_KB_XLAT 0x40
-
-/* psaux commands (use with d4 prefix) */
-#define AUX_CMD_STREAM_MODE 0xea
-#define AUX_CMD_READ_MOUSE 0xeb
-#define AUX_CMD_REMOTE_MODE 0xf0
-#define AUX_CMD_ENABLE 0xf4
-#define AUX_CMD_DEFAULTS 0xf6
-
-#define AUX_PKT0_LEFTBN 0x01
-#define AUX_PKT0_RIGHTBN 0x02
-#define AUX_PKT0_MIDDLEBN 0x04
-#define AUX_PKT0_ALWAYS1 0x08
-#define AUX_PKT0_XSIGN 0x10
-#define AUX_PKT0_YSIGN 0x20
-#define AUX_PKT0_XOVF 0x40
-#define AUX_PKT0_YOVF 0x80
-
-#define AUX_PKT0_OVF_BITS (AUX_PKT0_XOVF | AUX_PKT0_YOVF)
-#define AUX_PKT0_BUTTON_BITS \
- (AUX_PKT0_LEFTBN | AUX_PKT0_RIGHTBN | AUX_PKT0_MIDDLEBN)
-
-#endif /* KBREGS_H_ */
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef KBSCAN_H_
-#define KBSCAN_H_
-
-/* table with rough translations from set 1 scancodes to ASCII-ish */
-static int scantbl_set1[] = {
- 0, KB_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', /* 0 - e */
- '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', /* f - 1c */
- KB_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', /* 1d - 29 */
- KB_LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KB_RSHIFT, /* 2a - 36 */
- KB_NUM_MUL, KB_LALT, ' ', KB_CAPSLK, KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, KB_F7, KB_F8, KB_F9, KB_F10, /* 37 - 44 */
- KB_NUMLK, KB_SCRLK, KB_NUM_7, KB_NUM_8, KB_NUM_9, KB_NUM_MINUS, KB_NUM_4, KB_NUM_5, KB_NUM_6, KB_NUM_PLUS, /* 45 - 4e */
- KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_0, KB_NUM_DOT, KB_SYSRQ, 0, 0, KB_F11, KB_F12, /* 4d - 58 */
-#if 0
- 0, 0, 0, 0, 0, 0, 0, /* 59 - 5f */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6f */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 70 - 7f */
-#endif
-};
-
-/* extended scancodes, after the 0xe0 prefix */
-#if 0
-static int scantbl_set1_ext[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - f */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\r', KB_RCTRL, 0, 0, /* 10 - 1f */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2f */
- 0, 0, 0, 0, 0, KB_NUM_MINUS, 0, KB_SYSRQ, KB_RALT, 0, 0, 0, 0, 0, 0, 0, /* 30 - 3f */
- 0, 0, 0, 0, 0, 0, 0, KB_HOME, KB_UP, KB_PGUP, 0, KB_LEFT, 0, KB_RIGHT, 0, KB_END, /* 40 - 4f */
- KB_DOWN, KB_PGDN, KB_INSERT, KB_DEL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5f */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6f */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7f */
-};
-#endif
-
-
-#endif /* KBSCAN_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef ASMOPS_H_
+#define ASMOPS_H_
+
+#include <inttypes.h>
+
+#define enable_intr() asm volatile("sti")
+#define disable_intr() asm volatile("cli")
+#define halt_cpu() asm volatile("hlt")
+
+#define CALLER_EIP(eip) \
+ asm volatile( \
+ "mov 4(%%ebp), %0\n\t" \
+ : "=a" ((uint32_t)eip))
+
+static inline uint8_t inp(uint16_t port)
+{
+ uint8_t res;
+ asm volatile (
+ "inb %1, %0\n\t"
+ : "=a" (res)
+ : "dN" (port));
+ return res;
+}
+
+static inline uint16_t inpw(uint16_t port)
+{
+ uint16_t res;
+ asm volatile (
+ "inw %1, %0\n\t"
+ : "=a" (res)
+ : "dN" (port));
+ return res;
+}
+
+static inline uint32_t inpd(uint16_t port)
+{
+ uint32_t res;
+ asm volatile (
+ "inl %1, %0\n\t"
+ : "=a" (res)
+ : "dN" (port));
+ return res;
+}
+
+#define outp(port, src) \
+ asm volatile( \
+ "outb %0, %1\n\t" \
+ :: "a" ((uint8_t)(src)), "dN" ((uint16_t)(port)))
+
+#define outpw(port, src) \
+ asm volatile( \
+ "outw %0, %1\n\t" \
+ :: "a" ((uint16_t)(src)), "dN" ((uint16_t)(port)))
+
+#define outpd(port, src) \
+ asm volatile( \
+ "outl %0, %1\n\t" \
+ :: "a" ((uint32_t)(src)), "dN" ((uint16_t)(port)))
+
+/* delay for about 1us */
+#define iodelay() outp(0x80, 0)
+
+#define get_ebp(ebp) \
+ asm volatile( \
+ "mov %%ebp, %0\n\t" \
+ : "=g"((uint32_t)(ebp)))
+
+#endif /* ASMOPS_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "contty.h"
+#include "serial.h"
+#include "asmops.h"
+#include "config.h"
+
+#define VIRT_ROWS 200
+
+#define NCOLS 80
+#define NROWS 25
+#define TEXT_ADDR ((char*)0xb8000)
+
+#define CRTC_ADDR 0x3d4
+#define CRTC_DATA 0x3d5
+
+#define CRTC_REG_CURSTART 0x0a
+#define CRTC_REG_CUREND 0x0b
+#define CRTC_REG_START_H 0x0c
+#define CRTC_REG_START_L 0x0d
+#define CRTC_REG_CURLOC_H 0x0e
+#define CRTC_REG_CURLOC_L 0x0f
+
+#define VMEM_CHAR(c, attr) \
+ (((uint16_t)(c) & 0xff) | ((uint16_t)(attr) << 8))
+
+static void scroll(void);
+static void crtc_cursor(int x, int y);
+static void crtc_getcursor(int *x, int *y);
+static void crtc_setstart(int y);
+static int crtc_getstart(void);
+static inline unsigned char crtc_read(int reg);
+static inline void crtc_write(int reg, unsigned char val);
+
+static int cursor_x, cursor_y;
+static unsigned char txattr = 0x07;
+static int start_line;
+static unsigned char cy0, cy1;
+static int curvis;
+static int scr_on = 1;
+
+
+int con_init(void)
+{
+#ifdef CON_SERIAL
+ ser_open(1, 9600, SER_8N1);
+#endif
+
+#ifdef CON_TEXTMODE
+ cy0 = crtc_read(CRTC_REG_CURSTART);
+ curvis = cy0 & 0x20 ? 1 : 0;
+ cy0 &= 0x1f;
+ cy1 = crtc_read(CRTC_REG_CUREND) & 0x1f;
+
+ start_line = crtc_getstart();
+ crtc_getcursor(&cursor_x, &cursor_y);
+ con_show_cursor(1);
+ scr_on = 1;
+#endif
+
+ return 0;
+}
+
+void con_scr_enable(void)
+{
+ scr_on = 1;
+}
+
+void con_scr_disable(void)
+{
+ scr_on = 0;
+}
+
+void con_show_cursor(int show)
+{
+#ifdef CON_TEXTMODE
+ unsigned char val = cy0 & 0x1f;
+ if(!show) {
+ val |= 0x20;
+ }
+ crtc_write(CRTC_REG_CURSTART, val);
+ curvis = show;
+#endif
+}
+
+void con_cursor(int x, int y)
+{
+#ifdef CON_TEXTMODE
+ cursor_x = x;
+ cursor_y = y;
+ crtc_cursor(x, y);
+#endif
+}
+
+void con_curattr(int shape, int blink)
+{
+#ifdef CON_TEXTMODE
+ unsigned char start;
+ cy0 = (shape == CON_CURSOR_LINE) ? 0xd : 0;
+ cy1 = 0xe;
+
+ start = cy0;
+ if(curvis) {
+ start |= 0x20;
+ }
+
+ crtc_write(CRTC_REG_CURSTART, start);
+ crtc_write(CRTC_REG_CUREND, cy0);
+#endif
+}
+
+void con_fgcolor(int c)
+{
+ txattr = (txattr & 0xf0) | c;
+}
+
+void con_bgcolor(int c)
+{
+ txattr = (txattr & 0x0f) | (c << 4);
+}
+
+void con_setattr(unsigned char attr)
+{
+ txattr = attr;
+}
+
+unsigned char con_getattr(void)
+{
+ return txattr;
+}
+
+void con_clear(void)
+{
+#ifdef CON_TEXTMODE
+ memset16(TEXT_ADDR, VMEM_CHAR(' ', txattr), NCOLS * NROWS);
+
+ start_line = 0;
+ crtc_setstart(0);
+
+ cursor_x = cursor_y = 0;
+ crtc_cursor(0, 0);
+#endif
+}
+
+static inline void linefeed(void)
+{
+ if(++cursor_y >= NROWS) {
+ scroll();
+ --cursor_y;
+ }
+}
+
+void con_putchar(int c)
+{
+#ifdef CON_TEXTMODE
+ if(scr_on) {
+ switch(c) {
+ case '\n':
+ linefeed();
+ case '\r':
+ cursor_x = 0;
+ crtc_cursor(cursor_x, cursor_y);
+ break;
+
+ case '\t':
+ cursor_x = (cursor_x & 0x7) + 8;
+ if(cursor_x >= NCOLS) {
+ linefeed();
+ cursor_x = 0;
+ }
+ crtc_cursor(cursor_x, cursor_y);
+ break;
+
+ case '\b':
+ if(cursor_x > 0) cursor_x--;
+ con_putchar_scr(cursor_x, cursor_y, ' ');
+ crtc_cursor(cursor_x, cursor_y);
+ break;
+
+ default:
+ con_putchar_scr(cursor_x, cursor_y, c);
+
+ if(++cursor_x >= NCOLS) {
+ linefeed();
+ cursor_x = 0;
+ }
+ crtc_cursor(cursor_x, cursor_y);
+ }
+ }
+#endif
+
+#ifdef CON_SERIAL
+ ser_putchar(c);
+#endif
+}
+
+void con_putchar_scr(int x, int y, int c)
+{
+#ifdef CON_TEXTMODE
+ uint16_t *ptr = (uint16_t*)TEXT_ADDR;
+ ptr[(y + start_line) * NCOLS + x] = VMEM_CHAR(c, txattr);
+#endif
+}
+
+int con_printf(int x, int y, const char *fmt, ...)
+{
+#ifdef CON_TEXTMODE
+ va_list ap;
+ char buf[81];
+ char *ptr = buf;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, 80, fmt, ap);
+ va_end(ap);
+
+ while(*ptr && x < 80) {
+ con_putchar_scr(x++, y, *ptr++);
+ }
+ return ptr - buf;
+#else
+ return 0;
+#endif
+}
+
+static void scroll(void)
+{
+ int new_line;
+
+ if(++start_line > VIRT_ROWS - NROWS) {
+ /* The bottom of the visible range reached the end of our text buffer.
+ * Copy the rest of the lines to the top and reset start_line.
+ */
+ memcpy(TEXT_ADDR, TEXT_ADDR + start_line * NCOLS, (NROWS - 1) * NCOLS * 2);
+ start_line = 0;
+ }
+
+ /* clear the next line that will be revealed by scrolling */
+ new_line = start_line + NROWS - 1;
+ memset16(TEXT_ADDR + new_line * NCOLS * 2, VMEM_CHAR(' ', txattr), NCOLS);
+ crtc_setstart(start_line);
+}
+
+static void crtc_cursor(int x, int y)
+{
+ unsigned int addr;
+
+ addr = (y + start_line) * NCOLS + x;
+
+ crtc_write(CRTC_REG_CURLOC_L, addr);
+ crtc_write(CRTC_REG_CURLOC_H, addr >> 8);
+}
+
+static void crtc_getcursor(int *x, int *y)
+{
+ unsigned int addr;
+
+ addr = crtc_read(CRTC_REG_CURLOC_L);
+ addr |= (unsigned int)crtc_read(CRTC_REG_CURLOC_H) << 8;
+
+ *y = addr / NCOLS - start_line;
+ *x = addr % NCOLS;
+}
+
+static void crtc_setstart(int y)
+{
+ unsigned int addr = y * NCOLS;
+
+ crtc_write(CRTC_REG_START_L, addr);
+ crtc_write(CRTC_REG_START_H, addr >> 8);
+}
+
+static int crtc_getstart(void)
+{
+ unsigned int addr;
+
+ addr = crtc_read(CRTC_REG_START_L);
+ addr |= (unsigned int)crtc_read(CRTC_REG_START_H) << 8;
+
+ return addr / NCOLS;
+}
+
+static inline unsigned char crtc_read(int reg)
+{
+ outp(CRTC_ADDR, reg);
+ return inp(CRTC_DATA);
+}
+
+static inline void crtc_write(int reg, unsigned char val)
+{
+ outp(CRTC_ADDR, reg);
+ outp(CRTC_DATA, val);
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef CONTTY_H_
+#define CONTTY_H_
+
+enum {
+ CON_CURSOR_LINE,
+ CON_CURSOR_BLOCK
+};
+
+enum {
+ BLACK,
+ BLUE,
+ GREEN,
+ CYAN,
+ RED,
+ MAGENTA,
+ BROWN,
+ LTGREY,
+ GREY,
+ LTBLUE,
+ LTGREEN,
+ LTCYAN,
+ LTRED,
+ LTMAGENTA,
+ YELLOW,
+ WHITE
+};
+#define BRIGHT 8
+#define FG_BRIGHT 0x08
+#define BG_BRIGHT 0x80
+
+enum {
+ G_DIAMOND = 0x04,
+ G_CHECKER = 0xb1,
+ G_LR_CORNER = 0xd9,
+ G_UR_CORNER = 0xbf,
+ G_UL_CORNER = 0xda,
+ G_LL_CORNER = 0xc0,
+ G_CROSS = 0xc5,
+ G_HLINE = 0xc4,
+ G_L_TEE = 0xc3,
+ G_R_TEE = 0xb4,
+ G_B_TEE = 0xc1,
+ G_T_TEE = 0xc2,
+ G_VLINE = 0xb3,
+ G_CDOT = 0xf8,
+
+ G_HDBL = 0xcd,
+ G_UL_HDBL = 0xd5,
+ G_UR_HDBL = 0xb8,
+ G_T_HDBL_TEE = 0xd1
+};
+
+
+int con_init(void);
+void con_scr_enable(void);
+void con_scr_disable(void);
+void con_show_cursor(int show);
+void con_cursor(int x, int y);
+void con_curattr(int shape, int blink);
+void con_fgcolor(int c);
+void con_bgcolor(int c);
+void con_setattr(unsigned char attr);
+unsigned char con_getattr(void);
+void con_clear(void);
+void con_putchar(int c);
+
+void con_putchar_scr(int x, int y, int c);
+int con_printf(int x, int y, const char *fmt, ...);
+
+#endif /* CONTTY_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef DESC_H_
+#define DESC_H_
+
+#include <inttypes.h>
+
+typedef struct {
+ uint16_t d[4];
+} desc_t;
+
+#endif /* DESC_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include "config.h"
+#include "intr.h"
+#include "desc.h"
+#include "segm.h"
+#include "asmops.h"
+#include "panic.h"
+
+#define SYSCALL_INT 0x80
+
+/* IDT gate descriptor bits */
+#define GATE_TASK (5 << 8)
+#define GATE_INTR (6 << 8)
+#define GATE_TRAP (7 << 8)
+#define GATE_DEFAULT (1 << 11)
+#define GATE_PRESENT (1 << 15)
+
+/* PIC command and data ports */
+#define PIC1_CMD 0x20
+#define PIC1_DATA 0x21
+#define PIC2_CMD 0xa0
+#define PIC2_DATA 0xa1
+
+/* PIC initialization command word 1 bits */
+#define ICW1_ICW4_NEEDED (1 << 0)
+#define ICW1_SINGLE (1 << 1)
+#define ICW1_INTERVAL4 (1 << 2)
+#define ICW1_LEVEL (1 << 3)
+#define ICW1_INIT (1 << 4)
+/* PIC initialization command word 4 bits */
+#define ICW4_8086 (1 << 0)
+#define ICW4_AUTO_EOI (1 << 1)
+#define ICW4_BUF_SLAVE (1 << 3) /* 1000 */
+#define ICW4_BUF_MASTER (3 << 2) /* 1100 */
+#define ICW4_SPECIAL (1 << 4)
+
+/* PIC operation command word 2 bits */
+#define OCW2_EOI (1 << 5)
+
+
+static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type);
+
+/* defined in intr_asm.S */
+void set_idt(uint32_t addr, uint16_t limit);
+void intr_entry_default(void);
+void irq7_entry_check_spurious(void);
+void irq15_entry_check_spurious(void);
+
+/* the IDT (interrupt descriptor table) */
+static desc_t idt[256] __attribute__((aligned(8)));
+
+/* table of handler functions for all interrupts */
+static intr_func_t intr_func[256];
+
+static struct intr_frame *cur_intr_frame;
+static int eoi_pending;
+
+#define INTR_ENTRY_EC(n, name) \
+ void intr_entry_##name(void); \
+ set_intr_entry(n, intr_entry_##name);
+#define INTR_ENTRY_NOEC(n, name) INTR_ENTRY_EC(n, name)
+
+void init_intr(void)
+{
+ int i;
+
+ set_idt((uint32_t)idt, sizeof idt - 1);
+
+ /* initialize all entry points and interrupt handlers */
+ for(i=0; i<256; i++) {
+ set_intr_entry(i, intr_entry_default);
+ interrupt(i, 0);
+ }
+
+ /* by including intrtab.h here the series of INTR_ENTRY_* macros will be
+ * expanded to a series of function prototypes for all interrupt entry
+ * points and the corresponding calls to set_intr_entry to set up the IDT
+ * slots
+ */
+#include "intrtab.h"
+
+ /* change irq7 and irq15 to special entry points which first
+ * make sure we didn't get a spurious interrupt before proceeding
+ */
+ set_intr_entry(IRQ_TO_INTR(7), irq7_entry_check_spurious);
+ set_intr_entry(IRQ_TO_INTR(15), irq15_entry_check_spurious);
+
+ /* initialize the programmable interrupt controller
+ * setting up the maping of IRQs [0, 15] to interrupts [32, 47]
+ */
+ prog_pic(IRQ_OFFSET);
+ eoi_pending = 0;
+}
+
+void cleanup_intr(void)
+{
+ disable_intr();
+ /* reprogram the PIC to the default BIOS offset (8) */
+ prog_pic(8);
+}
+
+/* retrieve the current interrupt frame.
+ * returns 0 when called during init.
+ */
+struct intr_frame *get_intr_frame(void)
+{
+ return cur_intr_frame;
+}
+
+/* set an interrupt handler function for a particular interrupt */
+void interrupt(int intr_num, intr_func_t func)
+{
+ int iflag = get_intr_flag();
+ disable_intr();
+ intr_func[intr_num] = func;
+ set_intr_flag(iflag);
+}
+
+/* this function is called from all interrupt entry points
+ * it calls the appropriate interrupt handlers if available and handles
+ * sending an end-of-interrupt command to the PICs when finished.
+ */
+void dispatch_intr(struct intr_frame frm)
+{
+ cur_intr_frame = &frm;
+
+ if(IS_IRQ(frm.inum)) {
+ eoi_pending = frm.inum;
+ }
+
+ if(intr_func[frm.inum]) {
+ intr_func[frm.inum](frm.inum);
+ } else {
+ if(frm.inum < 32) {
+ panic("unhandled exception %u, error code: %u, at cs:eip=%x:%x\n",
+ frm.inum, frm.err, frm.cs, frm.eip);
+ }
+ /*printf("unhandled interrupt %d\n", frm.inum);*/
+ }
+
+ disable_intr();
+ if(eoi_pending) {
+ end_of_irq(INTR_TO_IRQ(eoi_pending));
+ }
+}
+
+void prog_pic(int offs)
+{
+ /* send ICW1 saying we'll follow with ICW4 later on */
+ outp(PIC1_CMD, ICW1_INIT | ICW1_ICW4_NEEDED);
+ outp(PIC2_CMD, ICW1_INIT | ICW1_ICW4_NEEDED);
+ /* send ICW2 with IRQ remapping */
+ outp(PIC1_DATA, offs);
+ outp(PIC2_DATA, offs + 8);
+ /* send ICW3 to setup the master/slave relationship */
+ /* ... set bit3 = 3rd interrupt input has a slave */
+ outp(PIC1_DATA, 4);
+ /* ... set slave ID to 2 */
+ outp(PIC2_DATA, 2);
+ /* send ICW4 to set 8086 mode (no calls generated) */
+ outp(PIC1_DATA, ICW4_8086);
+ outp(PIC2_DATA, ICW4_8086);
+ /* done, just reset the data port to 0 */
+ outp(PIC1_DATA, 0);
+ outp(PIC2_DATA, 0);
+}
+
+static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type)
+{
+ /* first 16bit part is the low 16bits of the entry address */
+ desc->d[0] = addr & 0xffff;
+ /* second 16bit part is the segment selector for the entry code */
+ desc->d[1] = sel;
+ /* third 16bit part has the privilege level, type, and present bit */
+ desc->d[2] = ((dpl & 3) << 13) | type | GATE_DEFAULT | GATE_PRESENT;
+ /* last 16bit part is the high 16bits of the entry address */
+ desc->d[3] = (addr & 0xffff0000) >> 16;
+}
+
+#define IS_TRAP(n) ((n) >= 32 && !IS_IRQ(n))
+void set_intr_entry(int num, void (*handler)(void))
+{
+ int type = IS_TRAP(num) ? GATE_TRAP : GATE_INTR;
+
+ /* the syscall interrupt has to have a dpl of 3 otherwise calling it from
+ * user space will raise a general protection exception. All the rest should
+ * have a dpl of 0 to disallow user programs to execute critical interrupt
+ * handlers and possibly crashing the system.
+ */
+ int dpl = (num == SYSCALL_INT) ? 3 : 0;
+
+ gate_desc(idt + num, selector(SEGM_CODE, 0), (uint32_t)handler, dpl, type);
+}
+
+void set_pic_mask(int pic, unsigned char mask)
+{
+ outp(pic > 0 ? PIC2_DATA : PIC1_DATA, mask);
+}
+
+unsigned char get_pic_mask(int pic)
+{
+ return inp(pic > 0 ? PIC2_DATA : PIC1_DATA);
+}
+
+void mask_irq(int irq)
+{
+ int port;
+ unsigned char mask;
+
+ if(irq < 8) {
+ port = PIC1_DATA;
+ } else {
+ port = PIC2_DATA;
+ irq -= 8;
+ }
+
+ mask = inp(port) | (1 << irq);
+ outp(port, mask);
+}
+
+void unmask_irq(int irq)
+{
+ int port;
+ unsigned char mask;
+
+ if(irq < 8) {
+ port = PIC1_DATA;
+ } else {
+ port = PIC2_DATA;
+ irq -= 8;
+ }
+
+ mask = inp(port) & ~(1 << irq);
+ outp(port, mask);
+}
+
+
+void end_of_irq(int irq)
+{
+ int intr_state = get_intr_flag();
+ disable_intr();
+
+ if(!eoi_pending) {
+ set_intr_flag(intr_state);
+ return;
+ }
+ eoi_pending = 0;
+
+ if(irq > 7) {
+ outp(PIC2_CMD, OCW2_EOI);
+ }
+ outp(PIC1_CMD, OCW2_EOI);
+
+ set_intr_flag(intr_state);
+}
+
+#ifdef ENABLE_GDB_STUB
+void exceptionHandler(int id, void (*func)())
+{
+ set_intr_entry(id, func);
+}
+#endif
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef INTR_H_
+#define INTR_H_
+
+#include <inttypes.h>
+#include "asmops.h"
+
+/* offset used to remap IRQ numbers (+32) */
+#define IRQ_OFFSET 32
+/* conversion macros between IRQ and interrupt numbers */
+#define IRQ_TO_INTR(x) ((x) + IRQ_OFFSET)
+#define INTR_TO_IRQ(x) ((x) - IRQ_OFFSET)
+/* checks whether a particular interrupt is an remapped IRQ */
+#define IS_IRQ(n) ((n) >= IRQ_OFFSET && (n) < IRQ_OFFSET + 16)
+
+/* general purpose registers as they are pushed by pusha */
+struct registers {
+ uint32_t edi, esi, ebp, esp;
+ uint32_t ebx, edx, ecx, eax;
+} __attribute__ ((packed));
+
+/* structure used to pass the interrupt stack frame from the
+ * entry points to the C dispatch function.
+ */
+struct intr_frame {
+ /* registers pushed by pusha in intr_entry_* */
+ struct registers regs;
+ /* data segment selectors */
+ /* XXX removed: not needed unless we do dpl3 transitions
+ uint32_t ds, es, fs, gs;
+ */
+ /* interrupt number and error code pushed in intr_entry_* */
+ uint32_t inum, err;
+ /* pushed by CPU during interrupt entry */
+ uint32_t eip, cs, eflags;
+ /* pushed by CPU during interrupt entry from user space */
+ /* XXX removed: again, not doing user space currently
+ uint32_t esp, ss;
+ */
+} __attribute__ ((packed));
+
+
+
+typedef void (*intr_func_t)(int);
+
+
+void init_intr(void);
+void cleanup_intr(void);
+
+struct intr_frame *get_intr_frame(void);
+
+/* install high level interrupt callback */
+void interrupt(int intr_num, intr_func_t func);
+
+/* install low-level interrupt vector in IDT
+ * must be able to handle EOI and return with iret
+ */
+void set_intr_entry(int num, void (*handler)(void));
+
+void prog_pic(int offs);
+void set_pic_mask(int pic, unsigned char mask);
+unsigned char get_pic_mask(int pic);
+void mask_irq(int irq);
+void unmask_irq(int irq);
+
+/* defined in intr_asm.S */
+int get_intr_flag(void);
+void set_intr_flag(int onoff);
+
+void intr_ret(struct intr_frame ifrm);
+
+void end_of_irq(int irq);
+
+#endif /* INTR_H_ */
--- /dev/null
+; pcboot - bootable PC demo/game kernel
+; Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY, without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+ section .data
+ align 4
+ dw 0
+idtr_desc:
+lim: dw 0
+addr: dd 0
+
+ section .text
+; void set_idt(uint32_t addr, uint16_t limit)
+ global set_idt
+set_idt:
+ mov eax, [esp + 4]
+ mov [addr], eax
+ mov ax, [esp + 8]
+ mov [lim], ax
+ lidt [idtr_desc]
+ ret
+
+; int get_intr_flag()
+ global get_intr_flag
+get_intr_flag:
+ pushf
+ pop eax
+ ; bit 9 of eflags is IF
+ shr eax, 9
+ and eax, 1
+ ret
+
+; void set_intr_flag(int onoff)
+ global set_intr_flag
+set_intr_flag:
+ cmp dword [esp + 4], 0
+ jz .off
+ sti
+ ret
+.off: cli
+ ret
+
+; interrupt entry with error code macro
+; this macro generates an interrupt entry point for the
+; exceptions which include error codes in the stack frame
+%macro ientry_err 2
+ global intr_entry_%2
+intr_entry_%2:
+ push dword %1
+ jmp intr_entry_common
+%endmacro
+
+; interrupt entry without error code macro
+; this macro generates an interrupt entry point for the interrupts
+; and exceptions which do not include error codes in the stack frame
+; it pushes a dummy error code (0), to make the stack frame identical
+%macro ientry_noerr 2
+ global intr_entry_%2
+intr_entry_%2:
+ push dword 0
+ push dword %1
+ jmp intr_entry_common
+%endmacro
+
+; common code used by all entry points. calls dispatch_intr()
+; defined in intr.c
+ extern dispatch_intr
+intr_entry_common:
+ ; save general purpose registers
+ pusha
+ call dispatch_intr
+intr_ret_local:
+ ; restore general purpose registers
+ popa
+ ; remove error code and intr num from stack
+ add esp, 8
+ iret
+
+; special case for the timer interrupt, to avoid all the overhead of
+; going through the C interrupt dispatcher 250 times each second
+; extern nticks
+; global intr_entry_fast_timer
+;intr_entry_fast_timer:
+; inc dword [nticks]
+; ; signal end of interrupt
+; push eax
+; mov al, 20h
+; out 20h, al
+; pop eax
+; iret
+
+; special case for IRQ 7 and IRQ 15, to catch spurious interrupts
+PIC1_CMD equ 0x20
+PIC2_CMD equ 0xa0
+OCW2_EOI equ 0x20
+OCW3_ISR equ 0x0b
+
+ global irq7_entry_check_spurious
+irq7_entry_check_spurious:
+ push eax
+ mov al, OCW3_ISR
+ out PIC1_CMD, al
+ in al, PIC1_CMD
+ and al, 80h
+ pop eax
+ jnz intr_entry_irq7
+ iret
+
+ global irq15_entry_check_spurious
+irq15_entry_check_spurious:
+ push eax
+ mov al, OCW3_ISR
+ out PIC2_CMD, al
+ in al, PIC2_CMD
+ and al, 80h
+ jnz .legit
+ ; it was spurious, send EOI to master PIC and iret
+ mov al, OCW2_EOI
+ out PIC1_CMD, al
+ pop eax
+ iret
+.legit: pop eax
+ jmp intr_entry_irq15
+
+
+; XXX not necessary for now, just leaving it in in case it's useful
+; down the road.
+;
+; intr_ret is called by context_switch to return from the kernel
+; to userspace. The argument is a properly formed intr_frame
+; structure with the saved context of the new task.
+;
+; First thing to do is remove the return address pointing back
+; to context_switch, which then leaves us with a proper interrupt
+; stack frame, so we can jump right in the middle of the regular
+; interrupt return code above.
+ global intr_ret
+intr_ret:
+ add esp, $4
+ jmp intr_ret_local
+
+; by including interrupts.h with ASM defined, the macros above
+; are expanded to generate all required interrupt entry points
+%define INTR_ENTRY_EC(n, name) ientry_err n, name
+%define INTR_ENTRY_NOEC(n, name) ientry_noerr n, name
+
+%include 'intrtab.h'
+
+; vi:set ts=8 sts=8 sw=8 ft=nasm:
--- /dev/null
+INTR_ENTRY_NOEC(0, div)
+INTR_ENTRY_NOEC(1, debug)
+INTR_ENTRY_NOEC(2, nmi)
+INTR_ENTRY_NOEC(3, bp)
+INTR_ENTRY_NOEC(4, overflow)
+INTR_ENTRY_NOEC(5, bound)
+INTR_ENTRY_NOEC(6, ill)
+INTR_ENTRY_NOEC(7, nodev)
+INTR_ENTRY_EC(8, dfault)
+INTR_ENTRY_NOEC(9, copseg)
+INTR_ENTRY_EC(10, tss)
+INTR_ENTRY_EC(11, segpres)
+INTR_ENTRY_EC(12, stack)
+INTR_ENTRY_EC(13, prot)
+INTR_ENTRY_EC(14, page)
+INTR_ENTRY_NOEC(15, reserved)
+INTR_ENTRY_NOEC(16, fpu)
+INTR_ENTRY_EC(17, align)
+INTR_ENTRY_NOEC(18, mce)
+INTR_ENTRY_NOEC(19, sse)
+INTR_ENTRY_NOEC(32, irq0)
+INTR_ENTRY_NOEC(33, irq1)
+INTR_ENTRY_NOEC(34, irq2)
+INTR_ENTRY_NOEC(35, irq3)
+INTR_ENTRY_NOEC(36, irq4)
+INTR_ENTRY_NOEC(37, irq5)
+INTR_ENTRY_NOEC(38, irq6)
+INTR_ENTRY_NOEC(39, irq7)
+INTR_ENTRY_NOEC(40, irq8)
+INTR_ENTRY_NOEC(41, irq9)
+INTR_ENTRY_NOEC(42, irq10)
+INTR_ENTRY_NOEC(43, irq11)
+INTR_ENTRY_NOEC(44, irq12)
+INTR_ENTRY_NOEC(45, irq13)
+INTR_ENTRY_NOEC(46, irq14)
+INTR_ENTRY_NOEC(47, irq15)
+INTR_ENTRY_NOEC(128, syscall)
+INTR_ENTRY_NOEC(255, default)
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef KBREGS_H_
+#define KBREGS_H_
+
+#define KB_IRQ 1
+#define PSAUX_IRQ 12
+
+#define KB_DATA_PORT 0x60
+#define KB_CMD_PORT 0x64
+#define KB_STATUS_PORT 0x64
+
+#define KB_ACK 0xfa
+#define KB_NACK 0xfe
+#define KB_TEST_PASSED 0x55
+#define KB_TEST_FAILED 0xfc
+
+#define KB_STAT_OUTBUF_FULL 0x01
+#define KB_STAT_INBUF_FULL 0x02
+#define KB_STAT_SYSFLAG 0x04
+#define KB_STAT_CMD 0x08
+#define KB_STAT_ACTIVE 0x10
+#define KB_STAT_AUX 0x20
+#define KB_STAT_TIMEOUT 0x40
+#define KB_STAT_PAR_ERROR 0x80
+
+#define KB_CMD_TEST 0xaa
+
+/* keyboard commands */
+#define KB_CMD_GET_CMDBYTE 0x20
+#define KB_CMD_SET_CMDBYTE 0x60
+#define KB_CMD_AUX_ENABLE 0xa8
+#define KB_CMD_READ_OUTPORT 0xd0
+#define KB_CMD_WRITE_OUTPORT 0xd1
+#define KB_CMD_PSAUX 0xd4
+#define KB_CMD_PULSE_RESET 0xfe
+
+/* controller command byte bits */
+#define KB_CCB_KB_INTREN 0x01
+#define KB_CCB_AUX_INTREN 0x02
+#define KB_CCB_SYSFLAG 0x04
+#define KB_CCB_KB_DISABLE 0x10
+#define KB_CCB_AUX_DISABLE 0x20
+#define KB_CCB_KB_XLAT 0x40
+
+/* psaux commands (use with d4 prefix) */
+#define AUX_CMD_STREAM_MODE 0xea
+#define AUX_CMD_READ_MOUSE 0xeb
+#define AUX_CMD_REMOTE_MODE 0xf0
+#define AUX_CMD_ENABLE 0xf4
+#define AUX_CMD_DEFAULTS 0xf6
+
+#define AUX_PKT0_LEFTBN 0x01
+#define AUX_PKT0_RIGHTBN 0x02
+#define AUX_PKT0_MIDDLEBN 0x04
+#define AUX_PKT0_ALWAYS1 0x08
+#define AUX_PKT0_XSIGN 0x10
+#define AUX_PKT0_YSIGN 0x20
+#define AUX_PKT0_XOVF 0x40
+#define AUX_PKT0_YOVF 0x80
+
+#define AUX_PKT0_OVF_BITS (AUX_PKT0_XOVF | AUX_PKT0_YOVF)
+#define AUX_PKT0_BUTTON_BITS \
+ (AUX_PKT0_LEFTBN | AUX_PKT0_RIGHTBN | AUX_PKT0_MIDDLEBN)
+
+#endif /* KBREGS_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef KBSCAN_H_
+#define KBSCAN_H_
+
+/* table with rough translations from set 1 scancodes to ASCII-ish */
+static int scantbl_set1[] = {
+ 0, KB_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', /* 0 - e */
+ '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', /* f - 1c */
+ KB_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', /* 1d - 29 */
+ KB_LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KB_RSHIFT, /* 2a - 36 */
+ KB_NUM_MUL, KB_LALT, ' ', KB_CAPSLK, KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, KB_F7, KB_F8, KB_F9, KB_F10, /* 37 - 44 */
+ KB_NUMLK, KB_SCRLK, KB_NUM_7, KB_NUM_8, KB_NUM_9, KB_NUM_MINUS, KB_NUM_4, KB_NUM_5, KB_NUM_6, KB_NUM_PLUS, /* 45 - 4e */
+ KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_0, KB_NUM_DOT, KB_SYSRQ, 0, 0, KB_F11, KB_F12, /* 4d - 58 */
+#if 0
+ 0, 0, 0, 0, 0, 0, 0, /* 59 - 5f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 70 - 7f */
+#endif
+};
+
+/* extended scancodes, after the 0xe0 prefix */
+#if 0
+static int scantbl_set1_ext[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\r', KB_RCTRL, 0, 0, /* 10 - 1f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2f */
+ 0, 0, 0, 0, 0, KB_NUM_MINUS, 0, KB_SYSRQ, KB_RALT, 0, 0, 0, 0, 0, 0, 0, /* 30 - 3f */
+ 0, 0, 0, 0, 0, 0, 0, KB_HOME, KB_UP, KB_PGUP, 0, KB_LEFT, 0, KB_RIGHT, 0, KB_END, /* 40 - 4f */
+ KB_DOWN, KB_PGDN, KB_INSERT, KB_DEL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7f */
+};
+#endif
+
+
+#endif /* KBSCAN_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#include "keyb.h"
+#include "intr.h"
+#include "asmops.h"
+#include "kbregs.h"
+#include "kbscan.h"
+#include "power.h"
+#include "panic.h"
+
+#define delay7us() \
+ do { \
+ iodelay(); iodelay(); iodelay(); iodelay(); \
+ iodelay(); iodelay(); iodelay(); \
+ } while(0)
+
+static void set_ccb(unsigned char ccb);
+static unsigned char get_ccb(void);
+
+static void kbintr();
+
+#define BUFSZ 64
+#define ADVANCE(x) ((x) = ((x) + 1) & (BUFSZ - 1))
+
+static int buffer[BUFSZ];
+static int buf_ridx, buf_widx;
+
+static unsigned int num_pressed;
+static unsigned char keystate[256];
+
+
+void kb_init(void)
+{
+ buf_ridx = buf_widx = 0;
+ num_pressed = 0;
+ memset(keystate, 0, sizeof keystate);
+
+ /* make sure set1 translation is enabled */
+ kb_set_translate(1);
+
+ interrupt(IRQ_TO_INTR(KB_IRQ), kbintr);
+ kb_intr_enable();
+ unmask_irq(KB_IRQ);
+}
+
+void kb_intr_enable(void)
+{
+ unsigned char ccb = get_ccb();
+ ccb |= KB_CCB_KB_INTREN;
+ set_ccb(ccb);
+}
+
+void kb_intr_disable(void)
+{
+ unsigned char ccb = get_ccb();
+ ccb &= ~KB_CCB_KB_INTREN;
+ set_ccb(ccb);
+}
+
+int kb_setmode(int mode)
+{
+ kb_send_data(0xf0);
+ if(!kb_wait_read() || kb_read_data() != KB_ACK) {
+ return -1;
+ }
+ kb_send_data(mode);
+ if(!kb_wait_read() || kb_read_data() != KB_ACK) {
+ return -1;
+ }
+ return 0;
+}
+
+int kb_getmode(void)
+{
+ int mode;
+
+ kb_send_data(0xf0);
+ if(!kb_wait_read() || kb_read_data() != KB_ACK) {
+ return -1;
+ }
+ kb_send_data(0);
+ if(!kb_wait_read() || kb_read_data() != KB_ACK) {
+ return -1;
+ }
+ mode = kb_read_data();
+
+ switch(mode) {
+ case 0x43: return 1;
+ case 0x41: return 2;
+ case 0x3f: return 3;
+ default:
+ break;
+ }
+ return mode;
+}
+
+void kb_set_translate(int xlat)
+{
+ unsigned char ccb = get_ccb();
+ if(xlat) {
+ ccb |= KB_CCB_KB_XLAT;
+ } else {
+ ccb &= ~KB_CCB_KB_XLAT;
+ }
+ set_ccb(ccb);
+}
+
+int kb_get_translate(void)
+{
+ return get_ccb() & KB_CCB_KB_XLAT;
+}
+
+int kb_isdown(int key)
+{
+ switch(key) {
+ case KB_ANY:
+ return num_pressed;
+
+ case KB_ALT:
+ return keystate[KB_LALT] + keystate[KB_RALT];
+
+ case KB_CTRL:
+ return keystate[KB_LCTRL] + keystate[KB_RCTRL];
+ }
+ return keystate[key];
+}
+
+void kb_wait(void)
+{
+ int key;
+ while((key = kb_getkey()) == -1) {
+ /* put the processor to sleep while waiting for keypresses, but first
+ * make sure interrupts are enabled, or we'll sleep forever
+ */
+ enable_intr();
+ halt_cpu();
+ }
+ kb_putback(key);
+}
+
+int kb_getkey(void)
+{
+ int res;
+
+ if(buf_ridx == buf_widx) {
+ return -1;
+ }
+ res = buffer[buf_ridx];
+ ADVANCE(buf_ridx);
+ return res;
+}
+
+void kb_putback(int key)
+{
+ /* go back a place */
+ if(--buf_ridx < 0) {
+ buf_ridx += BUFSZ;
+ }
+
+ /* if the write end hasn't caught up with us, go back one place
+ * and put it there, otherwise just overwrite the oldest key which
+ * is right where we were.
+ */
+ if(buf_ridx == buf_widx) {
+ ADVANCE(buf_ridx);
+ }
+
+ buffer[buf_ridx] = key;
+}
+
+int kb_wait_write(void)
+{
+ int i;
+ for(i=0; i<32768; i++) {
+ if(!(inp(KB_STATUS_PORT) & KB_STAT_INBUF_FULL)) {
+ return 1;
+ }
+ iodelay();
+ }
+ /*printf("kb_wait_write timeout\n");*/
+ return 0;
+}
+
+int kb_wait_read(void)
+{
+ int i;
+ for(i=0; i<32768; i++) {
+ if((inp(KB_STATUS_PORT) & KB_STAT_OUTBUF_FULL)) {
+ return 1;
+ }
+ iodelay();
+ }
+ /*printf("kb_wait_read timeout\n");*/
+ return 0;
+}
+
+void kb_send_cmd(unsigned char cmd)
+{
+ kb_wait_write();
+ outp(KB_CMD_PORT, cmd);
+}
+
+void kb_send_data(unsigned char data)
+{
+ kb_wait_write();
+ outp(KB_DATA_PORT, data);
+}
+
+unsigned char kb_read_data(void)
+{
+ kb_wait_read();
+ delay7us();
+ return inp(KB_DATA_PORT);
+}
+
+static void set_ccb(unsigned char ccb)
+{
+ kb_send_cmd(KB_CMD_SET_CMDBYTE);
+ kb_send_data(ccb);
+
+ if(kb_wait_read()) {
+ kb_read_data();
+ }
+}
+
+static unsigned char get_ccb(void)
+{
+ kb_send_cmd(KB_CMD_GET_CMDBYTE);
+ return kb_read_data();
+}
+
+static void kbintr()
+{
+ unsigned char code;
+ int key, press;
+ static int ext = 0;
+
+ code = inp(KB_DATA_PORT);
+
+ if(code == 0xe0) {
+ ext = 1;
+ return;
+ }
+
+ if(code & 0x80) {
+ press = 0;
+ code &= 0x7f;
+
+ if(num_pressed > 0) {
+ num_pressed--;
+ }
+ } else {
+ press = 1;
+
+ num_pressed++;
+ }
+
+ if(!ext) {
+ key = code < 0x59 ? scantbl_set1[code] : 0;
+ } else {
+ switch(code) {
+ case 0x1c: key = '\r'; break;
+ case 0x1d: key = KB_RCTRL; break;
+ //case 0x35: key = KB_NUM_MINUS; break;
+ //case 0x37: key = KB_SYSRQ; break;
+ case 0x38: key = KB_RALT; break;
+ case 0x47: key = KB_HOME; break;
+ case 0x48: key = KB_UP; break;
+ case 0x49: key = KB_PGUP; break;
+ case 0x4b: key = KB_LEFT; break;
+ case 0x4d: key = KB_RIGHT; break;
+ case 0x4f: key = KB_END; break;
+ case 0x50: key = KB_DOWN; break;
+ case 0x51: key = KB_PGDN; break;
+ case 0x52: key = KB_INSERT; break;
+ case 0x53: key = KB_DEL; break;
+ default:
+ key = 0;
+ }
+ ext = 0;
+ }
+
+ if(press) {
+ if(key == KB_DEL && (keystate[KB_LALT] || keystate[KB_RALT]) && (keystate[KB_LCTRL] || keystate[KB_RCTRL])) {
+ reboot();
+ }
+ /* append to buffer */
+ buffer[buf_widx] = key;
+ ADVANCE(buf_widx);
+ /* if the write end overtook the read end, advance the read end
+ * too, to discard the oldest keypress from the buffer
+ */
+ if(buf_widx == buf_ridx) {
+ ADVANCE(buf_ridx);
+ }
+ }
+
+ /* and update keystate table */
+ keystate[key] = press;
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef KEYB_H_
+#define KEYB_H_
+
+#define KB_ANY (-1)
+#define KB_ALT (-2)
+#define KB_CTRL (-3)
+#define KB_SHIFT (-4)
+
+/* special keys */
+enum {
+ KB_ESC = 27,
+ KB_BACKSP = 127,
+
+ KB_INSERT, KB_DEL, KB_HOME, KB_END, KB_PGUP, KB_PGDN,
+ KB_LEFT, KB_RIGHT, KB_UP, KB_DOWN,
+ KB_NUM_DOT, KB_NUM_ENTER, KB_NUM_PLUS, KB_NUM_MINUS, KB_NUM_MUL, KB_NUM_DIV,
+ KB_NUM_0, KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_4,
+ KB_NUM_5, KB_NUM_6, KB_NUM_7, KB_NUM_8, KB_NUM_9,
+
+ KB_LALT, KB_RALT,
+ KB_LCTRL, KB_RCTRL,
+ KB_LSHIFT, KB_RSHIFT,
+ KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6,
+ KB_F7, KB_F8, KB_F9, KB_F10, KB_F11, KB_F12,
+ KB_CAPSLK, KB_NUMLK, KB_SCRLK, KB_SYSRQ
+};
+
+void kb_init(void);
+
+void kb_intr_enable(void);
+void kb_intr_disable(void);
+
+int kb_setmode(int mode);
+int kb_getmode(void);
+
+void kb_set_translate(int xlat);
+int kb_get_translate(void);
+
+/* Boolean predicate for testing the current state of a particular key.
+ * You may also pass KB_ANY to test if any key is held down.
+ */
+int kb_isdown(int key);
+
+/* waits for any keypress */
+void kb_wait(void);
+
+/* removes and returns a single key from the input buffer. */
+int kb_getkey(void);
+
+void kb_putback(int key);
+
+/* returns 1 if the keyboard controller is ready to read/write
+ * returns 0 if the wait times out
+ */
+int kb_wait_write(void);
+int kb_wait_read(void);
+
+void kb_send_cmd(unsigned char cmd);
+void kb_send_data(unsigned char data);
+unsigned char kb_read_data(void);
+
+#endif /* KEYB_H_ */
--- /dev/null
+#include <stdio.h>
+#include "segm.h"
+#include "intr.h"
+#include "mem.h"
+#include "contty.h"
+#include "keyb.h"
+#include "psaux.h"
+#include "timer.h"
+
+int main(void)
+{
+ init_segm();
+ init_intr();
+
+ con_init();
+ kb_init();
+ /*init_psaux();*/
+
+ init_mem();
+
+ init_timer();
+
+ enable_intr();
+
+ printf("\nhello from C\n");
+
+ cleanup_intr(); /* also disables interrupts */
+ cleanup_timer();
+ return 0;
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+#include "panic.h"
+#include "mem.h"
+#include "intr.h"
+
+#define FREE 0
+#define USED 1
+
+#define BM_IDX(pg) ((pg) / 32)
+#define BM_BIT(pg) ((pg) & 0x1f)
+
+#define IS_FREE(pg) ((bitmap[BM_IDX(pg)] & (1 << BM_BIT(pg))) == 0)
+
+#define MEM_START ((uint32_t)&_mem_start)
+
+
+struct mem_range {
+ uint32_t start;
+ uint32_t size;
+};
+
+void move_stack(uint32_t newaddr); /* defined in startup.s */
+
+static void mark_page(int pg, int used);
+static void add_memory(uint32_t start, size_t size);
+
+#define MAX_MAP_SIZE 16
+extern struct mem_range boot_mem_map[MAX_MAP_SIZE];
+extern int boot_mem_map_size;
+
+/* linker supplied symbol, points to the end of the kernel image */
+extern uint32_t _mem_start;
+
+
+/* A bitmap is used to track which physical memory pages are used, and which
+ * are available for allocation by alloc_ppage.
+ *
+ * last_alloc_idx keeps track of the last 32bit element in the bitmap array
+ * where a free page was found. It's guaranteed that all the elements before
+ * this have no free pages, but it doesn't imply that there will be another
+ * free page there. So it's used as a starting point for the search.
+ */
+static uint32_t *bitmap;
+static int bmsize, last_alloc_idx;
+
+
+
+void init_mem(void)
+{
+ int i, pg, max_used_pg, end_pg = 0;
+ uint32_t used_end, start, end, sz, total = 0, rem;
+ const char *suffix[] = {"bytes", "KB", "MB", "GB"};
+
+ last_alloc_idx = 0;
+
+ /* the allocation bitmap starts right at the end of the kernel image */
+ bitmap = (uint32_t*)&_mem_start;
+
+ /* start by marking all posible pages (2**20) as used. We do not "reserve"
+ * all this space. Pages beyond the end of the useful bitmap area
+ * ((char*)bitmap + bmsize), which will be determined after we traverse the
+ * memory map, are going to be marked as available for allocation.
+ */
+ memset(bitmap, 0xff, 1024 * 1024 / 8);
+
+
+ if(boot_mem_map_size <= 0 || boot_mem_map_size > MAX_MAP_SIZE) {
+ panic("invalid memory map size reported by the boot loader: %d\n", boot_mem_map_size);
+ }
+
+ printf("Memory map:\n");
+ for(i=0; i<boot_mem_map_size; i++) {
+ printf(" start: %08x - size: %08x\n", (unsigned int)boot_mem_map[i].start,
+ (unsigned int)boot_mem_map[i].size);
+
+ start = boot_mem_map[i].start;
+ sz = boot_mem_map[i].size & 0xfffffffc;
+ end = start + sz;
+ if(end < sz) {
+ end = 0xfffffffc;
+ sz = end - start;
+ }
+
+ /* ignore any memory ranges which end before the end of the kernel image */
+ if(end < MEM_START) continue;
+ if(start < MEM_START) {
+ start = MEM_START;
+ }
+
+ add_memory(start, sz);
+ total += sz;
+
+ pg = ADDR_TO_PAGE(end);
+ if(end_pg < pg) {
+ end_pg = pg;
+ }
+ }
+
+ i = 0;
+ while(total > 1024) {
+ rem = total & 0x3ff;
+ total >>= 10;
+ ++i;
+ }
+ printf("Total usable RAM: %u.%u %s\n", total, 100 * rem / 1024, suffix[i]);
+
+ /* size of the useful part of the bitmap in bytes padded to 4-byte
+ * boundaries to allow 32bit at a time operations.
+ */
+ bmsize = (end_pg / 32) * 4;
+
+ /* mark all pages occupied by the bitmap as used */
+ used_end = (uint32_t)bitmap + bmsize - 1;
+
+ max_used_pg = ADDR_TO_PAGE(used_end);
+ printf("marking pages up to %x (page: %d) as used\n", used_end, max_used_pg);
+ for(i=0; i<=max_used_pg; i++) {
+ mark_page(i, USED);
+ }
+
+#ifdef MOVE_STACK_RAMTOP
+ /* allocate space for the stack at the top of RAM and move it there */
+ if((pg = alloc_ppages(STACK_PAGES, MEM_STACK)) != -1) {
+ printf("moving stack-top to: %x (%d pages)\n", PAGE_TO_ADDR(end_pg) - 4, STACK_PAGES);
+ move_stack(PAGE_TO_ADDR(pg + STACK_PAGES) - 4);
+ }
+#endif
+}
+
+int alloc_ppage(int area)
+{
+ return alloc_ppages(1, area);
+}
+
+/* free_ppage marks the physical page, free in the allocation bitmap.
+ *
+ * CAUTION: no checks are done that this page should actually be freed or not.
+ * If you call free_ppage with the address of some part of memory that was
+ * originally reserved due to it being in a memory hole or part of the kernel
+ * image or whatever, it will be subsequently allocatable by alloc_ppage.
+ */
+void free_ppage(int pg)
+{
+ int bmidx = BM_IDX(pg);
+
+ int intr_state = get_intr_flag();
+ disable_intr();
+
+ if(IS_FREE(pg)) {
+ panic("free_ppage(%d): I thought that was already free!\n", pg);
+ }
+
+ mark_page(pg, FREE);
+ if(bmidx < last_alloc_idx) {
+ last_alloc_idx = bmidx;
+ }
+
+ set_intr_flag(intr_state);
+}
+
+
+int alloc_ppages(int count, int area)
+{
+ int i, dir, pg, idx, max, intr_state, found_free = 0;
+
+ intr_state = get_intr_flag();
+ disable_intr();
+
+ if(area == MEM_STACK) {
+ idx = (bmsize - 1) / 4;
+ max = -1;
+ dir = -1;
+ } else {
+ idx = last_alloc_idx;
+ max = bmsize / 4;
+ dir = 1;
+ }
+
+ while(idx != max) {
+ /* if at least one bit is 0 then we have at least
+ * one free page. find it and try to allocate a range starting from there
+ */
+ if(bitmap[idx] != 0xffffffff) {
+ pg = idx * 32;
+ if(dir < 0) pg += 31;
+
+ for(i=0; i<32; i++) {
+ if(IS_FREE(pg)) {
+ if(!found_free && dir > 0) {
+ last_alloc_idx = idx;
+ found_free = 1;
+ }
+
+ if(alloc_ppage_range(pg, count) != -1) {
+ set_intr_flag(intr_state);
+ return pg;
+ }
+ }
+ pg += dir;
+ }
+ }
+ idx += dir;
+ }
+
+ set_intr_flag(intr_state);
+ return -1;
+}
+
+void free_ppages(int pg0, int count)
+{
+ int i;
+
+ for(i=0; i<count; i++) {
+ free_ppage(pg0++);
+ }
+}
+
+int alloc_ppage_range(int start, int size)
+{
+ int i, pg = start;
+ int intr_state;
+
+ intr_state = get_intr_flag();
+ disable_intr();
+
+ /* first validate that no page in the requested range is allocated */
+ for(i=0; i<size; i++) {
+ if(!IS_FREE(pg)) {
+ return -1;
+ }
+ ++pg;
+ }
+
+ /* all is well, mark them as used */
+ pg = start;
+ for(i=0; i<size; i++) {
+ mark_page(pg++, USED);
+ }
+
+ set_intr_flag(intr_state);
+ return 0;
+}
+
+int free_ppage_range(int start, int size)
+{
+ int i, pg = start;
+
+ for(i=0; i<size; i++) {
+ free_ppage(pg++);
+ }
+ return 0;
+}
+
+/* adds a range of physical memory to the available pool. used during init_mem
+ * when traversing the memory map.
+ */
+static void add_memory(uint32_t start, size_t sz)
+{
+ int i, szpg, pg;
+
+ szpg = ADDR_TO_PAGE(sz + 4095);
+ pg = ADDR_TO_PAGE(start);
+
+ for(i=0; i<szpg; i++) {
+ mark_page(pg++, FREE);
+ }
+}
+
+/* maps a page as used or free in the allocation bitmap */
+static void mark_page(int pg, int used)
+{
+ int idx = BM_IDX(pg);
+ int bit = BM_BIT(pg);
+
+ if(used) {
+ bitmap[idx] |= 1 << bit;
+ } else {
+ bitmap[idx] &= ~(1 << bit);
+ }
+}
+
+void print_page_bitmap(void)
+{
+ int i;
+
+ for(i=0; i<bmsize/4; i++) {
+ if((i & 3) == 0) {
+ uint32_t pg = i * 32;
+ uint32_t addr = PAGE_TO_ADDR(pg);
+ printf("\n%5d [%08x]:", (int)pg, (unsigned long)addr);
+ }
+ printf(" %08x", bitmap[i]);
+ }
+ printf("\n");
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef MEM_H_
+#define MEM_H_
+
+#define ADDR_TO_PAGE(x) ((uint32_t)(x) >> 12)
+#define PAGE_TO_ADDR(x) ((uint32_t)(x) << 12)
+#define PAGE_TO_PTR(x) ((void*)PAGE_TO_ADDR(x))
+
+#define BYTES_TO_PAGES(x) (((uint32_t)(x) + 4095) >> 12)
+
+/* for alloc_ppage/alloc_ppages */
+enum {
+ MEM_HEAP = 0, /* start searching from the bottom */
+ MEM_STACK = 1 /* start searching from the top */
+};
+
+void init_mem(void);
+
+int alloc_ppage(int area);
+void free_ppage(int pg);
+
+/* allocate a number of consecutive pages */
+int alloc_ppages(int count, int area);
+void free_ppages(int pg0, int count);
+
+/* allocate a specific range of pages.
+ * Fails (and returns -1) if any page in the requested range is not free.
+ */
+int alloc_ppage_range(int start, int size);
+int free_ppage_range(int start, int size);
+
+#endif /* MEM_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "intr.h"
+#include "asmops.h"
+
+struct all_registers {
+ uint32_t eax, ebx, ecx, edx;
+ uint32_t esp, ebp, esi, edi;
+ uint32_t eflags;
+ uint32_t cs, ss, ds, es, fs, gs;
+ uint32_t cr0, cr1, cr2, cr3;
+};
+
+/* defined in regs.S */
+void get_regs(struct all_registers *regs);
+
+void panic(const char *fmt, ...)
+{
+ va_list ap;
+ struct all_registers regs;
+ uint32_t eip;
+
+ disable_intr();
+
+ memset(®s, 0, sizeof regs);
+ get_regs(®s);
+
+ CALLER_EIP(eip);
+
+ printf("~~~~~ pcboot panic ~~~~~\n");
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+
+ printf("\nRegisters:\n");
+ printf("eax: %x, ebx: %x, ecx: %x, edx: %x\n", regs.eax, regs.ebx, regs.ecx, regs.edx);
+ printf("esp: %x, ebp: %x, esi: %x, edi: %x\n", regs.esp, regs.ebp, regs.esi, regs.edi);
+ printf("eip: %x, eflags: %x\n", eip, regs.eflags);
+ printf("cr0: %x, cr1: %x, cr2: %x, cr3: %x\n", regs.cr0, regs.cr1, regs.cr2, regs.cr3);
+ printf("cs: %x (%d|%d)\n", regs.cs, regs.cs >> 3, regs.cs & 3);
+ printf("ss: %x (%d|%d)\n", regs.ss, regs.ss >> 3, regs.ss & 3);
+ printf("ds: %x (%d|%d)\n", regs.ds, regs.ds >> 3, regs.ds & 3);
+ printf("es: %x (%d|%d)\n", regs.es, regs.es >> 3, regs.es & 3);
+ printf("fs: %x (%d|%d)\n", regs.fs, regs.fs >> 3, regs.fs & 3);
+ printf("gs: %x (%d|%d)\n", regs.gs, regs.gs >> 3, regs.gs & 3);
+
+ for(;;) halt_cpu();
+}
+
+void backtrace(void)
+{
+ int lvl = 0;
+ uint32_t *frmptr;
+ int ien = get_intr_flag();
+ disable_intr();
+ get_ebp(frmptr);
+ set_intr_flag(ien);
+
+ while(frmptr) {
+ printf("%d: %p\n", lvl++, (void*)frmptr[1]);
+ frmptr = (uint32_t*)*frmptr;
+ }
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef PANIC_H_
+#define PANIC_H_
+
+void panic(const char *fmt, ...) __attribute__((noreturn));
+void backtrace(void);
+
+#endif /* PANIC_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include "power.h"
+#include "keyb.h"
+#include "kbregs.h"
+#include "timer.h"
+#include "panic.h"
+#include "asmops.h"
+
+/* defined in intr_asm.S */
+void set_idt(uint32_t addr, uint16_t limit);
+
+void reboot(void)
+{
+ int i;
+
+ printf("reboot: keyboard controller\n");
+ kb_send_cmd(KB_CMD_PULSE_RESET);
+
+ for(i=0; i<32768; i++) {
+ iodelay();
+ }
+
+ printf("reboot: triple-fault\n");
+ set_idt(0, 0);
+ asm volatile("int $3");
+
+ panic("can't reboot!");
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef POWER_H_
+#define POWER_H_
+
+void reboot(void);
+
+#endif /* POWER_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include "psaux.h"
+#include "intr.h"
+#include "asmops.h"
+#include "keyb.h"
+#include "kbregs.h"
+
+static void init_mouse(void);
+static void proc_mouse_data(unsigned char *data);
+static void psaux_intr();
+
+static int mx, my;
+static unsigned int bnstate;
+static int present;
+static int bounds[4];
+static int intr_mode;
+
+void init_psaux(void)
+{
+ interrupt(IRQ_TO_INTR(12), psaux_intr);
+ unmask_irq(12);
+
+ init_mouse();
+ set_mouse_bounds(0, 0, 319, 199);
+}
+
+static void init_mouse(void)
+{
+ unsigned char val;
+
+ kb_send_cmd(KB_CMD_AUX_ENABLE);
+ if(kb_wait_read()) {
+ val = kb_read_data();
+ printf("aux enable: %02x\n", (unsigned int)val);
+ }
+
+ /*
+ kb_send_cmd(KB_CMD_PSAUX);
+ kb_send_data(AUX_CMD_REMOTE_MODE);
+ val = kb_read_data();
+ */
+
+ kb_send_cmd(KB_CMD_GET_CMDBYTE);
+ val = kb_read_data();
+ val &= ~KB_CCB_AUX_DISABLE;
+ val |= KB_CCB_AUX_INTREN;
+ kb_send_cmd(KB_CMD_SET_CMDBYTE);
+ kb_send_data(val);
+
+ if(kb_wait_read()) {
+ val = kb_read_data();
+ printf("set cmdbyte: %02x\n", (unsigned int)val);
+ }
+ intr_mode = 1;
+
+ kb_send_cmd(KB_CMD_PSAUX);
+ kb_send_data(AUX_CMD_DEFAULTS);
+ val = kb_read_data();
+
+ kb_send_cmd(KB_CMD_PSAUX);
+ kb_send_data(AUX_CMD_ENABLE);
+ val = kb_read_data();
+
+ present = (val == KB_ACK) ? 1 : 0;
+ printf("init_mouse: %spresent\n", present ? "" : "not ");
+}
+
+int have_mouse(void)
+{
+ return present;
+}
+
+void set_mouse_bounds(int x0, int y0, int x1, int y1)
+{
+ bounds[0] = x0;
+ bounds[1] = y0;
+ bounds[2] = x1;
+ bounds[3] = y1;
+}
+
+unsigned int mouse_state(int *xp, int *yp)
+{
+ if(!intr_mode) {
+ poll_mouse();
+ }
+
+ *xp = mx;
+ *yp = my;
+ return bnstate;
+}
+
+/* TODO: poll_mouse doesn't work (enable remote mode and disable interrupt on init to test) */
+#define STAT_AUX_PENDING (KB_STAT_OUTBUF_FULL | KB_STAT_AUX)
+static inline int aux_pending(void)
+{
+ return (inp(KB_STATUS_PORT) & STAT_AUX_PENDING) == STAT_AUX_PENDING ? 1 : 0;
+}
+
+void poll_mouse(void)
+{
+ static int poll_state;
+ static unsigned char pkt[3];
+ unsigned char rd;
+
+ ser_printf("poll_mouse(%d)\n", poll_state);
+
+ switch(poll_state) {
+ case 0: /* send read mouse command */
+ kb_send_cmd(KB_CMD_PSAUX);
+ kb_send_data(AUX_CMD_READ_MOUSE);
+ ++poll_state;
+ break;
+
+ case 1: /* wait for ACK */
+ if(kb_wait_read()) {// && aux_pending()) {
+ if((rd = kb_read_data()) == KB_ACK) {
+ ++poll_state;
+ } else {
+ ser_printf("poll_mouse state 1: expected ack: %02x\n", (unsigned int)rd);
+ }
+ }
+ break;
+
+ case 2: /* read packet data */
+ case 3:
+ case 4:
+ if(kb_wait_read() && aux_pending()) {
+ int i = poll_state++ - 2;
+ pkt[i] = kb_read_data();
+ }
+ if(poll_state == 5) {
+ ser_printf("proc_mouse_data(%02x %02x %02x)\n", (unsigned int)pkt[0],
+ (unsigned int)pkt[1], (unsigned int)pkt[2]);
+ proc_mouse_data(pkt);
+ poll_state = 0;
+ }
+ break;
+
+ default:
+ ser_printf("poll_mouse reached state: %d\n", poll_state);
+ }
+}
+
+static void proc_mouse_data(unsigned char *data)
+{
+ int dx, dy;
+
+ if(data[0] & AUX_PKT0_OVF_BITS) {
+ /* consensus seems to be that if overflow bits are set, something is
+ * fucked, and it's best to re-initialize the mouse
+ */
+ /*init_mouse();*/
+ } else {
+ bnstate = data[0] & AUX_PKT0_BUTTON_BITS;
+ dx = data[1];
+ dy = data[2];
+
+ if(data[0] & AUX_PKT0_XSIGN) {
+ dx |= 0xffffff00;
+ }
+ if(data[0] & AUX_PKT0_YSIGN) {
+ dy |= 0xffffff00;
+ }
+
+ mx += dx;
+ my -= dy;
+
+ if(mx < bounds[0]) mx = bounds[0];
+ if(mx > bounds[2]) mx = bounds[2];
+ if(my < bounds[1]) my = bounds[1];
+ if(my > bounds[3]) my = bounds[3];
+ }
+}
+
+static void psaux_intr()
+{
+ static unsigned char data[3];
+ static int idx;
+
+ if(!(inp(KB_STATUS_PORT) & KB_STAT_AUX)) {
+ /* no mouse data pending, ignore interrupt */
+ return;
+ }
+
+ data[idx] = kb_read_data();
+ if(++idx >= 3) {
+ idx = 0;
+
+ proc_mouse_data(data);
+ }
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef PSAUX_H_
+#define PSAUX_H_
+
+#define MOUSE_LBN_BIT 1
+#define MOUSE_RBN_BIT 2
+#define MOUSE_MBN_BIT 4
+
+void init_psaux(void);
+
+int have_mouse(void);
+
+void set_mouse_bounds(int x0, int y0, int x1, int y1);
+unsigned int mouse_state(int *xp, int *yp);
+
+void poll_mouse(void);
+
+#endif /* PSAUX_H_ */
--- /dev/null
+ section .text
+ align 4
+
+ global get_regs
+get_regs:
+ push ebp
+ mov ebp, esp
+
+ push edx
+ mov edx, [ebp + 8]
+
+ mov [edx], eax
+ mov [edx + 4], ebx
+ mov [edx + 8], ecx
+
+ ; juggle edx
+ mov eax, edx
+ pop edx
+ mov [eax + 12], edx
+ push edx
+ mov edx, eax
+
+ ; those two are pointless in a function
+ mov [edx + 16], esp
+ mov [edx + 20], ebp
+
+ mov [edx + 24], esi
+ mov [edx + 28], edx
+
+ pushf
+ pop eax
+ mov [edx + 32], eax
+
+ mov [edx + 36], cs
+ mov [edx + 40], ss
+ mov [edx + 44], ds
+ mov [edx + 48], es
+ mov [edx + 52], fs
+ mov [edx + 56], gs
+
+ push ebx
+ mov ebx, cr0
+ mov [edx + 60], ebx
+ ;mov ebx, cr1
+ ;mov [edx + 64], ebx
+ mov ebx, cr2
+ mov [edx + 68], ebx
+ mov ebx, cr3
+ mov [edx + 72], ebx
+ pop ebx
+
+ pop edx
+ pop ebp
+ ret
+
+
+; vi:set ts=8 sts=8 sw=8 ft=nasm:
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#include "segm.h"
+#include "desc.h"
+#include "tss.h"
+
+/* bits for the 3rd 16bt part of the descriptor */
+#define BIT_ACCESSED (1 << 8)
+#define BIT_WR (1 << 9)
+#define BIT_RD (1 << 9)
+#define BIT_EXP_DOWN (1 << 10)
+#define BIT_CONFORMING (1 << 10)
+#define BIT_CODE (1 << 11)
+#define BIT_NOSYS (1 << 12)
+#define BIT_PRESENT (1 << 15)
+/* TSS busy bit */
+#define BIT_BUSY (1 << 9)
+
+/* bits for the last 16bit part of the descriptor */
+#define BIT_BIG (1 << 6)
+#define BIT_DEFAULT (1 << 6)
+#define BIT_GRAN (1 << 7)
+
+enum {TYPE_DATA, TYPE_CODE};
+
+/* we need the following bit pattern at the 8th bit excluding the busy bit: 1001 */
+#define TSS_TYPE_BITS (9 << 8)
+
+static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type);
+static void segm_desc16(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type);
+/*static void task_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl);*/
+
+/* these functions are implemented in segm-asm.S */
+void setup_selectors(uint16_t code, uint16_t data);
+void set_gdt(uint32_t addr, uint16_t limit);
+void set_task_reg(uint16_t tss_selector);
+
+/*
+static void dbg_print_gdt(void);
+static void print_desc(desc_t *desc);
+*/
+
+/* our global descriptor table */
+static desc_t gdt[NUM_SEGMENTS] __attribute__((aligned(8)));
+uint16_t orig_seg;
+
+void init_segm(void)
+{
+ asm volatile (
+ "mov %%fs, %0\n\t"
+ : "=a"(orig_seg));
+
+ memset(gdt, 0, sizeof gdt);
+ segm_desc(gdt + SEGM_CODE, 0, 0xffffffff, 0, TYPE_CODE);
+ segm_desc(gdt + SEGM_DATA, 0, 0xffffffff, 0, TYPE_DATA);
+ segm_desc(gdt + SEGM_XCODE, orig_seg << 4, 0xffffffff, 0, TYPE_CODE);
+ segm_desc(gdt + SEGM_XDATA, orig_seg << 4, 0xffffffff, 0, TYPE_DATA);
+ segm_desc16(gdt + SEGM_CODE16, orig_seg << 4, 0xffff, 0, TYPE_CODE);
+
+ set_gdt((uint32_t)gdt, sizeof gdt - 1);
+
+ setup_selectors(selector(SEGM_CODE, 0), selector(SEGM_DATA, 0));
+}
+
+/* constructs a GDT selector based on index and priviledge level */
+uint16_t selector(int idx, int rpl)
+{
+ return (idx << 3) | (rpl & 3);
+}
+
+/*
+void set_tss(uint32_t addr)
+{
+ task_desc(gdt + SEGM_TASK, addr, sizeof(struct task_state) - 1, 3);
+ set_task_reg(selector(SEGM_TASK, 0));
+}
+*/
+
+static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type)
+{
+ desc->d[0] = limit & 0xffff; /* low order 16bits of limit */
+ desc->d[1] = base & 0xffff; /* low order 16bits of base */
+
+ /* third 16bit part contains the last 8 bits of base, the 2 priviledge
+ * level bits starting on bit 13, present flag on bit 15, and type bits
+ * starting from bit 8
+ */
+ desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT |
+ BIT_NOSYS | (type == TYPE_DATA ? BIT_WR : (BIT_RD | BIT_CODE));
+
+ /* last 16bit part contains the last nibble of limit, the last byte of
+ * base, and the granularity and deafult/big flags in bits 23 and 22 resp.
+ */
+ desc->d[3] = ((limit >> 16) & 0xf) | ((base >> 16) & 0xff00) | BIT_GRAN | BIT_BIG;
+}
+
+static void segm_desc16(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type)
+{
+ desc->d[0] = limit & 0xffff; /* low order 16bits of limit */
+ desc->d[1] = base & 0xffff; /* low order 16bits of base */
+
+ /* third 16bit part contains the last 8 bits of base, the 2 priviledge
+ * level bits starting on bit 13, present flag on bit 15, and type bits
+ * starting from bit 8
+ */
+ desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT |
+ BIT_NOSYS | (type == TYPE_DATA ? BIT_WR : (BIT_RD | BIT_CODE));
+
+ /* 16bit descriptors have the upper word 0 */
+ desc->d[3] = 0;
+}
+
+#if 0
+static void task_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl)
+{
+ desc->d[0] = limit & 0xffff;
+ desc->d[1] = base & 0xffff;
+
+ desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT |
+ TSS_TYPE_BITS; /* XXX busy ? */
+ desc->d[3] = ((limit >> 16) & 0xf) | ((base >> 16) & 0xff00) | BIT_GRAN;
+}
+
+static void dbg_print_gdt(void)
+{
+ int i;
+
+ printf("Global Descriptor Table\n");
+ printf("-----------------------\n");
+
+ for(i=0; i<NUM_SEGMENTS; i++) {
+ printf("%d: ", i);
+ print_desc(gdt + i);
+ }
+}
+
+static void print_desc(desc_t *desc)
+{
+ uint32_t base, limit;
+ int dpl, g;
+
+ if((desc->d[0] | desc->d[1] | desc->d[2] | desc->d[3]) == 0) {
+ printf("null\n");
+ }
+
+ base = (uint32_t)desc->d[1] | ((uint32_t)(desc->d[2] & 0xff) << 16) | ((uint32_t)(desc->d[3] >> 8) << 24);
+ limit = (uint32_t)desc->d[0] | ((uint32_t)(desc->d[3] & 0xf) << 16);
+ dpl = (desc->d[2] >> 13) & 3;
+ g = (desc->d[3] >> 7) & 1;
+
+ if(g) limit = ((limit + 1) << 12) - 1;
+
+ printf("base:%x lim:%x dpl:%d type:%s %dbit\n", base, limit, dpl,
+ desc->d[2] & BIT_CODE ? "code" : "data", desc->d[3] & BIT_BIG ? 32 : 16);
+}
+#endif
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef SEGM_H_
+#define SEGM_H_
+
+#include <inttypes.h>
+
+enum {
+ SEGM_NULL,
+ SEGM_CODE,
+ SEGM_DATA,
+ SEGM_XCODE, /* transition 32bit code with base matching the RM segment */
+ SEGM_XDATA, /* same for data */
+ SEGM_CODE16,
+
+ NUM_SEGMENTS
+};
+
+void init_segm(void);
+
+uint16_t selector(int idx, int rpl);
+
+void set_tss(uint32_t addr);
+
+
+#endif /* SEGM_H_ */
--- /dev/null
+; pcboot - bootable PC demo/game kernel
+; Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+;
+; This program is free software: you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, either version 3 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY, without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+ section .data
+ align 4
+; memory reserved for setup_selectors
+off: dd 0
+segm: dw 0
+; memory reserved for set_gdt
+lim: dw 0
+addr: dd 0
+
+ section .text
+; setup_selectors(uint16_t code, uint16_t data)
+; loads the requested selectors to all the selector registers
+ global setup_selectors
+setup_selectors:
+ ; set data selectors directly
+ mov eax, [esp + 8]
+ mov ss, ax
+ mov es, ax
+ mov ds, ax
+ mov gs, ax
+ ;mov fs, ax ; XXX don't touch fs, we use it to store initial seg
+ ; set cs using a long jump
+ mov eax, [esp + 4]
+ mov [segm], ax
+ mov dword [off], .ldcs
+ jmp [off]
+.ldcs: ret
+
+; set_gdt(uint32_t addr, uint16_t limit)
+; loads the GDTR with the new address and limit for the GDT
+ global set_gdt
+set_gdt:
+ mov eax, [esp + 4]
+ mov [addr], eax
+ mov ax, [esp + 8]
+ mov [lim], ax
+ lgdt [lim]
+ ret
+
+; set_task_reg(uint16_t tss_selector)
+; loads the TSS selector in the task register
+ global set_task_reg
+set_task_reg:
+ mov eax, [esp + 4]
+ ltr [esp + 4]
+ ret
+
+; vi:set ts=8 sts=8 sw=8 ft=nasm:
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "config.h"
+#include "serial.h"
+#include "asmops.h"
+#include "intr.h"
+#include "panic.h"
+
+#define UART1_BASE 0x3f8
+#define UART2_BASE 0x2f8
+#define UART1_IRQ 4
+#define UART2_IRQ 3
+
+#define UART_DATA 0
+#define UART_INTR 1
+#define UART_DIVLO 0
+#define UART_DIVHI 1
+#define UART_FIFO 2
+#define UART_IID 2
+#define UART_LCTL 3
+#define UART_MCTL 4
+#define UART_LSTAT 5
+#define UART_MSTAT 6
+
+/* interrupt enable register bits */
+#define INTR_RECV 1
+#define INTR_SEND 2
+#define INTR_LSTAT 4
+#define INTR_DELTA 8
+
+/* fifo control register bits */
+#define FIFO_ENABLE 0x01
+#define FIFO_RECV_CLEAR 0x02
+#define FIFO_SEND_CLEAR 0x04
+#define FIFO_DMA 0x08
+#define FIFO_TRIG_4 0x40
+#define FIFO_TRIG_8 0x80
+#define FIFO_TRIG_14 0xc0
+
+/* interrupt id register bits */
+#define IID_PENDING 0x01
+#define IID_ID0 0x02
+#define IID_ID1 0x04
+#define IID_ID2 0x08
+#define IID_FIFO_EN 0xc0
+
+#define IID_SOURCE 0xe
+
+#define IID_DELTA 0
+#define IID_SEND 0x2
+#define IID_RECV 0x4
+#define IID_FIFO 0xc
+#define IID_STATUS 0x6
+
+/* line control register bits */
+#define LCTL_BITS_8 0x03
+#define LCTL_STOP_2 0x04
+#define LCTL_DLAB 0x80
+#define LCTL_8N1 LCTL_BITS_8
+#define LCTL_8N2 (LCTL_BITS_8 | LCTL_STOP_2)
+
+/* modem control register bits */
+#define MCTL_DTR 0x01
+#define MCTL_RTS 0x02
+#define MCTL_OUT1 0x04
+#define MCTL_OUT2 0x08
+#define MCTL_LOOP 0x10
+
+/* line status register bits */
+#define LST_DRDY 0x01
+#define LST_ERR_OVER 0x02
+#define LST_ERR_PARITY 0x04
+#define LST_ERR_FRAME 0x08
+#define LST_ERR_BRK 0x10
+#define LST_TREG_EMPTY 0x20
+#define LST_TIDLE 0x40
+#define LST_ERROR 0x80
+
+/* modem status register bits */
+#define MST_DELTA_CTS 0x01
+#define MST_DELTA_DSR 0x02
+#define MST_TERI 0x04
+#define MST_DELTA_DCD 0x08
+#define MST_CTS 0x10
+#define MST_DSR 0x20
+#define MST_RING 0x40
+#define MST_DCD 0x80
+
+/* interrupt controller stuff */
+#define PIC1_CMD_PORT 0x20
+#define PIC1_DATA_PORT 0x21
+#define PIC2_CMD_PORT 0xa0
+#define PIC2_DATA_PORT 0xa1
+#define OCW2_EOI 0x20
+
+#define COM_FMT_8N1 LCTL_8N1
+#define COM_FMT_8N2 LCTL_8N2
+
+#define INBUF_SIZE 16
+#define BNEXT(x) (((x) + 1) & (INBUF_SIZE - 1))
+#define BEMPTY(b) (b##_ridx == b##_widx)
+
+struct serial_port {
+ int base, intr;
+ int blocking;
+ int ref;
+
+ char inbuf[INBUF_SIZE];
+ int inbuf_ridx, inbuf_widx;
+};
+
+
+static int have_recv(int base);
+static void recv_intr();
+
+static struct serial_port ports[2];
+static int num_open;
+
+static int uart_base[] = {UART1_BASE, UART2_BASE};
+static int uart_irq[] = {UART1_IRQ, UART2_IRQ};
+
+int ser_open(int pidx, int baud, unsigned int mode)
+{
+ unsigned short div = 115200 / baud;
+ int i, fd, base, intr;
+ unsigned int fmt;
+
+ if(pidx < 0 || pidx > 1) {
+ printf("ser_open: invalid serial port: %d\n", pidx);
+ return -1;
+ }
+
+ for(i=0; i<num_open; i++) {
+ if(ports[i].base == uart_base[pidx]) {
+ /* the port is already open, return the same fd and increment ref */
+ ports[i].ref++;
+ return i;
+ }
+ }
+
+ fd = num_open++;
+ memset(ports + fd, 0, sizeof ports[pidx]);
+
+ base = uart_base[pidx];
+ intr = uart_irq[pidx];
+
+ if(mode & SER_8N2) {
+ fmt = COM_FMT_8N2;
+ } else {
+ fmt = COM_FMT_8N1;
+ }
+
+ interrupt(IRQ_TO_INTR(uart_irq[pidx]), recv_intr);
+
+ outp(base + UART_LCTL, LCTL_DLAB);
+ outp(base + UART_DIVLO, div & 0xff);
+ outp(base + UART_DIVHI, (div >> 8) & 0xff);
+ outp(base + UART_LCTL, fmt); /* fmt should be LCTL_8N1, LCTL_8N2 etc */
+ outp(base + UART_FIFO, FIFO_ENABLE | FIFO_SEND_CLEAR | FIFO_RECV_CLEAR);
+ outp(base + UART_MCTL, MCTL_DTR | MCTL_RTS | MCTL_OUT2);
+ outp(base + UART_INTR, INTR_RECV);
+
+ ports[fd].base = base;
+ ports[fd].intr = intr;
+ ports[fd].blocking = 1;
+ ports[fd].ref = 1;
+ return fd;
+}
+
+void ser_close(int fd)
+{
+ if(!ports[fd].ref) return;
+
+ if(--ports[fd].ref == 0) {
+ outp(ports[fd].base + UART_INTR, 0);
+ outp(ports[fd].base + UART_MCTL, 0);
+ ports[fd].base = 0;
+ }
+}
+
+int ser_block(int fd)
+{
+ ports[fd].blocking = 1;
+ return 0;
+}
+
+int ser_nonblock(int fd)
+{
+ ports[fd].blocking = 0;
+ return 0;
+}
+
+int ser_pending(int fd)
+{
+ return !BEMPTY(ports[fd].inbuf);
+}
+
+/* if msec < 0: wait for ever */
+int ser_wait(int fd, long msec)
+{
+ int res;
+ while(!(res = ser_pending(fd))) {
+ /* TODO timeout */
+ }
+ return res;
+}
+
+static int can_send(int fd)
+{
+ int base = ports[fd].base;
+ return inp(base + UART_LSTAT) & LST_TREG_EMPTY;
+}
+
+void ser_putc(int fd, char c)
+{
+ int base = ports[fd].base;
+
+ if(c == '\n') {
+ ser_putc(fd, '\r');
+ }
+
+ while(!can_send(fd));
+ /*while((inp(base + UART_MSTAT) & MST_CTS) == 0);*/
+ outp(base + UART_DATA, c);
+}
+
+int ser_getc(int fd)
+{
+ struct serial_port *p = ports + fd;
+ int have, c = -1;
+
+ if(p->blocking) {
+ while(!(have = ser_pending(fd)));
+ } else {
+ have = ser_pending(fd);
+ }
+
+ if(have) {
+ c = p->inbuf[p->inbuf_ridx];
+ p->inbuf_ridx = BNEXT(p->inbuf_ridx);
+ }
+ return c;
+}
+
+int ser_write(int fd, const char *buf, int count)
+{
+ int n = count;
+ while(n--) {
+ ser_putc(fd, *buf++);
+ }
+ return count;
+}
+
+int ser_read(int fd, char *buf, int count)
+{
+ int c, n = 0;
+ while(n < count && (c = ser_getc(fd)) != -1) {
+ *buf++ = c;
+ ++n;
+ }
+ return n;
+}
+
+char *ser_getline(int fd, char *buf, int bsz)
+{
+ static int widx;
+ char linebuf[512];
+ int i, rd, size, offs;
+
+ size = sizeof linebuf - widx - 1;
+ while(size && (rd = ser_read(fd, linebuf + widx, size)) > 0) {
+ widx += rd;
+ size -= rd;
+ }
+
+ linebuf[widx] = 0;
+
+ for(i=0; i<widx; i++) {
+ if(linebuf[i] == '\r' || linebuf[i] == '\n') {
+ size = i >= bsz ? bsz - 1 : i;
+ memcpy(buf, linebuf, size);
+ buf[size] = 0;
+
+ offs = i + 1;
+ memmove(linebuf, linebuf + offs, widx - offs);
+ widx -= offs;
+ return buf;
+ }
+ }
+ return 0;
+}
+
+static int have_recv(int base)
+{
+ unsigned short stat = inp(base + UART_LSTAT);
+ if(stat & LST_ERROR) {
+ panic("serial receive error\n");
+ }
+ return stat & LST_DRDY;
+}
+
+static void recv_intr()
+{
+ int i, idreg, c;
+
+ for(i=0; i<2; i++) {
+ int base = uart_base[i];
+ struct serial_port *p = ports + i;
+
+ while(((idreg = inp(base + UART_IID)) & IID_PENDING) == 0) {
+ while(have_recv(base)) {
+ c = inp(base + UART_DATA);
+
+#ifdef ENABLE_GDB_STUB
+ if(c == 3 && i == GDB_SERIAL_PORT) {
+ asm("int $3");
+ continue;
+ }
+#endif
+
+ p->inbuf[p->inbuf_widx] = c;
+ p->inbuf_widx = BNEXT(p->inbuf_widx);
+
+ if(p->inbuf_widx == p->inbuf_ridx) {
+ /* we overflowed, drop the oldest */
+ p->inbuf_ridx = BNEXT(p->inbuf_ridx);
+ }
+ }
+ }
+ }
+}
+
+#ifdef ENABLE_GDB_STUB
+void putDebugChar(int c)
+{
+ ser_putc(GDB_SERIAL_PORT, c);
+}
+
+int getDebugChar(void)
+{
+ return ser_getc(GDB_SERIAL_PORT);
+}
+#endif
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef SERIAL_H_
+#define SERIAL_H_
+
+#define SER_8N1 0
+#define SER_8N2 1
+#define SER_HWFLOW 2
+
+int ser_open(int pidx, int baud, unsigned int mode);
+void ser_close(int fd);
+
+int ser_block(int fd);
+int ser_nonblock(int fd);
+
+int ser_pending(int fd);
+/* if msec < 0: wait for ever */
+int ser_wait(int fd, long msec);
+
+void ser_putc(int fd, char c);
+int ser_getc(int fd);
+
+int ser_write(int fd, const char *buf, int count);
+int ser_read(int fd, char *buf, int count);
+
+#define ser_putchar(c) ser_putc(0, c)
+
+char *ser_getline(int fd, char *buf, int bsz);
+
+
+#endif /* SERIAL_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include "intr.h"
+#include "asmops.h"
+#include "timer.h"
+#include "panic.h"
+#include "config.h"
+
+/* frequency of the oscillator driving the 8254 timer */
+#define OSC_FREQ_HZ 1193182
+
+/* macro to divide and round to the nearest integer */
+#define DIV_ROUND(a, b) ((a) / (b) + ((a) % (b)) / ((b) / 2))
+
+/* I/O ports connected to the 8254 */
+#define PORT_DATA0 0x40
+#define PORT_DATA1 0x41
+#define PORT_DATA2 0x42
+#define PORT_CMD 0x43
+
+/* command bits */
+#define CMD_CHAN0 0
+#define CMD_CHAN1 (1 << 6)
+#define CMD_CHAN2 (2 << 6)
+#define CMD_RDBACK (3 << 6)
+
+#define CMD_LATCH 0
+#define CMD_ACCESS_LOW (1 << 4)
+#define CMD_ACCESS_HIGH (2 << 4)
+#define CMD_ACCESS_BOTH (3 << 4)
+
+#define CMD_OP_INT_TERM 0
+#define CMD_OP_ONESHOT (1 << 1)
+#define CMD_OP_RATE (2 << 1)
+#define CMD_OP_SQWAVE (3 << 1)
+#define CMD_OP_SOFT_STROBE (4 << 1)
+#define CMD_OP_HW_STROBE (5 << 1)
+
+#define CMD_MODE_BIN 0
+#define CMD_MODE_BCD 1
+
+volatile unsigned long nticks;
+
+struct timer_event {
+ int dt; /* remaining ticks delta from the previous event */
+ void (*func)(void);
+ struct timer_event *next;
+};
+
+static void timer_handler(int inum);
+
+static struct timer_event *evlist;
+
+
+void init_timer(void)
+{
+ /* calculate the reload count: round(osc / freq) */
+ int reload_count = DIV_ROUND(OSC_FREQ_HZ, TICK_FREQ_HZ);
+
+ /* set the mode to square wave for channel 0, both low
+ * and high reload count bytes will follow...
+ */
+ outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE);
+
+ /* write the low and high bytes of the reload count to the
+ * port for channel 0
+ */
+ outp(PORT_DATA0, reload_count & 0xff);
+ outp(PORT_DATA0, (reload_count >> 8) & 0xff);
+
+ /* set the timer interrupt handler */
+ interrupt(IRQ_TO_INTR(0), timer_handler);
+ unmask_irq(0);
+}
+
+void cleanup_timer(void)
+{
+ /* return the timer to the original rate */
+ outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE);
+ outp(PORT_DATA0, 0);
+ outp(PORT_DATA0, 0);
+}
+
+void set_alarm(unsigned long msec, void (*func)(void))
+{
+ int ticks, tsum, iflag;
+ struct timer_event *ev, *node;
+
+ if((ticks = MSEC_TO_TICKS(msec)) <= 0) {
+ return;
+ }
+
+ if(!(ev = malloc(sizeof *ev))) {
+ panic("failed to allocate timer event");
+ return;
+ }
+ ev->func = func;
+
+ iflag = get_intr_flag();
+ disable_intr();
+
+ if(!evlist || ticks < evlist->dt) {
+ /* insert at the begining */
+ ev->next = evlist;
+ evlist = ev;
+
+ ev->dt = ticks;
+ if(ev->next) {
+ ev->next->dt -= ticks;
+ }
+ } else {
+ tsum = evlist->dt;
+ node = evlist;
+
+ while(node->next && ticks > tsum + node->next->dt) {
+ tsum += node->next->dt;
+ node = node->next;
+ }
+
+ ev->next = node->next;
+ node->next = ev;
+
+ /* fix the relative times */
+ ev->dt = ticks - tsum;
+ if(ev->next) {
+ ev->next->dt -= ev->dt;
+ }
+ }
+
+ set_intr_flag(iflag);
+}
+
+void cancel_alarm(void (*func)(void))
+{
+ int iflag;
+ struct timer_event *ev, *node;
+ struct timer_event dummy;
+
+ iflag = get_intr_flag();
+ disable_intr();
+
+ dummy.next = evlist;
+ node = &dummy;
+ while(node->next) {
+ ev = node->next;
+ if(ev->func == func) {
+ /* found it */
+ if(ev->next) {
+ ev->next->dt += ev->dt;
+ }
+ node->next = ev->next;
+ free(ev);
+ break;
+ }
+ node = node->next;
+ }
+
+ set_intr_flag(iflag);
+}
+
+static void timer_handler(int inum)
+{
+ nticks++;
+
+ if(evlist) {
+ evlist->dt--;
+
+ while(evlist && evlist->dt <= 0) {
+ struct timer_event *ev = evlist;
+ evlist = evlist->next;
+
+ ev->func();
+ free(ev);
+ }
+ }
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef _TIMER_H_
+#define _TIMER_H_
+
+#include "config.h"
+
+#define MSEC_TO_TICKS(ms) ((ms) * TICK_FREQ_HZ / 1000)
+#define TICKS_TO_MSEC(tk) ((tk) * 1000 / TICK_FREQ_HZ)
+
+extern volatile unsigned long nticks;
+
+void init_timer(void);
+void cleanup_timer(void);
+
+/*
+int sys_sleep(int sec);
+void sleep(unsigned long msec);
+*/
+
+/* schedule a function to be called 'msec' milliseconds into the future
+ * warning: this function will be called directly from the timer interrupt, and
+ * must be extremely quick.
+ */
+void set_alarm(unsigned long msec, void (*func)(void));
+void cancel_alarm(void (*func)(void));
+
+#endif /* _TIMER_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef TSS_H_
+#define TSS_H_
+
+#include <inttypes.h>
+
+struct task_state {
+ uint32_t prev_task;
+ uint32_t esp0, ss0; /* we only ever set these two values */
+ uint32_t esp1, ss1;
+ uint32_t esp2, ss2;
+ uint32_t cr3;
+ uint32_t eip;
+ uint32_t eflags;
+ uint32_t eax, ecx, edx, ebx;
+ uint32_t esp, ebp, esi, edi;
+ uint32_t es, cs, ss, ds, fs, gs;
+ uint32_t ldt_sel;
+ uint16_t trap, iomap_addr;
+} __attribute__((packed));
+
+#endif /* TSS_H_ */
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#include <stdio.h>
-#include <string.h>
-#include "keyb.h"
-#include "intr.h"
-#include "asmops.h"
-#include "kbregs.h"
-#include "kbscan.h"
-#include "power.h"
-#include "panic.h"
-
-#define delay7us() \
- do { \
- iodelay(); iodelay(); iodelay(); iodelay(); \
- iodelay(); iodelay(); iodelay(); \
- } while(0)
-
-static void set_ccb(unsigned char ccb);
-static unsigned char get_ccb(void);
-
-static void kbintr();
-
-#define BUFSZ 64
-#define ADVANCE(x) ((x) = ((x) + 1) & (BUFSZ - 1))
-
-static int buffer[BUFSZ];
-static int buf_ridx, buf_widx;
-
-static unsigned int num_pressed;
-static unsigned char keystate[256];
-
-
-void kb_init(void)
-{
- buf_ridx = buf_widx = 0;
- num_pressed = 0;
- memset(keystate, 0, sizeof keystate);
-
- /* make sure set1 translation is enabled */
- kb_set_translate(1);
-
- interrupt(IRQ_TO_INTR(KB_IRQ), kbintr);
- kb_intr_enable();
- unmask_irq(KB_IRQ);
-}
-
-void kb_intr_enable(void)
-{
- unsigned char ccb = get_ccb();
- ccb |= KB_CCB_KB_INTREN;
- set_ccb(ccb);
-}
-
-void kb_intr_disable(void)
-{
- unsigned char ccb = get_ccb();
- ccb &= ~KB_CCB_KB_INTREN;
- set_ccb(ccb);
-}
-
-int kb_setmode(int mode)
-{
- kb_send_data(0xf0);
- if(!kb_wait_read() || kb_read_data() != KB_ACK) {
- return -1;
- }
- kb_send_data(mode);
- if(!kb_wait_read() || kb_read_data() != KB_ACK) {
- return -1;
- }
- return 0;
-}
-
-int kb_getmode(void)
-{
- int mode;
-
- kb_send_data(0xf0);
- if(!kb_wait_read() || kb_read_data() != KB_ACK) {
- return -1;
- }
- kb_send_data(0);
- if(!kb_wait_read() || kb_read_data() != KB_ACK) {
- return -1;
- }
- mode = kb_read_data();
-
- switch(mode) {
- case 0x43: return 1;
- case 0x41: return 2;
- case 0x3f: return 3;
- default:
- break;
- }
- return mode;
-}
-
-void kb_set_translate(int xlat)
-{
- unsigned char ccb = get_ccb();
- if(xlat) {
- ccb |= KB_CCB_KB_XLAT;
- } else {
- ccb &= ~KB_CCB_KB_XLAT;
- }
- set_ccb(ccb);
-}
-
-int kb_get_translate(void)
-{
- return get_ccb() & KB_CCB_KB_XLAT;
-}
-
-int kb_isdown(int key)
-{
- switch(key) {
- case KB_ANY:
- return num_pressed;
-
- case KB_ALT:
- return keystate[KB_LALT] + keystate[KB_RALT];
-
- case KB_CTRL:
- return keystate[KB_LCTRL] + keystate[KB_RCTRL];
- }
- return keystate[key];
-}
-
-void kb_wait(void)
-{
- int key;
- while((key = kb_getkey()) == -1) {
- /* put the processor to sleep while waiting for keypresses, but first
- * make sure interrupts are enabled, or we'll sleep forever
- */
- enable_intr();
- halt_cpu();
- }
- kb_putback(key);
-}
-
-int kb_getkey(void)
-{
- int res;
-
- if(buf_ridx == buf_widx) {
- return -1;
- }
- res = buffer[buf_ridx];
- ADVANCE(buf_ridx);
- return res;
-}
-
-void kb_putback(int key)
-{
- /* go back a place */
- if(--buf_ridx < 0) {
- buf_ridx += BUFSZ;
- }
-
- /* if the write end hasn't caught up with us, go back one place
- * and put it there, otherwise just overwrite the oldest key which
- * is right where we were.
- */
- if(buf_ridx == buf_widx) {
- ADVANCE(buf_ridx);
- }
-
- buffer[buf_ridx] = key;
-}
-
-int kb_wait_write(void)
-{
- int i;
- for(i=0; i<32768; i++) {
- if(!(inp(KB_STATUS_PORT) & KB_STAT_INBUF_FULL)) {
- return 1;
- }
- iodelay();
- }
- /*printf("kb_wait_write timeout\n");*/
- return 0;
-}
-
-int kb_wait_read(void)
-{
- int i;
- for(i=0; i<32768; i++) {
- if((inp(KB_STATUS_PORT) & KB_STAT_OUTBUF_FULL)) {
- return 1;
- }
- iodelay();
- }
- /*printf("kb_wait_read timeout\n");*/
- return 0;
-}
-
-void kb_send_cmd(unsigned char cmd)
-{
- kb_wait_write();
- outp(KB_CMD_PORT, cmd);
-}
-
-void kb_send_data(unsigned char data)
-{
- kb_wait_write();
- outp(KB_DATA_PORT, data);
-}
-
-unsigned char kb_read_data(void)
-{
- kb_wait_read();
- delay7us();
- return inp(KB_DATA_PORT);
-}
-
-static void set_ccb(unsigned char ccb)
-{
- kb_send_cmd(KB_CMD_SET_CMDBYTE);
- kb_send_data(ccb);
-
- if(kb_wait_read()) {
- kb_read_data();
- }
-}
-
-static unsigned char get_ccb(void)
-{
- kb_send_cmd(KB_CMD_GET_CMDBYTE);
- return kb_read_data();
-}
-
-static void kbintr()
-{
- unsigned char code;
- int key, press;
- static int ext = 0;
-
- code = inp(KB_DATA_PORT);
-
- if(code == 0xe0) {
- ext = 1;
- return;
- }
-
- if(code & 0x80) {
- press = 0;
- code &= 0x7f;
-
- if(num_pressed > 0) {
- num_pressed--;
- }
- } else {
- press = 1;
-
- num_pressed++;
- }
-
- if(!ext) {
- key = code < 0x59 ? scantbl_set1[code] : 0;
- } else {
- switch(code) {
- case 0x1c: key = '\r'; break;
- case 0x1d: key = KB_RCTRL; break;
- //case 0x35: key = KB_NUM_MINUS; break;
- //case 0x37: key = KB_SYSRQ; break;
- case 0x38: key = KB_RALT; break;
- case 0x47: key = KB_HOME; break;
- case 0x48: key = KB_UP; break;
- case 0x49: key = KB_PGUP; break;
- case 0x4b: key = KB_LEFT; break;
- case 0x4d: key = KB_RIGHT; break;
- case 0x4f: key = KB_END; break;
- case 0x50: key = KB_DOWN; break;
- case 0x51: key = KB_PGDN; break;
- case 0x52: key = KB_INSERT; break;
- case 0x53: key = KB_DEL; break;
- default:
- key = 0;
- }
- ext = 0;
- }
-
- if(press) {
- if(key == KB_DEL && (keystate[KB_LALT] || keystate[KB_RALT]) && (keystate[KB_LCTRL] || keystate[KB_RCTRL])) {
- reboot();
- }
- /* append to buffer */
- buffer[buf_widx] = key;
- ADVANCE(buf_widx);
- /* if the write end overtook the read end, advance the read end
- * too, to discard the oldest keypress from the buffer
- */
- if(buf_widx == buf_ridx) {
- ADVANCE(buf_ridx);
- }
- }
-
- /* and update keystate table */
- keystate[key] = press;
-}
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef KEYB_H_
-#define KEYB_H_
-
-#define KB_ANY (-1)
-#define KB_ALT (-2)
-#define KB_CTRL (-3)
-#define KB_SHIFT (-4)
-
-/* special keys */
-enum {
- KB_ESC = 27,
- KB_BACKSP = 127,
-
- KB_INSERT, KB_DEL, KB_HOME, KB_END, KB_PGUP, KB_PGDN,
- KB_LEFT, KB_RIGHT, KB_UP, KB_DOWN,
- KB_NUM_DOT, KB_NUM_ENTER, KB_NUM_PLUS, KB_NUM_MINUS, KB_NUM_MUL, KB_NUM_DIV,
- KB_NUM_0, KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_4,
- KB_NUM_5, KB_NUM_6, KB_NUM_7, KB_NUM_8, KB_NUM_9,
-
- KB_LALT, KB_RALT,
- KB_LCTRL, KB_RCTRL,
- KB_LSHIFT, KB_RSHIFT,
- KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6,
- KB_F7, KB_F8, KB_F9, KB_F10, KB_F11, KB_F12,
- KB_CAPSLK, KB_NUMLK, KB_SCRLK, KB_SYSRQ
-};
-
-void kb_init(void);
-
-void kb_intr_enable(void);
-void kb_intr_disable(void);
-
-int kb_setmode(int mode);
-int kb_getmode(void);
-
-void kb_set_translate(int xlat);
-int kb_get_translate(void);
-
-/* Boolean predicate for testing the current state of a particular key.
- * You may also pass KB_ANY to test if any key is held down.
- */
-int kb_isdown(int key);
-
-/* waits for any keypress */
-void kb_wait(void);
-
-/* removes and returns a single key from the input buffer. */
-int kb_getkey(void);
-
-void kb_putback(int key);
-
-/* returns 1 if the keyboard controller is ready to read/write
- * returns 0 if the wait times out
- */
-int kb_wait_write(void);
-int kb_wait_read(void);
-
-void kb_send_cmd(unsigned char cmd);
-void kb_send_data(unsigned char data);
-unsigned char kb_read_data(void);
-
-#endif /* KEYB_H_ */
extern boot_mem_map_size
%include 'macros.inc'
-%define CON_SERIAL
[bits 16]
global _start
jmp exit
.notvm86:
+%ifdef BUG_WARNING
call warning ; issue a warning and allow the user to abort
+%endif
%ifdef CON_SERIAL
call setup_serial
str_enterpm db 'Entering 32bit protected mode ...',10,0
+%ifdef BUG_WARNING
; warns the user about the experimental and buggy state of this
; protected mode system, and allows aborting if having to reboot later
; is not acceptable.
db 'return cleanly to DOS on exit. You might be forced to reboot after quitting!',10
db 'If this is not acceptable, press ESC now to abort.',10,10
db 'Press ESC to abort and return to DOS, any other key to continue...',10,0
+%endif ; BUG_WARNING
+
+
+; ---------------------- A20 address line enable -----------------------
enable_a20:
call test_a20
+%define BUG_WARNING
+%define CON_SERIAL
+
UART_BASE equ 2f8h ; COM1: 3f8, COM2: 2f8
UART_DIVISOR equ 115200 / 9600 ; 9600 baud
+++ /dev/null
-#include <stdio.h>
-#include "segm.h"
-#include "intr.h"
-#include "mem.h"
-#include "contty.h"
-#include "keyb.h"
-#include "psaux.h"
-#include "timer.h"
-
-int main(void)
-{
- init_segm();
- init_intr();
-
- con_init();
- kb_init();
- /*init_psaux();*/
-
- init_mem();
-
- init_timer();
-
- enable_intr();
-
- printf("\nhello from C\n");
-
- cleanup_intr(); /* also disables interrupts */
- cleanup_timer();
- return 0;
-}
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#include <stdio.h>
-#include <string.h>
-#include "config.h"
-#include "panic.h"
-#include "mem.h"
-#include "intr.h"
-
-#define FREE 0
-#define USED 1
-
-#define BM_IDX(pg) ((pg) / 32)
-#define BM_BIT(pg) ((pg) & 0x1f)
-
-#define IS_FREE(pg) ((bitmap[BM_IDX(pg)] & (1 << BM_BIT(pg))) == 0)
-
-#define MEM_START ((uint32_t)&_mem_start)
-
-
-struct mem_range {
- uint32_t start;
- uint32_t size;
-};
-
-void move_stack(uint32_t newaddr); /* defined in startup.s */
-
-static void mark_page(int pg, int used);
-static void add_memory(uint32_t start, size_t size);
-
-#define MAX_MAP_SIZE 16
-extern struct mem_range boot_mem_map[MAX_MAP_SIZE];
-extern int boot_mem_map_size;
-
-/* linker supplied symbol, points to the end of the kernel image */
-extern uint32_t _mem_start;
-
-
-/* A bitmap is used to track which physical memory pages are used, and which
- * are available for allocation by alloc_ppage.
- *
- * last_alloc_idx keeps track of the last 32bit element in the bitmap array
- * where a free page was found. It's guaranteed that all the elements before
- * this have no free pages, but it doesn't imply that there will be another
- * free page there. So it's used as a starting point for the search.
- */
-static uint32_t *bitmap;
-static int bmsize, last_alloc_idx;
-
-
-
-void init_mem(void)
-{
- int i, pg, max_used_pg, end_pg = 0;
- uint32_t used_end, start, end, sz, total = 0, rem;
- const char *suffix[] = {"bytes", "KB", "MB", "GB"};
-
- last_alloc_idx = 0;
-
- /* the allocation bitmap starts right at the end of the kernel image */
- bitmap = (uint32_t*)&_mem_start;
-
- /* start by marking all posible pages (2**20) as used. We do not "reserve"
- * all this space. Pages beyond the end of the useful bitmap area
- * ((char*)bitmap + bmsize), which will be determined after we traverse the
- * memory map, are going to be marked as available for allocation.
- */
- memset(bitmap, 0xff, 1024 * 1024 / 8);
-
-
- if(boot_mem_map_size <= 0 || boot_mem_map_size > MAX_MAP_SIZE) {
- panic("invalid memory map size reported by the boot loader: %d\n", boot_mem_map_size);
- }
-
- printf("Memory map:\n");
- for(i=0; i<boot_mem_map_size; i++) {
- printf(" start: %08x - size: %08x\n", (unsigned int)boot_mem_map[i].start,
- (unsigned int)boot_mem_map[i].size);
-
- start = boot_mem_map[i].start;
- sz = boot_mem_map[i].size & 0xfffffffc;
- end = start + sz;
- if(end < sz) {
- end = 0xfffffffc;
- sz = end - start;
- }
-
- /* ignore any memory ranges which end before the end of the kernel image */
- if(end < MEM_START) continue;
- if(start < MEM_START) {
- start = MEM_START;
- }
-
- add_memory(start, sz);
- total += sz;
-
- pg = ADDR_TO_PAGE(end);
- if(end_pg < pg) {
- end_pg = pg;
- }
- }
-
- i = 0;
- while(total > 1024) {
- rem = total & 0x3ff;
- total >>= 10;
- ++i;
- }
- printf("Total usable RAM: %u.%u %s\n", total, 100 * rem / 1024, suffix[i]);
-
- /* size of the useful part of the bitmap in bytes padded to 4-byte
- * boundaries to allow 32bit at a time operations.
- */
- bmsize = (end_pg / 32) * 4;
-
- /* mark all pages occupied by the bitmap as used */
- used_end = (uint32_t)bitmap + bmsize - 1;
-
- max_used_pg = ADDR_TO_PAGE(used_end);
- printf("marking pages up to %x (page: %d) as used\n", used_end, max_used_pg);
- for(i=0; i<=max_used_pg; i++) {
- mark_page(i, USED);
- }
-
-#ifdef MOVE_STACK_RAMTOP
- /* allocate space for the stack at the top of RAM and move it there */
- if((pg = alloc_ppages(STACK_PAGES, MEM_STACK)) != -1) {
- printf("moving stack-top to: %x (%d pages)\n", PAGE_TO_ADDR(end_pg) - 4, STACK_PAGES);
- move_stack(PAGE_TO_ADDR(pg + STACK_PAGES) - 4);
- }
-#endif
-}
-
-int alloc_ppage(int area)
-{
- return alloc_ppages(1, area);
-}
-
-/* free_ppage marks the physical page, free in the allocation bitmap.
- *
- * CAUTION: no checks are done that this page should actually be freed or not.
- * If you call free_ppage with the address of some part of memory that was
- * originally reserved due to it being in a memory hole or part of the kernel
- * image or whatever, it will be subsequently allocatable by alloc_ppage.
- */
-void free_ppage(int pg)
-{
- int bmidx = BM_IDX(pg);
-
- int intr_state = get_intr_flag();
- disable_intr();
-
- if(IS_FREE(pg)) {
- panic("free_ppage(%d): I thought that was already free!\n", pg);
- }
-
- mark_page(pg, FREE);
- if(bmidx < last_alloc_idx) {
- last_alloc_idx = bmidx;
- }
-
- set_intr_flag(intr_state);
-}
-
-
-int alloc_ppages(int count, int area)
-{
- int i, dir, pg, idx, max, intr_state, found_free = 0;
-
- intr_state = get_intr_flag();
- disable_intr();
-
- if(area == MEM_STACK) {
- idx = (bmsize - 1) / 4;
- max = -1;
- dir = -1;
- } else {
- idx = last_alloc_idx;
- max = bmsize / 4;
- dir = 1;
- }
-
- while(idx != max) {
- /* if at least one bit is 0 then we have at least
- * one free page. find it and try to allocate a range starting from there
- */
- if(bitmap[idx] != 0xffffffff) {
- pg = idx * 32;
- if(dir < 0) pg += 31;
-
- for(i=0; i<32; i++) {
- if(IS_FREE(pg)) {
- if(!found_free && dir > 0) {
- last_alloc_idx = idx;
- found_free = 1;
- }
-
- if(alloc_ppage_range(pg, count) != -1) {
- set_intr_flag(intr_state);
- return pg;
- }
- }
- pg += dir;
- }
- }
- idx += dir;
- }
-
- set_intr_flag(intr_state);
- return -1;
-}
-
-void free_ppages(int pg0, int count)
-{
- int i;
-
- for(i=0; i<count; i++) {
- free_ppage(pg0++);
- }
-}
-
-int alloc_ppage_range(int start, int size)
-{
- int i, pg = start;
- int intr_state;
-
- intr_state = get_intr_flag();
- disable_intr();
-
- /* first validate that no page in the requested range is allocated */
- for(i=0; i<size; i++) {
- if(!IS_FREE(pg)) {
- return -1;
- }
- ++pg;
- }
-
- /* all is well, mark them as used */
- pg = start;
- for(i=0; i<size; i++) {
- mark_page(pg++, USED);
- }
-
- set_intr_flag(intr_state);
- return 0;
-}
-
-int free_ppage_range(int start, int size)
-{
- int i, pg = start;
-
- for(i=0; i<size; i++) {
- free_ppage(pg++);
- }
- return 0;
-}
-
-/* adds a range of physical memory to the available pool. used during init_mem
- * when traversing the memory map.
- */
-static void add_memory(uint32_t start, size_t sz)
-{
- int i, szpg, pg;
-
- szpg = ADDR_TO_PAGE(sz + 4095);
- pg = ADDR_TO_PAGE(start);
-
- for(i=0; i<szpg; i++) {
- mark_page(pg++, FREE);
- }
-}
-
-/* maps a page as used or free in the allocation bitmap */
-static void mark_page(int pg, int used)
-{
- int idx = BM_IDX(pg);
- int bit = BM_BIT(pg);
-
- if(used) {
- bitmap[idx] |= 1 << bit;
- } else {
- bitmap[idx] &= ~(1 << bit);
- }
-}
-
-void print_page_bitmap(void)
-{
- int i;
-
- for(i=0; i<bmsize/4; i++) {
- if((i & 3) == 0) {
- uint32_t pg = i * 32;
- uint32_t addr = PAGE_TO_ADDR(pg);
- printf("\n%5d [%08x]:", (int)pg, (unsigned long)addr);
- }
- printf(" %08x", bitmap[i]);
- }
- printf("\n");
-}
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef MEM_H_
-#define MEM_H_
-
-#define ADDR_TO_PAGE(x) ((uint32_t)(x) >> 12)
-#define PAGE_TO_ADDR(x) ((uint32_t)(x) << 12)
-#define PAGE_TO_PTR(x) ((void*)PAGE_TO_ADDR(x))
-
-#define BYTES_TO_PAGES(x) (((uint32_t)(x) + 4095) >> 12)
-
-/* for alloc_ppage/alloc_ppages */
-enum {
- MEM_HEAP = 0, /* start searching from the bottom */
- MEM_STACK = 1 /* start searching from the top */
-};
-
-void init_mem(void);
-
-int alloc_ppage(int area);
-void free_ppage(int pg);
-
-/* allocate a number of consecutive pages */
-int alloc_ppages(int count, int area);
-void free_ppages(int pg0, int count);
-
-/* allocate a specific range of pages.
- * Fails (and returns -1) if any page in the requested range is not free.
- */
-int alloc_ppage_range(int start, int size);
-int free_ppage_range(int start, int size);
-
-#endif /* MEM_H_ */
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include "intr.h"
-#include "asmops.h"
-
-struct all_registers {
- uint32_t eax, ebx, ecx, edx;
- uint32_t esp, ebp, esi, edi;
- uint32_t eflags;
- uint32_t cs, ss, ds, es, fs, gs;
- uint32_t cr0, cr1, cr2, cr3;
-};
-
-/* defined in regs.S */
-void get_regs(struct all_registers *regs);
-
-void panic(const char *fmt, ...)
-{
- va_list ap;
- struct all_registers regs;
- uint32_t eip;
-
- disable_intr();
-
- memset(®s, 0, sizeof regs);
- get_regs(®s);
-
- CALLER_EIP(eip);
-
- printf("~~~~~ pcboot panic ~~~~~\n");
- va_start(ap, fmt);
- vprintf(fmt, ap);
- va_end(ap);
-
- printf("\nRegisters:\n");
- printf("eax: %x, ebx: %x, ecx: %x, edx: %x\n", regs.eax, regs.ebx, regs.ecx, regs.edx);
- printf("esp: %x, ebp: %x, esi: %x, edi: %x\n", regs.esp, regs.ebp, regs.esi, regs.edi);
- printf("eip: %x, eflags: %x\n", eip, regs.eflags);
- printf("cr0: %x, cr1: %x, cr2: %x, cr3: %x\n", regs.cr0, regs.cr1, regs.cr2, regs.cr3);
- printf("cs: %x (%d|%d)\n", regs.cs, regs.cs >> 3, regs.cs & 3);
- printf("ss: %x (%d|%d)\n", regs.ss, regs.ss >> 3, regs.ss & 3);
- printf("ds: %x (%d|%d)\n", regs.ds, regs.ds >> 3, regs.ds & 3);
- printf("es: %x (%d|%d)\n", regs.es, regs.es >> 3, regs.es & 3);
- printf("fs: %x (%d|%d)\n", regs.fs, regs.fs >> 3, regs.fs & 3);
- printf("gs: %x (%d|%d)\n", regs.gs, regs.gs >> 3, regs.gs & 3);
-
- for(;;) halt_cpu();
-}
-
-void backtrace(void)
-{
- int lvl = 0;
- uint32_t *frmptr;
- int ien = get_intr_flag();
- disable_intr();
- get_ebp(frmptr);
- set_intr_flag(ien);
-
- while(frmptr) {
- printf("%d: %p\n", lvl++, (void*)frmptr[1]);
- frmptr = (uint32_t*)*frmptr;
- }
-}
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef PANIC_H_
-#define PANIC_H_
-
-void panic(const char *fmt, ...) __attribute__((noreturn));
-void backtrace(void);
-
-#endif /* PANIC_H_ */
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#include <stdio.h>
-#include "power.h"
-#include "keyb.h"
-#include "kbregs.h"
-#include "timer.h"
-#include "panic.h"
-#include "asmops.h"
-
-/* defined in intr_asm.S */
-void set_idt(uint32_t addr, uint16_t limit);
-
-void reboot(void)
-{
- int i;
-
- printf("reboot: keyboard controller\n");
- kb_send_cmd(KB_CMD_PULSE_RESET);
-
- for(i=0; i<32768; i++) {
- iodelay();
- }
-
- printf("reboot: triple-fault\n");
- set_idt(0, 0);
- asm volatile("int $3");
-
- panic("can't reboot!");
-}
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef POWER_H_
-#define POWER_H_
-
-void reboot(void);
-
-#endif /* POWER_H_ */
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#include <stdio.h>
-#include "psaux.h"
-#include "intr.h"
-#include "asmops.h"
-#include "keyb.h"
-#include "kbregs.h"
-
-static void init_mouse(void);
-static void proc_mouse_data(unsigned char *data);
-static void psaux_intr();
-
-static int mx, my;
-static unsigned int bnstate;
-static int present;
-static int bounds[4];
-static int intr_mode;
-
-void init_psaux(void)
-{
- interrupt(IRQ_TO_INTR(12), psaux_intr);
- unmask_irq(12);
-
- init_mouse();
- set_mouse_bounds(0, 0, 319, 199);
-}
-
-static void init_mouse(void)
-{
- unsigned char val;
-
- kb_send_cmd(KB_CMD_AUX_ENABLE);
- if(kb_wait_read()) {
- val = kb_read_data();
- printf("aux enable: %02x\n", (unsigned int)val);
- }
-
- /*
- kb_send_cmd(KB_CMD_PSAUX);
- kb_send_data(AUX_CMD_REMOTE_MODE);
- val = kb_read_data();
- */
-
- kb_send_cmd(KB_CMD_GET_CMDBYTE);
- val = kb_read_data();
- val &= ~KB_CCB_AUX_DISABLE;
- val |= KB_CCB_AUX_INTREN;
- kb_send_cmd(KB_CMD_SET_CMDBYTE);
- kb_send_data(val);
-
- if(kb_wait_read()) {
- val = kb_read_data();
- printf("set cmdbyte: %02x\n", (unsigned int)val);
- }
- intr_mode = 1;
-
- kb_send_cmd(KB_CMD_PSAUX);
- kb_send_data(AUX_CMD_DEFAULTS);
- val = kb_read_data();
-
- kb_send_cmd(KB_CMD_PSAUX);
- kb_send_data(AUX_CMD_ENABLE);
- val = kb_read_data();
-
- present = (val == KB_ACK) ? 1 : 0;
- printf("init_mouse: %spresent\n", present ? "" : "not ");
-}
-
-int have_mouse(void)
-{
- return present;
-}
-
-void set_mouse_bounds(int x0, int y0, int x1, int y1)
-{
- bounds[0] = x0;
- bounds[1] = y0;
- bounds[2] = x1;
- bounds[3] = y1;
-}
-
-unsigned int mouse_state(int *xp, int *yp)
-{
- if(!intr_mode) {
- poll_mouse();
- }
-
- *xp = mx;
- *yp = my;
- return bnstate;
-}
-
-/* TODO: poll_mouse doesn't work (enable remote mode and disable interrupt on init to test) */
-#define STAT_AUX_PENDING (KB_STAT_OUTBUF_FULL | KB_STAT_AUX)
-static inline int aux_pending(void)
-{
- return (inp(KB_STATUS_PORT) & STAT_AUX_PENDING) == STAT_AUX_PENDING ? 1 : 0;
-}
-
-void poll_mouse(void)
-{
- static int poll_state;
- static unsigned char pkt[3];
- unsigned char rd;
-
- ser_printf("poll_mouse(%d)\n", poll_state);
-
- switch(poll_state) {
- case 0: /* send read mouse command */
- kb_send_cmd(KB_CMD_PSAUX);
- kb_send_data(AUX_CMD_READ_MOUSE);
- ++poll_state;
- break;
-
- case 1: /* wait for ACK */
- if(kb_wait_read()) {// && aux_pending()) {
- if((rd = kb_read_data()) == KB_ACK) {
- ++poll_state;
- } else {
- ser_printf("poll_mouse state 1: expected ack: %02x\n", (unsigned int)rd);
- }
- }
- break;
-
- case 2: /* read packet data */
- case 3:
- case 4:
- if(kb_wait_read() && aux_pending()) {
- int i = poll_state++ - 2;
- pkt[i] = kb_read_data();
- }
- if(poll_state == 5) {
- ser_printf("proc_mouse_data(%02x %02x %02x)\n", (unsigned int)pkt[0],
- (unsigned int)pkt[1], (unsigned int)pkt[2]);
- proc_mouse_data(pkt);
- poll_state = 0;
- }
- break;
-
- default:
- ser_printf("poll_mouse reached state: %d\n", poll_state);
- }
-}
-
-static void proc_mouse_data(unsigned char *data)
-{
- int dx, dy;
-
- if(data[0] & AUX_PKT0_OVF_BITS) {
- /* consensus seems to be that if overflow bits are set, something is
- * fucked, and it's best to re-initialize the mouse
- */
- /*init_mouse();*/
- } else {
- bnstate = data[0] & AUX_PKT0_BUTTON_BITS;
- dx = data[1];
- dy = data[2];
-
- if(data[0] & AUX_PKT0_XSIGN) {
- dx |= 0xffffff00;
- }
- if(data[0] & AUX_PKT0_YSIGN) {
- dy |= 0xffffff00;
- }
-
- mx += dx;
- my -= dy;
-
- if(mx < bounds[0]) mx = bounds[0];
- if(mx > bounds[2]) mx = bounds[2];
- if(my < bounds[1]) my = bounds[1];
- if(my > bounds[3]) my = bounds[3];
- }
-}
-
-static void psaux_intr()
-{
- static unsigned char data[3];
- static int idx;
-
- if(!(inp(KB_STATUS_PORT) & KB_STAT_AUX)) {
- /* no mouse data pending, ignore interrupt */
- return;
- }
-
- data[idx] = kb_read_data();
- if(++idx >= 3) {
- idx = 0;
-
- proc_mouse_data(data);
- }
-}
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef PSAUX_H_
-#define PSAUX_H_
-
-#define MOUSE_LBN_BIT 1
-#define MOUSE_RBN_BIT 2
-#define MOUSE_MBN_BIT 4
-
-void init_psaux(void);
-
-int have_mouse(void);
-
-void set_mouse_bounds(int x0, int y0, int x1, int y1);
-unsigned int mouse_state(int *xp, int *yp);
-
-void poll_mouse(void);
-
-#endif /* PSAUX_H_ */
+++ /dev/null
- section .text
- align 4
-
- global get_regs
-get_regs:
- push ebp
- mov ebp, esp
-
- push edx
- mov edx, [ebp + 8]
-
- mov [edx], eax
- mov [edx + 4], ebx
- mov [edx + 8], ecx
-
- ; juggle edx
- mov eax, edx
- pop edx
- mov [eax + 12], edx
- push edx
- mov edx, eax
-
- ; those two are pointless in a function
- mov [edx + 16], esp
- mov [edx + 20], ebp
-
- mov [edx + 24], esi
- mov [edx + 28], edx
-
- pushf
- pop eax
- mov [edx + 32], eax
-
- mov [edx + 36], cs
- mov [edx + 40], ss
- mov [edx + 44], ds
- mov [edx + 48], es
- mov [edx + 52], fs
- mov [edx + 56], gs
-
- push ebx
- mov ebx, cr0
- mov [edx + 60], ebx
- ;mov ebx, cr1
- ;mov [edx + 64], ebx
- mov ebx, cr2
- mov [edx + 68], ebx
- mov ebx, cr3
- mov [edx + 72], ebx
- pop ebx
-
- pop edx
- pop ebp
- ret
-
-
-; vi:set ts=8 sts=8 sw=8 ft=nasm:
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#include <stdio.h>
-#include <string.h>
-#include "segm.h"
-#include "desc.h"
-#include "tss.h"
-
-/* bits for the 3rd 16bt part of the descriptor */
-#define BIT_ACCESSED (1 << 8)
-#define BIT_WR (1 << 9)
-#define BIT_RD (1 << 9)
-#define BIT_EXP_DOWN (1 << 10)
-#define BIT_CONFORMING (1 << 10)
-#define BIT_CODE (1 << 11)
-#define BIT_NOSYS (1 << 12)
-#define BIT_PRESENT (1 << 15)
-/* TSS busy bit */
-#define BIT_BUSY (1 << 9)
-
-/* bits for the last 16bit part of the descriptor */
-#define BIT_BIG (1 << 6)
-#define BIT_DEFAULT (1 << 6)
-#define BIT_GRAN (1 << 7)
-
-enum {TYPE_DATA, TYPE_CODE};
-
-/* we need the following bit pattern at the 8th bit excluding the busy bit: 1001 */
-#define TSS_TYPE_BITS (9 << 8)
-
-static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type);
-static void segm_desc16(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type);
-/*static void task_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl);*/
-
-/* these functions are implemented in segm-asm.S */
-void setup_selectors(uint16_t code, uint16_t data);
-void set_gdt(uint32_t addr, uint16_t limit);
-void set_task_reg(uint16_t tss_selector);
-
-/*
-static void dbg_print_gdt(void);
-static void print_desc(desc_t *desc);
-*/
-
-/* our global descriptor table */
-static desc_t gdt[NUM_SEGMENTS] __attribute__((aligned(8)));
-uint16_t orig_seg;
-
-void init_segm(void)
-{
- asm volatile (
- "mov %%fs, %0\n\t"
- : "=a"(orig_seg));
-
- memset(gdt, 0, sizeof gdt);
- segm_desc(gdt + SEGM_CODE, 0, 0xffffffff, 0, TYPE_CODE);
- segm_desc(gdt + SEGM_DATA, 0, 0xffffffff, 0, TYPE_DATA);
- segm_desc(gdt + SEGM_XCODE, orig_seg << 4, 0xffffffff, 0, TYPE_CODE);
- segm_desc(gdt + SEGM_XDATA, orig_seg << 4, 0xffffffff, 0, TYPE_DATA);
- segm_desc16(gdt + SEGM_CODE16, orig_seg << 4, 0xffff, 0, TYPE_CODE);
-
- set_gdt((uint32_t)gdt, sizeof gdt - 1);
-
- setup_selectors(selector(SEGM_CODE, 0), selector(SEGM_DATA, 0));
-}
-
-/* constructs a GDT selector based on index and priviledge level */
-uint16_t selector(int idx, int rpl)
-{
- return (idx << 3) | (rpl & 3);
-}
-
-/*
-void set_tss(uint32_t addr)
-{
- task_desc(gdt + SEGM_TASK, addr, sizeof(struct task_state) - 1, 3);
- set_task_reg(selector(SEGM_TASK, 0));
-}
-*/
-
-static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type)
-{
- desc->d[0] = limit & 0xffff; /* low order 16bits of limit */
- desc->d[1] = base & 0xffff; /* low order 16bits of base */
-
- /* third 16bit part contains the last 8 bits of base, the 2 priviledge
- * level bits starting on bit 13, present flag on bit 15, and type bits
- * starting from bit 8
- */
- desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT |
- BIT_NOSYS | (type == TYPE_DATA ? BIT_WR : (BIT_RD | BIT_CODE));
-
- /* last 16bit part contains the last nibble of limit, the last byte of
- * base, and the granularity and deafult/big flags in bits 23 and 22 resp.
- */
- desc->d[3] = ((limit >> 16) & 0xf) | ((base >> 16) & 0xff00) | BIT_GRAN | BIT_BIG;
-}
-
-static void segm_desc16(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type)
-{
- desc->d[0] = limit & 0xffff; /* low order 16bits of limit */
- desc->d[1] = base & 0xffff; /* low order 16bits of base */
-
- /* third 16bit part contains the last 8 bits of base, the 2 priviledge
- * level bits starting on bit 13, present flag on bit 15, and type bits
- * starting from bit 8
- */
- desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT |
- BIT_NOSYS | (type == TYPE_DATA ? BIT_WR : (BIT_RD | BIT_CODE));
-
- /* 16bit descriptors have the upper word 0 */
- desc->d[3] = 0;
-}
-
-#if 0
-static void task_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl)
-{
- desc->d[0] = limit & 0xffff;
- desc->d[1] = base & 0xffff;
-
- desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT |
- TSS_TYPE_BITS; /* XXX busy ? */
- desc->d[3] = ((limit >> 16) & 0xf) | ((base >> 16) & 0xff00) | BIT_GRAN;
-}
-
-static void dbg_print_gdt(void)
-{
- int i;
-
- printf("Global Descriptor Table\n");
- printf("-----------------------\n");
-
- for(i=0; i<NUM_SEGMENTS; i++) {
- printf("%d: ", i);
- print_desc(gdt + i);
- }
-}
-
-static void print_desc(desc_t *desc)
-{
- uint32_t base, limit;
- int dpl, g;
-
- if((desc->d[0] | desc->d[1] | desc->d[2] | desc->d[3]) == 0) {
- printf("null\n");
- }
-
- base = (uint32_t)desc->d[1] | ((uint32_t)(desc->d[2] & 0xff) << 16) | ((uint32_t)(desc->d[3] >> 8) << 24);
- limit = (uint32_t)desc->d[0] | ((uint32_t)(desc->d[3] & 0xf) << 16);
- dpl = (desc->d[2] >> 13) & 3;
- g = (desc->d[3] >> 7) & 1;
-
- if(g) limit = ((limit + 1) << 12) - 1;
-
- printf("base:%x lim:%x dpl:%d type:%s %dbit\n", base, limit, dpl,
- desc->d[2] & BIT_CODE ? "code" : "data", desc->d[3] & BIT_BIG ? 32 : 16);
-}
-#endif
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef SEGM_H_
-#define SEGM_H_
-
-#include <inttypes.h>
-
-enum {
- SEGM_NULL,
- SEGM_CODE,
- SEGM_DATA,
- SEGM_XCODE, /* transition 32bit code with base matching the RM segment */
- SEGM_XDATA, /* same for data */
- SEGM_CODE16,
-
- NUM_SEGMENTS
-};
-
-void init_segm(void);
-
-uint16_t selector(int idx, int rpl);
-
-void set_tss(uint32_t addr);
-
-
-#endif /* SEGM_H_ */
+++ /dev/null
-; pcboot - bootable PC demo/game kernel
-; Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
-;
-; This program is free software: you can redistribute it and/or modify
-; it under the terms of the GNU General Public License as published by
-; the Free Software Foundation, either version 3 of the License, or
-; (at your option) any later version.
-;
-; This program is distributed in the hope that it will be useful,
-; but WITHOUT ANY WARRANTY, without even the implied warranty of
-; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-; GNU General Public License for more details.
-;
-; You should have received a copy of the GNU General Public License
-; along with this program. If not, see <https://www.gnu.org/licenses/>.
-
- section .data
- align 4
-; memory reserved for setup_selectors
-off: dd 0
-segm: dw 0
-; memory reserved for set_gdt
-lim: dw 0
-addr: dd 0
-
- section .text
-; setup_selectors(uint16_t code, uint16_t data)
-; loads the requested selectors to all the selector registers
- global setup_selectors
-setup_selectors:
- ; set data selectors directly
- mov eax, [esp + 8]
- mov ss, ax
- mov es, ax
- mov ds, ax
- mov gs, ax
- ;mov fs, ax ; XXX don't touch fs, we use it to store initial seg
- ; set cs using a long jump
- mov eax, [esp + 4]
- mov [segm], ax
- mov dword [off], .ldcs
- jmp [off]
-.ldcs: ret
-
-; set_gdt(uint32_t addr, uint16_t limit)
-; loads the GDTR with the new address and limit for the GDT
- global set_gdt
-set_gdt:
- mov eax, [esp + 4]
- mov [addr], eax
- mov ax, [esp + 8]
- mov [lim], ax
- lgdt [lim]
- ret
-
-; set_task_reg(uint16_t tss_selector)
-; loads the TSS selector in the task register
- global set_task_reg
-set_task_reg:
- mov eax, [esp + 4]
- ltr [esp + 4]
- ret
-
-; vi:set ts=8 sts=8 sw=8 ft=nasm:
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include "config.h"
-#include "serial.h"
-#include "asmops.h"
-#include "intr.h"
-#include "panic.h"
-
-#define UART1_BASE 0x3f8
-#define UART2_BASE 0x2f8
-#define UART1_IRQ 4
-#define UART2_IRQ 3
-
-#define UART_DATA 0
-#define UART_INTR 1
-#define UART_DIVLO 0
-#define UART_DIVHI 1
-#define UART_FIFO 2
-#define UART_IID 2
-#define UART_LCTL 3
-#define UART_MCTL 4
-#define UART_LSTAT 5
-#define UART_MSTAT 6
-
-/* interrupt enable register bits */
-#define INTR_RECV 1
-#define INTR_SEND 2
-#define INTR_LSTAT 4
-#define INTR_DELTA 8
-
-/* fifo control register bits */
-#define FIFO_ENABLE 0x01
-#define FIFO_RECV_CLEAR 0x02
-#define FIFO_SEND_CLEAR 0x04
-#define FIFO_DMA 0x08
-#define FIFO_TRIG_4 0x40
-#define FIFO_TRIG_8 0x80
-#define FIFO_TRIG_14 0xc0
-
-/* interrupt id register bits */
-#define IID_PENDING 0x01
-#define IID_ID0 0x02
-#define IID_ID1 0x04
-#define IID_ID2 0x08
-#define IID_FIFO_EN 0xc0
-
-#define IID_SOURCE 0xe
-
-#define IID_DELTA 0
-#define IID_SEND 0x2
-#define IID_RECV 0x4
-#define IID_FIFO 0xc
-#define IID_STATUS 0x6
-
-/* line control register bits */
-#define LCTL_BITS_8 0x03
-#define LCTL_STOP_2 0x04
-#define LCTL_DLAB 0x80
-#define LCTL_8N1 LCTL_BITS_8
-#define LCTL_8N2 (LCTL_BITS_8 | LCTL_STOP_2)
-
-/* modem control register bits */
-#define MCTL_DTR 0x01
-#define MCTL_RTS 0x02
-#define MCTL_OUT1 0x04
-#define MCTL_OUT2 0x08
-#define MCTL_LOOP 0x10
-
-/* line status register bits */
-#define LST_DRDY 0x01
-#define LST_ERR_OVER 0x02
-#define LST_ERR_PARITY 0x04
-#define LST_ERR_FRAME 0x08
-#define LST_ERR_BRK 0x10
-#define LST_TREG_EMPTY 0x20
-#define LST_TIDLE 0x40
-#define LST_ERROR 0x80
-
-/* modem status register bits */
-#define MST_DELTA_CTS 0x01
-#define MST_DELTA_DSR 0x02
-#define MST_TERI 0x04
-#define MST_DELTA_DCD 0x08
-#define MST_CTS 0x10
-#define MST_DSR 0x20
-#define MST_RING 0x40
-#define MST_DCD 0x80
-
-/* interrupt controller stuff */
-#define PIC1_CMD_PORT 0x20
-#define PIC1_DATA_PORT 0x21
-#define PIC2_CMD_PORT 0xa0
-#define PIC2_DATA_PORT 0xa1
-#define OCW2_EOI 0x20
-
-#define COM_FMT_8N1 LCTL_8N1
-#define COM_FMT_8N2 LCTL_8N2
-
-#define INBUF_SIZE 16
-#define BNEXT(x) (((x) + 1) & (INBUF_SIZE - 1))
-#define BEMPTY(b) (b##_ridx == b##_widx)
-
-struct serial_port {
- int base, intr;
- int blocking;
- int ref;
-
- char inbuf[INBUF_SIZE];
- int inbuf_ridx, inbuf_widx;
-};
-
-
-static int have_recv(int base);
-static void recv_intr();
-
-static struct serial_port ports[2];
-static int num_open;
-
-static int uart_base[] = {UART1_BASE, UART2_BASE};
-static int uart_irq[] = {UART1_IRQ, UART2_IRQ};
-
-int ser_open(int pidx, int baud, unsigned int mode)
-{
- unsigned short div = 115200 / baud;
- int i, fd, base, intr;
- unsigned int fmt;
-
- if(pidx < 0 || pidx > 1) {
- printf("ser_open: invalid serial port: %d\n", pidx);
- return -1;
- }
-
- for(i=0; i<num_open; i++) {
- if(ports[i].base == uart_base[pidx]) {
- /* the port is already open, return the same fd and increment ref */
- ports[i].ref++;
- return i;
- }
- }
-
- fd = num_open++;
- memset(ports + fd, 0, sizeof ports[pidx]);
-
- base = uart_base[pidx];
- intr = uart_irq[pidx];
-
- if(mode & SER_8N2) {
- fmt = COM_FMT_8N2;
- } else {
- fmt = COM_FMT_8N1;
- }
-
- interrupt(IRQ_TO_INTR(uart_irq[pidx]), recv_intr);
-
- outp(base + UART_LCTL, LCTL_DLAB);
- outp(base + UART_DIVLO, div & 0xff);
- outp(base + UART_DIVHI, (div >> 8) & 0xff);
- outp(base + UART_LCTL, fmt); /* fmt should be LCTL_8N1, LCTL_8N2 etc */
- outp(base + UART_FIFO, FIFO_ENABLE | FIFO_SEND_CLEAR | FIFO_RECV_CLEAR);
- outp(base + UART_MCTL, MCTL_DTR | MCTL_RTS | MCTL_OUT2);
- outp(base + UART_INTR, INTR_RECV);
-
- ports[fd].base = base;
- ports[fd].intr = intr;
- ports[fd].blocking = 1;
- ports[fd].ref = 1;
- return fd;
-}
-
-void ser_close(int fd)
-{
- if(!ports[fd].ref) return;
-
- if(--ports[fd].ref == 0) {
- outp(ports[fd].base + UART_INTR, 0);
- outp(ports[fd].base + UART_MCTL, 0);
- ports[fd].base = 0;
- }
-}
-
-int ser_block(int fd)
-{
- ports[fd].blocking = 1;
- return 0;
-}
-
-int ser_nonblock(int fd)
-{
- ports[fd].blocking = 0;
- return 0;
-}
-
-int ser_pending(int fd)
-{
- return !BEMPTY(ports[fd].inbuf);
-}
-
-/* if msec < 0: wait for ever */
-int ser_wait(int fd, long msec)
-{
- int res;
- while(!(res = ser_pending(fd))) {
- /* TODO timeout */
- }
- return res;
-}
-
-static int can_send(int fd)
-{
- int base = ports[fd].base;
- return inp(base + UART_LSTAT) & LST_TREG_EMPTY;
-}
-
-void ser_putc(int fd, char c)
-{
- int base = ports[fd].base;
-
- if(c == '\n') {
- ser_putc(fd, '\r');
- }
-
- while(!can_send(fd));
- /*while((inp(base + UART_MSTAT) & MST_CTS) == 0);*/
- outp(base + UART_DATA, c);
-}
-
-int ser_getc(int fd)
-{
- struct serial_port *p = ports + fd;
- int have, c = -1;
-
- if(p->blocking) {
- while(!(have = ser_pending(fd)));
- } else {
- have = ser_pending(fd);
- }
-
- if(have) {
- c = p->inbuf[p->inbuf_ridx];
- p->inbuf_ridx = BNEXT(p->inbuf_ridx);
- }
- return c;
-}
-
-int ser_write(int fd, const char *buf, int count)
-{
- int n = count;
- while(n--) {
- ser_putc(fd, *buf++);
- }
- return count;
-}
-
-int ser_read(int fd, char *buf, int count)
-{
- int c, n = 0;
- while(n < count && (c = ser_getc(fd)) != -1) {
- *buf++ = c;
- ++n;
- }
- return n;
-}
-
-char *ser_getline(int fd, char *buf, int bsz)
-{
- static int widx;
- char linebuf[512];
- int i, rd, size, offs;
-
- size = sizeof linebuf - widx;
- while(size && (rd = ser_read(fd, linebuf + widx, size)) > 0) {
- widx += rd;
- size -= rd;
- }
-
- linebuf[widx] = 0;
-
- for(i=0; i<widx; i++) {
- if(linebuf[i] == '\r' || linebuf[i] == '\n') {
- size = i >= bsz ? bsz - 1 : i;
- memcpy(buf, linebuf, size);
- buf[size] = 0;
-
- offs = i + 1;
- memmove(linebuf, linebuf + offs, widx - offs);
- widx -= offs;
- return buf;
- }
- }
- return 0;
-}
-
-static int have_recv(int base)
-{
- unsigned short stat = inp(base + UART_LSTAT);
- if(stat & LST_ERROR) {
- panic("serial receive error\n");
- }
- return stat & LST_DRDY;
-}
-
-static void recv_intr()
-{
- int i, idreg, c;
-
- for(i=0; i<2; i++) {
- int base = uart_base[i];
- struct serial_port *p = ports + i;
-
- while(((idreg = inp(base + UART_IID)) & IID_PENDING) == 0) {
- while(have_recv(base)) {
- c = inp(base + UART_DATA);
-
-#ifdef ENABLE_GDB_STUB
- if(c == 3 && i == GDB_SERIAL_PORT) {
- asm("int $3");
- continue;
- }
-#endif
-
- p->inbuf[p->inbuf_widx] = c;
- p->inbuf_widx = BNEXT(p->inbuf_widx);
-
- if(p->inbuf_widx == p->inbuf_ridx) {
- /* we overflowed, drop the oldest */
- p->inbuf_ridx = BNEXT(p->inbuf_ridx);
- }
- }
- }
- }
-}
-
-#ifdef ENABLE_GDB_STUB
-void putDebugChar(int c)
-{
- ser_putc(GDB_SERIAL_PORT, c);
-}
-
-int getDebugChar(void)
-{
- return ser_getc(GDB_SERIAL_PORT);
-}
-#endif
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef SERIAL_H_
-#define SERIAL_H_
-
-#define SER_8N1 0
-#define SER_8N2 1
-#define SER_HWFLOW 2
-
-int ser_open(int pidx, int baud, unsigned int mode);
-void ser_close(int fd);
-
-int ser_block(int fd);
-int ser_nonblock(int fd);
-
-int ser_pending(int fd);
-/* if msec < 0: wait for ever */
-int ser_wait(int fd, long msec);
-
-void ser_putc(int fd, char c);
-int ser_getc(int fd);
-
-int ser_write(int fd, const char *buf, int count);
-int ser_read(int fd, char *buf, int count);
-
-#define ser_putchar(c) ser_putc(0, c)
-
-char *ser_getline(int fd, char *buf, int bsz);
-
-
-#endif /* SERIAL_H_ */
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#include <stdio.h>
-#include "intr.h"
-#include "asmops.h"
-#include "timer.h"
-#include "panic.h"
-#include "config.h"
-
-/* frequency of the oscillator driving the 8254 timer */
-#define OSC_FREQ_HZ 1193182
-
-/* macro to divide and round to the nearest integer */
-#define DIV_ROUND(a, b) ((a) / (b) + ((a) % (b)) / ((b) / 2))
-
-/* I/O ports connected to the 8254 */
-#define PORT_DATA0 0x40
-#define PORT_DATA1 0x41
-#define PORT_DATA2 0x42
-#define PORT_CMD 0x43
-
-/* command bits */
-#define CMD_CHAN0 0
-#define CMD_CHAN1 (1 << 6)
-#define CMD_CHAN2 (2 << 6)
-#define CMD_RDBACK (3 << 6)
-
-#define CMD_LATCH 0
-#define CMD_ACCESS_LOW (1 << 4)
-#define CMD_ACCESS_HIGH (2 << 4)
-#define CMD_ACCESS_BOTH (3 << 4)
-
-#define CMD_OP_INT_TERM 0
-#define CMD_OP_ONESHOT (1 << 1)
-#define CMD_OP_RATE (2 << 1)
-#define CMD_OP_SQWAVE (3 << 1)
-#define CMD_OP_SOFT_STROBE (4 << 1)
-#define CMD_OP_HW_STROBE (5 << 1)
-
-#define CMD_MODE_BIN 0
-#define CMD_MODE_BCD 1
-
-volatile unsigned long nticks;
-
-struct timer_event {
- int dt; /* remaining ticks delta from the previous event */
- void (*func)(void);
- struct timer_event *next;
-};
-
-static void timer_handler(int inum);
-
-static struct timer_event *evlist;
-
-
-void init_timer(void)
-{
- /* calculate the reload count: round(osc / freq) */
- int reload_count = DIV_ROUND(OSC_FREQ_HZ, TICK_FREQ_HZ);
-
- /* set the mode to square wave for channel 0, both low
- * and high reload count bytes will follow...
- */
- outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE);
-
- /* write the low and high bytes of the reload count to the
- * port for channel 0
- */
- outp(PORT_DATA0, reload_count & 0xff);
- outp(PORT_DATA0, (reload_count >> 8) & 0xff);
-
- /* set the timer interrupt handler */
- interrupt(IRQ_TO_INTR(0), timer_handler);
- unmask_irq(0);
-}
-
-void cleanup_timer(void)
-{
- /* return the timer to the original rate */
- outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE);
- outp(PORT_DATA0, 0);
- outp(PORT_DATA0, 0);
-}
-
-void set_alarm(unsigned long msec, void (*func)(void))
-{
- int ticks, tsum, iflag;
- struct timer_event *ev, *node;
-
- if((ticks = MSEC_TO_TICKS(msec)) <= 0) {
- return;
- }
-
- if(!(ev = malloc(sizeof *ev))) {
- panic("failed to allocate timer event");
- return;
- }
- ev->func = func;
-
- iflag = get_intr_flag();
- disable_intr();
-
- if(!evlist || ticks < evlist->dt) {
- /* insert at the begining */
- ev->next = evlist;
- evlist = ev;
-
- ev->dt = ticks;
- if(ev->next) {
- ev->next->dt -= ticks;
- }
- } else {
- tsum = evlist->dt;
- node = evlist;
-
- while(node->next && ticks > tsum + node->next->dt) {
- tsum += node->next->dt;
- node = node->next;
- }
-
- ev->next = node->next;
- node->next = ev;
-
- /* fix the relative times */
- ev->dt = ticks - tsum;
- if(ev->next) {
- ev->next->dt -= ev->dt;
- }
- }
-
- set_intr_flag(iflag);
-}
-
-void cancel_alarm(void (*func)(void))
-{
- int iflag;
- struct timer_event *ev, *node;
- struct timer_event dummy;
-
- iflag = get_intr_flag();
- disable_intr();
-
- dummy.next = evlist;
- node = &dummy;
- while(node->next) {
- ev = node->next;
- if(ev->func == func) {
- /* found it */
- if(ev->next) {
- ev->next->dt += ev->dt;
- }
- node->next = ev->next;
- free(ev);
- break;
- }
- node = node->next;
- }
-
- set_intr_flag(iflag);
-}
-
-static void timer_handler(int inum)
-{
- nticks++;
-
- if(evlist) {
- evlist->dt--;
-
- while(evlist && evlist->dt <= 0) {
- struct timer_event *ev = evlist;
- evlist = evlist->next;
-
- ev->func();
- free(ev);
- }
- }
-}
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef _TIMER_H_
-#define _TIMER_H_
-
-#include "config.h"
-
-#define MSEC_TO_TICKS(ms) ((ms) * TICK_FREQ_HZ / 1000)
-#define TICKS_TO_MSEC(tk) ((tk) * 1000 / TICK_FREQ_HZ)
-
-extern volatile unsigned long nticks;
-
-void init_timer(void);
-void cleanup_timer(void);
-
-/*
-int sys_sleep(int sec);
-void sleep(unsigned long msec);
-*/
-
-/* schedule a function to be called 'msec' milliseconds into the future
- * warning: this function will be called directly from the timer interrupt, and
- * must be extremely quick.
- */
-void set_alarm(unsigned long msec, void (*func)(void));
-void cancel_alarm(void (*func)(void));
-
-#endif /* _TIMER_H_ */
+++ /dev/null
-/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY, without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <https://www.gnu.org/licenses/>.
-*/
-#ifndef TSS_H_
-#define TSS_H_
-
-#include <inttypes.h>
-
-struct task_state {
- uint32_t prev_task;
- uint32_t esp0, ss0; /* we only ever set these two values */
- uint32_t esp1, ss1;
- uint32_t esp2, ss2;
- uint32_t cr3;
- uint32_t eip;
- uint32_t eflags;
- uint32_t eax, ecx, edx, ebx;
- uint32_t esp, ebp, esi, edi;
- uint32_t es, cs, ss, ds, fs, gs;
- uint32_t ldt_sel;
- uint16_t trap, iomap_addr;
-} __attribute__((packed));
-
-#endif /* TSS_H_ */