
Casio PB-2000C Personal Computer
The Casio PB-2000C is a successor to the Casio PB-1000 and was released in 1989. This post is not nearly as detailed as my post on the PB-1000 because this model is similar in so many ways. However, that post on the PB-1000 is helpful as a prerequisite for background information on how this model is not only similar, but how it is different as well.
The PB-2000C is a rare find these days and boasted “multi-language” capability with built-in support for programming in C. This is the first model I have used which departed from the BASIC programming capability usually found in pocket computers, aside from assembly.

A saved search I had set up in eBay triggered an alert for a PB-2000C. The seller had this up for auction along with a half-dozen other random Casio models. Some of those other models were on my radar for experimenting with, and the combined package was a bargain.
Another similar model, the AI-1000, had built-in support for the LISP programming language which was used in artificial intelligence. This model is even more rare than the PB-2000C. The AI-1000 was also released in 1989.

Similar to other Casio models more popular overseas, it is difficult to determine exact release timelines for a given location or even where these might have been sold here in the United States, if ever.
Throughout this post, any reference to the Casio PB-2000C includes the AI-1000, except where noted otherwise, as these two models are nearly identical.
Form Factor
Compared to the prior model, the Casio PB-1000, the PB-2000C was not a “clamshell” design like many other Casio models. This is good for durability, since many of those clamshell models eventually developed cracked hinges.
The PB-2000C had a card cover or case that the unit fits inside, which helps protect the screen and keyboard when not in use.
Swappable ROMs
The PB-2000C has a label on the face: “multi language”. This implied the computers had more than a single programming language. While this was certainly true for the PB-1000 (assembly and BASIC), this was not exactly the case with these newer models.
The PB-2000C has the addition of a ROM slot. This allowed for software to be produced by not only Casio, but others as well. This is very similar in concept to the Sharp PC-1500 (Radio Shack PC-2) which had a memory slot which could also be used for ROMs.

Several ROMs for the PB-2000C were produced by Casio. All of them were for switching programming languages such as a BASIC, PROLOG (Logic Programming labelled as “Artificial Intelligence”), CASL, and a C-Interpreter. These ROMs (I believe) are usable with either the AI-1000 or the PB-2000C.
There was even a Pascal ROM, DL-PASCAL which was developed by the Swedish company Data-Larsson.
Needless to say, these ROMs are even more rare to find than the pocket computers themselves.
The BIN files (ROM dumps) are available online and could be written to a flash ROM and attached to a connector compatible with these models. There is at least one such device which was produced fairly recently (as of the writing of this post) by a hobbyist. The device can store up to four 64KB ROMs, selectable via switches.

Unfortunately the creator was not currently selling these, and they were out of stock.
Batteries
These newer models ditched the “AA” batteries used in the PB-1000 for button cell batteries, 3 CR-2032 to be specific. And since there is no built-in backup mechanism like the PB-1000, a CR-1220 is needed for backup while the main batteries are replaced.
Luckily there is a DC input jack on the side, which would help extend battery life. Certainly these CR-2032 batteries do not last as long as 3 “AA” batteries would.
Display
The LCD is the same size as the PB-1000, and is also dot addressable. It supports 32 characters per line, for 4 lines total. This translates to 192 pixels wide by 32 pixels high.
The main difference with these models however is they are not touch screens like the PB-1000. Instead, 5 physical hot keys were added just below the display, four which correspond to the standard on screen menus, with an “etc” button to scroll to the next menu set.
Keyboard
The keyboard is improved in my opinion, as there is no top membrane section like the PB-1000 had. The keys are solid plastic and durable. The layout, like the previous model, has dedicated keys for usage as a calculator for basic functions.
Since this model has built-in support for C, the commonly used characters for C are readily accessible on the keyboard. For example, brackets, the semicolon, and parens are directly accessible via dedicated keys. However, there is no shift key for quick upper case keypresses – the shift key is more of a function key to invoke the functions/shortcuts marked on the keyboard overlay. For example, to type a capital “A”, one would need to press CAPS, then A, then CAPS again. Then again, with C programming, upper case characters would not be as common.
Casio provided a keyboard overlay for the C functions, and other language ROMs had an overlay as well. This is similar to the Sharp and rebadged Sharp models from Radio Shack, such as the PC-2 and PC-3.
CPU & Memory
These models used the same HD61700 processor as the PB-1000 had. Casio improved the base memory to 32 KB for the PB-2000C. The PB-1000 had a mere 4 KB of RAM out of the box. This model, in my opinion does not need the 32 KB expansion in order to be useful, as the PB-1000 needed.
An RP-33 RAM expansion module was available which adds another 32 KB of RAM, for a total of 64 KB. It is important to note that the RP-33 is physically smaller than the RP-32 for the PB-1000. Luckily, reproductions of the RP-33 are available from Jeff Birt.
The RP-33 is also used in the FX-840P, FX-841P, FX-850P, FX-860P, FX-860Pvc, FX-870P, FX-880P, FX-890P, VX-1, VX-2, VX-3, VX-4, Z-1, Z-1GR models.

