// PSFWorkArea.cpp : implementation file
//

#include "stdafx.h"
#include "PSFLab.h"
#include "PSFWorkArea.h"

#include "../Emu/emu.h"

#include "CodeView.h"
#include "MemoryView.h"
#include "EventView.h"

#include "ImportBinaryDlg.h"
#include "BreakpointsDlg.h"
#include "ExeSettingsDlg.h"
#include "RunToSpecificLineDlg.h"
#include "StateSlotDlg.h"
#include "TagDlg.h"
#include "OptimizeDlg.h"

#include "WaveOutput.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define R3000CLASS(x) (R3000_REG_##x)

static const int anRegisterHWID[PSFWORKAREA_REG_N] = {
  R3000CLASS(GEN)+ 0, R3000CLASS(GEN)+ 1, R3000CLASS(GEN)+ 2, R3000CLASS(GEN)+ 3,
  R3000CLASS(GEN)+ 4, R3000CLASS(GEN)+ 5, R3000CLASS(GEN)+ 6, R3000CLASS(GEN)+ 7,
  R3000CLASS(GEN)+ 8, R3000CLASS(GEN)+ 9, R3000CLASS(GEN)+10, R3000CLASS(GEN)+11,
  R3000CLASS(GEN)+12, R3000CLASS(GEN)+13, R3000CLASS(GEN)+14, R3000CLASS(GEN)+15,
  R3000CLASS(GEN)+16, R3000CLASS(GEN)+17, R3000CLASS(GEN)+18, R3000CLASS(GEN)+19,
  R3000CLASS(GEN)+20, R3000CLASS(GEN)+21, R3000CLASS(GEN)+22, R3000CLASS(GEN)+23,
  R3000CLASS(GEN)+24, R3000CLASS(GEN)+25, R3000CLASS(GEN)+26, R3000CLASS(GEN)+27,
  R3000CLASS(GEN)+28, R3000CLASS(GEN)+29, R3000CLASS(GEN)+30, R3000CLASS(GEN)+31,

  R3000CLASS(C0) + 0, R3000CLASS(C0) + 1, R3000CLASS(C0) + 2, R3000CLASS(C0) + 3,
  R3000CLASS(C0) + 4, R3000CLASS(C0) + 5, R3000CLASS(C0) + 6, R3000CLASS(C0) + 7,
  R3000CLASS(C0) + 8, R3000CLASS(C0) + 9, R3000CLASS(C0) +10, R3000CLASS(C0) +11,
  R3000CLASS(C0) +12, R3000CLASS(C0) +13, R3000CLASS(C0) +14, R3000CLASS(C0) +15,
  R3000CLASS(C0) +16, R3000CLASS(C0) +17, R3000CLASS(C0) +18, R3000CLASS(C0) +19,
  R3000CLASS(C0) +20, R3000CLASS(C0) +21, R3000CLASS(C0) +22, R3000CLASS(C0) +23,
  R3000CLASS(C0) +24, R3000CLASS(C0) +25, R3000CLASS(C0) +26, R3000CLASS(C0) +27,
  R3000CLASS(C0) +28, R3000CLASS(C0) +29, R3000CLASS(C0) +30, R3000CLASS(C0) +31,

  R3000CLASS(PC),
  R3000CLASS(HI),
  R3000CLASS(LO),

  R3000CLASS(CI)
};

extern "C" void CPSFWorkArea__console_out(
  void *context,
  char c
) {
  ((CPSFWorkArea*)context)->console.AddChar(c);
//  char asdf[2];
//  asdf[0] = c;
//  asdf[1] = 0;
//  OutputDebugString(asdf);
}

extern "C" sint32 CPSFWorkArea__readfile_cb(
  void *context,
  const char *path,
  sint32 offset,
  char *buffer,
  sint32 length
) {
  int r;
  char fool[1000];
//return -1;

//sprintf(fool,"[readfile: %s(%d) %d]",path,offset,length);OutputDebugString(fool);


  if(length < 0) return -1;
  if(offset < 0) return -1;
  strncpy(fool, 
    (LPCTSTR)( ((CPSFLabApp*)(AfxGetApp()))->m_strPSF2Path ),

//"C:/Corlett",
//((CPSFWorkArea*)context)->m_sPSF2Path,

    sizeof(fool)
  );
  fool[sizeof(fool)-1] = 0;
  int l =strlen(fool);
  while(l>0) {
    char c=fool[l-1];
    if(c=='/'||c=='\\'||c==':'){
      fool[--l]=0;
      continue;
    }
    break;
  }
  l = sizeof(fool)-strlen(fool);
  strncpy(fool+strlen(fool),path,l);
  fool[sizeof(fool)-1] = 0;


  FILE *f = fopen(fool, "rb");
  if(!f) return -1;
  if(!length) { int l; fseek(f,0,SEEK_END);l=ftell(f);fclose(f); return l; }
  fseek(f,offset,SEEK_SET);
  r = fread(buffer, 1, length, f);
  fclose(f);



//sprintf(fool,"(returned %d)",r);OutputDebugString(fool);OutputDebugString("\n");



  return r;
}

/////////////////////////////////////////////////////////////////////////////
// CPSFWorkArea

IMPLEMENT_DYNCREATE(CPSFWorkArea, CDocument)

CPSFWorkArea::CPSFWorkArea()
{
  /*
  ** Initialize state pointers
  */
  m_pStateDebug = NULL;
  m_pStatePlay = NULL;
  m_nVersion = 1;

  /*
  ** Initialize thread information
  */
  m_pDebugThread = NULL;
  m_pPlayThread = NULL;
  m_bRunning = FALSE;
  m_bPlaying = FALSE;

  /*
  ** Initialize saved state system
  */
  memset(m_apSavedStates, 0, sizeof(m_apSavedStates));
  m_nCurrentStateSlot = 0;

  /*
  ** Allocate debug state - this is always here
  */
  m_pStateDebug = malloc(emu_get_state_size(m_nVersion));
  ASSERT(m_pStateDebug);
  /*
  ** Initialize the state just to ensure no problems
  */
  emu_clear_state(m_pStateDebug, m_nVersion);
  emu_set_readfile(m_pStateDebug, CPSFWorkArea__readfile_cb, this);
  emu_set_console_out(m_pStateDebug, CPSFWorkArea__console_out, this);


//  strcpy(m_sPSF2Path,
 // (((CPSFLabApp*)(AfxGetApp()))->m_sPSF2Path)
 // );
}

BOOL CPSFWorkArea::OnNewDocument()
{
	TRACE("CPSFWorkArea::OnNewDocument()\n");
  if(IsModified()) {
    TRACE("Creating a new document over an existing modified one\n");
//    MessageBox(NULL,"trying to do a file new on a modified thingy\n",NULL,MB_OK);
//    return FALSE;
  }

  DeleteContents();

  int nDesiredVersion = ((CPSFLabApp*)(::AfxGetApp()))->m_nNewPSFVersion;
  if(m_nVersion != nDesiredVersion) {
    m_nVersion = nDesiredVersion;
    if(m_pStateDebug) {
      free(m_pStateDebug);
      m_pStateDebug = NULL;
    }
    m_pStateDebug = malloc(emu_get_state_size(m_nVersion));
    emu_clear_state(m_pStateDebug, m_nVersion);
    emu_set_readfile(m_pStateDebug, CPSFWorkArea__readfile_cb, this);
    emu_set_console_out(m_pStateDebug, CPSFWorkArea__console_out, this);

//  strcpy(m_sPSF2Path,
//  (((CPSFLabApp*)(AfxGetApp()))->m_sPSF2Path)
//  );

    DeleteContents();
  }

  m_strPathName.Empty();
  SetModifiedFlag(FALSE);

  /*
  ** Restart debugger
  */
  DebugRestart();
  RegisterMonitor();

  UpdateAllViews(NULL);

	return TRUE;
}

CPSFWorkArea::~CPSFWorkArea()
{
  KillDebugThreadAndBlock();
  KillPlayThreadAndBlock();

  ClearAllSavedStates();

  if(m_pStateDebug) free(m_pStateDebug);
  if(m_pStatePlay) free(m_pStatePlay);
}


