/* 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(EditWindow, #CpuPresenter, #(signals /* Dict of sigId -> #(sigVal, sigNum) */
timers /* Dict of timer, added by Eric 3/23/98 */
registerGroups
registerWidth /* in characters */
labelWidth      /* in characters */
fontInfo
selectedReg /* typically nil */
regPopup /* for value edit & radix display */
eventDescSet
oldPC
windowsMenuExisted
oldSelectedReg
minWidth /* don't make skinnier than ... */), 2, nil)!!

now(class(CpuPresenter))!!

/* Create a new window class Struct. Assigns the
 Arrow cursor style to windows of this class
 and descendants. */
Def newWClass(self, className, iconName | wc)
{ wc := newWClass(self:ancestor, className, iconName);
  putWord(wc, Call LoadCursor(0, IDC_ARROW), 14);
  ^wc;
}
!!

/* 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_THICKFRAME bitOr WS_SYSMENU
        bitOr WS_CAPTION bitOr WS_MINIMIZEBOX);

  if sizeRect then
    setCRect(aCpuPresenter, sizeRect);
    moveWindow(aCpuPresenter);
  endif;
  /* Display the presenter and return its handle */
  if (aCpuPresenter) then
    registerF1Help(CLIULibraryClass$Inst, HI_ENTRY_CPU,
       getHWnd(aCpuPresenter), HELP_ENTRY_CPU);
    Call SetScrollRange(getHWnd(aCpuPresenter), SB_HORZ,
                        0,0,0);
    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)!!

/* 3/23/1998 18:04 Created by Eric
User sets CPU timer
*/
Def cmdControlTimers(self,timer)
{ if timers
  then
    timers[timer][0] := not(timers[timer][0]) ;
    if setTimer( CpuLibClass$Inst, timers[timer][1], timers[timer][0] )
    then
      if timers[timer][0]
      then checkMenuItem(   self, timer ) ;
      else unCheckMenuItem( self, timer ) ;
      endif ;
    endif ;
  endif;
}
!!

/* 3/23/1998 8:49 created by Eric*/
Def updateTimers(self)
{ if timers
  then
    do(over(0,3),
    {using(index)
      timers[720 + index][0] := getTimer( CpuLibClass$Inst, 
                                    timers[720 + index][1] ) ;
      if timers[720 + index][0] then 
        checkMenuItem(   self, 720 + index ) ;
      else
        unCheckMenuItem( self, 720 + index ) ;
      endif ;
    });
  endif ;
}
!!

/* 3/23/1998 8:41 created by Eric - Private
  Initialize CPU control timers
*/
Def initTimers(self)
{ 
  timers := new( Dictionary, 3 ) ;  
  add (timers, 720,   tuple(nil, 0) ) ;
  add (timers, 721,   tuple(nil, 1) ) ;
  add (timers, 722,   tuple(nil, 2) ) ;
  updateTimers( self ) ;
}
!!