Peripherals
The PB-2000C can use the same peripherals as the PB-1000. The unit will physically fit into the MD-100 and FA-7, using the plastic case for the PB-2000C as a filler to fill the gap for the smaller size of the PB-2000C.

On my unit, the back cover did not fit well with the RP-33 remake, specifically the screw on the bottom of the module (pictured above) pushed the back cover up slightly. I had to remove the back cover in order to plug the unit into the MD-100. My PB-2000C is missing the plastic shell, which would have filled the gap above when plugged into the MD-100 or FA-7.

Programming
With all of the improvements in this model compared to the PB-1000 such as the form factor, increased memory, ROM capabilities, peripherals available, there are some significant downsides.
First, there is no built in assembler, which means you cannot create, assemble, and run assembly programs directly on the PB-2000 via built-in support. The PB-1000 had not only a BASIC interpreter, but it also had a full assembler built-in.
Further, there is no built-in BASIC support on this model. A ROM cartridge was released by Casio which would be a separate purchase if you wanted to program this model in BASIC.
Owners Manual
The PB-2000C owners manual is included below for reference:
C Language Support
The PB-2000C has a C interpreter built-in, which replaces BASIC and assembly options that the PB-1000 had.
C programs can be written directly on the PB-2000C and then loaded and executed. When a C file is loaded, it is parsed and loaded to the “p-code” area in memory. The loading process converts the C source file to p-code and then it is executable via the RUN command for example. There is a significant pause when this conversion process occurs.
The PB-2000C came with a guide “Introduction to the C Programming Language”, which is attached below for reference:
The built-in C editor is very easy to use. It works just like a text editor, allowing for insert and overwrite, using arrow keys to navigate between lines and columns. This is an improvement in my opinion over the built-in editor for assembly on the PB-1000. I mention this because the PB-1000’s editor worked more like line entries in a sequential file, rather than a multi-line text document. Inserting lines was not as simple or intuitive as with this model.
Without much effort, I was able to enter the following example C program which counts from zero:
main()
{
int x=0;
while(1)
{
printf("%d",x++);
}
}
The parser supports a format similar to ANSI C, with hardware support for I/O with not only the screen and keyboard, but the built-in file storage area, RS-232, disk, memory, and parallel port.
Luckily it supports pointers, which means memory can be read from or written to directly. It supports memory allocation as well, which is useful for storage of structured and unstructured data (via malloc and calloc).
String handling with C is far easier than with BASIC on these pocket computers. For example, the memory storage is allocated when a variable is declared and size can variable. For example, there is no limit to 8 character string variables like with previous models. In those previous models, I had to make use of the special string variable “$” or similar for strings greater than 8 characters. Even the special string variables had a limitation on length.
Banner Program
With each pocket computer I experiment with, I learn capabilities of each by making a banner program for each one. This is the first pocket computer that I have used which supports the C language. Of course, my expectations were a bit higher for this model for support of bitwise operators, shift operators, direct memory access (via pointers), and hardware support (such as printer output).
Whenever I write a banner program for a particular model of pocket computer, I try to leverage the built-in font data rather than key in my own, making the program as small as possible. Since this model did not include a detailed memory map in any technical guide, I do not know where the font data is stored, if it is even accessible. Nor do I know where the LCD buffer area is. My plan would be to either use the font data directly from ROM, and if that is not accessible (like with the PB-1000), use the LCD buffer area in RAM.