BEGIN_MESSAGE_MAP(CPSFWorkArea, CDocument)
	//{{AFX_MSG_MAP(CPSFWorkArea)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_GO, OnUpdateDebugGo)
	ON_COMMAND(ID_DEBUG_GO, OnDebugGo)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_BREAK, OnUpdateDebugBreak)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_PLAY, OnUpdateDebugPlay)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_STOP, OnUpdateDebugStop)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_PAUSE, OnUpdateDebugPause)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_STEPINTO, OnUpdateDebugStepinto)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_STEPOUT, OnUpdateDebugStepout)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_STEPOVER, OnUpdateDebugStepover)
	ON_COMMAND(ID_DEBUG_PLAY, OnDebugPlay)
	ON_COMMAND(ID_DEBUG_PAUSE, OnDebugPause)
	ON_COMMAND(ID_DEBUG_STOP, OnDebugStop)
	ON_COMMAND(ID_DEBUG_BREAK, OnDebugBreak)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_RESTART, OnUpdateDebugRestart)
	ON_COMMAND(ID_DEBUG_RESTART, OnDebugRestart)
	ON_COMMAND(ID_DEBUG_STEPINTO, OnDebugStepinto)
	ON_COMMAND(ID_DEBUG_STEPOUT, OnDebugStepout)
	ON_COMMAND(ID_DEBUG_STEPOVER, OnDebugStepover)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_LOADSTATE, OnUpdateDebugLoadstate)
	ON_COMMAND(ID_DEBUG_LOADSTATE, OnDebugLoadstate)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_SAVESTATE, OnUpdateDebugSavestate)
	ON_COMMAND(ID_DEBUG_SAVESTATE, OnDebugSavestate)
	ON_COMMAND(ID_FILE_IMPORTBINARY, OnFileImportbinary)
	ON_UPDATE_COMMAND_UI(ID_FILE_IMPORTBINARY, OnUpdateFileImportbinary)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_RUNTOINT, OnUpdateDebugRuntoint)
	ON_UPDATE_COMMAND_UI(ID_EDIT_BREAKPOINTS, OnUpdateEditBreakpoints)
	ON_COMMAND(ID_EDIT_BREAKPOINTS, OnEditBreakpoints)
	ON_UPDATE_COMMAND_UI(ID_EDIT_EXESETTINGS, OnUpdateEditExesettings)
	ON_COMMAND(ID_EDIT_EXESETTINGS, OnEditExesettings)
	ON_COMMAND(ID_DEBUG_EXECUTIONSTOPPED, OnDebugExecutionstopped)
	ON_UPDATE_COMMAND_UI(ID_DEBUG_SPECIFICLINE, OnUpdateDebugSpecificline)
	ON_COMMAND(ID_DEBUG_SPECIFICLINE, OnDebugSpecificline)
	ON_COMMAND(ID_DEBUG_RUNTOINT, OnDebugRuntoint)
	ON_COMMAND(ID_DEBUG_SELECTSLOT, OnDebugSelectslot)
	ON_COMMAND(ID_EDIT_TAG, OnEditTag)
	ON_COMMAND(ID_FILE_SAVEAS, OnFileSaveas)
	ON_COMMAND(ID_FILE_IMPORTEXE, OnFileImportexe)
	ON_UPDATE_COMMAND_UI(ID_FILE_IMPORTEXE, OnUpdateFileImportexe)
	ON_COMMAND(ID_FILE_EXPORTEXE, OnFileExportexe)
	ON_COMMAND(ID_FILE_FONDUE, OnFileFondue)
	ON_UPDATE_COMMAND_UI(ID_EDIT_OPTIMIZE, OnUpdateEditOptimize)
	ON_COMMAND(ID_EDIT_OPTIMIZE, OnEditOptimize)
	ON_COMMAND(ID_FILE_EXPORTBIOSAREA, OnFileExportbiosarea)
	ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
	ON_UPDATE_COMMAND_UI(ID_FILE_SAVEAS, OnUpdateFileSaveas)
	ON_UPDATE_COMMAND_UI(ID_EDIT_TAG, OnUpdateEditTag)
	ON_UPDATE_COMMAND_UI(ID_FILE_EXPORTEXE, OnUpdateFileExportexe)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CPSFWorkArea diagnostics

#ifdef _DEBUG
void CPSFWorkArea::AssertValid() const
{
	CDocument::AssertValid();
}

