// *********************************************************************
//    Copyright (c) 1989-2002  Warren Furlow
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// *********************************************************************

// *********************************************************************
// Dialogs.cpp : implementation file
// *********************************************************************

#include"StdAfx.h"
#include"V41.h"

#define WM_UPDATERAMVIEW  (WM_USER + 10)

/***********************/
// CAboutDlg dialog used for App About
/***********************/
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
  //{{AFX_MSG_MAP(CAboutDlg)
  ON_BN_CLICKED(IDLICENSE, OnLicense)
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/***********************/
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
  {
  //{{AFX_DATA_INIT(CAboutDlg)
  //}}AFX_DATA_INIT
  }

/***********************/
void CAboutDlg::OnLicense()
  {
  ShellExecute(NULL,NULL,"notepad.exe","License.txt",CMainWindow::getPath(theApp.szAppFullPath),SW_SHOWNORMAL);
  }


/***********************/
// CSettingsDlg dialog used for Options Settings
/***********************/
BEGIN_MESSAGE_MAP(CSettingsDlg, CDialog)
  //{{AFX_MSG_MAP(CSettingsDlg)
  ON_BN_CLICKED(IDDEFAULTS, OnDefaults)
  ON_BN_CLICKED(IDC_SOUND_WAVE, OnSoundWave)
  ON_BN_CLICKED(IDC_SOUND_SPEAKER, OnSoundSpeaker)
  ON_BN_CLICKED(IDC_SOUND_NONE, OnSoundNone)
  ON_BN_CLICKED(IDC_TRUETYPE, OnTrueType)
  ON_BN_CLICKED(IDC_CHECK_COLOR, OnCheckColor)
  ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_CONTRAST, OnSliderContrast)
  ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_RED, OnSliderColor)
  ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_BLUE, OnSliderColor)
  ON_NOTIFY(NM_RELEASEDCAPTURE, IDC_SLIDER_GREEN, OnSliderColor)
  ON_WM_PAINT()
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/***********************/
CSettingsDlg::CSettingsDlg() : CDialog(CSettingsDlg::IDD)
  {
  //{{AFX_DATA_INIT(CSettingsDlg)
  //}}AFX_DATA_INIT
  }

/***********************/
CSettingsDlg::~CSettingsDlg()
  {
  if (CFontLCD.m_hObject)
    VERIFY(CFontLCD.DeleteObject());
  }

/***********************/
void CSettingsDlg::DoDataExchange(CDataExchange* pDX)
  {
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CSettingsDlg)
  DDX_Control(pDX, IDC_INTERVAL, ctlInterval);
  DDX_Control(pDX, IDC_STATIC_SPEED, ctlStaticSpeed);
  DDX_Control(pDX, IDC_STATIC_TURBO, ctlStaticTurbo);
  DDX_Control(pDX, IDC_TURBO, ctlTurbo);
  DDX_Control(pDX, IDC_STATIC_SAMPLE, ctlSample);
  DDX_Control(pDX, IDC_STATIC_RED, ctlStaticRed);
  DDX_Control(pDX, IDC_STATIC_LIGHT, ctlStaticLight);
  DDX_Control(pDX, IDC_STATIC_GREEN, ctlStaticGreen);
  DDX_Control(pDX, IDC_STATIC_DARK, ctlStaticDark);
  DDX_Control(pDX, IDC_STATIC_BLUE, ctlStaticBlue);
  DDX_Control(pDX, IDC_SLIDER_RED, ctlRed);
  DDX_Control(pDX, IDC_SLIDER_GREEN, ctlGreen);
  DDX_Control(pDX, IDC_SLIDER_BLUE, ctlBlue);
  DDX_Control(pDX, IDC_CHECK_COLOR, ctlColor);
  DDX_Control(pDX, IDC_STATIC_CONTRAST, ctlStaticContrast);
  DDX_Control(pDX, IDC_STATIC_VOLUME, ctlStaticVolume);
  DDX_Control(pDX, IDC_SINGLE, ctlSingle);
  DDX_Control(pDX, IDC_AUTOIDY, ctlAutoIdy);
  DDX_Control(pDX, IDC_TRUETYPE, ctlTrueType);
  DDX_Control(pDX, IDDEFAULTS, ctlDefaults);
  DDX_Control(pDX, IDC_AUTOSAVE, ctlAutoSave);
  DDX_Control(pDX, IDC_SOUND_NONE, ctlSoundNone);
  DDX_Control(pDX, IDC_SOUND_SPEAKER, ctlSoundSpeaker);
  DDX_Control(pDX, IDC_SOUND_WAVE, ctlSoundWave);
  DDX_Control(pDX, IDC_VOLUME, ctlWaveVolume);
  DDX_Control(pDX, IDC_CONTRAST, ctlContrast);
  DDX_Control(pDX, IDC_OUTADDR, ctlOutAddr);
  DDX_Control(pDX, IDC_OUTPORT, ctlOutPort);
  DDX_Control(pDX, IDC_INPORT, ctlInPort);
  //}}AFX_DATA_MAP
  }

/***********************/
BOOL CSettingsDlg::OnInitDialog()
  {
  CDialog::OnInitDialog();

  ctlContrast.SetRange(0,0xf,TRUE);
  ctlContrast.SetTicFreq(1);
  ctlContrast.SetPos(Contrast);

  ctlColor.SetCheck(FontColor!=0);
  ctlRed.SetRange(0,0xff,TRUE);
  ctlGreen.SetRange(0,0xff,TRUE);
  ctlBlue.SetRange(0,0xff,TRUE);
  OnCheckColor();

  ctlInterval.SetRange(MIN_PROC_INTERVAL,MAX_PROC_INTERVAL,TRUE);
  ctlInterval.SetTicFreq((MAX_PROC_INTERVAL-MIN_PROC_INTERVAL)/10);
  ctlInterval.SetPos(MAX_PROC_INTERVAL-Interval+MIN_PROC_INTERVAL);

  ctlTurbo.SetRange(10,100);
  ctlTurbo.SetTicFreq(10);
  ctlTurbo.SetPos(Turbo);

  ctlStaticVolume.EnableWindow(0);
  ctlWaveVolume.EnableWindow(0);
  if (!fSpeakerSoundAvail)
    ctlSoundSpeaker.EnableWindow(0);
  if (!fWaveSoundAvail)
    ctlSoundWave.EnableWindow(0);

  if (fSpeakerSoundAvail && SoundMode==eSoundSpeaker)
    {
    ctlSoundSpeaker.SetCheck(1);
    ctlStaticSpeed.EnableWindow(0);
    ctlInterval.EnableWindow(0);
    ctlStaticTurbo.EnableWindow(0);
    ctlTurbo.EnableWindow(0);
    }
  else if (fWaveSoundAvail && SoundMode==eSoundWave)
    {
    ctlSoundWave.SetCheck(1);
    ctlStaticVolume.EnableWindow(1);
    ctlWaveVolume.EnableWindow(1);
    }
  else    // eSoundNone or other mode is disabled
    ctlSoundNone.SetCheck(1);

  ctlWaveVolume.SetRange(MIN_VOLUME,MAX_VOLUME,TRUE);
  ctlWaveVolume.SetTicFreq((MAX_VOLUME+1-MIN_VOLUME)/8);
  ctlWaveVolume.SetPos(WaveVolume);

  ctlTrueType.SetCheck(fTrueType);
  ctlAutoSave.SetCheck(fAutoSave);
  ctlSingle.SetCheck(fSingle);
  ctlAutoIdy.SetCheck(fAutoIdy);

  CString strOutPort,strInPort;
  strOutPort.Format("%u",wOutPort);
  strInPort.Format("%u",wInPort);
  ctlOutAddr.SetWindowText(strOutAddr);
  ctlOutPort.SetWindowText(strOutPort);
  ctlInPort.SetWindowText(strInPort);

  CDC dcMemory;  // get font background color from background bitmap
  dcMemory.CreateCompatibleDC(NULL);
  CBitmap* pOldBitmap=dcMemory.SelectObject(&pHP41->CBMKeyboard);
  BkFontColor=GetPixel(dcMemory.GetSafeHdc(),pHP41->pRectLCD->left, pHP41->pRectLCD->top);
  dcMemory.SelectObject(pOldBitmap);

  pHP41->CreateLcdFont(pHP41->GetKeyboard(),ctlTrueType.GetCheck(),CFontLCD);// the new font
  return TRUE;
  }

/***********************/
void CSettingsDlg::OnOK()
  {
  Contrast=ctlContrast.GetPos();

  Interval=MAX_PROC_INTERVAL-ctlInterval.GetPos()+MIN_PROC_INTERVAL;
  Turbo=ctlTurbo.GetPos();

  if (ctlSoundNone.GetCheck())
    SoundMode=eSoundNone;
  if (ctlSoundSpeaker.GetCheck())
    SoundMode=eSoundSpeaker;
  if (ctlSoundWave.GetCheck())
    SoundMode=eSoundWave;
  WaveVolume=ctlWaveVolume.GetPos();

  fTrueType=ctlTrueType.GetCheck();
  fAutoSave=ctlAutoSave.GetCheck();
  fSingle=ctlSingle.GetCheck();
  fAutoIdy=ctlAutoIdy.GetCheck();

  CString strOutPort,strInPort;
  ctlOutAddr.GetWindowText(strOutAddr);
  ctlOutPort.GetWindowText(strOutPort);
  ctlInPort.GetWindowText(strInPort);
  wOutPort=static_cast<word>(strtoul(strOutPort,NULL,10));
  wInPort=static_cast<word>(strtoul(strInPort,NULL,10));
  CDialog::OnOK();
  }

/***********************/
void CSettingsDlg::OnDefaults()
  {
  ctlContrast.SetPos(DEFAULT_CONTRAST);
  ctlColor.SetCheck(0);
  FontColor=0; // black
  OnCheckColor();

  ctlInterval.SetPos(MAX_PROC_INTERVAL-DEFAULT_PROC_INTERVAL+MIN_PROC_INTERVAL);
  ctlTurbo.SetPos(10);

  ctlSoundNone.SetCheck(1);
  ctlSoundSpeaker.SetCheck(0);
  ctlSoundWave.SetCheck(0);
  ctlStaticVolume.EnableWindow(0);
  ctlWaveVolume.EnableWindow(0);
  ctlWaveVolume.SetPos(MAX_VOLUME/4);

  ctlStaticSpeed.EnableWindow(1);
  ctlInterval.EnableWindow(1);
  ctlStaticTurbo.EnableWindow(1);
  ctlTurbo.EnableWindow(1);

  ctlTrueType.SetCheck(1);
  ctlAutoSave.SetCheck(1);
  ctlSingle.SetCheck(1);
  ctlAutoIdy.SetCheck(0);

  CString strOutPort,strInPort;
  strOutPort.Format("%u",DEFAULT_PORTOUT);
  strInPort.Format("%u",DEFAULT_PORTIN);
  ctlOutAddr.SetWindowText(DEFAULT_ADDROUT);
  ctlOutPort.SetWindowText(strOutPort);
  ctlInPort.SetWindowText(strInPort);
  }

/****************************/
void CSettingsDlg::InvalidateSample()
  {
  // redraw sample text
  RECT rectDlg,rectSample;
  GetWindowRect(&rectDlg);
  ctlSample.GetWindowRect(&rectSample);
  rectSample.left-=rectDlg.left;
  rectSample.top-=rectDlg.top-GetSystemMetrics(SM_CYCAPTION)-GetSystemMetrics(SM_CXBORDER);
  rectSample.right=rectDlg.right-rectSample.right;
  rectSample.bottom=rectDlg.bottom-rectSample.bottom;
  InvalidateRect(&rectSample);
  }

/****************************/
void CSettingsDlg::OnTrueType()
  {
  // update font
  CFontLCD.DeleteObject();
  pHP41->CreateLcdFont(pHP41->GetKeyboard(),ctlTrueType.GetCheck(),CFontLCD);
  InvalidateSample(); // and redraw sample text
  }

/****************************/
void CSettingsDlg::OnCheckColor()
  {
  int Checked=ctlColor.GetCheck();
  ctlStaticContrast.EnableWindow(!Checked);
  ctlStaticLight.EnableWindow(!Checked);
  ctlStaticDark.EnableWindow(!Checked);
  ctlContrast.EnableWindow(!Checked);
  ctlStaticRed.EnableWindow(Checked);

  ctlStaticGreen.EnableWindow(Checked);
  ctlStaticBlue.EnableWindow(Checked);
  ctlRed.EnableWindow(Checked);
  ctlGreen.EnableWindow(Checked);
  ctlBlue.EnableWindow(Checked);
  if (!Checked)
    FontColor=0;
  InvalidateRect(NULL,TRUE);
  ctlBlue.SetPos((FontColor&0xff0000)>>16);
  ctlGreen.SetPos((FontColor&0x00ff00)>>8);
  ctlRed.SetPos(FontColor&0x0000ff);
  }

void CSettingsDlg::OnSliderContrast(NMHDR* pNMHDR, LRESULT* pResult)
  {
  Contrast=ctlContrast.GetPos();
  InvalidateSample(); // redraw sample text
  *pResult=0;
  UNREFERENCED_PARAMETER(pNMHDR);
  }

/****************************/
void CSettingsDlg::OnSliderColor(NMHDR* pNMHDR, LRESULT* pResult)
  {
  FontColor=(ctlBlue.GetPos()<<16|ctlGreen.GetPos()<<8|ctlRed.GetPos());
  InvalidateSample(); // redraw sample text
  *pResult=0;
  UNREFERENCED_PARAMETER(pNMHDR);
  }

/****************************/
void CSettingsDlg::OnSoundWave()
  {
  ctlStaticVolume.EnableWindow(1);
  ctlWaveVolume.EnableWindow(1);
  ctlStaticSpeed.EnableWindow(1);
  ctlInterval.EnableWindow(1);
  ctlStaticTurbo.EnableWindow(1);
  ctlTurbo.EnableWindow(1);
  }

