sortof works
authorJohn Tsiombikas <nuclear@member.fsf.org>
Mon, 2 Oct 2023 23:40:41 +0000 (02:40 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Mon, 2 Oct 2023 23:40:41 +0000 (02:40 +0300)
21 files changed:
Makefile
src/asmops.h
src/config.h
src/contty.c
src/intr.c
src/intr_s.asm
src/kbregs.h [new file with mode: 0644]
src/kbscan.h [new file with mode: 0644]
src/keyb.c [new file with mode: 0644]
src/keyb.h [new file with mode: 0644]
src/loader.asm
src/main.c
src/panic.c
src/power.c [new file with mode: 0644]
src/power.h [new file with mode: 0644]
src/psaux.c [new file with mode: 0644]
src/psaux.h [new file with mode: 0644]
src/segm.h
src/startup.asm
src/timer.c [new file with mode: 0644]
src/timer.h [new file with mode: 0644]

index 1872bbf..accedf7 100644 (file)
--- 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)
index 6a25f15..09adb13 100644 (file)
@@ -26,7 +26,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #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)
index c83bb90..1c1f7ad 100644 (file)
@@ -22,6 +22,6 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 #define TICK_FREQ_HZ           250
 
 #define CON_TEXTMODE
-#define CON_SERIAL
+/*#define CON_SERIAL*/
 
 #endif /* PCBOOT_CONFIG_H_ */
index ce1115c..b36a9c2 100644 (file)
@@ -1,6 +1,6 @@
 /*
 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
@@ -44,11 +44,12 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 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;
index 5a351c8..6feb03d 100644 (file)
@@ -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);
        }
index cdae52a..58c31f6 100644 (file)
@@ -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 (file)
index 0000000..813fa1a
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+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_ */
diff --git a/src/kbscan.h b/src/kbscan.h
new file mode 100644 (file)
index 0000000..aaca8b8
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+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_ */
diff --git a/src/keyb.c b/src/keyb.c
new file mode 100644 (file)
index 0000000..b6e31f3
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+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;
+}
diff --git a/src/keyb.h b/src/keyb.h
new file mode 100644 (file)
index 0000000..dc92f62
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+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_ */
index 68504f8..4cdb702 100644 (file)
@@ -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
index 93cbfd5..08f9510 100644 (file)
@@ -1,14 +1,26 @@
-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;
 }
index 3daff29..18e7dc6 100644 (file)
@@ -1,6 +1,6 @@
 /*
 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
diff --git a/src/power.c b/src/power.c
new file mode 100644 (file)
index 0000000..8a677d0
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+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!");
+}
diff --git a/src/power.h b/src/power.h
new file mode 100644 (file)
index 0000000..5e0c9fb
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+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_ */
diff --git a/src/psaux.c b/src/psaux.c
new file mode 100644 (file)
index 0000000..4f1e2b0
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+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);
+       }
+}
diff --git a/src/psaux.h b/src/psaux.h
new file mode 100644 (file)
index 0000000..7acc3f6
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+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_ */
index b57375b..bf76c72 100644 (file)
@@ -18,6 +18,8 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 #ifndef SEGM_H_
 #define SEGM_H_
 
+#include <inttypes.h>
+
 enum {
        SEGM_KCODE = 1,
        SEGM_KDATA = 2,
index 16e08fa..21e25e9 100644 (file)
@@ -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 (file)
index 0000000..da313f9
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+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);
+               }
+       }
+}
diff --git a/src/timer.h b/src/timer.h
new file mode 100644 (file)
index 0000000..c41c0f5
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+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_ */