void CPSFWorkArea::Dump(CDumpContext& dc) const
{
	CDocument::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CPSFWorkArea serialization

void CPSFWorkArea::Serialize(CArchive& ar)
{
	if (ar.IsStoring())
	{
		// TODO: add storing code here
	}
	else
	{
		// TODO: add loading code here
	}
}

/////////////////////////////////////////////////////////////////////////////
// CPSFWorkArea commands

DWORD CPSFWorkArea::PeekWord(DWORD dwAddress) const
{
  ASSERT(m_pStateDebug);
  return iop_getword(emu_get_iop_state(m_pStateDebug), dwAddress);
}

BOOL CPSFWorkArea::IsWordPatched(DWORD dwAddress) const
{
  int nEXEIndex = AddressToEXEIndex(dwAddress);
  if(nEXEIndex < 0) return FALSE;
  return ((m_adwPatchMap[nEXEIndex / 32] >> (nEXEIndex & 31)) & 1);
}

DWORD CPSFWorkArea::GetPatchWord(DWORD dwAddress) const
{
  int nEXEIndex = AddressToEXEIndex(dwAddress);
  if(nEXEIndex < 0) return 0;
  return m_adwPatchData[nEXEIndex];
}

void CPSFWorkArea::SetPatchWord(DWORD dwAddress, DWORD dwValue)
{
  if(m_nVersion == 1) SetModifiedFlag();

  /*
  ** Copy the change to the current debug state too
  */
  if(m_pStateDebug) {
    iop_setword(emu_get_iop_state(m_pStateDebug), dwAddress, dwValue);
  }

  if(m_nVersion == 1) {
    int nEXEIndex = AddressToEXEIndex(dwAddress);
    if(nEXEIndex >= 0) {
      m_adwPatchMap[nEXEIndex / 32] |= (1 << (nEXEIndex & 31));
      m_adwPatchData[nEXEIndex] = dwValue;
    }
  }
}

int CPSFWorkArea::AddressToEXEIndex(DWORD dwAddress) const
{
  dwAddress &= 0x1FFFFFFC;
  if(dwAddress >= 0x1F000000) return -1;
  dwAddress &= 0x1FFFFC;
  if(dwAddress < 0x10000) return -1;
  return (dwAddress - 0x10000) / 4;
}

DWORD CPSFWorkArea::GetPC() const
{
  ASSERT(m_pStateDebug);
  return r3000_getreg(iop_get_r3000_state(emu_get_iop_state(m_pStateDebug)), R3000CLASS(PC));
}

void CPSFWorkArea::DeletePatchWord(DWORD dwAddress)
{
  int nWordIndex = AddressToEXEIndex(dwAddress);
  if(nWordIndex < 0) return;
  // If it wasn't patched to begin with, don't do anything
  if(!(m_adwPatchMap[nWordIndex / 32] & (1 << (nWordIndex & 31)))) return;
  m_adwPatchMap[nWordIndex / 32] &= ~(1 << (nWordIndex & 31));
  /*
  ** Copy the change to the current debug state too
  */
  ASSERT(m_pStateDebug);
  iop_setword(emu_get_iop_state(m_pStateDebug), dwAddress, m_adwEXEData[nWordIndex]);
  SetModifiedFlag();
}

int CPSFWorkArea::AddressToBreakpointIndex(DWORD dwAddress) const
{
  dwAddress &= 0x1FFFFFFC;
  if(dwAddress <  0x1F000000) return ((dwAddress & 0x1FFFFC) / 4);
  if(dwAddress >= 0x1FC00000) return ((dwAddress &  0x7FFFC) / 4) + 0x80000;
  return -1;
}

BOOL CPSFWorkArea::IsBreakpointOnExecute(DWORD dwAddress) const
{
  int nWordIndex = AddressToBreakpointIndex(dwAddress);
  if(nWordIndex < 0) return FALSE;
  return (m_adwBreakpointMapExecute[nWordIndex / 32] >> (nWordIndex & 31)) & 1;
}

void CPSFWorkArea::SetBreakpointOnExecute(DWORD dwAddress, BOOL bBreak)
{
  int nWordIndex = AddressToBreakpointIndex(dwAddress);
  if(nWordIndex < 0) return;
  if(bBreak) {
    m_adwBreakpointMapExecute[nWordIndex / 32] |=  (1 << (nWordIndex & 31));
  } else {
    m_adwBreakpointMapExecute[nWordIndex / 32] &= ~(1 << (nWordIndex & 31));
  }
}

BOOL CPSFWorkArea::IsBreakpointOnRead(DWORD dwAddress) const
{
  int nWordIndex = AddressToBreakpointIndex(dwAddress);
  if(nWordIndex < 0) return FALSE;
  return (m_adwBreakpointMapRead[nWordIndex / 32] >> (nWordIndex & 31)) & 1;
}

void CPSFWorkArea::SetBreakpointOnRead(DWORD dwAddress, BOOL bBreak)
{
  int nWordIndex = AddressToBreakpointIndex(dwAddress);
  if(nWordIndex < 0) return;
  if(bBreak) {
    m_adwBreakpointMapRead[nWordIndex / 32] |=  (1 << (nWordIndex & 31));
  } else {
    m_adwBreakpointMapRead[nWordIndex / 32] &= ~(1 << (nWordIndex & 31));
  }
}

BOOL CPSFWorkArea::IsBreakpointOnWrite(DWORD dwAddress) const
{
  int nWordIndex = AddressToBreakpointIndex(dwAddress);
  if(nWordIndex < 0) return FALSE;
  return (m_adwBreakpointMapWrite[nWordIndex / 32] >> (nWordIndex & 31)) & 1;
}

void CPSFWorkArea::SetBreakpointOnWrite(DWORD dwAddress, BOOL bBreak)
{
  int nWordIndex = AddressToBreakpointIndex(dwAddress);
  if(nWordIndex < 0) return;
  if(bBreak) {
    m_adwBreakpointMapWrite[nWordIndex / 32] |=  (1 << (nWordIndex & 31));
  } else {
    m_adwBreakpointMapWrite[nWordIndex / 32] &= ~(1 << (nWordIndex & 31));
  }
}

CEventLog* CPSFWorkArea::GetEventLog()
{
  return &m_eventlog;
}

void CPSFWorkArea::OnUpdateDebugGo     (CCmdUI* pCmdUI) { pCmdUI->Enable(!IsRunning()); }
void CPSFWorkArea::OnUpdateDebugRestart(CCmdUI* pCmdUI) { pCmdUI->Enable(!IsRunning()); }
void CPSFWorkArea::OnUpdateDebugBreak  (CCmdUI* pCmdUI) { pCmdUI->Enable( IsRunning()); }

void CPSFWorkArea::OnUpdateDebugStepinto   (CCmdUI* pCmdUI) { pCmdUI->Enable(!IsRunning()); }
void CPSFWorkArea::OnUpdateDebugStepout    (CCmdUI* pCmdUI) { pCmdUI->Enable(!IsRunning()); }
void CPSFWorkArea::OnUpdateDebugStepover   (CCmdUI* pCmdUI) { pCmdUI->Enable(!IsRunning()); }

void CPSFWorkArea::OnUpdateDebugPlay (CCmdUI* pCmdUI) { pCmdUI->Enable(!IsPlaying()); }
void CPSFWorkArea::OnUpdateDebugPause(CCmdUI* pCmdUI) { pCmdUI->Enable( IsPlaying()); }
void CPSFWorkArea::OnUpdateDebugStop (CCmdUI* pCmdUI) { pCmdUI->Enable( IsPlaying()); }

void CPSFWorkArea::OnDebugGo() 
{
  KillDebugThreadAndBlock();
  m_nDebugCommand = ID_DEBUG_GO;
  StartDebugThreadAndBlock();
  UpdateAllViews(NULL);
}


void CPSFWorkArea::OnDebugPlay() {
  PlayStart();
}

void CPSFWorkArea::OnDebugPause() {
  if(IsPlayPaused()) {
    PlayUnpause();
  } else {
    PlayPause();
  }
}

void CPSFWorkArea::OnDebugStop() 
{
  PlayStop();
}

void CPSFWorkArea::OnDebugBreak() 
{
  KillDebugThreadAndBlock();
  OnDebugExecutionstopped();

//FILE*f=fopen("statedump","wb");
//if(f){
//fwrite(m_pStateDebug,1,emu_get_state_size(2),f);
//fclose(f);
//}

}

void CPSFWorkArea::OnDebugRestart() 
{
	KillDebugThreadAndBlock();
  DebugRestart();
  UpdateAllViews(NULL);
}

void CPSFWorkArea::OnDebugStepinto() 
{
  KillDebugThreadAndBlock();
  m_nDebugCommand = ID_DEBUG_STEPINTO;
  StartDebugThreadAndBlock();
  UpdateAllViews(NULL);
}

void CPSFWorkArea::OnDebugStepout() 
{
  KillDebugThreadAndBlock();
  m_nDebugCommand = ID_DEBUG_STEPOUT;
  StartDebugThreadAndBlock();
  UpdateAllViews(NULL);
}

void CPSFWorkArea::OnDebugStepover() 
{
  KillDebugThreadAndBlock();
  m_nDebugCommand = ID_DEBUG_STEPOVER;
  StartDebugThreadAndBlock();
  UpdateAllViews(NULL);
}

void CPSFWorkArea::OnUpdateDebugLoadstate(CCmdUI* pCmdUI) 
{
  ASSERT(m_nCurrentStateSlot >= 0 && m_nCurrentStateSlot <= 9);
  if(IsRunning()) { pCmdUI->Enable(FALSE); return; }
  if(m_apSavedStates[m_nCurrentStateSlot]) {
    pCmdUI->Enable(TRUE);
  } else {
    pCmdUI->Enable(FALSE);
  }
}

void CPSFWorkArea::OnDebugLoadstate() 
{
  KillDebugThreadAndBlock();
  ASSERT(m_nCurrentStateSlot >= 0 && m_nCurrentStateSlot <= 9);
  LoadDebugState(m_nCurrentStateSlot);

  CodeJumpTo(r3000_getreg(iop_get_r3000_state(emu_get_iop_state(m_pStateDebug)), R3000CLASS(PC)));

  UpdateAllViews(FALSE);
}

void CPSFWorkArea::OnUpdateDebugSavestate(CCmdUI* pCmdUI) { pCmdUI->Enable(!IsRunning()); }

void CPSFWorkArea::OnDebugSavestate() 
{
  KillDebugThreadAndBlock();
  ASSERT(m_nCurrentStateSlot >= 0 && m_nCurrentStateSlot <= 9);
  SaveDebugState(m_nCurrentStateSlot);
}

void CPSFWorkArea::PlayStop()
{
  KillPlayThreadAndBlock();
  /*
  ** Free memory used by play state
  */
  if(m_pStatePlay) {
    free(m_pStatePlay);
    m_pStatePlay = NULL;
  }
}

void CPSFWorkArea::DebugRestart()
{
  ASSERT(m_pStateDebug);
  /*
  ** Initialize the state
  */
  emu_clear_state(m_pStateDebug, m_nVersion);
  emu_set_readfile(m_pStateDebug, CPSFWorkArea__readfile_cb, this);
  emu_set_console_out(m_pStateDebug, CPSFWorkArea__console_out, this);

//  strcpy(m_sPSF2Path,
//  (((CPSFLabApp*)(AfxGetApp()))->m_sPSF2Path)
//  );

  if(m_nVersion == 1) {
    /*
    ** Load EXE data and apply patches
    */
    LoadEXEToState(m_pStateDebug);
    ApplyPatchesToState(m_pStateDebug);
  }

  /*
  ** Update the code view with the place where we are
  */
//  CodeJumpTo(m_dwEXEStartPC);
  CodeJumpTo(GetPC());

  /*
  ** Clear the event log and call stack
  */
  m_eventlog.Clear();
  m_callstack.Clear();
  /*
  ** Reset the register change monitor
  */
  RegisterMonitorReset();
}

CView* CPSFWorkArea::FindViewOfClass(CRuntimeClass *pClass)
{
  POSITION p = GetFirstViewPosition();
  CView *v;
  for(;;) {
    v = GetNextView(p);
    if(!v) break;
    if(v->IsKindOf(pClass)) break;
  }
  ASSERT(v);
  return v;
}

DWORD CPSFWorkArea::GetRegister(int nRegisterIndex)
{
  ASSERT(m_pStateDebug);
  ASSERT(nRegisterIndex >= 0 && nRegisterIndex < PSFWORKAREA_REG_N);
  return r3000_getreg(iop_get_r3000_state(emu_get_iop_state(m_pStateDebug)), anRegisterHWID[nRegisterIndex]);
}

void CPSFWorkArea::RegisterMonitor()
{
  for(int i = 0; i < PSFWORKAREA_REG_N; i++) {
    if(m_anRegisterChangeTimeout[i]) m_anRegisterChangeTimeout[i]--;
    DWORD r = GetRegister(i);
    if(r != m_adwRegisterPrevious[i]) {
      m_adwRegisterPrevious[i] = r;
      m_anRegisterChangeTimeout[i] = PSFWORKAREA_REGISTER_CHANGE_TIMEOUT;
    }
  }
}

void CPSFWorkArea::RegisterMonitorReset()
{
  for(int i = 0; i < PSFWORKAREA_REG_N; i++) {
    m_adwRegisterPrevious[i] = GetRegister(i);
    m_anRegisterChangeTimeout[i] = 0;
  }
}

BOOL CPSFWorkArea::RegisterChangedRecently(int nRegisterIndex) const
{
  ASSERT(nRegisterIndex >= 0 && nRegisterIndex < PSFWORKAREA_REG_N);
  return m_anRegisterChangeTimeout[nRegisterIndex] ? TRUE : FALSE;
}

CCallStack* CPSFWorkArea::GetCallStack()
{
  return &m_callstack;
}

BOOL CPSFWorkArea::OnOpenDocument(LPCTSTR lpszPathName) 
{
	TRACE("CPSFWorkArea::OnOpenDocument(%s)\n", lpszPathName);
  if(IsModified()) {
    TRACE("Opening a new document over an existing modified one\n");
  }

  // can only open psf
  SetVersion(1);

  TRACE("set version to %d\n", m_nVersion);

//  LPBYTE lpEXE = new BYTE[0x200000];

  BOOL b;

  CPSF psf;
  CPSXEXE exe;

  /*
  ** Load the EXE into an EXE buffer which we have temporarily allocated
  ** (if there's an error here, it's recoverable)
  */
  b = psf.ReadFromFile(lpszPathName, CPSF::PSF_FILE_ALL, TRUE);
  if(!b) {
    CString s;
    s.Format(_T("%s\n%s"), lpszPathName, (LPCTSTR)psf.GetLastError());
    AfxGetMainWnd()->MessageBox(s, _T("Open"), MB_OK|MB_ICONHAND);
    return FALSE;
  }
  b = exe.ReadFromPSF(psf, TRUE);
  if(!b) {
    CString s;
    s.Format(_T("%s\n%s"), lpszPathName, (LPCTSTR)exe.GetLastError());
    AfxGetMainWnd()->MessageBox(s, _T("Open"), MB_OK|MB_ICONHAND);
    return FALSE;
  }

  /*
  ** Clear the document contents
  */
  DeleteContents();
  SetPathName(lpszPathName);
  SetModifiedFlag(FALSE);

  /*
  ** "Import" the EXE
  */
  ImportEXEFromBuffer(
    exe.GetEXEBuffer(),
    exe.GetEXESize()
  );

  /*
  ** Silly reminder to myself that this hasn't been unicode-proofed yet
  */
  ASSERT(sizeof(TCHAR)==1);

  /*
  ** Attempt to load the tag
  */
  m_tag.ReadFromPSF(psf, FALSE);

  /*
  ** Restart debugger
  */
  DebugRestart();
  RegisterMonitor();

  UpdateAllViews(NULL);

  TRACE("got here, version is %d\n",m_nVersion);

	return TRUE;
}

BOOL CPSFWorkArea::OnSaveDocument(LPCTSTR lpszPathName) 
{
	TRACE("CPSFWorkArea::OnSaveDocument(%s)\n", lpszPathName);

  CWaitCursor cwc;

  /*
  ** Silly reminder to myself that this hasn't been unicode-proofed yet
  */
  ASSERT(sizeof(TCHAR)==1);

  /*
  ** Create the EXE data
  */
  CByteArray aEXE;
  aEXE.SetSize(0x200000);
  DWORD dwEXELength = ExportEXEToBuffer(aEXE.GetData());
  aEXE.SetSize(dwEXELength);

  /*
  ** Save the EXE data and tag to a PSF file
  */

  CPSF psf;

  psf.SetVersion(0x01);
  psf.SetReservedSize(0);
  UINT uMethod = ((CPSFLabApp*)(::AfxGetApp()))->GetCompressionMethodNumber();
  BOOL b;
  b = psf.SetProgramData(aEXE.GetData(), aEXE.GetSize(), uMethod);
  if(!b) {
    CString s;
    s.Format(_T("%s\n%s"), lpszPathName, (LPCTSTR)psf.GetLastError());
    AfxGetMainWnd()->MessageBox(s, _T("Save"), MB_OK|MB_ICONHAND);
    return FALSE;
  }
  m_tag.WriteToPSF(psf, FALSE);

  b = psf.WriteToFile(lpszPathName, CPSF::PSF_FILE_ALL, TRUE);
  if(!b) {
    CString s;
    s.Format(_T("%s\n%s"), lpszPathName, (LPCTSTR)psf.GetLastError());
    AfxGetMainWnd()->MessageBox(s, _T("Save"), MB_OK|MB_ICONHAND);
    return FALSE;
  }

  /*
  ** Success
  ** Document is no longer modified
  ** (and may have a new pathname now)
  */
  SetPathName(lpszPathName);
  SetModifiedFlag(FALSE);

  UpdateAllViews(NULL);
  return TRUE;
}

void CPSFWorkArea::ClearWorkArea()
{
  /*
  ** Clear original EXE data
  */
  memset(m_adwEXEData, 0, sizeof(m_adwEXEData));
  m_dwEXEStartAddress = 0x80010000;
  m_dwEXEByteLength = 0;
  m_dwEXEStartPC = 0x80010000;
  m_dwEXEStartSP = 0x801FFFF0;
  memset(m_EXEHeader, 0, 0x800);
  memcpy(m_EXEHeader, "PS-X EXE", 8);
  memcpy(m_EXEHeader+0x4C, "Sony Computer Entertainment Inc. for North America area", 0x37);

  /*
  ** Clear patch/breakpoint info
  */
  memset(m_adwPatchMap, 0, sizeof(m_adwPatchMap));
  memset(m_adwBreakpointMapRead, 0, sizeof(m_adwBreakpointMapRead));
  memset(m_adwBreakpointMapWrite, 0, sizeof(m_adwBreakpointMapWrite));
  memset(m_adwBreakpointMapExecute, 0, sizeof(m_adwBreakpointMapExecute));

  // clear register change monitoring
  memset(m_adwRegisterPrevious, 0, sizeof(m_adwRegisterPrevious));
  memset(m_anRegisterChangeTimeout, 0, sizeof(m_anRegisterChangeTimeout));

  // clear console
  console.Clear();
}

/***************************************************************************/
/*
** Debugging thread / debug core
*/
UINT AFX_CDECL CPSFWorkArea__DebugThread(LPVOID lpParam) {
  CPSFWorkArea *me = (CPSFWorkArea*)lpParam;
  ASSERT(me);
  /*
  ** Signal thread start
  */
  me->m_csDebug.Lock();
  me->m_evDebug.SetEvent();
  /*
  ** Initial preparations
  */
  ASSERT(me->m_pStateDebug);
  me->m_bDebugBreakpointReadFlag = FALSE;
  me->m_bDebugBreakpointWriteFlag = FALSE;
  me->m_bDebugBreakpointExecuteFlag = FALSE;
  me->m_bDebugErrorFlag = FALSE;

  me->m_bDebugBreakOnWeed = TRUE;
  me->m_bDebugWeedFlag = FALSE;

  /*
  ** Initial call stack caching (size, top PC/SP)
  */
  int nInitialCallStackSize = me->m_callstack.GetSize();
  int nCallStackSize = nInitialCallStackSize;
  /*
  ** Clear the hardware event buffer
  */
  iop_clear_events(emu_get_iop_state(me->m_pStateDebug));
  /*
  ** Debug core loop
  */
  while(!me->m_dwDebugKillFlag) {
    DWORD dwIns;
    #define INS_S ((DWORD)((dwIns>>21)&0x1F))
    #define INS_T ((DWORD)((dwIns>>16)&0x1F))
    #define INS_D ((DWORD)((dwIns>>11)&0x1F))
    #define INS_H ((DWORD)((dwIns>>6 )&0x1F))
    #define INS_I ((DWORD)((dwIns    )&0xFFFF))
    #define   SIGNED16(x) ((INT32)(((SHORT)(x))))
    #define UNSIGNED16(x) (((UINT32)(x))&0xFFFF)
    #define REL_I(pc) ((pc)+4+((SIGNED16(INS_I))<<2))
    #define ABS_I(pc) (((pc)&0xF0000000)|((dwIns<<2)&0x0FFFFFFC))

    /*
    ** Prepare to execute the next instruction
    */
    DWORD dwBeforePC = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), R3000CLASS(PC));
    /*
    ** Check for possible jump/call instructions and compute the target.
    **
    ** This way we'll know whether there was a call to add to the call stack
    ** and be able to differentiate between jumps and exceptions.
    **
    ** "The program counter changed - was this intentional?"
    ** We can know this ahead of time.
    */
    BOOL bPossibleJump = FALSE;
    BOOL bPossibleCall = FALSE;
    DWORD dwPossibleJumpTarget = 0;
    dwIns = iop_getword(emu_get_iop_state(me->m_pStateDebug), dwBeforePC);
    if(dwIns < 0x04000000) {
      /* MINOR */
      switch(dwIns & 0x3F) {
      case 0x09: /* jalr  */
        if(INS_D == 31) bPossibleCall = TRUE;
        /* INTENTIONAL FALL THROUGH */
      case 0x08: /* jr    */
        bPossibleJump = TRUE;
        dwPossibleJumpTarget = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), R3000CLASS(GEN)+INS_S);
        break;
      }
    } else {
      /* MAJOR */
      switch(dwIns >> 26) {
      case 0x01: /* various branch types */
        switch(INS_T) {
        case 0x10: case 0x11: bPossibleCall = TRUE;
        }
        /* INTENTIONAL FALL THROUGH */
      case 0x04: /* beq   */
      case 0x05: /* bne   */
      case 0x06: /* blez  */
      case 0x07: /* bgtz  */
        bPossibleJump = TRUE;
        dwPossibleJumpTarget = REL_I(dwBeforePC);
        break;
      case 0x03: /* jal   */
        bPossibleCall = TRUE;
        /* INTENTIONAL FALL THROUGH */
      case 0x02: /* j     */
        bPossibleJump = TRUE;
        dwPossibleJumpTarget = ABS_I(dwBeforePC);
        break;
      }
    }

    /*
    ** Execute the next instruction
    */
    signed short samplebuffer[2*100];
    unsigned dwSoundSamples = 100;
    int nExecuteError = emu_execute(me->m_pStateDebug, 1, samplebuffer, &dwSoundSamples, me->m_dwDebugEventMask);
    /*
    ** Handle unrecoverable errors here
    ** Set the error flag and terminate directly
    */
    if(nExecuteError < 0) {
      me->m_bDebugErrorFlag = TRUE;
      AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_DEBUG_EXECUTIONSTOPPED, 0);
      break;
    }

    /*
    ** Update the event log
    */
    int c = iop_get_event_count(emu_get_iop_state(me->m_pStateDebug));
    for(; c > 0; c--) {
      TCHAR s[500];
      UINT64 time;
      char *fmt = "format not set(%08X,%08X,%08X,%08X)";
      UINT32 type;
      UINT32 arg[4];
      iop_get_event(emu_get_iop_state(me->m_pStateDebug), &time, &type, &fmt, arg);
      iop_dismiss_event(emu_get_iop_state(me->m_pStateDebug));
      int i;
      TCHAR *ps = s + 19;
      for(i = 19; i >= 0; i--) {
        int digit = (int)(time % 10);
        time /= 10;
        s[i] = _T("0123456789")[digit];
        if(digit) ps = s + i;
      }
      s[20] = _T(':');
      s[21] = _T(' ');
      s[22] = 0;
      wsprintf(s + lstrlen(s), _T("(pc=0x%08X) "), dwBeforePC);
      wsprintf(s + lstrlen(s), fmt,
        (UINT32)(arg[0]),
        (UINT32)(arg[1]),
        (UINT32)(arg[2]),
        (UINT32)(arg[3])
      );

      me->m_eventlog.Add(ps);
    }

    /*
    ** Perform post-analysis on the instruction just executed
    */
    DWORD dwPC = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), R3000CLASS(PC));
    DWORD dwSP = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), R3000CLASS(GEN)+29);
    BOOL bException = FALSE;
    if(dwPC != (dwBeforePC + 4)) {
      /*
      ** Check for return
      */
      if(nCallStackSize) {
        me->m_callstack.CheckForReturn(dwPC, dwSP);
        nCallStackSize = me->m_callstack.GetSize();
      }
      /*
      ** Do some voodoo to see if an exception happened
      */
      if(bPossibleJump && dwPC == dwPossibleJumpTarget) {
        bException = FALSE;
      } else {
        bException = TRUE;
      }
      /*
      ** Handle calls or exceptions
      */
      if(bPossibleCall || bException) {
        SCallStackEntry e;
        e.bException = bException;
        e.dwArg[0] = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), R3000CLASS(GEN)+4);
        e.dwArg[1] = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), R3000CLASS(GEN)+5);
        e.dwArg[2] = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), R3000CLASS(GEN)+6);
        e.dwArg[3] = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), R3000CLASS(GEN)+7);
        e.dwCause = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), R3000CLASS(C0)+13);
        e.dwInvokedAddress = dwPC;
        e.dwReturnPC = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), bException ? (R3000CLASS(C0)+14) : (R3000CLASS(GEN)+31));
        e.dwReturnSP = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), R3000CLASS(GEN)+29);
        me->m_callstack.Add(e);
        nCallStackSize++;
      }
    }

    /*
    ** Check for terminating conditions
    */
    BOOL bTerminate = FALSE;
    /*
    ** Code breakpoints
    */
    if(me->IsBreakpointOnExecute(dwPC)) {
      me->m_bDebugBreakpointExecuteFlag = TRUE;
      me->m_dwDebugBreakpointExecuteAddress = dwPC;
      bTerminate = TRUE;
    }

    /* Invalid PC */
    if(me->m_bDebugBreakOnWeed) {
      BOOL inv = FALSE;
      switch((dwPC >> 29) & 7) {
      case 1: case 2: case 3: case 6: case 7: inv = TRUE;
      }
      DWORD t = dwPC & 0x1FFFFFFF;
      if(t >= 0x00800000 && t < 0x1F000000) inv = TRUE;
      if(t >= 0x1F000000 && t < 0x1FC00000) inv = TRUE;
      if(inv) {
        me->m_bDebugWeedFlag = TRUE;
        me->m_dwDebugWeedAddress = dwPC;
        bTerminate = TRUE;
      }
    }

    /*
    ** Memory breakpoints
    ** Terminate if there's a triggering load or store instruction
    */
    dwIns = iop_getword(emu_get_iop_state(me->m_pStateDebug), dwPC);
    DWORD dwOperandAddress;
    switch(dwIns >> 26) {
    case 0x20: /* lb    */
    case 0x21: /* lh    */
    case 0x23: /* lw    */
    case 0x24: /* lbu   */
    case 0x25: /* lhu   */
      dwOperandAddress = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), R3000CLASS(GEN)+INS_S) + SIGNED16(INS_I);
      dwOperandAddress &= 0xFFFFFFFC;
      if(me->IsBreakpointOnRead(dwOperandAddress)) {
        me->m_bDebugBreakpointReadFlag = TRUE;
        me->m_dwDebugBreakpointReadAddress = dwOperandAddress;
        bTerminate = TRUE;
      }
      break;
    case 0x28: /* sb    */
    case 0x29: /* sh    */
    case 0x2B: /* sw    */
      dwOperandAddress = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), R3000CLASS(GEN)+INS_S) + SIGNED16(INS_I);
      dwOperandAddress &= 0xFFFFFFFC;
      if(me->IsBreakpointOnWrite(dwOperandAddress)) {
        me->m_bDebugBreakpointWriteFlag = TRUE;
        me->m_dwDebugBreakpointWriteAddress = dwOperandAddress;
        bTerminate = TRUE;
      }
      break;
    }

    /*
    ** Command-specific terminating conditions
    */
    switch(me->m_nDebugCommand) {
    /*
    ** Go: Never terminate
    */
    case ID_DEBUG_GO:
      break;
    /*
    ** Step Into: Always terminate
    */
    case ID_DEBUG_STEPINTO:
      bTerminate = TRUE;
      break;
    /*
    ** Step Over: Terminate if the call stack level is the same as before
    */
    case ID_DEBUG_STEPOVER:
      if(nCallStackSize == nInitialCallStackSize) bTerminate = TRUE;
      break;
    /*
    ** Step Out: Terminate if the call stack level is less than before
    */
    case ID_DEBUG_STEPOUT:
      if(nCallStackSize < nInitialCallStackSize) bTerminate = TRUE;
      break;
    /*
    ** Run to Cursor / Run to Specific Address: Terminate when that address is reached
    */
    case ID_DEBUG_RUNTOCURSOR:
    case ID_DEBUG_SPECIFICLINE:
      if((dwPC & 0x1FFFFFFC) == (me->m_dwDebugRunTarget & 0x1FFFFFFC)) bTerminate = TRUE;
      break;
    /*
    ** Run to Interrupt: Terminate if an interrupt just happened
    */
    case ID_DEBUG_RUNTOINT:
      if(bException) {
        DWORD dwCause = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(me->m_pStateDebug)), R3000CLASS(C0)+13);
        /*
        ** Examine Cause bits
        ** 0 means hardware interrupt
        */
        if(((dwCause >> 2) & 0xF) == 0x0) bTerminate = TRUE;
      }
      break;
    /*
    ** Unknown command: Just terminate
    */
    default:
      bTerminate = TRUE;
      break;
    }

    /*
    ** Handle terminating conditions, if they exist
    */
    if(bTerminate) {
      AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_DEBUG_EXECUTIONSTOPPED, 0);
      break;
    }

  }

  /*
  ** Signal thread end; return
  */
  me->m_pDebugThread = NULL;
  me->m_csDebug.Unlock();
  return 0;
}

