/* CLASS: Virtual Smart Mixed Object
   Provides intelligent behaviors for a Virtual Mixed Object.
   Contains breakpoints information of the module.

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

inherit(VirtMixedObject, #VirtSMixedObject, #(
callersCache /* save current position */
bkptDict     /* breakpoints dictionary */
moduleInfo   /* a ModuleInfo object - from the currentLoadFile */
modulePath   /* module file path */
), 2, nil)!!

now(class(VirtSMixedObject))!!

now(VirtSMixedObject)!!

/* 3/11/1994 5:25 - PUBLIC (to its parent) */
Def setCursorLinked(self, newLinked)
{ 
  clearCursorLinked(self);  
  cursorLinked := newLinked;
}
!!

/* 3/11/1994 11:27 - PUBLIC (to its browser)
  Check if there is a breakpoint setting at the input <addrDesc> then clear it.
  Else set a breakpoint at the input <addrDesc>.
  NOTES:  This method consumes the <addrDesc>.
*/
Def bkptSetOrClear(self, bkptType, addrDesc, selTxt | bkptId)
{ 
  if (bkptId := findBkptInDict(self, addrDesc)) then
    /* destroy the input <addrDesc> */
    destroyAddress(AddressLibClass$Inst, addrDesc);
    ^removeBkptDesc(self, bkptId);
  else
    /* check for instruction boundary */
    if (isInstrBoundAddress?(self, selTxt)) then
       /* Set breakpoint to server - Server comsumes addrDesc */
      ^setAsmBkpt(HLBreakLibClass$Inst,
                  HLB_BKPT_STATE_ENABLED, /* state: enable */
                  bkptType,               /* life: Permanent | Temporary */
                  addrDesc);              /* address descriptor */
    endif;
    /* Cannot set breakpoint on an non-instruction boundary */
    destroyAddress(AddressLibClass$Inst, addrDesc);    
  endif;  
  ^nil;
}
!!

/* 7/1/1992 11:09 - PUBLIC
  Reset the currentExecPoint of self to nil.
*/
Def clearCurrentExecPoint(self)
{
  if (currentExecPoint) then
    destroyAddress(AddressLibClass$Inst, currentExecPoint);
    currentExecPoint := nil;
  endif;  
  ^currentExecPoint;
}
!!

/* 12/4/1992 15:05 - PRIVATE
  Return the <addrDesc> of the specified line. 
  Return nil if error.
  NOTES: Caller must destroy the <addrDesc> when done.
*/
Def getBkptAddr(self, lineNum, colNum, txtStr | addrSym, addrDesc)
{
  /* Check if it's not dasm Line then convert it to address */
  if not(addrDesc := mapLine(self, nil, txtStr)) then
    /* Remap lineNum from txtStr to Source line Num */
    if not(lineNum := getLineNum(self, txtStr)) then
      ^nil;
    endif;
    addrSym := "#"+moduleName(moduleInfo)+"#"+asString(lineNum)+"#"+asString(colNum);
    if not(addrDesc := convertTextToAddress(AddressLibClass$Inst, addrSym)) then
      ^nil;
    endif;
  endif;
  ^addrDesc;
}
!!

/* 7/1/1992 11:38 - PUBLIC (to its browser only)
  Convert the address text of input <txtStr> into <addrDesc>, 
  Caller must provides the <addrDesc>.  
  Return GOOD if successful, else return nil.
  NOTES: 
    - This method will not destroy the <addrDesc> in any case.
    - When lineNum is nil, do not map <txtStr> lineNumber to address.
*/
Def mapLineUsingAddrDesc(self, lineNum, txtStr, addrDesc | addrTxt, addrSym, lineNumPart)
{
  /* The address is more interesting than the line # itself */
  if not(addrTxt := getAddressField(self, txtStr)) cor
    not(convertTextToAddressWithAddrDesc(AddressLibClass$Inst, addrTxt, addrDesc)) then
    if not(lineNum)
      ^nil;
    endif;  
    /* Remap lineNum from txtStr to Source line Num */
    if not(lineNumPart := getLineNum(self, txtStr)) then
      ^nil;
    endif;
    /* Make up the symbolic string to convert */
    addrSym := "#"+moduleName(moduleInfo)+"#"+asString(lineNumPart);
    /* now convert the symbolic string to address */
    ^convertTextToAddressWithAddrDesc(AddressLibClass$Inst, addrSym, addrDesc);
  endif;
  ^GOOD;   
}
!!