/* 3/20/1998 20:32 created by Eric - PRIVATE
  Define the Timer popup menu and its actions
*/
Def newTimersPopup(self | popup)
{ popup := newPopup(MenuItem,  "&Timer");
  addItem(popup, new(MenuItem, "Timer0" +  " Enable", 720,
                       #cmdControlTimers));
  addItem(popup, new(MenuItem, "Timer1" +  " Enable", 721,
                       #cmdControlTimers));
  addItem(popup, new(MenuItem, "Timer2" +  " Enable", 722,
                       #cmdControlTimers)); 
  ^popup;
}
!!

/* Initialize the workText instance variable. */
Def initWorkText(self)
{ workText := new(TextCollection, 8);
  dirty := nil;
  initEditParms(self);
}!!

/* 9/7/1994 10:39 */
Def reSize(self, wP, lP | visLines newTopLine)
{
  /* avoid customized processing if being iconized */
  if (wP = SIZEICONIC)
    ^reSize(self:ancestor, wP, lP);
  endif;
  
  /* maintain minimum width */
  cRect := windowRect(self);
  if cRect cand minWidth cand (width(cRect) < minWidth)
    setRight(cRect, left(cRect) + minWidth);
    moveWindow(self);
    invalidate(self);
  endif;
  
  reSize(self:ancestor, wP, lP);
  
  /* pull down display lines if space available */
  visLines := visLines(self);
  newTopLine := max(0, (size(workText) - visLines));
  if newTopLine < topLine
    topLine := newTopLine;
    invalidate(self);
  endif;
}!!

/* 7/26/1994 9:09 */
/* Handle the arrows (left, right, up, down) and */
/* tab and shift tab keys.                       */
Def arrows(self, wp | newIndex newReg numLines)
{ 
  if selectedReg then
    select
      case (wp == VK_LEFT or wp == VK_UP)
        /* See if we need to scroll the CPU window up one line */
        newIndex := sortIndex(selectedReg) - 1;
        if newIndex >= 0 then
          if newIndex < topLine then
            /* Scroll up one line because  */
            /* we are not at the top yet.  */
            WM_VSCROLL(self, SB_LINEUP, 0);
          endif;

          /* Now unhighlight the previously highlighted register */
          /* and highlight the new register.                     */
          newReg := line2register(self, newIndex);
          highLight(self, selectedReg, 0);
          highLight(self, newReg, 1);
          selectedReg := newReg;
          oldSelectedReg := selectedReg;
        endif;
      endCase

      case (wp == VK_RIGHT or wp == VK_DOWN or wp == VK_TAB)
        /* See if we need to scroll the CPU window down one line */
        newIndex := sortIndex(selectedReg) + 1;
        numLines := numLinesIn(self);
        if (newIndex >= 0) cand
           (newIndex < numLines) then
          if newIndex > (topLine + visLines(self)) then
            /* Scroll down one line because  */
            /* we are not at the bottom yet. */
            WM_VSCROLL(self, SB_LINEDOWN, 0);
          endif;

          /* Now unhighlight the previously highlighted register */
          /* and highlight the new register.                     */
          newReg := line2register(self, newIndex);
          highLight(self, selectedReg, 0);
          highLight(self, newReg, 1);
          selectedReg := newReg;
          oldSelectedReg := selectedReg;
        endif;
      endCase
    endSelect;
  else
    /* This handles the case where no register has been selected yet */
    /* (i.e. the CPU window was just opened). Select the register    */
    /* that is the first one displayed in the CPU window if the user */
    /* hits an arrow or tab key.                                     */
    selectedReg := line2register(self, topLine);
    oldSelectedReg := selectedReg;
    highLight(self, selectedReg, 1);
  endif;
}
!!

/* 4/14/1994 7:56 */
Def gotFocus(self, hWndPrev | procType)
{
   updateSignals(self);
   /*Eric 3/20/98*/
   /* get processor type for Am186 type*/
   procType := getSpecificProcessor(ProcLibClass$Inst);
   if ((procType>= 5888) and (procType <=6144) ) then
     updateTimers(self);
   endif;  
   /*Eof Eric 3/20/98*/
   ^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 );
  removeWindowMenu(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;
  
  select
    case wp == VK_RETURN
      if selectedReg
         editReg(self, wp, lp, selectedReg);
      else
         arrows(self,wp);
      endif;
    endCase
    case (wp == VK_TAB) or (wp == VK_LEFT) or (wp == VK_UP) or
         (wp = VK_RIGHT) or (wp = VK_DOWN)
         /* If we have a shift tab character, then call */
         /* arrows with an "up arrow" value (37).       */
         if (wp == VK_TAB) cand (Call GetKeyState(VK_SHIFT) < 0) then
           wp := VK_LEFT;
         endif;
      arrows(self,wp);
    endCase
    case (wp = EDIT_CUT) or
         (wp = EDIT_COPY) or
         (wp = EDIT_PASTE) or
         (wp = EDIT_CLEAR) or
         (wp = EDIT_SELALL) or
         (wp = EDIT_SRCH) or
         (wp = EDIT_RPLC) or
         (wp = RPLC_ALL)
      beep();
    endCase
    default
      /* Process Windows menu commands */
      if not(doAction(PreLauncher, wp)) then
        command(self:ancestor, wp, lp);
      endif;
  endSelect;
}
!!

/* 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) ;
}
!!

/* 8/4/1994 10:49 */
Def delChar(self)
{ beep();}
!!

/* 7/26/1994 8:26 */
Def editReg(self wp lp selectedReg | currValue newVal val tmpStr)
{
   if selectedReg then /*@@ cheap hack @@*/
      currValue := asLong(currVal(selectedReg) cor 0) ;

      regPopup := open(InputDlgWithHelp);

   if (getCpuType(ProcLibClass$Inst) = PROC_CPU_80186) then
      tmpStr := "0x" + right(asUnsignedStringRadix(currValue, 16), 4, "0");
   else
      tmpStr := "0x" + right(asUnsignedStringRadix(currValue, 16), 8, "0");
   endif;

      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);

         /* Added this call and commented the following code. The old code */
         /* used to call the address server to convert the register text   */
         /* to a value and then set the register. Now the cpu server is    */
         /* called to handle this. If the input value is a symbol, the cpu */
         /* server calls the address server to handle this. If it is a     */
         /* value, the cpu server handles this itself.                     */
         convTextToValue(CpuLibClass$Inst, tmpStr, id(selectedReg));
         /* <Judy 1/28/97> */
         notify(EvNoteLibClass$Inst, EVENT_CPU_EDIT);
         /* <Judy 1/28/97> */
         /*
          * Commented code out
          newVal := convertTextToAddress(AddressLibClass$Inst, tmpStr) ;
          * convert to address will report an error so don't follow it up
          * with another
         if newVal then
            if (val := getFullOffsetNoError(AddressLibClass$Inst, newVal)) then
               setRegValue( CpuLibClass$Inst, id(selectedReg), val ) ;
               destroyAddress(AddressLibClass$Inst, newVal);
            endif;
         endif ;

         * end of commented out code
         */

      endif;
      Call HideCaret(getHWnd(self));
   endif;
 }
!!

/* 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 }
!!

/* 7/26/1994 9:50 */
Def highLight(self, reg, onOrOff | regLine hDC)
{
  hDC := getContext(self);
  setRegSelected(reg, onOrOff);
  regLine := reg2line(reg) - topLine;
  if (regLine >= 0) then
    display(reg, hDC, (regLine*tmHeight), tmHeight, tmWidth);
  endif;
  releaseContext(self,hDC);
  }
!!

/* 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 hMenuOption procType)
{ 
  /* Use menu instance variable to create the main menu */
  menu := create(new(Menu), self);
  windowsMenuExisted := nil;
  /* 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, newSignalsPopup(self)); /* signals menu items */
  else
    addItem(popup, new(MenuItem, "&Signals", CPU_NO_SIGNAL, #cmdHelpPViews));
  endif;
  /*Eric 3/20/98*/
  /* get processor type for Am186 type*/
  procType := getSpecificProcessor(ProcLibClass$Inst);
  if ((procType>= 5888) and (procType <=6144) ) then
    addItem(popup, newTimersPopup(self));
  else
    addItem(popup, nil);  /* menu item separator */
  endif;  
  /*Eof Eric 3/20/98*/
  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 | procType)
{
  labelWidth    := 1 ;  /* characters */
  registerWidth := 1 ;  /* characters */
  initTextMetrics( self ) ;
  initSignals( self ) ;
  /*Eric 3/20/98*/
  /* get processor type for Am186 type*/
  procType := getSpecificProcessor(ProcLibClass$Inst);
  if ((procType >= 5888) and (procType <= 6144) ) then
    initTimers( self ) ; 
  endif;  
  /*Eof Eric 3/20/98*/
  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) then
    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 ) ;
  endif;
}
!!

/* 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)
{ 
  initWorkText(self); 
  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 visible)
    visible := getRegisterVisibility(CpuLibClass$Inst, index);
 if (visible = 1)
       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(workText,regName);

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

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

/* 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
           + (width(wRect) - width(cRect)));
  minWidth := width;  /* preserve minimum width */

  height := (tmHeight * numLinesIn(self)) + 2
              + (height(wRect) - height(cRect));
  
  /* get size of screen */
  maxX := x(screenSize());
  maxY := y(screenSize());
  if (height >= maxY) then
    height := maxY - tmHeight;
  endif;

  setRight(wRect, left(wRect)+width);
  setBottom(wRect, top(wRect)+height);
  
  /* 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 ) ;
  do(registerGroups,
     {using(regGroup)
        display( regGroup, hDC, tmHeight, topLine, tmWidth) ; }
  );
  setScrollPos(self);
}
!!

/* PRIVATE */
Def point2Register(self, mousePt | lineNum)
{
  lineNum := ( max( 0, y(mousePt)-2 ) / tmHeight )
             + topLine ;
  ^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
   Called by window close() routine.  Removes shared Windows menu.
*/
Def removeWindowMenu(self | hMenu hSubMenu) {
  if not(hMenu := menuHandle(self)) then ^nil; endif;
  if (class(hMenu) = Menu)
  then hMenu := handle(hMenu) ;
  endif ;
  hSubMenu := Call GetSubMenu(hMenu, 0);
  Call RemoveMenu( hSubMenu, 4, MF_BYPOSITION);
  ^#ok
}

!!

/* Set scroll bar position, avoiding divide by 0. */
Def setScrollPos(self)
{ Call SetScrollPos(hWnd, SB_VERT,
    (100*topLine)/max(1, size(workText)-(visLines(self)+1)), 1)
}!!

/* 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);
}
!!

/* Translate MS-Windows character input message to an
  Actor-style message for descendant classes. */
Def WM_CHAR(self, wP, lP)
{ 
}!!

/* WINDOWS -- sent by event manager (evNoteLib) */
Def WM_EVENT(self, target, event | procType)
{
  select
    case (event = EVENT_CPU_EDIT) cor /* Emulation Halted */ 
         (event = EVENT_CPU_HALTED)
    is
      updateSignals( self ) ;
      /*Eric 3/20/98*/
      /* get processor type for Am186 type*/
      procType := getSpecificProcessor(ProcLibClass$Inst);
      if ((procType >= 5888) and (procType <= 6144) ) then
        updateTimers( self );
      endif;  
      /*Eof Eric 3/20/98*/
      updateRegisters( self ) ;
      invalidate( self ) ;
    endCase
  endSelect;

}
!!

/* 7/9/1992 12:40 - WINDOWS
  Initialize Windows dynamic menu.
*/
Def WM_INITMENU(self, wp, lp | hMenuOption,hmenu)
{
  if not(windowsMenuExisted) then
    hmenu := Call GetMenu(hWnd(self));
    hMenuOption := Call GetSubMenu(hmenu, 
                 0 /* OPTIONS at index 0 */);
    /* Append Windows menu item */
    Call InsertMenu(hMenuOption, 4, 
        MF_POPUP bitOr MF_BYPOSITION,
        windowsMenu(PreLauncher), 
        asciiz("&Windows"));
    windowsMenuExisted := #true;
    ^WM_INITMENU(PreLauncher, wp, lp);           
  endif;
}

!!

/* 7/26/1994 8:44 */
Def WM_KEYDOWN(self, wp, lp)
{
  if between(wp, VK_LEFT, VK_DOWN) then
    command(self, wp, 0x100000);
  else      
    if (wp == VK_RETURN or wp == VK_TAB) then
      command(self, wp, lp);
    else
      if between(wp, 0x70, 0x79)
        checkFuncKey(self, wp, 0x10000);
      endif;
    endif;
  endif;
}
!!

/* 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)
{  
  mousePt := asPoint( lp ) ; /* N.B.: window relative */
  selectedReg := point2Register( self, mousePt ) ;
  if selectedReg then
     editReg(self, wp, lp, selectedReg);
  endif;
}
!!

/* WINDOWS (PUBLIC)
   Select register nearest to mouse point
*/
Def WM_LBUTTONDOWN(self, wp, lp 
   | mousePt currValue newVal edWind oldRegLine
   newRegLine hDC)
{
  mousePt := asPoint( lp ) ; /* N.B.: window relative */
  selectedReg := point2Register( self, mousePt ) ;
  if selectedReg
  then
    /* If a previous register had been selected, turn its selection */
    /* to off so that when it is redrawn, it will not be hilighted. */
    if oldSelectedReg then
      highLight(self, oldSelectedReg, 0);
    endif;

    /* Get the display line number of the previously selected register */
    /* and the newly selected register.                                */
    newRegLine := reg2line(selectedReg) - topLine;

    /* Turn on the register selection, causes hilighting when redrawn */ 
    highLight(self, selectedReg, 1);
    
    /* 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);
    */

    oldSelectedReg := selectedReg;
  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 HideCaret(getHWnd(self));
    Call SelectObject(hCD, oldFont);
    Call EndPaint(getHWnd(self), paintStruct);
  endif;
  ^0;
}

!!

/* Respond to MS-Window's vertical scrolling message. wP tells what kind of
  scrolling request has been made. */
Def WM_VSCROLL(self, wP, lP | oldTop fixRect vLadj newTopLine)
{ oldTop := topLine;
  vLadj := size(workText) - (visLines(self));
  select
    case height(clientRect(self))/tmHeight > size(workText)
    and topLine == 0
    is ^0;
    endCase
    case wP == SB_LINEDOWN cand topLine < vLadj
    is topLine := topLine + 1;
      Call ScrollWindow(hWnd, 0, negate(tmHeight), 0, 0);
      setScrollPos(self);
    endCase
    case wP == SB_PAGEDOWN is
      newTopLine := min(topLine + visLines(self), vLadj);
      if topLine <> newTopLine
        topLine := newTopLine;
        invalidate(self);
      endif;
    endCase
    case wP == SB_LINEUP and topLine > 0
    is topLine := topLine - 1;
      fixRect := clientRect(self);
      setBottom(fixRect, tmHeight + 2);
      Call ScrollWindow(hWnd, 0, tmHeight, 0, 0);
      Call InvalidateRect(hWnd, fixRect, 1);
      setScrollPos(self);
    endCase
    case wP == SB_PAGEUP is
      newTopLine := max(0, topLine - visLines(self));
      if topLine <> newTopLine
        topLine := newTopLine;
        invalidate(self);
      endif;
    endCase
    case wP == SB_THUMBPOSITION
    is invertSelText(self);
      topLine := asInt((vLadj*low(lP))/100);
      invalidate(self);
    endCase
  endSelect;
  yPos := yPos + (oldTop - topLine);
  moveCaret(self);
  showCaret(self);
}!!

