2022-12-24 06:51:44 +00:00
/* PCSX2 - PS2 Emulator for PCs
* Copyright ( C ) 2002 - 2022 PCSX2 Dev Team
*
* PCSX2 is free software : you can redistribute it and / or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found -
* ation , either version 3 of the License , or ( at your option ) any later version .
*
* PCSX2 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 PCSX2 .
* If not , see < http : //www.gnu.org/licenses/>.
*/
# include "PrecompiledHeader.h"
# include "DisassemblyWidget.h"
# include "DebugTools/DebugInterface.h"
# include "DebugTools/DisassemblyManager.h"
# include "DebugTools/Breakpoints.h"
# include "DebugTools/MipsAssembler.h"
# include "QtUtils.h"
# include "QtHost.h"
# include <QtGui/QMouseEvent>
# include <QtWidgets/QMenu>
# include <QtGui/QClipboard>
# include <QtWidgets/QInputDialog>
# include <QtWidgets/QMessageBox>
using namespace QtUtils ;
DisassemblyWidget : : DisassemblyWidget ( QWidget * parent )
: QWidget ( parent )
{
ui . setupUi ( this ) ;
CreateCustomContextMenu ( ) ;
connect ( this , & DisassemblyWidget : : customContextMenuRequested , this , & DisassemblyWidget : : customMenuRequested ) ;
}
DisassemblyWidget : : ~ DisassemblyWidget ( ) = default ;
void DisassemblyWidget : : CreateCustomContextMenu ( )
{
if ( m_contextMenu )
return ; // ???
m_contextMenu = new QMenu ( this ) ;
QAction * action = 0 ;
2022-12-24 11:17:52 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Copy Address " ) , this ) ) ;
2022-12-24 06:51:44 +00:00
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextCopyAddress ) ;
2022-12-24 11:17:52 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Copy Instruction Hex " ) , this ) ) ;
2022-12-24 06:51:44 +00:00
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextCopyInstructionHex ) ;
2022-12-24 11:17:52 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Copy Instruction Text " ) , this ) ) ;
2022-12-24 06:51:44 +00:00
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextCopyInstructionText ) ;
// TODO: Disassemble to file. Do people use that?
m_contextMenu - > addSeparator ( ) ;
2022-12-24 11:17:52 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Assemble new Instruction(s) " ) , this ) ) ;
2022-12-24 06:51:44 +00:00
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextAssembleInstruction ) ;
2023-01-01 04:24:39 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " NOP Instruction(s) " ) , this ) ) ;
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextNoopInstruction ) ;
2022-12-24 06:51:44 +00:00
m_contextMenu - > addSeparator ( ) ;
2022-12-24 11:17:52 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Run to Cursor " ) , this ) ) ;
2022-12-24 06:51:44 +00:00
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextRunToCursor ) ;
2022-12-24 11:17:52 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Jump to Cursor " ) , this ) ) ;
2022-12-24 06:51:44 +00:00
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextJumpToCursor ) ;
2022-12-24 11:17:52 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Toggle Breakpoint " ) , this ) ) ;
2022-12-24 06:51:44 +00:00
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextToggleBreakpoint ) ;
2022-12-24 11:17:52 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Follow Branch " ) , this ) ) ;
2022-12-24 06:51:44 +00:00
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextFollowBranch ) ;
m_contextMenu - > addSeparator ( ) ;
2022-12-24 11:17:52 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Go to Address " ) , this ) ) ;
2022-12-24 06:51:44 +00:00
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextGoToAddress ) ;
2022-12-24 11:17:52 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Go to in Memory View " ) , this ) ) ;
2022-12-24 06:51:44 +00:00
connect ( action , & QAction : : triggered , this , [ this ] ( ) { gotoInMemory ( m_selectedAddressStart ) ; } ) ;
m_contextMenu - > addSeparator ( ) ;
2022-12-24 11:17:52 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Add Function " ) , this ) ) ;
2022-12-24 06:51:44 +00:00
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextAddFunction ) ;
2022-12-24 11:17:52 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Rename Function " ) , this ) ) ;
2022-12-24 06:51:44 +00:00
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextRenameFunction ) ;
2022-12-24 11:17:52 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Remove Function " ) , this ) ) ;
2022-12-24 06:51:44 +00:00
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextRemoveFunction ) ;
2023-10-10 00:31:49 +00:00
m_contextMenu - > addAction ( action = new QAction ( tr ( " Stub (NOP) Function " ) , this ) ) ;
connect ( action , & QAction : : triggered , this , & DisassemblyWidget : : contextStubFunction ) ;
2022-12-24 06:51:44 +00:00
}
void DisassemblyWidget : : contextCopyAddress ( )
{
QGuiApplication : : clipboard ( ) - > setText ( FetchSelectionInfo ( SelectionInfo : : ADDRESS ) ) ;
}
void DisassemblyWidget : : contextCopyInstructionHex ( )
{
QGuiApplication : : clipboard ( ) - > setText ( FetchSelectionInfo ( SelectionInfo : : INSTRUCTIONHEX ) ) ;
}
void DisassemblyWidget : : contextCopyInstructionText ( )
{
QGuiApplication : : clipboard ( ) - > setText ( FetchSelectionInfo ( SelectionInfo : : INSTRUCTIONTEXT ) ) ;
}
void DisassemblyWidget : : contextAssembleInstruction ( )
{
if ( ! m_cpu - > isCpuPaused ( ) )
{
2022-12-24 11:17:52 +00:00
QMessageBox : : warning ( this , tr ( " Assemble Error " ) , tr ( " Unable to change assembly while core is running " ) ) ;
2022-12-24 06:51:44 +00:00
return ;
}
DisassemblyLineInfo line ;
bool ok ;
m_disassemblyManager . getLine ( m_selectedAddressStart , false , line ) ;
2022-12-24 11:17:52 +00:00
QString instruction = QInputDialog : : getText ( this , tr ( " Assemble Instruction " ) , " " ,
2022-12-24 06:51:44 +00:00
QLineEdit : : Normal , QString ( " %1 %2 " ) . arg ( line . name . c_str ( ) ) . arg ( line . params . c_str ( ) ) , & ok ) ;
if ( ! ok )
return ;
u32 encodedInstruction ;
std : : string errorText ;
bool valid = MipsAssembleOpcode ( instruction . toLocal8Bit ( ) . constData ( ) , m_cpu , m_selectedAddressStart , encodedInstruction , errorText ) ;
if ( ! valid )
{
2022-12-24 11:17:52 +00:00
QMessageBox : : warning ( this , tr ( " Assemble Error " ) , QString : : fromStdString ( errorText ) ) ;
2022-12-24 06:51:44 +00:00
return ;
}
else
{
Host : : RunOnCPUThread ( [ this , start = m_selectedAddressStart , end = m_selectedAddressEnd , cpu = m_cpu , val = encodedInstruction ] {
for ( u32 i = start ; i < = end ; i + = 4 )
{
cpu - > write32 ( i , val ) ;
}
QtHost : : RunOnUIThread ( [ this ] { VMUpdate ( ) ; } ) ;
} ) ;
}
}
2023-01-01 04:24:39 +00:00
void DisassemblyWidget : : contextNoopInstruction ( )
{
Host : : RunOnCPUThread ( [ this , start = m_selectedAddressStart , end = m_selectedAddressEnd , cpu = m_cpu ] {
for ( u32 i = start ; i < = end ; i + = 4 )
{
cpu - > write32 ( i , 0x00 ) ;
}
QtHost : : RunOnUIThread ( [ this ] { VMUpdate ( ) ; } ) ;
} ) ;
}
2022-12-24 06:51:44 +00:00
void DisassemblyWidget : : contextRunToCursor ( )
{
2023-07-26 23:51:06 +00:00
Host : : RunOnCPUThread ( [ & ] {
CBreakPoints : : AddBreakPoint ( m_cpu - > getCpuType ( ) , m_selectedAddressStart , true ) ;
m_cpu - > resumeCpu ( ) ;
} ) ;
2022-12-24 06:51:44 +00:00
}
void DisassemblyWidget : : contextJumpToCursor ( )
{
m_cpu - > setPc ( m_selectedAddressStart ) ;
this - > repaint ( ) ;
}
void DisassemblyWidget : : contextToggleBreakpoint ( )
{
2023-01-01 03:43:54 +00:00
if ( ! m_cpu - > isAlive ( ) )
return ;
2022-12-24 06:51:44 +00:00
if ( CBreakPoints : : IsAddressBreakPoint ( m_cpu - > getCpuType ( ) , m_selectedAddressStart ) )
{
Host : : RunOnCPUThread ( [ & ] { CBreakPoints : : RemoveBreakPoint ( m_cpu - > getCpuType ( ) , m_selectedAddressStart ) ; } ) ;
}
else
{
Host : : RunOnCPUThread ( [ & ] { CBreakPoints : : AddBreakPoint ( m_cpu - > getCpuType ( ) , m_selectedAddressStart ) ; } ) ;
}
breakpointsChanged ( ) ;
this - > repaint ( ) ;
}
void DisassemblyWidget : : contextFollowBranch ( )
{
DisassemblyLineInfo line ;
m_disassemblyManager . getLine ( m_selectedAddressStart , true , line ) ;
if ( line . type = = DISTYPE_OPCODE | | line . type = = DISTYPE_MACRO )
{
if ( line . info . isBranch )
gotoAddress ( line . info . branchTarget ) ;
else if ( line . info . hasRelevantAddress )
gotoAddress ( line . info . releventAddress ) ;
}
}
void DisassemblyWidget : : contextGoToAddress ( )
{
bool ok ;
2022-12-24 11:17:52 +00:00
const QString targetString = QInputDialog : : getText ( this , tr ( " Go to address " ) , " " ,
2022-12-24 06:51:44 +00:00
QLineEdit : : Normal , " " , & ok ) ;
if ( ! ok )
return ;
const u32 targetAddress = targetString . toUInt ( & ok , 16 ) & ~ 3 ;
if ( ! ok )
{
2022-12-24 11:17:52 +00:00
QMessageBox : : warning ( this , tr ( " Go to address error " ) , tr ( " Invalid address " ) ) ;
2022-12-24 06:51:44 +00:00
return ;
}
gotoAddress ( targetAddress ) ;
}
void DisassemblyWidget : : contextAddFunction ( )
{
// Get current function
const u32 curAddress = m_selectedAddressStart ;
const u32 curFuncAddr = m_cpu - > GetSymbolMap ( ) . GetFunctionStart ( m_selectedAddressStart ) ;
QString optionaldlgText ;
if ( curFuncAddr ! = SymbolMap : : INVALID_ADDRESS )
{
if ( curFuncAddr = = curAddress ) // There is already a function here
{
2022-12-24 11:17:52 +00:00
QMessageBox : : warning ( this , tr ( " Add Function Error " ) , tr ( " A function entry point already exists here. Consider renaming instead. " ) ) ;
2022-12-24 06:51:44 +00:00
}
else
{
const u32 prevSize = m_cpu - > GetSymbolMap ( ) . GetFunctionSize ( curFuncAddr ) ;
u32 newSize = curAddress - curFuncAddr ;
bool ok ;
2022-12-24 11:17:52 +00:00
QString funcName = QInputDialog : : getText ( this , tr ( " Add Function " ) ,
tr ( " Function will be (0x%1) instructions long. \n Enter function name " ) . arg ( prevSize - newSize , 0 , 16 ) , QLineEdit : : Normal , " " , & ok ) ;
2022-12-24 06:51:44 +00:00
if ( ! ok )
return ;
m_cpu - > GetSymbolMap ( ) . SetFunctionSize ( curFuncAddr , newSize ) ; // End the current function to where we selected
newSize = prevSize - newSize ;
m_cpu - > GetSymbolMap ( ) . AddFunction ( funcName . toLocal8Bit ( ) . constData ( ) , curAddress , newSize ) ;
m_cpu - > GetSymbolMap ( ) . SortSymbols ( ) ;
m_cpu - > GetSymbolMap ( ) . UpdateActiveSymbols ( ) ;
}
}
else
{
bool ok ;
QString funcName = QInputDialog : : getText ( this , " Add Function " ,
2022-12-24 11:17:52 +00:00
tr ( " Function will be (0x%1) instructions long. \n Enter function name " ) . arg ( m_selectedAddressEnd + 4 - m_selectedAddressStart , 0 , 16 ) , QLineEdit : : Normal , " " , & ok ) ;
2022-12-24 06:51:44 +00:00
if ( ! ok )
return ;
m_cpu - > GetSymbolMap ( ) . AddFunction ( funcName . toLocal8Bit ( ) . constData ( ) , m_selectedAddressStart , m_selectedAddressEnd + 4 - m_selectedAddressStart ) ;
m_cpu - > GetSymbolMap ( ) . SortSymbols ( ) ;
m_cpu - > GetSymbolMap ( ) . UpdateActiveSymbols ( ) ;
}
}
void DisassemblyWidget : : contextRemoveFunction ( )
{
u32 curFuncAddr = m_cpu - > GetSymbolMap ( ) . GetFunctionStart ( m_selectedAddressStart ) ;
if ( curFuncAddr ! = SymbolMap : : INVALID_ADDRESS )
{
u32 previousFuncAddr = m_cpu - > GetSymbolMap ( ) . GetFunctionStart ( curFuncAddr - 4 ) ;
if ( previousFuncAddr ! = SymbolMap : : INVALID_ADDRESS )
{
// Extend the previous function to replace the spot of the function that is going to be removed
u32 expandedSize = m_cpu - > GetSymbolMap ( ) . GetFunctionSize ( previousFuncAddr ) + m_cpu - > GetSymbolMap ( ) . GetFunctionSize ( curFuncAddr ) ;
m_cpu - > GetSymbolMap ( ) . SetFunctionSize ( previousFuncAddr , expandedSize ) ;
}
m_cpu - > GetSymbolMap ( ) . RemoveFunction ( curFuncAddr , true ) ;
m_cpu - > GetSymbolMap ( ) . SortSymbols ( ) ;
m_cpu - > GetSymbolMap ( ) . UpdateActiveSymbols ( ) ;
}
}
void DisassemblyWidget : : contextRenameFunction ( )
{
const u32 curFuncAddress = m_cpu - > GetSymbolMap ( ) . GetFunctionStart ( m_selectedAddressStart ) ;
if ( curFuncAddress ! = SymbolMap : : INVALID_ADDRESS )
{
bool ok ;
2022-12-24 11:17:52 +00:00
QString funcName = QInputDialog : : getText ( this , tr ( " Rename Function " ) , tr ( " Function name " ) , QLineEdit : : Normal , m_cpu - > GetSymbolMap ( ) . GetLabelString ( curFuncAddress ) . c_str ( ) , & ok ) ;
2022-12-24 06:51:44 +00:00
if ( ! ok )
return ;
if ( funcName . isEmpty ( ) )
{
2022-12-24 11:17:52 +00:00
QMessageBox : : warning ( this , tr ( " Rename Function Error " ) , tr ( " Function name cannot be nothing. " ) ) ;
2022-12-24 06:51:44 +00:00
}
else
{
m_cpu - > GetSymbolMap ( ) . SetLabelName ( funcName . toLocal8Bit ( ) . constData ( ) , curFuncAddress ) ;
m_cpu - > GetSymbolMap ( ) . SortSymbols ( ) ;
m_cpu - > GetSymbolMap ( ) . UpdateActiveSymbols ( ) ;
this - > repaint ( ) ;
}
}
else
{
2022-12-24 11:17:52 +00:00
QMessageBox : : warning ( this , tr ( " Rename Function Error " ) , tr ( " No function / symbol is currently selected. " ) ) ;
2022-12-24 06:51:44 +00:00
}
}
2023-10-10 00:31:49 +00:00
void DisassemblyWidget : : contextStubFunction ( )
{
const u32 curFuncAddress = m_cpu - > GetSymbolMap ( ) . GetFunctionStart ( m_selectedAddressStart ) ;
if ( curFuncAddress ! = SymbolMap : : INVALID_ADDRESS )
{
Host : : RunOnCPUThread ( [ this , curFuncAddress , cpu = m_cpu ] {
cpu - > write32 ( curFuncAddress , 0x03E00008 ) ; // jr $ra
cpu - > write32 ( curFuncAddress + 4 , 0x00000000 ) ; // nop
QtHost : : RunOnUIThread ( [ this ] { VMUpdate ( ) ; } ) ;
} ) ;
}
else
{
QMessageBox : : warning ( this , tr ( " Stub Function Error " ) , tr ( " No function / symbol is currently selected. " ) ) ;
}
}
2022-12-24 06:51:44 +00:00
void DisassemblyWidget : : SetCpu ( DebugInterface * cpu )
{
m_cpu = cpu ;
m_disassemblyManager . setCpu ( cpu ) ;
}
QString DisassemblyWidget : : GetLineDisasm ( u32 address )
{
DisassemblyLineInfo lineInfo ;
m_disassemblyManager . getLine ( address , true , lineInfo ) ;
return QString ( " %1 %2 " ) . arg ( lineInfo . name . c_str ( ) ) . arg ( lineInfo . params . c_str ( ) ) ;
} ;
// Here we go!
void DisassemblyWidget : : paintEvent ( QPaintEvent * event )
{
QPainter painter ( this ) ;
const u32 w = painter . device ( ) - > width ( ) - 1 ;
const u32 h = painter . device ( ) - > height ( ) - 1 ;
// Get the current font size
const QFontMetrics fm = painter . fontMetrics ( ) ;
// Get the row height
m_rowHeight = fm . height ( ) + 2 ;
// Find the amount of visible rows
m_visibleRows = h / m_rowHeight ;
m_disassemblyManager . analyze ( m_visibleStart , m_disassemblyManager . getNthNextAddress ( m_visibleStart , m_visibleRows ) - m_visibleStart ) ;
// Draw the rows
bool inSelectionBlock = false ;
bool alternate = m_visibleStart % 8 ;
const u32 curPC = m_cpu - > getPC ( ) ; // Get the PC here, because it'll change when we are drawing and make it seem like there are two PCs
for ( u32 i = 0 ; i < = m_visibleRows ; i + + )
{
const u32 rowAddress = ( i * 4 ) + m_visibleStart ;
// Row backgrounds
if ( inSelectionBlock | | ( m_selectedAddressStart < = rowAddress & & rowAddress < = m_selectedAddressEnd ) )
{
painter . fillRect ( 0 , i * m_rowHeight , w , m_rowHeight , this - > palette ( ) . highlight ( ) ) ;
inSelectionBlock = m_selectedAddressEnd ! = rowAddress ;
}
else
{
painter . fillRect ( 0 , i * m_rowHeight , w , m_rowHeight , alternate ? this - > palette ( ) . base ( ) : this - > palette ( ) . alternateBase ( ) ) ;
}
// Row text
painter . setPen ( GetAddressFunctionColor ( rowAddress ) ) ;
QString lineString = DisassemblyStringFromAddress ( rowAddress , painter . font ( ) , curPC ) ;
painter . drawText ( 2 , i * m_rowHeight , w , m_rowHeight , Qt : : AlignLeft , lineString ) ;
alternate = ! alternate ;
}
// Draw the branch lines
// This is where it gets a little scary
// It's been mostly copied from the wx implementation
u32 visibleEnd = m_disassemblyManager . getNthNextAddress ( m_visibleStart , m_visibleRows ) ;
std : : vector < BranchLine > branchLines = m_disassemblyManager . getBranchLines ( m_visibleStart , visibleEnd - m_visibleStart ) ;
s32 branchCount = 0 ;
s32 skippedBranches = 0 ;
for ( const auto & branchLine : branchLines )
{
if ( branchCount = = 5 )
break ;
const int winBottom = this - > height ( ) ;
const int x = this - > width ( ) - 10 - ( ( std : : max ( 0 , branchLine . laneIndex - skippedBranches ) ) * 10 ) ;
int top , bottom ;
// If the start is technically 'above' our address view
if ( branchLine . first < m_visibleStart )
{
top = - 1 ;
}
// If the start is technically 'below' our address view
else if ( branchLine . first > = visibleEnd )
{
top = winBottom + 1 ;
}
else
{
// Explaination
// ((branchLine.first - m_visibleStart) -> Find the amount of bytes from the top of the view
// / 4 -> Convert that into rowss in instructions
// * m_rowHeight -> convert that into rows in pixels
// + (m_rowHeight / 2) -> Add half a row in pixels to center the arrow
top = ( ( ( branchLine . first - m_visibleStart ) / 4 ) * m_rowHeight ) + ( m_rowHeight / 2 ) ;
}
if ( branchLine . second < m_visibleStart )
{
bottom = - 1 ;
}
else if ( branchLine . second > = visibleEnd )
{
bottom = winBottom + 1 ;
}
else
{
bottom = ( ( ( branchLine . second - m_visibleStart ) / 4 ) * m_rowHeight ) + ( m_rowHeight / 2 ) ;
}
if ( ( top < 0 & & bottom < 0 ) | | ( top > winBottom & & bottom > winBottom ) | | ( top < 0 & & bottom > winBottom ) | | ( top > winBottom & & bottom < 0 ) )
{
skippedBranches + + ;
continue ;
}
branchCount + + ;
if ( branchLine . first = = m_selectedAddressStart | | branchLine . second = = m_selectedAddressStart )
{
painter . setPen ( QColor ( 0xFF257AFA ) ) ;
}
else
{
painter . setPen ( QColor ( 0xFFFF3020 ) ) ;
}
if ( top < 0 ) // first is not visible, but second is
{
painter . drawLine ( x - 2 , bottom , x + 2 , bottom ) ;
painter . drawLine ( x + 2 , bottom , x + 2 , 0 ) ;
if ( branchLine . type = = LINE_DOWN )
{
painter . drawLine ( x , bottom - 4 , x - 4 , bottom ) ;
painter . drawLine ( x - 4 , bottom , x + 1 , bottom + 5 ) ;
}
}
else if ( bottom > winBottom ) // second is not visible, but first is
{
painter . drawLine ( x - 2 , top , x + 2 , top ) ;
painter . drawLine ( x + 2 , top , x + 2 , winBottom ) ;
if ( branchLine . type = = LINE_UP )
{
painter . drawLine ( x , top - 4 , x - 4 , top ) ;
painter . drawLine ( x - 4 , top , x + 1 , top + 5 ) ;
}
}
else
{ // both are visible
if ( branchLine . type = = LINE_UP )
{
painter . drawLine ( x - 2 , bottom , x + 2 , bottom ) ;
painter . drawLine ( x + 2 , bottom , x + 2 , top ) ;
painter . drawLine ( x + 2 , top , x - 4 , top ) ;
painter . drawLine ( x , top - 4 , x - 4 , top ) ;
painter . drawLine ( x - 4 , top , x + 1 , top + 5 ) ;
}
else
{
painter . drawLine ( x - 2 , top , x + 2 , top ) ;
painter . drawLine ( x + 2 , top , x + 2 , bottom ) ;
painter . drawLine ( x + 2 , bottom , x - 4 , bottom ) ;
painter . drawLine ( x , bottom - 4 , x - 4 , bottom ) ;
painter . drawLine ( x - 4 , bottom , x + 1 , bottom + 5 ) ;
}
}
}
// Draw a border
painter . setPen ( this - > palette ( ) . shadow ( ) . color ( ) ) ;
painter . drawRect ( 0 , 0 , w , h ) ;
}
void DisassemblyWidget : : mousePressEvent ( QMouseEvent * event )
{
2023-01-13 02:41:16 +00:00
const u32 selectedAddress = ( static_cast < int > ( event - > position ( ) . y ( ) ) / m_rowHeight * 4 ) + m_visibleStart ;
2022-12-24 06:51:44 +00:00
if ( event - > buttons ( ) & Qt : : LeftButton )
{
if ( event - > modifiers ( ) & Qt : : ShiftModifier )
{
if ( selectedAddress < m_selectedAddressStart )
{
m_selectedAddressStart = selectedAddress ;
}
else if ( selectedAddress > m_visibleStart )
{
m_selectedAddressEnd = selectedAddress ;
}
}
else
{
m_selectedAddressStart = selectedAddress ;
m_selectedAddressEnd = selectedAddress ;
}
}
else if ( event - > buttons ( ) & Qt : : RightButton )
{
if ( m_selectedAddressStart = = m_selectedAddressEnd )
{
m_selectedAddressStart = selectedAddress ;
m_selectedAddressEnd = selectedAddress ;
}
}
this - > repaint ( ) ;
}
void DisassemblyWidget : : mouseDoubleClickEvent ( QMouseEvent * event )
{
2023-01-01 03:43:54 +00:00
if ( ! m_cpu - > isAlive ( ) )
return ;
2023-01-13 02:41:16 +00:00
const u32 selectedAddress = ( static_cast < int > ( event - > position ( ) . y ( ) ) / m_rowHeight * 4 ) + m_visibleStart ;
2022-12-24 06:51:44 +00:00
if ( CBreakPoints : : IsAddressBreakPoint ( m_cpu - > getCpuType ( ) , selectedAddress ) )
{
Host : : RunOnCPUThread ( [ & ] { CBreakPoints : : RemoveBreakPoint ( m_cpu - > getCpuType ( ) , selectedAddress ) ; } ) ;
}
else
{
Host : : RunOnCPUThread ( [ & ] { CBreakPoints : : AddBreakPoint ( m_cpu - > getCpuType ( ) , selectedAddress ) ; } ) ;
}
breakpointsChanged ( ) ;
this - > repaint ( ) ;
}
void DisassemblyWidget : : wheelEvent ( QWheelEvent * event )
{
if ( event - > angleDelta ( ) . y ( ) < 0 ) // todo: max address bounds check?
{
m_visibleStart + = 4 ;
}
else if ( event - > angleDelta ( ) . y ( ) & & m_visibleStart > 0 )
{
m_visibleStart - = 4 ;
}
this - > repaint ( ) ;
}
void DisassemblyWidget : : keyPressEvent ( QKeyEvent * event )
{
switch ( event - > key ( ) )
{
case Qt : : Key_Up :
{
m_selectedAddressStart - = 4 ;
if ( ! ( event - > modifiers ( ) & Qt : : ShiftModifier ) )
m_selectedAddressEnd = m_selectedAddressStart ;
// Auto scroll
if ( m_visibleStart > m_selectedAddressStart )
m_visibleStart - = 4 ;
}
break ;
2022-12-27 08:29:16 +00:00
case Qt : : Key_PageUp :
{
m_selectedAddressStart - = m_visibleRows * 4 ;
m_selectedAddressEnd = m_selectedAddressStart ;
m_visibleStart - = m_visibleRows * 4 ;
}
break ;
2022-12-24 06:51:44 +00:00
case Qt : : Key_Down :
{
m_selectedAddressEnd + = 4 ;
if ( ! ( event - > modifiers ( ) & Qt : : ShiftModifier ) )
m_selectedAddressStart = m_selectedAddressEnd ;
// Purposely scroll on the second to last row. It's possible to
// size the window so part of a row is visible and we don't want to have half a row selected and cut off!
if ( m_visibleStart + ( ( m_visibleRows - 1 ) * 4 ) < m_selectedAddressEnd )
m_visibleStart + = 4 ;
2022-12-27 08:29:16 +00:00
break ;
}
case Qt : : Key_PageDown :
{
m_selectedAddressStart + = m_visibleRows * 4 ;
m_selectedAddressEnd = m_selectedAddressStart ;
m_visibleStart + = m_visibleRows * 4 ;
break ;
2022-12-24 06:51:44 +00:00
}
case Qt : : Key_G :
contextGoToAddress ( ) ;
break ;
case Qt : : Key_C :
contextCopyInstructionText ( ) ;
break ;
case Qt : : Key_B :
contextToggleBreakpoint ( ) ;
break ;
case Qt : : Key_M :
contextAssembleInstruction ( ) ;
break ;
case Qt : : Key_Right :
contextFollowBranch ( ) ;
break ;
case Qt : : Key_Left :
gotoAddress ( m_cpu - > getPC ( ) ) ;
break ;
}
this - > repaint ( ) ;
}
void DisassemblyWidget : : customMenuRequested ( QPoint pos )
{
2023-01-01 03:43:54 +00:00
if ( ! m_cpu - > isAlive ( ) )
return ;
2022-12-24 06:51:44 +00:00
m_contextMenu - > popup ( this - > mapToGlobal ( pos ) ) ;
}
inline QString DisassemblyWidget : : DisassemblyStringFromAddress ( u32 address , QFont font , u32 pc )
{
DisassemblyLineInfo line ;
if ( ! m_cpu - > isValidAddress ( address ) )
2022-12-24 11:17:52 +00:00
return tr ( " %1 NOT VALID ADDRESS " ) . arg ( address , 8 , 16 , QChar ( ' 0 ' ) ) . toUpper ( ) ;
2022-12-24 06:51:44 +00:00
// Todo? support non symbol view?
m_disassemblyManager . getLine ( address , true , line ) ;
const bool isConditional = line . info . isConditional & & m_cpu - > getPC ( ) = = address ;
const bool isConditionalMet = line . info . conditionMet ;
const bool isCurrentPC = m_cpu - > getPC ( ) = = address ;
const bool isBreakpoint = CBreakPoints : : IsAddressBreakPoint ( m_cpu - > getCpuType ( ) , address ) & & ! CBreakPoints : : IsTempBreakPoint ( m_cpu - > getCpuType ( ) , address ) ;
const std : : string addressSymbol = m_cpu - > GetSymbolMap ( ) . GetLabelString ( address ) ;
QString lineString ( " %1 %2 %3 %4 %5 %6 " ) ;
lineString = lineString . arg ( isBreakpoint ? " \u25A0 " : " " ) ; // Bp block ( ■ )
if ( addressSymbol . empty ( ) ) // The address wont have symbol text if it's the start of a function for example
lineString = lineString . arg ( address , 8 , 16 , QChar ( ' 0 ' ) ) . toUpper ( ) ;
else
{
// We want this text elided
QFontMetrics metric ( font ) ;
lineString = lineString . arg ( metric . elidedText ( QString : : fromStdString ( addressSymbol ) , Qt : : ElideRight , 8 * font . pointSize ( ) ) ) ;
}
lineString = lineString . leftJustified ( 4 , ' ' ) // Address / symbol
. arg ( line . name . c_str ( ) )
. arg ( line . params . c_str ( ) ) // opcode + arguments
. arg ( isConditional ? ( isConditionalMet ? " # true " : " # false " ) : " " )
. arg ( isCurrentPC ? " <-- " : " " ) ;
return lineString ;
}
QColor DisassemblyWidget : : GetAddressFunctionColor ( u32 address )
{
// This is an attempt to figure out if the current palette is dark or light
// We calculate the luminescence of the alternateBase colour
// and swap between our darker and lighter function colours
std : : array < QColor , 6 > colors ;
const QColor base = this - > palette ( ) . alternateBase ( ) . color ( ) ;
2022-12-27 08:29:16 +00:00
const auto Y = ( base . redF ( ) * 0.33 ) + ( 0.5 * base . greenF ( ) ) + ( 0.16 * base . blueF ( ) ) ;
2022-12-24 06:51:44 +00:00
if ( Y > 0.5 )
{
colors = {
QColor : : fromRgba ( 0xFFFA3434 ) ,
QColor : : fromRgba ( 0xFF206b6b ) ,
QColor : : fromRgba ( 0xFF858534 ) ,
QColor : : fromRgba ( 0xFF378c37 ) ,
QColor : : fromRgba ( 0xFF783278 ) ,
QColor : : fromRgba ( 0xFF21214a ) ,
} ;
}
else
{
colors = {
QColor : : fromRgba ( 0xFFe05555 ) ,
QColor : : fromRgba ( 0xFF55e0e0 ) ,
QColor : : fromRgba ( 0xFFe8e855 ) ,
QColor : : fromRgba ( 0xFF55e055 ) ,
QColor : : fromRgba ( 0xFFe055e0 ) ,
QColor : : fromRgba ( 0xFFC2C2F5 ) ,
} ;
}
2022-12-27 08:29:16 +00:00
const auto funNum = m_cpu - > GetSymbolMap ( ) . GetFunctionNum ( address ) ;
2022-12-24 06:51:44 +00:00
if ( funNum = = - 1 )
return this - > palette ( ) . text ( ) . color ( ) ;
return colors [ funNum % 6 ] ;
}
QString DisassemblyWidget : : FetchSelectionInfo ( SelectionInfo selInfo )
{
QString infoBlock ;
for ( u32 i = m_selectedAddressStart ; i < = m_selectedAddressEnd ; i + = 4 )
{
if ( i ! = m_selectedAddressStart )
infoBlock + = ' \n ' ;
if ( selInfo = = SelectionInfo : : ADDRESS )
{
infoBlock + = FilledQStringFromValue ( i , 16 ) ;
}
else if ( selInfo = = SelectionInfo : : INSTRUCTIONTEXT )
{
DisassemblyLineInfo line ;
m_disassemblyManager . getLine ( i , true , line ) ;
infoBlock + = QString ( " %1 %2 " ) . arg ( line . name . c_str ( ) ) . arg ( line . params . c_str ( ) ) ;
}
else // INSTRUCTIONHEX
{
infoBlock + = FilledQStringFromValue ( m_cpu - > read32 ( i ) , 16 ) ;
}
}
return infoBlock ;
}
void DisassemblyWidget : : gotoAddress ( u32 address )
{
const u32 destAddress = address & ~ 3 ;
// Center the address
m_visibleStart = ( destAddress - ( m_visibleRows * 4 / 2 ) ) & ~ 3 ;
m_selectedAddressStart = destAddress ;
m_selectedAddressEnd = destAddress ;
this - > repaint ( ) ;
2023-01-04 07:11:28 +00:00
this - > setFocus ( ) ;
2022-12-24 06:51:44 +00:00
}