The Virtual ][ Inspector

Copyright © 2006-2021, Gerard Putter

Contents

Introduction
Operation of the Inspector
The Inspector main window
Managing breakpoints
Managing watchpoints
Working with memory banks
Inspecting the disk drives
Using the "instruction trail"
Using the CPU and memory editors

Introduction

The Inspector is a part of Virtual ][ that allows you to closely observe the inner workings of the emulated Apple ][, and is therefore aimed at the technically interested. If you want to use Virtual ][ to run applications or play the old games, you can safely skip reading this chapter. However, if you want to find out what exactly goes on when running a specific Apple ][ application, the Inspector can be a great help. If you are a software developer, you'll feel comfortable with the Inspector, because it basically is a debugger for Apple ][ programs.

More specifically, the Inspector allows you to:

Note that the disassembly feature works for the 6502 and 65C02 procesors only. The Inspector does not support the Z80.

This documentation explains how to use the Inspector. It does not explain the processor instruction set, the Apple I/O addresses and other Apple ][ specific technical details.


Operation of the Inspector

When you start Virtual ][, the Inspector is not active. You must explictly activate it by choosing "Show Inspector" from the "Machine" menu. This opens the main Inspector window. With the Inspector active, the virtual machine can be in one of two states: either it is running or it is in an Inspector "break". While the virtual machine runs, its internal state changes continuously, and the different sections of the Inspector window are blank. However, in break mode the processor is temporarily halted, and the Inspector can be used to look around in the machine.
You can go from running mode to break mode in a number of ways: To go from break mode to running mode you can simply click the "Resume" button.

The Inspector main window

You open the main Inspector window by choosing "Show Inspector" from the "Machine" menu. Initially the window is empty, because the virtual machine is in "running" mode. As soon as the machine goes into Inspector break mode (for example when it encounters a breakpoint, or when you press the "Break" button), the window looks something like this.

The window title contains the document name of the virtual machine; in this case "Untitled". This allows you to distinguish between different Inspectors when you have multiple virtual machines running at the same time. At the very bottom of the window you see a status line indicating the reason we went into break mode: in the example it is because the machine encountered a breakpoint.
When you close the Inspector window the Inspector is no longer active; the virtual machine continues running and ignores any breakpoints and watchpoints. However, all breakpoints and watchpoints are still there, and will become active again when the Inspector window is re-opened.

The buttons

At the top row of the main window are some controls.
Resume Only active in break mode: resumes running the virtual machine.
Step Over Only active in break mode: executes one instruction, then returns to break mode. A JSR (jump to subroutine) instruction is considered one instruction, so it steps over a subroutine.
Step Into Only active in break mode: executes one instruction, then returns to break mode. If the program counter is at a JSR instruction, the first instruction of the subroutine is executed, so it steps into subroutines.
Step Out Only active in break mode: continues execution until the program exits from the current subroutine with an RTS or RTI instruction.
Break Only active in running mode: goes into break mode.
The "cogwheel" menu contains more Inspector functions, and will be described below.

The processor state

The box at the top left of the Inspector main window tells what kind of processor is in use (either 6502 or 65C02) and shows the current values of the registers and flags. The register values are hexadecimal; the flags are shown separately with value 0 or 1. Note it also shows the "unused" flag. Some Apple ][ software is known to actually depend on the value of this flag!

The stack

The stack is shown in a formatted form: the Inspector knows what instruction was used to push each value on the stack, and it shows this to the right of the value. The example shows some entries that were pushed by the JSR instruction, and one from the PHP instruction. Note the Inspector shows the entry as a 16-bit number in the case of a JSR.
Other possible instruction codes used are PHX and PHY (65C02 only), NMI and IRQ (interrupts) and BRK. When no code is shown the byte was pushed with a PHA instruction (the most common case).

The disassembly

The disassembly has a layout very similar to that of the Apple ][ monitor. It takes the current processor into account, so instructions that are only valid on a 65C02 will be disassembled when the current machine actually has a 65C02, but will show up as invalid instructions when the machine has a 6502.
To navigate you can use the scroll bar to quickly scroll through the entire memory space. Alternatively, you can enter an address in the text box and click "Show" (or press return). Clicking the "Current PC" button takes you to the address where the program counter currently is. The instruction to be executed is highlighted.
Each instruction is 1, 2 or 3 bytes long, and the disassembly is only accurate when it starts at instruction boundary. Therefore the smallest vertical scroll distance is not one line, but the starting address of the disassembly. For example, the start of the disassembly might look like this:

The second instruction is invalid, indicated by three question marks. We are looking at executable ROM code, so the start of the disassembly is probably misaligned. Scrolling down a bit corrects the situation:

Usually the disassembly is in sync after the first few instructions (like in the first example), but it sometimes is necessary to use scrolling to get the information you want.
The check box next to each instruction can be used to quickly set or disable a breakpoint. Clicking once sets the breakpoint; clicking again disables the breakpoint (but does not remove it). Removing breakpoints must be done with the "Breakpoints" function in the "cogwheel" menu.

The memory display

Memory is shown in a hexadecimal form with ASCII interpretation. You can navigate by scrolling or by typing an address in the text box and clicking "Show" (or pressing return). In the "Find" field you can enter a hexadecimal byte sequence. Use the "First" and "Next" buttons to find the bytes. When found, the Inspector highlights the bytes.
The Apple II family of computers used a variation on the regular ASCII character encoding with the high bit of characters set to 1. To make recognizing text easier, the "cogwheel" menu contains an option, "Apple II ASCII". If selected, the ASCII part of the memory display ignores the high bit and shows the characters as the Apple II would interpret them. If not selected (the default), the interpretation follows the strict ASCII rules.

Managing breakpoints

An easy way to set breakpoints is by clicking the checkboxes in the disassembly. However, the Inspector contains a separate breakpoint editor that contains more options. You can open the editor by selecting "Breakpoints" from the "cogwheel" menu. The editor has two tabs: one for breakpoints and one for I/O address breaks.

Breakpoints

A breakpoint essentially is the address where the program must stop, but the editor allows you to add two more items: breakpoints can be enabled / disabled with the checkbox, and they can be assigned an optional comment or description for easy identification. To make changes to the list, use the "+" and "-" buttons, or click on a cell to edit its value.
This window also contains checkboxes to activate breaking on a BRK instruction or on an invalid instruction. This can be helpful for a situation where the program counter gets lost in non-code.

I/O address breaks

This list presents all 256 I/O addresses (ranging from $C000 to $C0FF) with a description, adapted to the current machine. For example, the machine shown in the screenshot has a Disk ][ card in slot 6. Therefore the Inspector shows descriptions matching this type of card.
Selecting the checkbox in the "Fetch" column causes the virtual machine to go into break when that I/O address is read (for example with a LDA or BIT instruction). Selecting the checkbox in the "Put" column causes a break when the address is written to (for example with a STA instruction).

Managing Watchpoints

The watchpoints editor can be opened from the "cogwheel" menu.

The virtual machine will enter a break if a watchpoint changes value. At the left of the window you can set processor elements as watchpoints: the registers and the individual flags. At the right you can set memory addresses, qualified as necessary to identify a specific memory bank. As with the breakpoints, you can include an optional comment, for example to clarify the meaning of the memory location.
When defining a new watchpoint the editor chooses the currently active memory bank as a default. Also see Working with memory banks.

Working with memory banks

The Apple II computers have a 16-bit address bus and therefore an addressing range of 64KB. Yet it is possible to access more memory by switching to different memory banks. The disassembly and memory display in the main window initially show the contents of the memory banks currently in use by the virtual machine. The Memory Bank Inspector shows what banks are currently active. It allows you to change this configuration, so you can look at the contents of other banks.

The Apple ][ and ][+ had only one memory bank, with at most 48KB of RAM. An optional "language card" could be installed to extend the memory to 64KB, introducing two banks for the memory range $D000-$DFFF. The memory range $C000-$C0FF is reserved for interfacing with hardware, and the Inspector shows these addresses as value 0.

The Apple //e computer came with two memory banks of 64KB each: main and aux. The Inspector shows main as bank 0, and aux as bank 1. Additional banks can be added, which the Inspector numbers from 2 upward. The large number of possible bank switching configurations leads to the complexity of the Memory Bank Inspector.
To explain all these configuration options would go beyond the scope of this manual; they are well documented in the Apple //e documentation.

Note that any configuration change only applies to what the Inspector displays, not to the state of the virtual machine! So when you resume the virtual machine from its breakpoint, it will continue with the memory banks it actually was using, no matter what you changed in the Inspector.
The button "Virtual Machine Settings" restores the memory banks Inspector to the current configuration of the virtual machine.


The video inspector

Apple II computers offer a number of display modes: text (either 40 or 80 columns), low resolution graphics, high resolution graphics and double resolution graphics. There is also a mixed mode: graphics and 4 lines of text at the bottom of the screen. On top of that, the screen can be generated from two different memory areas (page 1 and page 2).

In order to understand how Apple II software works, it is sometimes useful to examine what the screen would look like in any of the possible display modes. The video inspector offers exactly this.

Choose the menu item "Video" in the "cogwheel" menu to show the video inspector. When the Apple II encounters a breakpoint (or if you hit the "Break" button), you'll see a window similar to the one shown below.

With the buttons on the left you can select any of the possible video configurations. On the right you'll see the resulting screen picture. The button "Use Actual Values" reverts everything to the settings of the virtual machine.

Note that the changed options only affect the video inspector; they have no effect on the state of the virtual machine.


Inspecting the disk drives

The menu item "Disk Drives" in the "cogwheel" menu opens the disk drive inspector.

The window allows selecting one of the connected 5.25" floppy disk drives (the Inspector does not support hard disk inspection). It shows the current track, which is the position of the read / write head, controlled by the stepper motor in the drive. Virtual ][ supports disk drives with up to 40 tracks, and also supports half-tracks. So the current track can go from 0 to 39.5, in 0.5 increments. Note however that DOS and other operating systems use only 35 tracks, and no half tracks.
The hexadecimal display shows the data of the current track (if a disk is inserted). The disk data is stored in an encoded way, usually called "nibbilized". This is quite different from a regular binary representation: for example, the data of a nibbilized sector is 342 bytes long, instead of the 256 bytes the DOS file system works with. At the nibbilized level the track contains special blocks that indicate where the sectors start ("address fields"). If you want to know more about these subjects, try to get hold of the book "Beneath Apple DOS" by Don Worth and Peter Lechner.
The hexadecimal display uses its ASCII column in a special way. Straightforward interpretation of the binary codes would be meaningless, because of the nibbilized encoding. Instead, the Inspector tries to analyze the track and shows the result of the analysis with these character sequences:
AA> The start of an address field. Such a field contains information about the sector that follows. The Inspector interprets this information according to the rules of DOS: the first two characters form the hexadecimal representation of the volume number (FE in the example, which is decimal 254). The next two characters are the track number (11, which is 17 in decimal). Next is the sector number (09 and 0A for the two sectors shown).
DD> The start of a data field. In standard DOS 3.3 encoding this is followed by 342 nibbilized data bytes. Copy-protected disks might use a different scheme.
<EE The end of either an address or a data field.
A byte that is part of a gap. Gaps are needed to separate address and data fields. In the Inspector, a gap shows up as a byte value FF (but not every FF is part of a gap).
The window also shows the codes it determined to indicate the start and end of address and data fields (the prologue and epilogue bytes). Note that in regular DOS only two of the three epilogue bytes are relevant; the third byte is ignored.
The "Current" field indicates where the read / write head is on the track. The byte at that address is the next one to be read or written.
The "Find" field allows searching the data for a specified hexadecimal byte sequence.

Using the "instruction trail"

This powerful feature allows you to examine what instructions were executed prior to reaching a break. This can be useful when examining an endless loop, or try to find why a program branched to a different point than expected. The Inspector is able to remember the most recently executed instructions. The maximum number of instructions remembered is 9999999, which is the equivalent of circa 6 seconds. Because keeping this list requires quite some memory and processing power, the feature is switched off by default. To switch it on, choose "Instruction Trail" from the "cogwheel" menu. This opens a new window; closing the window switches the trail feature off. The window remains empty while the virtual machine is in running mode. In break mode, the window shows the list of most recently executed instructions.

In break mode, you can change the number of instructions the Inspector must remember. The window shows the processor cycle count at the start of each instruction, and the values of the processor registers after the instruction finished excution. The example shows a sequence where the software polls the keyboard (I/O address $C000) in a loop.

The "Save As..." button lets you save the entire trail as a tab-separated text file. For a long trail this can take up to several minutes, and the resulting file can grow to 500 MB or more. While saving you see a progress indicator, and you'll have the option to cancel the operation at any time.


Using the CPU and memory editors

If you want to find out how an Apple II program works (or why it doesn't work) it can be useful to change memory, or change CPU registers, just to see what happens. That way you can skip some instructions by changing the value of the program counter, or force a branch to happen by setting a processor flag.

To accomplish this, the Inspector offers two editor windows: one for memory and one for the 6502/65C02 CPU. You can open them with the "cogwheel" menu.

Using the editors is straightforward. They only work while in break mode. All values are hexadecimal. If you click the "Apply" button, you'll see the new values appear in the Inspector main window.