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

			      PilotForth

      Copyright (C) 1997, Ivan A. Curtis.  All rights reserved.

			       based on
C-FORTH: a portable, C-coded figFORTH interpreter by Allan Pratt April 1985.

This program may not be redistibuted in part or in whole without this Copyright
Message being included at the top of each file.

   This is free software, licensed under the GNU Public License V2.
		  See the file COPYING for details.

*******************************************************************************
*******************************************************************************

Filename:	Forth/memory.c

Description:	Memory routines for PilotForth

Update History:   (most recent first)
   I. Curtis  15-May-97 20:59 -- v0.1 Released.
   I. Curtis  30-Apr-97 21:14 -- Created.

******************************************************************************/
#pragma pack(2)
#include "stdio.h"
#include <Common.h>
#include <System/SysAll.h>
#include <UI/UIAll.h>

#include "common.h"
#include "exec.h"
#include "memory.h"

/*****************************************************************************
 *                                                                           *
 * 			   Global Variables                                  *
 *                                                                           *
 *****************************************************************************/
static DmOpenRef VocabDB = NULL;     /* handle for forth vocabulary database */
static DmOpenRef BlockDB = NULL;     /* handle for forth blocks database */

/*
 * Pages
 */
#define PAGEBITS 4		/* max 16 pages */
#define MAXPAGES (1 << PAGEBITS)
#define PAGEMASK ((1 << PAGEBITS) - 1)

/*
 * Offset into page
 */
#define OFSBITS 10
#define PAGESIZE (1 << OFSBITS)
#define OFSMASK ((1 << OFSBITS) - 1)

static short *page[MAXPAGES];
static short npages;

/*
 * Blocks
 */
#define BLOCKSIZE 1024


/*
 * Open the Forth vocabulary and block databases
 * Return 0 on success, 1 on error
 */
int fth_db_open(void)
{
  VocabDB = DmOpenDatabaseByTypeCreator(VocabDBType, forthAppType,
					dmModeReadWrite);
  if (VocabDB == NULL) {
    return 1;
  }
  BlockDB = DmOpenDatabaseByTypeCreator(BlockDBType, forthAppType,
					dmModeReadWrite);
  if (BlockDB == NULL) {
    return 1;
  }
  return 0;
}

/*
 * Close the forth vocabulary database
 * Return 0 on success, 1 on error
 */
int fth_db_close(void)
{
  if (VocabDB != NULL) {
    DmCloseDatabase(VocabDB);
  }
  if (BlockDB != NULL) {
    DmCloseDatabase(BlockDB);
  }
  return 0;
}

/*
 * Lock the records of the vocabulary and
 * build the page pointer array
 * Each page[n] is a pointer to
 * the start of page n
 * Return 0 on success, 1 on failure
 */
int fth_db_map(void)
{
  VoidHand recordH;
  int nrecs;
  nrecs = DmNumRecords(VocabDB); /* npages is a global */
  for (npages = 0; (npages < nrecs) && (npages < MAXPAGES); npages ++) {
    recordH = DmGetRecord(VocabDB, npages);
    if (recordH == NULL) {
      printf("Could not get record %d\n", npages);
      return 1;
    }
				/* make sure record has correct size */
    if (MemHandleSize(recordH) != PAGESIZE * sizeof(short)) {
      printf("Bad size for record %d\n", npages);
      return 1;
    }
				/* lock down record */
    page[npages] = (short *)MemHandleLock(recordH);
    if (page[npages] == NULL) {
      printf("Cannot lock record %d\n", npages);
      return 1;
    }
  }
  return 0;
}

/*
 * Un-map the vocabulary.
 * Unlock each page[] pointer and release each record
 * Return 0 on success, 1 on failure
 */
int fth_db_unmap(void)
{
  int i;
  for (i = 0; i < npages; i++) {
    if (page[i] != NULL) {
      if (MemPtrUnlock(page[i]) != 0) {
	printf("Error unlocking page %d\n", i);
	return 1;
      }
      if (DmReleaseRecord(VocabDB, i, true) != 0) {
	printf("Error Releasing record %d\n", i);
	return 1;
      }
    }
  }
  return 0;
}

