/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Portions Copyright (c) 1999 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 1.1 (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.
 */

#include "libsaio.h"
#include "memory.h"
#include "kernBootStruct.h"

static biosBuf_t bb;
unsigned char uses_ebios[8] = {0, 0, 0, 0, 0, 0, 0, 0};

int bgetc(void)
{
    bb.intno = 0x16;
    bb.eax.r.h = 0x00;
    bios(&bb);
    return bb.eax.rr;
}

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;
}

int memsize(int which)
{
    int size;
        
    if ( which )
    {
        // Get the total system memory discovered by the
        // BIOS in kilobytes.

        get_memsize(&bb);
        size = (bb.edx.rr << 16) | bb.eax.rr;

        // Convert to extended memory size.

        size = ( size > 1024 ) ? size - 1024 : 0;
    }
    else
    {
        // Get amount of conventional memory available.

        bb.intno = 0x12;
        bios(&bb);
        size = bb.eax.rr;
    }
    return size;
}

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 = ptov(BIOS_ADDR);
	bb.es = ((unsigned long)&i & 0xffff0000) >> 4;
    
	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;
    struct {
        unsigned char size;
        unsigned char reserved;
        unsigned char numblocks;
        unsigned char reserved2;
        unsigned int buffer;
        unsigned long long startblock;
    } addrpacket = {0};
    addrpacket.size = sizeof(addrpacket);

    for (i=0;;) {
        bb.intno = 0x13;
        bb.eax.r.h = 0x42;
        bb.edx.r.l = dev;
        bb.esi.rr = ((unsigned)&addrpacket & 0xffff);
        addrpacket.numblocks = count;
        addrpacket.buffer = 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)
{
    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);
}

unsigned int get_diskinfo(int drive)
{
    struct {
        unsigned short size;
        unsigned short flags;
        unsigned long cylinders;
        unsigned long heads;
        unsigned long sectors;
        unsigned long long total_sectors;
        unsigned short bps;
        unsigned long params_p;
    } ebios = {0};
    unsigned char useEbios = 0;

    union {
	compact_diskinfo_t di;
	unsigned int ui;
    } ret;
    static int maxhd = 0;

    ret.ui = 0;
    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;

    /* 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 EBIOS drive info. */
        ebios.size = 26;
        bb.intno = 0x13;
        bb.eax.r.h = 0x48;
        bb.edx.r.l = drive;
        bb.esi.rr = ((unsigned)&ebios & 0xffff);
        bios(&bb);
        if(bb.flags.cf == 0 && ebios.cylinders != 0) {
            useEbios = 1;
        }
    }

    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(useEbios) {
            cyl = (ebios.total_sectors / ((hds + 1) * sec)) - 1;
        }
        else {
            cyl = bb.ecx.r.h | ((bb.ecx.r.l & 0xC0) << 2);
        }
        ret.di.heads = hds;
        ret.di.sectors = sec;
        ret.di.cylinders = cyl;
    }
    if(drive >= 0x80) uses_ebios[drive - 0x80] = useEbios;
    return ret.ui;
}

void setCursorPosition(int x, int y)
{
    bb.intno = 0x10;
    bb.eax.r.h = 0x02;
    bb.ebx.r.h = 0x00;		/* page 0 for graphics */
    bb.edx.r.l = x;
    bb.edx.r.h = y;
    bios(&bb);
}

#if DEBUG

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

#define APM_INTNO	0x15
#define APM_INTCODE	0x53

int
APMPresent(void)
{
    KERNBOOTSTRUCT *kbp = kernBootStruct;
    
    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 */
	    kbp->apmConfig.major_vers = bb.eax.r.h;
	    kbp->apmConfig.minor_vers = bb.eax.r.l;
	    kbp->apmConfig.flags.data = bb.ecx.rr;
	    return 1;
    }
    return 0;
}

int
APMConnect32(void)
{
    KERNBOOTSTRUCT *kbp = kernBootStruct;

    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 */
	kbp->apmConfig.cs32_base = (bb.eax.rr) << 4;
	kbp->apmConfig.entry_offset = bb.ebx.rx;
	kbp->apmConfig.cs16_base = (bb.ecx.rr) << 4;
	kbp->apmConfig.ds_base = (bb.edx.rr) << 4;
	if (kbp->apmConfig.major_vers >= 1 &&
	    kbp->apmConfig.minor_vers >= 1) {
		kbp->apmConfig.cs_length = bb.esi.rr;
		kbp->apmConfig.ds_length = bb.edi.rr;
	} else {
		kbp->apmConfig.cs_length = 
		    kbp->apmConfig.ds_length = 64 * 1024;
	}
	kbp->apmConfig.connected = 1;
	return 1;
    }
    return 0;
}

#ifdef EISA_SUPPORT  // turn off for now since there isn't enough room
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

#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;
}
