/* CPU Presenter - 
   Main Window of the CPU Presenter.  Provides user with the ability to
   edit, and view register's contents of the MC68332 CPU.
 */!!

inherit(Window, #CpuPresenter, #(signals /* Dict of sigId -> #(sigVal, sigNum) */
registerGroups
registerWidth /* in characters */
labelWidth      /* in characters */
fontInfo
tmWidth
tmHeight
selectedReg /* typically nil */
regPopup /* for value edit & radix display */
eventDescSet
oldPC), 2, nil)!!

now(class(CpuPresenter))!!

/* PUBLIC - Open a new CPU presenter. Example: open(CpuPresenter). */
Def open(self | aCpuPresenter evtDesc)
{
  ^openWithPosAndState(self, nil, SW_SHOW)
}!!

/* PUBLIC - Open a new CPU presenter. Example: open(CpuPresenter). */
Def openWithPosAndState(self, sizeRect, showVal | aCpuPresenter evtDesc)
{
  aCpuPresenter := newStyle(self, nil, nil, "CPU" , 
        sizeRect, nil, WS_OVERLAPPED bitOr WS_MINIMIZEBOX bitOr WS_CAPTION
        bitOr WS_SYSMENU);

  /* Display the presenter and return its handle */
  if (aCpuPresenter) then
    registerF1Help(CLIULibraryClass$Inst, HI_ENTRY_CPU,
       getHWnd(aCpuPresenter), HELP_ENTRY_CPU);
    show(aCpuPresenter, showVal ) ;
  endif;
  
  ^aCpuPresenter;
}!!



/* 2/19/1992 14:46 - PUBLIC 
  Define self class
*/
Def wndClass(self)
{ 
  ^"CpuPresenter";
}
!!

/* 2/19/1992 14:47 - PUBLIC 
  Return the name of self window icon */
Def wndIcon(self)
{ 
  ^"CPU";
}
!!

now(CpuPresenter)!!

/* 4/14/1994 7:56 */
Def gotFocus(self, hWndPrev)
{
   updateSignals(self);
   ^gotFocus(self:ancestor, hWndPrev);
}
!!

/* PRIVATE -- Call CPU server to reset the CPU only. */
Def cmdResetCpuOnly(self, msg)
{ 
  /* 04/25/94 - Nghia
  ** Move Warning message to CpuLib 
  */
  if (CpuLibClass$Inst) then
    resetCpuOnly(CpuLibClass$Inst);
  endif;  
}
!!

/* 2/19/1992 15:48 - CPU_HELP Event */
Def menuCPUHelp(self, msg)
{ 
  contextNumber(TheHelpObject, hWnd(self), HELP_ENTRY_CPU);
  ^GOOD;
}
!!

/* 2/19/1992 15:48 - CPU_HELP Event */
Def cmdHelpPViews(self, msg)
{ 
  menuHelpContents(TheToolBarPresenter);
  ^GOOD;
}
!!

/* PUBLIC */
Def close(self)
{
  closeEvents( self ) ;
  TheCPUPresenter := nil ;  /* kill global reference */
  close( self:ancestor ) ;
}
!!

/* PRIVATE 
   UNREGISTER with Event Notification for events opened in initEvent()
*/
Def closeEvents(self)
{
  if eventDescSet
  then
    do(eventDescSet,
    {using(eventDescriptor)
      unRegister( EvNoteLibClass$Inst, eventDescriptor ) ;
    });
    eventDescSet := new( Set, 3 ) ;  /* not needed, but ... */
  endif ;
}
!!

/* PRIVATE -- user sets CPU signal */
Def cmdControlSignals(self, signal)
{ 
  if signals
  then
    signals[signal][0] := not(signals[signal][0]) ;
    if setSignal( CpuLibClass$Inst, signals[signal][1], signals[signal][0] )
    then
      if signals[signal][0]
      then checkMenuItem(   self, signal ) ;
      else unCheckMenuItem( self, signal ) ;
      endif ;
    endif ;
  endif;
}
!!

