/* CLASS: VIRTSMRTDASMOBJECT
    Provides intelligent behaviors for a VirtDasmObject.  Specialization for
    browsing disassembly text associated with an address range.  Also,
    contains breakpoints information to set/clear and display.
    Used by SOURCECB.

    REQUIRE: VIRTDATA.CLS, VIRTDASM.CLS
*/!!

inherit(VirtDasmObject, #VirtSmrtDasmObject, #(moduleDescriptor
callersCache /* save current Position  */
bkptDict /* breakpoints dictionary */), 2, nil)!!

now(class(VirtSmrtDasmObject))!!

now(VirtSmrtDasmObject)!!

/* 3/29/1992 13:58 - PUBLIC
   Disable the specified breakpoint.
*/
Def disableStmtBkpt(self, lineNumIgnored, colNumIgnored, txtStr | addrOffset)
{
  /* Check if it's a source Line - Set source breakpoint then */
  if not(addrOffset := getAddressField(self, txtStr)) then
    ^nil;
  endif;

  /* Do associate to remove - bkptDict = [bkptID->#(addrOffset, colorTag)] */
  assocsDo (bkptDict,
    {using(bkpt)
      if (compareOffsets(AddressLibClass$Inst, addrOffset,
                         value(bkpt)[0]) = ADRLIB_ADDR_EQUAL) then
        ^disableBkpt(HLBreakLibClass$Inst, key(bkpt));
      endif;
    });
  /* Can't find breakpoint */
  reportBkptError(self, SRC_BKPT_DISABLE_BKPT);
  ^nil;
}
!!

/* 3/29/1992 13:58 - PUBLIC
   Enable the specified breakpoint.
*/
Def enableStmtBkpt(self, lineNumIgnored, colNumIgnored, txtStr | addrOffset)
{
  /* Check if it's a source Line - Set source breakpoint then */
  if not(addrOffset := getAddressField(self, txtStr)) then
    ^nil;
  endif;

  /* Do associate to remove - bkptDict = [bkptID->#(addrOffset, colorTag)] */
  assocsDo (bkptDict,
    {using(bkpt)
      if (compareOffsets(AddressLibClass$Inst, addrOffset,
                         value(bkpt)[0]) = ADRLIB_ADDR_EQUAL) then
        ^enableBkpt(HLBreakLibClass$Inst, key(bkpt));
      endif;
    });
  /* Can't find breakpoint */
  reportBkptError(self, SRC_BKPT_ENABLE_BKPT);
  ^nil;
}
!!

/* 3/29/1992 13:58 - PUBLIC
   Clear the specified breakpoint from the breakpoint dictionary.
*/
Def clearStmtBkpt(self, lineNumIgnored, colNumIgnored, txtStr | addrOffset)
{
  /* Get address offset of selected line */
  if not(addrOffset := getAddressField(self, txtStr)) then
    ^nil;
  endif;

  /* Do associate to remove - bkptDict = [bkptID->#(addrOffset, colorTag)] */
  assocsDo (bkptDict,
    {using(bkpt)
      if (compareOffsets(AddressLibClass$Inst, addrOffset,
                         value(bkpt)[0]) = ADRLIB_ADDR_EQUAL) then
        ^removeBkptDesc(self, key(bkpt));
      endif;
    });
  /* Can't find breakpoint */
  reportBkptError(self, SRC_BKPT_CLR_BKPT);
  ^nil;
}
!!

