/*
   *                                                                      
   * MODULE DESCRIPTION                                                   
   *   This module provides all the simulator functions that deal with    
   * memory locations in the external memory map of a dsp device.  It     
   * provides simulation of the external memory space for dsp.  These are     
   * the functions currently used with the dsp simulator and 
   * with the example programs included with the    
   * simulation package.  The module is provided so that the user may     
   * customize any special external memory responses - for example the    
   * implementation of dual port memory in multiple dsp simulations.      
   *                                                                      
   *   This implementation of the external memory functions uses          
   * a virtual memory structure which represents the DSP memory.          
   * The virtual memory structure operates on 256 word blocks of          
   * memory.  Each word is contained within a "unsigned long integer" data         
   * type.  It is assumed that the long integer type will have at         
   * least 32 bits.  The module functions dynamically allocate        
   * memory for a block if it is written to.  Memory areas that are       
   * unwritten are not allocated memory.  If the memory allocation        
   * call fails (no room for the next block), an existing memory          
   * block will be stored out to disk file with suffix ".MEM".  The virtual      
   * memory structure contains information to indicate which blocks       
   * are in memory or on disk, and their locations.                       
   * Blocks filled with a constant value will not require memory.         
   * The most recently referenced block of memory is switched out         
   * only as a last resort.                                               
   *                                                                      
   * MAIN FUNCTIONS: Called from other modules                            
   * dspl_xmrd(dev,mem,address,buf,fetch): Read device mem address to buf.    
   * dspl_xmwr(dev,mem,address,value,store): Write device mem address with value.
   * dspl_xmstart(dev,mem,address): Start of external memory access for device.
   * dspl_xmend(dev,mem,address): End of external memory access for device.
   * dspl_xmsave(dev,fp): Save devices external memory to a state file.    
   * dspl_xmload(dev,fp): Load devices external memory from a state file.  
   * dspl_xmnew (dev):    Create external memory for a new device.         
   * dspl_xmfree(dev):    Free external memory structure of a device.      
   * dspl_xminit(dev):    Initialize a device's external memory.           
   * dsp_alloc(nbytes,clearmem)   Memory allocation call used by simulator.        
   *                                                                      
   * LOCAL FUNCTIONS:  Lower level functions only used by this module     
   * m_frb():    Free a virtual memory block after saving it to disk. 
   * m_bsave(mem,fp):   Save memory map of a dsp to a state file.        
   * m_bload(mem,fp):   Load memory map of a dsp from a state file.      
   * m_pdisk(meml):   Put memory block in virtual memory disk file.  
   * m_gdisk(mem,block):   Get memory block from virtual memory disk file.
   * m_lastb(mem): Last resort virtual memmory block gets put to disk.    
   * m_pickb(mem): Pick a virtual memory block to put on disk.            
 */
#include "simcom.h"
#include "simdev.h"
#include "simusr.h"
#include "protocom.h"
static struct dev_memlist *memfind (struct dev_memory *mem, unsigned long address);
static unsigned long *m_gdisk (struct dev_memlist *meml);
static int m_pickb (struct dev_memory *mem);
static int m_lastb (struct dev_memory *mem);
static void m_frba (struct dev_memory *mem);
static int m_bsave (struct dev_memory *mem, FILE *fp);
static int m_bload (struct dev_memory *mem, FILE *fp);
static int m_frb (void);
static void m_pdisk (struct dev_memlist *meml);

extern struct dev_var *dv_var;
extern struct dt_var *dx_var;
extern struct dev_const dv_const;

#if FULLSIM
extern struct sev_var *sv_var;
extern struct sev_const sv_const;

#endif

/* Each of the three memory spaces will be represented by a circular */
/* linked list of structures.  The list is linked in order of increasing */
/* memory block.  A block is 256 memory locations.  They may be on disk, */
/* in memory, or defined as a constant memory value.  If a block is */
/* in memory, the memory list structure will contain a pointer to the */
/* allocated 256 word block of memory.  If it is on-disk, the memory */
/* list structure will contain the block address of the disk block that */
/* contains the 256 word memory values.  If it is blockfilled, the list */
/* structure will contain a block fill value for the entire range of */
/* memory from the block start address to the block start of the next */
/* memlist structure in the circular linked list. */
static unsigned int blocksz = (256 * sizeof (long));	/* size of a block of 256 long integers */

