Last update: December 28th 2018

USB to TI99/4A Keyboard converter

Download USB to TI99/4A Keyboard converter version 2.2 now (inclusief source code)
zip file as zipped file (ZIP)

Version 1.0 01-03-2018 Initial release
Version 2.0 03-05-2018 Added TI99_Background_Kscan() function for those special key scan functions like Quit and Break
Version 2.1 04-09-2018 Fixed PageUp equ fctn-6 and PageDn equ fctn-4
Version 2.2 24-12-2018 Fixed autorepeat problem for converted SHIFT [,],\,',/,-,` to {,},|,",?,_,~ after releasing shift key before the other key

If you are going to compile the code yourself make sure to set the IDE environment as follows:
  • Board : Teensy 3.2
  • CPU speed : 48Mhz
  • Optimize : Fast


1 - INTRODUCTION
2 - PROJECT
3 - SOFTWARE
3.1 - BASIC MODE

1 - INTRODUCTION

This is a small project for converting an USB keyboard into a TI99/4A keyboard to be used on the TI99/4A home computer. The project consists of an USB (wireless) keyboard and a Teensy 3.2 controller board. The Teensy 3.2 is a complete USB-based microcontroller development system, in a very small footprint, capable of implementing many types of projects (see "https://www.pjrc.com/teensy/").

Some key features of the Teensy 3.2 controller board are:
  • Compatible with Arduino Software & Libraries
  • USB can be any type of device
  • Free software development tools
  • Works with Mac OS X, Linux & Windows
  • Tiny size, perfect for many projects

Some specifications of the Teensy 3.2 controller board are:
  • Processor: MK20DX256VLH7 32 bit AVR at 72 Mhz
  • Flash Memory: 256 kbytes
  • RAM Memory: 64 kbytes
  • EEPROM: 2 Kbytes
  • IO: 34 programmable input/output pins, 5V tolerant
  • Analog: 21 inputs, 16 bit resolution
  • Analog: 1 output, 12 bit resolution
  • PWM: 7 inputs
  • Ports: 3x UART, 2x I2C, 1x SPI, 1x CAN, 1x USB, 1x I2S audio

Teensy 3.2
The Teensy 3.2 controller board


2 - PROJECT

To connect an USB keyboard to the Teensy controller board an USB host shield is needed. To connect the Teensy 3.2 with the USB host shield it is best to make use of a Teensy 3.1 arduino shield adapter, this will give the Teensy the same pinout as an arduino processor board. An USB keyboard, or maybe better a wireless USB keyboard can be connected to the USB host shield. The whole system is powered via power supply of the TI99/4A home computer.

Teeny 3.1 adapter
The Teensy 3.1 Arduino shield adapter.
USB host shield
And a USB host shield from Sparkfun.
Usb Teensy TI99/4A Keyboard
The complete setup
(click on picture for a larger image).
Front view
Front view, bottom Arduine shield adapter
with Teensy 3.2, top USB host shield (copfali).
The keyboard cable
The keyboard cable
(copfali).
Front view
Drawing of the cable
(copfali).
SUB-D Connector
Console inside view of the keyboard
SUB-D connector on the right. (copfali).
Keyboard connection
Console inside view of the keyboard connection
(copfali).


Power connection
Console inside view of the power
connection for the USB keyboard converter (copfali).
Complete view
Console inside view of all connections
(copfali).


Both shields
Top view of USB host shield with wireless
USB keyboard receiver and LED's for
Caps lock, Num lock and Basic mode (copfali).
Complete view
The TI99 USB keyboard converter
with cable in blue box in all its glory (copfali).




3 - SOFTWARE

The main task of the software is to convert the USB keyboard codes into the TI99/4A keyboard scan codes. Therefor are two tables created: table one (see file usb_conv_table.h) contains ASCII codes and corresponding USB keyboard codes and table two (see file ti99_conv_table.h) contains ASCII codes and the corresponding TI99/4A keyboard codes and a bit mask for key modifiers (fnction, control, shift). Translation of an USB keyboard code to a TI99/4A keyboard code for a key-down straight forward:
  • Lookup the USB keyboard code in the UsbKeyConv table
  • Get the ASCII code
  • Lookup the ASCII code in the TI99/4A conversion table
  • Get the TI99/4A keyboard row/column code and key modifier
  • Wait for the TI99/4A to scan the keyboard
  • Present the pressed key to the TI99/4A


