PC-8 with Printer

Tandy PC-8 Pocket Computer

I recently wrote an article on my favourite pocket computer model, the PC-2. In that article, I referenced the PC-7 which I had spent hours and hours using back then. At the time I had wanted the PC-8, but it was in such high demand and available for only a single catalog season at Radio Shack. My parents were unable to get one.

In this article I fix up a thermal printer for the PC-8 and describe a banner program I created using only 964 bytes of memory leaving room to spare for additional expansion or programs.

Tandy PC-8 26-3655

Radio Shack Pocket Computer Releases

I searched through RadioShackCatalogs.com from 1980 through 1993 to compile a timeline of pocket computer releases and catalog years when each pocket computer model was available.

  • 1981 – PC-1 (new)
  • 1982 – PC-1
  • 1983 – PC-1, PC-2 (new)
  • 1984 – PC-2, PC-3 (new), PC-4 (new)
  • 1985 – PC-3a, PC-4
  • 1986 – PC-3a, PC-4b, PC-5 (new)
  • 1987 – PC-6 (new), PC-7 (new)
  • 1988 – PC-6, PC-7, PC-8 (new)
  • 1989 – PC-6, PC-7
  • 1990 – PC-6
  • 1991 – PC-6
  • 1992 – PC-6

The first pocket computer, the PC-1 was introduced for catalog year 1981 and the last catalog year for a pocket computer was 1992 with the PC-6. Interestingly, the PC-6 was the longest running model, from 1987 through 1992.

The PC-8 was introduced in catalog year 1988 and was not listed the following year. It was a one year offering. However, the PC-7 was still listed.

PC-8 Courtesy of RadioShackCatalogs.com

I recently found a PC-8 on eBay, and as luck would have it, I also found a deal on the printer cassette interface for this model. The PC-8 is a rebadged Sharp PC-1246, and all of the PC-3 accessories work with the PC-8.

The PC-8 was far superior to the PC-7 in many ways. First, it had an expansion port for a printer and cassette interface. Second, it didn’t have the folding cover and membrane keyboard that the PC-7 had. This resulted in improved durability, especially since it also had a hard cover that was removable. On the PC-7, you needed to place it on a flat surface such as a table in order to use the alpha-numeric keys and other function keys. With the PC-8, you can use it while holding it and you did not have to place it on a book or table to use it.

Tandy PC-7 26-3673

Interestingly, I was unable to find the PC-8 owners manual online, and could find only the German version of the Sharp PC-1246 operation manual. I was lucky that I had received the owners manual for with eBay purchase. Incidentally, I have had similar difficulty finding the PC-6 manual online, for one of the longest sold pocket computers in the line.

Memory for User Programs

When it comes to memory, similar to the PC-7, the PC-8 had only 2 KB of RAM total, with 1,278 bytes available for user programs while the PC-7 had 1,568 bytes available for user programs. Even though the PC-8 had such a small amount of RAM available, the cassette interface could be used to swap out programs. The PC-7 had no such option. You had to key in the programs by hand.

The PC-8 like all other rebadged Sharp models Radio Shack sold, had a single area for program storage, while the rebadged Casio models Radio Shack sold had 10 program areas, P0 through P9 (like the PC-7).

Even though the Sharp models had a single program space, if you wanted to store several programs in memory at the same time, you could simply number each program in its own line numbering segment and execute each program at its starting number, such as RUN 500.

Alternatively, you could add a label at the beginning of each program segment, and then run that segment using the DEF key.

PC-3 Printer / Cassette Interface

The PC-8 was compatible with all of the PC-3 accessories. This meant that the PC-3 printer / cassette was compatible with the PC-8.

The printer for the PC-3 was thermal. This had some advantages over the plotter printers for the PC-2, mainly because there are no pens or ink to consume. The thermal paper was the only consumable item. Further, the thermal paper is a standard size, 2.25”. This is a common size for portable credit card machines and small cash registers and can easily be found today on Amazon.

In addition, the drive mechanism for the print head and paper on the PC-3 printer is more durable over time. The PC-2 plotters used an Alps mechanism infamous for cracked pinion gears and cracked nubs on the anti backlash gears.

PC-3 Printer and Cassette Interface