/****************************/
void CSettingsDlg::OnSoundSpeaker()
  {
  ctlStaticVolume.EnableWindow(0);
  ctlWaveVolume.EnableWindow(0);
  ctlStaticSpeed.EnableWindow(0);
  ctlInterval.EnableWindow(0);
  ctlStaticTurbo.EnableWindow(0);
  ctlTurbo.EnableWindow(0);
  }

/****************************/
void CSettingsDlg::OnSoundNone()
  {
  ctlStaticVolume.EnableWindow(0);
  ctlWaveVolume.EnableWindow(0);
  ctlStaticSpeed.EnableWindow(1);
  ctlInterval.EnableWindow(1);
  ctlStaticTurbo.EnableWindow(1);
  ctlTurbo.EnableWindow(1);
  }

/***********************/
void CSettingsDlg::OnPaint()
  {
  PAINTSTRUCT ps;
  CDC *pCDC=BeginPaint(&ps);

  RECT rectDlg,rectSample;
  GetWindowRect(&rectDlg);
  ctlSample.GetWindowRect(&rectSample);
  pCDC->SetBkColor(BkFontColor);
  pCDC->SetBkMode(OPAQUE);
  pCDC->SetTextColor(pHP41->GetLcdColor(Contrast,FontColor));
  pCDC->SelectObject(CFontLCD);
  CString str_dis;
  pHP41->ConvertASCIItoLCD("0.0000",str_dis);
  // center sample output
  CSize textSize=pCDC->GetOutputTextExtent(str_dis);
  int xo=(rectSample.right-rectSample.left-textSize.cx)/2;
  int yo=(rectSample.bottom-rectSample.top-textSize.cy)/2;
  pCDC->TextOut(xo+rectSample.left-rectDlg.left,
    yo+rectSample.top-rectDlg.top-GetSystemMetrics(SM_CYCAPTION)-GetSystemMetrics(SM_CXBORDER),str_dis);

  EndPaint(&ps);
  }

/***********************/
// CBackDlg dialog used for Back
/***********************/
BEGIN_MESSAGE_MAP(CBackDlg, CDialog)
  //{{AFX_MSG_MAP(CBackDlg)
  ON_WM_PAINT()
  ON_WM_DESTROY()
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/***********************/
CBackDlg::CBackDlg(CWnd* pParent)
  : CDialog(CBackDlg::IDD, pParent)
  {
  //{{AFX_DATA_INIT(CBackDlg)
  //}}AFX_DATA_INIT
  }

/***********************/
BOOL CBackDlg::OnInitDialog()
  {
  CDialog::OnInitDialog();

  if (!HP41::LoadBMP("back.bmp",pBMBack,pPalBack,pPalIndexBack))
    return TRUE;

  // center and size dialog around bitmap
  CRect rect(0,0,pBMBack->biWidth,pBMBack->biHeight);
  AdjustWindowRectEx(&rect,GetStyle(),GetMenu()!=NULL,GetExStyle());
  int DialogWidth=rect.right-rect.left;
  int DialogHeight=rect.bottom-rect.top;
  MoveWindow((GetSystemMetrics(SM_CXSCREEN)-DialogWidth)/2,(GetSystemMetrics(SM_CYSCREEN)-DialogHeight)/2,
    DialogWidth,DialogHeight,TRUE);

  return TRUE;
  }

/***********************/
void CBackDlg::OnPaint()
  {
  PAINTSTRUCT ps;
  CBitmap *pCBMOld,CBMBack;
  CDC CDCMem;
  CPalette *pCPalOld,*pCPal;
  flag fUsePal;
  CDC *pCDC=BeginPaint(&ps);
  if (pBMBack!=NULL)
    {
    // setup palette
    fUsePal=(GetDeviceCaps(pCDC->m_hAttribDC,BITSPIXEL)==8)&&(pBMBack->biClrUsed==256);
    if (fUsePal)
      {
      pCPal=new CPalette;
      pCPal->CreatePalette(pPalBack);
      pCPalOld=pCDC->SelectPalette(pCPal,FALSE);
      pCDC->RealizePalette();
      }
    // draw bitmap
    CDCMem.CreateCompatibleDC(pCDC);
    CBMBack.CreateCompatibleBitmap(pCDC,pBMBack->biWidth,pBMBack->biHeight);
    SetDIBits(pCDC->m_hDC,(HBITMAP)CBMBack.m_hObject,0,pBMBack->biHeight,
      (char*)pBMBack+pBMBack->biSize+sizeof(RGBQUAD)*pBMBack->biClrUsed,
      fUsePal?(BITMAPINFO*)pPalIndexBack:(BITMAPINFO*)pBMBack,
      fUsePal?DIB_PAL_COLORS:DIB_RGB_COLORS);
    pCBMOld=CDCMem.SelectObject(&CBMBack);
    pCDC->BitBlt(0,0,pBMBack->biWidth,pBMBack->biHeight,&CDCMem,0,0,SRCCOPY);
    // cleanup
    CDCMem.SelectObject(pCBMOld);
    CDCMem.DeleteDC();
    if (fUsePal)
      {
      pCDC->SelectPalette(pCPalOld,FALSE);
      delete pCPal;
      }
    }
  EndPaint(&ps);
  }

/***********************/
void CBackDlg::OnDestroy()
  {
  CDialog::OnDestroy();
  HP41::FreeBMP(pBMBack,pPalBack,pPalIndexBack);
  }


/***********************/
// CSelectDlg dialog used for user code program selection
/***********************/
BEGIN_MESSAGE_MAP(CSelectDlg, CDialog)
  //{{AFX_MSG_MAP(CSelectDlg)
  ON_LBN_DBLCLK(IDC_PROGLIST, OnDblclkProgList)
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/***********************/
CSelectDlg::CSelectDlg() : CDialog(CSelectDlg::IDD)
  {
  //{{AFX_DATA_INIT(CSelectDlg)
  //}}AFX_DATA_INIT
  vnProgramSel.clear();
  }

/***********************/
void CSelectDlg::DoDataExchange(CDataExchange* pDX)
  {
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CSelectDlg)
  DDX_Control(pDX, IDC_PROGLIST, ctlProgList);
  //}}AFX_DATA_MAP
  }

/***********************/
void CSelectDlg::OnDblclkProgList()
  {
  CSelectDlg::OnOK();
  }

/***********************/
void CSelectDlg::OnOK()
  {
  int nItems = ctlProgList.GetCount();
  if (nItems<=0)
    return;

  // scan all selections
  for (int i = 0; i < nItems; ++i)
    {
    if (ctlProgList.GetSel(i) > 0)
      vnProgramSel.push_back(i);
    }
  CDialog::OnOK();
  }

/***********************/
BOOL CSelectDlg::OnInitDialog()
  {
  CDialog::OnInitDialog();

  // fill the list box with cat1
  for (INT_PTR i=pCatArray->GetSize()-1;i>=0;i--)
    {
    Cat1Label *pLbl=(Cat1Label*)(*pCatArray)[i];
    ctlProgList.AddString(pLbl->szText);
    }

  return TRUE;
  }


/***********************/
// CMcodeDlg
/***********************/
CMcodeDlg *CMcodeDlg::pThis=NULL;

BEGIN_MESSAGE_MAP(CMcodeDlg, CDialog)
  //{{AFX_MSG_MAP(CMcodeDlg)
  ON_BN_CLICKED(IDC_INDICATOR, OnActivityIndicator)
  ON_WM_SHOWWINDOW()
  ON_WM_SETCURSOR()
  ON_BN_CLICKED(IDC_STEP, OnSingleStep)
  ON_BN_CLICKED(IDC_RUN, OnMultiStep)
  ON_WM_HSCROLL()
  ON_WM_PAINT()
  ON_WM_CONTEXTMENU()
  ON_BN_CLICKED(IDC_SET_HP, OnSetHP)
  ON_BN_CLICKED(IDC_SET_ZENCODE, OnSetZencode)
  ON_BN_CLICKED(IDC_SET_JDA, OnSetJDA)
  ON_WM_CLOSE()
  ON_WM_DESTROY()
  ON_COMMAND(ID_REG_A, OnRegA)
  ON_COMMAND(ID_REG_B, OnRegB)
  ON_COMMAND(ID_REG_C, OnRegC)
  ON_COMMAND(ID_REG_M, OnRegM)
  ON_COMMAND(ID_REG_N, OnRegN)
  ON_COMMAND(ID_REG_P, OnRegP)
  ON_COMMAND(ID_REG_Q, OnRegQ)
  ON_COMMAND(ID_REG_PT, OnRegPT)
  ON_COMMAND(ID_REG_G, OnRegG)
  ON_COMMAND(ID_REG_ST, OnRegST)
  ON_COMMAND(ID_REG_CARRY, OnRegCARRY)
  ON_COMMAND(ID_REG_HEX, OnRegHEX)
  ON_COMMAND(ID_REG_PC, OnRegPC)
  ON_COMMAND(ID_REG_BK, OnRegBK)
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/***********************/
CMcodeDlg::CMcodeDlg(CWnd* pParent)
  : CDialog(CMcodeDlg::IDD, pParent)
  {
  //{{AFX_DATA_INIT(CMcodeDlg)
  //}}AFX_DATA_INIT
  pThis=this;
  Top=0;
  Left=0;
  }

/***********************/
CMcodeDlg::~CMcodeDlg()
  {
  if (CallbackStepTimer)
    ::KillTimer(NULL,CallbackStepTimer);
  CallbackStepTimer=NULL;
  }

/***********************/
void CMcodeDlg::DoDataExchange(CDataExchange* pDX)
  {
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CMcodeDlg)
  DDX_Control(pDX, IDC_SPEED, ctlSpeed);
  DDX_Control(pDX, IDC_RUN, ctlMultiStep);
  DDX_Control(pDX, IDC_INDICATOR, ctlIndicator);
  DDX_Control(pDX, IDC_SET_HP, ctlSetHP);
  DDX_Control(pDX, IDC_SET_ZENCODE, ctlSetZencode);
  DDX_Control(pDX, IDC_SET_JDA, ctlSetJDA);
  //}}AFX_DATA_MAP
  }

/***********************/
BOOL CMcodeDlg::OnInitDialog()
  {
  CDialog::OnInitDialog();

  // get size of available area for display
  int NonClientX=2*GetSystemMetrics(SM_CXBORDER);
  int NonClientY=2*GetSystemMetrics(SM_CYBORDER)+GetSystemMetrics(SM_CYCAPTION);
  RECT rectClient;
  GetClientRect(&rectClient);

  // setup for font
  CSize Size;
  LOGFONT lf;
  memset(&lf,0,sizeof(LOGFONT));
  lf.lfPitchAndFamily=VARIABLE_PITCH|FF_DONTCARE;
  lf.lfWeight=FW_NORMAL;
  strcpy(lf.lfFaceName,"courier");
  lf.lfQuality=ANTIALIASED_QUALITY;

  // find the largest font size that will fit inside
  CDC *pCDC;
  pCDC=GetDC();
  pCDC->SetMapMode(MM_TEXT);
  lf.lfHeight=40;
  do
    {
    lf.lfHeight--;
    fontDlg.CreateFontIndirect(&lf);
    pCDC->SelectObject(fontDlg);
    Size=pCDC->GetOutputTextExtent("A",1);
    fontDlg.DeleteObject();
    }
  while (45*Size.cx>rectClient.right-NonClientX || 12*Size.cy>rectClient.bottom-NonClientY);
  fontDlg.CreateFontIndirect(&lf);
  theApp.m_pMainWnd->ReleaseDC(pCDC);
  CharWidth=Size.cx;
  CharHeight=Size.cy;

  CallbackStepTimer=NULL;
  ctlSpeed.SetRange(0,1000,TRUE);
  ctlSpeed.SetPos(ctlSpeed.GetRangeMax()-500);
  ctlSpeed.SetTicFreq(100);

  return TRUE;
  }

/***********************/
void CMcodeDlg::OnDestroy()
  {
  WINDOWPLACEMENT wp;
  wp.length = sizeof(wp);
  if (GetWindowPlacement(&wp))
    {
    Left=(uint)wp.rcNormalPosition.left;
    Top=(uint)wp.rcNormalPosition.top;
    }

  CDialog::OnDestroy();
  }

/***********************/
void CMcodeDlg::OnShowWindow(BOOL bShow, UINT nStatus)
  {
  CDialog::OnShowWindow(bShow,nStatus);
  fMultiStepping=FALSE;
  ctlIndicator.SetCheck(pHP41->Indicator);
  ctlMultiStep.SetCheck(fMultiStepping);
  if (!bShow)
    {
    if (CallbackStepTimer)
      ::KillTimer(NULL,CallbackStepTimer);
    CallbackStepTimer=NULL;
    }
  if (pHP41->GetInstSet()==0)
    ctlSetHP.SetCheck(1);
  else if (pHP41->GetInstSet()==1)
    ctlSetZencode.SetCheck(1);
  else if (pHP41->GetInstSet()==2)
    ctlSetJDA.SetCheck(1);
  }

/***********************/
BOOL CMcodeDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
  if (theApp.pMainWnd->getJumpToForeground() && GetActiveWindow()!=this)
    CMainWindow::ForceToForeground(GetLastActivePopup());

  return CDialog::OnSetCursor(pWnd, nHitTest, message);
}

/***********************/
void CMcodeDlg::OnCancel()
  {
  // disable cancel
  }

/****************************/
void CMcodeDlg::OnOK()
  {
  // disable OK/enter
  }

/***********************/
void CMcodeDlg::OnActivityIndicator()
  {
  pHP41->Indicator^=1;       // toggle indicator
  theApp.m_pMainWnd->InvalidateRect(&pHP41->RectIndicator,FALSE);
  }

