TI-99/4A Computer

Page Contents

small arrow Description
small arrow Cortex BASIC
small arrow TI Invaders - Disk Version
small arrow Miscellaneous Programs
small arrow "The Valley" Adventure Game
small arrow EPROM Programmer
small arrow Compact Flash Drive Utility
small arrow Power Supply Consumption
small arrow 32K Memory Expansion in Console
small arrow Mini Memory Module Line-By-Line Assembler Bug
small arrow Mini Memory Line-By-Line Assembler, TIBUG
     and Disassembler Cartridge
small arrow Cartridge Bank Switching
small arrow Resurrecting a TI Disk System
small arrow Checking a Disk Controller Card DSR
small arrow Connecting a Serial Mouse
small arrow Internet Web Browser
small arrow I2C Interface
small arrow Example TIPI Interface Routines
     small arrow TIPI Network Socket Interface
     small arrow TIPI Mouse Interface
Rob Tempelmans Plat award logo
Honoured to receive the Rob Tempelmans Plat award in 2013
and an Edgar Mauk Award in 2018

Description

The Texas Instruments TI-99/4A was an early home computer, released in June of 1981. The computer holds the distinction of being the first 16-bit personal computer, having a 16-bit TMS 9900 CPU running at 3.0 MHz.

TI-99/4A photo For a full description of the system, see this comprehensive Wikipedia entry. TI-99/4A start screen

Cortex BASIC

This is a port of the 'Power BASIC' interpreter used with the TMS9995-based Powertran Cortex machine. Features and constraints are as follows:

Code for direct control of the RS-232 and parallel port hardware (bypassing use of the DSRs) is also available, but including this will obviously reduce the amount of RAM available for program storage.

Program Download

Various download options are available, depending on how you intend to run the program. For each option, there is also the choice of whether to download the 40-column or 80-column text version. Download links are in the table below, with a description of the various options below that.

Text Version Disk Image 32K EPROM Image,
Inverted Bank Switching 
32K EPROM Image,
Non-Inverted Bank Switching 
512K EPROM Image,
Non-Inverted Bank Switching 
40-column Cortex BASIC 40 v1_7.zip  Cortex BASIC 40 32K
Inverted v1_7.zip
Cortex BASIC 40 32K
Non-Inverted v1_7.zip
Cortex BASIC 40 512K
Non-Inverted v1_7.zip
80-column
(requires F18A)
Cortex BASIC 80 v1_7.zip  Cortex BASIC 80 32K
Inverted v1_7.zip
Cortex BASIC 80 32K
Non-Inverted v1_7.zip
Cortex BASIC 80 512K
Non-Inverted v1_7.zip

Disk Image

The program is on a TiDisk format image and named "CBASIC". This disk image format works with all the simulators listed above (F18A support needed for the 80-column version) and with the CF7+/NanoPEB device on real hardware. To run the program, boot with the Editor Assembler cartridge, select E/A option 3 and type the filename DSKn.CBASIC. The program should load and display the *Ready prompt within a few seconds.

With the disk versions, the size of the code means that only about 5K of RAM is available for your program and variable storage. Programs are stored in a tokenised format so what free memory there is is used quite efficiently.

32K EPROM Image, Inverted Bank Switching

This is a binary image which can be used as follows:

With the bank-switched cartridge versions, some of the code is copied from the cartridge to RAM, and 8K is left running from the cartridge, so just over 14K of RAM is available for your program and variable storage.

32K EPROM Image, Non-Inverted Bank Switching

This is a binary image which can be used as follows:

As with the 32K EPROM image version above, just over 14K of RAM is available for your program and variable storage.

512K EPROM Image, Non-Inverted Bank Switching

This is a binary image which can be used as follows:

As with the 32K EPROM image versions, just over 14K of RAM is available for your program and variable storage.

Documentation

The Cortex BASIC user guide is available which details the Cortex BASIC implementation. This document was recreated by Jim Fetzner from a poor quality photocopy.

Keyboard Mapping

Cortex Key Console Key Classic99 (PC Keyboard) Key  
Enter ENTER Return
Edit CTRL-T Ctrl-T
Clear CTRL-L Ctrl-L
Escape FCTN-9 (BACK) Esc or Alt-9
Insert FCTN-2 (INSERT) Insert or Alt-2
Delete FCTN-1 (DELETE)   Delete or Alt-1
Rubout FCTN-3 (ERASE) Alt-3
Cursor Up FCTN-E Cursor Up
Cursor Down FCTN-X Cursor Down
Cursor Left FCTN-S Cursor Left
Cursor Right   FCTN-D Cursor Right
Home CTRL-H Home

Sample Program Listings

Some sample program listings can be downloaded here. See the @Program Descriptions.txt file within the zip file for program descriptions.

All these programs were typed in or developed on my TM990 system but many will run on the TI-99/4A without modification. Those that interact with the hardware such as the SID chip will require modification to run on the TI-99/4A - I leave these for the reader to experiment with. You will have to work out a way to enter these listings on your system if you don't simply want to type them in (hint: if you're using the Classic99 simulator, copy the text listing to the clipboard then use the Edit > Paste command to 'type' them in).

Most of these programs will require you to use a cartridge version of Cortex BASIC (the 40-column version is probably best to match the original display) - there won't be enough RAM available if you use the disk version.

Changes and Restrictions in the TI Implementation

There are some changes and restrictions in the TI implementation as compared to the implementation described in the Cortex user guide. Note that some commands work differently to their familiar equivalents in TI BASIC. If you think you've found a bug in the interpreter code, download the Cortex emulator (written by David Hunter) and check your program on there. You can report any confirmed bugs to me at ti99(at)stuartconner(dot)me(dot)uk.

Revisions
1.0 - 29 Jan 2011 - Initial release.
1.1 - 10 Feb 2011 - Added load/save functionality.
1.2 - 17 Feb 2011 - Moved most processor workspaces to 16-bit 256-byte console RAM to increase speed.
1.3 - 01 Apr 2011 - Improved key handling with the KEY() function, which would previously miss some key strokes. To check for keys pressed in a game for example, call NOESC at the start of the program, then A=KEY(0) to check for a key press, and IF A=01BH THEN STOP as part of the key handling routine to enable <Escape>ing out of the program.
1.7 - 05 Dec 2015 - Refactored to enable code to build image for either E/A option 3 file or TI-99/4A bank-switched cartridge.

TI Invaders - Disk Version

This is a disk version of the TI Invaders cartridge, created from an original program listing. The game can be downloaded here (Invaders.zip, V9T9/.tidisk format). The program name is INVADERS - load using Editor/Assembler option 3. The program auto-starts on loading.

The game controls are 'S' and 'D' to move left and right, and '.' to fire.

The program supports the cheat mode, which is accessed when on the title screen by pressing <Shift>838. This prompts for a Y/N to play slow speed then two digits for the number of the screen to start on. None of these inputs will be displayed, and it is not necessary to press <ENTER>.

Miscellaneous Programs

Disk: Miscellaneous.zip (V9T9/.tidisk format)

Programs with names in black are written/generated by me. Programs with names in red are copied from other sources.

CATALOG BASIC disk catalogue program from TI disk drive manual.
CFMGR
CFMGS
Compact Flash Drive Volume Manager.
E/A 5 format.
CONV_BASES Subroutine (for MERGE-ing into other programs) to convert a binary, decimal or hexadecimal number to binary, decimal or hexadecimal. Lines numbered from 10000.
DISASSMBLR TMS 9900 assembly language disassembler, running under TI Extended Basic with 32K RAM.
DISASS/S
DISASS/O
Source and object files for assembly language utility to print to the screen in text mode (40 characters × 24 lines).
MGR3
MGR4
DM-1000 (V3.5) Disk Manager (copied from Funnelweb).
E/A 5 format.
CART_RAM/S
CART_RAM/O
Source and object files for assembly language program to save/load the contents of the SuperCart RAM (>6000 - >7FFF) or MiniMem RAM (>7000 - >7FFF) to/from a data file. Also has an option to fill the RAM with zeroes. Load using E/A option 3 (for the SuperCart) or MiniMem option 3, 1 (for the MiniMem). Program auto-starts on loading and detects the cartridge type. Requires 32K RAM expansion to be present.

The following RAM image files can be loaded using the CART_RAM/O program:
SC_START Data file containing the RAM image for the SuperCart startup menu.
MM_LBLA Data file containing the RAM image for the MiniMem Line-By-Line Assembler (LBLA). Program names: OLD, NEW.
MM_LINES Data file containing the RAM image for the MiniMem Lines demonstration program. Program name: LINES.

To load and run the MiniMem LBLA for example:

(1) Download the Miscellaneous.zip file from the link above, unzip it, and make a disk out of the file. (In the case of a CF7+/nanoPEB device, copy the file onto the CF card using the PC and the file transfer utilities provided with the device.)