/* 7/1/1992 11:38 - PUBLIC (to its browser only)
  Return the address descriptor of the input <txtStr>, 
  Return nil if error occurred.
  NOTES: 
    - Caller must destroy the returned <addrDesc> when done
*/
Def mapLine(self, lineNum, txtStr | addrDesc)
{
  /* map the txtStr contains address offset to a descriptor */
  if not(addrDesc := duplicateAddress(AddressLibClass$Inst, 
         addressRange(moduleInfo))) then
    ^nil;
  endif;   
  if not(mapLineUsingAddrDesc(self, lineNum, txtStr, addrDesc)) then
    /* failed to map, destroy the created addrDesc */ 
    destroyAddress(AddressLibClass$Inst, addrDesc);
    ^nil;  
 endif;
 ^addrDesc;   
}
!!

/* 3/9/1994 11:34 - PRIVATE
  Destroy all address descriptor of breakpoint set in data object.
  clear the content of the dictionary.
*/
Def clearBkptDict(self)
{ 
  if (size(bkptDict) > 0) then
    assocsDo (bkptDict,
      {using(bkpt)
        /* destroy the breakpoint address */
        destroyAddress(AddressLibClass$Inst, value(bkpt)[0]);
      });
    clear(bkptDict);
  endif;
}
!!

/* 10/15/1992 13:58 - PUBLIC
   Disable the specified breakpoint.
*/
Def disableStmtBkpt(self, lineNum, colNum, txtStr | bkptId, addrDesc)
{
  if (addrDesc := getBkptAddr(self, lineNum, colNum, txtStr)) then
    /* find the breakpoint */
    if (bkptId := findBkptInDict(self, addrDesc)) then
      destroyAddress(AddressLibClass$Inst, addrDesc);
      ^disableBkpt(HLBreakLibClass$Inst, bkptId);
    endif;
  endif;
  /* Can't find breakpoint */
  destroyAddress(AddressLibClass$Inst, addrDesc);
  reportBkptError(self, SRC_BKPT_DISABLE_BKPT);
  ^nil;
}
!!

/* 10/15/1992 13:58 - PUBLIC
   Enable the specified breakpoint.
*/
Def enableStmtBkpt(self, lineNum, colNum, txtStr | bkptId, addrDesc)
{
  if (addrDesc := getBkptAddr(self, lineNum, colNum, txtStr)) then
    /* find Bkpt in bkptDict */
    if (bkptId := findBkptInDict(self, addrDesc)) then
      destroyAddress(AddressLibClass$Inst, addrDesc);
      ^enableBkpt(HLBreakLibClass$Inst, bkptId);
    endif;
  endif;
  /* Can't find breakpoint */
  destroyAddress(AddressLibClass$Inst, addrDesc);
  reportBkptError(self, SRC_BKPT_ENABLE_BKPT);
  ^nil;
}
!!

/* 3/29/1992 13:58 - PUBLIC
   Clear the specified breakpoint.
*/
Def clearStmtBkpt(self, lineNum, colNum, txtStr | bkptId, addrDesc)
{
  if (addrDesc := getBkptAddr(self, lineNum, colNum, txtStr)) then
    /* findBkptInDict() consumes the <addrDesc> */
    if (bkptId := findBkptInDict(self, addrDesc)) then
      destroyAddress(AddressLibClass$Inst, addrDesc);
      ^removeBkptDesc(self, bkptId);
    endif;
  endif;
  /* Can't find breakpoint */
  destroyAddress(AddressLibClass$Inst, addrDesc);  
  reportBkptError(self, SRC_BKPT_CLR_BKPT);
  ^nil;
}
!!

