One True Sprite

GIME Hardware Cursor Extension Proposal

Overview

The GIME video system can provide a hardware mouse cursor by adding a small independent cursor overlay. The cursor is not stored in normal CoCo memory and does not require software to modify the display memory.

The cursor is drawn after the normal video image is generated. Software only needs to provide:

  • A cursor shape.
  • A cursor position.
  • Two palette selections.
  • Enable/disable control.

This provides a fast, flicker-free cursor suitable for graphical user interfaces while requiring very little programming overhead.

The hardware cursor is especially useful for GUI applications because moving the mouse pointer no longer requires saving the background, drawing the cursor, restoring the background, or modifying screen memory.


Registers

The cursor uses two memory-mapped registers.

Address Name Description
$FF96 Cursor Data Cursor shape upload or cursor position data
$FF97 Cursor Control Cursor enable, colors, and data mode control

Cursor Control Register ($FF97)

Bit  7   6   5   4   3   2   1   0
    EN  W1  W0  B1  B0  --  SYNC SIZE

Bit 7: EN – Cursor Enable

0 = Cursor disabled
1 = Cursor displayed

Bits 6-5: W1:W0 – Foreground Palette Slot

Selects the GIME palette entry used for foreground cursor pixels.

Valid values:

00 = palette entry 0
01 = palette entry 1
10 = palette entry 2
11 = palette entry 3

Bits 4-3: B1:B0 – Background Palette Slot

Selects the GIME palette entry used for background cursor pixels.

Valid values:

00 = palette entry 0
01 = palette entry 1
10 = palette entry 2
11 = palette entry 3

Bit 2: Reserved

Unused. Reads back as 0. Reserved for future expansion.


Bit 1: SYNC – Enter Shape Upload Mode / Upload Status

SYNC is both readable and writable, and its meaning differs by direction.

Writing SYNC

Writing a one switches the Cursor Data register ($FF96) into shape-upload mode for the next sixteen writes, and resets the upload pointer to zero.

Write 0 = no action
Write 1 = begin (or restart) a 16-byte cursor shape upload

This is the only way $FF96 enters shape-upload mode. At all other times – including immediately after power-on, and immediately after the 16th shape byte is written – $FF96 is in position mode (see below). Software never needs to write SYNC to move the cursor; it is only needed when replacing the cursor’s image.

Reading SYNC

0 = A shape upload is in progress and not yet at byte zero
1 = $FF96 is ready for a fresh shape upload

In practice this reads as 1 essentially all the time, since $FF96 sits in position mode – upload pointer parked at zero – except for the brief window during an actual upload.


Position Mode (default state of $FF96)

Unlike shape upload, position mode is not something software enters – it is where $FF96 always is unless a shape upload is in progress. Every write to $FF96 while in position mode is consumed by a free-running, self-aligning 3-byte cycle:

Byte 0: Cursor X high bits
Byte 1: Cursor X low byte
Byte 2: Cursor Y position

…then it repeats: byte 3 is X high again, byte 4 is X low, and so on. No control-register write is needed to start, continue, or reset this cycle – software can simply POKE three bytes per cursor move, forever, with zero per-move overhead beyond the position data itself. This is the expected hot path for mouse polling.

The cursor position range is:

X: 0-639
Y: 0-199

The X high byte only uses the lower two bits:

Bit 1-0 = X bits 9-8

If a shape upload (triggered by SYNC) interrupts a position write partway through its 3-byte cycle, the position pointer is reset to zero as part of entering upload mode, and reset to zero again as part of leaving it. This guarantees the next 3 writes to $FF96 after an upload finishes are always a clean, aligned position triple – a position write can never be left half-consumed by an upload landing in the middle of it.


Bit 0: SIZE – Cursor Size

Selects cursor rendering size.

0 = 8x8 cursor
1 = 16x16 cursor

When 16×16 mode is selected, each cursor pixel is doubled horizontally and vertically.

The cursor image memory remains the same 8×8 image in both modes.


Cursor Shape Data Register ($FF96)

The cursor image is an 8×8 two-bit-per-pixel image.

The image requires sixteen bytes.

Each row uses two bytes:

Byte 0: Plane 0, Row 0
Byte 1: Plane 1, Row 0

Byte 2: Plane 0, Row 1
Byte 3: Plane 1, Row 1

...

Byte 14: Plane 0, Row 7
Byte 15: Plane 1, Row 7

Bits are arranged:

Bit 7 = leftmost pixel
Bit 0 = rightmost pixel

Each pixel is decoded as:

Plane 1 Plane 0 Result
0 0 Transparent — underlying pixel unchanged
0 1 Background palette color
1 0 Foreground palette color
1 1 XOR — underlying pixel value inverted

Clarification: XOR Mode Semantics

The original pixel decode table left the “Foreground XOR Background” result ambiguous — it could be read as either a fixed third color (foreground palette color combined with background palette color), or as a true XOR against whatever is already on screen. Only the latter reading satisfies the stated goal that “the cursor color remains visible over changing backgrounds,” so the specification is clarified as follows:

XOR pixel behavior

