Flame Demo (64-byte assembly)

The first attempt was to see if we could get some assembly language going, so a good thing to try is my 64B Flame Demo.
The first attempt uses the traditional method of entering assembly language from BASIC via Applesoft: you have data staements holding the raw 8-bit data for the 6502 Machine Language, you poke them to a free area of memory (often $300 (which is 768 in decimal)) and then you call to it.

Note this code is longer than 256 chars long which means while it is a valid Applesoft program, you can't type it in on an actual Apple II as it will overflow the keyboard input buffer. You can however tokenize it and load from disk, which must be what the Apple II bot does.

1 FOR X=768TO832:READ A:POKE X,A:NEXT:CALL 768:DATA 44,80,192,44,82,192,32,112,252,169,255,145,42,136,16,251,169,22,133,37,32,34,252,160,39,104,240,5,10,240,4,144,2,73,29,72,48,10,177,40,41,7,170,189,57,3,145,40,136,16,230,198,37,16,221,48,205,0,187,0,170,0,153,0,221
This worked, but then 4am got me thinking about more compact ways to try and fit larger programs. I looked into the way that Sellam Abraham did things with his "Mesmerizer Exorciser" Kansasfest 2020 Hackfest entry, but it turns out that is a bit too big to fit in a tweet.

My fist attempt loads a short assembly language program that then decodes a string. The string is machine code encoded with two values, the first shifted left by three then xored with the second to get the value.

Why not just include raw 8-bit values? For one on twitter we're going to assume you can only pass in ASCII text, so that limits you to 7-bit values. In addition you can't send control characters (the first 32 values) and on Apple II+ you can't send lowercase, further reducing things. In the end you are sort of limited to around 6-bits (64 values) and have to construct the machine code from that.

Also note that by now I had remembered that Applesoft ignores all spaces so you can leave them out to get more room.
1 FOR X=768TO789:READ A:POKE X,A:NEXT:CALL768:DATA162,0,189,106,8,48,12,10,10,10,93,171,8,157,0,12,232,208,239,76,0,12
9 "MBPMBPLFW]WZMYJW]JXLLLW\LEVHIVHZHAKANI^MMH]_OIZMYJTPLJSNQH_H]H[HSD@@DB@@@DAGAB@@CAFEE@BD@G@@EB@D@BAE@@BA@AGBEADA@@@FFE@E@E@C@B@A@E"+

Circles Demo (101-byte assembly)

This is an unreleased demo of mine based on part of Hellmood's "Memories" Demo

In this case we are using 4+4 bit encoding, so it takes two bytes to represent each byte. This is a bit wasteful but it's a lot easier to write a 4+4 decoder than a more efficient 6+2 one. Applesoft in particular makes this really difficult as it has no shift or bitwise logic functions. It's not easy to write decoders in assembly either, so multiplication in Applesoft it is. The A=(A-INT(A)) code is the way to get a remainder (modulus) in Applesoft.

Note in this case I abandoned using a string. It has some problems, the worst being that you can't include a quote character (ASCII 34). Using a REM remark (comment) statement takes slightly more room but everything after it is ignored.

You might ask how we load values from a REM statement. In Applseoft the whole program is loaded into memory, REM statements and all. Programs are loaded at $800 in tokenized format, but it's fairly easy to poke around and find where lines get loaded in memory and then use PEEK to load the values in.
2 GR:FOR X=0TO100:A=(PEEK(2245+X/2)-32)/4:IFJTHENA=(A-INT(A))*4
3 J=NOTJ:POKE768+X,(PEEK(2144+X)-32)*4+A:NEXT:CALL768:REM+4PY_H&A\RH%A\B&N3 ""?2 29_*'222A^JN8 A,I\B( ^I\( ^I^B( ^I\( ^Y\B$SY\R$P,N)('&&%%$$##""!!!!       5 ="""". ! &.)+)!.*#!'$!( '  . !, ) "."!!+&"""')#)$ $,

Autumn (117 byte demo)

This is a slightly shorter version of my Seasons demo (without color cycling) that was entered in the Outline Online 2020 Demo Party.

This is using 6+2 coding, with the 6 bit values (with 32 added to get the bits up in visible ASCII) first followed by the packed remaining 2 bits in chunks of three. Qkumba figured out the crazy use of the exponent to handle the proper shifting of the 2-bit values.

Because it uses a lot of multiplies and shifts, this is fairly slow to decode which is why it has a directive to skip 9 seconds. It should skip more but there were only 4 free bytes in the tweet so we couldn't delay more.

