You are on page 1of 13

Cassette tape interface

The TI-99/4A was issued with a built-in interface for cassette tape recorders. This was meant as a cheap
alternative to a disk drive. The console contains the necessary hardware to read from, write to, and
control the motor of two tape recorders. The relevant software is located in the console ROMs and
GROMs.
Texas Instruments originally recommended the following recorders, but many others will work. I even
used the one on my stereo, with tune-tracking system, etc. Most of the time the volume control should be
set at mid-range, and the tone control (if any) at the highest possible pitch.

Brand
Superscope
Panasonic
Sears
Sears
JC Penney

Model
C2L00LP
RQ2309A
2165
21686
6568

Volume set
8.0
5.0
Mid Range
Mid Range
Mid Range

Tone set
N/A
10
N/A
HI
High

Hardware
Connector
Internal circuitery
CRU map
Software
Direct control
Cassette tape format
ROM routines
DSRs in GROM

Hardware
Connector
___________
( 1 2 3 4 5 )
\ 6 7 8 9 /
\_______/

# I/O Use
- --- ------1 > Cass 1 motor control
2 > Ditto (negative)
3 > Output to tape 1 or 2 (neg)
4 > Audio gate
5 > Output to tape 1 or 2
6 > Cass 2 motor control
7 > Ditto (negative)
8 < Input from tape 1
9 < Ditto (neg)

Texas Instruments marketed a connection cable, with a 9-holes connector at one end, and 5 jacks at the
other (three for one tape recorder, two for the other).
venerd 26 ottobre 2007 11.14.08

Pins 5 and 3 go to two jacks (on red cables) to be plugged in the the microphone socket on each recorder.
Pins 8 and 9 go to a jack (on a white cable) to be plugged in the "ear-plug" socket of recorder #1. Finally
pins 1 and 2 go to a smaller jack (on a black cable) to be plugged into the remote control socket on
recorder #1, while pins 6 and 7 play the same role for recorder #2.
As you see, only one recorder can be read from. I'm not sure why. May be because, if the output lines of
two recorders are connected together, there is a risk that one is damaged when the other is outputing
sound?
An important point is that the connections are polarised (see circuitery below). In particular, the remotecontrol jacks are connected to transistors, not to relays. This may cause some tape recorders to
malfunction if their polarity is inverted with respect to that of the cassette jack. But it's a trivial job to
build an adapter: get a male and a female jack and just cross their wires. I think you can even buy such
adapters.

Internal circuitery
Motor control
TMS9901
----+
|
,---+5V
P6|_____|/
Opto-isolator
|
|\ TIS92
TIL119
F
|
V__________
,---------+----+---------uuu---> Pin 1
|
| |/
|
|
1nF
CS1 motor
|
V |\________|/
'---||---Gnd
jack
|
|
|\
|
Gnd
TIS92 V___,----------------> Pin 2
|
|
1nF
|
,---+5V
'---||---Gnd
P7|_____|/
Opto-isolator
|
|\ TIS92
TIL119
F
|
V__________
,---------+----+---------uuu---> Pin 6
|
| |/
|
|
1nF
CS2 motor
|
V |\________|/
'---||---Gnd
|
jack
----+
Gnd
TIS92 V____,---------------> Pin 7
|
1nF
'---||---Gnd

Magnetic output
TMS9901
----+
|
6.8K
1nF 5.6K
P9|---WWW---+---||---WWW---+-------------------+---> Pin 5
|
|
|
200 Ohm
|
Microphone
|
|
|
CS1 + CS2
----+
Gnd
Gnd---+---> Pin 3

Magnetic input

TMS
9901
---+
|

120K
+--WWW---+
|
/| |

220pF
+---||---+
| 39K
|
+--WWW---+
|
/| | 6.8K 10nF
F
6.8K |
/6|--+--WWW--||--+---+-----uuu----< Pin 8

venerd 26 ottobre 2007 11.14.08

= 0.1uF