/*
   * dsp_alloc --- allocate memory
   *
   * All the simulator routines use this function to allocate needed memory.
   * This implementation is integrated with the virtual memory scheme and
   * will call m_frb() to shove an external memory block to disk if the
   * requested memory is not immediately available from malloc().  
 */

void *
dsp_alloc (unsigned int nbytes,
	   int clearmem)

{
    char *px;

#if FULLSIM
    if (sv_const.gui)
    {
        return (siml_gui_alloc (nbytes, clearmem));
    }
#endif
    
    while (!(px = (char *) malloc (nbytes)))
    {
	if (!m_frb ())
	{
#if FULLSIM
	    struct sev_var *sv = sv_var;
	    struct dev_var *dv = dv_var;

	    sv_var = sv_const.sv[sv_const.viewdev];
	    dv_var = dv_const.sv[sv_const.viewdev];
	    simw_wscr ("Insufficient memory: dsp_alloc", 1);
	    sv_var = sv;
	    dv_var = dv;
#else
	    (void) printf ("\nInsufficient memory: dsp_alloc");
#endif
	    return ((void *) px);
	}
    }

    if (clearmem)
	(void) memset ((char *) px, 0, (int) nbytes);

    return ((void *) px);
}

void
dsp_free_mem (char *cp)
{
#if FULLSIM
    if (sv_const.gui)
    {
        siml_gui_free_mem (cp);
        return;
    }
#endif    
    (void) free (cp);
}

/*
   * dsp_realloc --- reallocate memory
 */

void *
dsp_realloc (char *cp,
	     unsigned int nbytes)

{
    char *px;

#if FULLSIM
    if (sv_const.gui)
    {
        return (siml_gui_realloc (cp, nbytes));
    }
#endif
    
    while (!(px = (char *) realloc (cp, nbytes)))
    {
	if (!m_frb ())
	{
#if FULLSIM
	    struct sev_var *sv = sv_var;
	    struct dev_var *dv = dv_var;

	    sv_var = sv_const.sv[sv_const.viewdev];
	    dv_var = dv_const.sv[sv_const.viewdev];
	    simw_wscr ("Insufficient memory: dsp_realloc", 1);
	    sv_var = sv;
	    dv_var = dv;
#else
	    (void) printf ("\nInsufficient memory: dsp_realloc");
#endif
	    return ((void *) px);
	}
    }

    return ((void *) px);
}

static struct dev_memlist *
memfind (struct dev_memory *mem	/* start block */
	 ,
	 unsigned long address)

{
    struct dev_memlist *mp;

    mp = mem->recent;		/* start with most recently used block for search */
    while (address < mp->add)
	mp = mp->prev;
    while ((mp->next->add) && (address >= mp->next->add))
	mp = mp->next;
    return (mp);
}

static
unsigned long *
m_gdisk (struct dev_memlist *meml)

{
    /* Get a virtual memory block from disk and put it in memory. */
    int pos;
    int status;
    unsigned long *bptr,
     block;
    struct dev_garbage *garbagep;

    bptr = (unsigned long *) dsp_alloc (blocksz, 0);	/* get memory to store the data */
    if (bptr)
    {
	block = meml->memd.disk;	/* read block from disk to memory */
	pos = fseek (dv_var->vmem.memfn, (long) block, 0);
	if (pos)
	{
#if FULLSIM
	    simw_puts (sv_const.cmdline, 0, "Error seeking in m_gdisk", 1);
#else
	    (void) printf ("\nError seeking in m_gdisk");
#endif
	    (void) fclose (dv_var->vmem.memfn);
	    exit (1);
	}

	/* read data from disk to buffer */
	status = fread ((char *) bptr, (int) blocksz, 1, dv_var->vmem.memfn);
	if (status != 1)
	{
#if FULLSIM
	    simw_puts (sv_const.cmdline, 0, "Error reading in m_gdisk.", 1);
#else
	    (void) printf ("\nError reading in m_gdisk. Status=%d", status);
#endif
	    (void) fclose (dv_var->vmem.memfn);
	    exit (1);
	}

	/* mark the disk block as free */
	if ((garbagep = (struct dev_garbage *) dsp_alloc (sizeof (struct dev_garbage), 0)))
	{
	    garbagep->next = dv_var->vmem.garbagep;
	    garbagep->offset = block;
	    dv_var->vmem.garbagep = garbagep;
	}

	/* mark the mem structure to indicate the data isn't on disk */
	meml->memf = DSP_IN_MEMORY;	/* mark block NOT ON DISK */
	meml->memd.memp = bptr;
    }
    return (bptr);
}

