64K RAM Ought to be Enough: a demo for the PCjr

Updated 2018–10–08

A demo for the IBM PCjr by Pungas de Villa Martelli. It was presented at Flashparty 2018 and won the Demo category.

Requirements

An IBM PCjr with at least 64k RAM.

Download

Technical description

The demo is divided in the boot loader and demo 3 parts

Boot loader

Boot loader
Boot loader

The demo is intended to work with a 64K RAM (or more) PCjr. Booting from its own boot loader is needed to save precious memory. DOS alone takes ~20K of RAM. That is 30% of the total memory. You don’t want to waste that memory.

The boot loader is pretty simple:

  • It has a list of sectors-to-load for each part. Each part is loaded starting at 0050:0100 address (which is the same as 0060:0000) and after loading it, jumps to that address.
  • Installs ricarDOS, a mini “DOS” that hooks the 0x21 interrupt handler with:
    • ah == 0x4c:
      • in ricarDOS it terminates current scene and loads the next one.
      • in real DOS it terminates the current DOS program.
    • ah == 0x09: prints a $-terminated string

ricarDOS was created to have a “rapid testing framework”. It allows to run each part on an emulator using real DOS, but when run from the boot loader, the actual DOS calls are trapped to ricarDOS.

The memory is organized like this:

Memory map:
      0 - 0x01ff: 512 bytes used for the vector table for the first 0x80 interrupts
 0x0200 - 0x03ff: 512 bytes used for ricarDOS
 0x0400 - 0x04ff: 256 bytes. BIOS variables
 0x0500 - 0x05ff: 256 bytes. stack. used globally for all the demo parts
 0x0600 - 0xffff: 64000 bytes free to be used for the demo (including video)

Part I

Graphic

Big fonts

Big Fonts

The big fonts are based on Andrew Glassner’s Notebook. Basically the rectangle to draw each font is divided in 55 segments. Think of the typical 8-segment display, but instead of 8, it has 55 segments. With 55 segments you can render pretty nice fonts. Specially if they are designed by Andrew Glassner.

55 segments
55 segments

So we created 110 (55 + 55) primitives:

  • 55 primitives to turn on each segment
  • and 55 primitives to turn them off

Each letter consists of a 64-bit bitset containing the 55 segments.

The bitset for letter A is:

;ASCII: 0x41
table_a:
 dw 0b1110010011101110,0b1111111101000101,0b1000111111111111,0b0000000000000111

And this one is for letter B:

;ASCII: 0x42
table_b:
 dw 0b1010010011101111,0b0111111011111100,0b1111110001111111,0b0000000001111011

Notice that A and B have many segments in common. If letter B should be drawn right after letter A, then only the “diff” between A and B needs to be drawn.

A xor B
A xor B

And the diff between A and B is just a simple xor between A’s bitset and B’s bitset.

A: 0b1110010011101110,0b1111111101000101,0b1000111111111111,0b0000000000000111
 xor
B: 0b1010010011101111,0b0111111011111100,0b1111110001111111,0b0000000001111011
 —————————————————————————–
 0b0100000000000000,0b1000000110111001,0b0111001110000000,0b0000000001111000

0 are skipped (it means that the segemnts are the same), while 1 are processed. The segment is turned on or off accordingly. For A xor B only 18 segments needs to be updated, instead of 55.

And that’s it.

Misc:

  • Aresluna editor was used to get the definition of each letter.
  • We used a custom script to automatically generate all the 110 primitives and bitsets.

Graphic Loading

The Big Font uses the 320 x 200 @ 4 colors video mode. 16K RAM needed is needed for it. So, from the 64000 bytes reserved for the scene, 16k will be used for the video mode. That leaves 47616 bytes free for the code.

And the code to render the big fonts takes ~47000 bytes, with only a few hundred bytes free.

In order to add an additional 16K graphics, the new graphic was appended after the Big Font code (with some padding).

part1.com format
+--------------+  0x00600
|              |
| Big Font     |
| code + data  |
|              |
|      |       |
|      V       |
|              |
|  end of      |
|  Big Font    |
|~~~~~~~~~~~~~~|
| padding...   | ~0x0bf00
|~~~~~~~~~~~~~~|
|              |  0x0c000
| graphics     |
| data         |
| (16K RAM)    |
|              |  0x0ffff
+--------------+

So when the part1.com file is loaded, the graphic will be loaded right where the video card expects it. And it will be displayed automatically.

Part II

Graphic
Part II: Using 320×200 @ 16

From a technical point of view, nothing interesting really happens in Part II. It is just a simple horizontal scroll that consumes almost all the CPU cycles.

[Note: Additional effects were planned for this part, but we didn’t have the time]