/* 06/20/92 - PUBLIC
  Search the dataObject for the virtual line number of the searchAddr.
  Return lineNumber or nil.
*/
Def searchAddress(self, searchAddrOffset, startLine | lineNum, chunk, result
                            chunkNum, searchAddr, bestSoFar, subStr, dasmAddr) {
  chunk := lineNum := bestSoFar := startLine;
  searchAddr := dasmAddr := nil;
  /* If self is close then  need to reopen the dataObject */
  if closed? then
    chunk := reOpen(self) ;  /* if chunk is nil, return nil */
  endif ;

  /* create the search address and dasm address descriptor */
  if not(searchAddr := addressRange(self)) cor
     not(setOffset(AddressLibClass$Inst, searchAddr, searchAddrOffset)) cor
     not(dasmAddr := addressRange(self)) then
    if (searchAddr) 
      destroyAddress(AddressLibClass$Inst, searchAddr); 
    endif;
    ^nil;
  endif;

  loop while chunk
  begin
    chunkNum := chunkNumberFromLineNumber(self, lineNum);
    /* Get data and search for it */
    if (chunk := getChunk( self, chunkNum)) then
      if (errReported(self) <> GOOD)
        destroyAddress(AddressLibClass$Inst, searchAddr);
        destroyAddress(AddressLibClass$Inst, dasmAddr);      
        ^nil;
      endif; 
      /* NOTES: chunk = #(startLineNum, textColl) or nil */
      /* Loop over the chunk and check for a match */
      do(overBy(0, size(chunk[1]), 1),
        {using(i)
          subStr := subString(chunk[1][i], 0, addressSize);
          /* Stop when error occured in address conversion and compare */
          if not(convertTextToAddressWithAddrDesc(AddressLibClass$Inst, subStr, dasmAddr)) cor
             not(result := compareAddresses(AddressLibClass$Inst, dasmAddr, searchAddr)) then
            destroyAddress(AddressLibClass$Inst, searchAddr);
            destroyAddress(AddressLibClass$Inst, dasmAddr);             
            ^bestSoFar;
          endif;
          select
            case (result = ADRLIB_ADDR_EQUAL)
              /* RETURN:  (start-line + line-offset) */
              destroyAddress(AddressLibClass$Inst, searchAddr);
              destroyAddress(AddressLibClass$Inst, dasmAddr);              
              ^(chunk[0] + i);
            endCase
            case (result = ADRLIB_ADDR_LESS_THAN)
              /* Save the closest location to bestSoFar - dasmAddr < searchAddr */
              bestSoFar := chunk[0] + i;
            endCase
            case (result = ADRLIB_ADDR_GREATER_THAN)
              /* dasmAddr > searchAddr - Return the closest location */
              destroyAddress(AddressLibClass$Inst, searchAddr);
              destroyAddress(AddressLibClass$Inst, dasmAddr);
              ^bestSoFar; 
            endCase  
          endSelect;
        });
      /* Only move to the next chunk if the last compare result is < */
      if (result = ADRLIB_ADDR_LESS_THAN) then  
        /* NOTES: rememberedLocations = #(startOffset, numLinesInChunk, startLineOfChunk) */
        lineNum := (chunk[0] + rememberedLocations[chunkNum][1]) + 1;
      else
        chunk := nil;
      endif;     
    endif;
  endLoop;
  /* destroy unused addrDesc */
  destroyAddress(AddressLibClass$Inst, searchAddr);
  destroyAddress(AddressLibClass$Inst, dasmAddr);
  ^bestSoFar;
}
!!

/* 8/27/1992 18:28 - PUBLIC (to its browser)
  Dummy method to extract the addressField of the txtStr.
*/
Def mapLineNumToAddress(self, txtStr)
{
  ^getAddressField(self, txtStr);
}
!!

/* 7/1/1992 11:38 - PUBLIC (to its browser only)
  Map a virtual lineNumber to its equivalent object context value
    NOTES: Address offset is the lineNum in this case.
*/
Def mapLineNum(self, ignoreArg, txtStr)
{
 ^getAddressField(self, txtStr);
}
!!

/* 7/10/1992 13:35 - PRIVATE
  return the address range descriptor of module.
  NOTES: Caller must clean up the return address descriptor when done.
*/
Def addressRange(self)
{
  ^duplicateAddress(AddressLibClass$Inst, aMemoryRange);
}
!!

/* 6/26/1992 9:45 - PUBLIC
  Return breakpoint dictionary.
*/
Def bkptDict(self)
{
  ^bkptDict;
}
!!
/* 6/26/1992 9:47 -PUBLIC
  Return the callerCache - last position the caller had remembered.
*/
Def callersCache(self)
{
  ^callersCache;
}
!!

