Debugger in Emu71/Tools/Debugger...
-----------------------------------

This is a short description of the internal assembly debugger of Emu71. The debugger was designed to help customers inspecting assembler code objects.

After starting the debugger the emulation will stop at the current program counter position. The emulation will continue after closing the debugger window. Please remember that the clock now shows the wrong time.


1.) System Menu / Debugger Settings

This is the configuration dialog for the debugger.

In the "Disassembler" section you can switch between HP and Class Mnemonics for the disassembler. This switch is exactly the same like in the main Settings dialog.

In the "Symbolic" section you can setup the debugger for symbolic debugging. Therefore the debugger needs a reference file. The reference file must contain a table for converting an absolute address into a symbolic name. I decided to use the HP-TOOLS object files (file extension *.o) with the supported entries. These object files are usually provided by HP for the HP48 and HP49 series and are necessary for linking object files. In the case of older calculators you have to make them by your own. Using the object file for the linker as reference has two disadvantages. First such an object file has an assembler specific output format, in our case the debugger only understands the output format of HP SASM v3.x. Second, in some cases an entries has two or more different names and I cannot control which name is returned.

Example from ENTRIES.A (HP48 Supported ROM Entry Points)

=UNROT    EQU #60FAC *
=3UNROLL  EQU #60FAC *
=XYZ>ZXY  EQU #60FAC *

My implementation returns the last name inside the linker object file to the chosen address.

If you don't have the linker object file, the HP71B entry point list for example is only distributed as assembler source file HP71EP.A, you can simply generate it with

sasm -N HP71EP.A

- Enable

With an unchecked Enable check box you can disable the symbolic debugging without removing the reference file.

- Model

Each calculator model needs his own symbol reference file. The given models exactly corresponds to the one used in the KML script. When you opening the settings dialog automatically the actual model is chosen in the combo box. You can switch to all other possible calculator models to enter the corresponding reference files. When you exit the dialog with the Ok button all filenames are saved.

- Edit field

The edit field must contain the filename to the model specific symbol reference file which is chosen by the combo box.


2.) Menu Debug

- Run F5

Continue calculator emulation under debugger control. The emulation will stop at a breakpoint. Please remember that the emulation speed is slower than without debugger control.

- Run to Cursor F6

Execute program until address at cursor position is reached. Breakpoints are still active and may stop execution before.

- Step Into F7

Execute one code instruction.

- Step Over F8

Execute a GOSUB, GOSUBL or GOSBVL as one instruction. Normally the instruction cursor will set to the position behind the GOSUB instruction.

But this makes trouble in the following code part:

        GOSUB   +
        NIBASC  /Hello world/
+       C=RSTK

The program counter will never reach the address behind the GOSUB instruction. The debugger solve this problem by breaking the emulation when the stack has the same level before the GOSUB instruction. In this example the single step execution will continue after the C=RSTK instruction.

- Step Out F9

Continue the program until a RTI, RTN, RTNC, RTNCC, RTNNC, RTNSC, RTNSXN, RTNYES instruction is found above the current stack level.

At some code constructions (mostly used to save space on the hardware stack) like

        C=RSTK
        PC=C

and

        C=RSTK
        RSTK=C
        RTN

the stop address will be wrong. The problem in both code fragments is the C=RSTK opcode. In the first example there is no RTN instruction to stop. In the second one the C=RSTK instruction purge the original return address and then the RSTK=C instruction is interpreted as a GOSUB instruction.

In opposite the following code will work fine:

        RSTK=C
        ..
        code            <- F9 was pressed here
        ..
        GOSUB   -
        C=RSTK
        RTN             <- emulation will stop after this instruction
-       RTN

So be careful using the F9 key.

- Break F11

Stops the emulation at the current program counter position.


3.) Menu Breakpoints

- Set Breakpoint F2

Toggle a code breakpoint at the cursor position in the Code window.

- Edit Breakpoints...

You get a sorted list of all current breakpoints. When the breakpoint is checked it's enabled otherwise it's disabled. With "Add" you can add a new or enable an existing breakpoint, with "Delete" you can delete the selected ones. Addresses greater than #FFFFF are cut after the fifths nibble. When adding a new breakpoint, you must select if this is a "Code", "RPL", "Memory Access", "Memory Read" or "Memory Write" breakpoint.

  - "Code" stop before opcode execution on this address
  - "RPL" stop on the first opcode of the selected RPL address
  - "Memory Access" stop before reading or writing to the selected address
  - "Memory Read" stop before reading the selected address
  - "Memory Write" stop before writing to the selected address

