Bug99 user manual

This is the user manual for my PC-based disassembler and debugging emulator, which I call Bug99. Since Microsoft does not support its own online help program anymore (Vista specifically blocks it), this file must also serve as online help. When you press F1, Bug99 will open an html file inside your default browser, to provide context-sensitive help. For the moment, all these files simply redirect your browser to the proper position in the present file.

The package can be downloaded from my website at http://www.nouspikel.com/ti99/bug99_1.zip

Bug99 started its career as a remote debugger, it allows to control the TI-99/4A through a parallel cable. A small routine in the TI-99/4A memory communicates with the PC and allows for memory access, program execution, etc. Breakpoints are implemented by replacing a given instruction with an XOP. Unfortunately, the increasing level of paranoia packed in Windows made my task difficult: XP and Vista do not allow a program to control the parallel port directly. In addition, my new laptop does not even have a parallel port. And since I moved to the UK, I don't even have a TI-99/4A to play with.

So Bug99 acquired an internal emulator, and thus became a bifunctional program. The idea is that you debug your TI-99/4A program on your PC, using the emulator. When it works, you transfer it to the TI-99/4A and debug it further (if needed) with the remote function.

Be avare that Bug99 is not a very good emulator for playing games (for one thing, it has no sound). It is mainly intended as a debugger for assembly and GPL programers. It should be used in conjuction with a package allowing you to write assembly or GPL programs on your PC. You could use Wordpad, but I recommend Source Edit as a text editor, and I have configured it to do syntax coloring in assembly and GPL. From within Source Edit, you can launch an external assembler. Eventually, I am planning to write a procedural assembly myself, but so far I am using the excellent package by Jean-Francois Rossel. It contains an assembler, a GPL assembler and the corresponding linkers. Once linked, the program can be loaded into Bug99 with the File --> Load command and debugging can begin.

The main drive behind this program was to help me write DSRs for the exotic hardware that I designed, such as the IDE and USB cards. But you will find that Bug99 also emulates a number of more classical devices. I haven't bothered with emulating a floppy drive, though, since I always work from the Horizon Ramdisk instead.

In my opinion, a good debugger should offer the following functions:

Information windows

These window are opened via the File --> New menu command, and (for some of them) with the File --> Open command. You can open as many windows of each kind as you like, although in most cases you won't need more than one of each.

The Registers window
The GPL window
The Disassembly window
The Breakpoints window
The Memory window
The Watch window
The Trace window
The Hitmap window
The Screen window
The Bus window


Many commands on these menus are also available via accelerator keys (e.g. Ctrl-N for File --> New), or through buttons on the toolbar.

The File menu
The Edit menu
The View menu
The Execute menu
The GPL menu
The Trace menu
The Setup menu
The Window menu
The Help menu

The Toolbar


The program can emulate a variety of devices, as well as control them remotely. Obviously, the amount of control is much greater on emulated devices than on real ones. Control is achieved via a "property sheet" dialog box called by Setup --> Devices, which contains panes for each device.

General CRU interface
Console memory and modifications
Widget and cartridges
32K memory expansion
AMS and derivatives
IDE card
USB-SM card
_USB device controller
RS232 card
Horizon ramdisk
p-code card
German GramKarte
P-Gram card (not implemented)
HSGPL card (not implemented)

Dialog boxes

This is a list of the various dialog boxes that can be called by various functions. They are described together with the associated window or menu item, but I am listing them here so you can quickly jump there for reference.

Load memory
Dump memory
Disassemble memory
Trace setup
Trace filter
CRU map
Monitor setup
Breakpoint setup
Watch variable setup
Memory find
Memory copy
Memory compare


File formats and extensions
Extended address syntax

Legal disclaimer. Read this first.

The contents of this webpage are for educative purposes only. I'm not a professional programmer, and therefore I cannot guarantee that the program described in here will function properly. In fact, it probably won't. It may even cause damage to your computer. And quite possibly set it on fire and burn your house to ashes, sending toxic fumes in the whole neighbourhood. Actually, it may even kill you. So if you know where your interest is, don't install it! Don't even think of it. Furthermore, be aware that using some of the third-party software included with this package (including but not limited to ROM and GROM images) may constitute a copyright violation, an infringement on FCC regulations, a federal crime or whatever it is called in the country you live in. You have been warned! By reading this page further and/or installing the software described herein, you agree on taking whole legal and moral responsability for any disapointment, loss of data, damage, accident, catastrophe, or apocalypse that this package may directly or indirectly cause or favor. And if you were thinking of suing me, forget it. I may have an MD but I'm only a scientist: I don't have any money.

The program detailed below is copyrighted by me, Thierry Nouspikel, till the end of time or 50 years after my death, whichever occurs first. I grant you the right (but not the exclusive rights) to use it, and even to distribute it, as long as you understand that I take no responsability for it. If you distribute it, be sure to include my copyright and a link to the present webpage. Thanks.

Getting started

To install the program, download this zip file and expand it into a dedicated folder on your PC. For copyright reasons, the ROMs and GROMs and any third-party software have been further zipped into password-protected zip files. See the file "passwords.txt" for conditions of use and instructions on how to obtain the passwords. You can use the simulation without these, but you will be restricted in your choice of devices.

To use the remote control function, you will need to build a parallel cable according to the instructions found here. You will then need to transfer a small control routine onto your TI-99/4A. Alternatively, you can type it in: the source file is included in the main zip file under the name Bug99.s. Obviously, you will need a parallel port on your PC and an RS232 card on your TI-99/4A. Because Windows won't let a program access the parallel port directly, you also need a third party DLL, called inpout32.dll, which I have included within the package. As far as I know, it is an open source freeware and it's virus-free, but I cannot wouch for this. To enable remote connections, go to "Setup-->Options-->Remote" and select either the remote TI-99/4A or an external emulator. The internal RS232 simulation is selected by default (so that a faulty inpout32.dll won't prevent the Bug99 from starting) but it's not very useful for you..

To launch the program, double-click on the Bug99.exe file (you may want to create a shortcut for it). If the program crashes on startup, the most likely reason is a corrupted workspace file. Remove the file "workspace.wrk" from the application folder and try again.

The first thing you should do now is to configure your system using "Setup-->CRU map", "Setup-->Options", and some of the tabs in "Setup-->Devices" if so desired. Then save your workspace with "File-->Save workspace". I recommend that you create a dedicated folder for each program that you wishe to debug and save your workspace in it. This way you can have a customized set of options, info windows, etc for each project.

At this point, you will probably want to open a few information windows. Use File-->New (or the corresponding button) to open a Screen window, a Disassembly window, and a Register window. Later on, you may want to add a Memory window and a Watch window. You can arrange them, resize them, and customize some of them to you liking, then save your workspace. Next time you debug this program, just reload the workspace and you'll get the same set of windows at the proper place, with the same options.

What you do next depends on your goals: are you debugging your own program, reverse-engineering a third-party program, or just trying to get a better understanding of the TI-99/4A? To debug your own program, you will generally load it into the simulation with "File-->Load memory" and begin from there. But you may also load a "floppy disk" into your Ramdisk, using "Setup-->Device" and one of the [Import] button on the "Ramdisk" tab. Then start the emulation with "Execute-->Run" and load a program into memory by typing commands on your emulated TI-99/4A. While in Run mode the PC keyboard is assumed to represent the TI-99/4A keyboard. The <Alt> key is the Fctn key, and <Ctrl> is the Ctrl key. In some cases, you can use PC-specific keys, such as <Backspace> or the arrow keys, to emulate key combinations on the TI-99/4A (e.g. Alt+S for the left arrow), but the key combination works as well when typed on the PC keyboard. To stop the emulation and start debugging, just press the <Esc> key.

If you need a cartridge, you can load one into the "Widget" device, using "Setup-->Device" and clicking one (or two) of the [Change] buttons on the "Widget" tab. I am not providing any cartridge dump with this package, always for copyright reasons. If you have your own cartridges, you can easily transfer them from your TI-99/4A to a PC file with the "File-->Dump memory" command.

To walk through the debugged program, use the "Execute" menu, or the "GPL" menu if you are debuggin GPL. See the description of these menus for more details.

You can also set breakpoints, and trace execution. See the corresponding chapters below.

User input

An important point to notice is that all numbers displayed by Bug99 are in hexadecimal notation. Let's face it: you can't be an assembly programmer if you don't master hexadecimal :-) Since hex is the default, the > sign is ommited. There are a few cases in which the program displays a decimal number (such as the size of a disk in sectors), which is indicates by a decimal point following the number.

Similarly, when you enter a number, your input is assumed to be hexadecimal, whether or not you include a leading > sign. If you wish to enter a decimal number, append a decimal point to it (but no zero). You can also prefix a number with - (minus) to enter a negative number, or ~ (tildle) to obtain a bitwise inversion (i.e. ~0000 is FFFF). The value you entered is checked as soon as the cursor leaves the edited field (i.e. when you click or tab out of it). If it does not correspond to a valid number, the old value is restored. There are a few exceptions, in which you are allowed to enter an empty field, or a text string like "Current", but these will be mentioned when applicable.

Bug99 uses two diferent philosophies to handle user input. With information windows, any changes you make must be confirmed by clicking a dedicated button, generally called [Set], to commit the changes to memory. By contrast, when controlling a device from the Setup-->Device property sheet, any change you make become effective immediately.

Have fun!

This section describes the various menu items, in no particular order.

The Execute menu

This menu regroups most of the commands that allow you to control the execution of 9900 assembly language programs.

Executes one instruction, then updates all the information windows. Breakpoints are ignored by this command.

On the remote system, this command is implemented by copying the current instruction inside the small routine that enslaves the TI-99/4A to the PC. This way, once the instruction is executed, control is immediately returned to the PC. Obviously, this will not work with instructions that change the return point. The following instructions are thus emulated: all jumps, B, BL, BLWP, RTWP, XOP, X, LIMI, and illegal opcodes. Note that interrupts are always disabled in this mode.

Continuous execution, updating the information windows after each instruction. Execution will stop if a breakpoint fires, if you press the <Esc> key, or click the left mouse button inside a window. Note that GPL breakpoints are ignored when executing assembly. The command also introduces a delay between instructions, so you can follow the information presented. The speed is preset at 3 instructions per second, but you can change this in Setup --> Options-->Execute.

With the remote system, Walk is implemented by just repetedly calling Step. If you enable both the remote TI-99/4A and the simulation, execution will stop as soon as one of these systems hits a breakpoint (or when you hit <Esc>). Since execution is coordinated, both systems should be in the same state, (assuming they started in the same state, of course). However, execution on the remote system happens before the simulation, so if a PC breakpoint is hit in the simulation you will find that the remote system is one instruction ahead.

Continuous execution at maximum speed. The TI-99/4A sceeen image is the only window updated during execution, and then again only 10 times per second. All information windows will be updated when execution stops (either because a breakpoint fired, because you hit <Esc> or clicked inside the application). If you check the appropriate box in Setup --> Options, execution will slow down to match the speed of a real TI-99/4A (assuming your PC is fast enough to allow it). For maximum execution speed, uncheck that box.

With the remote system, this command does not call Step. Instead it returns full control to the TI-99/4A. The PC can only regain control if a breakpoint is hit, or if you manually re-enter the enslavement routine. Note that this is the only mode in which interrupts are active on the remote system.

If you enable both the remote TI-99/4A and the simulation, control will only be returned to you once both systems hit a breakpoint. If you stop the simulation by hitting <Esc> the program keeps running on the remote system. The only way to regain control on the remote system if your program fails to hit a breakpoint is to reset the console and reload the enslavement routine (and your program)..

This is a combination of the walk and run commands. It runs continuousely, and updates all the information windows after a given number of instructions. This number can be set in the Setup --> Options menu.

With the remote system, this command repeatedly calls Step. Since remote execution takes much longer than emulation, the number of instructions between window refreshes should be set at a much lower value than that used in simulation-only mode. If both modes are on, the value for the remote system will be used, since it is the limiting step. Just like with Walk, execution proceeds in parallel in the two systems, so they should stop after the same number of instructions.

Jumps over the current instruction without executing it.

Allows to simulate a reset. There are several possible types of reset:

Reset  > Soft is the software reset, equivalent to BLWP @>0000. This is the only type of reset that works with a remotely-controlled TI-99/4A.

Reset  > Console emulates a hardware reset, such as what happens when powering up the console, inserting a cartridge, or pressing the reset button on a Widget. The hardware signal resets several processors in the console and causes a software reset. It is also passed to the PE-Box, typically resetting all CRU bits to '0'. By contrast, RAM in PE-box card is not affected.

Reset  > PE-box is equivalent to turning the PE-Box off, then on. It will wipe out RAM in PE-box cards (which is not emulated), but will not affect the console, and thus won't produce a software reset.

Reset  > Both combines the above. It is equivalent to a full system shut off and power up.

Allows to trigger interrupts and control the interrupt service routine (ISR).
The top three items report the status of the three types of interrupt to the TMS9901 processor: VPD interrupts, interrupts for peripheral cards, and interrupts from the timer internal to the TMS9901. By clicking these items you can set or remove a given interrupt request. If any of the three is set, the ISR will be entered next time interrupts are allowed (i.e. after a LIMI 2).
With a remote system, you can view the current status of these interrupts, but you cannot modify it.

Interrupt > Call ISR is used to enter the ISR immediately, regardless of the value of the interrupt mask, or of any pending interrupts. Since interrupts are hard-wired at level 1 on the TI-99/4A, this command is equivalent to BLWP @>0004. Normally, this lands you at >0900, with a workspace at >83C0 (which the ISR immediately changes to >83E0).

Interrupt > Exit ISR is used to exit the interrupt service routine before it is finished, and resume execution where it was when the interrupt occured. It works by loading the workspace found at address >0004 (normally >83C0) and performing a RTWP.

Interrupt > Trigger NMI emulates the non-maskable interrupt caused by bringing the LOAD* line down. This requires a special hardware device, such as the WiPo mouse, or Jeff Brown's interrupt mod. The command performs a BLWP @>FFFC. To return from the NMI, just do a RTWP.

Only works when the current instruction is a jump: it forces the jump to be taken, whether the requested condition is true or not. To do the opposite (i.e. not to take the jump), use Skip.

B *R11
Emulates a B *R11, also known as RT. This allows you to return from a subroutine called with BL without waiting for it to finish.

Allows you to abort and return from a subroutine called with RTWP.

Calls a dialog box asking where you want to go, and how. In the dialog you can select the type of branching operation: B @ is a simple branch, BL @ saves the return point in R11, BLWP @ performs a context switch (i.e. changes the workspace and saves return parameters in the new R13-R15). You enter the address you want to branch to in the nearby edit box, in hexadecima format. In the case of a BLWP, this will be the address containing the branch vectors, i.e. new workspace and new address.
Optionally, you can specify in which device the memory address you want to branch to is located, and in which page if the device supports multiple pages.

This toggle item allows you to globally disregard breakpoints. When the item is checked, breakpoints work normally, when it is unchecked breakpoints are ignored: they won't fire and they won't be traced. The only exception is when using "Run" on the remote system: these breakpoints fire no matter what (but that's for "Run" only, "Jog" and "Walk" allow you to disregard remote breakpoints).

The GPL menu

This menu regroups commands that allow you to debug GPL programs. At the moment, this only works with the emulator, not with a remote TI-99/4A.

Step, Walk, Jog, Run, and Skip work pretty much like they do with assembly. Refer to the Execute menu for details. The main difference is that GPL breakpoints are active with these commands, whereas PC breakpoints are ignored.

Note that, after executing a GPL instruction, the PC will always be >0078 (in standard mode) or >04E6 (in FMT mode). If the current value of PC is different from that one, pressing Step will only trigger execution until PC reaches one of these two values, which may not correspond to a complete GPL instruction. For instance, if a memory breakpoint fired during a GPL instruction, selecting Step will complete execution of this instruction, rather than moving to the next one.

Emulates a Return with Condition GPL command. The return address is fetched from the GPL subroutine stack, and the GPL status bits are not cleared.

If the current instruction is a BR, BS, or B, the command takes the branch regardless of the value of the Eq status bit. To do the opposite (i.e. not to take the branch), use Skip.

Controls the Format sub-interpreter of the GPL interpreter. As you know, a given byte can correspond to two commands in GPL: one in normal mode, and one in format  mode. The normal opcode FMT enters format mode, which mostly comprises screen access command. The format opcode FEND is used to leave format mode. An additional complication is that the byte encoding FEND (>FB) is also understood as a LOOP instruction in format mode. The emulator keeps track of the number of loops opened (with format RPTB commands) and, upon meeting a >FB byte, decides whether it must close a loop or (if there are no more loops) exit format mode.

FMT > On emulates a FMT opcode, thereby entering the FMT subinterpreter.
FMT > Off emulates a FEND opcode and leaves FMT mode regardless of the number of RPTB loops currently opened.
FMT > Loop++ increments the loop counter. It means that one more >FB byte will be interpreted as a LOOP command rather than a FEND.
FMT > Loop-- decrement the loop counter.

The Trace menu

Tracing is an important tool when debugging. Rather than stepping though your program waiting for it to crash, you just launch it in Run mode with tracing enabled. When it crashes, you can inspect the trace log to find out what happened. Assuming you saved enough information, you can even backtrack your program upto the point when the crash occured.

Bug99 handles two distinct trace logs, one is intended to trace program execution, the other to trace any manual changes you make (e.g. patching your program). This being said, you are free to choose what you want to save in each log. The logs are saved as .txt files (Trace.txt and Patch.txt), so you can open them in an external viewer (although not while Bug99 is running). But it is probably more convenient for you to use the trace viewing window; it offers events coloring, filtering, etc.

This is a toggle item. When ticked, tracing is on for program execution. Assuming you have used Trace -->  Setup to select events to trace, these will be saved into the log file called "Trace.txt".

This toggles tracing into the Patches.txt file. Both log files can then be viewed within a Trace Window.

This toggles counting of memory access and CRU operations by the simulation. The counters are internal to Bug99, but their values can be displayed with the Hitmap Window.

Lets you select what you want to trace, and how much detail to include.
Setup > Execution... controls what will be saved in the Trace.txt file
Setup > Patches... controls what will be saved in the Patches.txt file
Both items call up a dialog box that lets you decide which events should be traced and how much information should be included in the log file. See below for details.

Empties a given log file.
Wipe > Execution clears the Trace.txt file.
Wipe > Patches clears the Patches.txt file.
Wipe > Hits clears the internal hit counters for all memory types (and CRU).

The next items are only active when viewing a trace log with the trace window. To do so, open a trace window from the file menu, with File --> New. You will be presented with a dialog box that lets you decide which of the traced events you wish to display (obviously, events that were not traced cannot be displayed, but the dialog doesn't know what was traced). You can also set the color of each type of event: just click on the colored button and pick a new color. Do not forget to indicate whether you want to display the Execution log, or the Patch log: this is done with the drop list at the top of the dialog..

Upon clicking [OK], the selected trace log will be displayed. You can affect the events being displayed with the trace menu.

Calls the filter/color setup dialog again. When you click [OK], the display is cleared and the log file is re-loaded according to your specifications.

Call the filter setup dialog, but only applies to the currently selected section. This way, you can display previously hidden details, or on the contrary hide useless information from a subsection of the log.

Display all the information between the current item (the one the cursor is on) and the next visible item. If several items are selected, all details within the selection and upto the next visible item will be displayed.

Within the selected section, revert to the initial settings of the filter, as specified when opening the window or reloading the log. You will find that this option works better if you selected only a few event types unpon loading. This way, you can reveal the other with Expand and hide them with Collapse.

For more information, see the chapter on the Trace window, below.

The Trace mode setup dialog box

This dialog box is called up by the Trace-->Setup-->Execution and Trace-->Setup-->Patches menu items. It lets you define which type of information you wish to log, and how much detail should be included. The dialog box is similar for both log files, except that the Patch log won't allow you to trace the PC, nor some types of breakpoints (those breaking on PC, cpu memory access, and assembly opcodes). This was done to speed up execution: double tracing these items is far too time-consuming. If you need to trace them, use Execution tracing.

The various events that can be traced are arranged in sets of two lines, each comprising several checkboxes. The first line lets you decide whether or not an event should be trace. The second line governs the amount of detail to be included with the traced event. A minimum amount of info is always saved (generally the address), but you can add more. For instance, when tracing memory access, only the memory address is logged. But you have the option to include the PC of the instruction that caused that memory access, or the contents of the memory address (and its previous content, in the case of a memory write).

You can trace the following events::

Tip: To trace subroutine calls and return, set opcode-type breakpoints for BL, BLWP, RTWP (no type, no value) and B *Rx (no value). Make them inactive, and trace inactive breakpoints including the PC in the log. The Trace window will then present you with a kind of "call stack", which I find quite useful to figure out what a program has been doing.

The Setup menu

This menu provides commands dealing with the setup of Bug99, or of the various emulated devices.

This cascading menu item gives you the choice between two systems to debug from: the internal simulation of a TI-99/4A, or a real TI-99/4A controlled via a parallel cable. Tick the one you wish to use.
You may tick both, in which case you will be writing to both systems, but reading from the remote TI-99/4A only. This may be useful, because the simulation lets you see more things than a real system.
If you untick both, you will not be writing to any device, but you're still reading from the simulation (since a no-read no-write state didn't appear very useful to me).

Some of the information windows (Memory, Disassembly and to some extent Watch window) allow you to bypass this setting and to access a system other than the one selected here.

CRU map
This item call a dialog box in which you enter the CRU addresses of the devices you wish to use in the simulation, and of those that are present in your remotely controlled system. On the right, you should enter the CRU address of the bits controlling any modification to the console, such as overclocking, suppresion of wait states, adding EEPROMs on top of the console ROMs, or triggering non-maskable interrupts together with regular ones.

This item calls a modeless dialog box, one that you can leave open while working with the rest of the application. It contains a property sheet with several pages that control the various devices that are emulated or remotely controlled. See below for details.

This item call a modal dialog box (i.e. one that you must close to continue working), containing a property sheet with several pages of options, which are detailed below.

The Options dialog box

This dialog box lets you customize various options. These are grouped by topics, and can be selected by clicking the appropriate pane. Unless otherwise stated, changes only become active when the dialog is dismissed with [OK]. Clicking [Cancel] instead discards any changes.


Controls the appearance of the Disassembly Window in 9900 mode and also affects bulk disassembly via the File-->Disassemble memory menu command.

Use R for register: decides whether workspace registers are listed as R0 through R15 or as 0 through 15. The TI assembly allows both, but no R is the default.
Use NOP. Replace JMP to the next instruction with NOP (no operation).
Use RT. Replace B *R11 with RT (return).
Use $-relative jumps. Decides whether the destination of jump instructions should appear as an address (e.g. JMP >A008) or as a PC-relative offset (e.g. JMP $+6, where $ represents the current instruction).
Prefix addresses with A. Decides whether addresses in left column and in instructions should appear as numbers (e.g. >2000 B @>3000) or as labels (e.g. A2000 B @A3000). The "A" prefix comes handy when saving the listing to a text file, as it can be assembled immediately, with all addresses understood as labels.


Controls the appearance of the Disassembly Window in GPL mode.

Use extended GPL. Decides whether non-standard GPL opcodes will be listed as BYTE instruction, or as XGPL instructions (the later may imply decoding extra bytes for arguments).
Always use G@ notation. Decides whether arguments representing a GROM address should be listed as G@address or as @address. This does not apply to the MOVE instruction that always uses G@ for GROM addresses since cpu addresses are also possible with MOVE.
Prefix addresses with G. Decides whether addresses in left column and in instructions should appear as numbers (e.g. >6000 BR G@>7000) or as labels (e.g. G6000 B G@G7000). The "G" prefix allows addresses to be interpreted as labels by a GPL assembler.


Controls the various execution modes. You can choose:

The number of instructions per second while in Walk mode.
The number of instructions executed before the information windows are refreshed in Jog mode. There can be different value for the simulation and the remote system, as Jog mode is much slower on the remote system.
Whether Run mode should be as fast as possible, or reined in to match the speed of a real TI-99/4A (taking clock modifications into account, if any). This assumes that your PC is fast enough to actually achieve real speed...
Display statistics when done. Decides whether you want to see a message box when leaving Walk, Jog or Run mode. The message box states the cause for interruption (breakpoint or manual), the number of instructions executed, the time it took, and the time it would have taken on a TI-99/4A. It must be dismissed by clicking [OK] which can be a pain, hence the option to suppress it.
Report conflicts on Read. Causes a message box to pop up when a memory read operation, or a CRU input operation is answered by two devices. The message box must be dismissed by clicking [OK].
Report conflicts on Write. Same as above for memory writes and CRU output. This is not really a conflict since it's often ok for more than one device to latch the same data, but you may want to know about it...


Decides which type of information will be loaded when loading a new workspace with the File --> Open Workspace menu item. Note that all types of info are loaded from the default workspace (Workspace.wrk) upon starting the program.

Load recently used files. This is the list of files appearing in the dialog boxes used with File --> Load memory and File --> Dump memory.
Load options. Load the list of options, i.e. the contents of the present dialog box.
Load windows position. Closes any existing information window and open new ones according to the information found in the workspace file. Only the position and the appearance of a window is loaded, not its contents (as this is not part of the workspace file). However, if a window has been saved on a file, the file name is saved in the workspace. When opening a new worskpace, the program will try to locate that file and load it into the window. This does not happen if a window was not saved on a file, or if the file does not have a standard extension (such as .cmt, .rgs, .bps, etc).
Load device setup. Loads all device characteristics listed in the Setup --> Device dialog.
Load trace setup. Loads the events to trace and list, as provided in the dialog boxes called by Trace-->Setup-->Execution and Trace-->Setup-->Patches. Does not load the file size, nor the on/off tracing toggles.


Decides what to save when you exit the application.

Save memory on exit. This refers to simulated TI-99/4A memory, not to the remote system. Your choices are to save nothing, to save EEPROMs and non-volatile RAMs, to save ROMs, EEPROMs and nvRAMs, or to save all memory types. The memory type is taken from the Setup-->Devices dialog. A given memory will only be saved if it was modified since the program started, or since the last time you saved it (by clicking the [Change] button in the Setup-->Device dialog).
Save workspace on exit. Your options are "Always", "Never" and "Ask me". The later will call a message box when you close the program and ask whether you want to save the current workspace or not.

The same pane also lets you decide the location of the two folders used by the application.
The simulation folder is the one containing all the ROM and EEPROM files used by the application. Normally, you will not change it.
The working folder should contain the program you are debugging and its specific workspace. This is the default folder used by File-->Save, File-->Open, File-->Load memory, File-->Dump memory, File-->Disassemble memory, File-->Open workspace, and File-->Save workspace.
Locked. Decides whether the working folder should change if you select a different folder in any of the File operations listed above. If you check the "locked" box, the only way to change the working folder is from the Setup-->Options dialog: just click the [Change] button and pick a new folder.


Sets up the parameters to connect with a remote TI-99/4A, whether real or emulated.

Contact. Bug99 offers you three options for remote debugging:
1) To communicate with a real TI-99/4A connected to the PC parallel port via a PIO cable. Note that this relies on the existence of a third-party DLL, inpout32.dll, which is included in the Bug99.zip package.
2) To communicate with a third-party emulator which simulates the TI-99/4A. Of course, this is only possible if the author of said emulator has made provisions to support Bug99.
3) To communicate with Bug99 internal simulation, via the simulated RS232 card. This is not very useful from your point of view, since you can do much more by accessing the simulation directly (with Setup-->System-->Simulation). I implemented this option for debugging purposes, and I figured I may as well make it available. Since this is the only option that does not rely on third-party programs, I made it the default upon power-up (so a faulty third party program won't prevent Bug99 from booting). If you want to perform remote debugging, you must change this field to one of the other options every time you launch Bug99.

PC port used. You can choose between 4 parallel ports, LPT1 through LPT4, assuming your PC has that many. This is only relevant when connecting to a real TI-99/4A.
Handshake in. Selects the PC parallel port pin connected to the "Handshake in" line of the TI-99/4A PIO port. This is determined when you build your parallel cable, the default is "Init" if you followed the instructions on my website.
Handshake out. Selects the PC parallel port pin connected to the "Handshake out" line of the PIO port. The default is "Ack*".
Timeout value. Determines how long the PC should wait for an answer from the TI-99/4A. Zero means "wait forever".
[Test] This button attempts to talk to the TI-99/4A and annouces the results. To do so, it must first accept any change you may have introduced in the above fields (thus, you cannot cancel these changes by pressing [Cancel] any longer).  For this to work properly, the "enslavement" routine should be running on the TI-99/4A, otherwise you will get an "Operation failed" message. The same error message may also be caused by you picking wrong values for Handshake in or Handshake out. If the test succeeds, the current position of the hook routine and its workspace will be obtained from the TI-99/4A and displayed below.

XOP used for breakpoints. Remote breakpoints are implemented by replacing the target instruction with a XOP instruction. In principle, there are 15 XOP instructions available to the TMS9000, but in practice the console ROMs only contain meaningful vectors for XOP1 and XOP2 (and then again, some consoles do not implement XOP2). Bug99 will read the proper vector from the console ROM and use them even if they make no sense, but for all practical purposes, you should use XOP1.
Hook routine location. Determines the location of the short 9900 assembly routine that handles communications and enslaves the TI-99/4A to the PC. This is preset when loading it on the TI-99/4A, but the routine can run anywhere. If you change the address in this dialog box and click [Move it], the routine will be moved to the specified location in the TI-99/4A.
Hook routine workspace. Specifies the location of the workspace registers used by the enslavement routine. This is determined when assembling and loading the routine on the TI-99/4A, but you can change it with this dialog and the new workspace will be used as soon as you click [Move it].
[Move it] This button uses the new values you entered in the above two fields and adjusts the position and/or the workspace of the enslavement routine accordingly. This commits any change you may have done to the above 3 fields, which means you cannot discard them by dismissing the dialog with [Cancel] (but you can retype the old values).

The File menu

This is how you create a new information window. The commands brings up a dialog box with a list of available window types. Just select the one you want. In most cases you only need one of each, but you are free to open more than one if you wish.

Save as...
With these commands you can save the contents of an information window. They bring up a dialog box in which you can select the file name and extension.
Most windows can be saved to a text file, which you can then open in a word processor (or with Wordpad) to edit, format or print it. The exceptions are the Screen window which is saved as a bitmap file, and the Trace window which is saved in RTF format.

Some windows can also be saved in an internal format that can then be re-loaded with the File-->Open command. See individual window descriptions for details.

This command is only available for windows that can  be saved in an internal format. It allows you to re-load the contents of a previously saved window. In some cases (e.g. the Register window), this contents is just displayed and will only be commited to memory when you press [Set]. See individual window descriptions for details.

This closes a given information window.

Open Workspace...
This lets you pick a new workspace, among previously saved ones. You can select which type of workspace information should be loaded by selecting the "Workspace" tab in the dialog box called by Setup-->Options.
When the program starts, it automatically loads the default workspace from a file called "workspace.wrk" located in the simulation folder. I suggest that you save a workspace file in the folder of the application you are debugging and load it when you start working on that program again. This way you can save customize your workspace differently for each program you are debugging (e.g. do you want GPL or assembly, etc). While you are at it, you should also use the "Save" pane of Setup-->Option to change the working folder (which is the simulation folder by default) to the one the program you are debugging is in.

Save Workspace as...
This lets you save the current workspace on a .wrk file, so you can reload it later. All types of workspace information are saved on file every time, but you can ignore some when loading (see above).
Your current workspace may be saved automatically when exiting Bug99, depending on what you selected in the "Save" pane of the dialog box called by Setup-->Options.

Load memory...
This is used to load a file into the TI-99/4A memory, whether on the remote system or in the simulation. The command brings up a dialog box that lets you pick up a file and set the loading parameters. Nothing is loaded until you click the [Load] button. See below for details.

Dump memory...
This commmand allows you to save chunks of the TI-99/4A memory (remote or simulated) into a file. The command brings up a dialog box that lets you select the memory to be saved, the file name, the file format, and optional additional parameters (depending on the format). See below for details.

Disassemble memory...
This command lets you disassemble a large chunk of memory (assembly or GPL), saving the listing to a text file. The command brings up a dedicated dialog box that lets you pick the file name and set various disassembly options. See below for details.

Print preview
Print setup...
Printing is not implemented in the current version. If you wish to print something, save it to a text file and print the file from a text editor.

Most recently used files
This the files that were recently used with the File-->Save and File-->Open command, i.e. corresponding to information windows. The list does not comprise memory files used by File-->Dump, File-->Load, and File-->Disassemble.

Leaves the program. Modified memories may be saved on file before exiting, depending on the options you selected with Setup-->Options-->Save. Similarly, your workspace may be saved automatically or not.

The Load Memory dialog box

This dialog box is called up by the File-->Load command and is used to load a file into the TI-99/4A memory, either on the remote machine or on the simulation. It contains the following fields:

Filename: The file to be loaded (if any). To pick a new file, just click the [Browse] button. Alternatively, to reload a previously loaded file, select it from the drop list. The file extension determines its format, and may cause some of the other fields to be filled in automatically.

Format: The format of the file to be used. This is filled automatically if you pick a file with a known extension (.bin, .ea5, .gk or .fb6). Otherwise, you'll have to select it manually.
Currently available formats are:

Memory: The type of memory to load into, cpu, GROM/GRAM, or VDP memory.
Device: The device to load into (optional).
Page: For paged devices, the page to use. For GROM/GRAM memory, the GROM base to use.

Flags: First word of the header (not applicable for .bin files).
Next file: This lets you decide what to do if the continuation flag indicates that another file should follow the current one. Your choices are: load = go on with the next file, stop = load this file only, ask = fill the dialog with the parameters of the next file but don't load it yet (i.e. you'll need to click [Load] after each file).
Address: The address where to load the contents of the file.
Place into PC: For assembly language only, place the address at which the first file is loaded into the program counter (PC). This will allow you to start execution right away. Be aware that a real EA5 loaded also prepares VDP memory prior to running a file, so you may have to simulate this by pre-loading another file into the VDP memory.
Size: The number of bytes to load.