/* 6/26/1992 9:53 - PUBLIC
  Close self.
*/
Def close(self)
{
  currentExecPoint := nil;
  close(self:ancestor);
}
!!

/* 6/26/1992 9:50 - PUBLIC
  Destroy object and its data components.
*/
Def destroyDataObj(self)
{
  if not(closed?) then close(self); endif;
  callersCache := nil;
  bkptDict     := nil; /* NOTES: do not remove bkpt from server - free Actor memory */
  languageName := nil;
  /* Get rid of the address descriptor */
  if aMemoryRange then
    destroyAddress(AddressLibClass$Inst, aMemoryRange);
    aMemoryRange := nil;
  endif;
}

!!

/* 6/30/1992 14:12 - PUBLIC
  Find a breakpoint in self dictionary. return breakpoint ID or nil;
  NOTES: bkptDict = [bkptID->#(addrOffset, colorTag]
*/
Def findBkptInDict(self, offset)
{
  /* Compare the input offset and all breakpoint offset */
  assocsDo(bkptDict,
    {using(bkpt)
      if (compareOffsets(AddressLibClass$Inst, offset,
            value(bkpt)[0]) = ADRLIB_ADDR_EQUAL) then
         ^key(bkpt);
      endif;
     });
  ^nil;
}

!!

/* 3/25/1992 11:36 - PUBLIC
  Check and see if the input offset is a special lines
  (e.g. Breakkpoints setting, and so on).
*/
Def findSpecialLine(self, offset | bkptId)
{
  if offset cand size(bkptDict) > 0 then
    /* NOTES: bkptDict = ([bkptDescId->#(lineNum, colorTag)], ...) */
    if (bkptId := findBkptInDict(self, offset)) then
      ^bkptDict[bkptId][1]; /* return colorTag */
    endif;
  endif;
  ^nil;
}
!!

/* 6/30/1992 14:17 - PUBLIC
   If a Dasm has been reOpen()'ed, cached info may be stale.  The user
   can call this function to flush cached info, or not call it to keep
   cached info.
   NOTES: caller should flush its own callersCache.
*/
Def flushCachedMemory(self)
{
  flushCachedMemory(self:ancestor);
  moduleDescriptor := nil ;
}

!!

/* 5/29/1992 14:23 - PRIVATE
  return the address offset given a dasm line.
*/
Def getAddressField(self, sourceTxt | lineOffset tmpDesc)
{
  if not(tmpDesc := convertTextToAddressNoError(AddressLibClass$Inst,
                 asciiz("0x" + subString(sourceTxt, 0, addressSize))))
    ^nil;
  endif;

  if (lineOffset := getOffset(AddressLibClass$Inst, tmpDesc)) then
    destroyAddress(AddressLibClass$Inst, tmpDesc);
    ^asLong(lineOffset);
  endif;
  ^nil;
}
!!

/* 4/2/1992 14:41 - PUBLIC
  Get the current program counter and set its corresponding current execution point.
*/
Def getCurrentExecPoint(self | pcAddr, addrInfo)
{
  if aMemoryRange cand (pcAddr := getProgramCounter(CpuLibClass$Inst)) then
    if (maskAddressMSB(AddressLibClass$Inst, pcAddr) <> nil) cand
        (isAddrInAddressRange(AddressLibClass$Inst, pcAddr, aMemoryRange)) then
      /* Get the offset of current Execution point of the data object */
      currentExecPoint := getOffset(AddressLibClass$Inst, pcAddr);
    else
      currentExecPoint := nil;
    endif;
    /* destroy pcAddr */
    destroyAddress(AddressLibClass$Inst, pcAddr);
  endif;
  /* Return either the old or new current execution point */
  ^currentExecPoint;
}
!!

/* 6/26/1992 10:06 - PRIVATE
  Initialize a VirtSmrtDasmObject.
*/
Def init(self, anAddrRange)
{
  /* Allocate holder for breakpoints */
  bkptDict := new(Dictionary, 2);
  clear(bkptDict);
  currentExecPoint := nil;
  languageName := #Unknown;
  ^init(self:ancestor, anAddrRange);
}
!!

