/* class comment */!!

inherit(MemServLib, #MemLibInst, #(
workAddr1
workAddr2), 2, nil)!!

setClassVars(MemLibInst, #())!!

now(class(MemLibInst))!!

now(MemLibInst)!!

/* 3/23/1995 22:42 */
/* PUBLIC -- Test memory. */
Def memChecksum(self, startAddr, toAddr | temp resultPtr tmpStr value)
{
  /* compare address offsets for proper order */
  temp := compareAddresses(AddressLibClass$Inst, startAddr, toAddr);
  /* ADDR_GREATER_THAN = 0, ADDR_LESS_THAN = 1, ADDR_EQUAL = 2 */
  if not((temp = ADRLIB_ADDR_GREATER_THAN) cor 
      (temp = ADRLIB_ADDR_LESS_THAN) cor 
      (temp = ADRLIB_ADDR_EQUAL))
    ^nil;  /* error */
  endif;
 
  if (temp = ADRLIB_ADDR_GREATER_THAN)
    displayFormattedError(ErrorTextLibClass$Inst, 
       ER_MEM_FROM_TO, FORCE_POPUP, nil, nil, nil);
    ^nil;
  endif;
  
  /* do test call */
  if pcallLock(self)
    resultPtr := new(Struct, 4);
    if (physicalRangeOfAddresses(AddressLibClass$Inst,startAddr,toAddr)=nil)
      ^nil;
    endif;
    lastError := pcall(procs[#MEMCHECKSUM], startAddr,
              physicalRangeOfAddresses(AddressLibClass$Inst,startAddr,toAddr),
              0, resultPtr);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError <> GOOD) 
    /* Report error */
    displayFormattedError(ErrorTextLibClass$Inst, 
       lastError, CHECK_MODE, nil, nil, nil);
    ^nil; 
  else
    value := longAt(resultPtr,0);
    tmpStr := asHex(value mod 0x10000);
    if (value < 16)
      tmpStr := "0" + tmpStr;
    endif;
    if (memGetAccessSize(self) > 1)  /* word access */
      if (value < 0x100)
        tmpStr := "00" + tmpStr;
      else
        if (value < 0x1000)
          tmpStr := "0" + tmpStr;
        endif;
      endif;
    endif;
    Call MessageBox(0, asciiz("Checksum is 0x"+tmpStr),
      asciiz("Checksum Memroy"), MB_OK bitOr MB_ICONEXCLAMATION);
  endif;
  ^0;
}!!

/* 5/23/1993 15:28 */
Def memCompareDlg (self, hWnd)
{

  /* call dialog box that lives in MEM.DLL */
  if pcallLock(self)
    lastError :=
      pcall(procs[#MEMDLGCOMPAREMEMORY],  hWnd );
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError <> GOOD)
    displayError(ErrorTextLibClass$Inst, lastError, CHECK_MODE);
    ^nil;  /* error */
  endif;
  ^0;
}
!!

/* 3/7/1995 14:36 */
/* PUBLIC -- Test memory. */
Def memTest(self, startAddr, toAddr | index temp resultPtr descPtr tmpStr)
{
  /* compare address offsets for proper order */
  temp := compareAddresses(AddressLibClass$Inst, startAddr, toAddr);
  /* ADDR_GREATER_THAN = 0, ADDR_LESS_THAN = 1, ADDR_EQUAL = 2 */
  if not((temp = ADRLIB_ADDR_GREATER_THAN) cor 
      (temp = ADRLIB_ADDR_LESS_THAN) cor 
      (temp = ADRLIB_ADDR_EQUAL))
    ^nil;  /* error */
  endif;
 
  if (temp = ADRLIB_ADDR_GREATER_THAN)
    displayFormattedError(ErrorTextLibClass$Inst, 
       ER_MEM_FROM_TO, FORCE_POPUP, nil, nil, nil);
    ^nil;
  endif;
  
  /* do test call */
  if pcallLock(self)
    resultPtr := new(Struct, 1);
    descPtr   := new(String, 32);
    if (physicalRangeOfAddresses(AddressLibClass$Inst,startAddr,toAddr)=nil)
      ^nil;
    endif;

    lastError := pcall(procs[#MEMTEST], startAddr,
              physicalRangeOfAddresses(AddressLibClass$Inst,startAddr,toAddr),
              resultPtr, descPtr);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError <> GOOD) 
    /* Report error */
    displayFormattedError(ErrorTextLibClass$Inst, 
       lastError, CHECK_MODE, nil, nil, nil);
    ^nil; 
  else
    if (wordAt(resultPtr, 0) = 0)
      tmpStr := "Test failure at " + descPtr;
      Call MessageBox(0, asciiz(tmpStr), asciiz("Test Memroy"),
                      MB_OK bitOr MB_ICONEXCLAMATION);
    else
      Call MessageBox(0, asciiz("RAM test OK"), asciiz("Test Memroy"),
                      MB_OK bitOr MB_ICONEXCLAMATION);
    endif;
  endif;
  ^0;
}!!

/* PUBLIC -- Write memory using address descriptor. */
Def writeMemAD(self, addressDescriptor, length, bytes | mem)
{
  /* don't propogate bad data */
  if (not(addressDescriptor) cor not(length) cor not(bytes))
    ^nil;
  endif;

  if ((mem := cMalloc(MallocLibClass$Inst, 4)) = 0)
    ^nil;  /* error */
  endif;

  putLong(mem, longAt(bytes, 0));
  /* do the write thing */
  if pcallLock(self)
    lastError := pcall(procs[#MEMWRITE], addressDescriptor, length, mem);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError <> GOOD)
    displayError(ErrorTextLibClass$Inst, lastError, CHECK_MODE);
    ^nil;
  endif;

  ^0;
}!!

/* PUBLIC: Set show cycles OFF. */
Def showCyclesOff(self | sim sysType proc)
{
  sysType := getCpuType(ProcLibClass$Inst);
  proc := getSpecificProcessor(ProcLibClass$Inst);
  
  if ((sysType = PROC_CPU_CPU32) cor (sysType = PROC_CPU_CPU32P)) then
    if not(sim := simRead(self))
      /* error reporting already done */
      ^nil;
    endif;
  else
    ^nil;
  endif;
  
  if ((sysType = PROC_CPU_CPU32) cor (proc = PROC_MC68349_TB)
                                 cor (proc = PROC_MC68349_SH)) then
    putByte(sim,(byteAt(sim,0) bitAnd SHOW_CYCLES_OFF_MASK),0);
    ^simWrite(self, sim);
  else
    if ((sysType = PROC_CPU_CPU32P) cand ((proc = PROC_MC68360_TB) cor 
                                          (proc = PROC_MC68360_SH))) then
      putByte(sim,(byteAt(sim,2) bitAnd SHOW_CYCLES_OFF_MASK),2);
      ^simWrite(self, sim );
    endif;
    ^nil;
  endif;
}
!!

/* PUBLIC: Set show cycles ON. */
Def showCyclesOn(self | sim sysType proc)
{
  sysType := getCpuType(ProcLibClass$Inst);
  proc := getSpecificProcessor(ProcLibClass$Inst);
  
  if ((sysType = PROC_CPU_CPU32) cor (sysType = PROC_CPU_CPU32P)) then
    if not(sim := simRead(self))
      /* error reporting already done */
      ^nil;
    endif;
  else
    ^nil;
  endif;
  
  if ((sysType = PROC_CPU_CPU32) cor (proc = PROC_MC68349_TB)
                                 cor (proc = PROC_MC68349_SH)) then
    putByte(sim,(byteAt(sim,0) bitOr SHOW_CYCLES_ON_MASK),0);
    ^simWrite(self, sim);
  else
    if ((sysType = PROC_CPU_CPU32P) cand ((proc = PROC_MC68360_TB) cor 
                                          (proc = PROC_MC68360_SH))) then
      putByte(sim,(byteAt(sim,2) bitOr SHOW_CYCLES_ON_MASK),2);
      ^simWrite(self, sim);
    endif;
    ^nil;
  endif;
}
!!

/* PUBLIC: Return 1 if show cycles on, 0 if off, nil if error. */
Def showCycles?(self | sim sysType proc)
{
  sysType := getCpuType(ProcLibClass$Inst);
  proc := getSpecificProcessor(ProcLibClass$Inst);
  if ((sysType = PROC_CPU_CPU32) cor (proc = PROC_MC68349_TB)
                                 cor (proc = PROC_MC68349_SH)) then
    if not(sim := simRead(self))
      ^nil;
    endif;
  
    if ((sim[0] bitAnd SHOW_CYCLES_ON_MASK) <> 0)
      ^1;
    endif;
  
    ^0;
  endif;
  if ((sysType = PROC_CPU_CPU32P) cand ((proc = PROC_MC68360_TB) cor 
                                          (proc = PROC_MC68360_SH))) then
    if not(sim := simRead(self))
      ^nil;
    endif;
    if ((sim[2] bitAnd SHOW_CYCLES_ON_MASK) <> 0)
      ^1;
    endif;
  
    ^0;
  endif;
  
  /* there isn't a sim */
  ^nil;
  
}
!!

/* PRIVATE: write the SIM. */
Def simWrite(self, byte | simAddress sysType proc mem)
{
  sysType := getCpuType(ProcLibClass$Inst);
  proc := getSpecificProcessor(ProcLibClass$Inst);
  
  if ((sysType = PROC_CPU_CPU32) cor (sysType = PROC_CPU_CPU32P)) then
    /* get the SIM address */
    if not(simAddress := getSimAddress(SharedDataLibClass$Inst))
      ^nil;
    endif;
  else 
    ^nil;
  endif;
  
  if ((sysType = PROC_CPU_CPU32) cor (proc = PROC_MC68349_TB)
                                 cor (proc = PROC_MC68349_SH)) then
    ^writeMem(self, simAddress, 1, byte);
  else
    if ((sysType = PROC_CPU_CPU32P) cand ((proc = PROC_MC68360_TB) cor 
                                          (proc = PROC_MC68360_SH))) then
      ^writeMem(self, simAddress, 4, byte);
    endif;
    ^nil;
  endif;
}
!!

/* PRIVATE: read the SIM. */
Def simRead(self | simAddress simByte sysType proc)
{
  sysType := getCpuType(ProcLibClass$Inst);
  proc := getSpecificProcessor(ProcLibClass$Inst);

  if ((sysType = PROC_CPU_CPU32) cor (sysType = PROC_CPU_CPU32P)) then
    /* get the SIM address (error reporting already done if needed). */
    if not(simAddress := getSimAddress(SharedDataLibClass$Inst))
      ^nil;
    endif;
  else
    ^nil;
  endif;
  
  if ((sysType = PROC_CPU_CPU32) cor (proc = PROC_MC68349_TB)
                                 cor (proc = PROC_MC68349_SH)) then
    /* read the contents */
    simByte := memRead(self, simAddress, 1);
  
    /* simByte will be the contents of the SIM byte, or nil for error */
    ^simByte;
  else
    if ((sysType = PROC_CPU_CPU32P) cand ((proc = PROC_MC68360_TB) cor 
                                          (proc = PROC_MC68360_SH))) then
      simByte := memRead(self, simAddress, 4);
      ^simByte;
    endif;
    ^nil;
  endif;
}
!!

/* Read memory bytes. Don't display error. */
Def memRead(self, offset, byteCount | bufRef tempArray aStruct index)
{
  /* stuff offset into address descriptor */
  if not(setOffset(AddressLibClass$Inst, workAddr1, offset))
    ^nil
  endif;

  /* read block of bytes at address */
  bufRef := new(Struct, 4);
  
  if pcallLock(self)
    lastError := pcall(procs[#MEMREAD], workAddr1, byteCount, bufRef, 0);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError = GOOD)
     aStruct := copyFromLong(new(Struct, byteCount), longAt(bufRef, 0));
     /* free memory allocated by Memory server. */
     cFree(MallocLibClass$Inst, longAt(bufRef, 0));
     ^aStruct;
  endif;

  if (longAt(bufRef,0) <> 0)
     /* free memory allocated by Memory server. */
     cFree(MallocLibClass$Inst, longAt(bufRef, 0));
  endif;
  ^nil;   
}!!

/* 5/23/1993 15:28 */
Def memCopyDlg (self, hWnd)
{

  /* call dialog box that lives in MEM.DLL */
  if pcallLock(self)
    lastError :=
      pcall(procs[#MEMDLGCOPYMEMORY],  hWnd );
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError <> GOOD)
    displayError(ErrorTextLibClass$Inst, lastError, CHECK_MODE);
    ^nil;  /* error */
  endif;
  ^0;
}
!!

/* PUBLIC -- close session
*/
Def closeSession(self)
{
  if workAddr1
    destroyAddress(AddressLibClass$Inst, workAddr1);
    workAddr1 := nil;
  endif;
  if workAddr2
    destroyAddress(AddressLibClass$Inst, workAddr2);
    workAddr2 := nil;
  endif;
  
  if (lastError <> GOOD) 
    ^nil;
  endif;
  ^0;
}!!

/* PUBLIC
   create work address. */
Def createWorkAddrs(self)
{
  /* create temporary work address if not already done */
  if not(workAddr1)
    workAddr1 := createAddress(AddressLibClass$Inst);
    if not(workAddr1)
      lastError := lastError(AddressLibClass$Inst);
      clearError(AddressLibClass$Inst);
      ^nil
    endif;
  endif;

  /* create temporary work address if not already done */
  if not(workAddr2)
    workAddr2 := createAddress(AddressLibClass$Inst);
    if not(workAddr2)
      lastError := lastError(AddressLibClass$Inst);
      clearError(AddressLibClass$Inst);
      ^nil
    endif;
  endif;

  ^0
}!!

/* PRIVATE */
Def initialize(self)
{
  /* create work address structure */
  if not(createWorkAddrs(self))
    ^nil;
  endif;
  
  ^self
}
!!

/* PUBLIC -- Compare memory. */
Def memCompare(self, firstAddr, secondAddr, length |
               resultRef result returnValue addr1 addr2)
{
  /* set offsets from text */
  lastError :=
    setOffsetText(AddressLibClass$Inst, workAddr1, firstAddr);
  if (lastError <> GOOD)
    ^#(nil, nil, nil);
  endif;
  lastError :=
    setOffsetText(AddressLibClass$Inst, workAddr2, secondAddr);
  if (lastError <> GOOD)
    ^#(nil, nil, nil);
  endif;

  /* do compare call */
  resultRef := new(Struct, 2);
  if pcallLock(self)
  then
    lastError := pcall(procs[#MEMCOMPARE], workAddr1, workAddr2,
                       asLong(length), resultRef);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError <> GOOD)
    displayError(ErrorTextLibClass$Inst,lastError,CHECK_MODE);
    ^#(nil, nil, nil);
  endif;

  /* result true: blocks identical; result false: get mismatch addresses */
  result := wordAt(resultRef, 0);
  /* test for C true */
  if (result <> 0)
     returnValue := #(1, 0, 0);
  else
    lastError := getAddressText(AddressLibClass$Inst, workAddr1);
    if not(lastError)
      addr1 := nil;
    else
      addr1 := lastError;
      lastError := getAddressText(AddressLibClass$Inst, workAddr2);
      if not(lastError)
        addr2 := nil;
      else
        addr2 := lastError;
      endif;
    endif;
    returnValue := #(0, addr1, addr2);  /* return beginning offsets */
  endif;
  
  ^returnValue;
}!!

/* PUBLIC -- Fill memory. */
Def memFill(self, startAddr, toAddr, pattern | struct1 index temp)
{
  /* compare address offsets for proper order */
  temp := compareAddresses(AddressLibClass$Inst, startAddr, toAddr);
  /* ADDR_GREATER_THAN = 0, ADDR_LESS_THAN = 1, ADDR_EQUAL = 2 */
  if not((temp = ADRLIB_ADDR_GREATER_THAN) cor 
      (temp = ADRLIB_ADDR_LESS_THAN) cor 
      (temp = ADRLIB_ADDR_EQUAL))
    ^nil;  /* error */
  endif;
 
  if (temp = ADRLIB_ADDR_GREATER_THAN)
    displayFormattedError(ErrorTextLibClass$Inst, 
       ER_MEM_FROM_TO, FORCE_POPUP, nil, nil, nil);
    ^nil;
  endif;
  
  struct1 := processPattern(self, pattern);
  if not(struct1)
    ^nil;
  endif;

  /* do fill call */
  if pcallLock(self)
    if (physicalRangeOfAddresses(AddressLibClass$Inst,startAddr,toAddr)=nil)
      ^nil;
    endif;
    lastError := pcall(procs[#MEMFILL], startAddr,
      physicalRangeOfAddresses(AddressLibClass$Inst,startAddr,toAddr), struct1, size(struct1));
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError <> GOOD) 
    /* Report error */
    displayFormattedError(ErrorTextLibClass$Inst, 
       lastError, CHECK_MODE, nil, nil, nil);
    ^nil; 
  endif;
  ^0;
}!!

/* PUBLIC -- Get access size. */
Def memGetAccessSize(self | wordRef)
{
  wordRef := new(Struct, 2);
  if pcallLock(self)
    lastError := pcall(procs[#MEMGETACCESSSIZE], wordRef);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError <> GOOD)
    ^nil;
  endif;
  ^wordAt(wordRef, 0);
}!!

/* PUBLIC -- Get access size. */
Def memGetVerifyWrites(self | byteRef)
{
  byteRef := new(Struct, 2);
  if pcallLock(self)
    lastError := pcall(procs[#MEMGETVERIFYWRITES], byteRef);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError <> GOOD)
    ^nil
  endif;
  ^byteAt(byteRef, 0);
}!!

/* PUBLIC -- Move memory. */
Def memMove(self, firstAddr, secondAddr, length)
{
  /* set offsets from text */
  if not(setOffset(AddressLibClass$Inst, workAddr1, firstAddr))
    ^nil;  /* error */
  endif;

  if not(setOffset(AddressLibClass$Inst, workAddr2, secondAddr))
    ^nil;  /* error */
  endif;

  /* do move call */
  if pcallLock(self)
    lastError := pcall(procs[#MEMMOVE], workAddr1, workAddr2, asLong(length));
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

 
  if (lastError <> GOOD)
    /* check for failure due to not implemented yet */
    if (lastError = 0x1d0001)
      displayFormattedError(ErrorTextLibClass$Inst, 
         ER_MEM_NO_FUNC, FORCE_POPUP, nil, nil, nil);
      ^0;
    endif;      
    displayError(ErrorTextLibClass$Inst, lastError, CHECK_MODE);
    ^nil;  /* error */
  endif;
  ^0;
}!!

/* PUBLIC -- Search memory. nil= error, -1=not found, otherwise offset. */
Def memSearch(self, firstAddr, secondAddr, invert, pattern | byte
  foundRef foundFlag struct1 temp)
{
  /* compare address offsets */
  temp := compareAddresses(AddressLibClass$Inst, firstAddr, secondAddr);
  /* NOTES:
  ** ADRLIB_ADDR_GREATER_THAN = 0, 
  ** ADRLIB_ADDR_LESS_THAN = 1, 
  ** ADRLIB_ADDR_EQUAL = 2 
  */
  if not((temp = ADRLIB_ADDR_GREATER_THAN) cor 
      (temp = ADRLIB_ADDR_LESS_THAN) cor 
      (temp = ADRLIB_ADDR_EQUAL))
    ^nil;  /* error */
  endif;
  
  if (temp = ADRLIB_ADDR_GREATER_THAN)
    displayFormattedError(ErrorTextLibClass$Inst, 
       ER_MEM_FROM_TO, FORCE_POPUP, nil, nil, nil);
    ^nil;
  endif;
  
  /* stuff pattern into structure */
  if not(struct1 := processPattern(self, pattern)) 
    ^nil;
  endif;
  
  /* do search call */
  foundRef := new(Struct, 2);
  if pcallLock(self)
    lastError := pcall(procs[#MEMSEARCH], firstAddr, secondAddr, 
      asInt(invert), struct1, size(struct1), foundRef);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  temp := nil;
  if (lastError <> GOOD)
    displayError(ErrorTextLibClass$Inst, lastError, CHECK_MODE);
  else
    /* -1 is NOT FOUND flag */
    temp := -1;
    foundFlag := wordAt(foundRef, 0);
    /* if found, return address where found */
    if (foundFlag <> 0)
      temp := getOffset(AddressLibClass$Inst, firstAddr);
    endif;
  endif;  
  ^temp;
}!!

/* PUBLIC -- Set access size. */
Def memSetAccessSize(self, size | wordRef)
{
  if pcallLock(self)
    lastError := pcall(procs[#MEMSETACCESSSIZE], size);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError <> GOOD)
    ^nil;
  endif;
  ^0;
}!!

/* PUBLIC -- Set access size. */
Def memSetVerifyWrites(self, flag | flagVal)
{
  if flag
    flagVal := 1
  else
    flagVal := 0;
  endif;

  if pcallLock(self)
    lastError := pcall(procs[#MEMSETVERIFYWRITES], flagVal);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError <> GOOD)
    ^nil
  endif;
  ^0;
}!!

/* PRIVATE: return byte stream constructed from pattern.
   Modified to match Shell behavior. */
Def processPattern(self, pattern | intVal index struct1 byteCount
  col temp maxByteWidth)
{
  /* remove leading, trailing blanks; break into individuals */
  col := findStrings(" "+peelBlanks(pattern), " ");
  
  /* remove "0x" on each string, determine byte/word/long */
  maxByteWidth := 1; /* assume byte until we find other */
  index := 0;
  loop
  while index < size(col)
    temp := col[index];
    /* remove "0x" if there */
    if (size(temp) > 2) cand (temp[0] = '0') cand
      ((temp[1] = 'x') cor (temp[1] = 'X'))
        temp := delete(temp, 0, 2);
        col[index] := temp;
    endif;
    
    if (maxByteWidth < 4) cand (size(temp) > 4)
      maxByteWidth := 4;
    else
      if (maxByteWidth < 2) cand (size(temp) > 2)
        maxByteWidth := 2;
      endif;
    endif;
    
    index := index + 1;
  endLoop;
  
  /* create a struct to hold each of the bytes */
  byteCount := maxByteWidth * size(col);
  struct1 := new(Struct, byteCount);
  
  /* stuff each of the bytes into the struct */
  index := 0;
  loop
  while index < size(col)
  begin
    temp := col[index];
    if not(intVal := asInt(temp,16))
      displayFormattedError(ErrorTextLibClass$Inst, 
         ER_MEM_NON_NUM, FORCE_POPUP, nil, nil, nil);
      ^nil;
    endif;
    select
      case maxByteWidth = 1
        putByte(struct1, intVal, index);
      endCase
      case maxByteWidth = 2 is
        putWord(struct1,
          swapBytesInWord(MemServLibClass$Inst, intVal),
          2 * index);
      endCase
      case maxByteWidth = 4 is
        putLong(struct1,
          swapBytesInLong(MemServLibClass$Inst, intVal),
          4 * index);
      endCase
    endSelect;
    
    index := index + 1;
  endLoop;
  
  ^struct1;
}!!

/* PUBLIC -- Read a block of lines, each line consisting of 16 bytes, from
   the Memory Server.  Return an array of 16-byte arrays (workTextBytes),
   or nil if there is an error.
*/
Def readLines(self, numLines, addressDescriptor | bufRef
    bigArray littleArray bigIndex littleIndex numBytes aStruct)
{
  /* read block of bytes at address */
  bufRef := new(Struct, 4);
  numBytes := numLines * 16;
  
  if pcallLock(self)
    lastError := pcall(procs[#MEMREAD], addressDescriptor, numBytes, bufRef, 0);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;

  if (lastError <> GOOD)
    displayError(ErrorTextLibClass$Inst,lastError,CHECK_MODE);
    ^nil;
  else
    aStruct := copyFromLong(new(Struct, numBytes), longAt(bufRef, 0));
    bigArray := new(Array, numLines);
    bigIndex := 0;
    loop
    while bigIndex < numLines
      littleArray := new(Array, 16);
      littleIndex := 0;
      loop
      while littleIndex < 16
        littleArray[littleIndex] :=
          byteAt(aStruct, (bigIndex*16) + littleIndex);
        littleIndex := littleIndex + 1;
      endLoop;
      bigArray[bigIndex] := littleArray;
      bigIndex := bigIndex + 1;
    endLoop;
  endif;
  
  /* clean up and return */
  cFree(MallocLibClass$Inst, longAt(bufRef, 0));

  ^bigArray;
}!!

/* PRIVATE */
Def setIVars(self, hL, n, o, p, gNS)
{
  hLib     := hL;
  name     := n;
  ordinals := o;
  procs    := p;
  globalNameSymbol := gNS;
}
!!

/* PUBLIC -- Write memory. */
Def writeMem(self, offset, length, bytes | mem)
{
  /* don't propogate bad data */
  if (not(offset) cor not(length) cor not(bytes))
    ^nil;
  endif;

  /* set offset */
  if not(setOffset(AddressLibClass$Inst, workAddr1, offset))
    ^nil;  /* error */
  endif;
  
  ^writeMemAD(self, workAddr1, length, bytes);
}!!

/* MemLibInst Class Initialization Code */