/***********************/
void CMcodeDlg::OnSingleStep()
  {
  if (pHP41->IsSleeping())        // if sleeping nothing else to do
    return;
  pHP41->InfraredUpdateTimer();   // if infrared printer module equipped update timer
  pHP41->Execute();               // execute one instruction

  // increment instruction counter, elapsed 58 instructions (.01 sec)
  if (++pHP41->TimerInstrCnt >= ((INSTR_TIMERINC+50)/100))
    {
    pHP41->TimerInstrCnt=0;       // reset instruction counter
    pHP41->IncAllTimers();        // manually increment timer registers
    }

  if (pHP41->fRamWritten)         // wrote to RAM
    {
    theApp.pMainWnd->UpdateRamView(); // update RAM Viewer
    pHP41->fRamWritten=FALSE;
    }

  InvalidateRect(NULL,TRUE);      // invalidate the entire area.  Ideally, we would invalidate only the rect over the variables that changed
  if (pHP41->Indicator)
    theApp.m_pMainWnd->InvalidateRect(&pHP41->RectIndicator,FALSE);    // activity indicator
  if (pHP41->UpdateDisplay)
    theApp.m_pMainWnd->InvalidateRect(pHP41->pRectLCD,FALSE);          // refresh LCD
  if (pHP41->UpdateAnnun)
    theApp.m_pMainWnd->InvalidateRect(pHP41->pRectAnnun,FALSE);        // refresh Annunciator
  }

/***********************/
void CMcodeDlg::OnMultiStep()
  {
  if (CallbackStepTimer)
    ::KillTimer(NULL,CallbackStepTimer);
  CallbackStepTimer=NULL;

  fMultiStepping=!fMultiStepping;     // toggle stepping mode
  ctlMultiStep.SetCheck(fMultiStepping);
  if (!fMultiStepping)
    return;

  int StepSpeed=ctlSpeed.GetRangeMax()-ctlSpeed.GetPos();
  CallbackStepTimer=::SetTimer(NULL,0,StepSpeed,StepTimerProc);
  }

/****************************/
void CALLBACK CMcodeDlg::StepTimerProc(
  HWND hwnd,          // handle of window for timer messages
  UINT uMsg,          // WTIMER message
  UINT_PTR idEvent,   // timer identifier
  DWORD dwTime)       // current system time
  {
  if (pThis->pHP41->fBreak)      // breakpoint raised
    {
    pThis->OnMultiStep();        // stop
    pThis->pHP41->fBreak=FALSE;
    return;
    }
  pThis->OnSingleStep();
  UNREFERENCED_PARAMETER(hwnd);
  UNREFERENCED_PARAMETER(uMsg);
  UNREFERENCED_PARAMETER(idEvent);
  UNREFERENCED_PARAMETER(dwTime);
  }

/****************************/
void CMcodeDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
  if (pScrollBar->GetDlgCtrlID() == IDC_SPEED)
    {
    if (CallbackStepTimer)
      ::KillTimer(NULL,CallbackStepTimer);
    CallbackStepTimer=NULL;
    if (!fMultiStepping)
      return;

    int StepSpeed=ctlSpeed.GetRangeMax()-ctlSpeed.GetPos();
    CallbackStepTimer=::SetTimer(NULL,0,StepSpeed,StepTimerProc);
    }

  CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}

/***********************/
void CMcodeDlg::OnPaint()
  {
  PAINTSTRUCT ps;
  CDC *pCDC;
  int xpos,ypos,ich,nPos;
  char Reg14Format[]="%s%0X%0X%0X%0X%0X%0X%0X%0X%0X%0X%0X%0X%0X%0X";
  char Reg12Format[]="%0X%0X%0X%0X%0X%0X%0X%0X%0X%0X%0X%0X";
  char RAMRegFormat[]="%03X %02X %02X %02X %02X %02X %02X %02X";
  byte *pszReg;
  char szDisplay[50];

  pCDC=BeginPaint(&ps);
  pCDC->SetBkMode(TRANSPARENT);
  pCDC->SetTextColor(0);
  pCDC->SelectObject(fontDlg);

  xpos=5;
  ypos=5;
  sprintf(szDisplay, "   %cP:%1X %cQ:%1X",
    (pHP41->PT_REG==&pHP41->P_REG)?'>':' ',pHP41->P_REG & 0xF,
    (pHP41->PT_REG==&pHP41->Q_REG)?'>':' ',pHP41->Q_REG & 0xF);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;

  pszReg=pHP41->C_REG; sprintf(szDisplay,Reg14Format,"C: ",pszReg[13],pszReg[12],pszReg[11],pszReg[10],pszReg[9],pszReg[8],pszReg[7],pszReg[6],pszReg[5],pszReg[4],pszReg[3],pszReg[2],pszReg[1],pszReg[0]);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;

  pszReg=pHP41->A_REG; sprintf(szDisplay,Reg14Format,"A: ",pszReg[13],pszReg[12],pszReg[11],pszReg[10],pszReg[9],pszReg[8],pszReg[7],pszReg[6],pszReg[5],pszReg[4],pszReg[3],pszReg[2],pszReg[1],pszReg[0]);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;

  pszReg=pHP41->B_REG; sprintf(szDisplay,Reg14Format,"B: ",pszReg[13],pszReg[12],pszReg[11],pszReg[10],pszReg[9],pszReg[8],pszReg[7],pszReg[6],pszReg[5],pszReg[4],pszReg[3],pszReg[2],pszReg[1],pszReg[0]);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;

  pszReg=pHP41->M_REG; sprintf(szDisplay,Reg14Format,"M: ",pszReg[13],pszReg[12],pszReg[11],pszReg[10],pszReg[9],pszReg[8],pszReg[7],pszReg[6],pszReg[5],pszReg[4],pszReg[3],pszReg[2],pszReg[1],pszReg[0]);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;

  pszReg=pHP41->N_REG; sprintf(szDisplay,Reg14Format,"N: ",pszReg[13],pszReg[12],pszReg[11],pszReg[10],pszReg[9],pszReg[8],pszReg[7],pszReg[6],pszReg[5],pszReg[4],pszReg[3],pszReg[2],pszReg[1],pszReg[0]);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;

  nPos=sprintf(szDisplay,"BK: ");
  for (uint i = 0; i<sizeof(pHP41->active_bank)/sizeof(pHP41->active_bank[0]);++i)
    {
    if (i == 8) // separator between page 7 and 8
      szDisplay[nPos++]=' ';
    szDisplay[nPos++]='0'+pHP41->active_bank[i];
    }
  szDisplay[nPos]=0;
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;

  nPos=sprintf(szDisplay,"FI: ");
  for(ich=13; ich>=0; ich--)
    nPos+=sprintf(&szDisplay[nPos],((pHP41->FI_REG >> ich) & 0x1) ? "%1X" : "-",ich);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;

  pHP41->PC_TRACE=pHP41->PC_LAST;
  pHP41->pagebank_trace=pHP41->active_bank[(pHP41->PC_TRACE&0xf000)>>12]-1;
  pHP41->perph_in_control_trace=pHP41->perph_in_control_last;
  pHP41->control_perph_trace=pHP41->control_perph_last;
  pHP41->perph_selected_trace=pHP41->perph_selected_last;
  pHP41->ram_selected_trace=pHP41->ram_selected_last;
  pHP41->trace();
  for (ich=0;ich<(int)strlen(pHP41->szTraceOut);ich++)
    {
    if (pHP41->szTraceOut[ich]=='\t')
      pHP41->szTraceOut[ich]=' ';
    }
  if (pHP41->fOctal)
    sprintf(szDisplay,"%07o: %s",pHP41->PC_LAST,pHP41->szTraceOut);
  else
    sprintf(szDisplay,"%04X: %s",pHP41->PC_LAST,pHP41->szTraceOut);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;

  pHP41->PC_TRACE=pHP41->PC_REG;
  pHP41->pagebank_trace=pHP41->active_bank[(pHP41->PC_TRACE&0xf000)>>12]-1;
  pHP41->perph_in_control_trace=pHP41->perph_in_control;
  pHP41->control_perph_trace=pHP41->control_perph;
  pHP41->perph_selected_trace=pHP41->perph_selected;
  pHP41->ram_selected_trace=pHP41->ram_selected;
  pHP41->trace();
  for (ich=0;ich<(int)strlen(pHP41->szTraceOut);ich++)
    {
    if (pHP41->szTraceOut[ich]=='\t')
      pHP41->szTraceOut[ich]=' ';
    }
  if (pHP41->fOctal)
    sprintf(szDisplay,"%07o: %s",pHP41->PC_REG,pHP41->szTraceOut);
  else
    sprintf(szDisplay,"%04X: %s",pHP41->PC_REG,pHP41->szTraceOut);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;

  xpos+=CharWidth*23;
  ypos=5;

  pCDC->TextOut(xpos,ypos,"RTN STK",7);
  ypos+=CharHeight;

  sprintf(szDisplay," 4: %04X",pHP41->RET_STK3);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;
  sprintf(szDisplay," 3: %04X",pHP41->RET_STK2);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;
  sprintf(szDisplay," 2: %04X",pHP41->RET_STK1);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;
  sprintf(szDisplay," 1: %04X",pHP41->RET_STK0);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;
  sprintf(szDisplay,"PC: %04X",pHP41->PC_REG);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=2*CharHeight;

  nPos=sprintf(szDisplay,"ST: ");
  for(ich=5; ich>=0; ich--)
    nPos+=sprintf(&szDisplay[nPos],((pHP41->XST_REG >> ich) & 0x1) ? "%1X" : "-",ich+8);
  nPos+=sprintf(&szDisplay[nPos]," ");
  for(ich=7; ich>=0; ich--)
    nPos+=sprintf(&szDisplay[nPos],((pHP41->ST_REG >> ich) & 0x1) ? "%1X" : "-",ich);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;

  xpos+=CharWidth*13;
  ypos=5;

  sprintf(szDisplay,"CARRY: %X",pHP41->CARRY);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;
  sprintf(szDisplay,"    G: %02X",pHP41->G_REG);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;
  sprintf(szDisplay,"    F: %02X",pHP41->F_REG);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;
  sprintf(szDisplay,"  HEX: %X",(pHP41->BASE==16)?1:0);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;
  sprintf(szDisplay," ?KEY: %X %02X",pHP41->KEYDOWN,pHP41->KEY_REG);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;
  sprintf(szDisplay,"PERPH: %02X",pHP41->perph_selected);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;
  sprintf(szDisplay,"  RAM: %03X",pHP41->ram_selected);
  pCDC->TextOut(xpos,ypos,szDisplay,static_cast<int>(strlen(szDisplay)));
  ypos+=CharHeight;

  EndPaint(&ps);
  }

/****************************/
void CMcodeDlg::OnContextMenu(CWnd* pWnd, CPoint point)
  {
  if (point.x == -1 && point.y == -1) // VK_APPS
    {
      point.x = 15;                   // default position inside client area
      point.y = 15;
      ClientToScreen(&point);
    }
  CMenu PopupMenu;                   // the pop up menu for right click
  PopupMenu.LoadMenu(IDR_POPUPREGISTER);
  PopupMenu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN|TPM_LEFTBUTTON,point.x,point.y,this);
  PopupMenu.DestroyMenu();
  UNREFERENCED_PARAMETER(pWnd);
  }

/****************************/
void CMcodeDlg::OnSetHP()
  {
  pHP41->ChangeInstSet(0);
  InvalidateRect(NULL,TRUE);
  }

/****************************/
void CMcodeDlg::OnSetZencode()
  {
  pHP41->ChangeInstSet(1);
  InvalidateRect(NULL,TRUE);
  }

/****************************/
void CMcodeDlg::OnSetJDA()
  {
  pHP41->ChangeInstSet(2);
  InvalidateRect(NULL,TRUE);
  }

/****************************/
void CMcodeDlg::OnClose()
  {
  theApp.pMainWnd->CloseConsole();
  }

/****************************/
int CMcodeDlg::OnReg_(byte *reg, uint reglen,uint base)
  {
  int retCode;

  CRegisterValue dlg;
  dlg.putRegister(reg,reglen,base);
  if ((retCode=(int)dlg.DoModal())==IDOK)
    {
    dlg.getRegister(reg,reglen);
    InvalidateRect(NULL);
    }
  return retCode;
  }

/****************************/
void CMcodeDlg::OnRegA()
  {
  OnReg_(pHP41->A_REG,14,16);
  }

/****************************/
void CMcodeDlg::OnRegB()
  {
  OnReg_(pHP41->B_REG,14,16);
  }

/****************************/
void CMcodeDlg::OnRegC()
  {
  OnReg_(pHP41->C_REG,14,16);
  }

/****************************/
void CMcodeDlg::OnRegM()
  {
  OnReg_(pHP41->M_REG,14,16);
  }

/****************************/
void CMcodeDlg::OnRegN()
  {
  OnReg_(pHP41->N_REG,14,16);
  }

/****************************/
void CMcodeDlg::OnRegP()
  {
  OnReg_((byte *)&pHP41->P_REG,1,14);
  }

/****************************/
void CMcodeDlg::OnRegQ()
  {
  OnReg_((byte *)&pHP41->Q_REG,1,14);
  }

/****************************/
void CMcodeDlg::OnRegPT()
  {
  byte pt=(pHP41->PT_REG==&pHP41->Q_REG);
  if (OnReg_(&pt,1,2)==IDOK)
    {
    if (pt==1)
      pHP41->PT_REG=&pHP41->Q_REG;
    else if (pt==0)
      pHP41->PT_REG=&pHP41->P_REG;
    }
  }

/****************************/
void CMcodeDlg::OnRegG()
  {
  byte g[2];
  g[0]=pHP41->G_REG&0xF;
  g[1]=(pHP41->G_REG>>4)&0xF;
  if (OnReg_(g,2,16)==IDOK)
    {
      pHP41->G_REG=(g[1]<<4)|g[0];
    }
  }