/* 7/1/1992 11:38 - PUBLIC (to its browser only)
  Map a virtual lineNumber to its equivalent object context value
    NOTES: Address offset is the lineNum in this case.
*/
Def lineNum(self, ignoreArg, txtStr)
{
 if (size(txtStr) = 0)
   ^nil;
 endif;
 ^getAddressField(self, txtStr);
}
!!

/* 7/12/1992 13:50 - PUBLIC
  Return the moduleDescriptor of Self.
*/
Def moduleDescriptor(self)
{
  ^nil; /* There is no symbolic information for the dasm object */
}
!!

/* 7/22/1992 9:11 - PUBLIC
  Return the moduleInfo of self (in this case, it's nil.)
*/
Def moduleInfo(self)
{
  ^nil;
}
!!

/* 7/12/1992 13:54 - PUBLIC
  Open the dasm session of the memory range, then find out about it.
  Notes: aMemoryRange is an addrDesc - destroy by this object when it done.
*/
Def openDasm(self, aMemoryRange | newObj)
{
  if not(newObj := openDasm(self:ancestor, aMemoryRange)) then
    destroyAddress(AddressLibClass$Inst, aMemoryRange);
    ^nil;
  endif;
  /* Get the current execution point and its breakpoint setting */
  getCurrentExecPoint(self);
  rebuildBkptDict(self);
}
!!

/* 6/26/1992 10:18 - PUBLIC
  Rebuild bkptDict in respond to event notification.
    NOTES: bkptDict = ([bkptID->#(addrOffset, colorTag)],...)
*/
Def rebuildBkptDict(self | bkptInfo, bkptResult, loopBkpt )
{
  if not(aMemoryRange) then
    displayFormattedError(ErrorTextLibClass$Inst,
       ER_INVALID_DATA_OBJECT, FORCE_POPUP, nil, nil, nil);
    ^nil;
  endif;

  clear(bkptDict);
  loopBkpt := nil; /* initial starting point for the loop */
  loop
  while (bkptResult := getAllAddrBkptNext(HLBreakLibClass$Inst,
    aMemoryRange, loopBkpt))
    begin
      /* NOTES: bkptResult = #(loopDesc, bkptDesc) */
      loopBkpt := bkptResult[0];
      /* Get BkptInfo to add to dictionary - bkptInfo = #(state, life, addrDesc) */
      if bkptInfo := getBkpt(HLBreakLibClass$Inst, bkptResult[1]) then
        add(bkptDict, bkptResult[1],
            tuple(getOffset(AddressLibClass$Inst, bkptInfo[2]),
            getBkptColor(self, bkptInfo[0], bkptInfo[1])) );
        /* Get rid ot the unused address descriptor */
        destroyAddress(AddressLibClass$Inst, bkptInfo[2]);
      endif;
  endLoop;
  ^size(bkptDict);
}

!!

/* 3/29/1992 13:44 - PUBLIC
  Remove all breakpoints in bkptDict of self.

*/
Def removeAllBkpt(self)
{
  if size(bkptDict) > 0 then
    keysDo (bkptDict,
      {using(bkptId)
        removeBkpt(HLBreakLibClass$Inst, bkptId);
      });
    clear(bkptDict);
  endif;
}
!!

/* 6/30/1992 14:32 - PRIVATE */
Def removeBkptDesc(self, bkptDescId)
{
  /* Remove it from the dictionary first - otherwise, events will create problem */
  remove(bkptDict, bkptDescId);
  ^removeBkpt(HLBreakLibClass$Inst, bkptDescId);
}

!!

/* 6/26/1992 16:51 - PUBLIC
  Reopen the previouly opened Dasm session
*/
Def reOpen(self)
{
  if reOpen(self:ancestor) then
    getCurrentExecPoint(self);
    rebuildBkptDict(self);
    ^self;
  endif;
  ^nil;
}
!!