|
2.2K |
/3|--+--WWW--+__/7 |
|
|
Ear-plug P11|--+
| ^ PG3992
\2|--+
\|
|
+--uuu---< Pin 9
| |
\| |
4558
| 15 Ohm|
| Gnd
4558 Gnd
,---------+--WWW--+--||--Gnd
|
|
5.6K | 1nF
|
+---WWW----------------'
Gnd---WWW--'
|
|
10K
|
|
+---WWW---+5V
|
|
|
9.1K
|
12K
,---+---WWW---+--------------------WWW-------+--------> Pin 4
P8|---WWW---|/
|
10K
|
Sound chip
5.6K
|
|
|\
= 10nF
|
TMS9919
=
|
TIS92 V___|
|
+---------+
|
|
|
'---|AUDIOIN |
Gnd
---+
Gnd
|
|
+---------+

CRU map
Bit R12 addr I/O

Usage

22 >002C

1: Turn motor on for recorder #1

23 >002E

1: Turn motor on for recorder #2

24 >0030

Audio gate 1: Silent input

25 >0032

Data output to recorders #1 and #2

27 >0036

Data input from recorder #1

Software
Direct control
You can control the tape recorders directly, using the CRU bits mentionned above. Note that it does not
have to be tape recorders... I'm thinking of using the cassette port to hook-up two TI consoles and have a
debugger program running on one and monitor the other. Communications would be slow (in serial mode
by definition), but it would not require accessing any peripheral card.
* This routine performs some dummy cassette tape operations
* It assumes recorder #1 is set to record, and #2 to replay.
CSTEST LI
SBO
BL
SBO
BL
SBZ
...

R12,>0000
23
@WAIT
25
@DELAY
25

CRU address of the TMS9901


Turn motor on, recorder #2
Let it reach cruising speed
Toggle output
Leave it so, for a while
Toggle back
etc.

CSTES2 CLR
SBO
BL
TB
JEQ
JNE

R12
22
@WAIT
27
...
...

CRU address = >0000


Motor on, recorder #1

venerd 26 ottobre 2007 11.14.08

Get data from recorder


Do something

Cassette tape format


Texaz Instruments adopted a frequency modulation encoding system to store data on tape. This is only a
convention, and you may come up with another, if you feel like it. Similarly, TI defined the format the
data should have whithin a tape file. Again, this is only a convention.

Data encoding
Bits are encoded by output level changes. With a 3 MHz console, the output toggles every 725.3
microseconds. To encode a 1, invert the output in the middle of this time period:
0
|
c

0 _1 _0_ 1_ 1_ 0
|___| |_|
|_| |_| |___|
c
c
c
c
c
c
c

Bits to encode
Output (low/high)
Clock intervals

Courtesy Dean Corcoran

This results in frequencies of 689.37 Hz for a space (0) and 1379 Hz for a mark (1) which is well within
the audio range, thus suitable for a tape player. In addition, I was told that you can connect the cassette
port to a PC sound card and save/load programs as .wav files (The "Scott Adams compilation" CD ROM
by Frank Traut uses this trick, if I'm well informed).

File format
Name
# of bytes
--------- ---------File sync
768
Data mark
1
Size
1
Repeat size
1

Content
------->00
>FF
# of records
ditto

Rec sync
Data mark
Data
Checksum

>00
>FF
data bytes
sum of the 64 data bytes

8
1
64
1

}
} Rec 1
}
}

Repeat rec 1
Rec 2
Repeat rec 2
etc.

At the beginning of the files are 768 zeros (>300), for synchronisation purposes. The motor speed can
vary a little from recorder to recorder (not too much, otherwise you would notice it when listening to
music). So the tape reading routine in the console ROMs uses this stretch of zeros to time the tape
venerd 26 ottobre 2007 11.14.08

