/* * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 2.0 (the "License"). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. * * The Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright 1993 NeXT Computer, Inc. * All rights reserved. */ /* Copyright 2007 David Elliott 2007-12-30 dfe - Enhanced code to normalize segment/offset to huge pointers so that any linear address within the first MB of memory can be passed to BIOS functions. This allows some of the __DATA sections to span into the next segment and also allows stack variables to be used whereas the old code could only operate on static data in the first 64k. NOTE: Requires bios.s change to respect DS. */ /* Copyright 2007 VMware Inc. 2007-12-29 dfe - Added ebiosEjectMedia */ #include "bootstruct.h" #include "libsaio.h" #include "machsections.h" #define MAX_DRIVES 8 static biosBuf_t bb; int bgetc(void) SECTION_ATTR_FOR_LOTEXT_OR_TEXT; int bgetc(void) { /* Poll for the next character. Most real BIOS do not need this as the INT 16h,AH=0h function will block until one is received. Unfortunately, Apple's EFI CSM will never wake up. This idea is lifted from the grub-a20.patch to GRUB's stage2/asm.S file. */ while(!readKeyboardStatus()) ; bb.intno = 0x16; bb.eax.r.h = 0x00; bios(&bb); return bb.eax.rr; } int readKeyboardStatus(void) SECTION_ATTR_FOR_LOTEXT_OR_TEXT; int readKeyboardStatus(void) { bb.intno = 0x16; bb.eax.r.h = 0x01; bios(&bb); if (bb.flags.zf) { return 0; } else { return bb.eax.rr; } } int readKeyboardShiftFlags(void) { bb.intno = 0x16; bb.eax.r.h = 0x02; bios(&bb); return bb.eax.r.l; } unsigned int time18(void) { union { struct { unsigned int low:16; unsigned int high:16; } s; unsigned int i; } time; bb.intno = 0x1a; bb.eax.r.h = 0x00; bios(&bb); time.s.low = bb.edx.rr; time.s.high = bb.ecx.rr; return time.i; } unsigned long getMemoryMap( MemoryRange * rangeArray, unsigned long maxRangeCount, unsigned long * conMemSizePtr, unsigned long * extMemSizePtr ) { #define kMemoryMapSignature 'SMAP' #define kDescriptorSizeMin 20 MemoryRange * range = (MemoryRange *)BIOS_ADDR; unsigned long count = 0; unsigned long long conMemSize = 0; unsigned long long extMemSize = 0; // Prepare for the INT15 E820h call. Each call returns a single // memory range. A continuation value is returned that must be // provided on a subsequent call to fetch the next range. // // Certain BIOSes (Award 6.00PG) expect the upper word in EAX // to be cleared on entry, otherwise only a single range will // be reported. // // Some BIOSes will simply ignore the value of ECX on entry. // Probably best to keep its value at 20 to avoid surprises. //printf("Get memory map 0x%x, %d\n", rangeArray);getc(); if (maxRangeCount > (BIOS_LEN / sizeof(MemoryRange))) { maxRangeCount = (BIOS_LEN / sizeof(MemoryRange)); } bb.ebx.rx = 0; // Initial continuation value must be zero. while ( count < maxRangeCount ) { bb.intno = 0x15; bb.eax.rx = 0xe820; bb.ecx.rx = kDescriptorSizeMin; bb.edx.rx = kMemoryMapSignature; bb.edi.rr = NORMALIZED_OFFSET( (unsigned long) range ); bb.es = NORMALIZED_SEGMENT( (unsigned long) range ); bios(&bb); // Check for errors. if ( bb.flags.cf || bb.eax.rx != kMemoryMapSignature || bb.ecx.rx != kDescriptorSizeMin ) { //printf("Got an error %x %x %x\n", bb.flags.cf, // bb.eax.rx, bb.ecx.rx); break; } // Tally up the conventional/extended memory sizes. if ( range->type == kMemoryRangeUsable || range->type == kMemoryRangeACPI || range->type == kMemoryRangeNVS ) { // Tally the conventional memory ranges. if ( range->base + range->length <= 0xa0000 ) { conMemSize += range->length; } // Record the top of extended memory. if ( range->base >= EXTENDED_ADDR ) { extMemSize += range->length; } } range++; count++; // Is this the last address range? if ( bb.ebx.rx == 0 ) { //printf("last range\n"); break; } } *conMemSizePtr = conMemSize / 1024; // size in KB *extMemSizePtr = extMemSize / 1024; // size in KB // Copy out data bcopy((char *)BIOS_ADDR, rangeArray, ((char *)range - (char *)BIOS_ADDR)); #if DEBUG { int i; printf("%d total ranges\n", count);getc(); for (i=0, range = rangeArray; itype, (unsigned int)range->base, (unsigned int)range->length); getc(); } } #endif return count; } unsigned long getExtendedMemorySize() { // Get extended memory size for large configurations. Not used unless // the INT15, E820H call (Get System Address Map) failed. // // Input: // // AX Function Code E801h // // Outputs: // // CF Carry Flag Carry cleared indicates no error. // AX Extended 1 Number of contiguous KB between 1 and 16 MB, // maximum 0x3C00 = 15 MB. // BX Extended 2 Number of contiguous 64 KB blocks between // 16 MB and 4 GB. // CX Configured 1 Number of contiguous KB between 1 and 16 MB, // maximum 0x3C00 = 15 MB. // DX Configured 2 Number of contiguous 64 KB blocks between // 16 MB and 4 GB. bb.intno = 0x15; bb.eax.rx = 0xe801; bios(&bb); // Return the size of memory above 1MB (extended memory) in kilobytes. if ( bb.flags.cf == 0 ) return (bb.ebx.rr * 64 + bb.eax.rr); // Get Extended memory size. Called on last resort since the return // value is limited to 16-bits (a little less than 64MB max). May // not be supported by modern BIOSes. // // Input: // // AX Function Code E801h // // Outputs: // // CF Carry Flag Carry cleared indicates no error. // AX Memory Count Number of contiguous KB above 1MB. bb.intno = 0x15; bb.eax.rx = 0x88; bios(&bb); // Return the size of memory above 1MB (extended memory) in kilobytes. return bb.flags.cf ? 0 : bb.eax.rr; } unsigned long getConventionalMemorySize() { bb.intno = 0x12; bios(&bb); return bb.eax.rr; // kilobytes } void video_mode(int mode) { bb.intno = 0x10; bb.eax.r.h = 0x00; bb.eax.r.l = mode; bios(&bb); } int biosread(int dev, int cyl, int head, int sec, int num) { int i; bb.intno = 0x13; sec += 1; /* sector numbers start at 1 */ for (i=0;;) { bb.ecx.r.h = cyl; bb.ecx.r.l = ((cyl & 0x300) >> 2) | (sec & 0x3F); bb.edx.r.h = head; bb.edx.r.l = dev; bb.eax.r.l = num; bb.ebx.rr = OFFSET(ptov(BIOS_ADDR)); bb.es = SEGMENT(ptov(BIOS_ADDR)); bb.eax.r.h = 0x02; bios(&bb); if ((bb.eax.r.h == 0x00) || (i++ >= 5)) break; /* reset disk subsystem and try again */ bb.eax.r.h = 0x00; bios(&bb); } return bb.eax.r.h; } int ebiosread(int dev, long sec, int count) { int i; static struct { unsigned char size; unsigned char reserved; unsigned char numblocks; unsigned char reserved2; unsigned short bufferOffset; unsigned short bufferSegment; unsigned long long startblock; } addrpacket __attribute__((aligned(16))) = {0}; addrpacket.size = sizeof(addrpacket); for (i=0;;) { bb.intno = 0x13; bb.eax.r.h = 0x42; bb.edx.r.l = dev; bb.esi.rr = NORMALIZED_OFFSET((unsigned)&addrpacket); bb.ds = NORMALIZED_SEGMENT((unsigned)&addrpacket); addrpacket.reserved = addrpacket.reserved2 = 0; addrpacket.numblocks = count; addrpacket.bufferOffset = OFFSET(ptov(BIOS_ADDR)); addrpacket.bufferSegment = SEGMENT(ptov(BIOS_ADDR)); addrpacket.startblock = sec; bios(&bb); if ((bb.eax.r.h == 0x00) || (i++ >= 5)) break; /* reset disk subsystem and try again */ bb.eax.r.h = 0x00; bios(&bb); } return bb.eax.r.h; } int ebioswrite(int dev, long sec, int count) { int i; static struct { unsigned char size; unsigned char reserved; unsigned char numblocks; unsigned char reserved2; unsigned short bufferOffset; unsigned short bufferSegment; unsigned long long startblock; } addrpacket __attribute__((aligned(16))) = {0}; addrpacket.size = sizeof(addrpacket); for (i=0;;) { bb.intno = 0x13; bb.eax.r.l = 0; /* Don't verify */ bb.eax.r.h = 0x43; bb.edx.r.l = dev; bb.esi.rr = NORMALIZED_OFFSET((unsigned)&addrpacket); bb.ds = NORMALIZED_SEGMENT((unsigned)&addrpacket); addrpacket.reserved = addrpacket.reserved2 = 0; addrpacket.numblocks = count; addrpacket.bufferOffset = OFFSET(ptov(BIOS_ADDR)); addrpacket.bufferSegment = SEGMENT(ptov(BIOS_ADDR)); addrpacket.startblock = sec; bios(&bb); if ((bb.eax.r.h == 0x00) || (i++ >= 5)) break; /* reset disk subsystem and try again */ bb.eax.r.h = 0x00; bios(&bb); } return bb.eax.r.h; } void putc(int ch) SECTION_ATTR_FOR_LOTEXT_OR_TEXT; void putc(int ch) { bb.intno = 0x10; bb.ebx.r.h = 0x00; /* background black */ bb.ebx.r.l = 0x0F; /* foreground white */ bb.eax.r.h = 0x0e; bb.eax.r.l = ch; bios(&bb); } void putca(int ch, int attr, int repeat) { bb.intno = 0x10; bb.ebx.r.h = 0x00; /* page number */ bb.ebx.r.l = attr; /* attribute */ bb.eax.r.h = 0x9; bb.eax.r.l = ch; bb.ecx.rx = repeat; /* repeat count */ bios(&bb); } /* Check to see if the passed-in drive is in El Torito no-emulation mode. */ int is_no_emulation(int drive) { struct packet { unsigned char packet_size; unsigned char media_type; unsigned char drive_num; unsigned char ctrlr_index; unsigned long lba; unsigned short device_spec; unsigned short buffer_segment; unsigned short load_segment; unsigned short sector_count; unsigned char cyl_count; unsigned char sec_count; unsigned char head_count; unsigned char reseved; } __attribute__((packed)); static struct packet pkt; bzero(&pkt, sizeof(pkt)); pkt.packet_size = 0x13; bb.intno = 0x13; bb.eax.r.h = 0x4b; bb.eax.r.l = 0x01; // subfunc: get info bb.edx.r.l = drive; bb.esi.rr = NORMALIZED_OFFSET((unsigned)&pkt); bb.ds = NORMALIZED_SEGMENT((unsigned)&pkt); bios(&bb); #if DEBUG printf("el_torito info drive %x\n", drive); printf("--> cf %x, eax %x\n", bb.flags.cf, bb.eax.rr); printf("pkt_size: %x\n", pkt.packet_size); printf("media_type: %x\n", pkt.media_type); printf("drive_num: %x\n", pkt.drive_num); printf("device_spec: %x\n", pkt.device_spec); printf("press a key->\n");getc(); #endif /* Some BIOSes erroneously return cf = 1 */ /* Just check to see if the drive number is the same. */ if (pkt.drive_num == drive) { if ((pkt.media_type & 0x0F) == 0) { /* We are in no-emulation mode. */ return 1; } } return 0; } #if DEBUG /* * BIOS drive information. */ void print_drive_info(boot_drive_info_t *dp) { // printf("buf_size = %x\n", dp->params.buf_size); printf("info_flags = %x\n", dp->params.info_flags); printf(" phys_cyls = %lx\n", dp->params. phys_cyls); printf(" phys_heads = %lx\n", dp->params. phys_heads); printf(" phys_spt = %lx\n", dp->params. phys_spt); printf("phys_sectors = %lx%lx\n", ((unsigned long *)(&dp->params.phys_sectors))[1], ((unsigned long *)(&dp->params.phys_sectors))[0]); printf("phys_nbps = %x\n", dp->params.phys_nbps); // printf("dpte_offset = %x\n", dp->params.dpte_offset); // printf("dpte_segment = %x\n", dp->params.dpte_segment); // printf("key = %x\n", dp->params.key); // printf("path_len = %x\n", dp->params. path_len); // printf("reserved1 = %x\n", dp->params. reserved1); // printf("reserved2 = %x\n", dp->params.reserved2); //printf("bus_type[4] = %x\n", dp->params. bus_type[4]); //printf("interface_type[8] = %x\n", dp->params. interface_type[8]); //printf("interface_path[8] = %x\n", dp->params. interface_path[8]); //printf("dev_path[8] = %x\n", dp->params. dev_path[8]); // printf("reserved3 = %x\n", dp->params. reserved3); // printf("checksum = %x\n", dp->params. checksum); printf(" io_port_base = %x\n", dp->dpte.io_port_base); printf(" control_port_base = %x\n", dp->dpte.control_port_base); printf(" head_flags = %x\n", dp->dpte. head_flags); printf(" vendor_info = %x\n", dp->dpte. vendor_info); printf(" irq = %x\n", dp->dpte. irq); // printf(" irq_unused = %x\n", dp->dpte. irq_unused); printf(" block_count = %x\n", dp->dpte. block_count); printf(" dma_channe = %x\n", dp->dpte. dma_channel); printf(" dma_type = %x\n", dp->dpte. dma_type); printf(" pio_type = %x\n", dp->dpte. pio_type); printf(" pio_unused = %x\n", dp->dpte. pio_unused); printf(" option_flags = %x\n", dp->dpte.option_flags); // printf(" reserved = %x\n", dp->dpte.reserved); printf(" revision = %x\n", dp->dpte. revision); // printf(" checksum = %x\n", dp->dpte. checksum); } #endif int get_drive_info(int drive, struct driveInfo *dp) { boot_drive_info_t *di = &dp->di; int ret = 0; #if UNUSED if (maxhd == 0) { bb.intno = 0x13; bb.eax.r.h = 0x08; bb.edx.r.l = 0x80; bios(&bb); if (bb.flags.cf == 0) maxhd = 0x7f + bb.edx.r.l; }; if (drive > maxhd) return 0; #endif bzero(dp, sizeof(struct driveInfo)); dp->biosdev = drive; /* Check for El Torito no-emulation mode. */ dp->no_emulation = is_no_emulation(drive); /* Check drive for EBIOS support. */ bb.intno = 0x13; bb.eax.r.h = 0x41; bb.edx.r.l = drive; bb.ebx.rr = 0x55aa; bios(&bb); if((bb.ebx.rr == 0xaa55) && (bb.flags.cf == 0)) { /* Get flags for supported operations. */ dp->uses_ebios = bb.ecx.r.l; } if (dp->uses_ebios & (EBIOS_ENHANCED_DRIVE_INFO | EBIOS_LOCKING_ACCESS | EBIOS_FIXED_DISK_ACCESS)) { /* Get EBIOS drive info. */ static struct drive_params params; params.buf_size = sizeof(params); bb.intno = 0x13; bb.eax.r.h = 0x48; bb.edx.r.l = drive; bb.esi.rr = NORMALIZED_OFFSET((unsigned)¶ms); bb.ds = NORMALIZED_SEGMENT((unsigned)¶ms); bios(&bb); if(bb.flags.cf != 0 /* || params.phys_sectors < 2097152 */) { dp->uses_ebios = 0; di->params.buf_size = 1; } else { bcopy(¶ms, &di->params, sizeof(params)); if (drive >= BASE_HD_DRIVE && (dp->uses_ebios & EBIOS_ENHANCED_DRIVE_INFO) && di->params.buf_size >= 30 && !(di->params.dpte_offset == 0xFFFF && di->params.dpte_segment == 0xFFFF)) { void *ptr = (void *)(di->params.dpte_offset + ((unsigned int)di->params.dpte_segment << 4)); bcopy(ptr, &di->dpte, sizeof(di->dpte)); } } } if (di->params.phys_heads == 0 || di->params.phys_spt == 0) { /* Either it's not EBIOS, or EBIOS didn't tell us. */ bb.intno = 0x13; bb.eax.r.h = 0x08; bb.edx.r.l = drive; bios(&bb); if (bb.flags.cf == 0 && bb.eax.r.h == 0) { unsigned long cyl; unsigned long sec; unsigned long hds; hds = bb.edx.r.h; sec = bb.ecx.r.l & 0x3F; if((dp->uses_ebios & EBIOS_ENHANCED_DRIVE_INFO) && (sec != 0)) { cyl = (di->params.phys_sectors / ((hds + 1) * sec)) - 1; } else { cyl = bb.ecx.r.h | ((bb.ecx.r.l & 0xC0) << 2); } di->params.phys_heads = hds; di->params.phys_spt = sec; di->params.phys_cyls = cyl; } else { ret = -1; } } if (dp->no_emulation) { /* Some BIOSes give us erroneous EBIOS support information. * Assume that if you're on a CD, then you can use * EBIOS disk calls. */ dp->uses_ebios |= EBIOS_FIXED_DISK_ACCESS; } #if DEBUG print_drive_info(di); printf("uses_ebios = 0x%x\n", dp->uses_ebios); printf("result %d\n", ret); printf("press a key->\n");getc(); #endif if (ret == 0) { dp->valid = 1; } return ret; } int ebiosEjectMedia(int biosdev) { bb.intno = 0x13; bb.eax.r.h = 0x46; bb.eax.r.l = 0; bb.edx.rx = biosdev; bios(&bb); return bb.eax.r.h; } void setCursorPosition(int x, int y, int page) { bb.intno = 0x10; bb.eax.r.h = 0x02; bb.ebx.r.h = page; /* page 0 for graphics */ bb.edx.r.l = x; bb.edx.r.h = y; bios(&bb); } void setCursorType(int type) { bb.intno = 0x10; bb.eax.r.h = 0x01; bb.ecx.rr = type; bios(&bb); } void getCursorPositionAndType(int * x, int * y, int * type) { bb.intno = 0x10; bb.eax.r.h = 0x03; bios(&bb); *x = bb.edx.r.l; *y = bb.edx.r.h; *type = bb.ecx.rr; } void scollPage(int x1, int y1, int x2, int y2, int attr, int rows, int dir) { bb.intno = 0x10; bb.eax.r.h = (dir > 0) ? 0x06 : 0x07; bb.eax.r.l = rows; bb.ebx.r.h = attr; bb.ecx.r.h = y1; bb.ecx.r.l = x1; bb.edx.r.h = y2; bb.edx.r.l = x2; bios(&bb); } void clearScreenRows( int y1, int y2 ) { scollPage( 0, y1, 80 - 1, y2, 0x07, y2 - y1 + 1, 1 ); } void setActiveDisplayPage( int page ) { bb.intno = 0x10; bb.eax.r.h = 5; bb.eax.r.l = page; bios(&bb); } #if DEBUG int terminateDiskEmulation() { static char cd_spec[0x13]; bb.intno = 0x13; bb.eax.r.h = 0x4b; bb.eax.r.l = 0; // subfunc: terminate emulation bb.esi.rr = NORMALIZED_OFFSET((unsigned)&cd_spec); bb.ds = NORMALIZED_SEGMENT((unsigned)&cd_spec); bios(&bb); return bb.eax.r.h; } int readDriveParameters(int drive, struct driveParameters *dp) { bb.intno = 0x13; bb.edx.r.l = drive; bb.eax.r.h = 0x08; bios(&bb); if (bb.eax.r.h == 0) { dp->heads = bb.edx.r.h; dp->sectors = bb.ecx.r.l & 0x3F; dp->cylinders = bb.ecx.r.h | ((bb.ecx.r.l & 0xC0) << 2); dp->totalDrives = bb.edx.r.l; } else { bzero(dp, sizeof(*dp)); } return bb.eax.r.h; } #endif #ifdef APM_SUPPORT #define APM_INTNO 0x15 #define APM_INTCODE 0x53 int APMPresent(void) { bb.intno = APM_INTNO; bb.eax.r.h = APM_INTCODE; bb.eax.r.l = 0x00; bb.ebx.rr = 0x0000; bios(&bb); if ((bb.flags.cf == 0) && (bb.ebx.r.h == 'P') && (bb.ebx.r.l == 'M')) { /* Success */ bootArgs->apmConfig.major_vers = bb.eax.r.h; bootArgs->apmConfig.minor_vers = bb.eax.r.l; bootArgs->apmConfig.flags.data = bb.ecx.rr; return 1; } return 0; } int APMConnect32(void) { bb.intno = APM_INTNO; bb.eax.r.h = APM_INTCODE; bb.eax.r.l = 0x03; bb.ebx.rr = 0x0000; bios(&bb); if (bb.flags.cf == 0) { /* Success */ bootArgs->apmConfig.cs32_base = (bb.eax.rr) << 4; bootArgs->apmConfig.entry_offset = bb.ebx.rx; bootArgs->apmConfig.cs16_base = (bb.ecx.rr) << 4; bootArgs->apmConfig.ds_base = (bb.edx.rr) << 4; if (bootArgs->apmConfig.major_vers >= 1 && bootArgs->apmConfig.minor_vers >= 1) { bootArgs->apmConfig.cs_length = bb.esi.rr; bootArgs->apmConfig.ds_length = bb.edi.rr; } else { bootArgs->apmConfig.cs_length = bootArgs->apmConfig.ds_length = 64 * 1024; } bootArgs->apmConfig.connected = 1; return 1; } return 0; } #endif /* APM_SUPPORT */ #ifdef EISA_SUPPORT BOOL eisa_present( void ) { static BOOL checked; static BOOL isEISA; if (!checked) { if (strncmp((char *)0xfffd9, "EISA", 4) == 0) isEISA = TRUE; checked = TRUE; } return (isEISA); } int ReadEISASlotInfo(EISA_slot_info_t *ep, int slot) { union { struct { unsigned char char2h :2; unsigned char char1 :5; unsigned char char3 :5; unsigned char char2l :3; unsigned char d2 :4; unsigned char d1 :4; unsigned char d4 :4; unsigned char d3 :4; } s; unsigned char data[4]; } u; static char hex[0x10] = "0123456789ABCDEF"; bb.intno = 0x15; bb.eax.r.h = 0xd8; bb.eax.r.l = 0x00; bb.ecx.r.l = slot; bios(&bb); if (bb.flags.cf) return bb.eax.r.h; ep->u_ID.d = bb.eax.r.l; ep->configMajor = bb.ebx.r.h; ep->configMinor = bb.ebx.r.l; ep->checksum = bb.ecx.rr; ep->numFunctions = bb.edx.r.h; ep->u_resources.d = bb.edx.r.l; u.data[0] = bb.edi.r.l; u.data[1] = bb.edi.r.h; u.data[2] = bb.esi.r.l; u.data[3] = bb.esi.r.h; ep->id[0] = u.s.char1 + ('A' - 1); ep->id[1] = (u.s.char2l | (u.s.char2h << 3)) + ('A' - 1); ep->id[2] = u.s.char3 + ('A' - 1); ep->id[3] = hex[u.s.d1]; ep->id[4] = hex[u.s.d2]; ep->id[5] = hex[u.s.d3]; ep->id[6] = hex[u.s.d4]; ep->id[7] = 0; return 0; } /* * Note: ep must point to an address below 64k. */ int ReadEISAFuncInfo(EISA_func_info_t *ep, int slot, int function) { bb.intno = 0x15; bb.eax.r.h = 0xd8; bb.eax.r.l = 0x01; bb.ecx.r.l = slot; bb.ecx.r.h = function; bb.esi.rr = (unsigned int)ep->data; bios(&bb); if (bb.eax.r.h == 0) { ep->slot = slot; ep->function = function; } return bb.eax.r.h; } #endif /* EISA_SUPPORT */ #define PCI_SIGNATURE 0x20494350 /* "PCI " */ int ReadPCIBusInfo(PCI_bus_info_t *pp) { bb.intno = 0x1a; bb.eax.r.h = 0xb1; bb.eax.r.l = 0x01; bios(&bb); if ((bb.eax.r.h == 0) && (bb.edx.rx == PCI_SIGNATURE)) { pp->BIOSPresent = 1; pp->u_bus.d = bb.eax.r.l; pp->majorVersion = bb.ebx.r.h; pp->minorVersion = bb.ebx.r.l; pp->maxBusNum = bb.ecx.r.l; return 0; } return -1; } void sleep(int n) { unsigned int endtime = (time18() + 18*n); while (time18() < endtime); } void delay(int ms) { bb.intno = 0x15; bb.eax.r.h = 0x86; bb.ecx.rr = ms >> 16; bb.edx.rr = ms & 0xFFFF; bios(&bb); }