26. Console command line

The console interpreter provides an interactive command line with powerful features, including command completion, history, and editing.

Your command-line history is also saved between sessions in ~/.db68k/db68k.history.

26.1. Command structure

Commands consist of one or more space-separated words. If a word is a unique match, it does not need to be fully spelled out.

An argument specific to the command (e.g., a string, file path, or boolean value) may follow the command.

Command completion

Command completion is available via the TAB key. Pressing TAB directly at the prompt lists all words that can start a command:

(db68k) <TAB>
b                 enable            n                 s
break             exec-file         next              set
c                 file              nexti             show
cd                frame             ni                si
clear             ignore            p                 source
complete          info              platform          step
condition         interpreter-exec  plug-in           stepi
continue          interrupt         print             thread
delete            kill              pwd               tty
directory         lua               quit
disable           maintenance       r
disassemble       module            run
(db68k)

Pressing TAB completes the command to the left of the cursor as far as possible. If the word can be partially completed, the first TAB fills in as much as possible:

(db68k) int<TAB>

Results in:

(db68k) inter

Pressing TAB a second time results in:

(db68k) inter<TAB>
interpreter-exec  interrupt
(db68k) inter

26.2. A simple debugging session

To get a feeling for how to run a very simple debugging session here is how it can look.

$ db68k program.elf
Calypsi debugger for 68000
(db68k) b main
breakpoint 1
(db68k) info breakpoints
Num      Type            Disp  Enb  Lua/plug-in   Address     What
1        breakpoint      keep  y    no            0x80f1      main
(db68k)

Note

A command requires enough characters to be unique. For info breakpoints, i b suffices, as the interpreter considers all words for multi-word commands. The completer operates left of the cursor: i<TAB> shows all commands starting with ‘i’. To complete to info, type inf<TAB> followed by b<TAB>. However, i b<TAB> expands directly to i breakpoints because no other command matches this two-word prefix.

To start debugging, use the run command (abbreviated to r), which begins execution from the start. The program will stop almost immediately upon hitting the breakpoint at the beginning of the main() function.

(db68k) r
running
   12 int const cglob_int = 12;
   13
   14 size_t fossasas;
   15
-> 16 int main () {
   17   basic_expr(glob_int + 21, 2, 3);
   18   int xx = fact(4);
   19   int b = vla1(4);
   20   int c = struct1(2);
0x80f1 in main() at main.c:16
(db68k)

The breakpoint is hit at address 0x80f1 in main.c:16. Source lines are shown as context, with the current line indicated.

You can now perform a couple of single steps:

(db68k) s
   13
   14 size_t fossasas;
   15
   16 int main () {
-> 17   basic_expr(glob_int + 21, 2, 3);
   18   int xx = fact(4);
   19   int b = vla1(4);
   20   int c = struct1(2);
   21   int d = struct11(2);
0x80f8 in main() at main.c:17
(db68k)
   10 int bar (long* p) {
   11   return *p + 2;
   12 }
   13
-> 14 long basic_expr (long a, long a2, int a3) {
   15   char cglob_int = 0; // block a variable in global scope
   16   if (a) {
   17     int b = 10;
   18     a = foo(b + cglob_int);
0x8000 in basic_expr() at basic_expr.c:14
(db68k)

The step command (abbreviated as s) steps one source line. Subsequent steps can be performed by simply pressing RETURN, which repeats the most recent command.

Each step advances us one line, updating the source context shown.

The step command steps into functions, as demonstrated by entering basic_expr().

You can inspect variables with info locals.

(db68k) info locals
(int) a3 = 3
(long) a2 = 2
(long) a = 21
(db68k)

To trace your current position, use the backtrace (or bt) command.

(db68k) backtrace
#0 0x8000 in basic_expr() at basic_expr.c:14
#1 0x8137 in main() at main.c:17
(db68k)

To stop at a specific line further down the source code, use:

(db68k) b basic_expr.c:18
breakpoint 2
(db68k) c
running
program hit breakpoint 2
   14 long basic_expr (long a, long a2, int a3) {
   15   char cglob_int = 0; // block a variable in global scope
   16   if (a) {
   17     int b = 10;
-> 18     a = foo(b + cglob_int);
   19     a += bar(&a2);  // take address of a2, forcing it to stack.
   20   }
   21   if (a > 2) {
   22     // Make the variable in global scope visible
0x8030 in basic_expr() at basic_expr.c:18
(db68k)

The continue command (or c) resumes execution (unlike run, which starts from the beginning).

Lets take a look at the local variables again.

(db68k) info locals
(int) b = 10
(char) cglob_int = 0
(int) a3 = 3
(long) a2 = 2
(long) a - value not available, reason: "a" has no valid value here
(db68k)

The local context now includes two additional variables: b and cglob_int. At this point, a has no value because its last use was on line 16, and it will receive a new value on the current line. Thus, the debugger knows about a but it currently holds no examinable value.

Step over two function calls and inspect the value of a using the print command, which can be entered as p:

(db68k) n
   15   char cglob_int = 0; // block a variable in global scope
   16   if (a) {
   17     int b = 10;
   18     a = foo(b + cglob_int);
-> 19     a += bar(&a2);  // take address of a2, forcing it to stack.
   20   }
   21   if (a > 2) {
   22     // Make the variable in global scope visible
   23     extern int const cglob_int;
0x8050 in basic_expr() at basic_expr.c:19
(db68k)
   17     int b = 10;
   18     a = foo(b + cglob_int);
   19     a += bar(&a2);  // take address of a2, forcing it to stack.
   20   }
-> 21   if (a > 2) {
   22     // Make the variable in global scope visible
   23     extern int const cglob_int;
   24     a = foo(cglob_int);
   25   }
0x8082 in basic_expr() at basic_expr.c:21
(db68k) p a
(long) $8 = 8
(db68k)

The variable a now holds a value from the function calls.

The local char cglob_int shadows a global variable of the same name. Stepping to line 24 makes the global cglob_int visible:

(db68k) n
   20   }
   21   if (a > 2) {
   22     // Make the variable in global scope visible
   23     extern int const cglob_int;
-> 24     a = foo(cglob_int);
   25   }
   26   return a + a3;   // a3 alive and on the stack
   27 }
   28
0x8098 in basic_expr() at basic_expr.c:24
(db68k) p cglob_int
(int const) $9 = 12
(db68k)

The global variable is now visible again, with a different value and type.

If you no longer need the first breakpoint, you can disable it using the disable command, rather than deleting it:

(db68k) disable 1
(db68k) info breakpoints
Num      Type            Disp  Enb  Lua/plug-in   Address     What
1        breakpoint      keep  n    no            0x80f1      main
         breakpoint already hit 1 time
2        breakpoint      keep  y    no            0x8030      basic_expr.c:18
         breakpoint already hit 1 time
(db68k)

The first breakpoint still exists but is not enabled (n in the Enb column). The debugger also tracks breakpoint statistics.