The Apple II hi-res screen sits on 8KB of memory, so it should be possible to erase it quickly. The Applesoft HGR
command is slow enough that the "Venetian blind" erasure is clearly visible. Clearing the screen to a color with CALL 62454
feels even slower. Why is this?
1 Answer
If you look at the screen clear code in the Applesoft BASIC ROM, you'll find this:
f3f6 lda $e6 ;put base address of current hi-res page
sta $1b ; into $1a/1b (will be $2000 or $4000)
ldy #$00
sty $1a
f3fe lda $1c ;get color value
sta ($1a),y ;store it in frame buffer
jsr $f47e ;update color value
iny ;advance, looping...
bne $f3fe
inc $1b
lda $1b
and #$1f
bne $f3fe ;...until we get to $4000/$6000
rts
f47e asl ;lose the hi bit, now $00/$fe for black/white,
cmp #$c0 ; or $54/$aa for green/purple/orange/blue
bpl $f489 ;branch if black or white
lda $1c
eor #$7f ;invert color bits for odd/even
sta $1c
f489 rts
This is a ROM routine, so size is at a premium, and self-modifying code is not allowed. It's optimized for space, not speed.
The most significant impact of this optimization choice is the code at $f47e, which tests the color value and potentially updates it for every byte. This code is necessary because of the rather strange hi-res graphics layout, which (among other oddities) represents an odd number of pixels with each byte. So while a solid black line is 00 00 00 00 ...
, a solid green line is 2a 55 2a 55 ...
. The high bit doesn't change, hence the exclusive-OR with $7f. (The code at $f47e is shared with other routines, e.g. the line-drawing code, that must update the color when moving across the screen.)
The total time required to erase the screen to black is 271,121 cycles. Clearing to green would require 328,465 cycles.
The fastest possible screen clear would write a byte to 7680 addresses -- it's not 8192 because we don't need to overwrite the "screen holes". On a 6502, that requires 4 cycles per byte, or about 30720 cycles. The code to do that would fill about half of available RAM on an Apple II though.
A 192-byte unrolled loop can do it in about 39480 cycles. That's ~5x the code, for ~8x the speed.
While these approaches are faster, they still have the "Venetian blind" look. While that's something of an Apple II hallmark, not everyone wants that, and it may not be possible to simply show page 2 while you're erasing page 1. In a mere 127 bytes, for ~41,300 cycles you can erase the screen from right to left, using a clever function written by John Brooks.
-
As an addendum -- the screen holes are not only not used for pixel data, but they aren't just unused, either. Memory was at such a premium back then that these bytes sitting there unused was unthinkable. So they're used to store working data for the I/O cards and the ROM. May 4, 2016 at 3:09
-
@EricShepherd: I think that's only the holes in the page 1 text screen. AFAIK nothing in the ROM, DOS, or ProDOS relies on the hi-res holes.– faddenMay 4, 2016 at 5:30
-
Yeah, only page 1 text screen screen holes are used. And the //c uses some in the aux page 1 text screen too! Here's a decent list. May 5, 2016 at 9:29
-
1I wonder if anyone considered processing even and odd bytes separately [using the same loop, with $1A equal to 1 on the first pass and 0 on the second]? That would reduce the cost per byte to 6+2+2+3=13--a pretty major speed-up.– supercatMay 9, 2016 at 22:33
-
1Why is this routine considering a "screen color" when
HGR
always clears to black? Or is this routine more general than that, allowing for arbitrary colors when filling? It seems to me you could clear screen to black very quickly in a very small routine, even taking into account memory holes. Mar 21, 2022 at 3:18