/* CLASS: DisAsmLib
 Provide interface to the DisAssembler (DASM) Server.

 REQUIRE: related libraries: Symbol, Mem, Cpu, Address.
*/!!

inherit(ProvidedLibs, #DisAsmLib, #(workAddr /* work address descriptor */), 2, nil)!!

now(class(DisAsmLib))!!

/* semiPRIVATE -- should only be called via require(DisAsmLib)
   Define entry points and load library.  Define any structs required.
*/
Def provide(self, path)
{
  ^initialise( new(self:ancestor), path )
}
!!

now(DisAsmLib)!!

/* 11/22/1995 10:44 */
/* PUBLIC -- Set address space. */
Def setDadAddrSpace(self, dasmID, space)
{
  if pcallLock(self)then
    lastError := pcall(procs[#DADSETADDRSPACE], dasmID, space);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION ;
  endif ;
  if (lastError <> GOOD) then
    displayError(ErrorTextLibClass$Inst,lastError, CHECK_MODE);
    ^nil;
  endif;  
  ^GOOD;
} 
!!

/* 3/18/1994 13:10 - PUBLIC
  Set Dasm session Operand/Address Size option
  Where <addrSize> =
     ADDR_USE_16, ADDR_USE_32, ADDR_USE_AUTO.
*/
Def setDasmAddressSize(self, dasmID, addrSize)
{ 
  if pcallLock(self) then
    lastError := pcall(procs[#DADSETDASMOPADDRSIZE], dasmID, addrSize);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;
  if lastError <> GOOD
    displayError(ErrorTextLibClass$Inst, lastError, CHECK_MODE);
    ^nil;
  endif;
  ^lastError
}
!!

/* 3/10/1994 11:38 - PUBLIC
  Copy the current dasm section address to the input <addrDesc>
  Return GOOD if successful, else nil.
  NOTES: Caller is responsible for the <addrDesc>.
*/
Def currentDasmAddress(self, dasmID, inAddrDesc)
{  
  if pcallLock(self)then
    lastError := pcall(procs[#DADCURRENTDASMADDR], dasmID, inAddrDesc);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION ;
  endif ;
  if (lastError <> GOOD) then
    displayError(ErrorTextLibClass$Inst,lastError, CHECK_MODE);
    ^nil;
  endif;  
  ^GOOD;
}
!!

/* PUBLIC */
Def setDasmSymbol(self, sym | addrStruct)
{
  if pcallLock(self)then
    lastError := pcall(procs[#DADSETDASMSYMBOL], sym);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION ;
  endif ;
  if (lastError <> GOOD) then
    displayError(ErrorTextLibClass$Inst,lastError, CHECK_MODE);
    ^nil;
  endif;  
  ^0
}
!!

/* PUBLIC */
Def getDasmSymbol(self | addrStruct)
{
  addrStruct := new(Struct, 2);
  if pcallLock(self)then
    lastError := pcall(procs[#DADGETDASMSYMBOL], addrStruct);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION ;
  endif ;
  if (lastError <> GOOD) then
    displayError(ErrorTextLibClass$Inst,lastError, CHECK_MODE);
    ^nil;
  endif;  
  ^wordAt(addrStruct, 0);
}
!!

/* 2/4/1992 17:06 - PRIVATE */
Def addImportProcs(self)
{
  add(self, #INITCSERVER,              1, #(0, 0)           );
  add(self, #DADCURRENTDASMADDR,       1, #(1, 1)           );
  add(self, #DADDASMCLOSE,             1, #(1)              );
  add(self, #DADDASMGETRANGEINSTCOUNT, 1, #(1, 1, 1, 1)     );
  add(self, #DADDASMGETRANGEOFFSET,    1, #(1, 0, 1, 1)     );
  add(self, #DADDASMINST,              1, #(1, 0, 1, 1)     );
  add(self, #DADDASMINSTBYLINE,        1, #(1, 0, 1, 1)     );
  add(self, #DADDASMOPEN,              1, #(1, 1)           );
  add(self, #DADDASMPREVINST,          1, #(1, 0, 1, 1)     );
  add(self, #DADDASMRANGEOFINST,       1, #(1, 1, 1, 1)     );
  add(self, #DADGETDASMADDR,           1, #(1, 1)           );
  add(self, #DADGETDASMSYMBOL,         1, #(1)              );
  add(self, #DADSETDASMADDR,           1, #(1, 1)           );
  add(self, #DADSETDASMSYMBOL,         1, #(0)              );
  add(self, #DADSETDASMOPADDRSIZE,     1, #(1, 0)           );   
  add(self, #DADSETADDRSPACE,          1, #(1, 0)           );   
}
!!

/* PUBLIC */
Def closeDasm(self, dasmID)
{
  if pcallLock(self) then
    lastError := pcall(procs[#DADDASMCLOSE], dasmID);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION ;
  endif ;

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

/* PUBLIC
   create work address. */
Def createWorkAddr(self)
{
  /* create temporary work address if not already done */
  if not(workAddr) then
    if not(workAddr := createAddress(AddressLibClass$Inst)) then
      lastError := lastError(AddressLibClass$Inst);
      clearError(AddressLibClass$Inst);
      ^nil
    endif;
    if not(setAddrSpace(AddressLibClass$Inst, workAddr, 6)) then
      lastError := lastError(AddressLibClass$Inst);
      clearError(AddressLibClass$Inst);
      ^nil
    endif;
  endif;
  ^GOOD;
}

!!

/* PUBLIC */
Def getAddress(self, dasmID | addrStruct)
{
  addrStruct := new(Struct, 4);
  if pcallLock(self)then
    lastError := pcall(procs[#DADGETDASMADDR], dasmID, addrStruct);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION ;
  endif ;
  if (lastError <> GOOD) then
    displayError(ErrorTextLibClass$Inst,lastError, CHECK_MODE);
    ^nil;
  endif;  
  ^longAt(addrStruct, 0);
}
!!

/* PUBLIC
   Simplified entry point for dasm display of memory presenter.
   Returns at least the requested number of lines before and after a
   particular offset. */
Def getDasmInstAroundOffset(self, dasmID, offset, linesBefore, linesAfter |
      revisedOffset instructionsBefore addrDesc offsetAdjustment)
{
  if not(offset)
    revisedOffset := 0;
    linesBefore := 0
  else    
    /* back up offset if needed */
    if linesBefore <= 0
      revisedOffset := offset;
      linesBefore := 0;
    else  /* linesBefore positive */
      addrDesc := createAndSetAddress(AddressLibClass$Inst, offset);
      if not(addrDesc)
        ^nil;
      endif; /* address creation failed */
    
      /* adjust beginning address, but don't back beyond 0 */
      offsetAdjustment :=
        min(offset, (linesBefore * DASM_MAX_BYTES_PER_LINE));
      lastError := subtractFromAddress(AddressLibClass$Inst, addrDesc,
        offsetAdjustment);
      if not(lastError)
        destroyAddress(AddressLibClass$Inst, addrDesc);
        ^nil;
      endif;

      revisedOffset := getOffset(AddressLibClass$Inst, addrDesc);
      if not(revisedOffset)
        destroyAddress(AddressLibClass$Inst, addrDesc);
        ^nil;
      endif;
    
      if not(destroyAddress(AddressLibClass$Inst, addrDesc))
        ^nil;
      endif;
    endif;
  endif;

  /* set offset; error has already been posted. */
  lastError := simpleSetOffset(self, dasmID, revisedOffset);
  if not(lastError)
    ^nil
  endif;

  /* calculate smallest number of instructions before that will
     guarantee coverage of desired range. */
  instructionsBefore := linesBefore *
    (DASM_MAX_BYTES_PER_LINE / DASM_MIN_BYTES_PER_INSTRUCTION);

  /* read lines; error has already been posted. */
  /* just use number of lines after as the number of instructions after
     because there is a minimum of one instruction per line. */
  lastError :=
    getDasmInstructions(self, dasmID, instructionsBefore + linesAfter);
  if not(lastError)
    ^nil
  endif;

  ^lastError
}
!!

/* 7/10/1992 15:27 - PUBLIC
  Get the number of dasm instruction by number of wanted lines.
  NOTES: Server may returns more lines than requested.
  Caller: VirtDasmObject
  04/22/94 - This routine is provide only to the Source Window. 
  DON'T TOUCH IT.
*/
Def getDasmInstByLines(self, dasmID, wantedLines | buflenRef buflen str bufRef dasmCollection)
{
  bufRef    := new(Struct, 4);
  buflenRef := new(Struct, 4);
  if pcallLock(self) then
    lastError := pcall(procs[#DADDASMINSTBYLINE], dasmID, 
                     asInt(wantedLines), bufRef, buflenRef);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION ;
  endif;
  if (lastError <> GOOD) then
    /* Do not display error - Let the caller handle error cases */
    if (longAt(bufRef, 0) <> 0) then /* Buffer is not NULL, require cleanup */
       /* free memory allocated by server. */
       cFree(MallocLibClass$Inst, longAt(bufRef, 0));
    endif;
    ^nil;
  endif;

  buflen := longAt(buflenRef, 0);
  str := physicalString(
         copyFromLong(new(Struct, buflen), longAt(bufRef, 0)));
  /* Break dasm string into text collection of instructions */
  add(dasmCollection := new(TextCollection, 5), "");
  insertText(dasmCollection, removeNulls(str), 0, 0);
  /* free memory allocated by server. */
  cFree(MallocLibClass$Inst, longAt(bufRef, 0));
  ^dasmCollection
}


!!

/* 06/20/92 - PUBLIC
  Get the disassembly text of the address in the state table.
  Update to point to the next instruction.
*/
Def getDasmInstructions(self, dasmID, lineCount
                        | buflenRef buflen str bufRef dasmCollection index)
{
  bufRef := new(Struct, 4);
  buflenRef := new(Struct, 4);
  if pcallLock(self) then
    lastError :=
      pcall(procs[#DADDASMINST], dasmID, lineCount, bufRef, buflenRef);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;
  if lastError <> GOOD then
    displayError(ErrorTextLibClass$Inst,lastError, CHECK_MODE);
    ^nil;
  endif;

  buflen := longAt(buflenRef, 0);
  str := physicalString(
             copyFromLong(new(Struct, buflen), longAt(bufRef, 0)));
  /* break str into text collection */
  add(dasmCollection := new(TextCollection, 5), "");
  insertText(dasmCollection, removeNulls(str), 0, 0);
  /* free memory allocated by server. */
  cFree(MallocLibClass$Inst, longAt(bufRef, 0));
  ^dasmCollection
}
!!

/* 6/11/92 - PUBLIC
   Get number of disassembly instructions and number of text line
   of the address range.
   NOTES: return a tuple #(numInsts, numLines).
   Caller: VirtMixedObject
*/
Def getDasmRangeInstCount(self, dasmID, addrRangeDesc | instCountRef lineCountRef )
{
  instCountRef := new(Struct, 4);
  lineCountRef := new(Struct, 4);
  if pcallLock(self) then
    lastError :=
      pcall(procs[#DADDASMGETRANGEINSTCOUNT], dasmID, addrRangeDesc,
           instCountRef, lineCountRef);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION ;
  endif ;
  if lastError <> GOOD then
    if lastError <> ER_BUS_TERMINATION then
      displayError(ErrorTextLibClass$Inst, lastError, CHECK_MODE);    
    endif;  
    ^nil;
  endif;

  ^tuple(longAt(instCountRef, 0), longAt(lineCountRef, 0));
}
!!

/* 6/11/92 - PUBLIC
   Get disassembly instructions of address range.
   NOTES: return text collection or nil.
   Caller: VirtMixedObject.
*/
Def getDasmRangeInstructions(self, dasmID, addrRange
                        | buflenRef buflen str bufRef dasmCollection)
{
  bufRef := new(Struct, 4);
  buflenRef := new(Struct, 4);
  if not(dasmID) cor not(addrRange) then
    displayError(ErrorTextLibClass$Inst, ER_DASM_CANT, CHECK_MODE);    
    ^nil;
  endif;
  if not(bufRef) cor not(buflenRef) then
    /* Make sure that we have valid suffs */ 
    displayError(ErrorTextLibClass$Inst, ER_NO_MEMORY, CHECK_MODE);    
    ^nil;
  endif;  
  if pcallLock(self) then
    lastError :=
      pcall(procs[#DADDASMRANGEOFINST], dasmID, addrRange, bufRef, buflenRef);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION ;
  endif ;
  if (lastError <> GOOD) then
    /* Do not report error - Caller handles error recovery */
    if longAt(bufRef, 0) <> 0 then /* Buffer is not NULL, require cleanup */
      /* free memory allocated by server. */
      cFree(MallocLibClass$Inst, longAt(bufRef, 0));
    endif;
    ^nil;
  endif;
  buflen := wordAt(buflenRef, 0);
  if ((buflen > 16383) cor (buflen < 0)) then
    buflen := 16382; /* buflen is TOO LARGE - adjust the size to 16K-1 max */
  else
    buflen := asInt(buflen);
  endif;     
  /* transfer C buffer to Actor string */
  str := physicalString(
         copyFromLong(new(Struct, buflen), longAt(bufRef, 0)));
  /* Break dasm string into text collection of instructions */
  if (dasmCollection := new(TextCollection, 5)) 
    add(dasmCollection, "");
    insertText(dasmCollection, removeNulls(str), 0, 0);
  endif;  
  /* free memory allocated by server. */
  cFree(MallocLibClass$Inst, longAt(bufRef, 0));
  ^dasmCollection
}
!!

/* PUBLIC */
Def getInstruction(self, dasmID)
{
  ^getDasmInstructions(self, dasmID, 1);
}
!!

/* 7/10/1992 13:45 - PUBLIC
  Move the Dasm offset to new offset of a number of wanted lines.
  Return #(nextOffsetAddrDesc, actualLines)
*/
Def getOffsetOfNextDasmLine(self, dasmID, numWantedLines | addrDescRef, numLineRef)
{
  addrDescRef := new(Struct, 4);
  numLineRef := new(Struct, 4);

  if pcallLock(self) then
    lastError :=
      pcall(procs[#DADDASMGETRANGEOFFSET], dasmID, numWantedLines,
        addrDescRef, numLineRef);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION ;
  endif ;
  if lastError <> GOOD then
    displayError(ErrorTextLibClass$Inst,lastError, CHECK_MODE);
    ^nil;
  endif;
  /* Return #(addrDesc, numLine) */
  ^tuple(longAt(addrDescRef, 0), longAt(numLineRef, 0));
}
!!

/* return the previous instruction
*/
Def getPreviousInstruction(self, dasmID, count |
      buflenRef buflen bufRef str dasmCollection)
{
  bufRef := new(Struct, 4);
  buflenRef := new(Struct, 2);

  if pcallLock(self)
  then
    lastError :=
      pcall(procs[#DADDASMPREVINST], dasmID, count, bufRef, buflenRef);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION ;
  endif ;

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

  buflen := wordAt(buflenRef, 0);
  str := physicalString(
             copyFromLong(new(Struct, buflen), longAt(bufRef, 0)));
  cFree(MallocLibClass$Inst, longAt(bufRef, 0));
  /* break str into text collection */
  add(dasmCollection := new(TextCollection, 5), "");
  insertText(dasmCollection, removeNulls(str), 0, 0);
  ^dasmCollection;
}
!!

/* init (British version) */
Def initialise(self, path)
{
  createWorkAddr(self);
  ^initialise(self:ancestor, path)
}
!!

/* init */
Def initialize(self, path)
{
  createWorkAddr(self);
  ^initialize(self:ancestor, path)
}

!!

/* move the offset backward from the current position by line.
   For each line to back up:
     get previous line
     set address to offset in previous line
*/
Def moveOffsetBackward(self, dasmID, count | buflenRef bufRef offset col 
                       addrDesc addrDigits)
{
  bufRef := new(Struct, 4);
  buflenRef := new(Struct, 4);

  lastError := getPreviousInstruction(self, dasmID, count);
  if not(lastError)
    ^nil
  else
    /* get offset from first line of text collection */
    col := lastError;
    
    addrDigits := maxOutputAddrDigits(AddressLibClass$Inst,0);
    if not(addrDigits) ^nil; endif;
    
    /* get the address text at the top of the collection */
    addrDesc := convertTextToAddress(AddressLibClass$Inst, 
                asciiz(subString(col[0], 0, addrDigits + 1)));
    offset := getOffset(AddressLibClass$Inst, addrDesc);
    if not(offset) then
      destroyAddress(AddressLibClass$Inst, addrDesc);
      ^nil;
    endif;
    
    if not(destroyAddress(AddressLibClass$Inst, addrDesc)) 
      ^nil;
    endif;
    
    /* set offset */
    if not(simpleSetOffset(self, dasmID, offset))
    then
      ^nil;
    endif;
  endif;
}
!!

/* move the offset forward from the current position by line.
   (read and discard the desired number of lines.) */
Def moveOffsetForward(self, dasmID, count | returnValue)
{
  returnValue := getDasmInstructions(self, dasmID, count);
  if not(returnValue)
    ^nil;
  endif;
  returnValue := nil;
  ^0;
}
!!

/* 07/10/92 - PUBLIC */
Def openDasm(self, startAddr | descRef)
{
  descRef := new(Struct, 4);
  if pcallLock(self) then
    lastError := pcall(procs[#DADDASMOPEN], descRef, startAddr);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION ;
  endif ;
  if lastError <> GOOD then
    displayError(ErrorTextLibClass$Inst,lastError, CHECK_MODE);
    ^nil;
  endif;
  ^longAt(descRef, 0)
}
!!

/* PUBLIC 
  NOTES: Caller has ownership of the input <AddrDesc>.
*/
Def setAddress(self, dasmID, addrDesc)
{
  if pcallLock(self) then
    lastError := pcall(procs[#DADSETDASMADDR], dasmID, addrDesc);
    pcallUNLock(self);
  else
    lastError := ERR_PCALL_RECURSION;
  endif;
  if lastError <> GOOD
    displayError(ErrorTextLibClass$Inst, lastError, CHECK_MODE);
    ^nil;
  endif;
  ^lastError
}
!!

/* PUBLIC
   Simplified open; don't require user to mess with address structure;
   initialize dasm at address 0 with memSize/processor defaults. */
Def simpleOpenDasm(self)
{
  if not(workAddr)
    lastError := createWorkAddr(self);
    if not(lastError)
      ^nil
    endif;
  else
    /* set offset in address structure */
    lastError := setOffset(AddressLibClass$Inst, workAddr, 0);
    if not(lastError)
      ^nil
    endif;
  endif;

  ^openDasm(self, workAddr);
}

!!

/* PUBLIC */
Def simpleSetOffset(self, dasmID, offset | temp addrDesc)
{
  if (offset bitAnd 0x1) = 0x1
    offset := offset bitAnd 0xFFFFFFFE;
  endif;
  
  if not(addrDesc := getAddress(self, dasmID)) then
     ^nil; /* failed to get current dasm address, bail out */
  endif;

  /* set offset in address structure */
  if not(lastError := setOffset(AddressLibClass$Inst, addrDesc, offset))
    destroyAddress(AddressLibClass$Inst, addrDesc);
    ^nil
  endif;

  /* send address to dasm server */
  if not(lastError := setAddress(self, dasmID, addrDesc))
    destroyAddress(AddressLibClass$Inst, addrDesc);
    ^nil
  endif;

  destroyAddress(AddressLibClass$Inst, addrDesc);
  ^GOOD;
}!!