A TI99/4A keyboard is divided into rows and columns. When te keyboard is scanned the TI99/4A pulls a row (output) signal low and checks all column (input) signals for a key being pressed. This procedure is repeated for every row signal. If a key has been pressed the row/column combination is then converted to the corresponding ASCII code which is returned by the key scan function.

The keyboard scan codes for the TI99/4A are placed in a two dimensional array and set on the Teensy output pins when the coresponding input signal is pulled low by the TI99/4A.

There is also a special modes build in the USB to TI99/4A keyboard converter software. This modes can be activated by pressing simultaneously the LeftGui + RightGui + B keys for entering the (Extended) Basic mode (Gui keys are equal to the Window keys on some keyboards). By pressing simultaneously the LeftGui + RightGui keys the special mode is disabled.

3.1 - BASIC MODE

When the USB to TI99/4A keyboard converter software is in (Extended) Basic mode some predefined Basic commands are send to the TI99/4A. The predefined command to send is determined by pressing simultaneously the Left- or RightGui key + the first character of the command. Bij pressing Left- or RightGui key + DownArrow or UpArrow it is possible to walk through the list of commands wich start with the same character. Bij pressing Left- or RightGui key + RightArrow or LeftArrow it is possible to walk through alternatives of the same command. The cursor is left at a convenient location for the user so that changes can be made easely in the predefined command. The complete command will be replaced (removed) when immediately another Left- or RightGui + character combination is used.