/***************************************************************************/
/*
** Play thread must never die until specifically told to
** (via either m_dwPlayKillFlag or a WM_APP+1 message)
*/
UINT AFX_CDECL CPSFWorkArea__PlayThread(LPVOID lpParam) {
  CPSFWorkArea *me = (CPSFWorkArea*)lpParam;
  me->m_csPlay.Lock();
  me->m_evPlay.SetEvent();

  DWORD samplebuffer[8*4096];
  CWaveOutput waveoutput;
  waveoutput.Open(samplebuffer, 8, 4096,
    (me->m_nVersion == 1) ? 44100 : 48000
  );

//  OutputDebugString("PlayThread started\n");

//  void *pFilter = filter_open();

  int q = 0;
  while(!me->m_dwPlayKillFlag) {
    MSG msg;
    ::GetMessage(&msg, NULL, WM_APP, WM_APP+2);
    if(msg.message == WM_APP+2) {
//TRACE("pause %d\n",msg.wParam);
      waveoutput.Pause(msg.wParam);
      continue;
    }

    if(msg.message == WM_APP+1) break;
    if(msg.message != WM_APP) continue;

    short *pBuf = (short*)(msg.lParam);
    int nSamples = 4096;

    int r = 0;
    while(nSamples > 0) {
      int n = nSamples;
      r = emu_execute(me->m_pStatePlay, 0x7FFFFFFF, pBuf, (unsigned*)(&n), 0);
      /*
      ** On error, simply play silence
      */
      if(r < 0) {
        memset(pBuf, 0, sizeof(short)*2*nSamples);
        n = nSamples;
      } else {
        /*
        ** Shove it through the filter
        */
//        n = filter_process(pFilter, pBuf, n);
      }
//for(r=0;r<2*n;r++)pBuf[r]=((q++)%168)*10;
      pBuf += 2 * n;
      nSamples -= n;
    }
    waveoutput.BufferReady(msg.wParam);
  }

//  filter_close(pFilter);

  waveoutput.Close();

  me->m_pPlayThread = NULL;
  me->m_csPlay.Unlock();
  return 0;
}