static int
m_pickb (struct dev_memory *mem)

{
    struct dev_memlist *memr,
    *memr2,
    *mp;

    /* Pick a block and write it out to disk, but don't pick the most */
    /* recently referenced blocks. */
    memr = mem->recent;
    memr2 = mem->recent2;
    for (mp = memr->prev; mp != memr; mp = mp->prev)
    {
	if ((mp->memf == DSP_IN_MEMORY) && mp != memr2)
	{
	    m_pdisk (mp);
	    return (1);
	}
    }
    return (0);
}

static
int
m_lastb (struct dev_memory *mem)

{
    /* Switches out one of the most recently referenced blocks of memory */
    /* This is used as a last resort when no other memory blocks */
    /* are available */

    struct dev_memlist *mp;

    if (((mp = mem->recent2)->memf == DSP_IN_MEMORY) ||
	((mp = mem->recent)->memf == DSP_IN_MEMORY))
    {
	m_pdisk (mp);
	return (1);
    }
    return (0);
}

/*
   * dspl_xmfree --- free a devices external memory
   *
   * Free the external virtual memory structure for the specified device. 
 */

void
dspl_xmfree (int devindex)

{
    int i,
     j;
    struct dev_memory *mp;
    struct dt_memory *mpt;
    struct dev_memlist *ml,
    *mr,
    *mn;

    dv_var = dv_const.sv[devindex];
    dx_var = dv_const.dt[dv_var->devtype];
    mp = dv_var->mem;
    mpt = dx_var->mem;

    if (mp)
    {
	for (i = 0, j = dx_var->nummemory; i < j; i++)
	{
	    if ((mpt[i].memattr) & DSP_MEMA_EXT)
	    {
		if ((ml = mr = mp[i].recent))
		{
		    do
		    {
			mn = ml->next;
			if (ml->memf == DSP_IN_MEMORY)
			    dsp_free_mem ((char *) ml->memd.memp);
			dsp_free_mem ((char *) ml);
			ml = mn;
		    }
		    while (ml != mr);
		}
		mp[i].recent = mp[i].recent2 = NULL;
	    }
	}
    }
}

/*
   * dspl_xmsave --- save device's external memory to a simulator state file
   *
   * This function must save the external memory associated with a device
   * to a simulator state file.  The file is already opened with a "w+"
   * designator.  The entire simulator state except for the external memory
   * state has been saved prior to calling this function (it is called from
   * within the dsp_save() simulator function as the last step in saving the
   * device state to a file.  There is no fixed format in which you must
   * save the data,  but the dspl_xmload() function must provide the exact
   * steps to reload the saved memory state from the same file.  This function
   * must return 1 if the save is successful, 0 otherwise.
 */

int 
dspl_xmsave (int devindex,
	     FILE *fp		/* Pointer to file already opened with "w+" designator. */
)

{
    /* Save a device's external memory to a state file.   */
    int writok,
     i,
     j;
    struct dev_memory *mp;
    struct dt_memory *mpt;

    dv_var = dv_const.sv[devindex];
    dx_var = dv_const.dt[dv_var->devtype];
    mp = dv_var->mem;
    mpt = dx_var->mem;

    for (i = 0, j = dx_var->nummemory, writok = 1; i < j && writok; i++)
    {
	if ((mpt[i].memattr) & DSP_MEMA_EXT)
	    writok = m_bsave (&mp[i], fp);
    }
    return (writok);
}

/*
   * dspl_xminit --- initialize a device's external memory
   *
   * This function must reset a device's external memory to an initialized state.
   * It is called from the dsp_init() simulator function as part of the process
   * of re-initializing a device's state.  This is used in response to the
   * simulator RESET S command and when loading a new simulator state with the
   * LOAD S command or dsp_load() function call.
 */

void
dspl_xminit (int devindex)

