Skip to main content

Graphics Overview

State of the part

The graphics module was developed and built by Jonathan Baumann, Adrian Dapprich and Oliver Valta. The module is connected to the CPU and working, however, the length of the cable and quality of connections limits the clock speed at which the module works properly.

Design Choices

Design goals for our graphics module included

  • a text mode, so that simple applications do not need to deal with drawing fonts
  • an image mode supporting color
  • VGA output for availability of compatible monitors
  • stable video signal independent of CPU clock speed (down to single-stepping the CPU)

The main difficulty is that the graphics unit operates largely independently of the CPU, but needs to share some memory with it. This memory needs to be writable from the CPU and readable from the graphics unit, but those accesses must not happen at the same time.

While historical systems could handle this with timing the CPU was only allowed to access this memory during the blanking intervals, when no image was being drawn we wanted to be independent of CPU clock speed, as that would change a lot during development.

Therefore, a hardware mechanism for preventing simultaneous accesses was necessary.

We considered two options:

  • A "lock" for the graphics memory chip. While the graphics unit is drawing, if the CPU tries to access this memory, its clock is halted until the graphics unit completes the current line or frame. If the CPU is currently accessing memory at the end of a blanking interval, since the graphics unit cannot wait, it reads zeroes instead (drawing a black image until the CPU memory access is complete).
  • Two separate memory chips, so the CPU and graphics unit can access one each, with no conflicts. The CPU can switch the chips around as wanted. Since the CPU will never switch during a write access and the graphics unit only ever reads, this switch can occur at any moment while the graphics unit is drawing, without issues.

We decided on the latter option, as it provides a video signal without any interruptions, and is less dependent on the rest of the CPU. Apart from the address bus and read/write signals shared with other I/O devices, it only requires a single control line for switching the graphics memory.

This choice is not without downsides, however:

  • It requires two RAM chips instead of one, and a lot of additional chips for switching the address and data lines connected to them.
  • It is not sufficient to only update the memory locations that changed compared to the previous frame, as that frame resides in the other memory chip.
    Instead, all locations that differ to the frame before that one must be updated.

Features

In accordance with the design choices mentioned above, our design has the following features:

  • VGA output at 640x480, 60Hz
  • Image mode with a resolution of 160x120 pixels at 2 bits per color per pixel (As the VGA output is 640x480, everything is scaled up by a factor of 4)
  • Text mode supporting 80x30 characters, monochrome (using the full 640x480 resolution)
  • Per-line toggling of image/text modes (per text line, corresponding to 4 rows of pixels in image mode)
  • Two separate frame buffers, which can be switched by the CPU at any point:
    • One buffer is always available for read and write accesses, without interfering with the VGA output
    • Stable video output even at extremely low clock speeds

Interface with CPU

The graphics unit connects to the common address and data busses (including the ADDR16 line), as well as the BUSREQ and IO/MEM_FROM_DBUS control lines. Additionally, it connects to the FB_SEL and ~FB_SEL control lines (the latter simply because we could save a chip by using one already available in the control unit).

The graphics unit listens to addresses in the upper half of the I/O region, so any address with ADDR16 and the highest bit of the address bus set.

Using the CPU

To write data to the GPU, write to any address in the range 0x8000-0xFFFF following a prefix_a16 instruction. The data can be read again in a similar way.

To toggle between the two frame buffers, execute a switch_fb instruction.

Keep in mind that graphics memory is not cleared on power-up, so it may contain random values.

Mode Selection

The graphics unit supports two modes (text and image), which can be selected independently for every line of text/four rows of pixels. The active mode for a line is determined by the value at address 0b111y yyyy y101 1110, where y is the index of the line (This corresponds to the 94th byte of a text line, which is not part of the drawn text) If this byte is 0, text mode is used. If it is 1, image mode is used.

Text Mode

Text mode supports 30 lines of 80 characters, at 8x16 pixels per character (making full use of the 640x480 resolution of the VGA mode), but only in white on black.

Characters are encoded with an ASCII-like encoding, see below: Character ROM E.g. the code for the character 'A' is 0x41 as it is in the (0-based) 4th row and 1st column of the character ROM.

To display a character, write its code to address 0b111y yyyy yxxx xxxx, where y is the row and x the column in which the character should appear (with the origin in the top left corner).

Image Mode

In image mode, each pixel is encoded by a single byte in the following way: 0b__rrggbb where r is the red component, g is the green component, and b is the blue component. The first two bits are unused.

To write a pixel, write its encoding to address 0b1xxx xxxx xyyy yyyy, where x is the x-coordinate and y is the y-coordinate of the pixel (with the origin in the top left corner).