/***************************************************************************/

void CPSFWorkArea::DeleteContents()
{
  KillDebugThreadAndBlock();
  KillPlayThreadAndBlock();
  ClearWorkArea();
  ClearAllSavedStates();
  m_tag.Empty();
}

void CPSFWorkArea::OnCloseDocument() 
{
	// TODO: Add your specialized code here and/or call the base class
	
	CDocument::OnCloseDocument();
}

void CPSFWorkArea::OnFileImportbinary() 
{
  CImportBinaryDlg dlgImportBinary;

  if(dlgImportBinary.DoModal() != IDOK) return;

  CString strPathName = dlgImportBinary.m_strPathName;
  DWORD dwOffset = dlgImportBinary.m_dwOffset;
  DWORD dwLength = dlgImportBinary.m_dwLength;
  DWORD dwDestinationAddress = dlgImportBinary.m_dwDestinationAddress;

//  dwOffset &= 0xFFFFFFFC;
//  dwLength &= 0xFFFFFFFC;
//  dwDestinationAddress &= 0xFFFFFFFC;

  if(!dwLength) return;
  if(strPathName.IsEmpty()) return;

  CString strCaption;
  strCaption.LoadString(IDS_IMPORT_BINARY);

  FILE *f = fopen((LPCSTR)strPathName, "rb");
  if(!f) {
    CString s;
    s.Format(_T("Unable to open '%s'."), (LPCTSTR)strPathName);
    AfxGetMainWnd()->MessageBox(s, (LPCTSTR)strCaption, MB_OK|MB_ICONHAND);
  }

  LPBYTE lpBuffer = new BYTE[dwLength];
  memset(lpBuffer, 0, dwLength);
  fseek(f, dwOffset, SEEK_SET);
  fread(lpBuffer, 1, dwLength, f);
  fclose(f);

  ImportBinaryFromBuffer(lpBuffer, dwDestinationAddress, dwLength);

  delete[] lpBuffer;

  SetModifiedFlag();
  UpdateAllViews(NULL);
}