/* 06/20/92 - PUBLIC
   Look for text in dasm starting at line startLine, column 0.
   If not found, return nil, else return #(line, column).
*/
Def searchText(self, searchText, startLine, getTxtFuncIgnore | lineNum, chunk, chunkNum, pointPos)
{
  chunk := lineNum := startLine ;
  if closed? then
    chunk := reOpen(self) ;  /* if chunk is nil, return nil */
  endif ;
  loop while chunk
  begin
    /* Check Abort */
    if TaskLibClass$Inst cand queryAbort(TaskLibClass$Inst) then
      ^nil;
    endif;
    chunkNum := chunkNumberFromLineNumber(self, lineNum);
    if chunk := getChunk( self,  chunkNum) then
      /* chunk is: #(startLineNum, textColl) or nil */
      if (pointPos := findStringInChunk(self, chunk[1],
          searchText, (lineNum-chunk[0]), 0, getTxtFuncIgnore)) then
        /* RETURN:  #((start-line + line-offset) , char-offset) */
        ^tuple((chunk[0] + y(pointPos)), x(pointPos));
      endif ;
      /* NOTES: rememberedLocations = #(startOffset, numLinesInChunk, startLineOfChunk) */
      lineNum := (chunk[0] + rememberedLocations[chunkNum][1]) + 1;
    endif ;
   endLoop;

  ^nil
}
!!

/* 6/26/1992 17:01 - PUBLIC
  Set a breakpoint to server and save the breakpoint ID to dictionary
  NOTES: ignored lineNum and colNum info.
*/
Def setBkpt(self, bkptType, lineNumIgnored, colNumIgnored, txtStr |
  addrOffset, addrDesc,addrSpace)
{
  /* Create an address descriptor to set break */
  if not(isInstrBoundAddress?(self, txtStr)) then
    ^nil;
  endif;
  if not(addrOffset := getAddressField(self, txtStr)) cor
    not(addrDesc := addressRange(self)) then
    ^nil;
  endif;
  if not(setOffset(AddressLibClass$Inst, addrDesc, addrOffset)) then
    destroyAddress(AddressLibClass$Inst, addrDesc);
    ^nil;
  endif;
  /* added by cjchen 1996.11.26*/
  if not(addrSpace:=getSpaceMode(CpuLibClass$Inst)) then
    ^nil;
  endif;
  if (addrSpace=0)
    addrSpace:=6;
  else
    addrSpace:=2;
  endif;
  if not(setAddrSpace(AddressLibClass$Inst,addrDesc,addrSpace)) then
    ^nil;
  endif;
  /* added by cjchen EOF*/
  /* Set breakpoint to server - Server comsumes addrDesc */
  if not(setAsmBkpt(HLBreakLibClass$Inst,
      HLB_BKPT_STATE_ENABLED, /* state: enable */
      bkptType,               /* life: Permanent | Temporary */
      addrDesc)) then         /* address descriptor */
    ^nil;
  endif;
  /* Let the Event Notification update the bkptDict */
  ^GOOD;
}
!!

/* 6/26/1992 17:18 - PUBLIC
  Set caller cached position.
*/
Def setCallersCache(self, newCacheObject)
{
  ^(callersCache := newCacheObject);
}
!!

/* 7/21/1992 11:32 - PUBLIC (to its Parent)
  Set the current PC to the corresponding address of the input line.
  NOTES: return nil if failed.
*/
Def setPC(self, lineNum, colNum, txtStr | addrOffset, addrDesc)
{
  /* Create an address descriptor to set PC */
  if not(addrOffset := getAddressField(self, txtStr)) cor
    not(addrDesc := createAddress(AddressLibClass$Inst)) then
    ^nil;
  endif;
  if not(setOffset(AddressLibClass$Inst, addrDesc, addrOffset)) then
    destroyAddress(AddressLibClass$Inst, addrDesc);
    ^nil;
  endif;

  /* Callee will consume the addrDesc */
  ^setProgramCounter(CpuLibClass$Inst, addrDesc);
}
!!

/* 6/30/1992 16:39 - PUBLIC */
Def virtObjTitle(self)
{ ^"Disassembly";}
!!
