I think I'll use child streams to separately explore the preamble files and main game file.
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 PRINT
ed 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
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 CHR$(4)
preceding and dropped by the de-tokenizer?)
POKE 254,32
POKE 255,32
POKE 32840,169:POKE 32841,0
(LDA $#00
)CALL 32768
calls the code belowPOKE 32840,177:POKE 32841,8
(LDA ($08),Y
)BLOAD AKA2,A$2000
is printed.
POKE - 16302,0
set full-screen graphicsPOKE - 16297,0
set hi-res graphics modePOKE - 16300,0
set hi-res page 1POKE - 16304,0
set graphics modeBLOAD AKA5,A$4000
is printed.
CALL 32768
calls the code belowBLOAD AKA3,A$4000
is printed.
POKE 32769,39
($8001
)POKE 32888,136
(set $8078
to DEY
)POKE 32890,255
($807A
)CALL 32768
calls the code belowPOKE 32769,0
($8001
)POKE 32888,200
(restore $8078
to INY
)POKE 32890,40
($807A
)There is a pause caused by looping 1 to 4000.
POKE 32840,169:POKE 32841,0
(LDA $#00
)CALL 32768
calls the code belowPOKE 32840,177:POKE 32841,8
(LDA ($08),Y
)Another pause caused by looping 1 to 500.
POKE - 16303,0
set text modeThe loading message is printed.
POKE 33,1
set width of scrolling window to 1POKE 34,23
set top of scrolling window to 23RUN AKA1
is printed.
@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
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
Y
to $27
instead of $00
Y
to decrement instead of incrementY
test to compare for $FF
instead 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:
AKA2
into hires page oneAKA5
into hires page twoAKA3
into hires page twoAKA1
Remaining questions (about AKA0
, not overall):
AKA2
before screen is shown?AKA4
appear to be used?Now I'm going to add support to a2disk
for rendering hires graphics files.
And AKA1
is:
10 POKE 103,0: POKE 104,64
20 HGR : POKE - 16302,0
30 PRINT CHR$ (4)"RUN AKA6"
Same as AKALABETH START
in the first disk image.
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.