/*
 * Fetch a memory word at address addr
 */
short fth_mfetch(short addr)
{
  short page_no = (addr >> OFSBITS) & PAGEMASK;
  return *(page[page_no] + (addr & OFSMASK));
}

/*
 * Store a word to memory
 */
void fth_mstore(short addr, short data)
{
  short page_no = (addr >> OFSBITS) & PAGEMASK;
  DmWrite(page[page_no], (addr & OFSMASK) * sizeof(short),
	  &data, sizeof(short));
}

/*
 * allot n words in the dictionary
 * currently will not get any more store, but
 * in the future will do a DmRecordNew to get
 * another page
 * Return 0 on success, 1 on failure
 */
int fth_callot(short n)
{
  unsigned short newsize;
  newsize = fth_mfetch(DP) + n;
  if (newsize >= npages * PAGESIZE) {
    return 1;
  }
  fth_mstore(DP, newsize);			/* move DP */
  return 0;
}

/*
 * Write a block to the BlockDB
 * The block number is blkno, and
 * addr is the address of the data buffer to
 * be written. Note the problem caused by the
 * fact that the buffer consists of shorts
 */
int fth_blk_write(short blkno, short addr)
{
  short i;
  unsigned char buf[BLOCKSIZE];
  VoidHand blockH;
  VoidPtr blockP;

  blockH = DmGetRecord(BlockDB, blkno);
  if (blockH == NULL) {
    printf("Could not get BLOCK %d\n", blkno);
    return 1;
  }
				/* make sure record has correct size */
  if (MemHandleSize(blockH) != BLOCKSIZE) {
    printf("Bad size for block %d\n", blkno);
    return 1;
  }
				/* lock down record */
  blockP = MemHandleLock(blockH);
  if (blockP == NULL) {
    printf("Cannot lock BLOCK %d\n", blkno);
    return 1;
  }
				/* copy data to a buffer */
  for (i = 0; i < BLOCKSIZE; i++) {
    buf[i] = (unsigned char)fth_mfetch(addr++);
  }
				/* write it to the BLOCK */
  DmWrite(blockP, 0, (VoidPtr)buf, BLOCKSIZE);
				/* unlock the block */
  if (MemPtrUnlock(blockP) != 0) {
    printf("Error unlocking BLOCK %d\n", blkno);
    return 1;
  }
				/* release the record */
  if (DmReleaseRecord(BlockDB, blkno, true) != 0) {
    printf("Error Releasing BLOCK %d\n", i);
    return 1;
  }
  return 0;
}

/*
 * Read a block from the BlockDB
 * The block number is blkno, and
 * addr is the address of the data buffer to
 * be read into. Note the problem caused by the
 * fact that the buffer consists of shorts
 */
int fth_blk_read(short blkno, short addr)
{
  short i;
  VoidHand blockH;
  VoidPtr blockP;

  blockH = DmGetRecord(BlockDB, blkno);
  if (blockH == NULL) {
    printf("Could not get BLOCK %d\n", blkno);
    return 1;
  }
				/* make sure record has correct size */
  if (MemHandleSize(blockH) != BLOCKSIZE) {
    printf("Bad size for block %d\n", blkno);
    return 1;
  }
				/* lock down record */
  blockP = MemHandleLock(blockH);
  if (blockP == NULL) {
    printf("Cannot lock BLOCK %d\n", blkno);
    return 1;
  }
				/* copy data to the buffer */
  for (i = 0; i < BLOCKSIZE; i++) {
    fth_mstore(addr++, (short) *((unsigned char *)blockP + i));
  }
				/* unlock the block */
  if (MemPtrUnlock(blockP) != 0) {
    printf("Error unlocking BLOCK %d\n", blkno);
    return 1;
  }
				/* release the record */
  if (DmReleaseRecord(BlockDB, blkno, false) != 0) {
    printf("Error Releasing BLOCK %d\n", i);
    return 1;
  }
  return 0;
}