The LCD buffers are usually organized with a single byte in memory for each row of each character. For example, the characters on this model are 8 pixels tall by 6 pixels wide. This means there are 6 bytes per character.
The least significant bit in each byte is either the top or bottom of the character, and in the case of the PB-2000C, the top most pixel is the least significant bit in each byte (bit 0). Looking at the picture above, the horizontal red arrow points from left to right. Bit zero is to the left (the first pixel for the column), and bit 7 is to the right (the last pixel for the column).
Note that the bottom most pixel is usually off (except for “g” and “q” and similar characters which hang over the standard 7 pixels). Each character then uses 6 bytes in total, usually the last byte being blank for most english characters.
The direction therefore is top to bottom for the columns, left to right for the rows. The letter “A” would then be byte pattern 126, 144, 144, 144, 126. I wrote a small program to start at memory location 0 and look for the byte pattern of the character “A” in memory:
main()
{
unsigned char *p=0;
unsigned char a[5];
char i=0;
a[0] = 126;
a[1] = 144;
a[2] = 144;
a[3] = 144;
a[4] = 126;
clrscr();
/* Place an "A" on the screen, beginning of LCD buffer */
printf("A");
while(p<32766 && i<5)
{
gotoxy(0,1);
printf("%3d\n%5d",*p,p);
if( a[i] == *p++ )
{
beep(0);
i++;
}
else
{
i = 0;
}
}
printf("\n%d",p-5);
}
The program searches for these 5 bytes in a row (the pattern for the letter “A”), and stops when it finds the pattern. When it stops, it outputs the starting address of the pattern. In this case, this would be the start of the LCD buffer.
The program prints the value (in decimal) that it finds for the current pointer location, then the pointer location itself. Of course, this can easily be switched to display hexadecimal if desired, using the %x formatter in the printf function(s).
The program output 513, which is 0x201 (in hex) – this is the starting address of the LCD buffer area in RAM. For the banner program, I can write the banner text content to the LCD, then read the pixels from the LCD buffer much like I did for the PB-1000 banner program.
The following is a simple example of a banner program for an Epson POS thermal printer attached to the parallel port of the MD-100 (or FA-7):
#define LCD 513
main()
{
unsigned char bt[32],ot[33];
unsigned char *o,m,*p,e;
FILE *stream;
clrscr();
printf("Banner Text?");
scanf("%f",bt);
clrscr();
printf("%s",bt);
stream = fopen("PRN","wb");
/* set ~ char in printer as a solid block */
fprintf(stream,"%c&%c~~%c",27,3,12);
for(m = 0; m < 36; m++) fprintf(stream,"%c",255);
fprintf(stream,"%c%c%c",27,37,1);
/* set line spacing to zero */
fprintf(stream,"%c3%c",27,0);
e = LCD + (strlen(bt) * 6);
for(p = LCD; p < e; p++)
{
m = 1;
o = ot;
while(m > 0)
{
if( ((*p)&m) > 0 )
sprintf(o,"~~~~~");
else
sprintf(o," ");
o += 5;
m *= 2;
}
fprintf(stream,"%s\n%s\n",ot,ot);
}
/* extra lines and cut paper */
fprintf(stream," \n \n \n \n \n%cVB%c",29,0);
fclose(stream);
}
The C program above could be adapted to other printer types such as a line printer for example. The comments above are to highlight the special commands specific to the Epson POS printer. These commands include custom bitmapping for a character, in this case I redefined the “~” character to be a solid block. Further, I issue a paper cut command at the end of the print job.

Similar to my banner program for the PB-1000, I could easily redirect the output to a file, then send that file to the printer for faster printing (but same processing time for the file creation itself). The PB-2000C has C functions to delete files, create files, and stream data to files, so no assembly subroutines would be necessary. I needed to use an assembly subroutine to accomplish the file to printer output for the PB-1000.
P-Code
I was curious to see if the C interpreter created an actual assembly output to the p-code area or if it was some form of tokenized output that another parser would interpret. After inspecting the memory in this location and searching for known string constants in my C program(s), I determined that the p-code is not assembly.

