What's new

New Debugger branch - Functional

Tillin9

Mupen64Plus Dev.
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.
 
OP
D

DarkJezter

New member
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

Raving Lunatic
Wheeeee

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:

Code:
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(clBreakpoints), 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(clBreakpoints), 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();
 
OP
D

DarkJezter

New member
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

Raving Lunatic
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.
 
OP
D

DarkJezter

New member
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

Raving Lunatic
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:
Code:
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. ;)
 

nmn

Mupen64Plus Dev.
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.
 
OP
D

DarkJezter

New member
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

Raving Lunatic
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. :)
 
Last edited:
OP
D

DarkJezter

New member
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.

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.

P.S. Anyone care to take over writing a variable byte disassembler?? >:)
 
Last edited by a moderator:

Richard42

Emulator Developer
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.
 

HyperHacker

Raving Lunatic
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

Emulator Developer
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

Raving Lunatic
k, so...
Code:
hyperhacker@Mercury:~/Desktop/junk/mupen$ svn update
svn: Unable to lock 'debugger'
>_<
 
Last edited:

HyperHacker

Raving Lunatic
Time to kick ass and chew memory

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:
Code:
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(clBreakpoints), 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:
Code:
/**
 * Mupen64 - memedit.h
 * Copyright (C) 2002 DavFr - [email protected]
 *
 * 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: [email protected]
 * 
 *
 * 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:
Code:
/**
 * Mupen64 - memedit.c
 * Copyright (C) 2002 DavFr - [email protected]
 *
 * 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: [email protected]
 * 
 *
 * 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(textbox));
	
	textfont = gtk_text_buffer_create_tag(textbuf, "font", "font", "monospace", NULL);
	update_memory_editor();
	
	gtk_container_add(GTK_CONTAINER(winMemEdit), 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(textbuf, &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
 
Last edited:
OP
D

DarkJezter

New member
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

Raving Lunatic
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.
 

Top