/***************************************************************************
*        NAME:  IWDRAM.C $Revision: 1.6 $
**       COPYRIGHT:
**       "Copyright (c) 1994,1995 by e-Tek Labs"
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
* $Log: iwdram.c $
* Revision 1.6  1995/08/01 20:34:27  mleibow
* large data transfers with io_poke_block were going into wrong segment.
* Revision 1.5  1995/05/11 11:15:11  jerry
* fixed a broken comment.  :)
* Revision 1.4  1995/05/11 10:04:29  jerry
* _good_dram modifyed a byte, and never changed it back.. better now.
* Revision 1.3  1995/05/03 08:22:06  sdsmith
* Added ROM debugging
* Revision 1.2  1995/04/10 11:22:13  mleibow
* Added ROM support
* Revision 1.1  1995/02/23 11:07:05  unknown
* Initial revision
***************************************************************************/
#include <dos.h>
#include "iw.h"
#include "iwl.h"
#include "globals.h"

/***************************************************************************

FUNCTION DEFINITION:
iw_peek - read a byte from dram at specified address

NOTE: address should be between 0 and 1024K

RETURNS: unsigned char - value at specified dram address
*/
unsigned char iw_peek(unsigned long address)
{
    unsigned char ret;

    ENTER;
    IWL_OUT_W(SET_DRAM_LOW, (unsigned)address);
    IWL_OUT_B(SET_DRAM_HIGH, (unsigned char)((address>>16)&0xFF));
    ret = OS_INPORTB(iwl_dram_io);
    LEAVE;
    return(ret);
}

void iw_peek_rom(
  unsigned char RFAR *data,   /* data to be transferred */
  unsigned long address,      /* starting ROM address */
  unsigned short len)         /* number of bytes to transfer */
{
    ENTER;
#ifdef ROM_DEBUG
    IWL_OUT_B(IW_LMCI, IWL_LMCI_AI);
#else
    IWL_OUT_B(IW_LMCI, IWL_LMCI_AI|IWL_LMCI_ROMIO);
#endif
    IWL_OUT_W(SET_DRAM_LOW, (unsigned)address);
    IWL_OUT_B(SET_DRAM_HIGH, (unsigned char)((address>>16)&0xFF));
    os_ins(iwl_dram_io, data, len);
    IWL_OUT_B(IW_LMCI, IWL_LMCI_AI);
    LEAVE;
}

unsigned short iw_peek16(unsigned long address)
{
    unsigned short ret;

    ENTER;
    IWL_OUT_W(SET_DRAM_LOW, (unsigned)address);
    IWL_OUT_B(SET_DRAM_HIGH, (unsigned char)((address>>16)&0xFF));
    IWL_INP_W(IW_LMSBAI, ret);
    LEAVE;
    return(ret);
}

/***************************************************************************

FUNCTION DEFINITION:
iw_poke - write a byte to dram at specified address

NOTE: address should be between 0 and 1024K

RETURNS: void
*/
void iw_poke(unsigned long address, unsigned char datum)
{
    ENTER;
    IWL_OUT_W(SET_DRAM_LOW, (unsigned)address);
    IWL_OUT_B(SET_DRAM_HIGH, (unsigned char)((address>>16)&0xFF));
    OS_OUTPORTB(iwl_dram_io, datum);
    LEAVE;
}

void iw_poke16(unsigned long address, unsigned short datum)
{
    ENTER;
    IWL_OUT_W(SET_DRAM_LOW, (unsigned)address);
    IWL_OUT_B(SET_DRAM_HIGH, (unsigned char)((address>>16)&0xFF));
    IWL_OUT_W(IW_LMSBAI, datum);
    LEAVE;
}