I used the following subroutine to dump memory contents to the RS-232 port:
send()
{
/* Start of Dump Address */
unsigned char *p = 0x0000;
FILE *ostream;
/* 300 baud, 8,N,1 */
ostream = fopen("COM0:2,N,8,1,N,N,N,N,N","w");
/* Dump 64 K bytes */
fwrite(p,1,0xFFFF,ostream);
fclose(ostream);
}
Note that the C interpreter for the PB-2000C is robust enough where it allows “includes” to other C programs. For example, the send() function above was created in a separate C file named “send”. Then, in my banner program, I added the #include “send”, and was able to call the send() function from my banner program.
Being able to include references to other C files allows for organization of libraries for reusing code across multiple programs, saving space and ease of updates.
This method allowed me to push the memory contents out to the RS-232 port while my banner program was loaded into the p-code area.
Maze Program
Similar to the PB-1000, the dot addressable display of the PB-2000C would be a candidate for a sample maze program. Even though I have already written a maze program for the PB-1000, this model does not support BASIC built-in, and I had to create a version in C.
The PB-2000C has C functions to draw a line and clear a line on the LCD display, but it does not have a draw point or clear point like the PB-1000 and PB-700/PB-770 have. The PB-2000C is missing a function to detect if a point is on or off on the LCD display, which means that a maze program would need to track pixel values in an additional memory array, as opposed to direct inspection in the LCD RAM buffer. This would be faster than indexing the LCD buffer area and performing bitwise comparisons.
With the differences in the PB-2000C, I was able to work around most of them to get a version of the maze program working:
MAZE GENERATOR VERSION 1.0
#include "rand"
#define WIDTH 31
#define HEIGHT 31
unsigned char open[8];
double nc;
unsigned char blocks[WIDTH][HEIGHT];
char *stackx,*stacky,nci;
main()
{
unsigned char *stackstart,*ox,*oy;
printf("Pick a number from 0-100:");
scanf("%f",nc);
srand(nc*0.01);
stackx = (unsigned char*)malloc((WIDTH*HEIGHT) / 2);
stacky = (unsigned char*)malloc((WIDTH*HEIGHT) / 2);
stackstart = stackx;
*stackx = 0;
*stacky = 0;
clrscr();
/* FILL WORKSPACE WITH CLOSED BLOCKS */
for(nc=0; i < HEIGHT; nc++)
{
line(0,nc,WIDTH-1,nc);
}
/* Cut opening at top */
linec(1,0,1,0);
/* Start at random position */
while(*stacky%2 == 0) *stacky = rand() * (HEIGHT-2) + 1;
while(*stackx%2 == 0) *stackx = rand() * (WIDTH-2) + 1;
linec(*stackx,*stacky,*stackx,*stacky);
/* Mark this spot open */
blocks[*stackx][*stacky] = 1;
do
{
findfree();
if( nc > 0 )
{
nci = (char)((rand() * (nc - 1.0)) + 0.5);
ox = open + (((char)rand() * nc) -1 ) * 2;
oy = ox + 1;
linec(*stackx,*stacky,*ox,*oy);
/* Mark this spot open */
blocks[*ox][*oy] = 1;
/* Push opening to stack */
*++stackx = *ox;
*++stacky = *oy;
}
else
{
/* Pop from stack, no more openings found nearby */
stackx--;
stacky--;
}
} while(stackstart != stackx)
/* Cut opening at bottom */
linec(WIDTH-2,HEIGHT-1,WIDTH-2,HEIGHT-2);
free(stackx);
free(stacky);
beep(0);
/* Wait for BRK */
while(1);
}
findfree()
{
/* Look around current position for open blocks */
unsigned char *n;
n = open;
nc = 0;
if(*stacky - 2 > 0 && blocks[*stackx][*stacky-2] == 0 )
{
*n++ = *stackx;
*n++ = *stacky-2;
nc++;
}
if(*stacky + 2 < HEIGHT && blocks[*stackx][*stacky+2] == 0 )
{
*n++ = *stackx;
*n++ = *stacky+2;
nc++;
}
if(*stackx - 2 > 0 && blocks[*stackx-2][*stacky] == 0 )
{
*n++ = *stackx-2;
*n++ = *stacky;
nc++;
}
if(*stackx + 2 < WIDTH && blocks[*stackx+2][*stacky] == 0 )
{
*n++ = *stackx+2;
*n++ = *stacky;
nc++;
}
}

