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,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 $8078to 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,0set 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.

Some Information From Twitter

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 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?

Image Files

Now I'm going to add support to a2disk for rendering hires graphics files.

AKA2

load image 1

AKA5

load image 2

AKA3

load image 3

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.