HP-85 to HP 7970E

Connecting an HP-85 to an HP 7970 tape drive via HP-IB

The HP 7970E magnetic tape drive has an optional HP-IB interface. My particular unit has it. It seemed natural to connect it to my HP-85, which also talks HP-IB natively. But this turned out to be much more challenging than expected :

  • The HP-85 mass storage ROM does not support the HP 7970E
  • The HP-85 is too slow to keep up with the HP 7970E data rate
  • The HP-85 HP-IB implementation differs fundamentally from that of the HP 7970E in its treatment of parity
  • The HP 7970E HP-IB protocol is complicated and not clearly documented.

This page explains how I was able to solve the issues, and have the HP 85 read from the big tape. The video demonstrates the result.

HP 7970E HP-IB Protocol

The HP7970E HP-IB interface is an optional feature of the HP 7970E, and the interface protocol is described in this document:

(note: the online document hosted at hpmuseum.net is missing a critical page, and it took me a while to find it. The document linked above is "repaired" and complete).

The protocol is complex and difficult to understand, mostly because the documentation is quite poor. Doing simple operations requires a series carefully orchestrated commands, using advanced HP-IB features such as secondary commands, parallel poll responses, and EOI tagging.

Timing is critical. Since the tape HP-IB interface only has a 128 byte R/W buffer, and the tapes moves quite rapidly at 45 IPS, data has to be written in or read out of the buffer to keep up with the tape, or the buffer will overflow (on read) or underflow (on write). The tape then stops with a timing error. Unfortunately the HP 85 is not fast enough to keep up with the tape, even using its advanced buffered TRANSFER FHS command. So the HP-85 will be limited to reading tapes formatted with short records of 128 bytes length maximum, or read only the first 128 bytes of longer records. Writing to the tape from the HP-85 is not possible (or at least, I have not succeeded).

Command sequencing has to be followed strictly. Most tape commands have to be followed by waiting for a Parallel Poll response, then reading the DSJ (Device Specific Jump) to acknowledge the parallel poll receipt, and based on the DSJ return value (0 for no error, 1 for error), read the 3 status bytes. Skipping any of these steps will hang the whole process.

Parity turned out to be a huge and unexpected problem (diagnosing it was difficult, the details are in my handwritten lab notes). Parity is an optional feature of HP-IB. Not only does the HP 7970E expect odd parity, but it reacts very poorly if a parity error is detected: the HP 7970 simply hangs the entire HP-IB bus, even on a command not addressed to it. So the optional parity bit (DIO 8) must be set properly. Which the HP-85 does not do: although there is a control bit for setting parity in the interface registers, setting it did absolutely nothing in my testing. If a command is sent with wrong (even) parity, the HP 7970 never releases the NDAC line (Not Data ACknowledge), which hangs the whole HP-IB bus. To make things more complicated, odd parity is only enforced on command words, but data words can be any parity.

If the only device on the HP-IB bus is the HP 7970 tape, you can get around it by sending raw binary command codes, as will be shown below. But in my setup I also had a printer and a floppy disk on the bus, both of which are controlled by ROM over which you have no control. It is frequently sending even parity command words, resulting in the 7970 hanging the bus. I ended up making a small FPGA contraption that continuously monitors the HP-IB bus and corrects the parity bit on the fly.

Demo programs for reading from the HP 7970 to the HP-85

Here are the two HP-85 programs I wrote to access the HP 7970E over the HP-IB interface. The first one is the minimum needed to read 100 tape records. The second one is the full fledged program used in my ASCII demo video, capable of reading a printing a whole file, position the tape by rewinding, skipping forward and backwards a file, and print excerpts of a file record by record, forward or backward.

The code looks quite incomprehensible at first, but should make sense after you read the line by line description below. It uses many of the more advanced commands described in the HP 85 IO programming reference guide:

I will give page references to this guide in the explanations below.

If you are unfamiliar with HP-IB at the very low level we will be using here, this HP Journal article is one of the best descriptions I have found:

Walk-through the basic interface example code

The full code is here: HP 7970 to HP 85 basic interface and reprinted below:


