blob: 0f13b5397516b0b985ae61c1aeed1fb242691aea [file] [log] [blame]
/////////////////////////////////////////////////////////////////////////
// $Id$
/////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2002 MandrakeSoft S.A.
//
// MandrakeSoft S.A.
// 43, rue d'Aboukir
// 75002 Paris - France
// http://www.linux-mandrake.com/
// http://www.mandrakesoft.com/
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
// ROM BIOS for use with Bochs/Plex86/QEMU emulation environment
// ROM BIOS compatability entry points:
// ===================================
// $e05b ; POST Entry Point
// $e2c3 ; NMI Handler Entry Point
// $e3fe ; INT 13h Fixed Disk Services Entry Point
// $e401 ; Fixed Disk Parameter Table
// $e6f2 ; INT 19h Boot Load Service Entry Point
// $e6f5 ; Configuration Data Table
// $e729 ; Baud Rate Generator Table
// $e739 ; INT 14h Serial Communications Service Entry Point
// $e82e ; INT 16h Keyboard Service Entry Point
// $e987 ; INT 09h Keyboard Service Entry Point
// $ec59 ; INT 13h Diskette Service Entry Point
// $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
// $efc7 ; Diskette Controller Parameter Table
// $efd2 ; INT 17h Printer Service Entry Point
// $f045 ; INT 10 Functions 0-Fh Entry Point
// $f065 ; INT 10h Video Support Service Entry Point
// $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
// $f841 ; INT 12h Memory Size Service Entry Point
// $f84d ; INT 11h Equipment List Service Entry Point
// $f859 ; INT 15h System Services Entry Point
// $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
// $fe6e ; INT 1Ah Time-of-day Service Entry Point
// $fea5 ; INT 08h System Timer ISR Entry Point
// $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
// $ff53 ; IRET Instruction for Dummy Interrupt Handler
// $ff54 ; INT 05h Print Screen Service Entry Point
// $fff0 ; Power-up Entry Point
// $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
// $fffe ; System Model ID
// NOTES for ATA/ATAPI driver (cbbochs@free.fr)
// Features
// - supports up to 4 ATA interfaces
// - device/geometry detection
// - 16bits/32bits device access
// - pchs/lba access
// - datain/dataout/packet command support
//
// NOTES for El-Torito Boot (cbbochs@free.fr)
// - CD-ROM booting is only available if ATA/ATAPI Driver is available
// - Current code is only able to boot mono-session cds
// - Current code can not boot and emulate a hard-disk
// the bios will panic otherwise
// - Current code also use memory in EBDA segement.
// - I used cmos byte 0x3D to store extended information on boot-device
// - Code has to be modified modified to handle multiple cdrom drives
// - Here are the cdrom boot failure codes:
// 1 : no atapi device found
// 2 : no atapi cdrom found
// 3 : can not read cd - BRVD
// 4 : cd is not eltorito (BRVD)
// 5 : cd is not eltorito (ISO TAG)
// 6 : cd is not eltorito (ELTORITO TAG)
// 7 : can not read cd - boot catalog
// 8 : boot catalog : bad header
// 9 : boot catalog : bad platform
// 10 : boot catalog : bad signature
// 11 : boot catalog : bootable flag not set
// 12 : can not read cd - boot image
//
// ATA driver
// - EBDA segment.
// I used memory starting at 0x121 in the segment
// - the translation policy is defined in cmos regs 0x39 & 0x3a
//
// TODO :
//
// int74
// - needs to be reworked. Uses direct [bp] offsets. (?)
//
// int13:
// - f04 (verify sectors) isn't complete (?)
// - f02/03/04 should set current cyl,etc in BDA (?)
// - rewrite int13_relocated & clean up int13 entry code
//
// NOTES:
// - NMI access (bit7 of addr written to 70h)
//
// ATA driver
// - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
// - could send the multiple-sector read/write commands
//
// El-Torito
// - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
// - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
// - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
// - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
// This is ok. But DL should be reincremented afterwards.
// - Fix all "FIXME ElTorito Various"
// - should be able to boot any cdrom instead of the first one
//
// BCC Bug: find a generic way to handle the bug of #asm after an "if" (fixed in 0.16.7)
#include "rombios.h"
#define DEBUG_ATA 0
#define DEBUG_INT13_HD 0
#define DEBUG_INT13_CD 0
#define DEBUG_INT13_ET 0
#define DEBUG_INT13_FL 0
#define DEBUG_INT15 0
#define DEBUG_INT16 0
#define DEBUG_INT1A 0
#define DEBUG_INT74 0
#define DEBUG_APM 0
#define BX_CPU 3
#define BX_USE_PS2_MOUSE 1
#define BX_CALL_INT15_4F 1
#define BX_USE_EBDA 1
#define BX_SUPPORT_FLOPPY 1
#define BX_FLOPPY_ON_CNT 37 /* 2 seconds */
#define BX_PCIBIOS 1
#define BX_APM 1
#define BX_USE_ATADRV 1
#define BX_ELTORITO_BOOT 1
#define BX_MAX_ATA_INTERFACES 4
#define BX_MAX_ATA_DEVICES (BX_MAX_ATA_INTERFACES*2)
#define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
#define BX_DEBUG_SERIAL 0 /* output to COM1 */
/* model byte 0xFC = AT */
#define SYS_MODEL_ID 0xFC
#define SYS_SUBMODEL_ID 0x00
#define BIOS_REVISION 1
#define BIOS_CONFIG_TABLE 0xe6f5
#ifndef BIOS_BUILD_DATE
# define BIOS_BUILD_DATE "06/23/99"
#endif
// 1K of base memory used for Extended Bios Data Area (EBDA)
// EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
#define EBDA_SEG 0x9FC0
#define EBDA_SIZE 1 // In KiB
#define BASE_MEM_IN_K (640 - EBDA_SIZE)
/* 256 bytes at 0x9ff00 -- 0x9ffff is used for the IPL boot table. */
#define IPL_SEG 0x9ff0
#define IPL_TABLE_OFFSET 0x0000
#define IPL_TABLE_ENTRIES 8
#define IPL_COUNT_OFFSET 0x0080 /* u16: number of valid table entries */
#define IPL_SEQUENCE_OFFSET 0x0082 /* u16: next boot device */
#define IPL_BOOTFIRST_OFFSET 0x0084 /* u16: user selected device */
#define IPL_SIZE 0xff
#define IPL_TYPE_FLOPPY 0x01
#define IPL_TYPE_HARDDISK 0x02
#define IPL_TYPE_CDROM 0x03
#define IPL_TYPE_BEV 0x80
// Sanity Checks
#if BX_USE_ATADRV && BX_CPU<3
# error The ATA/ATAPI Driver can only to be used with a 386+ cpu
#endif
#if BX_USE_ATADRV && !BX_USE_EBDA
# error ATA/ATAPI Driver can only be used if EBDA is available
#endif
#if BX_ELTORITO_BOOT && !BX_USE_ATADRV
# error El-Torito Boot can only be use if ATA/ATAPI Driver is available
#endif
#if BX_PCIBIOS && BX_CPU<3
# error PCI BIOS can only be used with 386+ cpu
#endif
#if BX_APM && BX_CPU<3
# error APM BIOS can only be used with 386+ cpu
#endif
// define this if you want to make PCIBIOS working on a specific bridge only
// undef enables PCIBIOS when at least one PCI device is found
// i440FX is emulated by Bochs and QEMU
#define PCI_FIXED_HOST_BRIDGE 0x12378086 ;; i440FX PCI bridge
// #20 is dec 20
// #$20 is hex 20 = 32
// #0x20 is hex 20 = 32
// LDA #$20
// JSR $E820
// LDD .i,S
// JSR $C682
// mov al, #$20
// all hex literals should be prefixed with '0x'
// grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
// no mov SEG-REG, #value, must mov register into seg-reg
// grep -i "mov[ ]*.s" rombios.c
// This is for compiling with gcc2 and gcc3
#define ASM_START #asm
#define ASM_END #endasm
ASM_START
.rom
.org 0x0000
#if BX_CPU >= 3
use16 386
#else
use16 286
#endif
MACRO HALT
;; the HALT macro is called with the line number of the HALT call.
;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex
;; to print a BX_PANIC message. This will normally halt the simulation
;; with a message such as "BIOS panic at rombios.c, line 4091".
;; However, users can choose to make panics non-fatal and continue.
#if BX_VIRTUAL_PORTS
mov dx,#PANIC_PORT
mov ax,#?1
out dx,ax
#else
mov dx,#0x80
mov ax,#?1
out dx,al
#endif
MEND
MACRO JMP_AP
db 0xea
dw ?2
dw ?1
MEND
MACRO SET_INT_VECTOR
mov ax, ?3
mov ?1*4, ax
mov ax, ?2
mov ?1*4+2, ax
MEND
ASM_END
typedef unsigned char Bit8u;
typedef unsigned short Bit16u;
typedef unsigned short bx_bool;
typedef unsigned long Bit32u;
void memsetb(seg,offset,value,count);
void memcpyb(dseg,doffset,sseg,soffset,count);
void memcpyd(dseg,doffset,sseg,soffset,count);
// memset of count bytes
void
memsetb(seg,offset,value,count)
Bit16u seg;
Bit16u offset;
Bit16u value;
Bit16u count;
{
ASM_START
push bp
mov bp, sp
push ax
push cx
push es
push di
mov cx, 10[bp] ; count
test cx, cx
je memsetb_end
mov ax, 4[bp] ; segment
mov es, ax
mov ax, 6[bp] ; offset
mov di, ax
mov al, 8[bp] ; value
cld
rep
stosb
memsetb_end:
pop di
pop es
pop cx
pop ax
pop bp
ASM_END
}
// memcpy of count bytes
void
memcpyb(dseg,doffset,sseg,soffset,count)
Bit16u dseg;
Bit16u doffset;
Bit16u sseg;
Bit16u soffset;
Bit16u count;
{
ASM_START
push bp
mov bp, sp
push ax
push cx
push es
push di
push ds
push si
mov cx, 12[bp] ; count
test cx, cx
je memcpyb_end
mov ax, 4[bp] ; dsegment
mov es, ax
mov ax, 6[bp] ; doffset
mov di, ax
mov ax, 8[bp] ; ssegment
mov ds, ax
mov ax, 10[bp] ; soffset
mov si, ax
cld
rep
movsb
memcpyb_end:
pop si
pop ds
pop di
pop es
pop cx
pop ax
pop bp
ASM_END
}
// memcpy of count dword
void
memcpyd(dseg,doffset,sseg,soffset,count)
Bit16u dseg;
Bit16u doffset;
Bit16u sseg;
Bit16u soffset;
Bit16u count;
{
ASM_START
push bp
mov bp, sp
push ax
push cx
push es
push di
push ds
push si
mov cx, 12[bp] ; count
test cx, cx
je memcpyd_end
mov ax, 4[bp] ; dsegment
mov es, ax
mov ax, 6[bp] ; doffset
mov di, ax
mov ax, 8[bp] ; ssegment
mov ds, ax
mov ax, 10[bp] ; soffset
mov si, ax
cld
rep
movsd
memcpyd_end:
pop si
pop ds
pop di
pop es
pop cx
pop ax
pop bp
ASM_END
}
// read_dword and write_dword functions
static Bit32u read_dword();
static void write_dword();
Bit32u
read_dword(seg, offset)
Bit16u seg;
Bit16u offset;
{
ASM_START
push bp
mov bp, sp
push bx
push ds
mov ax, 4[bp] ; segment
mov ds, ax
mov bx, 6[bp] ; offset
mov ax, [bx]
add bx, #2
mov dx, [bx]
;; ax = return value (word)
;; dx = return value (word)
pop ds
pop bx
pop bp
ASM_END
}
void
write_dword(seg, offset, data)
Bit16u seg;
Bit16u offset;
Bit32u data;
{
ASM_START
push bp
mov bp, sp
push ax
push bx
push ds
mov ax, 4[bp] ; segment
mov ds, ax
mov bx, 6[bp] ; offset
mov ax, 8[bp] ; data word
mov [bx], ax ; write data word
add bx, #2
mov ax, 10[bp] ; data word
mov [bx], ax ; write data word
pop ds
pop bx
pop ax
pop bp
ASM_END
}
// Bit32u (unsigned long) and long helper functions
ASM_START
;; and function
landl:
landul:
SEG SS
and ax,[di]
SEG SS
and bx,2[di]
ret
;; add function
laddl:
laddul:
SEG SS
add ax,[di]
SEG SS
adc bx,2[di]
ret
;; cmp function
lcmpl:
lcmpul:
and eax, #0x0000FFFF
shl ebx, #16
or eax, ebx
shr ebx, #16
SEG SS
cmp eax, dword ptr [di]
ret
;; sub function
lsubl:
lsubul:
SEG SS
sub ax,[di]
SEG SS
sbb bx,2[di]
ret
;; mul function
lmull:
lmulul:
and eax, #0x0000FFFF
shl ebx, #16
or eax, ebx
SEG SS
mul eax, dword ptr [di]
mov ebx, eax
shr ebx, #16
ret
;; dec function
ldecl:
ldecul:
SEG SS
dec dword ptr [bx]
ret
;; or function
lorl:
lorul:
SEG SS
or ax,[di]
SEG SS
or bx,2[di]
ret
;; inc function
lincl:
lincul:
SEG SS
inc dword ptr [bx]
ret
;; tst function
ltstl:
ltstul:
and eax, #0x0000FFFF
shl ebx, #16
or eax, ebx
shr ebx, #16
test eax, eax
ret
;; sr function
lsrul:
mov cx,di
jcxz lsr_exit
and eax, #0x0000FFFF
shl ebx, #16
or eax, ebx
lsr_loop:
shr eax, #1
loop lsr_loop
mov ebx, eax
shr ebx, #16
lsr_exit:
ret
;; sl function
lsll:
lslul:
mov cx,di
jcxz lsl_exit
and eax, #0x0000FFFF
shl ebx, #16
or eax, ebx
lsl_loop:
shl eax, #1
loop lsl_loop
mov ebx, eax
shr ebx, #16
lsl_exit:
ret
idiv_:
cwd
idiv bx
ret
idiv_u:
xor dx,dx
div bx
ret
ldivul:
and eax, #0x0000FFFF
shl ebx, #16
or eax, ebx
xor edx, edx
SEG SS
mov bx, 2[di]
shl ebx, #16
SEG SS
mov bx, [di]
div ebx
mov ebx, eax
shr ebx, #16
ret
ASM_END
// for access to RAM area which is used by interrupt vectors
// and BIOS Data Area
typedef struct {
unsigned char filler1[0x400];
unsigned char filler2[0x6c];
Bit16u ticks_low;
Bit16u ticks_high;
Bit8u midnight_flag;
} bios_data_t;
#define BiosData ((bios_data_t *) 0)
#if BX_USE_ATADRV
typedef struct {
Bit16u heads; // # heads
Bit16u cylinders; // # cylinders
Bit16u spt; // # sectors / track
} chs_t;
// DPTE definition
typedef struct {
Bit16u iobase1;
Bit16u iobase2;
Bit8u prefix;
Bit8u unused;
Bit8u irq;
Bit8u blkcount;
Bit8u dma;
Bit8u pio;
Bit16u options;
Bit16u reserved;
Bit8u revision;
Bit8u checksum;
} dpte_t;
typedef struct {
Bit8u iface; // ISA or PCI
Bit16u iobase1; // IO Base 1
Bit16u iobase2; // IO Base 2
Bit8u irq; // IRQ
} ata_channel_t;
typedef struct {
Bit8u type; // Detected type of ata (ata/atapi/none/unknown)
Bit8u device; // Detected type of attached devices (hd/cd/none)
Bit8u removable; // Removable device flag
Bit8u lock; // Locks for removable devices
Bit8u mode; // transfer mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
Bit16u blksize; // block size
Bit8u translation; // type of translation
chs_t lchs; // Logical CHS
chs_t pchs; // Physical CHS
Bit32u sectors_low; // Total sectors count
Bit32u sectors_high;
} ata_device_t;
typedef struct {
// ATA channels info
ata_channel_t channels[BX_MAX_ATA_INTERFACES];
// ATA devices info
ata_device_t devices[BX_MAX_ATA_DEVICES];
//
// map between (bios hd id - 0x80) and ata channels
Bit8u hdcount, hdidmap[BX_MAX_ATA_DEVICES];
// map between (bios cd id - 0xE0) and ata channels
Bit8u cdcount, cdidmap[BX_MAX_ATA_DEVICES];
// Buffer for DPTE table
dpte_t dpte;
// Count of transferred sectors and bytes
Bit16u trsfsectors;
Bit32u trsfbytes;
} ata_t;
#if BX_ELTORITO_BOOT
// ElTorito Device Emulation data
typedef struct {
Bit8u active;
Bit8u media;
Bit8u emulated_drive;
Bit8u controller_index;
Bit16u device_spec;
Bit32u ilba;
Bit16u buffer_segment;
Bit16u load_segment;
Bit16u sector_count;
// Virtual device
chs_t vdevice;
} cdemu_t;
#endif // BX_ELTORITO_BOOT
// for access to EBDA area
// The EBDA structure should conform to
// http://www.frontiernet.net/~fys/rombios.htm document
// I made the ata and cdemu structs begin at 0x121 in the EBDA seg
// EBDA must be at most 768 bytes; it lives at EBDA_SEG, and the boot
// device tables are at IPL_SEG
typedef struct {
unsigned char filler1[0x3D];
// FDPT - Can be splitted in data members if needed
unsigned char fdpt0[0x10];
unsigned char fdpt1[0x10];
unsigned char filler2[0xC4];
// ATA Driver data
ata_t ata;
#if BX_ELTORITO_BOOT
// El Torito Emulation data
cdemu_t cdemu;
#endif // BX_ELTORITO_BOOT
} ebda_data_t;
#define EbdaData ((ebda_data_t *) 0)
// for access to the int13ext structure
typedef struct {
Bit8u size;
Bit8u reserved;
Bit16u count;
Bit16u offset;
Bit16u segment;
Bit32u lba1;
Bit32u lba2;
} int13ext_t;
#define Int13Ext ((int13ext_t *) 0)
// Disk Physical Table definition
typedef struct {
Bit16u size;
Bit16u infos;
Bit32u cylinders;
Bit32u heads;
Bit32u spt;
Bit32u sector_count1;
Bit32u sector_count2;
Bit16u blksize;
Bit16u dpte_offset;
Bit16u dpte_segment;
Bit16u key;
Bit8u dpi_length;
Bit8u reserved1;
Bit16u reserved2;
Bit8u host_bus[4];
Bit8u iface_type[8];
Bit8u iface_path[8];
Bit8u device_path[8];
Bit8u reserved3;
Bit8u checksum;
} dpt_t;
#define Int13DPT ((dpt_t *) 0)
#endif // BX_USE_ATADRV
typedef struct {
union {
struct {
Bit16u di, si, bp, sp;
Bit16u bx, dx, cx, ax;
} r16;
struct {
Bit16u filler[4];
Bit8u bl, bh, dl, dh, cl, ch, al, ah;
} r8;
} u;
} pusha_regs_t;
typedef struct {
union {
struct {
Bit32u edi, esi, ebp, esp;
Bit32u ebx, edx, ecx, eax;
} r32;
struct {
Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
} r16;
struct {
Bit32u filler[4];
Bit8u bl, bh;
Bit16u filler1;
Bit8u dl, dh;
Bit16u filler2;
Bit8u cl, ch;
Bit16u filler3;
Bit8u al, ah;
Bit16u filler4;
} r8;
} u;
} pushad_regs_t;
typedef struct {
union {
struct {
Bit16u flags;
} r16;
struct {
Bit8u flagsl;
Bit8u flagsh;
} r8;
} u;
} flags_t;
#define SetCF(x) x.u.r8.flagsl |= 0x01
#define SetZF(x) x.u.r8.flagsl |= 0x40
#define ClearCF(x) x.u.r8.flagsl &= 0xfe
#define ClearZF(x) x.u.r8.flagsl &= 0xbf
#define GetCF(x) (x.u.r8.flagsl & 0x01)
typedef struct {
Bit16u ip;
Bit16u cs;
flags_t flags;
} iret_addr_t;
typedef struct {
Bit16u type;
Bit16u flags;
Bit32u vector;
Bit32u description;
Bit32u reserved;
} ipl_entry_t;
static Bit8u inb();
static Bit8u inb_cmos();
static void outb();
static void outb_cmos();
static Bit16u inw();
static void outw();
static void init_rtc();
static bx_bool rtc_updating();
static Bit8u read_byte();
static Bit16u read_word();
static void write_byte();
static void write_word();
static void bios_printf();
static Bit8u inhibit_mouse_int_and_events();
static void enable_mouse_int_and_events();
static Bit8u send_to_mouse_ctrl();
static Bit8u get_mouse_data();
static void set_kbd_command_byte();
static void int09_function();
static void int13_harddisk();
static void int13_cdrom();
static void int13_cdemu();
static void int13_eltorito();
static void int13_diskette_function();
static void int14_function();
static void int15_function();
static void int16_function();
static void int17_function();
static void int19_function();
static void int1a_function();
static void int70_function();
static void int74_function();
static Bit16u get_CS();
static Bit16u get_SS();
static unsigned int enqueue_key();
static unsigned int dequeue_key();
static void get_hd_geometry();
static void set_diskette_ret_status();
static void set_diskette_current_cyl();
static void determine_floppy_media();
static bx_bool floppy_drive_exists();
static bx_bool floppy_drive_recal();
static bx_bool floppy_media_known();
static bx_bool floppy_media_sense();
static bx_bool set_enable_a20();
static void debugger_on();
static void debugger_off();
static void keyboard_init();
static void keyboard_panic();
static void shutdown_status_panic();
static void nmi_handler_msg();
static void delay_ticks();
static void delay_ticks_and_check_for_keystroke();
static void interactive_bootkey();
static void print_bios_banner();
static void print_boot_device();
static void print_boot_failure();
static void print_cdromboot_failure();
# if BX_USE_ATADRV
// ATA / ATAPI driver
void ata_init();
void ata_detect();
void ata_reset();
Bit16u ata_cmd_non_data();
Bit16u ata_cmd_data_in();
Bit16u ata_cmd_data_out();
Bit16u ata_cmd_packet();
Bit16u atapi_get_sense();
Bit16u atapi_is_ready();
Bit16u atapi_is_cdrom();
#endif // BX_USE_ATADRV
#if BX_ELTORITO_BOOT
void cdemu_init();
Bit8u cdemu_isactive();
Bit8u cdemu_emulated_drive();
Bit16u cdrom_boot();
#endif // BX_ELTORITO_BOOT
static char bios_cvs_version_string[] = "$Revision$ $Date$";
#define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
#if DEBUG_ATA
# define BX_DEBUG_ATA(a...) BX_DEBUG(a)
#else
# define BX_DEBUG_ATA(a...)
#endif
#if DEBUG_INT13_HD
# define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
#else
# define BX_DEBUG_INT13_HD(a...)
#endif
#if DEBUG_INT13_CD
# define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
#else
# define BX_DEBUG_INT13_CD(a...)
#endif
#if DEBUG_INT13_ET
# define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
#else
# define BX_DEBUG_INT13_ET(a...)
#endif
#if DEBUG_INT13_FL
# define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
#else
# define BX_DEBUG_INT13_FL(a...)
#endif
#if DEBUG_INT15
# define BX_DEBUG_INT15(a...) BX_DEBUG(a)
#else
# define BX_DEBUG_INT15(a...)
#endif
#if DEBUG_INT16
# define BX_DEBUG_INT16(a...) BX_DEBUG(a)
#else
# define BX_DEBUG_INT16(a...)
#endif
#if DEBUG_INT1A
# define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
#else
# define BX_DEBUG_INT1A(a...)
#endif
#if DEBUG_INT74
# define BX_DEBUG_INT74(a...) BX_DEBUG(a)
#else
# define BX_DEBUG_INT74(a...)
#endif
#define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
#define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
#define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
#define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
#define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
#define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
#define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
#define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
#define GET_AL() ( AX & 0x00ff )
#define GET_BL() ( BX & 0x00ff )
#define GET_CL() ( CX & 0x00ff )
#define GET_DL() ( DX & 0x00ff )
#define GET_AH() ( AX >> 8 )
#define GET_BH() ( BX >> 8 )
#define GET_CH() ( CX >> 8 )
#define GET_DH() ( DX >> 8 )
#define GET_ELDL() ( ELDX & 0x00ff )
#define GET_ELDH() ( ELDX >> 8 )
#define SET_CF() FLAGS |= 0x0001
#define CLEAR_CF() FLAGS &= 0xfffe
#define GET_CF() (FLAGS & 0x0001)
#define SET_ZF() FLAGS |= 0x0040
#define CLEAR_ZF() FLAGS &= 0xffbf
#define GET_ZF() (FLAGS & 0x0040)
#define UNSUPPORTED_FUNCTION 0x86
#define none 0
#define MAX_SCAN_CODE 0x58
static struct {
Bit16u normal;
Bit16u shift;
Bit16u control;
Bit16u alt;
Bit8u lock_flags;
} scan_to_scanascii[MAX_SCAN_CODE + 1] = {
{ none, none, none, none, none },
{ 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
{ 0x0231, 0x0221, none, 0x7800, none }, /* 1! */
{ 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
{ 0x0433, 0x0423, none, 0x7a00, none }, /* 3# */
{ 0x0534, 0x0524, none, 0x7b00, none }, /* 4$ */
{ 0x0635, 0x0625, none, 0x7c00, none }, /* 5% */
{ 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
{ 0x0837, 0x0826, none, 0x7e00, none }, /* 7& */
{ 0x0938, 0x092a, none, 0x7f00, none }, /* 8* */
{ 0x0a39, 0x0a28, none, 0x8000, none }, /* 9( */
{ 0x0b30, 0x0b29, none, 0x8100, none }, /* 0) */
{ 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
{ 0x0d3d, 0x0d2b, none, 0x8300, none }, /* =+ */
{ 0x0e08, 0x0e08, 0x0e7f, none, none }, /* backspace */
{ 0x0f09, 0x0f00, none, none, none }, /* tab */
{ 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
{ 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
{ 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
{ 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
{ 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
{ 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
{ 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
{ 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
{ 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
{ 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
{ 0x1a5b, 0x1a7b, 0x1a1b, none, none }, /* [{ */
{ 0x1b5d, 0x1b7d, 0x1b1d, none, none }, /* ]} */
{ 0x1c0d, 0x1c0d, 0x1c0a, none, none }, /* Enter */
{ none, none, none, none, none }, /* L Ctrl */
{ 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
{ 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
{ 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
{ 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
{ 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
{ 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
{ 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
{ 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
{ 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
{ 0x273b, 0x273a, none, none, none }, /* ;: */
{ 0x2827, 0x2822, none, none, none }, /* '" */
{ 0x2960, 0x297e, none, none, none }, /* `~ */
{ none, none, none, none, none }, /* L shift */
{ 0x2b5c, 0x2b7c, 0x2b1c, none, none }, /* |\ */
{ 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
{ 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
{ 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
{ 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
{ 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
{ 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
{ 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
{ 0x332c, 0x333c, none, none, none }, /* ,< */
{ 0x342e, 0x343e, none, none, none }, /* .> */
{ 0x352f, 0x353f, none, none, none }, /* /? */
{ none, none, none, none, none }, /* R Shift */
{ 0x372a, 0x372a, none, none, none }, /* * */
{ none, none, none, none, none }, /* L Alt */
{ 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
{ none, none, none, none, none }, /* caps lock */
{ 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
{ 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
{ 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
{ 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
{ 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
{ 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
{ 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
{ 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
{ 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
{ 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
{ none, none, none, none, none }, /* Num Lock */
{ none, none, none, none, none }, /* Scroll Lock */
{ 0x4700, 0x4737, 0x7700, none, 0x20 }, /* 7 Home */
{ 0x4800, 0x4838, none, none, 0x20 }, /* 8 UP */
{ 0x4900, 0x4939, 0x8400, none, 0x20 }, /* 9 PgUp */
{ 0x4a2d, 0x4a2d, none, none, none }, /* - */
{ 0x4b00, 0x4b34, 0x7300, none, 0x20 }, /* 4 Left */
{ 0x4c00, 0x4c35, none, none, 0x20 }, /* 5 */
{ 0x4d00, 0x4d36, 0x7400, none, 0x20 }, /* 6 Right */
{ 0x4e2b, 0x4e2b, none, none, none }, /* + */
{ 0x4f00, 0x4f31, 0x7500, none, 0x20 }, /* 1 End */
{ 0x5000, 0x5032, none, none, 0x20 }, /* 2 Down */
{ 0x5100, 0x5133, 0x7600, none, 0x20 }, /* 3 PgDn */
{ 0x5200, 0x5230, none, none, 0x20 }, /* 0 Ins */
{ 0x5300, 0x532e, none, none, 0x20 }, /* Del */
{ none, none, none, none, none },
{ none, none, none, none, none },
{ 0x565c, 0x567c, none, none, none }, /* \| */
{ 0x8500, 0x8700, 0x8900, 0x8b00, none }, /* F11 */
{ 0x8600, 0x8800, 0x8a00, 0x8c00, none }, /* F12 */
};
Bit8u
inb(port)
Bit16u port;
{
ASM_START
push bp
mov bp, sp
push dx
mov dx, 4[bp]
in al, dx
pop dx
pop bp
ASM_END
}
#if BX_USE_ATADRV
Bit16u
inw(port)
Bit16u port;
{
ASM_START
push bp
mov bp, sp
push dx
mov dx, 4[bp]
in ax, dx
pop dx
pop bp
ASM_END
}
#endif
void
outb(port, val)
Bit16u port;
Bit8u val;
{
ASM_START
push bp
mov bp, sp
push ax
push dx
mov dx, 4[bp]
mov al, 6[bp]
out dx, al
pop dx
pop ax
pop bp
ASM_END
}
#if BX_USE_ATADRV
void
outw(port, val)
Bit16u port;
Bit16u val;
{
ASM_START
push bp
mov bp, sp
push ax
push dx
mov dx, 4[bp]
mov ax, 6[bp]
out dx, ax
pop dx
pop ax
pop bp
ASM_END
}
#endif
void
outb_cmos(cmos_reg, val)
Bit8u cmos_reg;
Bit8u val;
{
ASM_START
push bp
mov bp, sp
mov al, 4[bp] ;; cmos_reg
out 0x70, al
mov al, 6[bp] ;; val
out 0x71, al
pop bp
ASM_END
}
Bit8u
inb_cmos(cmos_reg)
Bit8u cmos_reg;
{
ASM_START
push bp
mov bp, sp
mov al, 4[bp] ;; cmos_reg
out 0x70, al
in al, 0x71
pop bp
ASM_END
}
void
init_rtc()
{
outb_cmos(0x0a, 0x26);
outb_cmos(0x0b, 0x02);
inb_cmos(0x0c);
inb_cmos(0x0d);
}
bx_bool
rtc_updating()
{
// This function checks to see if the update-in-progress bit
// is set in CMOS Status Register A. If not, it returns 0.
// If it is set, it tries to wait until there is a transition
// to 0, and will return 0 if such a transition occurs. A 1
// is returned only after timing out. The maximum period
// that this bit should be set is constrained to 244useconds.
// The count I use below guarantees coverage or more than
// this time, with any reasonable IPS setting.
Bit16u count;
count = 25000;
while (--count != 0) {
if ( (inb_cmos(0x0a) & 0x80) == 0 )
return(0);
}
return(1); // update-in-progress never transitioned to 0
}
Bit8u
read_byte(seg, offset)
Bit16u seg;
Bit16u offset;
{
ASM_START
push bp
mov bp, sp
push bx
push ds
mov ax, 4[bp] ; segment
mov ds, ax
mov bx, 6[bp] ; offset
mov al, [bx]
;; al = return value (byte)
pop ds
pop bx
pop bp
ASM_END
}
Bit16u
read_word(seg, offset)
Bit16u seg;
Bit16u offset;
{
ASM_START
push bp
mov bp, sp
push bx
push ds
mov ax, 4[bp] ; segment
mov ds, ax
mov bx, 6[bp] ; offset
mov ax, [bx]
;; ax = return value (word)
pop ds
pop bx
pop bp
ASM_END
}
void
write_byte(seg, offset, data)
Bit16u seg;
Bit16u offset;
Bit8u data;
{
ASM_START
push bp
mov bp, sp
push ax
push bx
push ds
mov ax, 4[bp] ; segment
mov ds, ax
mov bx, 6[bp] ; offset
mov al, 8[bp] ; data byte
mov [bx], al ; write data byte
pop ds
pop bx
pop ax
pop bp
ASM_END
}
void
write_word(seg, offset, data)
Bit16u seg;
Bit16u offset;
Bit16u data;
{
ASM_START
push bp
mov bp, sp
push ax
push bx
push ds
mov ax, 4[bp] ; segment
mov ds, ax
mov bx, 6[bp] ; offset
mov ax, 8[bp] ; data word
mov [bx], ax ; write data word
pop ds
pop bx
pop ax
pop bp
ASM_END
}
Bit16u
get_CS()
{
ASM_START
mov ax, cs
ASM_END
}
Bit16u
get_SS()
{
ASM_START
mov ax, ss
ASM_END
}
#if BX_DEBUG_SERIAL
/* serial debug port*/
#define BX_DEBUG_PORT 0x03f8
/* data */
#define UART_RBR 0x00
#define UART_THR 0x00
/* control */
#define UART_IER 0x01
#define UART_IIR 0x02
#define UART_FCR 0x02
#define UART_LCR 0x03
#define UART_MCR 0x04
#define UART_DLL 0x00
#define UART_DLM 0x01
/* status */
#define UART_LSR 0x05
#define UART_MSR 0x06
#define UART_SCR 0x07
int uart_can_tx_byte(base_port)
Bit16u base_port;
{
return inb(base_port + UART_LSR) & 0x20;
}
void uart_wait_to_tx_byte(base_port)
Bit16u base_port;
{
while (!uart_can_tx_byte(base_port));
}
void uart_wait_until_sent(base_port)
Bit16u base_port;
{
while (!(inb(base_port + UART_LSR) & 0x40));
}
void uart_tx_byte(base_port, data)
Bit16u base_port;
Bit8u data;
{
uart_wait_to_tx_byte(base_port);
outb(base_port + UART_THR, data);
uart_wait_until_sent(base_port);
}
#endif
void
wrch(c)
Bit8u c;
{
ASM_START
push bp
mov bp, sp
push bx
mov ah, #0x0e
mov al, 4[bp]
xor bx,bx
int #0x10
pop bx
pop bp
ASM_END
}
void
send(action, c)
Bit16u action;
Bit8u c;
{
#if BX_DEBUG_SERIAL
if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
uart_tx_byte(BX_DEBUG_PORT, c);
#endif
#if BX_VIRTUAL_PORTS
if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
#endif
if (action & BIOS_PRINTF_SCREEN) {
if (c == '\n') wrch('\r');
wrch(c);
}
}
void
put_int(action, val, width, neg)
Bit16u action;
short val, width;
bx_bool neg;
{
short nval = val / 10;
if (nval)
put_int(action, nval, width - 1, neg);
else {
while (--width > 0) send(action, ' ');
if (neg) send(action, '-');
}
send(action, val - (nval * 10) + '0');
}
void
put_uint(action, val, width, neg)
Bit16u action;
unsigned short val;
short width;
bx_bool neg;
{
unsigned short nval = val / 10;
if (nval)
put_uint(action, nval, width - 1, neg);
else {
while (--width > 0) send(action, ' ');
if (neg) send(action, '-');
}
send(action, val - (nval * 10) + '0');
}
void
put_luint(action, val, width, neg)
Bit16u action;
unsigned long val;
short width;
bx_bool neg;
{
unsigned long nval = val / 10;
if (nval)
put_luint(action, nval, width - 1, neg);
else {
while (--width > 0) send(action, ' ');
if (neg) send(action, '-');
}
send(action, val - (nval * 10) + '0');
}
void put_str(action, segment, offset)
Bit16u action;
Bit16u segment;
Bit16u offset;
{
Bit8u c;
while (c = read_byte(segment, offset)) {
send(action, c);
offset++;
}
}
void
delay_ticks(ticks)
Bit16u ticks;
{
long ticks_to_wait, delta;
Bit32u prev_ticks, t;
/*
* The 0:046c wraps around at 'midnight' according to a 18.2Hz clock.
* We also have to be careful about interrupt storms.
*/
ASM_START
pushf
sti
ASM_END
ticks_to_wait = ticks;
prev_ticks = read_dword(0x0, 0x46c);
do
{
ASM_START
hlt
ASM_END
t = read_dword(0x0, 0x46c);
if (t > prev_ticks)
{
delta = t - prev_ticks; /* The temp var is required or bcc screws up. */
ticks_to_wait -= delta;
}
else if (t < prev_ticks)
{
ticks_to_wait -= t; /* wrapped */
}
prev_ticks = t;
} while (ticks_to_wait > 0);
ASM_START
cli
popf
ASM_END
}
Bit8u
check_for_keystroke()
{
ASM_START
mov ax, #0x100
int #0x16
jz no_key
mov al, #1
jmp done
no_key:
xor al, al
done:
ASM_END
}
Bit8u
get_keystroke()
{
ASM_START
mov ax, #0x0
int #0x16
xchg ah, al
ASM_END
}
void
delay_ticks_and_check_for_keystroke(ticks, count)
Bit16u ticks, count;
{
Bit16u i;
for (i = 1; i <= count; i++) {
delay_ticks(ticks);
if (check_for_keystroke())
break;
}
}
//--------------------------------------------------------------------------
// bios_printf()
// A compact variable argument printf function.
//
// Supports %[format_width][length]format
// where format can be x,X,u,d,s,S,c
// and the optional length modifier is l (ell)
//--------------------------------------------------------------------------
void
bios_printf(action, s)
Bit16u action;
Bit8u *s;
{
Bit8u c, format_char;
bx_bool in_format;
short i;
Bit16u *arg_ptr;
Bit16u arg_seg, arg, nibble, hibyte, shift_count, format_width, hexadd;
arg_ptr = &s;
arg_seg = get_SS();
in_format = 0;
format_width = 0;
if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
#if BX_VIRTUAL_PORTS
outb(PANIC_PORT2, 0x00);
#endif
bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
}
while (c = read_byte(get_CS(), s)) {
if ( c == '%' ) {
in_format = 1;
format_width = 0;
}
else if (in_format) {
if ( (c>='0') && (c<='9') ) {
format_width = (format_width * 10) + (c - '0');
}
else {
arg_ptr++; // increment to next arg
arg = read_word(arg_seg, arg_ptr);
if (c == 'x' || c == 'X') {
if (format_width == 0)
format_width = 4;
if (c == 'x')
hexadd = 'a';
else
hexadd = 'A';
for (i=format_width-1; i>=0; i--) {
nibble = (arg >> (4 * i)) & 0x000f;
send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
}
}
else if (c == 'u') {
put_uint(action, arg, format_width, 0);
}
else if (c == 'l') {
s++;
c = read_byte(get_CS(), s); /* is it ld,lx,lu? */
arg_ptr++; /* increment to next arg */
hibyte = read_word(arg_seg, arg_ptr);
if (c == 'd') {
if (hibyte & 0x8000)
put_luint(action, 0L-(((Bit32u) hibyte << 16) | arg), format_width-1, 1);
else
put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
}
else if (c == 'u') {
put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
}
else if (c == 'x' || c == 'X')
{
if (format_width == 0)
format_width = 8;
if (c == 'x')
hexadd = 'a';
else
hexadd = 'A';
for (i=format_width-1; i>=0; i--) {
nibble = ((((Bit32u) hibyte <<16) | arg) >> (4 * i)) & 0x000f;
send (action, (nibble<=9)? (nibble+'0') : (nibble-10+hexadd));
}
}
}
else if (c == 'd') {
if (arg & 0x8000)
put_int(action, -arg, format_width - 1, 1);
else
put_int(action, arg, format_width, 0);
}
else if (c == 's') {
put_str(action, get_CS(), arg);
}
else if (c == 'S') {
hibyte = arg;
arg_ptr++;
arg = read_word(arg_seg, arg_ptr);
put_str(action, hibyte, arg);
}
else if (c == 'c') {
send(action, arg);
}
else
BX_PANIC("bios_printf: unknown format\n");
in_format = 0;
}
}
else {
send(action, c);
}
s ++;
}
if (action & BIOS_PRINTF_HALT) {
// freeze in a busy loop.
ASM_START
cli
halt2_loop:
hlt
jmp halt2_loop
ASM_END
}
}
//--------------------------------------------------------------------------
// keyboard_init
//--------------------------------------------------------------------------
// this file is based on LinuxBIOS implementation of keyboard.c
// could convert to #asm to gain space
void
keyboard_init()
{
Bit16u max;
/* ------------------- Flush buffers ------------------------*/
/* Wait until buffer is empty */
max=0xffff;
while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
/* flush incoming keys */
max=0x2000;
while (--max > 0) {
outb(0x80, 0x00);
if (inb(0x64) & 0x01) {
inb(0x60);
max = 0x2000;
}
}
// Due to timer issues, and if the IPS setting is > 15000000,
// the incoming keys might not be flushed here. That will
// cause a panic a few lines below. See sourceforge bug report :
// [ 642031 ] FATAL: Keyboard RESET error:993
/* ------------------- controller side ----------------------*/
/* send cmd = 0xAA, self test 8042 */
outb(0x64, 0xaa);
/* Wait until buffer is empty */
max=0xffff;
while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
if (max==0x0) keyboard_panic(00);
/* Wait for data */
max=0xffff;
while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
if (max==0x0) keyboard_panic(01);
/* read self-test result, 0x55 should be returned from 0x60 */
if ((inb(0x60) != 0x55)){
keyboard_panic(991);
}
/* send cmd = 0xAB, keyboard interface test */
outb(0x64,0xab);
/* Wait until buffer is empty */
max=0xffff;
while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
if (max==0x0) keyboard_panic(10);
/* Wait for data */
max=0xffff;
while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
if (max==0x0) keyboard_panic(11);
/* read keyboard interface test result, */
/* 0x00 should be returned form 0x60 */
if ((inb(0x60) != 0x00)) {
keyboard_panic(992);
}
/* Enable Keyboard clock */
outb(0x64,0xae);
outb(0x64,0xa8);
/* ------------------- keyboard side ------------------------*/
/* reset kerboard and self test (keyboard side) */
outb(0x60, 0xff);
/* Wait until buffer is empty */
max=0xffff;
while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
if (max==0x0) keyboard_panic(20);
/* Wait for data */
max=0xffff;
while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
if (max==0x0) keyboard_panic(21);
/* keyboard should return ACK */
if ((inb(0x60) != 0xfa)) {
keyboard_panic(993);
}
/* Wait for data */
max=0xffff;
while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
if (max==0x0) keyboard_panic(31);
if ((inb(0x60) != 0xaa)) {
keyboard_panic(994);
}
/* Disable keyboard */
outb(0x60, 0xf5);
/* Wait until buffer is empty */
max=0xffff;
while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
if (max==0x0) keyboard_panic(40);
/* Wait for data */
max=0xffff;
while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
if (max==0x0) keyboard_panic(41);
/* keyboard should return ACK */
if ((inb(0x60) != 0xfa)) {
keyboard_panic(995);
}
/* Write Keyboard Mode */
outb(0x64, 0x60);
/* Wait until buffer is empty */
max=0xffff;
while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
if (max==0x0) keyboard_panic(50);
/* send cmd: scan code convert, disable mouse, enable IRQ 1 */
outb(0x60, 0x61);
/* Wait until buffer is empty */
max=0xffff;
while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
if (max==0x0) keyboard_panic(60);
/* Enable keyboard */
outb(0x60, 0xf4);
/* Wait until buffer is empty */
max=0xffff;
while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
if (max==0x0) keyboard_panic(70);
/* Wait for data */
max=0xffff;
while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
if (max==0x0) keyboard_panic(70);
/* keyboard should return ACK */
if ((inb(0x60) != 0xfa)) {
keyboard_panic(996);
}
outb(0x80, 0x77);
}
//--------------------------------------------------------------------------
// keyboard_panic
//--------------------------------------------------------------------------
void
keyboard_panic(status)
Bit16u status;
{
// If you're getting a 993 keyboard panic here,
// please see the comment in keyboard_init
BX_PANIC("Keyboard error:%u\n",status);
}
//--------------------------------------------------------------------------
// shutdown_status_panic
// called when the shutdown statsu is not implemented, displays the status
//--------------------------------------------------------------------------
void
shutdown_status_panic(status)
Bit16u status;
{
BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
}
void s3_resume_panic()
{
BX_PANIC("Returned from s3_resume.\n");
}
//--------------------------------------------------------------------------
// print_bios_banner
// displays a the bios version
//--------------------------------------------------------------------------
void
print_bios_banner()
{
printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ",
BIOS_BUILD_DATE, bios_cvs_version_string);
printf(
#if BX_APM
"apmbios "
#endif
#if BX_PCIBIOS
"pcibios "
#endif
#if BX_ELTORITO_BOOT
"eltorito "
#endif
#if BX_ROMBIOS32
"rombios32 "
#endif
"\n\n");
}
//--------------------------------------------------------------------------
// BIOS Boot Specification 1.0.1 compatibility
//
// Very basic support for the BIOS Boot Specification, which allows expansion
// ROMs to register themselves as boot devices, instead of just stealing the
// INT 19h boot vector.
//
// This is a hack: to do it properly requires a proper PnP BIOS and we aren't
// one; we just lie to the option ROMs to make them behave correctly.
// We also don't support letting option ROMs register as bootable disk
// drives (BCVs), only as bootable devices (BEVs).
//
// http://www.phoenix.com/en/Customer+Services/White+Papers-Specs/pc+industry+specifications.htm
//--------------------------------------------------------------------------
static char drivetypes[][10]={"", "Floppy","Hard Disk","CD-Rom", "Network"};
static void
init_boot_vectors()
{
ipl_entry_t e;
Bit16u count = 0;
Bit16u ss = get_SS();
/* Clear out the IPL table. */
memsetb(IPL_SEG, IPL_TABLE_OFFSET, 0, IPL_SIZE);
/* User selected device not set */
write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF);
/* Floppy drive */
e.type = IPL_TYPE_FLOPPY; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
count++;
/* First HDD */
e.type = IPL_TYPE_HARDDISK; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
count++;
#if BX_ELTORITO_BOOT
/* CDROM */
e.type = IPL_TYPE_CDROM; e.flags = 0; e.vector = 0; e.description = 0; e.reserved = 0;
memcpyb(IPL_SEG, IPL_TABLE_OFFSET + count * sizeof (e), ss, &e, sizeof (e));
count++;
#endif
/* Remember how many devices we have */
write_word(IPL_SEG, IPL_COUNT_OFFSET, count);
/* Not tried booting anything yet */
write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xffff);
}
static Bit8u
get_boot_vector(i, e)
Bit16u i; ipl_entry_t *e;
{
Bit16u count;
Bit16u ss = get_SS();
/* Get the count of boot devices, and refuse to overrun the array */
count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
if (i >= count) return 0;
/* OK to read this device */
memcpyb(ss, e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (*e), sizeof (*e));
return 1;
}
#if BX_ELTORITO_BOOT
void
interactive_bootkey()
{
ipl_entry_t e;
Bit16u count;
char description[33];
Bit8u scan_code;
Bit8u i;
Bit16u ss = get_SS();
Bit16u valid_choice = 0;
while (check_for_keystroke())
get_keystroke();
printf("Press F12 for boot menu.\n\n");
delay_ticks_and_check_for_keystroke(11, 5); /* ~3 seconds */
if (check_for_keystroke())
{
scan_code = get_keystroke();
if (scan_code == 0x86) /* F12 */
{
while (check_for_keystroke())
get_keystroke();
printf("Select boot device:\n\n");
count = read_word(IPL_SEG, IPL_COUNT_OFFSET);
for (i = 0; i < count; i++)
{
memcpyb(ss, &e, IPL_SEG, IPL_TABLE_OFFSET + i * sizeof (e), sizeof (e));
printf("%d. ", i+1);
switch(e.type)
{
case IPL_TYPE_FLOPPY:
case IPL_TYPE_HARDDISK:
case IPL_TYPE_CDROM:
printf("%s\n", drivetypes[e.type]);
break;
case IPL_TYPE_BEV:
printf("%s", drivetypes[4]);
if (e.description != 0)
{
memcpyb(ss, &description, (Bit16u)(e.description >> 16), (Bit16u)(e.description & 0xffff), 32);
description[32] = 0;
printf(" [%S]", ss, description);
}
printf("\n");
break;
}
}
count++;
while (!valid_choice) {
scan_code = get_keystroke();
if (scan_code == 0x01 || scan_code == 0x58) /* ESC or F12 */
{
valid_choice = 1;
}
else if (scan_code <= count)
{
valid_choice = 1;
scan_code -= 1;
/* Set user selected device */
write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, scan_code);
}
}
printf("\n");
}
}
}
#endif // BX_ELTORITO_BOOT
//--------------------------------------------------------------------------
// print_boot_device
// displays the boot device
//--------------------------------------------------------------------------
void
print_boot_device(e)
ipl_entry_t *e;
{
Bit16u type;
char description[33];
Bit16u ss = get_SS();
type = e->type;
/* NIC appears as type 0x80 */
if (type == IPL_TYPE_BEV) type = 0x4;
if (type == 0 || type > 0x4) BX_PANIC("Bad drive type\n");
printf("Booting from %s", drivetypes[type]);
/* print product string if BEV */
if (type == 4 && e->description != 0) {
/* first 32 bytes are significant */
memcpyb(ss, &description, (Bit16u)(e->description >> 16), (Bit16u)(e->description & 0xffff), 32);
/* terminate string */
description[32] = 0;
printf(" [%S]", ss, description);
}
printf("...\n");
}
//--------------------------------------------------------------------------
// print_boot_failure
// displays the reason why boot failed
//--------------------------------------------------------------------------
void
print_boot_failure(type, reason)
Bit16u type; Bit8u reason;
{
if (type == 0 || type > 0x3) BX_PANIC("Bad drive type\n");
printf("Boot failed");
if (type < 4) {
/* Report the reason too */
if (reason==0)
printf(": not a bootable disk");
else
printf(": could not read the boot disk");
}
printf("\n\n");
}
//--------------------------------------------------------------------------
// print_cdromboot_failure
// displays the reason why boot failed
//--------------------------------------------------------------------------
void
print_cdromboot_failure( code )
Bit16u code;
{
bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
return;
}
void
nmi_handler_msg()
{
BX_PANIC("NMI Handler called\n");
}
void
int18_panic_msg()
{
BX_PANIC("INT18: BOOT FAILURE\n");
}
void
log_bios_start()
{
#if BX_DEBUG_SERIAL
outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
#endif
BX_INFO("%s\n", bios_cvs_version_string);
}
bx_bool
set_enable_a20(val)
bx_bool val;
{
Bit8u oldval;
// Use PS2 System Control port A to set A20 enable
// get current setting first
oldval = inb(0x92);
// change A20 status
if (val)
outb(0x92, oldval | 0x02);
else
outb(0x92, oldval & 0xfd);
return((oldval & 0x02) != 0);
}
void
debugger_on()
{
outb(0xfedc, 0x01);
}
void
debugger_off()
{
outb(0xfedc, 0x00);
}
int
s3_resume()
{
Bit32u s3_wakeup_vector;
Bit8u s3_resume_flag;
s3_resume_flag = read_byte(0x40, 0xb0);
s3_wakeup_vector = read_dword(0x40, 0xb2);
BX_INFO("S3 resume called %x 0x%lx\n", s3_resume_flag, s3_wakeup_vector);
if (s3_resume_flag != 0xFE || !s3_wakeup_vector)
return 0;
write_byte(0x40, 0xb0, 0);
/* setup wakeup vector */
write_word(0x40, 0xb6, (s3_wakeup_vector & 0xF)); /* IP */
write_word(0x40, 0xb8, (s3_wakeup_vector >> 4)); /* CS */
BX_INFO("S3 resume jump to %x:%x\n", (s3_wakeup_vector >> 4),
(s3_wakeup_vector & 0xF));
ASM_START
jmpf [0x04b6]
ASM_END
return 1;
}
#if BX_USE_ATADRV
// ---------------------------------------------------------------------------
// Start of ATA/ATAPI Driver
// ---------------------------------------------------------------------------
// Global defines -- ATA register and register bits.
// command block & control block regs
#define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0
#define ATA_CB_ERR 1 // error in pio_base_addr1+1
#define ATA_CB_FR 1 // feature reg out pio_base_addr1+1
#define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2
#define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3
#define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4
#define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5
#define ATA_CB_DH 6 // device head in/out pio_base_addr1+6
#define ATA_CB_STAT 7 // primary status in pio_base_addr1+7
#define ATA_CB_CMD 7 // command out pio_base_addr1+7
#define ATA_CB_ASTAT 6 // alternate status in pio_base_addr2+6
#define ATA_CB_DC 6 // device control out pio_base_addr2+6
#define ATA_CB_DA 7 // device address in pio_base_addr2+7
#define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC
#define ATA_CB_ER_BBK 0x80 // ATA bad block
#define ATA_CB_ER_UNC 0x40 // ATA uncorrected error
#define ATA_CB_ER_MC 0x20 // ATA media change
#define ATA_CB_ER_IDNF 0x10 // ATA id not found
#define ATA_CB_ER_MCR 0x08 // ATA media change request
#define ATA_CB_ER_ABRT 0x04 // ATA command aborted
#define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found
#define ATA_CB_ER_NDAM 0x01 // ATA address mark not found
#define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask)
#define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request
#define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort
#define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media
#define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication
// ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
#define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask)
#define ATA_CB_SC_P_REL 0x04 // ATAPI release
#define ATA_CB_SC_P_IO 0x02 // ATAPI I/O
#define ATA_CB_SC_P_CD 0x01 // ATAPI C/D
// bits 7-4 of the device/head (CB_DH) reg
#define ATA_CB_DH_DEV0 0xa0 // select device 0
#define ATA_CB_DH_DEV1 0xb0 // select device 1
#define ATA_CB_DH_LBA 0x40 // use LBA
// status reg (CB_STAT and CB_ASTAT) bits
#define ATA_CB_STAT_BSY 0x80 // busy
#define ATA_CB_STAT_RDY 0x40 // ready
#define ATA_CB_STAT_DF 0x20 // device fault
#define ATA_CB_STAT_WFT 0x20 // write fault (old name)
#define ATA_CB_STAT_SKC 0x10 // seek complete
#define ATA_CB_STAT_SERV 0x10 // service
#define ATA_CB_STAT_DRQ 0x08 // data request
#define ATA_CB_STAT_CORR 0x04 // corrected
#define ATA_CB_STAT_IDX 0x02 // index
#define ATA_CB_STAT_ERR 0x01 // error (ATA)
#define ATA_CB_STAT_CHK 0x01 // check (ATAPI)
// device control reg (CB_DC) bits
#define ATA_CB_DC_HD15 0x08 // bit should always be set to one
#define ATA_CB_DC_SRST 0x04 // soft reset
#define ATA_CB_DC_NIEN 0x02 // disable interrupts
// Most mandtory and optional ATA commands (from ATA-3),
#define ATA_CMD_CFA_ERASE_SECTORS 0xC0
#define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03
#define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87
#define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD
#define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38
#define ATA_CMD_CHECK_POWER_MODE1 0xE5
#define ATA_CMD_CHECK_POWER_MODE2 0x98
#define ATA_CMD_DEVICE_RESET 0x08
#define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90
#define ATA_CMD_FLUSH_CACHE 0xE7
#define ATA_CMD_FORMAT_TRACK 0x50
#define ATA_CMD_IDENTIFY_DEVICE 0xEC
#define ATA_CMD_IDENTIFY_DEVICE_PACKET 0xA1
#define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1
#define ATA_CMD_IDLE1 0xE3
#define ATA_CMD_IDLE2 0x97
#define ATA_CMD_IDLE_IMMEDIATE1 0xE1
#define ATA_CMD_IDLE_IMMEDIATE2 0x95
#define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91
#define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
#define ATA_CMD_NOP 0x00
#define ATA_CMD_PACKET 0xA0
#define ATA_CMD_READ_BUFFER 0xE4
#define ATA_CMD_READ_DMA 0xC8
#define ATA_CMD_READ_DMA_QUEUED 0xC7
#define ATA_CMD_READ_MULTIPLE 0xC4
#define ATA_CMD_READ_SECTORS 0x20
#define ATA_CMD_READ_VERIFY_SECTORS 0x40
#define ATA_CMD_RECALIBRATE 0x10
#define ATA_CMD_REQUEST_SENSE 0x03
#define ATA_CMD_SEEK 0x70
#define ATA_CMD_SET_FEATURES 0xEF
#define ATA_CMD_SET_MULTIPLE_MODE 0xC6
#define ATA_CMD_SLEEP1 0xE6
#define ATA_CMD_SLEEP2 0x99
#define ATA_CMD_STANDBY1 0xE2
#define ATA_CMD_STANDBY2 0x96
#define ATA_CMD_STANDBY_IMMEDIATE1 0xE0
#define ATA_CMD_STANDBY_IMMEDIATE2 0x94
#define ATA_CMD_WRITE_BUFFER 0xE8
#define ATA_CMD_WRITE_DMA 0xCA
#define ATA_CMD_WRITE_DMA_QUEUED 0xCC
#define ATA_CMD_WRITE_MULTIPLE 0xC5
#define ATA_CMD_WRITE_SECTORS 0x30
#define ATA_CMD_WRITE_VERIFY 0x3C
#define ATA_IFACE_NONE 0x00
#define ATA_IFACE_ISA 0x00
#define ATA_IFACE_PCI 0x01
#define ATA_TYPE_NONE 0x00
#define ATA_TYPE_UNKNOWN 0x01
#define ATA_TYPE_ATA 0x02
#define ATA_TYPE_ATAPI 0x03
#define ATA_DEVICE_NONE 0x00
#define ATA_DEVICE_HD 0xFF
#define ATA_DEVICE_CDROM 0x05
#define ATA_MODE_NONE 0x00
#define ATA_MODE_PIO16 0x00
#define ATA_MODE_PIO32 0x01
#define ATA_MODE_ISADMA 0x02
#define ATA_MODE_PCIDMA 0x03
#define ATA_MODE_USEIRQ 0x10
#define ATA_TRANSLATION_NONE 0
#define ATA_TRANSLATION_LBA 1
#define ATA_TRANSLATION_LARGE 2
#define ATA_TRANSLATION_RECHS 3
#define ATA_DATA_NO 0x00
#define ATA_DATA_IN 0x01
#define ATA_DATA_OUT 0x02
// ---------------------------------------------------------------------------
// ATA/ATAPI driver : initialization
// ---------------------------------------------------------------------------
void ata_init( )
{
Bit16u ebda_seg=read_word(0x0040,0x000E);
Bit8u channel, device;
// Channels info init.
for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
}
// Devices info init.
for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low,0L);
write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high,0L);
}
// hdidmap and cdidmap init.
for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
}
write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
}
#define TIMEOUT 0
#define BSY 1
#define NOT_BSY 2
#define NOT_BSY_DRQ 3
#define NOT_BSY_NOT_DRQ 4
#define NOT_BSY_RDY 5
#define IDE_TIMEOUT 32000u //32 seconds max for IDE ops
int await_ide();
static int await_ide(when_done,base,timeout)
Bit8u when_done;
Bit16u base;
Bit16u timeout;
{
Bit32u time=0,last=0;
Bit16u status;
Bit8u result;
status = inb(base + ATA_CB_STAT); // for the times you're supposed to throw one away
for(;;) {
status = inb(base+ATA_CB_STAT);
time++;
if (when_done == BSY)
result = status & ATA_CB_STAT_BSY;
else if (when_done == NOT_BSY)
result = !(status & ATA_CB_STAT_BSY);
else if (when_done == NOT_BSY_DRQ)
result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_DRQ);
else if (when_done == NOT_BSY_NOT_DRQ)
result = !(status & ATA_CB_STAT_BSY) && !(status & ATA_CB_STAT_DRQ);
else if (when_done == NOT_BSY_RDY)
result = !(status & ATA_CB_STAT_BSY) && (status & ATA_CB_STAT_RDY);
else if (when_done == TIMEOUT)
result = 0;
if (result) return 0;
if (time>>16 != last) // mod 2048 each 16 ms
{
last = time >>16;
BX_DEBUG_ATA("await_ide: (TIMEOUT,BSY,!BSY,!BSY_DRQ,!BSY_!DRQ,!BSY_RDY) %d time= %ld timeout= %d\n",when_done,time>>11, timeout);
}
if (status & ATA_CB_STAT_ERR)
{
BX_DEBUG_ATA("await_ide: ERROR (TIMEOUT,BSY,!BSY,!BSY_DRQ,!BSY_!DRQ,!BSY_RDY) %d time= %ld timeout= %d\n",when_done,time>>11, timeout);
return -1;
}
if ((timeout == 0) || ((time>>11) > timeout)) break;
}
BX_INFO("IDE time out\n");
return -1;
}
// ---------------------------------------------------------------------------
// ATA/ATAPI driver : device detection
// ---------------------------------------------------------------------------
void ata_detect( )
{
Bit16u ebda_seg=read_word(0x0040,0x000E);
Bit8u hdcount, cdcount, device, type;
Bit8u buffer[0x0200];
#if BX_MAX_ATA_INTERFACES > 0
write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
#endif
#if BX_MAX_ATA_INTERFACES > 1
write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
#endif
#if BX_MAX_ATA_INTERFACES > 2
write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
#endif
#if BX_MAX_ATA_INTERFACES > 3
write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
#endif
#if BX_MAX_ATA_INTERFACES > 4
#error Please fill the ATA interface informations
#endif
// Device detection
hdcount=cdcount=0;
for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
Bit16u iobase1, iobase2;
Bit8u channel, slave, shift;
Bit8u sc, sn, cl, ch, st;
channel = device / 2;
slave = device % 2;
iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
// Disable interrupts
outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
// Look for device
outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
outb(iobase1+ATA_CB_SC, 0x55);
outb(iobase1+ATA_CB_SN, 0xaa);
outb(iobase1+ATA_CB_SC, 0xaa);
outb(iobase1+ATA_CB_SN, 0x55);
outb(iobase1+ATA_CB_SC, 0x55);
outb(iobase1+ATA_CB_SN, 0xaa);
// If we found something
sc = inb(iobase1+ATA_CB_SC);
sn = inb(iobase1+ATA_CB_SN);
if ( (sc == 0x55) && (sn == 0xaa) ) {
write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
// reset the channel
ata_reset(device);
// check for ATA or ATAPI
outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
sc = inb(iobase1+ATA_CB_SC);
sn = inb(iobase1+ATA_CB_SN);
if ((sc==0x01) && (sn==0x01)) {
cl = inb(iobase1+ATA_CB_CL);
ch = inb(iobase1+ATA_CB_CH);
st = inb(iobase1+ATA_CB_STAT);
if ((cl==0x14) && (ch==0xeb)) {
write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
} else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) {
write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
} else if ((cl==0xff) && (ch==0xff)) {
write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
}
}
}
type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
// Now we send a IDENTIFY command to ATA device
if(type == ATA_TYPE_ATA) {
Bit32u sectors_low, sectors_high;
Bit16u cylinders, heads, spt, blksize;
Bit8u translation, removable, mode;
//Temporary values to do the transfer
write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) !=0 )
BX_PANIC("ata-detect: Failed to detect ATA device\n");
removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
blksize = read_word(get_SS(),buffer+10);
cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
heads = read_word(get_SS(),buffer+(3*2)); // word 3
spt = read_word(get_SS(),buffer+(6*2)); // word 6
if (read_word(get_SS(),buffer+(83*2)) & (1 << 10)) { // word 83 - lba48 support
sectors_low = read_dword(get_SS(),buffer+(100*2)); // word 100 and word 101
sectors_high = read_dword(get_SS(),buffer+(102*2)); // word 102 and word 103
} else {
sectors_low = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
sectors_high = 0;
}
write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low, sectors_low);
write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high, sectors_high);
BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
translation = inb_cmos(0x39 + channel/2);
for (shift=device%4; shift>0; shift--) translation >>= 2;
translation &= 0x03;
write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
switch (translation) {
case ATA_TRANSLATION_NONE:
BX_INFO("none");
break;
case ATA_TRANSLATION_LBA:
BX_INFO("lba");
break;
case ATA_TRANSLATION_LARGE:
BX_INFO("large");
break;
case ATA_TRANSLATION_RECHS:
BX_INFO("r-echs");
break;
}
switch (translation) {
case ATA_TRANSLATION_NONE:
break;
case ATA_TRANSLATION_LBA:
spt = 63;
sectors_low /= 63;
heads = sectors_low / 1024;
if (heads>128) heads = 255;
else if (heads>64) heads = 128;
else if (heads>32) heads = 64;
else if (heads>16) heads = 32;
else heads=16;
cylinders = sectors_low / heads;
break;
case ATA_TRANSLATION_RECHS:
// Take care not to overflow
if (heads==16) {
if(cylinders>61439) cylinders=61439;
heads=15;
cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
}
// then go through the large bitshift process
case ATA_TRANSLATION_LARGE:
while(cylinders > 1024) {
cylinders >>= 1;
heads <<= 1;
// If we max out the head count
if (heads > 127) break;
}
break;
}
// clip to 1024 cylinders in lchs
if (cylinders > 1024) cylinders=1024;
BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
// fill hdidmap
write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
hdcount++;
}
// Now we send a IDENTIFY command to ATAPI device
if(type == ATA_TYPE_ATAPI) {
Bit8u type, removable, mode;
Bit16u blksize;
//Temporary values to do the transfer
write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, 0L, get_SS(),buffer) != 0)
BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
type = read_byte(get_SS(),buffer+1) & 0x1f;
removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
mode = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
blksize = 2048;
write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
// fill cdidmap
write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
cdcount++;
}
{
Bit32u sizeinmb;
Bit16u ataversion;
Bit8u c, i, version, model[41];
switch (type) {
case ATA_TYPE_ATA:
sizeinmb = (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_high) << 21)
| (read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors_low) >> 11);
case ATA_TYPE_ATAPI:
// Read ATA/ATAPI version
ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
for(version=15;version>0;version--) {
if((ataversion&(1<<version))!=0)
break;
}
// Read model name
for(i=0;i<20;i++){
write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
}
// Reformat
write_byte(get_SS(),model+40,0x00);
for(i=39;i>0;i--){
if(read_byte(get_SS(),model+i)==0x20)
write_byte(get_SS(),model+i,0x00);
else break;
}
if (i>36) {
write_byte(get_SS(),model+36,0x00);
for(i=35;i>32;i--){
write_byte(get_SS(),model+i,0x2E);
}
}
break;
}
switch (type) {
case ATA_TYPE_ATA:
printf("ata%d %s: ",channel,slave?" slave":"master");
i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
if (sizeinmb < (1UL<<16))
printf(" ATA-%d Hard-Disk (%4u MBytes)\n", version, (Bit16u)sizeinmb);
else
printf(" ATA-%d Hard-Disk (%4u GBytes)\n", version, (Bit16u)(sizeinmb>>10));
break;
case ATA_TYPE_ATAPI:
printf("ata%d %s: ",channel,slave?" slave":"master");
i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
else
printf(" ATAPI-%d Device\n",version);
break;
case ATA_TYPE_UNKNOWN:
printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
break;
}
}
}
// Store the devices counts
write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
write_byte(0x40,0x75, hdcount);
printf("\n");
// FIXME : should use bios=cmos|auto|disable bits
// FIXME : should know about translation bits
// FIXME : move hard_drive_post here
}
// ---------------------------------------------------------------------------
// ATA/ATAPI driver : software reset
// ---------------------------------------------------------------------------
// ATA-3
// 8.2.1 Software reset - Device 0
void ata_reset(device)
Bit16u device;
{
Bit16u ebda_seg=read_word(0x0040,0x000E);
Bit16u iobase1, iobase2;
Bit8u channel, slave, sn, sc;
Bit8u type;
Bit16u max;
channel = device / 2;
slave = device % 2;
iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
// Reset
// 8.2.1 (a) -- set SRST in DC
outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
// 8.2.1 (b) -- wait for BSY
await_ide(BSY, iobase1, 20);
// 8.2.1 (f) -- clear SRST
outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);