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:
- Setting SYNC.
- 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.