/* 10/14/1992 16:00 - PRIVATE
  Return the lineNumber of the txtStr.
*/
Def getLineNum(self, txtStr | lineParts)
{
  /* Parse the line num out - txtStr = [xxxxxx]bttttttttt.... */
  if (lineParts := findStrings(txtStr, "[]")) then
    ^asInt(lineParts[0], 10); /* Convert number string to int - decimal radix */
 endif;
 ^nil;
}
!!

/* 06/20/92 - PUBLIC
  Search the dataObject for the virtual line number of the searchAddrDesc.
  Return lineNumber or nil.
  NOTES: This method does not destroy the <searchAddrDesc>
*/
Def searchAddress(self, searchAddrDesc, startLine |
    chunk, chunkNum, bestSoFar, dasmAddr, result, 
    found, line, addrType, compareFunc)
{
  /* Algorithm:
  **  Call VirtMixedObject to find the chunk number containing the startline.
  **  Get the chunk and search the chunk by compare the addresses.
  */
  if not(addrType := getAddressType(AddressLibClass$Inst, searchAddrDesc)) then
     addrType := ADDR_PHYSICAL;
  endif;       
  
  if not(chunkNum := chunkNumberFromSourceLine(self, startLine))
    ^nil;
  endif;
  /* temp address descriptor to compare */
  if not(dasmAddr := duplicateAddress(AddressLibClass$Inst, 
                                      addressRange(moduleInfo))) then
    ^nil;
  endif;
  found := nil;
  chunk := getChunk(self, chunkNum);  
  /* Locate the closest chunk */
  loop while ((chunk) cand not(found)) 
  begin
    /* chunk = #(startLineNum, textColl) or nil */
    bestSoFar := chunk[0] + size(chunk[1])-1;                        
    /* Compare against the last line of chunk to skip over to the next chunk */
    if (line := getLineNum(self, chunk[1][size(chunk[1])-1])) cand
       (line >= startLine) then
       found := #true;
    else    
      chunkNum := chunkNum + 1;
      chunk := getChunk(self, chunkNum);  
    endif;  
  endLoop;
  /* Have the closest chunk and search for address or line */
  loop while (chunk := getChunk(self, chunkNum)) begin
    /* Loop over the chunk */
    do(overBy(0, size(chunk[1]), 1),
    {using(i)
      /* map the line to address descriptor */
      if not(mapLineUsingAddrDesc(self, nil, chunk[1][i], dasmAddr)) cor             
         not(result := comparePhysicalAddressesNoError(AddressLibClass$Inst, 
              dasmAddr, searchAddrDesc)) then
        if (line := getLineNum(self, chunk[1][i])) then
          select
            case (line > startLine)
              result := ADRLIB_ADDR_GREATER_THAN;
            endCase
            case (line = startLine)
              result := ADRLIB_ADDR_EQUAL;
            endCase
            case (line < startLine)
              result := ADRLIB_ADDR_LESS_THAN;  
            endCase 
          endSelect;
        else
          /* cannot continue */
          destroyAddress(AddressLibClass$Inst, dasmAddr);
          ^bestSoFar;
        endif;  
      endif;  

      /* check compare result */
      if (result = ADRLIB_ADDR_EQUAL) then
        /* dasmAddr == searchAddrDesc - RETURN: (startline + lineoffset) */
        destroyAddress(AddressLibClass$Inst, dasmAddr);
        ^(chunk[0] + i);
      endif;   
      if (result = ADRLIB_ADDR_GREATER_THAN) then
        /* dasmAddr > searchAddrDesc - RETURN: the closest one so far */
        destroyAddress(AddressLibClass$Inst, dasmAddr);
        ^bestSoFar;
      endif;
      if (result = ADRLIB_ADDR_LESS_THAN) then
        /* dasmAddr < searchAddrDesc - Save closest line to bestSoFar */
        bestSoFar := chunk[0] + i;
      endif;            
    });    
    /* For out-of-order code, dasm could be showed into the next chunk 
    ** (or a much later chunk). Try looking there if we didn't find it 
    ** in the chunk with its source. */
    chunkNum := chunkNum + 1;
  endLoop;
  /* Can't find it, return closest match */
  destroyAddress(AddressLibClass$Inst, dasmAddr);
  ^bestSoFar;  
}
!!