For pixels where both bitplanes are set, the cursor does not draw a color at all. Instead, it inverts whatever pixel is already present at that screen location: output_pixel = existing_pixel XOR $FFFFFF This is the same trick used by hardware cursors on other classic platforms (and by early VGA/Mac text cursors): because the operation is applied to the live framebuffer contents rather than to a fixed cursor color, the cursor pixel is guaranteed to differ from its background regardless of what that background is — solid color, photo, or another rapidly animating sprite. Inverting twice restores the original pixel, so no background save/restore buffer is required even in XOR regions.

Foreground and background palette slots (W1:W0 and B1:B0) have no effect on XOR pixels — those bits only apply to the solid-color (10/01) cases. XOR pixels are purely a function of the two bitplane bits and the framebuffer they land on.

Practical implication for cursor art

Because XOR pixels invert rather than paint, they read differently depending on the underlying pixel’s luminance and hue — a common tradeoff for XOR cursors. Designers using XOR pixels (e.g., a thin outline meant to “always show”) should expect variable, not fixed, apparent color, and treat it as a contrast trick rather than a color choice.


Uploading a Cursor Shape

A cursor shape is uploaded by:

  1. Setting SYNC.
  2. Writing sixteen bytes to Cursor Data.

After the sixteenth byte, $FF96 automatically reverts to position mode – no further register write is needed before moving the cursor again.

Example 6809 code:

CURSOR_CTRL  EQU $FF97
CURSOR_DATA  EQU $FF96

load_cursor:
        lda   CURSOR_CTRL
        ora   #%00000010      ; SYNC - begin shape upload
        sta   CURSOR_CTRL

        ldx   #cursor_shape
        ldb   #16

upload_loop:
        lda   ,x+
        sta   CURSOR_DATA
        decb
        bne   upload_loop
                               ; $FF96 is back in position mode here
        rts

Moving the Cursor

Cursor position is the default behavior of Cursor Data – no control register write is needed at all. Software simply writes three bytes, high-byte-first, matching the 6809’s natural big-endian storage order:

CURSOR_DATA  EQU $FF96

set_cursor_position:
        lda   cursor_x         ; X high bits (bits 9-8)
        anda  #%00000011
        sta   CURSOR_DATA

        lda   cursor_x+1       ; X low byte
        sta   CURSOR_DATA

        lda   cursor_y
        sta   CURSOR_DATA

        rts

Because this is the default mode of the register, the same three writes can be issued every frame from a mouse-polling loop with no per-move setup cost. A shape upload landing between two calls to this routine cannot desynchronize it – see SYNC above.


Example Cursor Shape: Black Arrow with White Outline

The cursor palette selections can be used to create a traditional high-contrast pointer.

For this example:

  • Background palette slot = black
  • Foreground palette slot = white
  • Transparent pixels leave the underlying display unchanged.

The cursor image:

W.......
WB......
WBB.....
WBBB....
WBBBB...
WBBBBB..
WBBBBBB.
WWWWWWWW

Where:

  • . = transparent
  • B = solid black pixel (background palette color)
  • W = white outline pixel (foreground palette color)

The cursor hotspot is always the top-left corner of the 8×8 cursor image. In this example, the active pointing location is the upper-left W pixel. Software positions the cursor by writing the desired screen coordinate of this corner.

The two bitplanes are:

Plane 0 (background/black pixels)

Row 0: 10000000  $80
Row 1: 01000000  $40
Row 2: 01100000  $60
Row 3: 01110000  $70
Row 4: 01111000  $78
Row 5: 01111100  $7C
Row 6: 01111110  $7E
Row 7: 00000000  $00

Plane 1 (foreground/white outline pixels)

Row 0: 10000000  $80
Row 1: 10000000  $80
Row 2: 10000000  $80
Row 3: 10000000  $80
Row 4: 10000000  $80
Row 5: 10000000  $80
Row 6: 10000000  $80
Row 7: 11111111  $FF

The cursor upload data is interleaved by row:

cursor_arrow:
        ; Plane 0, Plane 1

        fcb $80,$80
        fcb $40,$80
        fcb $60,$80
        fcb $70,$80
        fcb $78,$80
        fcb $7C,$80
        fcb $7E,$80
        fcb $00,$FF

When the SIZE bit is enabled, each cursor pixel is doubled horizontally and vertically, producing a 16×16 cursor while preserving the same top-left hotspot location.


Crosshair Cursor

Useful for drawing programs:

...X....
...X....
...X....
XXXXXXXX
...X....
...X....
...X....
...X....

Box Cursor

Useful for text selection:

XXXXXXXX
X......X
X......X
X......X
X......X
X......X
X......X
XXXXXXXX

Benefits

A hardware cursor provides several advantages:

  • GUI applications no longer need to redraw the mouse pointer.
  • Screen memory is never modified by cursor movement.
  • Cursor movement requires only three writes, with no control-register overhead – position mode is the default state of Cursor Data.
  • Cursor shape changes require only sixteen writes, plus one SYNC write to begin.
  • The cursor works in all GIME display modes.
  • No software background save/restore routines are needed.
  • Cursor updates are independent of application graphics.

The interface requires only two registers while providing a complete programmable cursor system compatible with the GIME design philosophy.