The mechanism to advance the paper is part of the same mechanism to move the printer head. This cost reduced design required a single motor and did not have the pinion gears that could crack over time. Also, there was no need to move the paper backwards for the Y-Axis on these printers since they aren’t using pens.

The major disadvantage to this printer is that it cannot do graphics, only text. It supports 24 characters per line.

The PC-3 printer cassette interface also had a NiCd battery pack (“AA” x 4) inside which eventually leaks over time. This could potentially damage nearby components. I replaced the NiCd pack with NiMh equivalent cells. The unit I received did not yet leak, but replacing it preemptively was a good idea.

NiMh Replacement Pack

Testing

After replacing the NiCd pack, I typed in the “Biorythm” program found in the PC-3 printer’s user manual and ran it. The printer worked perfectly!

The low available RAM on the PC-8 was really apparent after keying in the Biorythm program. Afterwards I had about 250 bytes of RAM available. Saving to and then loading from cassette worked without issues.

There is a low battery light on the printer which, when lit, can be used as the indication the battery needs to be recharged.

These printer cassette modules do not have a modern charge controller or battery management system, so you do not want to leave the AC adapter connected beyond what is necessary to charge the internal NiMh battery pack.

NiCd Pack (old, left) vs NiMh Pack (new, right)

The original AC adapter is 8.5 VDC, center pin negative, 350mA. The user manual states that the NiCd batteries are fully charged in 15 hours. The original NiCd pack was 500 mAh. I am going to assume the 500 mAh rating is per cell, for a total of 2,000 mAh for the pack.

The replacement pack I created uses 2,000 mAh cells. This is four times the capacity of the NiCd pack. In theory, the recharge time using the same AC adapter would be four times longer, or 60 hours.

I did notice it took quite awhile to get enough charge into the pack to be able to print for extended periods. I ended up using a variable power supply at the same voltage (8.5 VDC) but with more current capacity (3 A max) to charge the pack quicker.

Sharp CE-126P Printer

Sharp released a thermal printer and cassette tape interface for their pocket computers which was compatible with the Tandy PC-3 and PC-8, the CE-126P. The CE-126P was also thermal, and took the same sized paper. The only major difference is the form factor – this printer has a wire and plug which connects to the pocket computer peripheral interface.

In addition to a different form factor, this printer and cassette interface does not have a built in rechargeable battery pack – it runs on 4 “AA” batteries. This is a great alternative to the Radio Shack model since so many of those had packs which either leaked and/or are dead and need replacing before the unit will work again.

In my opinion, this printer is far better – it’s a bit more portable and more convenient with the “AA” battery option.

Memory Map

The PC-8 has no memory expansion capability, and boasts only 2 KB of RAM. I could not find a service manual online for the PC-8, but I was able to find the Sharp PC-1246 service manual, which is the functional equivalent of the PC-8 (a rebadged Sharp PC-1246).

After a full reset, the PC-8 indicates 1,278 bytes of available memory, using the MEM command. The 2 KB of RAM is divided into a system area and user area. The system area is 768 bytes (from 0800H to 0AFFH), and the remainder is the user area, 1,280 bytes (2,048 minus 768 = 1,280 bytes. I’m not sure where the 2 missing bytes are).

The user area is where your program(s) reside and where dimensioned arrays are allocated.

I started to experiment a bit with the usage of memory, not just on this pocket computer but a few others. It’s interesting to see the differences between models. Because of the limited amount of RAM available, programming style and choices can make a difference.

These pocket computers (both Sharp and Casio) store a tokenized representation of your BASIC program. Each of the defined S’-BASIC commands are represented by a single byte stored in the program area (or two bytes for the calculators that used S-BASIC). Within the ROM or operating system of the calculator, the BASIC runtime logic will use these tokens as pointers or jumps to machine code subroutines stored in ROM. This results in not only a faster runtime than a non-tokenized equivalent, but this also consumes less memory to store your program.

This is because the text of your program is not stored in memory, but the tokenized version of it is. For example, if you had the line “999 PRINT A”, and if you stored that exactly as is in memory, that might take as much as 12 bytes of memory (11 characters and a null/terminator byte). Tokenized, this could be stored in only 5 bytes (2 bytes for the line number, 1 byte for the PRINT command, 1 for the variable argument, 1 byte for the terminator). Further, if I had two lines:

