From 10b5eee2fea98a1209e42ffdaee3a613bdde958f Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 19 Oct 2017 20:45:02 +0100 Subject: [PATCH] ibmpc: Add work in progress --- Kernel/platform-ibmpc/8259a.c | 76 +++++++++ Kernel/platform-ibmpc/Makefile | 7 +- Kernel/platform-ibmpc/bioscon.c | 98 +++++++++++ Kernel/platform-ibmpc/biosdisk.c | 33 +++- Kernel/platform-ibmpc/bioskey.S | 163 ++++++++++++++++++ Kernel/platform-ibmpc/biosvid.S | 111 ++++++++++++ Kernel/platform-ibmpc/bootstrap.S | 77 +++++++++ Kernel/platform-ibmpc/config.h | 12 +- Kernel/platform-ibmpc/confspace.h | 67 ++++++++ Kernel/platform-ibmpc/devices.c | 4 +- Kernel/platform-ibmpc/devtty.c | 31 +--- Kernel/platform-ibmpc/devtty.h | 6 - Kernel/platform-ibmpc/emm.c | 233 +++++++++++++++++++++++++ Kernel/platform-ibmpc/emm_bios.S | 84 +++++++++ Kernel/platform-ibmpc/ibmpc.S | 53 +++--- Kernel/platform-ibmpc/ibmpc.h | 19 +++ Kernel/platform-ibmpc/main.c | 38 ++++- Kernel/platform-ibmpc/xmsrd.c | 107 ++++++++++++ Kernel/platform-ibmpc/xmsrd_asm.S | 50 ++++++ Kernel/platform-ibmpc/xt_key.c | 274 ++++++++++++++++++++++++++++++ 20 files changed, 1479 insertions(+), 64 deletions(-) create mode 100644 Kernel/platform-ibmpc/8259a.c create mode 100644 Kernel/platform-ibmpc/bioscon.c create mode 100644 Kernel/platform-ibmpc/bioskey.S create mode 100644 Kernel/platform-ibmpc/biosvid.S create mode 100644 Kernel/platform-ibmpc/bootstrap.S create mode 100644 Kernel/platform-ibmpc/confspace.h create mode 100644 Kernel/platform-ibmpc/emm.c create mode 100644 Kernel/platform-ibmpc/emm_bios.S create mode 100644 Kernel/platform-ibmpc/ibmpc.h create mode 100644 Kernel/platform-ibmpc/xmsrd.c create mode 100644 Kernel/platform-ibmpc/xmsrd_asm.S create mode 100644 Kernel/platform-ibmpc/xt_key.c diff --git a/Kernel/platform-ibmpc/8259a.c b/Kernel/platform-ibmpc/8259a.c new file mode 100644 index 00000000..53ae1bec --- /dev/null +++ b/Kernel/platform-ibmpc/8259a.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include + +/* + * 8259A interrupt controllers + * + * Implement the interrupt logic for classic PC, PC/XT and PC/AT + * platforms. + */ + +static irqvector_t irqvec[16]; +static uint8_t irq_mask[2]; +static uint8_t irq_port[2] = { 0x21, 0xA1 }; + +void mask_irq(uint8_t irq) +{ + irqflags_t mask = di(); + uint8_t chip = irq >> 3; + irq &= 7; + irq_mask[chip] |= (1 << irq); + outb(irq_mask[chip], irq_port[chip]); + irqrestore(mask); +} + +void unmask_irq(uint8_t irq) +{ + irqflags_t mask = di(); + uint8_t chip = irq >> 3; + irq &= 7; + irq_mask[chip] &= ~(1 << irq); + outb(irq_mask[chip], irq_port[chip]); + irqrestore(mask); +} + +int8_t int_to_irq(uint8_t irq) +{ + if (irq > 15) + return -EINVAL; + if (irq > 7 && pc_at == 0) + return -EINVAL; + if (irq == 2 && pc_at == 1) + irq = 9; + return irq; +} + +void init_irq(void) +{ + if (inb(0xA1) == 0xFF) + pc_at = 0; +} + +int set_irq(int8_t i, irqvector_t handler) +{ + irqflags_t mask = di(); + i = int_to_irq(i); + if (i < 0) + return i; + if (irqvec[i]) + return -EBUSY; + /* + * Replace the existing vector with the right irq{n} handler from our + * table. We don't replace them at start up as we are mostly a BIOS user + */ +//FIXME hook_irqvec(i); + irqvec[i] = handler; + unmask_irq(i); + irqrestore(mask); + return 0; +} + +void do_platform_interrupt(int16_t irq) +{ + (irqvec[irq])(irq); +} diff --git a/Kernel/platform-ibmpc/Makefile b/Kernel/platform-ibmpc/Makefile index dd57aedd..1005662b 100644 --- a/Kernel/platform-ibmpc/Makefile +++ b/Kernel/platform-ibmpc/Makefile @@ -1,8 +1,8 @@ CSRCS = devtty.c -CSRCS += devices.c main.c libc.c biosdisk.c +CSRCS += devices.c main.c libc.c biosdisk.c bioscon.c 8259a.c -ASRCS = ibmpc.S crt0.S +ASRCS = ibmpc.S crt0.S biosvid.S ASRCS += tricks.S COBJS = $(CSRCS:.c=$(BINEXT)) @@ -30,5 +30,6 @@ image: ../tty.o ../devio.o ../filesys.o ../process.o ../inode.o ../syscall_fs.o \ ibmpc.o ../syscall_proc.o ../syscall_other.o ../mm.o \ ../devsys.o ../usermem.o ../syscall_exec16.o ../syscall_fs2.o \ - tricks.o ../syscall_fs3.o biosdisk.o \ + tricks.o ../syscall_fs3.o biosdisk.o ../vt.o bioscon.o biosvid.o \ + ../bank8086.o 8259a.o \ ../usermem_std-8086.o devtty.o libc.o > ../fuzix.map diff --git a/Kernel/platform-ibmpc/bioscon.c b/Kernel/platform-ibmpc/bioscon.c new file mode 100644 index 00000000..90f7c836 --- /dev/null +++ b/Kernel/platform-ibmpc/bioscon.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Minimal BIOS mode console. We could do a lot more by using the + * attributes, mode information and pages properly but this will get + * us going. + */ + +/* + * Private helpers in the asm library + */ +extern uint16_t bios_videomode(void); +extern uint16_t bios_setpage(uint16_t page); +extern void bios_videopage(uint16_t page); +extern void bios_setcursor(uint16_t xy, uint16_t page); +extern void bios_setchar(uint16_t ch, uint16_t pgattr, uint16_t rep); +extern void bios_scrollup(void); +extern void bios_scrolldown(void); +extern void bios_clearscreen(void); + +/* + * These get mapped to VT_HEIGHT and friends by config.h + */ +int8_t vt_height = 25; /* Fixed for now */ +int8_t vt_width; +uint8_t vtattr_cap = VTA_INVERSE|VTA_UNDERLINE|VTA_BOLD|VTA_NOCURSOR; +/* Ok we don't do them yet but .. */ + +/* Methods we must supply to the VT core */ +void cursor_off(void) +{ +} + +void cursor_on(int8_t y, int8_t x) +{ + bios_setcursor((y << 8) | x, 0); +} + +void plot_char(int8_t y, int8_t x, uint16_t ch) +{ + bios_setcursor((y << 8) | x, 0); + bios_setchar(ch, 7, 1); +} + +void clear_lines(int8_t y, int8_t ct) +{ + uint8_t i; + if (ct == 25) + bios_clearscreen(); + else { + for(i = 0; i < ct; y++) { + bios_setcursor((y + i) << 8, 0); + bios_setchar(' ', 7, vt_width); + } + } +} + +void clear_across(int8_t y, int8_t x, int16_t l) +{ + bios_setchar(' ', y + (x << 8), l); +} + +void vtattr_notify(void) +{ +} + +void scroll_up(void) +{ + bios_scrollup(); +} + +void scroll_down(void) +{ + bios_scrolldown(); +} + +/* We only have one console */ +void console_switch(uint8_t n) +{ +} + +/* Sit in the current mode in black and white and hope it carries on working */ +void bioscon_init(void) +{ + uint16_t mode = bios_videomode(); + vt_width = mode >> 8; + if (vt_width == 0) /* In case of busted stuff we try */ + vt_width = 80; + bios_setpage(0); + bios_videopage(0); +} diff --git a/Kernel/platform-ibmpc/biosdisk.c b/Kernel/platform-ibmpc/biosdisk.c index 7736115e..7fad06f9 100644 --- a/Kernel/platform-ibmpc/biosdisk.c +++ b/Kernel/platform-ibmpc/biosdisk.c @@ -1,5 +1,22 @@ /* * PC BIOS disk driver + * + * FIXME: make this use the block stuff and partitions + * FIXME2: some crapware blows up if the buffer crosses a 64K boundary + * so we need to check this for user (and do smaller I/O) and it probably + * means we need to 512 byte align the buffer cache (ick). Whle user is + * 4K aligned they could pass a pointer on a DMA boundary so we may need + * a global disk bounce buffer too 8( + * (OTOH it's a real hardware limit so we can't escape it in native + * drivers either) + * FIXME3: some crapware also blows up if a track or head boundary + * is crossed, maybe we should just do 512 bytes at a time and cry + * in our beer. + * FIXME4: for floppies we need to try and read sector 0/0/1 on open + * and use fn 17 to try different settings until it works (and work out + * how to deal with 1.44 ??) + * FIXME5: we need an ioctl to setup and use the format services for + * floppy */ #include @@ -52,27 +69,33 @@ int hd_write(uint8_t minor, uint8_t rawflag, uint8_t flag) return bios_transfer(false, rawflag, &harddisk[minor]); } +/* FIXME: udata.u_page points into the mm so we need a helper to get our + mapping *but* we don't know if it's cs: or ds: because we might be + loading it ???? */ static int bios_transfer(bool is_read, uint8_t rawflag, struct disk_geom *g) { uint16_t dptr; uint16_t ct = 0; uint32_t st; - int map = 0; uint16_t page = kernel_ds; uint16_t left; uint16_t cylsec; uint8_t tries; /* Sort the actual request out */ - if(rawflag == 1) { + if(rawflag == 1 || rawflag == 3) { if (d_blkoff(9)) return -1; - map = 1; - page = udata.u_page; + /* We need a standard way to wrap the segment gets and page so + we can share code with PDP11 etc */ + if (rawflag == 1) + page = get_data_segment(udata.u_page2); + else + page = get_code_segment(udata.u_page2); #ifdef SWAPDEV } else if (rawflag == 2) { /* Swap device special */ page = swappage; /* Acting on this page */ - map = 1; + /* FIXME: EMM means bank flipping here */ #endif } diff --git a/Kernel/platform-ibmpc/bioskey.S b/Kernel/platform-ibmpc/bioskey.S new file mode 100644 index 00000000..a2f2932e --- /dev/null +++ b/Kernel/platform-ibmpc/bioskey.S @@ -0,0 +1,163 @@ +/* + * BIOS keyboard driver for annoying systems + * + * We are hooked into the int handler for the keyboard and then abuse + * that as a way to tell that we are safe from re-entry because we only + * do keyboard in the keyboard handler + * + * In addition we include an int 4f handler so that on most machines we + * can screen out annoyances like pause and printscreen. + */ + +/* Called every keypress. We run the BIOS handler and see if a char is + present. If not get out. This is the fast path */ +bioskbd_handler: + lcall %cs:*bioskbd_old + cli + pushw %ax + movb $01,%ah + int $0x16 + jnz nochar + xorw %ax,%ax + int $0x16 + cmpb $0,%al + jnz has_char +nochar: + popw %ax + reti + +/* The heavyweight path - dropping into C space */ +has_char: + pushw %ds + pushw %es + pushw %bp + pushw %bx + pushw %cx + pushw %dx + movw kernel_ds,%ds + movw %ss, kbd_ss + movw %sp, kbd_sp + cld + movw %ds,%bx + movw %bx,%es + movw %bx,%ss + movw $kbstack,%sp + pushw %ax + movw $1,%ax + pushw %ax + call tty_inproc + popw %ax + popw %ax + movw kbd_ss,%ss + movw kbd_sp,%sp + popw %dx + popw %cx + pop %bx + popw %bp + popw %es + popw %ds + popw %ax + reti + +/* + * We screen out printscreen and pause funnies. Some ancient systems + * don't have this hook so pause will cause the system to hang until + * it is done, print screen will call int 5 (we fix it up there) and + * ctrl-alt-del will do the usual + */ + +int4f_hook: + pushw %ds + pushw %bx + movw %cs,%bx + movw %bx,%ds + movb %al,%ah + andb $0x7F,%ah + cmpb $0x1D,%ah + je cntrl + cmpb $0x2A,%ah + je lshift + cmpb $0x36,%ah + je rshift + cmpb $0x38,%ah + je alt + cmpb $37,%al /* prtscrn */ + je magicop1 + cmpb $45,%al + je magicop2 + cmp $46,%al + je magicop2 + cmp $0x53,%al + je magicop3 +passthrough: + popf + sec + ret +/* + * Prevent the print screen callback and also the pause behaviour + * on the pause key. + */ +magicop1: /* ignore with shift | ctrl */ + movw shift_1,%bx + cmpw $0,%bx + je passthrough +magicop2: /* igmore with ctrl */ + movb cntrl_1,%bl + cmpb $0,%bl + je passthrough + clc + ret +magicop3: /* ignore with ctrl alt */ + movw alt_1,%bx + cmpw $0,%bx + je passthrough + clc + ret +lshift: + pushw %ax + andb $0x80,%al + movb %al,shift_1 + popw %ax + sec + ret +rshift: + push %ax + andb $0x80,%al + movb %al,shift_2 + popw %ax + sec + ret +cntrl: + push %ax + andb $0x80,%al + movb %al,cntrl_1 + popw %ax + sec + ret +alt: + push %ax + andb $0x80,%al + movb %al,alt_1 + popw %ax + sec + ret + +/* In CS: for simplicity */ +alt_1: + .byte 0 +cntrl_1: + .byte 0 +shift_1: + .byte 0 +shift_2: + .byte 0 + + +bioskbd_old: + .dword 0 + + .data +kbd_ss .word 0 +kbd_sp .word 0 + .bss 128 +kbstack: diff --git a/Kernel/platform-ibmpc/biosvid.S b/Kernel/platform-ibmpc/biosvid.S new file mode 100644 index 00000000..6695bafb --- /dev/null +++ b/Kernel/platform-ibmpc/biosvid.S @@ -0,0 +1,111 @@ +/* + * Stubs for BIOS video + * We need to sort out the logic for blink/underline/attributes + * + * For now we present a simple black and white console although in + * many cases we can actually do 8 colours plus bright plus under + */ + + .arch i8086,jumps + .code16 + .att_syntax prefix + .text + + .global bios_videomode + .global bios_setpage + .global bios_videopage + .global bios_setcursor + .global bios_setchar + .global bios_scrollup + .global bios_scrolldown + .global bios_clearscreen + + +/* Return mode and columns */ +bios_videomode: + movb $0x0F,%ah + int $0x10 + andb $0x7F,%al + movb %ah,dwidth + ret +/* Set our display page */ +bios_setpage: + pushw %bp + movw %sp,%bp + movb 4(%bp),%al + movb $5,%ah + int $0x10 + pop %bp + /* and drop through to see if it worked */ +/* Display page */ +bios_videopage: + movb $0x0F,%ah + int $0x10 + movb %bh,%al + xorb %ah,%ah + ret +bios_setcursor: + pushw %bp + movw %sp,%bp + movw 4(%bp),%dx + movb 6(%bp),%bh + int $0x10 + popw %bp + ret +bios_setchar: + pushw %bp + movw %sp,%bp + movw 4(%bp),%ax + movw 6(%bp),%bx + movw 8(%bp),%cx + movb $0x09,%ah + int $0x10 + popw %bp + ret +bios_scrollup: + pushw %ds /* Trident bug */ + pushw %bp + movw %sp,%bp + xorw %cx,%cx + xorb %bh,%bh /* Until we add colour/attributes */ + movb $24,%dh /* Hack for the moment */ + movb dwidth,%dl + decb %dl + movw $0x0601,%ax + int $0x10 + popw %bp + popw %ds + ret +bios_scrolldown: + pushw %ds /* Trident bug */ + pushw %bp + movw %sp,%bp + xorw %cx,%cx + xorb %bh,%bh /* Until we add colour/attributes */ + movb $24,%dh /* Hack for the moment */ + movb dwidth,%dl + decb %dl + movw $0x0701,%ax + int $0x10 + popw %bp + popw %ds + ret +bios_clearscreen: + pushw %ds /* Trident bug */ + pushw %bp + movw %sp,%bp + xorw %cx,%cx + xorb %bh,%bh /* Until we add colour/attributes */ + movb $24,%dh /* Hack for the moment */ + xorb %al,%al + movb dwidth,%dl + decb %dl + movw $0x0600,%ax + int $0x10 + popw %bp + popw %ds + ret + + .data + +dwidth: .byte 0 diff --git a/Kernel/platform-ibmpc/bootstrap.S b/Kernel/platform-ibmpc/bootstrap.S new file mode 100644 index 00000000..e3d10fc0 --- /dev/null +++ b/Kernel/platform-ibmpc/bootstrap.S @@ -0,0 +1,77 @@ + .arch i8086,jumps + .code16 + .att_syntax prefix + + .text + +/* + * We are loaded somewhere high by the bootstrap and need + * to move the payload down, set up and enter it. The payload never + * returns to us. We are guaranteed not tbe in the payload's way + * + * On entry + * cs/ds are set so our address is cs:0 etc + * es is undefined + * ss:sp is a stack somewhere potentially to be overwritten + * We are in the BIOS environment + * Interrupts are *off* + * + * Tasks: + * Move the stack to 0x500-0x5FF + * Wipe 0x600-0x7FF + * Copy the data and code starting at 0x800 as two blocks (we skip + * the BSS) then jump to the code as cs:0 + */ + +bootstrap: + movb $'B',%al + call printchar + movw $0x50,%ax /* 0x500 is base, 0x600->0x7FF is the + udata and stack we need to clear */ + movw %ax,%es /* Point ES: at the target */ + + cld + + xorw %ax,%ax + movw $0x100,%di + movw $0x100,%cx + rep stosw /* Wipe 0x600-0x7FF */ + movb $'o',%al + call printchar + movw $0x50,%ax + movw %ax,%ss /* Switch stack segment */ + movw $0x100,%sp /* Stack pointer */ + movb $'o',%al + call printchar + sti /* Interrupts are now safe */ + movb $'t',%al + call printchar + movw $0x300,%di /* es: Offset to start coping image */ + movw $end,%si /* ds: Offset of image */ + movw end,%cx /* Length to copy (words, 16 byte pad) */ + rep movsw /* Copy all but BSS */ + movb $'i',%al + call printchar + movw end+2,%cx /* Code length (words) */ + movw end+4,%di /* Code (and discard) base */ + rep movsw /* follows on in block so si is ok */ + movb $'n',%al + call printchar + movw end+6,%bx /* Segment value for code */ + movw $0x50,%ax + movw %ax,%ds + pushw %bx /* Segment of code */ + xorw %ax,%ax + pushw %ax + movb 'g',%al + call printchar + retf /* Jump to new CS:0 */ +printchar: + pushw %ax + pushw %bx + movb $0x0E,%ah + movb $7,%bl + int $10 + popw %bx + popw %ax + ret diff --git a/Kernel/platform-ibmpc/config.h b/Kernel/platform-ibmpc/config.h index 10e8889c..61b41984 100644 --- a/Kernel/platform-ibmpc/config.h +++ b/Kernel/platform-ibmpc/config.h @@ -6,7 +6,7 @@ #undef CONFIG_PROFIL #define CONFIG_MULTI -#define CONFIG_SWAP_ONLY +#define CONFIG_BANK_8086 #define CONFIG_USERMEM_DIRECT #define CONFIG_BANKS 1 #define PROC_SIZE 128 /* 64K, 128 * 512 */ @@ -15,16 +15,24 @@ #define UDATA_SIZE 512 #define UDATA_BLKS 1 +#define CONFIG_VT +#define VT_WIDTH vt_width +#define VT_HEIGHT vt_height +#define VT_RIGHT (vt_width - 1) +#define VT_BOTTOM (vt_height - 1) +extern signed char vt_width, vt_height; + #define PROGBASE 0x8000UL #define PROGLOAD PROGBASE #define PROGTOP 0xE000UL +#if 0 #define SWAP_SIZE (130 + 2) /* 2 for the udata */ #define SWAPBASE PROGBASE #define SWAPTOP 0xE000UL #define MAX_SWAPS PTABSIZE /* Mandatory for swap only */ #define swap_map(x) ((uint8_t *)(x)) - #define SWAPDEV (1) +#endif #define TICKSPERSEC 50 /* Ticks per second */ diff --git a/Kernel/platform-ibmpc/confspace.h b/Kernel/platform-ibmpc/confspace.h new file mode 100644 index 00000000..b7c670b5 --- /dev/null +++ b/Kernel/platform-ibmpc/confspace.h @@ -0,0 +1,67 @@ +/* + * The boot time configuration space loaded at 0x800 + */ + +#define NUM_SER 4 +#define NUM_PAR 4 + +struct serconf { + uint8_t driver; +#define SER_8250 0 + uint8_t info; /* Usually IRQ for non BIOS */ + uint16_t port; +}; + +struct parconf { + uint8_t driver +#define PAR_BIOS 0 + uint8_t info; + uint16_t port; +}; + +struct kconf { + uint16_t magic; +#define MAGIC 0xBA10 + uint8_t kbd_driver; +#define KBD_BIOSHOOK 0 /* (info gives int to hook) */ +#define KBD_XTKEY 1 + uint8_t kbd_info; + + uint8_t video_driver; +#define VID_BIOS 0 /* video_info gives mode to set or FF */ +#define VID_PC 1 /* native PC driver */ + uint8_t video_info[3]; + + uint8_t xms_driver; +#define XMS_OFF 0 +#define XMS_RAMDISK 1 + uint8_t xms_info; + + uint8_t fd_driver +#define FD_BIOS 0 + uint8_t fd_info; + + uint8_t hd_driver +#define HD_BIOS 0 + uint8_t hd_info; + + uint8_t js_driver; +#define JS_BIOS 0 + uint8_t js_info; + + uint8_t ems_driver; +#define EMS_NONE 0 +#define EMS_ABOVEBOARD 1 +#define EMS_LOTECH 2 +#define EMS_OPTI 3 +#define EMS_BIOS 4 + uint8_t ems_ctrl; + uint16_t ems_port; + uint16_t ems_step; + uint16_t ems_off; + uint16_t ems_mem; /* KB or 0 */ + + struct serconf ser[NUM_SER]; + struct lpconf par[NUM_PAR]; + +}; diff --git a/Kernel/platform-ibmpc/devices.c b/Kernel/platform-ibmpc/devices.c index c8f55ca5..f51acaf7 100644 --- a/Kernel/platform-ibmpc/devices.c +++ b/Kernel/platform-ibmpc/devices.c @@ -35,8 +35,8 @@ bool validdev(uint16_t dev) void device_init(void) { - int i; +/* int i; for (i = 1; i <= MAX_SWAPS; i++) - swapmap_init(i); + swapmap_init(i); */ } diff --git a/Kernel/platform-ibmpc/devtty.c b/Kernel/platform-ibmpc/devtty.c index 3954fa8c..8b72096d 100644 --- a/Kernel/platform-ibmpc/devtty.c +++ b/Kernel/platform-ibmpc/devtty.c @@ -1,3 +1,9 @@ +/* + * Just to get us going assume we have a console and keyboard and leave + * the PC serial ports alone as we can't sanely drive them via BIOS. + * We have other stuff to bring up before working on those tricky bits. + */ + #include #include #include @@ -5,9 +11,7 @@ #include #include #include - -volatile uint8_t *uart_data = (volatile uint8_t *)0xF03000; /* UART data */ -volatile uint8_t *uart_status = (volatile uint8_t *)0xF03010; /* UART status */ +#include unsigned char tbuf1[TTYSIZ]; @@ -26,13 +30,12 @@ void kputchar(char c) ttyready_t tty_writeready(uint8_t minor) { - uint8_t c = *uart_status; - return (c & 2) ? TTY_READY_NOW : TTY_READY_SOON; /* TX DATA empty */ + return TTY_READY_NOW; } void tty_putc(uint8_t minor, unsigned char c) { - *uart_data = c; /* Data */ + vtoutput(&c, 1); } void tty_setup(uint8_t minor) @@ -47,19 +50,3 @@ int tty_carrier(uint8_t minor) void tty_sleeping(uint8_t minor) { } - -/* Currently run off the timer */ -void tty_interrupt(void) -{ - uint8_t r = *uart_status; - if (r & 1) { - r = *uart_data; - tty_inproc(1,r); - } -} - -void platform_interrupt(void) -{ - timer_interrupt(); - tty_interrupt(); -} diff --git a/Kernel/platform-ibmpc/devtty.h b/Kernel/platform-ibmpc/devtty.h index 14c28c31..c387e904 100644 --- a/Kernel/platform-ibmpc/devtty.h +++ b/Kernel/platform-ibmpc/devtty.h @@ -1,10 +1,4 @@ #ifndef __DEVTTY_DOT_H__ #define __DEVTTY_DOT_H__ -#define KEY_ROWS 8 -#define KEY_COLS 7 -extern uint8_t keymap[8]; -extern uint8_t keyboard[8][7]; -extern uint8_t shiftkeyboard[8][7]; - #endif diff --git a/Kernel/platform-ibmpc/emm.c b/Kernel/platform-ibmpc/emm.c new file mode 100644 index 00000000..de4aa87e --- /dev/null +++ b/Kernel/platform-ibmpc/emm.c @@ -0,0 +1,233 @@ +#include +#include +#include + +#ifdef CONFIG_IBMPC_EMM + +/* + * Handle EMM memory space using typical implementations of the hardware + * with the triple ports (208/9/A or 218/9/A). Assumes only one EMM card + * + * The Intel board uses 0x258 for the port and adds 0x4000 to get higher + * decodes and usually maps at C0000. 0x259 controls C000/D000 base + * (write 128,0,128,0 for D 128,0,0,0 to 259,4259,8259,C259) + * + * For data write 0x258/4258,8258,C258 with the page number. + */ + +static uint16_t emm[MAX_EMM]; +static uint16_t ems_seg; +static uint16_t emm_ptr; +static uint16_t emm_max; +static uint16_t emm_over; /* EMM we couldn't use for processes but is + a RAMDISC candidate */ + +static uint8_t emsport; /* Port range - usually 208/9/A */ +static uint8_t emsctrl; /* The bits we set for autoinc, base etc */ +static uint16_t emsstep; /* Step for Intel style ports */ +static uint16_t emsoff; /* Offset to add to emm */ + +uint16_t emm_alloc_bank(struct proc_mmu *m) +{ + if (emm_ptr == 0) { + m->emm = 0; + return ENOMEM; + } + m->emm = emm[emm_ptr--]; + m->dseg = ems_seg; + return 0; +} + +void emm_free_bank(struct proc_mmu *m) +{ + emm[++emm_ptr] = m->emm; +} + +/* + * Used for things like the Opti chipset support + */ +static void emm_select_1(uint16_t emm) +{ + /* Select first 16K slot, with autoincrement etc */ + outb(emsctrl, emsport + 2); + for (i = 0; i < 4; i++) { + outb(0x80 |(emm >> 8), emsport + 2); /* A22/A23 + Enable */ + outb(emm, emsport + 1); /* A14 - A 21 , autoincrements bank */ + } +} + +/* + * Intel AboveBoard, + */ +static void emm_select_2(uint16_t emm) +{ + unt16_t port = emsport; + for (i = 0; i < 4; i++) { + outb(emm + emmoff, port); + port += emsstep; + } +} + +void emm_select(uint16_emm) +{ +#ifdef CONFIG_EMM_BIOS + if (emm_type == 3) + emm_select_bios(emm); + else +#endif + if (emm_type == 1) + emm_select_1(emm); + else + emm_select_2(emm); + +} + +uint16_t emm_space_used(void) +{ + return (emm_max - emm_ptr) * 64; +} + +/* Discard for these eventually */ + +void emm_new_bank(struct proc_mmu *m) +{ + if (emm_max == MAX_EMM) { + emm_over++; + return; + } + emm[++emm_ptr] = m->emm; + emm_max++; + ramsize += 64; +} + +/* FIXME: always needs to run with IRQ off */ +uint8_t opti_r(uint8_t c) +{ + outb(c, 0x22); + return inb(0x23); +} + +/* Need a probing version of this */ +static void do_emm_init(uint16_t base, uint16_t pages) +{ + uint16_t i; + for (i = 0; i < pages; i++) + emm_new_bank(base + 4 * i); +} + +int opti_emm_init(void) +{ + uint8_t x = opti_r(0x4F); + + /* EMS off */ + if ((x & 0xC0) != 0xC0) + return 0; + + if (x & 1) + emsport = 0x218; + else + emsport = 0x208; + + emsctrl = 0x80; /* D0000, autoinc */ + emstype = 1; + ems_seg = 0xD000; + + /* Add all the memory from 1MB to the top of extended memory as we are + only using it in EMS mode. Don't get clever and add < 1MB shadow + space */ + do_ems_init(16, xmm_kb / 64); + return 1; +} + +/* Intel Above Board (original to 2MB) */ +int intel_emm_init(uint8_t seg, uint16_t kb) +{ + /* The board lives at 0x258 by default */ + emsport = 0x258; + emsstep = 0x4000; + emstype = 2; + ems_seg = seg; + emmoff = 0; + if (seg != 0xC000 && seg != 0xD000) + return -1; + /* Program the base address */ + outb(0x80, emsport); + outb(0x00, emsport + 0x4000); + outb((seg & 0x1000) >> 9, emsport + 0x8000); + outb(0x00, emsport + 0xC000); + /* Set up the pages */ + do_ems_init(0x80, kb / 64); + return 1; +} + +/* Not clear if writing 209 with values sets the high 3 bits for the + bigger ranges */ +int neat_emm_init(uint8_t seg, uint16_t kb) +{ + emsport = 0x208; + emsstep = 0x4000; + emstype = 2; + ems_seg = 0xD000; + emmoff = 0; + do_ems_init(0x80, kb / 64); + return 1; +} + +/* Four consecutive write only ports giving each bank. About as simple as + it gets */ +int lotech_emm_init(uint8_t seg, uint16_t kb) +{ + emsport = 0x260; + emsstep = 1; + emstype = 2; + ems_seg = seg; + emmoff = -1; + do_ems_init(0x01, kb / 64); + return 1; +} + +#ifdef CONFIG_EMM_BIOS + +/* A few emulators don't have any sane EMM interface but do have the int + hooks in the BIOS environment. That's annoying as **** but we need to + support this for development work. + + We don't behave in a civilised manner we just grab all free pages knowing + nothing else is running */ + +static uint16_t handle; + +/* This is completely unsafe on a real platform, but it's not a real one + so it works... */ +static void bios_emm_select(uint16_t mm) +{ + uint8_t i; + for (i = 0; i < 4; i++) + if (emm_map_memory(i, handle, mm - 1)) + panic("emmfault"); +} + +int bios_emm_init(void) +{ + int num; + int handle; + /* Load the segment from 0x019E and check that :000A contains the ascii + EMS string */ + if (!emm_detect()) + return 0; + /* Where does it map ? */ + ems_seg = emm_page_frame_segment(); + if (ems_seg == 0) + return 0; + /* How much can we get */ + num = emm_free_pages(); + if (num == 0) + return 0; + handle = emm_allocate(num); + do_ems_init(0x01, num / 4); + return 1; +} + +#endif + +#endif diff --git a/Kernel/platform-ibmpc/emm_bios.S b/Kernel/platform-ibmpc/emm_bios.S new file mode 100644 index 00000000..132e3909 --- /dev/null +++ b/Kernel/platform-ibmpc/emm_bios.S @@ -0,0 +1,84 @@ +/* + * EMM bios crap for emulator stuff + */ + + + .arch i8086,jumps + .code16 + .att_syntax prefix + + .global emm_detect + .global emm_page_frame_segment + .global emm_free_pages + .global emm_allocate + .global emm_map_memory + + .data +emm_string: + .byte "EMMXXXX0" + .even + + .text +emm_detect: + pushw %es + pushw %di + pushw %si + xorw %ax,%ax + movw %ax,%es ; set segment to 0 + movw 0x19E:%ax ; read the vector + movw ax,%es ; segment is now the driver segment + + movw $10,%di + movw $emm_string,%si + movw $4,%cx + repe cmpsw + + cmpw $0,%cx + je emm_ok + movw %cx,%ax +emm_det_done: + popw %si + popw %di + popw %es + ret +emm_ok: + movw $1,%cx + jmp emm_det_done + +emm_page_frame_segment: + movb $0x41,%ah + int $0x67 + movw %bx,%ax + ret + +emm_free_pages: + movb %0x42,%ah + int $0x67 + movw %bx,%ax + ret + +emm_allocate: + pushw %bp + movw %sp,%bp + movw 4(%bp),%bx + movb $0x43,%ah + int $0x67 + cmpw $0,%ax + jne failed_alloc + movw %dx,%ax +failed_alloc: + popw %bp + ret + +emm_map_memory: + pushw %bp + movw %sp,%bp + movw 4(%bp),%al ; handle id + movw 6(%bp),%dx ; handle + movw 8(%bp),%bx ; page + + movb $0x43,%ah + int $0x67 + xorb %al,%al + popw %bp + ret diff --git a/Kernel/platform-ibmpc/ibmpc.S b/Kernel/platform-ibmpc/ibmpc.S index 8d231e3c..56aa9532 100644 --- a/Kernel/platform-ibmpc/ibmpc.S +++ b/Kernel/platform-ibmpc/ibmpc.S @@ -63,59 +63,62 @@ int2: jmp do_irq int3: pushw %ax - movb $2,%al + movb $3,%al jmp do_irq int4: pushw %ax - movb $2,%al + movb $4,%al jmp do_irq int5: pushw %ax - movb $2,%al + movb $5,%al jmp do_irq int6: pushw %ax - movb $2,%al + movb $6,%al jmp do_irq int7: pushw %ax - movb $2,%al + movb $7,%al jmp do_irq int8: pushw %ax - movb $2,%al + movb $8,%al jmp do_irq int9: pushw %ax - movb $2,%al + movb $9,%al jmp do_irq int10: pushw %ax - movb $2,%al + movb $10,%al jmp do_irq int11: pushw %ax - movb $2,%al + movb $11,%al jmp do_irq int12: pushw %ax - movb $2,%al + movb $12,%al jmp do_irq int13: pushw %ax - movb $2,%al + movb $13,%al jmp do_irq int14: pushw %ax - movb $2,%al + movb $14,%al jmp do_irq int15: pushw %ax - movb $2,%al + movb $15,%al jmp do_irq int0: pushw %ax xorb %al,%al + + /* FIXME: we need a way to cleanly hook the schedule/signal parts + of this into an int handler for the BIOS timer callback */ do_irq: /* Save base registers on current stack */ pushw %ds @@ -145,8 +148,24 @@ do_irq: movb $1,inint movb $1,udata + U_DATA__U_ININTERRUPT pushw %ax - call platform_interrupt - popw %bx + call do_platform_interrupt + popw %ax + + /* This little chunk is machine specific so we'll need to work out + how to move most of this to lowlevel-8086 and keep the 8259A bits + behind */ + cmpb $16,%al + jb internal /* CPU generated */ + cmpb $8,%al + movb $0x20,%al + jb xt_int + outb %al,$0xA0 + /* The 8259 is slower than the 8086 so we need to wait */ + jmp delay +delay: jmp xt_int +xt_int: + outb %al,$0x20 +internal: movb $0,inint movb $0,udata + U_DATA__U_ININTERRUPT @@ -402,8 +421,4 @@ biosdata_read: kernel_ds: .word 0 - .data -equipment_word: - .word 0 - diff --git a/Kernel/platform-ibmpc/ibmpc.h b/Kernel/platform-ibmpc/ibmpc.h new file mode 100644 index 00000000..7f09c8ff --- /dev/null +++ b/Kernel/platform-ibmpc/ibmpc.h @@ -0,0 +1,19 @@ +typedef void (*irqvector_t)(int8_t); + +extern uint8_t inb(uint16_t); +extern void outb(uint8_t, uint16_t); + +extern uint8_t pc_at; + +static inline uint8_t inb_p(uint16_t port) +{ + uint8_t value = inb(port); + inb(0x80); +} + +static inline void outb_p(uint8_t value, uint16_t port) +{ + outb(value, port); + inb(0x80); +} + diff --git a/Kernel/platform-ibmpc/main.c b/Kernel/platform-ibmpc/main.c index 633168e3..41eb3d7b 100644 --- a/Kernel/platform-ibmpc/main.c +++ b/Kernel/platform-ibmpc/main.c @@ -3,25 +3,55 @@ #include #include #include -#include +#include uint8_t kernel_flag = 1; +uint8_t pc_at = 1; +uint16_t equipment_word; +uint8_t cpu_type; +uint8_t bus_width; +uint8_t fpu_type; void platform_idle(void) { - /* FIXME: disable IRQ, run tty interrupt, re-enable ? */ + /* Do a halt or what ? */ } void do_beep(void) { } +void pagemap_init(void) +{ + /* FIXME: use proper kernel _discard to correct end allowing for EBDA */ + pagemap_init_range(64, 640); +} + /* - * MMU initialize + * MMU initialize. We use this to set everything up. Should move into + * discard */ void map_init(void) { + static char *cpu[]={"808","80C8","NEC V","8018","8028"}; + static char *fpu[]={"87","287+"}; + cpu_detect(); + kputs("Processor: "); + kputs(cpu[cpu_type]); + if (cpu_type == 2) + kputs(bus_width == 8 ? "20" : "30"); + else + kputs(bus_width == 8 ? "8" : "6"); + if (cpu_type == 4) + kputs("+"); + if (fpu_type) { + kputs(" / 80"); + kputs(fpu[fpu_type]); + } + kputs("\n"); + /* Should we do bogomips 8) */ +// init_irq(); } uaddr_t ramtop; @@ -40,5 +70,3 @@ void memzero(void *p, usize_t len) { memset(p, 0, len); } - -uint16_t irqstack[128]; /* Used for swapping only */ diff --git a/Kernel/platform-ibmpc/xmsrd.c b/Kernel/platform-ibmpc/xmsrd.c new file mode 100644 index 00000000..81c808e4 --- /dev/null +++ b/Kernel/platform-ibmpc/xmsrd.c @@ -0,0 +1,107 @@ +/* + * XMS Ramdisk Driver. + * + * This allows us to use high memory on a 286 system as a RAM disk + * + * FIXME: make this use the block stuff and partitions + */ + +#include +#include +#include +#include +#include + +static uint16_t xms_size; + +static int xm_transfer(bool is_read, uint8_t rawflag) +{ + uint16_t page = kernel_ds; + uint32_t dptr; + + if (rawflag == 1) { + if (d_blkoff(9)) + return -1; + page = get_segment(udata.u_page); +#ifdef SWAPDEV + } else if (rawflag == 2) + page = swappage; +#endif + + /* dptr is effectively a far pointer */ + dptr = (uint32_t)udata.u_dptr; + dptr += page << 4; + + /* Should we add a simple length check to the core disk I/O ? */ + if (udata.u_block + udata.u_nblock > xms_size || + udata.u_block + udata.u_nblock < udata.u_block) + { + udata.u_error = EINVAL; + return -1; + } + if (is_read) { + os = gdt + 0x20; + disk = gdt + 0x30; + else { + os = gdt + 0x30; + disk = gdt + 0x20; + } + + /* + FIXME: we should loop this with a limited transfer size because + 1. We can't do > 32k words at a time (not clear if can do exactly 32k) + 2. It runs with IRQs off + */ + /* Turn the disk block into a 24 bit address remembering we start at 1MB */ + disk[2] = 0x00; /* A0-A7 */ + disk[3] = udata.u_block << 1; /* A8-A15 */ + disk[4] = (udata.u_block >> 7) + 16; /* A16-A23 */ + /* Turn the native far address into a 24bit address */ + os[2] = dptr; + os[3] = dptr >> 8; + os[4] = dptr >> 16; + /* The other bytes are pre-filled */ + /* Length is in words */ + err = xmm_xfer(udata.u_nblock << 8); + if (err) { + /* 0x01 - parity, 0x02 exception, 0x03 gate a20 failed */ + kprintf("xm0: error %d\n", err); + udata.u_error = EIO; + return -1; + } + return udata.u_nblock << 9; +} + +int xm_read(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + return xm_transfer(true, rawflag); +} + +int xm_write(uint8_t minor, uint8_t rawflag, uint8_t flag) +{ + return xm_transfer(false, rawflag); +} + +int xm_open(uint8_t minor, uint16_t flag) +{ + if (minor || !xms_size) + return ENODEV; + return 0; +} + +void xm_init(void) +{ + if (xm_lockout) + return; + xms_size = xmm_probe(); + if (xms_size) + kprintf("xm0: %dKb extended memory RAMdisc\n", xms_size); + xms_size *= 2; /* Blocks */ +} + +/* Certain 286 systems can make memory visible as both extended and expanded. + On those we need to lock out the less useful extended form */ +void xm_disable(void) +{ + xm_lockout = 1; +} diff --git a/Kernel/platform-ibmpc/xmsrd_asm.S b/Kernel/platform-ibmpc/xmsrd_asm.S new file mode 100644 index 00000000..f450a037 --- /dev/null +++ b/Kernel/platform-ibmpc/xmsrd_asm.S @@ -0,0 +1,50 @@ + .arch i8086,jumps + .code16 + .att_syntax prefix + + .text + + .global xmm_probe + .global xmm_transfer + +/* + * BIOS probe for extended memory in Kb. This doesn't work on modern + * systems but it does on 286 era boxes just fine. + */ +xmm_probe: + scf + movb %0x88,%ah + int $0x15 + jnc xmm_ok + xorw %ax,%ax +xmm_ok: + ret + +/* + * Perform an extended memory transfer + */ +xmm_transfer: + pushw %bp + movw %sp,%bp + movw 4(%bp),%cx ; count + pushw %si + movw $xmm_gdt,%si + movb $0x87,%ah + scf + int $0x15 + jc no_xmm_xfer + popw %si + popw %bp + ret + + .data + +xmm_gdt: + .byte 0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0 + .byte 0xFFFF,0,0,0,0,0x93,0,0 + .byte 0xFFFF,0,0,0,0,0x93,0,0 + .byte 0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0 diff --git a/Kernel/platform-ibmpc/xt_key.c b/Kernel/platform-ibmpc/xt_key.c new file mode 100644 index 00000000..102819ce --- /dev/null +++ b/Kernel/platform-ibmpc/xt_key.c @@ -0,0 +1,274 @@ +/* + * The BIOS keyboard interface is pretty much unusable for a Unixlike + * OS, so sadly we have to take over the keyboard ourselves. Hopefully + * the early non-clones didn't do anything too weird in this space. + * + * Based on the ELKS driver by Saku Airila and Sefaan Van Dooren + * + * We need to look at the following + * - Some basic AT support + * - Nicer mapping of key codes onto ELKS standard FN etc codes + * - Loadable keymaps + * + * The console layer expects us to call console_queue(uint8_t x) + * with FUZIX codes for each key. If we see Alt-Fn we call + * console_switch(n) to indicate a possible console switch. Other + * than that we try to be blissfully ignorant of the tty layer as it's + * simply not our problem and we are not the only console driver. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_KBD_XT + +#define KBD_IO 0x60 +#define KBD_CTL 0x61 + +#define ESC 27 +#define KB_SIZE 64 + +static uint8_t xtkb_scan[] = { + 0, 033, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', '\b', '\t', +/*10*/ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', KEY_ENTER, /* lctrl */0x82, 'a', 's', +/*20*/ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '#', /* lshift */0x80, '\\', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', /* rshift */0x81, '*', + /* lalt */0x83, ' ', /* capslock */0x84, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, + KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, /* numlock */0x85, /* scrollock */0x90, KEY_HOME, + KEY_UP, KEY_PGUP, KEY_MINUS, KEY_LEFT, /* kb-5 ?? */0265, KEY_RIGHT, KEY_PLUS, KEY_END, + KEY_DOWN, KEY_PGDOWN, KEY_INSERT, KEY_DEL +}; + +static uint8_t xtkb_scan_shifted[] = { + 0, 033, '!', '"', '£', '$', '%', '^', + '&', '*', '(', ')', '_', '+', '\b', '\t', + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '{', '}', KEY_ENTER, /* lctrl */0x82, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', + '"', '~', /* lshift */0x80, '|', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', /* rshift */0x81, '*', + /* FIXME: we don't have shift- and ctrl Fx maps but need them */ + /* lalt*/0x83, ' ', /* capslock */0x84, KEY_F1, KEY_F1, KEY_F3, KEY_F4, KEY_F5, + KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, /* numlock */0x84, /* scrollock */0x93, '7', + '8', '9', /* keypad - */'-', '4', '5', '6', '+', '1', + '2', '3', '0', KEY_DEL +}; + +static uint8_t xtkb_scan_ctrl_alt[] = { + 0, 033, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', KEY_BS, KEY_TAB, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', KEY_ENTER, /* lctrl */0x82, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', /* lshift */0x80, '`', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', /* rshift */0x81, '*', + /* lalt */0x83, ' ', /* capslock */0x84, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, + KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, /* numlock */0x85, /* scrollock */0x90, KEY_HOME, + KEY_UP, KEY_PGUP, KEY_MINUS, KEY_LEFT, /* kb-5 ?? */0265, KEY_RIGHT, KEY_PLUS, KEY_END, + KEY_DOWN, KEY_PGDOWN, KEY_INSERT, KEY_DEL +}; + +static uint8_t xtkb_scan_caps[] = { + 0, 033, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', KEY_BS, KEY_TAB, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '[', ']', KEY_ENTER, 0x82, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', + '\'', '~', 0x80, '|', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ',', '.', '/', 0x81, '*', + /* lalt*/0x83, ' ', /* capslock */0x84, KEY_F1, KEY_F1, KEY_F3, KEY_F4, KEY_F5, + KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, /* numlock */0x84, /* scrollock */0x93, '7', + '8', '9', /* keypad - */'-', '4', '5', '6', '+', '1', + '2', '3', '0', KEY_DEL +}; + +#define LSHIFT 1 +#define RSHIFT 2 +#define CTRL 4 +#define ALT 8 +#define CAPS 16 +#define NUM 32 +#define ALT_GR 64 + +#define ANYSHIFT (LSHIFT | RSHIFT) + +#define SSC 0xC0 + +static uint8_t tb_state[] = { + 0x80, CTRL, SSC, SSC, /*1C->1F */ + SSC, SSC, SSC, SSC, SSC, SSC, SSC, SSC, /*20->27 */ + SSC, SSC, LSHIFT, SSC, SSC, SSC, SSC, SSC, /*28->2F */ + SSC, SSC, SSC, SSC, SSC, SSC, RSHIFT, SSC, /*30->37 */ + SSC, SSC, CAPS, /*38->3A */ + 'a', 'b', 'c', 'd', 'e', /*3B->3F, Function Keys */ + 'f', 'g', 'h', 'i', 'j', /*40->44, Function Keys */ + NUM, SSC, SSC, /*45->47 */ + 0xB7, SSC, SSC, 0xBA, SSC, 0xB9, SSC, SSC, /*48->4F */ + 0xB8, SSC, SSC, SSC, SSC, SSC, ALT, SSC, /*50->57 */ +}; + +static uint8_t state_code[] = { + 0, /* All status are 0 */ + 1, /* SHIFT */ + 0, /* CTRL */ + 1, /* SHIFT CTRL */ + 0, /* ALT */ + 1, /* SHIFT ALT */ + 3, /* CTRL ALT */ + 1, /* SHIFT CTRL ALT */ + 2, /* CAPS */ + 0, /* CAPS SHIFT */ + 2, /* CAPS CTRL */ + 0, /* CAPS SHIFT CTRL */ + 2, /* CAPS ALT */ + 0, /* CAPS SHIFT ALT */ + 2, /* CAPS CTRL ALT */ + 3, /* CAPS SHIFT CTRL ALT */ +}; + +static uint8_t *scan_tabs[] = { + xtkb_scan, + xtkb_scan_shifted, + xtkb_scan_caps, + xtkb_scan_ctrl_alt, +}; + +uint8_t keymap[16]; /* 128 codes, we don't try and deal with + extended keys in this for now */ + +/* + * XT style keyboard I/O is almost civilised compared + * with the monstrosity AT keyboards became. + */ + +void xt_keyboard_irq(int irq) +{ + static uint8_t e0_prefix; + static unsigned int kbd_state = 0; + uint8_t E0; + uint8_t key_up; + uint8_t code; + uint8_t mode; + uint8_t E0 = 0; + uint8_t keyp; + + code = inb_p((void *) KBD_IO); + mode = inb_p((void *) KBD_CTL); + + /* Necessary for the XT. */ + outb_p((uint8_t) (mode | 0x80), (void *) KBD_CTL); + outb_p((uint8_t) mode, (void *) KBD_CTL); + + /* Need to put back a raw scancode mode eventually */ + + /* Remember this has been received */ + if (code == 0xE0) { + e0_prefix = 1; + return; + } + e0 = e0_prefix; + e0_prefix = 0; + + /* On an error we cross our fingers */ + if (code == 0 || code == 0xFF) + return; + + key_up = code & 0x80; + code &= 0x7F; + + /* Track all the key up and down bits for the FUZIX raw keymap + interface. Probably nobody on x86 will use it but it's cheap */ + if (key_up == 0) + key_map[code >> 3] |= 1 << (code & 7); + else + key_map[code >> 3] &= ~(1 << (code & 7)); + + /* Work out what table to use */ + /* FIXME: check for over the end of the table ?? */ + mode = (code >= 0x1C) ? tb_state[code - 0x1C] : SSC; + + /* Process status keys */ + if (!(mode & 0xC0)) { +#if defined(CONFIG_KEYMAP_DE) || defined(CONFIG_KEYMAP_SE) + if ((mode == ALT) && (E0 != 0)) + mode = ALT_GR; +#endif + if (key_up) + kbd_state ~= ~mode; + else + kbd_state |= mode; + return; + } + if (key_up) + return; + + switch (mode & 0xC0) { + case 0x40: /* F1 .. F10 */ + /* Handle Function keys */ + code -= 0x38; + if (kbd_mode & ALT) { + console_switch(code); + return; + } + console_queue(KEY_F1 + code); + return; + + /* Handle extended scancodes */ + case 0x80: + /* We have no idea how to map these so we don't */ + if (E0) { /* Is extended scancode? */ + mode &= 0x3F; + if (mode) + console_queue(ESC); + console_queue(mode + 0x0A); + return; + } + } + + /* Handle CTRL-ALT-DEL */ +/* if (code == 0x53 && (kbd_mode & CTRL) && (kbd_mode & ALT)) + ctrl_alt_del(); */ + + /* + * Pick the right keymap + */ + + mode = ((kbd_mode & ~(NUM | ALT_GR)) >> 1) | (kbd_mode & 0x01); + mode = state_code[mode]; + + if (!mode && (kbd_mode & ALT_GR)) + mode = 3; + keyp = *(scan_tabs[mode] + code); + + if ((kbd_mode & CTRL) && code < 14 && !(kbd_mode & ALT)) + keyp = xtkb_scan_shifted[code]; + if (code < 70 && (kbd_mode & NUM)) + keyp = xtkb_scan_shifted[code]; + /* + * Apply special modifiers. Needs looking at more + */ + if ((kbd_mode & ALT && !(kbd_mode & CTRL)) /* Changed to support CTRL-ALT */ + keyp |= 0x80; /* META-.. */ + if (!keyp) /* non meta-@ is 64 */ + keyp = '@'; + if (kbd_mode & CTRL && !(kbd_mode & ALT)) /* Changed to support CTRL-ALT */ + keyp &= 0x1F; /* CTRL-.. */ + if (keyp == '\r') + keyp = '\n'; + console_queue(keyp); +} + +void xt_keyboard_init(void) +{ + request_irq(1, xt_keyboard_irq); +} + +#endif -- 2.34.1