/* 06/20/92 - PUBLIC
  Close self and save the browsing information to moduleInfo object for later use.
*/
Def close(self)
{
  clearBkptDict(self);
  clearCurrentExecPoint(self);
  clearCursorLinked(self);
  close(self:ancestor);
  if moduleInfo then
    setMixedLocations(moduleInfo, rememberedLocations);
    setMixedHighestChunk(moduleInfo, highestChunkSeen);
    setTotalMixedLines(moduleInfo, totalLinesGuess, totalLinesExact);
  endif;
}
 !!

/* 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)
{
  if not(moduleInfo) then
    ^nil;
  endif;
  ^duplicateAddress(AddressLibClass$Inst, addressRange(moduleInfo));
}
!!

/* 3/25/1992 11:30 - PUBLIC
  Return breakpoint Dictionary.
*/
Def bkptDict(self)
{
  ^bkptDict;
}
  !!

/* 06/20/92 - PUBLIC
  Return the callersCache - last position the caller had remembered.
*/
Def callersCache(self)
{
  ^callersCache;
}
!!

/* 3/24/1992 14:11 - 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 breakpoints - free Actor memory */
  moduleInfo   := nil;
  modulePath   := nil;
  languageName := nil;
}
!!

/* 6/30/1992 14:12 - PUBLIC
  Find a breakpoint in self dictionary. return breakpoint ID or nil;
  NOTES: bkptDict = [bkptID->#(addrDesc, colorTag)],...
  This method does not destroys the <bkptAddrDesc>
*/
Def findBkptInDict(self, bkptAddrDesc)
{
  /* validate input */
  if not(bkptAddrDesc) then
    ^nil; /* should not happen */
  endif;
  assocsDo(bkptDict,
    {using(bkpt)
      /* DEBUG 
      printLine(asAddr(AddressLibClass$Inst, value(bkpt)[0]));
      */
      if (comparePhysicalAddressesNoError(AddressLibClass$Inst, 
          bkptAddrDesc, value(bkpt)[0]) = ADRLIB_ADDR_EQUAL) then
        ^key(bkpt);
      endif;
     });
  ^nil;
}

!!

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

/* 5/29/1992 14:23 - PRIVATE
  Extract the address part of the mixed text string.  
  First, offset the starting point from the lineNumber porttion
  then find the first blank character from the offset.
  if cannot find the blank character then use the actual address size.
  Return the address text substring of a given mixed line.
*/
Def getAddressField(self, sourceTxt | startLoc, endLoc, tmpStr)
{
  if not(sourceTxt) ^nil; endif;
  if (size(sourceTxt) = 0) ^nil; endif;
  /* sourceTxt = [xxxxxx]b##########bNNNNN..... */
  if (sourceTxt[0] = '[') then
    /* use the addressSize for default location */      
    startLoc := 9; 
  else  
    startLoc := 0;
  endif;  
  endLoc := startLoc+addressSize;
  /* sourceTxt = [xxxxxx]b########..... */
  if (tmpStr := subString(sourceTxt, startLoc, endLoc)) cand
     (size(tmpStr) < addressSize) then
    /* bogus address text - return nil */
    ^nil;
  endif; 
  /* DEBUG -
  printLine(asString(startLoc)+"-"+asString(endLoc)+":"+tmpStr);
  */
  
  ^tmpStr;
}!!

