ibmpc: Add work in progress
authorAlan Cox <alan@linux.intel.com>
Thu, 19 Oct 2017 19:45:02 +0000 (20:45 +0100)
committerAlan Cox <alan@linux.intel.com>
Thu, 19 Oct 2017 19:45:02 +0000 (20:45 +0100)
20 files changed:
Kernel/platform-ibmpc/8259a.c [new file with mode: 0644]
Kernel/platform-ibmpc/Makefile
Kernel/platform-ibmpc/bioscon.c [new file with mode: 0644]
Kernel/platform-ibmpc/biosdisk.c
Kernel/platform-ibmpc/bioskey.S [new file with mode: 0644]
Kernel/platform-ibmpc/biosvid.S [new file with mode: 0644]
Kernel/platform-ibmpc/bootstrap.S [new file with mode: 0644]
Kernel/platform-ibmpc/config.h
Kernel/platform-ibmpc/confspace.h [new file with mode: 0644]
Kernel/platform-ibmpc/devices.c
Kernel/platform-ibmpc/devtty.c
Kernel/platform-ibmpc/devtty.h
Kernel/platform-ibmpc/emm.c [new file with mode: 0644]
Kernel/platform-ibmpc/emm_bios.S [new file with mode: 0644]
Kernel/platform-ibmpc/ibmpc.S
Kernel/platform-ibmpc/ibmpc.h [new file with mode: 0644]
Kernel/platform-ibmpc/main.c
Kernel/platform-ibmpc/xmsrd.c [new file with mode: 0644]
Kernel/platform-ibmpc/xmsrd_asm.S [new file with mode: 0644]
Kernel/platform-ibmpc/xt_key.c [new file with mode: 0644]

diff --git a/Kernel/platform-ibmpc/8259a.c b/Kernel/platform-ibmpc/8259a.c
new file mode 100644 (file)
index 0000000..53ae1be
--- /dev/null
@@ -0,0 +1,76 @@
+#include <kernel.h>
+#include <timer.h>
+#include <kdata.h>
+#include <ibmpc.h>
+
+/*
+ *     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);
+}
index dd57aed..1005662 100644 (file)
@@ -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 (file)
index 0000000..90f7c83
--- /dev/null
@@ -0,0 +1,98 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devtty.h>
+#include <device.h>
+#include <vt.h>
+#include <tty.h>
+#include <graphics.h>
+
+/*
+ *     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);
+}
index 7736115..7fad06f 100644 (file)
@@ -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 <kernel.h>
@@ -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 (file)
index 0000000..a2f2932
--- /dev/null
@@ -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 (file)
index 0000000..6695baf
--- /dev/null
@@ -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 (file)
index 0000000..e3d10fc
--- /dev/null
@@ -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
index 10e8889..61b4198 100644 (file)
@@ -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 */
 #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 (file)
index 0000000..b7c670b
--- /dev/null
@@ -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];
+
+};
index c8f55ca..f51acaf 100644 (file)
@@ -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); */
 }
index 3954fa8..8b72096 100644 (file)
@@ -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 <kernel.h>
 #include <kdata.h>
 #include <printf.h>
@@ -5,9 +11,7 @@
 #include <devtty.h>
 #include <device.h>
 #include <tty.h>
-
-volatile uint8_t *uart_data = (volatile uint8_t *)0xF03000;    /* UART data */
-volatile uint8_t *uart_status = (volatile uint8_t *)0xF03010;  /* UART status */
+#include <vt.h>
 
 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();
-}
index 14c28c3..c387e90 100644 (file)
@@ -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 (file)
index 0000000..de4aa87
--- /dev/null
@@ -0,0 +1,233 @@
+#include <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+
+#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 (file)
index 0000000..132e390
--- /dev/null
@@ -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
index 8d231e3..56aa953 100644 (file)
@@ -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 (file)
index 0000000..7f09c8f
--- /dev/null
@@ -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);
+}
+
index 633168e..41eb3d7 100644 (file)
@@ -3,25 +3,55 @@
 #include <kdata.h>
 #include <printf.h>
 #include <devtty.h>
-#include <buddy.h>
+#include <ibmpc.h>
 
 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 (file)
index 0000000..81c808e
--- /dev/null
@@ -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 <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <devhd.h>
+#include <bios.h>
+
+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 (file)
index 0000000..f450a03
--- /dev/null
@@ -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 (file)
index 0000000..102819c
--- /dev/null
@@ -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 <kernel.h>
+#include <kdata.h>
+#include <printf.h>
+#include <stdbool.h>
+#include <tty.h>
+#include <vt.h>
+#include <devtty.h>
+
+#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