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.