With a left mouse button double click on a breakpoint you can toggle the check box inside. When you use the space key instead, on all selected breakpoints the check box is toggled.

- Clear All Breakpoints

Clear all address specific breakpoints.

- NOP3 Code Breakpoints

What are NOP3 code breakpoints? As you know user programs are loaded somewhere in memory and can be moved after a garbage collection. So it's very difficult to break a user program at a hard set breakpoint with F2. To solve this problem the debugger will stop emulation at a NOP3 opcode. So you can easily add a NOP3 command into your sources to force a break condition. To enable this you have to check this item.

NOP3 and NOP3, what's the difference? The Saturn CPU has no NOP command, so NOP3 is an opcode that is three nibbles long and doesn't change a register. In the HP SASM.DOC document two different opcodes are defined for NOP3:

  Opcode 820 for HST=0 0

and

  Opcode 420 for GOC + (next line)

In the assembler of the HPTOOLS 3.x package NOP3 is defined as opcode 820. The advantage of the opcode is that the execution time is always the same, independent from the carry flag. This code is used in the HP48 ROM as well. So I decided to use the GOC opcode for a code breakpoint condition.

A short example how to use a NOP3 Code breakpoint:

ASSEMBLE
  NIBASC /HPHP28-C/

BREAK   MACRO
        CON(3)  #024            NOP3
        ENDM

RPL
CODE
        BREAK                   code breakpoint

        GOSBVL  =SAVPTR         save register

        GOSUB   +               problem for step over
        NIBASC  /Hello world/
+       C=RSTK

        GOVLNG  =GETPTRLOOP
ENDCODE


4.) Menu Interrupts

- Step Over Interrupts

If this item is checked, interrupt handler code will be skipped. This option is useful when you don't want to debug the interrupt handler. But be careful, when you disable the interrupts all code until interrupt enable belong to the interrupt handler code and couldn't executed in single step any more. Enabled breakpoints are still active.

You can also use this option if you want to quit the interrupt handler. Just check this option, press F7 for "Step Into" for stopping the debugger behind the RTI instruction, and uncheck this option again.


5.) Menu Trace

- Settings...

This opens a dialog to configure the trace file output.

The "Log File:" editbox contains the log output file. A filename without the file path save the file in the emulator installation directory.

For "File Mode" select "New" for creating a new empty trace file or select "Append" for appending the new trace output when trace is enabled. If you chosen "Append" and the trace file don't exists, a new trace file is created.

The "Logging" checkboxes "Register" and "Opcode" determine the output content of the trace file.

"Register" enables printing the CPU register content in front of the disassembled opcode. For some, mainly size output reasons the memory mapping is not shown.

"Opcode" enables the opcode output in the disassembled and if necessary in the following lines.

- Enable

If this item is checked, the file trace is enabled. Dependent on the "File Mode" setting, a "New" empty trace file is generated or in "Append" Mode additional data is appended to the current log file. Unchecking this item stops logging and flushing the trace file.


6.) Menu Info

- Last Instructions...

This is a short viewer for the last 255 executed CPU addresses. The disassembled opcode maybe wrong, because only the CPU address of each command was saved and memory mapping may have changed meanwhile. In the "Last Instructions" dialog you can copy selected lines to the clipboard or clear this list.

- Memory Mapping...

This is a viewer showing the current memory mapping of all modules. The window is now non modal and can be open during the debugging session. On module configuration change, the content is updated automatically. Only when you add or remove modules over the main menu item "Edit/Port Configuration...", you should close this dialog before and then after changing the configuration reopen it again.

The tree is separated into "Internal" and "Port" leaves. The leaf "Internal" showing the hard configured modules of the base system the other "Port" leaves the status of the six available daisy chain loops. The standard 16KB of the Port0 memory is always shown as the first four 4K hybrid RAM modules in the "Port0" leaf and cannot removed by the user because the memory is part of the base system.

The module size information in each headline is given in bytes, whereas the chip information below the headline is given in hexadecimal as nibble based address area or as nibble based size.

- Profiler...

This opens a small non modal toolbox window which shows the number of CPU cycles and the corresponding execution time of the instruction sequence between the last two breakpoints. The CPU cycles are close to the real values, so the reported execution time should be near to the execution time of a real machine.


7.) Code window

This windows shows you the disassembled code. The line with the current PC is marked with a "->" between the address and the disassembly.

You can use the UP, PAGE UP, DOWN and PAGE DOWN keys to scroll the window content. There is one strange behavior, when you move to higher addresses the debugger is able to disassemble the next line correctly, but when you move to cursor to lower addresses the debugger does not know if this address is at the begin or inside of an opcode. In result you get wrong disassembled lines.

