URL
https://opencores.org/ocsvn/zet86/zet86/trunk
Subversion Repositories zet86
[/] [zet86/] [trunk/] [soc/] [bios/] [rombios.c] - Rev 49
Go to most recent revision | Compare with Previous | Blame | View Log
// ROM BIOS compatability entry points: // =================================== // $e05b ; POST Entry Point // $e6f2 ; INT 19h Boot Load 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) // $fff0 ; Power-up Entry Point // $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY // $fffe ; System Model ID #include "rombios.h" #define BX_CPU 0 /* model byte 0xFC = AT */ #define SYS_MODEL_ID 0xFC #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 // This is for compiling with gcc2 and gcc3 #define ASM_START #asm #define ASM_END #endasm ASM_START .rom .org 0x0000 use16 8086 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 } // Bit32u (unsigned long) and long helper functions ASM_START idiv_u: xor dx,dx div bx ret ldivul: mov cx,[di] mov di,2[di] call ludivmod xchg ax,cx xchg bx,di ret .align 2 ldivmod: mov dx,di ; sign byte of b in dh mov dl,bh ; sign byte of a in dl test di,di jns set_asign neg di neg cx sbb di,*0 set_asign: test bx,bx jns got_signs ; leave r = a positive neg bx neg ax sbb bx,*0 j got_signs .align 2 ludivmod: xor dx,dx ; both sign bytes 0 got_signs: push bp push si mov bp,sp push di ; remember b push cx b0 = -4 b16 = -2 test di,di jne divlarge test cx,cx je divzero cmp bx,cx jae divlarge ; would overflow xchg dx,bx ; a in dx:ax, signs in bx div cx xchg cx,ax ; q in di:cx, junk in ax xchg ax,bx ; signs in ax, junk in bx xchg ax,dx ; r in ax, signs back in dx mov bx,di ; r in bx:ax j zdivu1 divzero: ; return q = 0 and r = a test dl,dl jns return j negr ; a initially minus, restore it divlarge: push dx ; remember sign bytes mov si,di ; w in si:dx, initially b from di:cx mov dx,cx xor cx,cx ; q in di:cx, initially 0 mov di,cx ; r in bx:ax, initially a ; use di:cx rather than dx:cx in order ; to have dx free for a byte pair later cmp si,bx jb loop1 ja zdivu ; finished if b > r cmp dx,ax ja zdivu ; rotate w (= b) to greatest dyadic multiple of b <= r loop1: shl dx,*1 ; w = 2*w rcl si,*1 jc loop1_exit ; w was > r counting overflow (unsigned) cmp si,bx ; while w <= r (unsigned) jb loop1 ja loop1_exit cmp dx,ax jbe loop1 ; else exit with carry clear for rcr loop1_exit: rcr si,*1 rcr dx,*1 loop2: shl cx,*1 ; q = 2*q rcl di,*1 cmp si,bx ; if w <= r jb loop2_over ja loop2_test cmp dx,ax ja loop2_test loop2_over: add cx,*1 ; q++ adc di,*0 sub ax,dx ; r = r-w sbb bx,si loop2_test: shr si,*1 ; w = w/2 rcr dx,*1 cmp si,b16[bp] ; while w >= b ja loop2 jb zdivu cmp dx,b0[bp] jae loop2 zdivu: pop dx ; sign bytes zdivu1: test dh,dh js zbminus test dl,dl jns return ; else a initially minus, b plus mov dx,ax ; -a = b * q + r ==> a = b * (-q) + (-r) or dx,bx je negq ; use if r = 0 sub ax,b0[bp] ; use a = b * (-1 - q) + (b - r) sbb bx,b16[bp] not cx ; q = -1 - q (same as complement) not di negr: neg bx neg ax sbb bx,*0 return: mov sp,bp pop si pop bp ret .align 2 zbminus: test dl,dl ; (-a) = (-b) * q + r ==> a = b * q + (-r) js negr ; use if initial a was minus mov dx,ax ; a = (-b) * q + r ==> a = b * (-q) + r or dx,bx je negq ; use if r = 0 sub ax,b0[bp] ; use a = b * (-1 - q) + (b + r) ; (b is now -b) sbb bx,b16[bp] not cx not di mov sp,bp pop si pop bp ret .align 2 negq: neg di neg cx sbb di,*0 mov sp,bp pop si pop bp ret .align 2 ltstl: ltstul: test bx,bx je ltst_not_sure ret .align 2 ltst_not_sure: test ax,ax js ltst_fix_sign ret .align 2 ltst_fix_sign: inc bx ret .align 2 lmull: lmulul: mov cx,ax mul word ptr 2[di] xchg ax,bx mul word ptr [di] add bx,ax mov ax,ptr [di] mul cx add bx,dx ret .align 2 lsubl: lsubul: sub ax,[di] sbb bx,2[di] ret .align 2 laddl: laddul: add ax,[di] adc bx,2[di] ret .align 2 lorl: lorul: or ax,[di] or bx,2[di] ret .align 2 lsrul: mov cx,di jcxz lsru_exit cmp cx,*32 jae lsru_zero lsru_loop: shr bx,*1 rcr ax,*1 loop lsru_loop lsru_exit: ret .align 2 lsru_zero: xor ax,ax mov bx,ax ret .align 2 landl: landul: and ax,[di] and bx,2[di] ret .align 2 lcmpl: lcmpul: sub bx,2[di] je lcmp_not_sure ret .align 2 lcmp_not_sure: cmp ax,[di] jb lcmp_b_and_lt jge lcmp_exit inc bx lcmp_exit: ret .align 2 lcmp_b_and_lt: dec bx 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) 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 { 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 Bit16u inw(); static void outw(); static Bit8u read_byte(); static Bit16u read_word(); static void write_byte(); static void write_word(); static void bios_printf(); static void int09_function(); static void int13_harddisk(); static void transf_sect(); static void int13_diskette_function(); static void int16_function(); static void int19_function(); static void int1a_function(); static Bit16u get_CS(); static Bit16u get_SS(); static unsigned int enqueue_key(); static unsigned int dequeue_key(); static void set_diskette_ret_status(); static void set_diskette_current_cyl(); static void print_bios_banner(); static void print_boot_device(); static void print_boot_failure(); #if DEBUG_INT16 # define BX_DEBUG_INT16(a...) BX_DEBUG(a) #else # define BX_DEBUG_INT16(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 }, /* \| */ { 0x5700, 0x5700, none, none, none }, /* F11 */ { 0x5800, 0x5800, none, none, none } /* F12 */ }; 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 } 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 } 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 } 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 (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++; } } //-------------------------------------------------------------------------- // 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) 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 } } static char bios_svn_version_string[] = "$Revision: 1.13 $ $Date: 2009-03-05 00:26:53 $"; //-------------------------------------------------------------------------- // print_bios_banner // displays a the bios version //-------------------------------------------------------------------------- void print_bios_banner() { printf("Zet ROMBIOS - build: %s\n%s\n\n", BIOS_BUILD_DATE, bios_svn_version_string); } //-------------------------------------------------------------------------- // 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[][20]={"", "Floppy flash image" }; 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++; /* 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; } //-------------------------------------------------------------------------- // 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\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"); } void int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS) Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS; { Bit8u scan_code, ascii_code, shift_flags, led_flags, count; Bit16u kbd_code, max; shift_flags = read_byte(0x0040, 0x17); led_flags = read_byte(0x0040, 0x97); switch (GET_AH()) { case 0x00: /* read keyboard input */ if ( !dequeue_key(&scan_code, &ascii_code, 1) ) { BX_PANIC("KBD: int16h: out of keyboard input\n"); } if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0; else if (ascii_code == 0xE0) ascii_code = 0; AX = (scan_code << 8) | ascii_code; break; case 0x01: /* check keyboard status */ if ( !dequeue_key(&scan_code, &ascii_code, 0) ) { SET_ZF(); return; } if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0; else if (ascii_code == 0xE0) ascii_code = 0; AX = (scan_code << 8) | ascii_code; CLEAR_ZF(); break; case 0x02: /* get shift flag status */ shift_flags = read_byte(0x0040, 0x17); SET_AL(shift_flags); break; case 0x05: /* store key-stroke into buffer */ if ( !enqueue_key(GET_CH(), GET_CL()) ) { SET_AL(1); } else { SET_AL(0); } break; case 0x09: /* GET KEYBOARD FUNCTIONALITY */ // bit Bochs Description // 7 0 reserved // 6 0 INT 16/AH=20h-22h supported (122-key keyboard support) // 5 1 INT 16/AH=10h-12h supported (enhanced keyboard support) // 4 1 INT 16/AH=0Ah supported // 3 0 INT 16/AX=0306h supported // 2 0 INT 16/AX=0305h supported // 1 0 INT 16/AX=0304h supported // 0 0 INT 16/AX=0300h supported // SET_AL(0x30); break; case 0x10: /* read MF-II keyboard input */ if ( !dequeue_key(&scan_code, &ascii_code, 1) ) { BX_PANIC("KBD: int16h: out of keyboard input\n"); } if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0; AX = (scan_code << 8) | ascii_code; break; case 0x11: /* check MF-II keyboard status */ if ( !dequeue_key(&scan_code, &ascii_code, 0) ) { SET_ZF(); return; } if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0; AX = (scan_code << 8) | ascii_code; CLEAR_ZF(); break; case 0x12: /* get extended keyboard status */ shift_flags = read_byte(0x0040, 0x17); SET_AL(shift_flags); shift_flags = read_byte(0x0040, 0x18) & 0x73; shift_flags |= read_byte(0x0040, 0x96) & 0x0c; SET_AH(shift_flags); BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX); break; case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */ SET_AH(0x80); // function int16 ah=0x10-0x12 supported break; case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */ // don't change AH : function int16 ah=0x20-0x22 NOT supported break; case 0x6F: if (GET_AL() == 0x08) SET_AH(0x02); // unsupported, aka normal keyboard default: BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH()); } } unsigned int dequeue_key(scan_code, ascii_code, incr) Bit8u *scan_code; Bit8u *ascii_code; unsigned int incr; { Bit16u buffer_start, buffer_end, buffer_head, buffer_tail; Bit16u ss; Bit8u acode, scode; #if BX_CPU < 2 buffer_start = 0x001E; buffer_end = 0x003E; #else buffer_start = read_word(0x0040, 0x0080); buffer_end = read_word(0x0040, 0x0082); #endif buffer_head = read_word(0x0040, 0x001a); buffer_tail = read_word(0x0040, 0x001c); if (buffer_head != buffer_tail) { ss = get_SS(); acode = read_byte(0x0040, buffer_head); scode = read_byte(0x0040, buffer_head+1); write_byte(ss, ascii_code, acode); write_byte(ss, scan_code, scode); if (incr) { buffer_head += 2; if (buffer_head >= buffer_end) buffer_head = buffer_start; write_word(0x0040, 0x001a, buffer_head); } return(1); } else { return(0); } } void int09_function(DI, SI, BP, SP, BX, DX, CX, AX) Bit16u DI, SI, BP, SP, BX, DX, CX, AX; { Bit8u scancode, asciicode, shift_flags; Bit8u mf2_flags, mf2_state; // // DS has been set to F000 before call // scancode = GET_AL(); if (scancode == 0) { BX_INFO("KBD: int09 handler: AL=0\n"); return; } shift_flags = read_byte(0x0040, 0x17); mf2_flags = read_byte(0x0040, 0x18); mf2_state = read_byte(0x0040, 0x96); asciicode = 0; switch (scancode) { case 0x3a: /* Caps Lock press */ shift_flags ^= 0x40; write_byte(0x0040, 0x17, shift_flags); mf2_flags |= 0x40; write_byte(0x0040, 0x18, mf2_flags); break; case 0xba: /* Caps Lock release */ mf2_flags &= ~0x40; write_byte(0x0040, 0x18, mf2_flags); break; case 0x2a: /* L Shift press */ shift_flags |= 0x02; write_byte(0x0040, 0x17, shift_flags); break; case 0xaa: /* L Shift release */ shift_flags &= ~0x02; write_byte(0x0040, 0x17, shift_flags); break; case 0x36: /* R Shift press */ shift_flags |= 0x01; write_byte(0x0040, 0x17, shift_flags); break; case 0xb6: /* R Shift release */ shift_flags &= ~0x01; write_byte(0x0040, 0x17, shift_flags); break; case 0x1d: /* Ctrl press */ if ((mf2_state & 0x01) == 0) { shift_flags |= 0x04; write_byte(0x0040, 0x17, shift_flags); if (mf2_state & 0x02) { mf2_state |= 0x04; write_byte(0x0040, 0x96, mf2_state); } else { mf2_flags |= 0x01; write_byte(0x0040, 0x18, mf2_flags); } } break; case 0x9d: /* Ctrl release */ if ((mf2_state & 0x01) == 0) { shift_flags &= ~0x04; write_byte(0x0040, 0x17, shift_flags); if (mf2_state & 0x02) { mf2_state &= ~0x04; write_byte(0x0040, 0x96, mf2_state); } else { mf2_flags &= ~0x01; write_byte(0x0040, 0x18, mf2_flags); } } break; case 0x38: /* Alt press */ shift_flags |= 0x08; write_byte(0x0040, 0x17, shift_flags); if (mf2_state & 0x02) { mf2_state |= 0x08; write_byte(0x0040, 0x96, mf2_state); } else { mf2_flags |= 0x02; write_byte(0x0040, 0x18, mf2_flags); } break; case 0xb8: /* Alt release */ shift_flags &= ~0x08; write_byte(0x0040, 0x17, shift_flags); if (mf2_state & 0x02) { mf2_state &= ~0x08; write_byte(0x0040, 0x96, mf2_state); } else { mf2_flags &= ~0x02; write_byte(0x0040, 0x18, mf2_flags); } break; case 0x45: /* Num Lock press */ if ((mf2_state & 0x03) == 0) { mf2_flags |= 0x20; write_byte(0x0040, 0x18, mf2_flags); shift_flags ^= 0x20; write_byte(0x0040, 0x17, shift_flags); } break; case 0xc5: /* Num Lock release */ if ((mf2_state & 0x03) == 0) { mf2_flags &= ~0x20; write_byte(0x0040, 0x18, mf2_flags); } break; case 0x46: /* Scroll Lock press */ mf2_flags |= 0x10; write_byte(0x0040, 0x18, mf2_flags); shift_flags ^= 0x10; write_byte(0x0040, 0x17, shift_flags); break; case 0xc6: /* Scroll Lock release */ mf2_flags &= ~0x10; write_byte(0x0040, 0x18, mf2_flags); break; default: if (scancode & 0x80) { break; /* toss key releases ... */ } if (scancode > MAX_SCAN_CODE) { BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode); return; } if (shift_flags & 0x08) { /* ALT */ asciicode = scan_to_scanascii[scancode].alt; scancode = scan_to_scanascii[scancode].alt >> 8; } else if (shift_flags & 0x04) { /* CONTROL */ asciicode = scan_to_scanascii[scancode].control; scancode = scan_to_scanascii[scancode].control >> 8; } else if (((mf2_state & 0x02) > 0) && ((scancode >= 0x47) && (scancode <= 0x53))) { /* extended keys handling */ asciicode = 0xe0; scancode = scan_to_scanascii[scancode].normal >> 8; } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */ /* check if lock state should be ignored * because a SHIFT key are pressed */ if (shift_flags & scan_to_scanascii[scancode].lock_flags) { asciicode = scan_to_scanascii[scancode].normal; scancode = scan_to_scanascii[scancode].normal >> 8; } else { asciicode = scan_to_scanascii[scancode].shift; scancode = scan_to_scanascii[scancode].shift >> 8; } } else { /* check if lock is on */ if (shift_flags & scan_to_scanascii[scancode].lock_flags) { asciicode = scan_to_scanascii[scancode].shift; scancode = scan_to_scanascii[scancode].shift >> 8; } else { asciicode = scan_to_scanascii[scancode].normal; scancode = scan_to_scanascii[scancode].normal >> 8; } } if (scancode==0 && asciicode==0) { BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n"); } enqueue_key(scancode, asciicode); break; } if ((scancode & 0x7f) != 0x1d) { mf2_state &= ~0x01; } mf2_state &= ~0x02; write_byte(0x0040, 0x96, mf2_state); } unsigned int enqueue_key(scan_code, ascii_code) Bit8u scan_code, ascii_code; { Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail; #if BX_CPU < 2 buffer_start = 0x001E; buffer_end = 0x003E; #else buffer_start = read_word(0x0040, 0x0080); buffer_end = read_word(0x0040, 0x0082); #endif buffer_head = read_word(0x0040, 0x001A); buffer_tail = read_word(0x0040, 0x001C); temp_tail = buffer_tail; buffer_tail += 2; if (buffer_tail >= buffer_end) buffer_tail = buffer_start; if (buffer_tail == buffer_head) { return(0); } write_byte(0x0040, temp_tail, ascii_code); write_byte(0x0040, temp_tail+1, scan_code); write_word(0x0040, 0x001C, buffer_tail); return(1); } #define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status) void int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS) Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS; { write_byte(0x0040, 0x008e, 0); // clear completion flag switch (GET_AH()) { case 0x08: SET_AL(0); SET_CH(0); SET_CL(0); SET_DH(0); SET_DL(0); /* FIXME returns 0, 1, or n hard drives */ // FIXME should set ES & DI goto int13_fail; break; default: BX_INFO("int13_harddisk: function %02xh unsupported, returns fail\n", GET_AH()); goto int13_fail; break; } int13_fail: SET_AH(0x01); // defaults to invalid function in AH or invalid parameter int13_fail_noah: SET_DISK_RET_STATUS(GET_AH()); int13_fail_nostatus: SET_CF(); // error occurred return; int13_success: SET_AH(0x00); // no error int13_success_noah: SET_DISK_RET_STATUS(0x00); CLEAR_CF(); // no error return; } void transf_sect(seg, offset) Bit16u seg; Bit16u offset; { ASM_START push bp mov bp, sp push ax push bx push cx push dx push di push ds mov ax, 4[bp] ; segment mov ds, ax mov bx, 6[bp] ; offset mov dx, #0xe000 mov cx, #256 xor di, di one_sect: in ax, dx ; read word from flash mov [bx+di], ax ; write word inc dx inc dx inc di inc di loop one_sect pop ds pop di pop dx pop cx pop bx pop ax pop bp ASM_END } void int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS) Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS; { Bit8u drive, num_sectors, track, sector, head, status; Bit16u base_address, base_count, base_es; Bit8u page, mode_register, val8, dor; Bit8u return_status[7]; Bit8u drive_type, num_floppies, ah; Bit16u es, last_addr; Bit16u log_sector, tmp, i, j; ah = GET_AH(); switch ( ah ) { case 0x00: // diskette controller reset SET_AH(0); set_diskette_ret_status(0); CLEAR_CF(); // successful set_diskette_current_cyl(drive, 0); // current cylinder return; case 0x02: // Read Diskette Sectors num_sectors = GET_AL(); track = GET_CH(); sector = GET_CL(); head = GET_DH(); drive = GET_ELDL(); if ((drive > 1) || (head > 1) || (sector == 0) || (num_sectors == 0) || (num_sectors > 72)) { BX_INFO("int13_diskette: read/write/verify: parameter out of range\n"); SET_AH(1); set_diskette_ret_status(1); SET_AL(0); // no sectors read SET_CF(); // error occurred return; } page = (ES >> 12); // upper 4 bits base_es = (ES << 4); // lower 16bits contributed by ES base_address = base_es + BX; // lower 16 bits of address // contributed by ES:BX if ( base_address < base_es ) { // in case of carry, adjust page by 1 page++; } base_count = (num_sectors * 512) - 1; // check for 64K boundary overrun last_addr = base_address + base_count; if (last_addr < base_address) { SET_AH(0x09); set_diskette_ret_status(0x09); SET_AL(0); // no sectors read SET_CF(); // error occurred return; } log_sector = track * 36 + head * 18 + sector - 1; last_addr = page << 12; // Configure the sector address for (j=0; j<num_sectors; j++) { outw(0xe000, log_sector+j); base_count = base_address + (j << 9); transf_sect (last_addr, base_count); } // ??? should track be new val from return_status[3] ? set_diskette_current_cyl(drive, track); // AL = number of sectors read (same value as passed) SET_AH(0x00); // success CLEAR_CF(); // success return; default: BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH()); // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) { SET_AH(0x01); // ??? set_diskette_ret_status(1); SET_CF(); return; // } } } void set_diskette_ret_status(value) Bit8u value; { write_byte(0x0040, 0x0041, value); } void set_diskette_current_cyl(drive, cyl) Bit8u drive; Bit8u cyl; { /* TEMP HACK: FOR MSDOS */ if (drive > 1) drive = 1; /* BX_PANIC("set_diskette_current_cyl(): drive > 1\n"); */ write_byte(0x0040, 0x0094+drive, cyl); } void int19_function(seq_nr) Bit16u seq_nr; { Bit16u ebda_seg=read_word(0x0040,0x000E); Bit16u bootdev; Bit8u bootdrv; Bit8u bootchk; Bit16u bootseg; Bit16u bootip; Bit16u status; Bit16u bootfirst; ipl_entry_t e; // Here we assume that BX_ELTORITO_BOOT is defined, so // CMOS regs 0x3D and 0x38 contain the boot sequence: // CMOS reg 0x3D & 0x0f : 1st boot device // CMOS reg 0x3D & 0xf0 : 2nd boot device // CMOS reg 0x38 & 0xf0 : 3rd boot device // boot device codes: // 0x00 : not defined // 0x01 : first floppy // 0x02 : first harddrive // 0x03 : first cdrom // 0x04 - 0x0f : PnP expansion ROMs (e.g. Etherboot) // else : boot failure // Get the boot sequence /* * Zet: we don't have a CMOS device * bootdev = inb_cmos(0x3d); bootdev |= ((inb_cmos(0x38) & 0xf0) << 4); bootdev >>= 4 * seq_nr; bootdev &= 0xf; */ bootdev = 0x1; /* Read user selected device */ bootfirst = read_word(IPL_SEG, IPL_BOOTFIRST_OFFSET); if (bootfirst != 0xFFFF) { bootdev = bootfirst; /* User selected device not set */ write_word(IPL_SEG, IPL_BOOTFIRST_OFFSET, 0xFFFF); /* Reset boot sequence */ write_word(IPL_SEG, IPL_SEQUENCE_OFFSET, 0xFFFF); } else if (bootdev == 0) BX_PANIC("No bootable device.\n"); /* Translate from CMOS runes to an IPL table offset by subtracting 1 */ bootdev -= 1; /* Read the boot device from the IPL table */ if (get_boot_vector(bootdev, &e) == 0) { BX_INFO("Invalid boot device (0x%x)\n", bootdev); return; } /* Do the loading, and set up vector as a far pointer to the boot * address, and bootdrv as the boot drive */ print_boot_device(&e); switch(e.type) { case IPL_TYPE_FLOPPY: /* FDD */ case IPL_TYPE_HARDDISK: /* HDD */ bootdrv = (e.type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00; bootseg = 0x07c0; status = 0; ASM_START push bp mov bp, sp push ax push bx push cx push dx mov dl, _int19_function.bootdrv + 2[bp] mov ax, _int19_function.bootseg + 2[bp] mov es, ax ;; segment xor bx, bx ;; offset mov ah, #0x02 ;; function 2, read diskette sector mov al, #0x01 ;; read 1 sector mov ch, #0x00 ;; track 0 mov cl, #0x01 ;; sector 1 mov dh, #0x00 ;; head 0 int #0x13 ;; read sector jnc int19_load_done mov ax, #0x0001 mov _int19_function.status + 2[bp], ax int19_load_done: pop dx pop cx pop bx pop ax pop bp ASM_END if (status != 0) { print_boot_failure(e.type, 1); return; } /* Canonicalize bootseg:bootip */ bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; break; default: return; } /* Debugging info */ BX_INFO("Booting from %x:%x\n", bootseg, bootip); /* Jump to the boot vector */ ASM_START mov bp, sp ;; Build an iret stack frame that will take us to the boot vector. ;; iret pops ip, then cs, then flags, so push them in the opposite order. pushf mov ax, _int19_function.bootseg + 0[bp] push ax mov ax, _int19_function.bootip + 0[bp] push ax ;; Set the magic number in ax and the boot drive in dl. mov ax, #0xaa55 mov dl, _int19_function.bootdrv + 0[bp] ;; Zero some of the other registers. xor bx, bx mov ds, bx mov es, bx mov bp, bx ;; Go! iret ASM_END } void int1a_function(regs, ds, iret_addr) pusha_regs_t regs; // regs pushed from PUSHA instruction Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper iret_addr_t iret_addr; // CS,IP,Flags pushed from original INT call { Bit8u val8; ASM_START sti ASM_END switch (regs.u.r8.ah) { case 0: // get current clock count ASM_START cli ASM_END regs.u.r16.cx = BiosData->ticks_high; regs.u.r16.dx = BiosData->ticks_low; regs.u.r8.al = BiosData->midnight_flag; BiosData->midnight_flag = 0; // reset flag ASM_START sti ASM_END // AH already 0 ClearCF(iret_addr.flags); // OK break; default: SetCF(iret_addr.flags); // Unsupported } } ASM_START ;---------------------- ;- INT13h (relocated) - ;---------------------- ; ; int13_relocated is a little bit messed up since I played with it ; I have to rewrite it: ; - call a function that detect which function to call ; - make all called C function get the same parameters list ; int13_relocated: push ax push cx push dx push bx int13_legacy: push dx ;; push eltorito value of dx instead of sp push bp push si push di push es push ds push ss pop ds ;; now the 16-bit registers can be restored with: ;; pop ds; pop es; popa; iret ;; arguments passed to functions should be ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS test dl, #0x80 jnz int13_notfloppy mov ax, #int13_out push ax jmp _int13_diskette_function int13_notfloppy: int13_disk: ;; int13_harddisk modifies high word of EAX ; shr eax, #16 ; push ax call _int13_harddisk ; pop ax ; shl eax, #16 int13_out: pop ds pop es ; popa ; we do this instead: pop di pop si pop bp add sp, #2 pop bx pop dx pop cx pop ax iret ;---------- ;- INT18h - ;---------- int18_handler: ;; Boot Failure recovery: try the next device. ;; Reset SP and SS mov ax, #0xfffe mov sp, ax xor ax, ax mov ss, ax ;; Get the boot sequence number out of the IPL memory mov bx, #IPL_SEG mov ds, bx ;; Set segment mov bx, IPL_SEQUENCE_OFFSET ;; BX is now the sequence number inc bx ;; ++ mov IPL_SEQUENCE_OFFSET, bx ;; Write it back mov ds, ax ;; and reset the segment to zero. ;; Carry on in the INT 19h handler, using the new sequence number push bx jmp int19_next_boot ;---------- ;- INT19h - ;---------- int19_relocated: ;; Boot function, relocated ;; int19 was beginning to be really complex, so now it ;; just calls a C function that does the work push bp mov bp, sp ;; Reset SS and SP mov ax, #0xfffe mov sp, ax xor ax, ax mov ss, ax ;; Start from the first boot device (0, in AX) mov bx, #IPL_SEG mov ds, bx ;; Set segment to write to the IPL memory mov IPL_SEQUENCE_OFFSET, ax ;; Save the sequence number mov ds, ax ;; and reset the segment. push ax int19_next_boot: ;; Call the C code for the next boot device call _int19_function ;; Boot failed: invoke the boot recovery function int #0x18 ;---------- ;- INT1Ch - ;---------- int1c_handler: ;; User Timer Tick iret ;-------------------- ;- POST: EBDA segment ;-------------------- ; relocated here because the primary POST area isnt big enough. ebda_post: xor ax, ax ; mov EBDA seg into 40E mov ds, ax mov word ptr [0x40E], #EBDA_SEG ret;; rom_checksum: push ax push bx push cx xor ax, ax xor bx, bx xor cx, cx mov ch, [2] shl cx, #1 checksum_loop: add al, [bx] inc bx loop checksum_loop and al, #0xff pop cx pop bx pop ax ret ;; We need a copy of this string, but we are not actually a PnP BIOS, ;; so make sure it is *not* aligned, so OSes will not see it if they scan. .align 16 db 0 pnp_string: .ascii "$PnP" rom_scan: ;; Scan for existence of valid expansion ROMS. ;; Video ROM: from 0xC0000..0xC7FFF in 2k increments ;; General ROM: from 0xC8000..0xDFFFF in 2k increments ;; System ROM: only 0xE0000 ;; ;; Header: ;; Offset Value ;; 0 0x55 ;; 1 0xAA ;; 2 ROM length in 512-byte blocks ;; 3 ROM initialization entry point (FAR CALL) rom_scan_loop: push ax ;; Save AX mov ds, cx mov ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k cmp [0], #0xAA55 ;; look for signature jne rom_scan_increment call rom_checksum jnz rom_scan_increment mov al, [2] ;; change increment to ROM length in 512-byte blocks ;; We want our increment in 512-byte quantities, rounded to ;; the nearest 2k quantity, since we only scan at 2k intervals. test al, #0x03 jz block_count_rounded and al, #0xfc ;; needs rounding up add al, #0x04 block_count_rounded: xor bx, bx ;; Restore DS back to 0000: mov ds, bx push ax ;; Save AX push di ;; Save DI ;; Push addr of ROM entry point push cx ;; Push seg ;; push #0x0003 ;; Push offset - not an 8086 valid operand mov ax, #0x0003 push ax ;; Point ES:DI at "$PnP", which tells the ROM that we are a PnP BIOS. ;; That should stop it grabbing INT 19h; we will use its BEV instead. mov ax, #0xf000 mov es, ax lea di, pnp_string mov bp, sp ;; Call ROM init routine using seg:off on stack db 0xff ;; call_far ss:[bp+0] db 0x5e db 0 cli ;; In case expansion ROM BIOS turns IF on add sp, #2 ;; Pop offset value pop cx ;; Pop seg value (restore CX) ;; Look at the ROM's PnP Expansion header. Properly, we're supposed ;; to init all the ROMs and then go back and build an IPL table of ;; all the bootable devices, but we can get away with one pass. mov ds, cx ;; ROM base mov bx, 0x001a ;; 0x1A is the offset into ROM header that contains... mov ax, [bx] ;; the offset of PnP expansion header, where... cmp ax, #0x5024 ;; we look for signature "$PnP" jne no_bev mov ax, 2[bx] cmp ax, #0x506e jne no_bev mov ax, 0x1a[bx] ;; 0x1A is also the offset into the expansion header of... cmp ax, #0x0000 ;; the Bootstrap Entry Vector, or zero if there is none. je no_bev ;; Found a device that thinks it can boot the system. Record its BEV and product name string. mov di, 0x10[bx] ;; Pointer to the product name string or zero if none mov bx, #IPL_SEG ;; Go to the segment where the IPL table lives mov ds, bx mov bx, IPL_COUNT_OFFSET ;; Read the number of entries so far cmp bx, #IPL_TABLE_ENTRIES je no_bev ;; Get out if the table is full push cx mov cx, #0x4 ;; Zet: Needed to be compatible with 8086 shl bx, cl ;; Turn count into offset (entries are 16 bytes) pop cx mov 0[bx], #IPL_TYPE_BEV ;; This entry is a BEV device mov 6[bx], cx ;; Build a far pointer from the segment... mov 4[bx], ax ;; and the offset cmp di, #0x0000 je no_prod_str mov 0xA[bx], cx ;; Build a far pointer from the segment... mov 8[bx], di ;; and the offset no_prod_str: push cx mov cx, #0x4 shr bx, cl ;; Turn the offset back into a count pop cx inc bx ;; We have one more entry now mov IPL_COUNT_OFFSET, bx ;; Remember that. no_bev: pop di ;; Restore DI pop ax ;; Restore AX rom_scan_increment: push cx mov cx, #5 shl ax, cl ;; convert 512-bytes blocks to 16-byte increments ;; because the segment selector is shifted left 4 bits. pop cx add cx, ax pop ax ;; Restore AX cmp cx, ax jbe rom_scan_loop xor ax, ax ;; Restore DS back to 0000: mov ds, ax ret ;; for 'C' strings and other data, insert them here with ;; a the following hack: ;; DATA_SEG_DEFS_HERE ;; the following area can be used to write dynamically generated tables .align 16 bios_table_area_start: dd 0xaafb4442 dd bios_table_area_end - bios_table_area_start - 8; ;-------- ;- POST - ;-------- .org 0xe05b ; POST Entry Point post: xor ax, ax normal_post: ; case 0: normal startup cli mov ax, #0xfffe mov sp, ax xor ax, ax mov ds, ax mov ss, ax ;; zero out BIOS data area (40:00..40:ff) mov es, ax mov cx, #0x0080 ;; 128 words mov di, #0x0400 cld rep stosw ;; set all interrupts to default handler xor bx, bx ;; offset index mov cx, #0x0100 ;; counter (256 interrupts) mov ax, #dummy_iret_handler mov dx, #0xF000 post_default_ints: mov [bx], ax add bx, #2 mov [bx], dx add bx, #2 loop post_default_ints ;; set vector 0x79 to zero ;; this is used by 'gardian angel' protection system SET_INT_VECTOR(0x79, #0, #0) ;; base memory in K 40:13 (word) mov ax, #BASE_MEM_IN_K mov 0x0413, ax ;; Manufacturing Test 40:12 ;; zerod out above ;; Warm Boot Flag 0040:0072 ;; value of 1234h = skip memory checks ;; zerod out above ;; Bootstrap failure vector SET_INT_VECTOR(0x18, #0xF000, #int18_handler) ;; Bootstrap Loader vector SET_INT_VECTOR(0x19, #0xF000, #int19_handler) ;; User Timer Tick vector SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler) ;; Memory Size Check vector SET_INT_VECTOR(0x12, #0xF000, #int12_handler) ;; Equipment Configuration Check vector SET_INT_VECTOR(0x11, #0xF000, #int11_handler) ;; EBDA setup call ebda_post ;; Keyboard SET_INT_VECTOR(0x09, #0xF000, #int09_handler) SET_INT_VECTOR(0x16, #0xF000, #int16_handler) xor ax, ax mov ds, ax mov 0x0417, al /* keyboard shift flags, set 1 */ mov 0x0418, al /* keyboard shift flags, set 2 */ mov 0x0419, al /* keyboard alt-numpad work area */ mov 0x0471, al /* keyboard ctrl-break flag */ mov 0x0497, al /* keyboard status flags 4 */ mov al, #0x10 mov 0x0496, al /* keyboard status flags 3 */ /* keyboard head of buffer pointer */ mov bx, #0x001E mov 0x041A, bx /* keyboard end of buffer pointer */ mov 0x041C, bx /* keyboard pointer to start of buffer */ mov bx, #0x001E mov 0x0480, bx /* keyboard pointer to end of buffer */ mov bx, #0x003E mov 0x0482, bx ;; CMOS RTC SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler) ;; Video setup SET_INT_VECTOR(0x10, #0xF000, #int10_handler) mov cx, #0xc000 ;; init vga bios mov ax, #0xc780 call rom_scan call _print_bios_banner ;; Floppy setup SET_INT_VECTOR(0x13, #0xF000, #int13_handler) call _init_boot_vectors mov cx, #0xc800 ;; init option roms mov ax, #0xe000 call rom_scan sti ;; enable interrupts int #0x19 ;------------------------------------------- ;- INT 13h Fixed Disk Services Entry Point - ;------------------------------------------- .org 0xe3fe ; INT 13h Fixed Disk Services Entry Point int13_handler: //JMPL(int13_relocated) jmp int13_relocated .org 0xe401 ; Fixed Disk Parameter Table ;---------- ;- INT19h - ;---------- .org 0xe6f2 ; INT 19h Boot Load Service Entry Point int19_handler: jmp int19_relocated ;---------------------------------------- ;- INT 16h Keyboard Service Entry Point - ;---------------------------------------- .org 0xe82e int16_handler: sti push ds pushf ;pusha ; we do this instead: push ax push cx push dx push bx push sp mov bx, sp sseg add [bx], #10 sseg mov bx, [bx+2] push bp push si push di cmp ah, #0x00 je int16_F00 cmp ah, #0x10 je int16_F00 mov bx, #0xf000 mov ds, bx call _int16_function ; popa ; we do this instead: pop di pop si pop bp add sp, #2 pop bx pop dx pop cx pop ax popf pop ds jz int16_zero_set int16_zero_clear: push bp mov bp, sp //SEG SS and BYTE [bp + 0x06], #0xbf pop bp iret int16_zero_set: push bp mov bp, sp //SEG SS or BYTE [bp + 0x06], #0x40 pop bp iret int16_F00: mov bx, #0x0040 mov ds, bx int16_wait_for_key: cli mov bx, 0x001a cmp bx, 0x001c jne int16_key_found sti nop #if 0 /* no key yet, call int 15h, function AX=9002 */ 0x50, /* push AX */ 0xb8, 0x02, 0x90, /* mov AX, #0x9002 */ 0xcd, 0x15, /* int 15h */ 0x58, /* pop AX */ 0xeb, 0xea, /* jmp WAIT_FOR_KEY */ #endif jmp int16_wait_for_key int16_key_found: mov bx, #0xf000 mov ds, bx call _int16_function ; popa ; we do this instead: pop di pop si pop bp add sp, #2 pop bx pop dx pop cx pop ax popf pop ds #if 0 /* notify int16 complete w/ int 15h, function AX=9102 */ 0x50, /* push AX */ 0xb8, 0x02, 0x91, /* mov AX, #0x9102 */ 0xcd, 0x15, /* int 15h */ 0x58, /* pop AX */ #endif iret ;------------------------------------------------- ;- INT09h : Keyboard Hardware Service Entry Point - ;------------------------------------------------- .org 0xe987 int09_handler: cli push ax in al, #0x60 ;;read key from keyboard controller sti push ds ;pusha ; we do this instead: push ax push cx push dx push bx push sp mov bx, sp sseg add [bx], #10 sseg mov bx, [bx+2] push bp push si push di ;; check for extended key cmp al, #0xe0 jne int09_check_pause xor ax, ax mov ds, ax mov al, BYTE [0x496] ;; mf2_state |= 0x02 or al, #0x02 mov BYTE [0x496], al jmp int09_done int09_check_pause: ;; check for pause key cmp al, #0xe1 jne int09_process_key xor ax, ax mov ds, ax mov al, BYTE [0x496] ;; mf2_state |= 0x01 or al, #0x01 mov BYTE [0x496], al jmp int09_done int09_process_key: mov bx, #0xf000 mov ds, bx call _int09_function int09_done: ; popa ; we do this instead: pop di pop si pop bp add sp, #2 pop bx pop dx pop cx pop ax pop ds cli pop ax iret ;---------- ;- INT10h - ;---------- .org 0xf065 ; INT 10h Video Support Service Entry Point int10_handler: ;; dont do anything, since the VGA BIOS handles int10h requests iret .org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh) ;---------- ;- INT12h - ;---------- .org 0xf841 ; INT 12h Memory Size Service Entry Point ; ??? different for Pentium (machine check)? int12_handler: push ds mov ax, #0x0040 mov ds, ax mov ax, 0x0013 pop ds iret ;---------- ;- INT11h - ;---------- .org 0xf84d ; INT 11h Equipment List Service Entry Point int11_handler: push ds mov ax, #0x0040 mov ds, ax mov ax, 0x0010 pop ds iret ;---------- ;- INT1Ah - ;---------- .org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point int1a_handler: push ds ;pusha ; we do this instead: push ax push cx push dx push bx push sp mov bx, sp sseg add [bx], #10 sseg mov bx, [bx+2] push bp push si push di xor ax, ax mov ds, ax int1a_callfunction: call _int1a_function ; popa ; we do this instead: pop di pop si pop bp add sp, #2 pop bx pop dx pop cx pop ax pop ds iret ;------------------------------------------------ ;- IRET Instruction for Dummy Interrupt Handler - ;------------------------------------------------ .org 0xff53 ; IRET Instruction for Dummy Interrupt Handler dummy_iret_handler: iret .org 0xfff0 ; Power-up Entry Point ; hlt jmp 0xf000:post .org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY .ascii BIOS_BUILD_DATE .org 0xfffe ; System Model ID db SYS_MODEL_ID db 0x00 ; filler ASM_END ASM_START .org 0xcc00 bios_table_area_end: // bcc-generated data will be placed here ASM_END
Go to most recent revision | Compare with Previous | Blame | View Log