Preamble Files
AKALABETH
AKALABETH in the second disk image is:
0 TEXT : HOME
1 IF PEEK (55) = 253 THEN END
2 POKE - 16292,1
10 ONERR GOTO 100
20 PRINT "NOMON I,O,C": HOME
22 POKE 977,1: POKE 978,3
26 DATA 32,234,3,162,2,173,0,224,201,76,240,7,202,157,128,192,16,243,2,162,20,76,18,212,184,3,166
27 FOR I = 952 TO 975: READ J: POKE I,J: NEXT : FOR I = 1010 TO 1012: READ J: POKE I,J: NEXT
30 TEXT : HOME : PRINT "RUN AKA0"
99 END
100 GOTO 0
55 ($37) is the high-byte of the output routine address (CSWH). Akalabeth quits right here if the value is $FD.
I presume that's something to do with testing whether DOS is loaded or how much RAM there is ($FDXX would be the default ROM location, I think)
POKE - 16292,1 supposedly is a write to the joystick but no idea what that's doing here.
NOMON I,O,C would normally cancel the display of disk executions (set by MON) but I'm not sure why this is PRINTed here, unless my de-tokenization has dropped a control character equivalent to CHR$(4).
Similarly with the PRINT "RUN AKA0" later on.
The POKE 977,1: POKE 978,3 sets the DOS warmstart address to $0301 (rather than $9DBF).
Lines 26 and 27 end up poking the following (hand disassembled):
3B8: 20 EA 03 JSR $03EA
3BB: A2 02 LDX #$02
3BD: AD 00 E0 CHECK LDA $E000
3C0: C9 4C CMP #$4C
3C2: F0 07 BEQ GO
3C4: CA DEX
3C5: 9D 80 C0 STA $C080,X
3C8: 10 F3 BPL CHECK
3CA: 02
3CB: A2 14 GO LDX #$14
3CD: 4C 12 D4 JMP $D412
3F2: B8 03
3F4: A6
$03EA is a DOS routine (which calls $A851) to reconnect the DOS intercepts to the keyboard and screen streams.
The rest of the code above seems to check if the right memory bank is selected.
If the value at $E000 is #$4C (which means Applesoft BASIC is loaded) it will branch to $3CB (what I've labeled GO).
Otherwise it will write to $C081 and branch back to check $E000 again. If it fails to match #$4C it will write to $C080 before finally falling through to GO (because X is 0).
So I believe this will leave the memory bank switching in the desired state then jump to $D412 after setting the X register to #$14.
I'm not sure why there is the 02 in 3CA unless I've messed up my disassembly or if it's just necessary to keep certain addresses in the same place.
$3F2 is the reset vector. So if the RESET key is pressed, this code at $3B8 will be run.
$3F4 is just a check byte ($3F3 XOR #$A5) that is tested before the reset vector is called.
AKA0
AKA0 is:
1 TEXT : HOME : NORMAL : VTAB (5): HTAB (9): PRINT "WELCOME, FOOLISH MORTAL": VTAB (7): HTAB (14): PRINT "INTO THE WORLD": VTAB (9): HTAB (16): INVERSE : PRINT "AKALABETH!": NORMAL
2 VTAB (11): HTAB (7): PRINT "HEREIN THOU SHALT FIND GRAND": VTAB (13): HTAB (16): INVERSE : PRINT "ADVENTURE!": NORMAL
3 VTAB (16): PRINT " CREATED BY LORD BRITISH": PRINT : PRINT "(C) 1980 BY CALIFORNIA PACIFIC COMPUTER"
4 VTAB (23): HTAB (8): PRINT "(PRESS SPACE TO CONTINUE) ";: GET R$: PRINT
5 PRINT "BLOAD AKA4"
6 POKE 254,32: POKE 255,32: POKE 32840,169: POKE 32841,0: CALL 32768: POKE 32840,177: POKE 32841,8
7 PRINT "BLOADAKA2,A$2000": POKE - 16302,0: POKE - 16297,0: POKE - 16300,0: POKE - 16304,0
8 PRINT "BLOADAKA5,A$4000"
9 CALL 32768
10 PRINT "BLOADAKA3,A$4000"
11 POKE 32769,39: POKE 32888,136: POKE 32890,255: CALL 32768: POKE 32769,0: POKE 32888,200: POKE 32890,40
12 FOR O7 = 1 TO 4000: NEXT O7
13 POKE 32840,169: POKE 32841,0: CALL 32768: POKE 32840,177: POKE 32641,8
14 FOR O7 = 1 TO 500: NEXT O7
15 POKE - 16303,0
8450 VTAB (23): HTAB (8): PRINT " (LOADING PROGRAM) ";
8997 PRINT
8998 POKE 33,1: POKE 34,23
8999 PRINT "RUN AKA1"
Lines 1–4 print out the welcome message and wait for a key to be pressed.
BLOAD AKA4 is printed (with a CTRL-D preceding and dropped by the de-tokenizer?)
POKE 254,32POKE 255,32POKE 32840,169:POKE 32841,0(LDA $#00)CALL 32768calls the code belowPOKE 32840,177:POKE 32841,8(LDA ($08),Y)
BLOAD AKA2,A$2000 is printed.
POKE - 16302,0set full-screen graphicsPOKE - 16297,0set hi-res graphics modePOKE - 16300,0set hi-res page 1POKE - 16304,0set graphics mode
BLOAD AKA5,A$4000 is printed.
CALL 32768calls the code below
BLOAD AKA3,A$4000 is printed.
POKE 32769,39($8001)POKE 32888,136(set$8078toDEY)POKE 32890,255($807A)CALL 32768calls the code belowPOKE 32769,0($8001)POKE 32888,200(restore$8078toINY)POKE 32890,40($807A)
There is a pause caused by looping 1 to 4000.
POKE 32840,169:POKE 32841,0(LDA $#00)CALL 32768calls the code belowPOKE 32840,177:POKE 32841,8(LDA ($08),Y)
Another pause caused by looping 1 to 500.
POKE - 16303,0set text mode
The loading message is printed.
POKE 33,1set width of scrolling window to 1POKE 34,23set top of scrolling window to 23
RUN AKA1 is printed.
Some Information From Twitter
@jtauber Quick comments: Instructions likely on the original disk, I'll check mine tmw. 34 sector B files = HGR images. AKA4 loads at $8000.
— Yesterbits (@yesterbits) March 30, 2014 @jtauber Fairly sure that the 6502 code at $8000 is code to perform screen wipe effect.
— Yesterbits (@yesterbits) March 30, 2014 AKA4
AKA4 is indeed loaded at $8000 (as the first two bytes indicate). The length (in the next two bytes) is given as $1000.
The first part of it looks like
00 80 00 10 A0 00 A9 00 85 F9 A9 00 85 FA 85 FB
A9 00 85 FC 85 FD A9 00 85 06 A5 FE 85 07 18 A5
06 65 F9 85 06 A5 07 69 00 85 07 18 A5 06 65 FA
85 06 A5 07 65 FB 85 07 18 A5 06 65 FC 85 06 85
08 A5 07 65 FD 85 07 18 65 FF 85 09 B1 00 91 06
18 A5 FD 69 04 85 FD C9 20 D0 BB 18 A5 FA 69 80
85 FA A5 FB 69 00 85 FB C9 04 D0 A4 18 A5 F9 69
28 85 F9 C9 78 D0 93 A2 00 CA D0 FD C8 C0 28 D0
85 60 FF 00 00 FF FF 00 00 FF FF 00 00 FF FF 00
with the rest of the file exhibiting very data-like patterns.
Let's disassemble the first part:
8000: A0 00 LDY #$00 or
LDY #$27
8002: A9 00 LOOP5 LDA #$00
8004: 85 F9 STA $F9
8006: A9 00 LOOP3 LDA #$00
8008: 85 FA STA $FA
800A: 85 FB STA $FB
800C: A9 00 LOOP2 LDA #$00
800E: 85 FC STA $FC
8010: 85 FD STA $FD
8012: A9 00 LOOP1 LDA #$00
8014: 85 06 STA $06
8016: A5 FE LDA $FE
8018: 85 07 STA $07
801A: 18 CLC
801B: A5 06 LDA $06
801D: 65 F9 ADC $F9
801F: 85 06 STA $06
8021: A5 07 LDA $07
8023: 69 00 ADC #$00
8025: 85 07 STA $07
8027: 18 CLC
8028: A5 06 LDA $06
802A: 65 FA ADC $FA
802C: 85 06 STA $06
802E: A5 07 LDA $07
8030: 65 FB ADC $FB
8032: 85 07 STA $07
8034: 18 CLC
8035: A5 06 LDA $06
8037: 65 FC ADC $FC
8039: 85 06 STA $06
803B: 85 08 STA $08
803D: A5 07 LDA $07
803F: 65 FD ADC $FD
8041: 85 07 STA $07
8043: 18 CLC
8044: 65 FF ADC $FF
8046: 85 09 STA $09
8048: B1 00 LDA ($00),Y or
LDA $#00 or
LDA ($08),Y
804A: 91 06 STA ($06),Y
804C: 18 CLC
804D: A5 FD LDA $FD
804F: 69 04 ADC #$04
8051: 85 FD STA $FD
8053: C9 20 CMP #$20
8055: D0 BB BNE LOOP1
8057: 18 CLC
8058: A5 FA LDA $FA
805A: 69 80 ADC #$80
805C: 85 FA STA $FA
805E: A5 FB LDA $FB
8060: 69 00 ADC #$00
8062: 85 FB STA $FB
8064: C9 04 CMP #$04
8066: D0 A4 BNE LOOP2
8068: 18 CLC
8069: A5 F9 LDA $F9
806B: 69 28 ADC #$28
806D: 85 F9 STA $F9
806F: C9 78 CMP #$78
8071: D0 93 BNE LOOP3
8073: A2 00 LDX #$00
8075: CA LOOP4 DEX
8076: D0 FD BNE LOOP4
8078: C8 INY or
DEY
8079: C0 28 CPY #$28 or
CPY #$FF
807B: D0 85 BNE LOOP5
807D: 60 RTS
In trying to work out what the above code is doing, my first thought is: where is it using all the data after the initial code?
My second thought is: where is it modifying the screen (if that's indeed what it's doing).
There are no absolute addresses so the only places that can be referring to either the data or the screen are the indirect loads and stores at $8048 and $804A respectively.
The
POKE 254,32
POKE 255,32
in the BASIC before the binary code is run put #$20 into $FE and $FF.
The following therefore initially puts $2000 in $06/$07.
8012: A9 00 LOOP1 LDA #$00
8014: 85 06 STA $06
8016: A5 FE LDA $FE
8018: 85 07 STA $07
The following then adds the value in $F9 (initially #$00) to $06, carrying to $07.
801A: 18 CLC
801B: A5 06 LDA $06
801D: 65 F9 ADC $F9
801F: 85 06 STA $06
8021: A5 07 LDA $07
8023: 69 00 ADC #$00
8025: 85 07 STA $07
This then adds the 16-bit contents of $FA/$FB (initially $#0000) to $06/$07.
8027: 18 CLC
8028: A5 06 LDA $06
802A: 65 FA ADC $FA
802C: 85 06 STA $06
802E: A5 07 LDA $07
8030: 65 FB ADC $FB
8032: 85 07 STA $07
We then add the 16-bit contents of $FC/$FD (yet again, initially $#0000). We also (in 803B) store the new value of $06 in $08.
8034: 18 CLC
8035: A5 06 LDA $06
8037: 65 FC ADC $FC
8039: 85 06 STA $06
803B: 85 08 STA $08
803D: A5 07 LDA $07
803F: 65 FD ADC $FD
8041: 85 07 STA $07
Then we add whatever was in $FF ($#20 from the POKE) to what we just put in $07 and store in in $09.
8043: 18 CLC
8044: 65 FF ADC $FF
8046: 85 09 STA $09
By this stage, $8048 has been changed so we have:
8048: B1 00 LDA $#00
804A: 91 06 STA ($06),Y
so we end up storing $#00 in whatever we have in $06/$7 + Y.
So this is where we're writing to $2000, the screen.
Next, we add $#04 to $FD
804C: 18 CLC
804D: A5 FD LDA $FD
804F: 69 04 ADC #$04
8051: 85 FD STA $FD
and if $FD is not #$20, we go back to LOOP1:
8053: C9 20 CMP #$20
8055: D0 BB BNE LOOP1
It might be helpful to rewrite this in pseudocode with variables replacing the 8-bit and 16-bit values stored on the zero-page.
Let's name the "variables" stored at each zero-page locations as follows:
$FE=b1$FF=b2$06/$07=w1$08/$09=w2$F9=b3$FA/$FB=w3$FC/$FD=w4
Then the initial run of the code looks something like this:
b1 = 0x20
b2 = 0x20
Y = 0
b3 = 0
w3 = 0
w4 = 0
do:
w1 = b1 * 0x100 + 0
w1 += b3
w1 += w3
w1 += w4
w2 = w1 + b2 * 0x100
mem[w1 + Y] = 0
w4 += 0x400
until w4 == 0x2000
This sets
- $2000
- $2400
- $2800
- $2C00
- $3000
- $3400
- $3800
- $3c00
to $#00.
Of course, the code continues....
We add #$80 to $FA, carrying it to $FB.
8057: 18 CLC
8058: A5 FA LDA $FA
805A: 69 80 ADC #$80
805C: 85 FA STA $FA
805E: A5 FB LDA $FB
8060: 69 00 ADC #$00
8062: 85 FB STA $FB
In our pseudo-code:
w3 += 0x80
and we loop until the high-byte of w3 is #$04.
8064: C9 04 CMP #$04
8066: D0 A4 BNE LOOP2
And continuing...
We add #$28 to $F9 until it's #$78.
8068: 18 CLC
8069: A5 F9 LDA $F9
806B: 69 28 ADC #$28
806D: 85 F9 STA $F9
806F: C9 78 CMP #$78
8071: D0 93 BNE LOOP3
The next part seems to just be a sleep:
8073: A2 00 LDX #$00
8075: CA LOOP4 DEX
8076: D0 FD BNE LOOP4
With the initial code, the remainder is:
8078: C8 INY
8079: C0 28 CPY #$28
807B: D0 85 BNE LOOP5
which just runs everything we've seen 0x28 times with incrementing versions of Y.
So the entire pseudo-code for the first run would be:
b1 = 0x20
b2 = 0x20
Y = 0
do:
b3 = 0
do:
w3 = 0
do:
w4 = 0
do:
w1 = b1 * 0x100 + 0
w1 += b3
w1 += w3
w1 += w4
w2 = w1 + b2 * 0x100
mem[w1 + Y] = 0
w4 += 0x400
until w4 == 0x2000
w3 += 0x80
until w3 // 0x100 == 0x04
b3 += 0x28
until b3 == 0x78
# sleep
Y += 1
until Y == 0x28
But of course, it gets run 4 times.
Between the first and second time,
POKE 32840,177:POKE 32841,8
is called which means that, the second time around, instead of zeros being written to the screen like
mem[w1 + Y] = 0
above, we instead have a copying operation:
mem[w1 + Y] = mem[w2 + Y]
where:
w2 = w1 + 0x2000
So this effectively copies from hires page two to hires page one.
Prior to doing this, we've loaded AKA2 into page one and AKA5 into the two, so this is a transformation from AKA2 into AKA5.
Between the second and third times,
POKE 32769,39
POKE 32888,136
POKE 32890,255
is called. This
- sets
Yto$27instead of$00 - causes
Yto decrement instead of increment - changes the
Ytest to compare for$FFinstead of$28
That just means that the third time we run it, Y goes from $27 to $00 instead of $00 to $27.
But prior to this third time, we've loaded AKA3 into the second page so this transformation is from AKA5 to AKA3.
For the fourth call to $8000, we change the code back to simply writing $#00.
Oddly, after the fourth and final call, we change the code one last time to go back to copying page two to page one even though I don't believe the code is called again.
And also, we don't appear to have ever made use of any of the data after this code in the AKA4 file.
So, in summary, AKA0 (with help from binary code in AKA4) does the following:
- prints out a welcome message and waits for a key to be pressed
- zeros-out hires page one
- loads
AKA2into hires page one - shows hires page one
- loads
AKA5into hires page two - copies page two to page one in a wipe pattern
- loads
AKA3into hires page two - copies page two to page one with one of the loops going in the opposite order to the previous wipe
- sleeps for bit
- zeros-out hires page one in the original wipe pattern
- sleeps for a shorter time
- switches to text mode
- prints a loading message
- runs
AKA1
Remaining questions (about AKA0, not overall):
- why does it zero-out page one initially if only to load
AKA2before screen is shown? - why change the code back from zeroing-out to copying if it isn't run again?
- why does none of the "data" after the code in
AKA4appear to be used?
Image Files
Now I'm going to add support to a2disk for rendering hires graphics files.
AKA2

AKA5

AKA3

AKA1
And AKA1 is:
10 POKE 103,0: POKE 104,64
20 HGR : POKE - 16302,0
30 PRINT CHR$ (4)"RUN AKA6"
POKE 103,0: POKE 104,64 sets LOMEM to $4000.
POKE - 16302,0 sets full screen graphics mode.
Here there is an explicit CHR$(4) to make PRINT run a DOS command.