{				/* reinitialize external virtual memory for a device. */

    struct dev_garbage *garb,
    *garbn;
    int i,
     j;
    struct dev_memory *mp;

    dv_var = dv_const.sv[devindex];
    dx_var = dv_const.dt[dv_var->devtype];
    mp = dv_var->mem;

    for (i = 0, j = dx_var->nummemory; i < j; i++)
    {
	if ((dx_var->mem[i].memattr) & DSP_MEMA_EXT)
	    m_frba (&mp[i]);
    }
    if (dv_var->vmem.memfn)
    {
	(void) fclose (dv_var->vmem.memfn);	/* close old virtual memory file */
	dv_var->vmem.memfn = NULL;
	for (garb = dv_var->vmem.garbagep; garb; garb = garbn)
	{
	    garbn = garb->next;
	    dsp_free_mem ((char *) garb);
	}
	dv_var->vmem.garbagep = NULL;
    }
}

/*
   * dspl_xmnew --- Create and initialize a device's external memory structure
   * 
   * This function will be called from the sim_new() simulator function as
   * a part of the procedure of creating a new dsp device.  It must perform
   * whatever steps necessary to allocate and initialize the external memory
   * associated with the selected device.
 */

int 
dspl_xmnew (void)

{
    struct dev_memlist *memx;
    int i,
     j,
     retok;
    struct dev_memory *mp;

    mp = dv_var->mem;

    for (i = 0, j = dx_var->nummemory, retok = 1; i < j && retok; i++)
    {
	if ((dx_var->mem[i].memattr) & DSP_MEMA_EXT)
	{
	    retok = (retok && (
				  memx = (struct dev_memlist *) dsp_alloc (sizeof (struct dev_memlist), 1)));

	    if (retok)
	    {
		mp[i].recent = mp[i].recent2 = memx;
		memx->next = memx->prev = memx;
		memx->memf = DSP_BLOCK_FILL;
	    }
	}
    }
    /* Create a virtual memory structure to represent the external memory */
    dv_var->vmem.memfn = 0;
    dv_var->vmem.garbagep = NULL;
    return (retok);
}

/*
   * dspl_xmload --- load device's external memory from a simulator state file
   *
   * This function is called from dsp_load() as the last step in loading a
   * device's state.  The external memory will have already been initialized by
   * a call to dspl_xminit() prior to calling this function, and the remainder
   * of the device state will have been loaded.  The state file will already
   * be opened with the "r" designator and the file pointer advanced to the
   * position where the external memory state should have been previously stored
   * with a dspl_xmsave() call.  This function should not close the file.  It
   * must return 1 if the load is successful, 0 otherwise.
 */

int 
dspl_xmload (int devindex,
	     FILE *fp)
{
    /* reload external memory blocks from state file */
    int readok,
     i,
     j;
    struct dev_memory *mp;

    dv_var = dv_const.sv[devindex];
    dx_var = dv_const.dt[dv_var->devtype];
    mp = dv_var->mem;

    for (i = 0, j = dx_var->nummemory, readok = 1; i < j && readok; i++)
    {
	if ((dx_var->mem[i].memattr) & DSP_MEMA_EXT)
	    readok = m_bload (&mp[i], fp);
    }
    return (readok);
}

/*
   * dspl_xmrd --- read an external memory location 
   *
   * This function must return the value of the specified external memory
   * address for the specified device and memory map in to_buf.  It must also
   * return 1 as the function return value if the memory location exists, 0
   * otherwise.  This particular implementation gets the value from the
   * virtual memory structure that defines the entire external memory of the
   * device.  This function is called by the dsp device when executing memory
   * fetches and by the simulator user interface routines for
   * display or breakpoint purposes.  The "fetch" parameter will be set to 1
   * if a dsp memory fetch is occurring, 0 otherwise.
   * The memt parameter is one of the external memory maps defined in
   * coreaddr.h.  The valid memory maps for the 56000 are memory_map_xe
   * memory_map_ye, and memory_map_pe; for the 96000 - memory_map_xa,
   * memory_map_ya, memory_map_pa, memory_map_xb, memory_map_yb, memory_map_pb; 
   * for the 56116 memory_map_xe and memory_map_pe.  In general, for each 
   * device type,  memt is valid if it corresponds to the "maintype" member of
   * a dt_memory structure which has DSP_MEMA_EXT and DSP_MEMA_REAL in the 
   * corresponding "memattr" structure member.
 */

/*ARGSUSED */
int 
dspl_xmrd (int devindex,
	   enum memory_map memt,
	   unsigned long address,
	   unsigned long *to_buf,
	   int fetch)