The maze program worked as expected, albeit far slower than the PB-1000 version written in BASIC. I experimented with variations of the program by using pointers and indexed arrays, which made little to no difference in speed in any variation I tried.
Challenges with the C Interpreter
There were a number of challenges writing the backtracking maze algorithm in C for the PB-2000C. The C library in the PB-2000C does not include a “random” number generator. Casio provided a sample function in the introduction to C guide. I typed in this function and saved it as “rand”. I then included this in the maze generator program at the top with the #include “rand” statement. This is helpful for writing functions or libraries once and reusing them in multiple programs.
/* Store following in file "rand" */
int _seed1 = 1000, _seed 2 = 12000, _seed3=28000;
double rand()
{
double r;
_seed1 = (_seed1 % 177) * 171 - (_seed1 / 177) * 2;
if(_seed1<0)_seed1+=30269;
_seed2 = (_seed2 % 176) * 172 - (_seed2 / 176) * 35;
if(_seed2 < 0) _seed2 += 30307;
_seed3 = (_seed3 % 178) * 170 - (_seed3 / 178) * 63;
if(_seed3 < 0) _seed3 += 30323;
r = _seed1 / 30269.0 + _seed2 / 30307.O + _seed3 / 30323.0;
while(r > 1.0) r -= 1.0;
return r;
}
This example random number generator does not have any way to “seed” the generator. It will provide the same random number sequence each time it is run. Another “seed” function was necessary:
double srand(seed);
double seed;
{
_seed1 = seed*30000.0;
if(_seed1 < 30000) _seed1 -= 30000;
while(_seed1 < 0) _seed1 += 30000;
_seed2 = rand() * 30000;
if(_seed2 == 0) _seed2 = 1;
_seed3 = rand() * 30000;
if(_seed3 == 0) _seed3 = 1;
}
The seed function accepts a double, intended to be from 0 to 1. Ideally, at the beginning of a program, call the seed function with a random number between 0 and 1. Unfortunately the PB-2000C does not have a clock that we can use the current date and time from as a seed value.
I ended up asking the user for a number between 0 and 100 and simply converting that to a decimal between 0 and 1 to feed into the seed function.
Random Number Generator
Since the PB-2000C has no random number generator (RNG), the sample Casio provided produces predictable random numbers, and therefore the maze program follows a predictable path. One way around this is to ask the user for a number between 1 and 100 for example, in the beginning of the program as I did with the maze program. However, if the user picks the same number twice, then the generated maze will be exactly the same each time.
I am no expert in true RNGs. However, I found it an interesting subject to explore due to this limitation in the PB-2000C. I spent some time attempting to build an RNG for the maze program. Before doing so, there are some considerations for an RNG:
- All numbers should have an equal chance of being picked (an even spread statistically over the entire range of outputs for the RNG)
- There should be no periodic sequence produced (if running the RNG for multiple requests, it should not repeat previous sequences)
These two concepts are simple enough to implement on a pocket computer using formulas that include infinitely repeating numbers, pi, trig functions, with enough precision in the calculations.
The challenge however is on the second bullet above – if I run my RNG generator once, it appears to meet both requirements above. However, if I stop my program, load it, and run it again, I see the same sequence. While I can save the seed value(s) from prior executions, everything is reset if I load the program again. I could write the seed values somewhere in RAM that the program loading process will not overwrite, this could corrupt the file system.
In addition, even if I solve for the seed issue, if I had two pocket computers, each would produce the exact same sequence, since they have the same initial seed value. In fact, if I note the sequence on one, I can predict the sequence on the other.
Using the current date and time would allow for the appearance of a more randomly generated set of numbers, and would even do so across several pocket computers of the same model and memory contents. However, the PB-2000C does not have a real-time clock like the PB-1000 does.
Rounding Function
Another missing function from the C interpreter is a rounding function. Since most random number generators return a decimal or double from zero to one, I needed a function to convert this random number into an integer. Normally we simply multiply the random number generated by a number representing the desired upper limit. For example, in this case, I needed a random number from zero to six for indexing an array.
double r;
r = rand() * 100;
In the example above, the random number “r” will be in the range between 0 and 100 as a double (or decimal). If you cast a double to an integer, you get just the whole number portion of the value. For example, 3.86 cast to an integer is 3. Of course, we likely want 4, and a rounding function would do this.
The simple solution to this would be:
double r;
int ri;
r = rand() * 100.0;
ri = (int)(r + 0.5);
In the example above, integer “ri” is assigned a value using a cast, but before the cast we add 0.5. This allows for the “rounding up” logic we expect from a rounding function. Similarly, if we were to want this logic for negative numbers as well, we would use:
double r;
int ri;
r = rand() * 100.0 + rand() * -100.0;
ri = (int)(r < 0 ? r - 0.5 : r + 0.5);
In the example above, “r” is a random number between -100 and 100, and “ri” is rounded to the nearest value, whether negative or positive.
The C interpreter does not have nearly as much access to calculator features as the previous models, such as statistics, conversions, date and time, and others.
Memory Contents
Out of curiosity, I dumped the memory from the PB-2000C to a file. Since I did not have any technical documentation on the address space of this model, I dumped the memory using a pointer in C starting at address location zero and moved upwards from there (see send() function in previous section).
It seems the ROM is not accessible via this method, only the RAM is accessible.
Hidden Commands
The C language ROM (included in the PB-2000C, optional ROM for the AI-1000 and other variants) has a hidden monitor to view memory contents, similar to the PB-1000.
CLEAR MON
There appears to be 4 banks, 0 – 3. The functionality of the monitor is similar to the PB-1000 and supports both reading and writing of memory contents and bank switching.
Executing Assembly Programs
It is possible for the PB-2000C to execute assembly programs by loading the program from disk or from a serial port, or poked into memory via a C program. The trick is to load the assembled code into a safe place in memory then redirecting the BRK key to jump to that program code using the monitor to make this redirection.
The description and example of this process can be found here.
This reminds me a bit of the Radio Shack PC-2 / Sharp PC-1500. This model was able to run assembly programs, however, they needed to be written and assembled externally, then loaded in a similar way. One of the benefits of the previous model, the PB-1000, was that you could write an assembly program directly with the PB-1000, assemble it, and run it.
Final Thoughts
This is one of those cases where the successor to a previous model pocket computer was not necessarily “better”. The PB-2000C had a better form factor (no clamshell design), more memory built-in, used the same peripherals as the previous model, and replaced the BASIC language support with C support.
Before actually using this model and understanding how it works, I assumed the C support was going to be an improvement in speed over the BASIC functionality in pocket computers in general. I had incorrectly assumed the C would be compiled, and compiled to HD61700 assembly then executed. This is not the case at all – the C programs are converted to a tokenized “p-code” which is then interpreted and run (similar to BASIC).
The performance of the C programs I have written as examples is mixed. While some things are faster with this version due to optimizations in the C interpreter, such as output to the printer, almost every other aspect is far slower. Random number generation is slower due to the lack of a built-in RNG and line drawing commands are far slower.
For example, on the PB-1000, a 31 x 31 maze takes 3 minutes and 51 seconds to generate. The same size maze on the PB-2000C takes 13 minutes and 38 seconds to generate. This is another example where programming language does not alone determine speed of execution.
The loss of an assembler in this model is a real drawback. Further, not only was the built-in support for an assembler dropped in this model, there was no ROM offered for this functionality either. Of course, one could use the previous model to develop an assembled application and side load it into the PB-2000C via the serial port, but this is kludgy.
I am glad I was able to find one of these PB-2000C pocket computers for a bargain in working condition. Now that I have been able to use both the PB-2000C and the PB-1000, the PB-1000 would be my preference given the choice between these two models. The program execution speed of the PB-1000 is faster generally, and has more capabilities, especially given the built-in support for assembly.
I suppose the PB-2000C was aimed at students who could use this model to learn C programming on, without requiring a laptop or desktop computer. If I had one of these models for my “Intro to C Programming” in college, it would have been convenient. If this model was used as a learning tool like the PC-6’s “assembly” support, it makes sense why Casio released this model with C support.
Perhaps some of the ROMs will pop up in the future or perhaps someone will make a modern replacement which could be loaded from saved binary images. I would be curious to try the BASIC ROM to see if execution speed would be as fast as the PB-1000 for the maze program.