TOP

The Mantle Display

A personal computer isn't of much use without some kind of relatively high bandwidth output device. Mantle assumes the presence of at least one bitmapped graphics display capable of rendering at least two contrasting colors, typically black and white, or black and amber, etc.

Resolution

A bitmapped display consists of a matrix of on/off bits called pixels, arranged in such a manner as to form an image on a viewing device. This could be a CRT, an LCD display, or a laser projection system at a rock music concert. The data that feeds this display is frequently called a frame buffer.

A video display needs periodic repainting in order to keep the image on the screen, just as a movie projector needs to keep flashing individual picture frames onto the screen. The memory containing the current image to display is called a frame buffer because, like a frame on a film strip, it holds the image to show.

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+  ----  +--+
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |    ^   |  | = 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    |   +--+
|  |  |  |  |  |  |  |##|##|  |  |  |  |  |  |  |    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    |   +--+
|  |  |  |  |  |  |##|  |  |##|  |  |  |  |  |  |    |   |##| = 1
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    |   +--+
|  |  |  |  |  |##|  |  |  |  |##|  |  |  |  |  |    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    |
|  |  |  |  |##|  |  |  |  |  |  |##|  |  |  |  |    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    |
|  |  |  |##|  |  |  |  |  |  |  |  |##|  |  |  |    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+     
|  |  |  |##|##|##|##|  |  |##|##|##|##|##|  |  |  12 bits
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+     
|  |  |  |  |  |  |##|  |  |##|##|##|##|##|  |  |    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    |
|  |  |  |  |  |  |##|  |  |##|##|  |  |  |  |  |    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    |
|  |  |  |  |  |  |##|  |  |##|##|  |  |  |  |  |    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    |
|  |  |  |  |  |  |  |##|##|##|##|  |  |  |  |  |    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    |
|  |  |  |  |  |  |  |  |##|##|  |  |  |  |  |  |    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    |
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |    V
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+  ----

|                                               |
|<-----------------  16 bits  ----------------->|

The total number of bits in the display is often expressed as two figures: a width and a height, each measured in pixels. This pair of numbers is frequently referred to as a resolution.

In other fields of endeavor, a resolution implies a measurement of precision. If you were to keep the display device the same physical size, then a larger number of pixels implies a finer degree of rendering precision. This is why display sizes are frequently referred to as "resolutions," even though they're not really measurements of display precision per se.

Different host computers can display a different quantity of pixels. For instance, a Z180-based computer equipped with a TMS9918A VDP video chip is limited to 256 pixels across by 192 pixels down. More conventional displays offer a resolution of 640x400, 640x480, or even 800x600. Higher resolutions are possible if you have enough resources.

Video Memory

As of this writing, the Kestrel-2/EX virtual machine emulates a video display FPGA core I developed many years ago called the Monochrome Graphics Interface Adapter, and places its video frame buffer at the very end of RAM. The start address depends on the video resolution, according to the following table:

Resolution Start Address
256x192 $FFFFFFFFFFFFE800
320x200 $FFFFFFFFFFFFE0C0
512x342 $FFFFFFFFFFFFAA80
640x400 $FFFFFFFFFFFF8300
640x480 $FFFFFFFFFFFF6A00
800x600 $FFFFFFFFFFFF15A0
1024x768 $FFFFFFFFFFFE8000
1280x1024 $FFFFFFFFFFFD8000

You can calculate the base address with the following function of a screen resolution:

uint64_t
base_address_from_resolution(int width, int height)
{
        return (uint64_t)((int64_t)(-(width * height / 8)));
}

Unlike other video chips you might be familiar with, the MGIA (by design!) exposes no registers to the host processor. Thus, at present, there is no mechanism to relocate the frame buffer. There is also no mechanism defined to support multiple frame buffers. I'd like to support these features some day, but I need to think about how to do so while retaining the conceptual simplicity of the Kestrel-2/EX virtual machine. For now, you can access only one display.

Single vs. Double Buffered

You would be forgiven for thinking that changing a location within the video frame buffer would result an in instantaneous update on the virtual display device. This is not guaranteed to happen. Depending on the technology stack your specific emulator is built on top of, it might work fine. As soon as you update a frame buffer location, you should see a change on the screen right away. Such emulators are single-buffered, because there is exactly one buffer.