1 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!2 ! !!! HP7970E INTERFACE PROGRAM !!!!!!!!!!!!!!!!!!!!!!!!!!!!!3 ! !!! ASSUMES TAPE ADDRESS = 1 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!4 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!5 !10 CLEAR20 DISP "HP7970 INTERFACE PROGRAM"25 ! !!! INTERFACE INIT !!!!!!!!!!!!!!!!!!30 RESET 7 ! Reset HPIB interf40 CONTROL 7,16 ; 128 ! send EOI on data end50 ON TIMEOUT 7 GOTO 100060 SET TIMEOUT 7;1000 ! 1s timeout70 DIM T$[136] ! 128 char buf75 DIM U$[128] ! READ STRING80 IOBUFFER T$ ! use for tsfr90 ! !!! TAPE DRIVE INIT !!!!!!!!!!!!!!!!!100 ! !! sel 0/ppoll/DSJ/Status 110 A=FNS0 ! sel unit 0114 GOSUB 1100 ! read PPOLL115 D=FND ! read dsj116 GOSUB 1400 ! wait tape online117 ! !!! Tape Rewind and Status !!!!!!!!!!118 DISP "REWINDING TAPE..."120 A=FNR ! rewind124 GOSUB 1100 ! ppoll loop130 ! DISP "READING DSJ"140 D=FND ! D$=FND$150 DISP "DSJ =";D ! DISP D$160 DISP "STATUS:"170 S=FNS180 DISP DTH$(S1);" ";DTB$(S1)181 DISP DTH$(S2);" ";DTB$(S2)182 DISP DTH$(S3);" ";DTB$(S3)190 IF BIT(S2,2) THEN GOSUB 1200 ! wait for rewind195 GOSUB 1400 ! wait for tape online200 ! !!! READ TAPE RECORDS !!!!!!!!!!!!!!!\\996 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!997 ! !! SUBROUTINE AREA998 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!999 !1000 ! !!!!!!!!!!!!!!!!!!!1001 ! !!! HP-IB TIMEOUT 1002 ! !!!!!!!!!!!!!!!!!!!1005 DISP "HP-IB TIMEOUT"1010 DISP "Resetting interface..."1020 RESET 71030 END1100 ! !!!!!!!!!!!!!!!!!!!1101 ! !!! PPOLL WAIT LOOP 1102 ! !!!!!!!!!!!!!!!!!!!1105 ! DISP ""1110 FOR J=1 TO 1001120 A=PPOLL(7)1123 ! DISP A;".";1130 IF BIT(A,6) THEN RETURN ! assumes tape addr = 11140 NEXT J1145 DISP "PPOL timed out"1150 RETURN1200 ! !!!!!!!!!!!!!!!!!!!!!!!1201 ! !!! REWIND COMPLETE 1202 ! !!!!!!!!!!!!!!!!!!!!!!!1210 DISP "Tape rewinding..."1220 S=FNS ! read status1230 IF BIT(S2,2) THEN GOTO 12201240 DISP "Rewind done."1250 RETURN1300 ! !!!!!!!!!!!!!!!!!!!!!!1301 ! !!! DSJ AND STATUS PRINT ON ERROR1302 ! !!!!!!!!!!!!!!!!!!!!!!1310 D=FND1320 IF D=0 THEN RETURN1330 S=FNS ! read status1335 IF BIT(S2,4) THEN RETURN ! ignore timing errors1340 DISP "DSJ=1! Status is:"1350 DISP DTH$(S1);" ";DTB$(S1)1360 DISP DTH$(S2);" ";DTB$(S2)1370 DISP DTH$(S3);" ";DTB$(S3)1375 RETURN ! continue for now1380 DISP "BYE" @ END1400 ! !!!!!!!!!!!!!!!!!!!!!1401 ! !!! WAIT TAPE ONLINE1402 ! !!!!!!!!!!!!!!!!!!!!!1410 IF BIT(FNS,0) THEN RETURN1415 BEEP1420 DISP "TAPE NOT ONLINE!"1430 DISP "PRESS CONT WHEN READY"1440 PAUSE1450 IF BIT(FNS,0) THEN RETURN1460 GOTO 14201996 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1997 ! !! TAPE FUNCTION AREA1998 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1999 !2000 ! !!!!!!!!!!!!!!!!!!!!2001 ! FNS0 - select unit 02002 ! !!!!!!!!!!!!!!!!!!!!2005 DEF FNS0 = ! sel unit 02010 SEND 7 ; CMD 191,213,161,97 DATA 12020 FNS0=1 ! dummy return val2030 FN END2100 ! !!!!!!!!!!!!!!!!!!!!2101 ! FNR - tape rewind2102 ! !!!!!!!!!!!!!!!!!!!!2105 DEF FNR ! rewind2110 SEND 7 ; CMD 191,213,161,97 DATA 132120 FNR=12130 FN END2200 ! !!!!!!!!!!!!!!!!!!!!2201 ! FND$ - dsj with xfer2202 ! !!!!!!!!!!!!!!!!!!!!2205 DEF FND$ = ! read DSJ2210 SEND 7 ; CMD 191,181,193,1122215 IOBUFFER T$ ! reset buf2220 TRANSFER 7 TO T$ FHS ; EOI2230 FND$=T$ ! return DSJ2240 FN END2300 ! !!!!!!!!!!!!!!!!!!!!2301 ! FND - dsj with enter2302 ! !!!!!!!!!!!!!!!!!!!!2305 DEF FND = ! read DSJ2310 SEND 7 ; CMD 191,181,193,1122320 ENTER 7 USING "#%,B" ; D2330 FND=D2340 SEND 7 ; CMD 223 ! UNTALK2350 FN END2400 ! !!!!!!!!!!!!!!!!!!!!2401 ! FNS - read Status2402 ! !!!!!!!!!!!!!!!!!!!!2405 DEF FNS = ! read status2410 SEND 7 ; CMD 191,181,193,972420 ENTER 7 USING "#%,B" ; S1,S2,S32430 FNS=S12440 SEND 7 ; CMD 223 ! untalk2450 FN END2500 ! !!!!!!!!!!!!!!!!!!!!2501 ! FNF - read record fwd2502 ! !!!!!!!!!!!!!!!!!!!!2510 DEF FNF2520 SEND 7 ; CMD 191,213,161,97 DATA 82530 FNF=12540 FN END2600 ! !!!!!!!!!!!!!!!!!!!!!!!2601 ! FNG - get data2602 ! !!!!!!!!!!!!!!!!!!!!!!!2605 DEF FNG$2610 SEND 7 ; CMD 191,181,193,2242620 ! ENTER 7 USING "#%,#%K" ; U$ ! slower ENTER based version2621 IOBUFFER T$2623 TRANSFER 7 TO T$ FHS ; COUNT 128 EOI2625 SEND 7 ; CMD 223 ! untalk2630 FNG$="" ! dummy return value2650 FN END