recorder. Once it has determine how long a "0" bit lasts, it is a simple matter to detect a "1" which cuts
this interval in half.
The data mark signals the end of the sync stretch. It is followed by the number of records, repeted twice
(to ensure reading is correct).
Each record in the file is repeted twice. It begins with a short sync stretch of 8 zeros, followed by an >FF
data mark. The only allowed file format is Dis/Fix 64, so there is always 64 data bytes in a record. The
record ends with a checksum: this is the sum of all 64 data bytes: it is likely to be greater than 256, and
thus requires two bytes. However, only the least significant byte is recorded on tape.
The tape reading routine calculates its own checksum while reading data, then compares it with the
recorded value. If it does not match, the routine will get another chance, as the record repeats. If the
checksum matches the first time, the second repeat is just ignored. This format slows down cassette
operations by a factor of two, but is more user-friendly: since a tape recorder is by definition sequential, a
bad record that aborts reading forces you to start all over again, from the beginning of the file. In the
worst case, it means the file cannot be read. At least with this method, even if a record is unreadable its
copy might still be good. The probabilty that both copies go bad is very low (the square of the probability
that one record goes bad).
There is no special end-of-file mark. Recording just stops after the second repeat of the last record.

ROM routines
There are three cassette operating routines in the console ROM: cassette write, cassette read and cassette
verify. All three can be called by the GPL opcode "I/O", with respectively 3, 4 or 5 as a source argument.
The destination argument consists in two words: the number of bytes to transfer, and the VDP buffer
address.
Operation
Write
Read
Verify

GPL I/O
3
4
5

Address in ROM
>1346
>142E
>1426

For obvious reasons, they all require precise timing and this is done by using the built-in timer in the
TMS9901 interface controller chip, which is part of the console. The cassette routines enable the interrupt
function of the TMS9901, which requires taking control of the interrupt service routine. This is done by
setting a flag bit (value >20) in byte >83FD. From now on, the console main ISR will treat any interrupt
as issued by the TMS9901 and branch to the cassette ISR at >1404. If you are not familiar with the
concept of interrupts service routines (ISR) you may want to have a look at the page on interrupts, as well
as at the page describing the TMS9901.
What all this implies is that no other interrupt should be allowed, and cassette routines begin by masking
them off (by setting CRU bit 1 and 2 to 0 in the TMS9901). This would be necessary anyway, given the
nature of a tape recorder: once you start reading you can't stop and come back later, as the tape keeps
running! A temporary interruption would mean skipping one or more records, which would force the user
to start the cassette operation all over again.
* This routine prepares for cassette interrupts
* It is adapted from the one in the console ROM (at >13CC-13E0)
SETCS

LI
CLR
CLR

R3,>0023
R1
R12

venerd 26 ottobre 2007 11.14.08

Timer value (>0011) plus clock bit, inverted


Flag: no vector in R6
Console CRU base

SOC
SBZ
SBZ
LDCR
SBZ
SBZ
SBO
B

@H0020,R14
2
12
R3,15
0
1
3
*R11

Set bit >0020 in >83FD (WS is >83E0 in GPL)


Disable VDP interrupts
This pin is not connected (INT12: +5V pull-up)
Load >0011 in decrementer
Leave clock mode
Disable peripheral interrupts
Enable clock interrupts

* This routine returns to normal operations


* It is found at >155E-1570 in the console ROM
CLRCS

SZC
SZC
SBZ
SBO
SBO
SBO
B
B

@H0010,R14
@H0020,R14
3
12
1
2
*R11
@>0070

Clear read/verify flag


Clear cassette ints flag (byte >83FD)
Disable cassette interrupts
Enable interrupts by pin INT12 (nc)
Enable VDP interrupts
Enable peripheral interrupt
The real routine returns to the GPL interpreter:

Cassette write
Upon writing, the timing is ensured by loading >0011 (i.e. 17) into the clock register of the TMS9901
chip. The resulting delay is:
17
= 363.6 usec
(3MHz / 64)

The write subroutine writes to the output by toggling CRU bit 19 and entering a forever loop:
HERE

