2020-12-05 13:08:24 +01:00
# include "debugger_list.h"
2020-02-22 20:42:49 +01:00
# include "gui_settings.h"
2021-01-22 09:11:54 +01:00
# include "qt_utils.h"
2020-02-22 20:42:49 +01:00
# include "breakpoint_handler.h"
2018-03-02 22:40:29 +01:00
# include "Emu/Cell/SPUThread.h"
2020-02-22 20:42:49 +01:00
# include "Emu/Cell/PPUThread.h"
# include "Emu/CPU/CPUDisAsm.h"
# include "Emu/CPU/CPUThread.h"
2021-01-22 09:11:54 +01:00
# include "Emu/RSX/RSXDisAsm.h"
2022-03-25 16:17:25 +01:00
# include "Emu/RSX/RSXThread.h"
2018-03-02 22:40:29 +01:00
# include "Emu/System.h"
# include <QMouseEvent>
# include <QWheelEvent>
2021-01-22 09:11:54 +01:00
# include <QVBoxLayout>
# include <QLabel>
2018-03-02 22:40:29 +01:00
# include <memory>
constexpr auto qstr = QString : : fromStdString ;
2021-04-07 23:05:18 +02:00
debugger_list : : debugger_list ( QWidget * parent , std : : shared_ptr < gui_settings > gui_settings , breakpoint_handler * handler )
2020-02-21 13:20:10 +01:00
: QListWidget ( parent )
2021-04-07 23:05:18 +02:00
, m_gui_settings ( std : : move ( gui_settings ) )
2021-10-15 01:26:51 +02:00
, m_ppu_breakpoint_handler ( handler )
2018-03-02 22:40:29 +01:00
{
setWindowTitle ( tr ( " ASM " ) ) ;
2022-05-13 18:11:05 +02:00
2018-03-02 22:40:29 +01:00
for ( uint i = 0 ; i < m_item_count ; + + i )
{
insertItem ( i , new QListWidgetItem ( " " ) ) ;
}
2022-05-13 18:11:05 +02:00
2018-03-02 22:40:29 +01:00
setSizeAdjustPolicy ( QListWidget : : AdjustToContents ) ;
2022-05-13 18:11:05 +02:00
setVerticalScrollBarPolicy ( Qt : : ScrollBarAlwaysOff ) ;
2022-07-29 17:58:38 +02:00
connect ( this , & QListWidget : : currentRowChanged , this , [ this ] ( int row )
{
if ( row < 0 )
{
m_selected_instruction = - 1 ;
m_showing_selected_instruction = false ;
return ;
}
u32 pc = m_start_addr ;
for ( ; m_cpu & & m_cpu - > id_type ( ) = = 0x55 & & row ; row - - )
{
// If scrolling forwards (downwards), we can skip entire commands
pc + = std : : max < u32 > ( m_disasm - > disasm ( pc ) , 4 ) ;
}
m_selected_instruction = pc + row * 4 ;
} ) ;
2018-03-02 22:40:29 +01:00
}
2021-01-22 09:11:54 +01:00
void debugger_list : : UpdateCPUData ( cpu_thread * cpu , CPUDisAsm * disasm )
2018-03-02 22:40:29 +01:00
{
2022-05-01 14:48:37 +02:00
if ( m_cpu ! = cpu )
{
m_cpu = cpu ;
m_selected_instruction = - 1 ;
2022-07-29 17:58:38 +02:00
m_showing_selected_instruction = false ;
2022-05-01 14:48:37 +02:00
}
2018-03-02 22:40:29 +01:00
m_disasm = disasm ;
}
2022-07-29 17:58:38 +02:00
u32 debugger_list : : GetStartAddress ( u32 address )
2018-03-02 22:40:29 +01:00
{
2022-07-29 17:58:38 +02:00
const u32 steps = m_item_count / 3 ;
u32 result = address ;
if ( m_cpu & & m_cpu - > id_type ( ) = = 0x55 )
{
if ( auto [ count , res ] = static_cast < rsx : : thread * > ( m_cpu ) - > try_get_pc_of_x_cmds_backwards ( steps , address ) ; count = = steps )
{
result = res ;
}
}
else
{
result = address - ( steps * 4 ) ;
}
if ( address > m_pc | | m_start_addr > address )
{
m_pc = address ;
m_start_addr = result ;
}
return m_start_addr ;
2018-03-02 22:40:29 +01:00
}
2022-07-29 17:58:38 +02:00
void debugger_list : : ShowAddress ( u32 addr , bool select_addr , bool direct )
2018-03-02 22:40:29 +01:00
{
2022-09-13 15:08:55 +02:00
const decltype ( spu_thread : : local_breakpoints ) * spu_bps_list { } ;
2021-10-15 01:26:51 +02:00
if ( m_cpu & & m_cpu - > id_type ( ) = = 2 )
{
spu_bps_list = & static_cast < spu_thread * > ( m_cpu ) - > local_breakpoints ;
}
auto IsBreakpoint = [ & ] ( u32 pc )
2018-03-02 22:40:29 +01:00
{
2021-10-15 01:26:51 +02:00
switch ( m_cpu ? m_cpu - > id_type ( ) : 0 )
{
case 1 : return m_ppu_breakpoint_handler - > HasBreakpoint ( pc ) ;
case 2 : return ( * spu_bps_list ) [ pc / 4 ] . load ( ) ;
default : return false ;
}
2018-03-02 22:40:29 +01:00
} ;
2022-07-29 17:58:38 +02:00
if ( select_addr | | direct )
2021-01-22 09:11:54 +01:00
{
2022-09-13 15:08:55 +02:00
// The user wants to survey a specific memory location, do not interfere from this point forth
2022-05-01 14:04:56 +02:00
m_follow_thread = false ;
2021-01-22 09:11:54 +01:00
}
2022-09-20 09:19:35 +02:00
m_dirty_flag = false ;
2022-07-29 17:58:38 +02:00
u32 pc = m_start_addr ;
if ( ! direct & & ( m_follow_thread | | select_addr ) )
2018-03-02 22:40:29 +01:00
{
2022-07-29 17:58:38 +02:00
pc = GetStartAddress ( addr ) ;
2018-03-02 22:40:29 +01:00
}
2021-03-23 20:39:39 +01:00
const auto & default_foreground = palette ( ) . color ( foregroundRole ( ) ) ;
const auto & default_background = palette ( ) . color ( backgroundRole ( ) ) ;
2020-06-13 22:17:51 +02:00
2022-07-29 17:58:38 +02:00
m_showing_selected_instruction = false ;
2022-05-01 14:48:37 +02:00
if ( select_addr )
{
m_selected_instruction = addr ;
}
for ( uint i = 0 ; i < m_item_count ; + + i )
{
if ( auto list_item = item ( i ) ; list_item - > isSelected ( ) )
{
list_item - > setSelected ( false ) ;
}
}
2021-01-22 09:11:54 +01:00
if ( ! m_cpu | | ! m_disasm | | + m_cpu - > state + cpu_flag : : exit + cpu_flag : : wait = = + m_cpu - > state )
2018-03-02 22:40:29 +01:00
{
2020-06-13 22:37:56 +02:00
for ( uint i = 0 ; i < m_item_count ; + + i )
2018-03-02 22:40:29 +01:00
{
2021-03-23 20:39:39 +01:00
QListWidgetItem * list_item = item ( i ) ;
list_item - > setText ( qstr ( fmt : : format ( " [%08x] ?? ?? ?? ??: " , 0 ) ) ) ;
list_item - > setForeground ( default_foreground ) ;
list_item - > setBackground ( default_background ) ;
2018-03-02 22:40:29 +01:00
}
}
else
{
2021-01-22 09:11:54 +01:00
const bool is_spu = m_cpu - > id_type ( ) = = 2 ;
2020-03-19 06:11:59 +01:00
const u32 address_limits = ( is_spu ? 0x3fffc : ~ 3 ) ;
2022-08-26 21:23:57 +02:00
const u32 current_pc = m_cpu - > get_pc ( ) ;
2022-07-29 17:58:38 +02:00
m_start_addr & = address_limits ;
2022-09-13 15:08:55 +02:00
pc = m_start_addr ;
2020-04-01 00:02:24 +02:00
2021-03-23 20:39:39 +01:00
for ( uint i = 0 , count = 4 ; i < m_item_count ; + + i , pc = ( pc + count ) & address_limits )
2018-03-02 22:40:29 +01:00
{
2021-03-23 20:39:39 +01:00
QListWidgetItem * list_item = item ( i ) ;
2022-08-26 21:23:57 +02:00
if ( pc = = current_pc )
2020-06-13 22:17:51 +02:00
{
2021-03-23 20:39:39 +01:00
list_item - > setForeground ( m_text_color_pc ) ;
list_item - > setBackground ( m_color_pc ) ;
2020-06-13 22:17:51 +02:00
}
2022-05-01 14:48:37 +02:00
else if ( pc = = m_selected_instruction )
2022-05-01 14:04:56 +02:00
{
2022-05-01 14:48:37 +02:00
// setSelected may invoke a resize event which causes stack overflow, terminate recursion
if ( ! list_item - > isSelected ( ) )
{
list_item - > setSelected ( true ) ;
}
2022-07-29 17:58:38 +02:00
m_showing_selected_instruction = true ;
2022-05-01 14:04:56 +02:00
}
2020-06-13 22:17:51 +02:00
else if ( IsBreakpoint ( pc ) )
{
2021-03-23 20:39:39 +01:00
list_item - > setForeground ( m_text_color_bp ) ;
list_item - > setBackground ( m_color_bp ) ;
2020-06-13 22:17:51 +02:00
}
else
{
2021-03-23 20:39:39 +01:00
list_item - > setForeground ( default_foreground ) ;
list_item - > setBackground ( default_background ) ;
2020-06-13 22:17:51 +02:00
}
2021-03-23 11:33:07 +01:00
if ( m_cpu - > id_type ( ) = = 1 & & ! vm : : check_addr ( pc , 0 ) )
2018-03-02 22:40:29 +01:00
{
2021-03-23 20:39:39 +01:00
list_item - > setText ( ( IsBreakpoint ( pc ) ? " >> " : " " ) + qstr ( fmt : : format ( " [%08x] ?? ?? ?? ??: " , pc ) ) ) ;
2018-03-02 22:40:29 +01:00
count = 4 ;
continue ;
}
2021-01-22 09:11:54 +01:00
if ( m_cpu - > id_type ( ) = = 1 & & ! vm : : check_addr ( pc , vm : : page_executable ) )
2020-05-22 03:48:10 +02:00
{
2020-07-17 10:18:04 +02:00
const u32 data = * vm : : get_super_ptr < atomic_be_t < u32 > > ( pc ) ;
2021-03-23 20:39:39 +01:00
list_item - > setText ( ( IsBreakpoint ( pc ) ? " >> " : " " ) + qstr ( fmt : : format ( " [%08x] %02x %02x %02x %02x: " , pc ,
2020-05-22 03:48:10 +02:00
static_cast < u8 > ( data > > 24 ) ,
static_cast < u8 > ( data > > 16 ) ,
static_cast < u8 > ( data > > 8 ) ,
static_cast < u8 > ( data > > 0 ) ) ) ) ;
count = 4 ;
continue ;
}
2020-12-16 07:53:59 +01:00
count = m_disasm - > disasm ( pc ) ;
2018-03-02 22:40:29 +01:00
2021-01-22 09:11:54 +01:00
if ( ! count )
{
2021-03-23 20:39:39 +01:00
list_item - > setText ( ( IsBreakpoint ( pc ) ? " >> " : " " ) + qstr ( fmt : : format ( " [%08x] ??? ?? ?? " , pc ) ) ) ;
2021-01-22 09:11:54 +01:00
count = 4 ;
continue ;
}
2021-03-23 20:39:39 +01:00
list_item - > setText ( ( IsBreakpoint ( pc ) ? " >> " : " " ) + qstr ( m_disasm - > last_opcode ) ) ;
2018-03-02 22:40:29 +01:00
}
}
setLineWidth ( - 1 ) ;
}
2022-05-01 14:04:56 +02:00
void debugger_list : : RefreshView ( )
{
const bool old = std : : exchange ( m_follow_thread , false ) ;
ShowAddress ( 0 , false ) ;
m_follow_thread = old ;
}
void debugger_list : : EnableThreadFollowing ( bool enable )
{
m_follow_thread = enable ;
}
2021-01-22 09:11:54 +01:00
void debugger_list : : scroll ( s32 steps )
{
2022-07-29 17:58:38 +02:00
for ( ; m_cpu & & m_cpu - > id_type ( ) = = 0x55 & & steps > 0 ; steps - - )
2021-01-22 09:11:54 +01:00
{
// If scrolling forwards (downwards), we can skip entire commands
2022-07-29 17:58:38 +02:00
m_start_addr + = std : : max < u32 > ( m_disasm - > disasm ( m_start_addr ) , 4 ) ;
2021-01-22 09:11:54 +01:00
}
2022-03-25 16:17:25 +01:00
if ( m_cpu & & m_cpu - > id_type ( ) = = 0x55 & & steps < 0 )
{
2022-09-13 15:08:55 +02:00
// If scrolling backwards (upwards), try to obtain the start of commands tail
2022-07-29 17:58:38 +02:00
if ( auto [ count , res ] = static_cast < rsx : : thread * > ( m_cpu ) - > try_get_pc_of_x_cmds_backwards ( - steps , m_start_addr ) ; count = = 0u - steps )
2022-03-25 16:17:25 +01:00
{
steps = 0 ;
2022-07-29 17:58:38 +02:00
m_start_addr = res ;
2022-03-25 16:17:25 +01:00
}
}
2022-05-01 14:04:56 +02:00
EnableThreadFollowing ( false ) ;
2022-07-29 17:58:38 +02:00
m_start_addr + = steps * 4 ;
ShowAddress ( 0 , false , true ) ;
2021-01-22 09:11:54 +01:00
}
2018-03-02 22:40:29 +01:00
void debugger_list : : keyPressEvent ( QKeyEvent * event )
{
2023-05-30 10:30:44 +02:00
// Always accept event (so it would not bubble upwards, debugger_frame already sees it)
struct accept_event_t
{
QKeyEvent * event ;
~ accept_event_t ( ) noexcept
{
event - > accept ( ) ;
}
} accept_event { event } ;
2020-11-23 18:57:34 +01:00
if ( ! isActiveWindow ( ) )
2018-03-02 22:40:29 +01:00
{
2023-05-28 09:57:15 +02:00
QListWidget : : keyPressEvent ( event ) ;
2018-03-02 22:40:29 +01:00
return ;
}
2023-04-30 04:21:14 +02:00
if ( event - > modifiers ( ) )
{
2023-05-28 09:57:15 +02:00
QListWidget : : keyPressEvent ( event ) ;
2023-04-30 04:21:14 +02:00
return ;
}
2018-03-02 22:40:29 +01:00
switch ( event - > key ( ) )
{
2021-01-22 09:11:54 +01:00
case Qt : : Key_PageUp : scroll ( 0 - m_item_count ) ; return ;
case Qt : : Key_PageDown : scroll ( m_item_count ) ; return ;
case Qt : : Key_Up : scroll ( 1 ) ; return ;
case Qt : : Key_Down : scroll ( - 1 ) ; return ;
case Qt : : Key_I :
{
2021-06-12 15:04:33 +02:00
if ( event - > isAutoRepeat ( ) )
{
2023-05-28 09:57:15 +02:00
QListWidget : : keyPressEvent ( event ) ;
2021-06-12 15:04:33 +02:00
return ;
}
2023-05-28 09:57:15 +02:00
2021-01-22 09:11:54 +01:00
if ( m_cpu & & m_cpu - > id_type ( ) = = 0x55 )
{
2022-07-29 17:58:38 +02:00
create_rsx_command_detail ( m_showing_selected_instruction ? m_selected_instruction : m_pc ) ;
2021-01-22 09:11:54 +01:00
return ;
}
2023-05-28 09:57:15 +02:00
break ;
2021-01-22 09:11:54 +01:00
}
2018-03-02 22:40:29 +01:00
default : break ;
}
2023-05-28 09:57:15 +02:00
QListWidget : : keyPressEvent ( event ) ;
2018-03-02 22:40:29 +01:00
}
2021-01-22 09:11:54 +01:00
void debugger_list : : showEvent ( QShowEvent * event )
{
if ( m_cmd_detail ) m_cmd_detail - > show ( ) ;
QListWidget : : showEvent ( event ) ;
}
void debugger_list : : hideEvent ( QHideEvent * event )
{
if ( m_cmd_detail ) m_cmd_detail - > hide ( ) ;
QListWidget : : hideEvent ( event ) ;
}
2022-07-29 17:58:38 +02:00
void debugger_list : : create_rsx_command_detail ( u32 pc )
2021-01-22 09:11:54 +01:00
{
2021-10-12 22:12:30 +02:00
RSXDisAsm rsx_dis = * static_cast < RSXDisAsm * > ( m_disasm ) ;
rsx_dis . change_mode ( cpu_disasm_mode : : list ) ;
2021-01-22 09:11:54 +01:00
// Either invalid or not a method
if ( rsx_dis . disasm ( pc ) < = 4 ) return ;
if ( m_cmd_detail )
{
// Edit the existing dialog
m_detail_label - > setText ( QString : : fromStdString ( rsx_dis . last_opcode ) ) ;
m_cmd_detail - > setFixedSize ( m_cmd_detail - > sizeHint ( ) ) ;
return ;
}
m_cmd_detail = new QDialog ( this ) ;
m_cmd_detail - > setWindowTitle ( tr ( " RSX Command Detail " ) ) ;
m_detail_label = new QLabel ( QString : : fromStdString ( rsx_dis . last_opcode ) , this ) ;
m_detail_label - > setFont ( font ( ) ) ;
gui : : utils : : set_font_size ( * m_detail_label , 10 ) ;
m_detail_label - > setTextInteractionFlags ( Qt : : TextSelectableByMouse | Qt : : TextSelectableByKeyboard ) ;
QVBoxLayout * layout = new QVBoxLayout ( this ) ;
layout - > addWidget ( m_detail_label ) ;
m_cmd_detail - > setLayout ( layout ) ;
m_cmd_detail - > setFixedSize ( m_cmd_detail - > sizeHint ( ) ) ;
m_cmd_detail - > show ( ) ;
connect ( m_cmd_detail , & QDialog : : finished , [ this ] ( int )
{
// Cleanup
std : : exchange ( m_cmd_detail , nullptr ) - > deleteLater ( ) ;
} ) ;
}
2018-03-02 22:40:29 +01:00
void debugger_list : : mouseDoubleClickEvent ( QMouseEvent * event )
{
2020-12-16 15:44:41 +01:00
if ( event - > button ( ) = = Qt : : LeftButton )
2018-03-02 22:40:29 +01:00
{
2022-07-29 17:58:38 +02:00
int i = currentRow ( ) ;
2018-03-02 22:40:29 +01:00
if ( i < 0 ) return ;
2022-07-29 17:58:38 +02:00
u32 pc = m_start_addr ;
for ( ; m_cpu & & m_cpu - > id_type ( ) = = 0x55 & & i ; i - - )
{
// If scrolling forwards (downwards), we can skip entire commands
pc + = std : : max < u32 > ( m_disasm - > disasm ( pc ) , 4 ) ;
}
pc + = i * 4 ;
m_selected_instruction = pc ;
2018-03-02 22:40:29 +01:00
// Let debugger_frame know about breakpoint.
// Other option is to add to breakpoint manager directly and have a signal there instead.
// Either the flow goes from debugger_list->breakpoint_manager->debugger_frame, or it goes debugger_list->debugger_frame, and I felt this was easier to read for now.
Q_EMIT BreakpointRequested ( pc ) ;
}
}
void debugger_list : : wheelEvent ( QWheelEvent * event )
{
2021-03-23 20:39:39 +01:00
const QPoint numSteps = event - > angleDelta ( ) / 8 / 15 ; // http://doc.qt.io/qt-5/qwheelevent.html#pixelDelta
2018-03-02 22:40:29 +01:00
const int value = numSteps . y ( ) ;
2020-04-01 00:02:24 +02:00
const auto direction = ( event - > modifiers ( ) = = Qt : : ControlModifier ) ;
2018-03-02 22:40:29 +01:00
2021-01-22 09:11:54 +01:00
scroll ( direction ? value : - value ) ;
2018-03-02 22:40:29 +01:00
}
void debugger_list : : resizeEvent ( QResizeEvent * event )
{
2022-05-13 18:11:05 +02:00
QListWidget : : resizeEvent ( event ) ;
2018-03-02 22:40:29 +01:00
if ( count ( ) < 1 | | visualItemRect ( item ( 0 ) ) . height ( ) < 1 )
{
return ;
}
2022-05-13 17:20:57 +02:00
const u32 old_size = m_item_count ;
2018-03-02 22:40:29 +01:00
2022-09-13 15:08:55 +02:00
// It is fine if the QWidgetList is a tad bit larger than the frame
2022-05-13 18:11:05 +02:00
m_item_count = utils : : aligned_div < u32 > ( rect ( ) . height ( ) - frameWidth ( ) * 2 , visualItemRect ( item ( 0 ) ) . height ( ) ) ;
2018-03-02 22:40:29 +01:00
2022-05-13 17:20:57 +02:00
if ( old_size < = m_item_count )
{
for ( u32 i = old_size ; i < m_item_count ; + + i )
{
insertItem ( i , new QListWidgetItem ( " " ) ) ;
2022-09-20 09:19:35 +02:00
m_dirty_flag = true ;
2022-05-13 17:20:57 +02:00
}
}
else
{
for ( u32 i = old_size - 1 ; i > = m_item_count ; - - i )
{
delete takeItem ( i ) ;
}
}
2018-03-02 22:40:29 +01:00
}