Initializing the HP-IB interface


30 RESET 7 ! Reset HPIB interf40 CONTROL 7,16 ; 128 ! send EOI on data end50 ON TIMEOUT 7 GOTO 100060 SET TIMEOUT 7;1000 ! 1s timeout70 DIM T$[136] ! 128 char buf75 DIM U$[128] ! READ STRING80 IOBUFFER T$ ! use for tsfr

This first snipped of code configures the HP-85 HP-IB interface module. It is assumed to be on its default IO address select code 7. The tape is assumed to be on the HP-IB address 1, and the printer on HP-IB address 9.

  • 30 RESET 7 : this simply resets the HP-85 interface and releases all the control lines in case the bus was previously hanged up.
  • 40 CONTROL 7,16;128 : This cryptic line is very important. See p115 of the IO manual. The 7970 expects all data fields to tag the last data byte by raising the EOI line (End or Identify). The interface is told to do this automatically by writing a 1 on bit 8 of control register 16. 128 decimal is 10000000 in binary, therefore setting bit 8 to 1 as desired.
  • 50 ON TIMEOUT 7 GOTO 1000
  • 60 SET TIMEOUT 7;3000 : these two lines establish a timeout of 3 seconds if the interface becomes unresponsive (tape is off line, runs out, read error, etc...). This is needed because we will use a buffered TRANSFER statement that could hang up waiting for characters. The error recovery routine starts at address 1000.
  • 70 DIM T$[136] : this declares T$, the IO buffer that will be used in the fast I/O TRANSFER statements. It is dimensioned to hold 128 characters (the max length of the tape record we can read) plus 8 control characters needed for the buffer management (see p. 280 of the IO programming guide).
  • 75 DIM U$[128] : this declares U$, a 128 character string that can is alternately used for the slower data input method using the ENTER statement
  • 80 IOBUFFER T$ : this declares T$ as an IO buffer and clears it, again for later use in TRANSFER input statements.

Selecting Device 0


110 A=FNS0 ! sel unit 0114 GOSUB 1100 ! read PPOLL115 D=FND ! read dsj116 GOSUB 1400 ! wait tape online