JMP

HERE

The only way to get out of this loop is via the interrupt that will occur once the timer has elapsed. The
cassette ISR checks whether the main program is trapped into such a loop and (if this is the case) returns
to the next instruction, thereby effectively jumping out of the loop.
*
*
*
*

This routine writes a byte to the cassette. The byte is in R4.


R8 contains either >1E19 which means SBZ 19
or >1D19 which means SBO 19
This routine is located in the console ROM at >13E2-1402

EMITBY LI
INV
LP1
JMP

LP2

SK1

R6,>0008
R4
LP1

Bit counter: 8 bits per byte


Invert the byte
Wait for an interrupt

X
XOR
JMP

R8
@H0200,R8
LP2

Toggle CRU bit 19


Change SBZ to SBO and conversely
Wait for an interrupt

MOV
JLT
X
XOR
SLA
DEC
JNE
B

R4,R4
SK1
R8
@H0200,R8
R4,1
R6
LP1
*R11

Test leftmost bit


It's "1" (was "0" before inversion)
It's "0" (was "1"): toggle CRU bit 19
Change SBO to SBZ and conversely
Next bit
More to do?
Yes
No: return. R8 is ready for next byte

And here is the infamous cassette-specific interrupt service routine:


* Cassette ISR, located at >1404-1422 in the console ROM.

venerd 26 ottobre 2007 11.14.08

* The main ISR enters it with WS >83E0 if bit >0020 is set in >83FD
CASISR SBZ
SBO
MOV
JLT
LWPI
C
JNE
INCT
RTWP
SK2

0
3
R1,R1
SK2
>83C0
*R14,@HERE
SK2
R14

Make sure we're not in timer mode


Clear pending interrupt (remain enabled)
Flag: is there a vector in R6?
Yes
Back to ISR workspace
Is instruction at return point a forever loop?
No: branch to vector in R6 (crashes if none!!)
Skip the HERE JMP HERE
Return just outside the loop

LWPI >83C0
MOV @>83E6,R14
RTWP

Branch to vector in R6, with ISR workspace


AAAARGH! Overwrites the return address!!!
Branch, with no hope of return

Now, all Write has to do is to use the above routines to send the appropriate bytes. Please refer to file
format, above.
Get # of bytes (round in up to next 64, derive the # of records) and the buffer address in VDP
memory
Initialize cassette interrupts (with SETCS above)
Allow interrupts with LIMI 1
Send >300 times byte >00 (file synchronisation)
Send byte >FF once
Send # of records, twice
Send data in 64 bytes records. Each record is sent twice:
Send 8 times byte >00 (record sync)
Send byte >FF once
Send 64 bytes
Send checksum as two bytes
Wait for interrupt
Branch to CLRCS (return to GPL interpreter, which resets LIMI 0)

Cassette read
The Read subroutine also makes use of the timer to time the stretches of "0" bits in the sync field at the
beginning of each record. First it loads the maximum value (>3FFF) in the TMS9901 timer and waits for
8 bits to arrive. Then it reads the timer and see how much time has elapsed. It uses 8 bits rather than just
one to get an average value.
*
*
*
*
*

This routine checks whether the input toggled.


If yes, it returns with B *R11
If no, it returns with INCT R11, B *R11
R1(lsb) contains the previous value of the input (>xx00 or >xxFF)
It's located at >15BA-15D2 in the console ROM

ISTOGL TB
JEQ
CZC
JEQ
TOGGLE XOR
B

27
ISONE
@H00FF,R1
NOTOGL
@H00FF,R1
*R11

Check tape input port


It's 1
It's 0. What was it before?
It was 0: no toggle
Save new value
Return, signaling a toggle

ISONE

@H00FF,R1
TOGGLE
R11
*R11

Input is 1. What was it before?


It was 0: toggle detected
It was 1: signal no toggling
Return

CZC
JEQ
NOTOGL INCT
B

* This routine makes use of the above to time the cassette recorder