void CPSFWorkArea::OnUpdateFileImportbinary(CCmdUI* pCmdUI) {
  pCmdUI->Enable(
    (GetVersion() == 1) && (!IsRunning())
  );
}

void CPSFWorkArea::OnUpdateDebugRuntoint(CCmdUI* pCmdUI) { pCmdUI->Enable(!IsRunning()); }
void CPSFWorkArea::OnUpdateEditBreakpoints(CCmdUI* pCmdUI) { pCmdUI->Enable(!IsRunning()); }

void CPSFWorkArea::OnEditBreakpoints() 
{
  CBreakpointsDlg dlgBreakpoints;

  dlgBreakpoints.m_pWorkArea = this;

  /*
  ** Quick run through the breakpoint map to add all breakpoint addresses
  ** This makes the dialog's job a lot easier
  */
  for(int i = 0; i < 0x5000; i++) {
    DWORD dwMap =
      m_adwBreakpointMapExecute[i] |
      m_adwBreakpointMapRead[i] |
      m_adwBreakpointMapWrite[i];
    if(dwMap) {
      DWORD dwBase = BreakpointIndexToAddress(32 * i);
      for(int j = 0; j < 32; j++) {
        if(dwMap & (1 << j)) dlgBreakpoints.m_adwBreakpoints.Add(dwBase + 4 * j);
      }
    }
  }

  /*
  ** Dialog will handle everything from here
  */
  dlgBreakpoints.DoModal();

}

void CPSFWorkArea::OnUpdateEditExesettings(CCmdUI* pCmdUI) {
  BOOL b = FALSE;
  if((m_nVersion == 1) && (!IsRunning())) b = TRUE;
  pCmdUI->Enable(b);
}

void CPSFWorkArea::OnEditExesettings() 
{
  CExeSettingsDlg dlgExeSettings;

  dlgExeSettings.m_dwPC = m_dwEXEStartPC;
  dlgExeSettings.m_dwSP = m_dwEXEStartSP;

  dlgExeSettings.m_dwTextStart = m_dwEXEStartAddress;
  dlgExeSettings.m_dwTextSize  = m_dwEXEByteLength;

  if(dlgExeSettings.DoModal() != IDOK) return;

  m_dwEXEStartPC      = dlgExeSettings.m_dwPC;
  m_dwEXEStartSP      = dlgExeSettings.m_dwSP;
  m_dwEXEStartAddress = dlgExeSettings.m_dwTextStart;
  m_dwEXEByteLength   = dlgExeSettings.m_dwTextSize;

}

void CPSFWorkArea::PlayStart()
{
  /*
  ** Kill old thread, if there was one
  */
  KillPlayThreadAndBlock();
  /*
  ** Allocate a play state if it doesn't already exist
  */
  if(!m_pStatePlay) {
    m_pStatePlay = malloc(emu_get_state_size(m_nVersion));
  }
  ASSERT(m_pStatePlay);
  emu_clear_state(m_pStatePlay, m_nVersion);
  emu_set_readfile(m_pStatePlay, CPSFWorkArea__readfile_cb, this);

//  strcpy(m_sPSF2Path,
//  (((CPSFLabApp*)(AfxGetApp()))->m_sPSF2Path)
//  );

  //emu_set_console_out(m_pStatePlay, CPSFWorkArea__console_out, this);
  //spu_enable_reverb(iop_get_spu_state(emu_get_iop_state(m_pStatePlay)), 0);
  /*
  ** Set emulation core settings
  */
  SPSFEmuSettings es;
  theApp.GetEmuSettings(PSF_EMUSETTINGS_PLAY, es);
  iop_set_compat(emu_get_iop_state(m_pStatePlay), es.bFriendly ? IOP_COMPAT_FRIENDLY : IOP_COMPAT_HARSH);
  /*
  ** Load the patched EXE into this play state
  */
  if(m_nVersion == 1) {
    LoadEXEToState(m_pStatePlay);
    ApplyPatchesToState(m_pStatePlay);
  }
  /*
  ** Begin unpaused!
  */
  m_bPlayPaused = FALSE;
  /*
  ** Begin new thread
  */
  StartPlayThreadAndBlock();
}

void CPSFWorkArea::PlayPause() {
  if(m_bPlayPaused) return;
  if(m_pPlayThread) m_pPlayThread->PostThreadMessage(WM_APP+2, TRUE, 0);
  m_bPlayPaused = TRUE;
}

void CPSFWorkArea::PlayUnpause() {
  if(!m_bPlayPaused) return;
  if(m_pPlayThread) m_pPlayThread->PostThreadMessage(WM_APP+2, FALSE, 0);
  m_bPlayPaused = FALSE;
}

void CPSFWorkArea::LoadEXEToState(void *pState)
{
  ASSERT(pState);
  /*
  ** Copy EXE data
  */
  for(DWORD i = 0; i < 0x7C000; i++) {
    iop_setword(emu_get_iop_state(pState), 0x80010000+4*i, m_adwEXEData[i]);
  }
  /*
  ** Set the initial PC/SP
  */
  r3000_setreg(iop_get_r3000_state(emu_get_iop_state(pState)), R3000CLASS(PC)    , m_dwEXEStartPC);
  r3000_setreg(iop_get_r3000_state(emu_get_iop_state(pState)), R3000CLASS(GEN)+29, m_dwEXEStartSP);
}

void CPSFWorkArea::ApplyPatchesToState(void *pState)
{
  ASSERT(pState);
  DWORD dwAddress;
  for(dwAddress = 0x80010000; dwAddress < 0x80200000; dwAddress += 4) {
    if(IsWordPatched(dwAddress)) iop_setword(emu_get_iop_state(pState), dwAddress, GetPatchWord(dwAddress));
  }
}

/***************************************************************************/

void CPSFWorkArea::KillDebugThreadAndBlock() {
  m_dwDebugKillFlag = 1;
  m_csDebug.Lock(); m_csDebug.Unlock();
  m_bRunning = FALSE;
}

void CPSFWorkArea::KillPlayThreadAndBlock() {
  if(m_pPlayThread) m_pPlayThread->PostThreadMessage(WM_APP+1,0,0);
  m_dwPlayKillFlag = 1;
  m_csPlay.Lock(); m_csPlay.Unlock();
  m_bPlaying = FALSE;
}

void CPSFWorkArea::StartDebugThreadAndBlock() {
  KillDebugThreadAndBlock();
  m_evDebug.ResetEvent();
  m_dwDebugKillFlag = 0;
  m_bRunning = TRUE;
  /*
  ** Must get app-global options here... can't really get them in the
  ** debug thread itself
  */
  /*
  ** Event mask
  */
  m_dwDebugEventMask = theApp.GetEventMask();
  /*
  ** Emulation core settings
  */
  SPSFEmuSettings es;
  theApp.GetEmuSettings(PSF_EMUSETTINGS_DEBUG, es);
  iop_set_compat(emu_get_iop_state(m_pStateDebug), es.bFriendly ? IOP_COMPAT_FRIENDLY : IOP_COMPAT_HARSH);
  /*
  ** Now begin the thread
  */
  m_pDebugThread = AfxBeginThread(CPSFWorkArea__DebugThread, (LPVOID)this);
  m_evDebug.Lock();
}

void CPSFWorkArea::StartPlayThreadAndBlock() {
  KillPlayThreadAndBlock();
  m_evPlay.ResetEvent();
  m_dwPlayKillFlag = 0;
  m_bPlaying = TRUE;
  m_pPlayThread = AfxBeginThread(CPSFWorkArea__PlayThread, (LPVOID)this);
  m_evPlay.Lock();
}

/***************************************************************************/
/*
** This command is sent INTERNALLY when execution has stopped in the debug
** thread.
*/
void CPSFWorkArea::OnDebugExecutionstopped() 
{
  KillDebugThreadAndBlock();
  /*
  ** Update views depending on what exactly happened
  */
  DWORD dwPC = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(m_pStateDebug)), R3000CLASS(PC));
  CodeJumpTo(dwPC);
  if(m_bDebugBreakpointReadFlag ) MemoryJumpTo(m_dwDebugBreakpointReadAddress );
  if(m_bDebugBreakpointWriteFlag) MemoryJumpTo(m_dwDebugBreakpointWriteAddress);
  CEventView *ev = (CEventView *)FindViewOfClass(RUNTIME_CLASS(CEventView ));
  if(ev) { ev->JumpToBottom(); }
  RegisterMonitor();
  UpdateAllViews(NULL);

  /*
  ** Message box depending on what happened
  */
  CString str, strMessage;
  CString strCaption;
  strCaption.LoadString(IDS_BREAKPOINT);
  UINT nType = MB_OK;
  if(m_bDebugErrorFlag) {
    str.Format(IDS_UNRECOVERABLE_ADDRESS, dwPC);
    strMessage += str;
    strCaption.LoadString(IDS_ERROR);
    nType = MB_OK|MB_ICONHAND;
  }
  if(m_bDebugBreakpointExecuteFlag) {
    str.Format(IDS_BREAKPOINT_EXECUTE_ADDRESS, dwPC);
    strMessage += str;
  }
  if(m_bDebugBreakpointReadFlag) {
    str.Format(IDS_BREAKPOINT_READ_ADDRESS, m_dwDebugBreakpointReadAddress);
    strMessage += str;
  }
  if(m_bDebugBreakpointWriteFlag) {
    str.Format(IDS_BREAKPOINT_WRITE_ADDRESS, m_dwDebugBreakpointWriteAddress);
    strMessage += str;
  }
  if(m_bDebugWeedFlag) {
    str.Format(IDS_WEED_ADDRESS, m_dwDebugWeedAddress);
    strMessage += str;
  }

  if(!strMessage.IsEmpty()) {
    AfxGetMainWnd()->MessageBox(strMessage, (LPCTSTR)strCaption, nType);
  }

  // commands will re-enable themselves automatically when this returns
}

