1
0
mirror of https://git.code.sf.net/p/fuse-emulator/fuse synced 2026-01-28 14:20:54 +03:00
Files
fuse/ui/win32/debugger.c
2011-06-06 22:02:44 +00:00

1171 lines
33 KiB
C

/* debugger.c: the Win32 debugger
Copyright (c) 2004 Philip Kendall, Marek Januszewski
$Id$
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 <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.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 int update_memory_map( void );
static int update_breakpoints( void );
static int update_disassembly( void );
static int 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 );
/* The top line of the current disassembly */
static libspectrum_word disassembly_top;
/* helper constants for disassembly listview's scrollbar */
static const int disassembly_min = 0x0000;
static const int disassembly_max = 0xffff;
static const float disassembly_step = 0.5;
/* 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;
}
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()
{
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 < 18; i++ ) {
win32ui_set_font( fuse_hDBGWnd, IDC_DBG_REG_PC + i, font );
}
return 0;
}
static int
create_breakpoints()
{
size_t i;
TCHAR *breakpoint_titles[] = { "ID", "Type", "Value", "Ignore", "Life",
"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 = 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;
TCHAR *disassembly_titles[] = { TEXT( "Address" ), TEXT( "Instruction" ) };
/* The disassembly listview itself */
/* 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;
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_PC,
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 < 2; i++ ) {
if( i != 0 ) {
lvc.mask |= LVCF_SUBITEM;
}
lvc.cx = ( i == 0 ) ? 65 : 125;
lvc.pszText = disassembly_titles[i];
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_PC, LVM_INSERTCOLUMN, i,
( LPARAM ) &lvc );
}
win32ui_set_font( fuse_hDBGWnd, IDC_DBG_LV_PC, font );
/* 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;
TCHAR *stack_titles[] = { "Address", "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 );
/* create columns */
LVCOLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT ;
lvc.fmt = LVCFMT_LEFT;
for( i = 0; i < 2; i++ ) {
if( i != 0 )
lvc.mask |= LVCF_SUBITEM;
lvc.cx = ( i == 0 ) ? 65 : 55;
lvc.pszText = stack_titles[i];
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_STACK, LVM_INSERTCOLUMN, i,
( LPARAM ) &lvc );
}
win32ui_set_font( fuse_hDBGWnd, IDC_DBG_LV_STACK, font );
return 0;
}
static void
stack_click( LPNMITEMACTIVATE lpnmitem )
{
libspectrum_word address, destination;
int error, row;
row = lpnmitem->iItem;
if( row < 0 ) return;
address = SP + ( 19 - row ) * 2;
destination =
readbyte_internal( address ) + readbyte_internal( address + 1 ) * 0x100;
error = debugger_breakpoint_add_address(
DEBUGGER_BREAKPOINT_TYPE_EXECUTE, memory_source_any, 0, address, 0,
DEBUGGER_BREAKPOINT_LIFE_ONESHOT, NULL
);
if( error ) return;
debugger_run();
}
static int
create_events()
{
size_t i;
TCHAR *titles[] = { "Time", "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_WIDTH | LVCF_TEXT ;
lvc.fmt = LVCFMT_LEFT;
for( i = 0; i < 2; i++ ) {
if( i != 0 )
lvc.mask |= LVCF_SUBITEM;
lvc.cx = ( i == 0 )? 40 : 75;
lvc.pszText = titles[i];
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_EVENTS, LVM_INSERTCOLUMN, i,
( LPARAM ) &lvc );
}
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;
int error;
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( "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 the memory map display */
error = update_memory_map(); if( error ) return error;
error = update_breakpoints(); if( error ) return error;
/* Update the disassembly */
error = update_disassembly(); if( error ) return error;
/* 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 */
error = update_events(); if( error ) return error;
return 0;
}
static int
update_memory_map( void )
{
size_t i;
TCHAR buffer[ 40 ];
for( i = 0; i < 8; i++ ) {
_sntprintf( buffer, 40, format_16_bit(), (unsigned)i * 0x2000 );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_MAP11 + ( i * 4 ),
WM_SETTEXT, ( WPARAM ) 0, ( LPARAM ) buffer );
/* FIXME: memory_source_description is not unicode */
_snprintf( buffer, 40, TEXT( "%s %d" ),
memory_source_description( memory_map_read[i].source ),
memory_map_read[i].page_num );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_MAP11 + ( i * 4 ) + 1,
WM_SETTEXT, ( WPARAM ) 0, ( LPARAM ) buffer );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_MAP11 + ( i * 4 ) + 2,
WM_SETTEXT, (WPARAM) 0,
( LPARAM ) ( memory_map_read[i].writable
? TEXT( "Y" ) : TEXT( "N" ) ) );
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_MAP11 + ( i * 4 ) + 2,
WM_SETTEXT, (WPARAM) 0,
( LPARAM ) ( memory_map_read[i].contended
? TEXT( "Y" ) : TEXT( "N" ) ) );
}
return 0;
}
static int
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 );
}
}
return 0;
}
static int
update_disassembly()
{
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 = SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_PC,
LVM_GETITEMCOUNT, 0, 0 );
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 );
}
return 0;
}
static int
update_events( void )
{
/* clear the listview */
SendDlgItemMessage( fuse_hDBGWnd, IDC_DBG_LV_EVENTS,
LVM_DELETEALLITEMS, 0, 0 );
event_foreach( add_event, NULL );
return 0;
}
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 )
{
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 );
return 0;
}
/* Called when the disassembly scrollbar is moved */
static int
move_disassembly( WPARAM scroll_command )
{
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
GetScrollInfo( GetDlgItem( fuse_hDBGWnd, IDC_DBG_SB_PC ), SB_CTL, &si );
float value = si.nPos;
/* in Windows we have to read the command and scroll the scrollbar manually */
switch( LOWORD( scroll_command ) ) {
case SB_BOTTOM:
value = disassembly_max;
break;
case SB_TOP:
value = disassembly_min;
break;
case SB_LINEDOWN:
value += disassembly_step;
break;
case SB_LINEUP:
value -= disassembly_step;
break;
case SB_PAGEUP:
value -= disassembly_page;
break;
case SB_PAGEDOWN:
value += disassembly_page;
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
value = HIWORD( scroll_command );
break;
default:
return 1;
}
if( value > disassembly_max ) value = disassembly_max;
if( value < disassembly_min ) value = disassembly_min;
/* FIXME when holding sb's down arrow at 0xffff, 0x0000 is showing */
size_t length;
/* disassembly_top < value < disassembly_top + 1 => 'down' button pressed
Move the disassembly on by one instruction */
if( value > disassembly_top && value - disassembly_top < 1 ) {
debugger_disassemble( NULL, 0, &length, disassembly_top );
ui_debugger_disassemble( disassembly_top + length );
/* disassembly_top - 1 < value < disassembly_top => 'up' button pressed
See notes regarding how this function works in GTK's move_disassembly
*/
} else if( value < disassembly_top && disassembly_top - value < 1 ) {
size_t i, longest = 1;
for( i = 1; i <= 8; i++ ) {
debugger_disassemble( NULL, 0, &length, disassembly_top - i );
if( length == i ) longest = i;
}
ui_debugger_disassemble( disassembly_top - longest );
/* Anything else, just set disassembly_top to that value */
} else {
ui_debugger_disassemble( value );
}
/* And update the disassembly if the debugger is active */
if( debugger_active ) update_disassembly();
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;
}