On other systems, however, you may need to invoke a Mantle system call (e.g., ecBeginPaint and ecEndPaint) to make updates visible on the display. There is a hidden second buffer which the virtual CPU cannot directly reach. Such emulators are double-buffered, as it lets you make bulk updates to the frame buffer, then "commit" the changes in one swoop.

NOTE: This is an active area of research! It remains to be seen if this behavior is ultimately desirable or not. The k2 emulator is double-buffered.

Why Monochrome?

Nearly all contemporary video displays offer some manner of color support. However, Mantle still treats the display as though it were monochrome. This is a conscious decision for several reasons.

Monochrome is by far and away the most compatible video display format; whether your display device is indexed or true-color, whether your palette is fixed black and white or is a billion colors, you can always render a monochrome image accurately. Though colors may be transformed through a color expansion process, there is no loss of color information.

                           _________
                          /        /
                         /        /   1bpp image
                        /________/
                             
                             ||
                             ||
                             
                     ( color expansion )
                     
                             ||
                            _||_
                            \  /
                             \/
         _________________________________________________
        /                                                /
       /                                                /
      /                                                /
     /                                                /
    /                                                /  32bpp image
   /                                                / 
  /                                                /
 /                                                /
/________________________________________________/

The reverse is not always true; displaying a true-color image on a 16-color display will always require some form of lossy quantization (such as extensive and computationally expensive dithering) to achieve.

         _________________________________________________
        /                                                /
       /                                                /
      /                                                /
     /                                                /
    /                                                /  32bpp image
   /                                                / 
  /                                                /
 /                                                /
/________________________________________________/

                             ||
                             ||
                             
                      ( quantization )
                     
                             ||
                            _||_
                            \  /
                             \/
                           _________
                          /        /
                         /        /   4bpp image
                        /________/

Monochrome uses the least amount of memory to store assets or to display, various compression techniques notwithstanding.

Last, but certainly not least, monochrome graphics greatly simplifies user interface/experience design choices. If you can draw it out on a piece of paper with just a pencil, you can make a comparable looking user interface. It puts the focus back on the problem being solved, and relieves the designer of having to worry about, or possibly setting new, current UI trends.

Bitmaps

A Kestrel-2/EX frame buffer is arranged as a linear array of rows. A 480 pixel tall display would have memory laid out like so:

$FFFFFFFFFFFF6A00
|
V
+-------+-------+-------+---------+---------+---------+---------+
| row 0 | row 1 | row 2 |  . . .  | row 477 | row 478 | row 479 |
+-------+-------+-------+---------+---------+---------+---------+
                                                                ^
                                                                |
                                                $FFFFFFFFFFFFFFFF

Each row, in turn, consists of a linear array of bytes. Since you can fit 8 pixels to a byte, a 640-pixel wide display requires 80 bytes per row. Thus, each row consists of 80 bytes.

NOTE. Recall that the base address of a frame buffer's bitmap will depend on the screen resolution!

                             Halfword +0
                               Columns
         0  1  2  3  4  5  6  7    8  9  10 11 12 13 14 15
         
                Byte +0                    Byte +1
                Columns                    Columns
         0  1  2  3  4  5  6  7    0  1  2  3  4  5  6  7
         
        +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+
Row 0   |  |  |  |##|##|  |  |  | |  |##|##|  |  |  |  |  |
        +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+
Row 1   |  |  |##|##|##|##|  |  | |  |##|##|  |  |  |  |  |
        +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+
Row 2   |  |  |##|##|##|##|  |  | |  |##|##|##|##|##|  |  |
        +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+
Row 3   |  |##|##|  |  |##|##|  | |  |##|##|  |  |##|##|  |     . . .
        +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+
Row 4   |  |##|##|##|##|##|##|  | |  |##|##|  |  |##|##|  |
        +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+
Row 5   |##|##|  |  |  |  |##|##| |  |##|##|  |  |##|##|  |
        +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+
Row 6   |##|##|  |  |  |  |##|##| |  |##|##|##|##|##|  |  |
        +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+
Row 7   |  |  |  |  |  |  |  |  | |  |  |  |  |  |  |  |  |
        +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+
                                
                                .
                                .
                                .
                                

Based on the information presented so far, you might think that the left-most pixel displayed corresponds to the most significant bits of each byte in a row of pixels. In other words, you might think that dots are drawn from the MSB of a byte to the LSB of the same byte.

This would be the case if the host processor were big-endian. However, this kind of "most-significant bit first" would prevent the RISC-V core from processing pixels in bulk using half-word, word, or double-word instructions. The figure below illustrates why.

