| ///////////////////////////////////////////////////////////////////////// |
| // $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); |
| |
|