AppleIIbot 280 char Applesoft Demos -- Part 1

Flame Demo (64-byte assembly)


My first idea was to see if we could get some assembly language going, so a good thing to try is my 64B Flame Demo.

This first attempt uses the traditional method of entering assembly language from BASIC via Applesoft: you have DATA statements holding the raw 8-bit data for the 6502 Machine Language, you READ the values and then POKE them to a free area of memory (often $300 (which is 768 in decimal, on 6502 machines $ is used to mean hexadecimal)) 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 next attempt loads a short assembly language program that then decodes a string. The string is machine language with each byte split into 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? 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 even though that makes the code really hard to read.
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
Link to the assembly source: fire_extreme.s

Circles Demo (101-byte assembly)


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

This program uses 4+4 bit encoding, so it takes two bytes to represent each machine language 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 things 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 Applesoft 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 ="""". ! &.)+)!.*#!'$!( '  . !, ) "."!!+&"""')#)$ $,
Link to assembly source: circles.s

Autumn (117 byte assembly)


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. The top 6 bits of the machine code byte are converted to ASCII by shifting them two bits to the right and then adding 32. The bottom two bits are accumulated in groups of three and then encoded in the same way and stored at the end of the string. Qkumba figured out the crazy use of the exponent (^) operator to handle the proper shifting to reconstruct the original values.

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

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,\
Link to assembly source: autumn.s

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 "Shape Tables" which are a software vector drawing library included in the Applesoft BASIC ROMs.

To use shape tables, 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 operation 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 cassette tape). POKEing 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.

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.

Line 2 in the code tells Applesoft where the shapetable is by POKEing the shapetable address into the proper zero page addresses. The shape table in the REM depends on being at line 5, because we depend on the actual layout of the BASIC program.

We point the shapetable 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 the first one. 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.

6HGR2:FOR X=1 TO 100:HCOLOR=7:HPLOT RND(1)*280,RND(1)*192:NEXT

Animated Nyan Cat -- Shape Tables and Page Flipping


I wanted to do something with 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 work. 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, as there's an annoying pause while PAGE1 is drawn and you can't see it. However I was out of characters to flip before drawing.

As some have pointed out, I used tail-call optimization here, where instead of GOSUB9 one last 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 rendering loop is line 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.


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 an 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.

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#
Link to assembly source: raster2.s

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
Link to source code: raster4.s

Box-drawn Pumpkin


I got the idea for this while sitting in a really long faculty meeting. It's just drawing a bunch of boxes. Each box is 5 values: color and x1,x2 to y1,y2. These are drawn in Applesoft with HLIN calls.

I drew a jack-o-lantern as it seemed the seasonable thing to do, also people were doing that on the Atari bot.

9DATA 1,0,39,31,47,9,11,27,10,44,9,8,30,13,42,9,6,32,16,37,9,5,33,20,30,4,17,21,4,9,13,11,15,18,22,13,24,28,18,22,13,10,28,31,34,13,12,26,35,36,9,14,15,31,33,9,22,23,34,36,13,19,20,22,27,13,17,22,26,27
Then I had the thought: this is just plain data, and the value are always 0 to 48, so why not make it 6-bit encoded ASCII in a REM statement?

The updated code had a lot of PEEKs, so I tried a new trick where we use a DEF FN function definition to save some space (and at the same time find a bug in my Applesoft tokenizer utility with regards to parsing the DEF token). The updated code is a lot slower, but it makes it more fun* to watch as it is being drawn. (* see 4am's standard disclaimer)

0REM! G?O)+;*L)(>-J)&@0E)%A4>$15$)-+/26-8<26-*<?B-,:CD)./?A)67BD-346;-16:;

Box-drawn Person


The pumpkin was only 14 boxes, but with the new compressed representation you can fit up to 30 so I was looking for a more complex image to draw. I thought I'd try to do a person but I underestimated how hard it is to optimally take a complex sprite and translate it into boxes. So it took a while to try to do is manually.

I did not design the sprite myself, it's based on one that came up on a google search for pixelart here

I optimized the code slightly so you can fit in one more box than the previous code.