/* 4/2/1992 14:41 - PUBLIC
  Get the current program counter and set its corresponding current execution point.
  Return PC address descriptor.
  NOTES: 
    - Caller must NOT destroy it.
*/
Def getCurrentExecPoint(self | pcAddr)
{
  /* NOTES: addressRange(moduleInfo) does not create addrDesc */
  if addressRange(moduleInfo) cand 
    (pcAddr := getProgramCounter(CpuLibClass$Inst)) then
    /* destroy the old currentExecPoint */
    if (currentExecPoint) then
      destroyAddress(AddressLibClass$Inst, currentExecPoint);
      currentExecPoint = nil;
    endif;  
    /* Check if the new PC address is inside of the module address range */
    if (maskAddressMSB(AddressLibClass$Inst, pcAddr) <> nil) cand
        (isAddrInAddressRangeNoError1(AddressLibClass$Inst, pcAddr, 
         addressRange(moduleInfo)) ) then
      currentExecPoint := pcAddr;
    endif;
  endif;
  /* Return either the old or new current execution point */
  ^currentExecPoint;
}
!!

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

/* 06/20/92 - PUBLIC
  Return the moduleDescriptor of self.
*/
Def moduleDescriptor(self)
{
  if not(moduleInfo) then
    ^nil;
  endif;
  ^moduleDescriptor(moduleInfo);
}
!!

/* 7/22/1992 9:11 - PUBLIC
  Return the moduleInfo of self.
*/
Def moduleInfo(self)
{
  ^moduleInfo;
}!!

/* 7/17/1992 16:41 - PRIVATE
  Open a mixed data object - Ancestor do the dirty work.
*/
Def openMixed(self, aModuleInfo, addrSize | newObj)
{
  if (newObj := openMixed(self:ancestor, aModuleInfo, addrSize)) <> self then
    ^newObj /* error case */
  endif;
  getCurrentExecPoint(self);
  rebuildBkptDict(self);
}!!

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

  clearBkptDict(self);
  loopBkpt := nil; /* initial starting point for the loop */
  loop
  while (bkptResult := getAllAddrBkptNext(HLBreakLibClass$Inst,
    addressRange(moduleInfo), loopBkpt))
    begin
      /* NOTES: bkptResult = #(loopDesc, bkptDesc) */
      loopBkpt := bkptResult[0];
      /* Get BkptInfo to add to dictionary - NOTES: 
      ** bkptInfo = #(state, life, addrDesc) 
      */
      if bkptInfo := getBkpt(HLBreakLibClass$Inst, bkptResult[1]) then
        add(bkptDict, bkptResult[1], tuple(bkptInfo[2], 
                      getBkptColor(self, bkptInfo[0], bkptInfo[1])));
      endif;
  endLoop;
  ^size(bkptDict);
}
!!

/* 3/29/1992 13:44 - PUBLIC
  Remove all breakpoints in bkptDict of self and the server.
    NOTES: bkptDict = ([bkptID->#(addrDesc, colorTag)], ...)
*/
Def removeAllBkpt(self)
{
  if bkptDict cand (size(bkptDict) > 0) then
    assocsDo (bkptDict,
      {using(bkpt)   
        destroyAddress(AddressLibClass$Inst, value(bkpt)[0]);
        removeBkpt(HLBreakLibClass$Inst, key(bkpt));
      });
    clear(bkptDict);
  endif;
}
!!

/* 7/28/1992 11:46 - PRIVATE 
    NOTES: bkptDict = ([bkptID->#(addrDesc, colorTag)], ...)
*/
Def removeBkptDesc(self, bkptId)
{
  /* 
  ** Remove bkpt from the dictionary first - otherwise, 
  ** events notification will not work properly.
  */
  destroyAddress(AddressLibClass$Inst, bkptDict[bkptId][0]);  
  remove(bkptDict, bkptId);
  ^removeBkpt(HLBreakLibClass$Inst, bkptId);
}


!!

/* 6/26/1992 16:51 - PUBLIC
  Reopen the previouly opened Mixed Data object.
*/
Def reOpen(self, addrSize)
{
  if (moduleInfo) cand 
     (reOpen(self:ancestor, moduleInfo, addrSize)) then
    if not(moduleDescriptor(self)) then
      ^nil;
    endif;
    getCurrentExecPoint(self);
    rebuildBkptDict(self);
    ^self;
  endif;
  ^nil;
}
!!

