Altirra Help Using the debugger Altirra includes a powerful debugger for diagnosing programs that are not working properly or that are under development. Contents 1. Switching to debugger mode 2. Using the command line 3. Execution control 4. Setting breakpoints 1. Switching to debugger mode To switch to debugger mode, select Debug > Enable Debugger from the menu. This will change the window layout to the default for debugging, which you can then modify by dragging panes or opening new ones from the Debug > Windows menu. Debug mode and normal mode have separate window layouts, which are saved independently. 2. Using the command line In debug mode, various panes are available to display the state of the CPU, of memory, and of emulation. However, the main port for interacting with the debugger is the Output pane, which contains both a log window and a command line at the bottom. While some of the debugger commands can be used through the GUI, all of them are available through the command line, and thus mastering the command line is important for debugging proficiency. All commands are of the form: command-name [optional arguments] To execute a command, type it in the command line and hit Enter. The command will be echoed in the log, along with any results. The command line supports history and allows past commands to be repeated using the up and down arrow keys. The .help command gives a list of available commands. You can also specify a command name, i.e. .help s, in order to get help on a specific command. 3. Execution control In order to execute commands, however, simulator execution should be halted. This can be done by Run/Break (F8) or by Break (Ctrl+Break). Once execution has been halted, the title bar will change to indicate that the simulator is stopped and all of the debug panes will be updated with the current emulation status. No changes will occur to emulation until a command is given. The t (trace) command allows the emulation to be single-stepped under debugger control. The trace command runs the simulator until the CPU has completed executing an instruction, after which it is halted again and the current execution state is reported. If an interrupt occurs, the simulator will step into the interrupt handler routine. For cases where a particular execution path is being followed, the s (step) command is useful. It acts like the trace command except that any interrupt handler or subroutine call is stepped through automatically. If the simulator has already stepped into a subroutine, the gr (go until return) command will execute instructions until the subroutine is complete. While it is possible to debug a program using the t, s, and gr commands in the command line, these commands are so frequently used that they have shortcuts: Step Into (F11), Step Over (F10), and Step Out (Shift+F11). The go (g) command resumes execution. Its analogous GUI command is Run/Break (F8). 4. Setting breakpoints Breakpoints allow the simulator to automatically break into the debugger when a specific condition is reached, permitting much faster debugging than just manually stepping the program since the program runs at full speed while the debugger waits for a breakpoint to fire. A common approach is to use breakpoints to detect an particular condition and then single-step the program around the area of interest. The most common type of breakpoint is a PC breakpoint. These are set with the bp (breakpoint) command, and listed or cleared with the bl (breakpoint list) and bc (breakpoint clear) commands, and cause the simulator to break into the debugger when the CPU PC register equals certain values. There is no limit to the number of PC breakpoints that can be set, and breakpoints persist until cleared. Since PC breakpoints are also a frequently used feature, they can be toggled in the GUI by selecting an instruction in the Disassembly view and pressing the F9 key. The Disassembly view shows a red background on any instruction on which a breakpoint is set. Unlike traditional on-machine debuggers which may insert BRK instructions into the instruction stream to implement breakpoints, breakpoints in Altirra work by address matching. Memory is never modified by a breakpoint, and they can be placed even in ROM or on hardware registers. Execution timing is also unaffected by breakpoints. Altirra also supports memory access breakpoints, which allow detection of reads or writes to a particular address. These are set and cleared using the ba (break on access) command. Memory range breakpoints can also be set, which trip whenever any location within the memory range is accessed. Taken from: Altirra 4.10 help ============================================================================================================================================ General The debugger has built-in help. Use [.help] to get a list of commands and [.help command] to get help on a specific command. Essential keyboard shortcuts: F8 (run/stop), F10 (step over), F11 (step into) In the command line, Up/Down accesses command history, Shift+Up/Down scrolls the output portion, and Enter repeats last command with continuation. Newer versions of the debugger support tabs; use View > Reset Window Layout if you have the old layout, and you should get the new, more compact default layout with stacked Registers/Disassembly/History. You can manually stack tabs by dragging them and hovering over the center of a docking diamond. If you are running Altirra through WINE, reset the font in Debug > Options > Font. It's supposed to be a monospace font, but WINE lacks one of the standard fonts. The default radix is mixed, which allows bare hex except in an expression: 10 = $10 = (16). Use [.base dec] or [.base hex] to force a different radix. Create a text file called startup.atdbg in the program dir if you have favorite commands you want to run on startup. Assemble/run setup If you are using MADS, build labels (-t) and a listing (-l). .lab and .lst files with the same name as an .xex will automatically get picked up if you start the .xex from the emulator. You really want labels loaded. If you are loading the executable from DOS, use the .loadsym command to manually load labels and listings. The /singleinstance command-line switch is very useful as it forces reuse of an already running instance of the emulator, regardless of the setting in Options. It allows a batch file to build the program and automatically launch it in the existing debugger without restarting it, keeping history and settings. If you have data files, consider using virtual disk mounting to avoid having to constantly rebuild a disk image. This is done through the [F] button in the Disk Drive dialog in versions through 2.20, and the right arrow drop-menu in 2.30+. The virtual disk driver monitors for file changes and will automatically update the virtual disk when a host file changes. You can also use the host device (H:) for this, if you are using CIO. When using MADS with a listing file loaded, you can automatically set conditional breakpoints through comments that begin with ;##ASSERT and ;##TRACE. Examples: [;##TRACE "dy = %d" db(deltar)] and [;##ASSERT dw(oldadr) >= dw(savmsc)]. TRACEs print out info using .printf syntax, and ASSERTs stop if the condition is false. These will show up in [bl] output if they were picked up correctly. 6502 debugging (immediate) Use [t] or F11 to step by one instruction, [o] or F10 to step over one instruction (skips calls and interrupts), and [g] to continue execution. Use [r] to get your bearings without looking at the Register window or to change registers: [r pc $0600]. Use [a8] to set aliases to match most Atari800 debugger commands. Use [.help a8] to see the mappings from Atari800 commands to Altirra commands. To set a breakpoint, use [bp address]. [bl] lists breakpoints, and [bc] clears breakpoints (by index, not address). The index is shown in [bl] output or when you set the breakpoint, i.e. "Breakpoint 0 set at address $A000." Use [bc *] to clear all breakpoints. To set a data breakpoint on an address, use [ba]. For instance, [ba r vcount] will set a read breakpoint on VCOUNT, and [ba w d300 L100] will break on any writes into the PIA region. To set a conditional breakpoint, use [bx "condition"]. This can be either a PC or a data breakpoint: [bx "write=irqen and pc>$c000 and (value&8)"] will fire when the OS enables the serial output complete interrupt. Breakpoints can automatically execute a list of commands when they fire, separated by semicolons. This is most useful in conjunction with the .printf command: [bp siov ".printf \"SIO request from $%04X\" (pc); g"]. The set tracepoint [bt] command makes this easier in 2.40-test. [k] dumps the call stack, if you want to know what called the current routine (or more precisely, where it will return to). To modify memory, use [e]: [e 4000 00 01 03]. To display memory, use db (display byte). There are also dw (word), dd (doubleword), df (decimal float), da (ATASCII string), and di (INTERNAL string). If you only want to view one byte or word, and particularly if you want to see decimal, use [? db(address)] or [? dw(address)]. [ln] does a lookup from address to symbol. [?] evaluates an expression. Useful for checking out state, and very useful when you're bad at math or just need a dec/hex conversion. [.vectors] dumps out a list of OS and CPU vectors. [.bank] and [.pia] show the memory banking and PIA state, respectively, and are useful for debugging extended memory usage bugs. Extended addressing syntax also allows the debugger to access xmem directly even if it isn't banked in: [db x:$8000]. [.map] shows the status of all memory layers. It's most useful to tell when you've accidentally enabled BASIC or the self-test ROM, or forgot to disable a cartridge. When debugging a program that takes over the whole machine, use [.unloadsym kerneldb] to unload the OS kernel database symbols so you don't see OS symbols where low page zero is reused. Quick-assemble small code fragments with [a address]. Type one line of asm at a time and hit Enter alone to end. It's useful to enable System > CPU > Stop on BRK instruction. 6502 debugging (deferred) Enable instruction-level history (System > CPU > Record instruction history). This enables the History debug pane, which is extremely useful for telling what happened after the fact. It has a tree-view that collapses loops, subroutine calls, and interrupts, making it easier to tell what's going on. I spend most of the time in the Console and History panes. For monitoring values over a long period of time and that correspond to on-screen activity, on-screen watches can be useful: [wb/ww] to watch a byte or word by address, and [wc] to clear. For instance, you can watch horizontal and vertical position values this way. Note that these only update once a frame. Path tracing can be useful to find out what the active regions of a program are. It is enabled either in CPU Options or with [.pathrecord on], and modified by the other path commands; executed code can be dumped with [.pathdump]. Another use for it is to find the exit path that responds to an input: use [.pathclear] to reset the path history, let the emulation run for a while to capture the loop path, then enable [.pathbreak on] and hit the joystick button or START to find out what responds to those buttons. 6502 optimization Profile View (Debug > Profile > Profile View) will show which portions of a program are using the most CPU time. Click on a header button to re-sort, and double-click on an entry to see annotated disassembly. The History window can also be used to assess performance: right click on the entry to a block and select [set Timestamp Origin], then right-click again and select [Timestamp Format > Show Cycles]. Every instruction in the history will then show when it relatively started in machine cycles. You can also use [Timestamp Format > Show Unhalted Cycles] to show how many CPU cycles were taken, ignoring DMA. Audio [.pokey] dumps POKEY state. Enable the audio monitor (Audio > Audio Monitor) to visually see what POKEY's audio channels are doing. Toggle individual audio channels with Ctrl+Alt+[1-4] to isolate squawking channels. BASIC [.basic] shows the current BASIC pointers and the memory taken by each region. [.basic_vars] dumps out all variables. [.basic_dumpline] dumps out a tokenized BASIC line by address. When running, i.e. not stopped in the debugger, shift+hover over an error message will translate the error code. Character I/O (CIO) [.tracecio on] will log all calls made to OS CIO, decoding parameters for some of them. It's most useful for figuring out what commands a program is issuing to DOS. The debug display (Debug > Window > Debug Display) can show you what is on the OS screen when your program crashes before ANTIC+GTIA have drawn it. [.ciodevs] dumps out all character devices registered in HATABS. [.iocb] dumps out the I/O control blocks. Disk I/O / SIO For sector-level access, enable the DISKCMD and HOOKSIO logging channels ([lfe diskcmd] / [lfe hooksio]). The debugger will then log which sectors are being read, and if regular OS SIO is being used, the SIO patch hook will also print out addresses. There are other useful logging channels: [lfl] to list. For hardware-level access, use [.traceser on] to get a dump of what bytes are going in and out of the serial port registers and whether overruns/underruns are happening. [bs sector] will fire a breakpoint when a particular disk sector is read. [.sio] dumps out the disk control block (DCB), which contains the current SIO request. [.diskdumpsec] will dump out a raw disk sector. You can explore the contents of any mounted DOS 2 or SpartaDOS disk by selecting Arrow -> Explore on a drive in the Disk Drives dialog. Files can then be dragged out of the Disk Explorer into the host filesystem. Graphics The display window will show the current beam position on screen as a yellow line, immediately below the beam position. [.antic] dumps ANTIC state, and [.gtia] dumps GTIA state. The go until scanline [gs] command is very useful for debugging graphics glitches. Typically I do [gs 240] or so until the glitch appears on-screen, then use the History window and the display list history to figure out what happened. The [gs] command automatically runs a full frame if you give it the vertical position that the beam is already on, so you can just spam it by alternating Up/Enter until a broken frame comes up. When execution is stopped, shift+hover over the display window will indicate beam position and display list activity for a point on screen. The display list history (.dlhistory) shows what ANTIC has executed. It's useful for telling whether something is going wrong with the display list. Conditional breakpoint expressions can include the horizontal and vertical beam position (hpos/vpos), so it's possible to have the debugger break when a DLI runs late or at the wrong time: [bx "pc=addr and vpos != 100"]. (2.40-test only) If your display list is glitching out when trying HSCROL tricks, enable Debug > Verifier > Abnormal playfield DMA. This will cause a break into the debugger when playfield DMA goes haywire when HSCROL is hit at the wrong time. Expansion hardware VideoBoard XE: [.vbxe] for general status, [.vbxe_bl] to dump a blitter command list, [.vbxe_xdl] to dump the extended display list, and [.vbxe_traceblits] to log blits. Ultimate1MB: [.ultimate] to dump status, [.ds1305] to dump real-time clock status. SIDE: [.ds1305] to dump real-time clock status. MyIDE/SIDE/IDEPlus: [.ide] to dump IDE status, [.ide_dumpsec] to dump out a sector, and [.ide_rdsec]/[.ide_wrsec] to do DMA to and from CPU memory. Taken from: https://forums.atariage.com/topic/216515-debugging-using-altirra/