Click [Cancel] to abort, or [Load] to proceed. If there are more files to load, and you set the "Next file" field to "Ask", you will need to click [Load] again for each file.

The Dump Memory dialog box

This dialog box is called up by the File-->Dump command and is used to save a portion of the TI-99/4A memory into a file, either from the remote machine or from the simulation. It contains the following fields:

Filename: The file to be loaded. To pick a new file, just click the [Browse] button. Alternatively, to overwrite a previously saved file, select it from the drop list. The file extension determines its format, and may cause some of the other fields to be filled in automatically (provided the file already exists).

Format: The format of the file to be used. This is filled automatically if you pick a file with a known extension (.bin, .ea5, .gk or .fb6). Otherwise, you'll have to select it manually. Formats are the same as in the File Load dialog.

Memory: The type of memory to load into, cpu, GROM/GRAM, or VDP memory.
Device: The device to load into (optional).
Page: For paged devices, the page to use. For GROM/GRAM memory, the GROM base to use.

Flags: First word of the header (not applicable for .bin files). Leave this field blank if you want the program to coin the flags automatically.
More files to come: This is only used if you left the "Flags" field blank. Tick the box if you are planning to add more files, untick it for the last file. (If you are planning to save one big chunk of memory into contiguous files, you may use "Split after" instead).
Address: The memory address where to start saving from.
Size: The number of bytes to save.
Spit after: The maximum number of bytes that can fit into one file. If Size is greater than this number, additional file(s) will be created. In the latter case, the program overrides the "Flags" and "More files to come" settings and forces the presence of the continuation flag.

Click [Cancel] to abort, or [Load] to proceed. If there are more files to load, and you set the Next file field to "Ask", you will need to click [Load] again for each file.

The Disassembly Memory dialog box

This dialog box is call be the File-->Dissassemble command, and is used to disassemble 9900 code or GPL directly to a file (which is more convenient than saving the Disassembly window). It contain the following fields:

Filename. The file into which to write the disassembly. This should be a text file, but it does not necessarily need to have a .txt extension; it could be .asm, .gpl, .s or anything you use for your source files. To pick a new file, click the [Browse] button. Alternatively, you can select a previously used file from the drop list.

Append if exists. If this box is checked and the file you selected already exists, anything you disassemble will be appended to the end of the file. If the box is not checked, an existing file will be erased before use.

The next series of fields let you select the memory that you wish to disassemble.
Memory type could be cpu, grom or VDP, no matter what you assembly language you are using. This way, you can disassemble 9900 assembly language embeded within a GROM (and meant to be copied to cpu memory for execution), GPL stored in VDP memory, etc.
From is the address where to start from.
To is the address where to stop, an instruction beginning at this address will not be included in the listing.
You can also select the system (remote TI-99/4A or simulation), and optionally select a specific device and a page number (or a GROM base, if you selected GROM memory).

Format: This drop list lets you select the language you want to disassemble into. You can choose between 9900 assembly and GPL. You can also elect to disassemble a chunk of memory as a list of DATA statements, BYTE statements, or TEXT statements. Note that in the case of TEXT, any non-printable character (i.e. ascii code lower than 32 or higher than 127) will be replaced with character 127.

Start with Fmt on. This checkbox only appears if you selected the GPL format. It lets you decide whether the Fmt sub-interpreter is on or off at the start of the disassembly. Subsequently, the subinterpreter will be automatically turned on by FMT statements and off by FEND statements.

Words per line, bytes per line, characters per line: This field only appears if you selected DATA, BYTE, or TEXT as the format. It lets you specify how many words of data will follow a DATA statement before a new line is issued. Similarly, you can select the number of data bytes following a BYTE statement, or the number of characters to be included in a TEXT string.

The options you selected for the Disassembly windows in Setup-->Options-->Disassembly also apply to this bulk disassembly. In addition, you have a few more specifc options that are only relevant to disassembling on file:

Include comments. If you have entered comments via a Disassembly window, you have the option to include them in the disassembled listing by checking this box. Comments are not included with DATA, BYTE or TEXT formats.

Include hex values. This is only relevant to assembly and GPL format. It causes the listing to include a list of the hexadecimal words/bytes that make up an instruction. These values will appear after the instruction, but before comments.

Use names of watched addresses. If you have any named variable or addresses listed in a Watched window, with the "Use in disassembly" property selected, checking this box will cause them to be used in the disassembled listing in place of their hexadecimal values.

Generate names for labels. This causes the program to go over your disassembled section twice. On the first pass, it makes a note of any address the program is refering to and creates a dummy name for it. Cpu memory addresses are named Axxxx where xxxx is the hexadecimal value of the address. Similarly, GROM addresses are named Gxxxx, and VDP addresses Vxxxx. On the second pass, when the listing is generated, these temporary labels are used any time the corresponding address appears in the listing. They also appear as labels in the left margin if an instruction happens to begin at such an address. Note that if you select both this option and the "watched addresses" option, the name of a watched variable is prefered over a temporary one.

File formats and extensions

The following file extensions are used by Bug99:

.bin   A simple binary dump of a chunk of memory
.ea5  A binary dump of cpu memory, preceeded with a 6-byte header compatible with the Editor/Assembler cartridge, option 5.
.gk   A binary dump of GROM or cpu memory, preceeded with a 6-byte header compatible with the Gram Kracker.
.fb6  A binary dump of any type of memory, preceeded with a 6-byte "ea5" header, followed with a varible-size trailer containing extra info. See this page for details.

.dsk  A sector dump (in numerical order) from a floppy disk. Does not contain track info (i.e. not compatible with PC99 format).
.hdd  Ditto, but meant for hard drives. Sector size is generally 512 bytes, and there can be many more sectors, but basically, it's the same format as .dsk.
.smc   A sector dump of a SmartMedia card, in numerical order, preceeded with a 16-byte header. Sectors can be 256 or 512 bytes, and each sector is followed with 8 or 16 bytes of special info.