Before the tape drive can be accessed at all, it needs to be explicitly selected. There are 4 possible selectable units (1 master and 3 slaves). We assume our 7970 is configured as tape unit 0 (by pushing the 0 button on its front panel).

  • 110 A=FNS0 : this calls the function FNS0, defined later in the program, which sends the command to select device 0. This function is further detailed below.
  • 114 GOSUB 1100 : after every command, a wait for a PPOLL (Parallel Poll) response is required
  • 115 D=FND : after a PPOLL return, a read of the DSJ (Device Specific Jump) is required to acknowledge and de-assert the parallel poll, and return 0 or 1 depending on error status. Function FND implements the read. This function is detailed below.
  • 116 GOSUB 1400 : Go to subroutine that waits for status to show that the "online" button has been pressed. Also detailed below.

FNS0 "select device 0" function subroutine and the odd parity saga


2005 DEF FNS0 = ! sel unit 02010 SEND 7 ; CMD 191,213,161,97 DATA 12020 FNS0=1 ! dummy return val2030 FN END

Since this function is similar to all the tape commands used in the program. The select device 0 command syntax is detailed in p. 2-18 of the tape HP-IB interface manual. It says to send a secondary HP-IB command 011000001, then data 1, accompanied by the EOI signal. The way to do this on the HP-85 is to use the custom command, as detailed on p. 105 of the HP-85 IO programming manual. This would normally be written like:

  • SEND 7; UNL MTA LISTEN 1 SCG 97 DATA 1

SEND 7 sends the output command on the HP-IB interface #7, UNL is an unlisten command to all devices, MTA (MyTalkAddress) makes the controller the talker, LISTEN 1 makes the tape the listener. These are generic steps to prepare for sending a command from the controller to the tape. Finally, SCG 97 sends the secondary command 97 (binary 011000001), and DATA 1 sends the data 1 with EOI, which is the "Select Tape 0" tape function. However, if you try this perfectly valid sequence, the 7970 will hang the bus. It took me a long while to figure out why. This is because the default commands generated by the HP-85 do not enforce parity (bit 8 of the command word).

For example, UNL sends code 77 Octal = 63 Decimal = 00111111 Binary. Which is an even parity word. The command will not be accepted by the 7970, which will keep the NDAC (Not Data Acknowledge) signal low forever, which in turn hangs the HP-IB bus. However, if we flip the parity bit 8 to 1 and send 10111111 instead, now the parity is even and the 7970 will gladly accept it, release the NDAC line, and everything is fine. Flipping bit 8 is the same as adding 128, so UNL(odd) = UNL + 128 = 63+128 = 191.

Similarly, other command codes have to be substituted by their odd parity equivalents (all values in decimal):

  1. UNL=63 -> UNL(odd)=191
  2. MTA=85 -> MTA(odd)=213
  3. MLA=53 -> MLA(odd)=181
  4. LISTEN 1=33 -> LISTEN 1(odd)=161
  5. TALK 1=65 -> TALK 1(odd)=193
  6. UNT 1= 95 ->UNT(odd)=223
  7. SCG 97 = 97 is odd already

Fortunately, the HP-85 includes a facility to send arbitrary codes, using the SEND 7; CMD code1, code2, .... syntax. That's how we arrive at the final code:

  • 2005 DEF FNS0 : calling name of function is FNS0
  • 2010 SEND 7 ; CMD 191,213,161,97 DATA 1 : sends UNL(odd), MTA(odd), LISTEN 1(odd), SCG 97 and DATA 1 codes. Since already we set up the interface register on line 40, the EOI line will automatically be set with the 1 data code. Yeah! We have finally been able to communicate with the HP 7970E.
  • 2020 FNS0=1 : the function needs to return a value, so we arbitrarily return 1
  • 2030 FN END : end of function

Other commands are implemented in a similar manner, using functions SENDing pre-calculated CMD and DATA codes of the correct parity.


Parallel Poll wait loop subroutine


1110 FOR J=1 TO 1001120 A=PPOLL(7)1123 ! DISP A;".";1130 IF BIT(A,6) THEN RETURN ! assumes tape addr = 11140 NEXT J1145 DISP "PPOL timed out"1150 RETURN

