Cursors and primitives and resolutions, oh my!

Anything QL Software or Programming Related.
Post Reply
User avatar
Dave
SandySuperQDave
Posts: 2863
Joined: Sat Jan 22, 2011 6:52 am
Location: Austin, TX
Contact:

Cursors and primitives and resolutions, oh my!

Post by Dave »

The QL cursor is a bit of a beast.

Here’s what I believe happens when the cursor blinks: when it toggles, the OS reads the video contents under the cursor, modifies the affected bytes/words, and writes them back. If a single pixel in a byte/word is touched, it still has to load/store the whole thing. It may even stash the original data somewhere to restore on the next blink, or maybe it’s using a flash/toggle bit — I’m not 100% sure which. Net effect: it’s CPU-intensive and can show little glitches on the left/right edges. Even though it’s infrequent, it’s distracting. The entire process is inefficient, too.

Same basic story with mouse pointers on GUI systems, just more obvious: the pointer may need redrawing every frame, it’s larger, and it overlaps more bytes/words.

My video card handles this differently with hardware pointers. I can preload two pointers (A and B) into a small area of the frame buffer. They’re 4 bpp images: 1 bit is a mask (transparent), and 3 bits select color via a little LUT, so you can map them to any colors you like. Sizes are 16×16 or 32×32, and the pixel pitch matches whatever mode is active. You flip A/B on/off with a simple register write, move them by writing X/Y, and they’re not written into VRAM at all — they’re spliced in hardware into the video stream. So the OS basically says “put this here” and it happens immediately (dozen-ish instructions).

There’s also a 1-bit overlay bitmap (think OSD): any X×Y size, any on-screen position, any single color (or use it as a mask). That makes things like “PLAY>” overlays, menus, or a blinking text cursor trivial. Real-world example: I’ve used it to cover the screen in black while I redraw the frame buffer underneath, then unblank it. That keeps sync happy, doesn’t touch VRAM, and essentially emulates the 8301 screen-blanking behavior via a register.

The chip also has a hardware rectangle fill. You give it a rectangle and a color; it fills VRAM while the CPU carries on. That maps nicely to window clears and other chunky UI ops where software loops are slow. The catch: if the OS keeps a shadow copy of the frame buffer, you still need to mirror the change there, so the classic strategy is “do the hardware fill right now for instant visual response, then let the CPU update the shadow copy afterward.” You don’t keep the full throughput win, but you do keep the perception win.

Some concrete numbers for a 960×540 screen at 4 bpp (2 pixels/byte): that’s 259,200 bytes to write for a full-screen solid fill. On a 7.5 MHz 68008 with an 8-bit bus and zero wait states, a very tight assembly loop takes about 0.32 s. The hardware rectangle fill does it in about 0.028 s. At 25 fps, that’s roughly 8 frames vs 0.7 frames. You can see the difference. With a 16-bit 68000 at 30 MHz this shrinks a lot, but then the video chip’s FIFO starts to become the bottleneck.

I’m working on adding blit capabilities next — copying regions within the display and to/from off-screen VRAM. That would let me cache the background under windows and restore instantly when they move/close. Conceptually this is cloning a VIDC20-ish feature set and extending it. Outputs today: A digital video port that looks a lot like HDMI (but not called that) and VGA.

OS integration: I think these are “relatively difficult” (extensive work!) to fold into SMSQ/E, and “harder” (almost but not quite impossible) to spoon into Minerva. A common driver for both would be lovely if doable. I’ve got Aurora-mode compatibility to the point where Aurora drivers/GD2 just work in 8 or 16 bpp modes. All non-packed-pixel formats work in any resolution. 800x600 @ 65 fps or 1024x768 @ 50 fps are nice. On an HD TV, 960 x 540 @ 25 fps upscales exactly 2:1 and looks incredible. Truly rock solid corner to corner.

To make this approachable without OS changes, I’m writing an assembly library and BASIC procedures/functions that wrap the features.

