Difference between revisions of "Introduction to the MAME debugger"
(→watch points) |
(→debugger quirks) |
||
(20 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | ==introduction to the MAME debugger== | + | ==introduction to the A7800/MAME debugger== |
The MAME debugger is handy when you aren't sure what's going on with your program, and need to dig deeper. This entry introduces you to the basics of the debugger. | The MAME debugger is handy when you aren't sure what's going on with your program, and need to dig deeper. This entry introduces you to the basics of the debugger. | ||
Line 7: | Line 7: | ||
mame64 a7800 -cart digdug.a78 -debug | mame64 a7800 -cart digdug.a78 -debug | ||
− | Mame will launch in a window, and a separate debugger window will also appear. The debugger is active initially, program paused and ready for your input. | + | Or if you want to enable extra hardware, like the XM, use something like the following: |
+ | |||
+ | mame64 a7800 -cart1 xm -cart2 digdug.a78 -debug | ||
+ | |||
+ | Mame will launch in a window, and a separate debugger window will also appear. The debugger is active initially, with the program paused and ready for your input. | ||
If you want your debugger commands to be active prior to the 7800 bios running, go ahead and enter them now. Otherwise, hit F5 to allow the bios to run, and when you see your game screen appear, hit F11 (step into) to get the debugger to hold up the program again. | If you want your debugger commands to be active prior to the 7800 bios running, go ahead and enter them now. Otherwise, hit F5 to allow the bios to run, and when you see your game screen appear, hit F11 (step into) to get the debugger to hold up the program again. | ||
Your bread-and-butter commands in the debugger are setting break points, and setting watch points. More detail on those and other useful commands in the following sections. | Your bread-and-butter commands in the debugger are setting break points, and setting watch points. More detail on those and other useful commands in the following sections. | ||
− | |||
==break points== | ==break points== | ||
Line 23: | Line 26: | ||
Hit F5 to resume execution. When $8000 is reached, the game will stop and the debugger window will pop-up. (if it's been hidden or minimized) | Hit F5 to resume execution. When $8000 is reached, the game will stop and the debugger window will pop-up. (if it's been hidden or minimized) | ||
− | At this point you can examine the register values, or step through the code using the Step selections in the Debug menu. | + | At this point you can examine the register values, or step through the code using the Step selections in the Debug menu. (or their keyboard equivalents) |
bpset has a lot more complexity available - you can get it to print information into the debug window, conditionally apply the break points, and more. Type "help bpset" for more info. | bpset has a lot more complexity available - you can get it to print information into the debug window, conditionally apply the break points, and more. Type "help bpset" for more info. | ||
Line 39: | Line 42: | ||
And hit F5 to resume execution. When the register $282 is read, the game will stop and the debugger window will pop-up. (if it's been hidden or minimized) | And hit F5 to resume execution. When the register $282 is read, the game will stop and the debugger window will pop-up. (if it's been hidden or minimized) | ||
− | If you want to watch memory access, without having to restart with F5 every time, | + | If you want to watch memory access, without having to restart with F5 every time, modify the read or write examples below for your address: |
+ | |||
+ | wp 0x282,1,r,1,{printf "Read %04X (%02D)",wpaddr,pb@wpaddr; g} | ||
− | |||
wp 0xcc,1,w,1,{printf "Write %04X=%02X",wpaddr,wpdata; g} | wp 0xcc,1,w,1,{printf "Write %04X=%02X",wpaddr,wpdata; g} | ||
Line 48: | Line 52: | ||
You can set multiple watch points, list, clear, enable or disable watch points. Check of the help for wpset, wpclear, wplist, wpdisable, and wpenable. | You can set multiple watch points, list, clear, enable or disable watch points. Check of the help for wpset, wpclear, wplist, wpdisable, and wpenable. | ||
− | ==other handy commands== | + | |
+ | ==viewing memory== | ||
+ | |||
+ | If you select the Debug menu in the MAME debugger window, a window will pop-up that displays the entire memory range of the first CPU. You can watch memory values change live as the program runs, or also use this window to view memory contents as you single-step through instructions in the debugger. | ||
+ | |||
+ | You can also see the memory contents of other devices in the driver, but there's a lot of information, especially a lot of driver specific internal information. Usually it's more fruitful to just stick to the main CPU memory, and if you need extra debug diagnostic information, you can write to some free area of memory in your game, and watch it's values change in this window. | ||
+ | |||
+ | |||
+ | ==other handy commands and tips== | ||
+ | |||
+ | |||
+ | [ENTER] - hitting ENTER in the debugger window without any other text will pause the emulation and start single-stepping through instructions. | ||
+ | |||
+ | |||
+ | [UP ARROW] - recall previous items you typed into the debugger. | ||
+ | |||
dasm - create a dissassembly file from certain memory range | dasm - create a dissassembly file from certain memory range | ||
e.g. dasm disassembly.txt,0x8000,0x1000 | e.g. dasm disassembly.txt,0x8000,0x1000 | ||
− | dump - create a memory dump file from a certain memory range | + | |
+ | dump - create a text memory dump file from a certain memory range | ||
e.g. dump dump.txt,0x8000,0x1000 | e.g. dump dump.txt,0x8000,0x1000 | ||
+ | |||
+ | |||
+ | save - create a binary memory dump file from a certain memory range | ||
+ | e.g. save dump.bin,0x4000,0x2000 | ||
+ | |||
statesave - save the current state to a file | statesave - save the current state to a file | ||
e.g. statesave precrash.state | e.g. statesave precrash.state | ||
+ | |||
stateload - load a saved state from a file | stateload - load a saved state from a file | ||
e.g. stateload precrash.state | e.g. stateload precrash.state | ||
+ | |||
+ | |||
+ | trace - log all executed instructions to a file | ||
+ | e.g. trace tracefile.txt (trace instructions to file) | ||
+ | e.g. trace tracefile.txt,0,noloop (trace instructions to file, and show instructions within loops) | ||
+ | e.g. trace off (turn off tracing) | ||
+ | |||
help - used without arguments, displays help overview. Used with an argument, it provides help on a section or command. | help - used without arguments, displays help overview. Used with an argument, it provides help on a section or command. | ||
Line 66: | Line 99: | ||
e.g. help memory | e.g. help memory | ||
e.g. help bplist | e.g. help bplist | ||
− | |||
==quality check your beta games with the debugger== | ==quality check your beta games with the debugger== | ||
Line 72: | Line 104: | ||
Its a good idea to check that your game doesn't try to write to ROM, or write to read-only registers. A number of commercial and homebrew games have been observed to do this seemingly weird behavior, and it's typically the result of off-by-1 errors in code that uses 6502 indirect memory access. | Its a good idea to check that your game doesn't try to write to ROM, or write to read-only registers. A number of commercial and homebrew games have been observed to do this seemingly weird behavior, and it's typically the result of off-by-1 errors in code that uses 6502 indirect memory access. | ||
− | + | wpset 0x8001,0x7fff,w (can be used with supergame bankswitch games, if you stick to using 0x8000 as the hot spot) | |
+ | An off-by-1 error that causes bad indirect memory access may not be immediately obvious or fatal, but can result in unexpected behavior on some consoles and/or bank switching cartridges. | ||
==debugger quirks== | ==debugger quirks== | ||
Line 79: | Line 112: | ||
You need to keep in mind that the debugger operates in the live 6502 address space. If your game is bank switched, you need to keep in mind that commands that inspect memory only apply to the currently active bank. The debugger is like the 6502 - it doesn't know or care what cartridge magic is going on. | You need to keep in mind that the debugger operates in the live 6502 address space. If your game is bank switched, you need to keep in mind that commands that inspect memory only apply to the currently active bank. The debugger is like the 6502 - it doesn't know or care what cartridge magic is going on. | ||
− | It should also be noted a watch point that looks for reads of a memory location will also be triggered by writes to that same location. | + | It should also be noted a watch point that looks for reads of a memory location will also be triggered by writes to that same location. On some internal level the 6502 does a read during a write, and the MAME devs have decided to act on this. Not the most intuitive, so you may need to check the command that triggered the break point, and confirm it was indeed a read. |
− | |||
==Authorship== | ==Authorship== | ||
Introduction to the MAME debugger was written by Mike Saarna (aka RevEng) as original content for 7800.8bitdev.org. | Introduction to the MAME debugger was written by Mike Saarna (aka RevEng) as original content for 7800.8bitdev.org. |
Latest revision as of 14:33, 6 January 2021
Contents
introduction to the A7800/MAME debugger
The MAME debugger is handy when you aren't sure what's going on with your program, and need to dig deeper. This entry introduces you to the basics of the debugger.
To launch MAME with the debugger enabled, type in the following, substituting your own ROM file for the one provided:
mame64 a7800 -cart digdug.a78 -debug
Or if you want to enable extra hardware, like the XM, use something like the following:
mame64 a7800 -cart1 xm -cart2 digdug.a78 -debug
Mame will launch in a window, and a separate debugger window will also appear. The debugger is active initially, with the program paused and ready for your input.
If you want your debugger commands to be active prior to the 7800 bios running, go ahead and enter them now. Otherwise, hit F5 to allow the bios to run, and when you see your game screen appear, hit F11 (step into) to get the debugger to hold up the program again.
Your bread-and-butter commands in the debugger are setting break points, and setting watch points. More detail on those and other useful commands in the following sections.
break points
Break points tell the MAME debugger to stop execution of your program when a certain address is reached. This is useful if you know the exact ROM address the routine you wish to inspect is located at. You can confirm that for your own programs by locating the routine in your DASM listing.
To set a break point at $8000, enter:
bpset 0x8000
Hit F5 to resume execution. When $8000 is reached, the game will stop and the debugger window will pop-up. (if it's been hidden or minimized)
At this point you can examine the register values, or step through the code using the Step selections in the Debug menu. (or their keyboard equivalents)
bpset has a lot more complexity available - you can get it to print information into the debug window, conditionally apply the break points, and more. Type "help bpset" for more info.
You can set multiple break points, list, clear, enable or disable break points. Check out the help for bpset, bpclear, bplist, bpdisable, and bpenable.
watch points
Watch points tell the MAME debugger to stop execution when some bit of memory is accessed. This is useful for tracking down the routines that use certain registers, or to determine how a variable is being updated in your program.
To set a watch point to see when the game reads console switchings, enter:
wpset 0x282,1,r
And hit F5 to resume execution. When the register $282 is read, the game will stop and the debugger window will pop-up. (if it's been hidden or minimized)
If you want to watch memory access, without having to restart with F5 every time, modify the read or write examples below for your address:
wp 0x282,1,r,1,{printf "Read %04X (%02D)",wpaddr,pb@wpaddr; g}
wp 0xcc,1,w,1,{printf "Write %04X=%02X",wpaddr,wpdata; g}
wpset has a lot more complexity available than the above example - you can watch a range of addresses, trigger only on certain conditions, print out useful information, and more. Type "help wpset" for more info.
You can set multiple watch points, list, clear, enable or disable watch points. Check of the help for wpset, wpclear, wplist, wpdisable, and wpenable.
viewing memory
If you select the Debug menu in the MAME debugger window, a window will pop-up that displays the entire memory range of the first CPU. You can watch memory values change live as the program runs, or also use this window to view memory contents as you single-step through instructions in the debugger.
You can also see the memory contents of other devices in the driver, but there's a lot of information, especially a lot of driver specific internal information. Usually it's more fruitful to just stick to the main CPU memory, and if you need extra debug diagnostic information, you can write to some free area of memory in your game, and watch it's values change in this window.
other handy commands and tips
[ENTER] - hitting ENTER in the debugger window without any other text will pause the emulation and start single-stepping through instructions.
[UP ARROW] - recall previous items you typed into the debugger.
dasm - create a dissassembly file from certain memory range
e.g. dasm disassembly.txt,0x8000,0x1000
dump - create a text memory dump file from a certain memory range
e.g. dump dump.txt,0x8000,0x1000
save - create a binary memory dump file from a certain memory range
e.g. save dump.bin,0x4000,0x2000
statesave - save the current state to a file
e.g. statesave precrash.state
stateload - load a saved state from a file
e.g. stateload precrash.state
trace - log all executed instructions to a file
e.g. trace tracefile.txt (trace instructions to file) e.g. trace tracefile.txt,0,noloop (trace instructions to file, and show instructions within loops) e.g. trace off (turn off tracing)
help - used without arguments, displays help overview. Used with an argument, it provides help on a section or command.
e.g. help e.g. help memory e.g. help bplist
quality check your beta games with the debugger
Its a good idea to check that your game doesn't try to write to ROM, or write to read-only registers. A number of commercial and homebrew games have been observed to do this seemingly weird behavior, and it's typically the result of off-by-1 errors in code that uses 6502 indirect memory access.
wpset 0x8001,0x7fff,w (can be used with supergame bankswitch games, if you stick to using 0x8000 as the hot spot)
An off-by-1 error that causes bad indirect memory access may not be immediately obvious or fatal, but can result in unexpected behavior on some consoles and/or bank switching cartridges.
debugger quirks
You need to keep in mind that the debugger operates in the live 6502 address space. If your game is bank switched, you need to keep in mind that commands that inspect memory only apply to the currently active bank. The debugger is like the 6502 - it doesn't know or care what cartridge magic is going on.
It should also be noted a watch point that looks for reads of a memory location will also be triggered by writes to that same location. On some internal level the 6502 does a read during a write, and the MAME devs have decided to act on this. Not the most intuitive, so you may need to check the command that triggered the break point, and confirm it was indeed a read.
Authorship
Introduction to the MAME debugger was written by Mike Saarna (aka RevEng) as original content for 7800.8bitdev.org.