/* simulate dma xfer using direct I/O */
/***************************************************************************

FUNCTION DEFINITION:
iw_poke_block - write a number of bytes to dram

DESCRIPTION:
iw_poke_block simulates a DMA transfer to dram by poking one byte at a 
time from the array specified by data to the dram starting at the address
specified by address.  The number of bytes to transfer is indicated by the
len parameter.  The dma_control flags indicated if the data is 16 bit or
8 bit signed or unsigned.  This will accunt for the DMA hardware's ability
to invert the most significant bit of 8 bit data if necessary.

NOTE: address should be between 0 and 1024K

RETURNS: void
*/
void iw_poke_block(
  unsigned char RFAR *data,   /* data to be transferred */
  unsigned long address,      /* starting dram address to receive data */
  unsigned long len,          /* number of bytes to transfer */
  unsigned char dma_control)  /* DMA control flags */
{
    unsigned char high;
    unsigned long lsize;
    unsigned short low, size;
    unsigned char xor;
    unsigned char c;
    unsigned char sixteen = dma_control & (unsigned char)IW_DMA_DATA_16;
    unsigned char toggle=0;

    low = (unsigned short)address;
    high = (unsigned char)(address >> 16);
    xor = (dma_control & (unsigned char)IW_DMA_INVERT_MSB) ? ((unsigned char)0x80) :  ((unsigned char)0x0);

    ENTER;
    /* Set high 4 bit pointer. */
    OS_OUTPORTB(iwl_register_select, SET_DRAM_HIGH );	
    OS_OUTPORTB(iwl_data_high, high);
    /* Set low 16 bit pointer. */
    OS_OUTPORTB(iwl_register_select, SET_DRAM_LOW );	
    OS_OUTPORTW(iwl_data_low, low);
    if (xor) {
	while (len) {
	    do {
		c = *data++;
		if (sixteen) {
		    if (toggle) c ^= xor;
		    toggle ^= 1;
		} else {
		    c ^= xor;
		}
		OS_OUTPORTB(iwl_dram_io, c);
#if !defined(__WATCOMC__) && !defined(__FLAT__) && !defined(_WINDOWS)
		if (FP_OFF(data) == 0) {
		    FP_SEG(data) += 0x1000;
		}
#endif
	    } while (--len);
	}
    } else {
	while (len) {
	    lsize = 65536L - FP_OFF(data);
	    if (lsize > 32768) {
		size = 32768;
	    } else {
		size = (unsigned short) lsize;
	    }
	    if ((unsigned long)size > len) size = (unsigned short)len;
	    os_outs(/*iwl_dram_io, */ data, size);
	    len -= size;
	    data += size;
#if !defined(__WATCOMC__) && !defined(__FLAT__) && !defined(_WINDOWS)
	    if (FP_OFF(data) == 0) {
		FP_SEG(data) += 0x1000;
	    }
#endif
	}
    }
    LEAVE;
}

/***************************************************************************

FUNCTION DEFINITION:
iw_good_dram - test for dram at a given address

DESCRIPTION:
iw_poke_block checks the dram address specified by the caller to see if
dram has been installed.  This routine writes several values to the 
address specified and checks if the value remains upon a read from the
same address.  If so, then dram exists at that location.  This routine
also checks if other banks of dram exist.  The upper 2 bits of the 20
bit address specify which bank the lower 18 bits of the address refers
to.  

RETURNS: 0 - if no dram at given location
         1 - if dram present in one 
	 2 -
*/
char iw_good_dram(unsigned long address)
{
    unsigned long addr20 = address ^ 0x20;
    unsigned char old_byte, old_byte1, old_byte_20;
    unsigned long wraparound_loc, bank;
    unsigned short i;

    /* writing to two locations will prevent the bus from holding on
    ** to the last value written.  addr20 is the second address */
    old_byte = iw_peek(address);
    old_byte_20 = iw_peek(addr20);
    iw_poke(address, 0x55);
    iw_poke(addr20, 0x0);
    if (iw_peek(address) != 0x55) {
	iw_poke(address, old_byte);  //change these back just in case...
	iw_poke(addr20, old_byte_20);
	return(0);
    }
    iw_poke(address, 0x0);
    iw_poke(addr20, 0x55);
    if (iw_peek(address) != 0x0) {
	iw_poke(address, old_byte);
	iw_poke(addr20, old_byte_20);
	return(0);
    }
    iw_poke(address, 0xaa);
    iw_poke(addr20, 0x00);
    if (iw_peek(address) != 0xaa) {
	iw_poke(address, old_byte);
	iw_poke(addr20, old_byte_20);
	return(0);
    }
    iw_poke(address, 0xff);
    iw_poke(addr20, 0x00);
    if (iw_peek(address) != 0xff) {
	iw_poke(address, old_byte);
	iw_poke(addr20, old_byte_20);
	return(0);
    }
/* Check for wrap around effect within bank */
    bank = address & 0xC00000;
    for (i=0; i <= 22; i++) {
	wraparound_loc = bank | (address & ((1L<<i)-1L));
	if (i) wraparound_loc |= (1L<<(i-1));
	if (wraparound_loc >= address) break;
	old_byte1 = iw_peek(wraparound_loc);
	iw_poke(wraparound_loc, 0x00);
	iw_poke(address, 0xaa);
	if (iw_peek(wraparound_loc) == 0xaa) {
	    iw_poke(wraparound_loc, old_byte1); // revert this one first
	    iw_poke(address, old_byte);         //  just in case it's the
	    iw_poke(addr20, old_byte_20);       //  same as address
	    return(0);
	}
	iw_poke(wraparound_loc, old_byte1);
    }
/* Check for wrap around effect in other banks */
    for (i=0; i < 4; i++) {
	if (bank == ((unsigned long)i<<22)) break;
	wraparound_loc = (address & 0x3FFFFFL) | ((long)i<<22);
	old_byte1 = iw_peek(wraparound_loc);
	iw_poke(wraparound_loc, 0x00);
	iw_poke(address, 0xaa);
	if (iw_peek(wraparound_loc) == 0xaa) {
	    iw_poke(wraparound_loc, old_byte1);  // same...
	    iw_poke(address, old_byte);
	    iw_poke(addr20, old_byte_20);
	    return(0);
	}
	iw_poke(wraparound_loc, old_byte1);
    }
    iw_poke(address, old_byte);
    iw_poke(addr20, old_byte_20);
    return( 0x1 );
}