Hardware status: it already plugs in and makes pictures, and it can act as a second screen on a BBQL with some caveats. I page the 2 MB frame buffer in 32 KB windows into an expansion slot region, and I map the (extensive) register block into internal I/O. That paging dance is because the QL’s 68008 only has a 1 MB address space. That’s what originally pushed me toward a 68SEC000 to get a 4 MB map. As a standalone video card, it probably wants its own local CPU; once you do that, it goes much, much faster.

This design is meant to carry me from 68000 → 68030. It’ll “work” on 68040/060, but eventually the CPU outruns the video chip. I plan to add a 16-bit FIFO when that bottleneck shows up. Right now, writes are roughly 3× faster than reads.

A few things I’d love feedback on:
  • * Does anyone have a definitive description of how the QL cursor blink is actually implemented (byte vs word granularity, any “flash bit,” whether it stashes original bytes or repaints each toggle)?
    * Best hook points in SMSQ/E and Minerva to drive: hardware pointer enable/move, overlay updates for OSD/menus/cursor, and rectangle fills (plus a lazy shadow-buffer update afterward) — without tearing up existing code paths. I suspect these are delegated to extension functions, which is ideal as it keeps SMSQ/E consistent across branches.
    * Edge cases worth testing: mixed depths, odd widths, partial-byte cursor overlays, etc.
If anyone wants pseudocode of what I'm implementing, I can post that soon. I've been focusing on bring up register sequences for initial configuration, not later processed like primitives or altering LUTs. I need to triple check that and clean it up.

Thanks for reading this far!


User avatar
NormanDunbar
Forum Moderator
Posts: 2512
Joined: Tue Dec 14, 2010 9:04 am
Location: Buckie, Scotland
Contact:

Re: Cursors and primitives and resolutions, oh my!

Post by NormanDunbar »

Morning Dave,

Interesting post. And way above my knowledge, but I'll be following along.

Cheers,
Norm.


Why do they put lightning conductors on churches?
Author of Arduino Software Internals
Author of Arduino Interrupts

No longer on Twitter, find me on https://mastodon.scot/@NormanDunbar.
User avatar
janbredenbeek
Super Gold Card
Posts: 724
Joined: Wed Jan 21, 2015 4:54 pm
Location: Hilversum, The Netherlands
Contact:

Re: Cursors and primitives and resolutions, oh my!

Post by janbredenbeek »

It's actually a scheduler task that flashes the cursor by printing and unprinting a rectangle on the current position using OVER -1 (XOR) impressing... nothing to do with hardware.

Minerva has fixed the edge glitches and allows the colour and shape of the cursor to be modified - e.g. POKE !124!51,76 gives you a flashing underscore.


User avatar
tofro
Font of All Knowledge
Posts: 3202
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: Cursors and primitives and resolutions, oh my!

Post by tofro »

SMSQ/E has a more sophisticated cursor handling:
  • The cursor is no longer a simple rectangle, but rather a loadable sprite (i.e. free-form, as long as it''s 6x10)
  • Because it's a sprite, it can be more than just a simple red blob, but anything within the limits of screen depth (i.e. multi-colour).
  • Cursors can be modified on a per-job base
Cursor sprite support can be switched off, so there is a fallback to "traditional" cursor handling.


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
User avatar
ql_freak
Super Gold Card
Posts: 601
Joined: Sun Jan 18, 2015 1:29 am

Re: Cursors and primitives and resolutions, oh my!

Post by ql_freak »

XOR Painting is great! I miss this in e.g. Pythons TKInter (at least I have not found, that this is possible). On the QL it's reltively easy to paint a hair wire, which you can move over the screen. First print it to show it (all pixels below it are inverted), to move it print it again (restores all pixels) and print it one pixel to the left (right) or/and top (bottom).


http://peter-sulzer.bplaced.net
GERMAN! QL-Download page also available in English: GETLINE$() function, UNIX-like "ls" command, improved DIY-Toolkit function EDLINE$ - All with source. AND a good Python 3 Tutorial (German) for Win/UNIX :-)
Post Reply