It uses a 320×200 @ 16 colors video mode. In order to enable this video mode in a 64k-only PCjr you have to do:

sub   ax,ax
mov   ds,ax              ;ds = 0
mov   word [0x0415],128  ;make BIOS set_video_modo believe that we
                         ; have at least 128K RAM, otherwise it won't let
                         ; us set video mode 9

Bug: the graphic won’t look that good on 64k-only PCjr. We tested this idea about 2 months before the deadline with a random graphic, and it looked Ok. So we though it was possible to use 32k video modes on the 64k-only PCjr. But one day before the release we noticed that the graphic didn’t look that good. And unfortunately we couldn’t fix it yet. Not sure whether this is a hardware limitation (it shouldn’t be, in theory), or not. We’ll try to fix this soon.

Part III

Vector fonts

Moon

Still there

[Note: Originally, we wanted to add some 3d effects in Part III. But after doing some performance tests with basic 2D polygons, we decided it was not worth it. The PCjr was too slow for what we wanted to do. We reused part of that code for the Vector Fonts.]

Vector fonts

By vector font we mean a letter that is defined by one or more polygons. I’m using a primitive to draw a line called Line08_Draw (code based on Richard Wilton book).

On top of that we implemented a draw_poly function. And each letter is defined by a list of polygons.

The benefits of vector fonts, it is to scale them up/down & rotate them for free, without losing precision.

The fonts are defined using polar coordinates (radius + angle); not the more common cartesian coordinates (x + y). This is legacy code from my 2d-polygon experiment. Using polar coordinates + lookup table is a nice and fast way to rotate polygons. But in retrospective, kind of overkill for Part III.

Render buffer

Mode 160×100 @ 16 colors (Trixter’s variation) is being used for the vector font part. And the bottom 40 rows are reserved for the fonts. That’s a total of 3200 bytes (160*40/2). We are using an additional 3200-bytes buffer (a render buffer) where the text is pre-rendered. Then the render buffer is copied row by row, one at the time, to the video buffer. The video buffer is scrolled up or down (depending on the effect). And that is basically it.

Easter Egg

Easter Egg
Easter Egg

The final part is pure PCjr BIOS code.

  • Populate the keyboard buffer with “PVM RULEZ!”
  • Put sprite data in the correct place
  • Initialize some video variables
  • …and jump to the correct place in BIOS.

The sprite data needs to be placed at 0060:0000, the same space used for the demo, so extra careful was needed to not overwrite our own init_easter_egg routine.

Misc

All the “official” PCjr video modes were used in this demo, without any
repetition.

  • Boot loader: 40×25, 80×25 (when PCjr is not detected)
  • Part I: 160×200 @ 16, 320×200 @ 4
  • Part II: 320×200 @ 16
  • Part III: 160×100 @ 16, 640×200 @ 4, 640×200 @ 2

Embarrassing bug

The demo is all about the 64k-RAM PCjr. The embarrassing part is that some graphics don’t look Ok with the 64k-RAM PCjr. The high-density video mode (80×25 text, 320×200 @ 16 colors and 640×200 @ 4 colors) are disabled in the 64K-RAM PCjr for a good reason: the PCjr can’t display them, at least, in theory.

Two months before the deadline, we did a test on a 64k-RAM PCjr, and the 320×200 @ 16 colors video mode looked correct… only because we tested with a single color graphic. We painted the screen with one color, and it looked fine.

But one day before the deadline, while we were testing a release-candidate on the 64k-RAM PCjr, we noticed that some graphics weren’t looking Ok. We panicked. We couldn’t believe what were seeing. We tried to understand what was happening, but we failed and couldn’t fix the bug.

Looks OKish from far away…
…but looks jagged from a closer look.

So we shipped the demo with this embarrassing bug. A few days after the party, and after discussing it in the PCjr forum, we realized that the 64k-RAM PCjr has the following limitations:

The system board 64K RAM is mapped at the bottom of the 1Mb address space. The system board 64Kb RAM is mapped to the next 64K bytes of address space if the the 64Kb Memory and Display Expansion option is not installed.

When inserted, the memory expansion option uses the ODD memory space, while the system memory space is decoded as the EVEN memory. Thus, when used as video memory, the memory expansion option has the video attributes while the on-board system memory has the video characters. This arrangement provides a higher bandwidth of video characters.

In addition to the eight memory modules, the expansion card has logic to do EVEN/ODD address decoding, video data multiplexing and Card Present wrap.

And that explains why the 640×200@4 graphic looked as a monochrome bitmap and the 320×200@16 looked jagged. It is related how these 2 video modes are represented. (See “PCjr Technical Reference” Section 2–55 for further details).

Monochrome when in 64K RAM only

