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 below * `POKE 32840,177:POKE 32841,8` (`LDA ($08),Y`) `BLOAD AKA2,A$2000` is printed. * `POKE - 16302,0` set full-screen graphics * `POKE - 16297,0` set hi-res graphics mode * `POKE - 16300,0` set hi-res page 1 * `POKE - 16304,0` set graphics mode `BLOAD AKA5,A$4000` is printed. * `CALL 32768` calls the code below `BLOAD 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 below * `POKE 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 below * `POKE 32840,177:POKE 32841,8` (`LDA ($08),Y`) Another pause caused by looping 1 to 500. * `POKE - 16303,0`set text mode The loading message is printed. * `POKE 33,1` set width of scrolling window to 1 * `POKE 34,23` set top of scrolling window to 23 `RUN 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
* sets `Y` to `$27` instead of `$00`
* causes `Y` to decrement instead of increment
* changes the `Y` 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:
* prints out a welcome message and waits for a key to be pressed
* zeros-out hires page one
* loads `AKA2` into hires page one
* shows hires page one
* loads `AKA5` into hires page two
* copies page two to page one in a wipe pattern
* loads `AKA3` into 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 `AKA2` before 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 `AKA4` appear to be used?
----
Now I'm going to add support to `a2disk` for rendering hires graphics files.
----
## AKA2
![AKA2.png](/media/171/AKA2.png)
## AKA5
![AKA5.png](/media/173/AKA5.png)
## AKA3
![AKA3.png](/media/172/AKA3.png)
----
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.