The implemented predefined (Extended) Basic commands are:
  • == A ==
    • ABS(ne)
    • ACCEPT AT(r,c):v
    • ASC(se)
    • ATN(ne)
  • == B ==
    • BREAK
    • BYE
  • == C ==
    • CALL CHAR(cc,pi)
    • CALL CHARPAT(cc,sv)
    • CALL CHARSET
    • CHR$(ne)
    • CALL CLEAR
    • CLOSE #fn
    • CALL COINC(#sn,#sn,t,nv)
    • CALL COINC(#sn,dr,dc,t,nv)
    • CALL COINC(ALL,nv)
    • CALL COLOR(#sn,fc)
    • CALL COLOR(cs,fc,bc)
    • CONTINUE
    • COS(rad)
  • == D ==
    • DATA dl
    • DEF fu=ex
    • DELETE \"dn\"
    • CALL DELSPRITE(#sn)
    • CALL DELSPRITE(ALL)
    • DIM an(i1[,i2...])
    • DISPLAY AT(r,c)
    • DISPLAY [ol:] USING se:vl
    • DISPLAY [ol:] USING ln:vl
    • CALL DISTANCE(#sn,#sn,nv)
    • CALL DISTANCE(#sn,dr,dc,nv)
  • == E ==
    • END
    • EOF(fn)
    • CALL ERR(ec,et)
    • CALL ERR(ec,et,es,ln)
    • EXP(ne)
  • == F ==
    • FOR nv=iv TO lmt
    • FOR nv=iv TO lmt STEP inc
  • == G ==
    • CALL GCHAR(r,c,nv)
    • GOSUB ln
    • GOTO ln
  • == H ==
    • CALL HCHAR(r,c,cc)
    • CALL HCHAR(r,c,cc,rp)
  • == I ==
    • IF re THEN ln/st
    • IF re THEN ln/st ELSE ln/st
    • IMAGE fs
    • CALL INIT
    • INPUT vl
    • INPUT \"prompt\": vl
    • INPUT #fn: vl
    • INPUT #fn, REC rn: vl
    • INT(ne)
  • == J ==
    • CALL JOYST(1,xr,yr)
    • CALL JOYST(2,xr,yr)
  • == K ==
    • CALL KEY(ku[0-5],rv,st)
  • == L ==
    • LEN(se)
    • LET nv=ne
    • CALL LINK(\"name\",al)
    • LINPUT vl
    • LINPUT \"prompt\": vl
    • LINPUT #fn: vl
    • LINPUT #fn, REC rn: vl
    • LIST [\"dn\":]ln
    • LIST [\"dn\":]sln-eln
    • CALL LOAD(\"name\",ad,bv[,...]
    • CALL LOCATE(#sn,dr,dc[,...])
    • LOG(ne)
  • == M ==
    • CALL MAGNIFY(mf[1-4])
    • MAX(ne,ne)
    • MERGE dn
    • MIN(ne,ne)
    • CALL MOTION(#sn,rv,cv[,...])
  • == N ==
    • NEW
    • NEXT nv
    • NUM
    • NUM ln
    • NUM ln,inc
  • == O ==
    • OLD dn
    • ON BREAK STOP
    • ON BREAK NEXT
    • ON ERROR STOP
    • ON ERROR ln
    • ON ne GOSUB ln[,...]
    • ON ne GOTO ln[,...]
    • ON WARNING PRINT
    • ON WARNING STOP
    • ON WARNING NEXT
    • OPEN #fn:dn,fo,ft,om,rt
    • OPTION BASE 0/1
  • == P ==
    • CALL PATTERN(#sn,cv[,...])
    • CALL PEEK(ad,nv[,...])
    • PI
    • POS(sv,sv,ne)
    • CALL POSITION(#sn,dr,dc[,...])
    • PRINT pl
    • PRINT TAB(ne);pl
    • PRINT USING se: pl
    • PRINT USING ln: pl
    • PRINT #fn: pl
    • PRINT #fn, REC rn: pl
    • PRINT #fn USING se: pl
    • PRINT #fn USING ln: pl
    • PRINT #fn, REC rn USING se: pl
    • PRINT #fn, REC rn USING ln: pl
  • == R ==
    • RANDOMIZE
    • READ vl
    • REC(fn)
    • REM cs
    • RES
    • RES ln
    • RES ln,inc
    • RESTORE
    • RESTORE ln
    • RESTORE #fn
    • RESTORE #fn, REC rn
    • RETURN
    • RETURN ln
    • RETURN NEXT
    • RND
    • RPT$(se,ne)
    • RUN
    • RUN ln
    • RUN \"dn\"
  • == S ==
    • SAVE dn
    • SAVE dn,MERGE
    • SAVE dn,PROTECTED
    • CALL SAY(ws)
    • CALL SAY(,ds)
    • CALL SAY(ws,ds)
    • CALL SCREEN(cc[1-16])
    • SEG$(se,ps,ln)
    • SIGN(ne)
    • SIN(rad)
    • SIZE
    • CALL SOUND(dur,frq,vol[,...])
    • CALL SPGET(ws,sv)
    • CALL SPRITE(#sn,cv,sc,dr,dc[,...])
    • CALL SPRITE(#sn,cv,sc,dr,dc,rv,cv[,...])
    • SQR(ne)
    • STOP
    • STR$(ne)
    • SUB name
    • SUB name(pl)
    • SUBEND
    • SUBEXIT
  • == T ==
    • TAN(rad)
    • TRACE
  • == U ==
    • UNBREAK
    • UNTRACE
  • == V ==
    • VAL(se)
    • CALL VCHAR(r,c,cc)
    • CALL VCHAR(r,c,cc,rp)
    • CALL VERSION(nv)


An other method to use the predefined (Extended) Basic commands is by using the Tab key. First enter one or more characters of the Basic command and press the Tab key. If the command is recognized it will be completed by the USB keyboard converter software. By pressing Tab again the next alternative will be send. The cursor will always be left at the end of the send suggestion. By pressing the escape key the suggestion will be removed so that more characters for the correct/desired command can be entered.

The implemented (Extended) Basic commands for command completion are:
  • == A ==
    • ABS(
    • ACCEPT
    • ALL
    • ASC(
    • APPEND,
    • AT(
    • ATN(
  • == B ==
    • BASE
    • BEEP
    • BREAK
    • BYE
  • == C ==
    • CALL
    • CHAR(
    • CHARPAT(
    • CHARSET
    • CHR$(
    • CLEAR
    • CLOSE #
    • COINC(#
    • COINC(ALL,
    • COLOR(
    • COLOR(#
    • CON
    • CONTINUE
    • COS(
  • == D ==
    • DATA
    • DEF
    • DELETE
    • DELSPRITE(#
    • DELSPRITE(ALL)
    • DIM
    • DISPLAY
    • DISPLAY,
    • DISTANCE(#
    • DSK1.
    • DSK2.
    • DSK3.
    • DSK4.
  • == E ==
    • ELSE
    • END
    • EOF(
    • ERASE ALL
    • ERR(
    • ERROR
    • EXP(
  • == F ==
    • FOR
    • FIXED,
  • == G ==
    • GCHAR(
    • GOSUB
    • GOTO
  • == H ==
    • HDS1.
    • HDS2.
    • HDX1.
    • HCHAR(
  • == I ==
    • IDE1.
    • IDE2.
    • IDE3.
    • IDE4.
    • IF
    • IMAGE
    • INIT
    • INPUT
    • INPUT,
    • INT(
    • INTERNAL,
  • == J ==
    • JOYST(
  • == K ==
    • KEY(
  • == L ==
    • LEN(
    • LET
    • LINK(
    • LINPUT
    • LINPUT #
    • LIST
    • LOAD(
    • LOCATE(#
    • LOG(
  • == M ==
    • MAGNIFY(
    • MAX(
    • MERGE
    • MIN(
    • MOTION(#
  • == N ==
    • NEW
    • NEXT
    • NUM
  • == O ==
    • OLD
    • ON
    • ON BREAK NEXT
    • ON BREAK STOP
    • ON ERROR
    • ON ERROR STOP
    • ON X GOTO
    • ON X GOSUB
    • ON WARNING NEXT
    • ON WARNING PRINT
    • ON WARNING STOP
    • OPEN #
    • OPTION
    • OPTION BASE
    • OUTPUT,
  • == P ==
    • PATTERN(#
    • PEEK(
    • PI
    • POS(
    • POSITION(#
    • PRINT
    • PROTECTED
  • == R ==
    • RANDOM
    • RANDOMIZE
    • READ
    • REC X
    • REC(
    • RELATIVE,
    • REM
    • RES
    • RESEQUENCE
    • RESTORE
    • RESTORE #
    • RETURN
    • RETURN NEXT
    • RND(
    • RPT$(
    • RUN
  • == S ==
    • SAVE
    • SAY(
    • SCS1.
    • SCS2.
    • SCS3.
    • SCS4.
    • SCREEN(
    • SEG$(
    • SEQUENTIAL
    • SIGN(
    • SIN(
    • SIZE
    • SOUND(
    • SPGET(
    • SPRITE(#
    • SQR(
    • STEP
    • STOP
    • STR$(
    • SUB
    • SUBEND
    • SUBEXIT
  • == T ==
    • TAB(
    • TAN(
    • THEN
    • TO
    • TRACE
  • == U ==
    • UNBREAK
    • UNTRACE
    • UPDATE,
    • USING
  • == V ==
    • VAL(
    • VALIDATE(
    • VARIABLE,
    • VCHAR(
    • VERSION(
  • == W ==
    • WDS1.
    • WDS2.
    • WARNING


The meaning of the abbreviations in the arguments list are:
  • ad : Address (-32768 to 32767)
  • al : Argument List
  • bc : Background Color (1 to 16)
  • bv : Byte Value (0 to 255)
  • c : Column (1 to 32)
  • cc : Character Code (ASCII value)
  • cs : Character Set (0 to 14)
  • dc : Dot Column (1 to 256)
  • dl : Data List
  • dn : Device Program Name (DSK1.FILENAME)
  • dr : Dot Row (1 to 192)
  • dur : Duration (1 to 4250 or -1 to -4250
  • ec : Error Code
  • eln : End Line Number
  • es : Error Severity
  • et : Error Type
  • ex : Expression
  • fc : Forground Color (1 to 16)
  • fn : File Number (0 to 255)
  • fo : File Organization (RELATIVE / SEQUENTIAL)
  • fu : Function Name
  • frq : Frequency (110 to 44733 or -1 to -8)
  • fs : Format String
  • ft : File Type (DISPLAY / INTERNAL)
  • inc : Increment Value
  • iv : Initial Value
  • ku : Key Unit (0 to 5)
  • lmt : Limit
  • ln : Line Number (1 to 32767)
  • mf : Magnify Factor (1 to 4)
  • ne : Nummeric Expression
  • nv : Numeric Variable
  • om : Open Mode (INPUT / UPDATE / OUTPUT / APPEND)
  • pl : Print List (String / Numeric variables, Strings)
  • r : Row (1 to 24)
  • re : Relation Expression
  • rad : Radian Expression
  • rp : Repetition
  • rt : Record Type (VARIABLE / FIXED)
  • rn : Record Number (0 to 32767)
  • se : String Expression
  • sln : Start Line Number
  • sn : Sprite Number (1 to 28)
  • st : Statement
  • sv : String Variable
  • t : Tolerance (pixels)
  • v : Variable (string / numeric)
  • vl : Variable List
  • vol : Volume (0to 30)
  • xr : X-return (-4 or 0 or 4)
  • yr : Y-return (-4 or 0 or 4)