We tried to disable the “even/odd” decoder by setting “Mode Control 1”’s High-Bandwidth register to 0. But that brings other issues. We still don’t know whether or not it is feasible to display a high-density graphic in the 64K-RAM PCjr. Although everything seems to indicate that it is not feasible.

The not-so-bad news is that the 128K-RAM PCjr (the most popular model), is as slow as the 64k-RAM version but runs the demo correctly. No glitches in the graphics.In fact, if we had targeted the 128k-RAM version, probably we could have fit everything in a single executable + our boot loader.

Published by ricardoquesada

cocos2d, unicyclist, commodore 8-bit

10 thoughts on “64K RAM Ought to be Enough: a demo for the PCjr

    1. Download the binary from here:
      http://pungas.space/pvm-64ko.zip

      then transfer the floppy image “64kought.360” to a 5 1/4 double-side / double-density floppy using, `dd` (for Linux), or `dskimage` (for DOS).

      For dd, something like this should work:
      $ sudo dd if=64kought of=/dev/fda

      For dskimage, something like this should work:
      c:> dskimage 64kought.360 0:40:2:9

    1. At least emulators like DosBox-x doesn’t fully emulate the PCjr… only certain parts of it. This demo uses many PCjr only features that were not common to use back in the 80s. So I guess it is not a priority to for the emulators to support “weird” PCjr features.

      1. DOSbox is not a system emulator, it implements ‘a pc’ with embedded DOS.

        Emulators like VARCem, 86Box and PCem are true system emulators, cycle-times and all,
        and I am going over our PCjr implementation ‘by the letter of the TRM’.

        So.. the boot disk just cycles. I do see an ‘ERROR’ flash by just before the first reboot,
        after that, it just keeps reading and rebooting.

        The other disk, with the separate parts, is more succesful. RUNME does works, DETECT
        detects the emulator as a PCjr model A (not sure if this is only based on timing – we match
        cycles exactly for example when running 88MPH …) but it does detect it.

        Part1 starts, i hear some noise, stuff is drawn in the lower-left corner, and then it dies…

        Any suggestions?

        –fred

      2. ah! good to know that you are working on support PCjr. That’s great news!
        So, you are using the real BIOS, correct? And by TRM, did you mean “Technical Reference Manual”?

        If so, then those are the the two requirements. Although the TRM is huge.
        Does your emulator have a debugger? If so, then it would be kind-of-easy to know where it is failing.

        The difference between demo_pvm.360 and demo_pvm_emu.360 is that the “emu” one is more friendly to DosBox-x… It is compiled with the “EMULATOR” defined. And grepping the code for EMULATOR should show some hints regarding to what is failing.

        For example, detecting a key press is done with “in” in the real version, but uses BIOS calls for the “emu” one.
        https://gitlab.com/ricardoquesada/pcjr-flashparty-2018/blob/master/part1/part1.asm#L367

        Detect should detect it as model “B”. If it detects it as “A”, then it is a PCjr that is a bit too fast… for example some memory expansions affect the speed. But a regular PCjr should be a “B”.
        https://gitlab.com/ricardoquesada/pcjr-flashparty-2018/blob/master/detect/detect.asm#L179

        it calls my attention that boot doesnt work. It is pure BIOS calls:
        https://gitlab.com/ricardoquesada/pcjr-flashparty-2018/blob/master/boot_loader/boot_loader.asm#L202

        One thing that I would do, is take a look at the part1 source code, and search for all the “in” and “out”. They documented. And make sure that you have them implemented.
        https://gitlab.com/ricardoquesada/pcjr-flashparty-2018/blob/master/part1/part1.asm

      3. > ah! good to know that you are working on support PCjr. That’s great news!
        > So, you are using the real BIOS, correct? And by TRM, did you mean “Technical Reference Manual”?
        Yes.

        > Does your emulator have a debugger?
        Not a good one, sadly.

        > For example, detecting a key press is done with “in” in the real version, but uses BIOS calls for
        > the “emu” one.
        It should work just fine using IN on port 60H. We emulate the actual hardware (which passes the
        ROM extended diags, for example), not just the BIOS API which DOSbox emulates.

        > Detect should detect it as model “B”. If it detects it as “A”, then it is a PCjr that is a bit too fast…
        > for example some memory expansions affect the speed. But a regular PCjr should be a “B”.
        Yes, I noticed the two numbers in that speed test. I will make a modified ‘detect’ that shows the
        number as detected on the emulator.

        What is strange: the ‘real’ demo does not show the ‘detect’ info at all…

        > it calls my attention that boot doesnt work. It is pure BIOS calls:
        Yes. My next step is to modify Boot to show some additional info when an error occurs, and
        then stop so I can actually see the error 😉

        Can we continue in email? My address is [EDITED]

        –fred

Leave a Reply to Fred Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: