From: John Tsiombikas Date: Mon, 2 Oct 2023 23:40:41 +0000 (+0300) Subject: sortof works X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=commitdiff_plain;h=54223b51a0e2b47a306f73ded68e4bdd6b56f478;p=com32 sortof works --- diff --git a/Makefile b/Makefile index 1872bbf..accedf7 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,8 @@ inc = -Isrc -Isrc/libc AS = nasm ASFLAGS = -Isrc/ -CFLAGS = -m32 -march=i386 $(warn) $(opt) $(dbg) -fno-pic -ffreestanding -nostdinc $(inc) $(def) +CFLAGS = -m32 -march=i386 $(warn) $(opt) $(dbg) -fno-pic -ffreestanding -nostdinc \ + -ffast-math $(inc) $(def) LDFLAGS = -m elf_i386 -nostdlib -T com32.ld -Map test.map $(bin): $(obj) diff --git a/src/asmops.h b/src/asmops.h index 6a25f15..09adb13 100644 --- a/src/asmops.h +++ b/src/asmops.h @@ -26,7 +26,7 @@ along with this program. If not, see . #define CALLER_EIP(eip) \ asm volatile( \ - "mov (%%esp), %0\n\t" \ + "mov 4(%%ebp), %0\n\t" \ : "=a" ((uint32_t)eip)) static inline uint8_t inp(uint16_t port) diff --git a/src/config.h b/src/config.h index c83bb90..1c1f7ad 100644 --- a/src/config.h +++ b/src/config.h @@ -22,6 +22,6 @@ along with this program. If not, see . #define TICK_FREQ_HZ 250 #define CON_TEXTMODE -#define CON_SERIAL +/*#define CON_SERIAL*/ #endif /* PCBOOT_CONFIG_H_ */ diff --git a/src/contty.c b/src/contty.c index ce1115c..b36a9c2 100644 --- a/src/contty.c +++ b/src/contty.c @@ -1,6 +1,6 @@ /* pcboot - bootable PC demo/game kernel -Copyright (C) 2018 John Tsiombikas +Copyright (C) 2018-2023 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 @@ -44,11 +44,12 @@ along with this program. If not, see . static void scroll(void); static void crtc_cursor(int x, int y); +static void crtc_getcursor(int *x, int *y); static void crtc_setstart(int y); static inline unsigned char crtc_read(int reg); static inline void crtc_write(int reg, unsigned char val); -extern int cursor_x, cursor_y; +static int cursor_x, cursor_y; static unsigned char txattr = 0x07; static int start_line; static unsigned char cy0, cy1; @@ -67,9 +68,9 @@ int con_init(void) cy0 &= 0x1f; cy1 = crtc_read(CRTC_REG_CUREND) & 0x1f; + crtc_getcursor(&cursor_x, &cursor_y); con_show_cursor(1); crtc_setstart(0); - crtc_cursor(cursor_x, cursor_y); scr_on = 1; #endif @@ -265,6 +266,17 @@ static void crtc_cursor(int x, int y) crtc_write(CRTC_REG_CURLOC_H, addr >> 8); } +static void crtc_getcursor(int *x, int *y) +{ + unsigned int addr; + + addr = crtc_read(CRTC_REG_CURLOC_L); + addr |= (unsigned int)crtc_read(CRTC_REG_CURLOC_H) << 8; + + *y = addr / NCOLS - start_line; + *x = addr % NCOLS; +} + static void crtc_setstart(int y) { unsigned int addr = y * NCOLS; diff --git a/src/intr.c b/src/intr.c index 5a351c8..6feb03d 100644 --- a/src/intr.c +++ b/src/intr.c @@ -143,7 +143,8 @@ void dispatch_intr(struct intr_frame frm) intr_func[frm.inum](frm.inum); } else { if(frm.inum < 32) { - panic("unhandled exception %d, error code: %d\n", frm.inum, frm.err); + panic("unhandled exception %u, error code: %u, at cs:eip=%x:%x\n", + frm.inum, frm.err, frm.cs, frm.eip); } printf("unhandled interrupt %d\n", frm.inum); } diff --git a/src/intr_s.asm b/src/intr_s.asm index cdae52a..58c31f6 100644 --- a/src/intr_s.asm +++ b/src/intr_s.asm @@ -90,16 +90,16 @@ intr_ret_local: ; special case for the timer interrupt, to avoid all the overhead of ; going through the C interrupt dispatcher 250 times each second - extern nticks - global intr_entry_fast_timer -intr_entry_fast_timer: - inc dword [nticks] - ; signal end of interrupt - push eax - mov al, 20h - out 20h, al - pop eax - iret +; extern nticks +; global intr_entry_fast_timer +;intr_entry_fast_timer: +; inc dword [nticks] +; ; signal end of interrupt +; push eax +; mov al, 20h +; out 20h, al +; pop eax +; iret ; special case for IRQ 7 and IRQ 15, to catch spurious interrupts PIC1_CMD equ 0x20 diff --git a/src/kbregs.h b/src/kbregs.h new file mode 100644 index 0000000..813fa1a --- /dev/null +++ b/src/kbregs.h @@ -0,0 +1,81 @@ +/* +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 KBREGS_H_ +#define KBREGS_H_ + +#define KB_IRQ 1 +#define PSAUX_IRQ 12 + +#define KB_DATA_PORT 0x60 +#define KB_CMD_PORT 0x64 +#define KB_STATUS_PORT 0x64 + +#define KB_ACK 0xfa +#define KB_NACK 0xfe +#define KB_TEST_PASSED 0x55 +#define KB_TEST_FAILED 0xfc + +#define KB_STAT_OUTBUF_FULL 0x01 +#define KB_STAT_INBUF_FULL 0x02 +#define KB_STAT_SYSFLAG 0x04 +#define KB_STAT_CMD 0x08 +#define KB_STAT_ACTIVE 0x10 +#define KB_STAT_AUX 0x20 +#define KB_STAT_TIMEOUT 0x40 +#define KB_STAT_PAR_ERROR 0x80 + +#define KB_CMD_TEST 0xaa + +/* keyboard commands */ +#define KB_CMD_GET_CMDBYTE 0x20 +#define KB_CMD_SET_CMDBYTE 0x60 +#define KB_CMD_AUX_ENABLE 0xa8 +#define KB_CMD_READ_OUTPORT 0xd0 +#define KB_CMD_WRITE_OUTPORT 0xd1 +#define KB_CMD_PSAUX 0xd4 +#define KB_CMD_PULSE_RESET 0xfe + +/* controller command byte bits */ +#define KB_CCB_KB_INTREN 0x01 +#define KB_CCB_AUX_INTREN 0x02 +#define KB_CCB_SYSFLAG 0x04 +#define KB_CCB_KB_DISABLE 0x10 +#define KB_CCB_AUX_DISABLE 0x20 +#define KB_CCB_KB_XLAT 0x40 + +/* psaux commands (use with d4 prefix) */ +#define AUX_CMD_STREAM_MODE 0xea +#define AUX_CMD_READ_MOUSE 0xeb +#define AUX_CMD_REMOTE_MODE 0xf0 +#define AUX_CMD_ENABLE 0xf4 +#define AUX_CMD_DEFAULTS 0xf6 + +#define AUX_PKT0_LEFTBN 0x01 +#define AUX_PKT0_RIGHTBN 0x02 +#define AUX_PKT0_MIDDLEBN 0x04 +#define AUX_PKT0_ALWAYS1 0x08 +#define AUX_PKT0_XSIGN 0x10 +#define AUX_PKT0_YSIGN 0x20 +#define AUX_PKT0_XOVF 0x40 +#define AUX_PKT0_YOVF 0x80 + +#define AUX_PKT0_OVF_BITS (AUX_PKT0_XOVF | AUX_PKT0_YOVF) +#define AUX_PKT0_BUTTON_BITS \ + (AUX_PKT0_LEFTBN | AUX_PKT0_RIGHTBN | AUX_PKT0_MIDDLEBN) + +#endif /* KBREGS_H_ */ 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 new file mode 100644 index 0000000..b6e31f3 --- /dev/null +++ b/src/keyb.c @@ -0,0 +1,294 @@ +/* +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 +#include "keyb.h" +#include "intr.h" +#include "asmops.h" +#include "kbregs.h" +#include "kbscan.h" +#include "power.h" +#include "panic.h" + +#define delay7us() \ + do { \ + iodelay(); iodelay(); iodelay(); iodelay(); \ + iodelay(); iodelay(); iodelay(); \ + } while(0) + +static void set_ccb(unsigned char ccb); +static unsigned char get_ccb(void); + +static void kbintr(); + +#define BUFSZ 64 +#define ADVANCE(x) ((x) = ((x) + 1) & (BUFSZ - 1)) + +static int buffer[BUFSZ]; +static int buf_ridx, buf_widx; + +static unsigned int num_pressed; +static unsigned char keystate[256]; + + +void kb_init(void) +{ + buf_ridx = buf_widx = 0; + num_pressed = 0; + memset(keystate, 0, sizeof keystate); + + /* make sure set1 translation is enabled */ + kb_set_translate(1); + + interrupt(IRQ_TO_INTR(KB_IRQ), kbintr); + kb_intr_enable(); +} + +void kb_intr_enable(void) +{ + unsigned char ccb = get_ccb(); + ccb |= KB_CCB_KB_INTREN; + set_ccb(ccb); +} + +void kb_intr_disable(void) +{ + unsigned char ccb = get_ccb(); + ccb &= ~KB_CCB_KB_INTREN; + set_ccb(ccb); +} + +int kb_setmode(int mode) +{ + kb_send_data(0xf0); + if(!kb_wait_read() || kb_read_data() != KB_ACK) { + return -1; + } + kb_send_data(mode); + if(!kb_wait_read() || kb_read_data() != KB_ACK) { + return -1; + } + return 0; +} + +int kb_getmode(void) +{ + int mode; + + kb_send_data(0xf0); + if(!kb_wait_read() || kb_read_data() != KB_ACK) { + return -1; + } + kb_send_data(0); + if(!kb_wait_read() || kb_read_data() != KB_ACK) { + return -1; + } + mode = kb_read_data(); + + switch(mode) { + case 0x43: return 1; + case 0x41: return 2; + case 0x3f: return 3; + default: + break; + } + return mode; +} + +void kb_set_translate(int xlat) +{ + unsigned char ccb = get_ccb(); + if(xlat) { + ccb |= KB_CCB_KB_XLAT; + } else { + ccb &= ~KB_CCB_KB_XLAT; + } + set_ccb(ccb); +} + +int kb_get_translate(void) +{ + return get_ccb() & KB_CCB_KB_XLAT; +} + +int kb_isdown(int key) +{ + switch(key) { + case KB_ANY: + return num_pressed; + + case KB_ALT: + return keystate[KB_LALT] + keystate[KB_RALT]; + + case KB_CTRL: + return keystate[KB_LCTRL] + keystate[KB_RCTRL]; + } + return keystate[key]; +} + +void kb_wait(void) +{ + int key; + while((key = kb_getkey()) == -1) { + /* put the processor to sleep while waiting for keypresses, but first + * make sure interrupts are enabled, or we'll sleep forever + */ + enable_intr(); + halt_cpu(); + } + kb_putback(key); +} + +int kb_getkey(void) +{ + int res; + + if(buf_ridx == buf_widx) { + return -1; + } + res = buffer[buf_ridx]; + ADVANCE(buf_ridx); + return res; +} + +void kb_putback(int key) +{ + /* go back a place */ + if(--buf_ridx < 0) { + buf_ridx += BUFSZ; + } + + /* if the write end hasn't caught up with us, go back one place + * and put it there, otherwise just overwrite the oldest key which + * is right where we were. + */ + if(buf_ridx == buf_widx) { + ADVANCE(buf_ridx); + } + + buffer[buf_ridx] = key; +} + +int kb_wait_write(void) +{ + int i; + for(i=0; i<32768; i++) { + if(!(inp(KB_STATUS_PORT) & KB_STAT_INBUF_FULL)) { + return 1; + } + iodelay(); + } + /*printf("kb_wait_write timeout\n");*/ + return 0; +} + +int kb_wait_read(void) +{ + int i; + for(i=0; i<32768; i++) { + if((inp(KB_STATUS_PORT) & KB_STAT_OUTBUF_FULL)) { + return 1; + } + iodelay(); + } + /*printf("kb_wait_read timeout\n");*/ + return 0; +} + +void kb_send_cmd(unsigned char cmd) +{ + kb_wait_write(); + outp(KB_CMD_PORT, cmd); +} + +void kb_send_data(unsigned char data) +{ + kb_wait_write(); + outp(KB_DATA_PORT, data); +} + +unsigned char kb_read_data(void) +{ + kb_wait_read(); + delay7us(); + return inp(KB_DATA_PORT); +} + +static void set_ccb(unsigned char ccb) +{ + kb_send_cmd(KB_CMD_SET_CMDBYTE); + kb_send_data(ccb); + + if(kb_wait_read()) { + kb_read_data(); + } +} + +static unsigned char get_ccb(void) +{ + kb_send_cmd(KB_CMD_GET_CMDBYTE); + return kb_read_data(); +} + +static void kbintr() +{ + unsigned char code; + int key, press; + static int ext = 0; + + code = inp(KB_DATA_PORT); + + if(code == 0xe0) { + ext = 1; + return; + } + + if(code & 0x80) { + press = 0; + code &= 0x7f; + + if(num_pressed > 0) { + num_pressed--; + } + } else { + press = 1; + + num_pressed++; + } + + 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); + /* if the write end overtook the read end, advance the read end + * too, to discard the oldest keypress from the buffer + */ + if(buf_widx == buf_ridx) { + ADVANCE(buf_ridx); + } + } + + /* and update keystate table */ + keystate[key] = press; +} diff --git a/src/keyb.h b/src/keyb.h new file mode 100644 index 0000000..dc92f62 --- /dev/null +++ b/src/keyb.h @@ -0,0 +1,79 @@ +/* +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 KEYB_H_ +#define KEYB_H_ + +#define KB_ANY (-1) +#define KB_ALT (-2) +#define KB_CTRL (-3) +#define KB_SHIFT (-4) + +/* special keys */ +enum { + KB_ESC = 27, + KB_BACKSP = 127, + + KB_INSERT, KB_DEL, KB_HOME, KB_END, KB_PGUP, KB_PGDN, + KB_LEFT, KB_RIGHT, KB_UP, KB_DOWN, + KB_NUM_DOT, KB_NUM_ENTER, KB_NUM_PLUS, KB_NUM_MINUS, KB_NUM_MUL, KB_NUM_DIV, + KB_NUM_0, KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_4, + KB_NUM_5, KB_NUM_6, KB_NUM_7, KB_NUM_8, KB_NUM_9, + + KB_LALT, KB_RALT, + KB_LCTRL, KB_RCTRL, + KB_LSHIFT, KB_RSHIFT, + KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, + KB_F7, KB_F8, KB_F9, KB_F10, KB_F11, KB_F12, + KB_CAPSLK, KB_NUMLK, KB_SCRLK, KB_SYSRQ +}; + +void kb_init(void); + +void kb_intr_enable(void); +void kb_intr_disable(void); + +int kb_setmode(int mode); +int kb_getmode(void); + +void kb_set_translate(int xlat); +int kb_get_translate(void); + +/* Boolean predicate for testing the current state of a particular key. + * You may also pass KB_ANY to test if any key is held down. + */ +int kb_isdown(int key); + +/* waits for any keypress */ +void kb_wait(void); + +/* removes and returns a single key from the input buffer. */ +int kb_getkey(void); + +void kb_putback(int key); + +/* returns 1 if the keyboard controller is ready to read/write + * returns 0 if the wait times out + */ +int kb_wait_write(void); +int kb_wait_read(void); + +void kb_send_cmd(unsigned char cmd); +void kb_send_data(unsigned char data); +unsigned char kb_read_data(void); + +#endif /* KEYB_H_ */ diff --git a/src/loader.asm b/src/loader.asm index 68504f8..4cdb702 100644 --- a/src/loader.asm +++ b/src/loader.asm @@ -4,6 +4,8 @@ extern _ldr_main_start extern _main_start extern _main_size + extern boot_mem_map + extern boot_mem_map_size %include 'macros.inc' @@ -34,6 +36,7 @@ _start: .notvm86: call setup_serial call enable_a20 + call detect_memory ; calculate GDT linear address xor eax, eax @@ -79,6 +82,14 @@ _start: shr ecx, 2 rep movsd + ; copy memory map + mov eax, [mem_map_size] + mov [es:boot_mem_map_size], eax + mov esi, mem_map + mov edi, boot_mem_map + mov ecx, 32 ; 128 bytes + rep movsd + mov ax, 10h mov ds, ax mov ss, ax @@ -202,8 +213,187 @@ kbc_wait_write: jnz kbc_wait_write ret -UART_BASE equ 2f8h ; COM1: 3f8, COM2: 2f8 -UART_DIVISOR equ 115200 / 9600 ; 9600 baud + +; ---------------------- memory detection ----------------------- + +detect_memory: + mov si, memdet_e820_msg + call printstr + call detect_mem_e820 + jnc .done + mov si, str_fail + call printstr + + mov si, memdet_e801_msg + call printstr + call detect_mem_e801 + jnc .done + mov si, str_fail + call printstr + + mov esi, memdet_88_msg + call printstr + call detect_mem_88 + jnc .done + mov esi, str_fail + call printstr + + mov si, memdet_fail_msg + call printstr + jmp exit +.done: + mov si, str_ok + call printstr + ret + +str_ok db 'OK',10,0 +str_fail db 'failed',10,0 +memdet_fail_msg db 'Failed to detect available memory!',10,0 +memdet_e820_msg db "Detecting RAM (BIOS 15h/0xe820)... ",0 +memdet_e801_msg db "Detecting RAM (BIOS 15h/0xe801)... ",0 +memdet_88_msg db "Detecting RAM (BIOS 15h/0x88, max 64mb)... ",0 + + ; detect extended memory using BIOS call 15h/e820 +detect_mem_e820: + mov dword [mem_map_size], 0 + + mov edi, .buffer + xor ebx, ebx + mov edx, 534d4150h + +.looptop: + mov eax, 0xe820 + mov ecx, 24 + int 15h + jc .fail + cmp eax, 534d4150h + jnz .fail + + ; skip areas starting above 4GB as we won't be able to use them + cmp dword [di + 4], 0 + jnz .skip + + ; only care for type 1 (usable ram), otherwise ignore + cmp dword [di + 16], 1 + jnz .skip + + mov eax, [.buffer] + mov esi, mem_map + mov ebp, [mem_map_size] + mov [ebp * 8 + esi], eax + + ; skip areas with 0 size (also clamp size to 4gb) + ; test high 32bits + cmp dword [edi + 12], 0 + jz .highzero + ; high part is non-zero, make low part ffffffff + xor eax, eax + not eax + jmp .skiph0 + +.highzero: + ; if both high and low parts are zero, ignore + mov eax, [di + 8] + test eax, eax + jz .skip + +.skiph0:mov [ebp * 8 + esi + 4], eax + inc dword [mem_map_size] + +.skip: + ; terminate the loop if ebx was reset to 0 + test ebx, ebx + jz .done + jmp .looptop +.done: + clc + ret + +.fail: ; if size > 0, then it's not a failure, just the end + cmp dword [mem_map_size], 0 + jnz .done + stc + ret + +.buffer times 32 db 0 + + ; detect extended memory using BIOS call 15h/e801 +detect_mem_e801: + mov si, mem_map + mov ebp, [mem_map_size] + mov dword [ebp], 0 + + xor cx, cx + xor dx, dx + mov ax, 0xe801 + int 15h + jc .fail + + test cx, cx + jnz .foo1 + test ax, ax + jz .fail + mov cx, ax + mov dx, bx + +.foo1: mov dword [si], 100000h + movzx eax, cx + ; first size is in KB, convert to bytes + shl eax, 10 + jnc .foo2 + ; overflow means it's >4GB, clamp to 4GB + mov eax, 0xffffffff +.foo2: mov [si + 4], eax + inc dword [mem_map_size] + test dx, dx + jz .done + mov dword [si + 8], 1000000h + movzx eax, dx + ; second size is in 64kb blocks, convert to bytes + shl eax, 16 + jnc .foo3 + ; overflow means it's >4GB, clamp to 4GB + mov eax, 0xffffffff +.foo3: mov [si + 12], eax + inc dword [mem_map_size] +.done: + clc + ret +.fail: + stc + ret + +detect_mem_88: + ; reportedly some BIOS implementations fail to clear CF on success + clc + mov ah, 88h + int 15h + jc .fail + + test ax, ax + jz .fail + + ; ax has size in KB, convert to bytes in eax + and eax, 0xffff + shl eax, 10 + + mov esi, mem_map + mov dword [si], 100000h + mov [si + 4], eax + + mov dword [mem_map_size], 1 + clc + ret +.fail: stc + ret + + align 4 +mem_map_size dd 0 +mem_map times 128 db 0 + + +; ----------------------- serial console ------------------------ + setup_serial: ; set clock divisor mov dx, UART_BASE + 3 ; LCTL diff --git a/src/main.c b/src/main.c index 93cbfd5..08f9510 100644 --- a/src/main.c +++ b/src/main.c @@ -1,14 +1,26 @@ -void putchar(int c); /* in startup.asm */ - -static void printstr(const char *s) -{ - while(*s) { - putchar(*s++); - } -} +#include +#include "segm.h" +#include "intr.h" +#include "mem.h" +#include "contty.h" +#include "keyb.h" +#include "psaux.h" +#include "timer.h" int main(void) { - printstr("hello from C\n"); + init_intr(); + + con_init(); + kb_init(); + init_psaux(); + + init_mem(); + + init_timer(); + + enable_intr(); + + printf("hello from C\n"); return 0; } diff --git a/src/panic.c b/src/panic.c index 3daff29..18e7dc6 100644 --- a/src/panic.c +++ b/src/panic.c @@ -1,6 +1,6 @@ /* pcboot - bootable PC demo/game kernel -Copyright (C) 2018 John Tsiombikas +Copyright (C) 2018-2023 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/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/psaux.c b/src/psaux.c new file mode 100644 index 0000000..4f1e2b0 --- /dev/null +++ b/src/psaux.c @@ -0,0 +1,207 @@ +/* +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 . +*/ +#include +#include "psaux.h" +#include "intr.h" +#include "asmops.h" +#include "keyb.h" +#include "kbregs.h" + +static void init_mouse(void); +static void proc_mouse_data(unsigned char *data); +static void psaux_intr(); + +static int mx, my; +static unsigned int bnstate; +static int present; +static int bounds[4]; +static int intr_mode; + +void init_psaux(void) +{ + interrupt(IRQ_TO_INTR(12), psaux_intr); + + init_mouse(); + set_mouse_bounds(0, 0, 319, 199); +} + +static void init_mouse(void) +{ + unsigned char val; + + kb_send_cmd(KB_CMD_AUX_ENABLE); + if(kb_wait_read()) { + val = kb_read_data(); + printf("aux enable: %02x\n", (unsigned int)val); + } + + /* + kb_send_cmd(KB_CMD_PSAUX); + kb_send_data(AUX_CMD_REMOTE_MODE); + val = kb_read_data(); + */ + + kb_send_cmd(KB_CMD_GET_CMDBYTE); + val = kb_read_data(); + val &= ~KB_CCB_AUX_DISABLE; + val |= KB_CCB_AUX_INTREN; + kb_send_cmd(KB_CMD_SET_CMDBYTE); + kb_send_data(val); + + if(kb_wait_read()) { + val = kb_read_data(); + printf("set cmdbyte: %02x\n", (unsigned int)val); + } + intr_mode = 1; + + kb_send_cmd(KB_CMD_PSAUX); + kb_send_data(AUX_CMD_DEFAULTS); + val = kb_read_data(); + + kb_send_cmd(KB_CMD_PSAUX); + kb_send_data(AUX_CMD_ENABLE); + val = kb_read_data(); + + present = (val == KB_ACK) ? 1 : 0; + printf("init_mouse: %spresent\n", present ? "" : "not "); +} + +int have_mouse(void) +{ + return present; +} + +void set_mouse_bounds(int x0, int y0, int x1, int y1) +{ + bounds[0] = x0; + bounds[1] = y0; + bounds[2] = x1; + bounds[3] = y1; +} + +unsigned int mouse_state(int *xp, int *yp) +{ + if(!intr_mode) { + poll_mouse(); + } + + *xp = mx; + *yp = my; + return bnstate; +} + +/* TODO: poll_mouse doesn't work (enable remote mode and disable interrupt on init to test) */ +#define STAT_AUX_PENDING (KB_STAT_OUTBUF_FULL | KB_STAT_AUX) +static inline int aux_pending(void) +{ + return (inp(KB_STATUS_PORT) & STAT_AUX_PENDING) == STAT_AUX_PENDING ? 1 : 0; +} + +void poll_mouse(void) +{ + static int poll_state; + static unsigned char pkt[3]; + unsigned char rd; + + ser_printf("poll_mouse(%d)\n", poll_state); + + switch(poll_state) { + case 0: /* send read mouse command */ + kb_send_cmd(KB_CMD_PSAUX); + kb_send_data(AUX_CMD_READ_MOUSE); + ++poll_state; + break; + + case 1: /* wait for ACK */ + if(kb_wait_read()) {// && aux_pending()) { + if((rd = kb_read_data()) == KB_ACK) { + ++poll_state; + } else { + ser_printf("poll_mouse state 1: expected ack: %02x\n", (unsigned int)rd); + } + } + break; + + case 2: /* read packet data */ + case 3: + case 4: + if(kb_wait_read() && aux_pending()) { + int i = poll_state++ - 2; + pkt[i] = kb_read_data(); + } + if(poll_state == 5) { + ser_printf("proc_mouse_data(%02x %02x %02x)\n", (unsigned int)pkt[0], + (unsigned int)pkt[1], (unsigned int)pkt[2]); + proc_mouse_data(pkt); + poll_state = 0; + } + break; + + default: + ser_printf("poll_mouse reached state: %d\n", poll_state); + } +} + +static void proc_mouse_data(unsigned char *data) +{ + int dx, dy; + + if(data[0] & AUX_PKT0_OVF_BITS) { + /* consensus seems to be that if overflow bits are set, something is + * fucked, and it's best to re-initialize the mouse + */ + /*init_mouse();*/ + } else { + bnstate = data[0] & AUX_PKT0_BUTTON_BITS; + dx = data[1]; + dy = data[2]; + + if(data[0] & AUX_PKT0_XSIGN) { + dx |= 0xffffff00; + } + if(data[0] & AUX_PKT0_YSIGN) { + dy |= 0xffffff00; + } + + mx += dx; + my -= dy; + + if(mx < bounds[0]) mx = bounds[0]; + if(mx > bounds[2]) mx = bounds[2]; + if(my < bounds[1]) my = bounds[1]; + if(my > bounds[3]) my = bounds[3]; + } +} + +static void psaux_intr() +{ + static unsigned char data[3]; + static int idx; + + if(!(inp(KB_STATUS_PORT) & KB_STAT_AUX)) { + /* no mouse data pending, ignore interrupt */ + return; + } + + data[idx] = kb_read_data(); + if(++idx >= 3) { + idx = 0; + + proc_mouse_data(data); + } +} diff --git a/src/psaux.h b/src/psaux.h new file mode 100644 index 0000000..7acc3f6 --- /dev/null +++ b/src/psaux.h @@ -0,0 +1,34 @@ +/* +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 PSAUX_H_ +#define PSAUX_H_ + +#define MOUSE_LBN_BIT 1 +#define MOUSE_RBN_BIT 2 +#define MOUSE_MBN_BIT 4 + +void init_psaux(void); + +int have_mouse(void); + +void set_mouse_bounds(int x0, int y0, int x1, int y1); +unsigned int mouse_state(int *xp, int *yp); + +void poll_mouse(void); + +#endif /* PSAUX_H_ */ diff --git a/src/segm.h b/src/segm.h index b57375b..bf76c72 100644 --- a/src/segm.h +++ b/src/segm.h @@ -18,6 +18,8 @@ along with this program. If not, see . #ifndef SEGM_H_ #define SEGM_H_ +#include + enum { SEGM_KCODE = 1, SEGM_KDATA = 2, diff --git a/src/startup.asm b/src/startup.asm index 16e08fa..21e25e9 100644 --- a/src/startup.asm +++ b/src/startup.asm @@ -22,16 +22,23 @@ startup: in al, 60h ret - global putchar -putchar: - mov eax, [esp + 4] - cmp al, 10 - jnz .nonl - push eax - mov al, 13 - SER_PUTCHAR - pop eax -.nonl: SER_PUTCHAR - ret +; global putchar +;putchar: +; mov eax, [esp + 4] +; cmp al, 10 +; jnz .nonl +; push eax +; mov al, 13 +; SER_PUTCHAR +; pop eax +;.nonl: SER_PUTCHAR +; ret + + section .data + global boot_mem_map_size + global boot_mem_map + align 4 +boot_mem_map_size dd 0 +boot_mem_map times 128 db 0 ; vi:set ts=8 sts=8 sw=8 ft=nasm: diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000..da313f9 --- /dev/null +++ b/src/timer.c @@ -0,0 +1,183 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2023 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 "intr.h" +#include "asmops.h" +#include "timer.h" +#include "panic.h" +#include "config.h" + +/* frequency of the oscillator driving the 8254 timer */ +#define OSC_FREQ_HZ 1193182 + +/* macro to divide and round to the nearest integer */ +#define DIV_ROUND(a, b) ((a) / (b) + ((a) % (b)) / ((b) / 2)) + +/* I/O ports connected to the 8254 */ +#define PORT_DATA0 0x40 +#define PORT_DATA1 0x41 +#define PORT_DATA2 0x42 +#define PORT_CMD 0x43 + +/* command bits */ +#define CMD_CHAN0 0 +#define CMD_CHAN1 (1 << 6) +#define CMD_CHAN2 (2 << 6) +#define CMD_RDBACK (3 << 6) + +#define CMD_LATCH 0 +#define CMD_ACCESS_LOW (1 << 4) +#define CMD_ACCESS_HIGH (2 << 4) +#define CMD_ACCESS_BOTH (3 << 4) + +#define CMD_OP_INT_TERM 0 +#define CMD_OP_ONESHOT (1 << 1) +#define CMD_OP_RATE (2 << 1) +#define CMD_OP_SQWAVE (3 << 1) +#define CMD_OP_SOFT_STROBE (4 << 1) +#define CMD_OP_HW_STROBE (5 << 1) + +#define CMD_MODE_BIN 0 +#define CMD_MODE_BCD 1 + +volatile unsigned long nticks; + +struct timer_event { + int dt; /* remaining ticks delta from the previous event */ + void (*func)(void); + struct timer_event *next; +}; + +static void timer_handler(int inum); + +static struct timer_event *evlist; + + +void init_timer(void) +{ + /* calculate the reload count: round(osc / freq) */ + int reload_count = DIV_ROUND(OSC_FREQ_HZ, TICK_FREQ_HZ); + + /* set the mode to square wave for channel 0, both low + * and high reload count bytes will follow... + */ + outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE); + + /* write the low and high bytes of the reload count to the + * port for channel 0 + */ + outp(PORT_DATA0, reload_count & 0xff); + outp(PORT_DATA0, (reload_count >> 8) & 0xff); + + /* set the timer interrupt handler */ + interrupt(IRQ_TO_INTR(0), timer_handler); +} + +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 new file mode 100644 index 0000000..c41c0f5 --- /dev/null +++ b/src/timer.h @@ -0,0 +1,42 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2023 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 _TIMER_H_ +#define _TIMER_H_ + +#include "config.h" + +#define MSEC_TO_TICKS(ms) ((ms) * TICK_FREQ_HZ / 1000) +#define TICKS_TO_MSEC(tk) ((tk) * 1000 / TICK_FREQ_HZ) + +extern volatile unsigned long nticks; + +void init_timer(void); + +/* +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_ */