/****************************/
void CMcodeDlg::OnRegST()
  {
  // 14 bit ST register (0-13)
  word st=(pHP41->XST_REG<<8)|pHP41->ST_REG;

  // create byte array for edit
  byte st_reg[14];
  for(int i=0;i<14;++i)
    {
    st_reg[i]=st&0x1;
    st>>=1;
    }

  if (OnReg_(st_reg,14,'S')==IDOK)
    {
    // recreate 14 bit ST register
    st=0;
    for(int i=14-1;i>=0;--i)
      {
      st<<=1;
      st|=st_reg[i]&0x1;
      }
    pHP41->XST_REG=st>>8;
    pHP41->ST_REG=st&0xFF;
    }
  }

/****************************/
void CMcodeDlg::OnRegCARRY()
  {
  OnReg_((byte *)&pHP41->CARRY,1,2);
  }

/****************************/
void CMcodeDlg::OnRegHEX()
  {
  byte hex=pHP41->BASE>>4;
  if (OnReg_(&hex,1,2)==IDOK)
    {
    pHP41->BASE=hex?16:10;
    }
  }

/****************************/
void CMcodeDlg::OnRegPC()
  {
  word pc=pHP41->PC_REG;
  byte pc_reg[4];
  for(int i=0;i<4;++i)
    {
    pc_reg[i]=pc&0xF;
    pc>>=4;
    }

  if (OnReg_(pc_reg,4,16)==IDOK)
    {
    pHP41->PC_REG = (pc_reg[3]<<12)
                  | (pc_reg[2]<<8)
                  | (pc_reg[1]<<4)
                  | (pc_reg[0]<<0);
    }
  }

/****************************/
void CMcodeDlg::OnRegBK()
  {
  const uint reg_size=sizeof(pHP41->active_bank)/sizeof(pHP41->active_bank[0]);
  byte active_bank_reg[reg_size];
  for (uint i=0;i<reg_size;++i)
    {
    active_bank_reg[i]=pHP41->active_bank[reg_size-1-i];
    }
  if (OnReg_(active_bank_reg,reg_size,4)==IDOK)
    {
    for (uint i=0;i<reg_size;++i)
      {
      pHP41->active_bank[i]=active_bank_reg[reg_size-1-i];
      }
    }
  }

/***********************/
// CRamViewDlg
/***********************/
BEGIN_MESSAGE_MAP(CRamViewDlg, CDialog)
  ON_MESSAGE(WM_UPDATERAMVIEW, OnUpdateRamView)

  //{{AFX_MSG_MAP(CRamViewDlg)
  ON_WM_SHOWWINDOW()
  ON_WM_SETCURSOR()
  ON_WM_CLOSE()
  ON_WM_DESTROY()
  ON_WM_SIZE()
  ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_RAMVIEW_TREE, OnBeginlabeleditRamviewTree)
  ON_NOTIFY(TVN_ENDLABELEDIT, IDC_RAMVIEW_TREE, OnEndlabeleditRamviewTree)
  ON_WM_CONTEXTMENU()
  ON_COMMAND(ID_RAM_SHOWEMPTYREG, OnRamShowemptyreg)
  ON_COMMAND(ID_RAM_SHOWFLAGS, OnRamShowFlags)
  ON_COMMAND(ID_RAM_VISIBLE, OnRamVisible)
  ON_COMMAND(ID_RAM_ALL, OnRamAll)
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/***********************/
CRamViewDlg::CRamViewDlg(CWnd* pParent)
  : CDialog(CRamViewDlg::IDD, pParent)
  , m_peditLine(NULL)
  {
  //{{AFX_DATA_INIT(CMemMapDlg)
  //}}AFX_DATA_INIT
  Top=0;
  Left=0;
  Right=0;
  Bottom=0;
  fRamShowEmptyReg=TRUE;
  fRamShowFlags=FALSE;
  fRamBufferName=TRUE;
  m_bWin8=FALSE;
  }

/***********************/
CRamViewDlg::~CRamViewDlg()
  {
  }

/***********************/
void CRamViewDlg::DoDataExchange(CDataExchange* pDX)
  {
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CRamViewDlg)
  // NOTE: the ClassWizard will add DDX and DDV calls here
  DDX_Control(pDX, IDC_RAMVIEW_TREE, m_TreeCtrl);
  //}}AFX_DATA_MAP
  }

/***********************/
BOOL CRamViewDlg::PreTranslateMessage(MSG* pMsg)
  {
  if (pMsg->message == WM_KEYDOWN)
    {
    // activate edit with F2
    if (pMsg->wParam == VK_F2)
      {
      HTREEITEM hSel = m_TreeCtrl.GetSelectedItem();
      if (hSel != NULL)
        {
        m_TreeCtrl.EditLabel(hSel);         // activate edit control on row
        }
      return TRUE;
      }

    // handle ENTER and ESC in edit control
    if (pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
      {
      // get pointer to edit control
      CEdit *pEditCtrl = m_TreeCtrl.GetEditControl();

      if (pEditCtrl)                        // edit control enabled
        {
        // send message to edit control
        pEditCtrl->SendMessage(pMsg->message,pMsg->wParam,pMsg->lParam);
        return TRUE;
        }
      }
    }
  return CDialog::PreTranslateMessage(pMsg);
  }

/****************************/
BOOL CRamViewDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
  {
  if (theApp.pMainWnd->getJumpToForeground() && GetActiveWindow()!=this)
    CMainWindow::ForceToForeground(GetLastActivePopup());

  return CDialog::OnSetCursor(pWnd, nHitTest, message);
  }

/****************************/
void CRamViewDlg::OnClose()
  {
  theApp.pMainWnd->CloseRamView();
  }

/***********************/
void CRamViewDlg::OnDestroy()
  {
  WINDOWPLACEMENT wp;
  wp.length = sizeof(wp);
  if (GetWindowPlacement(&wp))
    {
    Left=(uint)wp.rcNormalPosition.left;
    Top=(uint)wp.rcNormalPosition.top;
    Right=(uint)wp.rcNormalPosition.right;
    Bottom=(uint)wp.rcNormalPosition.bottom;
    }

  Clear();                                  // delete tree
  CDialog::OnDestroy();
  }

/***********************/
void CRamViewDlg::OnSize(UINT nType, int cx, int cy)
  {
  CDialog::OnSize(nType, cx, cy);

  if (m_TreeCtrl.m_hWnd != NULL)            // tree control created?
    {
    RECT rc,rcw;
    GetClientRect(&rc);                     // new client window size
    m_TreeCtrl.GetWindowRect(&rcw);         // get tree view offsets
    ScreenToClient(&rcw);

    rc.right = (rc.right - rc.left) - 2 * rcw.left;
    rc.bottom = (rc.bottom - rc.top) - 2 * rcw.top;
    m_TreeCtrl.SetWindowPos(NULL,0,0,rc.right,rc.bottom,SWP_NOMOVE | SWP_NOZORDER);
    }
  }

/***********************/
void CRamViewDlg::OnContextMenu(CWnd* pWnd, CPoint point)
  {
  if (point.x == -1 && point.y == -1)       // VK_APPS
    {
      point.x = 15;                         // default position inside client area
      point.y = 15;
      ClientToScreen(&point);
    }
  CMenu PopupMenu;                          // the pop up menu for right click
  PopupMenu.LoadMenu(IDR_POPUPRAMCOPY);
  CMenu *pMenu = PopupMenu.GetSubMenu(0);
  pMenu->CheckMenuItem(ID_RAM_SHOWEMPTYREG,fRamShowEmptyReg?MF_CHECKED:MF_UNCHECKED);
  pMenu->CheckMenuItem(ID_RAM_SHOWFLAGS,fRamShowFlags?MF_CHECKED:MF_UNCHECKED);
  pMenu->TrackPopupMenu(TPM_LEFTALIGN|TPM_LEFTBUTTON,point.x,point.y,this);
  PopupMenu.DestroyMenu();
  UNREFERENCED_PARAMETER(pWnd);
  }

/***********************/
void CRamViewDlg::OnShowWindow(BOOL bShow, UINT nStatus)
  {
  CDialog::OnShowWindow(bShow,nStatus);

  if (bShow)
    {
    UpdateRamView(true);
    }
  }

/***********************/
void CRamViewDlg::OnBeginlabeleditRamviewTree(NMHDR *pNMHDR, LRESULT *pResult)
  {
  LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
  CTreeCtrl *pTreeWnd = static_cast<CTreeCtrl *>(FromHandle(pTVDispInfo->hdr.hwndFrom));

  // return 1 if head line else 0 for enable edit
  *pResult = pTreeWnd->GetChildItem(pTVDispInfo->item.hItem) ? 1 : 0;

  if (*pResult == 0)                        // edit enabled
    {
    ASSERT(m_peditLine == NULL);
    m_peditLine = new CMaskEdit();          // create inheritance of CEdit class
    m_peditLine->SubclassWindow(pTreeWnd->GetEditControl()->GetSafeHwnd());
    m_peditLine->SetLimitText(m_peditLine->GetWindowTextLength());
    m_peditLine->SetMask(
      _T("     AA AA AA AA AA AA AA"),
      _T("     __ __ __ __ __ __ __"),
      _T("0123456789ABCDEFabcdef"));

    // set cursor to the 1st element
    m_peditLine->PostMessage(EM_SETSEL,5,5);
    }
  }

/***********************/
void CRamViewDlg::OnEndlabeleditRamviewTree(NMHDR *pNMHDR, LRESULT *pResult)
  {
  LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);

  *pResult = 0;                             // ignore changes

  if (pTVDispInfo->item.pszText != NULL)    // text changed
    {
    // valid entry without literals
    if (strchr(pTVDispInfo->item.pszText, _T('_')) == NULL)
      {
      WORD RegIndex,i0,i1,i2,i3,i4,i5,i6;
      sscanf(pTVDispInfo->item.pszText,"%hX: %hX %hX %hX %hX %hX %hX %hX",&RegIndex,&i6,&i5,&i4,&i3,&i2,&i1,&i0);

      RAM_REG& RAM=pHP41->pRAM[RegIndex];
      if ((RegIndex & 0x3F8)!=0x028)        // not infrared module RAM 0x028-0x02F which is only 8 bit wide
        {
        RAM.Reg[6] = static_cast<BYTE>(i6);
        RAM.Reg[5] = static_cast<BYTE>(i5);
        RAM.Reg[4] = static_cast<BYTE>(i4);
        RAM.Reg[3] = static_cast<BYTE>(i3);
        RAM.Reg[2] = static_cast<BYTE>(i2);
        RAM.Reg[1] = static_cast<BYTE>(i1);
        }
      RAM.Reg[0] = static_cast<BYTE>(i0);

      PostMessage(WM_UPDATERAMVIEW,0,0);    // asynchronous RAM view update
      *pResult = 1;                         // accept changes
      }
    }

  m_peditLine->UnsubclassWindow();
  delete m_peditLine;
  m_peditLine = NULL;
  }

/***********************/
//
// add text leaf
//
HTREEITEM __cdecl CRamViewDlg::AddLeaf(CTreeCtrl *pTreeWnd, HTREEITEM hti, LPCTSTR lpFormat, ...)
  {
  TCHAR cOutput[1024];
  va_list arglist;

  va_start(arglist,lpFormat);
  wvsprintf(cOutput,lpFormat,arglist);
  va_end(arglist);

  return pTreeWnd->InsertItem(cOutput, hti, TVI_LAST);
  }

/***********************/
//
// decode BCD number
//
INT CRamViewDlg::GetBcd(BYTE CONST *pbyNum,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPTSTR cp,INT nSize)
  {
  BYTE byNib;
  LONG v,lExp;
  BOOL bPflag,bExpflag;
  INT  i;

  lExp = 0;
  for (v = 1; nExpLen--; v *= 10)           // fetch exponent
    {
    lExp += (LONG) *pbyNum++ * v;           // calc. exponent
    }

  if (*pbyNum++ != 0)                       // negative exponent (normally 9)
    {
    lExp -= 100;
    }

  lExp -= nMantLen - 1;                     // set decimal point to end of mantissa

  i = 0;                                    // first character
  bPflag = FALSE;                           // show no decimal point
  bExpflag = FALSE;                         // show no exponent

  // scan mantissa
  for (v = (LONG) nMantLen - 1; v >= 0 || bPflag; v--)
    {
    if (v >= 0L)                            // still mantissa digits left
      byNib = *pbyNum++;
    else
      byNib = 0;                            // zero for negativ exponent

    if (!i)                                 // still delete zeros at end
      {
      if (byNib == 0 && lExp && v > 0)      // delete zeros
        {
        lExp++;                             // adjust exponent
        continue;
        }

      // TRUE at x.E
      bExpflag = v + lExp >= nMantLen || lExp < -nMantLen;
      bPflag = !bExpflag && v < -lExp;      // decimal point flag at neg. exponent
      }

    // set decimal point
    if ((bExpflag && v == 0) || (!lExp && i))
      {
      if (i >= nSize) return 0;             // dest buffer overflow
      cp[i++] = cDec;                       // write decimal point
      if (v < 0)                            // no mantissa digits any more
        {
        if (i >= nSize) return 0;           // dest buffer overflow
        cp[i++] = _T('0');                  // write heading zero
        }
      bPflag = FALSE;                       // finished with negativ exponents
      }

    if (v >= 0 || bPflag)
      {
      if (i >= nSize) return 0;             // dest buffer overflow
      cp[i++] = (TCHAR) byNib + _T('0');    // write character
      }

    lExp++;                                 // next position
    }

  if (*pbyNum != 0)                         // negative number (normally 9)
    {
    if (i >= nSize) return 0;               // dest buffer overflow
    cp[i++] = _T('-');                      // write sign
    }

  if (i >= nSize) return 0;                 // dest buffer overflow
  cp[i] = 0;                                // set EOS

  for (v = 0; v < (i / 2); v++)             // reverse string
    {
    TCHAR cNib = cp[v];                     // swap chars
    cp[v] = cp[i-v-1];
    cp[i-v-1] = cNib;
    }

  // write number with exponent
  if (bExpflag)
    {
    if (i + 5 >= nSize) return 0;           // dest buffer overflow
    i += wsprintf(&cp[i],_T("E%d"),lExp-1);
    }
  return i;
  }

