From: John Tsiombikas Date: Mon, 2 Sep 2019 00:24:29 +0000 (+0300) Subject: backported fixes from 256boss X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=bootcensus;a=commitdiff_plain;h=81c11bdd80190ec319a82b0402173cfb65fcbf72 backported fixes from 256boss --- diff --git a/src/boot.h b/src/boot.h new file mode 100644 index 0000000..e4bda1c --- /dev/null +++ b/src/boot.h @@ -0,0 +1,7 @@ +#ifndef BOOT_H_ +#define BOOT_H_ + +extern unsigned char low_mem_buffer[]; +extern int boot_drive_number; + +#endif /* BOOT_H_ */ diff --git a/src/boot/boot.s b/src/boot/boot.s index 4f0023a..fee0bb2 100644 --- a/src/boot/boot.s +++ b/src/boot/boot.s @@ -1,5 +1,5 @@ # pcboot - bootable PC demo/game kernel -# Copyright (C) 2018-2019 John Tsiombikas +# Copyright (C) 2018 John Tsiombikas # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/boot/boot2.s b/src/boot/boot2.s index 68dffd4..53dff6d 100644 --- a/src/boot/boot2.s +++ b/src/boot/boot2.s @@ -1,5 +1,5 @@ -# pcboot - bootable PC demo/game kernel -# Copyright (C) 2018-2019 John Tsiombikas +# 256boss - bootable launcher for 256byte intros +# Copyright (C) 2018 John Tsiombikas # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/contty.c b/src/contty.c index 6601ce8..9fdf1c7 100644 --- a/src/contty.c +++ b/src/contty.c @@ -40,18 +40,20 @@ along with this program. If not, see . #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) { @@ -60,26 +62,39 @@ 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 } @@ -92,6 +107,23 @@ void con_cursor(int x, int 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; @@ -102,10 +134,20 @@ 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 - memset(TEXT_ADDR, 0, NCOLS * NROWS * 2); + memset16(TEXT_ADDR, VMEM_CHAR(' ', txattr), NCOLS * NROWS); start_line = 0; crtc_setstart(0); @@ -126,33 +168,39 @@ static inline void linefeed(void) 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 @@ -163,12 +211,15 @@ void con_putchar(int c) 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; @@ -180,6 +231,10 @@ void con_printf(int x, int y, const char *fmt, ...) while(*ptr && x < 80) { con_putchar_scr(x++, y, *ptr++); } + return ptr - buf; +#else + return 0; +#endif } static void scroll(void) @@ -229,12 +284,3 @@ static inline void crtc_write(int reg, unsigned char val) 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); -} diff --git a/src/contty.h b/src/contty.h index bbd2775..fcc6e37 100644 --- a/src/contty.h +++ b/src/contty.h @@ -18,15 +18,70 @@ along with this program. If not, see . #ifndef CONTTY_H_ #define CONTTY_H_ +enum { + CON_CURSOR_LINE, + CON_CURSOR_BLOCK +}; + +enum { + BLACK, + BLUE, + GREEN, + CYAN, + RED, + MAGENTA, + BROWN, + LTGREY, + GREY, + LTBLUE, + LTGREEN, + LTCYAN, + LTRED, + LTMAGENTA, + YELLOW, + WHITE +}; +#define BRIGHT 8 +#define FG_BRIGHT 0x08 +#define BG_BRIGHT 0x80 + +enum { + G_DIAMOND = 0x04, + G_CHECKER = 0xb1, + G_LR_CORNER = 0xd9, + G_UR_CORNER = 0xbf, + G_UL_CORNER = 0xda, + G_LL_CORNER = 0xc0, + G_CROSS = 0xc5, + G_HLINE = 0xc4, + G_L_TEE = 0xc3, + G_R_TEE = 0xb4, + G_B_TEE = 0xc1, + G_T_TEE = 0xc2, + G_VLINE = 0xb3, + G_CDOT = 0xf8, + + G_HDBL = 0xcd, + G_UL_HDBL = 0xd5, + G_UR_HDBL = 0xb8, + G_T_HDBL_TEE = 0xd1 +}; + + int con_init(void); +void con_scr_enable(void); +void con_scr_disable(void); void con_show_cursor(int show); void con_cursor(int x, int y); +void con_curattr(int shape, int blink); void con_fgcolor(int c); void con_bgcolor(int c); +void con_setattr(unsigned char attr); +unsigned char con_getattr(void); void con_clear(void); void con_putchar(int c); void con_putchar_scr(int x, int y, int c); -void con_printf(int x, int y, const char *fmt, ...); +int con_printf(int x, int y, const char *fmt, ...); #endif /* CONTTY_H_ */ diff --git a/src/intr.c b/src/intr.c index 1946708..2ec1f1b 100644 --- a/src/intr.c +++ b/src/intr.c @@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include +#include "config.h" #include "intr.h" #include "desc.h" #include "segm.h" @@ -151,12 +152,17 @@ void dispatch_intr(struct intr_frame frm) 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); @@ -257,3 +263,10 @@ void end_of_irq(int irq) set_intr_flag(intr_state); } + +#ifdef ENABLE_GDB_STUB +void exceptionHandler(int id, void (*func)()) +{ + set_intr_entry(id, func); +} +#endif diff --git a/src/intr.h b/src/intr.h index fe6ed44..f33ec23 100644 --- a/src/intr.h +++ b/src/intr.h @@ -72,6 +72,7 @@ void interrupt(int intr_num, intr_func_t func); */ 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); diff --git a/src/kbregs.h b/src/kbregs.h index 5c56c48..813fa1a 100644 --- a/src/kbregs.h +++ b/src/kbregs.h @@ -27,6 +27,8 @@ along with this program. If not, see . #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 @@ -37,6 +39,8 @@ along with this program. If not, see . #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 diff --git a/src/kbscan.h b/src/kbscan.h new file mode 100644 index 0000000..aaca8b8 --- /dev/null +++ b/src/kbscan.h @@ -0,0 +1,48 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef KBSCAN_H_ +#define KBSCAN_H_ + +/* table with rough translations from set 1 scancodes to ASCII-ish */ +static int scantbl_set1[] = { + 0, KB_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', /* 0 - e */ + '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', /* f - 1c */ + KB_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', /* 1d - 29 */ + KB_LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KB_RSHIFT, /* 2a - 36 */ + KB_NUM_MUL, KB_LALT, ' ', KB_CAPSLK, KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, KB_F7, KB_F8, KB_F9, KB_F10, /* 37 - 44 */ + KB_NUMLK, KB_SCRLK, KB_NUM_7, KB_NUM_8, KB_NUM_9, KB_NUM_MINUS, KB_NUM_4, KB_NUM_5, KB_NUM_6, KB_NUM_PLUS, /* 45 - 4e */ + KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_0, KB_NUM_DOT, KB_SYSRQ, 0, 0, KB_F11, KB_F12, /* 4d - 58 */ + 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_ */ diff --git a/src/keyb.c b/src/keyb.c index 3e86f43..faf08a9 100644 --- a/src/keyb.c +++ b/src/keyb.c @@ -1,6 +1,6 @@ /* pcboot - bootable PC demo/game kernel -Copyright (C) 2018 John Tsiombikas +Copyright (C) 2018-2019 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,6 +21,9 @@ along with this program. If not, see . #include "intr.h" #include "asmops.h" #include "kbregs.h" +#include "kbscan.h" +#include "power.h" +#include "panic.h" #define delay7us() \ do { \ @@ -28,19 +31,8 @@ along with this program. If not, see . 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(); @@ -53,13 +45,85 @@ 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(); +} + +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) @@ -165,16 +229,38 @@ unsigned char kb_read_data(void) 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--; @@ -185,9 +271,13 @@ static void kbintr() 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); diff --git a/src/keyb.h b/src/keyb.h index a5297e4..d031aee 100644 --- a/src/keyb.h +++ b/src/keyb.h @@ -43,6 +43,15 @@ enum { 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. */ diff --git a/src/libc/alloca.h b/src/libc/alloca.h new file mode 100644 index 0000000..ae27a71 --- /dev/null +++ b/src/libc/alloca.h @@ -0,0 +1,23 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef ALLOCA_H_ +#define ALLOCA_H_ + +#define alloca(x) __builtin_alloca(x) + +#endif /* ALLOCA_H_ */ diff --git a/src/libc/ctype.c b/src/libc/ctype.c index 76746dd..89e7b93 100644 --- a/src/libc/ctype.c +++ b/src/libc/ctype.c @@ -69,5 +69,5 @@ int toupper(int c) int tolower(int c) { - return isupper(c) ? (c + ('A' - 'a')) : c; + return isupper(c) ? (c - ('A' - 'a')) : c; } diff --git a/src/libc/errno.h b/src/libc/errno.h index d7ab369..a4cf483 100644 --- a/src/libc/errno.h +++ b/src/libc/errno.h @@ -30,6 +30,9 @@ along with this program. If not, see . #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 */ diff --git a/src/libc/limits.h b/src/libc/limits.h new file mode 100644 index 0000000..b3abdad --- /dev/null +++ b/src/libc/limits.h @@ -0,0 +1,36 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef LIMITS_H_ +#define LIMITS_H_ + +#define CHAR_BIT 8 + +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 +#define INT_MIN (-2147483648) +#define INT_MAX 2147483647 +#define LONG_MIN (-2147483648) +#define LONG_MAX 2147483647 + +#define USHRT_MAX 65535 +#define UINT_MAX 0xffffffff +#define ULONG_MAX 0xffffffff + +#define PATH_MAX 256 + +#endif /* LIMITS_H_ */ diff --git a/src/libc/malloc.c b/src/libc/malloc.c index 3610d24..363ce03 100644 --- a/src/libc/malloc.c +++ b/src/libc/malloc.c @@ -1,6 +1,6 @@ /* pcboot - bootable PC demo/game kernel -Copyright (C) 2018 John Tsiombikas +Copyright (C) 2018-2019 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,20 +19,37 @@ along with this program. If not, see . #include #include #include +#include +#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 */ @@ -53,6 +70,150 @@ static int pool_index(int sz) } 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) { @@ -64,12 +225,12 @@ 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); @@ -94,7 +255,10 @@ void *malloc(size_t sz) 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); } @@ -103,14 +267,14 @@ void *malloc(size_t sz) /* 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 */ @@ -121,10 +285,17 @@ void *malloc(size_t sz) 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) { @@ -139,6 +310,8 @@ void free(void *p) add_to_pool(mem); } +#endif /* !def SINGLE_POOL */ + void *calloc(size_t num, size_t size) { @@ -166,11 +339,44 @@ void *realloc(void *ptr, 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; @@ -187,20 +393,22 @@ static int add_to_pool(struct mem_desc *mem) 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); @@ -210,8 +418,13 @@ static int add_to_pool(struct mem_desc *mem) } /* otherwise just add it to the pool */ + mem->magic = MAGIC_FREE; mem->next = pools[pidx]; pools[pidx] = mem; + +#ifdef MALLOC_DEBUG + check_cycles(pools[pidx]); +#endif return 0; } - +#endif /* !def SINGLE_POOL */ diff --git a/src/libc/stddef.h b/src/libc/stddef.h new file mode 100644 index 0000000..9ec6bda --- /dev/null +++ b/src/libc/stddef.h @@ -0,0 +1,32 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef STDDEF_H_ +#define STDDEF_H_ + +#include + +typedef int32_t ssize_t; +typedef uint32_t size_t; +typedef int wchar_t; + +typedef int32_t ptrdiff_t; +typedef uint32_t intptr_t; + +#define NULL 0 + +#endif /* STDDEF_H_ */ diff --git a/src/libc/stdio.c b/src/libc/stdio.c index 1f2bb13..1f12fd6 100644 --- a/src/libc/stdio.c +++ b/src/libc/stdio.c @@ -1,6 +1,6 @@ /* pcboot - bootable PC demo/game kernel -Copyright (C) 2018 John Tsiombikas +Copyright (C) 2018-2019 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,8 +18,10 @@ along with this program. If not, see . #include #include #include +#include #include "contty.h" #include "serial.h" +#include "panic.h" enum { OUT_DEF, @@ -30,8 +32,10 @@ enum { 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) { @@ -50,10 +54,6 @@ int puts(const char *s) /* -- printf and friends -- */ -static char *convc = "dioxXucsfeEgGpn%"; - -#define IS_CONV(c) strchr(convc, c) - int printf(const char *fmt, ...) { int res; @@ -102,6 +102,27 @@ int vsnprintf(char *buf, size_t sz, const char *fmt, va_list ap) return intern_printf(OUT_BUF, buf, sz, fmt, ap); } +int fprintf(FILE *fp, const char *fmt, ...) +{ + int res; + va_list ap; + + va_start(ap, fmt); + res = vfprintf(fp, fmt, ap); + va_end(ap); + return res; +} + +int vfprintf(FILE *fp, const char *fmt, va_list ap) +{ + if(fp == stdout || fp == stderr) { + return vprintf(fmt, ap); + } + + panic("*fprintf for anything other than stdout/stderr, not implemented yet\n"); + return 0; +} + int ser_printf(const char *fmt, ...) { int res; @@ -118,6 +139,11 @@ int ser_vprintf(const char *fmt, va_list ap) return intern_printf(OUT_SER, 0, 0, fmt, ap); } +void perror(const char *s) +{ + printf("%s: %s\n", s, strerror(errno)); +} + /* intern_printf provides all the functionality needed by all the printf * variants. * - buf: optional buffer onto which the formatted results are written. If null @@ -127,6 +153,9 @@ int ser_vprintf(const char *fmt, va_list ap) * 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)) @@ -145,9 +174,10 @@ static int intern_printf(int out, char *buf, size_t sz, const char *fmt, va_list 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 == '%') { @@ -167,6 +197,7 @@ static int intern_printf(int out, char *buf, size_t sz, const char *fmt, va_list if(alt) { bwrite(out, BUF(buf), SZ(sz), "0x", 2); + cnum += 2; } case 'u': @@ -178,15 +209,18 @@ static int intern_printf(int out, char *buf, size_t sz, const char *fmt, va_list 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++) { @@ -195,13 +229,28 @@ static int intern_printf(int out, char *buf, size_t sz, const char *fmt, va_list } slen = strlen(conv_buf); + + if(left_align) { + if(!unsig && sign && num >= 0) { + bwrite(out, BUF(buf), SZ(sz), "+", 1); + cnum++; + } + bwrite(out, BUF(buf), SZ(sz), conv_buf, slen); + cnum += slen; + padc = ' '; + } for(i=slen; i= 0) { + bwrite(out, BUF(buf), SZ(sz), "+", 1); + cnum++; + } + bwrite(out, BUF(buf), SZ(sz), conv_buf, slen); + cnum += slen; + } break; case 'c': @@ -216,12 +265,19 @@ static int intern_printf(int out, char *buf, size_t sz, const char *fmt, va_list 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 +Copyright (C) 2018-2019 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,6 +21,18 @@ along with this program. If not, see . #include #include +typedef struct FILE FILE; + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#define EOF (-1) + +#define stdin ((FILE*)0) +#define stdout ((FILE*)1) +#define stderr ((FILE*)2) + int putchar(int c); int puts(const char *s); @@ -33,8 +45,46 @@ int vsprintf(char *buf, const char *fmt, va_list ap); int snprintf(char *buf, size_t sz, const char *fmt, ...); int vsnprintf(char *buf, size_t sz, const char *fmt, va_list ap); +/* TODO */ +int fprintf(FILE *fp, const char *fmt, ...); +int vfprintf(FILE *fp, const char *fmt, va_list ap); + +/* TODO +int fscanf(FILE *fp, const char *fmt, ...); +int vfscanf(FILE *fp, const char *fmt, va_list ap); + +int sscanf(const char *str, const char *fmt, ...); +int vsscanf(const char *ptr, const char *fmt, va_list ap); +*/ + /* printf to the serial port */ int ser_printf(const char *fmt, ...); int ser_vprintf(const char *fmt, va_list ap); +void perror(const char *s); + + +/* FILE I/O */ +FILE *fopen(const char *path, const char *mode); +int fclose(FILE *fp); + +long filesize(FILE *fp); +int fseek(FILE *fp, long offset, int from); +void rewind(FILE *fp); +long ftell(FILE *fp); + +size_t fread(void *buf, size_t size, size_t count, FILE *fp); +size_t fwrite(const void *buf, size_t size, size_t count, FILE *fp); + +int fgetc(FILE *fp); +char *fgets(char *buf, int size, FILE *fp); + +int fputc(int c, FILE *fp); + +int fflush(FILE *fp); + +int feof(FILE *fp); +int ferror(FILE *fp); +void clearerr(FILE *fp); + #endif /* STDIO_H_ */ diff --git a/src/libc/stdlib.c b/src/libc/stdlib.c index b3acce2..6b130ed 100644 --- a/src/libc/stdlib.c +++ b/src/libc/stdlib.c @@ -1,6 +1,6 @@ /* pcboot - bootable PC demo/game kernel -Copyright (C) 2018 John Tsiombikas +Copyright (C) 2018-2019 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,7 +16,11 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include +#include #include +#include +#include +#include int atoi(const char *str) { @@ -32,6 +36,8 @@ long strtol(const char *str, char **endp, int base) { long acc = 0; int sign = 1; + int valid = 0; + const char *start = str; while(isspace(*str)) str++; @@ -55,24 +61,27 @@ long strtol(const char *str, char **endp, int base) } 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; @@ -134,3 +143,130 @@ void utoa(unsigned int val, char *buf, int base) *buf = 0; } +double atof(const char *str) +{ + return strtod(str, 0); +} + + +double strtod(const char *str, char **endp) +{ + char *ep; + const char *start = str; + int valid = 0; + long ival = 0, dval = 0; + int ddig = 0; + double res; + + /* integer part */ + ival = strtol(str, &ep, 10); + if(ep == str && *str != '.') { + if(endp) *endp = (char*)str; + return 0.0; + } + if(ep != str) valid = 1; + str = *ep == '.' ? ep + 1 : ep; + if(!isdigit(*str)) { + goto done; + } + valid = 1; + + dval = strtol(str, &ep, 10); + assert(dval >= 0); + ddig = ep - str; + str = ep; + +done: + if(*endp) { + *endp = (char*)(valid ? str : start); + } + + res = (double)ival; + if(ddig) { + double d = (double)dval; + while(ddig-- > 0) { + d /= 10.0; + } + res += d; + } + return res; +} + +int atexit(void (*func)(void)) +{ + /* there's no concept of exiting at the moment, so this does nothing */ + return 0; +} + +void abort(void) +{ + panic("Aborted\n"); +} + +#define QSORT_THRESHOLD 4 +#define ITEM(idx) ((char*)arr + (idx) * itemsz) + +#define SWAP(p, q) \ + do { \ + int nn = itemsz; \ + char *pp = (p); \ + char *qq = (q); \ + do { \ + char tmp = *pp; \ + *pp++ = *qq; \ + *qq++ = tmp; \ + } while(--nn > 0); \ + } while(0) + +static void ins_sort(void *arr, size_t count, size_t itemsz, int (*cmp)(const void*, const void*)) +{ + int i; + char *it, *a, *b; + + if(count <= 1) return; + + it = (char*)arr + itemsz; + for(i=1; i (char*)arr && cmp(a, (b = a - itemsz)) < 0) { + SWAP(a, b); + a -= itemsz; + } + } +} + +void qsort(void *arr, size_t count, size_t itemsz, int (*cmp)(const void*, const void*)) +{ + char *ma, *mb, *mc, *left, *right; + size_t sepidx, nleft, nright; + + if(count <= 1) return; + + if(count < QSORT_THRESHOLD) { + ins_sort(arr, count, itemsz, cmp); + return; + } + + ma = arr; + mb = ITEM(count / 2); + mc = ITEM(count - 1); + if(cmp(ma, mb) < 0) SWAP(ma, mb); + if(cmp(mc, ma) < 0) SWAP(mc, ma); + + left = ma + itemsz; + right = mc - itemsz; + for(;;) { + while(cmp(left, ma) < 0) left += itemsz; + while(cmp(ma, right) < 0) right -= itemsz; + if(left >= right) break; + SWAP(left, right); + } + SWAP(ma, right); + sepidx = (right - (char*)arr) / itemsz; + nleft = sepidx; + nright = count - nleft - 1; + + qsort(ma, nleft, itemsz, cmp); + qsort(right + itemsz, nright, itemsz, cmp); +} diff --git a/src/libc/stdlib.h b/src/libc/stdlib.h index 9a0eab4..981cf69 100644 --- a/src/libc/stdlib.h +++ b/src/libc/stdlib.h @@ -1,6 +1,6 @@ /* pcboot - bootable PC demo/game kernel -Copyright (C) 2018 John Tsiombikas +Copyright (C) 2018-2019 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,10 +18,11 @@ along with this program. If not, see . #ifndef STDLIB_H_ #define STDLIB_H_ -#include +#include -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); @@ -30,6 +31,19 @@ long strtol(const char *str, char **endp, int base); void itoa(int val, char *buf, int base); void utoa(unsigned int val, char *buf, int base); +double atof(const char *str); +double strtod(const char *str, char **endp); + +int atexit(void (*func)(void)); + +void abort(void); + +void qsort(void *arr, size_t count, size_t size, int (*cmp)(const void*, const void*)); + +int rand(void); +int rand_r(unsigned int *seedp); +void srand(unsigned int seed); + /* defined in malloc.c */ void *malloc(size_t sz); void *calloc(size_t num, size_t sz); diff --git a/src/libc/string.c b/src/libc/string.c index ce3c2e7..4246aad 100644 --- a/src/libc/string.c +++ b/src/libc/string.c @@ -1,6 +1,6 @@ /* pcboot - bootable PC demo/game kernel -Copyright (C) 2018 John Tsiombikas +Copyright (C) 2018-2019 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,41 +16,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include - -/* -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 +#include void *memmove(void *dest, const void *src, size_t n) { @@ -77,6 +44,49 @@ 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= 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; @@ -129,6 +139,24 @@ char *strstr(const char *str, const char *substr) 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) { @@ -137,3 +165,93 @@ int strcmp(const char *s1, const char *s2) } return *s1 - *s2; } + +int strcasecmp(const char *s1, const char *s2) +{ + while(*s1 && tolower(*s1) == tolower(*s2)) { + s1++; + s2++; + } + return tolower(*s1) - tolower(*s2); +} + +int strncmp(const char *s1, const char *s2, int n) +{ + if(n <= 0) return 0; + + while(n-- > 0 && *s1 && *s2 && *s1 == *s2) { + s1++; + s2++; + } + + if(n <= 0) return 0; + return *s1 - *s2; +} + +int strncasecmp(const char *s1, const char *s2, int n) +{ + if(n <= 0) return 0; + + while(n-- > 0 && *s1 && *s2 && tolower(*s1) == tolower(*s2)) { + s1++; + s2++; + } + + if(n <= 0) return 0; + return tolower(*s1) - tolower(*s2); +} + +char *strcpy(char *dest, const char *src) +{ + char *dptr = dest; + while((*dptr++ = *src++)); + return dest; +} + +char *strcat(char *dest, const char *src) +{ + strcpy(dest + strlen(dest), src); + return dest; +} + +char *strncpy(char *dest, const char *src, int n) +{ + char *dptr = dest; + while(n-- > 0 && (*dptr++ = *src++)); + return dest; +} + + +static const char *errstr[] = { + "Success", + "Foo", + "Interrupted", + "Invalid", + "Child", + "Timeout", + "Out of memory", + "I/O error", + "Not found", + "Name too long", + "No space left on device", + "Permission denied", + "Not a directory", + "Is a directory", + "Does not exist", + 0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0, + "Bug" +}; + +char *strerror(int err) +{ + if(err < 0 || err > sizeof errstr / sizeof *errstr || !errstr[err]) { + return "Unknown"; + } + return (char*)errstr[err]; +} diff --git a/src/libc/string.h b/src/libc/string.h index 63bb299..85490b2 100644 --- a/src/libc/string.h +++ b/src/libc/string.h @@ -1,6 +1,6 @@ /* pcboot - bootable PC demo/game kernel -Copyright (C) 2018 John Tsiombikas +Copyright (C) 2018-2019 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,19 +20,33 @@ along with this program. If not, see . #include -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_ */ diff --git a/src/libc/string_asm.s b/src/libc/string_asm.s index ce9b19a..a15a405 100644 --- a/src/libc/string_asm.s +++ b/src/libc/string_asm.s @@ -22,6 +22,7 @@ memset: push %edi mov 8(%ebp), %edi + push %edi mov 12(%ebp), %al mov %al, %ah mov %ax, %cx @@ -32,26 +33,29 @@ memset: 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) @@ -61,6 +65,7 @@ mspost2:stosb mspost1:stosb msdone: + pop %eax pop %edi pop %ebp ret @@ -75,6 +80,7 @@ memset16: push %edi mov 8(%ebp), %edi + push %edi mov 12(%ebp), %ax shl $16, %eax mov 12(%ebp), %ax @@ -127,7 +133,7 @@ ms16main: jz ms16done stosw ms16done: - + pop %eax pop %edi pop %ebp ret diff --git a/src/lowcode.s b/src/lowcode.s index 67901e4..48bf290 100644 --- a/src/lowcode.s +++ b/src/lowcode.s @@ -35,6 +35,7 @@ saved_eax: .long 0 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 @@ -44,6 +45,8 @@ int86: 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) @@ -80,6 +83,8 @@ int86: mov %ax, %ds mov %ax, %es mov %ax, %ss + mov %ax, %fs + mov %ax, %gs nop # load registers from the int86regs struct @@ -93,6 +98,11 @@ int86: 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 @@ -124,6 +134,8 @@ int_op: int $0 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 @@ -168,11 +180,14 @@ int_op: int $0 # 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 diff --git a/src/mem.c b/src/mem.c index 4125fe4..a013607 100644 --- a/src/mem.c +++ b/src/mem.c @@ -17,6 +17,7 @@ along with this program. If not, see . */ #include #include +#include "config.h" #include "panic.h" #include "mem.h" #include "intr.h" @@ -37,6 +38,8 @@ struct mem_range { 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); @@ -63,7 +66,7 @@ static int bmsize, last_alloc_idx; 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"}; @@ -107,8 +110,8 @@ void init_mem(void) total += sz; pg = ADDR_TO_PAGE(end); - if(max_pg < pg) { - max_pg = pg; + if(end_pg < pg) { + end_pg = pg; } } @@ -123,21 +126,29 @@ void init_mem(void) /* 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. @@ -167,26 +178,34 @@ void free_ppage(int pg) } -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; } @@ -196,9 +215,10 @@ int alloc_ppages(int count) return pg; } } + pg += dir; } } - idx++; + idx += dir; } set_intr_flag(intr_state); @@ -278,3 +298,17 @@ static void mark_page(int pg, int used) } } +void print_page_bitmap(void) +{ + int i; + + for(i=0; i. #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. diff --git a/src/panic.c b/src/panic.c index 3daff29..1e79be6 100644 --- a/src/panic.c +++ b/src/panic.c @@ -18,6 +18,7 @@ along with this program. If not, see . #include #include #include +#include "video.h" #include "asmops.h" struct all_registers { @@ -37,6 +38,7 @@ void panic(const char *fmt, ...) struct all_registers regs; uint32_t eip; + set_vga_mode(3); disable_intr(); memset(®s, 0, sizeof regs); diff --git a/src/panic.h b/src/panic.h index 67650b4..012eecb 100644 --- a/src/panic.h +++ b/src/panic.h @@ -18,6 +18,6 @@ along with this program. If not, see . #ifndef PANIC_H_ #define PANIC_H_ -void panic(const char *fmt, ...); +void panic(const char *fmt, ...) __attribute__((noreturn)); #endif /* PANIC_H_ */ diff --git a/src/power.c b/src/power.c new file mode 100644 index 0000000..8a677d0 --- /dev/null +++ b/src/power.c @@ -0,0 +1,45 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include "power.h" +#include "keyb.h" +#include "kbregs.h" +#include "timer.h" +#include "panic.h" +#include "asmops.h" + +/* defined in intr_asm.S */ +void set_idt(uint32_t addr, uint16_t limit); + +void reboot(void) +{ + int i; + + printf("reboot: keyboard controller\n"); + kb_send_cmd(KB_CMD_PULSE_RESET); + + for(i=0; i<32768; i++) { + iodelay(); + } + + printf("reboot: triple-fault\n"); + set_idt(0, 0); + asm volatile("int $3"); + + panic("can't reboot!"); +} diff --git a/src/power.h b/src/power.h new file mode 100644 index 0000000..5e0c9fb --- /dev/null +++ b/src/power.h @@ -0,0 +1,23 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef POWER_H_ +#define POWER_H_ + +void reboot(void); + +#endif /* POWER_H_ */ diff --git a/src/serial.c b/src/serial.c index e833672..164b806 100644 --- a/src/serial.c +++ b/src/serial.c @@ -17,8 +17,11 @@ along with this program. If not, see . */ #include #include +#include "config.h" #include "serial.h" #include "asmops.h" +#include "intr.h" +#include "panic.h" #define UART1_BASE 0x3f8 #define UART2_BASE 0x2f8 @@ -121,22 +124,20 @@ struct serial_port { #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); @@ -150,7 +151,7 @@ int ser_open(int pidx, int baud, unsigned int mode) 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; @@ -158,10 +159,7 @@ int ser_open(int pidx, int baud, unsigned int mode) 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); @@ -169,12 +167,10 @@ int ser_open(int pidx, int baud, unsigned int mode) 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; @@ -183,10 +179,8 @@ int ser_open(int pidx, int baud, unsigned int mode) 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; @@ -304,18 +298,16 @@ char *ser_getline(int fd, char *buf, int bsz) 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; @@ -327,7 +319,14 @@ static void __interrupt __far recv_intr() 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) { @@ -337,7 +336,16 @@ static void __interrupt __far recv_intr() } } } +} - 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 diff --git a/src/startup.s b/src/startup.s index ef3cd38..01090cd 100644 --- a/src/startup.s +++ b/src/startup.s @@ -1,5 +1,5 @@ # pcboot - bootable PC demo/game kernel -# Copyright (C) 2018 John Tsiombikas +# Copyright (C) 2018-2019 John Tsiombikas # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -23,9 +23,13 @@ .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 @@ -43,6 +47,34 @@ skip_bss_zero: 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 diff --git a/src/test/vbetest.c b/src/test/vbetest.c index ee7fb68..7dd2bad 100644 --- a/src/test/vbetest.c +++ b/src/test/vbetest.c @@ -41,7 +41,7 @@ extern int snd_click_size; 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; @@ -60,7 +60,12 @@ int vbetest(void) } } - 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); diff --git a/src/timer.c b/src/timer.c index 8578c94..77c4bbe 100644 --- a/src/timer.c +++ b/src/timer.c @@ -1,6 +1,6 @@ /* -pcboot - bootable PC demo/game kernel -Copyright (C) 2018 John Tsiombikas +256boss - bootable launcher for 256b intros +Copyright (C) 2018-2019 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,6 +19,7 @@ along with this program. If not, see . #include "intr.h" #include "asmops.h" #include "timer.h" +#include "panic.h" #include "config.h" /* frequency of the oscillator driving the 8254 timer */ @@ -57,11 +58,11 @@ along with this program. If not, see . 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; @@ -83,14 +84,99 @@ void init_timer(void) 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); + } + } } -*/ diff --git a/src/timer.h b/src/timer.h index b71e62f..3da5802 100644 --- a/src/timer.h +++ b/src/timer.h @@ -1,6 +1,6 @@ /* -pcboot - bootable PC demo/game kernel -Copyright (C) 2018 John Tsiombikas +256boss - bootable launcher for 256byte intros +Copyright (C) 2018-2019 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,6 +18,8 @@ along with this program. If not, see . #ifndef _TIMER_H_ #define _TIMER_H_ +#include "config.h" + #define MSEC_TO_TICKS(ms) ((ms) * TICK_FREQ_HZ / 1000) #define TICKS_TO_MSEC(tk) ((tk) * 1000 / TICK_FREQ_HZ) @@ -25,7 +27,16 @@ volatile unsigned long nticks; 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_ */ diff --git a/src/vbe.c b/src/vbe.c index 3f0f033..cbb940d 100644 --- a/src/vbe.c +++ b/src/vbe.c @@ -4,13 +4,12 @@ #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; @@ -83,3 +82,51 @@ void print_mode_info(struct vbe_mode_info *mi) 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); +} diff --git a/src/vbe.h b/src/vbe.h index 15fb101..ed6b176 100644 --- a/src/vbe.h +++ b/src/vbe.h @@ -55,6 +55,55 @@ struct vbe_mode_info { 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); @@ -62,4 +111,8 @@ int vbe_set_mode(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_ */ diff --git a/src/video.c b/src/video.c index d926107..ba89bad 100644 --- a/src/video.c +++ b/src/video.c @@ -103,10 +103,9 @@ void *set_video_mode(int mode) 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; @@ -116,12 +115,13 @@ int find_video_mode(int xsz, int ysz, int bpp) 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; } @@ -197,3 +197,15 @@ unsigned int color_mask(int nbits, int pos) 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; +} diff --git a/src/video.h b/src/video.h index bf42f53..f8d4940 100644 --- a/src/video.h +++ b/src/video.h @@ -30,7 +30,7 @@ struct video_mode { 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); @@ -39,7 +39,11 @@ int get_color_bits(int *rbits, int *gbits, int *bbits); 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_ */ diff --git a/src/video_asm.s b/src/video_asm.s index 1f7995c..ac91fd0 100644 --- a/src/video_asm.s +++ b/src/video_asm.s @@ -25,3 +25,20 @@ wait_vsync: 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