The below code is slightly better than the one in the actual tweet, it frees up another few bytes and also uses a GET A at the end to pause and not scroll the graphics.

0REM/ G O))>(O),;$')18"# ,<@G -909/-927$/727,6627,0027+,9:?+.7*/+06()+24(C+35@I+-8@A+/6BC),1FG)7<FG)8<CE$35HI,19JK,.:LM/-;NO#,-:;#89:;#24@A#46>?(>>BO(((.L

Box-drawn Selfie


Nothing new in this one, but I thought it would be fun in a recursive sort of way to have the AppleIIbot draw its own self portrait. (Yes I know the AppleIIbot is really a Raspberry Pi, but its avatar picture on twitter is this image).

A shame there's no beige in the lo-res palette, the machine looks like it's in desperate need of some retro-bright.

0REM/ G O(*;!K-#4HI-#6DG-%7@C-(8<?('.CF((2DG(-2??%#4JK%&3LM(47LM(<<(6-)8":(*5$8 +4&7(BD3H(BB5I%<C34%;A56-9A7K :@8@ :@BJ%;@<<%;@FF#???@#??IJ%=>9>%=>CH,77"#

Arbitrary 40x24 bitmap


For some reason I got the idea to see if I could make 4am's Beagle Bros disk-care-alligator bitmap.

At first I was trying to do it at 40x48 in GR mode, but that wouldn't fit in a tweet. 40x24 was more promising, and then I realized that if it's going to be in black and white I can just use NORMAL / INVERSE spaces and eliminate all of the excess co-ordinate and color setting.

I have a utility that will take a png file and create the 6-bit encoded data to stick in the REM statement. Need to figure out a way to print the last 6 chars without scrolling the screen.

1REM_'> $^__XW?#__'\][C__!>W\Y_/&W:?\_SC=W/^?^1_]G_G_P=_Y_8><W?\/+, \/_S4&__S_T+C__\/]/P_/_3\_XYS_,X?@\\/K@/N$_CK"&OP_YO*B?^?^_*PA_'__;XX_#F9&$__1F9.X_?8F9&^_/8F9P_

Kay Savetz pointed out we can save a byte by using IF Q*2-Z instead of Q*2<>Z

I also managed to shave some more bytes off: ? vs PRINT (had to fix my tokenizer for that), use % to avoid INT(), and notice that HOME technically wasn't needed (in fact scrolling up might be a mildly cooler effect). Just barely had enough room to add a conditional so we only skip the last char instead of the last 6 at the end.
1REM_'> $^__XW?#__'\][C__!>W\Y_/&W:?\_SC=W/^?^1_]G_G_P=_Y_8><W?\/+, \/_S4&__S_T+C__\/]/P_/_3\_XYS_,X?@\\/K@/N$_CK"&OP_YO*B?^?^_*PA_'__;XX_#F9&$__1F9.X_?8F9&^_/8F9P_
3Z=Q%:IFY+I-164THEN?" ";

V1 QR Code


I realized that the previous entry could be used to generate QR codes. But what to link to? Especially as to fit on the Apple II screen you are limited to V1 of the QR spec (21x21 pixels) which in general limits you to a 17 character long URL. Anyway it was a pain but you can probably guess what it links to.

Extra obfuscation on the INVERSE/NORMAL front here compared to last time. You can thank Qkumba for that. It saves a few chars but I do think it slows the whole thing down a bit.

2FORY=0TO159:Z=PEEK(2054+Y)-32:FORI=0TO5:Q%=Z/2:POKE50,(Q%*2-Z)*192+255:Z=Q%:IFY+I-164THEN?" ";

80 column bitmap -- Apple II Forever!


The bitmap mode is a bit chunky at 40x24 effective resolution, so I thought I'd try putting the bot in 80-column mode. This does effectively reduce the resolution to 80x12 although on the plus side you don't have to worry about the screen scrolling when you get to the end.