You have to wait for a parallel poll response every time you have sent a command before you do anything else. The Parallel Poll routine is a wait loop that returns as soon as the tape asserts its poll line, or if more than 100 poll iterations have occurred. The code is straightforward, except for the location of the polled bit to test:

  • 1110 FOR J=1 TO 100 : try a maximum of 100 times.
  • 1120 ! DISP A;"."; : line commented out, was used for debug during development to inspect the PPOLL return value
  • 1120 A=PPOLL(7) : poll HP-IB interface 7
  • 1130 IF BIT(A,6) THEN RETURN : Bit 6 is the one assigned tape address 1 in a parallel poll (don't ask). Return immediately if it has been set.
  • 1140 NEXT J : try to poll 100 times
  • 1145 DISP "PPOL timed out" : if we don't have a relatively quick answer, something probably went wrong, display warning.
  • 1150 RETURN : return

FND: Read DSJ function


2305 DEF FND ! read DSJ 2310 SEND 7 ; CMD 191,181,193,1122320 ENTER 7 USING "#%,B" ; D2330 FND=D2340 SEND 7 ; CMD 223 ! UNTALK2350 FN END

You have to read DSJ after doing a parallel poll. This does two things: it informs the tape you are servicing the poll, so the tape can de-assert its poll line. It also lets you know if the command resulted in an error by returning a value of 0 (no error) or 1 (error).

  • 2305 DEF FND : Function to read DSJ
  • 2310 SEND 7 ; CMD 191,181,193,112 : sends codes for UNL(odd), MLA(odd), TALK 1(odd), SCG 112 (read DSJ secondary command)
  • 2320 ENTER 7 USING "#%,B" ; D : reads the data back into variable D. Format "#%,B": B means binary read, % means that EOI is used as terminator, and # that linefeed is not a terminator.
  • 2330 FND=D : function return value is set to the DSJ value we just read
  • 2340 SEND 7 ; CMD 223 : we need to send an UNT 1(odd) to the tape, since we made it the talker on line 2310
  • 2350 FN END : function return

Wait for tape online


1410 IF BIT(FNS,0) THEN RETURN1415 BEEP1420 DISP "TAPE NOT ONLINE!"1430 DISP "PRESS CONT WHEN READY"1440 PAUSE1450 IF BIT(FNS,0) THEN RETURN1460 GOTO 1420

This code snippet makes sure the tape is online before going any further. The user needs to press the online button after loading for the tape to become responsive to anything, and it's easy to forget!

  • 1410 IF BIT(FNS,0) THEN RETURN : call FNS, the function that reads the status bytes, described below. Testing bit 0 will tell if the user has pressed the online button
  • 1415 BEEP : remind forgetful user
  • 1420 DISP "TAPE NOT ONLINE!"
  • 1430 DISP "PRESS CONT WHEN READY"
  • 1440 PAUSE : stop, user has to press CONT to restart after he puts the tape online
  • 1450 IF BIT(FNS,0) THEN RETURN : test again, return if online
  • 1460 GOTO 1420 : not online, go back.

FND: Status byte read function


2405 DEF FNS = ! read status2410 SEND 7 ; CMD 191,181,193,972420 ENTER 7 USING "#%,B" ; S1,S2,S32430 FNS=S12440 SEND 7 ; CMD 223 ! untalk2450 FN END

There are three status bytes, which are read into common variables S1, S2 and S3 by calling this functions. They need to be inspected by the calling routine should an error occur. The structure is similar to the function FND:

  • 2405 DEF FNS : function name
  • 2410 SEND 7 ; CMD 191,181,193,97 : sends UNL, MLA, TALK 1, SCG 97 (read status secondary command)
  • 2420 ENTER 7 USING "#%,B" ; S1,S2,S3 : The tape drive returns 3 bytes, with the last one marked with EOI. B specifies binary format, #% specify end marker is EOI and not Linefeed.
  • 2430 FNS=S1 : set the function value to S1
  • 2440 SEND 7 ; CMD 223 : sends UNTALK 1(odd). Untalk is needed after making the tape the talker.
  • 2450 FN END : return

Tape Rewind, Status Display


118 DISP "REWINDING TAPE..."120 A=FNR ! rewind124 GOSUB 1100 ! ppoll loop130 ! DISP "READING DSJ"140 D=FND ! D$=FND$150 DISP "DSJ =";D ! DISP D$160 DISP "STATUS:"170 S=FNS180 DISP DTH$(S1);" ";DTB$(S1)181 DISP DTH$(S2);" ";DTB$(S2)182 DISP DTH$(S3);" ";DTB$(S3)190 IF BIT(S2,2) THEN GOSUB 1200 ! wait for rewind195 GOSUB 1400 ! wait for tape online

The code then goes on to perform a tape rewind. By now this should start to make sense. On line 120, the FNR function is called which sends the pre-calculated CMD codes for a rewind command. Then the mandatory PPOLL wait loop is called on 124, followed by the mandatory DSJ read on line 140, followed by the status byte read on line 170. The status bytes are then displayed onscreen in Hex and Binary format (lines 180 to 182). Bit 2 of S2 (which contains status byte 2), the "rewind in progress" bit, is tested repeatedly until rewind is complete.

Read 100 records


205 DISP "READING 100 RECORDS"210 FOR I=1 TO 100220 A=FNF230 GOSUB 1100 ! ppoll wait233 A=FND ! read DSJ before status always235 S=FNS ! check status for end of file or EOT236 IF BIT(S1,5) THEN DISP "END OF TAPE" @ GOTO 990237 IF BIT(S1,7) THEN DISP "END OF FILE" @ GOTO 990238 IF BIT(S2,3) THEN DISP "TAPE RUNAWAY" @ GOTO 990250 A$=FNG$260 DISP T$ ! DISP U$ ! U$ if using ENTER based version of FNG$270 GOSUB 1300 ! dsj check280 NEXT I990 DISP "DONE" @ END

Finally we get to reading our data off the tape. It takes two commands to accomplish a read. First you have to issue a read forward motion through the FNF function on line 220, followed by the usual parallel poll wait, DSJ read, and status read. If all is well, you then have to issue a read through the FNG$ string function on line 250. We'll discuss these two functions below.

FNF - Read forward function


2510 DEF FNF2520 SEND 7 ; CMD 191,213,161,97 DATA 82530 FNF=12540 FN END

It's a variation of the tape function like FNS0 we first encountered. The work is accomplished by line 2520 which sends a UNL, MTA, LISTEN 1, Secondary Tape Command (97), which is generic for all tape functions. But this time we send 8 as the DATA byte, which selects the read forward function and starts moving the tape.

FNG$ - Read data function


2605 DEF FNG$2610 SEND 7 ; CMD 191,181,193,2242620 ! ENTER 7 USING "#%,#%K" ; U$ ! slower ENTER based version2621 IOBUFFER T$2623 TRANSFER 7 TO T$ FHS ; COUNT 128 EOI2625 SEND 7 ; CMD 223 ! untalk2630 FNG$="" ! dummy return value2650 FN END

Line 2610 sends an UNL, MLA, TALK1 and a SCG 224, the secondary command for reading data. Line 2620, which is commented out, would be a normal data read into the string U$ using the ENTER statement. ENTER fetches and processes data character by character, which is pretty slow. Instead we will use a fast TRANSFER FHS into buffer T$, on lines 2621 and 2623. This is the fastest way to input data on the HP-85:

  • IOBUFFER T$ : set the previously declared T$ string array as the current IO transfer buffer, and clear it
  • 2623 TRANSFER 7 TO T$ FHS ; COUNT 128 EOI : initiate a fast (FHS) buffered transfer using T$. Terminate either when 128 characters have been received, or when an EOI (end of data) is encountered, which ever occurs first. If neither of these conditions occurs, the timeout we set on line 50 and 60 will kick in and get us unstuck.

Even with a TRANSFER FHS, the HP-85 is not quite fast enough to keep up with the tape speed. The tape drive will generate a timing error because we read too slow to empty its 128 character buffer in time and stop at the end of the record in the inter-record gap. However, the 128 first characters of the record will still be in the tape buffer, so we can read the first 128 characters at our leisure. We'll just ignore the timing error and start again for the next record. Provided the records are no more than 128 character long, one can read the whole content on the tape error free, albeit a bit slowly with the tape stopping briefly in-between each record. If the records are longer than 128 bytes, it is only possible to read the first 128 characters of each record.

ASCII demo full program code and FPGA parity sniffer

You should now be able to walk through the more complete ASCII art demo program I used to make the video. It uses the same techniques but implements additional functions such as skipping over files forward and backward, reading records forward and backward, properly detecting the end of tape, peeking at the beginning of the files, etc.. It also implements a function key-driven user interface.

Beware that this ASCII art demo program expects a line printer on the same bus as the tape, on HP-IB address 9, on which it will print the content of the file read from the tape, hopefully resulting in a pretty ASCII art picture. But we cannot control the parity of the HP-IB print commands, since they are generated by the HP-85 built-in ROM. The printing commands will therefore cause the 7970 to hang the bus. For this program to work, I had to build an external FPGA-driven parity correcting device and put it on the bus. It continuously monitors the bus and corrects parity on the fly (for command words only), circumventing the problem.