(2) Insert the MiniMem cartridge, then from the title screen select option 3 then option 1, and specify file DSK1.CART_RAM/O (substitute DSK1 for whichever disk drive you're using). If this gives a Memory Full or Duplicate Definition error, reset the console, select option 3 then option 3 then press the <Proceed> key to reinitialise the MiniMem RAM, and start again.

(3) When the program loads and runs, select option 2 and specify file DSK1.MM_LBLA. When this has loaded, select option 4 to reset the console.

(4) From the title screen, select option 3 then option 2, and specify program name NEW. The LBLA screen should now be displayed ready for input.

RD_REF_TBL BASIC program to display the contents of the REF/DEF table when running with the Extended Basic, Editor/Assembler or MiniMem modules.
SYSTEX XB/assembly language hybridisation program. Version 1.0, ©1985, Barry Boone. Allows you to save assembly language programs/utilities as part of XB program files.
XBOPT5 E/A 5 format loader for Extended Basic. Version 3.1, 1987, Barry Boone. To use, make a copy of the program, then edit line 110 to specify the program to load.

"The Valley" Adventure Game

"The Valley" was an adventure game published as a "type in" program listing in the UK Computing Today magazine, April 1982. It was originally developed for the Commodore PET but the "universal BASIC" listing enabled it to be easily adapted to other micros of the time such as the Tandy TRS-80, Oric and Sharp MZ-80K. You can read more about the history of the program here and here.

Choose your character type carefully ... Barbarians recover quickly but their magic doesn't come easily. A Wizard? Slow on the draw and slow to mature ... but live long enough and grow wise enough and your lightning bolts are almost unstoppable ...

The Valley is a real-time game of adventure and survival. You may choose one of five character types to be your personal 'extension of self' to battle and pit your wits against a number of monsters. Find treasure, fight a Thunder-Lizard in the arid deserts of the Valley, conquer a Kraken in the lakes surrounding the dread Temples of Y'Nagioth, or cauterise a Wraith in the Black Tower. In fact, live out the fantasies you've only dared dream about. BUT BEWARE ... more die than live to tell the tale!
Computing Today Magazine Cover

A version of the game to run on the TI-99/4A can be downloaded here (The_Valley.zip, V9T9/.tidisk format). The program requires XB and 32K memory.
To run the program, load and run the file RUN (OLD DSK.THE_VALLEY.RUN). Program developed and tested on a console with 32K × 16 bit RAM and CF7+ Compact Flash Drive. Tested on the Win994a simulator.

The Valley - splash screen The Valley - main screen The Valley - Black Tower of Zaexon

EPROM Programmer

This project is an EPROM Programmer to read and program TMS 2708 (1K × 8) and TMS 2716 (2K × 8) EPROMs Note 1. It was developed to program EPROMs for my TM 990 system. The circuit is based around a TMS 9901 Programmable Systems Interface IC which is used to apply address, data and control signals to the EPROM. A control program, written in assembly language, enables the user to view the data on an EPROM as both hex and ASCII, to verify that an EPROM is blank, to save the data from an EPROM to a data file, to save an EPROM image in memory to a data file, to program an EPROM from a data file, and to compare the data on an EPROM with a data file.

The board is designed to plug into the TI-99/4A console side port via a Y-cable (side port splitter), which allows the PEB or other storage device to also be connected at the same time Note 2. The board requires a +12V supply, which can be provided by fitting a DC power socket to the side of the console, wired directly to the +12V supply from the internal power supply board.

A circuit diagram of the EPROM Programmer is available here. The object code for the control program Note 3 is available here (disk in V9T9 format; load program EPROM/O using Editor/Assembler option 3). Screenshots of the control program in use are shown below - click each for a larger image.
EPROM Programmer PCB

EPROM Programmer - menu EPROM Programmer - verify EPROM is blank EPROM Programmer - view EPROM data EPROM Programmer - programming EPROM

Note 1: Texas Instruments TMS 2716 EPROMs have a different pinout and programming requirements from 2716 EPROMs from other manufacturers. The board is compatible with TMS 2716 EPROMs from Texas Instruments only.

Note 2: The board and software have been developed using a TI-99/4A with a CF7+ Compact Flash Drive for program storage. It has not been tested with a PEB attached, but there is no reason why it should not work. Note also that I found the CF7+ Compact Flash Drive a bit temperamental connected to the Y-cable as supplied. I ended up shortening the 'arm' of the cable that the CF7+ was connected to, and it then worked perfectly. Positioning the two arms of the Y-cable at 90° to each other may also be sufficient for reliable operation.

Note 3: The algorithm to program an EPROM contains some timing loops which control the application of the +26V programming pulses. The object code compiled here is for a TI-99/4A with 32Kbyte RAM on the internal 16-bit data bus. If the program is to be used on a console with external 32Kbyte RAM (on a CF7+ or in the PEB for example), these programming pulses will be extended (as the console is running slower), but an EPROM will probably still program reliably.

Compact Flash Drive Utility

This CF To Disk Transfer Utility helps you copy disk volumes between a CF card and your hard drive. It is in essence a graphical front end to the cf2dsk.exe and dsk2cf.exe programs supplied with the TI-99/4A Compact Flash Drive. Full details are available in this readme file (including how to run the utility and the cf2dsk.exe and dsk2cf.exe programs under Windows 7).

(The files originally provided with the CF7+ drive can be downloaded here or from the designer's website.)
CF To Disk Transfer Utility screenshot

Power Supply Consumption

The console motherboard, fitted with the TI Extended BASIC cartridge, draws the following current from the power supply board:

On the 'standard' power supply board, the +5V regulator is a switched mode design, and the +12V and -5V regulators are of the linear type.

The NanoPEB draws from the +5V supply ~75mA 'at rest', rising to ~85mA when accessing the CF card .

32K Memory Expansion in Console

This modification adds 32K RAM to the console, internally on the 16-bit data bus. The RAM occupies the same address ranges as the 32K RAM expansion card normally fitted in the Peripheral Expansion Unit: >2000 - >3FFF and >A000 - >FFFF. The modification disables the wait state generation circuit when the RAM is accessed, making it faster than the RAM in the external 32K RAM expansion card.

The circuit diagram for the modification is shown below. The circuit diagram shows the original console components and connections in grey, with the modifications shown in black. A photo of the completed modification is shown below the circuit diagram.

The modification uses two 128Kbyte CMOS RAMs (NEC type UPD431000ACZ-70, equivalent parts should work). 24 pins of these 32 pin devices are largely pin-for-pin compatible with the existing console ROMs U610 and U611, which makes it easy to piggy-back them on the ROMs with the non-compatible pins and the extra pins bent out and wired individually. The extra address lines and the /WE signal are taken directly from the processor.

Existing 3-to-8 line decoder U504 provides convenient address decoding for the address space to be occupied by the RAM. A quad AND 74LS08 device is used to combine the chip select lines to drive the RAM /CS and /OE lines. A switch (fitted on the console back panel) and 1K pull up resistor on these lines enables the RAM expansion to be disabled, if required. The 74LS08 device is piggy-backed on another 14 pin LS device on the board for the +5V and GND supplies, with the other pins wired individually.

The existing console ROMs and scratchpad RAMs are fitted on the 16-bit data bus, and the existing circuit which includes NAND gate U606 pins 11/12/13 is used to disable the wait state generation circuit when the ROMs or scratchpad RAMs are accessed (wait states are introduced for all memory accessed through the console 16-to-8 bit data multiplexer). The same circuit is used to disable the wait states for the 32K expansion RAM by ANDing the ROM chip select with the RAM chip select, and feeding this to U606/13. This pin is physically cut to isolate it from the ROM chip select routed through the board (alternatively, the relevant trace on the board could be identified and cut), and the new ROM/RAM chip select wired to it.

32K RAM expansion in console circuit diagram

32K RAM expansion fitted in console
(Ignore the grey cable on the left - that's a reset switch)

Mini Memory Module Line-By-Line Assembler Bug

The Line-By-Line Assembler (LBLA) provided on cassette tape with the TI-99/4A Mini Memory module contains a bug, whereby if assembling instructions with two symbolic addressing operands (for example MOV @L1,@L2), and both operands are unresolved references (that is, L1 and L2 in the previous example have not yet been defined), the first instruction will be assembled correctly, but further instructions will not as the label for the second operand is not added to the symbol table, and hence is not 'filled in' when the label is defined later.

The bug is in a small loop starting at address >7248 that clears memory addresses >7194 - >719F after assembling each instruction. This loop also needs to clear one extra word (address >71A0) as this word is used when assembling an instruction to record whether the second operand is an unresolved symbolic reference or not. The bug can be fixed by simply increasing the loop counter by 1, as follows:

  1. Load the LBLA in the normal way using Easy Bug.
  2. After loading, while still in Easy Bug, type M724F followed by the <Enter> key.
  3. The value 06 should be displayed. Type 07 followed by the <Enter> key.
  4. Press the '.' key to return to the Easy Bug prompt.
  5. Type S7000 followed by the <Enter> key to save the corrected program back to cassette tape.

A .wav file of the LBLA cassette can be downloaded here.

Mini Memory Line-By-Line Assembler, TIBUG and Disassembler Cartridge

This is a simple cartridge which contains a copy of the Mini Memory Line-By-Line Assembler (LBLA), TIBUG monitor (which is similar to the Mini Memory EASY BUG and the Editor/Assembler Debugger program), and a disassembler.

The cartridge is 8K and works with a 27(C)64 EPROM on a 64K/128K ROM cartridge board. The EPROM image can be downloaded here.

32K RAM expansion is required to use the cartridge. The programs in the cartridge use the memory block >2680 to approximately >2840 in the low memory area, so avoid assembling any programs at these memory locations.

With the cartridge inserted, the TI-99/4A master selection list includes an entry "LBLA & Utilities". Selecting this option displays the menu screen shown to the right, where each program can be selected. You can return from within each program to this menu screen, enabling you to easily switch between the programs.

The following sections describe the three programs. This is followed by a section giving an example of how to use the three programs together.

Note that TIBUG and the disassembler use the same screen display routines as the LBLA, which has a nine-page buffer so you can scroll back to review previous content. If you use TIBUG or the disassembler for long periods then you may see previous content appear and be overwritten on the screen as the display buffer 'wraps round'.
LBLA & Utilities cartridge

Line-By-Line Assembler

The Mini Memory LBLA manual can be downloaded here (tip: if you're viewing the manual using Adobe Acrobat Reader, right-click the vertical pages then click 'Rotate Clockwise' to make them more readable). This implementation of the LBLA is as described in the manual except for the following:

A number of system utility routines are provided in the cartridge, as listed below. Use of these routines is demonstrated in the example code later.

The cartridge also contains some of the Mini Memory system utility routines, but at different addresses to those shown in the Mini Memory manual (which can be downloaded here). The utilities available and the addresses to use are listed below. Full details of each utility are given on pages 35 and 36 of the manual. Note that if your program returns to TIBUG, anything that you print to the screen using these routines is likely to be overwritten by the screen scrolling routine that TIBUG (and the LBLA) use.

TIBUG Monitor

TIBUG is a debug monitor which provides an interactive interface between the user and the computer. TIBUG is similar to the Mini Memory EASY BUG and the Editor/Assembler Debugger program.

TIBUG is described on this page. Note that in the TI-99/4A implementation:

Disassembler

The disassembler is a port of the TMS 9900 disassembler included in the Powertran Cortex.

Running the disassembler prompts for a start address and a stop address. If the start address is not specified, it defaults to >F000 (the default assembly start address for the LBLA).  If the stop address is not specified, it defaults to >F100.

If a non-zero stop address is specified, the program disassembles the code from the start address to the stop address. Pressing any key during the process terminates the listing and displays a prompt to press the <Back> key to quit (which returns to the program menu screen) or to press any other key to re-display the start and stop address prompts.

If an end address of >0 is specified then the program disassembles the code from the start address but pauses after each line. Press the <Back> key to quit or any other key to disassemble the next instruction.

Example

The following example shows how to use the LBLA to enter a program, to run that program using TIBUG, and then to disassemble the program you entered. The example program demonstrates the use of some of the system utility routines provided in the cartridge. The program:

  1. clears the screen;
  2. display a text message on the screen;
  3. loops waiting for a key press and prints that character on screen, until the <Space> key is pressed;
  4. returns to TIBUG.

Starting from the program menu screen, select option 1 to run the LBLA, then enter the program below. User input is shown underlined (and must be followed by pressing <Enter>). On lines where no label is to be input, remember to press <Space> to step over the symbol field before entering the instruction.

F000 0420    BLWP @>6292        Clear the screen.
F002 6292 
F004 0420    BLWP @>62A6        Print text message.
F006 62A6 
F008R0000    DATA HI            Address of the text string to display (label not yet defined).
F00A 0420    BLWP @>62A6        Print <CR><LF> to go to a new line on the screen.
F00C 62A6 
F00ER0000    DATA CR            Address of <CR><LF> text string (label not yet defined).
F010 020C    LI R12,>468A       Load hex number to print.
F012 468A 
F014 0420    BLWP @>629A        Print contents of R12 as 4 hex characters.
F016 629A 
F018 0420    BLWP @>62A6        Print <CR><LF> to go to a new line on the screen.
F01A 62A6 
F01CRF00E    DATA CR            Address of <CR><LF> text string (label not yet defined).
F01E 04CC    CLR R12            Clear R12.
F020 0420 L1 BLWP @>62A2        Read a character from keyboard into MSB of R12.
F022 62A2 
F024 028C    CI R12,>2000       <Space> character?
F026 2000 
F028R13FF    JEQ EX             If yes, jump to exit (label not yet defined).
F02A 0420    BLWP @>629E        Print character in MSB of R12.
F02C 629E 
F02E 10F8    JMP L1             Loop round.
F030 0460 EX B @>6E9E           Return to TIBUG.
F032 6E9E 
F028*1303                       (Address >F028 now filled in with jump offset to label EX)
F034 4845 HI TEXT 'HELLO WORLD' Text to display.
F036 4C4C 
F038 4F20 
F03A 574F 
F03C 524C 
F03E 4400                       (>00 terminating byte added automatically to make number of characters even)
F008*F034                       (Address >F008 now filled in with address of label HI)
F040 0D0A CR DATA >0D0A         ASCII codes for <CR><LF> characters.
F01C*F040                       (Address >F01C now filled in with address of label CR)
F00E*F040                       (Address >F00E now filled in with address of label CR)
F042 0000    DATA >0000         >00 terminating byte for message string.
F044 0000    END                Finished.
     0000 UNRESOLVED REFERENCES (Press <Enter> again to return to the program menu screen.)

You should now be back at the program menu screen. Select option 3 to run TIBUG.

At the TIBUG command prompt, type R to inspect the workspace pointer, program counter and status registers. The workspace pointer is displayed first. The default value of >2680 is fine for the example program. If an alternative value is needed it can either be entered here, or a LWPI instruction could be used as the first instruction in the program. Press <Space> to display the program counter. The default value of >F000 is pointing to where the example program was assembled, so there is no need to change it. Press <Space> again to display the status register. No special value is needed so press <Space> again to return to the command prompt.

To run the program, type E. This should display the text "HELLO WORLD" and the hex characters 468A, then display any key pressed. Pressing <Space> exits the program and returns to TIBUG.

To demonstrate the breakpoint command, a suitable address to break into the program is at address >F014, where the contents of R12 is printed as 4 hex characters. To set the breakpoint and run the program, type B F014<Enter> (the <Space> after the "B" is added automatically). The program runs from the address set in the program counter (check or change it using the R command) and displays the text "HELLO WORLD", then breaks and displays the contents of the workspace pointer, program counter and status register at the break point. Type E to continue running the program from the break point. Press <Space> to exit the program and return to TIBUG, then type Q to exit TIBUG and return to the program menu screen.

At the program menu screen, select option 4 to run the disassembler. Enter F000 at the start address prompt and F020 at the stop address prompt. Compare the disassembled listing with the program listing above. Note that the disassembler tries to interpret the DATA statements at addresses >F008, >F00E and >F01C as instructions as it has no way of knowing that these memory locations contain data, not instructions. Press <Enter> and disassemble the text strings between addresses >F034 and >F042, and note again how it tries to interpret the text strings as instructions. If disassembling some unknown code, you need to be aware that there might be data or text in the code that the disassembler is not able to recognise. Finally, press <Back> to return to the program menu screen.

Cartridge Bank Switching

Introduction

Software cartridges can contain up to 8Kbyte of ROM, which is decoded in the console to appear in the TI-99/4A memory map address range >6000 - >7FFF. A bank-switching technique can be used in a cartridge to 'page' different banks of a larger ROM into the >6000 - >7FFF address space. Such a cartridge requires additional hardware (self-contained in the cartridge) to control the paging. Some examples of how bank switching can be used include:

Implementing Bank Switching in a Cartridge

One method of implementing bank switching is to latch one or more low-order address lines when writing to the cartridge, and to apply these latched bits to the ROM high-order address lines to select different 8Kbyte banks. By writing to different specific addresses, different binary codes can be latched from the low-order address lines, and hence different binary codes can be applied to the ROM high-order address lines to select specific 8Kbyte banks. Writing to a ROM obviously has absolutely no effect on the data stored in the ROM, which can contain any data required - this is the data read when reading from the ROM, with bank page selection occurring when writing to the ROM. A circuit diagram of such an implementation is shown below, followed by a detailed description of how the circuit works when selecting a ROM bank. The circuit is from Jon Guidry, who has continued the design work of others and has had PCBs manufactured and cartridge kits made up for home assembly.

Bank switching circuit diagram

The circuit uses a 74LS379 device to latch one or more low-order address lines. For a 16Kbyte ROM, one address line needs to be latched, which gives 2 banks of 8Kbyte. For a 32Kbyte ROM, two address lines need to be latched, which gives 4 banks of 8Kbyte, and so on. The latch is clocked by the WE* line and enabled by the ROMG* line so that the device latches the address lines only on write operations to the >6000 - >7FFF address range. The ROM high-order address lines are fed from the latch Qx* outputs; there appears to be no particular reason why the latch's inverted outputs are used - it is possibly for compatibility with earlier designs. Processor address line A15 is not latched because of the word-based nature of the processor - A15 is toggled both high and low during a single memory write operation.

Selecting a ROM Bank

To select a ROM bank, a word of data (any data, it doesn't matter) has to be written to a specific address in the >6000 - >7FFF address range. At circuit level, this is what happens:

  1. The processor sets up the write address on its address bus A0 - A15. (It also sets the data up on the data bus but that is irrelevant at the moment.) It also brings its MEMEN* signal low - this is not shown in the circuit diagram, but that action in the console brings the ROMG* signal low (assuming the write address is an address in the cartridge address space), so the 74LS379 latch is now 'enabled' - meaning it is outputting signals on its Qx and Qx* outputs and will respond to the clock input.
  2. With the address bus now set up, the processor pulses WE* low. This clocks the 74LS379, so whatever binary code the device is seeing on its Dx inputs it presents on it Qx outputs, with inverted signals on the Qx* outputs. Each time the 74LS379 is enabled in the future, this same binary code will be presented on the outputs again until the device is clocked again to latch a (possibly) different binary code.
  3. The processor now brings MEMEN* high, which brings ROMG* high to disable the 74LS379, and performs the next instruction.

When an address in the cartridge ROM is read, the processor sets up the read address on the address bus A0 - A15, and brings *MEMEN low which brings *ROMG low. This enables the 74LS379, so as described before, it presents the address bits last latched (clocked) on the Qx* outputs. The ROM is now seeing 13 address lines from the console (which select an address within the 8Kbyte address space), plus another one or more address lines from the 74LS379 (which select an 8Kbyte bank within the ROM). These address lines are addressing a specific byte in the ROM, and as the ROM CS* line is low (because it is connected to ROMG*) the ROM outputs the data stored at that address to be read by the processor.

To select a ROM bank, a word of data has to be written to a specific address in the >6000 - >7FFF address range. To calculate the address to write to to select a particular bank, the following logic applies:

  1. Consider a 64Kbyte ROM which uses 3 latched address lines, giving 8 banks. Number the banks 0 - 7.
  2. Take the number of the bank required, in binary, and invert it (because the circuit uses the 74LS379 inverted Qx* outputs rather than the Qx outputs). So bank 0 is 111, bank 1 is 110, bank 2 is 101, and so on.
  3. Specify the inverted number of the bank required on processor address lines A12 - A14. Also, address lines A0 - A2 have to be 011 to select the cartridge space >6000. So, address:

<-A0          A15->
011x xxxx xxxx 111x will select bank 0
011x xxxx xxxx 110x will select bank 1
... up to ...
011x xxxx xxxx 001x will select bank 6
011x xxxx xxxx 000x will select bank 7

'x' can be either a 1 or a 0 - it doesn't matter to the latch - but for convention assume they are 0.

So, writing to address:

<-A0          A15->
0110 0000 0000 1110 = >600E will select bank 0
0110 0000 0000 1100 = >600C will select bank 1
... up to ...
0110 0000 0000 0010 = >6002 will select bank 6
0110 0000 0000 0000 = >6000 will select bank 7

As 'x' can be either a 1 or a 0, banks can be selected at other addresses as well ...

<-A0          A15->
0111 1111 1111 1111 = >7FFF will select bank 0
0111 1111 1111 0001 = >7FF1 will select bank 7

... but it makes sense to stick to a convention of having 'x' as 0.

To select a bank in assembly language, any of the following will work (just substitute >600E for the appropriate address of the bank to select):

MOV R0, @>600E (writes the value in R0 to the ROM; the value in R0 is immaterial)
CLR @>600E (writes the value 0 to the ROM)
MOV @>600E,@>600E   (reads from the ROM, then does a write)

Sample Code for Cartridge Header to Copy Cartridge Memory Banks to 32K Memory Expansion

The following code is for a cartridge header which copies code from a 2-bank cartridge to the 32K memory expansion, then branches to an address in the memory expansion to run it.

********************************
* JON GUIDRY - MARCH 2009      *
* ROM CARTRIDGE HEADER,        *
* 74LS379 BANK SWITCH ROUTINE, *
* & COPY/EXECUTE @ >A000       *
* 9900 ASSEMBLY LANGUAGE       *
********************************
* THANKS TO TURSI, STUART      *
* CONNER, AND BOB CARMANY      *
* FOR THE HELP!                *
********************************

* BANK 0/1 ROM HEADER FOR 16K BANKED
* SWITCHED CART

       DEF SFIRST,SLAST,SLOAD
       UNL

* ROM HEADER
       AORG >6000    * START ADDRESS OF CARTRIDGE ROM

SFIRST EQU $
SLOAD  EQU $

GRMHDR BYTE >AA      * INDICATES A STANDARD HEADER
       BYTE >01      * VERSION NUMBER
       BYTE >01      * NUMBER OF PROGRAMS (OPTIONAL)
       BYTE >00      * NOT USED
       DATA >0000    * POINTER TO POWER-UP LIST (CAN'T USE IN CARTRIDGE ROM)
       DATA PROG     * POINTER TO PROGRAM LIST
       DATA >0000    * POINTER TO DSR LIST
       DATA >0000    * POINTER TO SUBPROGRAM LIST
       DATA >0000
       DATA >0000
PROG   DATA >0000    * NO NEXT MENU ITEM
       DATA MAIN     * PROGRAM START ADDRESS FOR THIS MENU ITEM
       BYTE 12       * LENGTH OF TEXT FOR MENU SCREEN
       TEXT 'PROGRAM NAME'
       EVEN

* THIS IS THE ROUTINE THAT WILL
* SWAP BANKS TO HIGH BANK & COPY
MAIN   EQU $
       LWPI >8300
       MOV R0,@>6000 * SELECT BANK
       LI R4,>0740   * NUMBER OF BYTES TO COPY, DIVIDED BY 4
       LI R9,>6300   * ADDRESS TO COPY FROM
       LI R10,>A000  * ADDRESS TO COPY TO
LP1
       MOV *R9+,*R10+
       MOV *R9+,*R10+
       DEC R4
       JNE LP1

* THIS IS THE ROUTINE THAT WILL
* SWAP BANKS TO LOW BANK & COPY
MAIN2  MOV R0,@>6002
       LI R4,>0740
       LI R9,>6300
       LI R10,>BD00
LP2
       MOV *R9+,*R10+
       MOV *R9+,*R10+
       DEC R4
       JNE LP2

* ALRIGHT, WE'RE DONE. LET'S GO!
       B @>A000

FINISH EQU $
SLAST  END

Resurrecting a TI Disk System

Most TI disk systems use either a sidecar disk drive controller (PHP1800) or a disk drive controller expansion card (PHP1240) mounted in a peripheral expansion box (PEB). This section covers a third option - a disk drive controller expansion card connected directly to the console using a special ribbon cable. This was purchased off eBay having previously been acquired from a woman whose husband worked for TI. A thread about the system on one of the TI-99 forums suggests that the special ribbon cable may have been made at the TI-99 European research centre at Almelo in the Netherlands. As well as the ribbon cable and disk drive controller, two Siemens FDD200-5 floppy drives were included in the purchase. These are DSDD drives, but the disk controller limits their use to DSSD. The drives are mounted in a rather handy chassis that has been reused from some other piece of equipment. TI disk system
Console extension ribbon cable The ribbon cable has a connector at each end, one to mate with the console side port, and the other with the same pinout as the expansion card connectors in the PEB. The connectors are mounted in paxolin blocks which have been cut, milled and drilled - very tidy work. The 5-pin DIN plug is for connecting an external power supply to power the disk controller card. The whole arrangement is entirely passive apart from five 47Ω pullup resistors (to +5V) on pins 12, 16, 45, 46, 48 at the expansion card end which perform the function of those normally found on the flex cable interface card in the PEB (the flex cable interface card also has pullup resistors on pin 13 and 15, but these pins are not connected through the ribbon cable). Further detailed photos of the ribbon cable can be seen here, here and here. The connections between the two connectors on the ribbon cable are detailed in the table further down this page.

The sliding door has to be removed from the console side port to enable the connector at the console end of the cable to fit, and the connector wiggled a little so it slips past the metal earthing fingers each side of the console connector.

The disk controller card has been modified to disconnect the +5V, +12V and -5V voltage regulators and strap the power supplies straight through onto the card, allowing the use of an external regulated power supply. A few of the TTL ICs are socketed and presumably have been replaced at some point in the past. A picture of a standard card is available here.

Getting the disk system to work proved to be an interesting experience ...

First I had to disassemble the disk controller end of the cable to determine the wiring to the DIN plug used for power to the disk controller. As the disk controller card has been modified to accept regulated supplies directly, it can be conveniently powered from an old PC ATX power supply that can also power the floppy drives themselves, so I wired up a DIN plug to connect with the power plug at the end of the cable.

So with the cable connected to the console, power connected to the cable, but the disk controller card not connected, the console boots fine. Connect the disk controller card however and the console hangs immediately power is applied. So out with the multimeter and check for shorts to ground on each of the disk controller card connector pins. This reveals that the READY signal has a resistance of only 20Ω to ground. It is driven by a 74LS125, so remove the old one, fit a socket and pop a new one in. The console now boots fine with the disk controller connected.

Powering on the console doesn't give the usual brief flash from the disk controller card LED as the DSR power up routine is executed. So out with the MiniMem cartridge and use EasyBug to 'switch on' the card by writing a 1 to CRU address >1100. The disk controller card LED now lights so it looks like the CRU circuit is working. Now looking at memory locations >4000 onward should read the DSR from the disk controller card ROMs, but only 00s are returned. So use the logic probe to start checking the inputs to the PAL U18; nothing obviously wrong here from a quick check. Checking the PAL outputs, the logic probe doesn't give a signal on the line to the 74LS245 data buffer U3 /OE pin. Checking this with a multimeter shows a voltage of ~1.8V, in the indeterminate range between a logic 0 and a logic 1. The 'LS245 is already socketed (and heatsinked via a small block of aluminium stuck to the inside of the clam shell) so has been replaced before – this particular type of IC can be prone to failure. Remove it and the output from the PAL starts registering on the logic probe. Checking the 'LS245 itself, the /OE pin has a resistance of approximately 80Ω to ground, so replace that, and the output from the PAL still registers on the logic probe. Still no joy though reading the contents of the disk controller card DSR ROMs. Looking at the inputs to the PAL a bit closer, there is no activity on the /MEMEN input. Tracing this signal back, the /MEMEN input to the 74LS244 buffer U5 is floating – the signal is not getting down the cable from the console. Take the cable connectors apart again to trace the signal through the cable, and /MEMEN is routed down the outer-most wire on one edge of the ribbon cable. This had been trapped years before when the connectors were assembled – the cable wasn't quite aligned in the shallow cutout between the two halves of the block, and the insulation was completely flattened. I had noticed this before, but hadn't thought that the wire within could also be broken, which it actually was. I managed a neat repair with a short length of wire from a spare piece of ribbon cable. Everything assembled again, power on the console and the disk controller card LED now gives the usual brief flash, and using EasyBug I can now read the contents of the DSR ROMs.

Next stage is to look at the drives. First, on one of the drives the sector mark sensor (photodiode) has broken off its base and this is fixed with a little bit of glue.

So, connect power to the first drive and switch on. Smoke! Resistor R22 on the floppy PCB is glowing gently red! Oh dear. Let's try the second drive. Smoke! Resistor R22 on the second drive is also gently glowing. Checking both boards, one side of R22 is shorted to ground, and the culprit – electrolytic capacitor C3 has shorted on *both* boards. Snipping one end of C3 temporarily allows testing to continue; I'll replace them and the charred resistors later. Experimenting with the drives, which are double-sided, yields the following results: on one drive only the bottom head is working (will only format single-sided), and on the second drive, both heads appear to be working (can format double-sided). The PCB on one of the drives is also faulty – any disk operation gives disk error 16 "no disk or no drive". Looking at the circuit diagram (a comprehensive instruction manual for the Siemens FDD200-5 drive is included in the manual for the Siemens PU 670C Programming Unit), the outputs from the floppy drive to the controller are all open-collector and gated by the drive select signal so that multiple drives can be connected on the same ribbon cable. Checking a few of the outputs with the logic probe while trying to access the drives – nothing. Tracing the drive select signal through the circuit identifies a 74LS14 inverter with a stuck output so remove it, fit a socket and pop a new one in and both drives now working.

Running the Disk Manager comprehensive test on each drive for a while reveals no further problems.

Ribbon Cable Connections

The connections between the two connectors on the ribbon cable are detailed in the table below.

Remember that you will need to provide a power source for the expansion card, either supplies at the same voltages as the PEB if leaving the voltage regulators in the expansion card in place, or regulated 5/12V supplies if bypassing the regulators in the card. Check and check again that the power supplies are correct before connecting!

Expansion
Card
Connector
Signal Console
Side Port
Connector
60   +12V regulator supply  
  59 +12V regulator supply  
58   -12V regulator supply  
  57 -12V regulator supply  
56   /MEMEN ----------------------------------- 32
  55 CRUIN ------------------------------------ 33
54   /WE --------------------------------------- 26
  53 --- N/C (GND)  
52   DBIN -------------------------------------- 9
  51 /CRUCLK ---------------------------------- 22
50   /CLKOUT ---------------------------------- 24
  49 --- N/C (GND)  
48   Pullup to +5V (AMC)  
  47 GND -------------------------------------- 23
46   Pullup to +5V (AMA)  
  45 Pullup to +5V (AMB)  
44   A1 ---------------------------------------- 30
  43 A0 ---------------------------------------- 31
42   A3 ---------------------------------------- 10
  41 A2 ---------------------------------------- 20
40   A5 ---------------------------------------- 5
  39 A4 ---------------------------------------- 7
38   A7 ---------------------------------------- 17
  37 A6 ---------------------------------------- 29
36   A9 ---------------------------------------- 18
  35 A8 ---------------------------------------- 14
34   A11 --------------------------------------- 8
  33 A10 --------------------------------------- 6
32   A13 --------------------------------------- 15
  31 A12 --------------------------------------- 11
30   A15 /CRUOUT ---------------------------- 19
  29 A14 --------------------------------------- 16
28   D0 ---------------------------------------- 37
  27 --- N/C (GND)  
26   D2 ---------------------------------------- 39
  25 D1 ---------------------------------------- 40
24   D4 ---------------------------------------- 35
  23 D3 ---------------------------------------- 42
22   D6 ---------------------------------------- 36
  21 D5 ---------------------------------------- 38
20   --- N/C (GND)  
  19 D7 ---------------------------------------- 34
18   --- N/C (/LOAD)  
  17 --- N/C (/INTA)  
16   Pullup to +5V (/SENILB)  
  15 --- N/C (/SENILA)  
14   --- N/C (IAQHA)  
  13 --- N/C (/HOLD))  
12   Pullup to +5V (PCBEN)  
  11 --- N/C (/RDBENA)  
10   --- N/C (AUDIOIN)  
  9 --- N/C (/LCP)  
8   --- N/C (SCLK)  
  7 --- N/C (GND)  
6   /RESET ----------------------------------- 3
  5 --- N/C (GND)  
4   READY ------------------------------------ 12
  3 GND -------------------------------------- 21
2   +5V regulator supply  
  1 +5V regulator supply  

Checking a Disk Controller Card DSR

A useful check to perform on a faulty disk controller card (either a controller card in the PEB or a sidecar model) is to see if the card Device Service Routine (DSR) can be read. The DSR is stored in a ROM or EPROM on the card and contains a table of the device names supported by the card and the software to control these devices. When a command that involves a device is executed, such as OLD DSK1.PROGRAM, the computer switches on each controller card fitted to the system in turn and searches for the device name ("DSK1") in the device name table on that card. If it finds a match, the computer uses the software in that card's DSR to process the command. If the computer cannot find a matching device name on any card, an OLD command should return error code 50 (when using TI BASIC) or error code 00 (when using TI Extended BASIC). If you get this error code trying to load a program from disk, this suggests a problem reading the DSR on the disk controller card.

Reading the DSR on a disk controller card can be easily checked using either of two cartridges: a MiniMem cartridge, or a TI Extended BASIC cartridge. The technique using the MiniMem cartridge will work on just a bare console with disk controller attached. The technique using the Extended BASIC cartridge also requires 32K memory expansion to be present.

Using a MiniMem Cartridge

To check reading the disk controller card DSR using a MiniMem cartridge, proceed as follows:

  1. Insert the MiniMem cartridge. Press any key on the master title screen then press 2 to select Easy Bug.
  2. Press any key to dismiss the Command Types screen displayed.
  3. At the "?" prompt, type C1100<Enter> followed by 1<Enter>, followed by a "." (period). This writes the value 1 to CRU address >1100 which 'switches on' a disk controller card. If testing a disk controller card fitted to a PEB, the LED on the front of the card should now be on.
  4. Type M4000<Enter> which displays the first byte of data in the card DSR (which is mapped to the address range >4000 - >5FFF), followed by repeatedly pressing <Space> to read out successive bytes.
     
    If checking a TI disk controller card, the data should exactly match that shown on the page here, that is AA, 02, 00, 00, 40, 44, 00, 00, 40, 4A, 40, 10, and so on. There is no need to check each and every byte - checking just the first 100 or so should give confidence that the DSR can be read correctly.
     
    If checking a non-TI disk controller card, the first byte of data should be AA, followed by a low number or zero, followed by 00, 00. The rest of the data varies according to the card type, but continue stepping through the data and look for the sequence of bytes 44, 53, 4B, which is the device name "DSK".

Note that the technique using the MiniMem cartridge should also work if a fault with the disk controller card causes the console to lock up if powered on when the PEB is also powered on. Simply power off the PEB, power on the console, then power on the PEB, then proceed as above.

Using a TI Extended BASIC Cartridge

To check reading the disk controller card DSR using a TI Extended BASIC cartridge, type in the following Extended BASIC program:

10 CALL INIT
20 CALL LOAD(-16384,2,12,17,0,29,0,4,91)
30 CALL LOAD(-31804,192,0)
40 DIM B(8)
50 ADDR=16384
60 FOR A=ADDR TO ADDR+72 STEP 8
70 CALL PEEK(A,B(1),B(2),B(3),B(4),B(5),B(6),B(7),B(8))
80 FOR L=1 TO 8
90 PRINT STR$(B(L))&" ";
100 NEXT L
110 PRINT
120 FOR L=1 TO 8
130 IF B(L)<32 THEN PRINT "."; ELSE PRINT CHR$(B(L));
140 NEXT L
150 PRINT
160 NEXT A

[For those interested in how the program works, line 20 loads a small machine code program at address >C000 which sets register R12 to >1100, sets CRU bit 0 to "1" to 'switch on' the disk controller card, then returns. Line 30 sets the interrupt hook at address >83C4 to >C000 which runs the program. (Credit to Mizapf on AtariAge for this neat little bit of code which is more efficient than the code I first wrote.) Line 50 sets the variable ADDR to address >4000, which is the address of the start of the DSR. Line 60 sets up a loop to process the first 72 bytes of the DSR, in groups of 8 bytes. Line 70 reads each block of 8 bytes from the DSR. The loop starting at line 80 prints the value of each byte in decimal. The loop starting at line 120 prints the ASCII character represented by each byte (or a "." for non-printable byte values less than 32); printing the ASCII makes it easier to see device names in the DSR).]

Running the program should give results similar to the screenshot below, showing lines of 8 bytes of data read from the DSR in decimal then those same bytes as ASCII on the line below. For any brand of disk controller card, the first four numbers should be 170 (>AA), then a low number or zero, then two zeroes. The data after that varies by type of disk controller card (the screen below shows the DSR used by the Classic99 emulator). For a standard TI disk controller card, you can see the data (in hexadecimal) here. You can easily look for the device names supported by the DSR ("DSK") in the ASCII part of the listing (to do this for the TI disk controller card, you'll probably have to change the number in line 50 from 16384 to 16416 as the device names are further into the DSR).

Disk controller card DSR screenshot

Results

If the DSR data looks correct and you can see the device names, then the disk problem most likely lies with the floppy drives, the cable, or the floppy controller or associated circuitry on the card (but the DSR circuitry on the card appears to be OK).

If the DSR data looks corrupt (and you're absolutely sure you typed in the Extended BASIC program correctly!) then you're probably looking at diagnosing and repairing a hardware fault on the card.

Connecting a Serial Mouse

A standard, old PC serial mouse can be connected directly to the serial port on a TI-99/4A nanoPEB interface.

The listing below is for a mouse-driven menu and a simple sketch program. The listing also provides an example of how to initialise and control the RS-232 port, and how to plot individual pixels with the TMS 9918A VDP in graphics 2 mode. There is a video of the program in use here. The program can be downloaded on a TiDisk format image here. Copy the disk image to a spare volume on your nanoPEB. To run the program, boot with the Editor/Assembler cartridge, select E/A option 3, type the filename DSKn.MOUSE_O then the program name START.

I've tried two serial mice which both work - labelled on the bottom as a Microsoft "Serial Mouse 2.1A", and a Microsoft "Serial - PS/2 Compatible Mouse".
serial mouse 1 serial mouse 2

******************************************************************************
*Experiment using a PC serial mouse connected to the serial port on a NanoPEB.
*The program first displays a mouse-driven menu, which is configured in
*Graphics 1 mode and uses a sprite defined as the mouse pointer. Selecting the
*first menu option runs a simple sketch application which is configured in
*Graphics 2 mode, giving a resolution of 256 x 192. Instructions for using the
*sketch application are displayed on screen.
*
*Mouse protocol details reference: www.kryslix.com/nsfaq/Q.12.html
*TMS9918A VDP datasheet:  http://ftp.whtech.com/datasheets%20and%20manuals/
*                                              Datasheets%20-%20TI/TMS9918.pdf
******************************************************************************

        AORG >A000

        DEF  START

        REF  VSBW           VDP single byte write.
        REF  VMBW           VDP multiple byte write.
        REF  GPLLNK         GPL routine link.

*************
*Definitions.
*************

*Graphics 1 mode storage in VRAM.

PNTBA1  EQU  >0000          Pattern name table base address.
PGTBA1  EQU  >0800          Pattern generator table base address.
CTBA1   EQU  >0380          Colour table base address.
SATBA1  EQU  >1B00          Sprite attribute table base address.
SGTBA1  EQU  >3800          Sprite generator table base address.

*Graphics 2 (bitmap) mode storage in VRAM.

PNTBA2  EQU  >1800          Pattern name table base address.
PGTBA2  EQU  >0000          Pattern generator table base address.
CTBA2   EQU  >2000          Colour table base address.
SATBA2  EQU  SATBA1         Sprite attribute table base address.
SGTBA2  EQU  SGTBA1         Sprite generator table base address.

*Memory mapped I/O definitions.

VDPREG  EQU  >8C02          VDP VRAM address and register access address.
VRAMW   EQU  >8C00          VDP VRAM data write address.
VRAMR   EQU  >8800          VDP VRAM data read address.

*CRU base address definitions.

BACARD  EQU  >1300          NanoPEB serial interface base address.
BA9902  EQU  >1340          NanoPEB serial port 1 base address.

*******
*Start.
*******

START   LWPI MYWS           Load workspace.

******************************
*Initialise mouse RS-232 port.
******************************

*Switch on serial interface.

        LI   R12,BACARD
        SBO  0

*Set port 1 to 1200 Baud, 7 data bits, no parity, 1 stop bit.

        LI   R12,BA9902     CRU base address of 9902 in NanoPEB.
        SBO  31             Reset 9902. This sets /RTS inactive high, so the
*                           RTS output line to -ve voltage.
        LI   R1,>8200       Control register: 1 stop bit, no parity, 7 data
*                           bits (binary 10000010).
        LDCR R1,8           Load control register.
        SBZ  13             Disable loading of interval register.
        LI   R1,>01A0       1200 Baud.
        LDCR R1,12          Load transmit and receive data rate registers.

        SBO  16             Set /RTS active low, so the RTS output line to +ve
*                           voltage to power the mouse.

**************************************************************************
*Set up Graphics 1 mode.
*(Will already be in Graphics 1 mode after starting the program from E/A,
* but need routine to set up this mode when returning to the menu from the
* mouse drawing program which uses Graphics 2 mode.)
**************************************************************************

*Load VDP registers.

GRAPH1  BL   @LOADER

        BYTE >00,>80        Register 0 - graphics 1 mode, external video off.
        BYTE >E0,>81        Register 1 - 16K, display on, interrupts on,
*                                        graphics 1, size  mag = 0.
        BYTE >00,>82        Register 2 - PNTBA1 = >0000. Pattern name table.
        BYTE >0E,>83        Register 3 - CTBA1 =  >0380. Colour table.
        BYTE >01,>84        Register 4 - PGTBA1 = >0800. Pattern generator
*                                                        table.
        BYTE >36,>85        Register 5 - SATBA1 = >1B00. Sprite attribute
*                                                        table.
        BYTE >07,>86        Register 6 - SGTBA1 = >3800. Sprite generator
*                                                        table.
        BYTE >F5,>87        Register 7 - Backdrop=background colour.
        DATA 0

*Clear the sprite generator table (SGT) and sprite attribute table (SAT).

        BL   @CLRSPR

*Load character set (character codes >20 - >5F) (from E/A manual page 251).

        CLR  R1             Clear GPL STATUS byte.
        MOVB R1,@>837C

        LI   R1,8*>20+PGTBA1  Address in VDP RAM to load character set.
        MOV  R1,@>834A

        BLWP @GPLLNK
        DATA >0018

*Load colour table.

        LI   R8,CTBA1+>4000  Reference CT. Add >4000 for VDP write operations.

        BL   @SENDAD        Send VDP address.

        LI   R11,256/8      Set count.
        LI   R1,>F500       Colour - white on light blue.

CTGRH1  MOVB R1,*R7         Write to colour table.
        DEC  R11            Done all entries?
        JNE  CTGRH1         No, loop around.

*Redefine characters:
* 96 for the menu selection pointer.
* 97 for inverse video 'H'.
* 98 for inverse video 'E'.
* 99 for inverse video 'R'.

        LI   R0,96*8+>0800  Offset of >0800 as PGT starts at >0800.
        LI   R1,MSPDAT
        LI   R2,8*4
        BLWP @VMBW

*********************************
*Display menu in Graphics 1 mode.
*********************************

*Clear screen.

MNMENU  BL   @CLRSCN

*Display menu text.

        CLR  R0
        LI   R1,TXT1
        MOV  @TXT1L,R2
        BLWP @VMBW

        LI   R0,32*3
        LI   R1,TXT2
        MOV  @TXT2L,R2
        BLWP @VMBW

***********************************************************
*Define mouse pointer sprite and place at middle of screen.
***********************************************************

        BL   @DEFMSE

*Initialise mouse position to the middle of the screen.
*Mouse position range is twice the screen resolution (twice 192 x 256) to get
*an adequate movement of the mouse. The mouse position will be divided by two
*to map it to screen coordinates. (Can't divide mouse movement by two
*otherwise very slow movement will result in no movement.)

        LI   R9,256/2*2     Mouse X position in LSB.
        MOV  R9,@MSXPOS
        LI   R9,192/2*2     Mouse Y position in LSB.
        MOV  R9,@MSYPOS

**********************************************************************
*Get mouse position and display indicator against menu option if mouse
*Y position is on the same line.
**********************************************************************

GMPOS   BL   @UPDTMS        Update mouse position.

        MOV  @MSYPOS,R9     Get mouse Y position.
        SRL  R9,4           Divide by 2, then by 8 to convert from pixels
*                           to lines.

        LI   R4,3           Menu start line number.
        LI   R5,6           Menu end line number.
        LI   R0,3*32+2      Menu pointer display position for top line.

MEN01   LI   R1,' '*256     Default to no menu pointer for this line.
        C    R9,R4          Mouse on this line?
        JNE  MEN02          No, jump.
        LI   R1,96*256      Yes, select menu selection pointer character.

MEN02   BLWP @VSBW          Display or remove menu pointer for this line.
        C    R4,R5          Done last line of menu?
        JEQ  CHKBTN         Yes, jump.
        AI   R0,32          No, do next line of menu.
        INC  R4
        JMP  MEN01

************************************************************
*Mouse left button clicked? If yes then jump to menu option.
************************************************************

CHKBTN  MOV  @MSBTTN,R1
        COC  @H6000,R1      Check for left mouse button pressed.
        JNE  GMPOS          Left button not pressed, jump.

        CI   R9,3           Button pressed when on first line of menu?
        JEQ  MENU01         Yes, jump to menu.
        CI   R9,4           Button pressed when on second line of menu?
        JEQ  MENU02         Yes, jump to menu.
        CI   R9,5           -- Ditto --
        JEQ  MENU02
        CI   R9,6
        JEQ  MENU02
        JMP  GMPOS          Button not pressed when mouse on menu line.

********************
*Menu option 1 text.
********************

*Clear screen.

MENU01  BL   @CLRSCN

*Display instruction text for paint program.

        CLR  R0
        LI   R1,TXT3
        MOV  @TXT3L,R2
        BLWP @VMBW

        LI   R0,32*3
        LI   R1,TXT4
        MOV  @TXT4L,R2
        BLWP @VMBW

        LI   R0,32*23+5
        LI   R1,TXT6
        MOV  @TXT6L,R2
        BLWP @VMBW

*Loop until mouse clicked on 'HERE' on bottom line.

        BL   @WAITHR

*Jump to drawing program.

        JMP  RSTART

*************************
*Menu options 2 - 4 text.
*************************

*Clear screen.

MENU02  BL   @CLRSCN

*Display 'menu option not implemented' text.

        CLR  R0
        LI   R1,TXT5
        MOV  @TXT5L,R2
        BLWP @VMBW

        LI   R0,32*23+5
        LI   R1,TXT6
        MOV  @TXT6L,R2
        BLWP @VMBW

*Loop until mouse clicked on 'HERE' on bottom line.

        BL   @WAITHR

*Return to main menu.

        JMP  MNMENU

******************************************************************************
*                         MOUSE DRAWING PROGRAM                              *
******************************************************************************

************************
*Set up Graphics 2 mode.
************************

*Load VDP registers.

RSTART  BL   @LOADER

        BYTE >02,>80        Register 0 - graphics 2 mode, external video off.
        BYTE >80,>81        Register 1 - 16K, no display, no interrupt,
*                                        graphics 2, size  mag = 0.
        BYTE >06,>82        Register 2 - PNTBA2 = >1800. Pattern name table.
        BYTE >FF,>83        Register 3 - CTBA2 =  >2000. Colour table.
        BYTE >03,>84        Register 4 - PGTBA2 = >0000. Pattern generator
*                                                        table.
        BYTE >36,>85        Register 5 - SATBA2 = >1B00. Sprite attribute
*                                                        table.
        BYTE >07,>86        Register 6 - SGTBA2 = >3800. Sprite generator
*                                                        table.
        BYTE >00,>87        Register 7 - Backdrop=background colour.
        DATA 0

*Clear the sprite generator table (SGT) and sprite attribute table (SAT).

        BL   @CLRSPR

*Set up pattern name table.

        LI   R8,PNTBA2+>4000  Reference PNT. Add >4000 for VDP write
*                             operations.

        BL   @SENDAD        Send VDP address.

        SETO R11            Reset count.

INIPNT  INC  R11            Next pattern.
        SWPB R11            Position LS byte.
        MOVB R11,*R7        Write it.
        SWPB R11            Restore R11.
        CI   R11,3*256      Done all entries?
        JL   INIPNT         No, loop around.

*Set up pattern generator table.

        LI   R8,PGTBA2+>4000  Reference PGT. Add >4000 for VDP write
*                             operations.

        BL   @SENDAD        Send VDP address.

        LI   R11,3*256*8    Set count.

KILPGT  MOVB @B00,*R7       Reset entry.
        DEC  R11            Done all entries?
        JNE  KILPGT         No, loop around.

*Set up colour table.

        LI   R8,CTBA2+>4000  Reference CT. Add >4000 for VDP write operations.

        BL   @SENDAD        Send VDP address.

        CLR  R1             Clear R1 because we'll be shifting left.
        MOVB @MPTSAT+3,R1   Get initial mouse pointer sprite colour.
        SLA  R1,4           Shift to left nibble to align with foreground
*                           colour in CT.
        MOVB R1,@MSCOL      Store initial mouse pointer colour.

        LI   R11,3*256*8    Set count.

INICT   MOVB R1,*R7         Set colour to initial mouse pointer sprite colour.
        DEC  R11            Done all entries?
        JNE  INICT          No, loop around.

*Re-enable display.

        BL   @LOADER

        BYTE >C0,>81
        DATA 0

***********************************************************
*Define mouse pointer sprite and place at middle of screen.
***********************************************************

        BL   @DEFMSE

        LI   R9,256/2*2     Mouse X position in LSB.
        MOV  R9,@MSXPOS
        LI   R9,192/2*2     Mouse Y position in LSB.
        MOV  R9,@MSYPOS

***********************
*Update mouse position.
***********************

WAIT1   BL   @UPDTMS

*************************************************************************
*Check for left and right buttons pressed together. If pressed, return to
*main menu.
**************************************************************************

        MOV  @MSBTTN,R1

        COC  @H7000,R1      Check for left and right button indication in 1st
*                           byte received.
        JNE  CLRSCR         Left and right button not both pressed, jump.

        B    @GRAPH1        Return to main menu.

***********************************************************************
*Check for right button press. If pressed, change mouse pointer colour.
***********************************************************************

CLRSCR  COC  @H5000,R1      Check for right button indication in 1st byte
*                           received.
        JNE  UPDCL1         Right button not pressed, jump.

        AB   @H10,@MSCOL    Right button pressed. Move onto next colour.
        JNE  UPDCOL         Need to wrap colour index back round? No, jump.
        MOVB @H20,@MSCOL    Reset colour to 2, skipping transparent and black
*                           colours.

UPDCOL  LI   R8,SATBA2+>4000+3  Reference colour byte in SAT. Add >4000 for
*                               VDP write operations.

        BL   @SENDAD        Send VDP address.

        MOVB @MSCOL,R2      Get stored colour.
        SRL  R2,4           Shift to 2nd nibble to align with colour nibble in
*                           SAT.
        MOVB R2,*R7         Write new colour to mouse pointer sprite.

***********************************************************************
*Check for left button press. If pressed, draw pixel at mouse position.
***********************************************************************

UPDCL1  COC  @H6000,R1      Check for left button indication in 1st byte
*                           received.
        JNE  DRWEX          Left button not pressed, jump.

*Convert mouse X position to column number and remainder.

        MOV  @MSXPOS,R2     Get X position (which is at twice screen
*                           resolution).
        MOV  R2,R3          Copy it.

        SRL  R2,4           Divide by 2 to convert to screen resolution, then
*                           divide by 8 to get column number.

        SRL  R3,1           Divide by 2 to convert to screen resolution.
        ANDI R3,>0007       Get 3 LS bits which are the remainder
*                           (the pixel 0 to 7 within the 8-pixel column).

*Convert mouse Y position to row number and remainder.

        MOV  @MSYPOS,R8     Get Y position (which is at twice screen
*                           resolution).
        MOV  R8,R9          Copy it.

        SRL  R8,4           Divide by 2 to convert to screen resolution, then
*                           divide by 8 to get row number.

        SRL  R9,1           Divide by 2 to convert to screen resolution.
        ANDI R9,>0007       Get 3 LS bits which are the remainder
*                           (the pixel 0 to 7 within the 8-pixel row).

*Multiply row number by 32, then add column number to get cell number.

        SLA  R8,5           Multiply row number by 32.
        A    R2,R8          Add column number.

*Multiply cell number by 8 to get index into entry in the PGT for this cell
*(8 bytes in PGT per cell, 1 per pixel row).

        SLA  R8,3           Multiply cell number by 8.

*Get current VRAM data from cell number, add new pixel, and write back to
*VRAM.

        A    R9,R8          Add Y remainder to get correct byte in PGT for the
*                           pixel row.

        BL   @SENDAD        Send cell address to VDP.

        CLR  R10            Clear pixel mask register. The pixel mask gives
*                           the bit to set in the PGT byte for the pixel
*                           0 to 7 within the 8-pixel column.
        AI   R3,MASKTB      Add address of pixel mask table base to
*                           X remainder.
        MOVB *R3,R10        Fetch pixel mask.
        MOVB @VRAMR,R3      Get current VRAM data.
        SOCB R10,R3         Set pixel bit.

        ORI  R8,>4000       Set VDP write bit in address.

        BL   @SENDAD        Send address to VDP.

        MOVB R3,*R7         Write updated VRAM data.

*Set the colour table entry to the current mouse pointer sprite colour.

        AI   R8,CTBA2-PGTBA2  Get address of PCT entry.

        BL   @SENDAD        Send address to VDP.

        MOVB @MSCOL,*R7     Update colour entry.

********************************
*Loop round and do it all again.
********************************

DRWEX   B    @WAIT1

*************
*************
*Subroutines.
*************
*************

**************************************************
*Load the VDP registers from an inline data table.
**************************************************

LOADER  MOVB *R11+,@VDPREG  Write register data.

        C    *R11,*R11      Dummy delay for VDP.
        MOVB *R11+,@VDPREG  Write register number.

        MOV  *R11,*R11      End of data table?
        JNE  LOADER         No, loop.

        INCT R11            Yes, skip data table terminator.

        B    *R11           Return.

***********************************
*Send the address in R8 to the VDP.
***********************************

SENDAD  SWPB R8             Position LSB.
        MOVB R8,@VDPREG     Send LSB.

        SWPB R8             Position MSB.
        MOVB R8,@VDPREG     Send MSB.

        B    *R11           Return.

*************************************************************************
*Clear the sprite generator table (SGT) and sprite attribute table (SAT).
*************************************************************************

CLRSPR  MOV  R11,@SAVR11    Save return address.

        LI   R7,VRAMW
        LI   R8,SGTBA1+>4000  Reference SGT. Add >4000 for VDP write
*                             operations.

        BL   @SENDAD        Send VDP address.

        LI   R8,256*8       Do 256 8-bit patterns.

KILSGT  MOVB @B00,*R7       Null out the pattern.
        DEC  R8             Count it.
        JNE  KILSGT         Loop till all done.

        LI   R8,SATBA1+>4000  Reference SAT. Add >4000 for VDP write
*                             operations.

        BL   @SENDAD        Send VDP address.

        LI   R8,32*8        Kill off all 32 sprite planes.
        LI   R6,>D000       Fill it full of sprite terminators.

KILSAT  MOVB R6,*R7         Clear SAT.
        DEC  R8             Count it.
        JNE  KILSAT         Loop till all done.

        MOV  @SAVR11,R11    Restore return address.

        B    *R11           Return.

**************
*Clear screen.
**************

CLRSCN  CLR  R0             Start writing at start of screen image table.
        LI   R1,' '*256     Space character.
        LI   R2,32*24-1     Number of characters to do - but do 1 outside
*                           loop.
        BLWP @VSBW          Write one byte.
CLR1    MOVB R1,@VRAMW      Write successive bytes without doing a BLWP -
*                           quicker.
        DEC  R2
        JNE  CLR1

        B    *R11

***********************************************************
*Define mouse pointer sprite and place at middle of screen.
***********************************************************

*Write character pattern to the sprite generator table.

DEFMSE  MOV  R11,@SAVR11    Save return address.

        LI   R8,SGTBA1+>4000  Reference SGT. Add >4000 for VDP write
*                             operations.

        BL   @SENDAD        Send VDP address.

        LI   R8,8           Do one 8-bit pattern.
        LI   R6,MPTSPG      Pointer to character pattern.

MPTRSG  MOVB *R6+,*R7       Write the pattern.
        DEC  R8             Count it.
        JNE  MPTRSG         Loop till all done.

*Define sprite in the sprite attribute table.

        LI   R8,SATBA1+>4000  Reference SAT. Add >4000 for VDP write
*                             operations.

        BL   @SENDAD        Send VDP address.

        LI   R8,4           4 bytes to write.
        LI   R6,MPTSAT      Pointer to SAT for mouse pointer.

MPTRSA  MOVB *R6+,*R7       Write the SAT.
        DEC  R8             Count it.
        JNE  MPTRSA         Loop till all done.

        MOV  @SAVR11,R11    Restore return address.

        B    *R11           Return.

********************************************************
*Wait for a packet of data from the mouse.
*Determine the mouse movement and update mouse position.
*Move mouse pointer sprite.
********************************************************

**
*Wait for and store a packet of 3 bytes. The first byte of each packet should
*be a value greater than or equal to >40. Check for this to keep reception in
*sync.
**

UPDTMS  LI   R12,BA9902     CRU base address of 9902 in NanoPEB.

        CLR  R1
        CLR  R2
        CLR  R3

WAIT3   TB   21             Receive buffer register full?
        JNE  WAIT3          No, loop until a character is received.
        STCR R1,8           Read character into MSB of R1.
        SBZ  18             Reset receive buffer register.

        CI   R1,>4000       Bit set to indicate first byte in packet?
        JL   WAIT3          No, get another byte.

WAIT4   TB   21             Receive buffer register full?
        JNE  WAIT4          No, loop until a character is received.
        STCR R2,8           Read character into MSB of R2.
        SBZ  18             Reset receive buffer register.

WAIT5   TB   21             Receive buffer register full?
        JNE  WAIT5          No, loop until a character is received.
        STCR R3,8           Read character into MSB of R3.
        SBZ  18             Reset receive buffer register.

        MOV  R1,@MSBTTN     Save mouse buttons pressed data.

**
*Determine the mouse movement and update mouse position.
**

*Determine the X movement.

        MOV  R1,R5          Copy byte 1.
        SLA  R5,6           Move MS bits of X movement to top of byte.
        A    R5,R2          Add to rest of X movement in 2nd byte.
        SWPB R2             Move result into LSB.

*Determine the Y movement.

        MOV  R1,R5          Copy byte 1.
        ANDI R5,>0C00       Isolate bits 2 and 3.
        SLA  R5,4           Shift to move MS bits of Y movement to top of
*                           byte.
        A    R5,R3          Add to rest of Y movement in 3rd byte.
        SWPB R3             Move result into LSB.

*Update mouse X position.

        MOV  @MSXPOS,R9     Get stored mouse X position.

        CI   R2,128         Is X movement left (-ve) or right (+ve)?
        JLE  UPDTX1         Jump if to the right.

        AI   R2,>FF00       Movement is to the left. Change to two's
*                           complement word.
        A    R2,R9          Subtract (add two's complement) movement from
*                           mouse X position.
        JGT  UPDTX2         If position now > 0, it's valid.
        JEQ  UPDTX2         If position now 0, it's valid.
        CLR  R9             Result is < 0, so reset position to 0.
        JMP  UPDTX2

UPDTX1  A    R2,R9          Add movement to X position.
        CI   R9,2*255       If X position > 2*255 then set to 2*255.
        JLE  UPDTX2
        LI   R9,2*255

UPDTX2  MOV  R9,@MSXPOS     Store new mouse X position.

*Update mouse Y position.

        MOV  @MSYPOS,R9     Get stored mouse Y position.

        CI   R3,128         Is Y movement up (-ve) or down (+ve)?
        JLE  UPDTY1         Jump if down.

        AI   R3,>FF00       Movement is up. Change to two's complement word.
        A    R3,R9          Subtract (add two's complement) movement from
*                           mouse Y position.
        JGT  UPDTY2         If position now > 0, it's valid.
        JEQ  UPDTY2         If position now 0, it's valid.
        CLR  R9             Result is < 0, so reset position to 0.
        JMP  UPDTY2

UPDTY1  A    R3,R9          Add movement to Y position.
        CI   R9,2*191       If Y position > 2*191 then set to 2*191.
        JLE  UPDTY2
        LI   R9,2*191

UPDTY2  MOV  R9,@MSYPOS     Store new mouse Y position.

**
*Move mouse pointer sprite.
**

*First byte in SAT is vertical position, second byte is horizontal position.

MOVMS   MOV  R11,@SAVR11    Save return address.

        LI   R8,SATBA1+>4000  Reference SAT. Add >4000 for VDP write
*                             operations.

        BL   @SENDAD        Send VDP address.

        MOV  @MSYPOS,R2     Get stored mouse Y position (which is at twice
*                           screen resolution).
        DECT R2             Top row of the screen has value -1, so adjust.
        SLA  R2,7           Move to MSB and divide by 2 to convert to screen
*                           resolution.
        MOVB R2,*R7         Write the Y mouse position.

        MOV  @MSXPOS,R2     Get stored mouse X position (which is at twice
*                           screen resolution).
        SLA  R2,7           Move to MSB and divide by 2 to convert to screen
*                           resolution.
        MOVB R2,*R7         Write the X mouse position.

        MOV  @SAVR11,R11    Restore return address.

        B    *R11           Return.

***************************************************
*Loop until mouse clicked on 'HERE' on bottom line.
***************************************************

WAITHR  MOV  R11,@SAVR12    Save return address.

WAHR01  BL   @UPDTMS        Update mouse position.

        MOV  @MSBTTN,R1
        COC  @H6000,R1      Check for left mouse button pressed.
        JNE  WAHR01         Left button not pressed, jump.

        MOV  @MSYPOS,R9     Get mouse Y position.
        SRL  R9,4           Divide by 2, then by 8 to convert from pixels
*                           to lines.
        CI   R9,23          Mouse on bottom line of screen?
        JNE  WAHR01         No, jump.

        MOV  @MSXPOS,R9     Get mouse X position.
        SRL  R9,4           Divide by 2, then by 8 to convert from pixels
*                           to characters.
        CI   R9,11          Mouse over the correct characters?
        JL   WAHR01         No, jump.
        CI   R9,14
        JH   WAHR01         No, jump.

        MOV  @SAVR12,R11    Yes, restore return address.

        B    *R11           Return.

**************************
*Workspace, text and data.
**************************

MYWS    BSS  32             Workspace.

TXT1    TEXT '  STUART''S TI-99/4A MOUSE DEMO  '
        TEXT '  ----------------------------'
TXT1L   DATA $-TXT1

TXT2    TEXT '    MOUSE DRAWING PROGRAM       '
        TEXT '    (OPTION NOT IMPLEMENTED)    '
        TEXT '    (OPTION NOT IMPLEMENTED)    '
        TEXT '    (OPTION NOT IMPLEMENTED)    '
TXT2L   DATA $-TXT2

TXT3    TEXT '  SKETCH PROGRAM INSTRUCTIONS   '
        TEXT '  ---------------------------'
TXT3L   DATA $-TXT3

TXT4    TEXT 'USE THE MOUSE TO MOVE THE       '
        TEXT 'POINTER AROUND THE SCREEN. HOLD '
        TEXT 'DOWN THE LEFT MOUSE BUTTON TO   '
        TEXT 'DRAW AS YOU GO. CLICK THE RIGHT '
        TEXT 'MOUSE BUTTON TO CHANGE THE PEN  '
        TEXT 'COLOUR.                         '
        TEXT '                                '
        TEXT 'CLICK BOTH MOUSE BUTTONS        '
        TEXT 'TOGETHER TO RETURN TO THE MENU.'
TXT4L   DATA $-TXT4

TXT5    TEXT '*** THIS MENU OPTION IS NOT *** '
        TEXT '***       IMPLEMENTED       ***'
TXT5L   DATA $-TXT5

TXT6    TEXT 'CLICK '
        BYTE 97,98,99,98    'HERE' using video inverse characters.
        TEXT ' TO CONTINUE'
TXT6L   DATA $-TXT6

        EVEN

SAVR11  DATA 0              Saved return address.
SAVR12  DATA 0              Second saved return address.

MSXPOS  DATA 0              Mouse X position in LSB.
MSYPOS  DATA 0              Mouse Y position in LSB.
MSBTTN  DATA 0              Mouse buttons pressed.
MSCOL   BYTE 0              Mouse pointer colour.

B00     BYTE 0
B16     BYTE 16
H10     BYTE >10
H20     BYTE >20
H5000   DATA >5000
H6000   DATA >6000
H7000   DATA >7000

*Mouse pointer sprite data.

MPTSPG  BYTE >F8,>F0,>F0,>D0,>88,>04,>02,>01  Arrow pattern.
MPTSAT  BYTE 192/2-1,256/2,>00,>0F  Initial position middle of screen, colour
*                                   white.

*Menu selection pointer character data.

MSPDAT  BYTE >10,>18,>1C,>1E,>1E,>1C,>18,>10

*'HER' in inverse video characters data.

        BYTE >00,>BB,>BB,>BB,>83,>BB,>BB,>BB
        BYTE >00,>83,>BF,>BF,>87,>BF,>BF,>83
        BYTE >00,>87,>BB,>BB,>87,>AF,>B7,>BB

*Pixel bit mask table.

*Pixel------- 0 1   2 3   4 5   6 7
MASKTB  DATA >8040,>2010,>0804,>0201

        END

Internet Web Browser

Introduction

This is a web browser for the TI-99/4A (and now also ported to the Geneve), where the TI-99 is connected to the Internet using one of the following methods:

The browser will not work with any 'modern' web pages - there are just too many HTML tags and other stuff for the TI to handle, and there's no way you could display much of it anyway with the TI's low resolution screen. Instead, the browser works with a bespoke set of tags which are matched to the display capabilities of the TI-99.

The latest version of the browser is v9.4, which runs on a TIPI only. This version has been ported to the Geneve running under MDOS and connected to a TIPI. Most of the information below is common to both the TI-99 and Geneve versions (they both support the same tags and render screens in the same way), with some information specific to the Geneve version and a download link included here.

Main Features

TIPI Version

The program is on a TiDisk format image which can be downloaded here. Extract the TiDisk file from the zip file, then upload it to the root directory of the TIPI using the TIPI web interface. The TIPI will automatically extract the files into a directory named "BROWSER".

To run the program, boot with the Editor/Assembler cartridge, select E/A option 3 and type the filename TIPI.BROWSER.INTERNET (or DSK.BROWSER.INTERNET). The program will automatically start after loading.

The program is available as a FinalGROM99 image here. The program needs some of the files on the TiDisk image above in order to run, so you still need to download that TiDisk format image and upload it to the TIPI.

If using a mouse (connected to the Raspberry Pi) to control the browser, try using the oldest USB mouse you have available and move the mouse slowly. A newer mouse with a high resolution can make the browser pointer hard to control.

Serial Port Version

To be clear, all versions that support a serial port do not include the latest browser functionality. Use of the latest TIPI version is recommended.

Configuring and Connecting the Lantronix Serial-to-Ethernet Adaptor

The Lantronix UDS-10 will need this configuration for Channel 1:

NOTE: The configuration has been changed from that used for browser v9.0 and earlier. Note the different Baudrate and I/F Mode settings. These settings are required for browser v9.1 and later.

I usually configure the Lantronix over Telnet - the Lantronix User Guide contains details for doing this. I have problems configuring the Lantronix through the built-in web app - the Connect Mode Settings:Response field usually gets reset to the default setting each time I open the web app, rather than showing the current setting.

The Lantronix is wired to the serial port on a NanoPEB as follows:

NanoPEB                 Lantronix
9-way D-type Male       25-way D-type Female
Pin                              Pin
 2 (RX)  ------<<------ (RX out)  3
 3 (TX)  ------>>------ (TX in)   2
 5 (GND) -------------- (GND)     7
 7 (RTS) -->>--+
 8 (CTS) --<<--+ (pins 7 and 8 connected together)

The Lantronix is wired to the 1st serial port on a TI PEB RS-232 card as follows:

RS-232 Card              Lantronix
25-way D-type Female     25-way D-type Female
Pin                              Pin
 2 (RD-1) ------<<----- (RX out)  3
 3 (TX-1) ------>>----- (TX in)   2
 5 (CTS-1) ----->>----- (RTS in)  4
 7 (GND) -------------- (GND)     7
 20 (DTR-1) ----<<----- (CTS out) 5

Loading and Running the Program

Various download options are available, depending on how you intend to run the program. Download links are in the table below, with a description of the various options below that.

Disk Image 32K EPROM Image,
Inverted Bank Switching 
32K EPROM Image,
Non-Inverted Bank Switching
512K EPROM Image,
Non-Inverted Bank Switching 
Internet Browser v9_2.zip  Internet Browser 32K Inverted v9_2.zip Internet Browser 32K Non‑Inverted v9_2.zip Internet Browser 512K Non‑Inverted v9_2.zip

Disk Image

The program is on a TiDisk format image and named "INTERNET". To run the program, boot with the Editor/Assembler cartridge, select E/A option 3 and type the filename DSKn.INTERNET. After loading, the program name required depends on the type of RS-232 port you're using:

32K EPROM Image, Inverted Bank Switching

This is a binary image which can be burned into a 32K 27(C)256 EPROM for use on a 64K 'Guidry' bank-switched cartridge board fitted with a 74LS379 (inverted outputs) latch. The option to select from the master selection list depends on the type of RS-232 port you're using:

32K EPROM Image, Non-Inverted Bank Switching

This is a binary image which will work with a FlashROM99 cartridge.

The option to select from the master selection list depends on the type of RS-232 port you're using, as detailed for the 32K inverted EPROM image above.

512K EPROM Image, Non-Inverted Bank Switching

This is a binary image which can be used as follows:

The option to select from the master selection list depends on the type of RS-232 port you're using, as detailed for the 32K inverted EPROM image above.

With the cartridge versions of the program, you still need a disk file DSK.BROWSER.FAVS to use the favourites list, described below.

The Browser Screen

When the program starts, it displays an instruction page and prompts for the web page to load at the top of the screen. The "www." prefix is provided already (backspace over this for a host name without a "www." prefix or to enter an IP address). The URLs of some sample web sites are given further down this page. The web page address is limited to 80 characters. Use lower-case for the web page address - ALPHA LOCK up!

Internet web browser startup screen

After entering a web page address, the program shows status messages at the bottom of the screen as it resolves the server IP address (for the serial port version) then downloads the web page. With the serial port version, the Lantronix sometimes experiences a problem connecting to a web server - if the program says it is retrying the connection, it normally succeeds after 30 seconds or so.

Navigation and Control Keys

The navigation and control keys supported are as follows (these can be entered as either upper or lower case):

The keys auto-repeat to make it easier to move the pointer between hyperlinks.

The pointer can also be moved horizontally, vertically and diagonally using a joystick or (for the TIPI version) USB mouse connected to the Raspberry Pi. The joystick Fire button or left mouse button select any hyperlink, input field or submit button that the pointer is over.

From browser version v9.3 onwards, clicking on the first 3 character positions of the URL address bar prompts for entry of a new page URL (the same action as pressing the N key). Clicking on any other character position of the URL address bar displays the current URL for editing (the same action as pressing <Fctn>-E).

With the serial port version, pressing any key when the program is retrying a connection will abort the connection and prompt for a new URL. (Note that it may take a couple of seconds for the program to respond after pressing the key.)

When entering or editing text in an input field, the following control keys are active:

Favourites List

The program supports a list of favourites, which is displayed by pressing the I key. This loads and renders the contents of the file TIPI.BROWSER.FAVS (or DSK.BROWSER.FAVS). This is written/edited by the user using (for example) the E/A Editor, and saved in 'Variable 80' format. The file contains text and tags for hyperlinks to the user's favourite pages - it is in effect a web page stored on the disk. As well as hyperlinks, the page can contain any graphics or character definitions desired by the user. Long lines of text/tags can be split across several lines in the file. A sample file is included on the disk.

Sample Web Sites

www.stuartconner.me.uk/tibrowser/browser_help_tipi_92.htm
(for browser v9.2)
www.stuartconner.me.uk/tibrowser/browser_help_tipi_93.htm
(for browser v9.3)
www.stuartconner.me.uk/tibrowser/browser_help_tipi_94.htm
(for browser v9.4)
A page showing the same instructions displayed when the program initially loads.
www.stuartconner.me.uk/tibrowser/z.htm
www.stuartconner.me.uk/tibrowser/z_ti_test_2.htm 
www.stuartconner.me.uk/tibrowser/z_ti_test_3.htm 
 
A set of linked pages demonstrating the capabilities of the program. A screenshot of the first page is shown here. The second page gives a short history of the TI-99/4A. The third page shows the pinout of the TMS 9900 processor.
 
www.stuartconner.me.uk/tibrowser/tichat.php
www.stuartconner.me.uk/tibrowser/tichat.php?m=your_text
There is a simple chat application. Use the first URL on the left to view the current messages. To add a message to the page, add to the end of the same URL "?m=your_text" (without the quotes, and with underscores instead of spaces). Remember that the max length of the entire URL is 80 characters.
 
www.stuartconner.me.uk/tibrowser/getclientipaddress.php  Displays the external IP address of your home network.
 
www.stuartconner.me.uk/tibrowser/getdatetime.php Displays the local date and time on the (US) host server.
 
www.stuartconner.me.uk/tibrowser/getweather.php?l=city,country code
Example: www.stuartconner.me.uk/tibrowser/getweather.php?l=southampton,gb
*** Page not working at the moment - I need to check it ***
Displays the current weather at the specified city, which is identified by the city name and 2-character country code (the "Alpha-2 code" on this page). If the city name contains a space, replace it with an underscore, for example: ?l=new_york,us. The location is not case-sensitive.
 
www.stuartconner.me.uk/tibrowser/getwebipaddress.php?w=domain
Example: www.stuartconner.me.uk/tibrowser/getwebipaddress.php?w=news.bbc.co.uk
Displays the numeric IP address of the domain specified by [domain].
 
 
www.stuartconner.me.uk/tibrowser/valley.htm Graphics demo page.
 
www.stuartconner.me.uk/tibrowser/elite.htm Graphics demo pages. The program automatically cycles through a sequence of pages.
 
www.myti99.com This is by far the most sophisticated site yet supporting the TI web browser, and is developed and maintained by Corey Anderson. To make use of most of the site you first need to register a user name and password by accessing the site www.myti99.com from your PC (or direct from the browser if using v9.3 onwards), then you can access the same site from your TI for applications such as an impressive 2-player chess game.

The screenshots below show a few sample web pages.

browser example page 1 browser example page 2 browser example page 3


Web Page URL Formats

As well as web servers specified by host name, v9.2 onwards of the browser also supports web servers specified by IP address. So the following example web page URL formats are supported:

With the first two URL formats above where a host name is given, if using a serial port version of the browser, the browser accesses a script on a web page on the server 64.037.052.172 (which hosts my site www.stuartconner.me.uk) to resolve the specified host name to its IP address.

The URL in a hyperlink on a page can specify either a host name or an IP address.

Supported Tags

The tags supported are listed in the table below. Note that the tags are CASE sensitive. Example usage of the more complex tags is given in later sections.

Tag                                                                                Description
<99ml> Indicates start of page. All text before this tag is ignored.
</99ml> Indicates end of page. All text after this tag is ignored.
<p> Indicates start of a paragraph. All text outside <p></p> tags is treated as a comment and ignored.
</p> Indicates end of a paragraph. The following text is rendered starting on the next line on the screen.
<br> Line break. The following text is rendered starting on the next line on the screen.
<a:(URL)> Specifies a hyperlink for the text following the tag.
</a> Indicates end of hyperlink text.
<arect:AA:BB:CC:DD:(URL)> Defines a rectangular link area defined in pixels. AA, BB, CC and DD are upper-case hex values. If the <Return> key or <Fire> button is pressed, then if the screen pointer X position (in pixels, relative to the top left of the screen) is between the values AA and BB (inclusive) and the screen pointer Y position is between the values CC and DD (inclusive), then the link is invoked, and the specified URL is called with “?x=(pointer X position in hex)&y=(pointer Y position in hex)” appended to the URL (or "&x=..." if the link URL already contains a "?"). (Note that the screen pointer moves in steps of 2 pixels.)
<u> Underlines the text following the tag.
</u> Indicates end of underline text.
<clr:(foreground colour code)(background colour code)> Render the characters following the tag in the specified colours. The colour code definitions are given below.

Characters are 6 pixels wide so are not aligned with the 8-pixel blocks used by the VDP for colour control. Controlling text colour on a paragraph by paragraph basis will work. If trying to change text colour mid-paragraph, the results will depend on whether the start/end characters are exactly aligned with the edge of an 8-pixel block.
</clr> Revert back to normal colours (black on grey).
<cdef:(character code in hex):(hex character def)> Redefine the specified character. Characters in the range >00 to >FF are supported.

The default character definitions are automatically reloaded when browsing to a new page.

Characters have to be defined BEFORE they are used. The same characters can be redefined several times on the same page - define each with the pattern needed before using them.

The default character definitions are:

- Character >00 is a special character used internally and should not be redefined.
- Character >01 is a special character used internally and should not be redefined on a page that uses input fields.
- Character >0A is defined as a Line Feed character.
- Character >0D is defined as a Carriage Return character.
- Characters >0E - >18 are defined with table drawing characters, as follows:
   - Character >0E - ┏ top left corner
   - Character >0F - ━ horizontal line
   - Character >10 - ┳ top join
   - Character >11 - ┓ top right corner
   - Character >12 - ┃ vertical line
   - Character >13 - ┣ left vertical join
   - Character >14 - ╋ centre join
   - Character >15 - ┫ right vertical join
   - Character >16 - ┗ bottom left corner
   - Character >17 - ┻ bottom join
   - Character >18 - ┛ bottom right corner
- Characters >20 - >7E are defined as the normal ASCII character set.
- Character >7F is defined as a solid block.
- (All other characters are defined as blank)
<chr_lt> Displays the '<' character (this 'alias' is needed in order to be able to display tags on the screen as text).
<chr:(character code in hex)> Displays the specified character.
<8pchr:(character code in hex):(character position in hex, 2 bytes)> Render all 8 pixel columns of the specified character at the specified position. The position is based on the screen divided into 8*8 tiles, so positions are numbered from 0 (top left) to 767 (bottom right). This tag is provided for plotting graphics where the character rendering is exactly aligned with the VDP colour table.
<8pclr:(character colour in hex, 8 bytes)> Defines the colours used for characters rendered using the <8pchr> tag. Each byte of the colour code specifies the colour of one row of the 8x8 character. The first nibble of each byte specifies the foreground colour, and the second byte specifies the background colour. The colour code definitions are given below.

The default foreground and background colours are both transparent.

Note that the colour information is loaded to VDP RAM as the tags are processed, so colour changes to the currently displayed page might be seen as a new page is rendered.
<ptrclr:(sprite colour code)> Changes the colour of the sprite pointer. The sprite colour code is two hex characters, the first of which must be 0 and the second the required sprite colour according to the colour code definitions given below.
<noscroll> Disables the keyboard scroll keys on a web page. This is intended for web pages designed for display as a single page and using the <8pchr> and/or <arect> tags which don't work as intended if the page is scrolled down.
<noclearscreen> Disables clearing the screen when rendering a new web page. This enables one web page to update the screen rendered by the previous web page (so for example one web page could draw half a complex graphic on the screen and a second web page draw the other half).
<loadpage:(URL)> Displays the screen rendered so far then loads the specified web page.
<updatescreen> Displays the screen rendered by the tags processed so far, then continues processing tags on the page.
<startrenderline:(line number in hex, 2 bytes)>
(browser v9.4 onwards)
Specifies which line of the web page to display at the top of the screen when the page is initially displayed. As an example, if the tag is <startrenderline:0012>, the 18th line of the web page is initially displayed at the top of the screen. An example use of this tag is for a message page where the most recent message is initially displayed, and the user can then scroll up the page to see older messages.
<input:(foreground colour code)(background colour code):(tag ID)> Defines the start of a text input field. When the user clicks on an input field, they can type into the field, terminating input by pressing <Enter>. When the user then clicks on a submit field 'button', the tag ID and entered value of each input field on the page are appended as parameters to the end of the URL specified by the <submit> tag. The tag ID must be an alphanumeric string and can be up to 96 characters long.

The foreground and background colour control enables the field to easily be made a different colour to the surrounding text to make the field itself and the field length prominent.
</input> Defines the end of an input field. The field length is determined by the number of characters between the <input> and </input> tags. These characters may be spaces or some sort of text prompt. With the <input> tag, these characters are not a default field value - the reported field value is an empty string unless the user types a value into the field.
<input-dv:(foreground colour code)(background colour code):(num characters):(tag ID)>
(browser v9.3 onwards)
Same as the <input> tag but the (num characters) parameter (one hex byte) specifies the number of characters between the <input-dv> and </input-dv> tags to use as a default field value. This is the field value reported unless the user types a different value into the field.
</input-dv>
(browser v9.3 onwards)
Defines the end of an input-dv field.
<pwd:(foreground colour code)(background colour code):(tag ID)>
(browser v9.3 onwards)
Same as the <input> tag but any non-space characters typed into the field are displayed as asterisks, to obscure entered information such as a password.
</pwd>
(browser v9.3 onwards)
Defines the end of a password field.
<submit:(foreground colour code)(background colour code):(URL)> Defines the start of a submit field 'button'. The text between the <submit> and </submit> tags is displayed as the button caption. When the user clicks on the button, the tag ID and value of each input, input-dv and password field on the page are appended as a parameter to the end of the URL specified by the <submit> field. The total length of the URL is limited to 100 characters, so only a small number of fairly short fields can be used. The URL is added to the URL history.

The foreground and background colour control enables the button to easily be made a different colour to the surrounding text to make it prominent.
</submit> Defines the end of a submit field.
<submit-nh:(foreground colour code)(background colour code):(URL)> Same as the <submit> tag but the URL is not added to the URL history and the 100-character URL length limit does not apply. This allows far longer input fields to be used.
<cookie:(cookie value)>
(browser v9.3 onwards)
Stores a cookie value to be associated with the host name from which the page was downloaded. Cookie details are stored in a COOKIES file on the TIPI disk, and so are persistent across browser sessions. The cookie value is included in the HTTP request header for all future page requests to the same host.
<timeout:(period):(URL)> Loads a new page (or refreshes the current page) when no user input (keyboard or joystick movement) is made for the timeout period. The timeout period, in seconds, is specified as one hex byte. For example, a value '14' gives a timeout period of 20 seconds. The URL specifies the page to go to, and can be the same URL as the current page to refresh the page, or a different URL to go to a different page.

The timeout period should be fairly accurate for a conventional TI-99/4A with the 32K RAM on the 8-bit bus and normal wait states. For a TI with the 32K RAM on the 16-bus, the actual timeout period will be approx 10-20% less than the specified value.
<favs>
(browser v9.3 onwards)
Defines the start of a hyperlink that displays the user's favourites file.
</favs>
(browser v9.3 onwards)
Defines the end of a favourites hyperlink.
<tone:(duration):(volume):(frequency index)> Plays a tone on the speaker. The tone duration, in units of 1/10th of a second, is specified as one hex byte. Volume is in the range 00 (minimum volume (silence)) to 0F (maximum volume). The frequency is specified as the index value in the frequency table below. For example, the tag <tone:1E:0F:18> plays 'middle A' (440 Hz) at full volume for 3 seconds.

Note Frequency Index           Note Frequency Index
A  
A#
B  
C  
C#
D  
D#
E  
F  
F#
G  
G#
A  
A#
B  
C  
C#
D  
D#
E  
F  
F#
G  
G#
A  
A#
B  
C  
C#
D  
D#
110.00 Hz
116.54 Hz
123.47 Hz
130.81 Hz
138.59 Hz
146.83 Hz
155.56 Hz
164.81 Hz
174.61 Hz
185.00 Hz
196.00 Hz
207.65 Hz
220.00 Hz
233.08 Hz
246.94 Hz
261.63 Hz
277.18 Hz
293.66 Hz
311.13 Hz
329.63 Hz
349.23 Hz
369.99 Hz
392.00 Hz
415.30 Hz
440.00 Hz
466.16 Hz
493.88 Hz
523.25 Hz
554.37 Hz
587.33 Hz
622.25 Hz
00
01
02
03
04
05
06
07
08
09
0A
0B
0C
0D
0E
0F
10
11
12
13
14
15
16
17
18
19
1A
1B
1C
1D
1E
  E  
F  
F#
G  
G#
A  
A#
B  
C  
C#
D  
D#
E  
F  
F#
G  
G#
A  
A#
B  
C  
C#
D  
D#
E  
F  
F#
G  
G#
A  
A#
659.25 Hz
698.46 Hz
739.99 Hz
783.99 Hz
830.61 Hz
880.00 Hz
932.33 Hz
987.77 Hz
1046.50 Hz
1108.73 Hz
1174.66 Hz
1244.51 Hz
1318.51 Hz
1396.91 Hz
1479.98 Hz
1567.98 Hz
1661.22 Hz
1760.00 Hz
1864.65 Hz
1975.53 Hz
2093.00 Hz
2217.46 Hz
2349.32 Hz
2489.01 Hz
2637.02 Hz
2793.83 Hz
2959.96 Hz
3135.96 Hz
3322.44 Hz
3520.00 Hz
3729.31 Hz
1F
20
21
22
23
24
25
26
27
28
29
2A
2B
2C
2D
2E
2F
30
31
32
33
34
35
36
37
38
39
3A
3B
3C
3D

Colour Codes

The colour codes used by the <clr>, <8pclr>, <ptrclr>, <input>, <input-dv>, <pwd> and <submit> tags are as follows:

0 - Transparent 8 - Medium Red
1 - Black 9 - Light Red
2 - Medium Green     A - Dark Yellow
3 - Light Green B - Light Yellow
4 - Dark Blue C - Dark Green
5 - Light Blue D - Magenta
6 - Dark Red E - Grey
7 - Cyan F - White                   (Note: A-F must be upper-case)

Text Character Definitions

The diagram below shows some example text character patterns. Each character is 6 pixels wide - the right-most 2 columns in the 8-pixel character block are not used. To define these patterns for characters >80 and >81, use the following <cdef> tags:

<cdef:80:0038443008443800>
<cdef:81:0038101010103800>

Internet web browser example character definitions

These characters can then be used in text displayed using the <p></p> tags. The <chr> tag can be used to specify a particular character code to display. The <clr> tag enables the text colour to be specified - each character has a single foreground colour and a single background colour. See the web page listing below for examples.

Displaying Graphics

Although 'graphics' could be displayed by defining and displaying text characters (as shown by the TI logo in the example web page listing and screenshot below), using the <8pchr> and <8pclr> tags provides more flexibility and colour control.

Using the <8pchr> tag, all 8 pixel columns of a character definition are displayed. To define the patterns below for characters >A0 and >A1, use the following <cdef> tags:

<cdef:A0:8040201008040201>
<cdef:A1:8142241818244281>

Internet web browser example character definitions

When using the <8pchr> tag, each character is displayed at a specified position. The position is based on the screen divided into 8*8 tiles, so positions are numbered from 0 (top left) to 767 (bottom right) - 24 rows of 32 tiles. To display the two characters above as the first two characters on row 3 of the screen:

<8pchr:A0:0040>
<8pchr:A1:0041>

Note that the character code must be exactly two upper-case hex digits and the character position must be exactly four upper-case hex digits.

To set the colour for a character, use the <8pclr> tag, where the colour is specified by 8 bytes. Each byte of the colour code specifies the colour of one row of the 8x8 character. The first nibble of each byte specifies the foreground colour, and the second byte specifies the background colour. The colour specified by an <8pclr> tag is stored for use by all following <8pchr> tags until changed by another <8pclr> tag. The default foreground and background colours for displaying characters using the <8pchr> tag are both transparent. The following example displays the two characters above, the first character as dark red on light yellow, and the second character as a rainbow effect on a black background.

<8pclr:6B6B6B6B6B6B6B6B><8pchr:A0:0040>
<8pclr:21416181A1C1E1F1><8pchr:A1:0041>

Internet web browser example character definitions

Characters displayed using <8pchr> tags can be mixed with text displayed using the <p></p> tags. If displayed in the same position on the screen, whichever tags occur later in the web page source overwrite those that occur earlier in the page source.

As characters displayed using the <8pchr> tag are displayed at fixed positions on the screen, they cannot be scrolled. Consider including the <noscroll> tag on a page where <8pchr> tags are used to disable the keyboard scroll keys.

If wanting to display a very complex graphic, the 13,024 byte (from v8.2 onwards) web page size limitation might be an issue. This can be worked around by drawing part of the screen on one page, using the <loadpage> tag at the end of that page to automatically load a second page, including the <noclearscreen> tag at the top of the second page and continuing to draw the graphic. Obviously though there will be a delay while the second page loads.  The same technique can be used for simple animation by drawing a main graphic then automatically loading a further sequence of pages to draw updates over that graphic. An example <loadpage> tag is:

<loadpage:www.stuartconnerdownloads.me.uk/tibrowser/elite_2.htm>

Hyperlinks Over Graphics

The <a></a> tags enable hyperlinks over text displayed with the <p></p> tags. To place hyperlinks over graphics, use the <arect> tag, which has the format <arect:AA:BB:CC:DD:(URL)>. AA, BB, CC and DD are upper-case hex values which define a rectangular area of pixels relative to the top left of the screen. If the <Return> key or <Fire> button is pressed, then if the screen pointer X position (in pixels, relative to the top left of the screen) is between the values AA and BB (inclusive) and the screen pointer Y position is between the values CC and DD (inclusive), then the link is invoked, and the specified URL is called with "?x=(pointer X position in hex)&y=(pointer Y position in hex)" appended to the URL (or "&x=..." if the link URL already contains a "?"). (Note that the screen pointer moves in steps of 2 pixels.)

Looking at the part of the page below, to place a hyperlink over the black cross, AA has the value 8, BB has the value 16, CC is 16 and DD is 24. With these values in hex, this gives the following <arect> tag:

<arect:08:10:10:18:www.stuartconnerdownloads...>

Internet web browser example character definitions

Many <arect> tags can be used on a page. If any of the hyperlink areas overlap, clicking on an overlapping area selects the first of the overlapping hyperlinks defined in the web page source.

As the pointer position is appended to the hyperlink URL when the hyperlink is selected, this information can be further processed if required by a script on the linked page. For example, if hyperlinks are required over a chess board graphic, rather than defining one hyperlink per chess board square, define one hyperlink over the complete board then process the pointer information returned in the hyperlink URL to determine which square the pointer was over.

Page Timeout

The page timeout feature loads a new page (or refreshes the current page) when no user input (keyboard or joystick movement) is made for the timeout period. The page listing for the screenshot below includes an example page timeout tag.

The <timeout> tag specifies a timeout period (of between 0 and 255 seconds, expressed as one hex byte) and a URL. The program calls the specified URL when no user input is made for the timeout period. The URL can be the same URL as the current page to refresh the page, or a different URL to go to a different page. The timeout period should be fairly accurate for a conventional TI-99/4A with the 32K RAM on the 8-bit bus and normal wait states. For a TI with the 32K RAM on the 16-bus, the actual timeout period will be approx 10-20% less than the specified value.

Note that with a long web page where the user has to scroll down to see the entire page, the <timeout> tag only takes effect if the tag is in the code for the section of the page currently displayed, or in a 'higher' area of the page. If the user has not yet scrolled down far enough to view the section of the page that contains the <timeout> tag, the tag has no effect. This feature can be used to design a page that automatically refreshes if the user is viewing the end of the page, but stops refreshing if the user then scrolls up to view earlier content.

Text Input Fields and Submit 'Button'

Text input fields and a submit 'button' enable interactive web sites to request information from the user. The screenshot below and the corresponding page listing below that illustrate the use of these features.

Browser versions up to and including v9.2 support just one type of input field, defined by a pair of <input></input> tags. The <input> tag specifies the field colour and a unique (on that page) tag ID. When the user clicks on an input field, they can type into the field, terminating input by pressing <Enter>. The field length is determined by the number of characters between the <input> and </input> tags. These characters may be spaces or some sort of text prompt. Note that these characters are not a default field value - the reported field value is considered to be blank until the user types a value into the field.

Browser version v9.3 onwards supports two additional types of input field:

A submit 'button' is defined by a pair of <submit></submit> tags or <submit-nh></submit> tags. The <submit(-nh)> tag specifies the field colour and a URL. The text between the <submit(-nh)> and </submit> tags is displayed as the button caption.

With the <submit> tag, when the user clicks on a submit field 'button', the tag ID and value of each input, input-dv and password field on the page are appended as parameters to the end of the URL specified by the <submit> tag. Looking at the example below, if the user types the text "Stuart" into the first field and "Conner" into the second field then clicks the Submit button, the program calls the URL "www.somesite.com?fn=Stuart&ln=Conner". Note that when appending the input field values to the URL, any "<space>", "#", "&", ";", "=" or "?" characters are encoded into their "%xx" equivalents. The total length of the URL is limited to 100 characters, so only a small number of fairly short input fields can be used. The URL is added to the URL history.

To work around the 100-character URL length limit when a larger number of fields or longer fields are needed, use the <submit-nh> tag. This supports a total URL length of many hundreds of characters (the exact number depends on what else is on the page), but the URL is not added to the URL history.

Two further points when using the input field and submit tags:

Internet web browser example input and submit fields screen

<99ml>
<p><br>Enter·first·name:·<input:1A:fn>(first·name)······· </input></p>
<p><br>Enter·last·name:··<input:1A:ln>(last·name)········ </input></p>
<p><br><br>·········<submit:1C:www.somesite.com>Submit</submit></p>
<timeout:3C:www.someothersite.com>
</99ml>

If redisplaying the page and the two field values are already known, input-dv fields can be used instead to specify default field values so that the user does not have to enter them again:

<p><br>Enter·first·name:·<input-dv:1A:06:fn>Stuart············· </input-dv></p>
<p><br>Enter·last·name:··<input-dv:1A:06:ln>Conner············· </input-dv></p>

To include a password field:

<p><br>Enter·password:·<pwd:1A:pw>········· </pwd></p>

User-Agent Parameter

When making HTTP GET calls, the browser identifies itself with a User-Agent parameter in the HTTP request header.

The User-Agent parameter enables a web page script to see it is talking to the TI browser and to format a page accordingly. Corey Anderson's www.myti99.com website uses the parameter to serve different versions of the site according to whether it is accessed from a PC or TI.

Cookies

Browser v9.3 onwards supports a cookie mechanism implemented through the <cookie> tag. This tag specifies a cookie value to be associated with the host name from which the page was downloaded. Cookie details are stored in a COOKIES file on the TIPI disk, and so are persistent across browser sessions.

The cookie value is included in the HTTP request header for all future page requests to the same host. So if the page cookie_test.htm downloaded from www.stuartconner.me.uk contains the tag <cookie:some_cookie_value>, then all future HTTP page requests to www.stuartconner.me.uk will contain the parameter "Cookie: www.stuartconner.me.uk=some_cookie_value" in the HTTP request header.

Two further points when using cookies:

Moved pages, invalid URLs and Server Errors

Browser versions up to and including v9.2 will only display pages where the HTTP response code is 200 (OK).

Browser version v9.3 onwards will display pages where the HTTP response code is 200 (OK) or 301 (new permanent URI). In addition, the browser will also display pages where the HTTP response code is 404 (page not found) or 500 (internal server error) as long as the error page includes content within <99ml></99ml> tags. This enables a server administrator to set up browser-compatible error pages for these response codes.

Example Web Page Listing

The following listing displays the example web page below which uses many of the supported tags. Space characters in the listing have been replaced with a small dot to aid legibility.

Internet web browser example page

<99ml>

Character·definitions·for·TI·logo.
<cdef:01:7CC0C0C0C0C0C0C0>
<cdef:02:C0485454486020C0>
<cdef:03:0000000000003048>
<cdef:04:FC80C04060381C0C>
<cdef:05:C408080C000000B0>
<cdef:06:981818D858585840>
<cdef:07:E82828E884848488>
<cdef:08:F84C040400000000>
<cdef:09:24180084C4703C00>
<cdef:0A:3040800000808000>

Character·definition·for·bullet.
<cdef:0B:0000003030000000>

Colour·bar.
<p><br><clr:16>····<clr:13>····<clr:11>····<clr:1D>····<clr:14>····<clr:12>····<clr:18>····<clr:15>····<clr:19>····<clr:16>···</clr></p>

Blank·line·in·cyan.
<p><clr:17>·······································</clr></p>

TI·Logo·and·title·in·cyan.
<p><clr:17>··<chr:01><chr:02><chr:03>·<chr:0E><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:11></clr></p>
<p><clr:17>·<chr:04><chr:05><chr:06><chr:07>·<chr:12>··TEXAS·INSTRUMENTS·COMPUTERS··<chr:12></clr></p>
<p><clr:17>··<chr:08><chr:09><chr:0A>·<chr:16><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:0F><chr:18></clr></p>

Blank·line·in·cyan.
<p><clr:17>·······································</clr></p>

Colour·bar.
<p><clr:16>····<clr:13>····<clr:11>····<clr:1D>····<clr:14>····<clr:12>····<clr:18>····<clr:15>····<clr:19>····<clr:16>···</clr><br><br></p>

Menu.
<p><chr:0B>·Click·<a:www.stuartconner.me.uk/tibrowser/z_ti_test_2.htm>here</a>·for·a·short·history·of·the<br>··TI-99/4A.<br></p>

<p><chr:0B>·Click·<a:www.stuartconner.me.uk/tibrowser/z_ti_test_3.htm>here</a>·for·the·pinout·of·the<br>··TMS9900·processor.</p>

</99ml>

Using the Python Script to Emulate the Lantronix Serial-to-Ethernet Adaptor

As an alternative to using the Lantronix UDS-10 serial-to-Ethernet adaptor, I have also written a small Python script which emulates the functionality of the UDS-10 as used by the browser. This script will work on a Raspberry Pi, Windows (XP or later) PC or Mac. So you can connect the serial port on your TI to a serial port on your Pi/PC/Mac, and run the script to process web connection requests from the TI. This section is aimed at Python and Pi novices to help you get this running.

Running the Python Script on a Raspberry Pi

The following instructions are for a Raspberry Pi 2 Model B (the Python script has also been tested on a Raspberry Pi 3 Model B). They assume a fresh install of the Raspbian operating system, you've got an Internet network connection configured, and that you've got a USB serial adaptor plugged in and connected to the serial port on your TI. The Python software and the Python serial port module should already be installed along with the Raspbian O/S.

  1. Open a Terminal window from the task bar at the top of the screen. At the prompt, type python ‑m serial.tools.list_ports<Return> to list the serial ports available on the Pi. If you have more than one serial port, check which is connected to the TI, and note the port name - it is probably "/dev/ttyUSB0". Close the Terminal window.
  2. Open the Epiphany web browser from the task bar at the top of the screen, and navigate to this page.
  3. Click the link to this zip file to download it. Open the File Manager from the task bar at the top of the screen, and navigate to the /home/pi/Downloads/ folder, which should contain the downloaded Lantronix_Emulator.zip file. Right-click the zip file then click the Extract Here command to extract the Lantronix_Emulator.py file.
  4. In the File Manager, right-click the Lantronix_Emulator.py file then click the Python 2 (IDLE) command to load the script into the Python script editor.
  5. In the script, find the lines:
     
    #Initialise serial port.
    serial_port =
    'COM3' # <---- Set name of serial port on this line.
     
    Edit the name of the serial port to that connected to the TI, as noted in step 1. Enclose the name in single quotes, as shown above.
  6. Click File > Save to save the .py file.
  7. Click Run > Run Module to run the script in a Python Shell window. If all is well, the script should display a message "Waiting for connection request from TI", and show further messages as each connection request is processed as you use the browser.
  8. To exit the program, just close the Python Shell window. To run it again, just follow steps 4 and 7 above.

Running the Python Script on a Windows PC

The following instructions assume that you've got a COM serial port on your PC (and if it's a USB serial adaptor, you have the relevant drivers already loaded), and it is connected to the serial port on your TI.

  1. Download the latest Python v3.5.x version here and run the installer. On the setup screen, select the checkbox "Add Python 3.5 to PATH" then click the [Install Now] button.
  2. Click Start > All Programs > Accessories > Command Prompt to open a command window. Type pip install pyserial<Return> to download and install the Python serial port module required.
  3. In the command window, type python -m serial.tools.list_ports<Return> to list the COM serial ports available on the PC. If you have more than one COM port, check which is connected to the TI, and note the port number. Close the command window.
  4. Download this zip file and extract the Lantronix_Emulator.py file into a folder.
  5. Right-click the .py file, then select the Edit with IDLE > Edit with IDLE 3.5 (xx-bit) command from the popup menu. This will load the script into the Python script editor.
  6. In the script, find the lines:
     
    #Initialise serial port.
    serial_port =
    'COM3' # <---- Set name of serial port on this line.
     
    Edit the name of the serial port to that connected to the TI, as noted in step 3. Enclose the name in single quotes, as shown above.
  7. Click File > Save to save the .py file.
  8. Click Run > Run Module to run the script in a Python Shell window. If all is well, the script should display a message "Waiting for connection request from TI", and show further messages as each connection request is processed as you use the browser.
  9. To exit the program, just close the Python Shell window. Now the COM port number is set up in the script, you can run it again by just double-clicking the .py file.

Running the Python Script on a Mac

The following instructions assume that you've got a USB serial adaptor connected to your Mac and you have the relevant drivers already loaded, and it is connected to the serial port on your TI. The Python software should already be installed (Python comes as standard on a Mac).

  1. Click Applications > Utilities > Terminal to launch a Terminal window.
  2. In the Terminal window, type the following commands:
    1. sudo easy_install pip<Return> (then enter your password when prompted) to install the Python pip package management system.
    2. sudo pip install pyserial<Return> (then enter your password when prompted) to download and install the Python serial port module required.
    3. python -m serial.tools.list_ports<Return> to list the serial ports available on the Mac. If you have more than one serial port, check which is connected to the TI, and note the port name - it is may be something like "/dev/cu.usbserial". The name varies depending on the brand of USB serial adaptor you have connected.
    4. curl -O http://www.stuartconner.me.uk/ti/other_downloads/Lantronix_Emulator.zip<Return> to download the Python script.
    5. unzip Lantronix_Emulator.zip<Return> to extract the Python script Lantronix_Emulator.py file into your Home directory.
  3. Navigate to your Home directory in Finder, then right-click the Lantronix_Emulator.py file and open it with TextEdit.
  4. In the script, find the lines:
     
    #Initialise serial port.
    serial_port =
    'COM3' # <---- Set name of serial port on this line.
     
    Edit the name of the serial port to that connected to the TI, as noted in step 2(c). Enclose the name in single quotes, as shown above.
  5. Save the .py file and exit TextEdit.
  6. To run the script, type python Lantronix_Emulator.py<Return> in the Terminal window. If all is well, the script should display a message "Waiting for connection request from TI", and show further messages as each connection request is processed as you use the browser. You must keep the Terminal running the script while you're surfing on your TI.

I2C Interface

Introduction

This section looks at an I2C - "I Squared C" or "Inter-Integrated Circuit" - interface for the TI-99. The ICs in your old VCR would have used an I2C interface to communicate with each other, and your digital STB likely uses one internally as well. Devices with an I2C interface are gaining popularity in the hobbyist market also because an I2C interface is one of the interfaces that comes as standard on the Raspberry Pi and Arduino. There are many types of I2C device available - LED drivers, LCD drivers, serial memory (RAM and EEPROM), clocks, analogue-to-digital converters, digital-to-analogue converters, and so on.

Electrically, the I2C interface is very simple - just two wires plus ground. One wire is for a clock (SCL) and the other is for serial data (SDA). When writing to a 'slave' device, the 'master' drives both the clock and the data lines. When reading from a slave device, the master controls the clock line and the slave drives the data line. Multiple devices can be connected to the clock and data lines. Each device has a unique address, and the master sends the address of the device to access before reading or writing data to it. The clock frequency is very flexible - most devices have an upper limit of about 100 kHz, and anything below that is OK.

Implementing an I2C Interface on the TI

So how can we use this on the TI? Well the joystick port has two lines that we can control individually, and we can feed the data line back in through a third pin to sense its state. All we need is a 74LS06 inverter and two pullup resistors hooked onto the joystick port. You'll need a +5V power supply as well. A circuit diagram is shown below. The components to the left of the joystick connector are existing circuits internal to the TI console and are shown for reference only. The components to the right of the joystick connector show the circuit you need to build.

A few notes about the circuit:

I2C interface schematic

You won't be able to use the console keyboard scanning routine while using the I2C interface as that will send out some spurious signals over the interface (you actually need the interface powered down or disconnected for the keyboard to be read properly), but you could probably write a bespoke keyboard scanning routine that scans just certain keys on the keyboard matrix rows/columns that aren't connected to the joystick port. Oh - Alpha Lock needs to be up as well!

Software-wise, the code needs to be able to control pins on the joystick port directly over the CRU interface, so that means assembly code. It should be possible for an XB program (for example) to pass data to/from the assembly routines. Cortex BASIC should also work as that includes instructions to control the CRU interface. The routines fall into two groups:

  1. Routines that provide raw control of the I2C interface - send the start sequence, send the I2C device address, send (or read) a byte, and send the stop sequence. These routines will be common to any type of I2C device being controlled.
  2. Routines that determine the actual bytes to send or receive according to the I2C device type. So for a serial memory device you would need to send one or more bytes specifying the memory address to access, then read or write the contents of that address. For a clock device you would need to send bytes to set or read dd-mm-yy and hh:mm.

Controlling an I2C LCD Display

For a simple proof of concept, the photo below shows the TI connected to a 20 character by 4 four line LCD display with an I2C interface - £7.00 on eBay. The LCD display itself has a parallel interface but comes with an I2C interface 'backboard' already soldered on that performs the serial I2C interface to parallel conversion. A pair of LEDs on the interface board helped with debugging. The code I've used to display a message on this is below.

I2C interface LCD display

       DEF  START
 
SCL    EQU  19           CRU BIT FOR SCL SIGNAL.
SDA    EQU  31           CRU BIT FOR SDA SIGNAL.
 
START  LWPI WS           LOAD WORKSPACE.
 
       LIMI 0            DISABLE ALL INTERRUPTS.
 
*CONFIGURE TMS9901.
 
       CLR  R12          9901 CRU BASE ADDRESS.
 
       SBO  20           SET 9901 P4 HIGH TO SELECT DEMUX 1 IN U302.
       SBZ  18           SET 9901 P2 (A) LOW ...
  
*TO CONTROL SCL:
*- SBO SCL TO SET SCL HIGH
*- SBZ SCL TO SET SCL LOW
 
*TO CONTROL SDA:
*- SBO SDA TO SET SDA HIGH
*- SBZ SDA TO SET SDA LOW
 
*SET INITIAL CONDITION, WITH SCL AND SDA BOTH HIGH.
 
       SBO  SCL
       SBO  SDA
       BL   @DELAY1
 
*INITIALISE POINTER TO ADDRESS/DATA TO SEND.
 
       LI R4,I2CDAT
 
*SEND START SEQUENCE - SDA LOW, SCL REMAINS HIGH.
 
       SBZ  SDA
       BL   @DELAY1
 
*SEND I2C DEVICE ADDRESS.
 
       MOVB *R4+,R9      GET ADDRESS INTO MSB OF R9.
       BL   @SNDBYT      SEND ADDRESS.
 
*SEND COMMANDS AND DATA.
*HAVE TO SEND TO LCD DISPLAY AS TWO GROUPS OF 4 BITS,
*WITH CONTROL SIGNALS ALSO SET APPROPRIATELY.
*THIS SECTION OF CODE IS SPECIFIC TO THE TYPE OF I2C DEVICE
*BEING CONTROLLED.
 
GETBYT MOVB *R4+,R2      GET DATA INTO MSB OF R2.
       CB   R2,@BFF      >FF TERMINATOR CHARACTER?
       JEQ  STOP         YES, JUMP.
 
       LI   R7,>0900     RIGHT NIBBLE OF R7 CONTAIN DATA FOR LCD CONTROL PINS.
*                        SET TO STATE FOR SENDING AN ASCII CHARACTER
*                        (BACKLIGHT ON, RS HIGH, R/W LOW, E LOW).
 
       CB   R2,@B00      LCD DISPLAY CONTROL CHARACTER?
       JNE  GETBY2       NO, JUMP.
       LI   R7,>0800     YES. SET R7 TO STATE FOR SENDING AN LCD CNTRL CHAR
*                        (BACKLIGHT ON, RS LOW, R/W LOW, E LOW).
       MOVB *R4+,R2      GET THE CHARACTER AFTER THE >00.
 
*      DO LEFT NIBBLE.
 
GETBY2 MOVB R2,R5        MAKE COPY OF ORIGINAL DATA BYTE.
       ANDI R5,>F000     SET ALL BITS OF RIGHT NIBBLE TO 0.
       SOC  R7,R5        SET STATE OF BACKLIGHT, RS, R/W, E.
       MOV  R5,R8        COPY BYTE.
       MOV  R5,R9
       BL   @SNDBYT
       ORI  R5,>0400     SET E HIGH.
       MOV  R5,R9
       BL   @SNDBYT
       MOV  R8,R9        SET E LOW AGAIN.
       BL   @SNDBYT
 
*      DO RIGHT NIBBLE.
 
       MOVB R2,R5        COPY ORIGINAL DATA BYTE AGAIN.
       SLA  R5,4         SHIFT RIGHT NIBBLE TO LEFT NIBBLE.
       ANDI R5,>F000     SET ALL BITS OF RIGHT NIBBLE TO 0.
       SOC  R7,R5        SET STATE OF BACKLIGHT, RS, R/W, E.
       MOV  R5,R8        COPY BYTE.
       MOV  R5,R9
       BL   @SNDBYT
       ORI  R5,>0400     SET E HIGH.
       MOV  R5,R9
       BL   @SNDBYT
       MOV  R8,R9        SET E LOW AGAIN.
       BL   @SNDBYT
 
       JMP  GETBYT       DO NEXT BYTE.
 
*SEND STOP SEQUENCE - SDA TAKEN HIGH WHEN SCL ALREADY HIGH.
 
STOP   SBZ  SCL          SET SCL LOW.
       BL   @DELAY2      WAIT 1/2 CLOCK PERIOD.
 
       SBZ  SDA          SET SDA LOW AGAIN AFTER ACK BIT.
       BL   @DELAY2      WAIT 1/2 CLOCK PERIOD.
 
       SBO  SCL          SET CLOCK HIGH.
       BL   @DELAY1      WAIT 1 CLOCK PERIOD.
       SBO  SDA          SET SDA HIGH.
 
FINISH JMP FINISH        LOOP FOREVER.
 
*
*SEND BYTE IN MSB OF R9.
*R9 IS DESTROYED.
*
 
SNDBYT MOV  R11,@SAVR11  SAVE RETURN ADDRESS.
 
       LI   R3,8         LOAD BIT COUNTER.
 
SBITS  SLA  R9,1         SHIFT MSb OF BYTE INTO CARRY STATUS BIT.
       JOC  CARRY1
 
       MOV  @SBZ31,@SETSDA  NO CARRY - WRITE OP-CODE TO ROUTINE BELOW.
       JMP  CARRY2
 
CARRY1 MOV  @SBO31,@SETSDA  CARRY - WRITE OP-CODE TO ROUTINE BELOW.
 
CARRY2 SBZ  SCL          SET SCL LOW.
       BL   @DELAY2      WAIT 1/2 CLOCK PERIOD.
 
SETSDA DATA >0000        SET SDA - OP-CODE WRITTEN ABOVE.
       BL   @DELAY2      WAIT 1/2 CLOCK PERIOD.
 
       SBO  SCL          SET SCL HIGH.
       BL   @DELAY1      WAIT 1 CLOCK PERIOD.
 
       DEC  R3           INCREMENT BIT COUNTER.
       JNE  SBITS        LOOP UNTIL ALL BITS DONE.
 
       SBZ  SCL          SET CLOCK LOW.
       BL   @DELAY2      WAIT 1/2 CLOCK PERIOD.
 
       SBO  SDA          SET SDA HIGH SO SLAVE CAN SEND ACK BIT.
       BL   @DELAY2      WAIT 1/2 CLOCK PERIOD.
 
       SBO  SCL          SEND ANOTHER CLOCK PULSE FOR ACK BIT - IGNORE IT.
       BL   @DELAY1      WAIT 1 CLOCK PERIOD.
 
       MOV  @SAVR11,R11  RESTORE RETURN ADDRESS
       B    *R11         RETURN.
 
*
*DELAY LOOP - ONE PERIOD.
*
 
DLYPRD EQU  >0002
 
DELAY1 LI   R1,DLYPRD
DEL1   DEC  R1
       JNE  DEL1
 
       B    *R11
 
*
*DELAY LOOP - HALF PERIOD.
*
 
DELAY2 LI   R1,DLYPRD/2
       JMP  DEL1
 
*STORAGE.
 
WS     BSS  32           PROGRAM WORKSPACE.
 
SAVR11 DATA >0000        SAVED RETURN ADDRESS FOR SUB-ROUTINES.
 
SBO31  DATA >1D1F        OP-CODE FOR 'SBO 31'.
SBZ31  DATA >1E1F        OP-CODE FOR 'SBZ 31'.
 
B00    BYTE >00
 
*NOTE ABOUT I2C ADDRESS:
*RASPBERRY PI REPORTS LCD DISPLAY AS ADDRESS >27.
*NEED TO SHIFT THAT LEFT ONE BIT AS THE LSb IS THE R/W SIGNAL.
*SO NEED TO USE ADDRESS >4E TO WRITE, >4F TO READ.
 
*NOTE ABOUT I2C LCD DISPLAY DATA:
*PREFIX ALL DISPLAY INSTRUCTION (COMMAND) BYTES WITH >00.
*THIS IS USED TO SET THE DISPLAY RS PIN TO THE APPROPRIATE STATE.
*THE TEXT TO DISPLAY IS SEQUENTIAL ASCII CHARACTERS.
 
I2CDAT BYTE >4E          I2C INTERFACE IC ADDRESS. MUST IMMEDIATELY PRECEDE DATA.
 
       BYTE >00,>33,>00,>32,>00,>06  COMMAND BYTES TO CONFIGURE DISPLAY.
       BYTE >00,>0C,>00,>28,>00,>01
 
       BYTE >00,>80                 COMMAND BYTE TO ADDRESS LCD LINE 1.
       TEXT '*      Texas       *'  TEXT FOR LINE 1.
       BYTE >00,>C0                 COMMAND BYTE TO ADDRESS LCD LINE 2.
       TEXT '*   Instruments    *'  TEXT FOR LINE 2.
       BYTE >00,>94                 COMMAND BYTE TO ADDRESS LCD LINE 3.
       TEXT '*    TI-99/4A      *'  TEXT FOR LINE 3.
       BYTE >00,>D4                 COMMAND BYTE TO ADDRESS LCD LINE 4.
       TEXT '*  I2C Interface   *'  TEXT FOR LINE 4.
 
BFF    BYTE >FF          TERMINATOR CHARACTER.
 
       END

The LCD Display I2C Interface in Detail

As stated above, the LCD display used in this proof of concept has a parallel interface but comes with an I2C interface 'backboard' already soldered on that performs the serial I2C interface to parallel conversion.

The I2C interface IC used on the 'backboard' is a PCF8574T (datasheet). This has 8 input/output pins, referred to as P0 - P7. When a byte is written to the IC, the first bit sent (the code above sends the MSb first) is output on pin P7, and the last bit sent (the LS bit of the byte) is output on pin P0.

The digital connections on the LCD display (datasheet 1 or datasheet 2) are:

Clearly the 8-bit output of the I2C interface IC is not able to simultaneously control the 11 digital connections to the LCD display, so the display is connected and configured into 4-bit interface mode where each byte of data is transferred as two reads/writes to data bits DB7 - DB4 (the higher 4 bits of data first, then the lower 4 bits of data), and data bits DB3 - DB0 on the display are unused and not connected. The two 4-bit data transfers have to be handled by the I2C master device through the interface IC. As only 4 bits of data are used for the display, this leaves 4 input/output pins on the I2C interface IC free for controlling the LCD display RS, R/W and E signals, also for switching the display backlight on and off (this latter function is implemented on the 'backboard' by controlling the power supply to the backlight - logic 1 switches the backlight on, and logic 0 switches the backlight off). The actual connections between the I2C interface IC and the LCD display are shown in the diagram below, which also shows the mapping of the bits in the byte written by the TI to the P0 - P7 pins on the I2C interface IC. As an example, to set RS high, the backlight on, and all other signals low, write the binary value 0000 1001 = >09. To set E high as well, write the binary value 0000 1101 = >0D.

I2C LCD Display Connections

Writing an instruction (command) byte to the LCD display through the I2C interface IC requires the steps outlined below. These steps assume that the I2C interface IC address has already been sent, and that the code handles the setting of the start and stop sequences and the bit clock as detailed in the IC datasheet and the code comments above.

  1. Isolate the 4 MS bits (nibble) of data. Sets the bits in the LS nibble to 1000 (backlight on, RS, R/W, E low). Send the byte to the I2C interface IC.
  2. Set the bits in the LS nibble to 1100 (backlight on, RS and R/W low, E high). Send the byte to the I2C interface IC to start the E high pulse.
  3. Set the bits in the LS nibble to 1000 again (backlight on, RS, R/W, E low). Send the byte to the I2C interface IC to end the E high pulse.
  4. Copy the 4 LS bits of data to the MS nibble, then repeat the three steps above to send the data and pulse E high.

To write a data byte (a character or character pattern) to the LCD display requires the same steps but RS must be high instead of low. In the code above, instruction (command) bytes are prefixed with a >00 byte which is used to differentiate between instruction and data bytes so the state of the RS signal can be set accordingly.

The set of instruction (command) bytes initially sent to initialise the LCD display are as follows: (these are also detailed in the LCD display (datasheet 1 or datasheet 2)

  1. >33, >32 - this is a specific set of bytes to configure the LCD display into 4-bit data transfer mode. (The LCD display defaults to 8-bit data transfer mode at power on, so the display interprets each of the three >3 nibbles writes as full 8-bit transfers of >3X, and the final >2 nibble write as a full 8-bit transfer of >2X to configure the display into 4-bit data transfer mode.)
  2. >06 - set entry mode - cursor position increments, display does not shift.
  3. >0C - display on, cursor off, blink off.
  4. >28 - 4-bit data transfers, 2 line display, 5x8 dot character font. (Not sure how the 1 or 2 line display setting relates to 4 line displays.)
  5. >01 - clear display.
  6. >80 - set display data RAM address for first line.

Example TIPI Interface Routines

TIPI is a storage, network and device expansion for the TI-99/4A that connects to an external Raspberry Pi device. It is available in sideport and PEB expansion card versions. Further details are available on the TIPI wiki.

TIPI Network Socket Interface

The following code provides an example of using the TIPI network socket interface in assembly language to download a web page. The screenshot below shows the program output.

TIPI socket example

        DEF  START          Program entry point.

        REF  VSBW           VDP single byte write.
        REF  VMBW           VDP multiple byte write.
        REF  VWTR           VDP write to register.

GPLWS   EQU  >83E0          GPL workspace. For exchanging parameters with TIPI ROM routines.
GPLWSR0 EQU  GPLWS          Register 0 in GPL workspace.
GPLWSR1 EQU  GPLWS+2        Register 1 in GPL workspace.

SENDMSG EQU  >4012          Address holding address of send message routine in TIPI ROM.
RECVMSG EQU  >4010          Ditto for address of receive message routine.

START   LWPI MYWS           Load program workspace.

****************************************************
*Switch to text mode (40 x 24).
****************************************************

*Set VDP register 1 to set text mode but with display disabled until we have set up the screen.

        LI   R0,>01B0       VDP register 1 - Text mode with display disabled.
        BLWP @VWTR          VDP write to register.

*Clear screen.

        CLR  R0             Start writing at start of screen image table.
        LI   R1,' '*256     <Space> character in MSB.
        LI   R2,960-1       960 characters to do - but do 1 outside loop.
        BLWP @VSBW          Write one byte.
CLR1    MOVB R1,@>8C00      Write successive bytes without doing a BLWP - quicker.
        DEC  R2
        JNE  CLR1

*Set VDP register 7 to set screen colours.

        LI   R0,>07F5       VDP register 7 - colour: white on light blue.
        BLWP @VWTR          VDP write to register.

*Set VDP register 1 to enable display.

        LI   R0,>01F0       VDP register 1 - Text mode with display enabled.
        BLWP @VWTR          VDP write to register.

        SWPB R0             Write VDP register 1 contents to >83D4
        MOVB R0,@>83D4      See E/A manual page 326.

****************************************************
*Find CRU address of TIPI device.
****************************************************

        LI   R12,>1000      CRU address to start searching for TIPI card.

ISDSR   SBO  0              Switch card on. Card ROM mapped to >4000 - >5FFF.
        LI   R0,>AA00       >AA is first byte in a DSR header.
        CB   @>4000,R0      Card present with a valid DSR?
        JNE  NXTCARD        No, jump.
        MOV  @>4008,R0      Get address of start of device list in DSR. Only the first device in the list is checked.
        AI   R0,4           R0 now points to device name length (followed by device name itself).
        LI   R1,TIPINAM     Address of TIPI device name length and name.
        LI   R2,5           Compare 5 bytes.
TEST    CB   *R0+,*R1+      Bytes match?
        JNE  NXTCARD        No, jump.
        DEC  R2             Decrement byte count.
        JNE  TEST           Loop to check next byte.
        SBZ  0              Found the TIPI device. Switch card off.
        MOV  R12,@TIPICRU   Store CRU address at which TIPI device found.
        JMP  FOUNDTP

NXTCARD SBZ  0              Switch card off.
        AI   R12,>0100      Look at next PEB card.
        CI   R12,>2000      Already checked last possible card?
        JEQ  NOTIPI         Yes, TIPI device not found.
        JMP  ISDSR          No, check card.

NOTIPI  LI   R0,0           Print 'TIPI device not found' message.
        LI   R1,MSG01
        LI   R2,MSG02-MSG01
        BLWP @VMBW
        B    @HALT          Go to endless loop.

FOUNDTP LI   R0,0           Print 'TIPI device found' message.
        LI   R1,MSG02
        LI   R2,MSG03-MSG02
        BLWP @VMBW

        MOV  R12,R5         Print address at which TIPI device found.
        LI   R0,22
        BL   @DISPHX

****************************************************
*Open socket on TIPI device.
*Note that the host name MUST be followed by the
*port number for the open socket command to work.
****************************************************

        LI   R0,40          Print 'Opening socket' message.
        LI   R1,MSG03
        LI   R2,MSG04-MSG03
        BLWP @VMBW

        LI   R9,OPNSCK2-OPNSCK  Number of bytes in message to send.
        MOV  R9,@GPLWSR0    Save in GPL workspace R0.
        LI   R9,OPNSCK      Address of message to send.
        MOV  R9,@GPLWSR1    Save in GPL workspace R1.

        BL   @TIPICOM       Send TIPI command message and get result.

        MOV  @GPLWSR0,R1    Get length of message received from GPLWS R0 - should be 1.
        CI   R1,1
        JNE  OPNFAIL        Jump if command failed.

        CLR  R1
        MOVB @RECVBUF,R1    Get message value from first byte in buffer - should be 255.
        CI   R1,>FF00
        JNE  OPNFAIL        Jump if command failed.

        LI   R0,59          Print 'success' message.
        LI   R1,MSG04
        LI   R2,MSG05-MSG04
        BLWP @VMBW
        JMP  SENDGET

OPNFAIL LI   R0,59          Print 'failed' message.
        LI   R1,MSG05
        LI   R2,MSG06-MSG05
        BLWP @VMBW
        B    @HALT           Go to endless loop.

****************************************************
*Send GET request.
****************************************************

SENDGET LI   R0,2*40        Print 'Sending GET request' message.
        LI   R1,MSG06
        LI   R2,MSG07-MSG06
        BLWP @VMBW

        LI   R9,WRTSCK2-WRTSCK  Number of bytes in message to send.
        MOV  R9,@GPLWSR0    Save in GPL workspace R0.
        LI   R9,WRTSCK      Address of message to send.
        MOV  R9,@GPLWSR1    Save in GPL workspace R1.

        BL   @TIPICOM       Send TIPI command message and get result.

        MOV  @GPLWSR0,R1    Get length of message received from GPLWS R0 - should be 1.
        CI   R1,1
        JNE  SNDFAIL        Jump if command failed.

        CLR  R1
        MOVB @RECVBUF,R1    Get message value from first byte in buffer - should be 255.
        CI   R1,>FF00
        JNE  SNDFAIL        Jump if command failed.

        LI   R0,104         Print 'success' message.
        LI   R1,MSG04
        LI   R2,MSG05-MSG04
        BLWP @VMBW
        JMP  RCVRSP

SNDFAIL LI   R0,104         Print 'failed' message.
        LI   R1,MSG05
        LI   R2,MSG06-MSG05
        BLWP @VMBW
        JMP  HALT           Go to endless loop.

*******************************************************************************
*Receive and display GET request response.

*Having sent the GET request, it will take a period of time for the server to
*respond and for that response to be available through the Pi, so need to
*repeatedly call the TIPI socket read command until we get some data (it may
*take in excess of 70 read command loops until the data is returned). We should
*always get *some* response back, even if it's an HTTP error page.

*We check whether read data is available by checking the message length
*returned by the read command - a value of 0 indicates no data. If the received
*data does not all fit in the specified buffer then a succession of read commands
*can be used to retrieve all the data - keep sending read commands until a
*message length of 0 is returned again. (You could compare the message length
*against the buffer length, but you couldn't be sure if the message *exactly*
*fitted in the buffer without doing a further read command). In this example
*application we're only interested in whether the buffer has overflowed, not
*what the overflowed data actually is.
*******************************************************************************

RCVRSP  LI   R0,3*40        Print 'Reading GET response' message.
        LI   R1,MSG07
        LI   R2,MSG08-MSG07
        BLWP @VMBW

RCVRSP2 LI   R9,RDSCK2-RDSCK  Number of bytes in message to send.
        MOV  R9,@GPLWSR0    Save in GPL workspace R0.
        LI   R9,RDSCK       Address of message to send.
        MOV  R9,@GPLWSR1    Save in GPL workspace R1.

        BL   @TIPICOM       Send TIPI command message and get result.

        MOV  @GPLWSR0,R5    Get the length of the message received back.
        JEQ  RCVRSP2        If message length is 0, read socket again.

*Have received a message which is now in the buffer. Display the start of the message.

        LI   R0,4*40        Display position.
        LI   R1,RECVBUF
        LI   R2,880-160     (Leave bottom two lines blank)
        BLWP @VMBW

*Send a further read command and check the result to see if more data is available (that is, there is a buffer overflow).

        LI   R9,RDSCK2-RDSCK  Number of bytes in message to send.
        MOV  R9,@GPLWSR0    Save in GPL workspace R0.
        LI   R9,RDSCK       Address of message to send.
        MOV  R9,@GPLWSR1    Save in GPL workspace R1.

        BL   @TIPICOM       Send TIPI command message and get result.

        MOV  @GPLWSR0,R5    Get the length of the message received back.
        JEQ  RCVRSP3        If message length is 0, there was no buffer overflow.

        LI   R0,22*40       Print '(Buffer overflowed)' message.
        LI   R1,MSG08
        LI   R2,MSG09-MSG08
        BLWP @VMBW
        JMP  CLOSSCK

RCVRSP3 LI   R0,22*40       Print '(No buffer overflow)' message.
        LI   R1,MSG09
        LI   R2,MSG10-MSG09
        BLWP @VMBW

****************************************************
*Close the socket.
****************************************************

CLOSSCK LI   R0,23*40       Print 'Closing socket' message.
        LI   R1,MSG10
        LI   R2,MSG11-MSG10
        BLWP @VMBW

        LI   R9,CLSCK2-CLSCK  Number of bytes in message to send.
        MOV  R9,@GPLWSR0    Save in GPL workspace R0.
        LI   R9,CLSCK       Address of message to send.
        MOV  R9,@GPLWSR1    Save in GPL workspace R1.

        BL   @TIPICOM       Send TIPI command message and get result.

        MOV  @GPLWSR0,R1    Get length of message received from GPLWS R0 - should be 1.
        CI   R1,1
        JNE  CLSFAIL        Jump if command failed.

        CLR  R1
        MOVB @RECVBUF,R1    Get message value from first byte in buffer - should be 255.
        CI   R1,>FF00
        JNE  CLSFAIL        Jump if command failed.

        LI   R0,23*40+19    Print 'success' message.
        LI   R1,MSG04
        LI   R2,MSG05-MSG04
        BLWP @VMBW
        JMP  HALT

CLSFAIL LI   R0,23*40+19    Print 'failed' message.
        LI   R1,MSG05
        LI   R2,MSG06-MSG05
        BLWP @VMBW

HALT    JMP  HALT           Loop here forever.

*******************************************************************************
*Subroutine - display hex value as 4 hex digits.
*Call though BL *DISPHX.
*Before calling:
*- R0 needs to contain the screen display position (0-959).
*- R5 needs to contain the hex value to display.
*******************************************************************************

DISPHX  LI   R6,4           Set count to display 4 digits.

DISPHX2 MOV  R5,R7          Copy hex value to display.
        SRL  R7,12          Isolate MS hex digit into LS digit position.
        SLA  R7,8           Move to MS byte.
        CI   R7,>0900       Numeric value 0-9?
        JLE  DISPHX3        Yes, jump.
        AI   R7,>0700       No, adjust to value A-F.
DISPHX3 AI   R7,'0'*256     Convert to ASCII.

        MOV  R7,R1          Move to R1 for VSBW.
        BLWP @VSBW
        INC  R0             Increment screen display position.

        SRC  R5,12          Align next digit.
        DEC  R6             Done all 4 digits?
        JNE  DISPHX2        No, loop.

        B    *R11           Return.

*******************************************************************************
*Subroutine - send TIPI command message and get result.
*Call through BL @TIPICOM.
*Before calling:
*- GPLWSR0 needs to contain the number of bytes in the command message to send.
*- GPLWSR1 needs to contain the address of the message to send.
*After calling:
*- GPLWSR0 contains the length of the result message received.
*- Buffer RECVBUF contains the result message received.
*******************************************************************************

TIPICOM MOV  @TIPICRU,R12   Get address of TIPI device.
        SBO  0              Switch on TIPI card.
        LWPI GPLWS          Switch to GPL workspace.
        MOV  @SENDMSG,R4    Get address of send message routine in TIPI ROM.
        BL   *R4            Call send message routine.

*Get result. Receive message routine blocks until this is available.

        CLR  R0             Will contain the length of the message received back from the send message routine.
        LI   R1,RECVBUF     Address of buffer to store received message.
        MOV  @RECVMSG,R4    Get address of receive message routine in TIPI ROM.
        BL   *R4            Call receive message routine.
        LWPI MYWS           Back to program workspace.
        SBZ  0              Switch TIPI card off.

        B    *R11           Return.

****************************************************
*Data.
****************************************************

MYWS    BSS  32             Workspace.

TIPINAM BYTE >04            TIPI device name length and name to search for in DSR.
        TEXT 'TIPI'
        EVEN

TIPICRU DATA 0              TIPI device CRU address.

*Open socket command bytes.
OPNSCK  BYTE >22            TCP extension for socket access.
        BYTE >00            Socket handle.
        BYTE >01            Open command.
        TEXT 'www.stuartconner.me.uk:80' Host name and port.
OPNSCK2 EQU  $              (Label for calculating message length)

*Write socket command bytes.
WRTSCK  BYTE >22            TCP extension for socket access.
        BYTE >00            Socket handle.
        BYTE >03            Write command.
        TEXT 'GET /tibrowser/z.htm HTTP/1.0'
        BYTE >0D,>0A
        TEXT 'User-Agent: TI09.2'  (User-agent for TI browser)
        BYTE >0D,>0A
        TEXT 'Host: www.stuartconner.me.uk'
        BYTE >0D,>0A
        BYTE >0D,>0A
WRTSCK2 EQU  $              (Label for calculating message length)

*Read socket command bytes.
RDSCK   BYTE >22            TCP extension for socket access.
        BYTE >00            Socket handle.
        BYTE >04            Read command.
        BYTE >04,>00        Max message size >0400 (1024).
RDSCK2  EQU  $              (Label for calculating message length)

*Close socket command bytes.
CLSCK   BYTE >22            TCP extension for socket access.
        BYTE >00            Socket handle.
        BYTE >02            Close command.
CLSCK2  EQU  $              (Label for calculating message length)

        EVEN

RECVBUF BSS  1024           Receive buffer for data from TIPI.

****************************************************
*Text messages.
****************************************************

MSG01   TEXT 'TIPI DEVICE NOT FOUND'
MSG02   TEXT 'TIPI DEVICE FOUND AT >'
MSG03   TEXT 'OPENING SOCKET ...'
MSG04   TEXT 'SUCCESS'
MSG05   TEXT 'FAILED'
MSG06   TEXT 'SENDING GET REQUEST ...'
MSG07   TEXT 'READING GET RESPONSE ...'
MSG08   TEXT '(BUFFER OVERFLOWED)'
MSG09   TEXT '(NO BUFFER OVERFLOW)'
MSG10   TEXT 'CLOSING SOCKET ...'
MSG11   BYTE 0

        END

TIPI Mouse Interface

The following code provides an example of using the TIPI mouse interface. This is taken from my Internet Web Browser code.

GPLWS   EQU  >83E0          GPL workspace. For exchanging parameters with TIPI ROM routines.
GPLWSR0 EQU  GPLWS          Register 0 in GPL workspace.
GPLWSR1 EQU  GPLWS+2        Register 1 in GPL workspace.

SENDMSG EQU  >4012          Address holding address of send message routine in TIPI ROM.
RECVMSG EQU  >4010          Ditto for address of receive message routine.

...
...
...

*Before reading and using the mouse data, read and ignore any residual TIPI mouse data that is being buffered by the Raspberry Pi.

MSSP00  LI   R2,4           Number of times to read data from TIPI mouse after mouse movement data cleared, to clear
*                           any further mouse click data. TIPI sends 'no click' data between each 'click' data.
*                           Could actually check for this data, but click data is erratic after loading the browser and
*                           before the mouse is actually moved.

MSSP003 BL   @RDTPMSE       Read data from TIPI mouse. Data returned in buffer MSEBUFF.
        CLR  R1
        MOVB @MSEBUFF,R1    Get horizontal movement. >00 if no movement.
        JNE  MSSP00         There was movement, Read data again.
        MOVB @MSEBUFF+1,R1  Get vertical movement. >00 if no movement.
        JNE  MSSP00         There was movement, Read data again.
        DEC  R2             Decrement button click test counter.
        JNE  MSSP003

...
...
...

*Check if any movement or click from TIPI mouse.

CHKMSE  BL   @RDTPMSE       Read data from TIPI mouse. Data returned in buffer MSEBUFF.

        CLR  R4             Flag to indicate if there was mouse movement.

        CLR  R1
        MOVB @MSEBUFF,R1    Get horizontal movement. This is:
*                           >00 = no horizontal movement
*                           >FF (slow) - >81 (fast) = movement left
*                           >01 (slow) - >7F (fast) = movement right
*                           Only interested if there was movement - not how fast or slow it was.
        JEQ  CHKMSE2        No horizontal movement. Check vertical.
        SETO R4             Set flag to indicate mouse movement. (Instruction doesn't affect any status bits)
        JGT  CHKMSE4        Jump if movement to the right.
        DECT R2             Movement is to the left. Decrement X position.
        JMP  CHKMSE2
CHKMSE4 INCT R2             Increment X position.

CHKMSE2 MOVB @MSEBUFF+1,R1  Get vertical movement. This is:
*                           >00 = no vertical movement
*                           >FF (slow) - >81 (fast) = movement up
*                           >01 (slow) - >7F (fast) = movement down
*                           Only interested if there was movement - not how fast or slow it was.
        JEQ  CHKMSE3        No vertical movement. Check button.
        SETO R4             Set flag to indicate mouse movement. (Instruction doesn't affect any status bits)
        JGT  CHKMSE5        Jump if movement down.
        DECT R3             Movement is up. Decrement Y position.
        JMP  CHKMSE3
CHKMSE5 INCT R3             Increment Y position.

CHKMSE3 MOVB @MSEBUFF+2,R1  Get button press. This is:
*                           >x8 = no button press
*                           >x9 = left button press
*                           >xA = right button press (not used by the browser)

        ANDI R1,>0F00       Isolate required nibble.
        CI   R1,>0900       Left button press?
        JEQ  JOYFIRE        Yes. Jump to routine that handles keyboard  key.
        MOV  R4,R4          Flag set to indicate mouse movement?
        JEQ  MSST001        No, jump to continue.
        B    @MSSP10        Yes, process new sprite position.

...
...
...

****************************************************************
*Subroutine - read data from TIPI mouse.
*Call: BL @RDTPMSE
****************************************************************

RDTPMSE LI   R1,1           Number of bytes in message to send.
        MOV  R1,@GPLWSR0    Save in GPL workspace R0.
        LI   R1,B20         Address of message to send. This is a single byte >20 which is the mouse extension token.
        MOV  R1,@GPLWSR1    Save in GPL workspace R1.
        MOV  @MSEBUFA,@TIPIBUF  Set TIPI buffer address.

        BL   @TIPICOM       Send TIPI command message and get result, which is three bytes -
*                           horizontal movement, vertical movement, and button press.

        B    *R11           Return.

...
...
...

*******************************************************************************
*Subroutine - send TIPI command message and get result.
*Call: BL @TIPICOM.
*
*Before calling:
*- GPLWSR0 needs to contain the number of bytes in the command message to send.
*- GPLWSR1 needs to contain the address of the message to send.
*After calling:
*- GPLWSR0 contains the length of the result message received.
*- Specified buffer (set by writing value to @TIPIBUF) contains the result
*  message received.
*******************************************************************************

TIPICOM MOV  @TIPICRU,R12   Get address of TIPI device.
        SBO  0              Switch on TIPI card.
        LWPI GPLWS          Switch to GPL workspace.
        MOV  @SENDMSG,R4    Get address of send message routine in TIPI ROM.
        BL   *R4            Call send message routine.

*Get result. Receive message routine blocks until this is available.

        CLR  R0             Will contain the length of the message received back from the send message routine.
TIPIBUF EQU  $+2
        LI   R1,>0000       Address of buffer to store received message. This is set before each call to TIPICOM.
        MOV  @RECVMSG,R4    Get address of receive message routine in TIPI ROM.
        BL   *R4            Call receive message routine.
        LWPI >8300          Back to program workspace.
        SBZ  0              Switch TIPI card off.
        B    *R11           Return.

back to home page