Page Contents

small arrow Description
small arrow Programs
small arrow EPROM Programmer
small arrow Compact Flash Drive Utility
small arrow Power Supply Consumption
small arrow Mini Memory Module Line-By-Line Assembler Bug
small arrow Cartridge Bank Switching
small arrow Resurrecting a TI Disk System
small arrow Connecting a Serial Mouse
                Rob Tempelmans Plat award logo
Honoured to receive the Rob Tempelmans Plat award in 2013

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

Programs

Disk: Cortex_BASIC.zip (V9T9/.tidisk and FIAD formats)

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

  • It’s written in pure assembly (but uses the console keyboard scanning routine) so is reasonably fast, more so if you have 32K RAM on the 16-bit bus fitted in the console.
  • The default working mode is 40-column text mode, and it also directly supports the 256 × 192 pixel Graphics II mode so you can do things like plot graphs and annotate them directly in BASIC.
  • Sprites are supported but their automatic movement is not – your program is responsible for moving them.
  • The code can be loaded as an E/A option 3 file or programmed into an EPROM on a 64K 'Guidry' bank-switched cartridge.
  • The only hardware that the code is aware of at the moment is the console (but not the sound chip) and 32K RAM. No programmable sound, no speech, and no printer ports. Loading/saving from/to disk is now supported.
  • The code runs on real hardware and the following simulators: Tursi’s Classic99 simulator (which I used extensively for debugging – great program!), the Ti994w simulator and the Win994a simulator (V3.010 and later). I haven't tried it on any other simulator - if you try it and find it does or does not work, let me know and I'll add it to the list.

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.

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.

Loading the Program as an E/A Option 3 File

Download the .zip file. I'm assuming you know how to get the relevant file onto a real hardware storage device. If using Classic99, save the CBASIC file (no extension) from the .zip file into a simulator disk folder. Then switch on 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.

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.

Programming into an EPROM on a 64K 'Guidry' Bank-Switched Cartridge

The program requires a 32K 27(C)256 EPROM and a 74LS379 (with inverted outputs) latch on the cartridge board. Download the EPROM binary image here.

With the bank-switched cartridge version, 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.

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

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.

  • The following commands are not currently supported, and will display an error message if used: BOOT, MON, TIC, BAUD, MOTOR, TIME.
  • The extended command handler (*<command>) is not supported.
  • Loading/saving from/to disk is now supported using standard device/file names in quotes (for example, SAVE "DSK3.MYPROG" and LOAD "DSK3.MYPROG"). The auto-run feature as described in the Cortex user guide is also supported. Note though that I think the system crashes at the moment if you try to save a file with the same name as an existing file.
  • The UNIT command supports a value of 1 only (input from the keyboard and output to the screen).
  • Pressing the <Space> key pauses a listing, then pressing any key resumes the listing. Stepping through the listing line-by-line is not currently supported.
  • SYS values 10, 13-17, 19 are not relevant and will return -1.
  • A new SYS value of 20 returns the address of the colour substitution table used by the SWAP command (see the addendum to the COLOUR command on page 4-77 of the user guide).
  • The WAIT statement without an argument returns immediately. With an argument, no input is accepted and the <Escape> key is not active until the wait period expires.
  • There is a bug in the original implementation of the CHAR statement. When typing in a program, you need to put a space between the line number and the CHAR statement otherwise an 'Illegal Character' error is displayed.
  • Memory addresses mentioned in the user guide and the memory maps are not correct for the TI implementation. I can provide memory usage details if required.
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 - 03 Nov 2013 - Refactored to enable code to build image for either E/A option 3 file or TI-99/4A 4-bank Guidry cart.

Disk: Invaders.zip (V9T9/.tidisk format)
INVADERS   This is a disk version of the TI Invaders cartridge. Load using E/A 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>.

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 or MiniMem option 3, 1. 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.

Disk: The_Valley.zip (V9T9/.tidisk format)
RUN   An adventure game, published as a program listing in Computing Today magazine, April 1982.

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!


Program requires XB and 32K memory.
To run the program, load and run the file RUN.
Files must be on a disk named THE_VALLEY.
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.

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.

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. This can then be used in the following ways:

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. 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  

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.

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:  ftp://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

back to home page