998 PRINT A
999 PRINT B

With tokenization, this would take 10 bytes of memory. If we combined these two lines into a single line:

998 PRINT A : PRINT B

This would save 2 bytes, for a total storage of only 8 bytes.

Note that even if you use the abbreviated equivalents of these BASIC commands when writing a program, such as “P.” for print as an example, the tokenized storage space required is the same. This is because the token for PRINT and P. are the same. In fact, the program listing will always show the full verb, not the abbreviations, regardless of which you used when you keyed in your program.

Variable Storage

Some of the system area RAM space is reserved for variables, 26 spaces to be exact, A through Z. Assigning values to these variables does not consume more RAM, and clearing them does not free any RAM space.

For example, a CLEAR statement won’t free up RAM (unless you dimension an array). The storage location is the same for numeric or string values (A is the same as A$). The maximum size of these default string variables is 7 characters. This translates into at least 232 bytes of storage space (26 letters by 7 characters each) that is reserved. The maximum string length of a dimensioned variable is 16 characters.

The PC-8 can save and load variable values to and from the tape. This means that mapping or data for a program could be loaded into this variable space. Without this feature, your program must initialize these variables or load such mapping data. This consumes quite a bit more memory, leaving less available for your program.

Hidden Commands

The PC-8 has some undocumented or hidden commands. Two of those commands are PEEK and POKE. Similar to other pocket computers and other BASIC platforms, these commands allow for direct reading from and writing to memory.

The format of these commands on the PC-8 are:

PEEK {address}
POKE {address},{byte value}

Where {address} is between 0 and 2047. Note this address space covers only the RAM portion of the system. It does not allow for reading the ROM portion of the address space.

I am experimenting with mapping out the system and user storage area in RAM, as well as finding new hidden commands by POKEing directly into the program area and listing the result. I’ll update this post once I’ve finished the mapping.

Sample ProgramPrinting a Banner

I was curious to see if I could write a program which would produce a “banner” on the printer. The idea came from the Apple II days with those dot matrix printers and Print Shop.

My idea for a similar program for the PC-8 would either take a string or series of characters from the keyboard and print them out larger and rotated in a banner format to the printer.

Since thermal printer for the PC-3 has paper only 2.25” wide, the text needs to be enlarged and rotated for a banner with as large of a font size as possible.

There are a few challenges with this idea:

  • The PC-3 printer cannot print graphics, nor rotated text.
  • The PC-8 has only 1,280 bytes of RAM for variables, data, and program combined.
  • The PC-8 has a limited BASIC command set, not as extensive as the PC-2.
  • The undocumented PEEK command will not allow reading from the ROM, and therefore I cannot copy the bitmap font from ROM.

Approach

Unlike the PC-2, which has a 4 color plotter which has built in support for rotated text, this printer does not support drawing of lines or graphics, only text. In order to print a banner, I would need to have a bitmap of all of the letters, then use that bitmap to print enlarged and rotated versions of each character for the banner, in order to produce something similar to the output below:

Banner Example

This would take quite a bit of a RAM space for such a banner program. I was hoping to be able to read the bitmap representation of each ASCII character in ROM that the PC-8 uses to draw to the LCD using the PEEK statement. This would save on quite a bit of space in memory and time manually creating the bitmap for each ASCII character.

The idea would be that I could find this bitmap section within the ROM by inspection and then simply index it directly to look up and retrieve the bitmap given any ASCII character that the pocket computer supports. If this was possible, I could avoid having to recreate a font myself, and I could save a considerable amount of program space in RAM.

Since the PEEK command works only within the RAM space, I’ll need to create this bitmap data myself. I will not be able to read the internal ROM bitmap.

Built-in LCD Character Set

The LCD on the PC-8 is not a contiguous bitmap of pixels like the PC-2 was. It uses a series of 5×7 pixel segments.

Sample of the PC-8 Font

Looking closely at a sample of each character allows me to copy each one into a bitmap representation that I can store in my banner program.

An example of how to represent a pixelized image in binary format could be illustrated with the following example:

Image to Binary

In this example, we could represent the image as a series of 6 bytes (one byte per row, starting from the top) and use only the first 6 bits in each byte, one for each of the pixels, from left to right. The first row would be 0, the second would be 18, etc. The entire image could be represented as 0, 18, 18, 0, 30, 0.

