// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
// Copyright (C) 1999-2003 Forgotten
// Copyright (C) 2005 Forgotten and the VBA development team

// 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

// Disassemble.cpp : implementation file
//

#include "stdafx.h"
#include "vba.h"
#include "Disassemble.h"

#include "../System.h"
#include "../armdis.h"
#include "../agb/GBA.h"
#include "../Globals.h"

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

extern int emulating;

extern void CPUUpdateCPSR();


/////////////////////////////////////////////////////////////////////////////
// Disassemble dialog


Disassemble::Disassemble(CWnd* pParent /*=NULL*/)
  : ResizeDlg(Disassemble::IDD, pParent)
{
  //{{AFX_DATA_INIT(Disassemble)
  m_c = FALSE;
  m_f = FALSE;
  m_i = FALSE;
  m_n = FALSE;
  m_t = FALSE;
  m_v = FALSE;
  m_z = FALSE;
  mode = -1;
  //}}AFX_DATA_INIT
  mode = 0;
  address = 0;
  autoUpdate = false;
  count = 1;
}


void Disassemble::DoDataExchange(CDataExchange* pDX)
{
  CDialog::DoDataExchange(pDX);
  //{{AFX_DATA_MAP(Disassemble)
  DDX_Control(pDX, IDC_ADDRESS, m_address);
  DDX_Control(pDX, IDC_DISASSEMBLE, m_list);
  DDX_Check(pDX, IDC_C, m_c);
  DDX_Check(pDX, IDC_F, m_f);
  DDX_Check(pDX, IDC_I, m_i);
  DDX_Check(pDX, IDC_N, m_n);
  DDX_Check(pDX, IDC_T, m_t);
  DDX_Check(pDX, IDC_V, m_v);
  DDX_Check(pDX, IDC_Z, m_z);
  DDX_Radio(pDX, IDC_AUTOMATIC, mode);
  //}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(Disassemble, CDialog)
  //{{AFX_MSG_MAP(Disassemble)
  ON_BN_CLICKED(IDC_AUTO_UPDATE, OnAutoUpdate)
  ON_BN_CLICKED(IDC_AUTOMATIC, OnAutomatic)
  ON_BN_CLICKED(IDC_ARM, OnArm)
  ON_BN_CLICKED(IDC_CLOSE, OnClose)
  ON_BN_CLICKED(IDC_GO, OnGo)
  ON_BN_CLICKED(IDC_GOPC, OnGopc)
  ON_BN_CLICKED(IDC_NEXT, OnNext)
  ON_BN_CLICKED(IDC_REFRESH, OnRefresh)
  ON_BN_CLICKED(IDC_THUMB, OnThumb)
  ON_WM_VSCROLL()
  //}}AFX_MSG_MAP
  END_MESSAGE_MAP()

  /////////////////////////////////////////////////////////////////////////////
// Disassemble message handlers

void Disassemble::OnAutoUpdate()
{
  autoUpdate = !autoUpdate;
  if(autoUpdate) {
    theApp.winAddUpdateListener(this);
  } else {
    theApp.winRemoveUpdateListener(this);
  }
}

void Disassemble::OnAutomatic()
{
  mode = 0;
  refresh();
}

void Disassemble::OnArm()
{
  mode = 1;
  address&=0xfffffffC;
  refresh();
}

void Disassemble::OnClose()
{
  theApp.winRemoveUpdateListener(this);

  DestroyWindow();
}

void Disassemble::OnGo()
{
  CString buffer;
  m_address.GetWindowText(buffer);
  sscanf(buffer, "%x", &address);
  if (mode==1)
      address&=0xfffffffc;
  else if (mode==2)
      address&=0xfffffffe;
  refresh();
}

void Disassemble::OnGopc()
{
  if(rom != NULL)
  {
  if(armState)
    address = armNextPC - 16;
  else
    address = armNextPC - 8;

  refresh();
  }
}

void Disassemble::OnNext()
{
  if(rom != NULL)
  {
  CPULoop(1);
  if(armState) {
    u32 total = address+count*4;
    if(armNextPC >= address && armNextPC < total) {
    } else {
      OnGopc();
    }
  } else {
    u32 total = address+count*2;
    if(armNextPC >= address && armNextPC < total) {
    } else {
      OnGopc();
    }
  }
  refresh();
  }
}

void Disassemble::OnRefresh()
{
  refresh();
}

void Disassemble::OnThumb()
{
  mode = 2;
  address&=0xfffffffe;
  refresh();
}

BOOL Disassemble::OnInitDialog()
{
  CDialog::OnInitDialog();

  DIALOG_SIZER_START( sz )
    DIALOG_SIZER_ENTRY( IDC_DISASSEMBLE, DS_SizeY)
    DIALOG_SIZER_ENTRY( IDC_REFRESH, DS_MoveY)
    DIALOG_SIZER_ENTRY( IDC_CLOSE, DS_MoveY)
    DIALOG_SIZER_ENTRY( IDC_NEXT,  DS_MoveY)
    DIALOG_SIZER_ENTRY( IDC_AUTO_UPDATE, DS_MoveY)
    DIALOG_SIZER_ENTRY( IDC_GOPC, DS_MoveY)
    DIALOG_SIZER_ENTRY( IDC_VSCROLL, DS_SizeY)
    DIALOG_SIZER_END()
    SetData(sz,
            TRUE,
            HKEY_CURRENT_USER,
            "Software\\Emulators\\VisualBoyAdvance\\Viewer\\DisassembleView",
            NULL);

  SCROLLINFO si;
  ZeroMemory(&si, sizeof(si));
  si.cbSize = sizeof(si);
  si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  si.nMin = 0;
  si.nMax = 100;
  si.nPos = 50;
  si.nPage = 0;
  GetDlgItem(IDC_VSCROLL)->SetScrollInfo(SB_CTL, &si, TRUE);

  CFont *font = CFont::FromHandle((HFONT)GetStockObject(SYSTEM_FIXED_FONT));

  m_list.SetFont(font, FALSE);
  for(int i = 0; i < 17; i++)
    GetDlgItem(IDC_R0+i)->SetFont(font, FALSE);

  GetDlgItem(IDC_MODE)->SetFont(font, FALSE);


  m_address.LimitText(8);
  refresh();

  return TRUE;  // return TRUE unless you set the focus to a control
                // EXCEPTION: OCX Property Pages should return FALSE
}

void Disassemble::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
  switch(nSBCode) {
  case SB_LINEDOWN:
    if(mode == 0) {
      if(armState)
        address += 4;
      else
        address += 2;
    } else if(mode == 1)
      address += 4;
    else
      address += 2;
    break;
  case SB_LINEUP:
    if(mode == 0) {
      if(armState)
        address -= 4;
      else
        address -= 2;
    } else if(mode == 1)
      address -= 4;
    else
      address -= 2;
    break;
  case SB_PAGEDOWN:
    if(mode == 0) {
      if(armState)
        address += count*4;
      else
        address += count*2;
    } else if(mode == 1)
      address += count*4;
    else
      address += count*2;
    break;
  case SB_PAGEUP:
    if(mode == 0) {
      if(armState)
        address -= count*4;
      else
        address -= count*2;
    } else if(mode == 1)
      address -= count*4;
    else
      address -= count*2;
    break;
  }
  refresh();

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

void Disassemble::refresh()
{
  if(rom == NULL)
    return;

  bool arm = armState;

  if(mode != 0) {
    if(mode == 1)
      arm = true;
    else
      arm = false;
  }

  int h = m_list.GetItemHeight(0);
  RECT r;
  m_list.GetClientRect(&r);
  count = (r.bottom - r.top+1)/h;

  m_list.ResetContent();
  if(!emulating && theApp.cartridgeType == 0)
    return;

  char buffer[80];
  u32 addr = address;
  int i;
  int sel = -1;
  for(i = 0; i < count; i++) {
    if(addr == armNextPC)
      sel = i;
    if(arm) {
      addr += disArm(addr, buffer, 3);
    } else {
      addr += disThumb(addr, buffer, 3);
    }
    m_list.InsertString(-1, buffer);
  }

  if(sel != -1)
    m_list.SetCurSel(sel);

  CPUUpdateCPSR();

  for(i = 0; i < 17; i++) {
    sprintf(buffer, "%08x", reg[i].I);
    GetDlgItem(IDC_R0+i)->SetWindowText(buffer);
  }

  m_n = (reg[16].I & 0x80000000) != 0;
  m_z = (reg[16].I & 0x40000000) != 0;
  m_c = (reg[16].I & 0x20000000) != 0;
  m_v = (reg[16].I & 0x10000000) != 0;
  m_i = (reg[16].I & 0x80) != 0;
  m_f = (reg[16].I & 0x40) != 0;
  m_t = (reg[16].I & 0x20) != 0;

  UpdateData(FALSE);

  int v = reg[16].I & 0x1f;
  sprintf(buffer, "%02x", v);
  GetDlgItem(IDC_MODE)->SetWindowText(buffer);
}

void Disassemble::update()
{
  OnGopc();
  refresh();
}

void Disassemble::PostNcDestroy()
{
  delete this;
}