{
    /* Read an external memory location. */
    struct dev_memlist *meml;
    struct dev_memory *mem;
    int memc;

    memc = dspl_mindx (memt, address);
    dv_var = dv_const.sv[devindex];
    dx_var = dv_const.dt[dv_var->devtype];

    mem = &dv_var->mem[memc];
    if (!((dx_var->mem[memc].memattr) & DSP_MEMA_EXT))
	return (0);		/* location doesn't exist */
    meml = memfind (mem, address);
    if (meml->memf == DSP_BLOCK_FILL)
	*to_buf = meml->memd.blok;
    else
    {
	if ((meml->memf == DSP_ON_DISK) && !m_gdisk (meml))
	    return (0);
	*to_buf = meml->memd.memp[address & 0xffl];
	if (mem->recent != meml)
	{
	    mem->recent2 = mem->recent;
	    mem->recent = meml;
	}
    }
    return (1);			/* indicating that the memory location exists */
}

/*
   * dspl_xmwr --- store a value in a device's external memory location
   *
   * This function must store the provided value in the specified external
   * memory location for the device.  This particular implementation stores
   * the value in the virtual memory structure that defines the entire external
   * memory of the device.  The user may modify this to implement any special
   * features of the external memory, for example dual-port RAM or external
   * ROM locations. This function is called by the dsp device when executing
   * memory stores and by the simulator user interface routines when modifying
   * external memory locations.  The "store" parameter will be set to 1
   * if a dsp memory store is occurring, 0 otherwise.
   * The memt parameter is one of the external memory maps defined in
   * coreaddr.h.  The valid memory maps for the 56000 are memory_map_xe
   * memory_map_ye, and memory_map_pe; for the 96000 - memory_map_xa,
   * memory_map_ya, memory_map_pa, memory_map_xb, memory_map_yb, memory_map_pb; 
   * for the 56116 memory_map_xe and memory_map_pe.  In general, for each 
   * device type,  memt is valid if it corresponds to the "maintype" member of
   * a dt_memory structure which has DSP_MEMA_EXT and DSP_MEMA_REAL in the 
   * corresponding "memattr" structure member.
 */

/*ARGSUSED */
void
dspl_xmwr (int devindex,
	   enum memory_map memt,
	   unsigned long address,
	   unsigned long value,
	   int store)

{
    /* Write an external memory location */
    struct dev_memory *mem;
    struct dev_memlist *meml,
    *memn;
    int i;
    unsigned long *bptr,
     blockd,
     nextbadd,
     newbadd;
    int memc;

    memc = dspl_mindx (memt, address);
    dv_var = dv_const.sv[devindex];

    mem = &dv_var->mem[memc];
    if (!((dx_var->mem[memc].memattr) & DSP_MEMA_EXT))
	return;			/* location doesn't exist */
    meml = memfind (mem, address);
    if (meml->memf == DSP_BLOCK_FILL)
    {
	blockd = meml->memd.blok;
	if (value == blockd)
	    return;		/* return when constant block unaltered */

	bptr = (unsigned long *) dsp_alloc (blocksz, 0);	/* new memory block */
	if (!bptr)
	    return;
	for (i = 0; i < 256; i++)
	    bptr[i] = blockd;
	newbadd = address & (~0xffl);	/* where this address will go */
	nextbadd = newbadd + 256;	/* next block address */
	if (nextbadd != (meml->next)->add)
	{			/* insert block after new block */
	    memn = (struct dev_memlist *) dsp_alloc (sizeof (struct dev_memlist), 0);

	    if (!memn)
		return;
	    memn->add = nextbadd;
	    memn->memf = DSP_BLOCK_FILL;
	    memn->memd.blok = blockd;
	    memn->next = meml->next;
	    memn->prev = meml;
	    meml->next->prev = memn;
	    meml->next = memn;
	}
	if (newbadd != meml->add)
	{			/* insert new block */
	    memn = (struct dev_memlist *) dsp_alloc ((unsigned int) sizeof (struct dev_memlist), 0);

	    if (!memn)
		return;
	    memn->next = meml->next;
	    memn->prev = meml;
	    memn->add = newbadd;
	    meml->next->prev = memn;
	    meml->next = memn;
	    meml = memn;
	}
	meml->memf = DSP_IN_MEMORY;
	meml->memd.memp = bptr;
    }
    else if ((meml->memf == DSP_ON_DISK) && !m_gdisk (meml))
	return;

    meml->memd.memp[address & 0xffl] = value;
    if (mem->recent != meml)
    {
	mem->recent2 = mem->recent;
	mem->recent = meml;
    }
}