/* 06/20/92 - PUBLIC
   Look for text in mixed data object starting at line startLine, column 0.
   If not found, return nil, else return #(line, column).
*/
Def searchText(self, searchText, startLine, getTxtFunc 
   | lineNum, chunk, chunkNum, pointPos, startSearchLine, oldChunk)
{
  oldChunk := chunk := lineNum := startLine ; /* <Judy 9/3/97> */
  loop while chunk
  begin
    /* Check Abort */
    if queryAbort(TaskLibClass$Inst) then
      ^nil;
    endif;
    chunkNum := chunkNumberFromLineNumber(self, lineNum);
    if (chunk := getChunk( self, chunkNum)) then
      /* Added by <Judy 9/3/97> to fix search bug, test report id:102 */
      if (chunk = oldChunk) then
         ^nil;
      else
         oldChunk := chunk;
      endif;
      /* eof <Judy 9/3/97> */
      /* chunk is: #(startLineNum, textColl) or nil */
      if (startSearchLine := (lineNum - chunk[0])) < 0 then
        startSearchLine := 0;
      endif;
      if (pointPos := findStringInChunk(self, chunk[1],
            searchText, startSearchLine, 0, getTxtFunc)) then
        /* RETURN:  #((start-line + line-offset) , char-offset) */
        ^tuple((chunk[0] + y(pointPos)), x(pointPos));
      endif ;
      /* NOTES: 
      ** rememberedLocations = #(filePos,SourceLine#,virtStartLine,numLines) 
      */
      lineNum := (chunk[0] + rememberedLocations[chunkNum][3]) + 1;
    endif ;
  endLoop;
  ^nil
}
!!

/* 6/26/1992 17:01 - PUBLIC
  Set a breakpoint to self and save the breakpoint ID to dictionary
    Return GOOD or nil.
*/
Def setBkpt(self, bkptType, lineNum, colNum, txtStr | addrDesc) {
  /* Set Asm breakpoint - check for non-opcode asm line */
  if not(isInstrBoundAddress?(self, txtStr)) cor
     not(addrDesc := getBkptAddr(self, lineNum, colNum, txtStr)) then  
    ^nil;
  endif;
 
  /* Bkpt Server will consume the addrDesc */
  ^setAsmBkpt(HLBreakLibClass$Inst,
      HLB_BKPT_STATE_ENABLED, /* state: enable */
      bkptType,               /* life: Permanent | Temporary */
      addrDesc);
}
!!

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

/* 06/20/92 - PUBLIC */
Def setModulePath(self, newPath)
{
  modulePath := asLowerCase(newPath);
  if not(unixFile?(newPath)) cand
    sourceOutOfSynch?(LoaderLibClass$Inst, modulePath) then
    if not(reportOutOfSynch(moduleInfo)) then
      displayFormattedError(ErrorTextLibClass$Inst,
         ER_SOURCE_NEWER, FORCE_POPUP, nil, nil, nil);
    endif;
    modulePath := modulePath+"*";  /* Out of Synch indicator */
    setReportOutOfSynch(moduleInfo, #true);
  endif;
}
!!

/* 7/14/1992 15:06 - PUBLIC (to ancestor) */
Def setModuleTotalLines(self, numLines)
{
  if moduleInfo then
    setMixedLocations(moduleInfo, rememberedLocations);
    setMixedHighestChunk(moduleInfo, highestChunkSeen);
    setTotalMixedLines(moduleInfo, numLines, numLines);
  endif;
}
!!

/* 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 | addrDesc)
{
  /* Create an address descriptor to set PC */
  if (addrDesc := getBkptAddr(self, lineNum, colNum, txtStr)) then 
    /* CPU Server will consume the addrDesc */
    ^setProgramCounter(CpuLibClass$Inst, addrDesc);
  endif;
  ^nil;
}
!!

/* 06/20/92 - PUBLIC
  Return the title of data object (used the file name in this case.)
*/
Def virtObjTitle(self | len)
{
  /* Truncate the title to be less than 25 characters */
  if (len := size(modulePath)) > 25 then
     ^"..."+subString(modulePath, len - 25, len);
  endif;
  ^modulePath;
}
!!
