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)
#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)
#define TICK_FREQ_HZ 250
#define CON_TEXTMODE
-#define CON_SERIAL
+/*#define CON_SERIAL*/
#endif /* PCBOOT_CONFIG_H_ */
/*
pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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;
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
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;
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);
}
; 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
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef KBREGS_H_
+#define KBREGS_H_
+
+#define KB_IRQ 1
+#define PSAUX_IRQ 12
+
+#define KB_DATA_PORT 0x60
+#define KB_CMD_PORT 0x64
+#define KB_STATUS_PORT 0x64
+
+#define KB_ACK 0xfa
+#define KB_NACK 0xfe
+#define KB_TEST_PASSED 0x55
+#define KB_TEST_FAILED 0xfc
+
+#define KB_STAT_OUTBUF_FULL 0x01
+#define KB_STAT_INBUF_FULL 0x02
+#define KB_STAT_SYSFLAG 0x04
+#define KB_STAT_CMD 0x08
+#define KB_STAT_ACTIVE 0x10
+#define KB_STAT_AUX 0x20
+#define KB_STAT_TIMEOUT 0x40
+#define KB_STAT_PAR_ERROR 0x80
+
+#define KB_CMD_TEST 0xaa
+
+/* keyboard commands */
+#define KB_CMD_GET_CMDBYTE 0x20
+#define KB_CMD_SET_CMDBYTE 0x60
+#define KB_CMD_AUX_ENABLE 0xa8
+#define KB_CMD_READ_OUTPORT 0xd0
+#define KB_CMD_WRITE_OUTPORT 0xd1
+#define KB_CMD_PSAUX 0xd4
+#define KB_CMD_PULSE_RESET 0xfe
+
+/* controller command byte bits */
+#define KB_CCB_KB_INTREN 0x01
+#define KB_CCB_AUX_INTREN 0x02
+#define KB_CCB_SYSFLAG 0x04
+#define KB_CCB_KB_DISABLE 0x10
+#define KB_CCB_AUX_DISABLE 0x20
+#define KB_CCB_KB_XLAT 0x40
+
+/* psaux commands (use with d4 prefix) */
+#define AUX_CMD_STREAM_MODE 0xea
+#define AUX_CMD_READ_MOUSE 0xeb
+#define AUX_CMD_REMOTE_MODE 0xf0
+#define AUX_CMD_ENABLE 0xf4
+#define AUX_CMD_DEFAULTS 0xf6
+
+#define AUX_PKT0_LEFTBN 0x01
+#define AUX_PKT0_RIGHTBN 0x02
+#define AUX_PKT0_MIDDLEBN 0x04
+#define AUX_PKT0_ALWAYS1 0x08
+#define AUX_PKT0_XSIGN 0x10
+#define AUX_PKT0_YSIGN 0x20
+#define AUX_PKT0_XOVF 0x40
+#define AUX_PKT0_YOVF 0x80
+
+#define AUX_PKT0_OVF_BITS (AUX_PKT0_XOVF | AUX_PKT0_YOVF)
+#define AUX_PKT0_BUTTON_BITS \
+ (AUX_PKT0_LEFTBN | AUX_PKT0_RIGHTBN | AUX_PKT0_MIDDLEBN)
+
+#endif /* KBREGS_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef KBSCAN_H_
+#define KBSCAN_H_
+
+/* table with rough translations from set 1 scancodes to ASCII-ish */
+static int scantbl_set1[] = {
+ 0, KB_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', /* 0 - e */
+ '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', /* f - 1c */
+ KB_LCTRL, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', /* 1d - 29 */
+ KB_LSHIFT, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', KB_RSHIFT, /* 2a - 36 */
+ KB_NUM_MUL, KB_LALT, ' ', KB_CAPSLK, KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, KB_F7, KB_F8, KB_F9, KB_F10, /* 37 - 44 */
+ KB_NUMLK, KB_SCRLK, KB_NUM_7, KB_NUM_8, KB_NUM_9, KB_NUM_MINUS, KB_NUM_4, KB_NUM_5, KB_NUM_6, KB_NUM_PLUS, /* 45 - 4e */
+ KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_0, KB_NUM_DOT, KB_SYSRQ, 0, 0, KB_F11, KB_F12, /* 4d - 58 */
+ 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_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+#include "keyb.h"
+#include "intr.h"
+#include "asmops.h"
+#include "kbregs.h"
+#include "kbscan.h"
+#include "power.h"
+#include "panic.h"
+
+#define delay7us() \
+ do { \
+ iodelay(); iodelay(); iodelay(); iodelay(); \
+ iodelay(); iodelay(); iodelay(); \
+ } while(0)
+
+static void set_ccb(unsigned char ccb);
+static unsigned char get_ccb(void);
+
+static void kbintr();
+
+#define BUFSZ 64
+#define ADVANCE(x) ((x) = ((x) + 1) & (BUFSZ - 1))
+
+static int buffer[BUFSZ];
+static int buf_ridx, buf_widx;
+
+static unsigned int num_pressed;
+static unsigned char keystate[256];
+
+
+void kb_init(void)
+{
+ buf_ridx = buf_widx = 0;
+ num_pressed = 0;
+ memset(keystate, 0, sizeof keystate);
+
+ /* make sure set1 translation is enabled */
+ kb_set_translate(1);
+
+ interrupt(IRQ_TO_INTR(KB_IRQ), kbintr);
+ kb_intr_enable();
+}
+
+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;
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef KEYB_H_
+#define KEYB_H_
+
+#define KB_ANY (-1)
+#define KB_ALT (-2)
+#define KB_CTRL (-3)
+#define KB_SHIFT (-4)
+
+/* special keys */
+enum {
+ KB_ESC = 27,
+ KB_BACKSP = 127,
+
+ KB_INSERT, KB_DEL, KB_HOME, KB_END, KB_PGUP, KB_PGDN,
+ KB_LEFT, KB_RIGHT, KB_UP, KB_DOWN,
+ KB_NUM_DOT, KB_NUM_ENTER, KB_NUM_PLUS, KB_NUM_MINUS, KB_NUM_MUL, KB_NUM_DIV,
+ KB_NUM_0, KB_NUM_1, KB_NUM_2, KB_NUM_3, KB_NUM_4,
+ KB_NUM_5, KB_NUM_6, KB_NUM_7, KB_NUM_8, KB_NUM_9,
+
+ KB_LALT, KB_RALT,
+ KB_LCTRL, KB_RCTRL,
+ KB_LSHIFT, KB_RSHIFT,
+ KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6,
+ KB_F7, KB_F8, KB_F9, KB_F10, KB_F11, KB_F12,
+ KB_CAPSLK, KB_NUMLK, KB_SCRLK, KB_SYSRQ
+};
+
+void kb_init(void);
+
+void kb_intr_enable(void);
+void kb_intr_disable(void);
+
+int kb_setmode(int mode);
+int kb_getmode(void);
+
+void kb_set_translate(int xlat);
+int kb_get_translate(void);
+
+/* Boolean predicate for testing the current state of a particular key.
+ * You may also pass KB_ANY to test if any key is held down.
+ */
+int kb_isdown(int key);
+
+/* waits for any keypress */
+void kb_wait(void);
+
+/* removes and returns a single key from the input buffer. */
+int kb_getkey(void);
+
+void kb_putback(int key);
+
+/* returns 1 if the keyboard controller is ready to read/write
+ * returns 0 if the wait times out
+ */
+int kb_wait_write(void);
+int kb_wait_read(void);
+
+void kb_send_cmd(unsigned char cmd);
+void kb_send_data(unsigned char data);
+unsigned char kb_read_data(void);
+
+#endif /* KEYB_H_ */
extern _ldr_main_start
extern _main_start
extern _main_size
+ extern boot_mem_map
+ extern boot_mem_map_size
%include 'macros.inc'
.notvm86:
call setup_serial
call enable_a20
+ call detect_memory
; calculate GDT linear address
xor eax, eax
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
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
-void putchar(int c); /* in startup.asm */
-
-static void printstr(const char *s)
-{
- while(*s) {
- putchar(*s++);
- }
-}
+#include <stdio.h>
+#include "segm.h"
+#include "intr.h"
+#include "mem.h"
+#include "contty.h"
+#include "keyb.h"
+#include "psaux.h"
+#include "timer.h"
int main(void)
{
- 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;
}
/*
pcboot - bootable PC demo/game kernel
-Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include "power.h"
+#include "keyb.h"
+#include "kbregs.h"
+#include "timer.h"
+#include "panic.h"
+#include "asmops.h"
+
+/* defined in intr_asm.S */
+void set_idt(uint32_t addr, uint16_t limit);
+
+void reboot(void)
+{
+ int i;
+
+ printf("reboot: keyboard controller\n");
+ kb_send_cmd(KB_CMD_PULSE_RESET);
+
+ for(i=0; i<32768; i++) {
+ iodelay();
+ }
+
+ printf("reboot: triple-fault\n");
+ set_idt(0, 0);
+ asm volatile("int $3");
+
+ panic("can't reboot!");
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef POWER_H_
+#define POWER_H_
+
+void reboot(void);
+
+#endif /* POWER_H_ */
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include "psaux.h"
+#include "intr.h"
+#include "asmops.h"
+#include "keyb.h"
+#include "kbregs.h"
+
+static void init_mouse(void);
+static void proc_mouse_data(unsigned char *data);
+static void psaux_intr();
+
+static int mx, my;
+static unsigned int bnstate;
+static int present;
+static int bounds[4];
+static int intr_mode;
+
+void init_psaux(void)
+{
+ interrupt(IRQ_TO_INTR(12), psaux_intr);
+
+ init_mouse();
+ set_mouse_bounds(0, 0, 319, 199);
+}
+
+static void init_mouse(void)
+{
+ unsigned char val;
+
+ kb_send_cmd(KB_CMD_AUX_ENABLE);
+ if(kb_wait_read()) {
+ val = kb_read_data();
+ printf("aux enable: %02x\n", (unsigned int)val);
+ }
+
+ /*
+ kb_send_cmd(KB_CMD_PSAUX);
+ kb_send_data(AUX_CMD_REMOTE_MODE);
+ val = kb_read_data();
+ */
+
+ kb_send_cmd(KB_CMD_GET_CMDBYTE);
+ val = kb_read_data();
+ val &= ~KB_CCB_AUX_DISABLE;
+ val |= KB_CCB_AUX_INTREN;
+ kb_send_cmd(KB_CMD_SET_CMDBYTE);
+ kb_send_data(val);
+
+ if(kb_wait_read()) {
+ val = kb_read_data();
+ printf("set cmdbyte: %02x\n", (unsigned int)val);
+ }
+ intr_mode = 1;
+
+ kb_send_cmd(KB_CMD_PSAUX);
+ kb_send_data(AUX_CMD_DEFAULTS);
+ val = kb_read_data();
+
+ kb_send_cmd(KB_CMD_PSAUX);
+ kb_send_data(AUX_CMD_ENABLE);
+ val = kb_read_data();
+
+ present = (val == KB_ACK) ? 1 : 0;
+ printf("init_mouse: %spresent\n", present ? "" : "not ");
+}
+
+int have_mouse(void)
+{
+ return present;
+}
+
+void set_mouse_bounds(int x0, int y0, int x1, int y1)
+{
+ bounds[0] = x0;
+ bounds[1] = y0;
+ bounds[2] = x1;
+ bounds[3] = y1;
+}
+
+unsigned int mouse_state(int *xp, int *yp)
+{
+ if(!intr_mode) {
+ poll_mouse();
+ }
+
+ *xp = mx;
+ *yp = my;
+ return bnstate;
+}
+
+/* TODO: poll_mouse doesn't work (enable remote mode and disable interrupt on init to test) */
+#define STAT_AUX_PENDING (KB_STAT_OUTBUF_FULL | KB_STAT_AUX)
+static inline int aux_pending(void)
+{
+ return (inp(KB_STATUS_PORT) & STAT_AUX_PENDING) == STAT_AUX_PENDING ? 1 : 0;
+}
+
+void poll_mouse(void)
+{
+ static int poll_state;
+ static unsigned char pkt[3];
+ unsigned char rd;
+
+ ser_printf("poll_mouse(%d)\n", poll_state);
+
+ switch(poll_state) {
+ case 0: /* send read mouse command */
+ kb_send_cmd(KB_CMD_PSAUX);
+ kb_send_data(AUX_CMD_READ_MOUSE);
+ ++poll_state;
+ break;
+
+ case 1: /* wait for ACK */
+ if(kb_wait_read()) {// && aux_pending()) {
+ if((rd = kb_read_data()) == KB_ACK) {
+ ++poll_state;
+ } else {
+ ser_printf("poll_mouse state 1: expected ack: %02x\n", (unsigned int)rd);
+ }
+ }
+ break;
+
+ case 2: /* read packet data */
+ case 3:
+ case 4:
+ if(kb_wait_read() && aux_pending()) {
+ int i = poll_state++ - 2;
+ pkt[i] = kb_read_data();
+ }
+ if(poll_state == 5) {
+ ser_printf("proc_mouse_data(%02x %02x %02x)\n", (unsigned int)pkt[0],
+ (unsigned int)pkt[1], (unsigned int)pkt[2]);
+ proc_mouse_data(pkt);
+ poll_state = 0;
+ }
+ break;
+
+ default:
+ ser_printf("poll_mouse reached state: %d\n", poll_state);
+ }
+}
+
+static void proc_mouse_data(unsigned char *data)
+{
+ int dx, dy;
+
+ if(data[0] & AUX_PKT0_OVF_BITS) {
+ /* consensus seems to be that if overflow bits are set, something is
+ * fucked, and it's best to re-initialize the mouse
+ */
+ /*init_mouse();*/
+ } else {
+ bnstate = data[0] & AUX_PKT0_BUTTON_BITS;
+ dx = data[1];
+ dy = data[2];
+
+ if(data[0] & AUX_PKT0_XSIGN) {
+ dx |= 0xffffff00;
+ }
+ if(data[0] & AUX_PKT0_YSIGN) {
+ dy |= 0xffffff00;
+ }
+
+ mx += dx;
+ my -= dy;
+
+ if(mx < bounds[0]) mx = bounds[0];
+ if(mx > bounds[2]) mx = bounds[2];
+ if(my < bounds[1]) my = bounds[1];
+ if(my > bounds[3]) my = bounds[3];
+ }
+}
+
+static void psaux_intr()
+{
+ static unsigned char data[3];
+ static int idx;
+
+ if(!(inp(KB_STATUS_PORT) & KB_STAT_AUX)) {
+ /* no mouse data pending, ignore interrupt */
+ return;
+ }
+
+ data[idx] = kb_read_data();
+ if(++idx >= 3) {
+ idx = 0;
+
+ proc_mouse_data(data);
+ }
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef PSAUX_H_
+#define PSAUX_H_
+
+#define MOUSE_LBN_BIT 1
+#define MOUSE_RBN_BIT 2
+#define MOUSE_MBN_BIT 4
+
+void init_psaux(void);
+
+int have_mouse(void);
+
+void set_mouse_bounds(int x0, int y0, int x1, int y1);
+unsigned int mouse_state(int *xp, int *yp);
+
+void poll_mouse(void);
+
+#endif /* PSAUX_H_ */
#ifndef SEGM_H_
#define SEGM_H_
+#include <inttypes.h>
+
enum {
SEGM_KCODE = 1,
SEGM_KDATA = 2,
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:
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include "intr.h"
+#include "asmops.h"
+#include "timer.h"
+#include "panic.h"
+#include "config.h"
+
+/* frequency of the oscillator driving the 8254 timer */
+#define OSC_FREQ_HZ 1193182
+
+/* macro to divide and round to the nearest integer */
+#define DIV_ROUND(a, b) ((a) / (b) + ((a) % (b)) / ((b) / 2))
+
+/* I/O ports connected to the 8254 */
+#define PORT_DATA0 0x40
+#define PORT_DATA1 0x41
+#define PORT_DATA2 0x42
+#define PORT_CMD 0x43
+
+/* command bits */
+#define CMD_CHAN0 0
+#define CMD_CHAN1 (1 << 6)
+#define CMD_CHAN2 (2 << 6)
+#define CMD_RDBACK (3 << 6)
+
+#define CMD_LATCH 0
+#define CMD_ACCESS_LOW (1 << 4)
+#define CMD_ACCESS_HIGH (2 << 4)
+#define CMD_ACCESS_BOTH (3 << 4)
+
+#define CMD_OP_INT_TERM 0
+#define CMD_OP_ONESHOT (1 << 1)
+#define CMD_OP_RATE (2 << 1)
+#define CMD_OP_SQWAVE (3 << 1)
+#define CMD_OP_SOFT_STROBE (4 << 1)
+#define CMD_OP_HW_STROBE (5 << 1)
+
+#define CMD_MODE_BIN 0
+#define CMD_MODE_BCD 1
+
+volatile unsigned long nticks;
+
+struct timer_event {
+ int dt; /* remaining ticks delta from the previous event */
+ void (*func)(void);
+ struct timer_event *next;
+};
+
+static void timer_handler(int inum);
+
+static struct timer_event *evlist;
+
+
+void init_timer(void)
+{
+ /* calculate the reload count: round(osc / freq) */
+ int reload_count = DIV_ROUND(OSC_FREQ_HZ, TICK_FREQ_HZ);
+
+ /* set the mode to square wave for channel 0, both low
+ * and high reload count bytes will follow...
+ */
+ outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE);
+
+ /* write the low and high bytes of the reload count to the
+ * port for channel 0
+ */
+ outp(PORT_DATA0, reload_count & 0xff);
+ outp(PORT_DATA0, (reload_count >> 8) & 0xff);
+
+ /* set the timer interrupt handler */
+ interrupt(IRQ_TO_INTR(0), timer_handler);
+}
+
+void set_alarm(unsigned long msec, void (*func)(void))
+{
+ int ticks, tsum, iflag;
+ struct timer_event *ev, *node;
+
+ if((ticks = MSEC_TO_TICKS(msec)) <= 0) {
+ return;
+ }
+
+ if(!(ev = malloc(sizeof *ev))) {
+ panic("failed to allocate timer event");
+ return;
+ }
+ ev->func = func;
+
+ iflag = get_intr_flag();
+ disable_intr();
+
+ if(!evlist || ticks < evlist->dt) {
+ /* insert at the begining */
+ ev->next = evlist;
+ evlist = ev;
+
+ ev->dt = ticks;
+ if(ev->next) {
+ ev->next->dt -= ticks;
+ }
+ } else {
+ tsum = evlist->dt;
+ node = evlist;
+
+ while(node->next && ticks > tsum + node->next->dt) {
+ tsum += node->next->dt;
+ node = node->next;
+ }
+
+ ev->next = node->next;
+ node->next = ev;
+
+ /* fix the relative times */
+ ev->dt = ticks - tsum;
+ if(ev->next) {
+ ev->next->dt -= ev->dt;
+ }
+ }
+
+ set_intr_flag(iflag);
+}
+
+void cancel_alarm(void (*func)(void))
+{
+ int iflag;
+ struct timer_event *ev, *node;
+ struct timer_event dummy;
+
+ iflag = get_intr_flag();
+ disable_intr();
+
+ dummy.next = evlist;
+ node = &dummy;
+ while(node->next) {
+ ev = node->next;
+ if(ev->func == func) {
+ /* found it */
+ if(ev->next) {
+ ev->next->dt += ev->dt;
+ }
+ node->next = ev->next;
+ free(ev);
+ break;
+ }
+ node = node->next;
+ }
+
+ set_intr_flag(iflag);
+}
+
+static void timer_handler(int inum)
+{
+ nticks++;
+
+ if(evlist) {
+ evlist->dt--;
+
+ while(evlist && evlist->dt <= 0) {
+ struct timer_event *ev = evlist;
+ evlist = evlist->next;
+
+ ev->func();
+ free(ev);
+ }
+ }
+}
--- /dev/null
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018-2023 John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY, without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+#ifndef _TIMER_H_
+#define _TIMER_H_
+
+#include "config.h"
+
+#define MSEC_TO_TICKS(ms) ((ms) * TICK_FREQ_HZ / 1000)
+#define TICKS_TO_MSEC(tk) ((tk) * 1000 / TICK_FREQ_HZ)
+
+extern volatile unsigned long nticks;
+
+void init_timer(void);
+
+/*
+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_ */