void CPSFWorkArea::RunToAddress(DWORD dwAddress)
{
  KillDebugThreadAndBlock();
  m_nDebugCommand = ID_DEBUG_SPECIFICLINE;
  m_dwDebugRunTarget = dwAddress;
  StartDebugThreadAndBlock();
  UpdateAllViews(NULL);
}

void CPSFWorkArea::OnUpdateDebugSpecificline(CCmdUI* pCmdUI) {
  pCmdUI->Enable(!IsRunning());
}

void CPSFWorkArea::OnDebugSpecificline() {
  CRunToSpecificLineDlg dlgRunToSpecificLine;
  ASSERT(m_pStateDebug);
  dlgRunToSpecificLine.m_dwAddress = r3000_getreg(iop_get_r3000_state(emu_get_iop_state(m_pStateDebug)), R3000CLASS(PC));
  if(dlgRunToSpecificLine.DoModal() != IDOK) return;
  RunToAddress(dlgRunToSpecificLine.m_dwAddress);
}

void CPSFWorkArea::OnDebugRuntoint() 
{
  KillDebugThreadAndBlock();
  m_nDebugCommand = ID_DEBUG_RUNTOINT;
  StartDebugThreadAndBlock();
  UpdateAllViews(NULL);
}

void CPSFWorkArea::ClearAllSavedStates()
{
  int i;
  for(i = 0; i < 10; i++) {
    if(m_apSavedStates[i]) {
      free(m_apSavedStates[i]);
      m_apSavedStates[i] = 0;
    }
  }
  m_nCurrentStateSlot = 0;
}

void CPSFWorkArea::SaveDebugState(int nSlot)
{
  ASSERT(m_pStateDebug);
  ASSERT(nSlot >= 0 && nSlot <= 9);
  int nStateSize = emu_get_state_size(m_nVersion);
  if(!m_apSavedStates[nSlot]) {
    m_apSavedStates[nSlot] = malloc(nStateSize);
  }

  memcpy(m_apSavedStates[nSlot], m_pStateDebug, nStateSize);
  m_aSavedCallStacks[nSlot].CopyFrom(m_callstack);
  m_aSavedEventLogs[nSlot].CopyFrom(m_eventlog);

}

void CPSFWorkArea::LoadDebugState(int nSlot)
{
  ASSERT(m_pStateDebug);
  ASSERT(nSlot >= 0 && nSlot <= 9);
  if(!m_apSavedStates[nSlot]) return;
  int nStateSize = emu_get_state_size(m_nVersion);

  memcpy(m_pStateDebug, m_apSavedStates[nSlot], nStateSize);
  m_callstack.CopyFrom(m_aSavedCallStacks[nSlot]);
  m_eventlog.CopyFrom(m_aSavedEventLogs[nSlot]);
}

void CPSFWorkArea::OnDebugSelectslot() 
{
  CStateSlotDlg dlgStateSlot;

  dlgStateSlot.m_nSlot = m_nCurrentStateSlot;

  CString strInUse;
  CString strNotInUse;
  strInUse.LoadString(IDS_INUSE);
  strNotInUse.LoadString(IDS_NOTINUSE);

  dlgStateSlot.m_strInUse1 = m_apSavedStates[0] ? ((LPCTSTR)strInUse) : ((LPCTSTR)strNotInUse);
  dlgStateSlot.m_strInUse2 = m_apSavedStates[1] ? ((LPCTSTR)strInUse) : ((LPCTSTR)strNotInUse);
  dlgStateSlot.m_strInUse3 = m_apSavedStates[2] ? ((LPCTSTR)strInUse) : ((LPCTSTR)strNotInUse);
  dlgStateSlot.m_strInUse4 = m_apSavedStates[3] ? ((LPCTSTR)strInUse) : ((LPCTSTR)strNotInUse);
  dlgStateSlot.m_strInUse5 = m_apSavedStates[4] ? ((LPCTSTR)strInUse) : ((LPCTSTR)strNotInUse);
  dlgStateSlot.m_strInUse6 = m_apSavedStates[5] ? ((LPCTSTR)strInUse) : ((LPCTSTR)strNotInUse);
  dlgStateSlot.m_strInUse7 = m_apSavedStates[6] ? ((LPCTSTR)strInUse) : ((LPCTSTR)strNotInUse);
  dlgStateSlot.m_strInUse8 = m_apSavedStates[7] ? ((LPCTSTR)strInUse) : ((LPCTSTR)strNotInUse);
  dlgStateSlot.m_strInUse9 = m_apSavedStates[8] ? ((LPCTSTR)strInUse) : ((LPCTSTR)strNotInUse);
  dlgStateSlot.m_strInUse0 = m_apSavedStates[9] ? ((LPCTSTR)strInUse) : ((LPCTSTR)strNotInUse);

  if(dlgStateSlot.DoModal() != IDOK) return;

  m_nCurrentStateSlot = dlgStateSlot.m_nSlot;
}

DWORD CPSFWorkArea::BreakpointIndexToAddress(int nIndex)
{
  if(nIndex < 0) return 0;
  if(nIndex >= 0xA0000) return 0;
  if(nIndex < 0x80000) return 0x80000000 + 4 * nIndex;
  return 0xBFC00000 + 4 * (nIndex - 0x80000);
}

void CPSFWorkArea::CodeJumpTo(DWORD dwAddress)
{
  CCodeView *cv = (CCodeView*)FindViewOfClass(RUNTIME_CLASS(CCodeView));
  if(cv) {
    cv->JumpTo(dwAddress);
    cv->SetFocus();
  }
}

void CPSFWorkArea::MemoryJumpTo(DWORD dwAddress)
{
  CMemoryView *mv = (CMemoryView*)FindViewOfClass(RUNTIME_CLASS(CMemoryView));
  if(mv) {
    mv->JumpTo(dwAddress);
    mv->SetFocus();
  }
}

void CPSFWorkArea::OnEditTag() 
{
  CTagDlg dlgTag;
  CString strTag = m_tag.GetRaw();
  dlgTag.m_strTag = strTag;
  if(dlgTag.DoModal() != IDOK) return;
  if(!lstrcmp((LPCTSTR)strTag, (LPCTSTR)dlgTag.m_strTag)) return;
  m_tag.SetRaw(dlgTag.m_strTag);
  SetModifiedFlag();
}

void CPSFWorkArea::ImportEXEFromBuffer(LPBYTE lpBuffer, DWORD dwLength)
{
  if(dwLength < 0x800) return;
  memcpy(m_EXEHeader, lpBuffer, 0x800);
  m_dwEXEStartPC      = *((DWORD*)(lpBuffer+0x10));
  m_dwEXEStartSP      = *((DWORD*)(lpBuffer+0x30));
  m_dwEXEStartAddress = *((DWORD*)(lpBuffer+0x18));
  m_dwEXEByteLength   = *((DWORD*)(lpBuffer+0x1C));
  if(m_dwEXEByteLength > (dwLength-0x800)) m_dwEXEByteLength = (dwLength-0x800);
  ImportBinaryFromBuffer(lpBuffer+0x800, m_dwEXEStartAddress, m_dwEXEByteLength);
}

/*
** TODO
** Hi there!
** Fix me so I can load unaligned data!
*/
void CPSFWorkArea::ImportBinaryFromBuffer(LPBYTE lpBuffer, DWORD dwAddress, DWORD dwLength)
{
  dwLength &= 0xFFFFFFFC;
  for(DWORD i = 0; i < dwLength; i += 4) {
    DWORD d = *((DWORD*)(lpBuffer+i));
    DWORD a = (dwAddress + i) & 0xFFFFFFFC;
    DeletePatchWord(a);
    int nEXEIndex = AddressToEXEIndex(a);
    if(nEXEIndex >= 0) m_adwEXEData[nEXEIndex] = d;
    iop_setword(emu_get_iop_state(m_pStateDebug), a, d);
  }
}

/*
** TODO
** Hi there!
** Fix me so I can export unaligned data!
*/
void CPSFWorkArea::ExportBinaryToBuffer(LPBYTE lpBuffer, DWORD dwAddress, DWORD dwLength)
{
  dwLength &= 0xFFFFFFFC;
  for(DWORD i = 0; i < dwLength; i += 4) {
    DWORD a = (dwAddress & 0xFFFFFFFC) + i;
    DWORD d = 0;
    int nEXEIndex = AddressToEXEIndex(a);
    if(nEXEIndex >= 0) d = m_adwEXEData[nEXEIndex];
    if(IsWordPatched(a)) d = GetPatchWord(a);
    *((DWORD*)(lpBuffer+i)) = d;
  }
}