Creating the Bitmap for the PC-8 Character Set

I started by rotating the sample view of the PC-8 characters by 90 degrees and looking at each row, from top to bottom for each character. In this orientation, the font is 7 pixels wide by 5 rows tall. This means I need 5 bytes of data per character in order to represent the character in a bitmap. In each of these bytes, I represent each pixel in its position within the byte.

BYTE 1: 01111100
BYTE 2: 00010010
BYTE 3: 00010001
BYTE 4: 00010010
BYTE 5: 01111100

For example, the first row, or byte 1 in the character “A” can be represented by a decimal value of 124 (111100 binary). The 8th bit would always be zero, since the font is only 7 pixels wide.

I wrote down each of the bytes row by row character by character:

Bitmap for A-Z

Enlarging the Font for Printing

With the font rotated 90 degrees, I have 7 pixels of width defined for each character. The PC-8 printer can support up to 24 characters per line for width.

In order to get the largest output size possible, I could take each pixel in my font and output 3 pixels on the printer. This would stretch my 7 pixel wide font to 21 pixels wide on the printer. This would leave 3 spaces to spare. I could center the banner by having a one or two of these spaces space at the beginning of each printed line.

To keep the aspect ratio for this enlarged font, I would need to expand the height also. Because the line feed spacing is fixed and automatic on this printer, I have to work with what it can do out of the box and the existing spacing. It turns out that expanding by a printed factor of 2 rows for every row in the font was a close match.

Storing the Bitmap for the Character Set

Storing the bitmap of each 5×7 characters for A-Z and 0-9 in memory would take a minimum of 5 * 36 bytes of memory, or 180 bytes of memory in theory.

If I used an array in the PC-8, it would take memory for the array, and then more memory in the program to initialize or put the bitmap values in the array. Basically, this would take more than double or triple the size of the bitmap in memory.

The PC-8 supports the DATA keyword which allows for defining an array of data elements that can be read by the program. It’s much more memory efficient than using an array with initialized values.

You can have as many DATA statements as are needed, providing you have enough RAM available.

An example of encoding the letter “A”:

1 DATA 124,18,17,18,124

However, the DATA command would take a byte and the line number would take two bytes. The entire rest of the DATA string “124,18,17,18,124” would take 16 bytes, as it is stored as a literal string. This means each bitmap character representation would take at least 18 bytes (a few more for 3 digit bitmapped data bytes). This would require a total of at least 666 bytes of memory to store 185 bytes of bitmap data (37 characters total).

To save some memory, I chose to store 2 characters per DATA statement. This saves 3 bytes for each two DATA statement combined, 2 for the line number and 1 for the DATA statement (but, an extra comma is added). An example of encoding the letters “A” and “B” into a single DATA statement:

1 DATA 124,18,17,18,124,65,127,73,73,54

It makes no difference when indexing the data the number of items per line using the READ statement. Performance would be slightly better during runtime, avoiding extra hops over the DATA, line number, and terminator bytes. There are several benefits to include as many items per DATA line as the PC-8 will allow for. By fitting two characters’ encoding per DATA statement, I saved approximately 54 bytes of RAM.

Indexing the Data Array

Because I’m including the letters A-Z and the numbers 0-9 (and potentially punctuation or symbols), I need a simple way to index the data array to pull out the bitmap for each character I am printing on the banner. I made use of the RESTORE command and READ within a loop to start at the beginning of the data array and an adjusted offset based on the ASCII value of each character to be printed.

This isn’t the most efficient way to index the data, and, as the program pulls character bitmaps out toward the end of the array, you really can notice the slight pauses compared to pulling bitmaps out from the beginning of the array.

Once I complete the memory map project, I will be able to modify the program to use the PEEK statement to directly address the data array.

Program Flow

Now that I have the bitmap defined and stored in a data array in the program, I worked out a method to transpose the string input by the user to the printer in banner format. This may seem easy at first, but I’m working with a limited BASIC command set and limited memory.

The inner most part of the program fetches the 5 bytes from the data array for the character being printed. For each byte, the program looks at each bit within the byte to determine whether to print spaces or marks (here I use the character “X” as a mark. You can use anything, such as a “O” or “*”).