void CRamViewDlg::DecodeReg(const struct RAM_REG *pRam,CString& strData) const
  {
  /*
  * ALPHA DATA:
  * In the HP-41 ALPHA DATA is a real number object,
  * where the Mantissa sign is 1.
  * 6 characters fit in one ALPHA DATA object
  * followed by "01" for HP-41 Mantissa sign
  */

  strData = "";

  if (pRam->Reg[6] == 0x10)                 // ALPHA DATA
    {
    strData=" \"";
    for (int i=6; --i >= 0;)
      {
      if (pRam->Reg[i] != 0)
        {
        strData+=pRam->Reg[i];
        }
      }
    strData+="\"";
    }
  else                                      // BCD float
    {
    // unpack register for using with GetBcd()
    BYTE byNumber[14];
    for (UINT i=0; i<sizeof(byNumber);)
      {
      const BYTE byReg=pRam->Reg[i>>1];
      const BYTE byLow=byReg & 0xf;
      const BYTE byHigh=byReg >> 4;
      if (byLow > 9 || byHigh > 9)          // not BCD
        return;
      byNumber[i++]=byLow;
      byNumber[i++]=byHigh;
      }

    // get Display Punctuation from flag 28
    const TCHAR cRadix = pHP41->GetFlag(28) ? _T('.') : _T(',');

    // decode real object number
    LPTSTR cp = strData.GetBuffer(256);
    *cp++=' ';
    GetBcd(byNumber,10,2,cRadix,cp,strData.GetAllocLength()-1);
    strData.ReleaseBuffer();
    }
  }

/***********************/
//
//  initialize XMem map with address remarks
//
void CRamViewDlg::CreateXmFileList(CMap<int, int, CString, CString>& mapText) const
  {
  enum XMEMPART {eXMem0,eXMem1,eXMem2};
  enum FILETYPE {eHeadName=0,eHeadInfo};
  CString strData;

  mapText.RemoveAll();
  mapText.InitHashTable(399);

  // read link register content
  struct _LINKREG
    {
    int nRegAddr;                           // link reg addr
    int nId;                                // block id
    int nNext;                              // id of next block
    }
  sXmLink[] =
    {
      { 0x040, 0x000, 0x000 },              // XMem0
      { 0x201, 0x000, 0x000 },              // XMem1
      { 0x301, 0x000, 0x000 }               // XMem2
    };

  for (int i = 0; i < (sizeof(sXmLink)/sizeof(sXmLink[0])); ++i)
    {
    struct _LINKREG& Xm=sXmLink[i];         // reference to XMem block

    const int nAddr=Xm.nRegAddr;            // read link register
    if (pHP41->RamExist(nAddr))             // read link content
      {
      const RAM_REG& BufRAMLink=pHP41->pRAM[nAddr];
      Xm.nId = ((BufRAMLink.Reg[1] & 0xf) << 8) | BufRAMLink.Reg[0];
      Xm.nNext = (BufRAMLink.Reg[2] << 4) | (BufRAMLink.Reg[1] >> 4);
      strData.Format("Link ID=%03X Next=%03X",Xm.nId,Xm.nNext);
      mapText.SetAt(nAddr,strData);
      }
    }

  // naming xmem file
  bool bPartition = false;                  // partition register not found

  enum XMEMPART eXmId=eXMem0;               // begin with base module
  int nLinkAddr=sXmLink[eXmId].nRegAddr;    // link address of base XMem

  // scan from top of X-Functions
  int eFileType=eHeadName;
  for (int RegBuf=0x0bf;!bPartition && RegBuf>=nLinkAddr;)
    {
    // no RAM or empty register on file header
    if (!pHP41->RamExist(RegBuf) || pHP41->IsRegisterEmpty(RegBuf))
      {
      break;                                // no XM or XM not initialized
      }

    const RAM_REG& BufRAM=pHP41->pRAM[RegBuf];

    if (eFileType == eHeadName)             // get name header
      {
      // check for partition end
      bPartition = (memcmp(&BufRAM,"\xff\xff\xff\xff\xff\xff\xff",7) == 0);
      if (bPartition)
        {
        mapText.SetAt(RegBuf,"End of XM");
        break;
        }

      strData="";                           // get name
      for (int i=7; --i >= 0;)
        {
        strData+=BufRAM.Reg[i];
        }
      // remove tailing " " characters
      strData.TrimRight(' ');
      mapText.SetAt(RegBuf,(LPCTSTR) strData);
      eFileType=eHeadInfo;
      }
    else                                    // eHeadInfo
      {
      // get type and length header
      char type;
      switch (BufRAM.Reg[6] >> 4)
        {
        case 1: // program
          type = 'P';
          break;
        case 2: // data
          type = 'D';
          break;
        case 3: // ASCII
          type = 'A';
          break;
        default:
          type = '@';
        }
      const int nLength = (BufRAM.Reg[1] & 0xf) << 8 | BufRAM.Reg[0];
      strData.Format("%c%03d",type,nLength);
      mapText.SetAt(RegBuf,strData);
      RegBuf-=nLength;                      // register end of data
      eFileType=eHeadName;
      }

    --RegBuf;                               // next register
    while (RegBuf<=nLinkAddr)               // search for next XM block
      {
      // remaining data length
      const int nRemaining=nLinkAddr-RegBuf;
      if (sXmLink[eXmId].nNext == sXmLink[eXMem1].nId)
        {
        eXmId=eXMem1;                       // next module is XMem1
        }
      else if (sXmLink[eXmId].nNext == sXmLink[eXMem2].nId)
        {
        eXmId=eXMem2;                       // next module is XMem2
        }
      else                                  // no XM
        {
        bPartition = true;                  // break outer loop
        break;
        }
      nLinkAddr=sXmLink[eXmId].nRegAddr;    // link addr
      RegBuf=nLinkAddr+238;                 // block start addr (238 reg)
      RegBuf-=nRemaining;
      }
    }
  }

/***********************/
//
// draw the memory configuration tree
//
void CRamViewDlg::ShowMemCfgTree()
  {
  enum MEMAREA {eNone=0,eXMem2,eXMem1,eData,ePRGM,eFree,eBuffer,eXMem0,eIrMem,eStatus};

  const int DataAddr=((pHP41->pRAM[0x00d].Reg[2])<<4) | (pHP41->pRAM[0x00d].Reg[1]>>4);
  const int EndAddr=((pHP41->pRAM[0x00d].Reg[1]&0x0f)<<8) | pHP41->pRAM[0x00d].Reg[0];
  const int EndBufAddr=pHP41->GetRamBufferEnd();

  enum MEMAREA eMemType=eNone;

  // create XMEM file remarks
  CMap<int, int, CString, CString> mapText;
  CreateXmFileList(mapText);

  HTREEITEM htiLeaf = TVI_ROOT;

  for (int RegIndex=MAX_RAM-1;RegIndex>=0;--RegIndex)
    {
    if (pHP41->RamExist(RegIndex))
      {
      const RAM_REG& RAM=pHP41->pRAM[RegIndex];

      // adding headline for the data area
      if (RegIndex==0x3ef)
        {
        htiLeaf = AddLeaf(&m_TreeCtrl,TVI_ROOT,_T("X-Memory 2"));
        m_TreeCtrl.SetItemState(htiLeaf,TVIS_BOLD,TVIS_BOLD);
        m_TreeCtrl.SetItemData(htiLeaf,1);
        eMemType=eXMem2;
        }
      else if (RegIndex==0x2ef)
        {
        htiLeaf = AddLeaf(&m_TreeCtrl,TVI_ROOT,_T("X-Memory 1"));
        m_TreeCtrl.SetItemState(htiLeaf,TVIS_BOLD,TVIS_BOLD);
        m_TreeCtrl.SetItemData(htiLeaf,2);
        eMemType=eXMem1;
        }
      else if (RegIndex>=0x0c0 && RegIndex<=0x1ff && DataAddr>=0xc0 && RegIndex>=DataAddr && eMemType!=eData)
        {
        htiLeaf = AddLeaf(&m_TreeCtrl,TVI_ROOT,_T("Data"));
        m_TreeCtrl.SetItemState(htiLeaf,TVIS_BOLD,TVIS_BOLD);
        m_TreeCtrl.SetItemData(htiLeaf,3);
        eMemType=eData;
        }
      else if (RegIndex>=0x0c0 && RegIndex<DataAddr && EndAddr>=0xc0 && RegIndex>=EndAddr && eMemType!=ePRGM)
        {
        htiLeaf = AddLeaf(&m_TreeCtrl,TVI_ROOT,_T("PRGM"));
        m_TreeCtrl.SetItemState(htiLeaf,TVIS_BOLD,TVIS_BOLD);
        m_TreeCtrl.SetItemData(htiLeaf,4);
        eMemType=ePRGM;
        }
      else if (RegIndex>=EndBufAddr && (EndAddr==0 || RegIndex<EndAddr) && eMemType!=eFree && eMemType!=eBuffer)
        {
        htiLeaf = AddLeaf(&m_TreeCtrl,TVI_ROOT,_T("Free Registers"));
        m_TreeCtrl.SetItemState(htiLeaf,TVIS_BOLD,TVIS_BOLD);
        m_TreeCtrl.SetItemData(htiLeaf,5);
        eMemType=eFree;
        }
      else if (RegIndex>=0x0c0 && RegIndex<DataAddr && RegIndex<EndBufAddr && eMemType!=eBuffer)
        {
        htiLeaf = AddLeaf(&m_TreeCtrl,TVI_ROOT,_T("Buffer"));
        m_TreeCtrl.SetItemState(htiLeaf,TVIS_BOLD,TVIS_BOLD);
        m_TreeCtrl.SetItemData(htiLeaf,6);

        if (fRamBufferName)                 // naming buffers enabled
          {
          // naming buffers
          for (int RegBuf=0xc0;RegBuf<=RegIndex;++RegBuf)
            {
            const RAM_REG& BufRAM=pHP41->pRAM[RegBuf];

            if (BufRAM.Reg[6] == 0xf0)
              {
              mapText.SetAt(RegBuf,"(Key assignment)");
              }
            else if (BufRAM.Reg[5] > 0)     // no. of registers
              {
              static LPCTSTR lpszType[] =
                {
                "Ladybug",                                  // 0 (Ladybug Id = 10)
                "David Assembler",                          // 1
                "David Assembler",                          // 2
                "Eramco RSU-1B",                            // 3
                "Eramco RSU-1A",                            // 4
                "CCD / Advantage",                          // 5
                "Extended IL / Formula Eval",               // 6
                "PK3e & PK3f / Extended IL / Formula Eval", // 7
                "41Z",                                      // 8
                "SandMath / PowerCL / VoRaNoGe",            // 9
                "TIME",                                     // A
                "Plotter / HPIL-DIAG",                      // B
                "CMT-200 / HPIL-DEV",                       // C
                "CMT-300 / FORTH",                          // D
                "Advantage / SandMath",                     // E
                "OS4"                                       // F (OS4 Id = 1F)
                };

              CString strType;
              const uint uNo = BufRAM.Reg[6] & 0xf;
              strType.Format("(%s) (%u regs)",lpszType[uNo],BufRAM.Reg[5]);
              mapText.SetAt(RegBuf,strType);
              RegBuf+=BufRAM.Reg[5]-1;
              }
            }
          }
        eMemType=eBuffer;
        }
      else if (RegIndex==0x0bf)
        {
        htiLeaf = AddLeaf(&m_TreeCtrl,TVI_ROOT,_T("X-Functions"));
        m_TreeCtrl.SetItemState(htiLeaf,TVIS_BOLD,TVIS_BOLD);
        m_TreeCtrl.SetItemData(htiLeaf,7);
        eMemType=eXMem0;
        }
      else if (RegIndex==0x02f)
        {
        htiLeaf = AddLeaf(&m_TreeCtrl,TVI_ROOT,_T("IR-Memory"));
        m_TreeCtrl.SetItemState(htiLeaf,TVIS_BOLD,TVIS_BOLD);
        m_TreeCtrl.SetItemData(htiLeaf,8);
        eMemType=eIrMem;
        }
      else if (RegIndex==0x00f)
        {
        htiLeaf = AddLeaf(&m_TreeCtrl,TVI_ROOT,_T("Status"));
        m_TreeCtrl.SetItemState(htiLeaf,TVIS_BOLD,TVIS_BOLD);
        m_TreeCtrl.SetItemData(htiLeaf,9);
        eMemType=eStatus;
        }

      // add register name
      CString strRegName("");
      if (eMemType==eData)
        {
        // reg no. from curtain
        strRegName.Format(" (R%02u)",RegIndex-DataAddr);

        // register is not empty
        if (pHP41->IsRegisterEmpty(RegIndex) == 0)
          {
          // decode register content
          CString strData;
          DecodeReg(&RAM,strData);
          strRegName+=strData;
          }
        }
      else if (eMemType==eBuffer || eMemType==eXMem2 || eMemType==eXMem1 || eMemType==eXMem0)
        {
        // have text matching with register address
        if (mapText.Lookup(RegIndex,strRegName))
          {
          strRegName.Insert(0,' ');         // found, add space to head
          }
        }
      else if (eMemType==eIrMem)
        {
        if (!pHP41->InfraredRamEnabled())   // infrared RAM disabled
          strRegName=" *";                  // mark with an asterisk
        }
      else if (eMemType==eStatus)
        {
        const CHAR *cReg="TZYXLMNOPQRabcde";
        strRegName.Format(" (%c)",cReg[RegIndex]);

        if (fRamShowFlags && RegIndex==14)  // register d (flags)
          {
          word wFlag=0;
          for (int nReg=6;nReg>=0;--nReg)
            {
            const byte byVal=RAM.Reg[nReg];

            for (uint bit=0x80;bit!=0x00;bit>>=1)
              {
              if ((byVal & bit))            // bit set
                {
                CString strData;
                strData.Format(" %u",wFlag);
                strRegName+=strData;
                }
              ++wFlag;
              }
            }
            ASSERT(wFlag==56);
          }

        if (RegIndex>=5 && RegIndex<=8)     // register M-P (alpha)
          {
          CString strData;

          for (int nReg=(RegIndex==8)?2:6;nReg>=0;--nReg)
            {
            const byte byVal=RAM.Reg[nReg];
            if (byVal != 0)                 // contain character
              {
                strData+=byVal;
              }
            }

          if (!strData.IsEmpty())           // string contain data
            {
            strRegName+=" \""+strData+"\"";
            }
          }

        if (RegIndex>=0 && RegIndex<=4)     // register T-L
          {
          // decode register content
          CString strData;
          DecodeReg(&RAM,strData);
          strRegName+=strData;
          }
        }

      // show register content
      if (fRamShowEmptyReg || eMemType==eStatus || !pHP41->IsRegisterEmpty(RegIndex))
        {
        AddLeaf(&m_TreeCtrl,htiLeaf,_T("%03X: %02X %02X %02X %02X %02X %02X %02X%s"),
          RegIndex,
          RAM.Reg[6],
          RAM.Reg[5],
          RAM.Reg[4],
          RAM.Reg[3],
          RAM.Reg[2],
          RAM.Reg[1],
          RAM.Reg[0],
          strRegName
          );

        // m_TreeCtrl.Expand(htiLeaf,TVE_EXPAND);
        }
      }
    }
  }

