Casio PB-1000 Personal Computer
This is my first post about a pocket computer, or “personal computer” outside of the Radio Shack product line. My exposure to pocket computers was historically limited to what I would see in the Radio Shack catalogs. I have no idea where else, if anywhere, pocket computers would have been sold in the United States and if there were catalogs or advertisements, I had never seen them.
Over the years browsing eBay for parts and related vintage pocket computers and calculators, I would on occasion see the Casio PB-1000. They did not pop up nearly as often as a PC-2, PC-6, or some of the other more common Radio Shack models, but when they did, I would see heavily used units either not working or in rough shape. I would rarely see a unit in good condition for a price anywhere near what the Radio Shack models would be offered at.
This is a fairly long post, as I have spent a few months with this model and documented various parts as I went along. The second half of this post is geared toward those who have an interest in or little to no experience in assembly programming.
The Casio PB-1000
The Casio PB-1000 was released in 1986, but not available in the United States until 1987. I am considering the Casio PB-1000 as a pocket computer because it folds up and it can fit in your pocket. Interestingly, on the face of the top half, the model is labelled as a “Personal Computer”.
In a previous post about the PC-6 pocket computer (which is a rebadged Casio FX-790P), I commented that I had a preference to the Sharp line of pocket computers. This comparison was heavily based on my experience with the PC-2, which was a rebadged Sharp PC-1500. My reference up until recently was limited to the pocket computers Radio Shack had sold.
The PC-2 was by far the fastest, biggest, and most capable pocket computer Radio Shack ever sold. It can be programmed in BASIC, it can run assembly programs and subroutines, and it can be connected to parallel and serial devices such as printers, modems, terminals, TNCs and more. Radio Shack did not release any equivalent Casio models on par with the PC-2.
I should mention the Casio PB-700 here, because it is roughly the same size as the PC-2, was released in 1983 (around the same time as the PC-2), and had a plotter and cassette interface as accessories. It did not however have any RS-232 only parallel port capability.
My take on the Casio models changed a bit after experimenting with the PC-6. The PC-6 had a printer, cassette interface, and even a memory expansion pack bumping the total memory up to 16 KB. It was a solid device and was sold for more years than any other Radio Shack pocket computer model.
The PB-1000
Compared to Radio Shack pocket computers, I have far less information on the PB-1000 in terms of selling price, marketing, or real world applications in history.
It is a foldable design, like the PC-6, but bigger – much bigger. When folded, it is about the same size as a PC-2.
The PC-6 (rebadged Casio FX-790P) was also released by Radio Shack in 1986 and had 8 KB of RAM, the same as the PB-1000.
However, only 4 KB of RAM was available to the user with the PB-1000. This is a very low amount of memory given the capabilities this pocket computer has. It does not leave much room for user programs. The PC-2 was similarly hampered with only 2.5 KB of memory available to the user out of the box. I suppose memory was quite expensive at this time, otherwise Casio (and Sharp) would have included more memory out of the box.
The PB-1000 memory can be expanded with a 32 KB RAM expansion module (RP-32) for a total of 40 KB of RAM. I would argue that a PB-1000 must have the 32 KB expansion to be useful.
The PB-1000 is similar to the PC-6 in that they are both foldable or “clamshell” designs. They share a similar hinge design and also can suffer the same fate – cracked hinges. However, on the PB-1000 the “release” button addresses the broken clip issue most common on the PC-6 and similar models.
The PB-1000 is covered with an aluminum shell over a solid plastic housing. A foldable model with either solid plastic or aluminum for a casing is a very durable design. This protects the keys and screen from damage. These can easily be put into a bag or large pocket without worrying about breaking or bending it.
The PB-1000 runs on 3 “AA” batteries, as opposed to button cell batteries. The “AA” batteries can be changed quickly, while changing button cell batteries requires a small screwdriver and a few minutes. The only other pocket computer I have which can use “AA” batteries is the PC-2 which ran on four “AA” batteries. As I have mentioned in my PC-2 post, having a device running on “AA”, “AAA” batteries or similar is better than the button cell batteries. Of course the size of the pocket computer largely determines what types of batteries that can fit inside the unit.
The odd placement of the 3 “AA” batteries (on the top half of the clamshell design) makes it impossible to fold the device open half way on a desk while using it (like a laptop). It has to either be supported or fully open laying flat on a table.
The battery cover seems quite fragile and a bit of a challenge to remove. I can see how so many of these have broken tabs or become unable to stay securely attached to the body.
Touch Screen LCD
The PB-1000 has a 32-column 4-line dot addressable display. For comparison, the TRS-80 Model 100 had a 40 character 8-line LCD display. This is the first pocket computer I have which has a multi-line display. This makes for some interesting uses for graphics, games, and a terminal.
The PB-1000 has an innovative touch screen laid out in a 4×4 grid, providing for up to 16 “touch keys” on the display. Most of the built-in menu system utilizes the bottom 4 spots on the grid for touch keys. This might have been the first pocket computer with a touch screen, since this model was released back in 1986. Since then, there have been PDAs and other devices with touch screens.
The touch screen combined with the multi-line and graphics (dot addressable) display provides many opportunities not available on any previous pocket computers I have owned.
Keyboard
The keys on the bottom half of the PB-1000 are plastic, not rubber like on other Casio pocket computers (PC-5, PC-6). This is far more durable than rubber keys. The keyboard layout is impressive, with a large space bar and well thought out placement of the keys.
The alpha keys are a standard “qwerty” layout, with lots of space between them.
Commonly needed keys for programming (especially in assembly), such as the quote, comma, dollar sign and others, are dedicated and placed at the top so that most often no shift or function keypresses are necessary. This was actually quite a luxury when I first started using it.
The top half has a single row of membrane style keys, again similar to the PC-5 and PC-6. The menu (or home key as I call it) and navigation arrows are placed here.
It is worth noting that this pocket computer has a keyboard layout which allows normal “calculator” operations without extra effort of pressing function keys or having the basic operations hidden as secondary keyboard functions. You can turn it on and immediately use it as a calculator using the dedicated numeric and operation keys on the right side.
I do not often mention how easy a model is to use as a calculator, but lately I have seen models from other brands (Panasonic and HP for example), which have keyboard layouts and operation making it a challenge to pick up and use as a plain simple calculator.
Clock and Buzzer
The PB-1000 has a real-time clock similar to the PC-2. The clock can be used to wake up the computer and execute a program.
It also has a buzzer, but with the built in BASIC command for BEEP, it has a high and low tone only, like the PC-5 and PC-6, similar to many other pocket computer models.
Finding a PB-1000
As with many of the vintage items I have acquired from eBay, I usually find them with saved searches. I get an alert when that search finds something new that matches my search parameters. This often takes anywhere from a few weeks to a year or in some cases a few years for a hit to come back (like with the PC-6 printer cable or the PC-5).
After awhile, you get a feel for what an item will typically be offered for and eventually sell at. This information is useful for determining if an offer is a steal, a good deal, fair, or too high of a price.
Many listings for the PB-1000 showed heavily worn units with scratches and dents. Often times the battery cover would be missing, there would be heavy corrosion damage in the battery compartment and elsewhere, and broken hinges.
I recently found a PB-1000 in very good condition on the outside, and described as a “parts only” unit. Many times sellers will describe an item as “parts only” if they cannot test it or if they are unsure how operational it really is – this avoids returns and disputes.
The seller added in the description that the unit does turn on and beeps, but it had a bad screen. At only $30, I took a chance. Up until this point, I had seen units for sale heavily beat up or damaged for much more than this unit was offered at.
Easy Repair
When the unit arrived, I put three “AA” batteries in and gave it a go. Nothing appeared on the LCD and I heard no beeps from key presses. I tried the reset button a few times as well.
In the pictures for the description, there was evidence of battery leakage on the edge of the aluminum shell near the battery compartment. I had assumed when purchasing this unit that I would at worst get something I could use for parts, since this was great condition otherwise. At best I would have a simple repair or cleaning.
I removed the aluminum shell near the battery compartment and examined the extent of the damage from the leaking batteries.
There was quite a bit of corrosion on the negative battery terminal (the side with the spring) and it was very loose. I used a solution of citric acid (to neutralize the leakage from the batteries which is a base) and a swab. I touched up the battery connectors, the entire battery compartment, and much of the copper shielding behind the screen.
I have also had success cleaning up alkaline battery corrosion with distilled white vinegar.
Thankfully there is a plastic cover underneath the copper shield protecting the LCD. I secured the negative battery terminal back where it snaps in place and put the batteries back in.
Success
The PB-1000 showed signs of life! I hit the reset button and the menu came right up. Even though this unit did not have the 32 KB expansion pack installed, it is in great condition. I usually check to see if these pocket computers have the memory expansion inside. Often the sellers have no idea if they are there. Often these by themselves are valuable.
Expansion Options
The pocket computer models that have expansion capabilities are more interesting to experiment with because it allows for saving and loading programs and connection to other devices.
My first pocket computer was the PC-7, which had no expansion options whatsoever. It was a real drawback to this model.
The expansion port on the PB-1000 is on the right side, and is a 30 pin proprietary connector. Many if not all of the I/O pins here are unbuffered or directly connected to the CPU.
The Casio PB-1000 had four peripherals, the FA-7, the MD-100, the FP-100, and the FP-40.
The FP-100 is a full page four color plotter, similar to other larger plotters of the time. It used the same pens as the PC-2 and many other models. The issues with most of the plotter printers is they all have cracked pinion gears, and many have broken anti-backlash gears and they need pens, which are expensive these days. I did find an FP-100 and plan to update this post once I have more time to experiment with it.
The FP-40 is a thermal printer. The FP-40 requires an interface pack which varies depending on the pocket computer being connected to it. One of these interface packs allows for connection to a standard parallel printer port via a Centronics connector. Most of the FP-40s on auction sites come without any of these connection packs.
The FA-7 is the “interface box” which has a cassette interface, an RS-232 port, and a Centronics parallel port.
The MD-100 is an interface box like the FA-7, but with a 3.5” floppy drive. It also has the RS-232 port and Centronics parallel port.
Having a Centronics parallel port allows for connecting a standard line printer, which are easier to find and repair these days. In fact, you can even use a thermal POS printer, which use thermal paper rolls which are very inexpensive.
This is possibly the first pocket computer that had a floppy disk option. Sharp released the CE-140F in 1987 which used proprietary 2” floppies. The MD-100 on the other hand worked with standard 3.5” double density floppy disks and was released a year earlier.
Each disk, when formatted by the PB-1000 can hold approximately 320KB.
The MD-100 did not have a cassette interface like the FA-7 did. I suppose one is not needed if you can save to disk, which is far faster than tape.
It is worth noting that the PB-1000 plugs directly into the FA-7 or the MD-100 like many other pocket computers as a docking station. This is a bit easier to manage compared to the PC-5 and PC-6 which required a hard to find interface cable.
Finding an MD-100
Finding an MD-100 is probably as difficult as finding the PC-5/PC-6 printer cable, the memory expansion for the PC-4, a PC-5, or an original box for the PC-2. A saved search alerted me to a new item listing, another PB-1000, this time with an MD-100.
I had no idea how much the MD-100 typically sells for, and a search for previous auctions on eBay came up empty, which did not help me determine when the last one became available nor how much they are typically sold for.
There was one other active listing with the MD-100 including every accessory except the FA-7. However, working plotter pens are difficult to find, and the only source I found selling modern replacements was expensive and no longer offers them. The FP-100 is not a viable printer for actual use due to the inability to replace the pens. The FP-40 would be more practical due to it being a thermal printer requiring no pens or ribbons.
This existing listing was quite expensive and was not available to be shipped to the United States.
The offering price for this new listing with the MD-100 was reasonable with a “buy it now” option. If this went to auction, it would most definitely have sold for much more if the seller were patient. The selling price was not much more than the price as other listings with just the PB-1000 in excellent condition.
There was no description about it working or not, no pictures showing it powered up. The listing indicated returns not accepted. I took a chance and clicked “Buy it Now”. I figured I could fix the floppy drive and/or the pocket computer if needed, or at least make an attempt to do so.
Second Unit Working
When the second PB-1000 arrived with the MD-100, it was in very good condition. There was no sign of battery leakage in either the PB-1000 or the MD-100.
I put three “AA” batteries in the computer and nothing happened. I tried the reset button, nothing on the display and no key beeps. This unit, unlike the previous one I purchased was very clean. There were no signs of corrosion from leaking batteries, dents, or cracked hinges.
I had a hunch that this might have the memory expansion inside and the way those are seated could cause issues over time.
I took the bottom cover off and to my surprise, this unit had the RP-32 memory expansion installed. This memory expansion (similar to the PC-6) uses a pressure pad for the copper contacts to seat onto the copper pads on the PCB. My theory was, over time, these contacts could oxidize and prevent a good contact. Depending on which contact(s) are bad, it could prevent startup or cause lockups during operation.
I removed the memory expansion and the unit powered up! I then cleaned the pads on the PCB where the expansion unit makes contact, and cleaned the pressure pad on the memory expansion with IPA.
I put the memory expansion back in, powered it up and it worked! I now have a second unit, also in great condition, with a 32 KB memory expansion.
It should be noted that the RP-32 memory expansion module is physically much larger in size than the memory expansion module for the PC-6 (the RP-8). Before diving into the ecosystem of the PB-1000, I was unsure if the memory expansion modules were compatible between the two. It is often difficult to gauge whether items are the same size by pictures alone.
A Working MD-100
Now that I was able to get the PB-1000 working, the next step was to see if the MD-100 worked. The MD-100 disk drive could possibly stop working over time if the belt in the drive breaks. These are easy to fix. There was no sign of battery leakage so the odds of this working were good.
The MD-100 does not have an internal NiCd battery pack like many other pocket computer peripherals did. This means that the likelihood of finding a working unit is good. Peripherals which had an internal battery most likely leaked by now, and when this happens, it can cause minor damage or major damage depending on age and storage conditions. The MD-100 runs off six “AA” size batteries or an AC adapter.
I put six “AA” batteries in the MD-100 and connected the PB-1000. I put in a floppy disk and in BASIC I typed FORMAT. The MD-100 successfully formatted the floppy disk. As luck would have it, the MD-100 appeared to work!
Condition Notes of Interest
With any vintage computer, each has its own common points of failure or wear over time. These devices are well past their intended lifespan and the more time elapses, the more likely components will eventually fail. It appears the PB-1000 was fairly durable and well designed. When searching out for a Casio PB-1000, some of the things I looked for were:
- Battery cover was present
- No LCD damage or leakage
- Little or no corrosion in the battery compartment, little to no signs of corrosion on the aluminum case
- No broken hinges
- No excessive wear on the case, dents, gouges, etc.
- Hinge unlock button is present
The user guide and command reference books that came with the PB-1000 are essential, in my opinion. The MD-100 I found had come with both of these, and they are still available online. For convenience, I have posted copies of each below:
A technical reference manual was also published:
The technical reference manual expands on various sections of the architecture such as disk format, assembly examples, screen updates, and more. Each section of this manual details a particular interface, peripheral or internal feature. For example, the manual includes assembly examples for disk access, screen copying, printer port examples, and more.
Unlike a PC-6, PC-4, or PC-8, finding one of these new in box is highly unlikely, and based on my experience, finding one in good condition or better is difficult.
One thing that I noticed which might be confusing to some is the LCD can appear to have dots around the display and/or a line down the middle of the screen.
The dots are part of the membrane that detects touch.
The line down the center of the screen is most noticeable on almost all listings.
It might appear as a defect or damage to the screen, but this is not the case at all. Depending on the angle, lighting, and contrast, the line appears lighter, darker, or is not even visible.
It would appear this line is related to the LCD glass interacting with polarizer and perhaps the touch membrane. If you notice this in pictures, it is not an issue with the screen. Looking directly at the screen perpendicular to the LCD plane, this line is not visible.
Capabilities of the PB-1000
After finding a few units in great condition and getting them working, I was able to explore this model and experiment with what it can do. This model had a complete array of peripherals available, plenty of memory (with the RP-32 expansion pack), and very good documentation available. This documentation was immediately available, not years later like with the PC-1500. For example, a complete assembly programming reference was in the box with the pocket computer.
User Memory
The user memory organization for this Casio model is unlike any of the previous Radio Shack pocket computers I have. The Sharp models have a single shared space for variables and one program. The Casio models also share variable space and program space, but program space is divided into 10 separate spaces (P0 – P9).
The PB-1000 has more of a file structure organization (like the Model 100) where BASIC programs, assembly programs, sequential files (ASCII text files), and data bank files are stored in directories. File names follow the familiar DOS 8.3 naming format, with an additional file type specifier. The file types are “B” for BASIC files, “S” for ASCII / sequential files, “M” for assembly executables.
The home screen allows you to use the arrow keys on the top half (the membrane keys) to scroll around the files and open them.
To determine how much memory is available, the SYSTEM command in BASIC will report this.
Free is “text” storage, V is numeric variable storage, and $ is string variable storage. The line above the free space reporting is the angle and CLEAR setting – character area size, machine language area size, and system work area size.
As with all other pocket computers, memory is divided up between variable space and program space. Many models allow variable space to be resized to fit the needs of a particular application or purpose such as this model. Note that the PB-1000 has dedicated space for running assembly programs, more on this later.
BASIC Capabilities
This Casio model has a bit more BASIC commands available than the previous Casio models I have experience with. While there are no bitwise operators, there are PEEK and POKE commands for reading and writing to memory directly. The user guide mentions this pocket computer’s BASIC is C61 which is based on the Japan Industrial Standard (JIS) BASIC.
There is direct support for the disk operations, reading and writing files, RS-232 operations, printer output, string manipulation and more.
Variable names can be multiple characters, upper or lower case, including numbers. This makes for easier readability when writing programs, but consequently this can use much more of the limited program space available (unless you have the RP-32 memory expansion installed).
With the multiline display, editing programs becomes a little easier. There is also a search option to search for keywords within a program.
There are quite a few interesting commands added to this model for screen effects. For example, there is a command to invert the display when printing characters or strings (REV). There is a command to define custom character bitmaps using the DEFCHR$ command. Up to 16 spots in the character map can be custom defined and printed to the screen. There are commands to draw points and lines on the LCD as well (DRAW, DRAWC).
There are also commands to load and save memory contents directly to and from a file or RS-232 port.
Lastly, assembly programs (or subroutines) can be invoked directly from BASIC if needed. This allows for creation of functionality BASIC does not support or for faster more efficient subroutines if needed.
Four Color Plotter – FP-100
Although I do have the plotter for this model, I have not used it much with the PB-1000. There are BASIC commands for plotting lines, changing pen color, and much more. This is similar to the PC-2 in that respect. This would be useful for graphing output of functions for example, with minimal effort.
Unlike the PC-2 plotter and the PB-700/PB-770, the FP-100 is a full page plotter. The plotter can use smaller paper and has the ability for the paper width to be adjusted.
The pen holder has color markings to indicate which pen color is loaded into each position. The loading process is very simple compared to the smaller Alps plotters which have the horizontal pen position.
The plotter uses an Alps mechanism but the pens are vertically oriented and are both rotated and actuated (pressed down onto the paper) by a single motor and gear mechanism.
The large gear on the upper right when rotated counter-clockwise has a groove and stop which pulls the pen actuator toward the paper. When rotated the opposite direction (clockwise), it lifts the pen and then if continued to rotate clockwise, will change pens.
The paper feed is controlled by another stepper motor with an anti-backlash gear.
The carriage is controlled by another stepper motor with a switch to sense when it has returned to the left side.
Note that the pinion gears are nylon and are likely to crack over time like with the PC-2 and those smaller Alps mechanisms. Since this plotter is about 5 years or so newer, its likely to happen in the not too distant future.
The replacement pens I tried first had caused the pen rotation to jam. The replacement pens have a plastic body whereas the original pens have a metal body. The jam occurred when the top of the pen in the back of the holder (the pen opposite of the frontward selected pen) would hit the holding arm in the back as the holder rotated. It did not occur every time, but frequently enough where the pen position was altered during printing, rendering the print cycle useless.
I suspect the plastic-on-plastic friction was just enough to cause a jam, and, the shape of the top of the pen was not exactly the same as the originals.
I have yet to find a good way to refill the original pens. There were two versions of these pens, one had a metal tip for the ball bearing, and other versions had a plastic tip for the ball bearing. Needless to say, the plastic tip is easily damaged, rendering the pen useless for refills.
Even though I have enough working pens of the original type, I do not have all four colors. I plan to experiment a bit with refilling these, and if I find such a method, I will update this post.
All is not lost however, since the MD-100 has a Centronics parallel port, and the FP-100 is connected via Centronics port, you can use any compatible line printer with the same port. Unlike many other pocket computers, this one is not limited to only the plotter for printing.
Assembly Capability
The PB-1000 can not only run assembly programs (like the PC-2), it also has a built in assembler. This is not the assembly simulation that the PC-6 offered, but real processor level assembly or machine code execution. This means you have access to some of the hardware directly.
Having the assembler built-in provides a convenient way to write programs in assembly (with comments!) directly on the PB-1000, assemble and then and run them without the need for external tools or a PC.
The assembler outputs errors to a file with line numbers, which is extremely useful. Having support for comments is another helpful feature.
There is a “list?” prompt when assembling a source file, and if you choose “yes”, the following is output to the attached printer:
In this case I am using an inexpensive Epson POS thermal receipt printer (via the parallel port). The printout includes the line numbers from the source program, the corresponding absolute memory addresses, the opcodes and the source. This is really helpful when debugging with the monitor, especially variables, program labels, stack pointers, etc.
The PB-1000 even has a monitor feature where memory can be inspected and modified. For example, the monitor has a hexadecimal display output, allows for direct memory editing, and even bank switching.
Several games have been written for the PB-1000 in assembly and show off some of the capabilities of this pocket computer for specialized tasks.
Assembly programs can be run either from the main menu directly, or, from within a BASIC program, allowing for programs mixed with BASIC and assembly.
Memory usage becomes an important subject to master here, because with assembly programs, if you write the program on the PB-1000, that is done via the text editor, and that file takes space (the S file). Then, when the program is assembled, that file takes space (the EXE). Then, when the program is run, it is copied from the memory file area to the dedicated assembly execution space, the machine language area. It should be noted that there is a maximum size of 4KB for the machine language area. There must be enough space allocated to the machine language area using the CLEAR statement before an assembly program can be executed.
Programming in BASIC takes far less memory in the sense that the source file (the B file) is tokenized – the BASIC editor works directly on the B file, no separate source file is needed. When executing, it can be done directly as BASIC is interpreted at runtime. Of course the tradeoff is execution speed (in this case smaller program size but execution time is slower). Further, BASIC is limited to the commands and functions available.
Of course, with the MD-100, you can assemble your programs and move the source files to disk, leaving just the EXE files on the PB-1000 to save memory. In fact, you can even leave the EXE files on the disk as well, loading them directly from there for execution.
Parallel Port
Having either the FA-7 or MD-100 provides a parallel port that can be used to connect the PB-1000 to a standard parallel printer, such as a full page line printer, a POS printer, and other similar devices.
The parallel port is a Centronics port, unlike on other devices which are usually 25-pin connectors. I had two standard parallel port cables, and simply used a gender changer to connect the 25-pin sides of the cable together, forming a Centronics to Centronics cable.
I connected the parallel port to an Epson POS printer and used LLIST in BASIC to test the output to the printer and it worked as expected.
All lines written to the printer by default have a carriage return and line feed at the end, so automatic line feed is not required to be enabled on the printer, if it has such an option. In this case, the Epson has this as an option and I left it disabled.
Note that in BASIC, adding a semicolon at the end of an LPRINT statement suppresses the carriage return and line feed being sent to the printer.
RS-232 Port
In addition to the parallel port provided by either the FA-7 or MD-100, a standard RS-232 port is available for connection to serial devices such as printers, modems, TNCs, and other devices. You can also send and receive files with either another PB-1000 or a PC. The maximum speed supported is 9600 baud.
As a test to transfer programs from a PC to the PB-1000, I downloaded a version of Tetris, which consisted of a few BASIC programs and assembly source files. I had the source files on a PC and these needed to be transferred to the PB-1000 and using the RS-232 port to transfer them was possible.
When sending files from the PC to the PB-1000 at 9600 baud, I repeatedly received an FR error (Framing Error). I tried RTS/CTS and XON/XOFF flow control and neither solved the issue. No such issues occurred at 300 baud, and when sending from the PB-1000 to the PC, no issues occurred at 9600 baud.
The user guide mentions that “A parameter setting of no parity, data length 7, 1 stop bit cannot be used with a baud rate of 9600.” Note that I tried 9600, 8 bits, 1 stop bit, and I had framing errors. I am not certain which parameters would actually allow a reliable 9600 baud rate transfer.
The fastest reliable transfer speed I was able to get from the PC to the PB-1000 was at 4800 baud using a 10ms line delay. I suspect the PB-1000 gets overloaded at the software level, even to the point that it cannot toggle the hardware flow control.
I looked at the RS-232 levels on the data line from the PC to the PB-1000 on a scope and they appeared normal. I did not see the level shifting issues I saw with the Model 100 and a TNC for example.
Unlike the PC-2, the PB-1000 does not have a built-in terminal program, and the architecture of the PB-1000 does not allow for peripherals to add their own ROM into the memory map like the PC-2 does. Therefore, when plugging in the MD-100 for example, a terminal program does not pop into the ROM memory space and provide additional functionality.
However, it should be possible to write a terminal program in assembly if needed. Of course basic terminal interactions could be done with BASIC as well. If I have the time in the future, I might dabble with a terminal program for use with either a GPS or TNC.
Working with Files – Tetris Demo
I used the menu interface on the PB-1000 to load the Tetris source files from RS-232, no flow control, and a speed of 300 baud. I used CoolTerm to send each file from the PC to the PB-1000. Unlike with the PC-2, the PB-1000 did not indicate that it received a file (the menu did not reappear after the file was sent). There does not appear to be any termination sequence for the ASCII files that I could find. Pressing the BRK key after the file was sent seemed to work.
The Tetris game was provided as multiple files, some in BASIC and others in assembly language. All of the files themselves were text, or ASCII files. These not only need to be transferred to the PB-1000 from the PC, but they need to be converted or compiled to their actual format before the game will work.
The ASCII files sent from the PC to the PB-1000 are stored into the “DATA” working area in the PB-1000, and denoted with a file type of “S” with no name.
The BASIC files sent from the PC are ASCII and need to be tokenized and saved as a file type “B” in the PB-1000. I saved the working “S” file with a name using the menu option “name” (in this example, “TETRIS”). This took my “working area” file I transferred from the PC and saved it to a dedicated or actual file in the PB-1000. It is noted in the user guide to save “working” files as soon as possible, since they can easily be overwritten or inadvertently altered.
Now that I had an actual data file (with type “S”), the file was ASCII. I needed a way to convert this to tokenized BASIC before it would be executable. I then used the BASIC entry point from the menu, used the LOAD command to load that “S” file, then the SAVE command (specifying the correct target name, for example “TETRIS”.
LOAD "TETRIS"
SAVE "TETRIS"
The TETRIS “S” file was now a type of “B”, or tokenized BASIC. This means that it can now be executed directly from the menu. However, there were 3 more assembly files for this game that needed to be transferred and compiled.
For the 3 assembly programs, I loaded those the same way and then used the “asmbl” menu key and renamed the output when needed. I then ran the Tetris game, and it worked!
Note: Assembly runtime space must be allocated before running an assembly subroutine, via the CLEAR command.
Because of all the effort needed to load these 5 ASCII files and compile them, I saved all 5 to a 3.5″ floppy for easier loading next time. I included the source below, which I obtained here:
Assembly Programming
The PB-1000 uses the HD61700 processor. The command reference manual lists out all of the opcodes (or command sequences) that the HD61700 supports (there are some undocumented opcodes not included in this manual). If you are not familiar with assembly level programming, this section might help explain a bit of the mystery of assembly language. In fact, if you want to learn assembly, I would argue the easiest way to get started these days is with one of these devices or one that simulates assembly, such as the PC-6. These devices are great for learning and experimenting with, and simple enough that you can get started with something that is basic.
One might question, why would assembly language be useful? Assembly would be useful to perform a task that otherwise cannot be done using BASIC. Or, if a task can be done faster in assembly due to optimization. One example of a task that cannot be done in BASIC is to dump the system ROM to the serial port.
Dumping the System ROM to the Serial Port
I attempted a simple BASIC program to send the entire system ROM to the serial port (address space &H8000 to &HFFFF). In BASIC, such a program might look like this:
10 PARA$="7,N,8,1,N,N,N,N,N"
20 OPEN "COM0:"+PARA$ FOR OUTPUT AS #1
30 FOR A = &H8000 to &HFFFF
40 B = PEEK(A)
50 PRINT #1,B;
60 NEXT A
However, the system ROM shares the same address space as the RP-32 memory expansion, and in order to dump the ROM, I need to switch the bank from 1 (RAM) to 0 (ROM). There is no command in BASIC on the PB-1000 for bank switching.
I wrote an assembly program to switch to bank 0 (ROM):
;ASSEMBLER PROG. "BANK0"
ORG &H7000 ;load at beginning of assembly program area
START &H7000 ;start execution at first opcode assembled
;switch to ROM bank
GST UA,$0 ;load UA reg to reg 0
AN $0,&HCF ;set last two high order bits to 0
PST UA,$0 ;write reg 0 back to UA reg to switch bank to ROM
RTN
I then invoked this assembly program from my BASIC program:
5 CALL "BANK0.EXE"
10 PARA$="7,N,8,1,N,N,N,N,N"
20 OPEN "COM0:"+PARA$ FOR OUTPUT AS #1
30 FOR A = &H8000 to &HFFFF
40 B = PEEK(A)
50 PRINT #1,B;
60 NEXT A
Note: Assembly runtime space must be allocated before running an assembly subroutine, via the CLEAR command.
However, I received an SN error on line 5 which is an erroneous command or statement format. I suspect the issue is some or all of the system work area is in RAM when the RP-32 is installed, and when running a program in BASIC, I cannot switch banks without impacting the runtime of the BASIC program.
I even attempted to use the monitor (MON), switch to bank 0, then run the BASIC program, but it always switched back to bank 1 (RAM).
Lastly, I attempted to call some of the subroutines in the system ROM directly, i.e. CALL &HFFE5 (the BEEP subroutine) and this worked. The CALL routine must be switching banks to the system ROM before transferring execution, at least in the case when an address is specified.
I suspect if you have the RP-32 RAM expansion installed, the RAM file storage or other system resources are in the memory space of the RP-32. Switching banks in an assembly subroutine is probably not an issue by itself, but perhaps BASIC runtime cannot continue if its file is stored in the RP-32.
Out of curiosity, I switched to a PB-1000 without the RP-32 installed and ran the same test as above. I did not see the SN error on line 5, but I was still unable to read from the system ROM from within BASIC. It appeared the bank switch was undone or reset back to 1 when returning to the BASIC runtime.
As we can see in the output above, bank 1 must have been switched back in, as the data indicates nothing is on the bus at this address space (no RP-32 is present).
Assembly Program Needed
Because of the limitations on the PB-1000 BASIC for accessing the system ROM, an assembly program is needed to accomplish the task of dumping the system ROM to the serial port. Depending on the needs of an assembly program, the system ROM subroutines can be used if they are helpful, rather than writing the entire program functionality yourself.
The system ROM has documented RS-232 subroutines to initialize (and open) the communication port and send a byte. These and other subroutines are documented in the technical reference manual.
This subroutine references RS1 – RS4 needing to be set before using:
The RSOPN subroutine should be called only after initializing the RS1 through RS4 bytes. For the most basic form of connection to the PC, 9600 baud, 8,N,1 with no flow control, RS1 should be set to &H16, RS2 should be set to 0, RS3 should be set to 0. In addition, main registers 0, 11, and 13 (hex) need to be set, as these are three parameters to this subroutine.
Then, a send byte subroutine can be used:
“DUMPROM” Assembly Program
With the information from the technical reference manual, I was able to create a short assembly program that will initialize the RS-232 port and send all bytes for the system ROM out to the RS-232 port. I had the PB-1000 connected to a PC running CoolTerm and set CoolTerm to capture the received data to a file.
;ASSEMBLER PROG. "DUMPROM"
ORG &H7000 ;load at beginning of assembly program area
START &H7000 ;start execution at first opcode assembled
NTX0: EQU &HBF60 ;address of send byte to com port subroutine
RSOPN: EQU &HBE11 ;RS-232 open subroutine
PRE IX,&H6BFC ;initialize RS-232 starting at RS1
LD $0,0 ;zero offset
LD $1,16 ;9600,1,8,N
STI $1,(IX+0) ;RS1
LD $1,0 ;next 2 params
STI $1,(IX+0) ;RS2
ST $1,(IX+0) ;RS3
LD $0,&H03 ;Param 1 - Send/receive open
LD $11,16 ;Param 2 - 9600,1,8,N
LD $13,0 ;Param 3
CAL RSOPN
;switch to system ROM bank
GST UA,$0 ;load UA reg to reg 0
AN $0,&HCF ;set last two high order bits to 0
PST UA,$0 ;write reg 0 back to UA reg to switch bank to ROM
;dump system ROM to RS-232
PRE IX,&H8000 ;set index reg x to start address of ROM
LDW $4,&H0000 ;load 0 to main reg 4 and 5 (for 16 bit compare)
LOOP: LDI $0,(IX+0) ;load mem contents pointed to by reg x to main reg 0
CAL NTX0 ;call send byte routine in ROM
GRE IX,$2 ;load value of index reg x to main reg 2
SBW $2,$4 ;subtract 0 from reg 2 and store to reg 2
JR NZ,LOOP ;if subtract result is not zero, stay in loop
RTN ;return to operating system
Built-In Assembler
Now that I had my first assembly program written up, I need to assemble it before the PB-1000 can execute it. Assembly is the process which converts the mnemonics, labels, and parameters in an easy to read format and converts it to opcodes and parameters that the processor can read. This is similar to tokenization in BASIC, except that it is creating actual processor level commands which the processor will execute directly, as opposed to a runtime interpreter for tokenized BASIC.
I mentioned earlier that the PB-1000 has a built-in assembler. Having an on-board or built-in assembler on a pocket computer is very convenient. As I noted earlier, the assembler produces an error file, handles comments, labels, and constants.
In order to become familiar with the assembler in the PB-1000, I entered my program above and assembled it using the menus. Rather than type in the program directly into the text editor on the PB-1000, I entered the program in notepad++ on the PC and transferred it to the PB-1000 via the serial port. Note that the assembler does not like tab characters, only spaces for indentation (to make the program a bit easier to read).
After assembly, I was able to run the program from the menu, and I saw the output from the system ROM captured by CoolTerm:
In this example, I was able to push the contents of the system ROM to a PC and save it. I would not have been able to do this in BASIC. It was quite possible and not too difficult to accomplish this in assembly.
Performance and Compilation
In addition to being able to accomplish more with assembly programs than with BASIC programs (due to the limited functions built into BASIC on a given device), assembly programs could be a performance gain over BASIC programs.
You might have heard the saying “assembly programs run faster” than BASIC programs, or faster than programs written in (insert your favourite programming language here). While in many cases this might be true, it is not always the case. Processor design and the (insert your favourite programming language here) compiler play the biggest role in speed of program execution, whether that be BASIC, C, or assembly. We have to remember that programs written in most languages need to be compiled before they will run.
Compilation is the process which takes your (easy to read) source and translates it to run on a given processor by transforming your code to assembly (or in the case of .NET and Java, an intermediate instruction language). Compilers can be very efficient or “smart” and produce fast executable assembly code which takes advantage of the specific processor opcodes available.
BASIC on the other hand, is not compiled on these pocket computers, it is tokenized. Commands are translated into either one or two byte tokens which are then used to branch to assembly subroutines built into ROM. Those subroutines are assembly level routines, or, processor opcodes, so each one can be highly efficient.
However, the inefficiency of BASIC comes in the form of extra loading or fetch times, excessive jumps and returns, parameter exchanging, lack of specific commands specialized for a particular operation, and other overhead which is unavoidable.
Processor Cycles
When measuring performance of a system or program, since the processor runs at a fixed clock speed, it can execute only so many cycles per second.
On the HD61700, some opcodes can be executed in one cycle, others take six cycles or more for example. There is not always a one-to-one relationship between an opcode and cycles needed to execute it.
Before a command can be executed, it has to be fetched from memory. Then, there may be other fetches needed for arguments and/or data in order for the operation to commence. There may be additional cycles to complete the operation inside the CPU.
Not only does the CPU need cycles to execute an opcode, but if we are interacting with a serial port, LCD, or other external device, additional cycles might be needed and bus design factors in.
Architecture and Optimized Opcodes
In short, the processor and bus architecture most influence performance capability. For example, the HD61700 has several opcodes designed for moving memory contents in blocks and searching memory contents. There is a load and increment (LDI) and a store and increment (STI). In a single opcode, it loads/stores and increments the pointer.
If the processor did not have this opcode, it might need two opcodes (load/store as one opcode and increment as another opcode) to perform the same function (or a total of four opcodes instead of two opcodes for copying memory contents). Not to mention extra cycles for fetching. This is why, for example, in some programming languages, something like this:
*p++ = *j++;
is more efficient than this:
*p = *j;
p = p+1;
j = j+1;
If the compiler is efficient, it would leverage the single load/store and increment opcode (given the first example above), provided the programmer used an increment operator or decrement operator in this example and provided the processor has such an opcode. Good compilers will provide an assembly file that can be examined as part of it’s output.
Opcode / Command Table
To illustrate opcodes and cycles needed for each for the HD61700 commands, the command table in the technical reference manual outlines the technical details for each opcode, such as how many bytes are needed for the command sequence, the (processor) cycles needed to fetch and execute them.
This is where the architecture of the processor and bus become apparent. Paying close attention to the cycle and state times needed for a command sequence will help guide the most efficient use of the processor and bus.
For example, if there is a single command that can load and then jump, use that command sequence, rather than a load command, then a jump command.
Assembler Deep Dive
This command table is helpful in understanding the efficiencies (or lack of) for each command sequence. It is also needed to “assemble” or compile your assembly language source file into an executable that the processor will run.
Even in assembly language, we still prefer to write code using statements like “LD” and “RTN”, rather than have to remember their opcodes (in hex) and the order and bit patterns of their arguments.
An assembler usually supports “labels” as well, which represent locations in the program by name. The assembler will convert our plain text source to the binary equivalent, and a command table like the one above is necessary in order to build the assembler.
Each command type has formats defined for the parameters or options supported. For example, if we look at the CAL command (absolute), it needs the address of where to jump to. The high order byte is the third byte while the low order byte is the second byte (backwards). Given the the following example:
CAL &H1234
This will call (jump to) address &H1234. When RTN (return) is specified, the program will return back to the next statement after the call command (equivalent to the GOSUB/RETURN in BASIC). This is a three byte command sequence:
In memory, we would see the assembled code (in hex) as 77 34 12. Recall the “list? y/n” prompt the PB-1000 has when assembling a source file. I have included a hard-copy printout of what that “list” looks like below:
If you look at the left most box, the line numbers (0001 through 0005) are shown (which are your source lines) as well as the assembled bytes to the right. For example, the ORG and START keywords are assembler directives, and the entire program is assembled to four bytes.
I have rambled on with some reasons why assembly would be useful or necessary with a specific example (dumping the system ROM to a PC) and I have explained a bit more of the “assembler” process. My next adventure into the details of the PB-1000 will now make a bit more sense.
Better Understanding of the PB-1000 Hardware
Because of my experience with the PC-6 and it’s “assembly simulator” (which does not grant any access to actual hardware), I was a bit skeptical on how much I can access and do with the PB-1000 in assembly.
The peripherals (such as the serial port, parallel port, buzzer, etc) are not memory mapped. This means they do not interface with the bus or processor in such a way that their ports appear as a memory address.
The technical reference manual does not explain for example how the buzzer works. I am curious if the buzzer pitch can be changed to support more “tones” than the two that it supports in BASIC. I am also curious if I can interface with the serial port in a more efficient way than BASIC does.
As we saw in my sample program which dumps the system ROM to the serial port, I was able to use the built-in Casio subroutines in the system ROM for opening the serial port and sending a byte to the serial port. I simply used the CAL opcode given the address documented for each subroutine in the technical reference manual.
In order to understand a bit more how these peripherals are interfaced, I can examine the assembly subroutines Casio created. These are the subroutines the system and BASIC use when performing I/O on the serial port, or when the buzzer is activated for example.
If I look at how Casio accomplishes this, I can either modify it, or, leverage it in my own programs by jumping to those subroutines directly from my own assembly program.
Reading Casio’s Assembly Subroutines
There are few restrictions on reading directly from the supported address space in the PB-1000. For example, I can read not only all of the RAM address space, I can also read all of the system ROM address space (the internal CPU ROM is not accessible).
This is important for several reasons, one being that it is possible to dump out the system ROM and disassemble it (as I have demonstrated in a previous section). This was not possible for example on the PC-8, which had an undocumented PEEK command (and POKE), which did not work on the internal ROM space.
It should be noted that address 0000 to 0BFF is the internal ROM and not accessible.
Being able to read the system ROM space allows for assembly subroutines I create to be able to invoke other subroutines within the system ROM. As I have shown in the sample assembly program to dump out the system ROM, I use two built in Casio subroutines for opening the serial port and sending a byte to the serial port.
Further, reusing the LCD and printing subroutines is possible for example. If you disassemble those areas in ROM, you can see for yourself what the Casio subroutines are doing when writing to the LCD buffer area or operations with the RS-232 port or other hardware.
The technical reference manual is the key to successfully using the built in subroutines from ROM and if disassembled, a greater understanding of how the hardware is accessible.
A simple example of this is to take a peek at the BEEP subroutine in ROM. The technical reference manual indicates that the BEEP subroutine is located at &HFFE5. In BASIC, there is a CALL command to jump directly to an assembly subroutine:
10 CALL &HFFE5
The example above will jump directly to the assembly subroutine in ROM for the BEEP sequence.
In BASIC, you can also call out to an assembly routine that you created. For example, if we had an assembly program “PRINT.EXE”, we can invoke that directly from BASIC:
10 CALL "PRINT.EXE"
The PB-1000 will load the assembly program from the general storage area into the dedicated space for assembly programs (where the ORG statement has indicated), then execute from the starting address (where the START statement has indicated) specified in the assembly program.
Alternatively, if you have parameters or options which need to be set, you can load the assembly program, modify or set options or data, then execute it. For example:
10 BLOAD "PRINT.EXE"
10 POKE &H7001,&H1F : POKE &H7002,&H96
20 CALL &H7010
If our assembly subroutine had a START at &H7010, and 16 bytes of options in the beginning, we can use BLOAD to load the assembly program, then POKE values into the options area, then execute it.
You can also POKE opcodes directly into the assembly program area and execute them, without the assembler if needed:
10 POKE &H7000, &H77 : POKE &H7001,&HE5 : POKE &H7002,&HFF : POKE &H7003,&HF7
20 CALL &H7000
The example above creates the sequence CAL &HFFE5, which is a call to the beep subroutine in ROM.
Hardware Exploration Using Disassembly
Now that I was successfully able to jump to various subroutines in the system ROM, the next step was to figure out how some of these subroutines actually worked. For example, how does the BEEP command work? Does the hardware support varied duration or pitch? If so, how?
From the previous section, I referenced the BEEP subroutine starting at &HFFE5. Since I have all of the opcodes listed from the command reference manual, I can use the MONitor to inspect that subroutine and disassemble it. For those who are unfamiliar with the term “disassembler” or “disassembly”, it is the reverse process from “assembly”, that is, the reverse of taking assembler commands and arguments and converting them to opcodes or byte code.
Using the monitor, I can see the opcodes and parameters by looking them up in the command reference manual. Note that Bank 0 should be selected before dumping out areas of ROM – the default bank for the monitor is Bank 1, which would be the RP-32 memory expansion. The ROM area and RP-32 overlap the same memory address space.
The bytes at location &HFFE5 (the BEEP subroutine) are:
37 24 AB
Disassembly of these bytes at &HFFE5 is:
JP &HAB24
Opcode &H37 is JP or jump, followed by two more bytes which is the absolute address, in this case &HAB24.
In case you are wondering why the BEEP routine started at &HFFE5, and at that location we have another jump to &FAB24. This is a method to account for different ROM versions without changing the locations of the published subroutines. Further, internal CPU ROM can continue to use the same jump locations for interrupt handling as well. These are commonly referred to as “jump tables” and typically placed somewhere in ROM (or RAM) where they will not move with changes to the versions of ROM.
Next, I went on to dump location &HAB24. The bytes are:
02 A3 91
Disassembly of this block of bytes is a bit of a challenge. &02H is LD, register to register load. The next byte, &HA3, the first 5 bits specify the register if it is a 16 bit main register, or if the next 2 bits are set, it specifies one of the named registers.
It gets interesting after the register specifiers, if bit 8 is set, we then have a relative jump (JR), with the next byte specifying the offset. The offset uses 7 bits, with the 8th bit being the direction (forward or backward). Fully disassembled, this would be:
LD $3,$SY,JR -&H11
At this point, to arrive at this disassembled code, I was unable to find enough documentation in the command reference manual nor the technical reference manual. Bit 8 in the manuals is never set for this type of 3 byte command, which lead me to believe this was an undocumented command or sequence. I arrived at the disassembled code above by looking through a disassembler I found here.
Disassembler and Undocumented Commands
At this point in my experimentation with the PB-1000 and specifically how Casio’s BEEP subroutine worked, I ran across an undocumented sequence and needed to consult a disassembler program someone had wrote (probably decades ago).
The disassembler source I found was for C, and most likely compiled for an older version of DOS. It did not run on my desktop PC, so I converted it to C# (.NET) and compiled it as a console application, which will run on recent windows platforms. I enhanced the output a bit and added more options and posted the source on GitHub. Apparently this source handles the undocumented sequences, such as a relative jump after load.
Some items I added to the disassembler after refactoring it a bit:
- Absolute address display in parens (for relative jumps for example)
- Capitalization option for all opcodes
- Output on left side of address to visualize bytes needed for current line
- Binary output in parents for all 8 bit immediate arguments
I took my system ROM image that I had dumped to the RS-232 port and fed that in to the disassembler.
Using a disassembler program is far easier than attempting to decode by hand using the technical reference or command reference. I have included an example of my version of the disassembler below, with &HAB24 highlighted, which is the current statement I am looking at, within the BEEP subroutine:
So far we have:
LD $3,$SY,JR -&H11
We are loading the contents of the SY register to a main register (3). What is the SY register? The technical reference manual mentions an X, Y, and Z register (16 bit each), as IX, IY, and IZ. It makes no mention of an SY register. Searching around the interwebs, I came across a document which explains the HD61700 commands including undocumented commands:
From the above document:
SX/SY/SZ: Specific Index Registers (5-bit, UNDOCUMENTED) - pointers to the internal data RAM allowing indirect access to the main registers. In many cases they can substitute the main register operands, resulting in smaller code size and faster execution speed. In the Casio PB-1000 system their values are fixed:
SX=31, indicates the main register $31
SY=30, indicates the main register $30
SZ=0, indicates the main register $0
Note that the built-in assembler in the PB-1000 will not recognize SX, SY, SZ, or any of the other undocumented sequences. You would need to create your own assembler or use one that has been created which supports all of the undocumented sequences.
Skipping over the register to register loading in the current statement, we then jump to &HAB15:
Another register to register load, followed by one more, then another relative jump to &HAAE9.
At &HAAE9, GST is the opcode for get status from a status register to a main register. PST would be the opcode for the opposite, updating a status register. The argument for the GST opcode at &HAAE9 is PD, which is the port data register. The port data register can be configured for input or output for each of the 8 bits independently. We can assume that bits 7 and 6 are configured as outputs and these bits are the buzzer output.
The next 2 lines clear bit 7, turning that output off, and set bit 6, turning that output on. The next line then writes to the port data register with these changes.
The next line &HAAF3 is then inverting the bit pattern for bits 6 and 7. It appears that wiggling bits 6 and 7 is what is needed to make sound via the buzzer. To test this theory, I wrote a small assembly subroutine that will wiggle bits 6 and 7:
;ASSEMBLER PROG. "BUZZER"
ORG &H7000 ;load at beginning of assembly program area
START STRT ;start execution at first opcode assembled
DB &HFF,&H00 ;frequency
DB &HFF,&H00 ;duration
STRT: LDW $0,&H7002 ;look at input parameters (reg 0 and 1)
LDW $2,($0) ;load duration into reg 2,3
LDW $0,&H7000 ;frequency address
LDW $6,&H01 ;subtraction value
GST PD,$8 ;pull current port data to reg 8
AN $8,&H3F ;clear bits 7 and 6
OR $8,&H40 ;set bit 6
LOOPD: PST PD,$8 ;push reg 5 to data port
LDW $4,($0) ;reload frequency countdown
LOOPF: SBW $4,$6 ;frequency
JR NZ,LOOPF
XR $8,&HC0 ;flip bit 6 and 7
SB $2,$6 ;duration
JR NZ,LOOPD
RTN
The above subroutine was assembled as “BUZZER.EXE”. I then wrote a BASIC program which loaded the assembly subroutine, then POKEs the parameters for pitch (frequency) and duration into memory and then calls the buzzer assembly subroutine:
10 BLOAD "BUZZER.EXE"
20 POKE &H7000,&H55
30 POKE &H7001,&H00
40 POKE &H7002,&HFF
50 POKE &H7003,&H00
60 CALL &H7004
This worked, and as a result, I can conclude that it is in fact possible to change not only the duration of the tone but the pitch as well. With assembly on the PB-1000, it is in fact possible to output sounds other than “low” or “high” built into BASIC. This BASIC program can be used to POKE different values for pitch and duration to experiment with different tones, without modifying the assembly source.
With a disassembler, a system ROM image, and ability to follow Casio’s subroutines (unless they branch out to the internal ROM, which is not readable via these methods), you can see how they interface with the hardware. This allows you to create your own assembly subroutines for functionality not included in the system ROM or not possible with BASIC.
Banner Program
Of course I have to create a banner program for each pocket computer, and this one is no exception. Since this model has plenty of memory, it should be an easy task.
I could simply use the same program as the PC-6 with minor changes. However, it would be more interesting to improve it or use more of the features and capability of the PB-1000 if possible.
Since I am using a standard Centronics parallel printer, the banner program can be adapted to various paper sizes. For this example, I will use the Epson POS thermal printer. These thermal printers are widely available used or refurbished at quite inexpensive. The paper is inexpensive as well.
ESC/POS Standard
This Epson POS printer utilizes the ESC/POS standard that Epson created and is used in just about all of these POS printer models. It is optimized for receipt printing, graphics, formatting, justification, bar codes, and other features that would be required for this application.
Thankfully Epson published the entire standard with examples and it is quite easy to follow. The command structure is a series of bytes which allow for setting options during the printing cycle.
Banner Program Ideas
One of the optimizations for the banner program is to reuse the built in font bitmap rather than load my own. Another optimization is to improve the speed of printing. The Epson POS printer is quite fast and I can take advantage of that performance.
If you recall in a previous section, I described how it was not possible to read ROM contents from BASIC, as there is no bank switch command and my attempt to do so via an assembly subroutine did not work. Therefore, I cannot fetch the font bitmap from the system ROM as I did with the banner programs for the Model 100 and the PC-2. In an effort to avoid the extra memory needed to store the font bitmap data in the BASIC program, I came up with an unusual method.
Of course, I could write the banner program in assembly, which gets around this issue (as I did for dumping the system ROM to the RS-232 port).
My idea was to write a similar banner program as the others, but instead of working from a string input and fetching font bitmap data for each character in the string, I can write the string to the LCD and the pull the bitmap from the LCD memory area which I can access from BASIC. The PB-1000 has an area in RAM which is rendered to the LCD display via a system ROM subroutine.
This RAM area is accessible by BASIC (no bank switching needed) and it starts at &H6201 and is 1,536 bytes total. The LCD is 32 columns by 4 lines, which is 128 characters total. The font bitmaps or character size are 6 bytes each. This requires 768 bytes in total to represent the entire contents of the LCD screen. The PB-1000 has 4 “virtual” lines after this, for another 768 bytes, for a total of 1,536 bytes.
LCD Memory Area
I came up with this idea after reviewing an assembly routine to invert the display (this sample program is from the command reference manual). Below is the “Reversed Display” assembly routine (my comments added):
;ASSEMBLER PROG. "REV"
ORG &H7000
START &H7000
DOTDI: EQU &H022C ;address of built in system rom subroutine for LCD refresh
PRE IX,&H6201 ;LCD scratch pad memory start, index reg X
LDW $0,&H600 ;counter for number of bytes (reg 0,1)
LOOP:LD $2,(IX+0) ;loads memory contents (byte) at X index reg + offset $0
INV $2 ;inverts contents of this byte
STI $2,(IX+0) ;writes byte back and increments X index reg
SBW $0,$30 ;reg $30 is 1 by default, subtract from reg $0
JR NZ,LOOP ;is reg $0 zero?
CAL DOTDI ;invoke system rom subroutine to refresh LCD
RTN
For my BASIC banner program, I will concentrate only on what is on the first line of the LCD display, which would be the first 192 bytes (32 characters at 6 bytes each). The program will take each byte from left to right and mirror that to the printer, expanded or widened to make the most of the paper size.
Of course the program may in fact easily work with a longer string length, several LCD lines if needed for example.
The Epson POS printer is thermal and prints 42 characters per line. Since the fonts on the PB-1000 are 8 pixels “wide” (when transforming them to print sideways for the banner), I will use 5 characters on paper for every pixel on the screen. This allows for a printout large enough to use as much of the width of the paper as possible.
Similar to other banner programs I wrote for this printer, I change the line spacing to zero, add a custom character (a solid block), and add a paper cut step at the end of printing.
Below is an example of a working banner program:
REM print text to LCD and read first line
REM stretch and output to a file, BAN.S
10 CLEAR : INPUT "Text";TEXT$
20 L = &H6201 + LEN(TEXT$) * 6
30 CLS : PRINT TEXT$ : GOSUB 1000 : GOSUB 1100
300 FOR A = &H6201 TO L
310 B = PEEK(A) : ROW$ = ""
320 FOR F = 0 TO 7
330 H = FRAC(B/2) : IF H = 0 THEN 350
340 ROW$ = ROW$ + "~~~~~" : GOTO 360
350 ROW$ = ROW$ + " "
360 B = INT(B/2) : NEXT F
370 LPRINT ROW$ : LPRINT ROW$
380 NEXT A
390 GOSUB 1300
400 GOSUB 1200
410 END
REM redefine "~" to black box, enable custom font
1000 LPRINT CHR$(27);CHR$(38);CHR$(3);CHR$(126);CHR$(126);CHR$(12);
1010 FOR M=0 TO 35 : LPRINT CHR$(255); : NEXT M
1020 LPRINT CHR$(27);CHR$(37);CHR$(1)
1030 RETURN
REM set line spacing to 0
1100 LPRINT CHR$(27);"3";CHR$(0)
1120 RETURN
REM set line spacing to default
1200 LPRINT CHR$(27);"2"
1210 RETURN
REM cut paper
1300 LPRINT CHR$(29);"V";CHR$(66);CHR$(0)
1310 RETURN
The program above will print anything that exists on the first line of the LCD (given the input string length) and it worked perfectly well. But, I was curious if it would print faster.
Note that there are a few “extras” in this banner program that were not in previous versions. For example, there are command sequences for line spacing, a custom character definition, and a paper cutting sequence. These are all ESC/POS command sequences sent to the printer to perform various functions.
In previous banner programs, I would find a character in the character map for the printer that provided both the largest print area and contrast. In many cases, the printer would have a block character for example. With the Epson POS printers, you can redefine an existing character’s bitmap. In this example, I am redefining the “~” character as a solid block, with each bit set. This will fill the entire character’s space when printed. After the character’s bitmap is redefined, sending that character to the printer will result in my bitmap character definition to print instead of the original definition.
Faster Printing
In order to print faster, I can use an assembly subroutine which sends bytes to the printer, bypassing the BASIC printing subroutines. In an effort to compromise between writing as little in assembly as possible, yet having fast performance, I will use a mix of BASIC and assembly.
My idea was to write the banner text to a file in BASIC, then use an assembly subroutine to print the file to the printer. This allows for all of the processing to happen first before printing.
There is a subroutine in the system ROM to send a byte to the printer PROUT, and a subroutine to find a file given the same, FNSCH.
Unfortunately many of these subroutines in the system ROM have parameters passed in registers and not just memory locations. This makes it impossible to use them directly from BASIC. This means my assembly subroutine will need to invoke these system ROM subroutines.
I had difficulty using the FNSCH subroutine in the system ROM as it seemed to either falsely indicate it found the file, or it would not find the file. What I did not know is the exact format of the file name it expected, such as whether spaces for unused characters were used if the file name is less than 8.3 chars or nulls (zero) were to be used.
Understanding the File System
The technical reference manual did not directly indicate the structure of the “directory” for ASCII files, so I reverse engineered it. I took the detailed memory map and inspected the current values for some of the system variables (indicated in red below on the right of the map). I had four ASCII files in the directory, and I did in fact see the contents / bytes for them starting at &H9466. What I needed to understand is how to find the start and end of these bytes given a file name.
The directory entry size is 32 bytes (I found this while disassembling some of the system ROM subroutines). Given the memory pointer values I inspected, if I subtract &HFF3F from &HFFBF (the ASCII directory start and end addresses), I get 128 bytes total. If I divide that by 32, I get 4. This indicates I have 4 files in the ASCII directory. I did in fact have 4 files that would be stored in this area, two source files (“S” files) and two EXE files (“M” files).
The format of the directory entry appears to be the same as what is mentioned in the technical reference manual for a subdirectory in section 10-5:
When I inspected the bytes (using the built in monitor) starting at &HFF3F, it aligned with the format mentioned above for a “subdirectory”. Since I did not use a password for any of these files, the first byte in the password field was &HFF.
Incidentally, one could read the password of any file knowing the structure of the directory in RAM.
Given the file name locations in memory, I now discovered that spaces are used for file names where the file name has unused characters.
Further, I found the documentation in the technical reference manual was incorrect with respect to where the file extension is to be loaded for the FNSCH subroutine. I found this through the disassembly of the FNSCH subroutine in the system ROM. It compares all 11 bytes in a row in the WORK1 area, and the extension is not placed at &H690C as described in the technical reference manual. Further, the subroutine sets the carry flag if the file is not found, opposite to what is described in the technical reference manual.
After all of this inspection, trial and error, and debugging, I came up with the following assembly subroutine to print a file to the printer. I reserved 11 bytes at &H7000 for the input file name (8 + 3), and the next byte is the file classification. The default inputs to this routine are the file name “BAN” with a file classification of &H24, which is an “S” file. Of course this can be changed by POKEing bytes into this memory area before calling the subroutine.
;ASSEMBLER PROG. "PRNTFILE"
ORG &H7000 ;load at beginning of assembly program area
DB &H42,&H41,&H4E,&H20 ;input 8 + 3 bytes file name
DB &H20,&H20,&H20,&H20
DB &H20,&H20,&H20
DB &H24 ;input - file classification
START STRT ;start execution at first opcode assembled
FNSCH: EQU &HE818 ;$6,$7 addr in dir, filename in WORK1,$4 file type
WORK1: EQU &H68F4 ;WORK1, 24 bytes
PROUT: EQU &H961F ;$16 output byte
BUPDN: EQU &HFFF7 ;copies $2,$3 to $0,$1 for $4,$5 bytes
STRT: LDW $2,&H7000 ;source, copy filename
LDW $0,&H68F4 ;destination
LDW $4,11 ;copy 11 bytes
CAL BUPDN ;do copy
LDW $0,&H0000 ;zero offset
PRE IX,&H700B ;file classification byte
LD $4,(IX+0) ;reg 4 is input to the search function
CAL FNSCH ;do search
JP C, DONE ;if not found, exit
FIND: LDW $0,&H700C ;debug result location
;PROUT can modify regs 0-6 and others
LDW $20,&H1 ;value of 1, used in a few places
ADW $6,$20 ;reg 6 has search result, add 1 to skip file flag
LDW $8,&H0 ;reset to zero offset
PRE IX,$6 ;move addr of directory to index reg
LDIW $10,(IX+$8); start address in 10,11
LDW $12,(IX+$8) ; end address in 12,13
SBW $12,$10 ;12,13 now has # of bytes,sub start addr
PRE IX,$10 ;index reg points to start of data
LOOP: LDI $16,(IX+$8) ;fetch next byte
CAL PROUT ;output to printer
SBW $12,$20 ;any bytes left?
JR NZ,LOOP ;if so, repeat
DONE: RTN
If the file is not found, then nothing will be printed.
The challenge with using assembly subroutines in the system ROM is that they will alter register contents. Of course, you can push registers to the stack, call the subroutine you want, then pop values off the stack. This is viable but not within a loop for performance reasons. Therefore, in loops it is best to choose registers that are not altered within the subroutine you are invoking in your loop.
Now that I had the print file subroutine completed, I saved it as PRNTFILE.S and assembled it with the name PRNTFILE.EXE. I can now use this assembly subroutine from within my BASIC program:
10 CLEAR : CLS : R = 255 : GOTO 1400
20 CLS : INPUT "Banner Text";TEXT$
30 A = &H6201 : T = LEN(TEXT$)*6 : L = A + T - 1
40 CLS : GOSUB 1700 : PRINT TEXT$
50 OPEN "BAN" FOR OUTPUT AS #1
60 IF R = 0 THEN B=255 : GOSUB 1900
300 FOR A = A TO L
310 B = PEEK(A) : ROW$ = ""
320 GOSUB 1900
330 POKE A,R : CALL &H022C : NEXT A
340 CLOSE #1
350 GOSUB 1000 : GOSUB 1100
360 CALL "PRNTFILE.EXE"
370 GOSUB 1200 : GOSUB 1300
380 GOTO 1400
REM REDEFINE "~" to black box, enable custom font
1000 LPRINT CHR$(27);CHR$(38);CHR$(3);CHR$(126);CHR$(126);CHR$(12);
1010 FOR M=0 TO 35 : LPRINT CHR$(255); : NEXT M
1020 LPRINT CHR$(27);CHR$(37);CHR$(1)
1030 RETURN
REM set line spacing to 0
1100 LPRINT CHR$(27);"3";CHR$(0)
1120 RETURN
REM set line spacing to default
1200 LPRINT CHR$(27);"2"
1210 RETURN
REM cut paper
1300 LPRINT CHR$(29);"V";CHR$(66);CHR$(0)
1310 RETURN
REM Menu
1400 BEEP : GOSUB 1600
1420 K$=INKEY$
1430 IF K$=CHR$(252) THEN 20
1440 IF K$=CHR$(253) THEN 410
1450 IF K$=CHR$(254) THEN GOSUB 1500 : GOSUB 1600
1460 IF K$=CHR$(255) THEN END
1470 GOTO 1420
REM Toggle Reverse Mode
1500 IF R = 255 THEN 1520
1510 R = 255:RETURN
1520 R = 0:RETURN
REM Menu
1600 LOCATE 0,3 : PRINT NORM;"[ new ][reprnt][";
1610 GOSUB 1700
1630 PRINT " rev ";NORM;"][ quit ]"; : RETURN
1700 IF R = 0 THEN PRINT REV;:RETURN
1800 PRINT NORM;:RETURN
REM Process bits in B
1900 FOR F = 0 TO 7
1910 H = FRAC(B/2) : IF H = 0 THEN 1930
1920 ROW$ = ROW$ + "~~~~~" : GOTO 1940
1930 ROW$ = ROW$ + " "
1940 B = INT(B/2) : NEXT F
1950 IF R = 0 THEN ROW$=ROW$+"~~"
1960 PRINT#1,ROW$ : PRINT#1,ROW$ : RETURN
The result of all that effort was a success!
This version of the banner program has a few newer features which highlight the capabilities of the PB-1000. The print speed is improved due to use of an assembly subroutine which prints the processed output of the banner logic.
I used a menu taking advantage of the touch screen. Although this is not really necessary, it highlights how simple it is. It also allows the user to reprint the last banner, which is fast because the file output is not deleted after processing.
I added an inverted output option which can be toggled from the menu. This was an easy option to add because this version of the banner program outputs what it sees on the LCD screen buffer. This means that all supported characters and even graphics could be output as a banner.
Lastly, because the processing of the output is a bit slow (due to BASIC and lack of bitwise operators), I added an indicator on the screen to show the processing status. This was done via POKEing back to the LCD buffer and invoking the system ROM subroutine for LCD refresh.
It is certainly possible to write the entire banner program in assembly and if I end up doing that, I will update this post.
Final Thoughts
The Casio PB-1000 is packed with features and capability. Because the PB-1000 can be programmed in assembly directly from its keyboard with a built-in assembler, it is a great platform to learn computer architecture and assembly on.
I mention this as a highlight because it reminds me of the mid-nineties when I was taking computer engineering courses. Back then they used an old HP mainframe to teach 68000 assembly. Yes, even in the mid-nineties, they would use archaic platforms for hands-on learning. If they used these types of devices instead, such as the PB-1000, we would be able to learn the same skills without having to be on campus and schedule time with the mainframe.
In my post on the PC-6, I referenced an article on the subject of the “assembly” programming on this series of Casio pocket computers. Up until then, I had no idea what the purpose of an assembly simulator would be useful for. Having read a few articles on the subject, I came to learn that these assembly simulators were provided for learning and standardized testing in Japan.
Why were these features made available in the United States? These pocket computers were made in Japan, and primarily for the Japanese market. We were fortunate to have some imported and sold here (mainly under the Radio Shack branding), and those learning tools (assembly simulators) came with some models.
However, as it turns out, on the PB-1000 the assembly support is not a simulator – it actually allows for direct access to the CPU, memory, and hardware.
After spending a few months with the PB-1000, I was able to explore the typical accessories available for pocket computers such as memory expansion, the plotter, a thermal printer, and an RS-232 port. In this case, it was quite interesting to use a 3.5” floppy drive with a pocket computer allowing for lightning fast saving and loading of programs and data. Further, having a touch screen provided for interesting user interfaces for games and applications.
As if these features were not enough for the pocket computer enthusiast, having a full assembler and being able to write assembly programs complements the hardware and peripherals the PB-1000 has to offer.
The PB-1000 has become one of my favourite models, and I am glad I had the opportunity to experiment with it.