/*
** Buffer must have 0x200000 bytes available
*/
DWORD CPSFWorkArea::ExportEXEToBuffer(LPBYTE lpBuffer)
{
  DWORD dwLen = m_dwEXEByteLength;
  if(dwLen > 0x1F0000) dwLen = 0x1F0000;
  /*
  ** Write header, hooray
  */
  *((DWORD*)(m_EXEHeader+0x10)) = m_dwEXEStartPC;
  *((DWORD*)(m_EXEHeader+0x30)) = m_dwEXEStartSP;
  *((DWORD*)(m_EXEHeader+0x18)) = m_dwEXEStartAddress;
  *((DWORD*)(m_EXEHeader+0x1C)) = m_dwEXEByteLength;
  memcpy(lpBuffer, m_EXEHeader, 0x800);
  /*
  ** Export the text section
  */
  ExportBinaryToBuffer(lpBuffer + 0x800, m_dwEXEStartAddress, m_dwEXEByteLength);
  /*
  ** Return the total length
  */
  return (0x800 + m_dwEXEByteLength);
}

void CPSFWorkArea::OnFileSaveas() 
{
  CString strFilterName;
  CString strFilterExt;

  CDocTemplate *pDocTemplate = GetDocTemplate();
  ASSERT(pDocTemplate);
  pDocTemplate->GetDocString(strFilterName, CDocTemplate::filterName);
  pDocTemplate->GetDocString(strFilterExt , CDocTemplate::filterExt );

  CString strFilterString = strFilterName + _T("|*") + strFilterExt + _T("||");

  CFileDialog dlgFile(
    FALSE,
    (LPCTSTR)strFilterExt,
    (LPCTSTR)m_strPathName,
    OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
    (LPCTSTR)strFilterString
  );

  if(dlgFile.DoModal() != IDOK) return;

  CString strSaveAsPathName = dlgFile.GetPathName();

  OnSaveDocument((LPCTSTR)strSaveAsPathName);
}

void CPSFWorkArea::OnFileImportexe() 
{
  CString strEXEFilter;
  CString strAllFilter;
  strEXEFilter.LoadString(IDS_FILTER_EXE);
  strAllFilter.LoadString(IDS_FILTER_ALL);
  CString strFilterString = strEXEFilter + _T("|") + strAllFilter + _T("||");
  CFileDialog dlgFile(
    TRUE,
    _T(""),
    _T(""),
    OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
    (LPCTSTR)strFilterString
  );
  CString strTitle;
  strTitle.LoadString(IDS_IMPORT_EXE);
  dlgFile.m_ofn.lpstrTitle = (LPCTSTR)strTitle;
  if(dlgFile.DoModal() != IDOK) return;
  CString strPathName = dlgFile.GetPathName();

  FILE *f;
  f = fopen((LPCSTR)strPathName, "rb");
  if(!f) {
    CString s;
    s.Format(_T("Unable to open '%s'."), (LPCTSTR)strPathName);
    AfxGetMainWnd()->MessageBox(s, (LPCTSTR)strTitle, MB_OK|MB_ICONHAND);
  }

  fseek(f, 0, SEEK_END);
  int nLength = ftell(f);
  fseek(f, 0, SEEK_SET);
  if(nLength < 0) nLength = 0;

  LPBYTE lpEXE = new BYTE[nLength];
  ASSERT(lpEXE);
  memset(lpEXE, 0, nLength);

  fread(lpEXE, 1, nLength, f);
  fclose(f);

  ImportEXEFromBuffer(lpEXE, nLength);
  delete[] lpEXE;

  /*
  ** Might as well restart the debugger
  */
  DebugRestart();
  RegisterMonitor();

  SetModifiedFlag();
  UpdateAllViews(NULL);
}

void CPSFWorkArea::OnUpdateFileImportexe(CCmdUI* pCmdUI) {
  pCmdUI->Enable(
    (GetVersion() == 1) && (!IsRunning())
  );
}

void CPSFWorkArea::OnFileExportexe() 
{
  CString strEXEFilter;
  CString strAllFilter;
  strEXEFilter.LoadString(IDS_FILTER_EXE);
  strAllFilter.LoadString(IDS_FILTER_ALL);
  CString strFilterString = strEXEFilter + _T("|") + strAllFilter + _T("||");
  CFileDialog dlgFile(
    FALSE,
    _T(""),
    _T(""),
    OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
    (LPCTSTR)strFilterString
  );
  CString strTitle;
  strTitle.LoadString(IDS_EXPORT_EXE);
  dlgFile.m_ofn.lpstrTitle = (LPCTSTR)strTitle;
  if(dlgFile.DoModal() != IDOK) return;
  CString strPathName = dlgFile.GetPathName();

  LPBYTE lpEXE = new BYTE[0x200000];
  ASSERT(lpEXE);
  DWORD dwEXELength = ExportEXEToBuffer(lpEXE);

  FILE *f;
  f = fopen((LPCSTR)strPathName, "wb");
  if(!f) {
    delete[] lpEXE;
    CString s;
    s.Format(_T("Unable to open '%s'."), (LPCTSTR)strPathName);
    AfxGetMainWnd()->MessageBox(s, (LPCTSTR)strTitle, MB_OK|MB_ICONHAND);
  }
  fwrite(lpEXE, 1, dwEXELength, f);
  fclose(f);

  delete[] lpEXE;

}

void CPSFWorkArea::OnFileFondue() 
{
  while(AfxGetMainWnd()->MessageBox(
    _T("Please insert Disk 22 to continue"),
    _T("Disk Change"),
    MB_OKCANCEL|MB_ICONINFORMATION
  ) != IDCANCEL);
	AfxGetMainWnd()->MessageBox(
    _T("Unable to load overlay for CPSFWorkArea::OnFondue()\nFile not found"),
    _T("Critical Error"),
    MB_OK|MB_ICONHAND
  );
}

void CPSFWorkArea::OnUpdateEditOptimize(CCmdUI* pCmdUI) {
  BOOL b = FALSE;
  if((m_nVersion == 1) && (!IsRunning())) b = TRUE;
  pCmdUI->Enable(b);
}

void CPSFWorkArea::OnEditOptimize() 
{
  COptimizeDlg dlg;
  dlg.m_pWorkArea = this;
  if(dlg.DoModal() != IDOK) return;
}

void CPSFWorkArea::AuditCommit(CPSFAudit &audit) {
  DWORD i;
  DWORD last_i_read = 0;
  for(i = 0x80010000; i < 0x80200000; i += 4) {
    if(audit.IsWordUsed(i)) {
      last_i_read = i;
    } else {
      SetPatchWord(i, 0);
    }
  }
  last_i_read += 4;
  last_i_read &= 0x1FFFFC;
  if(last_i_read < (m_dwEXEStartAddress & 0x1FFFFC)) {
    last_i_read = (m_dwEXEStartAddress & 0x1FFFFC);
  }
  m_dwEXEByteLength = last_i_read - (m_dwEXEStartAddress & 0x1FFFFC);
  if(m_dwEXEByteLength > 0x1F0000) { m_dwEXEByteLength = 0x1F0000; }
  UpdateAllViews(NULL);
}

void CPSFWorkArea::AuditUpload(CPSFAudit &audit) {
  /*
  ** Copy the executable there
  */
  audit.Upload(0x80010000, m_adwEXEData, 0x1F0000);
  /*
  ** Set the initial PC/SP
  */
  audit.SetPC(m_dwEXEStartPC);
  audit.SetSP(m_dwEXEStartSP);
  /*
  ** Apply all patches
  */
  DWORD dwAddress;
  for(dwAddress = 0x80010000; dwAddress < 0x80200000; dwAddress += 4) {
    if(IsWordPatched(dwAddress)) {
      DWORD dwData = GetPatchWord(dwAddress);
      audit.Upload(dwAddress, &dwData, 4);
    }
  }
}

void CPSFWorkArea::OnFileExportbiosarea() 
{
	// TODO: Add your command handler code here
	if(!m_pStateDebug) return;

  FILE *f = fopen("test-ram", "wb");
  if(!f) return;

  DWORD tmp;
  DWORD i;
  for(i = 0; i < 0x200000; i += 4) {
    tmp = iop_getword(emu_get_iop_state(m_pStateDebug), 0x80000000 + i);
    fwrite(&tmp, 1, 4, f);
  }

  fclose(f);

}

void CPSFWorkArea::SetVersion(int n)
{
  if(m_nVersion == n) return;

  ((CPSFLabApp*)(AfxGetApp()))->m_nNewPSFVersion = n;
  ((CPSFLabApp*)(AfxGetApp()))->m_nWorkAreaVersion = n;

  DeleteContents();

  m_nVersion = n;

  if(m_pStateDebug) {
    free(m_pStateDebug);
    m_pStateDebug = NULL;
  }
  m_pStateDebug = malloc(emu_get_state_size(m_nVersion));
  emu_clear_state(m_pStateDebug, m_nVersion);
  emu_set_readfile(m_pStateDebug, CPSFWorkArea__readfile_cb, this);
  emu_set_console_out(m_pStateDebug, CPSFWorkArea__console_out, this);

//  strcpy(m_sPSF2Path,
//  (((CPSFLabApp*)(AfxGetApp()))->m_sPSF2Path)
//  );


  OnNewDocument();
}

int CPSFWorkArea::GetVersion()
{
  return m_nVersion;
}

void CPSFWorkArea::OnUpdateFileSave(CCmdUI* pCmdUI) 
{
  pCmdUI->Enable(GetVersion() == 1);
}

void CPSFWorkArea::OnUpdateFileSaveas(CCmdUI* pCmdUI) 
{
  pCmdUI->Enable(GetVersion() == 1);
}

void CPSFWorkArea::OnUpdateEditTag(CCmdUI* pCmdUI) 
{
  pCmdUI->Enable(GetVersion() == 1);
}

void CPSFWorkArea::OnUpdateFileExportexe(CCmdUI* pCmdUI) 
{
  pCmdUI->Enable(GetVersion() == 1);
}