.rgs  An internal format allowing to save and reload the contents of the Registers window.
.cmt   A text file allowing to save and reload the comments entered manually in Disassembly windows.
.bps  An internal format allowing to save a list of breakpoints and to reload it into a Breakpoints window.
.wtc  An internal format allowing to save a list of watched variables and to reload it into a Watch window.
.gpr  An internal format allowing to save and reload the contents of the GPL registers window.
.rtf  A standard rich-text format file allowing to save the contents of a Trace window while preserving colors and formatting.
.hit   A text file allowing to save a list of non-zero hits from the Hitmap window and reload it later. The list can also be viewed in a Trace window, if the extension is changed to .txt
.txt   A standard PC text file. Pretty much any window can be saved into a text file but, since the trace log files have a .txt extension, .txt files are loaded into a Trace window by File-->Open.
.bmp  A standard PC bitmap file produced when saving the Screen window (in which case it contains a 16-color palette) or the Hitmap window (in which case it's a 4-million colors bitmap).

The Edit menu

This feature is currently not implemented.

Currently only available for a Trace window. Deletes the selected text and places it in the Clipboard.

Currently only available for a Trace window. Places the selected text in the Clipboard.

Currently only available for a Trace window. Inserts the content of the Clipboard at the current cursor location, possible replacing selected text.

Memory copy
Only available for a Memory window. Calls a dialog box that allows you to copy a portion of memory onto another. See below for details.

Memory compare
Only available for a Memory window. Calls a dialog box that allows you to compare two section of memory and point at any mismatch. See below for details.

Compare again
Looks for the next mismatch.

Only available for a Memory window. Calls a dialog box that allows you to specify a string of bytes or text to look for within a given portion of memory. See below for details.

Find again
Finds the next occurence of the target string.

Only available after the target string was found. Allows you to replace it with the substitution you specified when setting up the search.

Replace all
Replaces all occurences of the target string with the substitution string. Only available after the target string was found once.

Only available for a Trace window. Displays the number of events of each type within the selected portion text.

The View menu

Toggles the toolbar on/off.

Status bar
Toggles the status bar on/off.

Only available for a Screen window, this menu cascades down to three sub-items.
Monitor > Color. Loads the predefined colors to emulate a color monitor.
Monitor > B & W. Loads a prededined greyscale to emulate a black-and-white TV, or a monochrome monitor.
Monitor > Custom... Calls a dialog box (described later), that lets you customize the Screen view by defining the colors, adding borders, or changing the aspect ratio.

Only available for a Screen window. Sets the magnification at which you wish to display the emulated TI-99/4A screen. Valid values are 1:4, 1:2, 1:1, 2:1, 4:1 and 8:1. You can also select "Smaller" which decreases magnification by 1.4 , or "larger" which increase it by 1.4. Finally, "Fit window" adjusts magnification so that the whole screen will fit inside the current window. Note that, if the window aspect ratio is not 192/256, the largest dimension will be decreased to fit the screen image.

This is used by most information windows (except for the Screen window and the Registers window), to set the number of lines to be displayed. See the description of the individual windows for details.

The Window menu

Arranges all open windows on top of each others.

Arranges and resizes windows so they do not overlap.

Arrange icons
Aligns the icons for minimized windows.

Adjust the size of a window. This may have different effects, depending on the type of window. For more details, see the description of this command in the description of each individual window.

List of open windows
Click on one of these to make it the active and topmost window. Convenient if it is hidden under many others.

The Help menu

Help topics
Believe it or not, Microsoft does not support WinHelp any more! Worse, Vista actually hunts down and disables WinHelp, so I cannot even include it with this package. Thus, all online help is doing is to open the present html document (bug99.htm) inside your default browser. In addtion, you can press F1 at pretty much any time and Bug99 will open one of several smaller html files in your default browser. At the moment, all these files are doing is to redirect your borwser to the present document and scroll down at the position relevant to the window, property page, or dialog box that has the focus.

About Bug99...
Displays version number and copyrights.

The toolbar

You can display or hide the toolbar with the View --> Toolbar command. When it is displayed, the following commands are accessible via a button on the toolbar:

File --> New
File --> Open
File --> Save
File --> Load memory
File --> Dump memory

Edit --> Cut
Edit --> Copy
Edit --> Paste
Edit --> Find next
Edit --> Replace

Execute --> Run
Execute --> Jog
Execute --> Walk
Execute --> Step
Execute --> Skip
Execute --> JMP
Execute --> RT
Execute --> RTWP

GPL --> Run
GPL --> Jog
GPL --> Walk
GPL --> Step
GPL --> Skip

Execute --> Breakpoints (toggle button)
Trace --> Execution (toggle button)
Setup --> Devices

File --> Print (inactive)
Help --> About Bug99
Help --> What's this (inactive)

This section describes the various information windows that can be opened with the File-->New command.

The Register window

This small window is especially useful when debugging assembly programs. It displays the microprocessor registers, toghether with the workspace. The information comes from either the emulator or the remote TI-99/4A, depending on which one you selected with Setup-->System.

ST Microprocessor status register. It's further broken down if the following bits: L logically greater than (unsigned), A greater than (signed) E equal, C carry, O overflow, P odd parity, X XOP in progress, IM interrupt mask. The later is a 4-bit field and accepts values from 0 to >F. However, since interrupt level is hardwired as 1 in the TI-99/4A console, the only relevant values are zero and non-zero.

PC Program counter, i.e. cpu memory location of the next assembly instruction to be executed.

WS Workspace pointer, i.e. cpu memory location of the 16 registers, R0-R15, as far as the microprocessor is concerned.

Grom is the current Grom/Gram address, as found in the internal counter of console GROM 0. This is the address of the next byte to be read from Grom (or Gram) memory.

Base is the value of the base address (e.g the one used to read bytes) for the current Grom port, which is found in >83FA. Normally it is in the range >9800-983C.

Workspace: address of the workspace that you wish to display. This does not necessary have to be the same as WS (e.g. if you want a glimpse at a different workspace). You can enter your own address, or pick one from the drop-box: >83E0 is the GPL workspace, >83C0 the initial interrupt workspace, >20BA the workspace provided by the Editor/Assembler cartridge. If you set this field to "= WS", it will automatically follow the value of the workspace pointer field (WS).

R0 through R15 display the contents of the 16 workspace registers, based on the cpu address found in the "workspace" field.

[Read] The content of this window is refreshed automatically upon assembly execution, but you also have the option of trigerring a manual refresh, by clicking the [Read] button. For instance, you can use it to view the workspace contents after you changed the value of the "Workspace" field. Note that [Read] will cancel any change that hasn't yet been saved with [Set], and revert all fields (except the "Workspace" field) to their current value in the emulation or the remote TI-99/4A.

[Set] You can modify the contents of any of the above fields, but your changes will only be commited to memory when you clicking one of the [Set] buttons. The upper one will save everyting except registers, the lower one will save the registers only.

Specific menu commands

File --> Save allows you to save the contents of the register window. You have the option to save it as a text file, or in an internal format that will allow you to reload the saved values. To select the loadable format, use .rgs as a file extension, any other extension results in a text file.

File --> Open allows you to set the contents of the register window from data previously saved with File-->Save. This happens automatically when you open a file with an .rgs extension: a new register window is open, with all fields preset according to the file contents. You will need to press [Set] to commit these values to memory, which gives you a chance to edit them first.

Window --> Resize returns the window to its default size.

The GPL window

This small windows does for GPL what the Registers window does for assembly: it summarizes information that you may find usefull when executing GPL.

GPL Current GROM/GRAM address. When running the GPL interpreter, this corresponds to the next GPL instruction to be executed.

Base Address used by the GPL interpreter to access GROM memory (normally >9800, but this can change). This address is stored into cpu memory word >83FA.

ST GPL status byte. A breakdown of the only 5 relevant bits is provided. L logically greater than (unsigned), A greater than (signed) E equal, C carry, O overflow.

FMT As you may known, the GPL interpreter comprises a sub-interpreter used for screen access. It is called FMT (for Format) and gives a different meaning to the opcodes used in GPL. Thus, it is very important to know whether you are in FMT mode or in standard GPL mode. FMT mode is entered via the FMT instruction and left via the FEND instruction. You can also toggle FMT mode manually, by clicking the checkbox. Note that this is only possible if the current GPL (or FMT) instruction has completed. If that's not the case, the FMT box is grayed out. Select GPL-->Step to complete the current instruction and this should enable the box.

Next to the FMT box is a numeric field that reflects the number of currently nested RPTB loops in FMT mode. This is necessary because the opcode to close a loop is the same as the one to leave FMT mode (>FB). The advantage is that you cannot possibly exit FMT mode with a loop left open, but it forces us to keep track of how many loops have been open. This info is stored in cpu memory, at word >83F2 while in FMT mode. You can enter a new value, or alter the current one with the spin buttons. This field has no meaning in standard GPL mode, and is thus disabled.

Row Current screen row. This is the position where a character written to @>837D will be echoed on screen in standard GPL mode. In FMT mode, this is where writing will continue for instructions like HCHAR, HTEX, etc.

Col Current screen column. See above.

Sub Subroutine stack pointer. This must be a scratch-pad address, i.e. comprised between >8300 and >83FF. You can enter a new value, or increment/decrement the current one with the spin buttons.

The subroutine stack is displayed directly below the pointer. It is always 32 bytes in length and aligned on a 32-byte boundary. A < mark points to the current location of the pointer. When a new return address is pushed on the stack (by the CALL instruction), the pointer is incremented first, then data is written to the stack. In other words, the pointer points at the last valid address that was placed on the stack, and will be used as a return point by the RTN and RTNC instruction. If the stack is empty, the pointer will contain an address inferior to the stack base, and you won't see any < mark in the stack list. You can type new values inside the stack, but they will not be commited to memory until you press [Set].

Data Data stack pointer, and associated data stack. These work exactly like the subroutine stack and pointer, except they respond to different GPL instructions (PUSH) and are used for different (and somewhat vague) purposes.

[Read] This button refreshes the contents of the GPL window. It is not terribly useful, as refreshing is performed automatically when a change is detected. However, you may use it to cancel changes that you entered in one of the editable fields, rather than committing them to memory by pressing [Set]

[Set] If you entered new values in any of the above fields, or in one of the stacks, they are confined to the GPL window. Memory will only be altered when you press [Set], at which point all fields in the window are copied to memory.

Specific menu commands

File --> Save allows you to save the contents of the GPL window. You have the option to save it as a text file, or in an internal format that will allow you to reload the saved values. To select the loadable format, use .gpr as a file extension, any other extension results in a text file.

File --> Open allows you to set the contents of the register window from data previously saved with File-->Save. This happens automatically when you open a file with an .gpr extension: a new GPL register window is open, with all fields preset according to the file contents. You will need to press [Set] to commit these values to memory, which gives you a chance to edit them first.

View --> Lines allow you to change the number of lines displayed in the stack lists. The minimum is 1, the maximum is 16 (since stacks are 16 words at max).

Window --> Resize adjusts the size of the window so that it matches that of the stack lists.

The Memory window

This window allows you to display and alter the contents of the three memory types: cpu, grom/gram, and vdp memory. The leftmost part of the window displays addresses, the middle part shown memory contents in hexadecimal notation, and the rightmost part shows it in text mode.

In the leftmost edit field, select the desired system you wish to address: either the simulation or the remote TI-99/4A. When the window is first created, it comes up with the current system selected by Setup-->System, but you can change this.

The next box selects the memory type: cpu, GROM/GRAM or VDP memory.

In the next box, enter the desired address. If you enter "Current" (or simply "Cur") the window will automatically follow the current address. For cpu memory, this is the value found in the program counter PC, i.e. the location of the next instruction. For GROM the current address is that of the next byte to be read, and is obtained from the device that answers the current base. For VDP memory, the current address is read from the VDP, and corresponds to the next byte to be read from VDP memory.

You can also use the drop-list to select a preset address. For VDP memory, you have the option to select "Screen" to show the address of the screen image table, "ColTab" for the color table, "CharPat" for the character pattern table, "Sprites" for the sprite attributes list, and "SpPat" for the sprite pattern table. You can also type these names (or at least the first 3 characters) directly into the address box. In any case, you will have to click out of the box to translate the name into an address, and you need to click [=] for the window to display this address.

Device lets you specify a given memory device. If you select "Current", the device accessed will be the one that currently answers at the selected address. If no device answers, the memory window will be empty with the simulation and filled with >FF bytes (most likely) with the remote TI-99/4A. If you select a given device, its memory will be displayed (assuming the address is in the correct range), even if the device is not currently active. This is a good way to quickly check the DSR memory of a simulated card without having to turn it on first.

Page is only meaningful with devices that have multiple cpu memory pages. You can use it to speficy which page you want to edit. If the page number is invalid, the memory window will be empty. If you enter "Current" or "Cur" to access the page that is currently selected on this device. Be aware that the remote system cannot restore the original page number afterwards...

Base replaces Page for GROM memory. It lets you select the GROM base through which memory should be accessed. You can use the drop box to select a base among the 16 supported by the console ROMs, or you can enter your own value. If you enter "Current" or "Cur", the base value will be taken from cpu address >83FA (which is R13 in the GPL workspace). If you enter "Any", the default base for the current device will be used (or >9800 on the remote TI-99/4A).

Text controls the display in the righmost part of the window. You can use the standard ascii code, or the biased version used by TI-Basic, in wich an offset of >60 is added to each character before display. In any case, only characters in the range 32-127 will be displayed, the others are represented with a bullet.

You can navigate in memory using the five small buttons:

[^^] moves one page up

[^] moves one line up (16 bytes)

[=] refreshes the display. You must click this button after changing the address (or device, or page). This is also useful to cancel changes in edit mode, or to return to the current address (if the address field says "Current") after you paged away from it. This is the default button, which means that pressing <return> while the Memory window has the focus is the same as clicking this button.

[v] moves one line down (16 bytes)

[vv] moves one page down

[Set] You can make changes in either the hedecimal or the text field. When entering hexadecimal bytes, you may use spaces to separate bytes but don't have to. When entering text, you can only type characters with an ascii code in the range 32-127. When you click out of the edited field, your changes will be reformated. If you are satisfied with the result, click [Set] to commit them to memory.  Otherwise, you can always click [=] to cancel your changes.

Specific menu commands

File --> Save
Allows you to save the contents of the memory window to a text file. If you want to save memory so as to reload it later, use File-->Dump

File --> Open
This menu item does not offer any file extension to load data into a memory window. Instead, you should use File-->Load to load data directly into memory.

Edit --> Find
Allows you to search for a text string, or an array of bytes in the memory of your choice. If the target is found, the memory window will be set to display it. The command first calls a dialog box in which you select the parameters of the search:

In the Source box, you select the type of memory and the addresses where to start and end the search (the end address is not included in the search).

You can also select the device to be searched, or just leave it as "Current".

For devices that have paged cpu memory, you can set the number of the first and last pages to be searched (the last page is included in the search). If the number of the last page is smaller than the first page, the seearch will go backwards, from highest to lowest page (you cannot do this with memory addresses, though). When searching GROM/GRAM, the page fields let you enter the first and last GROM bases to search.

In the "Find" field, you enter the string that you would like to find. The drop box on the right lets you indicate whether you have entered a series of hexadecimal bytes, an ascii string, or a text string in biased ascii. Text strings are case sensitive. By contrast, hexadecimal bytes are not: A-F and a-f are accepted. You may separate bytes with any character you like, or just stick them together (e.g. 123456AC9). If you enter an uneven number of digits, the last digit will encode the least significant nibble of the last byte (>09 in the example above).

In the "Substitute" field, you can specify another string that you would like to be substituted for the target one (Don't worry, nothing will happen just yet.) Here also, a drop box lets you specify the type of input you used. The format is the same as for the target string.

Click [Find] to start the search. If the target is found, the memory window will display it and several more options become available in the Edit menu.

Edit --> Find again
Keep searching for the target string, starting with the address and page at which it was last found

Edit --> Replace
Replaces the target string with the substitute one. The whole substitute string will be written to memory, even if its size is not the same as that of the target string. This means that you can replace "Cow" with "Bovine": you will just overwrite the 3 characters following each occurence of "Cow".

Edit --> Replace all
Replaces all occurences of the target string with the substitute string, within the memory range that you selected.

Edit --> Memory copy
This command allows you to copy a portion of memory into another location. It first calls a dialog box, which lets you define the source and destination for the copy operation.

The Source box is identical to the one used by the Find command (see above). It lets you select the memory type, the start and end address to be copied (the end address is not copied), an optional device, and the first and last memory page (or GROM base) to be copied (the last page is copied).

Below is a "Destination" box, that lets you decide where to copy the memory. You can enter a  memory type different from that of the source, if you so wish. You should then specify the address where to begin the copy, the end address is of course determined by the size of the source data. You can select the target device, which does not have to be the same as the source device. For paged devices, you can also select the first page to copy to (or the first base for GROM). The last page is determined by the number of pages specified for the source. Page numbers always increment in the target, even if they decrement in the source, which allows you to copy "backwards".

Click [Copy] to perform the copy operation

Edit --> Memory compare
This command lets you look for differences between two memory locations. If a mismatch is found, the memory window will be set to display it. The command calls a dialog box identical to the one used for memory copy.

The Source box lets you specify the first area of memory that you would like compare. The "With" box, does the same for the second area. Note that the end address and end page for the second area are determined by what you selected for the first area.

If no difference is found, the program displays "Exact match". Otherwise, the memory window is set to display the mismatch in the source memory.

Edit --> Compare again
You can use this command to keep comparing memory, starting from the location of the last detected mismatch.

View --> Lines
Lets you define how many lines you would like to display inside the window. Valid values are 1 to 48. In addition, the command refreshes the display and adjusts the size of the window..

Window --> Resize
Adjusts the size of the window so as to fit the number of lines and the minimal width that displays all fields.

The Disassembly window

This window interprets the contents of memory as a program and displays it in a human-readable form. Cpu memory is assumed to contain 9900 assembly language, whereas GROM is assumed to contain GPL.

In the leftmost edit field, select the system you want to access: either the simulation or the remote-controlled TI-99/4A.

In the second edit field you select the language to use, which automatically selects the memory type. Valid choices are:

In the third box, enter the address where to begin disassembly. If you enter "Current" or "cur", the address will be taken from PC for assembly language and from the current GROM device for GPL.

The Device field lets you specify a device to disassemble from. You can select it by name, or by CRU address. The device will be accessed, even if it would normally not answer to a memory request. You can also use "Current" or "cur" to select the device that's answering at the selected address. If none answers, the listing will be blank with the simulation and full of >FF bytes with the remote system.

Page is used with cpu memory devices that offer the option of displaying multiple memory pages at a given address. Enter the required page number (in hexadecimal notation), if it is invalid nothing will be displayed. Alternatively, you could use "Current" or "cur" to access the currently active page. Be aware that the remote system cannot restore the original page number afterwards.

Base replaces page for GROM devices. It lets you specify the base address of the GROM port to read from. You use the list box to select one of the 16 ports supported by the console ROMs, or enter your own value. If you enter "Current" or "cur", the base value will be read from cpu byte >83FA. If you enter "Any", the default base for the current device will be used (or >9800 on the remote TI-99/4A).

You can scroll the disassembly window using the five small buttons:

[^^] moves 16 bytes up for GPL, 16 words up for 9900 assembly.

[^] moves 1 byte up for GPL, 1 word up for 9900 assembly.

[=] refreshes the display, using the current address. You must click this button after changing the address, device, page or base. It can also be used to return to the current address after paging away from it. This is the default button, so hiting <return> while the Disassembly window as the focus is the same as clicking this button.

[v] scrolls down by one instruction

[vv] scrolls down one page, i.e. begins disassembly at the instruction immediately following the last one in the current window.

A 5-column list control is used to display the disassembled program.

The first column in the listing is used to display breakpoint. A star * indicates an active breakpoint, whereas an empty circle o indicates an inactive breakpoint.

The second column contains the address the instruction. The current instruction is marked with >> for the simulation and -> for the remote system. The simulation can check that the device and page/base match your specifications (or were set as "current", but the remote system cannot. So if you are disassembling memory from a device that's not currently on, you will never see the >> mark, but you may see the -> one.

The third column contains a disassembly of the memory contents. The Setup --> Option dialog box lets you specify a few options regarding the format. If the memory address contains a value that does not correspond to any opcode, it will be displayed as a DATA statement. In the case of GPL, unimplemented opcodes can be listed as XG-0 through XG-G, and XG-$ (where XG stands for extended GPL), depending on your options.

The fourth column displays the memory contents in hexadecimal notation. This can be useful if you have DATA or BYTE statements within a program, which is common practive both in assembly and GPL.

The fifth column gives you the option of entering comments. These comments can be saved on a .cmt file with the File-->Save command, and then reloaded with the File-->Open comment. Comments are "global", i.e. they are not restricted to a given Disassebly window, but rather linked to a given address. If the device is set at "Current" the comment will apply to any device. If a specific device is selected when a comment is entered, this comment will only be displayed when this device is selected or is the current one. The same apply for device pages and GROM bases: when this field is set as "current", new comments apply to any page/base. When the field specifies a given page or base, any new comment will be specific to that page/base (note that specific pages are only valid with a specific device).

Any execution or GPL command will move the >> mark within the window. If execution is transfered outside the displayed area, the >> mark of course disappears. If you have set the address to "Current", the window will move to keep displaying the current instruction. There are two possibilities here: if execution moves to an address close to the last address currently displayed, the window will scroll down by half a window and display the current instruction (I find this easier to follow than scrolling a whole window). If execution is transfered further away, the current instruction is displayed at the top of the window.

You can alter the contents of the disassembly window, i.e. patch the program, by clicking in a line. Due to a Windows quirck, the leftmost column must be visible for this to work (no horizontal scroll). To patch a program, enter a new instruction in the third column, or new numeric values in column 4. To enter new comments (or delete existing ones) edit column 5. To cancel editing, click out of the edited field. To confirm changes, either press <return> or click the [Set] button. The difference is that <return> moves to the next line, while [Set] leaves edit mode.

Context menu

The disassembly window features a context menu, which is called with the mouse right button. The context menu contains the following commands:

Set/Clear BP
Sets a "PC" or "GPL" breakpoint at this address. If a breakpoint already exists, the command removes it.

Activate/Inactivate breakpoint
Obviously only works of there is a breakpoint at the current address. If the breakpoint is active the commands inactivates it, and conversely.

Jump here
Sets the PC (or the GROM address, in case of GPL) to the instruction you clicked on. It thus becomes the next instruction to be executed.

Run to here
Sets a temporary breakpoint at the address you selected, then enters Run mode. The breapoint is removed when execution stops (for whatever reason). In most cases, it will be the temporary breakpoint that fires, and the pointed instruction will become the next to be executed. However it may not be the case if the program meets another breakpoint, or goes on a stray and has to be stopped manually. Note that globally disabling breakpoints with the Execute-->Breakpoints command will not disable this temporary breakpoint, so you can use it to make sure execution won't stop till it reaches the desired address.

Peek argument 1
Only works with opcodes that have at least one argument. It calls the dialog box that lets you view and edit a watched variable, optionally creating a Watch window in the process (if none was opened). This gives you a chance to check the current value of the argument, and possibly to edit it. You have two options to dismiss the dialog: click [Watch] to add the argument's address to the list of watched variables (in the first Watch window that was open). Alternatively, click [Done] to dismiss the dialog without modifying the Watch window.

Peek argument 2
Does the same thing for the second argument of opcodes that have more than one argument.

Peer argument 3
Only works with the GPL opcode MOVE, since it's the only one that can have 3 arguments.

Specific menu commands

File --> Save
Lets you save the contents of the list control to a text file OR lets you save the comments. If you choose a .cmt extension, all existing comments for both 9900 and GPL will be saved to a text file, together with a 32-bit value encoding the corresponding address (plus device, page and base if appropriate). Such files can be reloaded with File-->Open if you wish to reuse the same comments later. Any other extension results in saving the contents of the Disassembly Window onto a text file. All 5 columns are saved, but only for the range of addresses displayed in the window.

File --> Open
Lets you reload a set of comments previously saved to a .cmt file. These will be merged with existing comments, if any. In case a new comment shares its address with an existing one, it will replace the old one.

View --> Lines
Lets you define how many lines you would like to display inside the list control. Valid values are 1 to 1000. In addition, the command changes the size of the control so as to fit inside the current window.

Window --> Resize
Adjusts the size of the window so as to fit the list control.

The Screen window

This window displays a simulation of the TI-99/4A screen. This is the only window that is updated in Run mode.

Specific menu commands

File --> Save
Saves the sceen image to a bitmap file of your choice. This comes handy to take screen snapshots. The colour palette is saved together with the image, but the zoom factor, the aspect ratio and the borders are not (these are saved with the window, as part of your workspace).

File --> Open
Lets you view a previously saved bitmap file. The file must be in the proper format, i.e. include a 16-colour palette (not all bitmap files do). The new image will disapear from the screen window as soon as you perform an operation that causes VDP memory to be updated, but the colour palette remains attached to the newly opened window. This is thus a great way to save and reload custom-designed palettes.

View --> Zoom
Controls the size of the image displayed in the window. The size of the window itself is adjusted to encompass the entire image, if this is not acceptable to you, you can resize the window by hand. Scoll bars will appear automatically if the window is too small to display the entire screen image. This menu item cascades down to the following options:
Zoom > 1:1 No zoom, i.e. one pixel on the PC corresponds to 1 pixel the TI-99/4A.
Zoom > 2:1 Two-fold magnification. This is the default when opening a new screen window.
Zoom > 4:1, 8:1 Further magnification.
Zoom > 1:2, 1:4, 1:8 Respectively cause a 2-fold, 4-fold, and 8-fold reduction in size.
Zoom > Larger increases magnification by 40%
Zoom > Smaller reduces the magnification by 40%
Zoom > Fit window adjusts magnification so that the image will fit the current size of the window. If the aspect ratio of the window does not match that of the TI-99/4A screen, the image will be adjusted according to the smallest dimension of the window, then the window is resized so that its larger dimension fits the screen image.

View --> Monitor
Controls the way the screen image is displayed inside the Screen window. This menu item cascades down to three sub-items:
Monitor > Colour. Sets the colour palette to emulate a colour monitor.
Monitor > B & W. Sets the colour palette to emulate a black-and-white TV, or a monochrome monitor.
Monitor > Custom. Calls a dialog box that lets you define your own colors, add borders and change the aspect ratio (see below).

Window --> Resize
Adjust the size of the window to fit the bimap  image at its current level of magnification.

The monitor setup dialog

This dialog box lets you customize the appearance of the TI-99/4A screen within the Screen window from which it is called , i.e. if there are other Screen windows these won't be affected (a good way to have a before/after comparison). This dialog box is modeless, which means that you can keep working in the main application window without having to dismiss the dialog first. In particular, you can select View-->Monitor-->Color or View-->Monitor-->B&W to reload a default set of colors, updating the dialog accordingly.

The dialog displays the current settings for the 15 colors (transparent has no color of itself). A colored button displays a sample, followed with the three color components: Red, Green, and Blue. The next column contains the luminance value, i.e. the amount of brightness we perceive. Since our eyes are most sensitive to green and least sensitive to red, the relative contributions of the three color components are multiplied by different factors to give a decimal number from 0 to 1000. These factors vary a bit according to the exact wavelength of each component, which is hardware dependent. Thus, you have the choice between two formulas: the old-style luminance (Y) which is most useful with old phosphorus monitors, and the modern "luma" (Y') which may reflect modern monitors more accurately.

There are basically three ways to modify the color palette. First, you could click a colored button: this will call Window's color picker, from which you can select a new color. Its components and Y value will be displayed, and the Screen window updated immediately.

Second, you could manually edit color components. Legal values are >00 to >FF. Any change takes effect as soon as you click out of the modified field.

Finally, you could use the controls on the right, which affect all colors together. Just click the corresponding [-] or [+] buttons to decrease or increase the chosen property.
Brightness. This increases or decreases the value of all three components together. Note that, if a component is >FF it cannot be further increased. Conversely, a component cannot be deceased below zero. This may result in irreversible changes (i.e. clicking [-] may not reverse a click on [+]).
Contrast. When you increase contrast, any color with a Y value above 500 will have its brightness increased, any color with a Y value below 500 will have its brightness decreased. Decreasing contrast produces the opposite effect.
Saturation. This allows you to change the color saturation, i.e. the "intensity"of a color. Increasing saturation will increase the color component which has the highest value, and decrease the one with the lowest value. The third component sides with the one it is the closest with (e.g. if red=20, green=80, blue=90, green will increase with blue). Decreasing saturation produces the opposite effect and, at the limit, turns the palette into a scale of grey.
Red hue. Allows you to increase/decrease the amount of red for all colors.
Green hue. Does the same for green.
Blue hue. Does the same for blue.

At the bottom of the dialog is a frame called "Borders", that let you add borders to the screen picture. These borders will be displayed in the screen backgroung color, as specified in the second nibble of VDP register 7. Changes become effective when you leave the modified field.
Note that the maximum value for the the Left and Right borders is >10 (i.e. 16 pixels), whereas the top and bottom borders can have upto 64 pixels (max value >40).
There is a special case in text mode, since the screen picture is only 240-pixel wide (40 characters of 6 pixels each). If you click the checkbox, 8 pixels in screen color will be added on each side of the picture to bring it to 256-pixel in width. If you do not click this box, the screen image will be stretched horizontally so as to occupy the same space as a 256-pixel wide picture.

Finally, you can change the aspect ratio, i.e. the ratio of height over width. Normally, the TI-99/4A screen is 192 lines by 256 pixels, so the default value of the ratio field is >C0 (i.e. 192). By entering a different value, you can stretch or compress the image in the vertical dimension, an effect that most monitors allow.

When you are satisfied with your changes, click [OK] to make them permanent. Or click [Cancel] to revert to the settings that were in effect when the dialog was called.

Note that all these options (colors, borders, aspect ratio, and zoom factor) are saved as part of your workspace. In addition, the color palette is also saved within the bitmap file when you save a Screen window.

The Watch window

In this window, you can specify memory addresses that you'd like to watch during execution. You can give them a name if you wish, and choose the type of display that you like. This is one of the cases when it makes sense to have more than one window the same type: you can have different watch lists, each in its Watch window.

The following columns are displayed:

Name: an optional name that you can give to the watched variable. It can serve for you as a reminder, but you can also set it up so it is used in the Disassembly window, in place of the address it represents.
Address: is the address of the variable you want to watch. You can use simple addresses (e.g. @>1234) or very complex ones (e.g. V*>0001:1234[>1300]). See below for details.
Changed: this column is normally empty. If a variable changed since the last time the Watch window was updated, an exclamation mark ! will appear here.
Value: displays the current value of the watched variable. You can select the format you want when setting up a new variable: hexadecimal, decimal, binary, text, etc.

You can edit the Address and the Value field by just clicking in them and entering new data (due to a Window quirk, the Name column shouldn't be scrolled out for this to work). To abort editing, click out of the field. To confirm changes, press <return>. Alternatively, double-click on the Name or Changed column to bring up the same dialog box called by the [New] button, that lets you edit all details of a variable.

Click on this button to add a new variable to the watch list. You can also do this by right clicking in several other windows (e.g. the Disassembly window, which presets the address as one of the arguments of the instruction you clicked on). In either case, a dialog box appears, which lets you control what you want to display, and how (see below).

Removes the currently selected variables (one or more) from the watch list.

Specific menu command

File --> Save
Lets you save the contents of a Watch window to a text file, or in an internal format that allows you to reload a preset list. To select the internal format use a filename with a .wtc extension, any other extension saves the window in text format.

File --> Load
When loading a .wtc file, creates a new Watch window and fills it with the watch list found in the file. Only the first 53 watched addresses will be loaded.

View --> Lines
Lets you define how many lines you would like to display into the list control. Valid values are 1 to 53. In addition, the command changes the width of the control so as to fit inside the current window, then resizes the window to fit the height of the list control.

Window --> Resize
Adjusts the size of the window so as to fit the list control.

The watched variable dialog box

This dialog box is used to set up a new variable to watch.

You don't have to give a name to all watched variables, but it may be useful if you need a remainder of which is which. Also, you have the option to tick the "Use in disassembly" checkbox, which will cause the name you entered to be substituted for any occurence of the address in the disassembly window (both in the address column, and in the disassembled listing column). You can only do this, however, if the address is a direct one (e.g. G@>1234). There are no limitations on the length of the name, but if you plan to include it in the disassembly, it may be wise to stick with 6-character labels.

Here you specify the address that you'd like to watch. The address must follow a particular syntax because you can watch all three memory types, and CRU as well. You can also use various forms of indirect addressing (i.e. getting the address from another address), and you can select the target device, page, and base. See below for a detail explanation of this extended address syntax.

Display type
You can decide in which format the contents of the watched address should be displayed. Your choices are decimal, hexadecimal, binary, or text. For text, you can have it in standard ascii, or with the Basic bias of >60. For numbers, you can decide whether you prefer the byte or the word notation (i.e. >12,>34 or >1234).

Controls the number of bytes to be displayed, in whatever notation. If you display an uneven number of bytes as words, an implicit >00 byte is added at the end, i.e. in the least significant byte of the last word.

Display the contents of the address, in the selected format. This field also offers you the option of altering the contents of said address, by entering a new value (in the selected format) and pressing [Set]

This button refreshes the contents of the Value field. This is useful in case you changed the address: click on [Read] to see what's in there.

This button writes the contents of the Value field back to memory, at the specified address. It 's useful after you changed the contents of the Value field, or after you changed the address. In the latter case, you'll be performing a limited copy operation.

Click this button to add the specified address to your list of watched variables.

Well, cancels the operation.

Extended address syntax

The Watch window and the conditinal expression in breakpoints use extended addresses rather than a simple hexadecimal address. Extended addresses include the memory type and optionally a device name, a page number or a GROM base. Provision is also made for indirect addressing (pointers) of various kinds. The syntax is based on that of assembly and GPL, but somewhat expanded. Most of the time it's very simple, but it can also get quite complex, so let's take it slowly


As usual, numbers are assumed to be hexadecimal, so leading > marks are optional. To enter a decimal number, append a decimal point at the end of the number. Numbers can be prefixed with the following signs:
+ (plus) for positive number. This sign is implicit and thus ignored.
- (minus) for negative numbers. -1 is FFFF.
~ (tildle) for bitwise inversion. ~0 is FFFF.

Simple addresses

>1234 is a constant, not an address. It is not very useful in a Watch window since the "Value" column will always contain >1234, but it comes very handy in a conditional expression as it allows you to check if an address contains a given value (e.g. @2000 == 1234).

@>1234 is cpu memory address >1234. Note that the @ sign is required to make this an address rather than a constant.
V@>1234 is address >1234 in VDP memory. The @ and the > are optional, but the V is not (obviously).
G@>1234 is address >1234 in Grom/Gram. Again, you need the G but can dispense with the @>.
U@>1300 is CRU input bit >1300 (Didn't want to use C for CRU, as it may be confused with CPU).
W@>1300 is CRU output bit >1300. Remember that input and output bits may have different use with some cards. This only works with the emulator, as you cannot read an output bit on the real TI-99/4A.

Special addresses

R0 trough R15: represent the contents of the workspace registers, no matter where your workspace is located. These can be used for indexing (e.g. *R1) or in complex expressions like those listed below..
PC: the current value of the program counter, i.e. the address of the next instruction to be executed.
ST: the contents of the TMS9900 status register.
WS: the contents of the TMS9900 workspace register, i.e. the address of your workspace
GA: the current GROM address.
VR0 through VR7: the contents (byte) of the 8 VDP registers. These are only available with the simulation, as VDP registers are write-only on the real TI-99/4A.
VS: the contents of the VDP status register. Reading this does not clear the interrupt flag with the simulation, but it does with the remote TI-99/4A (unavoidably).
VA: the current VDP address, i.e. the address of the next byte to be read/written. Only available with the simulation.


There are two ways to specify indirect addresses (i.e. an address that contains the address of the variable you want to watch):

*>1234 is the simplest form of indirect addressing. It means that the address that you want to watch is to be found at address >1234. For instance, if address >1234 contains >5678, then you'll be watching @>5678. The advantage of the indirect notation is that, if the contents of address >1234 changes, you keep watching what it points at.
V*>1234 is similar: you get the address to watch in VDP memory from cpu address >1234.
G>*1234 is just like above, except that the watched address is in VDP memory. In all the above cases, the pointer is located in cpu memory.
*V@>1234 this one goes the other way around: the watched address is in cpu memory, but the pointer is in VDP memory, at >1234.
V*G>1234 watches the VDP memory address found in Grom address >1234.
**>1234 demonstrates multiple indexing. This get the address to watch from a cpu memory location, the address of which is found at cpu address >1234
G*V*@>1234 gets quite complicated: watch the GROM location the address of which is found in VDP memory, at the address found in cpu address >1234. And it can get even more complex, if you want!

@>0002(@>1234) is the second form of indexing, which allows for some basic arithmetic. The address within the parentheses is used as a pointer, just like with the * notation. However, the number found before the parenthesis is added to the pointer to find the final address (the contents of the pointer are not actually modified, the addition is purely internal to Bug99). For instance, if >1234 contained >2000, this exemple would watch >2002.
@>0000(>1234) is equivalent to *>1234, since nothing is added.
@-2(@>1234) demonstrates that negative numbers are allowed.
@>0001(@>0002(@>1234)) not very useful, as it just adds 3 to the address found in >1234. This is better written as @>0003(@>1234).
V@>0005(G@>1234) watches the VDP address calculated by adding 5 to the content of Grom address >1234.
@>0001(*>1234) is another way to perform multiple indexing: the watched address is calculated by adding 1 to the address found at the address found in cpu address >1234.

Device and pages

An extended address can be linked to a specific device. With the simulation, the address can always be accessed, even if the device would normally not answer. By contrast, if you do not speficy a device, the currently answering device is used. If none is answering, the Watch window displays "- no data -". To specify a device, include its name (found in the ID field of its property page called by Setup-->Devices) or its CRU address within square brackets, after the address:
@4000[>1300] watches address >4000 in the device that answers at CRU address >1300.
@4000[IDE] watches address >4000 in the IDE card.

For devices that support multiple pages (or multiple bases in the case of GROM), you can specify the page/base followed with a colon, ahead of the address. If you don't, the current page/base will be used (if any). The only restriction is that page numbers must be smaller than >07FE.
@>0001:>6000 watches address >6000 in memory page 1. Just like addresses, page numbers are assumed to be hexadecimal, so you can dispense with the > marks.
G@>9820:>6004 watches Grom address >6000, accessed via grom port >9820.

And of course, you can combine pages with devices. In fact, a page number only makes sense when attributed to a given device.
@12:A000[1500] watches cpu address >A000 in page 18 (hexadecimal >12) in the device answering at CRU address >1500.

If you want, you can use indirect addressing to specify a page number, a GROM base, or a device CRU:
*>1234:5678 watches address >5678 in the page specified in cpu address >1234.
>5678[*1234] watches address >5678 in a specific device, the CRU of which is found in cpu address >1234.
V*>1234:**>5678[>0010(>20BA)] I'll let you figure out what this one is watching...


In principle, a Watch window watches all variable in the current system, as specified with Setup-->System. However, you can force a variable to be read from a specific system by using one of the following prefixes:
_@1234 The underline represents the parallel cable and means that address >1234 must be read from the remote TI-99/4A.
$@1234 The dollar sign represents the simulation and forces the Watch window to display simulated address >1234 even if the current system is the remote TI-99/4A.

Note that you cannot mix and match: only one of these prefixes is allowed and it must be the first character in the address.
This only applies to the Watch windows. With breakpoints, the system is specified by the type of breakpoint (it's always the simulation except for remote breakpoints).


Generic format: +T@page:address[cru] where T is the memory type: G, V, U, W, or nothing (for cpu memory). + can be + (plus sign, ignored), - (minus sign, negative number) or ~ (tildle, bitwise inversion).

Indirect adressing:  +T@page(index):addr(index)[cru(index)]   where index has the generic format. Nested indirections are allowed.
Index shorthand: *addr = 0(addr) = (addr)
Note that *addr[cru] = (addr[cru])  but *addr [cru] = (addr)[cru] The space before [cru] makes the difference.

The Breakpoints Window

This window is used to display the current breakpoints, or a subset of these. It is important to understand that the breakpoints exist independently from the window: closing the window won't remove any breakpoint, and setting the window so as to display only one type of breakpoint won't inactivate the other types.

The box in the top part of the window serves as a display filter, so that you can choose what type of breakpoint you wish to view. Most checkboxes correspond to given breakpoint types or properties (e.g. active vs inactive) and will be discussed below. The "Expand size" checkbox is used with memory breakpoints that are covering more than one address: normally they are only displayed at their initial address, but by checking this box you can repeatedly display the breakpoint at each address it reacts to.

The display filter allows you to have several breakpoint windows open, each displaying different breakpoint types. For instance you could have one for assembly-related and one for GPL-related breakpoints.

Click this button to refresh the display after you modified the filter.

Creates a new breakpoint, calling a dialog box in which you can set up the breakpoint type and its properties (see below). You can also create PC and GPL breakpoints directly from the Disassembly window.

Toggles the currently selected breapoints between active and inactive. If more than one is selected, the status of the first one is toggled and applied to all others.

Deletes the currently selected breakpoint(s), no matter how many, no questions asked...

[Select all]
Selects all the breakpoints currently displayed. Be aware that there may be more breakpoints, depending on how you set the display filter.

The display contains the following columns:

BP: displays a star * if the breakpoint is active, an empty circle o if it is inactive.
Type: displays the breakpoint type. See below for details.
Address: The address at which the breakpoint is hit, or (for opcodes breakpoints) the opcode values that trigger it.
Count: For counting breakpoint, this columns displays the current count. Empty for non-counting breakpoints.
Condition: Display the conditional expession that must be satisfied for the breakpoint to fire. Empty if the breakpoint is unconditional

You can double-click on a breakpoint to edit it. This will call the same dialog box than [New], but with all values preset according to the selected breakpoint. You can alter any field, including the breakpoint type. Just be aware that your changes may render the breakpoint invisible in the current window, according to how you set the display filter.

You can also edit the Count and the Condition field directly from the list, by just clicking in them (the BP field must be visible for this to work). Enter a new count or condition and press <return> to confirm, or click out of the field to abort edition.

Specific menu commands

File --> Save
Saves the currently displayed breakpoints (and only those) onto a text file, or in an internal format that can be used to reload the breakpoints with File --> Open. The internal format is selected by having a file with a .bp extension. The settings of display filter in the Breakpoint window are not saved in either type of file (they are saved in your workspace, though).

File --> Open
When opening a file with a .bp extension, creates all the breakpoints listed in the file and creates a Breakpoints window to display them (even if you already have one open). If a breakpoint already exists, you will be asked if you wish to replace existing breakpoints. You'll only be asked once and your answer will apply to all further duplicates.

View --> Lines
Allows you to specify the number of lines to display in the list control, valid values are 1 to 43. The command also adjust  the width of the control to match that of the window, then performs an implicit Window --> Resize.

Window --> Resize
Adjusts the dimensions of the window to match the list control.

The New Breakpoint dialog box

This dialog box appears when you click [New] in the Breakpoints window, and lets you set up a new breakpoint. It also appears when you double-click an existing breakpoint, so you can modify its characteristics. The dialog box contains the following fields:

Determines the type of breakpoint.

Here you enter the address at which the breakpoint should fire. Or you can enter "Any" to break on any address in a given device or page.

For breakpoints reacting to opcodes, you should enter the hexadecimal value of the opcode (and the first byte of the argument for GPL), instead of the address. The easier way to do this  is to enter a valid instruction in the "Assembly" field, then click out of it.. This assemble the instruction and place its value in the opcode field.

For some breakpoint types, you can restrict the breakpoint to a specific device by selecting one from the drop list. You could also pick a CRU address, and the corresponding device will be selected.  If you select "Current" the device that is currently (i.e. at the time the breakpoint is set) answering at the specified address will be selected. By selecting "Any" you disable device specificity.

For paged devices, you can restrict the breakpoint to a given page by typing in a  page number. Be aware that you cannot specify a page if you selected "any device" (because pages are device-dependent). If you select "Current" the currently active page (at the time the breakpoint is set) in the selected device will be selected. You can disable page checking by specifying "Any" page.

You can do the same with GROM bases for GPL and GROM breakpoints, by selecting a base from the drop list. In this case, it is possible to select a specific base even if the device is set as "Any", because various devices may handle the same base. If you select "Current", the base will take the current value used by the GPL interpreter, found in word >83FA.

Only applies to memory and CRU breakpoints. If you wish the breakpoint to cover a group of address (e.g. a string or a buffer area), enter its size in here. The program will set a memory (or CRU) breakpoint at every address in the selected range. Leave the size as 0 if you only wish to break at a single address. Note that if there is another breakpoint withing the covered range, it will not be replaced with the one you're setting. And since there can only be one breakpoint by address, this will leave a gap in the area covered by the new breakpoint.

This field only appears for opcode and GPL opcode breakpoints. You can type an assembly instruction in here (e.g. B *R11) and when you click out of the field it will be assembled, and the corresponding value (>045B) placed in the Opcode field. Conversely, when you click out of the Opcode field, its value is disassembled and the result placed in here. With GPL opcode, if you assemble a Format opcode, the Fmt box will be checked automatically, whereas it will be unchecked when you assemble a regular GPL opcode.

Note that, when ignoring the argument type or register value, the ignored bits will be masked with 1s. This will become appearant when the breakpoint is displayed in the Breapoints window: CLR R5, which is encoded as >04C5 with both boxes checked, will appear as >04FF (i.e. CLR *R15+) with both boxes unchecked.

Check this box to activate the breakpoint, uncheck it to inactivate it. Inactive breakpoints do not halt execution when hit, but they can be traced. You can also toggle the active/inactive status of a breakpoint directly from the Breakpoints window, or from the Disassembly window.

Enter a decimal number, negative or positive, in this field to create a counting breakpoint. Leave this field empty if you do not wish to use this feature. Every time the breakpoint is hit, the count will increment by one. Counting breakpoints only fire when the count reaches zero. You may thus use negative numbers to fire a breakpoint only after a given number of hits (e.g. enter -5 to fire the fifth time the breakpoint is hit). Alternatively, enter 0 to count the number of times the breakpoint was hit before execution was stopped (manually, or by another breakpoint). The Breakpoints window will display the current count for every counting breakpoints, and you can include the count in the trace log.

Here you can enter a conditional expression that must be true for the breakpoint to fire. Leave this field empty if you want the breakpoint to fire unconditionally. A conditional expression consists in two variable addresses (as defined above), separated with a comparison operator. Both variables will be read, compared, and the breakpoint will fire if the comparison is true. Either side of the comparison may also contain an absolute value, so you must use the @ sign when specifying a cpu address. For insance, R1 == >1234 means "R1 equals the value >1234" (the > sign is optional), whereas "R1 == @>1234" means "R1 equals the contents of cpu address >1234".

Valid operators are:
== equal
!= not equal
> greater than
>= greater or equal
< smaller than
<= smaller or equal.
No operator means "different from zero" and in this case there is obviously only one variable.

By default, all comparisons are unsigned. To perform a signed comparison, include a + or a - sign ahead of the second variable: @>2000 == +R1 (Note that a minus sign negates the contents of the variable, not the address).

By default, all comparison are performed on words (i.e. 16-bit values). Optionally, you can precede the first expression with an expression in curly braces that specifies the width of the comparison. Valid values are:
{BYTE} consider only the most significant byte.
{MSB} most significant byte, same as the above.
{LSB} consider only the least significant byte.
{nnnn} where nnnn is an hexadecimal number speficying the bits to be used in the comparison. For instance >FF00 is the same as MSB and 00FF is the same as LSB.

Finally, you can use the symbol OLD in place of a variable, on the right side of the operator. Each time a conditional breapoint is hit, the value of the left member of the comparison is saved. OLD can be used to compare the current value to the previous one. In this way, you can restrict a breakpoint to firing upon changes (or lack of) in a given variable. E.g. R1 != OLD prevents the breakpoint from firing if R1 has not changed since the breakpoint was set, or the last time it was hit.

You can combine activation status, count and conditional expression in the same breakpoint.

Creates the breakpoint, or modifies it when editing. There can only be one breakpoint (of a given type) for a given memory address, so you will get an error message if another breakpoint already exists at this address. In this case, you will be given the option to replace it. Select [Yes] to replace the old breakpoint with the new one, [No] to return to editing the new breakpoint, or [Cancel] to give up creating the new breakpoint.

The Trace Window

This window is used to display the contents of one of the two current trace log files: "Trace.txt" and "Patches.txt". It can also be used to view a previously saved log file. These files can also be viewed with a standard word processor, but the Trace window offers you several nifty features, such a differential coloring, filtering, collapsing and expansion of lines, etc. It also automatically rearranges wrap-around files (i.e. file with a fixed length that overwrite themselves from the beginning once full).

Note that the window will be empty if there is nothing in the selected log file! Thus, you should first use the Trace menu to setup trace mode, turn it on, then perform some of the actions that you want to trace (generally, executing a program). This will accumulate data into the log files. At any time, you can display a "snapshot" of the log file within a Trace window. By snapshot I mean that the Trace window is not updated automatically to reflect the contents of the log file (this would be too slow), instead you must select Trace-->Reload to refresh it.

The Display Filter dialog box

Upon selecting File --> New --> Trace, you will be presented with a filter setup dialog box. In here, you can select which type of event you would like the window to display, and in what color. You can also select the color of the background. Tip: it is better to be fairly restrictive here, and to select only the most informative types of events. This makes the log easier to read and the remaining events can always be displayed by using the "Expand" or "Filter" commands if needed. Obviously, you can only display events that have been logged...

At the top of the setup dialog, you can select which log file you'd like to view: execution or patches. The file Trace.txt is normally used to trace program execution, wherease Patch.txt logs manual changes. This, however, is decided when setting up trace mode and it's entirely up to you to decide which kind of event will be logged into which file. The third option (File) appears when opening a previously saved log file.

Then follows a list of traceable events, each with a checkbox on the left and a colored button on the right. Check the box to display events of that kind in the Trace window (assuming, of course, that you have set up trace mode to log such events, and that some did occur while tracing was on). Tip: Don't check too many boxes, as it would make the "Collapse" and "Expand" functions less useful. By clicking on the colored button, you can change the color in which a given type of event will be listed. The bottom button decides the color of the background. Once you're ready, click [OK] to display the Trace window.

Be aware that the log file may become very large very quickly. In which case it will take a VERY LONG TIME (upto several minutes) to load the contents of the file into the Trace window. This is a known problem with Windows RichEdit control, nothing I can do about it.  So just be patient...

Trace log format

The selected log file is then read, and selected events are displayed in the Trace window, one per line, using the selected colors. If you were using a circular file, the current insertion point is found and the two halves of the file are displayed in chronological order (another advantage over using an external text editor).

NB. The Trace window is actually a RichEdit control, so you could type in it and modify its contents as you like, but be careful as this could make some functions unavailable. For instance, there is a hidden number at the end of each line (printed in background color) that indicates the offset of the current line within the log file. This number is required by functions like "Expand".

The type of event is identified by the beginning of the line:
 >nnnn  disassembly        PC tracing. nnnn is the current value of PC, the disassembled instruction is optional.
G>nnnn disassembly        GPL tracing. nnnn is the address of the current GPL instruction, the disassembly is optional.
@>nnnnw = xxxx -> yyyy  PC: pppp  GPL: gggg    Tracing of cpu memory access. nnnn is the memory address. A trailing w indicates a write operation. Optional: xxxx shows the contents of the memory, yyyy the new value (for writes). pppp show the address of the assembly instruction that triggered the memory access, gggg the current GPL instruction.
G@>nnnnw = xx -> yy  PC: pppp  GPL: gggg     Tracing of GROM access. Similar to the above, except that values are listed as bytes.
V@>nnnnw = xx -> yy  PC: pppp  GPL: gggg     Tracing of VDP memory access. Same as above.
U@>nnnn = xx (y bits) PC: pppp  GPL: gggg     Tracing of CRU read operations. nnnn is the CRU address. Optional: xx shows the value read, with y being the number of significant bits. PC and GPL as for memory operations.
W@>nnnn = xx (y bits) PC: pppp  GPL: gggg      Ditto, for CRU write operations.
BP tx* nnnnnnnn  #:ddd  = yyyy        Skipped breakpoints. x is the breakpoint type (as in the breakpoint setup dialog), a trailing * indicates an active breakpoint. nnnnnnnn is the address at which the breakpoint fires, including the device or page number in the most significant word. Optional: ddd is the current value of the counter, and yyyy shows the current value of the left member of the condition.

Modified address = xxxx -> yyyy    Manual changes to watched variables. address is the address modified, xxxx the old value, yyyy the new one (the later two are optional).
Modified registers WS= xxxx PC=xxxx ST=xxxx Grom=xxxx Base=xxxx     Manual changes introduced via the Registers window, upper [Set] button.
Modified nnnn = xxxx,xxxx,xxxx...xxxx -> yyyy,yyyy,yyyy....yyyy   Manual changes to the workspace portion of the Registers window. nnnn is the workspace address, xxxx the values of the 16 registers, yyyy the new values.
Edit CPU mem: pppp:xxxx-yyyy[cccc]  Manual changes to memory, via the memory window. pppp is the page number (if available), xxxx the start address, yyyy the end address, cccc the device CRU (if available).
Edit GROM: pppp:xxxx-yyyy[cccc]    Ditto for manual changes to GROM.
Edit VDP mem: pppp:xxxx-yyyy[cccc]    Ditto for manual changes to VDP memory.
Patch >nnnn = xxxx -> yyyy old new    Manual patch to an assembly program, via the disassembly window. nnnn is the address, xxxx the old value, yyyy the new one. Optional: old and new disassemblies can be shown.
Patch G>nnnn = xxxx -> yyyy old new    Ditto for manual patch to a GPL program.

Specific menu commands

Trace --> Execution
Toggles trace mode on/off for the events selected with Trace-->Setup-->Execution, which will be logged into the Trace.txt file

Trace --> Patches
Ditto for events selected with Trace-->Setup-->Patches and logged into the Patch.txt file.

Trace --> Setup --> Execution...
Trace --> Setup --> Patches...
Selects which type of events will be traced into the Trace.txt and Patch.txt files respectively, and how much extra information should be included. See above for details.

Trace --> Wipe --> Execution
Trace --> Wipe --> Patches
These commands are used to empty the corresponding log file (Trace.txt and Patch.txt respectively).

Trace --> Reload...
This reloads a log file into the Trace window, presenting you again with the filter/colors selection dialog. This can be used to view any changes that may have occured since the window was created/reloaded (e.g. execution continues). Remember that the Trace window is static, i.e. it does not reflect changes to the log file until you reload it. You can also use this command to cancels any editing, expansion, or filtering, that you may have done.

Trace --> Filter...
Lets you alter part of the display, by modifying the filter and/or color selection (except the background color). The process is similar to that of reloading the window, but only applies to the currently selected portion of the Trace window. This lets you reveal or hide information in specific parts of the log file.

Trace --> Expand
Displays all the information available in the log file, between the current line and the next one. If more than one line is selected in the Trace window, the information will be displayed for the whole selected section, upto the first unselected line.

Trace --> Collapse
Reverts the selected section to the filter settings that were chosen upon loading the file into the Trace window. Note that if you elected to display everything, the command will not produce any visible change, which is why it's good to select few event types upon loading a log file.

Trace --> Backtrack
Undoes any write operation that appears within the selected section, starting from then end up. If nothing is selected, backtracking starts from the bottom of the window upto the current position of the cursor. This function lets you "rewind" the simulation upto a given point, from which you can step forward again. For this to work, you must have traced  write operations for all three types of memory and the CRU, including values into the listing, and you should also have traced PC and GPL PC.

Trace --> Forward
Does the inverse of the above, i.e. re-executes the events found in the selected section. This allows you to move the system to a later stage in the trace file. Just as above, you must have traced all writes and included values in the listing.

File --> Save
This can be used for two purposes. You can save the current contents of the Trace window in RTF format, which will include coloring, etc. Or you can copy the entire log file (Trace.txt or Patches.txt) onto a different text file, so you can open it again later with File --> Open. Use a .txt extension to create a reloadable file, any other extension to save the contents of the window.

File --> Open
When opening a file with a .txt extension, pop up the trace filter selection dialog, then displays the file into a Trace Window. You will notice that the combo box in the filter selection dialog announces "File", followed with the selected file name.

Edit --> Count
This command calls a message box that displays the number of events of each type that are part of the selected section in the Trace window. If nothing is selected, only the current line is considered, so you'll get only one event.

Edit --> Find
Lets you search the Trace window for a given string of text, possibly restricting it to a given type of event. A dialog box pops up, into which you can enter the string to search for (e.g. an address). You may leave the string blank if you merely want to find events of a given type. The two check boxes affect the text  search: case-sensitivity and search for whole words only. The drop box lets you select a type of event. By default, it searches any events, but you can restrict the searches to events of a given type. For instance, you could search for memory accesses at address >2000, or for the next GPL instruction (by leaving the query string blank).

If you have selected a number of lines within the Trace window, only the selected lines will be searched. Otherwise, search begins at the line on which the cursor is and continues upto the end of the Trace window. If no match is found, a message box appears to give you the option of searching again, from the top of the file upto the current cursor position. If the search is unsuccessful again, nothing happens. If the target is found, the Trace window scrolls down (or up) to display the line containing the target, which then becomes the current line.

Edit --> Find again
Lets you repeat the previous search, starting from the current position of the cursor. The current line is not part of the search, which allows you to find all successive occurences of your query string/event.

Edit --> Copy
Nothing unusual here. The command just copies the selected text into the clipboard, without altering the contents of the Trace window.

Edit --> Cut
Same as above, but removes the selected text from the Trace window.

Edit --> Paste
Inserts the contents of the clipboard into the Trace window, at the position of the cursor. If text was selected in the Trace window, it is replaced with the clipboard's contents.

View --> Lines
Lets you adjust the height of the RichEdit control to display a given number of lines. Valid values are 1 to 50. The width of the control is adjusted to match the current width of the window. The command then automatically resizes the Trace window to match the new height of the control.

Window --> Resize
Resizes the Trace window so as to closely fit the dimensions of the RichEdit control.

Context menu

By right-clicking into the the window you can summon a pop-up "context" menu implementing some of the above commands, plus two specific commands.

Call the same commands as the Trace menu. If you right-click within a selection, the command applies to the whole selection. If you click outside a selection, the cursor is first moved there, the selection is canceled and the command behaves as if  nothing was selected.

Lets you view the contents of the address listed on the line you right-clicked on. This is achieved by presenting you with the dialog box used by Watched windows. You can modify the contents of the address with the [Set] button, add the address to the list of watched variables by clicking [Watch] or just dismiss the dialog with [Done].

Adjusts the Disassembly window (creating one if necessary) so as to start at the address found in the line you right-clicked on.

The Hitmap Window

The Hitmap window displays the contents of the memory hit counters in the form of a 256 x 256 pixel bitmap. The number of hits is color-coded on a black-blue-purple-red-orange-yellow-white scale (the scale is displayed on the right of the tab control). For this feature  to work you must first enable hit counting with Trace-->Hits, then execute an assembly or GPL program with Step, Walk, Jog, or Run.

Use the tab control to select the memory you would like to display: cpu, GROM, VDP, or CRU. Note that GROM and VDP access is also represented in the cpu memory tab, at the memory-mapped addresses: >8800, >8C00, >9800, >9802, >9804, etc.

Use the checkboxes to decide which kind of hits you want to display: read operations, write operations, instruction acquisitions, or any combination thereof. An instruction acquisition is defined as a cpu memory read within 3 words of the PC for cpu memory, and as a GROM read within 32 bytes of the current GPL instruction for GROM (the latter only works if you used the GPL menu to execute your program, as the Execute menu does not keep track of GPL instructions).

The drop box on the upper left corner of the bitmap is the zoom factor. It displays the number of memory bytes displayed in a line. Legal values range from >100 (i.e. 256 bytes) to >10 (16 bytes). Note that the number of lines per byte varies accordingly, since a byte is always represented by a square box. Your choices thus are:
>100 bytes per line, 64K total, each byte is 1x1 pixel.
>80 bytes per line, 16K total, each byte is 2x2 pixels.
>40 bytes per line, 4K total, each byte is 4x4 pixels.
>20 bytes per line, 1K total, each byte is 8x8 pixels.
>10 bytes per line, 256 bytes total, each byte is 16x16 pixels.

If the zoom factor does not allow to display the whole memory, you can use the scroll bar to move up and down the memory. The left axis labels are updated accordingly. Alternatively, you may want to enter the address of the first byte to be displayed in the text box at the top of the left axis.

Under the bitmap are the controls that let you adjust the color scale. The scale comprises 256 colors but, depending on how long you ran the simulation, you may have counts higher or lower than this number. To best visualize memory hits, you should adjust the color scale so that it brackets the range of counts in the displayed memory.

Min: this field and associated slider let you select the minimum count value. Any address with a number of hits equal or lower to this number will be represented with the "zero" color. Legal values are 0 to >FFFE. Most of the time, you will want to leave it as 0, but you may occasionally raise it to hide unfrequent hits.

Max: this field and its associated slider select the maximum count value. Any address with a number of hits equal or greater to this number will be represented with the "saturated" color. Legal values are 1 to >FFFF. For obvious reasons, Max must be greater than Min, so changing one may result in modifying the other. For best color resolution, you should adjust Max to a value slightly higher than the highest number of hits in the display. You can do this automatically, by checking the "Auto" box.

There are several reasons why a given address count may become saturated. For one thing, the hit counters are 1-word wide. They won't wrap around to zero, but they won't increase beyond >FFFF. So if an address reports >FFFF hits, it may actually have been hit much more frequently, there is just no way to know. Also, you may have set Max to a value lower than the highest number of hits in the display. In particular, if you are displaying two or three types of htis together (read, write and instructions), then the total can add up to 3 times >FFFF (i.e. >2FFFD), but the Max value still cannot go beyond >FFFF.
So that you can immediately detect such saturation problems, you can tick the "Saturated = green" box. This will cause the topmost color to be bright green instread of white. Since green is not part of the regular "heat scale", saturated values become immediately obvious.

Conversely, there are times when you will want to visualize even a single hit. This may be difficult, since it would be represented in very dark blue, which is not easy to tell from black. The "Zero" slider lets you change the color used to represent zero hits (or any value lower or equal to Min). You can pick any shade of grey, from pitch black to fully white. By selecting a light grey, you can make single hits immediately visible.

All these settings (read/write, scale, zoom and scrool) are specific for each memory type. When you change tabs, the previous values for that type of memory are restored.

On the right of the tab control are the statistics field. By entering an address in the first field, you can see how many reads, writes, and instruction acquisitions (if relevant) this address has accumulated. Rather than entering the address by hand, you may just click into the bitmap: the address of the corresponding byte will be entered automatically.

By entering another value in the "to" field, you can get statistics on a range of memory addresses. The top series of boxes give you the cumulated number of hits in the selected area, the bottom one reports the highest values in each category. Click inside the bitmap while holding the Shift key down to automatically fill in the "to" field.

Note that CRU addresses represent bits, not bytes. Since CRU addresses are always 2 bits apart (the least significant bit of R12 is ignored), every second bit should be zero. I didn't like this stippled appearance, so I decided that odd bits will always match the corresponding even bit. In other word, each bit is represented by a 2x1 pixel box (at lowest zoom). You may notice a similar phenomenon when displaying cpu memory, but here it is due to the fact that the TMS9900 always accesses two bytes at a time, even when using MOVB instructions. Thus, no matter what you do, the number of hits on the odd-numbered byte always equals that of the even byte.

Under the statistics are controls for reseting the counters. Click the [Wipe] button to reset all counters for all memory types. This is the same as using the menu item Trace-->Wipe-->Hits.
You can also set up automatic reset. This is useful to give you a "real time" impression of memory access, rather than a cumulative value. Check the "auto-wipe" box to enable this feature. You must then select the type of timeout event from the drop list, and enter the threshold value in the edit field. You have the option to reset the counters after a given time (in milliseconds), after a given number of assembly instructions, or after a given number of hits. In the latter case, only displayed hits are counted; hits in memory areas that are zoomed out, or not displayed because of the read/write settings, do not count.
Remember that the display is not refreshed in "Run" mode, and only refreshed after a given number of instructions in "Jog" mode (specified in Setup --> Options).

Specific menu commands

Trace --> Hits
Toggle this item to count or ignore memory/CRU hits upon execution.

Trace --> Wipe --> Hits
This wipes out the hit counters for all types of access to all memories. All counts are re-initialized to zero. If you wish to reset one particular counter without affecting others, right click into the Hitmap Window.

File --> Save
You have the option to save either the bitmap image, or a list of hits. Select a .bmp extension to save the bitmap, any other extension will produce a text file with a list of hits for all memory types. Only addresses with a non-zero number of hits will be included in the list. Use a ".hit" extension if you wish to reload the list in the Hitmap Window at a later time. If you use a ".txt" extension, the list can be loaded into a Trace Window.

File --> Open
This lets you reload a previously saved list of hits. The extension must be ".hit" for this to work, but the program also understands the syntax of a Trace log file. So if you were to save a Trace window onto a ".hit" file (or to rename a log file in such a way), you would be able to display it in a Hitmap Window.

Note that a trace log only reports the first address of an instruction acquisition (either assembly or GPL), whereas a hit list includes all such addresses. For instance MOV R1,@>2000 uses two memory words, but only the first one will be listed in a trace log. As a result, the hitmap for instruction acqusition will have a dot-pattern appearance. This is even more pronounced with GPL, where instructions can be upto 32 bytes in lenght.

Window --> Resize
Restores the default size of the Hitmap Window.

Context menu

By right-clicking inside the Hitmap Window, you can bring up a context menu. Some items will only be available if you clicked within the bitmap, and then again it depends on the type of memory you are displaying.

This item is only enabled with cpu memory and GROM. It opens a Disassembly Window (or reuses the first one that was open, if any) and sets it to display the address of the byte on which you right-clicked.

View memory
This item opens (or reuses) a Memory Window, and sets it so as to display the current memory type beginning with the location of the byte you right-clicked on. The current settings for Device and Page/Base are not modified. This item is disabled when viewing CRU hits.

Peek address
This items calls a Watch Variable setup window, preset with the address of the byte (or bit for CRU) on which you right clicked. This gives you a chance to check the current value of this address, and possibly to modify it (by entering a new one and clicking [Set]). Once done, you could either click [Done] to dismiss the dialog, or [Watch] to place this address into a list of watched variables.

Set breakpoint
This items call up the list of breakpoints if it isn't already displayed, and the dialog it uses to create a new breakpoint. The breakpoint type and address are preset according to the type of memory you selected. For cpu and GROM memory, a PC breakpoint is assumed if you are viewing instructions, a memory breakpoint otherwise. The Read and Write boxes in the breakpoint dialog are preset to match those in the current pane of the Hitmap Window. You can edit the breakpoint and click [Done] to install it, of [Cancel] to abort the operation.

Wipe shown
This resets the counters for the type of memory currently displayed. Only the selected counters (read, write or instructions) will be erased. Also, in cased the zoom factor does not allow to display the whole memory range, only the currently displayed addresses will be reset.

Wipe selected
This is very similar to the above, except that the range of counters to reset is that specified on the right of the tab control, in the "Address" and "to" fields.

The Bus Window

This window lets you interact with the simulation via one of its busses: USB host, USB device, PIO, or the two RS232C serial busses. The window displays a list of bus event, which you create manually or load from a file. You can then transmit the events to the simulation and see how it reacts. The tab control lets you select the bus you wish to work with. Different busses have different content, but they have a number of features in common.

[New] This button appends a new bus event at the bottom of the list. To insert an event elsewhere in the list, right-click on the desired position and select "insert" from the pop-up menu.
[Delete] This button deletes all the events that are currently highlighted (in the first column). Alternatively, you can delete an invidual event by right-clicking on it and selecting "delete" from the pop-up menu.
[Transmit] This buttons processes the next event in the bus list. Timing delays are not observed, nor are STOP statments.
Auto. When this box is checked, the next bus event will be automatically processed each time the Bus window is updated. This happens after each instruction in "Step" and "Walk" mode. In "Jog" mode it happens after the number of instruction you selected with Setup-->Options-->Execute. In "Run" mode it happens each time the screen is updated, which is every 20 milliseconds at the very least.
Send frame packets. This checkbox is only visible with the USB device controller bus. Checking it causes the controller to believe it received a valid frame packet every millisecond, thereby incrementing the frame counter and preventing the controller from going "suspended". If you don't tick this box, you will have to include SOF packets in your script, otherwise the controller will think the bus is suspended. If this were to happen, the controller would internally generate pseudo-SOF signals (and interrupts, if enabled), and may enter suspended mode after 3 milliseconds, unless the internal pull-up resistor is disabled (mode register bit >01).

All bus tabs begin with the same three columns

The remaining columns vary according to the type of bus.

PIO bus:

RS232 bus: USB DC bus:

Control flow events

As mentionned above, you can control the execution of the transmission script by including flow-control commands in the "When" column. These make up a rudimentary script language, which supports conditional blocks (IF-ELSEIF-ELSE-ENDIF), three types of loops (WHILE-LOOP, REPEAT-UNTIL, and FOR-NEXT), as well as simple subroutine calls (GOSUB-SUB-RETURN-SUBEND). The following commands are supported:

STOP. Prevents automatic transmission for proceeding any further. You can bypass this command with the [Transmit] button, though.

WAIT. Holds transmission until the various simulated lines match the values specified in the other columns. Normally, you would only use input fields here (e.g. Hin and Sin) since the simulation cannot influence output fields, but it's entirely up to you. You can also wait for a specific value on the PIO or serial data port. As long as one of the values you specified is not matched, the script remains on this event and keeps checking the bus. Once all the correct values are found on the bus, the pointer can move to the next line (it is executed immediately in automatic mode, but you will need to click [Transmit] again in manual mode). If you wish, you can invert the condition by including a ! before or after WAIT, in which case transmission will continue if at least one of the specified fields does not match the bus. This allows you to implement OR conditions by inverting all the specified fields: WAIT High [or] High can be coded as WAIT ! Low [and] Low.

WHILE. Opens a  loop that is closed by a LOOP statement. The loop will be entered if the current bus condition matches all the fields you cared to specify. Otherwise the pointer is transfered to the instruction immediately following the matching LOOP statement, further down the scripts. Just like WAIT, you can invert the condition by including a ! together with WHILE. You can nest as many loops as you wish, if a matching LOOP cannot be found, the script is terminated.

LOOP. Closes a "while" loop. The pointer is returned to the matching WHILE statment, which is evaluated again.

REPEAT. Opens a "repeat until" loop. This statment is ignored for transmission purposes.

UNTIL. Closes a loop opened by REPEAT. Execution will proceed past that point only if the bus matches all specified fields. Otherwise, the pointer is returned to the line immediately following the matching REPEAT, upstream of the current location. If a REPEAT cannot be found, the pointer is placed on the first line. Note that WAIT can be considered a special case of a one-line "repeat until" loop.

FOR. Opens a loop closed by a NEXT statment. Normally, FOR should be followed with a number (hexadecimal) indicating the number of times the loop should be repeated. This number will be copied into the Dir column, provided that the latter column does not already contain a valid number. Each time the FOR statment is reaches, the value in the "Dir" field is decremented by one. When it reaches zero, it is reinitialized to the value found after FOR in the "When" field and the cursor is moved past the matching NEXT statement further down the script. FOR-NEXT statments can be nested ad infinitum. If a matching NEXT is not found, the script is terminated.

NEXT. Returns the cursor to the matching FOR statment, upstream. If none is found, the cursor is placed on the first line.

IF. Opens a conditional block. If the bus matches all specified fields, execution continues with the next line. Otherwise, the pointer is moved down to the next ELSE, ELSEIF or ENDIF statment (which ever comes first). You can invert these outcomes by including a ! with the IF (i.e. execution continues if the bus doesn't match one of the specified fields). You can nest IF statments as much as you like.

ELSEIF. Works just like the above. In fact IF and ELSEIF are completely interchangeable.

ELSE. When the cursor reaches this point, it is moved to the line following the matching ENDIF further down the script. If none is found, the script is terminated. Note that all ENDIFs must preceede the ELSE, otherwise they would be ignored.

ENDIF. Marks the end of a conditonal block. This statment is ignored for transmission purposes.

Note that the three types of loops ignore each other, and could thus be interlaced (e.g. REPEAT - WHILE - UNTIL-  LOOP) without causing an error. This is also true when mixing loops and IF statments. Thus, exercise due caution when structuring your script.

GOSUB. This calls a subroutine identified by a SUB statement. The name of the subroutine should appear in the "Dir" column and match exactly the "Dir" column in the SUB statement. The other columns can be used to pass parameters to the subroutine, or to receive a return value. To receive a return value, a column should contain an equal sign. Anything that's left of the = is considered a parameter and won't be touched. Upon return, the subroutine may place its return value on the right of the = sign, replacing anything that's there. If you wish to accumulate return values (e.g. if your GOSUB is within a loop), include a vertical bar | on the right of the equal sign: the return value will be inserted before the | without deleting anything.

SUB. This marks the beginning of a subroutine. When execution encounters this line, it skips ahead to the corresponding SUBEND, or to the end of the script if none is found. The only way to enter a subroutine is with GOSUB, which will begin transmission on the line following SUB. Within the subroutine, you can use a % sign in place of data in columns 4 to 10. When it encounters a % sign, the program replaces it with the contents of the corresponding column in the GOSUB line that called the subroutine (upto the first = sign, if any). The % sign persists, so that parameters can be passed again next time the subroutine is executed.

RETURN. Leaves the subroutine and returns to the line immediately following the GOSUB that called it. Optionally, you can return a parameter, by including a name beginning with $ in column 2. This name should appear in the "When" column of another line (possibly after a timing command), and identifies this line as the one that contains the return value. Any column in the GOSUB statment that contains a = sign will be patched with data taken from this line.

SUBEND. Is exacly equivalent to RETURN, but can only appear at the end of the subroutine. There must be one and only one SUBEND, whereas you may use as many intermediary RETURN as you wish (e.g. within various IF and ELSEIF blocks).

For example, you could do something like this:

When   Dir   PIO  Hout Hin  Sout  Sin
GOSUB  Send  12
GOSUB  Get   =

SUB    Send
       Wr         High
WAIT                   Low
       Wr    %
       Wr         Low
WAIT                   High

SUB    Get
       Wr         Low
WAIT                   Low
$here  Rd
       Wr         High
WAIT                   High
SUBEND $here

The first line calls the "Send" subroutine. The subroutine synshronizes with the DSRs by setting Hout high and waiting for Hin to go low. Then it outputs byte >12, taken from the GOSUB line, onto the PIO bus. Upon execution, this line will become    Wr  12% Finally, the subroutine shakes hands with the DSRs again by setting Hout low and waiting for Hin to toggle before it returns.

The second line calls the "Get" subroutine, which inputs a byte from the PIO in the line labeled "$here", synchronizing with the DSRs in a manner similar to the above. Upon return, the subroutine will copy the received value after the = sign in line 2. Assuming the received value is >34, line 2 will thus become: GOSUB  Get  =34

Specific menu command

File --> Save
Saves the current bus script either onto a text file, or in an internal format that will allow you to reload the script. Use a .bus extension if you wish to reload it.

File --> Open
When opening a file with a .bus extension, reloads a script into a new Bus window.

View --> Lines
Allows you to specify the number of lines to display in the list control, valid values are 1 to 53. The command also adjust  the width of the control to match that of the window, then performs an implicit Window --> Resize.

Window --> Resize
Adjusts the dimensions of the window to match the list control.

Context menu

By right-clicking inside the Bus Window, you can bring up a context menu.

Creates a new bus event and inserts it before the event you right-clicked on.

Deletes the bus event you right-clicked on.

Jump here
Moves the transmission pointer onto the event you clicked on. This will be the next event to be executed.

View memory
This item is only available for the USB tabs. It allows you to view the contents of the ISP1161 memory in a Memory window. Even though you cannot do this with the real hardware, the simulation allow it: the device controller memory appears at address >5000, pages >5000 to >500F for the sixteen endpoints (beware there are two endpoints 0, in pages >5000 and >5001, so endpoint 1 is in page >5002, etc). If an enpoint has a dual stacks, both stacks are displayed consecutively. If an endpoint is inactive, the Memory window will be empty. You can also manually enter page >5100 to view the entire DC memory (total size is >99E bytes for some strange reason).


Devices property sheet

This window contains a property sheet with pages corresponding to the different devices that are supported/emulated by Bug99. It is called with Setup-->Device and is not confined within the main application window. Which means that it can get under it and become invisible. To bring it back on top either use the task bar or select Setup-->Device again. To close this window, use the [X] box on the menu bar.

The various tabs bear the ID of the supported devices, and cliking on one of them will display the property page dealing with this device. The various fields on a given page are used to setup the way the device is emulated, or to control  the device directly (as opposed to programmatically). Obviously, emulated devices are easier to control than real ones, and you'll be able to do "impossible" things, like reading CRU output bits, or writing to ROM. If you disable the simulation, many controls will thus become unavailable.

Most panes begin with a few common fields. In the top frame you'll see two non-editable fields containing the device ID and its current CRU address (you can't change the ID, but you can modify the CRU address via the Setup --> CRU map menu command). If the device has a master switch, you'll also see a checkbox labelled "Switched on". This box has to be ticked for the emulated device to answer during program execution. However, direct access to a device via the various information windows, is always granted when you specify the device by name, even if it is switched off.

In the same frame, you'll see the memory blocks used to emulate the device, and the PC files to which they correspond. Different devices may have different numbers of files, or possibly none at all, but the general structure is the same:
three fields display: the file name, the file size in bytes, and the type of memory emulated. The latter can be: ROM, RAM, EPROM, or nv-RAM (non-volatile, or battery-backed RAM). ROM, EEPROM and nvRAM are loaded from file when the you launch Bug99. EEPROM and nvRAM are saved back to file when Bug99 terminates. ROM and EEPROM are write-protected against standard memory writes during program execution. To programatically write to an EEPROM, use the same routine you would with a real device (e.g. write an unlocking code, etc). The Memory window (and other information windows) let you write freely to EEPROM and ROM, so be careful when using them.

Next to each file name is a [Change] button. When you click on it, you will first be asked whether the current memory contents should be saved to the current file. This happens whether the memory has been modified or not, for all memory types, and is meant to give you a chance to save a memory "snapshot". You can cancel the operation at this point, or answer by [Yes] or [No] to continue with saving.  Unless you canceled, you will then be presented with a file selection dialog box, which gives you the option to reload memory from a pre-existing file. If your purpose was to just save the current state of the memory, dismiss the file selection dialog with [Cancel]. Otherwise, pick a file that contains a raw memory dump (.bin extension) and click [Open] to load it into memory.

Most devices are controlled via the CRU, and the relevent CRU bits are displayed with checkboxes, generally arranged in numerical order (i.e. with bit 0 on top). By convention, a checked box means '1', an unchecked box means '0'. Since CRU input bits can be totally distinct from CRU output bits, there may be two columns of boxes, one for output bits, one for input bits. Obviously, you cannot read output bits, nor set input bits with a real device, only with an emulated one. Thus, if you disable the simulation (with Setup --> System), the checkboxes for CRU output bits change into two small buttons: a [0] and a [1]. You can set or reset a CRU bit by clicking on the appropriate button, but you cannot know what its current status is. Conversely, you can view CRU input bits, but you cannot change them: only the hardware can.

Many peripheral cards also comprise dual-inline package switches (DIP-switches), that let you control some functions of the card. Emulated devices use checkboxes again to represent these switches. A ticked box means a closed switch, an unchecked box means that the switch is open. Note that  DIP-swiches used to encode the CRU will not be represented with check boxes, instead you should use the Setup --> CRU map command to alter the CRU address of the card.

The CRU property page

This pane allow you to directly control CRU bits, on any device of your choosing. At the top of the page is an address field in which you should enter the CRU address (the one you would place in R12) of the first bit you want to access. Remember that only even addresses are valid. You can also use the drop-down list to pick up a device by name. Once you've entered the address, click the [=] button to activate it.

The page displays the status of the first 16 CRU bits as checkboxes, starting at the selected address. Because CRU input bits do not necessarily mirror output bits, the two types of bits are displayed in different columns. In either case, a ticked checkbox means a '1' bit, an empty checkbox means a '0' bit.You can change the address by using the 5 navigation buttons:
[^^] Previous device: address decreased by >0100
[^] Previous set of 16 bits: address decreased by >0020.
[=] Refresh the display at the current address. Use this after you entered a new address manually.
[v] Next set of 16 bits: address increased by >0020.
[vv] Next device: address increased by >0100.

You can alter the value of any CRU bit by just checking/unchecking the corresponding checkbox. When accessing real device on the remotely controled TI-99/4A, you can only change output bits, and only read inputn bits. With emulated devices, however, you can read and write both types at will. The change becomes effective as soon as you toggle the checkbox. Note that you will get an error message if there are no devices aswering at this CRU address, or if more than one device answered.

[Set] Rather than manipulating individual bits, you can also entered a 16-bit value in one of the two edit boxes (one for input bits, one for output bits) and click the corresponding [Set] button. This always sets 16 bits at a time, even if you enter a number that could be encoded with fewer bits: implicit '0' bits are just added in front of the number you entered (e.g. 2 is understood as >0002).

For more info on the CRU, check this page.

The VDP property page

This pane grants you direct control on the videoprocessor. Because the VDP has no CRU interface, there are no CRU bits available on this page.

Type.The frame box at the top of the page lets you select the type of VDP chip that you want: the North-American version TMS9918a, which issues interrupts 60 times per second, or the European TMS9929a, which issues 50 interrupts per second.

Under the frame box you can see the contents of the various VDP registers, generally accompanied with a  detailed breakdown. Remember that VR0-VR7 cannot be read on the real TI-99/4A, and VST cannot be written to. The emulation, of course, lets you do what you want. Just type in a new value, then click out of the field and it will be set.

Current address. The address in VDP RAM at which the next byte will be written or read from. You must click the [Set] button to change this value inside the simulation and/or the remote system.
VST. VDP status register. Int: interrupt pending. 5 spr: more than 4 sprites on a pixel row. Coinc: 2 sprites have overlapping 'on' pixels. 5th sprite: number of the offending fifth sprite on a pixel row.
VR0. Only two bits are relevant, Bit: bitmap mode, Ext: external video input (not used on the TI-99/4A).
VR1. Setup register. 16: memory is >4000 bytes (rather than >1000). On: screen is on. Int: generate interrupts. Text: text mode. Mult: Multicolor mode. Size: Sprite size. Mag: sprite magnification.
VR2. Address of the screen image table in VDP RAM. The combo box translates the register value into an address, and conversely. You can also pick the address from the list.
VR3. Address of the color table in VDP RAM. Again, the combo box translates the register value into an address, and conversely. In bitmap mode, another box provides the value of the address mask.
VR4. Address of the character patter table in VDP RAM. A combo box is used to translate the register contents, another to provide the address mask in bitmap mode (the mask is combined with VR3 unless in text mode).
VR5. Address of the sprite attributes table in VDP RAM. A text box translates the register value into an address, and conversely. No drop-list here, because there are just too many valid addresses (every >20 bytes).
VR6. Address of the sprite pattern table in VDP RAM. A combo box translates the register value into an address, and conversely.
VR7. Screen color (background color in text mode) and foreground color in text mode. The drop-down lists let you pick the color by name.

Quick set. Rather than setting up the VDP one register at a time, you could use this drop-list to load one of the preset configurations: power-up time, TI Basic, Extended Basic, Editor Assembler cartridge (in run mode), TI-Writer cartridge, and p-code card. VDP registers VR0 through VR7 are loaded with the proper values when you close the drop-list.

If you have disabled the simulation, there is no way for the program to read the value of the VDP register from the remote system (these are write-only registers), nor can it read back the current VDP address. The property sheet will display the last values you entered manually, but if anything was changed programmatically (by running a program on the remote system), you will not see the changes. By contrast, the VDP status register is read-only, so you can always read it, byt you cannot modify it if the simulation is disabled.

For more details on the VPD see this page.

The Horizon Ramdisk property page

This page lets you control the Horizon Ramdisk. The top frame features a main switch and the two files used by the emulator: one for the DSRs and one for the Ramdisk memory itself. In both cases, the memory type should be nvRAM, since the ramdisk is made of battery-backed SRAM chips. Note that, because there is room for 12 SRAM chips on the board, the total Ramdisk memory should be a multiple of three. If you pick a power of two instead (e.g. 4 megs), it will confuse the CFG configuration program which will take forever and eventually misdiagnose the type of Ramdisk.

Only two CRU output bits are displayed with checkboxes, bit 0 and 15. The others make up the page number (except for bit 14 that is not used).
Card on (bit 0): makes the card memory appear in the DSR space, at >4000-5FFF. The DSR chip maps at >4000-57FF and is not affected by the page number, the Ramdisk memory maps at >5800-5FFF.
Rambo on (bit 15): triggers the RAMBO modification, which makes Ramdisk memory available at >6000-7FFF.
Page (bits 1-14): selects which portion of the Ramdisk memory will appear at >5800-5FFF. Each page is >800-byte long and the maximum number of pages depends on the size of the Ramdisk; it is indicated further on the right, in the "Total" field. Click the [Set] button to modify the current page number. Because the Ramdisk does not have CRU input bits, the page number cannot be read back from the remote TI-99/4A, but the simulation has no such problem.

This pane also lets you modify the configuration and the contents of the Ramdisk. It assumes that the DSR loaded is ROS 8.14 or something similar enough so that it can find the necessary information at predefined addresses. For each of the 10 virtual drives provided by the ROS, you can read and possibly change:

[Export] these buttons let you save the contents of a virtual drive onto a disk file on your PC. This will be a simple memory dump, corresponding to a sector image in numerical order. If the size of the drive does not match that of the disk, you will be asked which one you want to use.

[Import] these buttons let you upload a file into Ramdisk memory, at the location corresponding to a virtual drive. The file should contain a disk image with sectors in numerical order (normally, a .dsk file). The command first checks that the file contains a valid sector 0 image, then checks the disk size announced in sector 0 against the actual size of the PC file. If there is a mismatch, you will be asked whether to use the actual size, or the size stated in sector 0. The command then makes sure that the disk can fit into the virtual drive. If that's not the case, an error message is displayed, which gives you the option to abort the operation. If you chose to continue, you should be aware that disks larger than the drive will be truncated. Smaller disks are loaded normally, and the unused part of the drive remains unchanged.

Rambo: Below the list of  drives is some info regarding Rambo mode: the first page used by the Rambo subroutines, the resulting number of available pages, and the total number of possible pages as calculated by the Ramdisk configuration program. The latter may or may not match the total number of pages calculated from the disk size. You can change the number of the first page, which will then modify the size field. Note that each Rambo page corresponds to 4 Ramdisk pages (displayed at >6000, >7000, >6800, and >7800, in that order), thus the page number is always a multiple of 4. You can also type in (or select from the drop box) "None" which disables Rambo mode, or "Rest" which selects the smallest possible start page that does not overlap with the drives. Note that there may be a gap left, if the total size used up by the drives is not a multiple of 4.

ROS: As you may know, the DSR chip only maps at >4000-57FF, which gives you 6K of memory. The area >5800-5FFF is reserved for ramdisk pages (except in Rambo mode, in which the DSR chip maps onto the whole range >4000-5FFF. The "hidden" part contains the Rambo DSR, i.e. opcode >B0). Since the DSRs are bigger than 6K, an extra 2K was allocated by the simple expedient of reserving a Ramdisk page for it. By default, this is page 0 and the first drive starts at page 1, but the DSRs would allow any page here. You can change it, but make sure that the corresponding drive page contains the rest of the DSRs. The next field displays the version number taken from that page, and it should read something like "8.14B", otherwise you are in trouble. Also note that the Ramdisk configuration program does not save/load this part of the DSRs in your ROS file, it loads it from an internal copy within the CFG program.
Finally, there is a checkbox that lets you toggle autostart mode on/off. This does the same as CALL AO and CALL AF, only without the need to enter TI-Basic.

For more information on the Horizon Ramdisk, check this page.

The 32K memory property page

Nothing exciting here, I'm afraid: no CRU address, no main switch, no special control funtions. Just one memory block, which is RAM and thus normally does not use a file. A filename is provided though, so you can save a "snapshot" of the memory if you so wish.

The p-code card property page

The p-code card uses two files: a 12K (>3000 bytes) file for the DSR ROMs, and a 64K file for the internal GROMs. Note that these GROMs are accessed via the DSR space, at base address >5FDC, so are not visible to the general TI-99/4A operating system.

Grom address. This field displays the current GROM address. To change it, enter a new value and click [Set].

CRU bits. There are only two CRU output bits with this card: Bit 0 turns the DSR space on/off, and bit 128 toggles between two pages in the  >5000-5FFF area.

Be aware that, for a reason that I do not understand, the p-code card simulation takes forever to boot (over 30 seconds). So be patient...

For more information on the p-code card, see this page.

The SAMS property page

This page can correspond to three versions of the Asgard Memory System: the old AEMS card, the Super-AMS, and my own Hyper-AMS card. The type of card is selected from the drop-list in upper right corner.

The AMS and SAMS card use only one RAM file, with a size of 1 meg for SAMS and only 512K for AEMS. The HAMS card supports RAM and EEPROM upto a cumulated maximum of 4 megs, and there is one file for each memory type.

Pages. This frame displays the 16 registers of the 74LS612 memory mapper. Each corresponds to a 4K memory block >0000-0FFF, >1000-1FFF, etc. The contents of the register indicates which memory page will show at these addresses. Page numbers range from >80 through >FF with the AEMS card, >00 through >FF with the SAMS card, and >000 through >FFF with the HAMS card. Be aware that only the HAMS card can map memory in the whole addressing space, the AEMS card only handles >A000 through >FFFF and the SAMS card handles >2000-3FFF and >A000-FFFF. The remaining registers are not used with these cards.

CRU bits and DIP-switches. The AEMS and SAMS cards have only two CRU output bits, bit 0 and 1. The HAMS card uses 6 extra CRU bits and 3 DIP-switches to control mapping in the "non-memory expansion" portions of the addressing space.

The DIP-switches control the default behaviour of the HAMS card, when all CRU bits are zero: will the card answer at >0000-1FFF (normally reserved for the console ROMs), at >8000-83FFF (console "scratch-pad" RAM), and in the normal memory expansion space (>2000-3FFF and >A000-FFFF).

CRU bit 0 turns the DSR space on, which is required to access the mapper's registers programatically.
CRU bit 1 toggles the mapping mode: when off the mapper is "transparent", i.e. page 0 maps at >0000-0FFF, page 1 at >1000-1FFF, etc. When CRU bit 1 is on (box checked), the 74LS612 is in mapping mode, i.e. it uses the pages specified in the various registers.
CRU bit 2, when set to "1", disables mapping at >0000-1FFF, in case it was enabled by the DIP-switch. This bit has no effect with the DIP-switch disabled mapping, nor on the AEMS and SAMS cards.
CRU bit 3, when set, puts the HAMS card in SAMS-compatibility mode, i.e. restricts its internal memory to the first layer of chips.
CRU bit 4 determines what appears in the area >4000-5FFF when CRU bit 0 is set. If bit 4 is set, the mappers register appear repeatedly in the whole area. If bit 4 is not set, registers only appear at >5FE0-5FFF whereas the selected page maps at >4000-5FDF. This allows you to implement DSRs for the card.
CRU bit 5 enables mapping at >6000-7FFF (cartridge ROM area) when set.
CRU bit 6 disables mapping at >8000-83FF, assuming it was allowed by the DIP-switch.
CRU bit 7 turns the area >4000-5FFF into "write-only" memory, which is needed to send the unlocking code to EEPROM chips, in order to write to them.

Chips. The HAMS card supports a total of  16 pairs of 512K memory chips, arranged in 4 layers. Two hardware jumpers, Lnk1 and Lnk2, select how many layers there are. Each pair of chips can be either SRAM or EEPROM, and two types of Flash-EEPROMs are supported, sectored chips like the 29C040 and registered chips like the 29F040 (this distinction in important when it comes to writing to the chips). The "Chips" frame lets you select how many layers you have, and the type of each chip. Be aware that the default layer at power-up time is layer 2, whereas layer 1 is the only one used in SAMS-compatibility mode (and should thus consists only of SRAM). Selecting "pages..." as a chip type calls a dialog box that tells you which page numbers correspond to this chip (the chip type remains unchanged).

For more information on the AEMS and SAMS card, see this page. For a description of the HAMS card, see here.

The  GramKarte property page

The GramKarte contains two types of memory: 8K of ROM used to implement the DSRs, and 128K of RAM that is used both as two sets of 64K of GRAM, and for 16 cartrige banks of 8K each. And yes, this adds up to more than 128K, because the cartrige banks overlap with the second set of GRAM.

Bases. The two sets of GRAMs always answer to base addresses that are >0020 apart, but you can choose which these should be. Normally, you would select >9800+9820, but you are free to pick another value (e.g. to avoid conflicts with another GRAM device).

CRU bits. The card features 8 CRU output bits.
Bit 0 turns on ROM memory in the DSR space >4000-5FFF.
Bit 1 turns off RAM memory in the cartridge bank space >6000-7FFF (it's inverted, so that banks are on by default).
Bit 2 and 3 control reading via the two GRAM ports. Writing to GRAM is never disabled (quite annoyingly). When both bits are 0, both ports are enabled.  To disable the secondary port (address >982x) set bit 3 to 1. To disable the primary port (>980x), you must set both bit 2 and bit 3. As a general rule, you would leave bit 2 set, and control both ports via bit 3. But there may be cases when you want to enable the primary port while disabling the secondary (for instance when a cartridge ROM would appear in the secondary port and confuse the system). To do so, reset bit 2 and set bit 3.
Bit 4 write-protects the cartridge space at >6000-7FFF, effectively turning it to a ROM.
Bit 5 enables GRAM in the range >0000-5FFF, normally reserved for the console GROMs.
Bit 6 determines whether the page appearing at >7000-7FFF is the software-selected (as shown in the "Ram page" field), or a default page. The default page number (from 0 to 3) is preset with DIP-switches on the remote system, and can be changed via the drop-list box with the simulation.
Bit 7 enables cartridge bank switching by writing to the cartridge space: write at >6000 to selecte page 0, at >6002 for page 1, upto >601E for page 15.

Gram address displays the current address counter, which is common to both GRAM ports. To change it, enter a new value and click [Set].
Ram page displays the currently selected page to appear at >7000-7FFF. To change it, enter a value from 0 to >F and click [Set]. Since RAM pages are implemented with the same memory chip than the second GRAM port, each RAM page overlaps a given portion of the second GRAM. A message appears under the page number to let you know which GRAM addresses correspond to the current RAM page.

For more information on the german GramKarte, check this page.

The TMS9901 property page

The TMS9901 is the interrupt and peripheral controller within the TI-99/4a console. It is a very complex and versatile chip, but the way it is wired inside the console kind of limits its abilities. The chip is entirely controlled via the CRU, with 32 output bits and 32 input bits at address >0000. So, if you wish to experiment with it, your probably better accessing via the "CRU" property page. The "9901" property page only deals with the bits that are used within the console.

Interrupts: Only two input lines are effectively used for interrupt control, one for VDP interrupts and one for PE-box interrupts. The check boxes on the left side (CRU output bits 1 and 2) enable interrupts when checked. Those on the right side (input bits 1 and 2) indicate the status of these interrupt pins. Note that, since interrupts are active low, an empty box means that an interrupt is pending.

Keyboard: With the column drop list, you can select which part of the keyboard (or which joystick) is currently active. The boxes on the right indicate whether a key is pressed. Since pressing a key connects it to the ground, a key down shows up as an empty  box. A CRU output bit allows to toggle the alpha-lock key, which returns its result in the fifth input line (same as Fctn).

Cassette: Four CRU output bits respectively control: the motors for the two cassette players, the audio gate (i.e. whether you will hear what's read from the tape through the monitor), and the bit currently written to the tape. One input bit returns the bit currently read from the tape. Cassette tapes are not emulated withing Bug99.

Timer: "Access" corresponds to CRU output bit 0 and puts the timer in access mode, freezing the read value and changing the meaning of CRU bits 1 through 15. The three text boxes display the contents of the three timer registers within the simulation: the value that was loaded (and will be reloaded when the counter reaches zero), the current value, and the value that was current when the timer was placed in access mode. You can modify these values and press [Set] to commit them to the TMS9901. Leaving access mode will start the timer. The check box on the right indicates whether an interrupt is pending, which happens the first time the counter reaches zero. This interrupt will be seen by the TMS9901 as if coming from interrupt pin I3.

Pins: The check boxes display the status of the I/O pins in the TMS9901, while the drop lists let you alter their function. There are three categories of pins. P0 through P6 are pure I/O pins: they are initially inputs (and cannot generate interrupts) but can be programmed as outputs. Pins I1 through I6 are pure input pins, but you can decide whether or not they will trigger interrupts when low. The remaining pins, P7/I15 through P15/I7 can be programmed either as input, interrupt, or output pins. Normally, an output pin cannot be programmed back to an input (or interrupt), but the simulation will let you do it with these drop-lists. The only way to bring all pins back to input mode with the real TI-99/4A is to perform a reset, either a hardware reset, or a software reset (which leaves the timer intact). You can simulate the later by pressing the [Soft reset] button. The IntReq* pin signals the microprocessor that an interrupt has been detected by the TMS9901, and is active low. There are 4 additional pins on the TMS9901 that can pass the interrupt number to the microprocessor, but unfortunately these pins are not connected in the TI-99/4A console: the microprocessor sees all interrupts as #1.

For more information on the TMS9901, check this page.

The Console property page

This page deals with the console-based memory, and also with several console modifications proposed by myself and others.

The simulation uses four files, three of which emulate the three memory blocks in the standard console.
The first file implements console GROM memory as a >5800 bytes block. Each GROM is actually only >1800 bytes, but the file is monolithic and includes fillers for the area >1800-1FFF and >3800-3FFF that are not accessed programatically.
The second file is 8K and corresponds to the console ROMs, at address >0000-1FFF.
Then there is a 256-byte long file that implements the tiny amount of RAM present in the console. Note that, due to incomplete address decoding in the console, these >100 bytes map 4 times, at >8000-8FFF, >8100-81FF, >8200-82FF and >8300-83FF.
Finally, there is one more file, which is used to emulate a console modification I came up with: replacing ROMs with EEPROMs (see this page for details). This file is 16K in length, allowing for two pages at addresses >0000-1FFF.

Memory selection. A series of switches can be added to the console to turn off the various memories. This comes in handy with some GRAM cards and with my HAMS memory expansion card, which have the ability to answer within the console memory space. You can disable the scratch-pad RAM (>8300-83FF), the console ROMs (>0000-1FFF), and enable console EEPROMs (>0000-1FFF). Since EEPROMs chips are larger than ROMs, there are two EEPROM pages; you can select the second one with a CRU bits (assigned in Setup-->CRU map). Finally, yet another switch can write-protect the EEPROMs, which is critical given the number of program that write at address >0000, assuming this will have no effect...

The situation is a bit more complicated for GROMs. These are socketed chips so you can theoretically remove some and leave the others in place. In practice, most GRAM cards require that at least one GROM remains in the console (generally GROM 0), to return the current address minus one, something that is difficult to implement in hardware. The notable exception is the HSGPL card, with which you can remove all three GROMs. Finally, you could install a small switch that lets you temporarily turn all GROMs off, in the console and in the cartridge slot (see this page for details). This can be used with the HSGPL card, so you don't have to open your console every time. The emulation implements these various options via a drop list, where you pick which GROMs remain: All, GROM 0 only, None, or all present but switched off.

Wait states. This controls a series of console modifications I suggested, which allow you to selectively remove some of the 4 wait states when accessing peripheral memory. Each wait state is controlled by a distinct CRU bit, which are assigned via Setup-->CRU map. Note that some combinations of wait states should not be used. See this page for details.

Interrupts trigger NMI. This is a modification suggested by Jeff Brown, in which standard interrupts are intercepted by hardware to generate non-maskable interrupts. See this page for details. The mechanism is controlled by a CRU bit, which can be assigned in Setup-->CRU map.

Clock speed. This lets you emulate the various clock speeds that can be implemented in the console. Normally it is 3 MHz, but many modifications have been described to speed it up by changing the crystal. I came up with a different solution, using oscillators, which has the advantage that clock speed can be toggled by software, between 3 and 4 MHz. If you select this option in the drop list, the "High speed" box corresponds to a CRU bit (defined in Setup-->CRU map) which switches from 3 MHz to 4 MHz. See this page for details.

The Widget property page

Navarone's Widget is a small gadget that lets you have three cartridges plugged in at the same time. A switch lets you select which cartridge you want to use. The "multi-base" Widget is a modification I came up with, which lets you have all three cartdridges on at the same time, when the switch is in position #1. Each cartridge appears under a different GROM base, which you can choose when building the circuitery. Position #2 and #3 work as usual, i.e. select only the cartridge that's in the corresponding slot.

The simluation uses two files per cartridge, one for the GROMs and one for ROMs (note that some cartridges have only GROM or only ROM, in which case only one file is used). If a cartridge contains more than one GROM, the contents of the GROMs should be concatenated in the same file, possibly adding a spacer if GROMs are smaller than >2000 bytes. Similarly, if there are several ROM pages, they should be placed in order within a single file. This can be done with an hexadecimal editor, or with the DOS COPY command. You could also load independent files with multiple File-->Load commands, then save them together with one File-->Dump operation.

Slot. This emulates the selector switch on the Widget and determines which cartridge will answer. Note that you are free to leave any slot empty, or even all three. You can also place the selector on "off" which emulates the situation when you have no cartridge plugged in.

Quick load. Lets you quickly load a cartridge in the currently selected slot. Just pick one from the drop list and the relevant file(s) will be loaded. Alternatively you can select -empty slot- to emulate cartridge removal. Note that I do not provide cartridge images with this package. If you have a hardware cartridge, it is quite easy to upload it to your PC with File-->Dump.

Multibase Widget. Tick this box if you wish to emulate a Widget modified at per my instructions. See this page for details.

Bases. Here you define which base corresponds to which slot, in a "multibase" Widget. Note that, due to hardware design, not all configurations are possible (at least one address line must be low). If you pick a wrong combination, you'll get an error message.

The IDE property page

This controls my IDE hard-drive controller card. Bug99 implements version 2 of the IDE card, i.e. the one that supports four different clock chips.

Memory. The card supports a 512K SRAM chip, to be used with the RTC65271 and bq4847 clocks, or a combined SRAM+clock chip which can be either bq4842 (128K) or bq4852 (512K). This memory (DSR RAM) will be implemented as RAM with the RTC65271 and non-volatile RAM with the other chips.In addition, the RTC65271 clock features an extra 4K of RAM, arranged as 128 pages of 32 bytes. This is emulated via a nvRAM file, which is not used by the other clock chips.

Drives. The simulation uses two more files on the PC to emulate the master and slave drives, but you can dispense with the slave drive if you wish. File size is illimited (as long as it fits on your PC hard drive, that is). Note that these are real files, not memory files, which means that any change is written to disk immediately, rather than when you end the program.

CRU bits. The IDE card is controlled by 8 CRU output bits, together with 8 CRU input bits.
Output bit 0 enables access to the DSR space at >4000-5FFF.
Ouput bit, 1 when matching DIP-switch 1, causes device registers to map at >4000-40FF. Otherwise the SRAM maps in the whole >4000-5FFF range.
Output bit 2 allows page switching upon writing to >4000-4FFF in a method similar to that used by cartridges. Write at >4000 for page 0, >4002, for page 1, >4004 for page 2, etc.
Output bit 3, when '0', forces page # 0 in the area >4000-4FFF, no matter what page has been selected. The selected page still appears at >5000-5FFF.
Output bit 4 enables memory mapping to the cartridge space, at >6000-7FFF.
Ouput bit 5 write-protects the SRAM (useful when switching pages).
Ouput bit 6 enables interrupts and bit 7-driven reset.
Output bit 7 only works when bit 6 is set. When '0' it causes a hardware reset of both drives.

Input bit 0 does not work, due to a hardware design flaw. It was supposed to indicate an IDE interrupt, but I didn't realize that the interrupt line is only active when accessing the IDE port, so in practice you cannot read it.
Input bit 1 returns the position of DIP-switch 1.Input bit 2 indicates whether a clock interrupt is pending. It is active-low, so a "0" means an interrupt.
Input bit 3 returns the status of the IORDY line in the IDE bus. A "1" means that the drives are ready.
Input bits 4 and 5 read back ouput bits 4 and 5.
Input bits 6 and 7 are not used.

DIP-switches. There are four DIP-switches on the card. The first two are used only with the RTC65271 clock, the last two are not used.
Switch 1 decides if the SRAM or the clock XRAM will appear at >4000-401F upon power-up. This allows you to boot from the clock RAM, which is battery backed.
Switch 2, when closed, resets the RTC65271 upon power up. This is useful to clear an interrupt condition that may have occured while the system was off (the clock keeps ticking and reacts to alarms). Such an interrupt would cause a system lockup when booting from the clock, because the interrupt service routine is not be present at power-up time.

SRAM page. This is the number of the current SRAM page. To change it, enter a new value and click the corresponding [Set] button. Valid values are 0 through >3F with the bq4842 clock, and 0 through >FF for all other clocks.
Clock page. This is only relevant to the RTC65271 chip. It is the page number for the internal clock RAM. To change it, enter a new value and click the corresponding [Set] button. Valid values are 0 through >7F.

Clock. The first drop-list lets you decide which clock chip you wish to emulate (if any). The second decides on how the simulation calculates elapsed time: it can either use the clock on your PC (i.e. real time), or the time "elapsed" during simulation. The difference becomes obvious when you stop execution: the PC clock keeps running, but the emulated clock stops.

Clock registers. These vary slightly according to the clock type. Time is displayed in hours : minutes : seconds and possibly hundredth of second. Date is in day / month / year / and day of the week notation. Alarm is in hours : minute : second, and alarm day when available. Exceptionally, input is in decimal notation for these.
Each clock chip also features 4 function registers, which have different roles with the various chips. Input is in hexadecimal, as ususal. Click on the corresponding [?] button to get a breakdown of the various bits in a given register. Click [Done] to dismiss the dialog, or [Set] to introduce changes.
You can also access any register by selecting its number from the drop-list: its contents appears in the neighbouring text field, in hexadecimal notation. Note that all clocks have extra registers not detailed in the "Clock registers" frame: 64 for the RTC65217 and 16 for the other clocks.
Click [Read] to update the contents of all registers, [Set] to send new values to the clock chip.

For more details on the IDE card, check this page.

The RS232 property page

This page implements the RS232/PIO card. At the moment, the RS232 part is not implemented and the PIO port is only implemented as a byte variable within the simulation. You can use this property page to control a RS232 card on your remote system, but be careful since transmission between your PC and your TI-99/4A is handled by the RS232 card: if you mess with it, you may lock up transmission (in which case you should reboot your TI-99/4A and reload the "enslavement" routine).

Memory. The card features a single 4K ROM chip, mapping at >4000-4FFF. The PIO port maps repeatedly from >5000 to >5FFF.

CRU bits. There are 8 CRU output bits and 8 CRU input bits at the card's base address. This normally is >1300, but it can be modified to appear at >1500, which lets you have two RS232 card in your system. You can select the address you want in Setup-->CRU map, but only one card can be emulated.

Output bit 0 enables access to the DSR space, at >4000-5FFF.
Output bit 1 puts the PIO port in input mode when "1", in output mode when "0".
Output bit 2 controls the "handshake out" line in the PIO port.
Output bit 3 controls the "spare out" line in the PIO port.
Output bit 4 is not used.
Output bit 5 controls the CTS-1 (clear to send) line in RS232 port.
Output bit 6 controls the CTS-2 line in the RS232 port.
Output bit 7 turns the card's front LED on.

Input bit 0 always reads "0".
Input bits 1, 4, 5, 6, and 7 read the corresponding output bits.
Input bit 2 returns the status of the "handshake in" line in the PIO port.
Input bit 3 returns the status of the "spare in" line in the PIO port.

TMS9902 chips. The RS232 card also contains two TMS9902 serial transmitters, each controlled by 32 CRU output bits and 32 CRU input bits. The first TMS9902 answers at the card's CRU address plus >0040 (i.e. >1340 or >1540), the second at the card's address plus >0080. The drop-list lets you select which TMS9902 you want to access. The relevant bits are then grouped by functions in several frames.

Registers. The drop box lets you select which internal register you wish to access in the TMS9902: reception, emission, output rate, input rate, interval, or control. The contents of the register are displayed in the neighbouring text box. You can modify this value and click [Set] to write it into the register. Click [Read] to update the current value. Loading means that the register is currently being loaded and cannot be accessed. Reset (bit 31) can be toggled on/off to trigger a software reset of the TMS9902. Test mode (bit 15) internally connects output to input to allow testing.
Transmission. Reports on the current transmission status. Break this output bit can be used to abort transmission.
Interrupts. The boxes on the left (CRU output bits) enable the various interrupt. Those on the right (CRU input bits) reports the current interrupt status.
Errors. Report error conditions, if any.
Pins. Return the state of the various pins of the TMS9902. The RTS, DSR, and CTS bits report inverted values (note that the CTS and DSR pins are connected together and take their input from the DTR line in the RS232 bus). The one output bit controls the RTS pin.

For more details on the RS232 card, check this page.

The USB-SM property page

This page deals with the non-USB portion of my USB-SM card. There is a separate page for the device controller portion of the card, and I am planing yet anothre for the host controller part (which is not emulated yet). This is how complex the ISP1161 USB chip is!

Memory. The card has two types of memory. A Flash-EEPROM chip that can be either 8 megs or 4 megs, depending on how much money you were willing to spend. The EEPROM chips maps at >4000-4FFF in the card DSR space. There is also one megabyte of SRAM (not battery backed), which maps at >5000-5FFF.

CRU interface. The card is controlled via 24 CRU output bits, most of which encode the page numbers for the EEPROM and the SRAM. There are also 8 input bits used to return various type of information.
Output bit 0 enables access to the DSR space, at >4000-5FFF.
Output bit 1 causes the Smartmedia card to map at >4FF0-4FFF, and the ISP1161 dual USB controller to map at >5FF0-5FFF. When bit 1 is "0", EEPROM and SRAM occupy the whole DSR memory range.
Output bit 2 enables interrupts.
Output bit 3 enables the Smartmedia card.
Output bit 4 enables writing to the Flash-EEPROM, provided DIP-switch 1 also allows it. In any case, it disables reading from the EEPROM.
Output bits 5 to 16 make up the page number for the EEPROM.
Output bits 17 to 24 make up the page number for the SRAM.

Input bit 0 signals an interrupt from the USB host controller. It is active-low, so a '0' means an interrupt is pending.
Input bit 1 signals an interrupt from the USB device controller. It also is active-low.
Input bit 2 signals that the USB host controller is in suspended mode.
Input bit 3 signals that the USB device controller is in suspended mode.
Input bit 4 signals that the Flash-EEPROM is busy (e.g. being erased) and should not be accessed.
Input bit 5, when  '0', signals that the Smartmedia card is busy.
Input bit 6 signals that Smartmedia card is present.
Input bit 7, when  '0', signals that the Smartmedia card is write-protected.

DIP-switches. The card comprises 6 DIP-switches, 4 of which are used to set the CRU address of the card. The remaining two switches have the following functions:
DIP-switch 1 write-protects the Flash-EEPROM when open. It should be closed to enable writing via CRU bit 4.
DIP-switch 2 is used to enforce write-protection of the Smartmedia card at the hardware level. When this switch is open, the software can ignore the write-protection signal.

SRAM page. This is the number of the SRAM page that appears at >5000-5FFF. To change it, enter a new value and click [Set]. Valid values are >00 through >FF.
EEPROM page. This is the number of the Flash-EEPROM page that appears at >4000-4FFF. To change it, enter a new value and click [Set]. Valid values are >000 through >7FF with an 8-meg chip, and >000 through >3FF with a 4-meg chip

Strataflash EEPROM

This frame gives you access to the internal emulation of the Strataflash EEPROM chip. In addition, the type of chip (4 megs versus 8 megs) is determined by the file size, and the current page by the EEPROM page field, above.
All the fields in this frame become inactive if you disable the simulation.

Busy: This is the time in nanoseconds for which the chip will be busy writing or erasing. It decrements every time you execute an instruction. Changing this field to zero sets the "ready" bit (>80) in the status register, entering a non-zero value resets the "ready" bit. Note that writing and erasing are actually emulated at the beginning of the wait period, so there is no harm in clearing this field immediately thereafter (in other words, waiting is emulated, but not necessary).
Status: Displays the Strataflash status register. Click the [?] button for a breakdown of the various bits. Note that setting the "ready" bit will reset the "busy" timer.
Command: Shows which command is currently executing or has just completed (note that some commands switch to "read status" when completed). Click the [?] button for a description of the command. It is best that you do not change this field, as most commands only work when programatically written to a given address. The four read commands (FF: data, 70: status, 90: ID, and 98: query) are safe to use, though.
Config: Displays the configuration of the STS pin, which is sensed via CRU bit 4. You should leave this as level-triggered mode (>00), since the pulses generated in edge-triggered mode are much too short for the TI-99/4A to detect. Thus, if you select a pulse mode, CRU input bit 4 cannot be used to sense when the chip is busy.
Locked: This 64-bit value shows which blocks are currently protected against writing and erasing. Each block is 128 KB, i.e. 32 EEPROM pages. The first block is >0000000000000001, whereas >0000000080000000 is the last block for a 4-megabyte chip, and >8000000000000000 is the last block for a 8-meg chip. Lock bits are saved as part of your workspace, and can be altered as desired.

Smartmedia card

The bottom frames deals with the emulation of a Smartmedia card and is thus inactive when you disable the simulation.

Filename: The PC file that hold the contents of a virtual Smartmedia card. Note that it is a real "hard disk" file, not a memory file, which means that any change to the emulated card will be saved immediately, rather than when you exit the program. Click [Change] to pick a new file, which be can either a binary file, or a .smc file comprising a 16-byte header with the card's characteristics. You will get a warning if the file does not contain a header, but the program will accept it and attempt to guess the card characteristics based on the file size. You can modify these characteristics later, if you so wish.

To create a new file you must first specify its size in the "Size field" since the program uses it to guess the cards characteristics. Then you can click [Change] and enter a non-existing file name. You will be asked if you wish to create the file and, if you said [Yes], a empty file will be created containing a Smartmedia header followed with the appropriate number of >FF bytes.

Size: As mentionned above, this field is only used when creating a new file. The rest of the time it simply displays the size of the current file. When specifying a size, make sure that it matches with the number of address bytes (i.e. the number of sectors) and the sector size. Remember than one of the address bytes is used to pass the offset within the sector, so a 3-byte address allows access to a maximum of 65536 sectors. The file can be smaller than the maximum size allowed by the addressing mode, but it shouldn't be larger (since the extra space would not be accessible).

[Wipe] Click this button to reformat the Smartmedia card, i.e. filling the file with >FF bytes. This is one of the few cases when Bug99 will ask you for confirmation of a command.

Inserted: This checkbox lets you emulate insertion and removal of a Smartmedia card.
Write-protected: This emulates write-protection of a card (which is normally achieved by sticking a small conductive patch on top of the card).

Address bytes: There are different internal formats among Smartmedia cards. Old cards have 3-byte addresses, whereas newer (and bigger) cards use 4 or 5 bytes. Pick you choice from the drop list or enter a value manually.
Bytes/sector: Old cards had 256 bytes per sector, plus 8 bytes of extra info. Newer cards have 512 bytes per sector, plus 16 bytes of info.
Sectors/block: Smartmedia cards are erased one block at a time, but cards differ on how many sectors a block represents. As far as I know, it could be either 16 or 32. Cards larger than 8 megs erase 32 sectors, smaller cards use 16.
Chip ID: Here you can enter the two bytes that the card will return in answer to the "read ID" command: the manufacturer ID and the chip ID. On some models, the ID is 4-byte long, with the third byte fixed as A5. Bytes are arranged from right to left in this field, i.e. the least significant byte will be read first. The drop-list provides default values for various chip sizes.
Extended ID: Some Smartmedia cards implement a "read extended ID" command, which returns one byte of data. Here you can enter the value to be returned by this command (generally >20 to indicate that the card supports multiple writes).

Flags: An internal variable in which the simulation keeps various elements of info about the emulated card. Click [?] for details of the various bits. Most of these correspond to the flags saved in the Smartmedia file header (see below). Beware that, if you change the "No file header" flag, you will need to reformat the file by clicking [Wipe].
Command: This is the software command that the Smartmedia is currently processing, or has just completed. Click [?] for an explanation. It is better if you do not change this command manually.
Status: The status register of the emulated Smartmedia card. Click the [?] button for a breakdown the the various bits. If you change one, click [Set].

Smartmedia file format

Smartmedia image files should have a .smc extension (for SmartMedia Card). Such files begin with a 16-byte header consisting in the following fields:

TAG    TEXT 'SM'            identification characters
FLAGS  BYTE >00             >80 protected, >40 no header, >02 extended ID, >01 multiple-write
ABYTES BYTE >04             number of address bytes: 3, 4 or 5
SECSZ  BYTE >02             sector size. >01 = 256 bytes, >02 = 512 bytes
BLOCSZ BYTE >20             number of sectors per block. 16 or 32.
CHIPID DATA >0000,>EC98     extra/>A5/vendor/chip
EXTID  DATA >0021           only 1 byte is used
       DATA >0000,>0000     reserved
The file then continues with sector data in numerical order, with extra bytes after each sector. Because of these extra bytes, Smartmedia image files are not compatible with other drive image files, such as floppies or IDE hard drives (*.dsk format).

For more details on the USB-SM card check this page.

The USB DC property page

This page deals with the USB device controller part of the ISP1161 chip, i.e. the part that makes your TI-99/4A look like a peripheral when connected to another computer. The top frame contains info about the controller, mostly taken from its many registers, the bottom part info about the various endpoints.

The drop list in the upper-right corner reports the current status of the controller, and allows you so change it and to trigger special commands by selecting them from the list (commands are activated when the drop-box closes).

Device #: The contents of the DeviceAddress register, i.e. the device number to which the controller will answer when it appears on the USB bus. Legal values are 0 through >7F, bit >80 indicates that the device is enabled.
Chip ID: This should be >6123 for the ISP1161A1, ealier versions of the chip may have lower numbers. This field cannot be modified with the remote system.
Locked: The DC is locked upon waking up from a suspended state, to prevent spurious operations. It must be unlocked programmatically (by writing >AA73 to register >B0) before any other register can be accessed. You can bypass this requirement by unchecking this box. This box is not used with the remote system.
Setup ack: After a SETUP packet arrived in endpoint 0, no control endpoint can be cleared or validated until you acknowledge the SETUP packet programmatically (by accessing register >F4). You can bypass this requirement by checking this box. This box is not used with the remote system.
Hardware config: The contents of the HardwareConfiguration register. Click on the [?] button to call a dialog detailing the meaning of the various bits. If you wish to change bits from within the dialog, click [Set] when done, otherwise click [Done]. You can also enter a new value directly in the text box. You cannot set this register on the remote system (because the enslavement routine writes data byte-wise).
Mode: The contents of the DeviceMode register. You can change this value directly, or click on the [?] button to get a breakdown of the various bits.
Frame #: The current frame number, as received from the USB bus via a frame packet. You cannot change this register on the remote system.
DMA config: The contents of the DmaConfiguration register. DMA is currently not emulated (since no hardware exists to take advantage of it). Click the [?] box for a bit-wise breakdown. You cannot change this register on the remote system.
DMA count: Byte count in DMA mode. Not used by the emulation. You cannot change this register on the remote system.
Scratch: The contents of the Scratch register, which is available to the programmer to store any value in the range >0000 through >1FFF (the first 3 bits are reserved). You cannot change this register on the remote system.
Int enable: The contents of the InterruptEnable register, which indicates which interrupts can be triggered. This is a 32-bit register, but not all bits are in use. Bits >0080,0000 through >0000,0100 enable interrupt for endpoints >0E through >00 respectively. You can also access them via the "Int enabled" box of the various endpoints. Click the [?] button for a breakdown of the lower bits (NB the "short-packet detected" bit comes from a previous version of the ISP1161 datasheet, and I'm not sure it's still active in the current version of the chip). You cannot change this register on the remote system.
Interrupts: The contents of the Interrupts register, which shows which interrupts are currently active. The meaning of the various bits is the same as for the InterruptEnable register, with one extra bit: >0000,0080 which indicates that the USB bus is currently suspended (i.e. inactive for more than 3 millisecconds).  You cannot change this register on the remote system, and reading it will reset the rightmost 7 bits. Thus, this register is not read when the simulation is inactive. If you wish to read it, click the [?] box: the property sheet will remember the value and use it for display purposes (e.g. in the enpoints data). Just remember that these values may not be up to date: click [?] again to refresh them.

The bottom part of the property page displays information pertaining to the various endpoints. There are sixteen endpoints in total, but only four at a time can be displayed.
Use the [<] and [>] buttons to navigate to the next/previous set of 4.

Endpoint #: The endpoint number, from the point of view of the bus. Note that there are two endpoints 0, one for input (labelled >0, which receives SETUP and OUTPUT packets from the host), one for output (labelled 0>, which sends IN packets to the host). The remaining endpoints are thus numbered 1 through E.
In: Indicates that the endpoint answers IN packets by sending back data to the host, and is thus an output endpoint! I know it's missleading, but the USB bus is host-centric by design.
Ping-pong: Indicates that the endpoint has a dual stack. Changing this box will reconfigure all endpoints, loosing any data currently on the stacks. Inactive is the endpoint has no size.
Isochronous: Indicates a real-time endpoint. Inactive is the endpoint has no size.
Stack size: The size in bytes of the stack reserved for the endpoint. If the endpoint has a dual stack, twice this amount is actually reserved. Only certain values are allowed, depending on whether the endpoint is isochronous or not. If you enter an illegal value, it will be converted to the nearest legal value below it. Leaving the box will reconfigure all endpoints, which will compromise any data currently on stack.
Int enabled: Indicates that handshake packets to/from this endpoint will trigger interrupts (data packets for isochronous endpoint). In debug mode, errors and NAK tokens can also trigger interrupts. This box correspond to a bit in the InterruptEnable register. This box cannot be changed with the remote system.
Interrupt: Indicates that the endpoint has triggered an interrupt. This box correspond to a bit in the Interrupt register discussed above. This box cannot be changed with the remote system (read-only register).
Error code: The contents of the endpoint's error register. Click the [?] button for an explanation of the content, and possibly to modify it. These registers cannot be modified on the remote system.
Stalled: Indicates that the endpoint is stalled, and responds to requests from the host with the STALL token (although an incoming SETUP token will unstall endpoint 0). This bit and the following ones are part of the EndpointStatus registers.
Pong full: Indicates that the "pong" stack contains data (this stack is only used by endpoints that have dual stacks, in alternance with the "ping" stack). With the remote system, you can only tick that box for output (i.e. "IN") endpoints (which sends a "validate endpoint" command) and you can only clear it for input endpoints (which sends a "clear endpoint" command).
Ping fill: Indicates the the regular "ping" stack contains data. The "Clear endpoint" command reset this bits, the "Validate endpoint" command sets it. See above for restrictions with the remote system.
DATA1: Indicates that the token for the next data packet should be DATA1 (as opposed to DATA0). This box cannot be modified with the remote system.
Setup ovf: Indicates a setup overflow, i.e. a second SETUP token has arrived to this endpoint before the previous one was acknowledged. This box cannot be modified with the remote system.
Setup arrived: Indicates that the endpoint has received a SETUP token from the host. This box cannot be modified with the remote system.
Use pong: For dual-stack endpoints only, indicates that the "pong" stack is currently in use. This toggles when you validate or clear the endpoint. This box cannot be modified with the remote system.
On stack: The number of bytes currenly on stack. This number is listed in the first two bytes of the stack for assynchronous endpoints. Isochronous endpoints do not save this value on stack, so the field will always contain 0 in this case. If an endpoint is not enabled, this field will contain "N/A". This field is not used with the remote system.
[View] Click this button to view the contents of the endpoint's stack in a Memory window. This is a trick of the simulation (the hardware does not allow this): stacks appear at address >5000 when you set the page number as >5000 (endpoint 0) through >500F (endpoint 14). You can also view the entire device controller memory by manually setting the page as >5100. These buttons are inactive with the remote system.

For more details on the USB device controller, see this page, or the ISP1161 datasheet.

Future plans

Bug99 is far from being completed. For one thing I am sure it still contains many bugs. As the saying goes: "He who thinks he got all the bugs hasn't turned enough stones". I have preciously little time to devote to this project, and most of it is used to implement new features rather than in turning stones. So I am counting on you there, to find the bugs and report them to me...

I am also taking requests. This program was mostly written as a tool for myself, to work on the development of the USB card DSRs. But since I've decided to make it available, I am also interested in knowing what feature you think is still missing. For your information, here are several things I am planning for the future:

- Add a source file viewer tool, which can keep track of execution and show the current address (and possibly set breakpoints) in the source file.
- Allow windows docking, wich windows stretching/shringkig automatically to cover the entire surface of the application window.
- Complete the emulation of the USB card (host controller missing).
- Emulate the P-Gram and HSGPL cards.
- Include a source file editor with syntax coloring, and a scanning routine to detect segments, procedure, lables, etc.
- Trace subroutine calls and returns, have a special window to display call stacking.
- Have a way to call external tools like assemble and linker and automate the process so that Bug99 may become a real Integrated Development Environment (IDE).
- Write a procedural cross-assembler, possibly even an object-oriented one.

Version 1.0 28/8/09
Version 1.1 22/12/09. Added bus window, USB DC page, remote debugging of emulators.
Version 1.2 10/3/10. Change protocol for external emulators, added online help, corrected SB bug in simulation.

Back to the TI-99/4A Tech Pages