Power64 features a built in Monitor/Debugger that works on the Macintosh side of the the emulation. Thus it possible to examine and modify the contents of the C64 state without using any C64 RAM. The monitor is completely invisible for the running C64 program.
To start the monitor/debugger choose the menu item File/Monitor/Debugger (Cmd-G). A text window will pop up where you can enter commands (see the list of supported commands below) and view results.
By default the monitor/debuggers commands will work on the C64, but you use the 'z' command (see below) to examine one of the disk drives.
To execute a command type it in one line and press <Return>. Note that you can 'recycle' old commands by moving the cursor back over an old command and pressing <Return> again or by using Cut & Paste. There is also a command history, that can be accessed using Ctrl-Up/Down.
To modify the contents of the RAM it is best to first use a command that displays the old contents in a convenient form (usually the 'm' command) and then edit the output. By pressing <Return> while the cursor is on the edited line, the memory can be modified.
The output of any command can be controlled using Space and Return. While pressing Space generates just a single line of output, pressing Return causes output to be generated until another key is pressed. Escape aborts any command.
The commands 'm', 'k', 'n', 'b' and 'y' can also be controlled using the cursor keys. Cursor down produces (like Space) the next line of output. Cursor up give the line before the current line and Cursor left/right generate the current line +/- 1 Byte.
Most commands expect one or two numeric parameter that can be given as C-like expressions. Values can be given in either hexadecimal (without prefix or with prefix '$' or '0x'), decimal (prefix '#') or binary (prefix '%') constants or using the CPU registers ('rA', 'rX', 'rY', 'PC', 'SP' (including the $0100 offset)) or the contents of the RAM ('RAM[xxxx]' or 'RAMb[xxxx]' for 8 bit access or 'RAMw[xxxx]' for 16 bit). MEM[xxxx] works like RAM[xxxx] but does take the current memory configuration into account. Thus MEM[xxxx] will sometimes access RAM, sometimes ROM sometime I/O Chips. Instead of MEM[xxxx] it is also possible to use PEEK(yyyy), where, like standard BASIC, the address yyyy is given in decimal notation. The arithmetic register names ('rA', 'rX', 'rY') and RAM references can be suffixed with 's' or 'u' (e.g. 'rAs','rXu', RAMsb[xxxx]) to denote a signed or unsigned value (default: unsigned).
These values can be combined with the operators '+', '-', '*', '/', '<<', '>>', '&', '|', '^', '~', '-'. All operations are performed using signed 32 bit integers. Note that (different from C) the shift operators have higher precedence than '+', '-', '*' and '/' (I believe that the C semantic is unfortunate in this point). Of course you can use parenthesis to enforce a specific order of evaluation.
The monitor supports the following commands (parameters in parenthesis are optional):
a StartAddr - Assemble
Assemble Code starting at StartAddr. All constants and addresses in the code must be given in hexadecimal. It is possible to use the common illegal opcodes. An empty line ends the assembly.
b StartAddr (EndAddr) - Binary Memory Dump
Displays the memory contents from StartAddr to EndAddr in binary notation. Set bits are denoted by '*', clear bits by '.'. This makes it easy to look at the bitmap for a character set. Change the contents of the memory by overwriting the data and pressing <Return>.
c StartAddr EndAddr CompareAddr - Compare Memory
Compare the memory contents from StartAddr to EndAddr with the memory contents of the range starting at CompareAddr. Equal and non-equal ranges of memory are reported. Note that equal ranges of less than 3 bytes that are surrounded by non-equal ranges are considered artifacts, and are suppressed in order to avoid clutter.
d StartAddr (EndAddr) - Disassemble
Disassembles the memory contents from StartAddr to EndAddr. Changes to a program are possible by overwriting the output and pressing <Return>.
f StartAddr EndAddr Bytes - Find
Find a series of bytes in a range of memory. The bytes must be given in hexadecimal notation. It is possible to use '?' as a wild card for a hex-digit. Furthermore it is possible to search for character strings. When searching for strings there is no distinction of upper/lowercase, ASCII/screen code or normal/reverse text.
Example: f a000 a080 43 42 ?d f e000 f000 "Commodore"
fa StartAddr EndAddr SearchAddr - Find Address
Find all references to the absolute address SearchAddr in the program within the memory range StartAddr to EndAddr. Again it is possible to use '?' as a wild card for a hex-digit.
Example: fa e000 ffff d0?? - find all accesses to the VIC in the Kernal ROM.
fp StartAddr EndAddr Value - Find Poke
Find all locations in a range of memory, that contain a specific value. Using + instead of StartAddr EndAddr causes fp to search only in the results of the previous search.
Example: Assume you want to know where Serpentine stores the number of lives left. After starting the game you have 5 lifes. Thus fp 0800 2A00 5 will find all locations, that contain the value 5. There is quite a lot of locations (too many to sort through by hand). Now play until you loose a life (now there are 4 lives left) and use fp + 4 to find all those locations that now contain 4 and used to contain 5. There are none - Too bad ;(. Second try: If we start with 5 lives, then we have one life that is currently going on and 4 lives left. So we restart the game, and use fp 0800 2a00 4 to find all locations that contain 4. After loosing one life, we use fp + 3 to search again. Behold: There is just one match ($0924)! Sometime there might be several matches, but usually there are so few, that they can easily be sifted through by hand. We now know where Serpentine stores the number of remaining lives. We can just use m 0924 to view and update this value or use fa 0800 2a00 0924 to search for all locations where Serpentine accesses or updates this value (and then apply appropriate patches there).
Note that fp is an advanced command for skilled users. There are many ways that a game might store a value. The 'Off-by-One' problem was shown above.
It is also possible that the game does not count the number of lifes left, but the number of lifes already used (or in use). Other quantities like scores sometimes stored with an factor (e.g. if all scores are a multiple of 100 points, then there is no point in storing the two zeros, so a game may or may not divide the score by 100 before storing it). The fp command searches for the given value in binary format (little- and big-endian), in BCD format (little- and big-endian), in ASCII format and in Float format. Patience, repeated trial and guesswork are the key to success when using the fp command.
g (RegPC) - Go
Resume the execution of the C64 program at address RegPC (default: at the current PC). This does not destroy the monitor window.
h - Help
Display a short summary of the monitor commands.
l (LoadAddr) - Load Data
Loads data from the Mac hard disk (*.C64, *.CBM, *.PRG or *.P00 files) into the C64 RAM. Usually you will load the data to the address stored in the file, but it is possible to override this by providing a new LoadAddr. The file is selected using a Mac file selector.
lr LoadAddr - Load Raw Data
Loads data from the Mac hard disk into the C64 RAM. This loads the entire(!) contents of the file into RAM, including the header data of a *.P00 file and the two leading bytes load address. Thus it is necessary to provide a new load address as a parameter to lr. The file is selected using a Mac file selector.
k StartAddr (EndAddr) - PETASCII Memory Dump
Displays the memory contents from StartAddr to EndAddr as PETASCII-characters. Change the contents of the memory by overwriting the data and pressing <Return>.
m StartAddr (EndAddr) - HEX/PETASCII Memory Dump
Displays the memory contents from StartAddr to EndAddr as hex-values and as PETASCII-characters. Change the contents of the memory by overwriting the hex-values and pressing <Return>.
n StartAddr (EndAddr) - Screen Code Dump
Displays the memory contents from StartAddr to EndAddr as C64 screen codes. Change the contents of the memory by overwriting the output and pressing <Return>.
o StartAddr EndAddr Data - Fill (Occupy) Memory
Fills the memory range from StartAddr to EndAddr with the value Data.
p ("FileName") - Reset Log File
Closes the current log file, and opens a new with the given file name (default: "C64 Monitor - dd.mm.yyyy" in the print file folder). Output of any command can be sent to the log file by capitalizing the command name.
r - Show CPU Registers
Displays the contents of the CPU registers. Changes are possible by overwriting the output and pressing <Return>.
s StartAddr EndAddr - Save Data
Saves the memory range StartAddr to EndAddr to the Mac hard disk. The file is selected using a Mac file selector. Depending on the files extension it will be written in either *.C64, or *.P00 file format. If you want to save the same range that was previously loaded it is not necessary to specify the StartAddr and EndAddr.
sr StartAddr EndAddr - Save Raw Data
Saves the memory range StartAddr to EndAddr to the Mac hard disk. Only the data from memory is saved, no load address or other header information is stored in the file. The file is selected using a Mac file selector.
t (RegPC) - Trace Program
The C64 program at address RegPC (default: at the current PC) is executed one statement at a time. After each command the state of the CPU is displayed and Power64 waits for a key. Press Space to execute another C64 instruction. Press Return to trace quickly - All information is displayed, but Power64 does not wait for a key. Press 'J' to quickly execute the remainder of the current subroutine. Press 'S' to execute the current line (a plain statement or a complete subroutine call). Press 'G' to abort trace mode and switch back to the C64 window, executing code at full speed.
tb (Addr (Cnt)) (!) - Set/Clear/Show Breakpoints
Without parameters tb displays a list of all currently set breakpoints. When given just an Addr, tb sets a breakpoint at the specified address. Every time the C64 reaches reaches this address, program execution will be shifted into Trace mode. If, in addition to Addr,tb also receives a Cnt it will only enter Trace mode when it program execution reaches Addr for the Cnt'th time. At that time the Breakpoint will be automatically deleted. To manually delete a single breakpoint use tb Addr!. Use tb ! to remove all breakpoints at once.
The special keywords IRQ, NMI and RESET can be used instead of Addr to create a breakpoint whenever an exception occurs.
Note: Breakpoints may fail if the C64 program uses self modifying code.
tl (Scanline (RegPC)) - Trace to Scanline
Run the C64 program at address RegPC (default: at the current PC) until the VIC has reached a specific Scanline (default: same as last tl command). Then change into Trace mode and execute the statements step by step. This is very useful when debugging Raster-IRQ code.
tq (Cnt (RegPC)) - Trace Quick
Execute Cnt statements of the C64 program at address RegPC (default: at the current PC) quickly. Then change to Trace mode. This helps finish long loops quickly without looking at every single iteration.
v vic - Show the state of the Video IC
Displays the state of the Video IC in an easy-to-read fashion.
v sid - Show the state of the Sound ID
Displays the state of the Sound ID in an easy-to-read fashion.
v cia1 | cia2 - Show the state of the CIA 1 or CIA2
Displays the state of the CIA1 or CIA2 an easy-to-read fashion (including internal data from shadow registers).
v ram - Show the memory configuration
Displays the memory configuration in an easy-to-read fashion.
w StartAddr EndAddr DestAddr - Write (Copy) Memory
Copies the contents of the memory range StartAddr to EndAddr into the memory range starting at DestAddr. This works correctly even if the source and destination ranges overlap (or are identical when copying the ROMs into RAM).
x - Exit/Quit Monitor
Closes the monitor window.
y StartAddr (EndAddr) - Sprite Dump
Displays the memory contents from StartAddr to EndAddr in binary notation with 3 bytes in a line. Set bits are denoted by '*', clear bits by '.'. This makes it easy to examine sprite bitmaps. Change the contents of the memory by overwriting the data and pressing <Return>.
z DeviceNumber - Select Device to Debug
Selects the device that is to be examined by the monitor. Selecting DeviceNumber 8-11 chooses the corresponding disk drive. All other numbers select the Commodore64.
$ HexExpr - Evaluate a Hexadecimal expression
# DecimalExpr - Evaluate a Decimal expression
% BinaryExpr - Evaluate a Binary expression
? Expression - Calculate Expression
Evaluate an expression and print its value in hexadecimal and decimal.
POKE DecimalAddr, DecimalExpr - Change Memory
Changes the value of one memory location. Note that in contrast to other debugger commands the address and the value are given in decimal notation, just like in C64 BASIC. This makes it easier to enter cheat-POKEs in a running program.
SYS DecimalAddr - Start Program
Like Go, but the address is given in decimal notation à la BASIC
The output of all these commands can be sent to a log file (in addition to the screen display) by capitalizing the command name. The log file is situated in the print file folder (see 5.15 Printer for more on the print file folder) and is called "C64 Monitor - dd.mm.yyyy" by default. See the 'p' command above on ways to rename the log file.