1REM______/X__  X_______N__[WO_/   # [& .<'^?^O_C_CC_[W=__.X.X.XN#W?W]_[N[N[N[NM_=W_O'N#G#GS!XP=X_Y[NGNGN\O[WO_? X@[@#. .  ^__O[O[__________@_@__
3Z=Q%:?" ";:NEXTI,Y
4?"F O R E V E R";

RLE Encoded Color Picture


The black and white bitmaps were nice, but I thought I'd try to see if I could get some more colorful images. I previously tried with the rectangle plots but they're a pain to use because you have to manually map out the rectangles.

One obvious thing to try is Run-Length Encoding an image. It's a very simple compression algorithm that should be small enough to fit in a tweet.

In this case it is drawing a bunch of horizontal lines, with the color stored in one 6-bit value and the width of the line (the run) in another. You can fit about 70 runs this way.

This is lo-res graphics again, but to save space we are outputting the graphics as text. This works because on the Apple II it goes to the same place in RAM. The CALL to 64500 is the monitor "ADVANCE" routine which moves the cursor to the right. The crazy PEEKing is getting the current cursor location at (BASL)+CH and poking the color in (we can't just use PRINT here as Applesoft PRINT strips out control characters and such).

I tried a lot of crazy methods to get this smaller but none really worked well. There are a few ways you can shave a byte here or there, but it involves putting long calculations inside of the primary loop which slows down the decoding by a lot.

1REM E,! F,! D,& */! 6-& +/" 5)& +/# 4!' */$ 3"' */% 3&% +'& @"! !/+"% 2-$""/!!#"$/#"& /-&""/"!!$#"!/$"' --$)#""/+"( .-%""/+") /-#""'*/+ 1"! !/)'!/, 
I did try to see if I could offload some of this to assembly language as all that PEEKing takes lots of room. I even have a version that uses USR() to call a custom assembly routine at $0A and decodes the argument to an int and then calls STORADV. It ends up being too big.

1REM E,! F,! D,& */! 6-& +/" 5)& +/# 4!' */$ 3"' */% 3&% +'& @"! !/+"% 2-$""/!!#"$/#"& /-&""/"!!$#"!/$"' --$)#""/+"( .-%""/+") /-#""'*/+ 1"! !/)'!/, 
6 DATA 10,76,11,0,12,3,768,32,769,12,770,225,771,165,772,161,773,76,774,240,775,251
I did try for a full-assembly version. If I could get it small enough, the RLE data and the payload would fit in the 128B assembly loader. I did get it working, but even when using a more advanced RLE encoding (runs of 15 plus color fit in one byte, with an overflow to two bytes if it doesn't fit) it still is not quite as compact as the BASIC version and won't fit in a tweet. It does use the fun trick of loading so the end of the program is a backwards jump living at $3f5 and using the & command to jump to it.

1REM\)'\)'\);H'\%;L+\%:L/\%<H3\$<H7\$5L9\($$O4\$3(',0/8\#;(+$-$3<T3.(O@X7(OD\#/(IO\$$$G%S (0^J A)(6^H N; 2(9^:2222\ZR#T!RN; JA_I,(\^RT^I_R3S 3P $( ,&3Q0,+SR@L8>N<>X2Y2N<^D\#<%\0, H*U QR!L #..

Animated 80s grid


I wanted to see if I could do one of those retro 80s battlezone type grid animations. I really wanted to have blue mountains in the background too, but couldn't manage to get it to fit. In the original version I only managed a single star, but eventually managed to shoehorn in a purple mountain.

No real tricks here, the primary thing holding back more features is because Applesoft doesn't crop out-of-bounds lines so you have to manually clip them at the screen boundary or else get an error.

The code does do page flipping for smooth animation. The CALL-3086 calls the ROM "clear screen to black" routine which saves a lot of complexity but kills the framerate.

I could get a blue mountain if I let the grid be entirely purple, it's just that due to how the Apple II does color the purple vertical lines have gaps in them that look pretty awful so having them be white looks a lot better.


Horizon Flyer


I wanted to get something more exciting than the previous 80s horizon so I added some shape tables into the mix. Not really that different than the previous shape table tricks, but by reusing the shape as both the flyer and the gateways I managed to get something interesting.

I spent some time trying to speed things up but it's difficult with such a small amount of code. Also the shape isn't perfect, and not symmetric, I should fix that.


On to part 2
Back to Main AppleIIbot page