1
0
mirror of https://git.code.sf.net/p/fuse-emulator/fuse synced 2026-01-27 01:41:34 +03:00
Files
fuse/ui/win32/debugger.c
Fredrick Meunier ba77c2ab67 Move memory.[ch] to memory_pages.[ch]
To avoid clashes with any similarly named system files (thanks,
BogDan Vatra and Sergio Baldoví).
2016-12-10 20:27:03 +11:00

1334 lines
39 KiB
C

/* debugger.c: the Win32 debugger
Copyright (c) 2004-2012 Philip Kendall, Marek Januszewski
Copyright (c) 2013-2015 Sergio Baldoví
Copyright (c) 2015 Stuart Brady
Copyright (c) 2016 BogDan Vatra
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.
Author contact information:
E-mail: philip-fuse@shadowmagic.org.uk
*/
#include <config.h>
/* FIXME: is that needed?
#include <stdio.h>
#include <string.h>
*/
#include <libspectrum.h>
#include <stdlib.h>
#include <tchar.h>
#include <windows.h>
#include "debugger/debugger.h"
#include "event.h"
#include "fuse.h"
#include "settings.h"
#include "peripherals/ide/zxcf.h"
#include "peripherals/scld.h"
#include "peripherals/ula.h"
#include "ui/ui.h"
#include "win32internals.h"
#include "z80/z80.h"
#include "z80/z80_macros.h"
#include "debugger.h"
/* FIXME: delete whatever is not needed
#include "machine.h"
#include "memory_pages.h"
*/
/* The various debugger panes */
typedef enum debugger_pane {
DEBUGGER_PANE_BEGIN = 1, /* Start marker */
DEBUGGER_PANE_REGISTERS = DEBUGGER_PANE_BEGIN,
DEBUGGER_PANE_MEMORYMAP,
DEBUGGER_PANE_BREAKPOINTS,
DEBUGGER_PANE_DISASSEMBLY,
DEBUGGER_PANE_STACK,
DEBUGGER_PANE_EVENTS,
DEBUGGER_PANE_END /* End marker */
} debugger_pane;
static int create_dialog( void );
static int hide_hidden_panes( void );
static UINT get_pane_menu_item( debugger_pane pane );
static BOOL show_hide_pane( debugger_pane pane, int show );
/* static int create_menu_bar( void ); this function is handled by rc */
static void toggle_display( debugger_pane pane, UINT menu_item_id );
static int create_register_display( HFONT font );
/* int create_memory_map( void ); this function is handled by rc */
static int create_breakpoints( void );
static int create_disassembly( HFONT font );
static int create_stack_display( HFONT font );
static void stack_click( LPNMITEMACTIVATE lpnmitem );
static int create_events( void );
static void events_click( LPNMITEMACTIVATE lpnmitem );
/* int create_command_entry( void ); this function is handled by rc */
/* int create_buttons( void ); this function is handled by rc */
static int activate_debugger( void );
static void update_memory_map( void );
static void update_breakpoints( void );
static void update_disassembly( void );
static void update_events( void );
static void add_event( gpointer data, gpointer user_data GCC_UNUSED );
static int deactivate_debugger( void );
static int move_disassembly( WPARAM scroll_command );
static void evaluate_command( void );
static void win32ui_debugger_done_step( void );
static void win32ui_debugger_done_continue( void );
static void win32ui_debugger_break( void );
static void delete_dialog( void );
static void win32ui_debugger_done_close( void );
static INT_PTR CALLBACK win32ui_debugger_proc( HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam );
static LRESULT CALLBACK
disassembly_listview_proc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
/* The top line of the current disassembly */
static libspectrum_word disassembly_top;
/* The next line below the current disassembly */
static libspectrum_word disassembly_bottom;
/* helper constants for disassembly listview's scrollbar */
static const int disassembly_min = 0x0000;
static const int disassembly_max = 0xffff;
/* Visual styles could change visible rows */
static unsigned int disassembly_page = 20;
/* Have we created the above yet? */
static int dialog_created = 0;
/* Is the debugger window active (as opposed to the debugger itself)? */
static int debugger_active;
#define STUB do { printf("STUB: %s()\n", __func__); fflush(stdout); } while(0)
static const TCHAR*
format_8_bit( void )
{
return debugger_output_base == 10 ? TEXT( "%3d" ) : TEXT( "0x%02X" );
}
static const TCHAR*
format_16_bit( void )
{
return debugger_output_base == 10 ? TEXT( "%5d" ) : TEXT( "0x%04X" );
}
int
ui_debugger_activate( void )
{
int error;
fuse_emulation_pause();
/* create_dialog will create the dialog or activate if it exists */
if( !dialog_created ) if( create_dialog() ) return 1;
ShowWindow( fuse_hDBGWnd, SW_SHOW );
error = hide_hidden_panes(); if( error ) return error;
EnableWindow( GetDlgItem( fuse_hDBGWnd, IDC_DBG_BTN_CONT ), TRUE);
EnableWindow( GetDlgItem( fuse_hDBGWnd, IDC_DBG_BTN_BREAK ), FALSE );
if( !debugger_active ) activate_debugger();
return 0;
}
void
ui_breakpoints_updated( void )
{
/* TODO: Refresh debugger list here */
}
static int
hide_hidden_panes( void )
{
debugger_pane i;
UINT checkitem;
MENUITEMINFO mii;
for( i = DEBUGGER_PANE_BEGIN; i < DEBUGGER_PANE_END; i++ ) {
checkitem = get_pane_menu_item( i ); if( !checkitem ) return 1;
mii.fMask = MIIM_STATE;
mii.cbSize = sizeof( MENUITEMINFO );
if( ! GetMenuItemInfo( GetMenu( fuse_hDBGWnd ), checkitem, FALSE, &mii ) )
return 1;
if( mii.fState && MFS_CHECKED ) continue;
if( ! show_hide_pane( i, SW_HIDE ) ) return 1;
}
return 0;
}
static UINT
get_pane_menu_item( debugger_pane pane )
{
UINT menu_item_id;
menu_item_id = 0;
switch( pane ) {
case DEBUGGER_PANE_REGISTERS: menu_item_id = IDM_DBG_REG; break;
case DEBUGGER_PANE_MEMORYMAP: menu_item_id = IDM_DBG_MEMMAP; break;
case DEBUGGER_PANE_BREAKPOINTS: menu_item_id = IDM_DBG_BPS; break;
case DEBUGGER_PANE_DISASSEMBLY: menu_item_id = IDM_DBG_DIS; break;
case DEBUGGER_PANE_STACK: menu_item_id = IDM_DBG_STACK; break;
case DEBUGGER_PANE_EVENTS: menu_item_id = IDM_DBG_EVENTS; break;
case DEBUGGER_PANE_END: break;
}
if( !menu_item_id ) {
ui_error( UI_ERROR_ERROR, "unknown debugger pane %u", pane );
return 0;
}
return menu_item_id;
}
static BOOL
show_hide_pane( debugger_pane pane, int show )
{
/* Instead of get_pane() and then hiding or showing the widget as it is done
in gtk ui, we need to show/hide multiple widgets, hence this combined
function.
show parameter needs to be SW_SHOW to reveal the pane,
or SW_HIDE to hide it.
*/
/* FIXME: window needs to resize/collapse as panel are being hidden */
int i;
switch( pane ) {
case DEBUGGER_PANE_REGISTERS:
for( i = IDC_DBG_REG_PC; i <= IDC_DBG_REG_IM; i++ ) {
ShowWindow( GetDlgItem( fuse_hDBGWnd, i ), show );
}
return TRUE;
case DEBUGGER_PANE_MEMORYMAP:
for( i = IDC_DBG_MAP11; i <= IDC_DBG_TEXT_CONTENDED; i++ ) {
ShowWindow( GetDlgItem( fuse_hDBGWnd, i ), show );
}
ShowWindow( GetDlgItem( fuse_hDBGWnd, IDC_DBG_GRP_MEMMAP ), show );
return TRUE;
case DEBUGGER_PANE_BREAKPOINTS:
ShowWindow( GetDlgItem( fuse_hDBGWnd, IDC_DBG_LV_BPS ), show );
return TRUE;
case DEBUGGER_PANE_DISASSEMBLY:
ShowWindow( GetDlgItem( fuse_hDBGWnd, IDC_DBG_LV_PC ), show );
ShowWindow( GetDlgItem( fuse_hDBGWnd, IDC_DBG_SB_PC ), show );
return TRUE;
case DEBUGGER_PANE_STACK:
ShowWindow( GetDlgItem( fuse_hDBGWnd, IDC_DBG_LV_STACK ), show );
return TRUE;
case DEBUGGER_PANE_EVENTS:
ShowWindow( GetDlgItem( fuse_hDBGWnd, IDC_DBG_LV_EVENTS ), show );
return TRUE;
case DEBUGGER_PANE_END: break;
}
ui_error( UI_ERROR_ERROR, "unknown debugger pane %u", pane );
return FALSE;
}
int
ui_debugger_deactivate( int interruptable )
{
if( debugger_active ) deactivate_debugger();
if( dialog_created ) {
EnableWindow( GetDlgItem( fuse_hDBGWnd, IDC_DBG_BTN_CONT ),
!interruptable ? TRUE : FALSE );
EnableWindow( GetDlgItem( fuse_hDBGWnd, IDC_DBG_BTN_BREAK ),
interruptable ? TRUE : FALSE );
}
return 0;
}
static int
create_dialog( void )
{
int error;
debugger_pane i;
MENUITEMINFO mii;
HFONT font;
error = win32ui_get_monospaced_font( &font ); if( error ) return error;
fuse_hDBGWnd = CreateDialog( fuse_hInstance, MAKEINTRESOURCE( IDD_DBG ),
fuse_hWnd, win32ui_debugger_proc );
/* The main display areas */
error = create_register_display( font );
if( error ) return error;
error = create_breakpoints(); if( error ) return error;
error = create_disassembly( font ); if( error ) return error;
error = create_stack_display( font ); if( error ) return error;
error = create_events(); if( error ) return error;
/* Initially, have all the panes visible */
for( i = DEBUGGER_PANE_BEGIN; i < DEBUGGER_PANE_END; i++ ) {
UINT check_item;
check_item = get_pane_menu_item( i ); if( !check_item ) break;
mii.fMask = MIIM_STATE;
mii.fState = MFS_CHECKED;
mii.cbSize = sizeof( MENUITEMINFO );
SetMenuItemInfo( GetMenu( fuse_hDBGWnd ), check_item, FALSE, &mii );
}
dialog_created = 1;
return 0;
}
static void
toggle_display( debugger_pane pane, UINT menu_item_id )
{
MENUITEMINFO mii;
mii.fMask = MIIM_STATE;
mii.cbSize = sizeof( MENUITEMINFO );
GetMenuItemInfo( GetMenu( fuse_hDBGWnd ), menu_item_id, FALSE, &mii );
/* Windows doesn't automatically checks/unchecks
the menus when they're clicked */
if( mii.fState && MFS_CHECKED ) {
show_hide_pane( pane, SW_HIDE );
mii.fState = MFS_UNCHECKED;
SetMenuItemInfo( GetMenu( fuse_hDBGWnd ), menu_item_id, FALSE, &mii );
} else {
show_hide_pane( pane, SW_SHOW );
mii.fState = MFS_CHECKED;
SetMenuItemInfo( GetMenu( fuse_hDBGWnd ), menu_item_id, FALSE, &mii );
}
}
static int
create_register_display( HFONT font )
{
/* this display is created in rc, just set the monospaced font */
size_t i;
for( i = 0; i < NUM_DBG_REGS; i++ ) {
win32ui_set_font( fuse_hDBGWnd, IDC_DBG_REG_PC + i, font );
}
return 0;
}
static int
create_breakpoints( void )
{
size_t i;
LPCTSTR breakpoint_titles[] = { _T( "ID" ), _T( "Type" ), _T( "Value" ),
_T( "Ignore" ), _T( "Life" ),
_T( "Condition" ) };
/* set extended listview style to select full row, when an item is selected */
DWORD lv_ext_style;
lv_ext_style = SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_BPS,
LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0 );
lv_ext_style |= LVS_EX_FULLROWSELECT;
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_BPS,
LVM_SETEXTENDEDLISTVIEWSTYLE, 0, lv_ext_style );
/* create columns */
LVCOLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT ;
lvc.fmt = LVCFMT_LEFT;
for( i = 0; i < 6; i++ ) {
if( i != 0 )
lvc.mask |= LVCF_SUBITEM;
lvc.cx = _tcslen( breakpoint_titles[i] ) * 8 + 10;
lvc.pszText = (LPTSTR)breakpoint_titles[i];
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_BPS, LVM_INSERTCOLUMN, i,
( LPARAM ) &lvc );
}
return 0;
}
static int
create_disassembly( HFONT font )
{
size_t i;
LPCTSTR disassembly_titles[] = { TEXT( "Address" ), TEXT( "Instruction" ) };
/* The disassembly listview itself */
/* subclass listview to catch keydown and mousewheel messages */
HWND hwnd_list = GetDlgItem( fuse_hDBGWnd, IDC_DBG_LV_PC );
WNDPROC orig_proc = (WNDPROC) GetWindowLongPtr( hwnd_list, GWLP_WNDPROC );
SetProp( hwnd_list, "original_proc", (HANDLE) orig_proc );
SetWindowLongPtr( hwnd_list, GWLP_WNDPROC,
(LONG_PTR) (WNDPROC) disassembly_listview_proc );
/* set extended listview style to select full row, when an item is selected */
DWORD lv_ext_style;
lv_ext_style = SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_PC,
LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0 );
lv_ext_style |= LVS_EX_FULLROWSELECT;
lv_ext_style |= LVS_EX_DOUBLEBUFFER;
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_PC,
LVM_SETEXTENDEDLISTVIEWSTYLE, 0, lv_ext_style );
win32ui_set_font( fuse_hDBGWnd, IDC_DBG_LV_PC, font );
/* create columns */
LVCOLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_TEXT;
lvc.fmt = LVCFMT_LEFT;
for( i = 0; i < 2; i++ ) {
if( i != 0 ) lvc.mask |= LVCF_SUBITEM;
lvc.pszText = (LPTSTR)disassembly_titles[i];
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_PC, LVM_INSERTCOLUMN, i,
( LPARAM ) &lvc );
}
/* Set columns width */
ListView_SetColumnWidth( hwnd_list, 0, LVSCW_AUTOSIZE_USEHEADER );
ListView_SetColumnWidth( hwnd_list, 1, LVSCW_AUTOSIZE_USEHEADER );
/* Recalculate visible rows, Visual Styles could change rows height */
disassembly_page = SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_PC,
LVM_GETCOUNTPERPAGE, 0, 0 );
/* The disassembly scrollbar */
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
si.nPos = 0;
si.nMin = disassembly_min;
si.nMax = disassembly_max;
si.nPage = disassembly_page;
SetScrollInfo( GetDlgItem( fuse_hDBGWnd, IDC_DBG_SB_PC ),
SB_CTL, &si, TRUE );
return 0;
}
static int
create_stack_display( HFONT font )
{
size_t i;
LPCTSTR stack_titles[] = { _T( "Address" ), _T( "Value" ) };
/* set extended listview style to select full row, when an item is selected */
DWORD lv_ext_style;
lv_ext_style = SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_STACK,
LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0 );
lv_ext_style |= LVS_EX_FULLROWSELECT;
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_STACK,
LVM_SETEXTENDEDLISTVIEWSTYLE, 0, lv_ext_style );
win32ui_set_font( fuse_hDBGWnd, IDC_DBG_LV_STACK, font );
/* create columns */
LVCOLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_TEXT;
lvc.fmt = LVCFMT_LEFT;
for( i = 0; i < 2; i++ ) {
if( i != 0 ) lvc.mask |= LVCF_SUBITEM;
lvc.pszText = (LPTSTR)stack_titles[i];
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_STACK, LVM_INSERTCOLUMN, i,
( LPARAM ) &lvc );
}
/* Set columns width */
HWND hwnd_list = GetDlgItem( fuse_hDBGWnd, IDC_DBG_LV_STACK );
ListView_SetColumnWidth( hwnd_list, 0, LVSCW_AUTOSIZE_USEHEADER );
ListView_SetColumnWidth( hwnd_list, 1, LVSCW_AUTOSIZE_USEHEADER );
return 0;
}
static void
stack_click( LPNMITEMACTIVATE lpnmitem )
{
libspectrum_word destination;
int retval, error, row;
char *stopstring;
TCHAR buffer[255];
LVITEM li;
row = lpnmitem->iItem;
if( row < 0 ) return;
li.iSubItem = 1;
li.pszText = buffer;
li.cchTextMax = 255;
retval = SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_STACK,
LVM_GETITEMTEXT, row, ( LPARAM ) &li );
if( !retval ) {
ui_error( UI_ERROR_ERROR, "couldn't get text for row %d", row );
return;
}
destination = strtoul( buffer, &stopstring, 16 );
error = debugger_breakpoint_add_address(
DEBUGGER_BREAKPOINT_TYPE_EXECUTE, memory_source_any, 0, destination, 0,
DEBUGGER_BREAKPOINT_LIFE_ONESHOT, NULL
);
if( error ) return;
debugger_run();
}
static int
create_events( void )
{
size_t i;
LPCTSTR titles[] = { _T( "Time" ), _T( "Type" ) };
/* set extended listview style to select full row, when an item is selected */
DWORD lv_ext_style;
lv_ext_style = SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_EVENTS,
LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0 );
lv_ext_style |= LVS_EX_FULLROWSELECT;
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_EVENTS,
LVM_SETEXTENDEDLISTVIEWSTYLE, 0, lv_ext_style );
/* create columns */
LVCOLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_TEXT;
lvc.fmt = LVCFMT_LEFT;
for( i = 0; i < 2; i++ ) {
if( i != 0 ) lvc.mask |= LVCF_SUBITEM;
lvc.pszText = (LPTSTR)titles[i];
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_EVENTS, LVM_INSERTCOLUMN, i,
( LPARAM ) &lvc );
}
/* Set columns width */
HWND hwnd_list = GetDlgItem( fuse_hDBGWnd, IDC_DBG_LV_EVENTS );
ListView_SetColumnWidth( hwnd_list, 0, LVSCW_AUTOSIZE_USEHEADER );
ListView_SetColumnWidth( hwnd_list, 1, LVSCW_AUTOSIZE_USEHEADER );
return 0;
}
static void
events_click( LPNMITEMACTIVATE lpnmitem )
{
int got_text, error, row;
TCHAR buffer[255];
LVITEM li;
libspectrum_dword tstates;
row = lpnmitem->iItem;
if( row < 0 ) return;
li.iSubItem = 0;
li.pszText = buffer;
li.cchTextMax = 255;
got_text = SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_EVENTS,
LVM_GETITEMTEXT, row, ( LPARAM ) &li );
if( !got_text ) {
ui_error( UI_ERROR_ERROR, "couldn't get text for row %d", row );
return;
}
tstates = _ttoi( buffer );
error = debugger_breakpoint_add_time(
DEBUGGER_BREAKPOINT_TYPE_TIME, tstates, 0,
DEBUGGER_BREAKPOINT_LIFE_ONESHOT, NULL
);
if( error ) return;
debugger_run();
}
static int
activate_debugger( void )
{
debugger_active = 1;
ui_debugger_disassemble( PC );
ui_debugger_update();
win32ui_process_messages( 0 );
return 0;
}
/* Update the debugger's display */
int
ui_debugger_update( void )
{
size_t i;
TCHAR buffer[1024], format_string[1024];
TCHAR *disassembly_text[2] = { &buffer[0], &buffer[40] };
libspectrum_word address;
int capabilities; size_t length;
const char *register_name[] = { TEXT( "PC" ), TEXT( "SP" ),
TEXT( "AF" ), TEXT( "AF'" ),
TEXT( "BC" ), TEXT( "BC'" ),
TEXT( "DE" ), TEXT( "DE'" ),
TEXT( "HL" ), TEXT( "HL'" ),
TEXT( "IX" ), TEXT( "IY" ),
};
libspectrum_word *value_ptr[] = { &PC, &SP, &AF, &AF_,
&BC, &BC_, &DE, &DE_,
&HL, &HL_, &IX, &IY,
};
if( !dialog_created ) return 0;
/* FIXME: verify all functions below are unicode compliant */
for( i = 0; i < 12; i++ ) {
_sntprintf( buffer, 5, "%3s ", register_name[i] );
_sntprintf( &buffer[4], 76, format_16_bit(), *value_ptr[i] );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_REG_PC + i,
WM_SETTEXT, (WPARAM) 0, (LPARAM) buffer );
}
_tcscpy( buffer, TEXT( " I " ) );
_sntprintf( &buffer[6], 76, format_8_bit(), I );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_REG_I,
WM_SETTEXT, (WPARAM) 0, (LPARAM) buffer );
_tcscpy( buffer, TEXT( " R " ) );
_sntprintf( &buffer[6], 80, format_8_bit(), ( R & 0x7f ) | ( R7 & 0x80 ) );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_REG_R,
WM_SETTEXT, (WPARAM) 0, (LPARAM) buffer );
_sntprintf( buffer, 80, TEXT( "Halted %d" ), z80.halted );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_REG_HALTED,
WM_SETTEXT, (WPARAM) 0, (LPARAM) buffer );
_sntprintf( buffer, 80, TEXT( "T-states %5d" ), tstates );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_REG_T_STATES,
WM_SETTEXT, (WPARAM) 0, (LPARAM) buffer );
_sntprintf( buffer, 80, TEXT( " IM %d\r\nIFF1 %d\r\nIFF2 %d" ),
IM, IFF1, IFF2 );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_REG_IM,
WM_SETTEXT, (WPARAM) 0, (LPARAM) buffer );
_tcscpy( buffer, TEXT( "SZ5H3PNC\r\n" ) );
for( i = 0; i < 8; i++ ) buffer[i+10] = ( F & ( 0x80 >> i ) ) ? '1' : '0';
buffer[18] = '\0';
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_REG_FLAGS,
WM_SETTEXT, (WPARAM) 0, (LPARAM) buffer );
capabilities = libspectrum_machine_capabilities( machine_current->machine );
_stprintf( format_string, " ULA %s", format_8_bit() );
_sntprintf( buffer, 1024, format_string, ula_last_byte() );
if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_AY ) {
_stprintf( format_string, "\r\n AY %s", format_8_bit() );
length = _tcslen( buffer );
_sntprintf( &buffer[length], 1024-length, format_string,
machine_current->ay.current_register );
}
if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY ) {
_stprintf( format_string, "\r\n128Mem %s", format_8_bit() );
length = _tcslen( buffer );
_sntprintf( &buffer[length], 1024-length, format_string,
machine_current->ram.last_byte );
}
if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_MEMORY ) {
_stprintf( format_string, "\r\n+3 Mem %s", format_8_bit() );
length = _tcslen( buffer );
_sntprintf( &buffer[length], 1024-length, format_string,
machine_current->ram.last_byte2 );
}
if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_VIDEO ||
capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_MEMORY ||
capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_SE_MEMORY ) {
_stprintf( format_string, "\r\nTmxDec %s", format_8_bit() );
length = _tcslen( buffer );
_sntprintf( &buffer[length], 1024-length, format_string,
scld_last_dec.byte );
}
if( capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_MEMORY ||
capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_SE_MEMORY ) {
_stprintf( format_string, "\r\nTmxHsr %s", format_8_bit() );
length = _tcslen( buffer );
_sntprintf( &buffer[length], 1024-length, format_string, scld_last_hsr );
}
if( settings_current.zxcf_active ) {
_stprintf( format_string, "\r\n ZXCF %s", format_8_bit() );
length = _tcslen( buffer );
_sntprintf( &buffer[length], 1024-length, format_string,
zxcf_last_memctl() );
}
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_REG_ULA,
WM_SETTEXT, (WPARAM) 0, (LPARAM) buffer );
update_memory_map();
update_breakpoints();
update_disassembly();
/* And the stack display */
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_STACK,
LVM_DELETEALLITEMS, 0, 0 );
LV_ITEM lvi;
lvi.mask = LVIF_TEXT;
for( i = 0, address = SP + 38; i < 20; i++, address -= 2 ) {
libspectrum_word contents = readbyte_internal( address ) +
0x100 * readbyte_internal( address + 1 );
_sntprintf( disassembly_text[0], 40, format_16_bit(), address );
_sntprintf( disassembly_text[1], 40, format_16_bit(), contents );
/* add the item */
lvi.iItem = i;
lvi.iSubItem = 0;
lvi.pszText = disassembly_text[0];
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_STACK, LVM_INSERTITEM, 0,
( LPARAM ) &lvi );
lvi.iSubItem = 1;
lvi.pszText = disassembly_text[1];
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_STACK, LVM_SETITEM, 0,
( LPARAM ) &lvi );
}
/* And the events display */
update_events();
return 0;
}
static void
update_memory_map( void )
{
TCHAR buffer[ 40 ];
int source, page_num, writable, contended;
libspectrum_word offset;
size_t i, j, block, row;
source = page_num = writable = contended = -1;
offset = 0;
row = 0;
for( block = 0; block < MEMORY_PAGES_IN_64K && row < 8; block++ ) {
memory_page *page = &memory_map_read[block];
if( page->source != source ||
page->page_num != page_num ||
page->offset != offset ||
page->writable != writable ||
page->contended != contended ) {
_sntprintf( buffer, 40, format_16_bit(),
(unsigned)block * MEMORY_PAGE_SIZE );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_MAP11 + ( row * 4 ),
WM_SETTEXT, ( WPARAM ) 0, ( LPARAM ) buffer );
/* FIXME: memory_source_description is not unicode */
_snprintf( buffer, 40, TEXT( "%s %d" ),
memory_source_description( page->source ), page->page_num );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_MAP11 + ( row * 4 ) + 1,
WM_SETTEXT, ( WPARAM ) 0, ( LPARAM ) buffer );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_MAP11 + ( row * 4 ) + 2,
WM_SETTEXT, (WPARAM) 0,
( LPARAM ) ( page->writable
? TEXT( "Y" ) : TEXT( "N" ) ) );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_MAP11 + ( row * 4 ) + 3,
WM_SETTEXT, (WPARAM) 0,
( LPARAM ) ( page->contended
? TEXT( "Y" ) : TEXT( "N" ) ) );
row++;
source = page->source;
page_num = page->page_num;
writable = page->writable;
contended = page->contended;
offset = page->offset;
}
/* We expect the next page to have an increased offset */
offset += MEMORY_PAGE_SIZE;
}
/* Hide unused rows */
for( i = row; i < 8; i++ ) {
for( j = 0; j < 4; j++ ) {
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_MAP11 + ( i * 4 ) + j,
WM_SETTEXT, (WPARAM) 0, (LPARAM) NULL );
}
}
}
static void
update_breakpoints( void )
{
/* FIXME: review this function for unicode compatibility */
TCHAR buffer[ 1024 ],
*breakpoint_text[6] = { &buffer[ 0], &buffer[ 40], &buffer[80],
&buffer[120], &buffer[160], &buffer[200] };
GSList *ptr;
TCHAR format_string[ 1024 ];
LV_ITEM lvi;
lvi.mask = LVIF_TEXT;
int i;
/* Create the breakpoint list */
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_BPS,
LVM_DELETEALLITEMS, 0, 0 );
for( ptr = debugger_breakpoints; ptr; ptr = ptr->next ) {
debugger_breakpoint *bp = ptr->data;
_sntprintf( breakpoint_text[0], 40, "%lu", (unsigned long)bp->id );
_sntprintf( breakpoint_text[1], 40, "%s",
debugger_breakpoint_type_text[ bp->type ] );
switch( bp->type ) {
case DEBUGGER_BREAKPOINT_TYPE_EXECUTE:
case DEBUGGER_BREAKPOINT_TYPE_READ:
case DEBUGGER_BREAKPOINT_TYPE_WRITE:
if( bp->value.address.source == memory_source_any ) {
_sntprintf( breakpoint_text[2], 40, format_16_bit(),
bp->value.address.offset );
} else {
snprintf( format_string, 1024, "%%s:%s:%s",
format_16_bit(), format_16_bit() );
snprintf( breakpoint_text[2], 40, format_string,
memory_source_description( bp->value.address.source ),
bp->value.address.page, bp->value.address.offset );
}
break;
case DEBUGGER_BREAKPOINT_TYPE_PORT_READ:
case DEBUGGER_BREAKPOINT_TYPE_PORT_WRITE:
_stprintf( format_string, "%s:%s", format_16_bit(), format_16_bit() );
_sntprintf( breakpoint_text[2], 40, format_string,
bp->value.port.mask, bp->value.port.port );
break;
case DEBUGGER_BREAKPOINT_TYPE_TIME:
_sntprintf( breakpoint_text[2], 40, "%5d", bp->value.time.tstates );
break;
case DEBUGGER_BREAKPOINT_TYPE_EVENT:
_sntprintf( breakpoint_text[2], 40, "%s:%s", bp->value.event.type,
bp->value.event.detail );
}
_sntprintf( breakpoint_text[3], 40, "%lu", (unsigned long)bp->ignore );
_sntprintf( breakpoint_text[4], 40, "%s",
debugger_breakpoint_life_text[ bp->life ] );
if( bp->condition ) {
debugger_expression_deparse( breakpoint_text[5], 80, bp->condition );
} else {
_tcscpy( breakpoint_text[5], "" );
}
/* get the count of items to insert as last element */
lvi.iItem = SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_BPS,
LVM_GETITEMCOUNT, 0, 0 );
/* append the breakpoint items */
for( i = 0; i < 6; i++ ) {
lvi.iSubItem = i;
lvi.pszText = breakpoint_text[i];
if( i == 0 )
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_BPS, LVM_INSERTITEM, 0,
( LPARAM ) &lvi );
else
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_BPS, LVM_SETITEM, 0,
( LPARAM ) &lvi );
}
}
}
static void
update_disassembly( void )
{
size_t i, length; libspectrum_word address;
TCHAR buffer[80];
TCHAR *disassembly_text[2] = { &buffer[0], &buffer[40] };
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_PC,
LVM_DELETEALLITEMS, 0, 0 );
LV_ITEM lvi;
lvi.mask = LVIF_TEXT;
for( i = 0, address = disassembly_top; i < disassembly_page; i++ ) {
int l;
_sntprintf( disassembly_text[0], 40, format_16_bit(), address );
debugger_disassemble( disassembly_text[1], 40, &length, address );
/* pad to 16 characters (long instruction) to avoid varying width */
l = _tcslen( disassembly_text[1] );
while( l < 16 ) disassembly_text[1][l++] = ' ';
disassembly_text[1][l] = 0;
address += length;
/* append the item */
lvi.iItem = i;
lvi.iSubItem = 0;
lvi.pszText = disassembly_text[0];
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_PC, LVM_INSERTITEM, 0,
( LPARAM ) &lvi );
lvi.iSubItem = 1;
lvi.pszText = disassembly_text[1];
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_PC, LVM_SETITEM, 0,
( LPARAM ) &lvi );
}
disassembly_bottom = address;
}
static void
update_events( void )
{
/* clear the listview */
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_EVENTS,
LVM_DELETEALLITEMS, 0, 0 );
event_foreach( add_event, NULL );
}
static void
add_event( gpointer data, gpointer user_data GCC_UNUSED )
{
event_t *ptr = data;
TCHAR buffer[80];
TCHAR *event_text[2] = { &buffer[0], &buffer[40] };
LV_ITEM lvi;
lvi.mask = LVIF_TEXT;
/* Skip events which have been removed */
if( ptr->type == event_type_null ) return;
_sntprintf( event_text[0], 40, "%d", ptr->tstates );
/* FIXME: event_name() is not unicode compliant */
_tcsncpy( event_text[1], event_name( ptr->type ), 40 );
/* append the item */
lvi.iItem = SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_EVENTS,
LVM_GETITEMCOUNT, 0, 0 );
lvi.iSubItem = 0;
lvi.pszText = event_text[0];
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_EVENTS, LVM_INSERTITEM, 0,
( LPARAM ) &lvi );
lvi.iSubItem = 1;
lvi.pszText = event_text[1];
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_EVENTS, LVM_SETITEM, 0,
( LPARAM ) &lvi );
}
static int
deactivate_debugger( void )
{
PostMessage( fuse_hWnd, WM_USER_EXIT_PROCESS_MESSAGES, 0, 0 );
debugger_active = 0;
fuse_emulation_unpause();
return 0;
}
/* Set the disassembly to start at 'address' */
int
ui_debugger_disassemble( libspectrum_word address )
{
/* Note: the scroll bar can not cope with "upper bound - page_size" value and
higher. PC register, key and wheel scrolling are fine with that */
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
si.nPos = disassembly_top = address;
SetScrollInfo( GetDlgItem( fuse_hDBGWnd, IDC_DBG_SB_PC ),
SB_CTL, &si, TRUE );
/* And update the disassembly if the debugger is active */
if( debugger_active ) update_disassembly();
return 0;
}
/* Called when the disassembly scrollbar is moved */
static int
move_disassembly( WPARAM scroll_command )
{
libspectrum_word address;
int cursor_row;
/* in Windows we have to read the command and scroll the scrollbar manually */
switch( LOWORD( scroll_command ) ) {
case SB_BOTTOM:
if( disassembly_bottom == disassembly_min ) return 0;
address = debugger_search_instruction( disassembly_min,
-disassembly_page );
break;
case SB_TOP:
if( disassembly_top == disassembly_min ) return 0;
address = disassembly_min;
break;
case SB_LINEDOWN:
if( disassembly_bottom == disassembly_min ) return 0;
address = debugger_search_instruction( disassembly_top, 1 );
break;
case SB_LINEUP:
if( disassembly_top == disassembly_min ) return 0;
address = debugger_search_instruction( disassembly_top, -1);
break;
case SB_PAGEUP:
address = debugger_search_instruction( disassembly_top,
-disassembly_page );
break;
case SB_PAGEDOWN:
address = debugger_search_instruction( disassembly_top,
disassembly_page );
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
/* just set disassembly_top to that value */
address = HIWORD( scroll_command );
/* The scrollbar should constrain to min/max values */
if( address > disassembly_max - disassembly_page )
address = debugger_search_instruction( disassembly_min,
-disassembly_page );
break;
default:
return 1;
}
/* Get selected row */
cursor_row = ListView_GetNextItem( GetDlgItem( fuse_hDBGWnd, IDC_DBG_LV_PC ),
-1, LVNI_SELECTED );
/* Scroll to new position */
ui_debugger_disassemble( address );
/* Mark selected row */
if( cursor_row >= 0 ) {
ListView_SetItemState( GetDlgItem( fuse_hDBGWnd, IDC_DBG_LV_PC ),
cursor_row, LVIS_FOCUSED|LVIS_SELECTED,
LVIS_FOCUSED|LVIS_SELECTED );
}
return 0;
}
/* Evaluate the command currently in the entry box */
static void
evaluate_command( void )
{
TCHAR *buffer;
int buffer_size;
/* poll the size of the value in Evaluate text box first */
buffer_size = SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_ED_EVAL, WM_GETTEXTLENGTH,
(WPARAM) 0, (LPARAM) 0 );
buffer = malloc( ( buffer_size + 1 ) * sizeof( TCHAR ) );
if( buffer == NULL ) {
ui_error( UI_ERROR_ERROR, "Out of memory in %s.", __func__ );
return;
}
/* get the value in Evaluate text box first */
if( SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_ED_EVAL, WM_GETTEXT,
(WPARAM) ( buffer_size + 1 ),
(LPARAM) buffer ) != buffer_size ) {
ui_error( UI_ERROR_ERROR,
"Couldn't get the content of the Evaluate text box" );
return;
}
/* FIXME: need to convert from TCHAR to char to comply with unicode */
debugger_command_evaluate( buffer );
free( buffer );
}
static void
win32ui_debugger_done_step( void )
{
debugger_step();
}
static void
win32ui_debugger_done_continue( void )
{
debugger_run();
}
static void
win32ui_debugger_break( void )
{
debugger_mode = DEBUGGER_MODE_HALTED;
EnableWindow( GetDlgItem( fuse_hDBGWnd, IDC_DBG_BTN_CONT ), TRUE);
EnableWindow( GetDlgItem( fuse_hDBGWnd, IDC_DBG_BTN_BREAK ), FALSE );
}
static void
delete_dialog( void )
{
win32ui_debugger_done_close();
}
static void
win32ui_debugger_done_close( void )
{
ShowWindow( fuse_hDBGWnd, SW_HIDE );
win32ui_debugger_done_continue();
}
static INT_PTR CALLBACK
win32ui_debugger_proc( HWND hWnd GCC_UNUSED, UINT msg,
WPARAM wParam, LPARAM lParam )
{
switch( msg ) {
case WM_COMMAND:
switch( LOWORD( wParam ) ) {
case IDCLOSE:
case IDCANCEL:
win32ui_debugger_done_close();
return 0;
case IDC_DBG_BTN_CONT:
win32ui_debugger_done_continue();
return 0;
case IDC_DBG_BTN_BREAK:
win32ui_debugger_break();
return 0;
case IDC_DBG_BTN_STEP:
win32ui_debugger_done_step();
return 0;
case IDC_DBG_BTN_EVAL:
evaluate_command();
return 0;
/* menus */
case IDM_DBG_REG:
toggle_display( DEBUGGER_PANE_REGISTERS, IDM_DBG_REG );
return 0;
case IDM_DBG_MEMMAP:
toggle_display( DEBUGGER_PANE_MEMORYMAP, IDM_DBG_MEMMAP );
return 0;
case IDM_DBG_BPS:
toggle_display( DEBUGGER_PANE_BREAKPOINTS, IDM_DBG_BPS );
return 0;
case IDM_DBG_DIS:
toggle_display( DEBUGGER_PANE_DISASSEMBLY, IDM_DBG_DIS );
return 0;
case IDM_DBG_STACK:
toggle_display( DEBUGGER_PANE_STACK, IDM_DBG_STACK );
return 0;
case IDM_DBG_EVENTS:
toggle_display( DEBUGGER_PANE_EVENTS, IDM_DBG_EVENTS );
return 0;
}
break;
case WM_CLOSE:
delete_dialog();
return 0;
case WM_NOTIFY:
switch ( ( ( LPNMHDR ) lParam )->code ) {
case NM_DBLCLK:
switch( ( ( LPNMHDR ) lParam)->idFrom ) {
case IDC_DBG_LV_EVENTS:
events_click( ( LPNMITEMACTIVATE ) lParam );
return 0;
case IDC_DBG_LV_STACK:
stack_click( ( LPNMITEMACTIVATE ) lParam );
return 0;
}
}
break;
case WM_VSCROLL:
if( ( HWND ) lParam != NULL )
if( ! move_disassembly( wParam ) )
return 0;
break;
}
return FALSE;
}
static LRESULT CALLBACK
disassembly_key_press( HWND hWnd, WPARAM wParam )
{
libspectrum_word initial_top, address;
int cursor_row;
initial_top = disassembly_top;
/* Get selected row */
cursor_row = ListView_GetNextItem( hWnd, -1, LVNI_SELECTED );
switch( wParam ) {
case VK_DOWN:
if( cursor_row == disassembly_page - 1 ) {
address = debugger_search_instruction( disassembly_top, 1 );
ui_debugger_disassemble( address );
}
break;
case VK_UP:
if( cursor_row == 0 ) {
address = debugger_search_instruction( disassembly_top, -1 );
ui_debugger_disassemble( address );
}
break;
case VK_NEXT:
ui_debugger_disassemble( disassembly_bottom );
break;
case VK_PRIOR:
address = debugger_search_instruction( disassembly_top,
-disassembly_page );
ui_debugger_disassemble( address );
break;
case VK_HOME:
cursor_row = 0;
ui_debugger_disassemble( disassembly_min );
break;
case VK_END:
cursor_row = disassembly_page - 1;
address = debugger_search_instruction( disassembly_min,
-disassembly_page );
ui_debugger_disassemble( address );
break;
}
if( initial_top != disassembly_top ) {
/* Mark selected row */
if( cursor_row >= 0 ) {
ListView_SetItemState( hWnd, cursor_row, LVIS_FOCUSED|LVIS_SELECTED,
LVIS_FOCUSED|LVIS_SELECTED );
}
return TRUE;
}
return FALSE;
}
static LRESULT CALLBACK
disassembly_wheel_scroll( HWND hWnd, WPARAM wParam )
{
libspectrum_word address;
int cursor_row;
short delta;
/* Get selected row */
cursor_row = ListView_GetNextItem( hWnd, -1, LVNI_SELECTED );
/* Convert wheel displacement to instruction displacement */
delta = (short) HIWORD( wParam ) / WHEEL_DELTA;
/* Scroll to new position */
address = debugger_search_instruction( disassembly_top, -delta );
ui_debugger_disassemble( address );
/* Mark selected row */
if( cursor_row >= 0 ) {
ListView_SetItemState( hWnd, cursor_row, LVIS_FOCUSED|LVIS_SELECTED,
LVIS_FOCUSED|LVIS_SELECTED );
}
return TRUE;
}
static LRESULT CALLBACK
disassembly_listview_proc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
WNDPROC orig_proc;
switch( msg ) {
case WM_DESTROY:
orig_proc = (WNDPROC) GetProp( hWnd, "original_proc" );
SetWindowLongPtr( hWnd, GWLP_WNDPROC, (LONG_PTR) orig_proc );
RemoveProp( hWnd, "original_proc" );
break;
case WM_KEYDOWN:
if( disassembly_key_press( hWnd, wParam ) ) return 0;
break;
case WM_MOUSEWHEEL:
disassembly_wheel_scroll( hWnd, wParam );
return 0;
}
orig_proc = (WNDPROC) GetProp( hWnd, "original_proc" );
return CallWindowProc( orig_proc, hWnd, msg, wParam, lParam );
}