/*
   * dspl_xmstart -- Start of external memory access for device.
   *
   * The core or dma simulations in call this function during the
   * first clock cycle of any external memory access.  It is provided as a  
   * convenient place to initiate changes in the state of the TA pins when
   * slow external memory accesses are simulated.  Since the standard simulator
   * assumes that external memory accesses require no wait states, this
   * function is left empty.  The memt parameter indicates which external 
   * port and which memory map is being accessed.  The memory address is 
   * available in the address parameter.  The A or B control busses will provide 
   * the read/write and page fault status.
 */

/*ARGSUSED */
void
dspl_xmstart (int devindex,
	      enum memory_map memt,
	      unsigned long address)

{
}

/*
   * dspl_xmend -- End of external memory access for device.
   *
   * The core or dma simulations in call this function during the
   * last clock cycle of any external memory access.
   * The memt parameter indicates which external 
   * port and which memory map is being accessed.  The memory address is 
   * available in the address parameter.
 */

/*ARGSUSED */
void
dspl_xmend (int devindex,
	    enum memory_map memt,
	    unsigned long address)

{
}

static void
m_frba (struct dev_memory *mem)

{
    struct dev_memlist *mp,
    *mr,
    *mpn;

    /* merge all blocks associated with a dsp external memory space */
    for (mr = mem->recent, mp = mr->next; mp != mr; mp = mpn)
    {
	mpn = mp->next;
	if (mp->memf == DSP_IN_MEMORY)
	    dsp_free_mem ((char *) mp->memd.memp);
	dsp_free_mem ((char *) mp);
    }
    /* now only one block left (mr), initialize it for block fill data */
    if (mp->memf == DSP_IN_MEMORY)
	dsp_free_mem ((char *) mp->memd.memp);
    mp->next = mp;
    mp->add = 0;
    mp->memf = DSP_BLOCK_FILL;
    mp->memd.blok = 0l;
    mem->recent2 = mp;
}

static int
m_bsave (struct dev_memory *mem,
	 FILE *fp)

{
    /* Stores one dsp external memory space to a simulator state file */
    struct dev_memlist *ml,
    *mf;
    unsigned long *mptr;
    long nb;
    int writok,
     i;

    for (mf = ml = mem->recent, nb = 1; ml->next != mem->recent; nb++, ml = ml->next)
	if (ml->add == 0)
	    mf = ml;		/* count number of blocks to nb; mf=block 0 */
    (void) fprintf (fp, "\n%ld", nb);	/* store number of blocks */
    for (; nb; nb--, mf = mf->next)
    {
	(void) fprintf (fp, "\n%lx", mf->add);	/* start address of block */
	mptr = (mf->memf == DSP_ON_DISK) ? m_gdisk (mf) :
	    (mf->memf == DSP_IN_MEMORY) ? mf->memd.memp : NULL;
	(void) fprintf (fp, "\n%d\n", mf->memf);	/* block fill flag or in memory */
	if (mptr)
	{			/* print memory block contents */
	    for (i = 0; i < 256; i++)
	    {
		if (i && (!(i & 7)))
		    (void) fprintf (fp, "\n");
		(void) fprintf (fp, "%lx ", *(mptr + i));
	    }
	}
	else
	    (void) fprintf (fp, "%lx ", mf->memd.blok);		/*block fill value */
    }
    writok = (fprintf (fp, "\n") != EOF);
    return (writok);
}

static int
m_bload (struct dev_memory *mem,
	 FILE *fp)