{B9}
1REM(V\I\I\B.Y\A\FY\R@:A\9\&B9\A\F9\R@:A\9\9]9]9]9]L'Y^T Y^I\:@A
\D Y\.J Y\A\J Y\A\I^")^1^A^*!" JI\,HI\*\TG([]I\I\I\(5]3  P")90'6"F)=8KBI.)H%1@&1F1WA6%=:S50@"E,\
2FORI=0TO116:C=INT((PEEK(2171+I/3)-32)/4^(I-INT(I/3)*3)):POKE768+I,((PEEK(2054+I)-32)*4)+C-INT(C/4)*4:NEXT:CALL768

Spaceship -- Applesoft Shapetable

After managing to fit Autumn in, I gave up on assembly language and went back to coding in Applesoft. One thing Applesoft has going for it is "Shapetables" which are a software vector drawing library included in the Applesoft BASIC ROMs.

To use shapetables, you map out the vectors. You can only do draw+move UP/DOWN/LEFT/RIGHT and pen-up move (no-draw) UP/DOWN/LEFT/RIGHT. Each operating is three bits, you can pack two or sometimes three directives per byte and there's a header with the number of shapes and the offset of the shapes.

Typically you'd POKE the shapetable into memory via data statements (there's no easy way to load them, though oddly there is a dedicated command to loading them from casette tape). Poking wastes a lot of space, so I thought maybe we can stick it in a REM statement like before. There's a trick here, you need to have your shapetable be valid ASCII. To do that you need the second value in each byte to not be a not-draw instruction, and also you can't have a third value either.

Once you have your shapes, you can DRAW them at a location, or XDRAW to xor draw (which makes it easier to draw and erase). There is ROT to rotate and SCALE to scale.

This example like 2 tells Applesoft where the shapetable is by POKEing in the locations to the proper zero page address. The shape table in the REM depends on being on line 5, because we depend on the actual layout of the BASIC program.

This points to address $814. If you look there in memory you find the values
29 08 05 00 B2 37 ...
The 29 08 (little endian) is actually part of the linked list of the BASIC program in memory pointing to the next line. The 05 00 is the line number. And B2 is the token for REM followed by the data. By pointing at the 29 we say we have 29 shapes, but that doesn't matter as we only use 1. The next value is ignored. The next is the offset to the first shape (from the beginning). Since this is 5 it skips the REM and goes to the data.

The rest is just regular Applesoft BASIC, it draws some random stars, moves the spaceship, and draws some flames and then repeats.

2POKE232,20:POKE233,8
5REM7:'%%,5..>'<29'
6HGR2:FOR X=1 TO 100:HCOLOR=7:HPLOT RND(1)*280,RND(1)*192:NEXT
7SCALE=5:FORR=0TO16:ROT=R:GOSUB9:GOSUB9:NEXT:FOR X=100TO270:GOSUB9
8HCOLOR=5:HPLOTX-30,86+RND(1)*16TOX-10,91:X1=X+SQR(X/25):GOSUB9:X=X1:NEXT:GOTO6
9XDRAW1ATX,91:RETURN

Nyan Cat -- Shape Tables and Page Flipping

I wanted to do something wiht page-flipping and shape tables, possibly the only two graphics features the Apple II can do better than other 8-bit computers. I was thinking something like my Shapetable Party demo.

In the end trying to make complex ASCII shape tables was too much. But then oddly I had a dream where I made a HGR version of Nyan Cat. So here it is, a program that brought a lot of joy.

Not much exciting about it, the poptart-cat shapetable is done more or less like in the previous example. It uses HGR:HGR2 to clear both graphics pages and leaves things on PAGE2. The rainbow and cat are first drawn on PAGE2 (the routine at line 8 draws them). Then they are drawn again at a slightly different offset on PAGE1. The POKE 230,32 tells the Applesoft routines to draw to PAGE1 ($2000) instead of PAGE2 ($4000). Ideally we'd switch back to page1 so we could see both being drawn, in fact when watching it there's an annoying pause while PAGE1 is drawn and you can't see it. However I was out of characters to do that.

As some have pointed out, I used tail-call optimization here, where instead of GOSUB9 one lsat time in line 8 I fall through to line 9 and let the return from there return from the GOSUB to line 8. This is something I do a lot in 6502 assembly and it's fun to do it in BASIC too.

