mirror of
https://github.com/deater/dos33fsprogs.git
synced 2025-02-09 03:31:05 +00:00
lovebyte2023: add README on the 8-byte code
much longer than the actual program
This commit is contained in:
parent
b91fdfcd4f
commit
1045460437
131
demos/lovebyte2023/tinyhgr_8/README
Normal file
131
demos/lovebyte2023/tinyhgr_8/README
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
tiny_hgr8
|
||||||
|
|
||||||
|
8-byte hi-res Apple II demo by Deater / dSr
|
||||||
|
|
||||||
|
Lovebyte 2023
|
||||||
|
|
||||||
|
I really wanted a hi-res 8-byte demo but that is trickier than you can think.
|
||||||
|
|
||||||
|
On Apple II/6502 to enable graphics you need three bytes, either
|
||||||
|
JSR HGR
|
||||||
|
which takes 3 bytes to jump to the ROM and enable graphics, clear the screen,
|
||||||
|
and set which PAGE is being viewed.
|
||||||
|
|
||||||
|
You can also try setting the graphics "soft switches" yourself, something like
|
||||||
|
BIT $C050
|
||||||
|
which is also 3-bytes, but to get hi-res you need to also set the hi-res
|
||||||
|
switch so too many bytes.
|
||||||
|
|
||||||
|
Once you set hi-res mode, you still need to draw to graphics memory.
|
||||||
|
The various ways of doing this like calling HPLOT need setup in A,X and Y
|
||||||
|
as well as the HCOLOR value so this can take a lot of bytes.
|
||||||
|
My 16-byte entries use the shapetable/XDRAW interface but even when x-or
|
||||||
|
drawing you usually still have to call HPOSN to set up some zero-page values
|
||||||
|
like GBASL/GBASH first, and you can't trust on them having good values
|
||||||
|
at boot. You can try drawing directly to screen memory at $2000 or $4000,
|
||||||
|
but that usually takes 3 bytes too and if you want to draw on the full screen
|
||||||
|
(which is 8k) you need to increment two bytes of addresses. In theory it's
|
||||||
|
a byte smaller if you have a pointer in the zero page, but unfortunately
|
||||||
|
that doesn't happen by default.
|
||||||
|
|
||||||
|
So in theory to do hi-res it takes 3 bytes to init and at least 3 to draw,
|
||||||
|
and then finally if you want a loop that takes 2 bytes. So we're at
|
||||||
|
8 bytes and no room for demo effects like actually changing the color.
|
||||||
|
So what can we do?
|
||||||
|
|
||||||
|
One trick I used for a previous 8-byte lo-res entry is abuse some code put into
|
||||||
|
the zero page by the Applesoft ROM (so on any Apple II from the Apple II+
|
||||||
|
onward, which is most of them). This is the CHRGET code for stepping
|
||||||
|
through BASIC programs, which is put in the zero page by the ROM on boot
|
||||||
|
so the address being loaded can be self-modified. Part of this routine
|
||||||
|
does a 16-bit increment into the self modified region, followed by 7 bytes
|
||||||
|
of code ending in a branch instruction. So if we can drop our 8 bytes
|
||||||
|
of code into this area here (starting roughly at $B1) we can get the benefits
|
||||||
|
of the increment as well as the branch, and have a few more bytes to work with.
|
||||||
|
|
||||||
|
So for this code to work we use two calls into the ROM. One to clear
|
||||||
|
the screen to full-screen hi-res.
|
||||||
|
jsr HGR2
|
||||||
|
As said before this sets the graphics modes, in this case full-screen hi-res
|
||||||
|
displaying PAGE2 ($4000). It does a linear clear of the screen to 0 (black),
|
||||||
|
but on the Apple II due to the weird way Woz designed the graphics memory
|
||||||
|
map this gives a horizontal venetian-blind effect which looks pretty neat.
|
||||||
|
|
||||||
|
The other thing we call into is
|
||||||
|
jsr BKGND0
|
||||||
|
this is a semi-unofficial entry point into the HGR2 code, the portion
|
||||||
|
that does the screen clear. It will clear the screen with the bit-pattern
|
||||||
|
in the accumulator.
|
||||||
|
|
||||||
|
So for this demo we just clear the screen to a random bit pattern (which
|
||||||
|
gives a variety of colors) and then immediate re-clear the screen to zero
|
||||||
|
over and over again.
|
||||||
|
|
||||||
|
You might say, doesn't that only take 6-bytes of code? Well we need to
|
||||||
|
set a random value in the accumulator. Here we load so we over-write
|
||||||
|
the CHRGET address being loaded with some values. By default it is $800,
|
||||||
|
the default load address of BASIC programs. If we can point this value
|
||||||
|
to somewhere more interesting, like into ROM, it will treat the code
|
||||||
|
there as random values. The problem is when we load our demo these bytes
|
||||||
|
will be the first things executed so we have to make sure they get executed
|
||||||
|
harmlessly as no-ops. An obvious choice that points to rom would be
|
||||||
|
$EAEA, or two NOPs. We'll see in a minute though there are some
|
||||||
|
complications here.
|
||||||
|
|
||||||
|
So if we drop a call to BKGND0 followed by a call to HGR2 and have it
|
||||||
|
followed up by the existing CHRGET BEQ instruction we have what we need,
|
||||||
|
as HGR2 always exits with Y=0 and the Zero flag set.
|
||||||
|
Try and run this though and the text screen will go weird and your program
|
||||||
|
will crash into the monitor (unhelpfully with the machine in graphics
|
||||||
|
mode so hard to tell what's going on).
|
||||||
|
|
||||||
|
The problem here is BKGND0 assumes the first page of graphics you want
|
||||||
|
to write to are in zero-page location HGR_PAGE $E6. On bootup this is likely
|
||||||
|
$00 or $FF, so the routine happily writes your color across the first 8
|
||||||
|
pages of RAM which is where the zero-page, stack, and your code live.
|
||||||
|
So not good. So we need a way to skip BKGND0 the first time through
|
||||||
|
the loop.
|
||||||
|
|
||||||
|
If we were entering the code from the keyboard it would be fine, we could
|
||||||
|
just specify the start after the BKGND0 code. However we'd like this able
|
||||||
|
to be BRUN from disk. So what can we do?
|
||||||
|
|
||||||
|
Well there's one way to sneakily skip code on 6502. This is the famous
|
||||||
|
BIT instruction. If you put the first byte of a BIT instruction in your
|
||||||
|
code, it will treat the next 2 bytes as a value to check bits on which
|
||||||
|
is (usually) harmless. So if we load our code into the middle of
|
||||||
|
the 16-bit LDA instruction in CHRGET, start on a bit instruction, it will
|
||||||
|
skip the next 2 bytes the first time through, but when the loop happens
|
||||||
|
this bit instruction will be part of the load address to LDA and so
|
||||||
|
no skipping happens the rest of the executions. This is good, as the
|
||||||
|
call to HGR2 does properly set $E6 to the graphics page we want and
|
||||||
|
BKGDN0 will work properly after that.
|
||||||
|
|
||||||
|
There is a problem though, the code the first time through eats the two
|
||||||
|
next bytes, avoiding the JSR to BKGND0. But it means the following
|
||||||
|
two bytes, the $F4F3 ($F3, $F4 in little endian) bytes get executed as
|
||||||
|
code. Will that be a problem? It turns out those are un-specified
|
||||||
|
opcodes on both 6502 and 65c02 but on both chips those apparently
|
||||||
|
are treated as NOP and so our code works. With the BIT in place
|
||||||
|
the "random" memory values are pulled initially from
|
||||||
|
$EA2C (where 2c is the bit, and EA can be arbitrary but why not use
|
||||||
|
a NOP. In theory we could alter the colors we get by moving things around).
|
||||||
|
|
||||||
|
The two values at the beginning are incremented in a self-modified way
|
||||||
|
by the earlier unchanged CHRGET code so we walk through ROM getting
|
||||||
|
random color patterns in the accumulator, writing them to the screen,
|
||||||
|
and quickly clearning back to black again in a venetian-blind
|
||||||
|
pattern. It actually looks lovely, much nicer than some 16-byte
|
||||||
|
demos I've done.
|
||||||
|
|
||||||
|
You can try things out on your own Apple II with
|
||||||
|
the following commands from the BASIC prompt
|
||||||
|
|
||||||
|
CALL -151
|
||||||
|
B8: 2c ea 20 f4 f3 20 d8 f3
|
||||||
|
B8G
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user