venerd 26 ottobre 2007 11.14.08

* It assumes a >00 byte is coming and waits for the input to


* toggle 8 times.
TIMECS LI
R9,>7FFF
LI
R8,>0008
LDCR R9,15
SBZ 0
SBO 3
LP3
@ISTOGL
JMP YES
JMP LP3
YES
DEC R8
JNE LP3
SBO
STCR
S
MOV
SLA
A
SRL
ORI
CI
JLT
...
LDCR
SBZ
SBO
...

0
R3,15
R3,R9
R9,R3
R9,2
R9,R3
R3,6
R3,>0001
R3,>001F
AGAIN
R3,15
0
3

Timer value >3FFF, plus clock bit


Count 8 toggling
Load the decrementer
Leave clock mode (which starts the timer)
Enable interrupts (in case no toggle occurs)
Check if input toggled
Yes, it did
Not yet
More to count?
Yes
Enter clock mode
Get value remaining in timer
Calculate # of times it was decremented
Multiply by 5

Divide by 64
Add clock bit
Check if resonnable value
Too fast (not >00): reinit and try again
(Actually, jumps to SK3 in GETBIT, below)
Load new value
Leave clock mode
Enable timer interrupts

Once this value has been determined, it is fed to the timer for further reading operations. If the timer fires
before the output toggles, the incoming bit is a "0". If the output toggles while the timer is still mid-way
to zero, the incoming bit is a "1". Simple enough, no?

* This routine receives a bit. It uses the toggle detection routine above.
* Once done, it resets the timer with the value calculated above (in R3).
* It is located at >1572-159E in the console ROM
GETBIT MOV
LP0
JMP
BL
INCT

SK3
LP4

LP5

R11,R10
LP0
@ISTOGL
R10

Wait for interrupt


Check if input toggled
No: bit is "0"

ORI
CZC
JEQ
TB
JNE
JMP

R1,>FF00
@H00FF,R1
LP5
27
LP5
LP4

Flag for ISR: use vector in R6 if times out


What is the detected input status?
It's 0
It's 1. We must wait until it toggles again
It did. Now wait for a 1
Not yet, keep waiting

TB
JNE

27
LP5

Wait till input toggles back to 1


Not yet, keep waiting

LDCR
SBZ
SBO
ANDI
XOR
B

R3,15
0
3
R1,>00FF
@H00FF,R1
*R10

Reload delay in clock


Leave clock mode
Enable timer interrupts
Clear ISR flag
Update last input bit

* This routine receives a byte.


* It uses the above one to build the byte in R4.
* It is located at >15A0-15B8 in the console ROM.

venerd 26 ottobre 2007 11.14.08

GETBY

LP6

SK4

LI
CLR
MOV
SLA
BL
JMP
INC
DEC
JNE
A
B

R8,>0008
R4
R11,R9
R4,1
@GETBIT
SK4
R4
R8
LP6
R4,R7
*R9

Bit counter: 8 bits per byte


Init byte
Save return point
Next bit
Get it
It was a "0"
It was a "1": put it in R4
More to do?
Yes
Build checksum in R7
Return

Now, here is how Read works:


Get max # of bytes (derive max # of records) and buffer address
Initialize cassette interrupts (see SETCS above) with a default delay of >0015
Receive all records:
Allow interrupts with LIMI 1
Set ISR vector in R6 to come here
Wait a long time. If it is elapsed, return with Cnd bit set in >837C.
Receive bytes until 6 in a row are >00 (12 for top of file)
Times the tape recorder with TIMECS, put the new value in timer
Receive bits until 8 are "1" (i.e. the >FF mark arrived)
Set ISR vector to "try once more, then error"
At top of file, get the number of records:
Receive one byte
If higher than max # allowed, return with Cnd bit and error bit (>01) in >837C
Receive the 2nd copy. If different return with error
Elsewhere, get a record:
Receive 64 bytes, transfer them to VDP buffer
Receive the checksum byte. Check if it matches
If not try once more. Then return with Cnd bit and error bit in >837C
If checksum matches the first time, skip the repetition of the record
Next record
Branch to CLRCS

