aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/biosvar.h184
-rw-r--r--src/boot.c118
-rw-r--r--src/cbt.c8
-rw-r--r--src/clock.c54
-rw-r--r--src/cmos.h51
-rw-r--r--src/config.h8
-rw-r--r--src/disk.c67
-rw-r--r--src/disk.h33
-rw-r--r--src/farptr.h57
-rw-r--r--src/floppy.c757
-rw-r--r--src/font.c139
-rw-r--r--src/ioport.h56
-rw-r--r--src/kbd.c35
-rw-r--r--src/output.c161
-rw-r--r--src/post.c312
-rw-r--r--src/rombios32.lds.S31
-rw-r--r--src/romlayout.S304
-rw-r--r--src/serial.c23
-rw-r--r--src/system.c529
-rw-r--r--src/types.h21
-rw-r--r--src/util.h55
21 files changed, 3003 insertions, 0 deletions
diff --git a/src/biosvar.h b/src/biosvar.h
new file mode 100644
index 0000000..8b15f5b
--- /dev/null
+++ b/src/biosvar.h
@@ -0,0 +1,184 @@
+// Variable layouts of bios.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "types.h" // u8
+#include "farptr.h" // SET_SEG
+
+
+/****************************************************************
+ * Bios Data Area (BDA)
+ ****************************************************************/
+
+struct ivec {
+ u16 offset;
+ u16 seg;
+};
+
+struct bios_data_area_s {
+ // 00:00
+ struct ivec ivecs[256];
+ // 30:00
+// u8 stack[256];
+ // 40:00
+ u16 port_com1, port_com2, port_com3, port_com4;
+ u16 port_lpt1, port_lpt2, port_lpt3;
+ u16 ebda_seg;
+ // 40:10
+ u16 equipment_list_flags;
+ u8 pad1;
+ u16 mem_size_kb;
+ u8 pad2;
+ u8 ps2_ctrl_flag;
+ u16 kbd_flag;
+ u8 alt_keypad;
+ u16 kbd_buf_head;
+ u16 kbd_buf_tail;
+ // 40:1e
+ u8 kbd_buf[32];
+ u8 floppy_recalibration_status;
+ u8 floppy_motor_status;
+ // 40:40
+ u8 floppy_motor_counter;
+ u8 floppy_last_status;
+ u8 floppy_return_status[7];
+ u8 other1[0x7];
+ // 40:50
+ u8 other2[0x1c];
+ // 40:6c
+ u32 timer_counter;
+ // 40:70
+ u8 timer_rollover;
+ u8 other4[0x0f];
+ // 40:80
+ u16 kbd_buf_start_offset;
+ u16 kbd_buf_end_offset;
+ u8 other5[7];
+ u8 floppy_last_data_rate;
+ u8 other6[3];
+ u8 floppy_harddisk_info;
+ // 40:90
+ u8 floppy_media_state[4];
+ u8 floppy_track0;
+ u8 floppy_track1;
+ u8 kbd_mode;
+ u8 kbd_led;
+ u32 ptr_user_wait_complete_flag;
+ u32 user_wait_timeout;
+ // 40:A0
+ u8 rtc_wait_flag;
+} __attribute__((packed));
+
+// BDA floppy_recalibration_status bitdefs
+#define FRS_TIMEOUT (1<<7)
+
+// BDA rtc_wait_flag bitdefs
+#define RWS_WAIT_PENDING (1<<0)
+#define RWS_WAIT_ELAPSED (1<<7)
+
+// BDA floppy_media_state bitdefs
+#define FMS_DRIVE_STATE_MASK (0x07)
+#define FMS_MEDIA_DRIVE_ESTABLISHED (1<<4)
+#define FMS_DOUBLE_STEPPING (1<<5)
+#define FMS_DATA_RATE_MASK (0xc0)
+
+// Accessor functions
+#define GET_BDA(var) ({ \
+ SET_SEG(ES, 0x0000); \
+ GET_VAR(ES, ((struct bios_data_area_s *)0)->var); })
+#define SET_BDA(var, val) do { \
+ SET_SEG(ES, 0x0000); \
+ SET_VAR(ES, ((struct bios_data_area_s *)0)->var, val); \
+ } while (0)
+#define CLEARBITS_BDA(var, val) do { \
+ typeof(((struct bios_data_area_s *)0)->var) __val = GET_BDA(var); \
+ SET_BDA(var, (__val & ~(val))); \
+ } while (0)
+#define SETBITS_BDA(var, val) do { \
+ typeof(((struct bios_data_area_s *)0)->var) __val = GET_BDA(var); \
+ SET_BDA(var, (__val | (val))); \
+ } while (0)
+
+
+/****************************************************************
+ * Extended Bios Data Area (EBDA)
+ ****************************************************************/
+
+struct extended_bios_data_area_s {
+ u8 size;
+ u8 other1[0x3c];
+
+ // FDPT - Can be splitted in data members if needed
+ u8 fdpt0[0x10];
+ u8 fdpt1[0x10];
+
+ u8 other2[0xC4];
+
+ // ATA Driver data
+ //ata_t ata;
+
+#if BX_ELTORITO_BOOT
+ // El Torito Emulation data
+ cdemu_t cdemu;
+#endif // BX_ELTORITO_BOOT
+};
+
+
+/****************************************************************
+ * Extended Bios Data Area (EBDA)
+ ****************************************************************/
+
+#define UREG(ER, R, RH, RL) union { u32 ER; struct { u16 R; u16 R ## _hi; }; struct { u8 RL; u8 RH; u8 R ## _hilo; u8 R ## _hihi; }; }
+
+struct bregs {
+ u16 ds;
+ u16 es;
+ UREG(edi, di, di_hi, di_lo);
+ UREG(esi, si, si_hi, si_lo);
+ UREG(ebp, bp, bp_hi, bp_lo);
+ UREG(esp, sp, sp_hi, sp_lo);
+ UREG(ebx, bx, bh, bl);
+ UREG(edx, dx, dh, dl);
+ UREG(ecx, cx, ch, cl);
+ UREG(eax, ax, ah, al);
+ u16 ip;
+ u16 cs;
+ u16 flags;
+} __attribute__((packed));
+
+// bregs flags bitdefs
+#define F_CF (1<<9)
+
+static inline void
+set_cf(struct bregs *regs, int cond)
+{
+ if (cond)
+ regs->flags |= F_CF;
+ else
+ regs->flags &= ~F_CF;
+}
+
+
+/****************************************************************
+ * Bios Config Table
+ ****************************************************************/
+
+struct bios_config_table_s {
+ // XXX
+ u8 x;
+};
+
+extern struct bios_config_table_s BIOS_CONFIG_TABLE;
+
+
+/****************************************************************
+ * Memory layout info
+ ****************************************************************/
+
+#define SEG_BIOS 0xf000
+
+#define EBDA_SEG 0x9FC0
+#define EBDA_SIZE 1 // In KiB
+#define BASE_MEM_IN_K (640 - EBDA_SIZE)
diff --git a/src/boot.c b/src/boot.c
new file mode 100644
index 0000000..828be14
--- /dev/null
+++ b/src/boot.c
@@ -0,0 +1,118 @@
+// 16bit code to load disk image and start system boot.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "types.h" // VISIBLE
+#include "util.h" // irq_enable
+#include "biosvar.h" // struct bregs
+#include "farptr.h" // SET_SEG
+
+static inline void
+__call_irq(u8 nr)
+{
+ asm volatile("int %0" : : "N" (nr));
+}
+
+static inline u32
+call_irq(u8 nr, struct bregs *callregs)
+{
+ u32 flags;
+ asm volatile(
+ // Save current registers
+ "pushal\n"
+ // Pull in calling registers.
+ "movl 0x04(%%eax), %%edi\n"
+ "movl 0x08(%%eax), %%esi\n"
+ "movl 0x0c(%%eax), %%ebp\n"
+ "movl 0x14(%%eax), %%ebx\n"
+ "movl 0x18(%%eax), %%edx\n"
+ "movl 0x1c(%%eax), %%ecx\n"
+ "movl 0x20(%%eax), %%eax\n"
+ // Invoke interrupt
+ "int %1\n"
+ // Restore registers
+ "popal\n"
+ // Exract flags
+ "pushfw\n"
+ "popl %%eax\n"
+ : "=a" (flags): "N" (nr), "a" (callregs), "m" (*callregs));
+ return flags;
+}
+
+static void
+print_boot_failure()
+{
+ bprintf(0, "Boot failed\n");
+}
+
+static void
+try_boot()
+{
+ // XXX - assume floppy
+ u16 bootseg = 0x07c0;
+ u8 bootdrv = 0;
+
+ // Read sector
+ struct bregs cr;
+ memset(&cr, 0, sizeof(cr));
+ cr.dl = bootdrv;
+ SET_SEG(ES, bootseg);
+ cr.bx = 0;
+ cr.ah = 2;
+ cr.al = 1;
+ cr.ch = 0;
+ cr.cl = 1;
+ cr.dh = 0;
+ u32 status = call_irq(0x13, &cr);
+
+ if (status & F_CF) {
+ print_boot_failure();
+ return;
+ }
+
+ u16 bootip = (bootseg & 0x0fff) << 4;
+ bootseg &= 0xf000;
+
+ u32 segoff = (bootseg << 16) | bootip;
+ asm volatile (
+ "pushf\n"
+ "pushl %0\n"
+ "movb %b1, %%dl\n"
+ // Set the magic number in ax and the boot drive in dl.
+ "movw $0xaa55, %%ax\n"
+ // Zero some of the other registers.
+ "xorw %%bx, %%bx\n"
+ "movw %%bx, %%ds\n"
+ "movw %%bx, %%es\n"
+ "movw %%bx, %%bp\n"
+ // Go!
+ "iretw\n"
+ : : "r" (segoff), "ri" (bootdrv));
+}
+
+// Boot Failure recovery: try the next device.
+void VISIBLE
+handle_18(struct bregs *regs)
+{
+ debug_enter(regs);
+ try_boot();
+}
+
+// INT 19h Boot Load Service Entry Point
+void VISIBLE
+handle_19(struct bregs *regs)
+{
+ debug_enter(regs);
+ try_boot();
+}
+
+// Callback from 32bit entry - start boot process
+void VISIBLE
+begin_boot()
+{
+ irq_enable();
+ __call_irq(0x19);
+}
diff --git a/src/cbt.c b/src/cbt.c
new file mode 100644
index 0000000..015f16f
--- /dev/null
+++ b/src/cbt.c
@@ -0,0 +1,8 @@
+#include "biosvar.h" // CONFIG_BIOS_TABLE
+
+// bios variables
+
+struct bios_config_table_s BIOS_CONFIG_TABLE = {
+ // XXX
+ 18,
+};
diff --git a/src/clock.c b/src/clock.c
new file mode 100644
index 0000000..5ca2b5c
--- /dev/null
+++ b/src/clock.c
@@ -0,0 +1,54 @@
+// 16bit code to handle system clocks.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "biosvar.h" // struct bregs
+#include "util.h" // debug_enter
+#include "disk.h" // floppy_tick
+
+// INT 1Ah Time-of-day Service Entry Point
+void VISIBLE
+handle_1a(struct bregs *regs)
+{
+ debug_enter(regs);
+ set_cf(regs, 1);
+}
+
+// User Timer Tick
+void VISIBLE
+handle_1c(struct bregs *regs)
+{
+ debug_enter(regs);
+}
+
+// INT 08h System Timer ISR Entry Point
+void VISIBLE
+handle_08(struct bregs *regs)
+{
+// debug_enter(regs);
+
+ floppy_tick();
+
+ u32 counter = GET_BDA(timer_counter);
+ counter++;
+ // compare to one days worth of timer ticks at 18.2 hz
+ if (counter >= 0x001800B0) {
+ // there has been a midnight rollover at this point
+ counter = 0;
+ SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1);
+ }
+
+ SET_BDA(timer_counter, counter);
+ // XXX - int #0x1c
+ eoi_master_pic();
+}
+
+// int70h: IRQ8 - CMOS RTC
+void VISIBLE
+handle_70(struct bregs *regs)
+{
+ debug_enter(regs);
+}
diff --git a/src/cmos.h b/src/cmos.h
new file mode 100644
index 0000000..33cde16
--- /dev/null
+++ b/src/cmos.h
@@ -0,0 +1,51 @@
+// Definitions for X86 CMOS non-volatile memory access.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+#ifndef __CMOS_H
+#define __CMOS_H
+
+#include "ioport.h" // inb, outb
+
+#define CMOS_RTC_SECONDS 0x00
+#define CMOS_RTC_SECONDS_ALARM 0x01
+#define CMOS_RTC_MINUTES 0x02
+#define CMOS_RTC_MINUTES_ALARM 0x03
+#define CMOS_RTC_HOURS 0x04
+#define CMOS_RTC_HOURS_ALARM 0x05
+#define CMOS_STATUS_B 0x0b
+#define CMOS_RESET_CODE 0x0f
+#define CMOS_FLOPPY_DRIVE_TYPE 0x10
+#define CMOS_EQUIPMENT_INFO 0x14
+#define CMOS_EXTMEM_LOW 0x30
+#define CMOS_EXTMEM_HIGH 0x31
+#define CMOS_EXTMEM2_LOW 0x34
+#define CMOS_EXTMEM2_HIGH 0x35
+
+// CMOS_STATUS_B bitdefs
+#define CSB_EN_ALARM_IRQ (1<<5)
+
+// CMOS_FLOPPY_DRIVE_TYPE bitdefs
+#define CFD_NO_DRIVE 0
+#define CFD_360KB 1
+#define CFD_12MB 2
+#define CFD_720KB 3
+#define CFD_144MB 4
+#define CFD_288MB 5
+
+static inline u8
+inb_cmos(u8 reg)
+{
+ outb(reg, PORT_CMOS_INDEX);
+ return inb(PORT_CMOS_DATA);
+}
+
+static inline void
+outb_cmos(u8 val, u8 reg)
+{
+ outb(reg, PORT_CMOS_INDEX);
+ outb(val, PORT_CMOS_DATA);
+}
+
+#endif // cmos.h
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 0000000..53996b4
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,8 @@
+// Configuration definitions.
+
+#define CONFIG_FLOPPY_SUPPORT 1
+#define CONFIG_PS2_MOUSE 0
+#define CONFIG_ATA 0
+#define CONFIG_STACK16_SEGMENT 0x00
+#define CONFIG_STACK16_OFFSET 0xfffe
+#define CONFIG_STACK32_OFFSET 0x80000
diff --git a/src/disk.c b/src/disk.c
new file mode 100644
index 0000000..8901b7d
--- /dev/null
+++ b/src/disk.c
@@ -0,0 +1,67 @@
+// 16bit code to access hard drives.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "disk.h" // floppy_13
+#include "biosvar.h" // struct bregs
+#include "util.h" // debug_enter
+
+static void
+disk_13(struct bregs *regs, u8 drive)
+{
+ set_cf(regs, 1);
+}
+
+static void
+handle_legacy_disk(struct bregs *regs, u8 drive)
+{
+ if (drive < 0x80) {
+ floppy_13(regs, drive);
+ return;
+ }
+#if BX_USE_ATADRV
+ if (drive >= 0xE0) {
+ int13_cdrom(regs); // xxx
+ return;
+ }
+#endif
+
+ disk_13(regs, drive);
+}
+
+void VISIBLE
+handle_40(struct bregs *regs)
+{
+ debug_enter(regs);
+ handle_legacy_disk(regs, regs->dl);
+ debug_exit(regs);
+}
+
+// INT 13h Fixed Disk Services Entry Point
+void VISIBLE
+handle_13(struct bregs *regs)
+{
+ debug_enter(regs);
+ u8 drive = regs->dl;
+#if BX_ELTORITO_BOOT
+ if (regs->ah >= 0x4a || regs->ah <= 0x4d) {
+ int13_eltorito(regs);
+ } else if (cdemu_isactive() && cdrom_emulated_drive()) {
+ int13_cdemu(regs);
+ } else
+#endif
+ handle_legacy_disk(regs, drive);
+ debug_exit(regs);
+}
+
+// record completion in BIOS task complete flag
+void VISIBLE
+handle_76(struct bregs *regs)
+{
+ debug_enter(regs);
+ SET_BDA(floppy_harddisk_info, 0xff);
+ eoi_both_pics();
+}
diff --git a/src/disk.h b/src/disk.h
new file mode 100644
index 0000000..d7b3547
--- /dev/null
+++ b/src/disk.h
@@ -0,0 +1,33 @@
+// Definitions for X86 bios disks.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "ioport.h" // outb
+
+#define DISK_RET_SUCCESS 0x00
+#define DISK_RET_EPARAM 0x01
+#define DISK_RET_ECHANGED 0x06
+#define DISK_RET_EBOUNDARY 0x09
+#define DISK_RET_ECONTROLLER 0x20
+#define DISK_RET_ETIMEOUT 0x80
+#define DISK_RET_EMEDIA 0xC0
+
+static inline void
+eoi_master_pic()
+{
+ outb(PIC1_IRQ5, PORT_PIC1);
+}
+
+static inline void
+eoi_both_pics()
+{
+ outb(PIC2_IRQ13, PORT_PIC2);
+ eoi_master_pic();
+}
+
+// floppy.c
+struct bregs;
+void floppy_13(struct bregs *regs, u8 drive);
+void floppy_tick();
diff --git a/src/farptr.h b/src/farptr.h
new file mode 100644
index 0000000..1c3044b
--- /dev/null
+++ b/src/farptr.h
@@ -0,0 +1,57 @@
+// Code to access multiple segments within gcc.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#define READ8_SEG(SEG, var) ({ \
+ u8 __value; \
+ __asm__ __volatile__("movb %%" #SEG ":%1, %b0" \
+ : "=Qi"(__value) : "m"(var)); \
+ __value; })
+#define READ16_SEG(SEG, var) ({ \
+ u16 __value; \
+ __asm__ __volatile__("movw %%" #SEG ":%1, %w0" \
+ : "=ri"(__value) : "m"(var)); \
+ __value; })
+#define READ32_SEG(SEG, var) ({ \
+ u32 __value; \
+ __asm__ __volatile__("movl %%" #SEG ":%1, %0" \
+ : "=ri"(__value) : "m"(var)); \
+ __value; })
+#define WRITE8_SEG(SEG, var, value) \
+ __asm__ __volatile__("movb %b0, %%" #SEG ":%1" \
+ : : "Q"(value), "m"(var))
+#define WRITE16_SEG(SEG, var, value) \
+ __asm__ __volatile__("movw %w0, %%" #SEG ":%1" \
+ : : "r"(value), "m"(var))
+#define WRITE32_SEG(SEG, var, value) \
+ __asm__ __volatile__("movl %0, %%" #SEG ":%1" \
+ : : "r"(value), "m"(var))
+
+#define GET_VAR(seg, var) ({ \
+ typeof(var) __val; \
+ if (__builtin_types_compatible_p(typeof(__val), u8)) \
+ __val = READ8_SEG(seg, var); \
+ else if (__builtin_types_compatible_p(typeof(__val), u16)) \
+ __val = READ16_SEG(seg, var); \
+ else if (__builtin_types_compatible_p(typeof(__val), u32)) \
+ __val = READ32_SEG(seg, var); \
+ __val; })
+
+#define SET_VAR(seg, var, val) do { \
+ if (__builtin_types_compatible_p(typeof(var), u8)) \
+ WRITE8_SEG(seg, var, (val)); \
+ else if (__builtin_types_compatible_p(typeof(var), u16)) \
+ WRITE16_SEG(seg, var, (val)); \
+ else if (__builtin_types_compatible_p(typeof(var), u32)) \
+ WRITE32_SEG(seg, var, (val)); \
+ } while (0)
+
+#define SET_SEG(SEG, value) \
+ __asm__ __volatile__("movw %w0, %%" #SEG : : "r"(value))
+#define GET_SEG(SEG) ({ \
+ u16 __seg; \
+ __asm__ __volatile__("movw %%" #SEG ", %w0" : "=r"(__seg)); \
+ __seg;})
+
diff --git a/src/floppy.c b/src/floppy.c
new file mode 100644
index 0000000..5e70df2
--- /dev/null
+++ b/src/floppy.c
@@ -0,0 +1,757 @@
+// 16bit code to access floppy drives.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "types.h" // u8
+#include "disk.h" // DISK_RET_SUCCESS
+#include "config.h" // CONFIG_FLOPPY_SUPPORT
+#include "biosvar.h" // struct bregs
+#include "util.h" // irq_disable
+#include "cmos.h" // inb_cmos
+
+#define BX_FLOPPY_ON_CNT 37 /* 2 seconds */
+
+////.org 0xefc7
+// Since no provisions are made for multiple drive types, most
+// values in this table are ignored. I set parameters for 1.44M
+// floppy here
+char diskette_param_table[11] = {
+ 0xAF,
+ 0x02, // head load time 0000001, DMA used
+ 0x25,
+ 0x02,
+ 18,
+ 0x1B,
+ 0xFF,
+ 0x6C,
+ 0xF6,
+ 0x0F,
+ 0x08,
+};
+
+// New diskette parameter table adding 3 parameters from IBM
+// Since no provisions are made for multiple drive types, most
+// values in this table are ignored. I set parameters for 1.44M
+// floppy here
+char diskette_param_table2[14] VISIBLE = {
+ 0xAF,
+ 0x02, // head load time 0000001, DMA used
+ 0x25,
+ 0x02,
+ 18,
+ 0x1B,
+ 0xFF,
+ 0x6C,
+ 0xF6,
+ 0x0F,
+ 0x08,
+ 79, // maximum track
+ 0, // data transfer rate
+ 4, // drive type in cmos
+};
+
+// Oddities:
+// Return codes vary greatly - AL not cleared consistenlty, BDA return
+// status not set consistently, sometimes panics.
+// Extra outb(0x000a, 0x02) in read?
+// Does not disable interrupts on failure paths.
+// numfloppies used before set in int_1308
+// int_1305 verifies track but doesn't use it?
+
+static inline void
+set_diskette_current_cyl(u8 drive, u8 cyl)
+{
+ if (drive)
+ SET_BDA(floppy_track1, cyl);
+ else
+ SET_BDA(floppy_track0, cyl);
+}
+
+static u16
+get_drive_type(u8 drive)
+{
+ // check CMOS to see if drive exists
+ u8 drive_type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
+ if (drive == 0)
+ drive_type >>= 4;
+ else
+ drive_type &= 0x0f;
+ return drive_type;
+}
+
+static u16
+floppy_media_known(u8 drive)
+{
+ if (!(GET_BDA(floppy_recalibration_status) & (1<<drive)))
+ return 0;
+ u8 v = GET_BDA(floppy_media_state[drive]);
+ if (!(v & FMS_MEDIA_DRIVE_ESTABLISHED))
+ return 0;
+ return 1;
+}
+
+static void
+floppy_reset_controller()
+{
+ // Reset controller
+ u8 val8 = inb(PORT_FD_DOR);
+ outb(val8 & ~0x04, PORT_FD_DOR);
+ outb(val8 | 0x04, PORT_FD_DOR);
+
+ // Wait for controller to come out of reset
+ while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
+ ;
+}
+
+static void
+floppy_prepare_controller(u8 drive)
+{
+ CLEARBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
+
+ // turn on motor of selected drive, DMA & int enabled, normal operation
+ u8 prev_reset = inb(PORT_FD_DOR) & 0x04;
+ u8 dor = 0x10;
+ if (drive)
+ dor = 0x20;
+ dor |= 0x0c;
+ dor |= drive;
+ outb(dor, PORT_FD_DOR);
+
+ // reset the disk motor timeout value of INT 08
+ SET_BDA(floppy_motor_counter, BX_FLOPPY_ON_CNT);
+
+ // wait for drive readiness
+ while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
+ ;
+
+ if (prev_reset == 0) {
+ irq_enable();
+ u8 v;
+ do {
+ v = GET_BDA(floppy_recalibration_status);
+ } while ((v & FRS_TIMEOUT) == 0);
+ irq_disable();
+
+ v &= ~FRS_TIMEOUT;
+ SET_BDA(floppy_recalibration_status, v);
+ }
+}
+
+static u8
+floppy_pio(u8 *cmd, u8 cmdlen)
+{
+ floppy_prepare_controller(cmd[1] & 1);
+
+ // send command to controller
+ u8 i;
+ for (i=0; i<cmdlen; i++)
+ outb(cmd[i], PORT_FD_DATA);
+
+ irq_enable();
+ u8 v;
+ do {
+ if (!GET_BDA(floppy_motor_counter)) {
+ irq_disable();
+ floppy_reset_controller();
+ return DISK_RET_ETIMEOUT;
+ }
+ v = GET_BDA(floppy_recalibration_status);
+ } while (!(v & FRS_TIMEOUT));
+ irq_disable();
+
+ v &= ~FRS_TIMEOUT;
+ SET_BDA(floppy_recalibration_status, v);
+
+ if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
+ BX_PANIC("int13_diskette: ctrl not ready\n");
+
+ return 0;
+}
+
+static u8
+floppy_cmd(struct bregs *regs, u16 count, u8 *cmd, u8 cmdlen)
+{
+ // es:bx = pointer to where to place information from diskette
+ // port 04: DMA-1 base and current address, channel 2
+ // port 05: DMA-1 base and current count, channel 2
+ u16 page = regs->es >> 12; // upper 4 bits
+ u16 base_es = regs->es << 4; // lower 16bits contributed by ES
+ u16 base_address = base_es + regs->bx; // lower 16 bits of address
+ // contributed by ES:BX
+ if (base_address < base_es)
+ // in case of carry, adjust page by 1
+ page++;
+
+ // check for 64K boundary overrun
+ u16 last_addr = base_address + count;
+ if (last_addr < base_address)
+ return DISK_RET_EBOUNDARY;
+
+ u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
+ if (cmd[0] == 0xe6)
+ // read
+ mode_register = 0x46;
+
+ DEBUGF("floppy dma c2");
+ outb(0x06, PORT_DMA1_MASK_REG);
+ outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
+ outb(base_address, PORT_DMA_ADDR_2);
+ outb(base_address>>8, PORT_DMA_ADDR_2);
+ outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
+ outb(count, PORT_DMA_CNT_2);
+ outb(count>>8, PORT_DMA_CNT_2);
+
+ // port 0b: DMA-1 Mode Register
+ // transfer type=write, channel 2
+ outb(mode_register, PORT_DMA1_MODE_REG);
+
+ // port 81: DMA-1 Page Register, channel 2
+ outb(page, PORT_DMA_PAGE_2);
+
+ outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
+
+ u8 ret = floppy_pio(cmd, cmdlen);
+ if (ret)
+ return ret;
+
+ // read 7 return status bytes from controller
+ u8 i;
+ for (i=0; i<7; i++) {
+ u8 v = inb(PORT_FD_DATA);
+ cmd[i] = v;
+ SET_BDA(floppy_return_status[i], v);
+ }
+
+ return 0;
+}
+
+static void
+floppy_drive_recal(u8 drive)
+{
+ // send Recalibrate command (2 bytes) to controller
+ u8 data[12];
+ data[0] = 0x07; // 07: Recalibrate
+ data[1] = drive; // 0=drive0, 1=drive1
+ floppy_pio(data, 2);
+
+ SETBITS_BDA(floppy_recalibration_status, 1<<drive);
+ set_diskette_current_cyl(drive, 0);
+}
+
+static u16
+floppy_media_sense(u8 drive)
+{
+ u16 rv;
+ u8 config_data, media_state;
+
+ floppy_drive_recal(drive);
+
+ // for now cheat and get drive type from CMOS,
+ // assume media is same as drive type
+
+ // ** config_data **
+ // Bitfields for diskette media control:
+ // Bit(s) Description (Table M0028)
+ // 7-6 last data rate set by controller
+ // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
+ // 5-4 last diskette drive step rate selected
+ // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
+ // 3-2 {data rate at start of operation}
+ // 1-0 reserved
+
+ // ** media_state **
+ // Bitfields for diskette drive media state:
+ // Bit(s) Description (Table M0030)
+ // 7-6 data rate
+ // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
+ // 5 double stepping required (e.g. 360kB in 1.2MB)
+ // 4 media type established
+ // 3 drive capable of supporting 4MB media
+ // 2-0 on exit from BIOS, contains
+ // 000 trying 360kB in 360kB
+ // 001 trying 360kB in 1.2MB
+ // 010 trying 1.2MB in 1.2MB
+ // 011 360kB in 360kB established
+ // 100 360kB in 1.2MB established
+ // 101 1.2MB in 1.2MB established
+ // 110 reserved
+ // 111 all other formats/drives
+
+ switch (get_drive_type(drive)) {
+ case 1:
+ // 360K 5.25" drive
+ config_data = 0x00; // 0000 0000
+ media_state = 0x25; // 0010 0101
+ rv = 1;
+ break;
+ case 2:
+ // 1.2 MB 5.25" drive
+ config_data = 0x00; // 0000 0000
+ media_state = 0x25; // 0010 0101 // need double stepping??? (bit 5)
+ rv = 1;
+ break;
+ case 3:
+ // 720K 3.5" drive
+ config_data = 0x00; // 0000 0000 ???
+ media_state = 0x17; // 0001 0111
+ rv = 1;
+ break;
+ case 4:
+ // 1.44 MB 3.5" drive
+ config_data = 0x00; // 0000 0000
+ media_state = 0x17; // 0001 0111
+ rv = 1;
+ break;
+ case 5:
+ // 2.88 MB 3.5" drive
+ config_data = 0xCC; // 1100 1100
+ media_state = 0xD7; // 1101 0111
+ rv = 1;
+ break;
+ //
+ // Extended floppy size uses special cmos setting
+ case 6:
+ // 160k 5.25" drive
+ config_data = 0x00; // 0000 0000
+ media_state = 0x27; // 0010 0111
+ rv = 1;
+ break;
+ case 7:
+ // 180k 5.25" drive
+ config_data = 0x00; // 0000 0000
+ media_state = 0x27; // 0010 0111
+ rv = 1;
+ break;
+ case 8:
+ // 320k 5.25" drive
+ config_data = 0x00; // 0000 0000
+ media_state = 0x27; // 0010 0111
+ rv = 1;
+ break;
+ default:
+ // not recognized
+ config_data = 0x00; // 0000 0000
+ media_state = 0x00; // 0000 0000
+ rv = 0;
+ }
+
+ SET_BDA(floppy_last_data_rate, config_data);
+ SET_BDA(floppy_media_state[drive], media_state);
+ return rv;
+}
+
+static inline void
+floppy_ret(struct bregs *regs, u8 code)
+{
+ regs->ah = code;
+ SET_BDA(floppy_last_status, code);
+ set_cf(regs, code);
+}
+
+static inline void
+floppy_fail(struct bregs *regs, u8 code)
+{
+ regs->al = 0; // no sectors read
+ floppy_ret(regs, code);
+}
+
+static u16
+check_drive(struct bregs *regs, u8 drive)
+{
+ // see if drive exists
+ if (drive > 1 || !get_drive_type(drive)) {
+ floppy_fail(regs, DISK_RET_ETIMEOUT);
+ return 1;
+ }
+
+ // see if media in drive, and type is known
+ if (floppy_media_known(drive) == 0 && floppy_media_sense(drive) == 0) {
+ floppy_fail(regs, DISK_RET_EMEDIA);
+ return 1;
+ }
+ return 0;
+}
+
+// diskette controller reset
+static void
+floppy_1300(struct bregs *regs, u8 drive)
+{
+ if (drive > 1) {
+ floppy_ret(regs, DISK_RET_EPARAM);
+ return;
+ }
+ if (!get_drive_type(drive)) {
+ floppy_ret(regs, DISK_RET_ETIMEOUT);
+ return;
+ }
+ set_diskette_current_cyl(drive, 0); // current cylinder
+ floppy_ret(regs, DISK_RET_SUCCESS);
+}
+
+// Read Diskette Status
+static void
+floppy_1301(struct bregs *regs, u8 drive)
+{
+ u8 v = GET_BDA(floppy_last_status);
+ regs->ah = v;
+ set_cf(regs, v);
+}
+
+// Read Diskette Sectors
+static void
+floppy_1302(struct bregs *regs, u8 drive)
+{
+ if (check_drive(regs, drive))
+ return;
+
+ u8 num_sectors = regs->al;
+ u8 track = regs->ch;
+ u8 sector = regs->cl;
+ u8 head = regs->dh;
+
+ if (head > 1 || sector == 0 || num_sectors == 0
+ || track > 79 || num_sectors > 72) {
+ BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
+ floppy_fail(regs, DISK_RET_EPARAM);
+ return;
+ }
+
+ // send read-normal-data command (9 bytes) to controller
+ u8 data[12];
+ data[0] = 0xe6; // e6: read normal data
+ data[1] = (head << 2) | drive; // HD DR1 DR2
+ data[2] = track;
+ data[3] = head;
+ data[4] = sector;
+ data[5] = 2; // 512 byte sector size
+ data[6] = sector + num_sectors - 1; // last sector to read on track
+ data[7] = 0; // Gap length
+ data[8] = 0xff; // Gap length
+
+ u16 ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
+ if (ret) {
+ floppy_fail(regs, ret);
+ return;
+ }
+
+ if (data[0] & 0xc0) {
+ floppy_fail(regs, DISK_RET_ECONTROLLER);
+ return;
+ }
+
+ // ??? should track be new val from return_status[3] ?
+ set_diskette_current_cyl(drive, track);
+ // AL = number of sectors read (same value as passed)
+ floppy_ret(regs, DISK_RET_SUCCESS);
+}
+
+// Write Diskette Sectors
+static void
+floppy_1303(struct bregs *regs, u8 drive)
+{
+ if (check_drive(regs, drive))
+ return;
+
+ u8 num_sectors = regs->al;
+ u8 track = regs->ch;
+ u8 sector = regs->cl;
+ u8 head = regs->dh;
+
+ if (head > 1 || sector == 0 || num_sectors == 0
+ || track > 79 || num_sectors > 72) {
+ BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
+ floppy_fail(regs, DISK_RET_EPARAM);
+ return;
+ }
+
+ // send write-normal-data command (9 bytes) to controller
+ u8 data[12];
+ data[0] = 0xc5; // c5: write normal data
+ data[1] = (head << 2) | drive; // HD DR1 DR2
+ data[2] = track;
+ data[3] = head;
+ data[4] = sector;
+ data[5] = 2; // 512 byte sector size
+ data[6] = sector + num_sectors - 1; // last sector to write on track
+ data[7] = 0; // Gap length
+ data[8] = 0xff; // Gap length
+
+ u8 ret = floppy_cmd(regs, (num_sectors * 512) - 1, data, 9);
+ if (ret) {
+ floppy_fail(regs, ret);
+ return;
+ }
+
+ if (data[0] & 0xc0) {
+ if (data[1] & 0x02) {
+ regs->ax = 0x0300;
+ set_cf(regs, 1);
+ return;
+ }
+ BX_PANIC("int13_diskette_function: read error\n");
+ }
+
+ // ??? should track be new val from return_status[3] ?
+ set_diskette_current_cyl(drive, track);
+ // AL = number of sectors read (same value as passed)
+ floppy_ret(regs, DISK_RET_SUCCESS);
+}
+
+// Verify Diskette Sectors
+static void
+floppy_1304(struct bregs *regs, u8 drive)
+{
+ if (check_drive(regs, drive))
+ return;
+
+ u8 num_sectors = regs->al;
+ u8 track = regs->ch;
+ u8 sector = regs->cl;
+ u8 head = regs->dh;
+
+ if (head > 1 || sector == 0 || num_sectors == 0
+ || track > 79 || num_sectors > 72) {
+ BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
+ floppy_fail(regs, DISK_RET_EPARAM);
+ return;
+ }
+
+ // ??? should track be new val from return_status[3] ?
+ set_diskette_current_cyl(drive, track);
+ // AL = number of sectors verified (same value as passed)
+ floppy_ret(regs, DISK_RET_SUCCESS);
+}
+
+// format diskette track
+static void
+floppy_1305(struct bregs *regs, u8 drive)
+{
+ DEBUGF("floppy f05\n");
+
+ if (check_drive(regs, drive))
+ return;
+
+ u8 num_sectors = regs->al;
+ u8 head = regs->dh;
+
+ if (head > 1 || num_sectors == 0 || num_sectors > 18) {
+ BX_INFO("int13_diskette: read/write/verify: parameter out of range\n");
+ floppy_fail(regs, DISK_RET_EPARAM);
+ return;
+ }
+
+ // send format-track command (6 bytes) to controller
+ u8 data[12];
+ data[0] = 0x4d; // 4d: format track
+ data[1] = (head << 2) | drive; // HD DR1 DR2
+ data[2] = 2; // 512 byte sector size
+ data[3] = num_sectors; // number of sectors per track
+ data[4] = 0; // Gap length
+ data[5] = 0xf6; // Fill byte
+
+ u8 ret = floppy_cmd(regs, (num_sectors * 4) - 1, data, 6);
+ if (ret) {
+ floppy_fail(regs, ret);
+ return;
+ }
+
+ if (data[0] & 0xc0) {
+ if (data[1] & 0x02) {
+ regs->ax = 0x0300;
+ set_cf(regs, 1);
+ return;
+ }
+ BX_PANIC("int13_diskette_function: read error\n");
+ }
+
+ set_diskette_current_cyl(drive, 0);
+ floppy_ret(regs, 0);
+}
+
+// read diskette drive parameters
+static void
+floppy_1308(struct bregs *regs, u8 drive)
+{
+ DEBUGF("floppy f08\n");
+
+ u8 drive_type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
+ u8 num_floppies = 0;
+ if (drive_type & 0xf0)
+ num_floppies++;
+ if (drive_type & 0x0f)
+ num_floppies++;
+
+ if (drive > 1) {
+ regs->ax = 0;
+ regs->bx = 0;
+ regs->cx = 0;
+ regs->dx = 0;
+ regs->es = 0;
+ regs->di = 0;
+ regs->dl = num_floppies;
+ set_cf(regs, 0);
+ return;
+ }
+
+ if (drive == 0)
+ drive_type >>= 4;
+ else
+ drive_type &= 0x0f;
+
+ regs->bh = 0;
+ regs->bl = drive_type;
+ regs->ah = 0;
+ regs->al = 0;
+ regs->dl = num_floppies;
+
+ switch (drive_type) {
+ case 0: // none
+ regs->cx = 0;
+ regs->dh = 0; // max head #
+ break;
+
+ case 1: // 360KB, 5.25"
+ regs->cx = 0x2709; // 40 tracks, 9 sectors
+ regs->dh = 1; // max head #
+ break;
+
+ case 2: // 1.2MB, 5.25"
+ regs->cx = 0x4f0f; // 80 tracks, 15 sectors
+ regs->dh = 1; // max head #
+ break;
+
+ case 3: // 720KB, 3.5"
+ regs->cx = 0x4f09; // 80 tracks, 9 sectors
+ regs->dh = 1; // max head #
+ break;
+
+ case 4: // 1.44MB, 3.5"
+ regs->cx = 0x4f12; // 80 tracks, 18 sectors
+ regs->dh = 1; // max head #
+ break;
+
+ case 5: // 2.88MB, 3.5"
+ regs->cx = 0x4f24; // 80 tracks, 36 sectors
+ regs->dh = 1; // max head #
+ break;
+
+ case 6: // 160k, 5.25"
+ regs->cx = 0x2708; // 40 tracks, 8 sectors
+ regs->dh = 0; // max head #
+ break;
+
+ case 7: // 180k, 5.25"
+ regs->cx = 0x2709; // 40 tracks, 9 sectors
+ regs->dh = 0; // max head #
+ break;
+
+ case 8: // 320k, 5.25"
+ regs->cx = 0x2708; // 40 tracks, 8 sectors
+ regs->dh = 1; // max head #
+ break;
+
+ default: // ?
+ BX_PANIC("floppy: int13: bad floppy type\n");
+ }
+
+ /* set es & di to point to 11 byte diskette param table in ROM */
+ regs->es = SEG_BIOS;
+ regs->di = (u16)diskette_param_table2;
+ /* disk status not changed upon success */
+}
+
+// read diskette drive type
+static void
+floppy_1315(struct bregs *regs, u8 drive)
+{
+ DEBUGF("floppy f15\n");
+ if (drive > 1) {
+ regs->ah = 0; // only 2 drives supported
+ // set_diskette_ret_status here ???
+ set_cf(regs, 1);
+ return;
+ }
+ u8 drive_type = get_drive_type(drive);
+
+ regs->ah = (drive_type != 0);
+ set_cf(regs, 0);
+}
+
+// get diskette change line status
+static void
+floppy_1316(struct bregs *regs, u8 drive)
+{
+ DEBUGF("floppy f16\n");
+ if (drive > 1) {
+ floppy_ret(regs, DISK_RET_EPARAM);
+ return;
+ }
+ floppy_ret(regs, DISK_RET_ECHANGED);
+}
+
+static void
+floppy_13XX(struct bregs *regs, u8 drive)
+{
+ BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
+ floppy_ret(regs, DISK_RET_EPARAM);
+}
+
+void
+floppy_13(struct bregs *regs, u8 drive)
+{
+ if (CONFIG_FLOPPY_SUPPORT) {
+ switch (regs->ah) {
+ case 0x00: floppy_1300(regs, drive); break;
+ case 0x01: floppy_1301(regs, drive); break;
+ case 0x02: floppy_1302(regs, drive); break;
+ case 0x03: floppy_1303(regs, drive); break;
+ case 0x04: floppy_1304(regs, drive); break;
+ case 0x05: floppy_1305(regs, drive); break;
+ case 0x08: floppy_1308(regs, drive); break;
+ case 0x15: floppy_1315(regs, drive); break;
+ case 0x16: floppy_1316(regs, drive); break;
+ default: floppy_13XX(regs, drive); break;
+ }
+ } else {
+ switch (regs->ah) {
+ case 0x01: floppy_1301(regs, drive); break;
+ default: floppy_13XX(regs, drive); break;
+ }
+ }
+}
+
+// INT 0Eh Diskette Hardware ISR Entry Point
+void VISIBLE
+handle_0e(struct bregs *regs)
+{
+ debug_enter(regs);
+ if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) {
+ outb(0x08, PORT_FD_DATA); // sense interrupt status
+ while ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
+ ;
+ do {
+ inb(PORT_FD_DATA);
+ } while ((inb(PORT_FD_STATUS) & 0xc0) == 0xc0);
+ }
+ eoi_master_pic();
+ // diskette interrupt has occurred
+ SETBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
+}
+
+// Called from int08 handler.
+void
+floppy_tick()
+{
+ // time to turn off drive(s)?
+ u8 fcount = GET_BDA(floppy_motor_counter);
+ if (fcount) {
+ fcount--;
+ SET_BDA(floppy_motor_counter, fcount);
+ if (fcount == 0)
+ // turn motor(s) off
+ outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
+ }
+}
diff --git a/src/font.c b/src/font.c
new file mode 100644
index 0000000..01c73ed
--- /dev/null
+++ b/src/font.c
@@ -0,0 +1,139 @@
+#include "types.h" // u8
+
+// Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
+
+/*
+ * This font comes from the fntcol16.zip package (c) by Joseph Gil
+ * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
+ * This font is public domain
+ */
+const u8 vgafont8[128*8] __attribute__((aligned (1))) = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
+ 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
+ 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
+ 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
+ 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
+ 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
+ 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
+ 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
+ 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
+ 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
+ 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
+ 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
+ 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
+ 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
+ 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
+ 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
+ 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
+ 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
+ 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
+ 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
+ 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
+ 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
+ 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
+ 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
+ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
+ 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
+ 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
+ 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
+ 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
+ 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
+ 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
+ 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
+ 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
+ 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
+ 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
+ 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
+ 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
+ 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
+ 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
+ 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
+ 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
+ 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
+ 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
+ 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
+ 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
+ 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
+ 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
+ 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
+ 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
+ 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
+ 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
+ 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
+ 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
+ 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
+ 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
+ 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
+ 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
+ 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
+ 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
+ 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
+ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
+ 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
+ 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
+};
diff --git a/src/ioport.h b/src/ioport.h
new file mode 100644
index 0000000..344803e
--- /dev/null
+++ b/src/ioport.h
@@ -0,0 +1,56 @@
+// Definitions for X86 IO port access.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+#ifndef __IOPORT_H
+#define __IOPORT_H
+
+#include "types.h" // u8
+
+#define PORT_DMA_ADDR_2 0x0004
+#define PORT_DMA_CNT_2 0x0005
+#define PORT_DMA1_MASK_REG 0x000a
+#define PORT_DMA1_MODE_REG 0x000b
+#define PORT_DMA1_CLEAR_FF_REG 0x000c
+#define PORT_DMA1_MASTER_CLEAR 0x000d
+#define PORT_PIC1 0x0020
+#define PORT_PIC1_DATA 0x0021
+#define PORT_PIT_COUNTER0 0x0040
+#define PORT_PIT_COUNTER1 0x0041
+#define PORT_PIT_COUNTER2 0x0042
+#define PORT_PIT_MODE 0x0043
+#define PORT_KBD_CTRLB 0x0061
+#define PORT_CMOS_INDEX 0x0070
+#define PORT_CMOS_DATA 0x0071
+#define PORT_DMA_PAGE_2 0x0081
+#define PORT_A20 0x0092
+#define PORT_PIC2 0x00a0
+#define PORT_PIC2_DATA 0x00a1
+#define PORT_DMA2_MASK_REG 0x00d4
+#define PORT_DMA2_MODE_REG 0x00d6
+#define PORT_DMA2_MASTER_CLEAR 0x00da
+#define PORT_FD_DOR 0x03f2
+#define PORT_FD_STATUS 0x03f4
+#define PORT_FD_DATA 0x03f5
+
+// PORT_PIC1 bitdefs
+#define PIC1_IRQ5 (1<<5)
+// PORT_PIC2 bitdefs
+#define PIC2_IRQ8 (1<<0)
+#define PIC2_IRQ13 (1<<5)
+
+// PORT_KBD_CTRLB bitdefs
+#define KBD_REFRESH (1<<4)
+
+
+static inline void outb(u8 value, u16 port) {
+ __asm__ __volatile__("outb %b0, %w1" : : "a"(value), "Nd"(port));
+}
+static inline u8 inb(u16 port) {
+ u8 value;
+ __asm__ __volatile__("inb %w1, %b0" : "=a"(value) : "Nd"(port));
+ return value;
+}
+
+#endif // ioport.h
diff --git a/src/kbd.c b/src/kbd.c
new file mode 100644
index 0000000..bcc1a59
--- /dev/null
+++ b/src/kbd.c
@@ -0,0 +1,35 @@
+// 16bit code to handle keyboard requests.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "biosvar.h" // struct bregs
+#include "util.h" // debug_enter
+
+void
+handle_15c2(struct bregs *regs)
+{
+}
+
+// INT 16h Keyboard Service Entry Point
+void VISIBLE
+handle_16(struct bregs *regs)
+{
+ //debug_enter(regs);
+}
+
+// INT09h : Keyboard Hardware Service Entry Point
+void VISIBLE
+handle_09(struct bregs *regs)
+{
+ debug_enter(regs);
+}
+
+// INT74h : PS/2 mouse hardware interrupt
+void VISIBLE
+handle_74(struct bregs *regs)
+{
+ debug_enter(regs);
+}
diff --git a/src/output.c b/src/output.c
new file mode 100644
index 0000000..9670163
--- /dev/null
+++ b/src/output.c
@@ -0,0 +1,161 @@
+// Raw screen writing code.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include <stdarg.h> // va_list
+
+#include "farptr.h" // GET_VAR
+#include "util.h" // bprintf
+#include "biosvar.h" // struct bregs
+
+static void
+screenc(char c)
+{
+ // XXX
+}
+
+// XXX
+#define PORT_DEBUG 0x403
+
+// Write a charcter to the framebuffer.
+static void
+putc(u16 action, char c)
+{
+ screenc(c);
+ outb(c, PORT_DEBUG);
+}
+
+// Write a string to the framebuffer.
+static void
+puts(u16 action, const char *s)
+{
+ for (; *s; s++)
+ putc(action, *s);
+}
+
+// Write a string to the framebuffer.
+static void
+puts_cs(u16 action, const char *s)
+{
+ for (;; s++) {
+ char c = GET_VAR(CS, (u8)*s);
+ if (!c)
+ break;
+ putc(action, c);
+ }
+}
+
+// Write an unsigned integer to the screen.
+static void
+putuint(u16 action, u32 val)
+{
+ char buf[12];
+ char *d = &buf[sizeof(buf) - 1];
+ *d-- = '\0';
+ for (;;) {
+ *d = val % 10;
+ val /= 10;
+ if (!val)
+ break;
+ d--;
+ }
+ puts(action, d);
+}
+
+// Write a single digit hex character to the screen.
+static inline void
+putsinglehex(u16 action, u32 val)
+{
+ if (val <= 9)
+ val = '0' + val;
+ else
+ val = 'a' + val - 10;
+ putc(action, val);
+}
+
+// Write an integer in hexadecimal to the screen.
+static void
+puthex(u16 action, u32 val)
+{
+ putsinglehex(action, (val >> 28) & 0xf);
+ putsinglehex(action, (val >> 24) & 0xf);
+ putsinglehex(action, (val >> 20) & 0xf);
+ putsinglehex(action, (val >> 16) & 0xf);
+ putsinglehex(action, (val >> 12) & 0xf);
+ putsinglehex(action, (val >> 8) & 0xf);
+ putsinglehex(action, (val >> 4) & 0xf);
+ putsinglehex(action, (val >> 0) & 0xf);
+}
+
+void
+bprintf(u16 action, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ const char *s = fmt;
+ for (;; s++) {
+ char c = GET_VAR(CS, (u8)*s);
+ if (!c)
+ break;
+ if (c != '%') {
+ putc(action, c);
+ continue;
+ }
+ const char *n = s+1;
+ c = GET_VAR(CS, (u8)*n);
+ s32 val;
+ const char *sarg;
+ switch (c) {
+ case '%':
+ putc(action, '%');
+ break;
+ case 'd':
+ val = va_arg(args, s32);
+ if (val < 0) {
+ putc(action, '-');
+ val = -val;
+ }
+ putuint(action, val);
+ break;
+ case 'u':
+ val = va_arg(args, s32);
+ putuint(action, val);
+ break;
+ case 'x':
+ val = va_arg(args, s32);
+ puthex(action, val);
+ break;
+ case 's':
+ sarg = va_arg(args, const char *);
+ puts_cs(action, sarg);
+ break;
+ default:
+ putc(action, *s);
+ n = s;
+ }
+ s = n;
+ }
+ va_end(args);
+}
+
+// Function called on handler startup.
+void
+__debug_enter(const char *fname, struct bregs *regs)
+{
+ bprintf(0, "enter %s: a=%x b=%x c=%x d=%x si=%x di=%x\n"
+ , fname, regs->eax, regs->ebx, regs->ecx, regs->edx
+ , regs->esi, regs->edi);
+ bprintf(0, "&=%x ds=%x es=%x bp=%x sp=%x ip=%x cs=%x f=%x\n"
+ , (u32)regs, regs->ds, regs->es, regs->ebp, regs->esp
+ , regs->ip, regs->cs, regs->flags);
+}
+
+void
+__debug_exit(const char *fname, struct bregs *regs)
+{
+ bprintf(0, "exit %s: a=%x b=%x c=%x d=%x s=%x i=%x\n"
+ , fname, regs->eax, regs->ebx, regs->ecx, regs->edx
+ , regs->esi, regs->edi);
+}
diff --git a/src/post.c b/src/post.c
new file mode 100644
index 0000000..8d35f97
--- /dev/null
+++ b/src/post.c
@@ -0,0 +1,312 @@
+// 32bit code to Power On Self Test (POST) a machine.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "ioport.h" // PORT_*
+#include "../out/rom16.offset.auto.h" // OFFSET_*
+#include "config.h" // CONFIG_*
+#include "cmos.h" // CMOS_*
+#include "util.h" // memset
+#include "biosvar.h" // struct bios_data_area_s
+
+#define bda ((struct bios_data_area_s *)0)
+#define ebda ((struct extended_bios_data_area_s *)(EBDA_SEG<<4))
+
+static void
+init_bda()
+{
+ memset(bda, 0, sizeof(*bda));
+
+ int i;
+ for (i=0; i<256; i++) {
+ bda->ivecs[i].seg = 0xf000;
+ bda->ivecs[i].offset = OFFSET_dummy_iret_handler;
+ }
+
+ bda->mem_size_kb = BASE_MEM_IN_K;
+}
+
+static void
+init_handlers()
+{
+ // set vector 0x79 to zero
+ // this is used by 'gardian angel' protection system
+ bda->ivecs[0x79].seg = 0;
+ bda->ivecs[0x79].offset = 0;
+
+ bda->ivecs[0x40].offset = OFFSET_entry_40;
+ bda->ivecs[0x0e].offset = OFFSET_entry_0e;
+ bda->ivecs[0x13].offset = OFFSET_entry_13;
+ bda->ivecs[0x76].offset = OFFSET_entry_76;
+ bda->ivecs[0x17].offset = OFFSET_entry_17;
+ bda->ivecs[0x18].offset = OFFSET_entry_18;
+ bda->ivecs[0x19].offset = OFFSET_entry_19;
+ bda->ivecs[0x1c].offset = OFFSET_entry_1c;
+ bda->ivecs[0x12].offset = OFFSET_entry_12;
+ bda->ivecs[0x11].offset = OFFSET_entry_11;
+ bda->ivecs[0x15].offset = OFFSET_entry_15;
+ bda->ivecs[0x08].offset = OFFSET_entry_08;
+ bda->ivecs[0x09].offset = OFFSET_entry_09;
+ bda->ivecs[0x16].offset = OFFSET_entry_16;
+ bda->ivecs[0x14].offset = OFFSET_entry_14;
+ bda->ivecs[0x1a].offset = OFFSET_entry_1a;
+ bda->ivecs[0x70].offset = OFFSET_entry_70;
+ bda->ivecs[0x74].offset = OFFSET_entry_74;
+ bda->ivecs[0x75].offset = OFFSET_entry_75;
+ bda->ivecs[0x10].offset = OFFSET_entry_10;
+}
+
+static void
+init_ebda()
+{
+ ebda->size = EBDA_SIZE;
+ bda->ebda_seg = EBDA_SEG;
+ bda->ivecs[0x41].seg = EBDA_SEG;
+ bda->ivecs[0x41].offset = 0x3d; // XXX
+ bda->ivecs[0x46].seg = EBDA_SEG;
+ bda->ivecs[0x46].offset = 0x4d; // XXX
+}
+
+static void
+pit_setup()
+{
+ // timer0: binary count, 16bit count, mode 2
+ outb(0x34, PORT_PIT_MODE);
+ // maximum count of 0000H = 18.2Hz
+ outb(0x0, PORT_PIT_COUNTER0);
+ outb(0x0, PORT_PIT_COUNTER0);
+}
+
+static void
+kbd_init()
+{
+}
+
+static void
+kbd_setup()
+{
+ bda->kbd_mode = 0x10;
+ bda->kbd_buf_head = bda->kbd_buf_tail = offsetof(struct bios_data_area_s, kbd_buf);
+ bda->kbd_buf_start_offset = offsetof(struct bios_data_area_s, kbd_buf);
+ bda->kbd_buf_end_offset = offsetof(struct bios_data_area_s, kbd_buf[sizeof(bda->kbd_buf)]);
+ kbd_init();
+
+ // XXX
+ u16 eqb = bda->equipment_list_flags;
+ eqb = (eqb & 0xff00) | inb_cmos(CMOS_EQUIPMENT_INFO);
+ bda->equipment_list_flags = eqb;
+}
+
+static void
+lpt_setup()
+{
+ // XXX
+}
+
+static void
+serial_setup()
+{
+ // XXX
+}
+
+static u32
+bcd2bin(u8 val)
+{
+ return (val & 0xf) + ((val >> 4) * 10);
+}
+
+static void
+timer_setup()
+{
+ u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
+ u32 ticks = (seconds * 18206507) / 1000000;
+ u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
+ ticks += (minutes * 10923904) / 10000;
+ u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
+ ticks += (hours * 65543427) / 1000;
+ bda->timer_counter = ticks;
+ bda->timer_rollover = 0;
+}
+
+static void
+pic_setup()
+{
+ outb(0x11, PORT_PIC1);
+ outb(0x11, PORT_PIC2_DATA);
+ outb(0x08, PORT_PIC1_DATA);
+ outb(0x70, PORT_PIC2_DATA);
+ outb(0x04, PORT_PIC1_DATA);
+ outb(0x02, PORT_PIC2_DATA);
+ outb(0x01, PORT_PIC1_DATA);
+ outb(0x01, PORT_PIC2_DATA);
+ outb(0xb8, PORT_PIC1_DATA);
+ if (CONFIG_PS2_MOUSE)
+ outb(0x8f, PORT_PIC2_DATA);
+ else
+ outb(0x9f, PORT_PIC2_DATA);
+}
+
+static void
+floppy_drive_post()
+{
+ u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
+ u8 out = 0;
+ if (type & 0xf0)
+ out |= 0x07;
+ if (type & 0x0f)
+ out |= 0x70;
+ bda->floppy_harddisk_info = out;
+ outb(0x02, PORT_DMA1_MASK_REG);
+
+ bda->ivecs[0x1E].offset = OFFSET_diskette_param_table2;
+}
+
+static void
+cdemu_init()
+{
+ //ebda->cdemu.active = 0;
+}
+
+static void
+ata_init()
+{
+}
+
+static void
+ata_detect()
+{
+}
+
+static void
+hard_drive_post()
+{
+}
+
+static void
+init_boot_vectors()
+{
+}
+
+static void __attribute__((noinline))
+call16(u16 seg, u16 offset)
+{
+ u32 segoff = (seg << 16) | offset;
+ asm volatile(
+ "pushal\n" // Save registers
+ "ljmp $0x20, %0\n" // Jump to 16bit transition code
+ ".globl call16_resume\n"
+ "call16_resume:\n" // point of return
+ "popal\n" // restore registers
+ : : "Z" (OFFSET_call16), "b" (segoff));
+}
+
+static int
+checksum(u8 *p, u32 len)
+{
+ u32 i;
+ u8 sum = 0;
+ for (i=0; i<len; i++)
+ sum += p[i];
+ return sum;
+}
+
+#define PTR_TO_SEG(p) ((((u32)(p)) >> 4) & 0xf000)
+#define PTR_TO_OFFSET(p) (((u32)(p)) & 0xffff)
+
+static void
+rom_scan()
+{
+ u8 *p = (u8*)0xc0000;
+ for (; p <= (u8*)0xe0000; p += 2048) {
+ u8 *rom = p;
+ if (*(u16*)rom != 0xaa55)
+ continue;
+ u32 len = rom[2] * 512;
+ if (checksum(rom, len) != 0)
+ continue;
+ p = (u8*)(((u32)p + len) / 2048 * 2048);
+ call16(PTR_TO_SEG(rom), PTR_TO_OFFSET(rom + 3));
+
+ // Look at the ROM's PnP Expansion header. Properly, we're supposed
+ // to init all the ROMs and then go back and build an IPL table of
+ // all the bootable devices, but we can get away with one pass.
+ if (rom[0x1a] != '$' || rom[0x1b] != 'P'
+ || rom[0x1c] != 'n' || rom[0x1d] != 'P')
+ continue;
+ // 0x1A is also the offset into the expansion header of...
+ // the Bootstrap Entry Vector, or zero if there is none.
+ u16 entry = *(u16*)&rom[0x1a+0x1a];
+ if (!entry)
+ continue;
+ // Found a device that thinks it can boot the system. Record
+ // its BEV and product name string.
+
+ // XXX
+ }
+}
+
+static void
+status_restart(u8 status)
+{
+#if 0
+ if (status == 0x05)
+ eoi_jmp_post();
+#endif
+
+ BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
+}
+
+static void
+post()
+{
+ // first reset the DMA controllers
+ outb(0, PORT_DMA1_MASTER_CLEAR);
+ outb(0, PORT_DMA2_MASTER_CLEAR);
+
+ // then initialize the DMA controllers
+ outb(0xc0, PORT_DMA2_MODE_REG);
+ outb(0x00, PORT_DMA2_MASK_REG);
+
+ // Get and then clear CMOS shutdown status.
+ u8 status = inb_cmos(CMOS_RESET_CODE);
+ outb_cmos(0, CMOS_RESET_CODE);
+
+ if (status != 0x00 && status != 0x09 && status < 0x0d)
+ status_restart(status);
+
+ BX_INFO("Start bios");
+
+ init_bda();
+ init_handlers();
+ init_ebda();
+
+ pit_setup();
+ kbd_setup();
+ lpt_setup();
+ serial_setup();
+ timer_setup();
+ pic_setup();
+ //pci_setup();
+ init_boot_vectors();
+ rom_scan();
+
+ printf("BIOS - begin\n\n");
+
+ floppy_drive_post();
+ hard_drive_post();
+ if (CONFIG_ATA) {
+ ata_init();
+ ata_detect();
+ }
+ cdemu_init();
+ call16(0xf000, OFFSET_begin_boot);
+}
+
+void VISIBLE
+_start()
+{
+ post();
+}
diff --git a/src/rombios32.lds.S b/src/rombios32.lds.S
new file mode 100644
index 0000000..dae62d8
--- /dev/null
+++ b/src/rombios32.lds.S
@@ -0,0 +1,31 @@
+// Linker definitions for 32bit code
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "config.h"
+#include "../out/rom16.offset.auto.h"
+
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start);
+SECTIONS
+{
+ . = (OFFSET_bios16c_end | 0xf0000);
+ . = ALIGN(16);
+ _text32_start = . ;
+ .text : { *(.text) }
+ .rodata : { *(.rodata) }
+ . = ALIGN(16);
+ .data : { *(.data) }
+ __bss_start = . ;
+ .bss : { *(.bss) *(COMMON) }
+ _end = . ;
+ /DISCARD/ : { *(.stab)
+ *(.stabstr)
+ *(.comment)
+ *(.note)
+ }
+}
diff --git a/src/romlayout.S b/src/romlayout.S
new file mode 100644
index 0000000..c9cc6ef
--- /dev/null
+++ b/src/romlayout.S
@@ -0,0 +1,304 @@
+// Rom layout and bios assembler to C interface.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "config.h"
+
+ .code16gcc
+ .text
+ .globl bios16c_start, bios16c_end
+bios16c_start:
+.include "out/blob.proc.16.s"
+ .text
+bios16c_end:
+
+
+ .org 0xe05b
+ .globl _start
+_start:
+ .globl post16
+post16:
+
+ // Entry point of rombios32 code - the actual instruction is
+ // altered later in the build process.
+ .globl set_entry32
+set_entry32:
+ mov $0xf0000000, %ebx
+
+ // init the stack pointer
+ movl $ CONFIG_STACK32_OFFSET , %esp
+
+transition32:
+ // Disable irqs
+ cli
+
+ // enable a20
+ inb $0x92, %al
+ orb $0x02, %al
+ outb %al, $0x92
+
+ // Set segment descriptors
+ lidt %cs:pmode_IDT_info
+ lgdt %cs:rombios32_gdt_48
+
+ // set PE bit in CR0
+ movl %cr0, %eax
+ orb $0x01, %al
+ movl %eax, %cr0
+
+ // start protected mode code
+ .word 0xea66, 1f, 0x000f, 0x0010 // ljmpl $0x10, $(post32 | 0xf0000)
+
+ .code32
+1:
+ // init data segments
+ movl $0x18, %eax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %ss
+ xorl %eax, %eax
+ movw %ax, %fs
+ movw %ax, %gs
+
+ cld
+
+ jmp *%ebx
+
+ .code16gcc
+
+// We need a copy of this string, but we are not actually a PnP BIOS,
+// so make sure it is *not* aligned, so OSes will not see it if they
+// scan.
+ .align 2
+ .byte 0
+pnp_string:
+ .ascii "$PnP"
+
+// Return from 32bit code to 16bit code - must pass in destination
+// code segment,offset (%ebx) and the return stack position (%esp).
+
+ .globl call16
+call16:
+ // restore data segment limits to 0xffff
+ movw $0x28, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %ss
+ movw %ax, %fs
+ movw %ax, %gs
+
+ // reset PE bit in CR0
+ movl %cr0, %eax
+ andb $0xfe, %al
+ movl %eax, %cr0
+
+ // far jump to flush CPU queue after transition to real mode
+ ljmpw $0xf000, $1f
+1:
+ // restore IDT to normal real-mode defaults
+ lidt %cs:rmode_IDT_info
+
+ // Setup segment registers
+ xorw %ax, %ax
+ movw %ax, %ds
+ movw %ax, %fs
+ movw %ax, %gs
+ movw $0xf000, %ax
+ movw %ax, %es
+ lea pnp_string, %di
+ movw $ CONFIG_STACK16_SEGMENT , %ax
+ movw %ax, %ss
+ movl %esp, %eax
+ movl $ CONFIG_STACK16_OFFSET , %esp
+
+ // Save info
+ pushl %eax
+ pushl %ebx
+ movl %esp, %ebp
+
+ lcallw %ss:*(%bp)
+
+ // Restore stack and jump back to 32bit mode.
+ popl %eax
+ popl %esp
+
+ // Resume point of rombios32 code - the actual instruction is
+ // altered later in the build process.
+ .globl set_resume32
+set_resume32:
+ mov $0xf0000000, %ebx
+
+ jmp transition32
+
+
+// Protected mode IDT descriptor
+//
+// I just make the limit 0, so the machine will shutdown
+// if an exception occurs during protected mode memory
+// transfers.
+//
+// Set base to f0000 to correspond to beginning of BIOS,
+// in case I actually define an IDT later
+// Set limit to 0
+pmode_IDT_info:
+ .word 0x0000 // limit 15:00
+ .word 0x0000 // base 15:00
+ .byte 0x0f // base 23:16
+
+// Real mode IDT descriptor
+//
+// Set to typical real-mode values.
+// base = 000000
+// limit = 03ff
+rmode_IDT_info:
+ .word 0x03ff // limit 15:00
+ .word 0x0000 // base 15:00
+ .byte 0x00 // base 23:16
+
+rombios32_gdt_48:
+ .word 0x30
+ .word rombios32_gdt
+ .word 0x000f
+
+rombios32_gdt:
+ .word 0, 0, 0, 0
+ .word 0, 0, 0, 0
+ .word 0xffff, 0, 0x9b00, 0x00cf // 32 bit flat code segment (0x10)
+ .word 0xffff, 0, 0x9300, 0x00cf // 32 bit flat data segment (0x18)
+ .word 0xffff, 0, 0x9b0f, 0x0000 // 16 bit code segment base=0xf0000 limit=0xffff
+ .word 0xffff, 0, 0x9300, 0x0000 // 16 bit data segment base=0x0 limit=0xffff
+
+
+ .macro ENTRY cfunc
+ pushal
+ pushw %es
+ pushw %ds
+ movw %ss, %ax
+ movw %ax, %ds
+ mov %esp, %eax
+ call \cfunc
+ popw %ds
+ popw %es
+ popal
+ .endm
+
+ .macro IRQ_ENTRY num
+ .globl entry_\num
+ entry_\num :
+ ENTRY handle_\num
+ iretw
+ .endm
+
+
+ .org 0xe2c3
+ IRQ_ENTRY nmi
+
+ IRQ_ENTRY 13
+ IRQ_ENTRY 19
+ IRQ_ENTRY 12
+ IRQ_ENTRY 11
+ IRQ_ENTRY 76
+ IRQ_ENTRY 18
+ IRQ_ENTRY 1c
+ IRQ_ENTRY 70
+ IRQ_ENTRY 74
+ IRQ_ENTRY 75
+
+ .org 0xe3fe
+ jmp entry_13
+
+ .org 0xe401
+ // XXX - Fixed Disk Parameter Table
+
+ .org 0xe6f2
+ jmp entry_19
+
+ .org 0xe6f5
+.include "out/cbt.proc.16.s"
+ .text
+
+ .org 0xe729
+ // XXX - Baud Rate Generator Table
+
+ .org 0xe739
+ IRQ_ENTRY 14
+
+ .org 0xe82e
+ IRQ_ENTRY 16
+
+ .org 0xe987
+ IRQ_ENTRY 09
+
+ .org 0xec59
+ IRQ_ENTRY 40
+
+ .org 0xef57
+ IRQ_ENTRY 0e
+
+ .org 0xefc7
+ // XXX - Diskette Controller Parameter Table
+
+ .org 0xefd2
+ IRQ_ENTRY 17
+
+ .org 0xf045
+ // XXX int 10
+ iretw
+
+ .org 0xf065
+ IRQ_ENTRY 10
+
+ .org 0xf0a4
+ // XXX int 1D
+ iretw
+
+ .org 0xf841
+ jmp entry_12
+
+ .org 0xf84d
+ jmp entry_11
+
+ .org 0xf859
+ IRQ_ENTRY 15
+
+ .org 0xfa6e
+.include "out/font.proc.16.s"
+ .text
+
+ .org 0xfe6e
+ IRQ_ENTRY 1a
+
+ .org 0xfea5
+ IRQ_ENTRY 08
+
+ .org 0xfef3
+ // XXX - Initial Interrupt Vector Offsets Loaded by POST
+
+ .org 0xff00
+ // XXX - BIOS_COPYRIGHT_STRING
+ .ascii "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
+
+ .org 0xff53
+ .globl dummy_iret_handler
+dummy_iret_handler:
+ iretw
+
+ .org 0xff54
+ IRQ_ENTRY 05
+
+ .org 0xfff0 // Power-up Entry Point
+ ljmpw $0xf000, $post16
+
+ .org 0xfff5
+ // BIOS build date
+ .ascii "06/23/99"
+
+ .org 0xfffe
+ // model byte 0xFC = AT
+ .byte 0xfc
+ .byte 0x00
+
+ .end
diff --git a/src/serial.c b/src/serial.c
new file mode 100644
index 0000000..5541089
--- /dev/null
+++ b/src/serial.c
@@ -0,0 +1,23 @@
+// 16bit code to handle serial and printer services.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "biosvar.h" // struct bregs
+#include "util.h" // debug_enter
+
+// INT 14h Serial Communications Service Entry Point
+void VISIBLE
+handle_14(struct bregs *regs)
+{
+ debug_enter(regs);
+}
+
+// INT17h : Printer Service Entry Point
+void VISIBLE
+handle_17(struct bregs *regs)
+{
+ debug_enter(regs);
+}
diff --git a/src/system.c b/src/system.c
new file mode 100644
index 0000000..3967fc4
--- /dev/null
+++ b/src/system.c
@@ -0,0 +1,529 @@
+// 16bit system callbacks
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "util.h" // irq_restore
+#include "biosvar.h" // CONFIG_BIOS_TABLE
+#include "ioport.h" // inb
+#include "cmos.h" // inb_cmos
+
+#define RET_EUNSUPPORTED 0x86
+
+
+// Use PS2 System Control port A to set A20 enable
+static inline u8
+set_a20(u8 cond)
+{
+ // get current setting first
+ u8 newval, oldval = inb(PORT_A20);
+ if (cond)
+ newval = oldval | 0x02;
+ else
+ newval = oldval & ~0x02;
+ outb(newval, PORT_A20);
+
+ return (newval & 0x02) != 0;
+}
+
+static inline void
+handle_ret(struct bregs *regs, u8 code)
+{
+ regs->ah = code;
+ set_cf(regs, code);
+}
+
+static void
+handle_152400(struct bregs *regs)
+{
+ set_a20(0);
+ handle_ret(regs, 0);
+}
+
+static void
+handle_152401(struct bregs *regs)
+{
+ set_a20(1);
+ handle_ret(regs, 0);
+}
+
+static void
+handle_152402(struct bregs *regs)
+{
+ regs->al = !!(inb(PORT_A20) & 0x20);
+ handle_ret(regs, 0);
+}
+
+static void
+handle_152403(struct bregs *regs)
+{
+ regs->bx = 3;
+ handle_ret(regs, 0);
+}
+
+static void
+handle_1524XX(struct bregs *regs)
+{
+ handle_ret(regs, RET_EUNSUPPORTED);
+}
+
+// removable media eject
+static void
+handle_1552(struct bregs *regs)
+{
+ handle_ret(regs, 0);
+}
+
+// Set Interval requested.
+static void
+handle_158300(struct bregs *regs)
+{
+ if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING) {
+ // Interval already set.
+ DEBUGF("int15: Func 83h, failed, already waiting.\n" );
+ handle_ret(regs, RET_EUNSUPPORTED);
+ }
+ // Interval not already set.
+ SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte.
+ u32 v = (regs->es << 16) | regs->bx;
+ SET_BDA(ptr_user_wait_complete_flag, v);
+ v = (regs->dx << 16) | regs->cx;
+ SET_BDA(user_wait_timeout, v);
+
+ // Unmask IRQ8 so INT70 will get through.
+ u8 irqDisable = inb(PORT_PIC2_DATA);
+ outb(irqDisable & ~PIC2_IRQ8, PORT_PIC2_DATA);
+ // Turn on the Periodic Interrupt timer
+ u8 bRegister = inb_cmos(CMOS_STATUS_B);
+ outb_cmos(CMOS_STATUS_B, bRegister | CSB_EN_ALARM_IRQ);
+
+ set_cf(regs, 0); // XXX - no set ah?
+}
+
+// Clear interval requested
+static void
+handle_158301(struct bregs *regs)
+{
+ SET_BDA(rtc_wait_flag, 0); // Clear status byte
+ // Turn off the Periodic Interrupt timer
+ u8 bRegister = inb_cmos(CMOS_STATUS_B);
+ outb_cmos(CMOS_STATUS_B, bRegister & ~CSB_EN_ALARM_IRQ);
+ set_cf(regs, 0); // XXX - no set ah?
+}
+
+static void
+handle_1583XX(struct bregs *regs)
+{
+ regs->al--;
+ handle_ret(regs, RET_EUNSUPPORTED);
+}
+
+// Sleep for n microseconds. currently using the
+// refresh request port 0x61 bit4, toggling every 15usec
+static void
+usleep(u32 count)
+{
+ count = count / 15;
+ u8 kbd = inb(PORT_KBD_CTRLB);
+ while (count)
+ if ((inb(PORT_KBD_CTRLB) ^ kbd) & KBD_REFRESH)
+ count--;
+}
+
+// Wait for CX:DX microseconds. currently using the
+// refresh request port 0x61 bit4, toggling every 15usec
+static void
+handle_1586(struct bregs *regs)
+{
+ irq_enable();
+ usleep((regs->cx << 16) | regs->dx);
+ irq_disable();
+}
+
+static void
+handle_1587(struct bregs *regs)
+{
+ // +++ should probably have descriptor checks
+ // +++ should have exception handlers
+
+ // turn off interrupts
+ unsigned long flags = irq_save();
+
+ u8 prev_a20_enable = set_a20(1); // enable A20 line
+
+ // 128K max of transfer on 386+ ???
+ // source == destination ???
+
+ // ES:SI points to descriptor table
+ // offset use initially comments
+ // ==============================================
+ // 00..07 Unused zeros Null descriptor
+ // 08..0f GDT zeros filled in by BIOS
+ // 10..17 source ssssssss source of data
+ // 18..1f dest dddddddd destination of data
+ // 20..27 CS zeros filled in by BIOS
+ // 28..2f SS zeros filled in by BIOS
+
+ //es:si
+ //eeee0
+ //0ssss
+ //-----
+
+// check for access rights of source & dest here
+
+ // Initialize GDT descriptor
+ u16 si = regs->si;
+ u16 base15_00 = (regs->es << 4) + si;
+ u16 base23_16 = regs->es >> 12;
+ if (base15_00 < (regs->es<<4))
+ base23_16++;
+ SET_VAR(ES, *(u16*)(si+0x08+0), 47); // limit 15:00 = 6 * 8bytes/descriptor
+ SET_VAR(ES, *(u16*)(si+0x08+2), base15_00);// base 15:00
+ SET_VAR(ES, *(u8 *)(si+0x08+4), base23_16);// base 23:16
+ SET_VAR(ES, *(u8 *)(si+0x08+5), 0x93); // access
+ SET_VAR(ES, *(u16*)(si+0x08+6), 0x0000); // base 31:24/reserved/limit 19:16
+
+ // Initialize CS descriptor
+ SET_VAR(ES, *(u16*)(si+0x20+0), 0xffff);// limit 15:00 = normal 64K limit
+ SET_VAR(ES, *(u16*)(si+0x20+2), 0x0000);// base 15:00
+ SET_VAR(ES, *(u8 *)(si+0x20+4), 0x000f);// base 23:16
+ SET_VAR(ES, *(u8 *)(si+0x20+5), 0x9b); // access
+ SET_VAR(ES, *(u16*)(si+0x20+6), 0x0000);// base 31:24/reserved/limit 19:16
+
+ // Initialize SS descriptor
+ u16 ss = GET_SEG(SS);
+ base15_00 = ss << 4;
+ base23_16 = ss >> 12;
+ SET_VAR(ES, *(u16*)(si+0x28+0), 0xffff); // limit 15:00 = normal 64K limit
+ SET_VAR(ES, *(u16*)(si+0x28+2), base15_00);// base 15:00
+ SET_VAR(ES, *(u8 *)(si+0x28+4), base23_16);// base 23:16
+ SET_VAR(ES, *(u8 *)(si+0x28+5), 0x93); // access
+ SET_VAR(ES, *(u16*)(si+0x28+6), 0x0000); // base 31:24/reserved/limit 19:16
+
+ asm volatile(
+ // Save registers
+ "pushw %%ds\n"
+ "pushw %%es\n"
+ "pushal\n"
+
+ // Load new descriptor tables
+ "lgdt %%es:(%1)\n"
+ "lidt %%cs:pmode_IDT_info\n"
+
+ // set PE bit in CR0
+ "movl %%cr0, %%eax\n"
+ "orb $0x01, %%al\n"
+ "movl %%eax, %%cr0\n"
+
+ // far jump to flush CPU queue after transition to protected mode
+ "ljmpw $0xf000, $1f\n"
+ "1:\n"
+
+ // GDT points to valid descriptor table, now load DS, ES
+ "movw $0x10, %%ax\n" // 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
+ "movw %%ax, %%ds\n"
+ "movw $0x18, %%ax\n" // 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
+ "movw %%ax, %%es\n"
+
+ // move CX words from DS:SI to ES:DI
+ "xorw %%si, %%si\n"
+ "xorw %%di, %%di\n"
+ "cld\n"
+ "rep movsw\n"
+
+ // reset PG bit in CR0 ???
+ "movl %%cr0, %%eax\n"
+ "andb $0xfe, %%al\n"
+ "movl %%eax, %%cr0\n"
+
+ // far jump to flush CPU queue after transition to real mode
+ "ljmpw $0xf000, $2f\n"
+ "2:\n"
+
+ // restore IDT to normal real-mode defaults
+ "lidt %%cs:rmode_IDT_info\n"
+
+ // restore regisers
+ "popal\n"
+ "popw %%es\n"
+ "popw %%ds\n" : : "c" (regs->cx), "r" (si + 8));
+
+ set_a20(prev_a20_enable);
+
+ irq_restore(flags);
+
+ handle_ret(regs, 0);
+}
+
+// Get the amount of extended memory (above 1M)
+static void
+handle_1588(struct bregs *regs)
+{
+ regs->al = inb_cmos(CMOS_EXTMEM_LOW);
+ regs->ah = inb_cmos(CMOS_EXTMEM_HIGH);
+ // According to Ralf Brown's interrupt the limit should be 15M,
+ // but real machines mostly return max. 63M.
+ if (regs->ax > 0xffc0)
+ regs->ax = 0xffc0;
+ set_cf(regs, 0);
+}
+
+// Device busy interrupt. Called by Int 16h when no key available
+static void
+handle_1590(struct bregs *regs)
+{
+}
+
+// Interrupt complete. Called by Int 16h when key becomes available
+static void
+handle_1591(struct bregs *regs)
+{
+}
+
+static void
+handle_15c0(struct bregs *regs)
+{
+ regs->es = SEG_BIOS;
+ regs->bx = (u16)&BIOS_CONFIG_TABLE;
+}
+
+static void
+handle_15c1(struct bregs *regs)
+{
+ regs->es = GET_BDA(ebda_seg);
+ set_cf(regs, 0);
+}
+
+static void
+handle_15e801(struct bregs *regs)
+{
+ // my real system sets ax and bx to 0
+ // this is confirmed by Ralph Brown list
+ // but syslinux v1.48 is known to behave
+ // strangely if ax is set to 0
+ // regs.u.r16.ax = 0;
+ // regs.u.r16.bx = 0;
+
+ // Get the amount of extended memory (above 1M)
+ regs->cl = inb_cmos(CMOS_EXTMEM_LOW);
+ regs->ch = inb_cmos(CMOS_EXTMEM_HIGH);
+
+ // limit to 15M
+ if (regs->cx > 0x3c00)
+ regs->cx = 0x3c00;
+
+ // Get the amount of extended memory above 16M in 64k blocs
+ regs->dl = inb_cmos(CMOS_EXTMEM2_LOW);
+ regs->dh = inb_cmos(CMOS_EXTMEM2_HIGH);
+
+ // Set configured memory equal to extended memory
+ regs->ax = regs->cx;
+ regs->bx = regs->dx;
+
+ set_cf(regs, 0);
+}
+
+#define ACPI_DATA_SIZE 0x00010000L
+
+static void
+set_e820_range(u16 DI, u32 start, u32 end, u16 type)
+{
+ SET_VAR(ES, *(u16*)(DI+0), start);
+ SET_VAR(ES, *(u16*)(DI+2), start >> 16);
+ SET_VAR(ES, *(u16*)(DI+4), 0x00);
+ SET_VAR(ES, *(u16*)(DI+6), 0x00);
+
+ end -= start;
+ SET_VAR(ES, *(u16*)(DI+8), end);
+ SET_VAR(ES, *(u16*)(DI+10), end >> 16);
+ SET_VAR(ES, *(u16*)(DI+12), 0x0000);
+ SET_VAR(ES, *(u16*)(DI+14), 0x0000);
+
+ SET_VAR(ES, *(u16*)(DI+16), type);
+ SET_VAR(ES, *(u16*)(DI+18), 0x0);
+}
+
+// XXX - should create e820 memory map in post and just copy it here.
+static void
+handle_15e820(struct bregs *regs)
+{
+ if (regs->edx != 0x534D4150) {
+ handle_ret(regs, RET_EUNSUPPORTED);
+ return;
+ }
+
+ u32 extended_memory_size = inb_cmos(CMOS_EXTMEM2_HIGH);
+ extended_memory_size <<= 8;
+ extended_memory_size |= inb_cmos(CMOS_EXTMEM2_LOW);
+ extended_memory_size *= 64;
+ // greater than EFF00000???
+ if (extended_memory_size > 0x3bc000)
+ // everything after this is reserved memory until we get to 0x100000000
+ extended_memory_size = 0x3bc000;
+ extended_memory_size *= 1024;
+ extended_memory_size += (16L * 1024 * 1024);
+
+ if (extended_memory_size <= (16L * 1024 * 1024)) {
+ extended_memory_size = inb_cmos(CMOS_EXTMEM_HIGH);
+ extended_memory_size <<= 8;
+ extended_memory_size |= inb_cmos(CMOS_EXTMEM_LOW);
+ extended_memory_size *= 1024;
+ }
+
+ switch (regs->bx) {
+ case 0:
+ set_e820_range(regs->di, 0x0000000L, 0x0009fc00L, 1);
+ regs->ebx = 1;
+ regs->eax = 0x534D4150;
+ regs->ecx = 0x14;
+ set_cf(regs, 0);
+ break;
+ case 1:
+ set_e820_range(regs->di, 0x0009fc00L, 0x000a0000L, 2);
+ regs->ebx = 2;
+ regs->eax = 0x534D4150;
+ regs->ecx = 0x14;
+ set_cf(regs, 0);
+ break;
+ case 2:
+ set_e820_range(regs->di, 0x000e8000L, 0x00100000L, 2);
+ regs->ebx = 3;
+ regs->eax = 0x534D4150;
+ regs->ecx = 0x14;
+ set_cf(regs, 0);
+ break;
+ case 3:
+ set_e820_range(regs->di, 0x00100000L,
+ extended_memory_size - ACPI_DATA_SIZE, 1);
+ regs->ebx = 4;
+ regs->eax = 0x534D4150;
+ regs->ecx = 0x14;
+ set_cf(regs, 0);
+ break;
+ case 4:
+ set_e820_range(regs->di,
+ extended_memory_size - ACPI_DATA_SIZE,
+ extended_memory_size, 3); // ACPI RAM
+ regs->ebx = 5;
+ regs->eax = 0x534D4150;
+ regs->ecx = 0x14;
+ set_cf(regs, 0);
+ break;
+ case 5:
+ /* 256KB BIOS area at the end of 4 GB */
+ set_e820_range(regs->di, 0xfffc0000L, 0x00000000L, 2);
+ regs->ebx = 0;
+ regs->eax = 0x534D4150;
+ regs->ecx = 0x14;
+ set_cf(regs, 0);
+ break;
+ default: /* AX=E820, DX=534D4150, BX unrecognized */
+ handle_ret(regs, RET_EUNSUPPORTED);
+ }
+}
+
+static void
+handle_15e8XX(struct bregs *regs)
+{
+ regs->al--;
+ handle_ret(regs, RET_EUNSUPPORTED);
+}
+
+static void
+handle_15XX(struct bregs *regs)
+{
+ regs->al--;
+ handle_ret(regs, RET_EUNSUPPORTED);
+}
+
+// INT 15h System Services Entry Point
+void VISIBLE
+handle_15(struct bregs *regs)
+{
+ debug_enter(regs);
+ switch (regs->ah) {
+ case 0x24:
+ switch (regs->al) {
+ case 0x00: handle_152400(regs); break;
+ case 0x01: handle_152401(regs); break;
+ case 0x02: handle_152402(regs); break;
+ case 0x03: handle_152403(regs); break;
+ default: handle_1524XX(regs); break;
+ }
+ break;
+ case 0x52: handle_1552(regs); break;
+ case 0x83:
+ switch (regs->al) {
+ case 0x00: handle_158300(regs); break;
+ case 0x01: handle_158301(regs); break;
+ default: handle_1583XX(regs); break;
+ }
+ break;
+ case 0x86: handle_1586(regs); break;
+ case 0x87: handle_1587(regs); break;
+ case 0x88: handle_1588(regs); break;
+ case 0x90: handle_1590(regs); break;
+ case 0x91: handle_1591(regs); break;
+ case 0xc0: handle_15c0(regs); break;
+ case 0xc1: handle_15c1(regs); break;
+ case 0xc2: handle_15c2(regs); break;
+ case 0xe8:
+ switch (regs->al) {
+ case 0x01: handle_15e801(regs); break;
+ case 0x20: handle_15e820(regs); break;
+ default: handle_15e8XX(regs); break;
+ }
+ break;
+ default: handle_15XX(regs); break;
+ }
+ debug_exit(regs);
+}
+
+// INT 12h Memory Size Service Entry Point
+void VISIBLE
+handle_12(struct bregs *regs)
+{
+ debug_enter(regs);
+ regs->ax = GET_BDA(mem_size_kb);
+ debug_exit(regs);
+}
+
+// INT 11h Equipment List Service Entry Point
+void VISIBLE
+handle_11(struct bregs *regs)
+{
+ debug_enter(regs);
+ regs->ax = GET_BDA(equipment_list_flags);
+ debug_exit(regs);
+}
+
+// INT 05h Print Screen Service Entry Point
+void VISIBLE
+handle_05(struct bregs *regs)
+{
+ debug_enter(regs);
+}
+
+// INT 10h Video Support Service Entry Point
+void VISIBLE
+handle_10(struct bregs *regs)
+{
+ debug_enter(regs);
+ // dont do anything, since the VGA BIOS handles int10h requests
+}
+
+void VISIBLE
+handle_nmi(struct bregs *regs)
+{
+ debug_enter(regs);
+ // XXX
+}
+
+// INT 75 - IRQ13 - MATH COPROCESSOR EXCEPTION
+void VISIBLE
+handle_75(struct bregs *regs)
+{
+ debug_enter(regs);
+}
diff --git a/src/types.h b/src/types.h
new file mode 100644
index 0000000..ea245bf
--- /dev/null
+++ b/src/types.h
@@ -0,0 +1,21 @@
+// Basic type definitions for X86 cpus.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+#ifndef __TYPES_H
+#define __TYPES_H
+
+typedef unsigned char u8;
+typedef signed char s8;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned int u32;
+typedef signed int s32;
+typedef u32 size_t;
+
+#define VISIBLE __attribute__((externally_visible))
+
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+#endif // types.h
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..0870ad5
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,55 @@
+// Basic x86 asm functions and function defs.
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "ioport.h" // outb
+
+static inline void irq_disable(void) {
+ asm volatile("cli": : :"memory");
+}
+
+static inline void irq_enable(void) {
+ asm volatile("sti": : :"memory");
+}
+
+static inline unsigned long irq_save(void)
+{
+ unsigned long flags;
+ asm volatile("pushfl ; popl %0" : "=g" (flags));
+ irq_disable();
+ return flags;
+}
+
+static inline void irq_restore(unsigned long flags)
+{
+ asm volatile("pushl %0 ; popfl" : : "g" (flags) : "memory", "cc");
+}
+
+#define DEBUGF(fmt, args...)
+#define BX_PANIC(fmt, args...)
+#define BX_INFO(fmt, args...)
+
+static inline void
+memset(void *s, int c, size_t n)
+{
+ while (n)
+ ((char *)s)[n--] = c;
+}
+
+// output.c
+void bprintf(u16 action, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+struct bregs;
+void __debug_enter(const char *fname, struct bregs *regs);
+void __debug_exit(const char *fname, struct bregs *regs);
+#define debug_enter(regs) \
+ __debug_enter(__func__, regs)
+#define debug_exit(regs) \
+ __debug_exit(__func__, regs)
+#define printf(fmt, args...) \
+ bprintf(0, fmt , ##args )
+
+// kbd.c
+void handle_15c2(struct bregs *regs);