{
    /* Loads one dsp external memory space from a simulator state file */
    int i;
    long nb;
    int readok;
    struct dev_memlist *ml,
    *memn;
    unsigned long *mptr;

    m_frba (mem);		/* clear memory first */
    for ((readok = fscanf (fp, "%ld ", &nb)), ml = mem->recent; nb > 0; nb--)
    {
	(void) fscanf (fp, "%lx ", &ml->add);	/* block start address */
	(void) fscanf (fp, "%d ", &ml->memf);	/* in memory or block fill constant flag */
	if (ml->memf == DSP_IN_MEMORY)
	{
	    ml->memd.memp = mptr = (unsigned long *) dsp_alloc (blocksz, 0);
	    if (!mptr)
		return (0);
	    for (i = 0; i < 256; i++)
		(void) fscanf (fp, "%lx ", mptr + i);
	}
	else
	    (void) fscanf (fp, "%lx ", &ml->memd.blok);
	if (nb > 1)
	{
	    memn = (struct dev_memlist *) dsp_alloc (sizeof (struct dev_memlist), 0);

	    if (!memn)
		return (0);
	    memn->next = ml->next;
	    memn->prev = ml;
	    ml->next->prev = memn;
	    ml->next = memn;
	    ml = memn;
	}
    }
    return (readok);
}

static int
m_frb (void)

{
    /* find some block of dsp memory and shove it to disk, free the memory */
    /* return 1 if successful, 0 otherwise.  Any block will do. */
    int i,
     j,
     k,
     m;
    struct dev_var *dev_vmtemp;
    struct dev_memory *mp;

    dev_vmtemp = dv_var;

    for (i = 0, j = 0; i < dv_const.maxdevices && !j; i++)
    {
	if ((dv_var = dv_const.sv[i]))
	{
	    dx_var = dv_const.dt[dv_var->devtype];
	    for (k = 0, m = dx_var->nummemory; k < m && !j; k++)
	    {
		if (!(dx_var->mem[k].memattr & DSP_MEMA_EXT))
		    continue;
		mp = &dv_var->mem[k];
		if (m_pickb (mp))
		    j = 1;
	    }
	}
    }

    for (i = 0; i < dv_const.maxdevices && !j; i++)
    {
	if ((dv_var = dv_const.sv[i]))
	{
	    dx_var = dv_const.dt[dv_var->devtype];
	    for (k = 0, m = dx_var->nummemory; k < m && !j; k++)
	    {
		if (!(dx_var->mem[k].memattr & DSP_MEMA_EXT))
		    continue;
		mp = &dv_var->mem[k];
		if (m_lastb (mp))
		    j = 1;
	    }
	}
    }
    dv_var = dev_vmtemp;
    dx_var = dv_const.dt[dv_var->devtype];
    return (j);
}

static void
m_pdisk (struct dev_memlist *meml)

{
    /* puts the specified memory block on disk and marks the disk bit table */
    /* to indicate it is on disk */

    long offset;
    int status;
    char memfile[81];
    char basename[81];
    struct dev_garbage *gp;

    /* get next free block in disk */
    gp = dv_var->vmem.garbagep;
    offset = (gp) ? gp->offset : 0l;

    /* save a memory block to disk.  */
    if (dv_var->vmem.memfn == NULL)
    {
	(void) sprintf (basename, "%s%d", dx_var->vmemprefix, dv_var->devindex);
	dsp_path (dv_var->pathwork, basename, ".MEM", memfile);
	if (!(dv_var->vmem.memfn = fopen (memfile, DSP_WRITEBIN)))
	{
#if FULLSIM
	    simw_puts (sv_const.cmdline, 0, "Error opening in m_pdisk", 1);
#else
	    (void) printf ("\nError opening in m_pdisk: %s", memfile);
#endif
	    exit (1);
	}
    }
    if (fseek (dv_var->vmem.memfn, offset, (gp) ? 0 : 2))
    {
#if FULLSIM
	simw_puts (sv_const.cmdline, 0, "Error seeking in m_pdisk", 1);
#else
	(void) printf ("\nError seeking in m_pdisk to %ld", offset);
#endif
	exit (1);
    }
    if (!gp)
	offset = ftell (dv_var->vmem.memfn);
    status = fwrite ((char *) meml->memd.memp, (int) blocksz, 1, dv_var->vmem.memfn);
    if (status != 1)
    {				/* check for error */
#if FULLSIM
	simw_puts (sv_const.cmdline, 0, "Error writing block in m_pdisk.", 1);
#else
	(void) printf ("\nError writing block in m_pdisk.");
#endif
	exit (1);
    }

    meml->memf = DSP_ON_DISK;
    dsp_free_mem ((char *) meml->memd.memp);
    meml->memd.disk = offset;
    /* delete gp from the disk block garbage list */
    if (gp)
    {
	dv_var->vmem.garbagep = gp->next;
	dsp_free_mem ((char *) gp);
    }
}