Cassette verify
This routine is nothing more than a cassette read that compares the incoming data with the content of the
buffer, instead of filling the buffer with them. If a mismatch occurs, the routine returns with bits >21 set
in >837C (GPL status byte).
Set flag to indicate verify: bit >0010 in R14 (byte >83FD)
Set VDP to read from data buffer, rather than to write to it
Enter the Read subroutine

DSRs in GROM
There are two DSR in the first console GROM: CS1 and CS2. They can be found as GROM addresses:
>1320-16DC. As you might expect, CS1 deals with the first tape recorder, and CS2 with the second.
These are unusually high-level DSRs, that directly interact with the user. For instance, they display
prompting messages on screen and wait for the user to press a key. It is one of the rare occurences when
byte 8 in the PAB is effectively used to pass a screen bias to a DSR.
By the way, the prompting messages are send by calling a subprogram, whose name is >03. It must be
called with the screen address in >8364 and the a string number in >8362. This number (0 to 32, by steps
of 2) is an offset into a table of 2-byte long string pointers located at GROM address >15A0. For some
venerd 26 ottobre 2007 11.14.08

strings, the subprogram also adds the cassette number (CS1 or CS2) to the string and displays "then press
enter". In any case it beeps by calling the "accept tone" GROM routine at >0034.
As mentionned above, the file type must be display, variable 64, and sequential. In addition, CS2 can
only be opened for output (as recorder #2 cannot be read from).
Open
Set record length to 64, of none is provided in the PAB. Else round it up to 64
If file "variable" or "relative" return with "Illegal opcode" code in PAB byte 1
If file is opened as "update" or "append" return with "Illegal opcode" code in PAB byte 1
If file is opened as "input":
If cassette is CS2, return with "Illegal opcode" code in PAB byte 1
Turn motor on. Wait for 30 VDP interrupts (0.5-0.6 sec).
Display "Rewind cassette tape. Then press enter". Beep
Call GETKEY
Turn motor off. Wait for 30 VDP interrupts (0.5-0.6 sec). Beep
Display "Press cassette play. Then press enter". Beep
If file is opened as "output":
Turn motor on. Wait for 30 VDP interrupts (0.5-0.6 sec).
Display "Rewind cassette tape. Then press enter". Beep
Call GETKEY
Turn motor off. Wait for 30 VDP interrupts (0.5-0.6 sec). Beep
Display "Press cassette record. Then press enter". Beep
Call GETKEY
Turn motor on.. Wait for 30 VDP interrupts (0.5 sec. 0.6 sec for european consoles).
Wait for 300 VDP interrupts (10 seconds. 12 sec for european consoles).
Turn motor off.. Wait for 30 VDP interrupts (0.5-0.6 sec).
Goto EXIT
EXIT
Turn motor on. Wait for 30 VDP interrupts (0.5-0.6 sec)
Scroll up one line, move cursor to column 1
Return to Basic (or caller, whatever it is)
GETKEY
Wait for a key to be pressed
If it is <enter>
Return to caller
If it is <R>:
Re-enter DSR from the beginning
If it is <C>:
Goto CHECK
If it is <E>:
Set error code for "Device error" in PAB byte 1
Display "Press cassette stop. Then press enter". Beep
Call GETKEY
Goto EXIT
If another key was pressed, keep waiting
CHECK
Display "Rewind cassette tape. Then press enter". Beep
Call GETKEY
If <enter> was pressed. turn motor off. Wait for 30 VDP interrupts (0.5-0.6 sec). Beep
venerd 26 ottobre 2007 11.14.08

Display "Checking". Beep


Wait for the beep to end
Call cassette verify ROM routine with GPL opcode I/O 6
If no error occured:
Display "Data OK". Beep
Goto Close
If an error occured:
Turn motor off. Wait for 30 VDP interrupts (0.5-0.6 sec). Beep
Check the error bit (value >01) was set in >837C:
If set, display "Error detected in data". Beep
Else display "Error - no data found". Beep
Check opcode:
For "Save", display "Press R to record, C to check, E to exit". Be-be-beep
For others, display "Press R to read, C to check, E to exit". Be-be-beep
Call GETKEY
Re-enter DSR from the beginning

Close
Turn motor off. Wait for 30 VDP interrupts (0.5-0.6 sec).
Display "Press cassette stop. Then press enter". Beep
Call GETKEY
Goto EXIT

Read
Copy record length in character count
Turn motor on. Wait for 30 VDP interrupts (0.5-0.6 sec).
Call the ROM routine, with GPL opcode I/O 5. Number of bytes taken from record length.
If Cnd bit set when returning, an error occured:
Set error code for "Device error" in PAB byte 1
Display "Press cassette stop. Then press enter". Beep
Call GETKEY
Goto EXIT.
Else turn motor off. Wait for 30 VDP interrupts (0.5-0.6 sec).
Goto EXIT

Write
Turn motor on. Wait for 30 VDP interrupts (0.5-0.6 sec).
Call the ROM routine, with GPL opcode I/O 4. Number of bytes taken from record length.
If Cnd bit set when returning, an error occured:
Display "Press cassette stop. Then press enter". Beep
Call GETKEY
Goto EXIT.
Turn motor off. Wait for 30 VDP interrupts (0.5-0.6 sec).
Goto EXIT

Restore
Same as Open

venerd 26 ottobre 2007 11.14.08

Load
If cassette is CS2, return with "Illegal opcode" code in PAB byte 1
Turn motor on. Wait for 30 VDP interrupts (0.5-0.6 sec).
Display "Rewind cassette tape. Then press enter". Beep
Call GETKEY
Turn motor off. Wait for 30 VDP interrupts (0.5-0.6 sec). Beep
Display "Press cassette play. Then press enter". Beep
Wait for the beep to end
Call the ROM "Read" routine, with GPL opcode I/O 5. Number of bytes PAB bytes 6-7.
If Cnd bit set when returning, an error occured:
Turn motor off. Wait for 30 VDP interrupts (0.5-0.6 sec). Beep
Check the error bit (value >01) was set in >837C:
If set, display "Error detected in data". Beep
Else display "Error - no data found". Beep
Display "Press R to read, C to check, E to exit". Be-be-beep
Call GETKEY
Re-enter DSR from the beginning
If no error occured, display "Data OK". Beep
Goto Close

Save
Turn motor on. Wait for 30 VDP interrupts (0.5-0.6 sec).
Display "Rewind cassette tape. Then press enter". Beep
Call GETKEY
Turn motor off. Wait for 30 VDP interrupts (0.5-0.6 sec). Beep
Display "Press cassette record. Then press enter". Beep
Wait for 600 VDP interrupts, i.e 10 seconds (12 sec for european consoles).
Call the ROM routine, with GPL opcode I/O 4. Number of bytes taken from PAB bytes 6+7.
Turn motor off. Wait for 30 VDP interrupts (0.5-0.6 sec).
Display "Press cassette stop. Then press enter". Beep
Call GETKEY
If cassette is CS2, goto EXIT
Display "Check tape (Y or N)?".Beep
Wait for <Y> or <N> to be pressed:
If <N> goto EXIT
If <Y> goto CHECK

Delete
Same as Close

Scratch record
Set error flags for "Illegal opcode" in PAB, byte 1.
Goto EXIT

Status
Clear byte 6 in PAB (should be byte 8, but used for bias)
Goto EXIT

venerd 26 ottobre 2007 11.14.08

Preliminary 4/1/99
Revision 1 4/3/99 Polished, OK to release

Back to the TI-99/4A Tech Pages

venerd 26 ottobre 2007 11.14.08