The printer has a single method for printing, and it prints a line at a time, using the LPRINT command. This means if I were to print a single character “X”, it would print it then line feed. This automatic line feed after each print command is not controllable. This means that I would need to issue all of the characters for the line to be printed into a single LPRINT command.

I dimensioned a string array for 22 characters and used that string for the LPRINT command.

DIM Z$(0)*22
...
LPRINT Z$(0)

I load one row of pixels at a time (1 byte from the data array) from the bitmap font into a variable and then inspect that byte bit by bit. Thankfully the PC-8 has a bitwise AND function. I use this function to mask 1 bit at a time, to look for a mark or space, and I use the division operator to shift the bit mask from left to right across the byte.

Using the first byte of the letter “A” as an example, every time I see a space (0) I add three spaces to my string variable. Every time I see a mark (1), I add three X’s to my string variable.

In the example below, I take the bitmap for the letter “A” and transpose it in it’s printed form. Note that dashes are used instead of spaces for illustration purposes.

BYTE 1: 01111100
BYTE 2: 00010010
BYTE 3: 00010001
BYTE 4: 00010010
BYTE 5: 01111100 

Printed: 

---XXXXXXXXXXXXXXX------
---XXXXXXXXXXXXXXX------
---------XXX------XXX---
---------XXX------XXX---
---------XXX---------XXX
---------XXX---------XXX
---------XXX------XXX---
---------XXX------XXX---
---XXXXXXXXXXXXXXX------
---XXXXXXXXXXXXXXX------

Because the font is 7 pixels wide, the 8th bit is always zero. To center the printout a bit, I add a leading space to the beginning of my string to print, and start looking at bit 7. This leaves two spaces at the end of the printed line.

As I inspect each bit, I add the 3 marks or 3 spaces to my string to print:

305 Z$(0) = " " : G = 64
310 FOR F = 1 TO 7
320 H = B AND G : IF H = 0 THEN GOTO 340
330 Z$(0) = Z$(0) + "XXX" : GOTO 350
340 Z$(0) = Z$(0) + "   "
350 G = G/2 : NEXT F

The section above is the resulting BASIC statements to build the string variable which is sent to the printer. Z$(0) is the string array sent to the printer. F is my counter to run the loop 7 times (looking at 7 bits total).

H is the result of the bitwise AND operator between G (my mask bit, which starts at the 7th bit, or 64) and B is the byte for the current font row. H will be zero if that bit is off, and non-zero if that bit is on (space and mark). Space is three spaces and mark is three X’s. Since there’s no bitwise shift operator for the PC-8, I simply divide G by two within this loop in order move from the left to right looking at the bits.

After all 7 bits from the current row are used to build the string, I send the string to the printer twice. This gives me the matching height that works well with the stretched width.

400 LPRINT Z$(0) : LPRINT Z$(0)

I repeat this logic block 5 times, one for each row in the current character being printed. I then print 2 spacer lines. This entire logic is then repeated for each character needing to be printed for the entire banner.

With the entire program and bitmap data loaded, I have 314 bytes of RAM available.

Testing the Program

Testing the banner program was fairly easy. I had to come up to speed on the details of some of the commands, array dimensioning on this model, and the intricacies of printing a full 24 character line on the printer.

I tested each letter and number by printing each one out making corrections to my data array as needed. Since this printer does not need ink and the paper is cheap and widely available, making a few mistakes on the real hardware was not a big deal. I didn’t need to use an emulator to save on consumables during testing (as I did with the QSL card program for the PC-2)

The program works well, and it can be expanded to include punctuation if needed. This version includes A-Z, 0-9, and space.

PC-8 Banner Program in Action
Sharp CE-126P with PC-8 Banner Program

Final Thoughts

Getting a hold of a PC-8 and the printer / cassette accessory was fun. After using the PC-8, this hands down is better than the PC-7. I wish I was able to get one of these back in the 80s instead of the PC-7 I had.

Being able to create a banner program with such a small amount of RAM was a satisfying accomplishment. It would have been much more interesting to have been able to read the character set bitmap out from the ROM. But, even without that possibility, fitting the program and it’s bitmap font data in under 1.2 KB was a mission accomplished.

