Difference between revisions of "Introducing jzIntv's Debugger"

From Intellivision Wiki
Jump to: navigation, search
(Stepping Through Code)
Line 1: Line 1:
 
[[jzIntv]] offers a simple command-line oriented debugger.  It should be familiar in style to anyone who has used the Apple ][ Monitor or DOS's DEBUG.EXE.
 
[[jzIntv]] offers a simple command-line oriented debugger.  It should be familiar in style to anyone who has used the Apple ][ Monitor or DOS's DEBUG.EXE.
  
= Debugger Overview =
+
= Invoking the Debugger =
 
 
== Invoking the Debugger ==
 
 
To invoke the debugger, add the "-d" flag to jzIntv's command line.  For example, using "hello2.rom" from the [[Hello World Tutorial]]:
 
To invoke the debugger, add the "-d" flag to jzIntv's command line.  For example, using "hello2.rom" from the [[Hello World Tutorial]]:
 
<CODE><pre>
 
<CODE><pre>
Line 43: Line 41:
 
Most of this output is jzIntv's initialization.  The last portion is the debugger prompt.
 
Most of this output is jzIntv's initialization.  The last portion is the debugger prompt.
  
== The Debugger Prompt ==
+
= The Debugger Prompt =
  
 
<CODE><pre>
 
<CODE><pre>
Line 74: Line 72:
 
In the example above, jzIntv shows the CPU as taking an interrupt.  Really, it's coming out of reset, which is similar.  Don't worry too much about interrupts for the moment.
 
In the example above, jzIntv shows the CPU as taking an interrupt.  Really, it's coming out of reset, which is similar.  Don't worry too much about interrupts for the moment.
  
== Introducing the Registers ==
+
= Introducing the Registers =
  
 
The CPU's [[CP1610#Registers|registers]] act as a scratch pad, holding values for instructions to operate on.  Some registers have special purposes.  All the registers can be used for general purpose computation.  Here's a quick reference to what each register can be used for.
 
The CPU's [[CP1610#Registers|registers]] act as a scratch pad, holding values for instructions to operate on.  Some registers have special purposes.  All the registers can be used for general purpose computation.  Here's a quick reference to what each register can be used for.
Line 94: Line 92:
 
You will want to pay attention to R7 to know where you are at in your program.  You can use the listing file, as [[Hello World Tutorial#Examining the Listing File|described in the Hello World tutorial]] to relate what you wrote to what the debugger shows you.
 
You will want to pay attention to R7 to know where you are at in your program.  You can use the listing file, as [[Hello World Tutorial#Examining the Listing File|described in the Hello World tutorial]] to relate what you wrote to what the debugger shows you.
  
== Debugger Commands ==
+
= Debugger Commands =
  
 
The debugger offers a series of single-letter commands.  You can find a [http://spatula-city.org/~im14u2c/intv/debugger.txt full summary here].  For this tutorial, we will focus on a small subset of these.   
 
The debugger offers a series of single-letter commands.  You can find a [http://spatula-city.org/~im14u2c/intv/debugger.txt full summary here].  For this tutorial, we will focus on a small subset of these.   
Line 110: Line 108:
 
The commands are case-insensitive.  That is, "R 100"  (run 100 instructions) is the same as "r 100".  The debugger also offers a short-cut:  Pressing enter alone on a line is the same as "s 1".  That is, it steps a single instruction.
 
The commands are case-insensitive.  That is, "R 100"  (run 100 instructions) is the same as "r 100".  The debugger also offers a short-cut:  Pressing enter alone on a line is the same as "s 1".  That is, it steps a single instruction.
  
== First Steps: Watching Things Happen ==
+
= First Steps: Watching Things Happen =
  
 
Start the debugger using "hello2.rom" from the [[Hello World Tutorial#Example_2:__Fixing_up_the_title_screen|Hello World Tutorial's Example 2]]:
 
Start the debugger using "hello2.rom" from the [[Hello World Tutorial#Example_2:__Fixing_up_the_title_screen|Hello World Tutorial's Example 2]]:
Line 149: Line 147:
 
In this particular code sequence, we can see the [[Executive ROM|EXEC]] begin to initialize itself.  First, it jumps from the start of ROM to the first real bit of code.  Then it sets up the stack pointer, and jumps to yet another subroutine.  Notice how the register values, particularly the program counter (R7), stack pointer (R6) and return address (R5) change values.  Click on the instructions themselves to get a description of what each one does.   
 
In this particular code sequence, we can see the [[Executive ROM|EXEC]] begin to initialize itself.  First, it jumps from the start of ROM to the first real bit of code.  Then it sets up the stack pointer, and jumps to yet another subroutine.  Notice how the register values, particularly the program counter (R7), stack pointer (R6) and return address (R5) change values.  Click on the instructions themselves to get a description of what each one does.   
  
== Running Ahead to Our Code ==
+
= Running Ahead to Our Code =
  
 
At this point, it would be useful to jump ahead to our own program code.  The simulated Intellivision needs to run the EXEC code that leads up our program, but we want the simulation to stop at the first instruction of our code.  We accomplish this with a breakpoint.
 
At this point, it would be useful to jump ahead to our own program code.  The simulated Intellivision needs to run the EXEC code that leads up our program, but we want the simulation to stop at the first instruction of our code.  We accomplish this with a breakpoint.
Line 211: Line 209:
 
</CODE>
 
</CODE>
  
== Pulling It Apart ==
+
= Pulling It Apart:  Disassembly and Dumping =
  
 
Now we can look ahead at the next couple of instructions that will execute.  The "U" command will <B>U</B>nassemble the next several instructions.  "U" by itself starts at the current instruction and outputs the next several:
 
Now we can look ahead at the next couple of instructions that will execute.  The "U" command will <B>U</B>nassemble the next several instructions.  "U" by itself starts at the current instruction and outputs the next several:
Line 261: Line 259:
 
</CODE>
 
</CODE>
 
The format of the memory dump is simple.  At the left is the starting address.  In the middle are hexadecimal values for 8 locations.  At the right is an ASCII representation of the data.  As you can see, starting on the second row at location $5028, the phrase "  Copyright 2007  " is right where we expect it.  The extra '.' characters between letters are due to the fact that memory locations are 16-bits wide, but characters are only 8 bits.  Additional information could be in those other bits.
 
The format of the memory dump is simple.  At the left is the starting address.  In the middle are hexadecimal values for 8 locations.  At the right is an ASCII representation of the data.  As you can see, starting on the second row at location $5028, the phrase "  Copyright 2007  " is right where we expect it.  The extra '.' characters between letters are due to the fact that memory locations are 16-bits wide, but characters are only 8 bits.  Additional information could be in those other bits.
 +
 +
= Wrapping Up =
 +
 +
That concludes the quick tour of the most useful commands in jzIntv's debugger.  You're encouraged at this point to try single-stepping through hello2.rom or other programs to see how the instructions work.  The next tutorial aims at introducing the various families of instructions.

Revision as of 01:34, 8 October 2007

jzIntv offers a simple command-line oriented debugger. It should be familiar in style to anyone who has used the Apple ][ Monitor or DOS's DEBUG.EXE.

Invoking the Debugger

To invoke the debugger, add the "-d" flag to jzIntv's command line. For example, using "hello2.rom" from the Hello World Tutorial:

    jzintv -d hello2.rom

This will invoke jzIntv, and present you with a prompt:

Loading:
  hello2.rom
jzintv:  Initializing Master Component and peripherals...
gfx:  Searching for video modes near 320x200x8 with:
gfx:      Hardware surf, Double buf, Sync blit, Software pal, Windowed
gfx:  Selected:  320x200x8 with:
gfx:      Software surf, Single buf, Sync blit, Hardware pal, Windowed
snd:  buf_size: wanted 2048, got 2048
ay8910:  Automatic sliding-window setting: 10
CP-1610          [0x0000...0x0000]
PSG0 AY8914      [0x01F0...0x01FF]
[Graphics]       [0x0000...0x0000]
[Sound]          [0x0000...0x0000]
Scratch RAM      [0x0100...0x01EF]
System RAM       [0x0200...0x035F]
EXEC ROM         [0x1000...0x1FFF]
Pad Pair 0       [0x01F0...0x01FF]
STIC             [0x0000...0x007F]
STIC             [0x4000...0x403F]
STIC             [0x8000...0x803F]
STIC             [0xC000...0xC03F]
STIC (BTAB)      [0x0200...0x02EF]
STIC (GRAM)      [0x3000...0x3FFF]
[Event]          [0x0000...0x0000]
[Rate Ctrl]      [0x0000...0x0000]
ICart   [R   ]   [0x5000...0x50FF]
CP-1610 Snoop    [0x0200...0x035F]
[Debugger]       [0x0000...0xFFFF]
 0000 0000 0000 0000 0000 0000 0000 1000 -------Q  JSRD R5,$1026             0
>

Most of this output is jzIntv's initialization. The last portion is the debugger prompt.

The Debugger Prompt

 0000 0000 0000 0000 0000 0000 0000 1000 -------Q  JSRD R5,$1026             0
>

The prompt shown above is the debugger's input prompt. From here, you can tell the debugger what to do next. Before each prompt, jzIntv reports specific information about the state of the machine. The following diagram illustrates:

Jzintv debugger status line.png

R0 through R7 are the CPU's 8 registers. Each register is 16 bits wide. jzIntv's debugger shows their values in hexadecimal.

The "flags" field shows what CPU flags are currently set. jzIntv tracks 8 separate flags:

  S   Sign Flag
  C   Carry Flag
  O   Overflow Flag
  Z   Zero Flag
  I   Interrupt Enable Flag
  D   Double Byte Data Flag
  i   Previous instruction was interruptible

When a flag is set, jzIntv shows the letter for that flag. When the flag is clear, jzIntv shows a dash. This makes it easy to see what flags are currently set, without having to remember their exact order. The last flag position is special. It shows the current interrupt status:

  q  Interrupt asserted
  b  BUSRQ asserted
  Q  Interrupt being taken
  B  CPU halted by BUSRQ

In the example above, jzIntv shows the CPU as taking an interrupt. Really, it's coming out of reset, which is similar. Don't worry too much about interrupts for the moment.

Introducing the Registers

The CPU's registers act as a scratch pad, holding values for instructions to operate on. Some registers have special purposes. All the registers can be used for general purpose computation. Here's a quick reference to what each register can be used for.

RegisterGeneral PurposeShift InstructionsIndirect PointerReturn Address
R0 X X   
R1 X X X  
R2 X X X  
R3 X X X  
R4 X  Auto-incrementX
R5 X  Auto-incrementX
R6 X  StackX
R7 *  Program Counter 

R6 and R7 are special. R6 is the stack pointer. R7 is the program counter. The assembler accepts SP and PC as aliases for R6 and R7. You can perform arbitrary arithmetic on either, although performing math on the program counter usually is a bad idea unless you really know what you're doing.

You will want to pay attention to R7 to know where you are at in your program. You can use the listing file, as described in the Hello World tutorial to relate what you wrote to what the debugger shows you.

Debugger Commands

The debugger offers a series of single-letter commands. You can find a full summary here. For this tutorial, we will focus on a small subset of these.

CommandDescription
R <#> Run for <#> cycles. If no argument given, runs "forever"
S <#> Step for <#> cycles. If no argument given, steps "forever"
B <#> set Breakpoint at location <#>
M <A> <B> show Memory at location <A>. Show at least <B> locations.
U <A> <B> Unassemble memory at location <A>. Show at least <B> instrs.
Q Quit jzIntv

The commands are case-insensitive. That is, "R 100" (run 100 instructions) is the same as "r 100". The debugger also offers a short-cut: Pressing enter alone on a line is the same as "s 1". That is, it steps a single instruction.

First Steps: Watching Things Happen

Start the debugger using "hello2.rom" from the Hello World Tutorial's Example 2:

   jzintv -d hello2.rom

Once at the debugger prompt, press [Enter] a couple of times. You should see output similar to the following:

    0000 0000 0000 0000 0000 0000 0000 1000 --------  JSRD R5,$1026             0
   >
   Starting jzIntv...
    RD a=1000 d=0004 CP-1610          (PC = $1000) t=0
    RD a=1001 d=0112 CP-1610          (PC = $1000) t=0
    RD a=1002 d=0026 CP-1610          (PC = $1000) t=0
    0000 0000 0000 0000 0000 1003 0000 1026 --------  MVII #$02f1,R6           12
   >
    RD a=1026 d=02BE CP-1610          (PC = $1026) t=12
    RD a=1027 d=02F1 CP-1610          (PC = $1026) t=12
    0000 0000 0000 0000 0000 1003 02f1 1028 ------i-  JSR  R5,$1a83            21
   >
    RD a=1028 d=0004 CP-1610          (PC = $1028) t=21
    RD a=1029 d=0118 CP-1610          (PC = $1028) t=21
    RD a=102A d=0283 CP-1610          (PC = $1028) t=21
    0000 0000 0000 0000 0000 102B 02f1 1A83 ------i-  PSHR R5                  33
   >
    RD a=1A83 d=0275 CP-1610          (PC = $1A83) t=33
    WR a=02F1 d=102B CP-1610          (PC = $1A83) t=33
    0000 0000 0000 0000 0000 102B 02f2 1A84 --------  PSHR R0                  42
   >

As you can see, jzIntv shows you the current status and the next instruction it will execute at each prompt. As each instruction executes, it also outputs memory transactions as they go by. This allows you to watch what the CPU is reading or writing. The following diagram illustrates how to interpret each of these lines.

Jzintv debugger memory watch.png

These lines show you every read and write the CPU makes. The debugger normally suppresses these unless you step through code as we have above.

In this particular code sequence, we can see the EXEC begin to initialize itself. First, it jumps from the start of ROM to the first real bit of code. Then it sets up the stack pointer, and jumps to yet another subroutine. Notice how the register values, particularly the program counter (R7), stack pointer (R6) and return address (R5) change values. Click on the instructions themselves to get a description of what each one does.

Running Ahead to Our Code

At this point, it would be useful to jump ahead to our own program code. The simulated Intellivision needs to run the EXEC code that leads up our program, but we want the simulation to stop at the first instruction of our code. We accomplish this with a breakpoint.

Take a look at hello2.lst from the Hello World's example 2. Below is just the first portion:

00005000 ROMHDR                     0000500d ZERO                       
00005022 MAIN                       0000500f ONES                       
00005014 TITLE                      0000503d PRINT.FLS                  
0000503b here                       0000503d PRINT                      
0000503e PRINT.LS                   00005048 PRINT.S                    
00005041 PRINT.FLP                  00005042 PRINT.LP                   
00005043 PRINT.P                    00005044 PRINT.R                    
00005053 PRINT.1st                  0000504d PRINT.tloop                
�			        ROMW    16      
 0x5000                         ORG     $5000

			;------------------------------------------------------------------------------
			; EXEC-friendly ROM header.
			;------------------------------------------------------------------------------
5000 000d 0050 		ROMHDR: BIDECLE ZERO            ; MOB picture base   (points to NULL list)
5002 000d 0050 		        BIDECLE ZERO            ; Process table      (points to NULL list)
5004 0022 0050 		        BIDECLE MAIN            ; Program start address
5006 000d 0050 		        BIDECLE ZERO            ; Bkgnd picture base (points to NULL list)
5008 000f 0050 		        BIDECLE ONES            ; GRAM pictures      (points to NULL list)
500a 0014 0050 		        BIDECLE TITLE           ; Cartridge title/date
500c 03c0 		        DECLE   $03C0           ; Flags:  No ECS title, run code after title,
			                                ; ... no clicks
500d 0000 		ZERO:   DECLE   $0000           ; Screen border control
500e 0000 		        DECLE   $0000           ; 0 = color stack, 1 = f/b mode
500f 0001 0001 0001 	ONES:   DECLE   1, 1, 1, 1, 1   ; Color stack initialization
5012 0001 0001 
			;------------------------------------------------------------------------------

5014 006b 0048 0065 	TITLE   DECLE   107, "Hello World!", 0
5017 006c 006c 006f 0020 0057 006f 0072 006c 
501f 0064 0021 0000 

5022 0002 		MAIN    EIS                     ; Enable interrupts

5023 0004 0150 003d 	        CALL    PRINT.FLS
5026 0007 		        DECLE   7               ; 7 is the color number for "white"
5027 02c9 		        DECLE   $200 + 10*20 + 1
5028 0020 0020 0043 	        DECLE   "  Copyright 2007  ", 0
502b 006f 0070 0079 0072 0069 0067 0068 0074 
5033 0020 0032 0030 0030 0037 0020 0020 0000 

503b 0220 0001 		here    B       here            ; Spin forever.

From this, you can see that the first instruction (the "EIS" instruction) is at location $5022. Use the "B" command to set a breakpoint here, and the "R" command to tell jzintv to run. jzIntv will stop when it reaches the breakpoint. The boldface portions below illustrate the commands you should type:

    0000 0000 0000 0000 0000 102B 02f2 1A84 --------  PSHR R0                  42
   > b 5022
   Set breakpoint at $5022
   > r
   Hit breakpoint at $5022
    0000 C0C0 0290 8007 5022 1E87 02f2 5022 ------i-  EIS                   54475
   > 

Pulling It Apart: Disassembly and Dumping

Now we can look ahead at the next couple of instructions that will execute. The "U" command will Unassemble the next several instructions. "U" by itself starts at the current instruction and outputs the next several:

   > u
       $5022:    0002                   EIS
       $5023:    0004 0150 003d         JSR  R5,$503d
       $5026:    0007                   SETC
       $5027:    02c9                   ADD@ R1,R1
       $5028:    0020                   NEGR R0
       $5029:    0020                   NEGR R0
       $502A:    0043                   SWAP R3
       $502B:    006f                   SAR  R3,2
       $502C:    0070                   RRC  R0
       $502D:    0079                   SARC R1
       $502E:    0072                   RRC  R2
       $502F:    0069                   SAR  R1
       $5030:    0067                   SLR  R3,2
       $5031:    0068                   SAR  R0
       $5032:    0074                   RRC  R0,2
       $5033:    0020                   NEGR R0
       $5034:    0032                   GSWD R2
   >

You can clearly see our EIS instruction. Looking back at the listing, the next instruction should be a CALL. The disassembly shows "JSR R5". This is correct: CALL is an alias for JSR R5. What about the rest of this?

If you look in Example 2's source code, you'll see that the CALL was followed by some data:

        CALL    PRINT.FLS
        DECLE   7               ; 7 is the color number for "white"
        DECLE   $200 + 10*20 + 1
        DECLE   "  Copyright 2007  ", 0

jzIntv's debugger doesn't know that there is data after the CALL instruction, so it assumes it's code when it disassembles it. This gives amusing results such as we saw above. We can verify, however, that the data is what we expect it to be by using the "M" command to display a memory dump.

   > m5022
   5020:  0021 0000 0002 0004  0150 003D 0007 02C9   # .........P......
   5028:  0020 0020 0043 006F  0070 0079 0072 0069   # .....C.o.p.y.r.i
   5030:  0067 0068 0074 0020  0032 0030 0030 0037   # .g.h.t...2.0.0.7
   5038:  0020 0020 0000 0220  0001 02A9 02AC 0200   # .............¼..
   5040:  0007 02A9 02AC 02A8  0275 0085 0007 000F   # .....¼.¿.u......
   5048:  0006 0049 0071 0200  0006 0338 0020 004C   # ...I.q.....8...L
   5050:  0048 00C8 0260 02A8  0080 022C 0009 0059   # .H.╚...¿.......Y
   5058:  0028 0061 00C7 00AF  02B7 0000 0000 0000   # ...a.╟.».╖......
   5060:  0000 0000 0000 0000  0000 0000 0000 0000   # ................
   >

The format of the memory dump is simple. At the left is the starting address. In the middle are hexadecimal values for 8 locations. At the right is an ASCII representation of the data. As you can see, starting on the second row at location $5028, the phrase " Copyright 2007 " is right where we expect it. The extra '.' characters between letters are due to the fact that memory locations are 16-bits wide, but characters are only 8 bits. Additional information could be in those other bits.

Wrapping Up

That concludes the quick tour of the most useful commands in jzIntv's debugger. You're encouraged at this point to try single-stepping through hello2.rom or other programs to see how the instructions work. The next tutorial aims at introducing the various families of instructions.