/***********************/
//
// get expanded/collapsed state of tree nodes
//
void CRamViewDlg::GetExpMemCfgTree(std::vector<UINT>& vState) const
  {
  HTREEITEM htiRoot = m_TreeCtrl.GetRootItem();

  while (htiRoot != NULL)
    {
    TVITEM tvi;
    ZeroMemory(&tvi, sizeof(tvi));
    tvi.mask = TVIF_PARAM | TVIF_CHILDREN | TVIF_STATE;
    tvi.hItem = htiRoot;
    m_TreeCtrl.GetItem(&tvi);

    if (tvi.cChildren == 1 && (tvi.state & TVIS_EXPANDED) != 0)
      vState.push_back(static_cast<UINT>(tvi.lParam));

    htiRoot = m_TreeCtrl.GetNextSiblingItem(htiRoot);
    }
  }

/***********************/
//
// restore state of tree nodes
//
void CRamViewDlg::SetExpMemCfgTree(const std::vector<UINT>& vState)
  {
  HTREEITEM htiRoot = m_TreeCtrl.GetRootItem();

  while (htiRoot != NULL)
    {
    UINT uId = static_cast<UINT>(m_TreeCtrl.GetItemData(htiRoot));

    std::vector<UINT>::const_iterator it = vState.begin();
    for (;it != vState.end(); it++)
      {
      if (*it == uId)
        {
        m_TreeCtrl.Expand(htiRoot,TVE_EXPAND);
        break;
        }
      }
    htiRoot = m_TreeCtrl.GetNextSiblingItem(htiRoot);
    }
  }

/***********************/
VOID CRamViewDlg::Clear()
  {
  m_TreeCtrl.DeleteAllItems();              // delete tree
  }

/***********************/
//
// update memory mapping dialog content
//
VOID CRamViewDlg::UpdateRamView(bool bForce)
  {
  if (bForce || IsWindowVisible())
    {
    if (m_bWin8)
      m_TreeCtrl.LockWindowUpdate();
    else
      m_TreeCtrl.SetRedraw(FALSE);

    // get actual position of scroll bar
    int nScPos = m_TreeCtrl.GetScrollPos(SB_VERT);

    std::vector<UINT> vState;
    GetExpMemCfgTree(vState);               // get expanded/collapsed state of nodes

    m_TreeCtrl.DeleteAllItems();            // delete tree
    ShowMemCfgTree();                       // rebuild tree

    SetExpMemCfgTree(vState);               // restore state of nodes

    if (!m_bWin8) m_TreeCtrl.SetRedraw();
    m_TreeCtrl.SetScrollPos(SB_VERT,nScPos,FALSE);

    if (m_bWin8) m_TreeCtrl.UnlockWindowUpdate();
    }
  }

/***********************/
//
// message handler for asynchronous RAM view update
//
LRESULT CRamViewDlg::OnUpdateRamView(WPARAM wParam,LPARAM lParam)
{
  UpdateRamView();
  return 0;
  UNREFERENCED_PARAMETER(wParam);
  UNREFERENCED_PARAMETER(lParam);
}

/***********************/
//
// get text of expanded/all tree nodes
//
HTREEITEM CRamViewDlg::GetTextMemCfgTree(HTREEITEM htiParent,CString& strText,bool bVisibleAll)
  {
  if (htiParent == NULL)                    // no parent
    {
    htiParent = m_TreeCtrl.GetRootItem();   // use root
    }

  do
    {
    TVITEM tvi;
    ZeroMemory(&tvi, sizeof(tvi));
    tvi.mask = TVIF_PARAM | TVIF_STATE;
    tvi.hItem = htiParent;
    m_TreeCtrl.GetItem(&tvi);

    HTREEITEM htiChild = m_TreeCtrl.GetChildItem(htiParent);

    bool bDataline = (tvi.lParam == 0);     // a data line has no item data
    bool bShowText = bVisibleAll || (htiChild && (TVIS_EXPANDED & tvi.state) != 0);
    if (bDataline || bShowText)
      {
      if (bDataline)      // data line
        strText += _T("  ");
      strText += m_TreeCtrl.GetItemText(htiParent) + _T("\n");
      }

    if (htiChild && bShowText)
      {
      htiParent = GetTextMemCfgTree(htiChild, strText, bVisibleAll);
      }
    else
      {
      do
        {
        htiChild = m_TreeCtrl.GetNextSiblingItem(htiParent);
        if (htiChild == NULL)
          {
          htiParent = m_TreeCtrl.GetParentItem(htiParent);
          }
        }
      while (htiParent != NULL && htiChild == NULL);
      htiParent = htiChild;
      }
    }
  while (htiParent != NULL);
  return htiParent;
  }

/****************************/
void CRamViewDlg::CopyRamToCopyClipboard(bool bVisibleAll)
  {
  CString strContent;
  GetTextMemCfgTree(NULL,strContent,bVisibleAll);

  if (OpenClipboard())
    {
    if (EmptyClipboard())
      {
      HANDLE hData=GlobalAlloc(GMEM_MOVEABLE,strContent.GetLength()+1);
      LPSTR lpData=(LPSTR)GlobalLock(hData);
      strcpy(lpData,(LPCTSTR)strContent);
      GlobalUnlock(hData);
      ::SetClipboardData(CF_TEXT,hData);
      }
    CloseClipboard();
    }
  }

/****************************/
void CRamViewDlg::OnRamShowemptyreg()
  {
  fRamShowEmptyReg=!fRamShowEmptyReg;
  UpdateRamView();
  }

/****************************/
void CRamViewDlg::OnRamShowFlags()
  {
  fRamShowFlags=!fRamShowFlags;
  UpdateRamView();
  }

/****************************/
void CRamViewDlg::OnRamVisible()
  {
  CopyRamToCopyClipboard(false);
  }

/****************************/
void CRamViewDlg::OnRamAll()
  {
  CopyRamToCopyClipboard(true);
  }

/****************************/
void CRamViewDlg::OnCancel()
  {
  // disable cancel
  }

/****************************/
void CRamViewDlg::OnOK()
  {
  // disable OK/enter
  }


/****************************/
// CEditConfig
/****************************/
CEditConfig::CEditConfig(CWnd* pParent /*=NULL*/)
  : CDialog(CEditConfig::IDD, pParent)
  {
  //{{AFX_DATA_INIT(CEditConfig)
  //}}AFX_DATA_INIT
  }

/****************************/
void CEditConfig::DoDataExchange(CDataExchange* pDX)
  {
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CEditConfig)
  DDX_Control(pDX, IDC_MODLIST, ctlModList);
  //}}AFX_DATA_MAP
  }

/****************************/
BEGIN_MESSAGE_MAP(CEditConfig, CDialog)
  //{{AFX_MSG_MAP(CEditConfig)
  ON_BN_CLICKED(IDC_ADD, OnAdd)
  ON_BN_CLICKED(IDC_REMOVE, OnRemove)
  ON_BN_CLICKED(IDC_INFO, OnInfo)
  ON_BN_CLICKED(IDC_UP, OnUp)
  ON_BN_CLICKED(IDC_DOWN, OnDown)
  ON_LBN_DBLCLK(IDC_MODLIST, OnDblclkModList)
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/****************************/
BOOL CEditConfig::OnInitDialog()
  {
  CDialog::OnInitDialog();
  posList=NULL;                             // no cursor selected
  LoadListBox();
  return TRUE;
  }

/****************************/
void CEditConfig::LoadListBox()
  {
  // load the mod list into list box
  ctlModList.ResetContent();
  POSITION pos=ModList.GetHeadPosition();
  while (pos!=NULL)
    {
    CString strText;
    const ModuleHeader *pMod=(ModuleHeader *)ModList.GetAt(pos);
    if (pMod->szPartNumber[0]!=0)
      {
      strText+=pMod->szPartNumber;
      strText+=" ";
      }
    strText+=pMod->szTitle;
    switch (pMod->Category)
      {
      case CATEGORY_UNDEF:
      case CATEGORY_APP_PAC:
      case CATEGORY_HPIL_PERPH:
      case CATEGORY_STD_PERPH:
      case CATEGORY_CUSTOM_PERPH:
        break;
      case CATEGORY_OS:
        strText+=" Operating System";
        break;
      case CATEGORY_BETA:
        strText+=" (BETA)";
        break;
      case CATEGORY_EXPERIMENTAL:
        strText+=" (Experimental)";
        break;
      }
    const int npos = ctlModList.AddString(strText);
    if (posList == pos)
      ctlModList.SetCurSel(npos);
    ModList.GetNext(pos);
    }
  // append empty field for insert
  ctlModList.AddString("");
  }

/****************************/
void CEditConfig::OnOK()
  {
  CDialog::OnOK();
  }

