A Source Level Debugger for ORCA-Compatible High-Level Languages
Copyright 2017–2018, Kelvin W Sherlock. All Rights Reserved.
ORCA/C, ORCA/M, ORCA/Pascal, ORCA/Integer Basic, ORCA/Modula-2, PRIZM, and The Byte Works are trademarks of the Byte Works, Inc. GNO and GNO/ME are trademarks of Procyon Enterprises Incorporated. Apple IIgs, ProDOS, Macintosh, HFS, and GS/OS are registered trademarks of Apple, Inc.
Contents
Golden Gate Debugger (iixdb) is a source-level debugger built into Golden Gate. It draws inspiration from GDB, the GNU Debugger, as well as Splat! and NiftyList. It works with the ORCA debugger protocol, similar to Splat!, PRIZM, or ORCA/Debugger. iixdb is enabled by using the iix -g flag. Software must be compiled with debugging enabled (compile +D, occ -g, or a suitable debug pragma).
Please note that the ORCA debugger protocol is intrusive (based on the 65816 COP instruction) and debugging should be disabled for redistribution. (iix will silently skip over COP debugger instructions when the -g flag is not specified; A real IIgs will crash.)
# iix -g my-buggy-program args ... Golden Gate Debug Shell (iixdb)
The iixdb shell initially opens after the program has loaded but before the program has begun executing. Normally, you would use this opportunity to set breakpoints and continue. Alternatively, step to quickly break at the first line (normally main in C, ~_PASMAIN in Pascal).
Breakpoints come in two flavors; permanent and temporary. Temporary breakpoints are one-shot and automatically delete themselves after being triggered. Permanent breakpoints are permanent (but can be manually removed).
The break (b) command sets permanent breakpoints
Set a breakpoint at the current line:
break
Set a breakpoint at a specified function (as a string or an identifier):
break function
Set a breakpoint at a specified line number within a file. file may be a string or identifier:
break file:line
Set a breakpoint at line offset (based on the current line and file):
break +number break -number
Temporary breakpoints are set with the tbreak command.
Breakpoints can be listed with the info break (or info tbreak) command.
Breakpoints can be removed with the delete command:
delete break number delete tbreak number
The continue (c) and run (r) commands will run to the next breakpoint.
The return (r) command will step out of the current function.
The next (n) command will break at the next line, stepping over function calls.
The step (s) command will break at the next line, stepping into function calls.
The quit (q) command will exit iixdb and Golden Gate.
Tip: r is short for run or return, depending on context. Before your program begins execution, r is run. Once running, r is return.
Tip: As an alternative to stepping, tracing (set trace on) will print lines as they are executed, without breaking.
The list (l) command lists source code.
List the current line (with 6 lines of context):
list
List the current line (with 10 lines of forward context):
list +
List the current line (with 10 lines of backward context):
list -
List a specified line (with 6 lines of context):
list number
List a specific range of lines:
list number,number
The disassemble (d) command disassembles memory as 65816 assembly language.
Disassemble the current line/program counter:
disassemble
Disassemble a specific address:
disassemble expression
The print (p) command prints an expression or variable.
Print an expression:
print expression
Print an expression in hexadecimal:
print/x expression
The print command can also be used to set a variable:
print expression*=*expression
(The /x modifier may also be specified, of course). The left hand side expression must be a variable or register.
The hd (x) command will hexdump memory.
hd expression
The backtrace (bt) command prints the function call stack.
(iixdb) backtrace frame #1 $0201d1 DEBUG.C at main:14 frame #2 $02003d DEBUG.C at beep:7
The * command will list the local and global symbol table entries.
(iixdb) * Global Symbols: (struct *) stderr (struct *) stdin (struct *) stdout Local Symbols: (int16_t) x
The info command will print miscellaneous toolbox, GS/OS, and other information.
Print profiling information (see also iix --profile):
info profile
Print register information:
info registers
Print GS/OS file information
info files
Print GS/OS Loader information:
info loader
Print ORCA Fast File information:
info fastfiles
Print Memory Manager information:
info mm
Print Resource Manager information:
info rm
The set command configures certain settings
Print current settings:
set
Set variable case-sensitivity:
set case on/off
(Default is on (case-sensitive), which is appropriate for C. Pascal is case-insensitive.)
Set the current directory, for locating source code files:
set cd string
Enable or disable tracing:
set trace on/off
With tracing enabled, source code lines are printed as they’re executed.
The : command toggles NiftyList mode to make certain commands more familiar for Apple II users. A NiftyList command can also be entered from the iixdb shell by preceding it with a single : character.
Print an expression (in hexadecimal):
: expression
Equivalent to:
print/x expression
Hexdump memory:
: expression;h
Equivalent to:
hd expression
Disassemble (list) memory:
: expression;l
Equivalent to:
disassemble expression
Many commands require an expression as an argument. An expression is composed of literal numbers, symbol table variables, registers, and operators.
Literal numbers may be decimal (123), hexadecimal (0xabc), binary (0b0101), or a character constant ('a').
Variables follow C naming conventions ([A-Za-z_][A-Za-z0-9_]*) but ~ and a trailing $ are also permitted for compatibility. (Use whitespace around the ~ operator). If the variable name contains other characters, it may be quoted with a back-tick (`), `like so`. On OS-X, the debug shell will attempt to tab-complete variable names. The * command will list known global and local variables.
Registers are preceded with a @ symbol. The special register @kpc is the 24-bit program counter with program bank.
Operators follow C semantics and precedence rules.
Precedence |
Operator |
Usage |
---|---|---|
1 |
[] |
Array subscript |
., -> |
Struct Member access |
|
2 |
+, - |
Unary plus, unary minus |
!, ~ |
Logical not, bitwise not |
|
* |
Indirection |
|
& |
Address of |
|
3 |
*, /, % |
Multiplication, division, remainder |
4 |
+, - |
Addition, subtraction |
5 |
<<, >> |
Bitwise Left shift, right shift |
6 |
<, <= |
Less than, less than or equal |
>, >= |
Greater than, greater than or equal |
|
7 |
==, != |
Equal, not equal |
8 |
& |
Bitwise and |
9 |
^ |
Bitwise exclusive or |
10 |
| |
Bitwise or |
11 |
&& |
Logical and |
12 |
|| |
Logical or |
Operations – other than indirection, array subscripting, and member access – are performed with 32-bit integers.
Tip: Some commands (such as hd or disassemble) expect an address. Pointer variables and array variables are addresses, as are the @pc, @d, and @s registers. The & address of operator can be used to take the address of a variable which is, of course, an address. 32-bit integers are also considered addresses; unary plus can be used to convert anything to a 32-bit integer and thus an address, if desired.
Examples:
print argv[argc-1] print *pointer print stdout->_flag hd @d+5
The ORCA debugger protocol has a number of limitations. Some of these may be fixed in future compiler updates; some of them are inherent to the protocol itself. The Byte Works originally wanted to add debugging information at the OMF level. Apple was “disagreeable” so the COP-based protocol was designed instead.
Debugger-enabled code will crash a IIgs without an installed debugger.
Debug information (symbol tables, file names, and function names) is not available until the code executes.
File names are stored in uppercase. Use a case-insensitive filesystem for storing source code.
Union and structure names are not retained.
Void pointers and function pointers are typed as integer pointers.
Global and static variables are only included with the main symbol table. Global variables from other translation units (“files”, as normal people call them) are not available.
Function prologue and epilogue code is mis-attributed in the profiler.
(ORCA/C) Static variables declared within a function are name-mangled (e.g., variable becomes ~0001variable).
(ORCA/C) Variables declared within a sub-block are not included in the symbol table.
(ORCA/C) Pointers to incomplete structs generate invalid symbol tables.
(ORCA/C) Struct within structs can generate invalid symbol tables.
(ORCA/C) Bit fields don’t retain their bit count.
(ORCA/C) Arrays of strings are typed as a c-string, losing the array size in the process.
(ORCA/Pascal) $pragma debug 2 doesn’t enable the necessary profiling information. Use $pragma debug 3, compile +D, or occ -g.
(ORCA/Pascal) Arrays (including strings) generate invalid symbol tables.
(ORCA/Modula-2) Symbol tables don’t include records or other compound types.