Some might argue this is a good reason why big-endian processors are better. However, I make no value judgement either way; it is what it is.

Let us consider the case where we want to scroll the display to the left. You might think that you can just use the CPU's left-shift instruction to shift each row left, 64 bits at a time.

NOTE. Unlike CISC processors, the idea of a "byte-wide register" or a "word-wide" register just doesn't exist in RISC processors. Registers are always maximally wide, so for the purposes of the Kestrel-2/EX, all logical operations always occur at 64-bits wide.

But, because it is a little-endian processor, bits that overflow out of one byte will shift counter to the desired scroll direction (to the right!) into the next byte.

+--+--+--+--+--+--+--+--+       +--+--+--+--+--+--+--+--+        +--+--+--+--+--+--+--+--+         
|0 |1 |2 |3 |4 |5 |6 |7 |<--0   |8 |9 |10|11|12|13|14|15|<---.   |16|17|18|19|20|21|22|23|<---.
+--+--+--+--+--+--+--+--+       +--+--+--+--+--+--+--+--+    |   +--+--+--+--+--+--+--+--+    |
 |                               |                           |    |                           |
 `-----------------------------------------------------------'    |                           |
                                 |                                |                           |
                                 `------------------------------------------------------------'
                                                                  |
                                                                  `-------------------->-- . . . 

However, if we logically flip the direction that bits are drawn onto a display device, namely if we draw the least significant bit as the left-most pixel, we can reconcile shifts across byte boundaries effortlessly.

+--+--+--+--+--+--+--+--+       +--+--+--+--+--+--+--+--+        +--+--+--+--+--+--+--+--+         
| 7| 6| 5| 4| 3| 2| 1| 0|<--0   |15|14|13|12|11|10| 9| 8|<---.   |23|22|21|20|19|18|17|16|<---.
+--+--+--+--+--+--+--+--+       +--+--+--+--+--+--+--+--+    |   +--+--+--+--+--+--+--+--+    |
 |                               |                           |    |                           |
 `-----------------------------------------------------------'    |                           |
                                 |                                |                           |
                                 `------------------------------------------------------------'
                                                                  |
                                                                  `-------------------->-- . . . 

By rendering bytes left to right, but bits from right to left within each byte, we now comply with the way a little-endian processor lays out multi-byte information in memory. (Astute readers may also recognize that we must also swap shift directions to affect a given scroll direction; that is, to scroll left, you now have to shift right, and vice versa.)

For example, here's the hex encoding for the lower-case letter b shown above as it would appear for the Kestrel-2/EX's monochrome bitmap format. Notice how the bits are flipped in each byte.

        Normal                     Flipped                    Hex
        
        +--+--+--+--+--+--+--+--+  +--+--+--+--+--+--+--+--+
Row 0   |  |##|##|  |  |  |  |  |  |  |  |  |  |  |##|##|  |  $06
        +--+--+--+--+--+--+--+--+  +--+--+--+--+--+--+--+--+
Row 1   |  |##|##|  |  |  |  |  |  |  |  |  |  |  |##|##|  |  $06
        +--+--+--+--+--+--+--+--+  +--+--+--+--+--+--+--+--+
Row 2   |  |##|##|##|##|##|  |  |  |  |  |##|##|##|##|##|  |  $3E
        +--+--+--+--+--+--+--+--+  +--+--+--+--+--+--+--+--+
Row 3   |  |##|##|  |  |##|##|  |  |  |##|##|  |  |##|##|  |  $66
        +--+--+--+--+--+--+--+--+  +--+--+--+--+--+--+--+--+
Row 4   |  |##|##|  |  |##|##|  |  |  |##|##|  |  |##|##|  |  $66
        +--+--+--+--+--+--+--+--+  +--+--+--+--+--+--+--+--+
Row 5   |  |##|##|  |  |##|##|  |  |  |##|##|  |  |##|##|  |  $66
        +--+--+--+--+--+--+--+--+  +--+--+--+--+--+--+--+--+
Row 6   |  |##|##|##|##|##|  |  |  |  |  |##|##|##|##|##|  |  $3E
        +--+--+--+--+--+--+--+--+  +--+--+--+--+--+--+--+--+
Row 7   |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  $00
        +--+--+--+--+--+--+--+--+  +--+--+--+--+--+--+--+--+