DarkJezter
April 23rd, 2008, 17:37
Just thought I'd open some dialog on the graphical debugger. As of right now I have it working, and anyone who would like to play with it can try the debugger branch of the SVN tree. It's still using the original GTK gui, and I haven't tested it yet with the new KDE4 interface.
I'm eager to build onto what's here, but before I do that I'd like to know first off that what's here now is portable enough to be used elsewhere. If it isn't, then I'd like to make that a first priority.
Anyways, in the mean time here are a few hair-brained ideas that I'd like to see come out of this tool:
OProfile support - performance analysis of native r4300 and recompiled code
Integration into cheat system - for uber geeks who want to make their own cheat codes
Register & Ram edit - because every debugger should have these
Other ideas - extract images & textures from roms, view memory
Anyone else have any thoughts?
Richard42
April 23rd, 2008, 19:47
OProfile integration would be nice (the current method works but is somewhat tedious) but not very useful for most people.
Cheat system integration would be good. XMAME/XMESS has a simple system for cheat discovery which is easy to use and works well.
More debugging functions: register and RAM edit would go here. It would also be nice to be able to set breakpoints and watchpoints.
DarkJezter
April 25th, 2008, 02:18
Richard, you got any issues with me merging the fixes to the debugger back to the trunk before I start ripping it apart?
Richard42
April 25th, 2008, 02:44
Richard, you got any issues with me merging the fixes to the debugger back to the trunk before I start ripping it apart?
Nope, go ahead. I was going to do it but am kind of swamped at the moment. Be sure to note the start and end rev of the branch that you're merging in the commit message on the trunk. That way it will be easier to find the right information to merge the remaining part of the branch.
DarkJezter
April 25th, 2008, 03:27
Ok, so the only thing I have not been able to test is the debugger built alongside the KDE4 gui...
Not sure if this is because I'm still running kde 3.5 or not, but I haven't been able to get KDE4 to compile at all. I'm guessing this is the case, or that there is a related dependency that's missing.
From what I gather it should compile fine, and should work... once the KDE gui has a menu added to allow enabling/disabling the debugger.
I hope this doesn't cause any head scratching, but the fixes thus far are only to 3 files, and instead of merging the branch, I've just committed those changes to the trunk. This way I'll just continue the rest of the work from the current debugger branch.
Slougi
April 25th, 2008, 13:17
I'll look into adding support in the KDE4 interface as soon as I have some spare time. I'll probably implement the cheat stuff as well and flesh out the config stuff. Just a fyi, I'm quite busy at the moment.
Richard42
April 25th, 2008, 19:02
Yeah everybody's pretty busy at the moment; development has slowed a little. I hope to get the makefiles written for your KDE4 port this weekend, and the regression tests up and running. I have the first 1/3rd of the regtest script done and tested; it's pretty cool.
DarkJezter
April 27th, 2008, 12:50
I've been playing around with the debugger. I currently have the breakpoints working in the interpreted cores, and have started work on displaying generated dynarec code. Once I have this working, I'm planning on getting breakpoints working with the dynamic recompiler, as well as single step instruction stepping, (either via the dynarec or if enough state is persistent and present, possibly by using the interpreted cores)
Splitting the code is almost done, the only thing that's really left to split up is reading registers. Modifying registers will come next. I suppose after that, a memory viewer/editor would be nice :D
MIO0
April 30th, 2008, 08:52
I think it would be helpful to use the symbolic general purpose register names instead of the numeric ones, and shrink down the window sizes. Perhaps a smaller font and less padding?
There are a couple of appearance bugs: the last digit and a half are cut off in the register text fields and the register labels do not line up.
HyperHacker
April 30th, 2008, 09:29
OK, yes, suggestions, some quite obvious :P:
RAM editing
Instruction editing ala No$GMB would be nice too (double-click an instruction and type in new code to replace it). Although No$GMB's only allows one opcode at a time, a better way would be a multi-line textbox to replace multiple instructions starting from the selected one.
Split the giant register window into multiple windows. One for GPR, and special, one for the others using the tab system it's got now. And of course register value editing.
Display the register contents in a fixed-width font.
Breakpoints on memory access (individual bytes, not just words, and including DMA access if possible - two things that drove me nuts in Nemu) [edit: done :D]
Be able to close the windows and open them again from the menu
Break when a specified register ==/>=/<=/!= a specified value or is changed by a given amountI've been using Nemu on Windows for a while for ROM hacking and man it's buggy. :P The idea of a decent debugger makes me very excited, especially an open-source Linux native one.
Also, bug reports:
Shouldn't "Desassembler" be "Disassembler"?
If I double-click an instruction, it adds a breakpoint for that address, but if I do it again, it doesn't get removed from the list, only grayed out (disabled?), and the first time I did, it also added one for 0x0.
Any way I can get involved in trying to add some of these features myself? /me looks into the breakpoint code to see if he can hack together memory-access breakpoints, and fixes the spelling error in desasm.c:71
[edit] Already fixed one bug around line 220 of gui_gtk/debugger/breakpoints.c:
gtk_clist_set_text( GTK_CLIST(clBreakpoints), new_row, 0, (gpointer) address);'address' should be 'line[0]'; it's a pointer to a string, not a hex number. :P No more segfault when clicking "Add" while the game is running.
ebenblues
April 30th, 2008, 16:55
HyperHacker,
Thanks so much for your help with the debugger. Not sure if you know about this, but svn makes it really easy to create a patch file of your changes. Just cd into the base dir (trunk or r0286-debugger) and type "svn diff > patch". Then open a new issue on our issue tracker site and upload the patch file as an attachment:
http://code.google.com/p/mupen64plus/issues/list
Then you can reference the issue in emutalk and not have to manually type all the changes that need to be made into your post.
HyperHacker
April 30th, 2008, 21:43
OK, so should I just fix whatever bugs I can find and submit one big patch? Does it matter if that patch also adds one or two new features? (I.E. I've enhanced the breakpoint system to allow setting a breakpoint on a range of addresses, not quite done yet though.)
Most of the bugs I find seem to just be signals not being correctly connected to widgets, causing segfaults when you click buttons. Pretty simple to fix.
okaygo
April 30th, 2008, 22:59
I would submit patches as you see them, that way it might be easier for Jezter to sort through a lot of this stuff. Also thanks for helping out, if you have interest in the development and would like to contribute please feel free to join the development team.
HyperHacker
April 30th, 2008, 23:31
OK, but say I find one bug, fix it, make a patch, find another one, fix that, won't the second patch have both fixes in it? How do I avoid that?
hmm... Well, All i can think of is to manually exclude. But the others are much better at these sort of things so I'll bet they'll have a more robust solution.
DarkJezter
May 1st, 2008, 01:18
HyperHacker, I'm currently working on the debugger branch, and was doing so almost exclusively. I had done most of the necessary work to get the debugger in the trunk debugged so that it would at least compile/function (however it looks like half of that work was undone when the debugger branch was forked)
I'm going to resubmit my patches to the trunk to at least get the trunk to compile and run properly with the debugger enabled.
Beyond that, in the debugger branch I'm currently doing a major rewrite of the disassembler GTK gui. As it is right now, the code for the functional parts of the debugger is mixed in with the GUI, and my first task I'm working on is seperating the two. This is almost done, however the register access needs more work, and I have yet to work on a UI for the memory access.
Once I get my current changes commited to the debugger branch, we'll see if we can't get you svn access. I'd love any help you can offer, however what I need most is someone who can take charge of the look of the debugger, while I clean up the internals.
HyperHacker
May 1st, 2008, 01:59
Well I imagine I'd be much better at adding and fixing things than GUI work as I've never dealt with GUIs outside of the Windows API before, but GTK looks pretty simple to work with so I can probably make some enhancements here and there. I'm mainly interested in enhancing it though, so that I never have to deal with Nemu in Wine again. ;)
Maybe I'll just fix what I can, submit a few patches, and then work separately on enhancements and try to merge them into the latest revision when they're done.
I hope you're not counting on me to become part of the "team" so to speak and be regularly fixing and testing things, because I don't have the time or attention span for that. :P I just had some enhancement ideas and thought I'd see how well I could implement them, rather than just make another request for some obscure feature that ends up at the bottom of the priority list because I'm basically the only one who cares and the developers have more important things to work on.
ebenblues
May 1st, 2008, 02:08
OK, but say I find one bug, fix it, make a patch, find another one, fix that, won't the second patch have both fixes in it? How do I avoid that?
You can submit all your changes in one patch and we'll sort through it. I don't think anyone's really going to be picky of any contributions. :) As others have said, if you're interested in contributing more long term, PM Richard42 and he'll give you svn access.
Tillin9
May 1st, 2008, 03:12
I seen to have picked up Gtk programing rather quick, almost surprisingly as I was only taught on MFC and Qt3. I happen to have a number of Gtk GUI improvements on my todo list already, but sadly I'm swamped till Tuesday. Anyway... if you make some kind of list of the interface improvements you need / want , I'm definitely willing to spend some time further improving the GUI.
DarkJezter
May 1st, 2008, 03:25
Well, I resubmitted my changes to the trunk for the debugger, and that consists mostly of the fixes you've submitted already. Any other patches you'd like to include can be sent to me directly, and if they don't conflict with what I'm doing already, I'll include them in the debugger branch.
This way, you'll be able to generate diffs without patches showing up repeatedly.
Tillin9
May 1st, 2008, 06:26
Okay, I rebuilt after the trunk merge and I must say I'm impressed with the work thus far. Also, I see a bit more what you meant by GUI cleanup. There's some general Gtk warnings, widget alignment, frame size, scrolling, etc. issues I can take care on my own. I'm sure a closer look at the code will also find more.
However, before I just crate a list and start fixing little items, I think we should seriously consider spending some time thinking about what our debugging window(s) should look like as the current many windows, vertical tabs, etc. probably could use a general re-design.
DarkJezter
May 1st, 2008, 08:24
However, before I just crate a list and start fixing little items, I think we should seriously consider spending some time thinking about what our debugging window(s) should look like as the current many windows, vertical tabs, etc. probably could use a general re-design.
I completely agree. The whole user-interface needs an overhaul, and even though I'm not super savy on GTK, I'm learning enough to be trouble.
I'm honestly more concerned with the raw functionality of the debugger right now, and a big part of that relates to trying to separate the actual gui itself, from the rest of the debugger. As far as window layouts, fonts, button sizes, etc... the rest of the debugger is undergoing a major enough rewrite that I don't even know what should be fixed now, or just left until it is replaced.
Perhaps for the mean time, designing some mock ups for the debugger should be fine, in fact, I'd love to see what we can come up with...
Right now I'm playing more with breakpoints & other details when used with the dynamic recompiler. Part of that process is going to include looking at the code generated by the dynarec, I've already got some preliminary support for this in the debugger branch, and hopefully tomorrow I'll have had the chance to get the new disassembler all ironed out and working at least as well as the old one was.
HyperHacker
May 1st, 2008, 09:38
This was made against the debugger build so I'm not sure how well it'll integrate into the trunk, but anyway it adds and fixes a few things I can remember:
Fixed segfaults if you clicked buttons on the breakpoint list at the wrong time
Added memory read/write breakpoints (only tested with RDRAM access); it stops at the instruction immediately after the one that triggered the break.
Added enable/disable/toggle (and non-functional edit) buttons to breakpoint list
Fixed memory leak when adding breakpoints
Breakpoints can now span an address range
With the memory access breakpoints the breakpoint entry handling was changed a bit. You can enter one or two hex addresses (no 0x prefix required) optionally preceded by one or more of the flags */r/w/x (meaning disable by default, break on read of this address, break on write to this address, and break on execution of this address). With two addresses it covers anywhere between the two inclusive. Examples:
"80123456" - break when PC=0x80123456
"rx 80123456 80123478" - break when 0x80123456 <= PC <= 0x80123478, or any read of this range
"w* 80222222" - break on any write to 0x80222222, but breakpoint is disabled by default
The breakpoint window also lists the R/W/X flags, Unix-style. Would look better in a fixed-width font but eh.
There's still a bit to fix (after sleep :)), if you have a breakpoint over multiple instructions and you double-click one in the disassembly, the breakpoint is toggled correctly, but the colour only updates for that instruction. The enable/disable/toggle buttons also don't update the colours.
File uploading seems to be broken (unable to move/copy file), so here it is:
Index: debugger/breakpoints.c
======================================== ===========================
--- debugger/breakpoints.c (revision 338)
+++ debugger/breakpoints.c (working copy)
@@ -42,6 +42,8 @@
return -1;
}
g_Breakpoints[g_NumBreakpoints].address=address;
+ g_Breakpoints[g_NumBreakpoints].endaddr=address;
+ BPT_SET_FLAG(g_Breakpoints[g_NumBreakpoints], BPT_FLAG_WRITE);
enable_breakpoint(g_NumBreakpoints);
@@ -49,6 +51,17 @@
}
+int add_breakpoint_struct(breakpoint* newbp)
+{
+ if( g_NumBreakpoints == BREAKPOINTS_MAX_NUMBER ) {
+ printf("BREAKPOINTS_MAX_NUMBER have been reached.\n");//REMOVE ME
+ return -1;
+ }
+ memcpy(&g_Breakpoints[g_NumBreakpoints], newbp, sizeof(breakpoint));
+ printf("newbp %08X - %08X\n", g_Breakpoints[g_NumBreakpoints].address, g_Breakpoints[g_NumBreakpoints].endaddr);
+ return g_NumBreakpoints++;
+}
+
void enable_breakpoint( int breakpoint )
{
BPT_SET_FLAG(g_Breakpoints[breakpoint], BPT_FLAG_ENABLED);
@@ -68,7 +81,7 @@
void remove_breakpoint_by_address( uint32 address )
{
- int bpt = lookup_breakpoint( address );
+ int bpt = lookup_breakpoint( address, 0 );
if(bpt==-1)
{
printf("Tried to remove Nonexistant breakpoint %x!", address);
@@ -77,13 +90,17 @@
remove_breakpoint_by_num( bpt );
}
-int lookup_breakpoint( uint32 address )
+int lookup_breakpoint( uint32 address, uint32 flags)
{
int i=0;
while( i != g_NumBreakpoints )
{
- if( g_Breakpoints[i].address==address )
+ if((address >= g_Breakpoints[i].address) && (address <= g_Breakpoints[i].endaddr) && (!flags || ((g_Breakpoints[i].flags & flags) == flags)))
+ {
+ printf("Bpt %d (0x%08X - 0x%08X) matches 0x%08X\n", i, g_Breakpoints[i].address,
+ g_Breakpoints[i].endaddr, address);
return i;
+ }
else
i++;
}
@@ -92,8 +109,36 @@
int check_breakpoints( uint32 address )
{
- int bpt=lookup_breakpoint( address );
- if( (bpt != -1) && BPT_CHECK_FLAG(g_Breakpoints[bpt], BPT_FLAG_ENABLED) )
+ int bpt=lookup_breakpoint( address, BPT_FLAG_ENABLED | BPT_FLAG_EXEC );
+ //if( (bpt != -1) && BPT_CHECK_FLAG(g_Breakpoints[bpt], BPT_FLAG_ENABLED))
return bpt;
+ //return -1;
+}
+
+
+int check_breakpoints_on_mem_access( uint32 address, uint32 size, uint32 flags )
+{
+ //This function handles memory read/write breakpoints. size specifies the address
+ //range to check, flags specifies the flags that all need to be set.
+ //It automatically stops and updates the debugger on hit, so the memory access
+ //functions only need to call it and can discard the result.
+
+ int i, bpt;
+ for(i=address; i<=(address + size); i++)
+ {
+ bpt=lookup_breakpoint( address, flags );
+ if(bpt != -1)
+ {
+ run = 0;
+ switch_button_to_run();
+ update_debugger_frontend();
+
+ previousPC = PC->addr;
+ // Emulation thread is blocked until a button is clicked.
+ pthread_cond_wait(&debugger_done_cond, &mutex);
+
+ return bpt;
+ }
+ }
return -1;
}
Index: debugger/debugger.c
======================================== ===========================
--- debugger/debugger.c (revision 338)
+++ debugger/debugger.c (working copy)
@@ -63,7 +63,7 @@
{
if(run==2) {
- if( check_breakpoints(PC->addr)==-1 ) {
+ if( check_breakpoints(PC->addr)==-1 ) {
previousPC = PC->addr;
return;
}
Index: debugger/breakpoints.h
======================================== ===========================
--- debugger/breakpoints.h (revision 338)
+++ debugger/breakpoints.h (working copy)
@@ -39,7 +39,10 @@
#define BPT_FLAG_ENABLED 0x01
#define BPT_FLAG_CONDITIONAL 0x02
-#define BPT_FLAG_COUNTER 0x04
+#define BPT_FLAG_COUNTER 0x04
+#define BPT_FLAG_READ 0x08
+#define BPT_FLAG_WRITE 0x10
+#define BPT_FLAG_EXEC 0x20
#define BPT_CHECK_FLAG(a, b) ((a.flags & b) == b)
#define BPT_SET_FLAG(a, b) a.flags = (a.flags | b);
@@ -47,7 +50,8 @@
#define BPT_TOGGLE_FLAG(a, b) a.flags = (a.flags ^ b);
typedef struct _breakpoint {
- uint32 address;
+ uint32 address;
+ uint32 endaddr;
uint32 flags;
//uint32 condition; //Placeholder for breakpoint condition
} breakpoint;
@@ -57,10 +61,12 @@
int add_breakpoint( uint32 address );
+int add_breakpoint_struct(breakpoint* newbp);
void remove_breakpoint_by_address( uint32 address );
void enable_breakpoint( int breakpoint );
void disable_breakpoint( int breakpoint );
int check_breakpoints( uint32 address );
-int lookup_breakpoint( uint32 address );
+int check_breakpoints_on_mem_access( uint32 address, uint32 size, uint32 flags );
+int lookup_breakpoint( uint32 address, uint32 flags );
#endif // BREAKPOINTS_H
Index: memory/memory.c
======================================== ===========================
--- memory/memory.c (revision 338)
+++ memory/memory.c (working copy)
@@ -48,6 +48,10 @@
#include "../main/plugin.h"
#include "../main/vcr.h"
+#if DBG
+#include "../debugger/breakpoints.h"
+#endif
+
/* definitions of the rcp's structures and memory area */
RDRAM_register rdram_register;
mips_register MI_register;
@@ -1421,22 +1425,34 @@
}
void read_rdram()
-{;
+{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned int *)(rdramb + (address & 0xFFFFFF)));
}
void read_rdramb()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *(rdramb + ((address & 0xFFFFFF)^S8));
}
void read_rdramh()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned short *)(rdramb + ((address & 0xFFFFFF)^S16)));
}
void read_rdramd()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = ((unsigned long long int)(*(unsigned int *)(rdramb + (address & 0xFFFFFF))) << 32) |
((*(unsigned int *)(rdramb + (address & 0xFFFFFF) + 4)));
}
@@ -1531,21 +1547,33 @@
void write_rdram()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*((unsigned int *)(rdramb + (address & 0xFFFFFF))) = word;
}
void write_rdramb()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*((rdramb + ((address & 0xFFFFFF)^S8))) = byte;
}
void write_rdramh()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*(unsigned short *)((rdramb + ((address & 0xFFFFFF)^S16))) = hword;
}
void write_rdramd()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*((unsigned int *)(rdramb + (address & 0xFFFFFF))) = dword >> 32;
*((unsigned int *)(rdramb + (address & 0xFFFFFF) + 4 )) = dword & 0xFFFFFFFF;
}
@@ -1945,75 +1973,114 @@
void read_rsp()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_READ); //FIXME: access breakpoints not tested in these functions; possibly should be using address_low here
+ #endif
*rdword = *(readrsp[*address_low]);
}
void read_rspb()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned char*)readrsp[*address_low & 0xfffc]
+ ((*address_low&3)^S8) );
}
void read_rsph()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned short*)((unsigned char*)readrsp[*address_low & 0xfffc]
+ ((*address_low&3)^S16) ));
}
void read_rspd()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = ((unsigned long long int)(*readrsp[*address_low])<<32) |
*readrsp[*address_low+4];
}
void write_rsp()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*readrsp[*address_low] = word;
}
void write_rspb()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*((unsigned char*)readrsp[*address_low & 0xfffc]
+ ((*address_low&3)^S8) ) = byte;
}
void write_rsph()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*((unsigned short*)((unsigned char*)readrsp[*address_low & 0xfffc]
+ ((*address_low&3)^S16) )) = hword;
}
void write_rspd()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*readrsp[*address_low] = dword >> 32;
*readrsp[*address_low+4] = dword & 0xFFFFFFFF;
}
void read_dp()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *(readdp[*address_low]);
}
void read_dpb()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned char*)readdp[*address_low & 0xfffc]
+ ((*address_low&3)^S8) );
}
void read_dph()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned short*)((unsigned char*)readdp[*address_low & 0xfffc]
+ ((*address_low&3)^S16) ));
}
void read_dpd()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = ((unsigned long long int)(*readdp[*address_low])<<32) |
*readdp[*address_low+4];
}
void write_dp()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0xc:
@@ -2043,6 +2110,9 @@
void write_dpb()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0xc:
@@ -2098,6 +2168,9 @@
void write_dph()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0xc:
@@ -2137,6 +2210,9 @@
void write_dpd()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x8:
@@ -2164,75 +2240,114 @@
void read_dps()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *(readdps[*address_low]);
}
void read_dpsb()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned char*)readdps[*address_low & 0xfffc]
+ ((*address_low&3)^S8) );
}
void read_dpsh()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned short*)((unsigned char*)readdps[*address_low & 0xfffc]
+ ((*address_low&3)^S16) ));
}
void read_dpsd()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = ((unsigned long long int)(*readdps[*address_low])<<32) |
*readdps[*address_low+4];
}
void write_dps()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*readdps[*address_low] = word;
}
void write_dpsb()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*((unsigned char*)readdps[*address_low & 0xfffc]
+ ((*address_low&3)^S8) ) = byte;
}
void write_dpsh()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*((unsigned short*)((unsigned char*)readdps[*address_low & 0xfffc]
+ ((*address_low&3)^S16) )) = hword;
}
void write_dpsd()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*readdps[*address_low] = dword >> 32;
*readdps[*address_low+4] = dword & 0xFFFFFFFF;
}
void read_mi()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *(readmi[*address_low]);
}
void read_mib()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned char*)readmi[*address_low & 0xfffc]
+ ((*address_low&3)^S8) );
}
void read_mih()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned short*)((unsigned char*)readmi[*address_low & 0xfffc]
+ ((*address_low&3)^S16) ));
}
void read_mid()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = ((unsigned long long int)(*readmi[*address_low])<<32) |
*readmi[*address_low+4];
}
void write_mi()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x0:
@@ -2252,6 +2367,9 @@
void write_mib()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x0:
@@ -2279,6 +2397,9 @@
void write_mih()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x0:
@@ -2302,6 +2423,9 @@
void write_mid()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x0:
@@ -2321,6 +2445,9 @@
void read_vi()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
switch(*address_low)
{
case 0x10:
@@ -2334,6 +2461,9 @@
void read_vib()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
switch(*address_low)
{
case 0x10:
@@ -2351,6 +2481,9 @@
void read_vih()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
switch(*address_low)
{
case 0x10:
@@ -2366,6 +2499,9 @@
void read_vid()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
switch(*address_low)
{
case 0x10:
@@ -2380,6 +2516,9 @@
void write_vi()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x0:
@@ -2409,6 +2548,9 @@
void write_vib()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
int temp;
switch(*address_low)
{
@@ -2455,6 +2597,9 @@
void write_vih()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
int temp;
switch(*address_low)
{
@@ -2495,6 +2640,9 @@
void write_vid()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x0:
@@ -2528,6 +2676,9 @@
void read_ai()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
switch(*address_low)
{
case 0x4:
@@ -2545,6 +2696,9 @@
void read_aib()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
unsigned int len;
switch(*address_low)
{
@@ -2568,6 +2722,9 @@
void read_aih()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
unsigned int len;
switch(*address_low)
{
@@ -2590,6 +2747,9 @@
void read_aid()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
switch(*address_low)
{
case 0x0:
@@ -2609,6 +2769,9 @@
void write_ai()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
unsigned int delay=0;
switch(*address_low)
{
@@ -2710,6 +2873,9 @@
void write_aib()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
int temp;
unsigned int delay=0;
switch(*address_low)
@@ -2820,6 +2986,9 @@
void write_aih()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
int temp;
unsigned int delay=0;
switch(*address_low)
@@ -2924,6 +3093,9 @@
void write_aid()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
unsigned int delay=0;
switch(*address_low)
{
@@ -3021,29 +3193,44 @@
void read_pi()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *(readpi[*address_low]);
}
void read_pib()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned char*)readpi[*address_low & 0xfffc]
+ ((*address_low&3)^S8) );
}
void read_pih()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned short*)((unsigned char*)readpi[*address_low & 0xfffc]
+ ((*address_low&3)^S16) ));
}
void read_pid()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = ((unsigned long long int)(*readpi[*address_low])<<32) |
*readpi[*address_low+4];
}
void write_pi()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x8:
@@ -3078,6 +3265,9 @@
void write_pib()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x8:
@@ -3139,6 +3329,9 @@
void write_pih()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x8:
@@ -3190,6 +3383,9 @@
void write_pid()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x8:
@@ -3220,75 +3416,114 @@
void read_ri()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *(readri[*address_low]);
}
void read_rib()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned char*)readri[*address_low & 0xfffc]
+ ((*address_low&3)^S8) );
}
void read_rih()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned short*)((unsigned char*)readri[*address_low & 0xfffc]
+ ((*address_low&3)^S16) ));
}
void read_rid()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = ((unsigned long long int)(*readri[*address_low])<<32) |
*readri[*address_low+4];
}
void write_ri()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*readri[*address_low] = word;
}
void write_rib()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*((unsigned char*)readri[*address_low & 0xfffc]
+ ((*address_low&3)^S8) ) = byte;
}
void write_rih()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*((unsigned short*)((unsigned char*)readri[*address_low & 0xfffc]
+ ((*address_low&3)^S16) )) = hword;
}
void write_rid()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
*readri[*address_low] = dword >> 32;
*readri[*address_low+4] = dword & 0xFFFFFFFF;
}
void read_si()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *(readsi[*address_low]);
}
void read_sib()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned char*)readsi[*address_low & 0xfffc]
+ ((*address_low&3)^S8) );
}
void read_sih()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned short*)((unsigned char*)readsi[*address_low & 0xfffc]
+ ((*address_low&3)^S16) ));
}
void read_sid()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = ((unsigned long long int)(*readsi[*address_low])<<32) |
*readsi[*address_low+4];
}
void write_si()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x0:
@@ -3316,6 +3551,9 @@
void write_sib()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x0:
@@ -3358,6 +3596,9 @@
void write_sih()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x0:
@@ -3392,6 +3633,9 @@
void write_sid()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
switch(*address_low)
{
case 0x0:
@@ -3486,6 +3730,9 @@
void read_rom()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
if (lastwrite)
{
*rdword = lastwrite;
@@ -3497,27 +3744,42 @@
void read_romb()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *(rom + ((address^S8) & 0x03FFFFFF));
}
void read_romh()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = *((unsigned short *)(rom + ((address^S16) & 0x03FFFFFF)));
}
void read_romd()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
*rdword = ((unsigned long long)(*((unsigned int *)(rom+(address&0x03FFFFFF))))<<32)|
*((unsigned int *)(rom + ((address+4)&0x03FFFFFF)));
}
void write_rom()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
lastwrite = word;
}
void read_pif()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
#ifdef EMU64_DEBUG
if ((*address_low > 0x7FF) || (*address_low < 0x7C0))
{
@@ -3531,6 +3793,9 @@
void read_pifb()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
#ifdef EMU64_DEBUG
if ((*address_low > 0x7FF) || (*address_low < 0x7C0))
{
@@ -3544,6 +3809,9 @@
void read_pifh()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
#ifdef EMU64_DEBUG
if ((*address_low > 0x7FF) || (*address_low < 0x7C0))
{
@@ -3558,6 +3826,9 @@
void read_pifd()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_READ);
+ #endif
#ifdef EMU64_DEBUG
if ((*address_low > 0x7FF) || (*address_low < 0x7C0))
{
@@ -3572,6 +3843,9 @@
void write_pif()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 4, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
#ifdef EMU64_DEBUG
if ((*address_low > 0x7FF) || (*address_low < 0x7C0))
{
@@ -3595,6 +3869,9 @@
void write_pifb()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 1, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
#ifdef EMU64_DEBUG
if ((*address_low > 0x7FF) || (*address_low < 0x7C0))
{
@@ -3618,6 +3895,9 @@
void write_pifh()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 2, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
#ifdef EMU64_DEBUG
if ((*address_low > 0x7FF) || (*address_low < 0x7C0))
{
@@ -3642,6 +3922,9 @@
void write_pifd()
{
+ #if DBG
+ check_breakpoints_on_mem_access(address, 8, BPT_FLAG_ENABLED | BPT_FLAG_WRITE);
+ #endif
#ifdef EMU64_DEBUG
if ((*address_low > 0x7FF) || (*address_low < 0x7C0))
{
Index: main/gui_gtk/debugger/desasm.c
======================================== ===========================
--- main/gui_gtk/debugger/desasm.c (revision 338)
+++ main/gui_gtk/debugger/desasm.c (working copy)
@@ -317,7 +317,7 @@
clicked_address =(uint32) gtk_clist_get_row_data( GTK_CLIST(clist), clicked_row);
printf( "[DASM] click on row: %d\t address: 0x%lX\n", clicked_row, clicked_address );
- break_number = lookup_breakpoint(clicked_address);
+ break_number = lookup_breakpoint(clicked_address, BPT_FLAG_EXEC);
if( break_number==-1 ) {
add_breakpoint( clicked_address );
gtk_clist_set_background( GTK_CLIST(clist), clicked_row, &color_BP);
Index: main/gui_gtk/debugger/breakpoints.c
======================================== ===========================
--- main/gui_gtk/debugger/breakpoints.c (revision 338)
+++ main/gui_gtk/debugger/breakpoints.c (working copy)
@@ -41,6 +41,11 @@
static void on_row_unselection(GtkCList *clist, gint row);
static void on_add();
static void on_remove();
+static void on_enable();
+static void on_disable();
+static void on_toggle();
+static void _toggle(int flag);
+static void on_edit();
static void on_close();
static GtkWidget *clBreakpoints;
@@ -55,7 +60,8 @@
GtkWidget *boxH1,
*scrolledwindow1,
*boxV1,
- *buAdd, *buRemove;
+ *buAdd, *buRemove, *buEnable, *buDisable, *buToggle,
+ *buEdit;
breakpoints_opened = 1;
@@ -84,7 +90,7 @@
clBreakpoints = gtk_clist_new( 1 );
gtk_container_add( GTK_CONTAINER(scrolledwindow1), clBreakpoints );
gtk_clist_set_selection_mode( GTK_CLIST(clBreakpoints), GTK_SELECTION_EXTENDED );
- gtk_clist_set_column_width( GTK_CLIST(clBreakpoints), 0, 80 );
+ gtk_clist_set_column_width( GTK_CLIST(clBreakpoints), 0, 160 );
gtk_clist_set_auto_sort( GTK_CLIST(clBreakpoints), TRUE );
//=== Creation of the Buttons ======================/
@@ -93,8 +99,23 @@
buAdd = gtk_button_new_with_label("Add");
gtk_box_pack_start( GTK_BOX(boxV1), buAdd, FALSE, FALSE, 0 );
+
buRemove = gtk_button_new_with_label( "Remove" );
gtk_box_pack_start( GTK_BOX(boxV1), buRemove, FALSE, FALSE, 0 );
+
+ buEnable = gtk_button_new_with_label( "Enable" );
+ gtk_box_pack_start( GTK_BOX(boxV1), buEnable, FALSE, FALSE, 0 );
+
+ buDisable = gtk_button_new_with_label( "Disable" );
+ gtk_box_pack_start( GTK_BOX(boxV1), buDisable, FALSE, FALSE, 0 );
+
+ buToggle = gtk_button_new_with_label( "Toggle" );
+ gtk_box_pack_start( GTK_BOX(boxV1), buToggle, FALSE, FALSE, 0 );
+
+ buEdit = gtk_button_new_with_label( "Edit" );
+ gtk_box_pack_start( GTK_BOX(boxV1), buEdit, FALSE, FALSE, 0 );
+
+ gtk_widget_set_sensitive(buEdit, FALSE); //not yet implemented
gtk_widget_show_all(winBreakpoints);
@@ -103,6 +124,10 @@
gtk_signal_connect( GTK_OBJECT(clBreakpoints), "unselect-row", on_row_unselection, NULL );
gtk_signal_connect( GTK_OBJECT(buAdd), "clicked", on_add, NULL );
gtk_signal_connect( GTK_OBJECT(buRemove), "clicked", on_remove, NULL );
+ gtk_signal_connect( GTK_OBJECT(buEnable), "clicked", on_enable, NULL );
+ gtk_signal_connect( GTK_OBJECT(buDisable), "clicked", on_disable, NULL );
+ gtk_signal_connect( GTK_OBJECT(buToggle), "clicked", on_toggle, NULL );
+ gtk_signal_connect( GTK_OBJECT(buEdit), "clicked", on_edit, NULL );
gtk_signal_connect( GTK_OBJECT(winBreakpoints), "destroy", on_close, NULL );
color_BPEnabled.red=0xFFFF;
@@ -119,13 +144,34 @@
//]=-=-=-=-=-=-=-=-=-=-=-=[ Update Breakpoints Display ]=-=-=-=-=-=-=-=-=-=-=-=[
+void get_breakpoint_display_string(char* buf, breakpoint* bpt)
+{
+ if(bpt->address == bpt->endaddr)
+ {
+ sprintf(buf, "%c%c%c 0x%08X",
+ (bpt->flags & BPT_FLAG_READ) ? 'R' : '-',
+ (bpt->flags & BPT_FLAG_WRITE) ? 'W' : '-',
+ (bpt->flags & BPT_FLAG_EXEC) ? 'X' : '-',
+ bpt->address);
+ }
+ else
+ {
+ sprintf(buf, "%c%c%c 0x%08X - 0x%08X",
+ (bpt->flags & BPT_FLAG_READ) ? 'R' : '-',
+ (bpt->flags & BPT_FLAG_WRITE) ? 'W' : '-',
+ (bpt->flags & BPT_FLAG_EXEC) ? 'X' : '-',
+ bpt->address, bpt->endaddr);
+ }
+}
+
void update_breakpoints( )
{
int num_rows=0;
- char line[1][16];
+ char line[1][64];
line[0][0] = 0;
+ printf("update_breakpoints()\n");
gtk_clist_freeze( GTK_CLIST(clBreakpoints) );
gtk_clist_clear( GTK_CLIST(clBreakpoints) );
int i;
@@ -134,7 +180,8 @@
for( i=0; i < g_NumBreakpoints; i++ )
{
- sprintf( line, "0x%lX", g_Breakpoints[i].address);
+ get_breakpoint_display_string(line, &g_Breakpoints[i]);
+ printf("%s\n", line);
gtk_clist_set_text( GTK_CLIST(clBreakpoints), i, 0, line );
if(BPT_CHECK_FLAG(g_Breakpoints[i], BPT_FLAG_ENABLED))
gtk_clist_set_background( GTK_CLIST(clBreakpoints), i, &color_BPEnabled);
@@ -152,8 +199,8 @@
//int i = lookup_breakpoint( address );
- remove_breakpoint_by_num( row );
-
+ remove_breakpoint_by_num( row );
+
//gtk_clist_remove( GTK_CLIST(clBreakpoints), row);
update_breakpoints();
}
@@ -175,17 +222,63 @@
static gint modify_address(ClistEditData *ced, const gchar *old, const gchar *new, gpointer data)
{
- uint32 address;
+ breakpoint newbp; //New breakpoint to be added
+ int i;
- if( sscanf(new, "%lX", &address) != 1)
+ //printf( "modify_address %lX \"%s\"\n", address, new);
+
+ //Input format: " [r][w][x] address [endaddr]"
+ //*: if present, disabled by default
+ //r: if present, break on read
+ //w: if present, break on write
+ //x: if present, break on execute (if none of r, w, x is specified execute is the default)
+ //address: where to break
+ //endaddr: if specified, break on all addresses address <= x <= endaddr
+
+ //Enabled by default
+ newbp.flags = BPT_FLAG_ENABLED;
+ newbp.address = 0;
+ newbp.endaddr = 0;
+
+ //Read flags
+ for(i=0; new[i]; i++)
{
- return FALSE;
- }
- printf( "%lX\n", address);
- gtk_clist_set_row_data( GTK_CLIST(ced->clist), ced->row, (gpointer) address );
-
- g_Breakpoints[g_NumBreakpoints-1].address=address;
- return TRUE;
+ if((new[i] == ' ') //if space,
+ || ((new[i] >= '0') && (new[i] <= '9')) //number,
+ || ((new[i] >= 'A') && (new[i] <= 'F')) //A-F,
+ || ((new[i] >= 'a') && (new[i] <= 'f'))) break; //or a-f, address begins here.
+ else if(new[i] == '*') newbp.flags &= ~BPT_FLAG_ENABLED;
+ else if((new[i] == 'r') || (new[i] == 'R')) newbp.flags |= BPT_FLAG_READ;
+ else if((new[i] == 'w') || (new[i] == 'w')) newbp.flags |= BPT_FLAG_WRITE;
+ else if((new[i] == 'x') || (new[i] == 'x')) newbp.flags |= BPT_FLAG_EXEC;
+ }
+
+ //If none of r/w/x specified, default to exec
+ if(!(newbp.flags & (BPT_FLAG_EXEC | BPT_FLAG_READ | BPT_FLAG_WRITE)))
+ BPT_SET_FLAG(newbp, BPT_FLAG_EXEC);
+
+ //Read address
+ printf("Address \"%s\"\n", &new[i]);
+ i = sscanf(&new[i], "%lX %lx", &newbp.address, &newbp.endaddr);
+ if(!i)
+ {
+ //fixme: better way to display error message
+ printf("Invalid address\n");
+ return FALSE;
+ }
+ else if(i == 1) newbp.endaddr = newbp.address;
+
+ printf("Adding breakpoint on 0x%08X - 0x%08X flags 0x%08X\n", newbp.address, newbp.endaddr, newbp.flags);
+ if(add_breakpoint_struct(&newbp) == -1)
+ {
+ //fixme: warning message
+ return FALSE;
+ }
+
+ gtk_clist_set_row_data( GTK_CLIST(ced->clist), ced->row, (gpointer) newbp.address );
+
+ update_breakpoints();
+ return FALSE; //don't add the typed string, update_breakpoints() handles it
}
static void on_add()
@@ -193,23 +286,40 @@
int new_row; //index of the appended row.
char **line;
- if(add_breakpoint(address) == -1)
+ //fixme: hacks ahoy!
+ line = malloc(1*sizeof(char*));
+ line[0] = malloc(64*sizeof(char));
+
+ line[0] = (char*)malloc(64);
+ //sprintf( line[0], "0x%lX", address);
+ line[0] = 0;
+ new_row = gtk_clist_append( GTK_CLIST(clBreakpoints), line );
+ gtk_clist_set_text( GTK_CLIST(clBreakpoints), new_row, 0, (gpointer) line[0] );
+
+ clist_edit_by_row(GTK_CLIST(clBreakpoint s), new_row, 0, modify_address, NULL);
+ free(line[0]);
+ free(line);
+
+/*
+ if(add_breakpoint(address) == -1)
{
//TODO: warn max number of breakpoints reached
return;
}
-
- line = malloc(1*sizeof(char*)); // new breakpoint:
- line[0] = malloc(16*sizeof(char)); // - address
+
+ line = malloc(1*sizeof(char*)); // new breakpoint:
+ line[0] = malloc(64*sizeof(char)); // - address
// TODO: line[1] = malloc(16*sizeof(char)); // - enabled/disabled
sprintf( line[0], "0x%lX", address);
new_row = gtk_clist_append( GTK_CLIST(clBreakpoints), line );
- gtk_clist_set_text( GTK_CLIST(clBreakpoints), new_row, 0, (gpointer) address );
+ gtk_clist_set_text( GTK_CLIST(clBreakpoints), new_row, 0, (gpointer) line[0] );
clist_edit_by_row(GTK_CLIST(clBreakpoint s), new_row, 0, modify_address, NULL);
+ update_breakpoints();
//FIXME:color are not updated +everything
+ */
}
@@ -218,10 +328,11 @@
int i;
uint32 address;
- gtk_clist_freeze( GTK_CLIST(clBreakpoints) );
+ if(!g_NumBreakpoints) return;
+ gtk_clist_freeze( GTK_CLIST(clBreakpoints) );
for( i=BREAKPOINTS_MAX_NUMBER-1; i>=0; i-- )
{
- if( selected[i] == 1 ) {
+ if( selected[i] == 1 ) {
address = (uint32) gtk_clist_get_row_data( GTK_CLIST(clBreakpoints), i);
remove_breakpoint_by_row( i );
update_desasm_color( address );
@@ -233,6 +344,51 @@
}
+static void on_enable()
+{
+ _toggle(1);
+}
+
+static void on_disable()
+{
+ _toggle(0);
+}
+
+
+static void on_toggle()
+{
+ _toggle(-1);
+}
+
+//flag is 1 for enable, 0 for disable, -1 for toggle
+static void _toggle(int flag)
+{
+ int i;
+
+ if(!g_NumBreakpoints) return;
+ for( i=BREAKPOINTS_MAX_NUMBER-1; i>=0; i-- )
+ {
+ if( selected[i] == 1 )
+ {
+ if(flag == 1) enable_breakpoint(i);
+ else if(flag == 0) disable_breakpoint(i);
+ else if(BPT_CHECK_FLAG(g_Breakpoints[i], BPT_FLAG_ENABLED)) disable_breakpoint(i);
+ else enable_breakpoint(i);
+ selected[i] = 0;
+ }
+ }
+ update_breakpoints();
+}
+
+
+static void on_edit()
+{
+ //FIXME: not yet implemented. should display the textbox again as if we were entering
+ //a new breakpoint, and update the selected one. probably disable the button if more
+ //than one or none selected.
+}
+
+
static void on_close()
{
breakpoints_opened = 0;
Index: main/gui_gtk/debugger/breakpoints.h
======================================== ===========================
--- main/gui_gtk/debugger/breakpoints.h (revision 338)
+++ main/gui_gtk/debugger/breakpoints.h (working copy)
@@ -45,6 +45,7 @@
GtkWidget *winBreakpoints;
void init_breakpoints();
+void get_breakpoint_display_string(char* buf, breakpoint* bpt);
int add_breakpoint( uint32 address );
//int check_breakpoints( uint32 address );
void update_breakpoints();
DarkJezter
May 1st, 2008, 11:20
I've committed your patch to the debugger branch, as of revision 341. Looks great so far, one snag though. I've completely gutted the disassembler, so any click functionality in the disassembler window is not likely to work on it's own.
I'm still working on the disassembler, and once it's able to scroll over a range of addresses (it's currently set to a fixed address) I'll be looking at color & breakpoint assignment.
HyperHacker
May 2nd, 2008, 02:22
Well I didn't touch the disassembler, hence the colouring issue. About instruction editing, I was thinking something like this:
1) When a letter/number key is pressed, pop up a textbox for the row of the selected instruction (like the Add button in the breakpoint window) and enter that character in it.
2) When Enter is pressed, if a valid instruction is typed, replace the instruction and move the cursor to the next one. Otherwise pop up an error message.
3) When Escape is pressed or the popup box loses focus, don't replace the instruction or move the cursor.
4) When Down is pressed, or if Enter is pressed and nothing has been typed (just the character that was initially in the box; I'd just check if string length < 2), move the cursor but don't replace the instruction.
That way you can pretty much just select an instruction and start typing in new code. Only downside I see is not being able to paste, unless the textbox responds to paste events and checks for line breaks appropriately.
DarkJezter
May 2nd, 2008, 02:40
Editing the assembly in that fashion is probably a little ways off, right now binary edits of the assembly are simple, as the disassembler can then decipher what ever was entered. However in the case of the dynamic recompiler, we need to make sure that any newly entered code is somehow flagged as dirty and recompiled.
As far as entering assembly straight into the debugger, I'm all for the idea, but prior to working on the user interface I think we should get the raw assembler working. Parsing for mnemonics, and decoding addressing modes from assembly are a must before this is going to fly.
Unfortunately the new disassembler frontend is due for a lot of work before it's ready, and I'd try to shoo you away from doing too much work that assumes the current structure, as it's currently a mish-mash of old and new code.
Past that, I took a brief look at the breakpoint code, and while I haven't quite managed to create any memory breakpoints, I'm very pleased with how it's been modded into the existing framework. I hadn't written that breakpoint system more than a few days ago, and you extended it in exactly the fashion I hoped. Any idea on if/how much it affects the performance of the emulator core?
p.s. A fellow albertan?? What city you in?
HyperHacker
May 2nd, 2008, 10:04
Yeah, I'm gonna wait for you to finish up what you're doing with the disassembler before I bugger with it. One step at a time. :P Also, the breakpoint entry code is a bit broken, it won't accept uppercase W and X:
else if((new[i] == 'w') || (new[i] == 'w')) newbp.flags |= BPT_FLAG_WRITE;
else if((new[i] == 'x') || (new[i] == 'x'))
Anyway I didn't notice any performance hit. I didn't try it with the dynamic recompiler, but it seems to work just fine with the interpreter.
And I'm paranoid, so check your PMs. ;)
Wouldn't the interpreter be nearly unaffected by this? I'd have to think the Recompiler would not have too much trouble but at least a little more than the interpreter. AFAIK the only thing the interpeter needs to do is cache it again.
okaygo
May 3rd, 2008, 20:02
We are going to need this debugger to finish soft-resets.
DarkJezter
May 4th, 2008, 03:41
Ok, last commit to the branch has the core functionality of the disassembler done, with scrolling, highlighting and the works. Don't get me wrong, lots of debugging to do yet, but anything that depends on the disassembler should now be fair game...
So, consider any mods to what's in svn at this point in time fair game. Any patches I make for now is either going to be minor patches for segfaults, other minor bugs, or work on the x86 and x86_64 disassemblers. (anything in the ./debugger/x86* folders)
HyperHacker
May 7th, 2008, 09:11
So I'm hacking together a RAM editor; what's the safest way to read emulated memory? read_byte_in_memory() segfaults if the game hasn't started yet, even though the hash tables are initialized (not sure just what's going on there). It would also seem to have some side effects, as games had a nasty tendency to crash when I refreshed the view of RDRAM that way.
I see the disassembler is basically doing it manually, which is kind of ugly. Its method is alright for a disassembler, because there are only so many places you'd find code, but a RAM editor needs to be able to look/poke at all memory locations. For now I've just copied its method, and RDRAM viewing is working nicely. :)
DarkJezter
May 7th, 2008, 11:35
I think the best thing to do for now is to put in some redundant checks to pull null values if the memory system hasn't been initialized. I think this is a temporary condition, and once this is remerged with the trunk there are some new calls pertaining to the soft reset that should allow us to initialize the debugger if, and only if the emulator is in a prepared state. For example, right now the disassembler shows 'NOTCOMPILED' when trying to view recompiled code that hasn't been generated yet, and doesn't get generated until you let the emulator run for one cycle.
What I foresee after we remerge with the trunk is that once a rom is loaded, the emulator state is prepared (including dynamic recompiliation), at which time the debugger can be started. Then by pressing the start button, all we're doing is starting the emulation itself, and not the initialization of the emulator. This would for example let us start the debugger, write values to memory, and then expect them to still be there when we hit the start button.
I'm not sure if there's a gain to this, but perhaps we may want to merge in the soft-reset code before remerging with the trunk, especially if the trunk will already have the soft-reset code merged with it. Just a thought for later consideration.
DarkJezter
May 7th, 2008, 11:37
Oh, and Hyperhacker, as far as memory access goes, we need to seperate the function of accessing memory from the form of the frontend. I'll start working on a memory.c file for the debugger folder and get it put into the svn asap.
[EDIT]
Ok, I hacked something together fairly quickly, this will at least replicate the readmem functionality used in the debugger, and it provides a nice interface for memory access. I hope this isn't too different from what you envisioned, however this is just a starting point that we should be able to extend to allow full access to all memory in the address space.
DarkJezter
May 7th, 2008, 11:38
P.S. Anyone care to take over writing a variable byte disassembler?? >:)
Richard42
May 7th, 2008, 17:04
P.S. Anyone care to take over writing a variable byte disassembler?? >:)
I would suggest that you use libopcodes instead of writing your own disassembler. You will need to link against "libopcodes" and include "dis-asm.h", which contains the API information.
MasterPhW
May 7th, 2008, 18:43
...
...
...
Sorry to jump into this discussion, but please DarkJezter, you know we have a edit button, you used it in the second post, so you don't need to triplepost in 5 minutes...
HyperHacker
May 8th, 2008, 02:15
OK, here's where my inexperience with SVN shows. :P How do I check out the version with your latest updates without losing all the changes I've made? Is it possible to like automatically get a patch between the version I checked out earlier and the version that's in there now and apply that to my working copy?
Richard42
May 8th, 2008, 04:42
It's even easier than that. Assuming that you did a 'checkout' and not an 'export', you can update to the head of the trunk (or whatever branch you checked out) by doing an 'svn update' from the root of your source tree. You can see what files are different between your local copy and the server by doing an 'svn status -u'. If you want to see a diff listing of all the changes that you've made, just do 'svn diff'.
Much of the recent work has been done on branches though. If you checked out from the trunk you'll have to wait until we merge the branch into the trunk or else do a merge from the branch into your local copy.
HyperHacker
May 8th, 2008, 05:32
k, so...
hyperhacker@Mercury:~/Desktop/junk/mupen$ svn update
svn: Unable to lock 'debugger'
>_<
okaygo
May 8th, 2008, 06:53
k, so...
hyperhacker@Mercury:~/Desktop/junk/mupen$ svn update
svn: Unable to lock 'debugger'
>_<
svn unlock
HyperHacker
May 8th, 2008, 08:54
Huh, and all of Google couldn't tell me that. I ended up moving 'debugger' elsewhere, updating, and just re-merging the changes, fortunately not many.
Memory editor is working nicely now. Needs a few minor tweaks (i.e. don't let the cursor end up between bytes with the arrow keys) but nothing showstopping. Also, you can add the 'L' flag to a breakpoint to have it log to the console (and later a message window) when it hits. The eventual goal is to have another flag that specifies the breakpoint is only for logging and does not stop execution. Not sure how difficult this will be.
Also, the edit button works now, and uppercase W and X flags are accepted.
The only thing I'm finding is the whole debugger frontend stops responding after playing with it a bit. I would set a breakpoint on writes to a given address, then when it hits, set another on execution of an instruction just before that. Once that one hits, not much works anymore. The run/pause button still responds, but everything seems to be frozen (i.e. working but not redrawing) or just doesn't respond. Thing is I'm not sure if this was a problem before I changed anything, so if it's not doing that now, there's probably a bug in here somewhere.
Anyway, patch:
Index: debugger/breakpoints.c
======================================== ===========================
--- debugger/breakpoints.c (revision 401)
+++ debugger/breakpoints.c (working copy)
@@ -128,6 +128,9 @@
bpt=lookup_breakpoint( address, flags );
if(bpt != -1)
{
+ if(BPT_CHECK_FLAG(g_Breakpoints[bpt], BPT_FLAG_LOG))
+ log_breakpoint(PC->addr, flags, address);
+
run = 0;
switch_button_to_run();
update_debugger_frontend();
@@ -141,3 +144,14 @@
}
return -1;
}
+
+int log_breakpoint(uint32 PC, uint32 Flag, uint32 Access)
+{
+ char msg[32];
+
+ if(Flag & BPT_FLAG_READ) sprintf(msg, "0x%08X read 0x%08X", PC, Access);
+ else if(Flag & BPT_FLAG_WRITE) sprintf(msg, "0x%08X wrote 0x%08X", PC, Access);
+ else sprintf(msg, "0x%08X executed", PC);
+ printf("BPT: %s\n", msg);
+ //todo: log to file
+}
Index: debugger/debugger.c
======================================== ===========================
--- debugger/debugger.c (revision 401)
+++ debugger/debugger.c (working copy)
@@ -63,15 +63,20 @@
// Update debugger state and display.
// Should be called after each R4300 instruction.
{
-
+ int bpt;
+
if(run==2) {
- if( check_breakpoints(PC->addr)==-1 ) {
+ bpt = check_breakpoints(PC->addr);
+ if( bpt==-1 ) {
previousPC = PC->addr;
return;
}
else {
run = 0;
switch_button_to_run();
+
+ if(BPT_CHECK_FLAG(g_Breakpoints[bpt], BPT_FLAG_LOG))
+ log_breakpoint(PC->addr, BPT_FLAG_EXEC, 0);
}
}
else if ( previousPC == PC->addr )
Index: debugger/breakpoints.h
======================================== ===========================
--- debugger/breakpoints.h (revision 401)
+++ debugger/breakpoints.h (working copy)
@@ -43,6 +43,7 @@
#define BPT_FLAG_READ 0x08
#define BPT_FLAG_WRITE 0x10
#define BPT_FLAG_EXEC 0x20
+#define BPT_FLAG_LOG 0x40 //Log to the console when this breakpoint hits.
#define BPT_CHECK_FLAG(a, b) ((a.flags & b) == b)
#define BPT_SET_FLAG(a, b) a.flags = (a.flags | b);
@@ -68,5 +69,6 @@
int check_breakpoints( uint32 address );
int check_breakpoints_on_mem_access( uint32 address, uint32 size, uint32 flags );
int lookup_breakpoint( uint32 address, uint32 flags );
+int log_breakpoint(uint32 PC, uint32 Flag, uint32 Access);
#endif // BREAKPOINTS_H
Index: debugger/memory.c
======================================== ===========================
--- debugger/memory.c (revision 401)
+++ debugger/memory.c (working copy)
@@ -227,6 +227,24 @@
}
}
+uint8 read_memory_8(uint32 addr)
+{
+ uint32 word;
+
+ word = read_memory_32(addr & ~3);
+ return (word >> ((3 - (addr & 3)) * 8)) & 0xFF;
+}
+
+void write_memory_8(uint32 addr, uint8 value)
+{
+ uint32 word, mask;
+
+ word = read_memory_32(addr & ~3);
+ mask = 0xFF << ((3 - (addr & 3)) * 8);
+ word = (word & ~mask) | (value << ((3 - (addr & 3)) * 8));
+ write_memory_32(addr & ~3, word);
+}
+
uint32 get_memory_flags(uint32 addr){
int type=get_memory_type(addr);
uint32 flags = 0;
Index: debugger/memory.h
======================================== ===========================
--- debugger/memory.h (revision 401)
+++ debugger/memory.h (working copy)
@@ -60,6 +60,8 @@
uint32 read_memory_32(uint32);
void write_memory_32(uint32, uint32);
+uint8 read_memory_8(uint32 addr);
+void write_memory_8(uint32 addr, uint8 value);
uint32 get_memory_flags(uint32);
int get_memory_type(uint32);
Index: pre.mk
======================================== ===========================
--- pre.mk (revision 401)
+++ pre.mk (working copy)
@@ -63,7 +63,7 @@
INSTALL = ginstall
# set base CFLAGS and LDFLAGS for all systems
-CFLAGS = -pipe -O0 -ffast-math -funroll-loops -fexpensive-optimizations -fno-strict-aliasing
+CFLAGS = -pipe -O3 -ffast-math -funroll-loops -fexpensive-optimizations -fno-strict-aliasing
LDFLAGS =
# set special flags per-system
Index: Makefile
======================================== ===========================
--- Makefile (revision 401)
+++ Makefile (working copy)
@@ -156,6 +156,7 @@
main/gui_gtk/debugger/debugger.o \
main/gui_gtk/debugger/breakpoints.o \
main/gui_gtk/debugger/desasm.o \
+ main/gui_gtk/debugger/memedit.o \
main/gui_gtk/debugger/registers.o \
main/gui_gtk/debugger/regGPR.o \
main/gui_gtk/debugger/regCop0.o \
Index: main/gui_gtk/debugger/debugger.c
======================================== ===========================
--- main/gui_gtk/debugger/debugger.c (revision 401)
+++ main/gui_gtk/debugger/debugger.c (working copy)
@@ -55,6 +55,10 @@
gdk_threads_enter();
init_breakpoints();
gdk_threads_leave();
+
+ gdk_threads_enter();
+ init_memedit();
+ gdk_threads_leave();
gdk_threads_enter();
init_TLBwindow();
@@ -80,4 +84,9 @@
update_TLBwindow();
gdk_threads_leave();
}
+ if(memedit_opened) {
+ gdk_threads_enter();
+ update_memory_editor();
+ gdk_threads_leave();
+ }
}
Index: main/gui_gtk/debugger/debugger.h
======================================== ===========================
--- main/gui_gtk/debugger/debugger.h (revision 401)
+++ main/gui_gtk/debugger/debugger.h (working copy)
@@ -42,6 +42,7 @@
#include "desasm.h"
#include "registers.h"
#include "regTLB.h"
+#include "memedit.h"
#define DEBUGGER_VERSION "0.0.2 - WIP2"
Index: main/gui_gtk/debugger/ui_disasm_list.c
======================================== ===========================
--- main/gui_gtk/debugger/ui_disasm_list.c (revision 401)
+++ main/gui_gtk/debugger/ui_disasm_list.c (working copy)
@@ -1,5 +1,6 @@
#include "ui_disasm_list.h"
#include "debugger.h"
+#include "../../../debugger/memory.h"
/* boring declarations of local functions */
Index: main/gui_gtk/debugger/breakpoints.c
======================================== ===========================
--- main/gui_gtk/debugger/breakpoints.c (revision 401)
+++ main/gui_gtk/debugger/breakpoints.c (working copy)
@@ -114,8 +114,6 @@
buEdit = gtk_button_new_with_label( "Edit" );
gtk_box_pack_start( GTK_BOX(boxV1), buEdit, FALSE, FALSE, 0 );
-
- gtk_widget_set_sensitive(buEdit, FALSE); //not yet implemented
gtk_widget_show_all(winBreakpoints);
@@ -148,18 +146,20 @@
{
if(bpt->address == bpt->endaddr)
{
- sprintf(buf, "%c%c%c 0x%08X",
+ sprintf(buf, "%c%c%c%c 0x%08X",
(bpt->flags & BPT_FLAG_READ) ? 'R' : '-',
(bpt->flags & BPT_FLAG_WRITE) ? 'W' : '-',
(bpt->flags & BPT_FLAG_EXEC) ? 'X' : '-',
+ (bpt->flags & BPT_FLAG_LOG) ? 'L' : '-',
bpt->address);
}
else
{
- sprintf(buf, "%c%c%c 0x%08X - 0x%08X",
+ sprintf(buf, "%c%c%c$c 0x%08X - 0x%08X",
(bpt->flags & BPT_FLAG_READ) ? 'R' : '-',
(bpt->flags & BPT_FLAG_WRITE) ? 'W' : '-',
(bpt->flags & BPT_FLAG_EXEC) ? 'X' : '-',
+ (bpt->flags & BPT_FLAG_LOG) ? 'L' : '-',
bpt->address, bpt->endaddr);
}
}
@@ -224,15 +224,17 @@
static gint modify_address(ClistEditData *ced, const gchar *old, const gchar *new, gpointer data)
{
breakpoint newbp; //New breakpoint to be added
- int i;
+ int i, j;
+ char line[64];
//printf( "modify_address %lX \"%s\"\n", address, new);
- //Input format: " [r][w][x] address [endaddr]"
+ //Input format: " [r][w][x][l] address [endaddr]"
//*: if present, disabled by default
//r: if present, break on read
//w: if present, break on write
//x: if present, break on execute (if none of r, w, x is specified execute is the default)
+ //l (L): if present, log to the console when this breakpoint hits.
//address: where to break
//endaddr: if specified, break on all addresses address <= x <= endaddr
@@ -241,26 +243,38 @@
newbp.address = 0;
newbp.endaddr = 0;
- //Read flags
+ //Copy line, stripping dashes, so sscanf() will work for getting addresses when editing
+ j = 0;
for(i=0; new[i]; i++)
{
- if((new[i] == ' ') //if space,
- || ((new[i] >= '0') && (new[i] <= '9')) //number,
- || ((new[i] >= 'A') && (new[i] <= 'F')) //A-F,
- || ((new[i] >= 'a') && (new[i] <= 'f'))) break; //or a-f, address begins here.
- else if(new[i] == '*') newbp.flags &= ~BPT_FLAG_ENABLED;
- else if((new[i] == 'r') || (new[i] == 'R')) newbp.flags |= BPT_FLAG_READ;
- else if((new[i] == 'w') || (new[i] == 'w')) newbp.flags |= BPT_FLAG_WRITE;
- else if((new[i] == 'x') || (new[i] == 'x')) newbp.flags |= BPT_FLAG_EXEC;
+ if(new[i] == '-') continue;
+ line[j] = new[i];
+ j++;
+ if((j + 1) >= sizeof(line)) break;
}
+ line[j] = 0;
+
+ //Read flags
+ for(i=0; line[i]; i++)
+ {
+ if((line[i] == ' ') //if space,
+ || ((line[i] >= '0') && (line[i] <= '9')) //number,
+ || ((line[i] >= 'A') && (line[i] <= 'F')) //A-F,
+ || ((line[i] >= 'a') && (line[i] <= 'f'))) break; //or a-f, address begins here.
+ else if(line[i] == '*') newbp.flags &= ~BPT_FLAG_ENABLED;
+ else if((line[i] == 'r') || (line[i] == 'R')) newbp.flags |= BPT_FLAG_READ;
+ else if((line[i] == 'w') || (line[i] == 'W')) newbp.flags |= BPT_FLAG_WRITE;
+ else if((line[i] == 'x') || (line[i] == 'X')) newbp.flags |= BPT_FLAG_EXEC;
+ else if((line[i] == 'l') || (line[i] == 'L')) newbp.flags |= BPT_FLAG_LOG;
+ }
//If none of r/w/x specified, default to exec
if(!(newbp.flags & (BPT_FLAG_EXEC | BPT_FLAG_READ | BPT_FLAG_WRITE)))
BPT_SET_FLAG(newbp, BPT_FLAG_EXEC);
//Read address
- printf("Address \"%s\"\n", &new[i]);
- i = sscanf(&new[i], "%lX %lx", &newbp.address, &newbp.endaddr);
+ //printf("Address \"%s\"\n", &line[i]);
+ i = sscanf(&line[i], "%lX %lx", &newbp.address, &newbp.endaddr);
if(!i)
{
//fixme: better way to display error message
@@ -269,12 +283,20 @@
}
else if(i == 1) newbp.endaddr = newbp.address;
- printf("Adding breakpoint on 0x%08X - 0x%08X flags 0x%08X\n", newbp.address, newbp.endaddr, newbp.flags);
- if(add_breakpoint_struct(&newbp) == -1)
- {
- //fixme: warning message
- return FALSE;
+ if(breakpoints_editing)
+ {
+ printf("Updating breakpoint #%u on 0x%08X - 0x%08X flags 0x%08X\n", ced->row, newbp.address, newbp.endaddr, newbp.flags);
+ memcpy(&g_Breakpoints[ced->row], &newbp, sizeof(breakpoint));
}
+ else
+ {
+ printf("Adding breakpoint on 0x%08X - 0x%08X flags 0x%08X\n", newbp.address, newbp.endaddr, newbp.flags);
+ if(add_breakpoint_struct(&newbp) == -1)
+ {
+ //fixme: warning message
+ return FALSE;
+ }
+ }
gtk_clist_set_row_data( GTK_CLIST(ced->clist), ced->row, (gpointer) newbp.address );
@@ -289,6 +311,7 @@
char **line;
//fixme: hacks ahoy!
+ breakpoints_editing = 0;
line = malloc(1*sizeof(char*));
line[0] = malloc(64*sizeof(char));
@@ -388,10 +411,24 @@
static void on_edit()
{
+ int i, row = -1;
//FIXME: not yet implemented. should display the textbox again as if we were entering
//a new breakpoint, and update the selected one. probably disable the button if more
//than one or none selected.
- refresh_desasm();
+
+ for(i=0; i < g_NumBreakpoints; i++)
+ {
+ if(selected[i])
+ {
+ row = i;
+ break;
+ }
+ }
+ if(row == -1) return;
+ breakpoints_editing = 1;
+ clist_edit_by_row(GTK_CLIST(clBreakpoint s), row, 0, modify_address, NULL);
+
+ refresh_desasm();
}
Index: main/gui_gtk/debugger/breakpoints.h
======================================== ===========================
--- main/gui_gtk/debugger/breakpoints.h (revision 401)
+++ main/gui_gtk/debugger/breakpoints.h (working copy)
@@ -41,6 +41,7 @@
int breakpoints_opened;
+int breakpoints_editing; //1 when editing a breakpoint, 0 when adding
GtkWidget *winBreakpoints;
And new files (in /main/gui_gtk/debugger) - memedit.h:
/**
* Mupen64 - memedit.h
* Copyright (C) 2002 DavFr - robind@esiee.fr
*
* If you want to contribute to this part of the project please
* contact me (or Hacktarux) first.
*
* Mupen64 homepage: http://mupen64.emulation64.com
* email address: hacktarux@yahoo.fr
*
*
* This program is free software; you can redistribute it and/
* or modify it under the terms of the GNU General Public Li-
* cence as published by the Free Software Foundation; either
* version 2 of the Licence.
*
* This program is distributed in the hope that it will be use-
* ful, but WITHOUT ANY WARRANTY; without even the implied war-
* ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public Licence for more details.
*
* You should have received a copy of the GNU General Public
* Licence along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**/
#ifndef GUIGTK_MEMEDIT_H
#define GUIGTK_MEMEDIT_H
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <gtk/gtk.h>
#include <glib.h>
#include "debugger.h"
#include "ui_clist_edit.h"
int memedit_opened;
GtkWidget *winMemEdit;
void update_memory_editor();
void init_memedit();
#endif // MEMEDIT_H
memedit.c:
/**
* Mupen64 - memedit.c
* Copyright (C) 2002 DavFr - robind@esiee.fr
*
* If you want to contribute to this part of the project please
* contact me (or Hacktarux) first.
*
* Mupen64 homepage: http://mupen64.emulation64.com
* email address: hacktarux@yahoo.fr
*
*
* This program is free software; you can redistribute it and/
* or modify it under the terms of the GNU General Public Li-
* cence as published by the Free Software Foundation; either
* version 2 of the Licence.
*
* This program is distributed in the hope that it will be use-
* ful, but WITHOUT ANY WARRANTY; without even the implied war-
* ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public Licence for more details.
*
* You should have received a copy of the GNU General Public
* Licence along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**/
#include "memedit.h"
#include <gdk/gdkkeysyms.h>
GtkTextBuffer *textbuf;
GtkTextTag *textfont;
GtkTextIter textstart, textend;
uint32 memedit_address, memedit_numlines;
static gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
static void on_close();
void update_memory_editor()
{
int i, j, k;
char line[75];
char* buf;
uint32 addr;
uint8 byte[16];
buf = (char*)malloc(memedit_numlines * sizeof(line) * sizeof(char));
if(!buf)
{
printf("update_memory_editor: not enough memory\n");
return;
}
//Read memory
//todo: display 'XX' or something for unreadable memory,
//maybe colour the text
addr = memedit_address;
buf[0] = 0;
for(i=0; i<memedit_numlines; i++)
{
for(j=0; j<16; j++)
byte[j] = read_memory_8(addr + j);
sprintf(line, "%08X|%02X %02X %02X %02X|%02X %02X %02X %02X|%02X %02X %02X %02X|%02X %02X %02X %02X|",
addr, byte[0], byte[1], byte[2], byte[3], byte[4], byte[5], byte[6], byte[7],
byte[8], byte[9], byte[10], byte[11], byte[12], byte[13], byte[14], byte[15]);
strcat(buf, line);
for(j=0; j<16; j++)
{
if((byte[j] >= 0x20) && (byte[j] <= 0x7E))
line[j] = byte[j];
else line[j] = '.';
}
if(i < (memedit_numlines - 1))
{
line[16] = '\n';
line[17] = 0;
}
else line[16] = 0;
strcat(buf, line);
addr += 16;
}
//Update textbox
gtk_text_buffer_set_text(textbuf, buf, -1);
gtk_text_buffer_get_bounds(textbuf, &textstart, &textend); //todo: there must be a better way to keep it in monospace
gtk_text_buffer_apply_tag(textbuf, textfont, &textstart, &textend);
free(buf);
}
//]=-=-=-=-=-=-=-=-=-=-=-=[ Memory Editor Initialisation ]=-=-=-=-=-=-=-=-=-=-=-=[
void init_memedit()
{
int i;
GtkWidget *boxH1,
*scrolledwindow1,
*boxV1,
*textbox;
memedit_opened = 1;
memedit_address = 0x800F6A10; //0x800EB5D0;
memedit_numlines = 16;
//=== Creation of Memory Editor ===========/
winMemEdit = gtk_window_new( GTK_WINDOW_TOPLEVEL );
gtk_window_set_title( GTK_WINDOW(winMemEdit), "Memory");
gtk_window_set_default_size( GTK_WINDOW(winMemEdit), 500, 200);
gtk_container_set_border_width( GTK_CONTAINER(winMemEdit), 2);
textbox = gtk_text_view_new();
textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(t extbox));
textfont = gtk_text_buffer_create_tag(textbuf, "font", "font", "monospace", NULL);
update_memory_editor();
gtk_container_add(GTK_CONTAINER(winMemEd it), textbox);
gtk_widget_show_all(winMemEdit);
//=== Signal Connections ===========================/
gtk_signal_connect( GTK_OBJECT(winMemEdit), "destroy", on_close, NULL );
gtk_signal_connect( GTK_OBJECT(textbox), "key-press-event", on_key_press, NULL );
}
static gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
GtkTextMark* cursor;
GtkTextIter cursor_iter;
int cursorpos, linepos, linenum, linelen = 74;
uint32 key, addr;
uint8 byte;
//Figure out what was typed.
//todo: make this not suck
switch(event->keyval)
{
case GDK_0:
case GDK_1:
case GDK_2:
case GDK_3:
case GDK_4:
case GDK_5:
case GDK_6:
case GDK_7:
case GDK_8:
case GDK_9:
key = event->keyval - GDK_0;
break;
case GDK_KP_0:
case GDK_KP_1:
case GDK_KP_2:
case GDK_KP_3:
case GDK_KP_4:
case GDK_KP_5:
case GDK_KP_6:
case GDK_KP_7:
case GDK_KP_8:
case GDK_KP_9:
key = event->keyval - GDK_KP_0;
break;
case GDK_A:
case GDK_B:
case GDK_C:
case GDK_D:
case GDK_E:
case GDK_F:
key = (event->keyval - GDK_A) + 10;
break;
case GDK_a:
case GDK_b:
case GDK_c:
case GDK_d:
case GDK_e:
case GDK_f:
key = (event->keyval - GDK_a) + 10;
break;
default:
key = event->keyval;
break;
}
//Get the cursor position.
cursor = gtk_text_buffer_get_insert(textbuf);
gtk_text_buffer_get_iter_at_mark(textbuf , &cursor_iter, cursor);
cursorpos = gtk_text_iter_get_offset(&cursor_iter);
//React to the keypress.
//todo: skip between-bytes and separator areas when
//navigating with arrow keys or mouse.
if(key == GDK_Up)
{
cursorpos -= linelen;
if(cursorpos < 0)
{
memedit_address -= 16;
cursorpos += linelen;
}
}
else if(key == GDK_Down)
{
cursorpos += linelen;
if(cursorpos >= (linelen * memedit_numlines))
{
memedit_address += 16;
cursorpos -= linelen;
}
}
else if(key == GDK_Left)
{
if(cursorpos) cursorpos--;
}
else if(key == GDK_Right)
{
if(cursorpos) cursorpos++;
}
else
{
//Determine where we are.
linenum = cursorpos / linelen;
linepos = cursorpos % linelen;
if((linepos == 8) || (linepos == 56)) //address/hex or hex/text separators
{
linepos++;
cursorpos++;
}
//printf("line %u, pos %u: ", linenum, linepos);
if(linepos >= (linelen - 1)); //end of line; do nothing
//If cursor is in address
else if(linepos < 8)
{
if(key < 16) //hex number entered
{
//yes, I probably _could_ have made this line uglier.
memedit_address = (memedit_address & ~(0xF << (4 * (7 - linepos)))) | (key << (4 * (7 - linepos)));
cursorpos++;
if((cursorpos % linelen) == 8) cursorpos++; //skip address/hex separator
}
//todo: else, beep or something
}
//If cursor is in text
else if(linepos >= 56)
{
//If a non-special key, except Enter, was pressed
if((event->keyval <= 0xFF) || (event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
{
linepos -= 57; //Character index
addr = memedit_address + (linenum * 16) + linepos; //Address to edit
if(event->keyval <= 0xFF) byte = event->keyval;
else if((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter)) byte = 0x0A; //Enter inserts line break
write_memory_8(addr, byte);
cursorpos++;
if((cursorpos % linelen) == 73) //wrote past last character
{
cursorpos += 58; //to first char on next line
if(cursorpos >= (linelen * memedit_numlines)) //past end of box
{
memedit_address += 16;
cursorpos -= linelen;
}
}
}
else if(event->keyval == GDK_BackSpace) //back one character
{
cursorpos--;
if((cursorpos % linelen) < 57) //before first character
{
cursorpos -= 58; //to last char on prev line
if(cursorpos < 0)
{
memedit_address -= 16;
cursorpos += linelen;
}
}
}
}
//If cursor is in hex
else
{
linepos -= 8;
if(!(linepos % 3)) //between bytes
{
cursorpos++;
linepos++;
}
addr = memedit_address + (linenum * 16) + (linepos / 3);
//printf("byte %u (%08X) nybble %u, ", linepos / 3, addr, (linepos % 3) - 1);
if(key < 16) //hex number entered
{
byte = read_memory_8(addr);
//printf("%02X -> ", byte);
if((linepos % 3) - 1) //low nybble
{
byte = (byte & 0xF0) | (uint8)key;
cursorpos++; //skip the between-bytes area
}
else byte = (byte & 0x0F) | ((uint8)key << 4);
//printf("%02X\n", byte);
write_memory_8(addr, byte);
cursorpos++;
if((cursorpos % linelen) == 57) //wrote past last byte
{
cursorpos += 26; //to first byte on next line
if(cursorpos >= (linelen * memedit_numlines)) //past end of box
{
memedit_address += 16;
cursorpos -= linelen;
}
}
} //if hex number entered
} //if in hex
}
//0.........1.........2.........3......... 4.........5.........6.........7..
//|00 01 02 03|04 05 06 07|08 09 0A 0B|0C 0D 0E 0F|0123456789ABCDEF
//80000000|00 01 02 03|04 05 06 07|08 09 0A 0B|0C 0D 0E 0F|0123456789ABCDEF
update_memory_editor();
//Restore the cursor position.
gtk_text_buffer_get_iter_at_offset(textb uf, &cursor_iter, cursorpos);
gtk_text_buffer_place_cursor(textbuf, &cursor_iter);
return TRUE;
}
static void on_close()
{
memedit_opened = 0;
}
The memory editor should be simple to operate. Type over the hex or ASCII to edit it, and type over the address to move. It updates every time you press a key, regardless of whether anything changed, so you can hold a key like . to constantly refresh. (Auto-refresh checkbox is planned for later.) The cursor should move smoothly between bytes and lines, scrolling as you pass the top or bottom. Backspace in text (not hex yet) moves back, enter inserts 0x0A.
Still needs to handle cut/copy/paste and backspacing in the hex/address areas. Works nice though. :D
DarkJezter
May 9th, 2008, 11:10
Awesome, I can't wait to try it out.
Did you think anything of the interface I tried to set up? I don;t know if you've used it at all or not. I should have the patch in the branch soon.
HyperHacker
May 9th, 2008, 11:36
You mean read_memory_32() and write_memory_32()? They seem to work fine (except obviously being incomplete), but I was thinking it'd be nice to have them return whether they were successful (with read being passed a pointer to read into), so it can somehow highlight unreadable/unwritable memory.
Also, even though I'm using the interpreter core, it seems like nothing happens if I change an opcode in memory after it's executed. Try in Super Mario 64 (USA, MD5 20B854B239203BAF6C961B850A4A51A2); the halfword at 802541AA is the lower part of an instruction that controls how fast you lose health underwater. (Change it to something like 00FF and you should pretty much die instantly.) It only seems to work if I change it before selecting a file. I can even change it there, select a file, and change it again, and the second change doesn't apply. So it seems like there's some caching or recompiling going on, and the changed instruction needs to be marked as dirty and re-read.
(Pure Interpreter just crashes, BTW.)
Also, a simple right-click menu (copy, set breakpoints) is implemented, but I have no idea how to put raw binary data on the clipboard, only text. And I'm still seeing that "interface freezes up" issue. Seems like it happens the second time a breakpoint hits.
ebenblues
May 9th, 2008, 19:36
And I'm still seeing that "interface freezes up" issue. Seems like it happens the second time a breakpoint hits.
I haven't looked at your code at all, but generally interface freeze-up's can be gtk threading issues. Have you tried running mupen64plus using gdb and doing a "bt" when the gui hangs to see where it's stuck? If it's stuck trying to get some lock, then that would be your issue. The way I setup gui multi-threading support for mupen64plus, you have to take the gdk lock before you make any gtk calls from threads outside of the main gui thread only. If you're on the main gui thread, you will already have the lock and attempting to take it again will result in a deadlock which can look like a hang.
Also, with this design I found that making calls like this from the main gui thread
while(g_main_context_iteration(NULL, FALSE));
to tell gtk to process any queued events also causes a deadlock. You may want to check if you put something like that in any of your debugger gtk code. I created a macro called GUI_PROCESS_QUEUED_EVENTS in main/gui_gtk/main_gtk.h that does this, but prevents the deadlock.
Hope that helps.
Tillin9
May 9th, 2008, 19:39
I concur. I ran into this issue trying to update the Gtk statusbar from the core. If you need to manipulate Gtk widgets from other threads you need to use a lock like in the info_message() function ebenblues added.
DarkJezter
May 10th, 2008, 00:15
The only functions that should be getting called to update the debugger is update_debugger_frontend, which last I worked on it had the locks added to it which resolved the issues I was seeing. I'll be taking a closer look at this in a bit to see if I can pin down what's going on here.
P.S. The memory editing system includes a call:
get_memory_flags(addr)
This function detmines in advance what operations can be performed on memory. I was thinking it may be nice if we use for example, a color-coded scheme for determining what kind of memory we're looking at, (virtual vs physical, readable/writable vs read-only or unreadable)
Slougi
May 10th, 2008, 00:43
Keep in mind that many platforms completely prohibit GUI calls except from one thread.
DarkJezter
May 10th, 2008, 02:33
Actually that's a good point
Our update debugger routine should probably be changed so that it pushes a message into the queue of the GUI thread, and that thread can then go ahead and update the frontend. One slight issue with that though, right now the debugger code assumes the emulator is NOT running when it accesses the parts of the emulator to show it's state.
To keep this assumption, we would have to make sure that the emulator blocks at pushing the message into the GUI's queue, until the debugger has successfully updated the display.
HyperHacker
May 10th, 2008, 06:40
I verified the problem occurs every time a second breakpoint hits. The interface keeps working, but stops redrawing. However if I don't call switch_button_to_run() when a breakpoint hits, it seems to work fine. As a temporary solution I'm just going to dummy out that function and change the button label to "> \ ||". :P
Also it looks like the displayed register values aren't always correct. For example set a breakpoint for writes to 0x8033B21E in Mario 64 and get hit by a Goomba. The instruction shown at 0x80254208 is SH reg25, 0x00AE(reg14), and it should be storing Mario's health back to the aforementioned address. The hex at 0x80254208 is 0xA5D900AE, and another disassembler confirms this is the correct instruction, but I see Mario's health in reg27, and the address to store it at (minus 0xAE) in regs 4, 11, 15, and 26.
Comparing to Nemu, I see SH $T9, 0x00AE($T6); T6 (reg14) = 0x8033B170 (address), T9 (reg25) = 0x840 (new health value). (Why don't we have real register names? <_<) So looks like the order of the registers is mixed up.
[edit] Wait, no, durr. The labels just aren't lining up with the rows. The reg24 box lines up with the reg26 label.
MIO0
May 10th, 2008, 11:13
I recently tried to make the labels line up by turning the GtkCList into a bunch of labels or entry boxes and then packing them into a GtkTable with the labels. This ended up making the window bigger, and attempts to fiddle with the widget sizes ended up looking ugly. I guess the easiest way to make the labels line up is to put them into their own column in the CList. (or two, one for the numeric and one for the symbolic names)
EDIT: I tried that idea. It is working rather well.
DarkJezter
May 11th, 2008, 01:45
Mio0, please feel free to either check in the changes you've made or if you prefer, post a patch and I can apply them for you :) I'm ecstatic that someone's been playing with lining up the labels... I wasn't looking forward to playing with that myself.
HyperHacker
May 11th, 2008, 03:37
Long as you're fiddling with it, they really should be in a fixed-width font.
MIO0
May 11th, 2008, 06:16
I'm already doing that as I go along. Not only am I using a monospace font, I'm also trying different font sizes. So far I have the GP, cop0, cop1, AI, and PI registers' appearance changed.
Does anyone think that it would a good idea to move the cop0 viewer into the notebook? I'm trying to find ways to make the register window smaller, and for what I think most users are going to use the debugger for, I don't think that the cop0 registers are very important.
HyperHacker
May 11th, 2008, 06:46
As I mentioned before, having those 3 pieces of info each on their own tab would be great. Being able to split the tabs off into their own window would be even cooler, but first things first. :P
Speaking of which, actual register names and float/double views (at least for the FPU) would be nice too.
MIO0
May 11th, 2008, 10:01
Summary of changes:
Register labels are now cells inside of the CList widgets used to display register values. This guarantees that the labels will line up.
Widgets used to display lists of register values now use a 9pt monospace font.
The highlight color is now a lighter shade of blue for better contrast.
GPRs are now also labeled with their symbolic names.
COP0 and hardware registers are displayed as 32 bit values like they really are.
The big long update sequences for the hardware register displays are now rolled up into loops.
Some code reformatting in edited areas.
This patch was made with a week old checkout, but applies correctly to the current debugger branch.
patch: (had to compress it to fit -- use uudecode | gunzip -c)
begin-base64 644 /dev/stdout
H4sIAHilJkgCA9Vda3PbOJb9vP4VaG/NRLYkS3zo4fZmq5nYcTTtJC5SYfdW
yquiJUriRBY1JGXH05X/vnjwTZACQdLJuqsjmwAuQBy87uG51GS7ML/9Ch4M
a9tb7a3ZyvvaW5j3+9XKdHqOubqdnM2PXlf/Oep2u8W1/EfLMR8t17K3QBoK
J0ftdvtQgSfb+WptV2Bu755Pjn77DXTlcUc+B234 IQ3Ab78dAfRzfDuZvdEu
Z5efPoiz22ttpl5dH3dS19Ub//oR+H5xBI7armd41hzst6612poLYG09cAqr
3nmOezv5cgdeg7+O2sj+33fWDCZYrmc6Z/D3hWM8zIzFwkFXO/Q8c8PxDuVx
FrONuS3I8OQUZXBMYzGDF9CN7N0CM/fuYrawH4TZxvBYsu2eFkzZVkyVOhuG
bCJb20S2tolsbRODth218ZCA//V6d6+7if++gMnW8ixjY7lwvMDBay/BrelY
u7XpGBuYCG0ujbkJLi13tzGeQczAF2Ty0bbQ4LLw 7d1OWidHcFx1UbOuva9/
WIuVCcfdvf3tvdAhl4MfdFFPX8QJG+NeRba+CNLd BZkDaPRaFyTvfG044PTU
++b5F+Bvrx+Mzcaet4Br/du0ly2U5/QEnEQ5vvTvwkxD+TSWD2cDR11S09LB
dcPpASftbAnngjnbmk+tY91amHbUIcekECqC784v sIZ/4PzgnXKjXXWAGLQB
pc7trQfXBNNBc6cFrqe/z95++jhVJh+v1JZf80nHN0gv6Jre7N52FvDXJ2vh
rdNWcFloY4CKEwO9HlzBwFs4oQKEb4x7c+PiFeQU Lxvgrb3ZP2xBsN71wjvT
gzt7TN9ZP95AlLgz5l/RdHU80qg3n/4Mm0PADorGLRAbIeb9O7++DWojqfBh
az7YW2tOkhmr1VG1MbNhrVP1M/xXCMwsbacFrNfCBbD+S5Dgv+32CUn5Kxqb
oR2ruHlW1LzSTbTSTQw7+HswOLNIqv68d4FubPbR NH2dQnK+iQ/rOZzuHm68
UAZE30jQvlQrI8NojLrmxpyjVs4e7IXpj9KbiTZt +VagPXRNu7q5ejudfPo4
0yYfr2+u6NbmeHgmRnzKVh9CKoVtcT1nvntu+RO/A44/w4PCEk6gxXEK934h
7lErjN3O3C7odcNaIqjaye7GS+P6CS6OxFBLkDqx EQPLtUuuDkG98ZJ53V6i
10F8iL0ztp79EBtdqV0icTjq+SsnrUPRqUbojzqC NAbtc6kj9vG5Bu8a+93C
8Mz4vgHC9R0BJ8p3/j36yz9ID42lY5r/pg4uEC0t1rIF0CFsie8JVTcjC8kv
r0HR0QdQhgPNTLGV2Grg7hx4I0s8KuGQ/NuZMNz8CU9yxY24oI1GBLVnfvNy
50Ifj8qC0vdwnq8ce583pqGBv8NZZztoIFnLcHwD uHGY9ClSzizcTrdefIUr
AEuggZU4gzKBJVDASlgpD1aqERxgCTWAJTQDlsAF lkgDK3IGmJASKUhFJsrD
FK+eAyOxBozEZjASuTCSMhiBhEPGBJKUBilpoyRK 6QZwwCTVAJPUDEwSF0wy
hqm1h50niSeg2D1mwkxOY5a1UxI3WkM4sJNrwE5u BjuZC7tBPnYU0oIJvAFl
VUwbKr82ZpvCAd+gBvgGzcA3yIOvTT1MZk2c+CRE EdpDNrR9focJ7WER2r6h
CmiHTeFAe1gD2sNm0B5m0G77zglopZyTgAAlqIJW FgIrgWorxp9adyeJ8nQI
MVuQU/wiWTiAL0BvjMErLEJBi9YjFj7IER+1oHysY3OspO AKbYWQ1WE9gRqZ
p8FH0fwbMc6/FftWOSqcfyu36vxb8W+Woxrm36iZ+Tfi2izHbPD5 hDYTfOMi
+HxDFeALm8IB37gG+MbNwDfmgu+cBT6x1FnnPB8+ sfJZR6x01jmvAb7zZuA7
56NX+mz4lTm9CP0iAKseX8RKxxehDoJMaIghEzgp MoERwxI7oCAUYlhxCxQr
bYFCLbxZU8QZH3MmiGwYltkGBbEIw6r7oFhpHxTq 4NWEhog14RCzFpnz1sZT
3kOINiUr3cX8fjQ5oGZRX0TNopZVs6h5aha5I49B G36Iw0jNok5m6tU79Up7
zy5YUZOCFSdOP1n4SVeHnjS3t0trlZe4dxwI7Gxj G4ucLORxWk6iYy4d0137
8oouq7xCvVSVD8XKClI2I69Qa5RXqIhO+jHqCpWi rkh1SkPqCvVF1RVqnrri
Byos1GKFhcqpsFBZFRaDkHpJaSvUYm2FWkFboTal rUiIK9Q6xBVqLeIKtUZx
hVpZXDHg1VaoBdoKlaqtGHRiAyZfWpG3MgS1cigr 1CaUFRRhxSChqxA7A7jB
jvs5sgqVTVaBf2djw1VyVGHUYaiHdBhqoMOg7OiH T7fBWkYpzHCgpVZZ7hCr
1iC2UJsRW6h0sUWGCx8wUeFqLhWuMlHhah4Vrpan wlU+KlythQpXG6XCVVYq
vHBGCbQZRQ7CTHNKoMwpUrz8rAqq5ZhXQg3zSmhm XnF492qgiynwQZjQEWno
xIxwYJRoAgdSYg1Iic0gJXIhJdGQIvs/E0YSBSNSvDw6QbUcuEg14CI1g4vE
hUtKDkN3x5kQkikI+eXLQxRWzIGRXANGcjMYydX4 LzXGf7GwYCorC6a/CAum
l2XB9DwW7Lwz6IM2/JBGEQv2yZ0+Tp93Jhv/pSf5r8fY+HsMlFcdeqLtWCtr
m5OIXb6ctMcZbIGTk+jvGTmp93vH9XLNus/beU7iuihxYxq7/HLIqc6vsjC1
qLnfZu7c2Jg5qc/JVAdJt7/0v0n9fk++y+cFaaxgKrwolxWk8YJ6jbwgHGrC
jyIG9R8WdqW/KDGo/4TEoF5MDOqcxKDOHHo1KAy90ovpQb0CPai/CD2o10EP
6rXQg3qN9KBePfaKmx/UM/xgN8YQ6vToqxhFqJeMvtInEQ48HGFRv5NjUn08
YdirgPQq4QuHHWHYJ3FYAj6LZCjDxFbS600/XX76FUxtcG8Cx3xyLM8zt2d1
xWnp1EekdOZRjzOPFPdAD6hE6uHosFMQrH/U4gw+QU615VwCvQZCUW+GUNQZ
CUWBjVHUcxlFnYlR1PMYRb08o6jzMYp6LYyi3iij qFcX1+oBo0h1LJgmlkCZ
WKR4+YkVVMsxsYQaJpbQzMTiYBT1gFGk+XRMsIgU WHDp8qj4lXKAItYAitgM
KCIXKBINFOJMM6EiUVAhxcvDElTLgYtUAy5SM7hI XLikyEM6ocGEkExByC9f
HqKwYg6M5BowkpvBSObCaFCIEeZpmBAaUBDCpcvj 41fKgc6gBnQGzaCTGxLX
LYxz0zNxblRyjwmiIXWZQ8V5ljlSLQdIwxpAGjYD 0pBrCo0KAVqzAzSiALTm
BGjND9CoBoBGzQA04gJoXAgQIrmZ4BlT4EGFy4ND quSAZlwDNONmoBlzQXN+
aO4gao4JnXP65EHluWYPqZgDo/MaMDpvBqNzLozSsUr0Zz1sbmmfvgfxofRY
ASWhDs5HaIj04QlI0rMBSfTHbmxACVSgOE90jxXO dEItHEJTJAIfi5COOqI/
AWUDikYp+AbKA+UX5MKpDlpBaIhXEPiIBUEqxOm5 DE40kuGZF6fnCjjVQTMI
DfEMAh/RIBCmISkGYIOFyJISBVnkSKmaeHCog0oQGuIShIp KJJ05Ek9n0iAF
vzQuQ4oqYlcixctkxUiS3BGGoA0/wgeAkV4EgNMnaxs+ccSqpFtju7Lf2Vvv
0nTnjrXDzx5Pg0pmS5gyW8Ak2L1xUUqUwYE50MPb 6KlibIScOeYCjvj+tz78
ucgmrxzT3OIMCs7Qzis/pidH5S9xBpDOcI8e3KP0d/AnjNSIjbWwBj9DOjmq
ISdDpgbcyGwHoiBd1NfRFb+3URc+wLMlnPur1vEH e2u7OyQQOj9OqutI1avF
Vziy0Tu53JmJRDOtk1DLQ8RCBNzwcrwA9AQfTZRy SIJ3fau+hAYPV1NKhOeX
oA78MR73RIPX6wHSFY55BjRzvzFdgP7Ho8Ka40fs LnAhFMBYwgtrEyYaYPPK
nDsGegDuq/VcuCAP5eRivto5XyTxDj+s7p36GYl6KlAewGZ+uY M3/1co+6Ol
B5E7x2ofve8dHCtT8qn7f+sCkg/iLEqQRfA/Rf9TCrNM/SxTP8vUzzJFWfx4
3ZEA2rLUkcVIqajJJJs28D+H/ucosjz2LZ2Tz9/9mn6P2nd965cKPv0iqgKz
dL9fnPait5T7XRITss03sEfgaO/1smnmAvWW3+HUd5wHorsPlmt2je4/7b0D
FIyqsTJ9hYVjgpxiSbUdrCu2lDEp7gpVdzgRSe/wDWaSS8rwxFNeIV6YTSjM
1o4LP9Bqst0/fBnexRNQ02a7L9JdTN0Hb25m78wtXkuFWETcW/VKQUIYMFXe
3Fwpn4F6dQ13Y/VKA6oMzzOBOBAayEoD4cWKckBmVR8oISeEzUqICX +Ano90
V4CjFA6LcKSexgPzer1b07M8sLsHi1fGBsmPH9Aj KRM9O9y8MsKpAnclcAxB
d48BHOxovXyE537oTIO1+c048+VfsVMq0X/BEn9bHMfukrSQqjckhRLdTVIR
VP/cu561fCZdcqO8ubpphaZ8adU/PmvTybv/mamT6/fTsnpFYindtZEkFO4a
81em64E53AoMALeKpemYW7gZw31zDjsEbipwd4X9 hJLv7f0cXur6P/+b1TxK
Yo70Lb8Prfjx3G+ydfe6uBfL9KRF7UkOZSUxRhun bdKXnnG/MaOJjf/EzZdE
/IwXlwjXnE2UM1JQSjQFEuXtflRBo7H3bLiEoamR8 API/LWIxjI4JsXeFvmE
ZxA5QD7j4xop1KGc6koIDEm1pPpwUYMjbnv/KyounIlnQh+4pvngAs8Gazjk
4IkEVgieLG+d6piTsPjr/wbH+XrOY2C5cND+a2/Bc+5ZIFeldXVJsSruj4pS
VdIjdQhViSVmmSqg6VRzJ2uxTpVUHROIke0Rr33R HppIw2zn8YfUz3E01Fup
JsWHekzq5ttGVEGwdITZSE2YrosfOq27C9qs8W+M el+zXTRHqoCCTss9UhpF
wBgbNGG2cHCQUp/e/AMOgbDY8f3e8+Co2cEJDD2dR7hXoYMkshD/wYNncv1R
uZm9+/zxbQuWgE2bf43PNDJRUvJatNUFHoILyMnedM+yQt twWMSFtuKwI0qg
fY70tuFJOhDHZrWzTHpZ1NpwYEbDM2d0FgyVsHNg Z934O3rqPuH+/mh56AwA
tzIHHgAcAx4BrPRpAN3Gv/ZmwmIlt6obKUZbKY/KuvvlNbSN9qcoW0JjWlAi
ftcg661hJSnJepHMmBaOCsMNodyI3dSJ/RDbFu4sEdVWRnMaFg8Vp4BJE0pv
QznBKaf1gK8DkeA07DFUTRzKbn3VdbMR84d4wmB6 MQQgKi8SgKiUDUBU8l/D
NRiQ13CNI7demcwulbeqMr0Kvk8OX3szmUbXWEIT lWRoohF7NGDEvsukA+gZ
NmYYm5hKQcclx97kpCZjHtO1GnA58cyc1HvLQ6lh eF6X7bVdb+1dPyILCsPz
2Awq+4Vlc70HTAmCNGqJ+FMm2IPvcvAN9YT9KZSw v1TnNBT2p7xo2J/yE4b9
KcVhfwpn2J+SF/aXdYKHhXF/SnHcn1Ih7k95kbg/pY64P6WWuD+lxrg/pXLc
35A37E8peC2YQg36G3ZiA6ZczJ8yiTDgiflTCt8L lhhjGvZ64NqAvZ7Mt6yl
v3ItCM6bADj6togSMLeIQgF713TQn4SgQAeBPgif +4BX9rbrrc3ucvP86gw/
CDnscsH7QLtfrtuFfR5B6gzPQXs8zHkZmVL7y8iU Ui8jUw69jEwJIgjzzjCH
n+EHq2meBYaH+fmVl3usr9QQSqg0E0qoMIYSDpki CZXcSEKFKZJQyYskVMpH
Eip8kYRKIpIQ8IYSKvWGEoJi8zyxhEoQS5j1Apgm l0CZXLBs+WmFK+SYUEIN
E0poZkJxiP+UIISQ7noxQSJSIPHLl4clrJgDGrEG aMRmoBG5oJFo0LCGsyt+
ICG1eHlg+MLZlRoCCZVmAgkVzkBCJRNISOcemBCS aWcFUp7jpBBUzIGRXANG
cjMYyVwYDQox8hkgJowGFIz88uUxCivmwGhQA0aD ZjAaHMCISaWpMKs0lXIq
zfVLqTTXHCrNNY2xHQgdGbThv0NfowkHhelsD0g1 /UylFJv/CR1seKbu9VAX
X169+Xx9faXO3h+kwJE7+BIkOKmnFA0eFKF066Az 6sN+HXTE8/jbbwJaFZWs
k1hF9pAwjVZMzCn2YWsGxfgo2cYlYN2gd/weSwi7uikhGJaB5YrGQOTr04z5
DDEm3jMEMaI1mmCFfaLjxUhheON//vlzMMJsyq1gYPcPqY5YFEeRsSLVETun
6xvLe+Fcr9eICCuouDYhVmSwJjFWZLBxwjs2XatQ 3shMlvRuF1fTiB4sYj8j
TVibRRMW3kUlXZhfd3TToXK/DPfvW6mJ/fetVeL/qYoVf77QpVEpYVRcEAUY
FFFRw6hyqKQYyp8tMeXCwecSfqfElBnfIxlfek59 8OtxaSt/avUXa1v9xeLn
gcHhI7u0J7qlxBNBkSw/kWG2Z4K5a3LMllXcyJKPBVMNZV4nf8hDG/LAZdBH
IS1C/7wTyTpyPOK53yO/YJlT+OdJVChfIBUvkCieEj/RXGDsA7dIkMxJouaS
yqnEGpyjnso0YFxYf5u7/oimB1xEemQp5YyDFJUeqqQY3DHhhdwxobw7JuSE
ZA07wjloww8p8HMbiN1pZyOGiMP37lpFmxx0+Nr+ ZgGzvL2B9lC8EeWhZOTU
iHeHImHK72bRfhYccjKnGzF+aqCcONAw+2NyeX2F xtnJwWNHeqsnRx+0GsHi
eJYlFPgsZYRUmcQxjF+snNl6UV0xSfX3ghaSA4x/Q1EOe+dZD/iBdzrXSeEN
RxkFJnPCSTJK0zG9PWJyElDBHN+T8bNwcMaellfh IaClOJnQzha8+NkYhlAX
gaYoJWxs6cAEKiMgoJMmpnpGncEQtEdhnDPjYRsa jjv/oGLIGPh/7f0vV07G
+0fDqaa4LWKqjrgtYik/bovT3Q/uP+3uo+pqc/WJsZrcfGKMCnL27IoOrw87
ePa8dcw52c4jTz/r48e8/Gj6JT38djw5ucnG5tJh9x+Wqqx3IzO5Dn+XWPoh
wUOk6oTWDSQQvLT395wIigcgFCtjKNYBolgfiuIP hFEswrHqy/5pwT0xD87F
05z4b0JHlOTBcDS+QNI/zX4wAUx17HvshT4ifi/xLtAS1SzwWCTVQJ90NBzI
sETIhHwnYnv692RnD/nIb6CM5rvCEH1fbUhOTyQSq98ZISdZ7IwSTnK+vx vv
rVPfZxSiq8UBQktfDJYqVNrfJRO/VKQQvXwlXzW0kuunBg+OazAePDrGsOEX
dbQFYcgMW3z0RQiEV8vAFpnigE2siptYD3Bio8iJ VOgO0RRkLr8EURHUVIqq
iAplyQphIHckOCLRB5E3+10Xlos8lT4WJ7TJf71T 8G6/JdSgZ4M5cgJQ4DZh
GnDo9oY4AxbMsDbBxlx6gOxjZ4A4DdCqZ8OBdArw ioIIQpQzeG592joBS78O
F/vcKBV6U4snwzGjFp4hE72jdsRzZKXyyAvcdgIHEP nk0L9yI44kTohQ+BIK
R/JTEB5ZOmKbZSNCuia47QPh0HQWgqZf/+n5CAzg4chH7UUiH7WykY9aXuSj
RL56UeqIw9gLjSaz28m7mXJ5qc7+UIfymzAAEqdp U2X6WWMPgNSSAZBuTPHl
xgMg6ek7a4mTZ85iKN8fyvTk5GdCbcz/ykF6CKJmOvDC4RjEbAiiVuNXDsIe
JK9v+gGxhxol9jDdKw0FH2ovGnyoXdKDD39Q3KFW HHeoccYdaqxfNygXRh1q
xVGHWoWoQ62hqMO4BEOrI+ZQqyXmUKsx5lCrHHMo 88YcaoVfNahRow7lTmzA
5As+8laGoF6OoEPtBb9oUKZ9z6DUkcegPTrPCQXU ag8F1EqFAmqHQgG1IBQw
bzcHJwWq8WBtyyvMoBvPK1pSOK7VEASoNRMEqDEG AcpMQYBabhCgxhQEqOUF
AWrlgwA1viBArZavE9Qa/TpBjTUEsHBqCbSplTwIHw7N0PxowEIz5WdasjzP
dBNqmG5CM9ONI0RQC0IEC10SJrjEIriwmQpw4fI8 cIk1wCU2A1dR2GARYBIN
MOQeMsEkUWBChcuDQ6rkwESqAROpGUykaq+i12JB TiyhThpTqFPE8K1fjFpd
81CredFOg0S0U4JoiL2uPHWKjFJ4GE2sJonCnz5P UM+Ttw9fqdrs/dH/AeK+
AHNbugAA
====
DarkJezter
May 11th, 2008, 10:24
Right now, my biggest priority is to get the treeview in the disassembler all fixed up. Once the scrolling issues are resolved and we can jump to specific addresses, I'm planning a temporary merge back into the trunk. Lets get what we have working, and as rock solid as possible for a public release. It won't be pretty, but it will be functional.
After the remerge, we'll be looking at a complete revamp of the debugger GUI. I want to see a single main window, with dockable subwindows that can be detached, or disabled. Having 5 seperate windows to juggle with the main window, the n64 display and possibly the console is simply ridiculous. People who want it to work that way can detach any of the windows they need, should they be so inclined. (multiple monitors perhaps) But so far, it seems this approach should be very flexible, and fully handled by GTK.
Once the GUI is rewritten, we will do what should be our final remerge to the trunk. If we keep this up, we may just have the finished debugger in before the next major release.
[EDIT] Looked at your code Mio0, and had no problems including it in the branch. Great work, although I may play with the font sizes for the registers a bit. I'd like to keep everything relatively consistent if possible.
HyperHacker
May 13th, 2008, 00:28
I hope not too much of this code will have to be rewritten for the GUI update. :P It's a bit rough around the edges, disassembler could use some work, but for the most part I think the only major issue right now is having so many windows, with no way to reopen them if closed, and I'm guessing no real defined behaviour if some are closed.
Anyway, here's my latest work on the memory editor, based on today's revision with MIO0's patch.
-Added right-click menu to memory editor.
-Fixed incorrect display of breakpoint ranges ('$c').
-Workaround for GUI deadlock when breakpoints hit.
-Disassembly columns are resizable.
-General code improvements in memory editor (replacing magic numbers, etc).
Index: debugger/breakpoints.c
======================================== ===========================
--- debugger/breakpoints.c (revision 426)
+++ debugger/breakpoints.c (working copy)
@@ -96,8 +96,8 @@
{
if((address >= g_Breakpoints[i].address) && (address <= g_Breakpoints[i].endaddr) && (!flags || ((g_Breakpoints[i].flags & flags) == flags)))
{
- printf("Bpt %d (0x%08X - 0x%08X) matches 0x%08X\n", i, g_Breakpoints[i].address,
- g_Breakpoints[i].endaddr, address);
+ //printf("Bpt %d (0x%08X - 0x%08X) matches 0x%08X\n", i, g_Breakpoints[i].address,
+ // g_Breakpoints[i].endaddr, address);
return i;
}
else
Index: main/gui_gtk/debugger/desasm.c
======================================== ===========================
--- main/gui_gtk/debugger/desasm.c (revision 426)
+++ main/gui_gtk/debugger/desasm.c (working copy)
@@ -115,11 +115,15 @@
renderer = gtk_cell_renderer_text_new();
col = gtk_tree_view_column_new_with_attributes ("Address", renderer, "text", 0, NULL);
gtk_tree_view_column_set_cell_data_func( col, renderer, disasm_set_color, NULL, NULL);
-
+ gtk_tree_view_column_set_resizable(col, TRUE);
gtk_tree_view_append_column( GTK_TREE_VIEW( clDesasm ), col);
+
col = gtk_tree_view_column_new_with_attributes ("Opcode", renderer, "text", 1, NULL);
+ gtk_tree_view_column_set_resizable(col, TRUE);
gtk_tree_view_append_column( GTK_TREE_VIEW( clDesasm ), col);
+
col = gtk_tree_view_column_new_with_attributes ("Args", renderer, "text", 2, NULL);
+ gtk_tree_view_column_set_resizable(col, TRUE);
gtk_tree_view_append_column( GTK_TREE_VIEW( clDesasm ), col);
adj = gtk_adjustment_new(0, -500, 500, 1, max_row, max_row);
@@ -137,7 +141,8 @@
boxV1 = gtk_vbox_new( FALSE, 2 );
gtk_box_pack_end( GTK_BOX(boxH1), boxV1, FALSE, FALSE, 0 );
- buRun = gtk_button_new_with_label( "Run" );
+ //buRun = gtk_button_new_with_label( "Run" );
+ buRun = gtk_button_new_with_label( "> \\ ||" );
gtk_box_pack_start( GTK_BOX(boxV1), buRun, FALSE, FALSE, 5 );
buStep = gtk_button_new_with_label( "Next" );
gtk_box_pack_start( GTK_BOX(boxV1), buStep, FALSE, FALSE, 0 );
@@ -198,7 +203,10 @@
void switch_button_to_run()
{ //Is called from debugger.c, when a breakpoint is reached.
- gtk_label_set_text( GTK_LABEL (GTK_BIN (buRun)->child), "Run");
+ //gtk_label_set_text( GTK_LABEL (GTK_BIN (buRun)->child), "Run");
+ //todo: this causes a deadlock or something the second time a
+ //breakpoint hits, breaking the interface. The other lines changing
+ //this label have been commented with the note "avoid deadlock".
}
@@ -208,10 +216,10 @@
{
if(run == 2) {
run = 0;
- gtk_label_set_text( GTK_LABEL (GTK_BIN (buRun)->child), "Run");
+ //gtk_label_set_text( GTK_LABEL (GTK_BIN (buRun)->child), "Run"); //avoid deadlock
} else {
run = 2;
- gtk_label_set_text( GTK_LABEL (GTK_BIN (buRun)->child),"Pause");
+ //gtk_label_set_text( GTK_LABEL (GTK_BIN (buRun)->child),"Pause"); //avoid deadlock
pthread_cond_signal(&debugger_done_cond);
}
}
@@ -220,7 +228,7 @@
static void on_step()
{
if(run == 2) {
- gtk_label_set_text( GTK_LABEL (GTK_BIN (buRun)->child), "Run");
+ //gtk_label_set_text( GTK_LABEL (GTK_BIN (buRun)->child), "Run"); //avoid deadlock
} else {
pthread_cond_signal(&debugger_done_cond);
}
Index: main/gui_gtk/debugger/memedit.c
======================================== ===========================
--- main/gui_gtk/debugger/memedit.c (revision 426)
+++ main/gui_gtk/debugger/memedit.c (working copy)
@@ -28,24 +28,39 @@
#include "memedit.h"
+#include <math.h>
#include <gdk/gdkkeysyms.h>
GtkTextBuffer *textbuf;
GtkTextTag *textfont;
-GtkTextIter textstart, textend;
+//note: just changing these constants is not (yet) enough to properly change the display.
+const uint32 num_menu_items = 6;
+const int bytesperline = 16;
+const int hexstartpos = 9;
+//apparently the compiler isn't smart enough to figure out that these equations are constant. -_-
+const int linelen = 74; //hexstartpos + (bytesperline * 3) + bytesperline + 1; //address, separator, hex, text, line break (hex/text separator included in hex)
+const int textstartpos = 57; //hexstartpos + (bytesperline * 3); //address, separator, hex
uint32 memedit_address, memedit_numlines;
+GtkWidget *menu;
+GtkWidget* menuitem[6]; //DO NOT FORGET TO UPDATE THIS WHEN YOU ADD ITEMS
+GtkClipboard* clipboard;
+
static gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data);
+static gboolean on_mouse_down(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
+static void on_menu_select(GtkMenuItem *menuitem, gpointer user_data);
static void on_close();
+static int GetSelectionRange(uint32* StartAddr, int AllowEmpty);
void update_memory_editor()
{
int i, j, k;
- char line[75];
+ char line[linelen + 1];
char* buf;
uint32 addr;
- uint8 byte[16];
+ uint8 byte[bytesperline];
+ GtkTextIter textstart, textend;
buf = (char*)malloc(memedit_numlines * sizeof(line) * sizeof(char));
if(!buf)
@@ -62,7 +77,7 @@
buf[0] = 0;
for(i=0; i<memedit_numlines; i++)
{
- for(j=0; j<16; j++)
+ for(j=0; j<bytesperline; j++)
byte[j] = read_memory_8(addr + j);
sprintf(line, "%08X|%02X %02X %02X %02X|%02X %02X %02X %02X|%02X %02X %02X %02X|%02X %02X %02X %02X|",
@@ -70,7 +85,7 @@
byte[8], byte[9], byte[10], byte[11], byte[12], byte[13], byte[14], byte[15]);
strcat(buf, line);
- for(j=0; j<16; j++)
+ for(j=0; j<bytesperline; j++)
{
if((byte[j] >= 0x20) && (byte[j] <= 0x7E))
line[j] = byte[j];
@@ -79,12 +94,12 @@
if(i < (memedit_numlines - 1))
{
- line[16] = '\n';
- line[17] = 0;
+ line[bytesperline] = '\n';
+ line[bytesperline + 1] = 0;
}
- else line[16] = 0;
+ else line[bytesperline] = 0;
strcat(buf, line);
- addr += 16;
+ addr += bytesperline;
}
//Update textbox
@@ -106,7 +121,7 @@
*textbox;
memedit_opened = 1;
- memedit_address = 0x800F6A10; //0x800EB5D0;
+ memedit_address = 0x80000000;
memedit_numlines = 16;
//=== Creation of Memory Editor ===========/
@@ -120,20 +135,42 @@
textfont = gtk_text_buffer_create_tag(textbuf, "font", "font", "monospace", NULL);
update_memory_editor();
+ gtk_container_add(GTK_CONTAINER(winMemEd it), textbox);
- gtk_container_add(GTK_CONTAINER(winMemEd it), textbox);
+ clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOAR D );
+
+ //Create right-click menu
+ menu = gtk_menu_new();
+ menuitem[0] = gtk_menu_item_new_with_mnemonic("_Copy");
+ menuitem[1] = gtk_menu_item_new_with_mnemonic("Copy as _Binary");
+ menuitem[2] = gtk_menu_item_new_with_mnemonic("Copy as _ASCII");
+ menuitem[3] = gtk_menu_item_new_with_mnemonic("Add _Read Breakpoint");
+ menuitem[4] = gtk_menu_item_new_with_mnemonic("Add _Write Breakpoint");
+ menuitem[5] = gtk_menu_item_new_with_mnemonic("_Disable Breakpoints");
+
+ for(i=0; i<num_menu_items; i++)
+ {
+ gtk_menu_shell_append(GTK_MENU_SHELL(men u), menuitem[i]);
+ gtk_widget_show(menuitem[i]);
+ }
+
gtk_widget_show_all(winMemEdit);
+ gtk_widget_show(menu);
//=== Signal Connections ===========================/
gtk_signal_connect( GTK_OBJECT(winMemEdit), "destroy", on_close, NULL );
gtk_signal_connect( GTK_OBJECT(textbox), "key-press-event", on_key_press, NULL );
+ gtk_signal_connect( GTK_OBJECT(textbox), "button-press-event", on_mouse_down, NULL );
+
+ for(i=0; i<num_menu_items; i++)
+ gtk_signal_connect( GTK_OBJECT(menuitem[i]), "activate", on_menu_select, (gpointer)i);
}
static gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
{
GtkTextMark* cursor;
GtkTextIter cursor_iter;
- int cursorpos, linepos, linenum, linelen = 74;
+ int cursorpos, linepos, linenum;
uint32 key, addr;
uint8 byte;
@@ -141,47 +178,27 @@
//todo: make this not suck
switch(event->keyval)
{
- case GDK_0:
- case GDK_1:
- case GDK_2:
- case GDK_3:
- case GDK_4:
- case GDK_5:
- case GDK_6:
- case GDK_7:
- case GDK_8:
+ case GDK_0: case GDK_1: case GDK_2:
+ case GDK_3: case GDK_4: case GDK_5:
+ case GDK_6: case GDK_7: case GDK_8:
case GDK_9:
key = event->keyval - GDK_0;
break;
- case GDK_KP_0:
- case GDK_KP_1:
- case GDK_KP_2:
- case GDK_KP_3:
- case GDK_KP_4:
- case GDK_KP_5:
- case GDK_KP_6:
- case GDK_KP_7:
- case GDK_KP_8:
+ case GDK_KP_0: case GDK_KP_1: case GDK_KP_2:
+ case GDK_KP_3: case GDK_KP_4: case GDK_KP_5:
+ case GDK_KP_6: case GDK_KP_7: case GDK_KP_8:
case GDK_KP_9:
key = event->keyval - GDK_KP_0;
break;
- case GDK_A:
- case GDK_B:
- case GDK_C:
- case GDK_D:
- case GDK_E:
- case GDK_F:
+ case GDK_A: case GDK_B: case GDK_C:
+ case GDK_D: case GDK_E: case GDK_F:
key = (event->keyval - GDK_A) + 10;
break;
- case GDK_a:
- case GDK_b:
- case GDK_c:
- case GDK_d:
- case GDK_e:
- case GDK_f:
+ case GDK_a: case GDK_b: case GDK_c:
+ case GDK_d: case GDK_e: case GDK_f:
key = (event->keyval - GDK_a) + 10;
break;
@@ -204,7 +221,7 @@
cursorpos -= linelen;
if(cursorpos < 0)
{
- memedit_address -= 16;
+ memedit_address -= bytesperline;
cursorpos += linelen;
}
}
@@ -213,7 +230,7 @@
cursorpos += linelen;
if(cursorpos >= (linelen * memedit_numlines))
{
- memedit_address += 16;
+ memedit_address += bytesperline;
cursorpos -= linelen;
}
}
@@ -223,7 +240,7 @@
}
else if(key == GDK_Right)
{
- if(cursorpos) cursorpos++;
+ if(cursorpos < (linelen * memedit_numlines)) cursorpos++;
}
else
{
@@ -231,7 +248,7 @@
linenum = cursorpos / linelen;
linepos = cursorpos % linelen;
- if((linepos == 8) || (linepos == 56)) //address/hex or hex/text separators
+ if((event->keyval != GDK_BackSpace) && ((linepos == (hexstartpos - 1)) || (linepos == (textstartpos - 1)))) //address/hex or hex/text separators
{
linepos++;
cursorpos++;
@@ -241,38 +258,47 @@
if(linepos >= (linelen - 1)); //end of line; do nothing
//If cursor is in address
- else if(linepos < 8)
+ else if(linepos < (hexstartpos - 1))
{
if(key < 16) //hex number entered
{
//yes, I probably _could_ have made this line uglier.
memedit_address = (memedit_address & ~(0xF << (4 * (7 - linepos)))) | (key << (4 * (7 - linepos)));
cursorpos++;
- if((cursorpos % linelen) == 8) cursorpos++; //skip address/hex separator
+ if((cursorpos % linelen) == (hexstartpos - 1)) cursorpos++; //skip address/hex separator
}
+ else if(linepos && (event->keyval == GDK_BackSpace)) //back one character
+ {
+ cursorpos--;
+ if(cursorpos < 0)
+ {
+ memedit_address -= bytesperline;
+ cursorpos++;
+ }
+ }
//todo: else, beep or something
}
//If cursor is in text
- else if(linepos >= 56)
+ else if(linepos >= (textstartpos - 1))
{
//If a non-special key, except Enter, was pressed
if((event->keyval <= 0xFF) || (event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter))
{
- linepos -= 57; //Character index
- addr = memedit_address + (linenum * 16) + linepos; //Address to edit
+ linepos -= textstartpos; //Character index
+ addr = memedit_address + (linenum * bytesperline) + linepos; //Address to edit
if(event->keyval <= 0xFF) byte = event->keyval;
else if((event->keyval == GDK_Return) || (event->keyval == GDK_KP_Enter)) byte = 0x0A; //Enter inserts line break
write_memory_8(addr, byte);
cursorpos++;
- if((cursorpos % linelen) == 73) //wrote past last character
+ if((cursorpos % linelen) == (linelen - 1)) //wrote past last character
{
- cursorpos += 58; //to first char on next line
+ cursorpos += (textstartpos + 1); //to first char on next line (+1 for line break)
if(cursorpos >= (linelen * memedit_numlines)) //past end of box
{
- memedit_address += 16;
+ memedit_address += bytesperline;
cursorpos -= linelen;
}
}
@@ -280,12 +306,12 @@
else if(event->keyval == GDK_BackSpace) //back one character
{
cursorpos--;
- if((cursorpos % linelen) < 57) //before first character
+ if((cursorpos % linelen) < textstartpos) //before first character
{
- cursorpos -= 58; //to last char on prev line
+ cursorpos -= (textstartpos + 1); //to last char on prev line
if(cursorpos < 0)
{
- memedit_address -= 16;
+ memedit_address -= bytesperline;
cursorpos += linelen;
}
}
@@ -295,14 +321,14 @@
//If cursor is in hex
else
{
- linepos -= 8;
- if(!(linepos % 3)) //between bytes
+ linepos -= (hexstartpos - 1);
+ if((event->keyval != GDK_BackSpace) && !(linepos % 3)) //between bytes
{
cursorpos++;
linepos++;
}
- addr = memedit_address + (linenum * 16) + (linepos / 3);
+ addr = memedit_address + (linenum * bytesperline) + (linepos / 3);
//printf("byte %u (%08X) nybble %u, ", linepos / 3, addr, (linepos % 3) - 1);
if(key < 16) //hex number entered
@@ -319,9 +345,9 @@
write_memory_8(addr, byte);
cursorpos++;
- if((cursorpos % linelen) == 57) //wrote past last byte
+ if((cursorpos % linelen) == textstartpos) //wrote past last byte
{
- cursorpos += 26; //to first byte on next line
+ cursorpos += bytesperline + 10; //to first byte on next line - skip text (bytesperline chars), line break, address (8 chars), separator
if(cursorpos >= (linelen * memedit_numlines)) //past end of box
{
memedit_address += 16;
@@ -329,6 +355,25 @@
}
}
} //if hex number entered
+ else if(event->keyval == GDK_BackSpace) //back one character
+ {
+ if(linepos < 2) cursorpos -= linepos + 1; //end of address (before/after separator)
+ else
+ {
+ cursorpos--;
+ if(!(((cursorpos % linelen) - (hexstartpos - 1)) % 3)) cursorpos--; //between bytes
+
+ if((cursorpos % linelen) < hexstartpos) //before first character
+ {
+ cursorpos -= (hexstartpos + bytesperline + 1); //to last char on prev line
+ if(cursorpos < 0)
+ {
+ memedit_address -= bytesperline;
+ cursorpos += linelen;
+ }
+ }
+ }
+ } //if backspace
} //if in hex
}
@@ -346,7 +391,179 @@
}
+static gboolean on_mouse_down(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
+{
+ if(event->button != 3) return FALSE;
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, 0, event->button, event->time);
+ return TRUE;
+}
+
+
+static void on_menu_select(GtkMenuItem *menuitem, gpointer user_data)
+{
+ gchar* text;
+ GtkTextIter start, end;
+ uint32 startaddr, endaddr, numbytes;
+ uint8 byte;
+ int i;
+ breakpoint newbp;
+
+ switch((int)user_data)
+ {
+ case 0: //Copy
+ if(!gtk_text_buffer_get_selection_bounds (textbuf, &start, &end)) break; //nothing selected if false
+ text = gtk_text_iter_get_text(&start, &end);
+ gtk_clipboard_set_text(clipboard, text, -1);
+ break;
+
+ case 1: //Copy as Binary
+ //TODO - not sure how to put raw binary data on clipboard <_<
+ gtk_clipboard_set_text(clipboard, "not implemented yet", i);
+ printf("Copy as Binary not implemented yet\n");
+ break;
+
+ case 2: //Copy as ASCII
+ numbytes = GetSelectionRange(&startaddr, 0);
+ if(!numbytes) break;
+ text = (gchar*)malloc((numbytes + 1) * sizeof(gchar));
+ if(!text) break;
+
+ for(i=0; i<numbytes; i++)
+ {
+ byte = read_memory_8(startaddr);
+ if((byte >= 0x20) && (byte <= 0x7E)) text[i] = byte;
+ else text[i] = '.';
+ startaddr++;
+ }
+ text[i] = 0;
+
+ gtk_clipboard_set_text(clipboard, text, i);
+ free(text);
+ break;
+
+ case 3: //Break on read
+ case 4: //Break on write
+ numbytes = GetSelectionRange(&startaddr, 1);
+ if(!numbytes) break;
+ endaddr = startaddr + (numbytes - 1);
+
+ //see if there's already a breakpoint for this range,
+ //and if so, just add the read/write flag.
+ for(i=0; i<g_NumBreakpoints; i++)
+ {
+ if((g_Breakpoints[i].address == startaddr)
+ && (g_Breakpoints[i].endaddr == endaddr))
+ {
+ g_Breakpoints[i].flags |= ((int)user_data == 3) ? BPT_FLAG_READ : BPT_FLAG_WRITE;
+ printf("Added %s flag to breakpoint %d.\n", ((int)user_data == 3) ? "read" : "write", i);
+ update_breakpoints();
+ numbytes = 0;
+ break;
+ }
+ }
+
+ if(!numbytes) break; //already found a breakpoint
+
+ newbp.address = startaddr;
+ newbp.endaddr = endaddr;
+ newbp.flags = BPT_FLAG_ENABLED | (((int)user_data == 3) ? BPT_FLAG_READ : BPT_FLAG_WRITE);
+ add_breakpoint_struct(&newbp);
+ printf("Added breakpoint.\n");
+ update_breakpoints();
+ break;
+
+ case 5: //Clear breakpoints
+ numbytes = GetSelectionRange(&startaddr, 1);
+ if(!numbytes)
+ {
+ //todo: prompt to clear all memory breakpoints
+ break;
+ }
+
+ for(i=0; i<g_NumBreakpoints; i++)
+ {
+ //if breakpoint overlaps this range
+ if(((g_Breakpoints[i].address <= startaddr) && (g_Breakpoints[i].endaddr >= startaddr))
+ || ((g_Breakpoints[i].address <= endaddr) && (g_Breakpoints[i].endaddr >= endaddr)))
+ {
+ g_Breakpoints[i].flags &= ~BPT_FLAG_ENABLED;
+ printf("Disabled breakpoint %d.\n", i);
+ }
+ }
+ update_breakpoints();
+ break;
+ }
+}
+
+
static void on_close()
{
memedit_opened = 0;
}
+
+
+//Determines range of bytes selected.
+//Returns # of bytes selected, sets StartAddr to address of first byte if any.
+//A byte is considered selected if the selection covers either the hex OR text view of it.
+//In the hex view, a byte is considered selected if either nybble and/or the space/pipe
+//after it is selected. I consider this a good thing, as it means if you accidentally don't
+//select an entire byte, it still gets copied.
+//When AllowEmpty is set, if there is no selection, but the cursor is in the hex or text,
+//the byte next to it is considered selected.
+static int GetSelectionRange(uint32* StartAddr, int AllowEmpty)
+{
+ GtkTextIter start, end;
+ GtkTextMark* cursor;
+ int startpos, endpos, startline, endline, startlinepos, endlinepos, numbytes;
+
+ //Determine what byte range is selected.
+ if(gtk_text_buffer_get_selection_bounds( textbuf, &start, &end))
+ {
+ startpos = gtk_text_iter_get_offset(&start);
+ endpos = gtk_text_iter_get_offset(&end);
+ }
+ else //nothing selected
+ {
+ if(!AllowEmpty) return 0;
+ cursor = gtk_text_buffer_get_insert(textbuf);
+ gtk_text_buffer_get_iter_at_mark(textbuf , &start, cursor);
+ startpos = gtk_text_iter_get_offset(&start);
+ endpos = startpos + 1;
+ }
+ //printf("selected %d - %d\n", startpos, endpos);
+
+ startline = startpos / linelen;
+ startlinepos = startpos % linelen;
+ endline = endpos / linelen;
+ endlinepos = endpos % linelen;
+
+ if((startlinepos == (textstartpos - 1)) && ((endpos - startpos) == 1)) return 0; //Only hex/text separator selected
+
+ if(startlinepos >= (linelen - 1)) //past end of line
+ {
+ startline++;
+ startlinepos = hexstartpos;
+ }
+
+ if(endlinepos <= hexstartpos) //before hex
+ {
+ endline--;
+ endlinepos = textstartpos - 1;
+ }
+
+ //It works.
+ if(startline > endline) return 0; //no actual hex/text selected
+ //else if((startline == endline) && ((endlinepos - startlinepos) < 2) && (startlinepos < (textstartpos - 1))) return 0; //no entire bytes
+
+ if(startlinepos >= textstartpos) //selection begins in text
+ (*StartAddr) = (startline * bytesperline) + (startlinepos - textstartpos);
+ else (*StartAddr) = (startline * bytesperline) + ((startlinepos - hexstartpos) / 3);
+
+ if(endlinepos >= textstartpos) //selection ends in text
+ numbytes = ((endline * bytesperline) + (endlinepos - textstartpos)) - (*StartAddr);
+ else numbytes = ((endline * bytesperline) + (int)ceil((float)(endlinepos - hexstartpos) / 3.0)) - (*StartAddr);
+
+ (*StartAddr) += memedit_address;
+ //printf("%u bytes from %08X\n", numbytes, (*StartAddr));
+ return numbytes;
+}
Index: main/gui_gtk/debugger/breakpoints.c
======================================== ===========================
--- main/gui_gtk/debugger/breakpoints.c (revision 426)
+++ main/gui_gtk/debugger/breakpoints.c (working copy)
@@ -155,7 +155,7 @@
}
else
{
- sprintf(buf, "%c%c%c$c 0x%08X - 0x%08X",
+ sprintf(buf, "%c%c%c%c 0x%08X - 0x%08X",
(bpt->flags & BPT_FLAG_READ) ? 'R' : '-',
(bpt->flags & BPT_FLAG_WRITE) ? 'W' : '-',
(bpt->flags & BPT_FLAG_EXEC) ? 'X' : '-',
HyperHacker
May 13th, 2008, 06:05
So if this is all being merged into the trunk at some point, if I want to change other things not related to the debugger (thinking remembering your filter in the ROM selector would be nice), should I just do it in the debugger branch?
Also, when I'm adding files, should I be changing the "Copyright (C) 2002 DavFr" in the comments?
ebenblues
May 13th, 2008, 08:48
So if this is all being merged into the trunk at some point, if I want to change other things not related to the debugger (thinking remembering your filter in the ROM selector would be nice), should I just do it in the debugger branch?
No, only changes for the debugger should go in that branch. For the rom filter change, I would submit a patch against trunk.
Also, when I'm adding files, should I be changing the "Copyright (C) 2002 DavFr" in the comments?
Yeah, for new files, I change the copyright year to 2008 and put ebenblues.
okaygo
May 13th, 2008, 20:57
When I try to build or compile the branch:
make DBG=1 mupen64plus
I get this error:
debugger/memory.c:23:21: error: dis-asm.h: No such file or directory
Edit: I was missing a dependency :cat:
HyperHacker
May 14th, 2008, 06:28
Where did ui_clist_edit come from? From the header it looks like it was copied from another open-source project, but I can't find anything about it on Google, only copies of the file. I need similar for a treeview and was hoping it already existed. :P
[edit] Or GtkCellRenderer has such a thing built in. v¯v;
DarkJezter
May 15th, 2008, 11:17
It's a template I found on the GTK webpage. They lay out in example how to create a simple treemodel, which I gutted and rewrote. :-$ In fact, a lot of the comments are still incorrect. I'll be going through and cleaning that before it goes in the trunk.
okaygo
May 19th, 2008, 04:08
How goes the development?
HyperHacker
May 19th, 2008, 05:01
I've been busy the last few days, but I've almost finished a variable viewer/editor, for convenient monitoring of multiple memory locations. I've hit a bit of a snag with floats though - the format my system wants to use doesn't seem to match the N64's, and I found a magic snippet that does some pointer casting to convert from N64 format, but I have no idea how to convert back, so entering new values doesn't work.
Not a whole lot of work left to do on it, the hard part will be finding free time :P and figuring out how to do things with Gtk since I've never used it before.
HyperHacker
May 20th, 2008, 07:23
Hm, the "next" button on the disassembler is doing some odd things. Occasionally it jumps to a random instruction, usually after switching to another window, as if the game had been running briefly. Also it doesn't seem to follow JR instructions, just skips right on by them.
Anyway here's a WIP patch because I can't figure out why gtk_file_chooser_dialog_new() is hanging, seems to be a GTK bug.
-Removed unused variables from init_memedit().
-Added variable list.
-Increased breakpoint window width a bit to prevent Bs at edge looking like Es
-Added auto-refresh checkbox to memory editor.
-Replaced some printf()s with alert_message()s.
The variable list isn't finished, so there are still some quirks:
-Type column is not a dropdown list. Choose a type by entering a hex number (0-C). Add 0x80 (i.e. prefix with 8) for a pointer.
-Import loads from a hardcoded path, because it crashes when I try to display a file open dialog. Export is not implemented yet.
-Float and Double values display correctly, but attempting to edit them writes the wrong values, because I have no idea how the magic conversion works.
-No checking for invalid values or addresses.
Too long to post now: patch (http://hyperhacker.no-ip.org/b/mupen64plus/patch), varlist.c (http://hyperhacker.no-ip.org/b/mupen64plus/varlist.c), varlist.h (http://hyperhacker.no-ip.org/b/mupen64plus/varlist.h)
DarkJezter
May 22nd, 2008, 01:08
Ok, I've been AWOL for a while here, but I've had the chance to solve some of the technical challenges for the disassembler frontend. I also have some free time tomorrow that I'm going to use to get everything polished and ready. Hopefully this will leave enough time to merge the branch, and also get the opportunity for some other people to try play with it before the 1.4 release.
Richard: you have a timeline in mind for the 1.4 release?
Hyperhacker: sorry btw about the latest patches, I'll be including them in the branch as soon as I have the scroll changes merged into the branch. (read end of Thursday at the latest)
HyperHacker
May 22nd, 2008, 01:15
Well I can't do much until I figure out what's up with the file chooser dialog anyway. Any ideas?
DarkJezter
May 22nd, 2008, 01:46
Haven't the foggiest, nothing appears to be obviously wrong with it. I'll play with it after I commit my changes. Knowing this, those patches probably won't make it into the branch until we get that fixed.
If you're have development questions, you may want to consider joining the irc channel, there's a sticky thread with information on it if you didn't already know. May make collaboration such as this a bit easier ;)
HyperHacker
May 22nd, 2008, 02:26
Well it's still functional, only the import/export feature needs that, so just disabling that button (or changing the hardcoded path to one that will work on other machines) would probably be enough for now. It's a GTK issue as best I can tell (there's been similar problems before), so I filed a bug, no response yet. Unfortunately it's not giving any sort of stack dump or trace so there's not much I can do to tell what's wrong with it.
Knowing my luck, it's probably some memory corruption or something somewhere, but I've narrowed it down as best I can (disabled all other debugger windows, etc) so if that is the case, it's unlikely in any code I've written.
DarkJezter
May 24th, 2008, 08:41
Hyper:
I was playing with the patch, and I didn't have any problems with the file load dialog after I commented the static filename code. Not sure if this is a system specific bug, or not, but I'll include it in the branch and we can decide where to go from there... but so far it works great for me.
Oh, and unfortunately, I did drop the code to make the columns of the disassembler resizable... I'm using fixed_height, which requires fixed column widths to be set if I'm not mistaken... however it's supposed to be for performance enhancement... and I haven't noticed if it's made any difference or not... I may revisit this later and try it with resizable columns instead.
HyperHacker
May 24th, 2008, 11:05
Well as it is, the columns are too small for longer opcodes like BGEZALL.
The only thing I've heard about that bug is a problem on 64-bit systems, but mine's 32-bit, so I don't know what's going on there.
DarkJezter
May 28th, 2008, 09:24
The debugger is nearly ready to be merged back into the trunk. The next phases of work on the debugger will involve patching the recompiler, and will likely need to wait until after the 1.4 release.
The current most pressing issues with the debugger:
1) Does not function as expected with dynamic-recompiler and repairing will require some alterations. In the meantime, disabling single-step execution and breakpoints while using the dynamic-recompiler will suffice. While it will probably not be ready for the 1.4 release, we can expect to see full debugger support for the recompiler in the 1.5 release.
2) The new components have been included from a couple contributors, and have not yet been thoroughly tested. Anyone willing to try them please do.
3) There is still no way to edit register values, this will also likely not be ready for the 1.4 release, but is anticipated to be ready soon after with full support for the recompiler. However, now thanks to HyperHacker there is a working memory editor that is able to edit r/w ram values, as well as monitor variables.
4) The GUI is VERY temporary, and very ugly. Currently over 8 seperate windows are open when using the debugger. The debugger is going to include full support for enabling and disabling these windows individually in time for the merge, through the menus in the rom-browser window. What we're hoping to begin afterward is a completely dockable interface that will also include the rom-browser.
At this point, since there's a push for release, I'm eager to ask anyone to try it and post if you find any serious bugs that need repair for the release.
Tillin9
May 29th, 2008, 06:42
Hm... it compiles with a lot of warnings but seems to be working. Also, you should mention that dis-asm.h (in binutils-dev on Debian based distros) is now needed.
I tried the debugger, the double scrollbar on the desassembler (is that the right word?) seems confusing, but more importantly, the font is too large so I couldn't see the registers. This is odd considering that the font was fine on the other windows. I can post an image if desired.
Some documentation would also be very useful, both for using the debugger and on figuring out how to refine the interface. We do seem to need a view menu, so I'll give that some more priority. Implementing one and dockable widgets (via GtkHandleBox) for the toolbar and filter is on my todo. I can get that done fairly rapidly this week-end and help out with some of the gtk stuff.
HyperHacker
May 29th, 2008, 07:14
Yeah, documentation is needed. :P "Desassembler" should be "Disassembler", but it's written like that in the code too.
DarkJezter
May 29th, 2008, 07:29
I'm guessing the mispell there is due to the original dev being French ;)
[edit]
Oh and on the scrollbars... one on the right is linear, the one on the left is logarithmic. Try scrolling over a couple lines with each, and you'll see the difference. I think it may be a bit more obvious if we put one on each side of the window, but that's more an issue of aesthetics.
HyperHacker
June 6th, 2008, 05:51
So I've been finding some difficulty actually using this, because of what would seem to be a cache or recompiler (even using the interpreter core). Changing instructions in memory has no effect; the disassembler shows the new instruction, but the CPU still executes the old one. That makes it somewhat difficult to hack. I tried changing it as soon as it was in RAM when the game started up (long before it would have executed), but typically the game just re-copies the block, changing it back; putting a breakpoint a few instructions before doesn't work, as it's already too late; changing it here has no effect.
Also, I was considering a way to log all RSP commands executed during one frame, effectively dumping all display lists. Could be useful in picking apart Mario Kart. I suppose that's better done in a graphic plugin, in which case, what's the best way to go about implementing debug features in plugins?
DarkJezter
June 6th, 2008, 09:46
I completely agree, especially for working on plugin artifacts. The interpreter does a pre-pass to populate a series of structures for each instruction, interestingly enough they are the same structures used for dynamic recompiliation. There's this and another issue with the recompiler that I want to work on for the disassembler, but once we're able to switch between the recompiler and interpreter on the fly (which is my next goal with the debugger) I'll be investigating both updating the recompiled code as well as the interpreted code. Right now, this is on hold until after 1.4 is in it's final release, saving the mean time to play with compiling on different platforms (I'm waiting on ram to get the win32 system ready, I have osx ready and waiting)
Richard42
June 6th, 2008, 21:58
So I've been finding some difficulty actually using this, because of what would seem to be a cache or recompiler (even using the interpreter core). Changing instructions in memory has no effect; the disassembler shows the new instruction, but the CPU still executes the old one.
I merged the debugger branch into the trunk, so to continue with this testing/development you'll have to nuke your local copy of the branch and check out the trunk.
Also, I committed a change in rev 503 which should fix this problem for both the interpreter and recompiler. Please test and let me know if it works or not.
HyperHacker
June 11th, 2008, 07:15
Yeah, it seems to be working now, but "make DBG=1 install" fails.
./install.sh /usr/local
Installing Mupen64Plus to /usr/local
/usr/bin/install: cannot stat `mupen64plus': No such file or directoryIt looks for mupen64plus instead of mupen64plus_dbg.
Also if you add some breakpoints, close the window, and open it again, they aren't listed; there needs to be an "update_breakpoints();" at the end of init_breakpoints() in breakpoints.c.
As for interface improvements, two things I can think of right away:
1) Remember window positions and sizes.
2) Put the video output in place of the ROM list (keeping the toolbar and status bar), instead of a separate window.
Richard42
June 11th, 2008, 15:34
HyperHacker, I have fixed these 2 issues in SVN r532. For the install problem, I got rid of the mupen64plus_dbg executable so we always build just 'mupen64plus' even when the graphical debugger is built. I also added the updated_breakpoints() call. Please update and re-test.
HyperHacker
August 8th, 2008, 06:59
Hey, sorry I haven't been around lately. I just tried to compile the latest trunk build:
TexLoad4b.h: In function ‘DWORD Load4bIA(unsigned char*, unsigned char*, int, int, int, int, int)’:
TexLoad4b.h:3059: error: PIC register ‘ebx’ clobbered in ‘asm’
TexLoad4b.h: In function ‘DWORD Load4bI(unsigned char*, unsigned char*, int, int, int, int, int)’:
TexLoad4b.h:3718: error: PIC register ‘ebx’ clobbered in ‘asm’
TexLoad8b.h: In function ‘DWORD Load8bIA(unsigned char*, unsigned char*, int, int, int, int, int)’:
TexLoad8b.h:1090: error: PIC register ‘ebx’ clobbered in ‘asm’
TexLoad8b.h: In function ‘DWORD Load8bI(unsigned char*, unsigned char*, int, int, int, int, int)’:
TexLoad8b.h:1230: error: PIC register ‘ebx’ clobbered in ‘asm’
TexLoad32b.h: In function ‘DWORD Load32bRGBA(unsigned char*, unsigned char*, int, int, int, int, int)’:
TexLoad32b.h:469: error: PIC register ‘ebx’ clobbered in ‘asm’
TexConv.h: In function ‘void TexConv_ARGB1555_ARGB4444(unsigned char*, unsigned char*, int, int)’:
TexConv.h:125: error: PIC register ‘ebx’ clobbered in ‘asm’
TexConv.h: In function ‘void TexConv_AI88_ARGB4444(unsigned char*, unsigned char*, int, int)’:
TexConv.h:193: error: PIC register ‘ebx’ clobbered in ‘asm’
TexConv.h: In function ‘void TexConv_AI44_ARGB4444(unsigned char*, unsigned char*, int, int)’:
TexConv.h:323: error: PIC register ‘ebx’ clobbered in ‘asm’
TexConv.h: In function ‘void TexConv_A8_ARGB4444(unsigned char*, unsigned char*, int, int)’:
TexConv.h:459: error: PIC register ‘ebx’ clobbered in ‘asm’
make[1]: *** [TexCache.o] Error 1
Richard42
August 8th, 2008, 14:27
This should be fixed in r838.
HyperHacker
August 19th, 2008, 12:30
Well I've been continuing to work on the version I have (needed to to get my hacks done :P) so hopefully that gets fixed soon so I can migrate it all to the latest build.
There seems to be a dependency on specific versions of libopcodes and libfd? It won't run on my laptop because it can't find these files, but binutils and binutils-dev are installed.
I finally found what was causing the random stack smashing when interrupting in the debugger. regCop1.c, at the beginning of update_FGR():
char txt[24];The size of this buffer needs to be increased - "double"-type numbers can reach 308 digits, and using %f will print them all.
It still crashes if I try to remove all breakpoints at once. Haven't looked into that yet.
I also figured out what's up with the "random" jumping around in the disassembler. It doesn't handle branches properly. If you step over a branch it steps to the second instruction after that and then branches, and if you step over a JAL, it doesn't follow it. Similarly if you put a breakpoint on an instruction two after a branch, the breakpoint hits when the branch is executed, even if it would have skipped the actual instruction the breakpoint is on.
I added a logging function to glN64, but how would I go about communicating with the plugin from the GUI code to tell it when to start and stop logging?
Richard42
August 19th, 2008, 17:13
I also figured out what's up with the "random" jumping around in the disassembler. It doesn't handle branches properly. If you step over a branch it steps to the second instruction after that and then branches, and if you step over a JAL, it doesn't follow it. Similarly if you put a breakpoint on an instruction two after a branch, the breakpoint hits when the branch is executed, even if it would have skipped the actual instruction the breakpoint is on.
I filed an issue report in the google issue tracker about some debugger improvements. One of the improvements that I listed is fixing the strange stepping behavior around branches.
I added a logging function to glN64, but how would I go about communicating with the plugin from the GUI code to tell it when to start and stop logging?
You have to go through the DLL interface to communicate between the core and the plugins. This would mean extending the video plugin API.
HyperHacker
August 19th, 2008, 23:40
DLL interface? Does that apply to Linux too? I wonder if the plugin can't simply catch key press events for its output window.
Richard42
August 20th, 2008, 14:08
DLL interface? Does that apply to Linux too? I wonder if the plugin can't simply catch key press events for its output window.
Yes, that does apply to linux. The video plugin is a shared library, so the communication has to go through that shared library API. The SDL events (keypresses, joystick movements, etc) are caught in the core and are passed to the input plugin, but not the video plugin. The video plugin only uses the OpenGL window.
HyperHacker
August 26th, 2008, 05:25
So after reinstalling Xubuntu on a new hard drive, I had to symlink /usr/lib/libopcodes-[bunch of numbers].so as /usr/lib/libopcodes.so, and the same for libbfd.so, to be able to compile the emulator. Seems like a makefile problem?
Richard42
August 28th, 2008, 02:33
So after reinstalling Xubuntu on a new hard drive, I had to symlink /usr/lib/libopcodes-[bunch of numbers].so as /usr/lib/libopcodes.so, and the same for libbfd.so, to be able to compile the emulator. Seems like a makefile problem?
I think that's a system problem. The makefile just tells the linker that it wants a certain library with a flag "-lopcodes -lbfd", and if the linker can't find the library then it's not installed or the system isn't configured properly.
HyperHacker
August 28th, 2008, 02:42
Odd, they are installed, and it's happened on fresh installs on two different machines. Maybe there's a problem with these libraries?
Richard42
August 28th, 2008, 04:22
Odd, they are installed, and it's happened on fresh installs on two different machines. Maybe there's a problem with these libraries?
I just tried it on a couple of my machines. The first one is my main desktop, a gentoo box. It compiles and runs fine there, and as you can see the .so files are there. The second is Fedora 8, my HTPC box. I don't think I've tried to compile the debugger there. Interestingly enough, it does not have the base .so files. I wonder if it's some kind of packaging issue.
21:59:01 ~ > locate libopcodes
/usr/i686-pc-linux-gnu/lib/libopcodes.so
/usr/i686-pc-linux-gnu/lib/libopcodes-2.18.so
/usr/x86_64-pc-linux-gnu/lib/libopcodes.la
/usr/x86_64-pc-linux-gnu/lib/libopcodes.so
/usr/x86_64-pc-linux-gnu/lib/libopcodes-2.18.so
/usr/x86_64-pc-linux-gnu/lib/libopcodes.a
/usr/lib64/binutils/x86_64-pc-linux-gnu/2.18/libopcodes.la
/usr/lib64/binutils/x86_64-pc-linux-gnu/2.18/libopcodes.so
/usr/lib64/binutils/x86_64-pc-linux-gnu/2.18/libopcodes-2.18.so
/usr/lib64/binutils/x86_64-pc-linux-gnu/2.18/libopcodes.a
21:59:06 ~ > locate libbfd
/usr/i686-pc-linux-gnu/lib/libbfd-2.18.so
/usr/i686-pc-linux-gnu/lib/libbfd.so
/usr/x86_64-pc-linux-gnu/lib/libbfd.a
/usr/x86_64-pc-linux-gnu/lib/libbfd-2.18.so
/usr/x86_64-pc-linux-gnu/lib/libbfd.la
/usr/x86_64-pc-linux-gnu/lib/libbfd.so
/usr/lib64/binutils/x86_64-pc-linux-gnu/2.18/libbfd.a
/usr/lib64/binutils/x86_64-pc-linux-gnu/2.18/libbfd-2.18.so
/usr/lib64/binutils/x86_64-pc-linux-gnu/2.18/libbfd.la
/usr/lib64/binutils/x86_64-pc-linux-gnu/2.18/libbfd.so
21:59:22 ~ >
22:11:46 ~ >
22:11:46 ~ >
22:11:51 ~ > ssh vgmuseum@arcadia
vgmuseum@arcadia's password:
Last login: Sat Aug 23 00:06:26 2008 from dualcore64
[vgmuseum@Arcadia ~]$ locate libopcodes
/usr/lib/libopcodes.a
/usr/lib64/libopcodes-2.17.50.0.18-1.so
/usr/lib64/libopcodes.a
[vgmuseum@Arcadia ~]$ locate libbfd
/usr/lib/libbfd.a
/usr/lib64/libbfd-2.17.50.0.18-1.so
/usr/lib64/libbfd.a
[vgmuseum@Arcadia ~]$
HyperHacker
August 28th, 2008, 05:06
That's about the same as what I get:hyperhacker@mercury:~$ locate libopcodes
/home/hyperhacker/dev/mingw/lib/libopcodes.a
/home/hyperhacker/dev/mingw/lib/libopcodes.la
/usr/lib/libopcodes-2.18.0.20080103.so
/usr/lib/libopcodes.so
hyperhacker@mercury:~$ locate libbfd
/home/hyperhacker/dev/mingw/lib/libbfd.a
/home/hyperhacker/dev/mingw/lib/libbfd.la
/usr/lib/libbfd-2.18.0.20080103.so
/usr/lib/libbfd.so
hyperhacker@mercury:~$ I created the libopcodes.so and libfd.so links manually.
Slougi
August 28th, 2008, 11:05
For the record, on arch linux:
[slougi@gondolin ~]$ ls -l /usr/lib/libbfd*
-rwxr-xr-x 1 root root 703251 2008-06-12 17:01 /usr/lib/libbfd-2.18.0.20080610.so
-rw-r--r-- 1 root root 714524 2008-06-12 17:01 /usr/lib/libbfd.a
[slougi@gondolin ~]$ ls -l /usr/lib/libopcodes*
-rwxr-xr-x 1 root root 200574 2008-06-12 17:01 /usr/lib/libopcodes-2.18.0.20080610.so
-rw-r--r-- 1 root root 210396 2008-06-12 17:01 /usr/lib/libopcodes.a
[slougi@gondolin ~]$
Günther
August 28th, 2008, 22:10
So after reinstalling Xubuntu on a new hard drive, I had to symlink /usr/lib/libopcodes-[bunch of numbers].so as /usr/lib/libopcodes.so, and the same for libbfd.so, to be able to compile the emulator. Seems like a makefile problem?
Try installing binutils-dev. Usually, those symlinks are separately packaged together with headers etc. because they are only needed to build applications, not to run them.
HyperHacker
November 14th, 2008, 04:41
I just built the SVN version (after many failed attempts to check it out, "connection reset by peer") and the debugger is broken. Whenever you pause (using the >\|| button, breakpoints, etc) it automatically steps through the code as if you were continuously clicking Next. (It does look neat though. :P Maybe adding a way to step through a specified number of instructions or step to the next breakpoint like this would be useful.)
The problem seems to be that this (debugger/debugger.c:91) isn't doing anything:// Emulation thread is blocked until a button is clicked.
SDL_CondWait(debugger_done_cond, mutex);
Also, I found a way to crash the old version I'd been using by accessing invalid memory addresses, but with the debugger not working I'm not able to test it on this build.
[edit] All I've managed to find is that pthread_cond_wait() is failing. SDL doesn't tell why. SDL_CreateCond() is working.
[edit again] I replaced the SDL_CondWait() with just setting rompause=1. It works on the title screen of Mario 64, but when the Mario head appears, it doesn't pause even though this flag is set. The pause button on the main window magically continues to work, but I see the "Mupen64Plus Started" text above the "Paused" text once the Mario head appears.
[more edit] Well I got that working by adding some more checks for rompause, then I accidentally hit a random key while trying to take this screenshot, and it proceeded to go into an endless loop complaining of SIGSEGV errors and popping up error messages until my X server locked up. >8^( (But I got it back under control by killing the process from SSH. Yay Linux!) I also reimplemented my fix for the buffer overflow in the FPU register display. (Those numbers get up to like 509 digits!) However SVN won't make a patch because "Directory 'mupen64_audio' is missing". O_o
Unfortunately, there's another bug. See if you can figure out what's missing from this image.
http://img139.imageshack.us/img139/7917/debuggercv2.png
[hey more editing] Yeah, if the core thread gets a SIGSEGV, it pops up message boxes in an infinite loop until the system locks up. V_v
vBulletin v3.6.2, Copyright ©2000-2009, Jelsoft Enterprises Ltd.