Context menu pressing the right mouse button:

- Go to address... G

Moves the cursor to the specified code address. Therefore you can enter a hexadecimal number or the symbolic reference name.

- Go to PC

Sets the cursor to the actual position of the PC.

- Set breakpoint F2

Toggle a code breakpoint at the cursor position in the Code window.

- Set PC to selection

Set the PC to the cursor position. Be careful with this command, you change the execution order of the commands!


8.) Register window

Here you can see the actual contents of the CPU registers. The values are only updated at a program execution stop. All changed CPU registers are highlighted.

With the left mouse button you change the content of the register. On bit registers, like CY and Mode, the state change immediately without any request.


9.) Memory window

This windows shows the memory content in the selected context.

You can use the arrow, PAGE UP and PAGE DOWN keys to move the cursor to a memory position, the + and - keys change the memory position by one nibble under the cursor. With a double click on the left mouse button (only in Map mode) you can change the content of the two addresses. When the memory position is read only (ROM or write protected RAM) the content wouldn't change.

Context menu pressing the right mouse button:

- Go to address... G

Moves the cursor to the specified memory address. Therefore you can enter a hexadecimal number or the symbolic reference name.

- Go to PC

Sets the cursor to the actual position of the PC.

- Go to D0

Sets the cursor to the actual position of the D0 register.

- Go to D1

Sets the cursor to the actual position of the D1 register.

- Go to Stack

Sets the cursor to the return address placed in the top level of the stack.

- Follow

Follow is a Pop-up menu to change the address behavior of the memory window. Normally the address of the memory window is static and only change by entering a new address. With Follow the memory window view follow the content of a selected address or register. In follow mode the memory window is only updated after an emulation step.

- Follow none

This is the default mode. The address of the memory window is static.

- Follow Address Content

This is a special mode of indirect addressing. You can specify an address which content will we interpreted as memory pointer. The memory window follow this memory pointer.

- Follow Register PC/D0/D1

The Memory window follow the content of the selected register.

- Find... F

Calls the "Find" dialog box, allowing you to search for a data sequence in hexadecimal or ASCII mode. The search area is selected by the memory view Mapping mode described in the following section. If the data sequence is found the Memory window and an opened "RPL Object Viewer" window will be updated.

With the button "Previous" you can search for the previous and with the button "Next" you can search for the next occurrence of the data sequence.

When you close the "Find" dialog box, you will loose all saved strings in the data combo box.

- Mapping

Mapping is a Pop-up menu to select the memory view of the Memory window. Normally the CPU see only 512KB of the total memory, the rest is banked or covered by other modules. The following menu entries select the memory chip connected with the chosen Chip Select signal of the MMU. The connections are calculator model dependent.

- Mapping Map

This is the default mode. Here the Memory window shows what the CPU see. In this mode you can also change the memory content of writeable memory.

- Mapping ROM

Here the Memory window shows only the content of operating system ROM.

- Load Memory Data...

The "Load Memory Data" dialog box allows loading memory dump files to the specified address inside the Saturn address area. The specified address must point to RAM, writing into ROM areas isn't possible. The memory dump file maybe in packed data format (8-bit), meaning each byte in file contain two Saturn data nibbles with the low nibble containing the even and the high nibble the following odd address or in unpacked data format (4-bit) with 4-bit in each byte.

The Auto mode tries to detect the packed or unpacked data format automatically. This may fail, when the memory dump only contain 00-Bytes, then all upper 4 bit are always zero which is an indicator for an unpacked data format. If you're not sure if the Auto detection will work, use the manual data format selection please.

- Save Memory Data...

The "Save Memory Data" dialog box allows saving the data of the specified Saturn address area into a memory dump file. The memory dump file may contain the data in packed data format (8-bit), meaning each byte in file contain two Saturn data nibbles with the low nibble containing the even and the high nibble the following odd address or in unpacked data format (4-bit) with 4-bit in each byte.


10.) Stack window

The content of the hardware stack is viewed here. In "1:" is the current return address. A double click on an item shows the address content in the Code window.

Context menu pressing the right mouse button:

- Push

Push a new element before the current selection onto the stack.

- Pop

Pop the selected element from the stack.

- Modify

Modifies the stack content of the current selection.


11.) Miscellaneous window

The Miscellaneous window show you the internal state of the interrupt flag and of the 1ms keyboard handler.

You can change the values by pressing the left mouse button over the old content.


10/29/24 (c) by Christoph Gieelink