The actual program is like 7 which just flips pages rapidly. The V=0 call was added to slow the animation a bit, it roughly is as slow as the GOTO7. I tried putting slower things (like SQR() square root) to slow it even more, but you have to put things twice or the animation is unbalanced and there wasn't enough room for two.

It was a bit more of a pain than you think to get the HGR colors to plot in rainbow order. Also in size coding like this you run into issues where it takes less characters to have things to the upper left part of the screen as the coordinates can be less than 3 digits.

2POKE232,20:POKE233,8
5REM$,.,6>???$$--5
6ROT=0:SCALE=5:P=49236:HGR:HGR2:GOSUB8:Q=1:POKE230,32:GOSUB8
7POKEP+1,0:V=0:POKEP,0:GOTO7
8C=5:Y=80:XDRAW1AT134,102+Q*2:GOSUB9:C=1:GOSUB9:C=6:GOSUB9:C=2
9HCOLOR=C:FORZ=YTOY+5:FORX=0TO13:Q=NOTQ:HPLOTX*8,Z+QTOX*8+7,Z+Q:NEXTX,Z:Y=Z:RETURN

Double Hires Pattern

I was interested in seeing if I could get some better colors going. I have done demos that do Vapor Lock and mid-screen race-the beam mode switching but I was pretty sure the Linux-based emulator used by the Apple II bot can't do that type of cycle-counted effects.

The emulator did seem to be emulating Apple IIe, which opened things up for double hi-res graphics. Those are tricky to do and you can't easily program them from Applesoft. I actually tried at first but had some issues fitting in a reasonable amount of room, so assembly language it is.

This was just a first test but it made a neat pattern. I had a lot of trouble just getting horizontal lines drawn. To do that in double-hires you have to draw an increasingly rotated-by-one bit version of the color across 4 bytes on two different bank-switched graphics pages, a huge pain.

{B10}
1REM(X\(V\C7PC#PC PJ(AYJ A_J(Y_I_(* Y_I_RPT[3$ H H ($]H)C5P(1 C5P(1 B$\8I_D)R@)_8 XP)%%1&9Z)OF!S+ !7 ' #1GA#
2FORI=0TO77:C=INT((PEEK(2132+I/3)-32)/4^(I-INT(I/3)*3)):POKE768+I,((PEEK(2054+I)-32)*4)+C-INT(C/4)*4:NEXT:CALL768

Double Hi-res Rasterbars (126 byte assembly)

My goal was to get two colors of double hi-res rasterbars going. It turns out this is much harder than it sounds. I did get one rasterbar moving fairly easily, but getting a second independent bar was trouble. Even then it was 136 bytes which was too big for the loader.

So I size optimized the code like crazy, including using the "BIT" trick to avoid a jump and various other things and got it down to 126 bytes. The pattern ended up being a bit nonsensical, as I had to use an EOR (xor) to calculate the location of the second bar, but there is more than one color.

Then I optimized down the BASIC loader. It's still qkumba's 6+2 loader but I found some parenthesis I could remove, did some algebra and orders-of-operation changes to remove some more parenthesis, and did some tricks with integer variables. Applesoft does everything in floating point, but you can specify 16-bit integer values which will truncate, which removes the need for an INT (though qkumba later noticed that might not be necessary).

1REM(X\(V\C7PC#PC P1YJ (+ J!(+ F,!R0L"HR+HBC* RTYA_F2$JJ!A_I_R!D 2 * &9_JO= A_F2Y_H H ($]H)C5P(; C5P(; B$\:JQ_$R8I_D)R@)_8 $(, W[_XP)%%:0'TP!((Z !!=5!V-AY-4"0P%T!T0@QT9X0.Y
2FORI=768TO894:C%=(PEEK(1924+I/3)-32)/4^(I-INT(I/3)*3):POKEI,C%+4*(PEEK(1286+I)-32-INT(C%/4)):NEXT:CALL768

The Future

So am I done for now?

There are still a few tricks remaining. There is some deep magic you can do if you call into the Applesoft interpreter direct from assembly language. Lots of articles on this from the 1980s.

Applesoft supports calls like USR and & besides CALL to call into assembly language. The most useful is & which just does a plain jump to $3F5. If you load your assembly properly you can use this to save another few characters by getting rid of the CALL768. (Also if you load your code at $3F5 you can watch it being decoded as it writes to the $400 text page).

So maybe I will come back to this again, but for now there's other projects I really need to finish. It's been fun!
Back to Apple II Demos