/* MENU */
Def cmdExit(self, ignored)
{ 
  close( self ) ;
}
!!

/* 2/19/1992 15:46 - CPU_FLOATREG Event */
Def cmdFloatRegs(self, msg)
{ 
  ^GOOD;
}
!!

/* 2/19/1992 15:48 - CPU_HELP Event */
Def cmdHelp(self, msg)
{ 
  menuHelpHelp(TheToolBarPresenter);
  ^GOOD;
}
!!

/* PRIVATE -- Call CPU server to assert the RESET signal */
Def cmdResetCpu(self, msg)
{ 
  /* 04/25/94 - Nghia
  ** Move Warning message to CpuLib 
  */
  if (CpuLibClass$Inst) then
    reset( CpuLibClass$Inst);
  endif;
}
!!

/* 2/19/1992 16:19 
  Respond to the menu events. The wp argument gives the selected menu ID.
  Get a message symbol from the menu object. */
Def command(self, wp, lp | msg)
{
  /* Perform the menu action */  
  if msg := action(menu, wp)
  then ^perform(self, wp, msg)
  endif;
}
!!

/* PRIVATE -- set up for 68332 */
Def configureRegisters(self | userG)
{
  userG  := new( RegGroup, self, CPU_USERREG,  0, initUserGroupRegs(self) ) ;
  makeVisible( userG ) ;
  setGroupPosition( userG, 0, labelWidth, registerWidth ) ;

  registerGroups := tuple(userG) ;
}
!!

/* PUBLIC */
Def eventDescSet(self)
{ ^eventDescSet }
!!

/* 3/26/1992 15:36 */
Def eventPCChange(self | newPC)
{ 
  requireWithPath( AddressLib,  "addr.dll" ) ;
  if TheCursorLinkServer cand 
     (newPC := getProgramCounter(CpuLibClass$Inst))
  then
    propagatePC( TheCursorLinkServer, self, newPC ) ;
  endif ;
}
!!

/* PUBLIC */
Def fontInfo(self)
{ ^fontInfo }
!!

/* 2/27/1992 15:24 */
Def getFont(self)
{ ^fontInfo }
!!

/* 2/19/1992 14:49 - PUBLIC 
  Initialize the CPU Presenter by initialize its menu, window position,
  registers set.*/
Def init(self)
{ 
  requireWithPath( CpuLib, "cpu.dll" ) ;
  showWaitCurs();
  if (getEmulationStatus(requireWithPath(HLBrkRootLib,"bkroot.dll")) 
                         <> HLB_EMU_HALTED) then 
    displayFormattedError(ErrorTextLibClass$Inst, 
       ER_CPU_PROC_EM, FORCE_POPUP, nil, nil, nil);
  endif;
    initMainMenu( self ) ; 
    initMainWindow( self ) ;
    initEvents( self ) ;
    showOldCurs();
    ^self;

  showOldCurs() ;
}
!!

/* PRIVATE */
Def initEvents(self | eventDesc, events)
{
  /*  REGISTER with Event Notification for Whatever... */

  eventDescSet := new( Set, 1 ) ;
  
  /* dynamic to pick up current values */
  events := tuple( EVENT_CPU_EDIT,
                   EVENT_CPU_HALTED
  ) ;
  do(events,
  {using(event)
    if (eventDesc := register( EvNoteLibClass$Inst, event, hWnd(self))) then
      add( eventDescSet, eventDesc ) ;
    endif ;
  });
  
}
!!

/* 2/19/1992 14:55 - PRIVATE
  Create and initialize main menu of the CPU Presenter Window */