/****************************/
void CEditConfig::OnAdd()
  {
  // set directory
  char szPath[_MAX_PATH];
  char drive[_MAX_DRIVE],dir[_MAX_DIR];
  _splitpath(theApp.szAppFullPath,drive,dir,NULL,NULL);
  _makepath(szPath,drive,dir,"Mod","");

  // get a mod file name
  CFileDialog dlgOpen(TRUE,NULL,"*.mod",OFN_HIDEREADONLY|OFN_FILEMUSTEXIST,"Module Files (*.mod)|*.mod||");
  dlgOpen.m_ofn.lpstrInitialDir=szPath;
  if (dlgOpen.DoModal()!=IDOK)
    return;
  // read header info out of module file
  HANDLE hFile=CreateFile(dlgOpen.m_ofn.lpstrFile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
  if (hFile==INVALID_HANDLE_VALUE)
    {
    CString sMsg;
    sMsg.Format("Error Opening File: %s",dlgOpen.m_ofn.lpstrFile);
    AfxMessageBox(sMsg);
    return;
    }
  DWORD SizeRead=0;
  int nFileformat=0;
  ModuleFileHeader *pMFH=(ModuleFileHeader*)malloc(sizeof(ModuleFileHeader));
  if (pMFH)
  {
    ReadFile(hFile,pMFH,sizeof(ModuleFileHeader),&SizeRead,NULL);
    nFileformat=get_file_format(pMFH->FileFormat);
  }
  CloseHandle(hFile);
  if (sizeof(ModuleFileHeader)!=SizeRead || nFileformat==0)
    {
    CString sMsg;
    sMsg.Format("Error Reading File: %s",dlgOpen.m_ofn.lpstrFile);
    AfxMessageBox(sMsg);
    free(pMFH);
    return;
    }
  // copy info to a module header and add to list
  ModuleHeader *pMod=new ModuleHeader;
  strncpy(pMod->szFullFileName,dlgOpen.m_ofn.lpstrFile,sizeof(pMod->szFullFileName));
  pMod->szFullFileName[sizeof(pMod->szFullFileName)-1]=0;
  strncpy(pMod->szTitle,pMFH->Title,sizeof(pMod->szTitle));
  pMod->szTitle[sizeof(pMod->szTitle)-1]=0;
  strncpy(pMod->szVersion,pMFH->Version,sizeof(pMod->szVersion));
  pMod->szVersion[sizeof(pMod->szVersion)-1]=0;
  strncpy(pMod->szPartNumber,pMFH->PartNumber,sizeof(pMod->szPartNumber));
  pMod->szPartNumber[sizeof(pMod->szPartNumber)-1]=0;
  strncpy(pMod->szAuthor,pMFH->Author,sizeof(pMod->szAuthor));
  pMod->szAuthor[sizeof(pMod->szAuthor)-1]=0;
  pMod->Category=pMFH->Category;
  free(pMFH);
  int SelIndex=ctlModList.GetCurSel();
  // no or last empty selection
  if (SelIndex==LB_ERR || SelIndex + 1==ctlModList.GetCount())
    {
    posList = ModList.AddTail(pMod);        // add at end
    }
    else                                    // have selection
    {
    int Index=0;
    POSITION pos=ModList.GetHeadPosition();
    while (pos!=NULL)
      {
      if (Index==SelIndex)
        {
        posList = ModList.InsertBefore(pos,pMod);
        break;
        }
      ModList.GetNext(pos);
      Index++;
      }
    }
  ModList.GetNext(posList);                 // set cursor after insert position
  LoadListBox();
  }

/****************************/
void CEditConfig::OnRemove()
  {
  int Index=0,SelIndex=ctlModList.GetCurSel();
  if (SelIndex==LB_ERR)
    return;
  ModuleHeader *pMod;
  POSITION pos=ModList.GetHeadPosition();
  while (pos!=NULL)
    {
    if (Index==SelIndex)
      {
      posList=pos;                          // get next position
      ModList.GetNext(posList);             // for cursor
      pMod=(ModuleHeader *)ModList.GetAt(pos);
      ModList.RemoveAt(pos);
      delete pMod;
      break;
      }
    ModList.GetNext(pos);
    Index++;
    }
  LoadListBox();
  }

/****************************/
void CEditConfig::OnInfo()
  {
  int Index=0,SelIndex=ctlModList.GetCurSel();
  if (SelIndex==LB_ERR || SelIndex+1==ctlModList.GetCount())
    return;
  // get the file name and build page address struct
  char MODFileName[MAX_PATH];
  ModulePageAddr *pPageAddr=NULL;
  POSITION pos=ModList.GetHeadPosition();
  while (pos!=NULL)
    {
    if (Index==SelIndex)
      {
      const ModuleHeader *pMod=(ModuleHeader *)ModList.GetAt(pos);
      strcpy(MODFileName,pMod->szFullFileName);

      // walk through all pages/bank/alternate pages to get address of selected module
      for (uint page=0;page<=0xf;page++)
        {
        for (uint bank=0;bank<4;bank++)
          {
          // pointer to module page
          const ModulePage *pPage=pHP41->PageMatrix[page][bank];

          // walk through alternate pages
          for (;pPage;pPage=pPage->pAltPage)
            {
            if (_stricmp(pPage->pModule->szFullFileName,MODFileName)==0)
              {
              // found page with MODFile, add info to page address struct
              ModulePageAddr *pData=new ModulePageAddr;
              pData->dwYCrc=YCRC(pPage->Image); // ROM image YCRC ID
              pData->byBank=pPage->Bank;        // bank no. (identical YCRC ID my lay in different banks)
              pData->wAddress=page*0x1000;
              pData->pNext=pPageAddr;
              pPageAddr=pData;
              }
            }
          }
        }
      break;
      }
    ModList.GetNext(pos);
    Index++;
    }
  // open info file
  char MODInfoFileName[MAX_PATH];
  char drive[_MAX_DRIVE],dir[_MAX_DIR],fname[_MAX_FNAME];
  _splitpath(MODFileName,drive,dir,fname,NULL);
  _makepath(MODInfoFileName,drive,dir,fname,"txt");
  FILE *MODInfoFile=fopen(MODInfoFileName,"wt");
  // dump file info out
  fputcUTF8(MODInfoFile,0xFEFF);                // BOM U+FEFF
  int Res=output_mod_info(MODInfoFile,MODFileName,1,1,pPageAddr);
  fclose(MODInfoFile);
  // free page address struct
  while (pPageAddr!=NULL)
  {
    ModulePageAddr *pData=pPageAddr->pNext;
    delete pPageAddr;
    pPageAddr=pData;
  }
  if (Res==0)
    {
    ShellExecute(NULL,NULL,"notepad.exe",MODInfoFileName,NULL,SW_SHOWNORMAL);
    }
  else
    {
    LPCTSTR errMsg;
    if (Res==1)
      errMsg="Open Failure";
    else if (Res==2)
      errMsg="Read Failure";
    else if (Res==3)
      errMsg="Invalid Format";
    else
      errMsg="Memory Allocation";
    AfxMessageBox(errMsg);
    }
  }

/****************************/
void CEditConfig::OnUp()
  {
  ModuleHeader *pMod;
  int Index=0,SelIndex=ctlModList.GetCurSel();
  if (SelIndex==LB_ERR || SelIndex==0)
    return;
  POSITION prev,pos=ModList.GetHeadPosition();
  while (pos!=NULL)
    {
    if (Index==SelIndex)
      {
      pMod=(ModuleHeader *)ModList.GetAt(pos);
      posList = ModList.InsertBefore(prev,pMod);
      ModList.RemoveAt(pos);
      break;
      }
    prev=pos;
    ModList.GetNext(pos);
    Index++;
    }
  LoadListBox();
  }

/****************************/
void CEditConfig::OnDown()
  {
  ModuleHeader *pMod;
  INT_PTR Index=ModList.GetCount()-1;
  int SelIndex=ctlModList.GetCurSel();
  if (SelIndex==LB_ERR || SelIndex==Index)
    return;
  POSITION prev,pos=ModList.GetTailPosition();
  while (pos!=NULL)
    {
    if (Index==SelIndex)
      {
      pMod=(ModuleHeader *)ModList.GetAt(pos);
      posList = ModList.InsertAfter(prev,pMod);
      ModList.RemoveAt(pos);
      break;
      }
    prev=pos;
    ModList.GetPrev(pos);
    Index--;
    }
  LoadListBox();
  }

/****************************/
void CEditConfig::OnDblclkModList()
  {
  OnInfo();
  }

/****************************/
// CBreakPointsDlg
/****************************/

/****************************/
CBreakPointsDlg::CBreakPointsDlg(CWnd* pParent /*=NULL*/)
  : CDialog(CBreakPointsDlg::IDD, pParent)
  {
  //{{AFX_DATA_INIT(CBreakPointsDlg)
  //}}AFX_DATA_INIT
  Top=0;
  Left=0;
  }

/****************************/
void CBreakPointsDlg::DoDataExchange(CDataExchange* pDX)
  {
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CBreakPointsDlg)
  DDX_Control(pDX, IDC_ENABLE, ctlEnable);
  DDX_Control(pDX, IDC_ADDRLIST, ctlAddrList);
  DDX_Control(pDX, IDC_ADDR, ctlAddr);
  //}}AFX_DATA_MAP
  }

/****************************/
BEGIN_MESSAGE_MAP(CBreakPointsDlg, CDialog)
  //{{AFX_MSG_MAP(CBreakPointsDlg)
  ON_BN_CLICKED(IDC_SET, OnSet)
  ON_BN_CLICKED(IDC_CLEAR, OnClear)
  ON_LBN_DBLCLK(IDC_ADDRLIST, OnDblclkAddrList)
  ON_BN_CLICKED(IDC_ENABLE, OnEnable)
  ON_WM_VKEYTOITEM()
  ON_WM_CLOSE()
  ON_WM_SHOWWINDOW()
  ON_WM_DESTROY()
  ON_WM_DRAWITEM()
  ON_WM_MEASUREITEM()
  ON_WM_SETCURSOR()
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/****************************/
BOOL CBreakPointsDlg::OnInitDialog()
  {
  CDialog::OnInitDialog();
  ctlAddr.SetLimitText(30);

  hBmpCheckBox = LoadBitmap(AfxGetResourceHandle(),MAKEINTRESOURCE(IDB_CHECKBOX));
  ASSERT(hBmpCheckBox);
  return TRUE;
  }

/****************************/
void CBreakPointsDlg::OnDestroy()
  {
  WINDOWPLACEMENT wp;
  wp.length = sizeof(wp);
  if (GetWindowPlacement(&wp))
    {
    Left=(uint)wp.rcNormalPosition.left;
    Top=(uint)wp.rcNormalPosition.top;
    }

  DeleteObject(hBmpCheckBox);
  CDialog::OnDestroy();
  }

/****************************/
void CBreakPointsDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
  {
  if (lpDrawItemStruct->itemID != -1)               // no item in list box
    {
    CDC dc;
    dc.Attach(lpDrawItemStruct->hDC);

    // Save these value to restore them when done drawing.
    COLORREF crOldTextColor = dc.GetTextColor();
    COLORREF crOldBkColor = dc.GetBkColor();

    if ((lpDrawItemStruct->itemState & ODS_SELECTED))
      {
      dc.SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
      dc.SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));
      dc.FillSolidRect(&lpDrawItemStruct->rcItem, ::GetSysColor(COLOR_HIGHLIGHT));
      }
    else
      dc.FillSolidRect(&lpDrawItemStruct->rcItem, crOldBkColor);

    CString strText;
    (reinterpret_cast<CListBox *>(FromHandle(lpDrawItemStruct->hwndItem)))->GetText(lpDrawItemStruct->itemID, strText);
    dc.ExtTextOut((int)(lpDrawItemStruct->rcItem.left)+17,(int)(lpDrawItemStruct->rcItem.top),
                  ETO_OPAQUE,(LPRECT)&lpDrawItemStruct->rcItem,strText,NULL);

    // draw checkbox
    CDC dcMem;
    dcMem.CreateCompatibleDC(&dc);
    CBitmap* pOldBitmap = dcMem.SelectObject(CBitmap::FromHandle(hBmpCheckBox));

    dc.BitBlt(lpDrawItemStruct->rcItem.left+2,lpDrawItemStruct->rcItem.top+1,
              11,lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top,
              &dcMem,pHP41->BreakPts[lpDrawItemStruct->itemData].En ? 0 : 10,0,SRCCOPY);

    dcMem.SelectObject(pOldBitmap);
    dc.SetTextColor(crOldTextColor);
    dc.SetBkColor(crOldBkColor);
    dc.Detach();
    }
  }

/****************************/
void CBreakPointsDlg::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
  {
  TEXTMETRIC tm;

  CClientDC dc(this);
  CFont* pFont = GetFont();
  CFont* def_font = dc.SelectObject(pFont);
  dc.GetTextMetrics(&tm);
  dc.SelectObject(def_font);
  lpMeasureItemStruct->itemHeight=tm.tmHeight;
  UNREFERENCED_PARAMETER(nIDCtl);
  }

/****************************/
BOOL CBreakPointsDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
  {
  if (theApp.pMainWnd->getJumpToForeground() && GetActiveWindow()!=this)
    CMainWindow::ForceToForeground(GetLastActivePopup());

  return CDialog::OnSetCursor(pWnd, nHitTest, message);
  }

/****************************/
void CBreakPointsDlg::LoadListBox()
  {
  char szText[100];
  // load the addr list into list box
  LPCSTR lpszFormat=(pHP41->fOctal)?"%07o: %s":"%04X: %s";

  ctlAddrList.ResetContent();
  for (int i=0;i<pHP41->nBreakPts;i++)
    {
    // show disassembly of breakpoint
    pHP41->PC_TRACE=pHP41->BreakPts[i].Addr;
    pHP41->pagebank_trace=pHP41->BreakPts[i].PageBank;
    pHP41->perph_in_control_trace=pHP41->perph_in_control;
    pHP41->perph_selected_trace=pHP41->perph_selected;
    pHP41->ram_selected_trace=pHP41->ram_selected;
    pHP41->trace();
    for (int ich=0;ich<(int)strlen(pHP41->szTraceOut);ich++)
      {
      if (pHP41->szTraceOut[ich]=='\t')
        pHP41->szTraceOut[ich]=' ';
      }
    uint nPos=0;
    // add bank information
    if (pHP41->BreakPts[i].PageBank)
      {
      szText[nPos++]='1'+pHP41->BreakPts[i].PageBank;
      szText[nPos++]='#';
      }
    sprintf(&szText[nPos],lpszFormat,pHP41->BreakPts[i].Addr,pHP41->szTraceOut);
    int nItem = ctlAddrList.AddString(szText);
    ctlAddrList.SetItemData(nItem,i);
    }
  }

/****************************/
void CBreakPointsDlg::OnSet()
  {
  char szText[30];
  flag fSet=FALSE;
  word Addr;

  if (pHP41->nBreakPts>=sizeof(pHP41->BreakPts)/sizeof(pHP41->BreakPts[0]))
    return;
  ctlAddr.GetWindowText(szText,30);
  byte PageBank=0;
  if (*szText)
    {
    const char *szAddr=szText;

    // searching for '#' as bank separator
    char *hash=strchr(szText,'#');
    if (hash)
      {
      // create page string
      CString strPage(szText,static_cast<int>(hash-szText));
      PageBank=static_cast<byte>(atol(strPage)-1);
      if (PageBank>=0 && PageBank<=3)
        szAddr=++hash;                            // valid bank no.
      }

    size_t uTextLength = strlen(szAddr);

    // octal input
    if (szAddr[uTextLength-1] == 'o')
      {
      char *endptr;
      Addr = (word) _tcstoul(szAddr,&endptr,8);
      fSet = *endptr == 'o' && *(endptr+1) == 0;  // reached end of string
      }
    // hexadecimal input
    else
      {
      char *endptr;
      Addr = (word) _tcstoul(szAddr,&endptr,16);
      fSet = *endptr == 0;                        // reached end of string
      }
    // no valid number, try text label
    if (!fSet)
      {
      if (Addr=pHP41->FindGlobalAddr(szText))
        {
        PageBank=0;                               // all text labels are bank 1
        fSet=TRUE;
        }
      }
    }
  else  // no input, use PC
    {
    Addr=pHP41->PC_REG;
    PageBank=pHP41->active_bank[(pHP41->PC_REG&0xf000)>>12]-1;
    fSet=TRUE;
    }

  if (fSet)
    {
    int i,j;
    for (i=0;i<pHP41->nBreakPts;i++)
      {
      // find place to insert
      if (   PageBank<pHP41->BreakPts[i].PageBank
          || (PageBank==pHP41->BreakPts[i].PageBank && Addr<=pHP41->BreakPts[i].Addr))
        break;
      }
    for (j=pHP41->nBreakPts;j>i;j--)    // make hole
      pHP41->BreakPts[j]=pHP41->BreakPts[j-1];
    pHP41->BreakPts[i].En=1;
    pHP41->BreakPts[i].PageBank=PageBank;
    pHP41->BreakPts[i].Addr=Addr;
    ++pHP41->nBreakPts;
    ctlAddr.SetWindowText("");
    }
  LoadListBox();
  }

