--- /dev/null
+#ifndef BOOT_H_
+#define BOOT_H_
+
+extern unsigned char low_mem_buffer[];
+extern int boot_drive_number;
+
+#endif /* BOOT_H_ */
# pcboot - bootable PC demo/game kernel
-# Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+# 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
-# pcboot - bootable PC demo/game kernel
-# Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+# 256boss - bootable launcher for 256byte intros
+# 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
#define CRTC_REG_CURLOC_L 0x0f
#define VMEM_CHAR(c, attr) \
- ((uint16_t)(c) | ((uint16_t)(attr) << 8))
+ (((uint16_t)(c) & 0xff) | ((uint16_t)(attr) << 8))
static void scroll(void);
static void crtc_cursor(int x, int y);
static void crtc_setstart(int y);
static inline unsigned char crtc_read(int reg);
static inline void crtc_write(int reg, unsigned char val);
-static inline void crtc_write_bits(int reg, unsigned char val, unsigned char mask);
extern int cursor_x, cursor_y;
static unsigned char txattr = 0x07;
static int start_line;
+static unsigned char cy0, cy1;
+static int curvis;
+static int scr_on = 1;
int con_init(void)
{
#endif
#ifdef CON_TEXTMODE
+ cy0 = crtc_read(CRTC_REG_CURSTART);
+ curvis = cy0 & 0x20 ? 1 : 0;
+ cy0 &= 0x1f;
+ cy1 = crtc_read(CRTC_REG_CUREND) & 0x1f;
+
con_show_cursor(1);
crtc_setstart(0);
crtc_cursor(cursor_x, cursor_y);
- /*
- printf("curloc: %x %x\n", (unsigned int)crtc_read(CRTC_REG_CURLOC_H),
- (unsigned int)crtc_read(CRTC_REG_CURLOC_L));
- printf("curstart: %x\n", (unsigned int)crtc_read(CRTC_REG_CURSTART));
- printf("curend: %x\n", (unsigned int)crtc_read(CRTC_REG_CUREND));
- */
+ 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 = show ? 0 : 0x20;
-
- crtc_write_bits(CRTC_REG_CURSTART, val, 0x20);
+ unsigned char val = cy0 & 0x1f;
+ if(!show) {
+ val |= 0x20;
+ }
+ crtc_write(CRTC_REG_CURSTART, val);
+ curvis = show;
#endif
}
#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;
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
- memset(TEXT_ADDR, 0, NCOLS * NROWS * 2);
+ memset16(TEXT_ADDR, VMEM_CHAR(' ', txattr), NCOLS * NROWS);
start_line = 0;
crtc_setstart(0);
void con_putchar(int c)
{
#ifdef CON_TEXTMODE
- uint16_t *ptr;
-
- 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;
-
- default:
- con_putchar_scr(cursor_x, cursor_y, c);
-
- if(++cursor_x >= NCOLS) {
+ 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);
}
- crtc_cursor(cursor_x, cursor_y);
}
#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
}
-void con_printf(int x, int y, const char *fmt, ...)
+int con_printf(int x, int y, const char *fmt, ...)
{
+#ifdef CON_TEXTMODE
va_list ap;
char buf[81];
char *ptr = buf;
while(*ptr && x < 80) {
con_putchar_scr(x++, y, *ptr++);
}
+ return ptr - buf;
+#else
+ return 0;
+#endif
}
static void scroll(void)
outb(reg, CRTC_ADDR);
outb(val, CRTC_DATA);
}
-
-static inline void crtc_write_bits(int reg, unsigned char val, unsigned char mask)
-{
- unsigned char prev;
- outb(reg, CRTC_ADDR);
- prev = inb(CRTC_DATA);
- val = (prev & ~mask) | (val & mask);
- outb(val, CRTC_DATA);
-}
#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);
-void con_printf(int x, int y, const char *fmt, ...);
+int con_printf(int x, int y, const char *fmt, ...);
#endif /* CONTTY_H_ */
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"
void init_pic(void)
{
+ prog_pic(IRQ_OFFSET);
+}
+
+void prog_pic(int offs)
+{
/* send ICW1 saying we'll follow with ICW4 later on */
outb(ICW1_INIT | ICW1_ICW4_NEEDED, PIC1_CMD);
outb(ICW1_INIT | ICW1_ICW4_NEEDED, PIC2_CMD);
/* send ICW2 with IRQ remapping */
- outb(IRQ_OFFSET, PIC1_DATA);
- outb(IRQ_OFFSET + 8, PIC2_DATA);
+ outb(offs, PIC1_DATA);
+ outb(offs + 8, PIC2_DATA);
/* send ICW3 to setup the master/slave relationship */
/* ... set bit3 = 3rd interrupt input has a slave */
outb(4, PIC1_DATA);
set_intr_flag(intr_state);
}
+
+#ifdef ENABLE_GDB_STUB
+void exceptionHandler(int id, void (*func)())
+{
+ set_intr_entry(id, func);
+}
+#endif
*/
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);
#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_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
--- /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 */
+ 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 */
+};
+
+/* extended scancodes, after the 0xe0 prefix */
+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 /* KBSCAN_H_ */
/*
pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+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
#include "intr.h"
#include "asmops.h"
#include "kbregs.h"
+#include "kbscan.h"
+#include "power.h"
+#include "panic.h"
#define delay7us() \
do { \
iodelay(); iodelay(); iodelay(); \
} while(0)
-/* table with rough translations from set 1 scancodes to ASCII-ish */
-static int scantbl[] = {
- 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 */
- 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 */
-};
+static void set_ccb(unsigned char ccb);
+static unsigned char get_ccb(void);
static void kbintr();
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();
+}
+
+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)
return inb(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 = inb(KB_DATA_PORT);
- if(code >= 128) {
+ if(code == 0xe0) {
+ ext = 1;
+ return;
+ }
+
+ if(code & 0x80) {
press = 0;
- code -= 128;
+ code &= 0x7f;
if(num_pressed > 0) {
num_pressed--;
num_pressed++;
}
- key = scantbl[code];
+ key = ext ? scantbl_set1_ext[code] : scantbl_set1[code];
+ 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);
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.
*/
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef ALLOCA_H_
+#define ALLOCA_H_
+
+#define alloca(x) __builtin_alloca(x)
+
+#endif /* ALLOCA_H_ */
int tolower(int c)
{
- return isupper(c) ? (c + ('A' - 'a')) : c;
+ return isupper(c) ? (c - ('A' - 'a')) : c;
}
#define ENOSPC 10
#define EPERM 11
#define ENOTDIR 12
+#define EISDIR 13
+#define EEXIST 14
+#define ERANGE 34
#define EBUG 127 /* for missing features and known bugs */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef LIMITS_H_
+#define LIMITS_H_
+
+#define CHAR_BIT 8
+
+#define SHRT_MIN (-32768)
+#define SHRT_MAX 32767
+#define INT_MIN (-2147483648)
+#define INT_MAX 2147483647
+#define LONG_MIN (-2147483648)
+#define LONG_MAX 2147483647
+
+#define USHRT_MAX 65535
+#define UINT_MAX 0xffffffff
+#define ULONG_MAX 0xffffffff
+
+#define PATH_MAX 256
+
+#endif /* LIMITS_H_ */
/*
pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+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
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <assert.h>
+#include "config.h"
#include "mem.h"
#include "panic.h"
+#define SINGLE_POOL
+
struct mem_desc {
size_t size;
uint32_t magic;
+#ifdef MALLOC_DEBUG
+ uint32_t dbg;
+#endif
struct mem_desc *next;
};
-#define MAGIC 0xdeadf00d
+#ifdef MALLOC_DEBUG
+static void check_cycles(struct mem_desc *mem);
+static void print_pool(void);
+#endif
+
+#define MAGIC_USED 0xdeadf00d
+#define MAGIC_FREE 0x1ee7d00d
#define DESC_PTR(b) ((void*)((struct mem_desc*)(b) + 1))
#define PTR_DESC(p) ((struct mem_desc*)(p) - 1)
+#ifdef SINGLE_POOL
+static struct mem_desc *pool;
+#else
+
#define NUM_POOLS 16
#define FIRST_POOL_POW2 5
/* 2**(x+5) size pools: 0->32, 1->64, 2->128 .. 15->1048576 */
}
return x - FIRST_POOL_POW2;
}
+#endif /* !SINGLE_POOL */
+
+
+#ifdef SINGLE_POOL
+#define MIN_BLOCK_SIZE (sizeof(struct mem_desc) * 2)
+
+void *malloc(size_t sz)
+{
+ int pg0, npages;
+ size_t total_sz, rest_sz;
+ struct mem_desc *mem, *rest, *prev, dummy;
+ int found = 0;
+
+ total_sz = (sz + sizeof(struct mem_desc) + 3) & 0xfffffffc;
+
+ dummy.next = pool;
+ prev = &dummy;
+ while(prev->next) {
+ mem = prev->next;
+ /* give the whole block to the allocation if mem->size == total_sz or
+ * if it's larger, but not large enough to fit another mem_desc in there
+ * for the new block that we're going to split off + some reasonable
+ * amount of memory for the new block.
+ */
+ if(mem->size >= total_sz && mem->size < total_sz + MIN_BLOCK_SIZE) {
+ prev->next = mem->next;
+ found = 1;
+ break;
+ }
+ /* if we have enough space, split the block and give the upper part
+ * to the allocation
+ */
+ if(mem->size > total_sz) {
+ void *ptr = (char*)mem + mem->size - total_sz;
+ mem->size -= total_sz;
+ mem = ptr;
+ found = 1;
+ break;
+ }
+ prev = prev->next;
+ }
+ pool = dummy.next;
+
+ if(found) {
+ mem->size = total_sz;
+ mem->magic = MAGIC_USED;
+ mem->next = 0;
+ return DESC_PTR(mem);
+ }
+
+ /* did not find a free block, grab a new one */
+ npages = BYTES_TO_PAGES(total_sz);
+ if((pg0 = alloc_ppages(npages, 0)) == -1) {
+ errno = ENOMEM;
+ return 0;
+ }
+ mem = PAGE_TO_PTR(pg0);
+ mem->size = total_sz;
+ mem->next = 0;
+ mem->magic = MAGIC_USED;
+
+ /* add the rest of the block to pool */
+ rest_sz = npages * 4096 - total_sz;
+ if(rest_sz > 0) {
+ rest = (struct mem_desc*)((char*)mem + total_sz);
+ rest->size = rest_sz;
+ rest->next = 0;
+ rest->magic = MAGIC_USED;
+ free(DESC_PTR(rest));
+ }
+
+ return DESC_PTR(mem);
+}
+
+void free(void *p)
+{
+ struct mem_desc *mem, *prev, *cur, dummy;
+ char *end;
+
+ if(!p) return;
+ mem = PTR_DESC(p);
+
+ if(mem->magic != MAGIC_USED) {
+ if(mem->magic == MAGIC_FREE) {
+ panic("free(%p): double-free\n", p);
+ } else {
+ panic("free(%p): corrupted magic (%x)!\n", p, mem->magic);
+ }
+ }
+ mem->magic = MAGIC_FREE;
+ mem->next = 0;
+
+ /* nothing in the pool, just add this one */
+ if(!pool) {
+ pool = mem;
+ return;
+ }
+
+ end = (char*)mem + mem->size;
+
+ dummy.next = pool;
+ prev = &dummy;
+
+ while(prev->next) {
+ cur = prev->next;
+
+ /* block starts right at the end of mem: coalesce */
+ if((char*)cur == end) {
+ mem->size += cur->size;
+ mem->next = cur->next;
+ cur->magic = 0;
+ prev->next = mem;
+ goto done;
+ }
+
+ /* block starts *after* the end of mem: add in front */
+ if((char*)cur > end) {
+ mem->next = cur;
+ prev->next = mem;
+ goto done;
+ }
+
+ prev = prev->next;
+ }
+
+ /* our block starts at the end of the last block in the pool: coalesce */
+ if((char*)prev + prev->size == (char*)mem) {
+ mem->magic = 0;
+ prev->size += mem->size;
+ goto done;
+ }
+
+ /* so our block starts after the end of the last block: append */
+ prev->next = mem;
+
+done:
+ pool = dummy.next;
+
+#ifdef MALLOC_DEBUG
+ print_pool();
+#endif
+}
+
+#else /* !SINGLE_POOL */
void *malloc(size_t sz)
{
if(total_sz > MAX_POOL_SIZE) {
/*printf(" allocation too big, hitting sys_alloc directly\n");*/
- if((pg0 = alloc_ppages(BYTES_TO_PAGES(total_sz))) == -1) {
+ if((pg0 = alloc_ppages(BYTES_TO_PAGES(total_sz), 0)) == -1) {
errno = ENOMEM;
return 0;
}
mem = PAGE_TO_PTR(pg0);
- mem->magic = MAGIC;
+ mem->magic = MAGIC_USED;
mem->size = total_sz;
mem->next = 0;
return DESC_PTR(mem);
add_to_pool(other);
}
- mem->magic = MAGIC;
+ if(mem->magic != MAGIC_FREE) {
+ panic("Trying to allocate range surrounded by an aura of wrong MAGIC\n");
+ }
+ mem->magic = MAGIC_USED;
return DESC_PTR(mem);
}
/* did not find a free block, add a new one */
pidx = NUM_POOLS - 1;
- if((pg0 = alloc_ppages(MAX_POOL_PAGES)) == -1) {
+ if((pg0 = alloc_ppages(MAX_POOL_PAGES, 0)) == -1) {
errno = ENOMEM;
return 0;
}
mem = PAGE_TO_PTR(pg0);
mem->size = MAX_POOL_SIZE;
mem->next = pools[pidx];
- mem->magic = MAGIC;
+ mem->magic = MAGIC_FREE;
pools[pidx] = mem;
/* try again now that there is a free block */
void free(void *p)
{
int pg0;
- struct mem_desc *mem = PTR_DESC(p);
+ struct mem_desc *mem;
+
+ if(!p) return;
- if(mem->magic != MAGIC) {
- panic("free: corrupted magic!\n");
+ mem = PTR_DESC(p);
+ if(mem->magic != MAGIC_USED) {
+ if(mem->magic == MAGIC_FREE) {
+ panic("free(%p): double-free\n", p);
+ } else {
+ panic("free(%p): corrupted magic (%x)!\n", p, mem->magic);
+ }
}
if(mem->size > MAX_POOL_SIZE) {
add_to_pool(mem);
}
+#endif /* !def SINGLE_POOL */
+
void *calloc(size_t num, size_t size)
{
if(!(newp = malloc(size))) {
return 0;
}
+ memcpy(newp, ptr, mem->size);
free(ptr);
return newp;
}
+#ifdef MALLOC_DEBUG
+static void check_cycles(struct mem_desc *mem)
+{
+ static uint32_t dbg = 42;
+ uint32_t cur_dbg = dbg++;
+ while(mem) {
+ if(mem->magic != MAGIC_FREE) {
+ panic("check_cycles: NON-FREE MAGIC!\n");
+ }
+ if(mem->dbg == cur_dbg) {
+ panic("CYCLE DETECTED\n");
+ }
+ mem->dbg = cur_dbg;
+ mem = mem->next;
+ }
+}
+
+static void print_pool(void)
+{
+ struct mem_desc *mem = pool;
+
+ printf("DBG: malloc pool:\n");
+ while(mem) {
+ printf(" %p (%ld) [%x]\n", mem, mem->size, mem->magic);
+ mem = mem->next;
+
+ assert((uint32_t)mem != MAGIC_USED);
+ }
+}
+#endif /* MALLOC_DEBUG */
+
+#ifndef SINGLE_POOL
static int add_to_pool(struct mem_desc *mem)
{
int pidx;
while(iter->next) {
pnode = iter->next;
if(mem->size == pnode->size) { /* only coalesce same-sized blocks */
- if((char*)mem == (char*)pnode - pnode->size) {
+ size_t size = mem->size;
+
+ if((char*)mem == (char*)pnode - size) {
iter->next = pnode->next; /* unlink pnode */
pools[pidx] = head.next;
mem->next = 0;
- mem->size += pnode->size;
+ mem->size += size;
/*printf(" coalescing %p with %p and ", (void*)mem, (void*)pnode);*/
return add_to_pool(mem);
}
- if((char*)mem == (char*)pnode + pnode->size) {
+ if((char*)mem == (char*)pnode + size) {
iter->next = pnode->next; /* unlink pnode */
pools[pidx] = head.next;
pnode->next = 0;
- pnode->size += mem->size;
+ pnode->size += size;
/*printf(" coalescing %p with %p and ", (void*)mem, (void*)pnode);*/
return add_to_pool(pnode);
}
/* otherwise just add it to the pool */
+ mem->magic = MAGIC_FREE;
mem->next = pools[pidx];
pools[pidx] = mem;
+
+#ifdef MALLOC_DEBUG
+ check_cycles(pools[pidx]);
+#endif
return 0;
}
-
+#endif /* !def SINGLE_POOL */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef STDDEF_H_
+#define STDDEF_H_
+
+#include <inttypes.h>
+
+typedef int32_t ssize_t;
+typedef uint32_t size_t;
+typedef int wchar_t;
+
+typedef int32_t ptrdiff_t;
+typedef uint32_t intptr_t;
+
+#define NULL 0
+
+#endif /* STDDEF_H_ */
/*
pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+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
#include <stdio.h>
#include <string.h>
#include <ctype.h>
+#include <errno.h>
#include "contty.h"
#include "serial.h"
+#include "panic.h"
enum {
OUT_DEF,
extern void pcboot_putchar(int c);
-static void bwrite(int out, char *buf, size_t buf_sz, char *str, int sz);
static int intern_printf(int out, char *buf, size_t sz, const char *fmt, va_list ap);
+static int intern_scanf(const char *instr, FILE *infile, const char *fmt, va_list ap);
+static void bwrite(int out, char *buf, size_t buf_sz, char *str, int sz);
+/*static int readchar(const char *str, FILE *fp);*/
int putchar(int c)
{
/* -- printf and friends -- */
-static char *convc = "dioxXucsfeEgGpn%";
-
-#define IS_CONV(c) strchr(convc, c)
-
int printf(const char *fmt, ...)
{
int res;
return intern_printf(OUT_BUF, buf, sz, fmt, ap);
}
+int fprintf(FILE *fp, const char *fmt, ...)
+{
+ int res;
+ va_list ap;
+
+ va_start(ap, fmt);
+ res = vfprintf(fp, fmt, ap);
+ va_end(ap);
+ return res;
+}
+
+int vfprintf(FILE *fp, const char *fmt, va_list ap)
+{
+ if(fp == stdout || fp == stderr) {
+ return vprintf(fmt, ap);
+ }
+
+ panic("*fprintf for anything other than stdout/stderr, not implemented yet\n");
+ return 0;
+}
+
int ser_printf(const char *fmt, ...)
{
int res;
return intern_printf(OUT_SER, 0, 0, fmt, ap);
}
+void perror(const char *s)
+{
+ printf("%s: %s\n", s, strerror(errno));
+}
+
/* intern_printf provides all the functionality needed by all the printf
* variants.
* - buf: optional buffer onto which the formatted results are written. If null
* by the (v)snprintf variants to avoid buffer overflows.
* The rest are obvious, format string and variable argument list.
*/
+static char *convc = "dioxXucsfeEgGpn%";
+
+#define IS_CONV(c) strchr(convc, c)
#define BUF(x) ((x) ? (x) + cnum : (x))
#define SZ(x) ((x) ? (x) - cnum : (x))
int fwidth = 0;
int padc = ' ';
int sign = 0;
- int left_align = 0; /* not implemented yet */
+ int left_align = 0;
int hex_caps = 0;
int unsig = 0;
+ int num, unum;
while(*fmt) {
if(*fmt == '%') {
if(alt) {
bwrite(out, BUF(buf), SZ(sz), "0x", 2);
+ cnum += 2;
}
case 'u':
if(alt) {
bwrite(out, BUF(buf), SZ(sz), "0", 1);
+ cnum++;
}
}
case 'd':
case 'i':
if(unsig) {
- utoa(va_arg(ap, unsigned int), conv_buf, base);
+ unum = va_arg(ap, unsigned int);
+ utoa(unum, conv_buf, base);
} else {
- itoa(va_arg(ap, int), conv_buf, base);
+ num = va_arg(ap, int);
+ itoa(num, conv_buf, base);
}
if(hex_caps) {
for(i=0; conv_buf[i]; i++) {
}
slen = strlen(conv_buf);
+
+ if(left_align) {
+ if(!unsig && sign && num >= 0) {
+ bwrite(out, BUF(buf), SZ(sz), "+", 1);
+ cnum++;
+ }
+ bwrite(out, BUF(buf), SZ(sz), conv_buf, slen);
+ cnum += slen;
+ padc = ' ';
+ }
for(i=slen; i<fwidth; i++) {
bwrite(out, BUF(buf), SZ(sz), (char*)&padc, 1);
cnum++;
}
-
- bwrite(out, BUF(buf), SZ(sz), conv_buf, strlen(conv_buf));
- cnum += slen;
+ if(!left_align) {
+ if(!unsig && sign && num >= 0) {
+ bwrite(out, BUF(buf), SZ(sz), "+", 1);
+ cnum++;
+ }
+ bwrite(out, BUF(buf), SZ(sz), conv_buf, slen);
+ cnum += slen;
+ }
break;
case 'c':
str = va_arg(ap, char*);
slen = strlen(str);
+ if(left_align) {
+ bwrite(out, BUF(buf), SZ(sz), str, slen);
+ cnum += slen;
+ padc = ' ';
+ }
for(i=slen; i<fwidth; i++) {
bwrite(out, BUF(buf), SZ(sz), (char*)&padc, 1);
cnum++;
}
- bwrite(out, BUF(buf), SZ(sz), str, slen);
- cnum += slen;
+ if(!left_align) {
+ bwrite(out, BUF(buf), SZ(sz), str, slen);
+ cnum += slen;
+ }
break;
case 'n':
}
}
- return 0;
+ return cnum;
+}
+
+
+#if 0
+static char *sconvc = "diouxcsefg%";
+
+#define IS_SCONV(c) strchr(sconvc, c)
+
+static int intern_scanf(const char *instr, FILE *infile, const char *fmt, va_list ap)
+{
+ return -1; /* TODO */
}
+#endif
/* bwrite is called by intern_printf to transparently handle writing into a
int i;
if(out == OUT_BUF) {
- if(buf_sz && buf_sz <= sz) sz = buf_sz - 1;
- memcpy(buf, str, sz);
-
+ if(buf_sz && buf_sz <= sz) sz = buf_sz;
buf[sz] = 0;
+ memcpy(buf, str, sz);
} else {
switch(out) {
case OUT_DEF:
}
}
+/*
+static int readchar(const char *str, FILE *fp)
+{
+ static const char *orig_str;
+ static const char *sptr;
+
+ if(str) {
+ if(str == orig_str) {
+ if(!*sptr) return -1;
+ return *sptr++;
+ } else {
+ orig_str = sptr = str;
+ return readchar(str, fp);
+ }
+ } else {
+ return fgetc(fp);
+ }
+
+ return -1;
+}
+*/
/*
pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+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
#include <stdlib.h>
#include <stdarg.h>
+typedef struct FILE FILE;
+
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+
+#define EOF (-1)
+
+#define stdin ((FILE*)0)
+#define stdout ((FILE*)1)
+#define stderr ((FILE*)2)
+
int putchar(int c);
int puts(const char *s);
int snprintf(char *buf, size_t sz, const char *fmt, ...);
int vsnprintf(char *buf, size_t sz, const char *fmt, va_list ap);
+/* TODO */
+int fprintf(FILE *fp, const char *fmt, ...);
+int vfprintf(FILE *fp, const char *fmt, va_list ap);
+
+/* TODO
+int fscanf(FILE *fp, const char *fmt, ...);
+int vfscanf(FILE *fp, const char *fmt, va_list ap);
+
+int sscanf(const char *str, const char *fmt, ...);
+int vsscanf(const char *ptr, const char *fmt, va_list ap);
+*/
+
/* printf to the serial port */
int ser_printf(const char *fmt, ...);
int ser_vprintf(const char *fmt, va_list ap);
+void perror(const char *s);
+
+
+/* FILE I/O */
+FILE *fopen(const char *path, const char *mode);
+int fclose(FILE *fp);
+
+long filesize(FILE *fp);
+int fseek(FILE *fp, long offset, int from);
+void rewind(FILE *fp);
+long ftell(FILE *fp);
+
+size_t fread(void *buf, size_t size, size_t count, FILE *fp);
+size_t fwrite(const void *buf, size_t size, size_t count, FILE *fp);
+
+int fgetc(FILE *fp);
+char *fgets(char *buf, int size, FILE *fp);
+
+int fputc(int c, FILE *fp);
+
+int fflush(FILE *fp);
+
+int feof(FILE *fp);
+int ferror(FILE *fp);
+void clearerr(FILE *fp);
+
#endif /* STDIO_H_ */
/*
pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+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
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
+#include <string.h>
#include <ctype.h>
+#include <limits.h>
+#include <assert.h>
+#include <alloca.h>
int atoi(const char *str)
{
{
long acc = 0;
int sign = 1;
+ int valid = 0;
+ const char *start = str;
while(isspace(*str)) str++;
}
while(*str) {
- long val;
+ long val = LONG_MAX;
char c = tolower(*str);
if(isdigit(c)) {
val = *str - '0';
- } else if(c >= 'a' || c <= 'f') {
+ } else if(c >= 'a' && c <= 'f') {
val = 10 + c - 'a';
+ } else {
+ break;
}
if(val >= base) {
break;
}
+ valid = 1;
acc = acc * base + val;
str++;
}
if(endp) {
- *endp = (char*)str;
+ *endp = (char*)(valid ? str : start);
}
return sign > 0 ? acc : -acc;
*buf = 0;
}
+double atof(const char *str)
+{
+ return strtod(str, 0);
+}
+
+
+double strtod(const char *str, char **endp)
+{
+ char *ep;
+ const char *start = str;
+ int valid = 0;
+ long ival = 0, dval = 0;
+ int ddig = 0;
+ double res;
+
+ /* integer part */
+ ival = strtol(str, &ep, 10);
+ if(ep == str && *str != '.') {
+ if(endp) *endp = (char*)str;
+ return 0.0;
+ }
+ if(ep != str) valid = 1;
+ str = *ep == '.' ? ep + 1 : ep;
+ if(!isdigit(*str)) {
+ goto done;
+ }
+ valid = 1;
+
+ dval = strtol(str, &ep, 10);
+ assert(dval >= 0);
+ ddig = ep - str;
+ str = ep;
+
+done:
+ if(*endp) {
+ *endp = (char*)(valid ? str : start);
+ }
+
+ res = (double)ival;
+ if(ddig) {
+ double d = (double)dval;
+ while(ddig-- > 0) {
+ d /= 10.0;
+ }
+ res += d;
+ }
+ return res;
+}
+
+int atexit(void (*func)(void))
+{
+ /* there's no concept of exiting at the moment, so this does nothing */
+ return 0;
+}
+
+void abort(void)
+{
+ panic("Aborted\n");
+}
+
+#define QSORT_THRESHOLD 4
+#define ITEM(idx) ((char*)arr + (idx) * itemsz)
+
+#define SWAP(p, q) \
+ do { \
+ int nn = itemsz; \
+ char *pp = (p); \
+ char *qq = (q); \
+ do { \
+ char tmp = *pp; \
+ *pp++ = *qq; \
+ *qq++ = tmp; \
+ } while(--nn > 0); \
+ } while(0)
+
+static void ins_sort(void *arr, size_t count, size_t itemsz, int (*cmp)(const void*, const void*))
+{
+ int i;
+ char *it, *a, *b;
+
+ if(count <= 1) return;
+
+ it = (char*)arr + itemsz;
+ for(i=1; i<count; i++) {
+ a = it;
+ it += itemsz;
+ while(a > (char*)arr && cmp(a, (b = a - itemsz)) < 0) {
+ SWAP(a, b);
+ a -= itemsz;
+ }
+ }
+}
+
+void qsort(void *arr, size_t count, size_t itemsz, int (*cmp)(const void*, const void*))
+{
+ char *ma, *mb, *mc, *left, *right;
+ size_t sepidx, nleft, nright;
+
+ if(count <= 1) return;
+
+ if(count < QSORT_THRESHOLD) {
+ ins_sort(arr, count, itemsz, cmp);
+ return;
+ }
+
+ ma = arr;
+ mb = ITEM(count / 2);
+ mc = ITEM(count - 1);
+ if(cmp(ma, mb) < 0) SWAP(ma, mb);
+ if(cmp(mc, ma) < 0) SWAP(mc, ma);
+
+ left = ma + itemsz;
+ right = mc - itemsz;
+ for(;;) {
+ while(cmp(left, ma) < 0) left += itemsz;
+ while(cmp(ma, right) < 0) right -= itemsz;
+ if(left >= right) break;
+ SWAP(left, right);
+ }
+ SWAP(ma, right);
+ sepidx = (right - (char*)arr) / itemsz;
+ nleft = sepidx;
+ nright = count - nleft - 1;
+
+ qsort(ma, nleft, itemsz, cmp);
+ qsort(right + itemsz, nright, itemsz, cmp);
+}
/*
pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+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
#ifndef STDLIB_H_
#define STDLIB_H_
-#include <inttypes.h>
+#include <stddef.h>
-typedef int32_t ssize_t;
-typedef uint32_t size_t;
+#define RAND_MAX 2147483647
+
+#define abs(x) __builtin_abs(x)
int atoi(const char *str);
long atol(const char *str);
void itoa(int val, char *buf, int base);
void utoa(unsigned int val, char *buf, int base);
+double atof(const char *str);
+double strtod(const char *str, char **endp);
+
+int atexit(void (*func)(void));
+
+void abort(void);
+
+void qsort(void *arr, size_t count, size_t size, int (*cmp)(const void*, const void*));
+
+int rand(void);
+int rand_r(unsigned int *seedp);
+void srand(unsigned int seed);
+
/* defined in malloc.c */
void *malloc(size_t sz);
void *calloc(size_t num, size_t sz);
/*
pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+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
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <string.h>
-
-/*
-void memset(void *s, int c, size_t n)
-{
- char *ptr = s;
- while(n--) {
- *ptr++ = c;
- }
-}
-*/
-
-/* Does the same thing as memset only with 16bit values.
- * n in this case is the number of values, not the number of bytes.
- */
-/*
-void memset16(void *s, int c, size_t n)
-{
- int16_t *ptr = s;
- while(n--) {
- *ptr++ = c;
- }
-}
-*/
-/*
-void *memcpy(void *dest, const void *src, size_t n)
-{
- char *dptr = dest;
- const char *sptr = src;
-
- while(n--) {
- *dptr++ = *sptr++;
- }
- return dest;
-}
-*/
+#include <ctype.h>
+#include <stddef.h>
void *memmove(void *dest, const void *src, size_t n)
{
return dest;
}
+int memcmp(void *aptr, void *bptr, size_t n)
+{
+ int i, startoffs, diff;
+ uint32_t *a32, *b32;
+ unsigned char *a = aptr;
+ unsigned char *b = bptr;
+
+ a32 = (uint32_t*)((intptr_t)(a + 3) & 0xfffffffc);
+ b32 = (uint32_t*)((intptr_t)(b + 3) & 0xfffffffc);
+
+ /* if both are aligned the same way... */
+ if((startoffs = (unsigned char*)a32 - a) == (unsigned char*)b32 - b) {
+ /* catch-up to the 32bit alignment */
+ for(i=0; i<startoffs; i++) {
+ if((diff = *a++ - *b++) != 0 || --n <= 0) {
+ return diff;
+ }
+ }
+
+ /* compare 32bit at once */
+ while(n >= 4) {
+ if(*a32 != *b32) break;
+ a32++;
+ b32++;
+ n -= 4;
+ }
+
+ /* update byte pointers to contine with the tail */
+ a = (unsigned char*)a32;
+ b = (unsigned char*)b32;
+ }
+
+ /* we're here both for the tail-end of same-alignment buffers, or for the
+ * whole length of mis-aligned buffers.
+ */
+ while(n-- > 0) {
+ if((diff = *a++ - *b++) != 0) {
+ return diff;
+ }
+ }
+ return 0;
+}
+
size_t strlen(const char *s)
{
size_t len = 0;
return 0;
}
+char *strcasestr(const char *str, const char *substr)
+{
+ while(*str) {
+ const char *s1 = str;
+ const char *s2 = substr;
+
+ while(*s1 && tolower(*s1) == tolower(*s2)) {
+ s1++;
+ s2++;
+ }
+ if(!*s2) {
+ return (char*)str;
+ }
+ str++;
+ }
+ return 0;
+}
+
int strcmp(const char *s1, const char *s2)
{
while(*s1 && *s1 == *s2) {
}
return *s1 - *s2;
}
+
+int strcasecmp(const char *s1, const char *s2)
+{
+ while(*s1 && tolower(*s1) == tolower(*s2)) {
+ s1++;
+ s2++;
+ }
+ return tolower(*s1) - tolower(*s2);
+}
+
+int strncmp(const char *s1, const char *s2, int n)
+{
+ if(n <= 0) return 0;
+
+ while(n-- > 0 && *s1 && *s2 && *s1 == *s2) {
+ s1++;
+ s2++;
+ }
+
+ if(n <= 0) return 0;
+ return *s1 - *s2;
+}
+
+int strncasecmp(const char *s1, const char *s2, int n)
+{
+ if(n <= 0) return 0;
+
+ while(n-- > 0 && *s1 && *s2 && tolower(*s1) == tolower(*s2)) {
+ s1++;
+ s2++;
+ }
+
+ if(n <= 0) return 0;
+ return tolower(*s1) - tolower(*s2);
+}
+
+char *strcpy(char *dest, const char *src)
+{
+ char *dptr = dest;
+ while((*dptr++ = *src++));
+ return dest;
+}
+
+char *strcat(char *dest, const char *src)
+{
+ strcpy(dest + strlen(dest), src);
+ return dest;
+}
+
+char *strncpy(char *dest, const char *src, int n)
+{
+ char *dptr = dest;
+ while(n-- > 0 && (*dptr++ = *src++));
+ return dest;
+}
+
+
+static const char *errstr[] = {
+ "Success",
+ "Foo",
+ "Interrupted",
+ "Invalid",
+ "Child",
+ "Timeout",
+ "Out of memory",
+ "I/O error",
+ "Not found",
+ "Name too long",
+ "No space left on device",
+ "Permission denied",
+ "Not a directory",
+ "Is a directory",
+ "Does not exist",
+ 0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,
+ "Bug"
+};
+
+char *strerror(int err)
+{
+ if(err < 0 || err > sizeof errstr / sizeof *errstr || !errstr[err]) {
+ return "Unknown";
+ }
+ return (char*)errstr[err];
+}
/*
pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+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
#include <stdlib.h>
-void memset(void *s, int c, size_t n);
-void memset16(void *s, int c, size_t n);
+void *memset(void *s, int c, size_t n);
+void *memset16(void *s, int c, size_t n);
void *memcpy(void *dest, const void *src, size_t n);
void *memmove(void *dest, const void *src, size_t n);
+int memcmp(void *aptr, void *bptr, size_t n);
+
size_t strlen(const char *s);
char *strchr(const char *s, int c);
char *strrchr(const char *s, int c);
char *strstr(const char *str, const char *substr);
+char *strcasestr(const char *str, const char *substr);
int strcmp(const char *s1, const char *s2);
+int strcasecmp(const char *s1, const char *s2);
+
+int strncmp(const char *s1, const char *s2, int n);
+int strncasecmp(const char *s1, const char *s2, int n);
+
+char *strcpy(char *dest, const char *src);
+char *strcat(char *dest, const char *src);
+
+char *strncpy(char *dest, const char *src, int n);
+
+char *strerror(int err);
#endif /* STRING_H_ */
push %edi
mov 8(%ebp), %edi
+ push %edi
mov 12(%ebp), %al
mov %al, %ah
mov %ax, %cx
cmp $0, %ecx
jz msdone
+ # write 1, 2, or 3 times until we reache a 32bit-aligned dest address
mov %edi, %edx
and $3, %edx
jz msmain
jmp *mspre_tab(,%edx,4)
mspre_tab: .long msmain, mspre1, mspre2, mspre3
-mspre1: stosb
+mspre3: stosb
dec %ecx
mspre2: stosb
dec %ecx
-mspre3: stosb
+mspre1: stosb
dec %ecx
jz msdone
+ # edi is 32bit-aligned here, write ecx>>2 32bit values
msmain:
push %ecx
shr $2, %ecx
rep stosl
pop %ecx
+ # write any trailing bytes
and $3, %ecx
jmp *mspost_tab(,%ecx,4)
mspost1:stosb
msdone:
+ pop %eax
pop %edi
pop %ebp
ret
push %edi
mov 8(%ebp), %edi
+ push %edi
mov 12(%ebp), %ax
shl $16, %eax
mov 12(%ebp), %ax
jz ms16done
stosw
ms16done:
-
+ pop %eax
pop %edi
pop %ebp
ret
saved_es: .word 0
saved_ds: .word 0
saved_flags: .word 0
+saved_if: .byte 0
saved_pic1_mask: .byte 0
saved_pic2_mask: .byte 0
push %ebp
mov %esp, %ebp
pushal
+ call get_intr_flag
+ mov %al, saved_if
cli
# save protected mode IDTR and replace it with the real mode vectors
sidt (saved_idtr)
mov %ax, %ds
mov %ax, %es
mov %ax, %ss
+ mov %ax, %fs
+ mov %ax, %gs
nop
# load registers from the int86regs struct
pop %ds
# ignore fs and gs for now, don't think I'm going to need them
+ # move to the real-mode stack, at the top of conventional memory
+ #mov $0x9000, %sp
+ #mov %sp, %ss
+ #mov $0, %esp
+
# move to the real-mode stack, accessible from ss=0
# just in case the BIOS call screws up our unreal mode
mov $0x7be0, %esp
mov %ax, %ds
mov %ax, %es
mov %ax, %ss
+ mov %ax, %fs
+ mov %ax, %gs
nop
# point the esp to our regs struct, to fill it with pusha/pushf
# can't receive any more keyboard interrupts afterwards. Reading from
# the keyboard data port (60h) once, seems to resolve this. And it's
# cheap enough, so why not... I give up.
- push %eax
in $0x60, %al
- pop %eax
- sti
+ # restore interrupts to their previous state
+ movzbl saved_if, %eax
+ pushl %eax
+ call set_intr_flag
+ add $4, %esp
+
popal
pop %ebp
ret
*/
#include <stdio.h>
#include <string.h>
+#include "config.h"
#include "panic.h"
#include "mem.h"
#include "intr.h"
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);
void init_mem(void)
{
- int i, pg, max_pg = 0;
+ 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"};
total += sz;
pg = ADDR_TO_PAGE(end);
- if(max_pg < pg) {
- max_pg = pg;
+ if(end_pg < pg) {
+ end_pg = pg;
}
}
/* size of the useful part of the bitmap in bytes padded to 4-byte
* boundaries to allow 32bit at a time operations.
*/
- bmsize = (max_pg / 32 + 1) * 4;
+ bmsize = (end_pg / 32) * 4;
/* mark all pages occupied by the bitmap as used */
used_end = (uint32_t)bitmap + bmsize - 1;
- max_pg = ADDR_TO_PAGE(used_end);
- printf("marking pages up to %x (page: %d) as used\n", used_end, max_pg);
- for(i=0; i<=max_pg; i++) {
+ 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(void)
+int alloc_ppage(int area)
{
- return alloc_ppages(1);
+ return alloc_ppages(1, area);
}
/* free_ppage marks the physical page, free in the allocation bitmap.
}
-int alloc_ppages(int count)
+int alloc_ppages(int count, int area)
{
- int i, pg, idx, max, intr_state, found_free = 0;
+ int i, dir, pg, idx, max, intr_state, found_free = 0;
intr_state = get_intr_flag();
disable_intr();
- idx = last_alloc_idx;
- max = bmsize / 4;
+ 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) {
+ 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) {
- for(i=0; i<32; i++) {
- pg = idx * 32 + i;
+ pg = idx * 32;
+ if(dir < 0) pg += 31;
+ for(i=0; i<32; i++) {
if(IS_FREE(pg)) {
- if(!found_free) {
+ if(!found_free && dir > 0) {
last_alloc_idx = idx;
found_free = 1;
}
return pg;
}
}
+ pg += dir;
}
}
- idx++;
+ idx += dir;
}
set_intr_flag(intr_state);
}
}
+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");
+}
#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(void);
+int alloc_ppage(int area);
void free_ppage(int pg);
/* allocate a number of consecutive pages */
-int alloc_ppages(int count);
+int alloc_ppages(int count, int area);
void free_ppages(int pg0, int count);
/* allocate a specific range of pages.
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
+#include "video.h"
#include "asmops.h"
struct all_registers {
struct all_registers regs;
uint32_t eip;
+ set_vga_mode(3);
disable_intr();
memset(®s, 0, sizeof regs);
#ifndef PANIC_H_
#define PANIC_H_
-void panic(const char *fmt, ...);
+void panic(const char *fmt, ...) __attribute__((noreturn));
#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_ */
*/
#include <stdio.h>
#include <string.h>
+#include "config.h"
#include "serial.h"
#include "asmops.h"
+#include "intr.h"
+#include "panic.h"
#define UART1_BASE 0x3f8
#define UART2_BASE 0x2f8
#define BNEXT(x) (((x) + 1) & 0xff)
#define BEMPTY(b) (b##_ridx == b##_widx)
-/*
static int have_recv(int base);
-static void recv_intr(void);*/
+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};*/
+static int uart_irq[] = {UART1_IRQ, UART2_IRQ};
int ser_open(int pidx, int baud, unsigned int mode)
{
unsigned short div = 115200 / baud;
- int base; /*intr*/
+ int base, intr;
unsigned int fmt;
- /*int prev_if;*/
if(pidx < 0 || pidx > 1) {
printf("ser_open: invalid serial port: %d\n", pidx);
memset(ports + pidx, 0, sizeof ports[pidx]);
base = uart_base[pidx];
- /*intr = uart_irq[pidx] | 8;*/
+ intr = uart_irq[pidx];
if(mode & SER_8N2) {
fmt = COM_FMT_8N2;
fmt = COM_FMT_8N1;
}
- /*prev_if = disable_intr();*/
- /* TODO set interrupt handler */
- /* unmask the appropriate interrupt */
- /*outb(inb(PIC1_DATA_PORT) & ~(1 << uart_irq[pidx]), PIC1_DATA_PORT);*/
+ interrupt(IRQ_TO_INTR(uart_irq[pidx]), recv_intr);
outb(LCTL_DLAB, base + UART_LCTL);
outb(div & 0xff, base + UART_DIVLO);
outb(fmt, base + UART_LCTL); /* fmt should be LCTL_8N1, LCTL_8N2 etc */
outb(FIFO_ENABLE | FIFO_SEND_CLEAR | FIFO_RECV_CLEAR, base + UART_FIFO);
outb(MCTL_DTR | MCTL_RTS | MCTL_OUT2, base + UART_MCTL);
- /*outb(INTR_RECV, base + UART_INTR);
-
- restore_intr(prev_if);*/
+ outb(INTR_RECV, base + UART_INTR);
ports[pidx].base = base;
- /*ports[pidx].intr = intr;*/
+ ports[pidx].intr = intr;
ports[pidx].blocking = 1;
++num_open;
return pidx;
void ser_close(int fd)
{
if(--num_open == 0) {
- /*int prev_if = disable_intr();*/
- /*outb(0, ports[fd].base + UART_INTR);*/
+ outb(0, ports[fd].base + UART_INTR);
outb(0, ports[fd].base + UART_MCTL);
- /*restore_intr(prev_if);*/
}
ports[fd].base = 0;
return 0;
}
-#if 0
static int have_recv(int base)
{
unsigned short stat = inb(base + UART_LSTAT);
if(stat & LST_ERROR) {
- printf("serial receive error\n");
- panic();
+ panic("serial receive error\n");
}
return stat & LST_DRDY;
}
-static void __interrupt __far recv_intr()
+static void recv_intr()
{
int i, idreg, c;
while(have_recv(base)) {
c = inb(base + UART_DATA);
- p->inbuf[p->inbuf_widx] = inb(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) {
}
}
}
+}
- outb(OCW2_EOI, PIC1_CMD_PORT);
+#ifdef ENABLE_GDB_STUB
+void putDebugChar(int c)
+{
+ ser_putc(GDB_SERIAL_PORT, c);
+}
+
+int getDebugChar(void)
+{
+ return ser_getc(GDB_SERIAL_PORT);
}
#endif
# pcboot - bootable PC demo/game kernel
-# Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+# 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
.extern wait_vsync
.extern kb_getkey
+ .equ STACKTOP,0x80000
+
# move the stack to the top of the conventional memory
cli
- movl $0x80000, %esp
+ movl $STACKTOP, %esp
+ # push a 0 ret-addr to terminate gdb backtraces
+ pushl $0
# zero the BSS section
xor %eax, %eax
hlt
jmp 0b
+ # this is called once after memory init, to move the protected mode
+ # stack to the top of usable memory, to avoid interference from 16bit
+ # programs (as much as possible)
+ .global move_stack
+move_stack:
+ # calculate the currently used lowest address of the stack (rounded
+ # down to 4-byte alignment), to see where to start copying
+ mov %esp, %esi
+ and $0xfffffffc, %esi
+ # calculate the size we need to copy
+ mov $STACKTOP, %ecx
+ sub %esi, %ecx
+ # load the destination address to edi
+ mov 4(%esp), %edi
+ sub %ecx, %edi
+ # size in longwords
+ shr $2, %ecx
+
+ # change esp to the new stack
+ mov $STACKTOP, %ecx
+ sub %esp, %ecx
+ mov 4(%esp), %eax
+ mov %eax, %esp
+ sub %ecx, %esp
+
+ rep movsd
+ ret
+
.global logohack
logohack:
pusha
int vbetest(void)
{
- int i, j, nmodes, mx, my;
+ int i, j, nmodes, mx, my, idx;
unsigned int st, prev_st = 0;
struct video_mode vi;
uint16_t *fbptr;
}
}
- if(!(framebuf = set_video_mode(find_video_mode(640, 480, 16)))) {
+ if((idx = find_video_mode_idx(640, 480, 16)) == -1) {
+ return -1;
+ }
+ video_mode_info(idx, &vi);
+
+ if(!(framebuf = set_video_mode(vi.mode))) {
return -1;
}
get_color_bits(&vi.rbits, &vi.gbits, &vi.bbits);
/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+256boss - bootable launcher for 256b intros
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include "intr.h"
#include "asmops.h"
#include "timer.h"
+#include "panic.h"
#include "config.h"
/* frequency of the oscillator driving the 8254 timer */
struct timer_event {
int dt; /* remaining ticks delta from the previous event */
+ void (*func)(void);
struct timer_event *next;
};
-/* defined in intr_asm.S */
-void intr_entry_fast_timer(void);
+static void timer_handler(int inum);
static struct timer_event *evlist;
outb((reload_count >> 8) & 0xff, PORT_DATA0);
/* set the timer interrupt handler */
- /*interrupt(IRQ_TO_INTR(0), timer_handler);*/
- /* set low level fast timer interrupt routine directly in the IDT */
- set_intr_entry(IRQ_TO_INTR(0), intr_entry_fast_timer);
+ interrupt(IRQ_TO_INTR(0), timer_handler);
+}
+
+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);
+ }
+ }
}
-*/
/*
-pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+256boss - bootable launcher for 256byte intros
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#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)
void init_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_ */
#include "vbe.h"
#include "asmops.h"
#include "int86.h"
+#include "boot.h"
#define SEG_ADDR(s) ((uint32_t)(s) << 4)
#define MODE_LFB (1 << 14)
-extern unsigned char low_mem_buffer[];
-
struct vbe_info *vbe_get_info(void)
{
struct vbe_info *info;
printf("blue bits: %d (mask: %x)\n", (int)mi->bmask_size, maskbits[mi->bmask_size] << mi->bpos);
printf("framebuffer address: %x\n", (unsigned int)mi->fb_addr);
}
+
+int vbe_get_edid(struct vbe_edid *edid)
+{
+ struct int86regs regs;
+
+ memset(®s, 0, sizeof regs);
+ regs.es = (uint32_t)low_mem_buffer >> 4;
+ regs.eax = 0x4f15;
+ regs.ebx = 1;
+ int86(0x10, ®s);
+
+ if((regs.eax & 0xffff) != 0x4f) {
+ return -1;
+ }
+ memcpy(edid, low_mem_buffer, sizeof *edid);
+ return 0;
+}
+
+int edid_preferred_resolution(struct vbe_edid *edid, int *xres, int *yres)
+{
+ if(memcmp(edid->magic, VBE_EDID_MAGIC, 8) != 0) {
+ return -1;
+ }
+
+ *xres = (int)edid->timing[0].hactive_lsb | ((int)(edid->timing[0].hact_hblank_msb & 0xf0) << 4);
+ *yres = (int)edid->timing[0].vactive_lsb | ((int)(edid->timing[0].vact_vblank_msb & 0xf0) << 4);
+ return 0;
+}
+
+void print_edid(struct vbe_edid *edid)
+{
+ char vendor[4];
+ int xres, yres;
+
+ if(memcmp(edid->magic, VBE_EDID_MAGIC, 8) != 0) {
+ printf("invalid EDID magic\n");
+ return;
+ }
+
+ vendor[0] = (edid->vendor >> 10) & 0x1f;
+ vendor[1] = (edid->vendor >> 5) & 0x1f;
+ vendor[2] = edid->vendor & 0x1f;
+ vendor[3] = 0;
+ printf("Manufacturer: %s\n", vendor);
+
+ edid_preferred_resolution(edid, &xres, &yres);
+ printf("Preferred resolution: %dx%d\n", xres, yres);
+}
uint8_t reserved4[206];
} __attribute__((packed));
+struct vbe_edid_chroma {
+ unsigned char redgreen_xy_lsb;
+ unsigned char bluewhite_xy_lsb;
+ unsigned char redx_msb, redy_msb;
+ unsigned char greenx_msb, greeny_msb;
+ unsigned char bluex_msb, bluey_msb;
+ unsigned char whitex_msb, whitey_msb;
+} __attribute__((packed));
+
+struct vbe_edid_timing {
+ uint16_t dotclock;
+ unsigned char hactive_lsb, hblank_lsb, hact_hblank_msb;
+ unsigned char vactive_lsb, vblank_lsb, vact_vblank_msb;
+ unsigned char hporch_lsb, hsync_lsb;
+ unsigned char vporch_vsync_lsb;
+ unsigned char hvporch_hvsync_msb;
+ unsigned char hsize_lsb, vsize_lsb; /* mm */
+ unsigned char hsize_vsize_msb;
+ unsigned char hborder, vborder;
+ unsigned char features;
+} __attribute__((packed));
+
+
+#define VBE_EDID_MAGIC "\0\xff\xff\xff\xff\xff\xff\0"
+
+struct vbe_edid {
+ char magic[8];
+ uint16_t vendor;
+ uint16_t product;
+ uint32_t serial;
+ unsigned char week, year;
+ unsigned char ver_major, ver_minor;
+
+ unsigned char vidinp;
+ unsigned char hsize, vsize;
+ unsigned char gamma;
+ unsigned char features;
+
+ struct vbe_edid_chroma chroma;
+
+ uint16_t modes_std;
+ unsigned char modes_ext;
+ uint16_t timing_std;
+
+ struct vbe_edid_timing timing[4];
+ unsigned char num_ext, csum;
+
+} __attribute__((packed));
+
struct vbe_info *vbe_get_info(void);
struct vbe_mode_info *vbe_get_mode_info(int mode);
void print_mode_info(struct vbe_mode_info *modei);
+int vbe_get_edid(struct vbe_edid *edid);
+int edid_preferred_resolution(struct vbe_edid *edid, int *xres, int *yres);
+void print_edid(struct vbe_edid *edid);
+
#endif /* VBE_H_ */
return (void*)mode_info->fb_addr;
}
-int find_video_mode(int xsz, int ysz, int bpp)
+int find_video_mode_idx(int xsz, int ysz, int bpp)
{
- int i;
- uint16_t best = 0;
+ int i, best = -1, best_bpp = 0;
struct vbe_mode_info *inf;
if(init_once() == -1) return -1;
if(!inf || inf->xres != xsz || inf->yres != ysz) {
continue;
}
- if(SAME_BPP(inf->bpp, bpp)) {
- best = modes[i];
+ if((bpp <= 0 && inf->bpp > best_bpp) || SAME_BPP(inf->bpp, bpp)) {
+ best = i;
+ best_bpp = inf->bpp;
}
}
- if(!best) {
+ if(best == -1) {
printf("Requested video mode (%dx%d %dbpp) is unavailable\n", xsz, ysz, bpp);
return -1;
}
static unsigned int maskbits[] = {0, 1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff};
return maskbits[nbits] << pos;
}
+
+const char *get_video_vendor(void)
+{
+ if(init_once() == -1) return 0;
+ return (char*)VBEPTR(vbe_info->oem_vendor_name_ptr);
+}
+
+int get_video_mem_size(void)
+{
+ if(init_once() == -1) return 0;
+ return vbe_info->total_mem << 6;
+}
void set_vga_mode(int mode);
void *set_video_mode(int mode);
-int find_video_mode(int xsz, int ysz, int bpp);
+int find_video_mode_idx(int xsz, int ysz, int bpp);
int video_mode_count(void);
int video_mode_info(int n, struct video_mode *vid);
int get_color_mask(unsigned int *rmask, unsigned int *gmask, unsigned int *bmask);
int get_color_shift(int *rshift, int *gshift, int *bshift);
+const char *get_video_vendor(void);
+int get_video_mem_size(void);
+
/* defined in video_asm.s */
void wait_vsync(void);
+void set_pal_entry(unsigned char idx, unsigned char r, unsigned char g, unsigned char b);
#endif /* VIDEO_H_ */
and $8, %al
jz 0b
ret
+
+ .global set_pal_entry
+set_pal_entry:
+ mov 4(%esp), %al
+ mov $0x3c8, %dx
+ out %al, %dx
+ inc %dx
+ mov 8(%esp), %al
+ shr $2, %al
+ out %al, %dx
+ mov 12(%esp), %al
+ shr $2, %al
+ out %al, %dx
+ mov 16(%esp), %al
+ shr $2, %al
+ out %al, %dx
+ ret