Def initMainMenu(self | popup)
{ 
  /* Use menu instance variable to create the main menu */
  menu := create(new(Menu), self);
  
  /* Options Menu */
  popup := newPopup(MenuItem, "&Options");
  addItem(popup, new(MenuItem, "R&eset", CPU_RESET, #cmdResetCpu));
  addItem(popup,
    new(MenuItem, "Reset CPU &Only", CPU_RESET_CPU_ONLY, #cmdResetCpuOnly));

  if (getSystemType(ProcLibClass$Inst) = PROC_POWERSCOPE) then
    addItem(popup, new(MenuItem, "&Signals", CPU_NO_SIGNAL, #cmdHelpPViews));
  else
    addItem(popup, newSignalsPopup(self)); /* signals menu items */
  endif;
  addItem(popup, nil); /* menu item separator */
  addItem(popup, new(MenuItem, "Help &Index", CPU_PV_HELP, #cmdHelpPViews));
  addItem(popup, new(MenuItem, "&Help With Help", CPU_WIN_HELP, #cmdHelp));
  addItem(popup, new(MenuItem, "Help With &CPU", CPU_HELP, #menuCPUHelp));
  addItem(popup, nil); /* menu item separator */
  addItem(popup, new(MenuItem, "E&xit", CPU_EXIT, #cmdExit));
  addItem(menu, popup);

  drawMenu(self);  
  if (getSystemType(ProcLibClass$Inst) = PROC_POWERSCOPE) then
    grayMenuItem(menu, CPU_NO_SIGNAL);
  endif;
}
!!

/* 2/19/1992 15:35 - PRIVATE
  Initialize the main window of the CPU Presenter */
Def initMainWindow(self)
{
  labelWidth    := 1 ;  /* characters */
  registerWidth := 1 ;  /* characters */
  initTextMetrics( self ) ;
  initSignals( self ) ;
  initRegisters( self ) ;
  newWinSize( self ) ; 
}
!!

/* PRIVATE
  Initialize CPU register groups & get register values
*/
Def initRegisters(self)
{ 
  configureRegisters( self ) ;
  updateRegisters( self ) ;
  updateRegisters( self ) ; /* 2nd time is to clear change hilight */
}
!!

/* PRIVATE -- Initialize CPU control signals */
Def initSignals(self | sigValue numSigs)
{
  if (getSystemType(ProcLibClass$Inst) = PROC_POWERSCOPE)
    ^nil;
  endif;
  numSigs := getNumberOfSignals(CpuLibClass$Inst);
  if ((numSigs > 16) cor (numSigs < 0))
    ^numSigs;
  endif;
  signals := new(Dictionary, numSigs);  
    
  do(over(0, numSigs),
  {using(index)
    add (signals, CPU_SIG_BASE+index, tuple(nil, index));
  });

  updateSignals(self);
}
!!

/* PRIVATE -- build status register bit definitions */
Def initSRBits(self, SR | bitColl xBase)
{
   /* Display: "SR 0SM0XNZVC" */
   bitColl := newWithCompareBlock( SortedCollection,
                                   8,
                                   {using(x, y) sortIndex(x) < sortIndex(y)}
                                 ) ;

   /* user bits */
/*@@  xBase := (4 * tmWidth(self)) + 2 ; /* see RegGroup:reposition() */
   xBase := 0 ;
   add( bitColl, 
     newWithLocation(RegBit, "X", 0x0010, SR, 3, point((tmWidth*3)+xBase, 0) ) ) ;
   add( bitColl, 
     newWithLocation(RegBit, "N", 0x0008, SR, 4, point((tmWidth*4)+xBase, 0) ) ) ;
   add( bitColl, 
     newWithLocation(RegBit, "Z", 0x0004, SR, 5, point((tmWidth*5)+xBase, 0) ) ) ;
   add( bitColl, 
     newWithLocation(RegBit, "V", 0x0002, SR, 6, point((tmWidth*6)+xBase, 0) ) ) ;
   add( bitColl, 
     newWithLocation(RegBit, "C", 0x0001, SR, 7, point((tmWidth*7)+xBase, 0) ) ) ;
   /* super bits */
   add( bitColl, 
         newWithLocation(RegMultiBit, 
                   "" /*I*/, SR, 0x0700,  8, 2, point((tmWidth*2)+xBase, 0)) ) ;
   add( bitColl, 
     newWithLocation(RegBit, "S", 0x2000, SR, 1, point((tmWidth*1)+xBase, 0) ) ) ;
   add( bitColl, 
     newWithLocation(RegMultiBit, 
                   "" /*T*/, SR, 0xC000, 14, 0, point((tmWidth*0)+xBase, 0)) ) ;
   setBitsDict( SR, bitColl ) ;
}
!!

/* PRIVATE -- @@ should get font from fontInfo @@ */
Def initTextMetrics(self | hDC, tm, origFont)
{ tm := new(Struct, 32);
  fontInfo := Call GetStockObject(SYSTEM_FIXED_FONT) ;
  hDC := getContext( self ) ;
  origFont := Call SelectObject( hDC, fontInfo ) ;
  Call GetTextMetrics( hDC, tm ) ;
  tmWidth  := asInt(wordAt(tm, 10));
  tmHeight := asInt(wordAt(tm, 8)) + asInt(wordAt(tm, 0));
  if origFont
    Call SelectObject( hDC, origFont ) ;
  endif;
  releaseContext( self, hDC ) ;
}!!

/*  PRIVATE -- return orderedCollection of user registers*/
Def initUserGroupRegs(self | regName regColl numRegs sortIndex width)
{ 
  numRegs := getNumRegs(CpuLibClass$Inst);
  
  regColl := newWithCompareBlock( SortedCollection,
                                  numRegs,
                                  {using(x, y)
                                   sortIndex(x) < sortIndex(y)
                                  }
                                ) ;

/*  sr := new(BitRegister, "SR", nil, nil, nil, 0  ) ;
  initSRBits( self, sr ) ;
  add( regColl, sr ) ;
*/

  do(over(0, numRegs),
  {using(index | type)
    type := getRegisterType(CpuLibClass$Inst, index);
    regName := getRegisterName(CpuLibClass$Inst, index);
    sortIndex := getRegisterSortIndex(CpuLibClass$Inst, index);
    width := (getRegisterWidth(CpuLibClass$Inst, index) + 3) / 4;
    if (size(regName)+1 > labelWidth) then
      labelWidth := size(regName)+1;
    endif;
    if (width > registerWidth) then
      registerWidth := width;
    endif;

    add( regColl, new(Register, regName, index, sortIndex, 0, width) ) ;
    if (type <> 0) then           /* add flag bit display line */
      add( regColl, new(Register, regName, index, sortIndex+1, type, type) ) ;
    endif;
  });
  
  ^regColl
}
!!

/* PRIVATE */
Def line2register(self, lineNum)
{
  do(registerGroups,
  {using(regGroup | reg)
    if visible?(regGroup) cand (reg := line2reg(regGroup, lineNum))
    then ^reg 
    endif ;
  });
  
}
!!

/* 2/19/1992 15:31 - PRIVATE 
  Define the Signals popup menu and its actions
*/
Def newSignalsPopup(self | popup numSigs sigName)
{ 
  popup := newPopup(MenuItem,  "&Signals");
  numSigs := getNumberOfSignals(CpuLibClass$Inst);
  if ( (numSigs <= 0) cor (numSigs > 16) ) ^ER_ILLEGAL_SIGNAL; endif;
  do(over(0, numSigs),
  {using(index)
    sigName := getSignalName(CpuLibClass$Inst, index);
    if (sigName = nil) ^ER_ILLEGAL_SIGNAL; endif;
    addItem(popup, new(MenuItem, sigName + " Enable", CPU_SIG_BASE + index,
                       #cmdControlSignals));
  });
  ^popup;

}
!!

/* PRIVATE -- calc new window size based on #regs displayed */
Def newWinSize(self | wRect cRect width height maxX maxY overrun)
{ 
  if not(registerGroups)
    ^nil
  endif;

  wRect := windowRect(self);
  cRect := clientRect(self);
  
  width := ((tmWidth) * (labelWidth + registerWidth + 2))+4;
  height := (tmHeight * numLinesIn(self)) + 2
              + (height(wRect) - height(cRect));
  
  setRight(wRect, left(wRect)+width);
  setBottom(wRect, top(wRect)+height);
  
  /* get size of screen */
  maxX := x(screenSize());
  maxY := y(screenSize());
  
  /* adjust width to be visible horizontally */
  overrun := right(wRect) - maxX;
  if overrun > 0
    /* if it can't fit on screen, give priority to left */
    if overrun > left(wRect)
      overrun := left(wRect);
    endif;
    setLeft(wRect, left(wRect) - overrun);
    setRight(wRect, right(wRect) - overrun);
  endif;
  
  /* adjust height to be visible vertically */
  overrun := bottom(wRect) - maxY;
  if overrun > 0
    /* if it can't fit on screen, give priority to top */
    if overrun > top(wRect)
      overrun := top(wRect);
    endif;
    setTop(wRect, top(wRect) - overrun);
    setBottom(wRect, bottom(wRect) - overrun);
  endif;
  
  setCRect(self, wRect);
  moveWindow(self);
  /* N.B.: no repaint */
}
!!

/* PUBLIC */
Def numLinesIn(self | sumLines)
{
  sumLines := 0 ;

  do(registerGroups,
     {using(rg) sumLines := sumLines + size(rg) ; }
    );

  ^sumLines
}
!!

/* 2/23/1992 19:25 - PUBLIC 
  Actor paint message for the Cpu presenter
*/
Def paint(self, hDC | x, y)
{
  initTextColors( self, hDC ) ;
  paint( self:ancestor, hDC ) ;

  do(registerGroups,
     {using(regGroup)  display( regGroup, hDC ) ; }
  );
}
!!

/* PRIVATE */
Def point2Register(self, mousePt | lineNum)
{
  lineNum := ( max( 0, y(mousePt)-2 ) / tmHeight ) ;
  ^line2register( self, lineNum )
}
!!

/* PRIVATE */
Def regDialogText(self, register | prev curr prevText )
{ 
/*
  if (prev := prevVal(register))
  then prevText := "Previous Value: 0x" + asUnsignedStringRadix( prev, 16 ) ;
  else prevText := "Previous Value: " + "????????" ;
  endif ;
*/
  curr := asLong( currVal(register) cor 0 ) ;
  ^(" Hex: " + right(asUnsignedStringRadix(curr, 16), 8, "0") +
    ",  Decimal: " + asUnsignedStringRadix(curr, 10));
}
!!

/* PUBLIC */
Def tmHeight(self)
{ ^tmHeight }
!!

/* PUBLIC */
Def tmWidth(self)
{ ^tmWidth }
!!

/* PRIVATE -- get new register values (for visible regs) */
Def updateRegisters(self)
{ if registerGroups
  then
    do(registerGroups,
       {using(regGroup) updateValues(regGroup) ;}
      ) ;
  endif ;
  
}
!!

/* PRIVATE */
Def updateSignals(self | numSigs)
{
  if signals
  then
    numSigs := getNumberOfSignals(CpuLibClass$Inst);
    do(over(0,numSigs),
    {using(index)
      signals[CPU_SIG_BASE + index][0] := getSignal( CpuLibClass$Inst, 
                                         signals[CPU_SIG_BASE + index][1] ) ;
      if signals[CPU_SIG_BASE + index][0] then 
        checkMenuItem(   self, CPU_SIG_BASE + index ) ;
      else
        unCheckMenuItem( self, CPU_SIG_BASE + index ) ;
      endif ;
    });

  endif ;
}
!!

/* Windows WM_ACTIVATE message */
Def WM_ACTIVATE(self, wp, lp)
{ 
  setFocus(self);
}
!!

/* WINDOWS -- sent by event manager (evNoteLib) */
Def WM_EVENT(self, target, event)
{
  select
    case (event = EVENT_CPU_EDIT) cor /* Emulation Halted */ 
         (event = EVENT_CPU_HALTED)
    is
      updateSignals( self ) ;
      updateRegisters( self ) ;
      invalidate( self ) ;
    endCase
  endSelect;

}
!!

/* WINDOWS (PUBLIC)
 * Select register nearest to mouse point
 *
 * Notes (Ron, 6/8/93): Changed to use InputDlgWithHelp instead of
 *                      InputDialog as part of PowerViews Improvement
 *                      Project.
 */
!!

Def WM_LBUTTONDBLCLK(self, wp, lp | mousePt currValue newVal val tmpStr)
{
   mousePt := asPoint( lp ) ; /* N.B.: window relative */
   selectedReg := point2Register( self, mousePt ) ;
   if selectedReg then /*@@ cheap hack @@*/
      currValue := asLong(currVal(selectedReg) cor 0) ;

      regPopup := open(InputDlgWithHelp);

      tmpStr := "0x" + right(asUnsignedStringRadix(currValue, 16), 8, "0");

      setUp(regPopup,"Register: " + name(selectedReg),
                    regDialogText(self, selectedReg), 
                    tmpStr, HE_DLGI_CPUPRESE_1);

      if (runModal(regPopup, DLG_INPUT_WITH_HELP, ThePort) = IDOK) then 
         tmpStr := getText(regPopup);
/**>>>**/
         if (size(tmpStr) < 2)
            tmpStr := "00"+tmpStr;
         endif;
         if (tmpStr[1]<>'x' cand tmpStr[1]<>'X')
            tmpStr := "0x"+tmpStr;
         endif;
         if (val:=string2int(tmpStr))
            setRegValue( CpuLibClass$Inst, id(selectedReg), val ) ;
         endif;
/**>>>**/
/**<<<**
         newVal := convertTextToAddress(AddressLibClass$Inst, tmpStr) ;
         if newVal then
            if (val := getFullOffsetNoError(AddressLibClass$Inst, newVal)) then
               setRegValue( CpuLibClass$Inst, id(selectedReg), val ) ;
               destroyAddress(AddressLibClass$Inst, newVal);
            endif;
         endif ;
**<<<**/
      endif;
      selectedReg := nil;
 
   endif;
}
!!

/* WINDOWS (PUBLIC)
   Select register nearest to mouse point
*/
Def WM_LBUTTONDOWN(self, wp, lp | mousePt currValue newVal edWind)
{
  mousePt := asPoint( lp ) ; /* N.B.: window relative */
  selectedReg := point2Register( self, mousePt ) ;
  if selectedReg
  then
    /* temporary test code for doing edit in place 
    edWind := newStyle(SEditWindow,self,nil,nil,rect(x(valueLoc(selectedReg)),
                       y(valueLoc(selectedReg)), x(valueLoc(selectedReg))+(9*tmWidth)+1,
                       y(valueLoc(selectedReg))+tmHeight), 0, WS_CHILD);
    setWorkingText(edWind,currVal(selectedReg));
    show (edWind,1);
    close(edWind);
    */
    /* edit in place code */
  endif ;
}
!!

/* This overrides Window:WM_PAINT
  so that CpuPresenter uses a local font instead of the System Font. */
Def WM_PAINT(self, wP, lP | hCD oldFont)
{ hCD := Call BeginPaint(getHWnd(self), paintStruct);
  if oldFont := Call SelectObject(hCD,getFont(self))
    paint(self, hCD);
    Call SelectObject(hCD, oldFont);
    Call EndPaint(getHWnd(self), paintStruct);
  endif;
  ^0;
}

!!