One thing that I have noticed is sometimes when printing (LPRINT command) from the PC-8, an error 8 occurred. Error 8 is an I/O device error. What I have found is this occurs when the internal battery pack in the printer is low, but not low enough for the low battery light to turn on. Perhaps an adjustment is needed for the low battery detection for NiMh batteries versus NiCd. I found that a full recharge is needed when this condition occurs.

I was able to load this program onto a PC-3 and verify it runs without issues. The PC-3 I have is the 4 KB version.

Regarding the various pocket computer models Radio Shack released over the years, it’s interesting that Radio Shack would choose to release models of Pocket Computers often times with less capability than previous models.

For example, the PC-3 had a 4 KB RAM version released. Then the PC-6 had 8 KB, expandable even further. Then they release the PC-7 with only 2 KB and the PC-8 with only 2 KB. Perhaps form factor and price drops were large influences on which Sharp or Casio models they chose to rebadge.

Some of the Radio Shack pocket computers have expandable RAM. It is nearly impossible to find the expansion RAM packs for these pocket computers – with the exception of the PC-2 where expansion modules are easy to find. Therefore, most of the versions you’ll find for sale on eBay and elsewhere will have only the base RAM available, and will likely not contain the RAM expansion.

Banner Program Listing

I’ve included the PC-8 banner program as a WAV file, if you have a PC-3 or PC-8 and the cassette interface:

I’ve also included the “biorythm” program found in the PC-3 printer user’s manual:

I’ve included the banner program listing below. Be sure to ignore the REM statements, I added these to illustrate the various sections of the program flow. If you wish to add punctuation characters to the data array, remove the last zero byte, add the characters and append a zero byte.

REM DATA ARRAY IS A-Z,0-9,SPACE.  LAST BYTE TO PREVENT INDEX ERROR IN LOOP

1 DATA 124,18,17,18,124,65,127,73,73,54
2 DATA 62,65,65,65,34,65,127,65,65,62
3 DATA 127,73,73,73,65,127,9,9,9,1
4 DATA 62,65,65,73,57,127,8,8,8,127
5 DATA 0,65,127,65,0,32,64,65,127,1
6 DATA 127,8,20,34,65,127,64,64,64,64
7 DATA 127,2,12,2,127,127,4,8,16,127
8 DATA 62,65,65,65,62,127,9,9,9,6
9 DATA 62,65,81,33,94,127,9,25,41,70
10 DATA 38,73,73,73,50,1,1,127,1,1
11 DATA 63,64,64,64,63,7,24,96,24,7
12 DATA 127,32,24,32,127,99,20,8,20,99
13 DATA 3,4,120,4,3,97,81,73,69,67
14 DATA 62,81,73,69,62,0,66,127,64,0
15 DATA 66,97,81,73,70,65,73,77,75,49
16 DATA 24,20,18,127,16,39,69,69,69,57
17 DATA 60,74,73,73,48,1,1,121,5,3
18 DATA 54,73,73,73,54,6,73,73,41,30
19 DATA 0,0,0,0,0,0 

REM INIT AND LOOP THROUGH EACH CHAR IN STRING TO PRINT FOR BANNER

195 CLEAR : DIM Z$(0) * 22 : DIM T$(0) * 25
200 INPUT "TEXT:";T(0)$
210 L = LEN T$(0)
220 FOR C = 1 TO L

REM READ TO LOCATION IN DATA ARRAY FOR CURRENT CHAR BITMAP
 
230 RESTORE
240 E$ = MID$(T$(0),C,1)
250 K = ASC E$ : A = K

251 IF K < 48 THEN LET A = A + 4
252 IF K > 47 THEN LET A = A - 22 
253 IF K > 64 THEN LET A = A - 43
260 FOR D = 0 TO A * 5 
270 READ B
290 NEXT D

REM READ EACH ROW IN BITMAP FOR THIS CHAR

300 FOR D = 1 TO 5
305 Z$(0) = " " : G = 64
310 FOR F = 1 TO 7
320 H = B AND G : IF H = 0 THEN GOTO 340
330 Z$(0) = Z$(0) + "XXX" : GOTO 350
340 Z$(0) = Z$(0) + "   "
350 G = G / 2 : NEXT F

REM PRINT TO PRINTER

400 LPRINT Z$(0) : LPRINT Z$(0)
410 READ B : NEXT D
420 LPRINT "" : LPRINT "" : NEXT C

Leave a Reply