/****************************/
void CBreakPointsDlg::OnClear()
  {
  int SelIndex,LastIndex=-1;

  // scan all breakpoints from top
  for (SelIndex=pHP41->nBreakPts-1;SelIndex>=0;--SelIndex)
  {
    // item selected
    if (ctlAddrList.GetSel(SelIndex)>0)
    {
      int i,j;

      // get breakpoint index
      LastIndex=SelIndex;
      i=(int)ctlAddrList.GetItemData(SelIndex);
      ctlAddrList.DeleteString(SelIndex);
      --pHP41->nBreakPts;

      // update remaining list box references
      for (j=0;j<pHP41->nBreakPts;++j)
      {
        int k = (int) ctlAddrList.GetItemData(j);
        if (k > i) ctlAddrList.SetItemData(j,k-1);
      }

      // remove breakpoint from breakpoint table
      while (++i<=pHP41->nBreakPts)
        pHP41->BreakPts[i-1]=pHP41->BreakPts[i];
    }
  }
  if (LastIndex==ctlAddrList.GetCount())
    LastIndex=ctlAddrList.GetCount()-1;
  ctlAddrList.SetSel(LastIndex);
  }

/****************************/
void CBreakPointsDlg::ToggleBreakpointItem(INT nItem)
  {
  RECT rc;

  // get breakpoint number
  int i = (int) ctlAddrList.GetItemData(nItem);
  pHP41->BreakPts[i].En = !pHP41->BreakPts[i].En;
  // update region of toggled item
  ctlAddrList.GetItemRect(nItem,&rc);
  ctlAddrList.InvalidateRect(&rc,TRUE);
  }

/****************************/
int CBreakPointsDlg::OnVKeyToItem(UINT nKey, CListBox* pListBox, UINT nIndex)
  {
  if (nKey == VK_SPACE)
    {
    int nItem;

    for (nItem=0;nItem<pHP41->nBreakPts;++nItem)
      {
      // item selected
      if (ctlAddrList.GetSel(nItem)>0)
        {
        ToggleBreakpointItem(nItem);
        }
      }
    return -2;
    }
  else if (nKey == VK_DELETE)
  {
    OnClear();
    return -2;
  }
  return CDialog::OnVKeyToItem(nKey, pListBox, nIndex);
  }

/****************************/
void CBreakPointsDlg::OnDblclkAddrList()
  {
  int nItem;

  if ((nItem = ctlAddrList.GetCurSel())!=LB_ERR)
    {
    ToggleBreakpointItem(nItem);
    }
  }

/****************************/
void CBreakPointsDlg::OnEnable()
  {
  pHP41->fBreakPtsEnable=ctlEnable.GetCheck();
  }

/****************************/
void CBreakPointsDlg::OnClose()
  {
  theApp.pMainWnd->CloseBreakpoints();
  }

/***********************/
void CBreakPointsDlg::OnShowWindow(BOOL bShow, UINT nStatus)
  {
  CDialog::OnShowWindow(bShow,nStatus);
  if (bShow)
    {
    ctlEnable.SetCheck(pHP41->fBreakPtsEnable);
    }
  LoadListBox();
  }

/***********************/
void CBreakPointsDlg::OnCancel()
  {
  // disable cancel
  }

/****************************/
void CBreakPointsDlg::OnOK()
  {
  // disable OK/enter
  }

/////////////////////////////////////////////////////////////////////////////
// CTraceSettings dialog

CTraceSettings::CTraceSettings(CWnd* pParent /*=NULL*/)
  : CDialog(CTraceSettings::IDD, pParent)
  {
  //{{AFX_DATA_INIT(CTraceSettings)
    // NOTE: the ClassWizard will add member initialization here
  //}}AFX_DATA_INIT
  }

void CTraceSettings::DoDataExchange(CDataExchange* pDX)
  {
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CTraceSettings)
    // NOTE: the ClassWizard will add DDX and DDV calls here
  //}}AFX_DATA_MAP
  }

BEGIN_MESSAGE_MAP(CTraceSettings, CDialog)
  //{{AFX_MSG_MAP(CTraceSettings)
  ON_BN_CLICKED(IDC_TRACE_BROWSE, OnTraceBrowse)
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTraceSettings message handlers

BOOL CTraceSettings::OnInitDialog()
  {
  CDialog::OnInitDialog();
  SetDlgItemText(IDC_TRACE_FILE,szTraceFilename);
  CheckDlgButton((eTraceMode == HP41::TRACE_FILE_NEW) ? IDC_TRACE_NEW : IDC_TRACE_APPEND,BST_CHECKED);
  CheckDlgButton(IDC_TRACE_REGISTER_CPU,(fTraceRegister & 0x1) != 0);
  CheckDlgButton(IDC_TRACE_REGISTER_CLK,(fTraceRegister & 0x2) != 0);
  CheckDlgButton(IDC_TRACE_REGISTER_IR, (fTraceRegister & 0x4) != 0);
  CheckDlgButton(IDC_TRACE_OCTAL,fOctal);
  CheckDlgButton(IDC_TRACE_OPCODE,fTraceOpcode);
  CheckDlgButton(IDC_TRACE_HPIL,fTraceHpil);
  return TRUE;
  }

/***********************/
void CTraceSettings::OnTraceBrowse()
  {
  CString strFilename;
  GetDlgItemText(IDC_TRACE_FILE,strFilename);
  CFileDialog dlgOpen(TRUE,"*.log",strFilename,OFN_HIDEREADONLY,"Trace Log Files (*.log)|*log|All Files (*.*)|*.*||");
  if (dlgOpen.DoModal()==IDOK)
    {
    SetDlgItemText(IDC_TRACE_FILE,dlgOpen.m_ofn.lpstrFile);
    }
  }

/***********************/
void CTraceSettings::OnOK()
  {
  // get filename
  GetDlgItemText(IDC_TRACE_FILE,szTraceFilename,sizeof(szTraceFilename)/sizeof(szTraceFilename[0]));

  // trace mode
  eTraceMode=IsDlgButtonChecked(IDC_TRACE_NEW) ? HP41::TRACE_FILE_NEW : HP41::TRACE_FILE_APPEND;

  // trace content
  fTraceRegister&=~0x7;
  fTraceRegister|=((IsDlgButtonChecked(IDC_TRACE_REGISTER_CPU) != 0) << 0);
  fTraceRegister|=((IsDlgButtonChecked(IDC_TRACE_REGISTER_CLK) != 0) << 1);
  fTraceRegister|=((IsDlgButtonChecked(IDC_TRACE_REGISTER_IR)  != 0) << 2);
  fOctal=IsDlgButtonChecked(IDC_TRACE_OCTAL);
  fTraceOpcode=IsDlgButtonChecked(IDC_TRACE_OPCODE);
  fTraceHpil=IsDlgButtonChecked(IDC_TRACE_HPIL);
  CDialog::OnOK();
  }

/***********************/
void CTraceSettings::OnCancel()
  {
  CDialog::OnCancel();
  }

/////////////////////////////////////////////////////////////////////////////
// CRegisterValue dialog

CRegisterValue::CRegisterValue(CWnd* pParent /*=NULL*/)
  : CDialog(CRegisterValue::IDD, pParent)
  {
  //{{AFX_DATA_INIT(CRegisterValue)
    // NOTE: the ClassWizard will add member initialization here
  //}}AFX_DATA_INIT
  }

void CRegisterValue::DoDataExchange(CDataExchange* pDX)
  {
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CRegisterValue)
    // NOTE: the ClassWizard will add DDX and DDV calls here
  //}}AFX_DATA_MAP
  }

BEGIN_MESSAGE_MAP(CRegisterValue, CDialog)
  //{{AFX_MSG_MAP(CRegisterValue)
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CRegisterValue message handlers

/***********************/
BOOL CRegisterValue::OnInitDialog()
  {
  CDialog::OnInitDialog();
  SetDlgItemText(IDC_REGVALUE,m_strValue);
  return TRUE;
  }

/***********************/
void CRegisterValue::OnOK()
  {
  CEdit *pRegVal=reinterpret_cast<CEdit *>(GetDlgItem(IDC_REGVALUE));

  CString strValue;
  pRegVal->GetWindowText(strValue);

  // check if string contains only valid characters
  if (strspn(strValue,m_strBaseFilter) != (size_t) strValue.GetLength())
    {
      pRegVal->SetSel(0,-1);
      pRegVal->SetFocus();
      return;
    }

  m_strValue=strValue;  // copy valid value
  CDialog::OnOK();
  }

/***********************/
void CRegisterValue::getFilter(uint base)
  {
  switch (base)
    {
    case 2:
      m_strBaseFilter="01";
      break;
    case 4:
      m_strBaseFilter="1234";
      break;
    case 14:
      m_strBaseFilter="0123456789ABCDabcd";
      break;
    case 16:
      m_strBaseFilter="0123456789ABCDEFabcdef";
      break;
    case 'S':
      m_strBaseFilter="-0123456789ABCDEFabcdef";
      break;
    default:
      ASSERT(FALSE);
    }
  }

/***********************/
void CRegisterValue::putRegister(byte *reg,uint reglen,uint base)
  {
  const char* cHex = "0123456789ABCDEF";

  m_strValue.Empty();
  for(int i=reglen-1;i>=0;i--)
    {
    if (base=='S')                          // special, set bit marked with position index
      {
        m_strValue+=(reg[i]!=0) ? cHex[i] : '-';
      }
    else
      {
        m_strValue+=cHex[reg[i]&0xF];
      }
    }

  getFilter(base);                          // characters for input validation
  }

/***********************/
void CRegisterValue::getRegister(byte *reg,uint reglen)
  {
  int i,nValuelen;

  LPCTSTR lpszValue=m_strValue;
  nValuelen=m_strValue.GetLength();
  memset(reg,0,reglen);                     // fill target register with zero
  for (i=reglen-1;i>=0;--i)
    {
    if (i<nValuelen)                        // character in string
      {
      byte byData=0;
      if (*lpszValue!='-')                  // have hex value
        {
        // convert to number
        byData=_totupper(*lpszValue)-_T('0');
        if (byData>9) byData-=_T('A')-_T('9')-1;
        }

      if (*m_strBaseFilter=='-')            // special, set bit marked with position index
        {
        if (*lpszValue!='-')                // have bit position
          {
          reg[byData]=1;                    // set bit at position
          }
        }
      else
        {
        reg[i]=byData;                      // save at register position
        }
      ++lpszValue;
      }
    }
  }

/////////////////////////////////////////////////////////////////////////////
// CPageView dialog

CPageView::CPageView(CWnd* pParent /*=NULL*/)
  : CDialog(CPageView::IDD, pParent)
  {
  //{{AFX_DATA_INIT(CPageView)
    // NOTE: the ClassWizard will add member initialization here
  //}}AFX_DATA_INIT
  }

void CPageView::DoDataExchange(CDataExchange* pDX)
  {
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(CPageView)
    // NOTE: the ClassWizard will add DDX and DDV calls here
  //}}AFX_DATA_MAP
  }

BEGIN_MESSAGE_MAP(CPageView, CDialog)
  //{{AFX_MSG_MAP(CPageView)
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPageView message handlers

/***********************/
BOOL CPageView::OnInitDialog()
  {
  CDialog::OnInitDialog();

  CListBox *p=reinterpret_cast<CListBox *>(GetDlgItem(IDC_MOD_PAGELIST));
  p->ResetContent();

  // walk through all pages
  for (int page=0;page<=0xf;++page)
    {
    CString strTitle,strName;

    if (ViewName(page,0,strTitle,strName))
      {
      if (strName.GetLength() < 8)          // a CAT2 name must have at least 8 characters
        {
        strName.Empty();                    // clear name
        }

      CString strModule;
      strModule.Format("%02d: %-11s [%s]",page,strName,strTitle);
      p->AddString(strModule);
      }
    }
  return TRUE;
  }

/****************************/
void CPageView::OnOK()
  {
  // disable OK/enter
  }

/****************************/
void CPageView::OnCancel()
  {
  CDialog::OnCancel();
  }

/****************************/
bool CPageView::ViewName(int nPage, int nFcn, CString& strTitle, CString& strName) const
  {
  bool bSucc;

  strTitle.Empty();                         // module title
  strName.Empty();                          // function name

  // pointer to module page with active bank
  const ModulePage *pMFP=pHP41->PageMatrix[nPage][pHP41->active_bank[nPage]-1];

  if ((bSucc = (pMFP != NULL)))             // page used
    {
    // decode module title string
    if (*pMFP->pModule->szPartNumber != 0)
      {
      strTitle=CString(pMFP->pModule->szPartNumber)+" ";
      }
    strTitle+=pMFP->pModule->szTitle;

    // page has FAT and function no. valid
    if (has_fat(pMFP->Image) && nFcn<pMFP->Image[1])
      {
      const word addr=(nFcn+1)*2;           // address of fncs in table

      // address of string in module
      const word jmp_addr=((pMFP->Image[addr]&0x0ff)<<8)|(pMFP->Image[addr+1]&0x0ff);

      // not USER CODE, not on terminator and inside 4K MCODE
      if ((pMFP->Image[addr] & 0x200)==0 && jmp_addr!=0 && jmp_addr<0x1000)
        {
        // decode name string
        char ch,punct;
        int end;
        int addr=jmp_addr;
        int end_addr=jmp_addr-11;           // name max 11 characters long
        if (end_addr < 0) end_addr=0;
        do
          {
          unsigned short wch;
          decode_fatchar(pMFP->Image[--addr],&ch,&wch,&punct,&end);
          strName+=ch;
          }
        while (!end && addr>end_addr);
        }
      }
    }
  return bSucc;
  }
