Fantavision (Reloaded)
Go to file
2017-03-25 01:35:57 -07:00
pics Update TOC 2017-03-25 01:34:43 -07:00
README.md Clarify disk nibbles 2017-03-25 01:35:57 -07:00

Fantavision Reloaded

Table of Contents

  • Introduction

  • Preparation

  • ESC to Bug out

  • Ye Olde Boot Tracing

    • P5 Boot PROM
  • Boot Tracing Stage 1b

  • RWTS

  • Boot Tracing Stage 2

    • Stupid Kids and their Graffiti
  • Boot Tracing Stage 3

  • Time to go Loco, er, Logo

    • Logo -- no, not the language
    • Logo 2
    • Logo 3 !
    • Logo 4 !!
  • "What this, Byte-Code, you say young chap?"

    • Backup 1
    • Backup 2
    • Backup Byte-Code Interpreter
      • Backup 1 Disassembly
      • Backup 2 Disassembly
  • Applesoft?!

    • Applesoft Tokens
  • Boot Tracing Stage 5

  • Boot Tracing Stage 6

    • Boot Stage Memory Map
  • Disk Information

    • Notes
    • COPY, eh?
    • "Kracking" Fantavision
      • Cracking in 3 Easy Payments, er Steps
      • Cracking Step 1: The Easy Stuff
      • Cracking Step 2: The Tedious Stuff
      • Cracking Step 3: Skip Nibble Count 2
    • ProDOS Hybrid!?
    • ProDOS Block Review
    • Disk Usage Summary
    • Disk Usage Details
  • Easter Eggs

    • Source Code
    • Original File Names
    • Deleted Files!?
  • In Search of a Better Beep, or two

Introduction

"Get off my LAN, Grandpa"

Before Macromedia's Flash became ubiquitous back in 1985 Brøderbund released a fantastic animation program called Fantavision

  • Logo

It had a "mouse cursor" and pull-down menus on the Apple II back in 1985 all in glorious 6 color 280x192 resolution.

  • Logo

But isn't there more then 6 colors in that picture? Technically, no, but dithering gives the impression there are more colors.

It is a pity it was never updated to use the Double Hi-Resolution graphics that the //e, //c, and Laser 128 supported. (YES, I'm aware there was a IIgs version.)

  • TODO: PIC: Disk

  • TODO: PIC: Box

  • TODO: PIC: Manual

Back then I had many unanswered questions about it:

  • Where is the Logo stored on disk?
  • Where are the (bitmap) icons stored in memory?
  • How is the (mouse) cursor drawing code done?
  • What is the movie file format?
  • How is it copy-protected?
  • How could we remove the copy-protection?
  • How does the backup utlity know if a copy has been made?

Let's have some fun exploring these. Along the way we'll discover:

  • Boot Tracing
  • Learn HOW Fantavision is actually copy-protected
  • Restore the "Backup Functionality" by writing an "UNBACKUP" program
  • Use COPYA to copy it so we can inspect it with any Sector Editor
    • Trivially crack it
  • Easter Eggs

Let's get started!

Preparation

Before we get started, first a word from our sponsor, er, experience.

WARNING: ALWAYS write-protect your original disk to prevent (accidental) mistakes from happening!

No, I've never done that, but it is WAY too easy to make this mistake. Take the 30 seconds (of insurance). You won't regret the peace of mind it will bring.

With that warning out of the way:

Let's create a blank DOS 3.3 data disk that we'll use to save our work-in-progress.

  1. Boot your favorite "Fast" DOS such as ProntoDOS or Diversi-DOS.

  2. Replace the disk with a blank floppy.

  3. Enter these lines:

    NEW
    10 ?CHR$(4);"CATALOG"
    INIT HELLO
  1. Remove it from the disk drive and label it as Fanta.Work

ESC to Bug Out

You may or may not know that while Fantavision boots up one can hold ESC down to enter the built-in backup utility!

Sadly, most originals will show this screen:

  • Backup used

What we want is this picture:

  • Backup available

Now my //e has 3 disk drives, two connected on Slot 6, and one on Slot 5. For fun let's boot Fantavision from drive 5 and enter the backup utility.

We get the message:

Make sure that the Fantavision disk is in slot 5, drive 1

OK.

Pressing RETURN and it reboots from slot 6. Whoops! The programmer detected which slot it was started from but were lazy and didn't properly "exit" (JMP) to the right slot.

If we were really bored we could write some boot sector tools on the blank disk in slot 6 and "chain" reverse engineering but let's take the more traditional approach.

Ye Olde Boot Tracing

  1. If you computer isn't already on, turn it on and press CTRl-RESET

  2. Insert the original Fantavision disk in Slot 6, Drive 1

  3. Type these instructions:

    CALL-151
    9600<C600.C6FFM
    96FA:1F
    1F01:00
    9600G

We end up with this "error" screen:

1F03-    A=01 X=60 Y=00 P=31 S=F0
*

Let's turn off that drive motor so we don't wear the drive (and disk) out.

    C0E8:1

We want to save this puppy so let's move the Boot Stage 1 to a safe place ...

    3800<800.8FFM

... and reboot with our Fanta.Work disk so we can save it.

I already have a work ProntoDOS disk in Drive 5 so I'll be using ...

    C500G

... but if you only have 1 drive you'll just need to flip disks more often. Anytime you see C500G replace it with:

    C600G

Once we are back in DOS land we can save it.

    800<3800.38FFM
    BSAVE B1.FANTAVISION_T00S0_0800,A$800,L$100

Now that we have the boot sector saved let's tear into it and see what it is doing. Now I could be a jerk and hand-wave it as

  • "Implementation Details" or the equally annoying
  • "Left as an Exercise for the Reader"

but I'll be nice guy and provide a fully documented disassembly:

                    ; Disk Drive P5 PROM Usage ZP and IO
                            P5.Buff             = $26   ; 16-bit pointer to dest
                            P5.SlotX16          = $2B   ; i.e. $60 = Slot 6
                            P5.WantTrack        = $41   ;

                            P5.SecWant          = $3D   ;
                            P5.SecTotal         = $0800 ; T00S0 @ $00: total number of sectors to load

                            PHASE_MASK          = $03   ; 4 phases
                            PHASE0_OFF          = $C080
                            PHASE0_ON           = $C081

                            DRIVE_OFF           = $C088 ; Motor Off
                            DRIVE_ON            = $C089 ; Motor On
                            DRIVE_DATA          = $C08C

                    ; Globals
                            boot_HalfTrack_Seek = $50
                            boot_HalfTrack_Have = $51
                            NumSecLeft          = $52
                            BufZP               = $1000  ; Zero Page is saved here

                            rwts_SlotX16        = $FD
                            rwts_HalfTrack_Have = $FF    ;

                            ReadBoot3           = $B500
                            LoadBoot3           = $BC00
                            ExecBoot3           = $BE00

                    ; Misc.
                            RESET               = $3F2  ; Ctrl-Reset Vector + Checksum
                            TXTHOLE31           = $4FB  ; 40x24 Text Sceen Hole

                    ; IO Switches
                            STORE40     = $C000 ; W
                            KEYBOARD    = $C000 ;R
                            KEYSTROBE   = $C010
                            ALTZPOFF    = $C008
                            CLR80VID    = $C00C
                            CLRALTCHAR  = $C00E ; Turn off Mouse Text
                            DHIRESOFF   = $C05F

                            ROMIN       = $C081

                    ; F800 ROM Entry Points
                            F8.INIT     = $FB2F ;
                            F8.HOME     = $FC58 ; Clear 40x24 text screen
                            F8.SETNORM  = $FE84 ;
                            F8.SETVID   = $FE93 ; PR#0
                            F8.SETKBD   = $FE89 ; IN#0

                            ORG $800

    0800:01                 DFB $01                 ; Tell P5 PROM to read only 1 Sector
                    Boot1:                          ; A=01 X=60 Y=00 P=31 S=F0 Boot Stage #1 Entry Point from $C6F8
    0801:A9 60              LDA #$60                ; Poke "RTS" @ $0801 so that when we call
    0803:8D 01 08           STA Boot1               ; `CallReadSec` it will return to NextSector @ $086F

    0806:A2 00              LDX #$00                ; Save _absolute_ track the drive head is on
    0808:86 FF              STX rwts_HalfTrack_Have ; since we can only move _relative_ tracks

    080A:           SaveZP:                         ; Copy $0000[ $00 .. $FF ] to $1000
    080A:B5 00              LDA $00,X               ; Just in case ALTZPON was accidently left on
    080C:9D 00 10           STA BufZP,X             ;
    080F:E8                 INX                     ;
    0810:D0 F8              BNE SaveZP              ;^ $080A

    0812:8D 08 C0           STA ALTZPOFF            ; //e+, //c, IIgs, Laser 128

                    LoadZP:                         ;
    0815:BD 00 10           LDA BufZP,X             ;
    0818:95 00              STA $00,X               ;
    081A:E8                 INX                     ;
    081B:D0 F8              BNE LoadZP              ;^ $0815

    081D:A9 FF              LDA #$FF                ;
    081F:8D FB 04           STA TXTHOLE31           ; Screen Text Hole Slot 3, Temp 1

    0822:8D F3 03           STA RESET+1             ; Make Ctrl-Reset
    0825:8D F4 03           STA RESET+2             ; do warm restart @ TODO: FIXME:

    0828:8D 00 C0           STA STORE40             ;
    082B:8D 0C C0           STA CLR80VID            ;
    082E:8D 0E C0           STA CLRALTCHAR          ;
    0831:8D 5F C0           STA DHIRESOFF           ;
    0834:8D 81 C0           STA ROMIN               ;

    0837:20 2F FB           JSR F8.INIT             ;
    083A:20 58 FC           JSR F8.HOME             ; Clear 40x24 text screen
    083D:20 84 FE           JSR F8.SETNORM          ;
    0840:20 93 FE           JSR F8.SETVID           ; PR#0
    0843:20 89 FE           JSR F8.SETKBD           ; IN#0

    0846:A6 2B              LDX P5SLOTx16           ; X=60 = Drive Slot * $10
    0848:8A                 TXA                     ;
    0849:4A                 LSR                     ;
    084A:4A                 LSR                     ;
    084B:4A                 LSR                     ;
    084C:4A                 LSR                     ; A=06
    084D:09 C0              ORA #$C0                ; A=C6 = $Cx00 = Memory-Mapped IO: i.e. $C6 = slot 6 P5 PROM
    084F:8D 6E 08           STA CallReadSec+2       ; *** SELF-MODIFYING Code !

    0852:A9 0F              LDA #$0F                ; Num Sectors to Read
    0854:85 52              STA NumSecLeft          ;
    0856:A9 15              LDA #$15                ; Track $15 contains Boot Stage 2
    0858:85 41              STA P5.WantTrack        ;
    085A:0A                 ASL                     ; $15 * 2 = $2A Half-Tracks
    085B:20 81 08           JSR BS.HalfTrackSeek    ;v $0881

                    LoadSector:
    085E:A5 52              LDY NumSecLeft          ;
    0860:B9 BE 08           LDA LogicalSector,Y     ;
    0863:85 3D              STA P5.SecWant          ;
    0865:B9 CE 08           LDA DestPage,Y          ;
    0868:F0 05              BEQ NextSector          ;v $086F Never read into Zero Page!
    086A:85 27              STA P5.Buff+1           ; Tell P5 PROM where in mem we want to load

                    CallReadSec:                    ; *** SELF-MODIFIED @ $084F
    086C:20 5C 00           JSR $005C               ; CALL P5 PROM Read Sector @ $C65C

                    NextSector:
    086F:C6 52              DEC NumSecLeft          ;
    0871:10 EB              BPL LoadSector          ;^ $085E

                    TestBoot3:                      ; Boot Stage 2 done, do stage 3
    0873:A6 2B              LDX P5.SlotX16          ;
    0875:86 FD              STX rwts_SlotX16        ;
    0877:A9 BC              LDA #>LoadBoot3         ; Load T$22 4&4 "4 sectors" @ $BC00, $BD00, $BE00, $BF00
    0879:20 00 B5           JSR   ReadBoot3         ; Boot Stage 2 reads in encrypted Stage 3
    087C:B0 F5              BCS   TestBoot3         ;^ $0873 C=1 error, Hang if NibbleCheck1 fails
    087E:4C 00 BE           JMP   ExecBoot3         ;

                    BS.HalfTrackSeek:               ; A = Half Track to Seek
    0881:85 50              STA boot_HalfTrack_Seek ; Save Half Track to seek

                    BS.NextTrack:                   ;
    0883:A5 FF              LDA rwts_HalfTrack_Have ; 6502 Math Reminder:
    0885:85 51              STA boot_HalfTrack_Have ;   Clear Carry before Addition
    0887:38                 SEC                     ;   Set   Carry before Subtraction
    0888:E5 50              SBC boot_HalfTrack_Seek ; Already on requested track?
    088A:F0 2C              BEQ SeekDone            ;v $08B8 Yup! Nothing to do!
    088C:B0 04              BCS BS.MoveTrackOut     ;v $0892 Move Track out to Track $00

                    BS.MoveTrackIn:
    088E:E6 FF              INC rwts_HalfTrack_Have ; Move Track in  to Track $22
    0890:90 02              BCC DoSeek              ;v $0894 Always, could also do: INC rwts_HalfTrack_Have

                    BS.MoveTrackOut:
    0892:C6 FF              DEC rwts_HalfTrack_Have ; Intentional fall-into DoSeek

                    DoSeek:                         ; Boot Sector Common code
    0894:20 AD 08           JSR BS.PhaseOn          ;v
    0897:20 B9 08           JSR BS.Delay            ;v
                    BS.PhaseOff:
    089A:A5 51              LDA boot_HalfTrack_Have ;
    089C:29 03              AND #PHASE_MASK         ; Drive Stepper Motor = 4 Phases
    089E:0A                 ASL                     ; 1 phase = 1/2 track
    089F:05 2B              ORA P5.SlotX16          ;
    08A1:A8                 TAY                     ;
    08A2:B9 80 C0           LDA PHASE0_OFF,Y        ; Turn stepper motor off
    08A5:20 B9 08           JSR BS.Delay            ;v
    08A8:F0 D9              BEQ BS.NextTrack        ;^ $0883 A=00, always
    08AA:20 B9 08           JSR BS.Delay            ;v *** DEAD CODE ??? Never reached!
                    BS.PhaseOn:
    08AD:A5 FF              LDA rwts_HalfTrack_Have ;
    08AF:29 03              AND #PHASE_MASK         ;
    08B1:0A                 ASL                     ; 1 phase = 1/2 track
    08B2:05 2B              ORA P5.SlotX16          ;
    08B4:A8                 TAY                     ;
    08B5:B9 81 C0           LDA PHASE0_ON,Y         ; Turn stepper motor on
                    SeekDone:
    08B8:60                 RTS                     ;
                    BS.Delay:
    08B9:A9 28              LDA #$28                ; You _could_ minimize the delay time like in DOS 3.3
    08BB:4C A8 FC           JMP WAIT                ; but that bloats the code

                    LogicalSector:              ; Logical->Physical Sector = Interleave 2, DOS order?? TODO

    08BE:00 0D 0B 09        DFB $0,$D,$B,$9,$7,$5,$3,$1
    08C2:07 05 03 01
    08C6:0E 0C 0A 08        DFB $E,$C,$A,$8,$6,$4,$2,$F
    08CA:06 04 02 0F

                    DestPage:
    08CE:B0 B1 B2 B3        DFB $B0,$B1,$B2,$B3,$B4,$B5,$B6,$B7 ; "Pages" of 16-bit address
    08D2:B4 B5 B6 B7
    08D6:18 19 1A 1B        DFB $18,$19,$1A,$1B,$1C,$1D,$1E,$1F ; to load into
    08DA:1C 1D 1E 1F
                            DS $900-*               ; unused/wasted
    08DE:00 00                                      ;
    08E0:00 00 00 00                                ;
    08E4:00 00 00 00                                ;
    08E8:00 00 00 00                                ;
    08EC:00 00 00 00                                ;
    08F0:00 00 00 00                                ;
    08F4:00 00 00 00                                ;
    08F8:00 00 00 00                                ;
    08FC:00 00 00 00                                ;

P5 Boot PROM

The boot sector contains code to:

  • Seek a Track, and
  • Read a Track.

Who knew we would already have 75% of RWTS -- Read, Write, Track, Sector :-)

In case you are wondering where Sector comes from -- it comes from the P5 PROM ReadSector routine @ $C65C. On my //e it looks this:

                            ORG $C65C

                    P5.ReadSector
    C65C:18         ^0      CLC             ; C=0 Look Address D5,AA,96
    C65D:08         ^1      PHP             ; C=1 look Data    D5,AA,AD

    C65E:BD 8C C0   ^2      LDA DRIVE_DATA,X
    C661:10 FB              BPL ^2          ;^ $C65E
    C663:49 D5      ^3      EOR #$D5        ; Address 1st Prologue Field
    C665:D0 F7              BNE ^2          ;^ $C65E

    C667:BD 8C C0   ^4      LDA DRIVE_DATA,X
    C66A:10 FB              BPL ^4          ;^ $C667
    C66C:C9 AA              CMP #$AA        ; Address 2nd Prologue Field
    C66E:D0 F3              BNE ^3          ;^ $C663
    C670:EA                 NOP

    C671:BD 8C C0   ^5      LDA DRIVE_DATA,X
    C674:10 FB              BPL ^5          ;^ $C671
    C676:C9 96              CMP #$96        ; Address 3rd Prologue Field, NOTE: C=1 for $C68C !
    C678:F0 09              BEQ ^6          ;v $C683

    C67A:28                 PLP             ;Looking for Address or Data prologue?
    C67B:90 DF              BCC ^0          ;^ $C65C
    C67D:49 AD              EOR #$AD        ; Prologue Data Field: 3rd
    C67F:F0 25              BEQ ^10         ;v $C6A6
    C681:D0 D9              BNE ^0          ;^ $C65C
    C683:A0 03      ^6      LDY #$03        ; Read 3 nibbles: Vol,Track,Sec, skip Checksum
    C685:85 40      ^7      STA TrackHave   ; Eventually contains Track but init #$96 from $C676

    C687:BD 8C C0   ^8      LDA DRIVE_DATA,X
    C68A:10 FB              BPL ^8          ;^ $C687  mask = $FF  since: C=1 from $C676
    C68C:2A                 ROL             ; ... 4&4 encoded: 1a1c1e1g, C=1 since A > $80
    C68D:85 3C              STA $3C         ; ... 1st half  A= a1c1e1gC

    C68F:BD 8C C0   ^9      LDA DRIVE_DATA,X
    C692:10 FB              BPL ^9          ;^ $C68F        &= 1b1d1f1h
    C694:25 3C              AND $3C         ; ... 2nd half   = abcdefgh
    C696:88                 DEY
    C697:D0 EC              BNE ^7          ;^ $C685
    C699:28                 PLP             ; Don't care about carry, restore stack
    C69A:C5 3D              CMP P5.SecWant  ; A=SectorHave == SectorWant? (init $00 from $C654)
    C69C:D0 BE              BNE ^0          ;^ $C65C
    C69E:A5 40              LDA TrackHave   ; (read from prologue)
    C6A0:C5 41              CMP TrackWant   ;
    C6A2:D0 B8              BNE ^0          ;^ $C65C !=
    C6A4:B0 B7              BCS ^1          ;^ $C65D <=
    C6A6:A0 56      ^10     LDY #$56        ; Decode $56 nibbles in 6&2
    C6A8:84 3C      ^11     STY $3C

    C6AA:BC 8C C0   ^12     LDY DRIVE_DATA,X
    C6AD:10 FB              BPL ^12         ;^ $C6AA
    C6AF:59 D6 02           EOR $36C-$96,Y  ; [$96] $36C:00, $36C-$96=$2D6
    C6B2:A4 3C              LDY $3C         ; [$FF] $3D5:3F, $2D6+$FF=$3D5
    C6B4:88                 DEY
    C6B5:99 00 03           STA $0300,Y     ; Buf1 = [$300 .. $35B]
    C6B8:D0 EE              BNE ^11         ;^ $C6A8

    C6BA:84 3C      ^13     STY $3C         ; Y = #FF
    C6BC:BC 8C C0   ^14     LDY DRIVE_DATA,X
    C6BF:10 FB              BPl ^14         ;^ $C6BC
    C6C1:59 D6 02           EOR $36C-$96,Y
    C6C4:A4 3C              LDY $3C
    C6C6:91 26              STA ($26),Y
    C6C8:C8                 INY
    C6C9:D0 EF              BNE ^13         ;^ $C6BA
    C6CB:BC 8C C0   ^15     LDY DRIVE_DATA,X
    C6CE:10 FB              BPL ^15
    C6D0:59 D6 02           EOR $36C-$96,Y
    C6D3:D0 87      ^16     BNE             ;^ $C65C

    C6D5:A0 00              LDY #$00
    C6D7:A2 56      ^17     LDX #$56
    C6D9:CA         ^18     DEX
    C6DA:30 FB              BMI ^17         ; $C6D7
    C6DC:B1 26              LDA ($26),Y
    C6DE:5E 00 03           LSR $0300,X
    C6E1:2A                 ROL
    C6E2:5E 00 03           LSR $0300,X
    C6E5:2A                 ROL
    C6E6:91 26              STA (P5.Buff),Y
    C6E8:C8                 INY
    C6E9:D0 EE              BNE ^18         ;^ $C6D9

    C6EB:E6 27              INC P5.Buff+1   ; DestAddr += $0100
    C6ED:E6 3D              INC P5.SecWant  ; SectorsLoaded++
    C6EF:A5 3D              LDA P5.SecWant  ;    also alias for NextSectorToLoad
    C6F1:CD 00 08           CMP P5.SecTotal ; SectorsLoaded <= SectorsTotal ?
    C6F4:A6 2B              LDX P5.SlotX16  ;
    C6F6:90 DB              BCC ^16         ;^ $C6D3

    C6F8:4C 01 08           JMP $0801       ; $0800 = Num of Sectors to read
    C6FB:00                 DS $C700-*      ; unused last 5 bytes
    C6FC:00 00 00 00

Let's map out the Track/Sector (T/S) boot stages

        ; Stage Addr T/S   Load
        ; ---------------------
        ; 0     C600 T00S0 0800
        ; ---------------------
        ; 1     0801
        ; ---------------------
        ; 2          T15S0 B000
        ; 2          T15SD B100
        ; 2          T15SB B200
        ; 2          T15S9 B300
        ; 2          T15S7 B400
        ; 2          T15S5 B500
        ; 2          T15S3 B600
        ; 2          T15S1 B700
        ; 2          T15SE 1800
        ; 2          T15SC 1900
        ; 2          T15SA 1A00
        ; 2          T15S8 1B00
        ; 2          T15S6 1C00
        ; 2          T15S4 1D00
        ; 2          T15S2 1E00
        ; 2          T15SF 1F00
        ; ---------------------
        ; 2     B500       
        ; ---------------------
        ; ?     BE00 T??S? 
        ; ---------------------

Boot Tracing Stage 1b

Since we'll be typing in the same commands over and over again let's make a "Turn-Key" system on our DOS data disk.

    1EEE:A2 00      LDX #$00
    1EF0:BD 00 C6   LDA $C600,X
    1EF3:9D 00 96   STA $9600,X
    1EF6:E8         INX
    1EF7:D0 F7      BNE $1EF0
    1EF9:A9 1F      LDA #$1F    ; Stop @ $1F01
    1EFB:8D FA 96   STA $96FA
    1EFE:4C 00 96   JMP $9600
    1F01:00         BRK

That is,

    CALL-151
    1EEE:A2 00 BD 00 C6 9D 00 96
    1EF6:E8 D0 F7 A9 1F 8D FA 96
    1EFE:4C 00 96 00

Save it via:

    BSAVE BOOT.STOP,A$1EEE,L$14

And let's make it auto-load when we boot our DOS disk:

    LOAD HELLO
    5 ? CHR$(4);"BLOAD BOOT.STOP"
    SAVE HELLO

However, we need to stop after boot stage 2 has loaded:

    1F01:A9 0E      LDA #$0E
    1F03:8D 7F 08   STA $087F   ; 87E: JMP $BE00
    1F06:A9 1F      LDA #$1F    ;  ->
    1F08:8D 80 08   STA $0880   ; 87E: JMP $1F0E
    1F0B:4C 01 08   JMP $0801
    1F0E:00
    1EEEG
    C0E8:1
    4000<B000.BFFFM

Boot to our Fanta.Work disk ...

    C500G

... so we can save it:

    BSAVE B2.FANTAVISION_T15_B000,A$4000,L$800

We'll also want to save Boot Stage 3 while we're here to use later:

    BSAVE B3.FANTAVISION_T22_BC00,A$4C00,L$400

RWTS

Let's take a moment to analyze this mini-RWTS @ $B000

I'll use the prefix:

  • lowercase rwts for variables
  • uppercase RWTS for functions
                            rwts_ReadChecksum       = $E1   ; Addrress Prologue, Volume, Track, Sector, Checksum
                            rwts_Sector_Have        = $E3
                            rwts_E4                 = $E4   ; ???
                            rwts_LoadAddr           = $E6   ; 16-bit Pointer to dest buf
                            rwts_ReadCount          = $E8   ; Attempts Remaining to read sector
                            rwts_HalfTrack_Want     = $EB
                            rwts_HalfTrack_Prev     = $EC   ; (mirror of $FF)
                            rwts_Track              = $ED

                            rwts_Return             = $EB   ; 16-bit Pointer to caller

                            rwts_temp               = $EC

                            rwts_SlotX16            = $FD
                            rwts_HalfTrack_Have     = $FF   ; Current track

                            rwts_DestAddr           = $B3E9 ; ProDOS block load dest

                            ORG $B000

    B000:4C 51 B4   RWTS_ReadTrack:      JMP Do_ReadTrack   ; [0] Y = Track, A = Addr
    B003:4C ED B3   RWTS_LoadCode:       JMP Do_LoadCode    ; [1] Y = Track, $11 bytes after Program Counter
    B006:4C 22 B4   RWTS_LoadBlocks:     JMP Do_LoadBlocks  ; [2] Addr = $A,Y, X = ??? TODO
    B009:4C E9 B0   RWTS_Seek:           JMP Do_Seek        ; [3] A = Track
    B00C:4C 62 B4   RWTS_ReadTrackQuiet: JMP Do_B462        ; [4] Y = Track, A = Addr, no zap if bad read

    B00F:38                 SEC                             ; Error TODO: Who calls us???
    B010:60                 RTS

                    ; ====================
                    ; X = Slot * $10
                    ; ====================
                    RWTS_ReadSector
    B011:86 FD              STX rwts_SlotX16        ; $E6 = Pointer to Dest Address
    B013:8A                 TXA
    B014:09 8C              ORA #<DRIVE_DATA        ; DRIVE_DATA = $C08C
    B016:8D 70 B0           STA FixupA+1
    B019:8D 87 B0           STA FixupB+1
    B01C:8D 9D B0           STA _B09C+1
    B01F:8D B1 B0           STA _B0B0+1
    B022:8D C6 B0           STA _B0C5+1

    B025:A5 E6              LDA rwts_LoadAddr
    B027:A4 E7              LDY rwts_LoadAddr+1
    B029:8D C3 B0           STA _B0C2+1
    B02C:8C C4 B0           STY _B0C2+2
    B02F:38                 SEC
    B030:E9 54              SBC #$54
    B032:B0 02              BCS                     ;v $B036 TODO
    B034:88                 DEY
    B035:38                 SEC

    B036:8D AB B0           STA                     ; $B0AA+1
    B039:8C AC B0           STA                     ; $B0AA+2
    B03C:E9 57              SBC #$57
    B03E:B0 01              BCS                     ;v $B041

    B041:8D 84 B0           STA                     ; $B083+1
    B044:8C B5 B0           STA                     ; $B083+2
    B047:A0 20              LDY #$20

    B049:88                 DEY
                    DataProlog1:
    B04C:BD 8C C0           LDA DRIVE_DATA,X
    B04F:10 FB              BPL DataProlog1
                    TestProlog1:
    B051:49 D5              EOR #$D5
    B053:D0 F4              BNE _B049
    B055:EA                 NOP
                    DataProlog2:
    B056:BD 8C C0           LDA DRIVE_DATA,X
    B059:10 FB              BPL DataProlog2         ;^ $B056
    B05B:C9 AA              CMP #$AA
    B05D:D0 F2              BNE TestProlog1         ;^ $B051
    B05F:EA                 NOP
                    DataProlog3:
    B060:BD 8C C0           LDA DRIVE_DATA,X
    B063:10 FB              BPL DataProlog3
                    TestProlog3:
    B065:C9 AD              CMP #$AD
    B067:D0 E8              BNE                     ;^ $B051
    B069:A0 AA              LDY #$AA                ;Decode #$56 nibbles into $2AA..$2FF
    B06B:A9 00              LDA #$00
                    NextChecksum:
    B06D:85 E1              STA rwts_ReadChecksum
                    FixupA:
    B06F:AE 8C C0           LDX DRIVE_DATA          ; *** SELF-MODIFIED CODE @ $B016
    B072:10 FB              BPL FixupA              ; A = $96 .. $FF
    B074:BD 00 B1           LDA DiskNibble64-$96,X  ; [nib]
    B077:99 00 02           STA Decode200,Y         ; $02AA .. $02FF buffer of $56 bytes
    B07A:45 E1              EOR rwts_ReadChecksum
    B07C:C8                 INY
    B07D:D0 EE              BNE NextChecksum        ;^ $B06D

    B07F:A0 AA              LDY #$AA
    B081:D0 03              BNE FixupB              ;v $B086, always
    B083:99 FF FF           STA $FFFF,Y             ; *** SELF-MODIFYING code!

                    FixupB:
    B086:AE 8C C0           LDX $C08C               ; *** SELF-MODIFIED CODE @ $B019
    B089:10 FB              BPL FixupB
    B08B:

... TODO ...


                    DataEpilog1:
    B0DC:C9 DE              CMP #$DE
    B0DE:B0 02              BCS DataEpilogGood      ;v $B0E2
    B0E0:38                 SEC
    B0E1:24                 DFB $24                 ; bit $zp
                    DataEpilogGood:
    B0E2:18                 CLC
    B0E3:68                 PLA
    B0E4:A0 55              LDY #$55
    B0E6:91 E6              STY (rwts_LoadAddr),Y
    B0E8:60                 RTS

                    Do_Seek:
    B0E9:0A                 ASL
    B0EA:85 EB              STA rwts_HalfTrack_Want
                    _NextTrack:                     ; Very similiar to BS.HalfTrackSeek T00S0 @ $0881
    B0EC:A5 FF              LDA rwts_HalfTrack_Have
    B0EE:85 EC              STA rwts_HalfTrack_Prev
    B0F0:38                 SEC
    B0F1:E5 EB              SBC rwts_HalfTrack_Want
    B0F3:F0 29              BEQ _Do_Seek_Done       ;v $B11E
    B0F5:B0 04              BCS MoveTrackOut        ;v $B0FB

                    _MoveHeadIn:
    B0F7:E6 FF              INC rwts_HalfTrack_Have
    B0F9:90 02              BCC _MoveHead           ;v $B0FD (always)

                    _MoveHeadOut:
    B0FB:C6 FF              DEC rwts_HalfTrack_Have
                    _MoveHead:
    B0FD:20 13 B1           JSR _PhaseOn
    B100:20 1F B1           JSR _Delay
                    _PhaseOff:
    B103:A5 EC              LDA rwts_HalfTrack_Prev
    B105:29 03              AND #PHASE
    B107:0A                 ASL
    B108:05 FD              ORA rwts_SlotX16
    B10A:A8                 TAY
    B10B:B9 80 C0           LDA PHASE0_OFF,Y
    B10E:20 1F B1           JSR _Delay
    B111:F0 D9              BEQ _NextTrack          ;^ $B0EC always
                    _PhaseOn:
    B113:A5 FF              LDA rwts_HalfTrack_Have
    B115:29 03              AND #PHASE_MASK
    B117:0A                 ASL
    B118:05 FD              ORA $rwts_SlotX16
    B11A:A8                 TAY
    B11B:B9 81 C0           LDA PHASE0_ON,Y
    B11E:60                 RTS
                    _Delay:
    B11F:A9 28              LDA #$28
    B121:4C 16 B4           JMP _Wait

                    LostPrologue:
    B124:38                 SEC
    B125:60                 RTS

                    RWTS_ReadPrologue           ; read sector prologue D5 AA 96 [xx yy zz] DE
    B126:A0 FC              LDY #$FC            ; save Sector $E3
    B128:84 EB              STY $EB             ; TODO rwts_find
                    FindAddr:
    B12A:C8                 INY
    B12B:D0 04              INC $EB
    B12F:F0 F3              BEQ LostPrologue    ;^ $B124
                    AddrProlog1:
    B131:BD 8C C0           LDA DRIVE_DATA,X
    B134:10 FB              BPL AddrProlog1
                    TestProlog1:
    B136:C9 D5              CMP #$D5
    B138:D0 F0              BNE FindAddr        ;^ $B12A
    B13A:EA                 NOP
                    AddrProlog2:
    B13B:BD 8C C0           LDA DRIVE_DATA,X
    B13E:10 FB              BPL AddrProlog2
                    TestProlog2:
    B140:C9 AA              CMP #$AA
    B142:D0 F2              BNE TestProlgo1     ;^ $B136
    B144:A0 03              LDY #$03            ; Vol, Track, Sector, Checksum

                    AddrProlog3:
    B146:BD 8C C0           LDA DRIVE_DATA,X
    B149:10 FB              BPL AddrProlog3
    B14B:C9 96              CMP #$96
    B14D:D0 E7              BNE TestProlog1     ;^ $B136
    B14F:A9 00              LDA #$00
    B151:85 EC              STA $EC

                    AddrVTSC:
    B153:BD 8C C0           LDA DRIVE_DATA,X
    B156:10 FB              BPL AddrVTSC
    B158:2A                 ROL
    B159:85 EB              STA $EB
    B15B:BD 8C C0           LDA DRIVE_DATA,X
    B15E:10 FB              BPL                 ;^ $B15B
    B160:25 EB              AND $EB
    B162:99 E2 00           STA $00E2,Y
    B165:45 EC              EOR $EC
    B167:88                 DEY
    B168:10 E7              BPL                 ;^ $B151
    B16A:A8                 TAY
    B16B:D0 B7              BNE                 ;^ $B124
                    AddrEpilog1:
    B16D:BD 8C C0           LDA DRIVE_DATA,X
    B170:10 FB              BPL AddrEpilog1     ;^ $B16D
    B172:C9 DE              CMP #$DE
    B174:90 AE              BCC                 ;^ $B124
    B176:18                 CLC                 ; C=0 Read Good
    B177:60                 RTS

    B178:FF FF FF FF        DFB $FF,$FF,$FF,$FF
    B17C:FF FF FF FF        DFB $FF,$FF,$FF,$FF
    B180:00 00 00 00        DS $B196-*
    B184:00 00 00 00
    B188:00 00 00 00
    B18C:00 00 00 00
    B190:00 00 00 00
    B194:00 00

                    DiskNibble64:
                                                ;Valid Disk Nibbles (6-bit * 4) Lookup Table
                                                ;[+0 +1 +2 +3 +4 +5 +6 +7]
                                                ;[+8 +9 +A +B +C +D +E +F]
    B196:      00 04        DFB                 ; -- -- -- -- -- -- 96 97
    B198:98 99 08 0C        DFB $98,$99,$08,$0C ; -- -- 9A 9B -- 9D 9E 9F
    B19C:9C 10 14 18        DFB $9C,$10,$14,$18 ;
    B1A0:A0 A1 A2 A3        DFB $A0,$A1,$A2,$A3 ; -- -- -- -- -- -- A6 A7
    B1A4:A4 A5 1C 20        DFB $A4,$A5,$1C,$20
    B1A8:A8 A9 AA 24        DFB $A8,$A9,$AA,$24 ; -- -- -- AB AC AD AE AF
    B1AC:28 2C 30 34        DFB $28,$2C,$30,$34
    B1B0:B0 B1 38 3C        DFB $B0,$B1,$38,$3C ; -- -- B2 B3 B4 B5 B6 B7
    B1B4:40 44 48 4C        DFB $40,$44,$48,$4C
    B1B8:B8 50 54 58        DFB $B8,$50,$54,$58 ; -- B9 BA BB BC BD BE BF
    B1BC:5C 60 64 68        DFB $5C,$60,$64,$68
    B1C0:C0 C1 C2 C3        DFB $C0,$C1,$C2,$C3 ; -- -- -- -- -- -- -- --
    B1C4:C4 C5 C6 C7        DFB $C4,$C5,$C6,$C7
    B1C8:C8 C9 CA 6C        DFB $C8,$C9,$CA,$6C ; -- -- -- CB -- CD CE CF
    B1CC:CC 70 74 78        DFB $CC,$70,$74,$78
    B1D0:D0 D1 D2 7C        DFB $D0,$D1,$D2,$7C ; -- -- -- D3 -- -- D6 D7
    B1D4:D4 D5 80 84        DFB $D4,$D5,$80,$84
    B1D8:D8 88 8C 90        DFB $D8,$88,$8C,$90 ; -- D9 DA DB DC DD DE DF
    B1DC:94 98 9C A0        DFB $94,$98,$9C,$A0
    B1E0:E0 E1 E2 E3        DFB $E0,$E1,$E2,$E3 ; -- -- -- -- -- E5 E6 E7
    B1E4:E4 A4 A8 AC        DFB $E4,$A4,$A8,$AC
    B1E8:E8 B0 B4 B8        DFB $E8,$B0,$B4,$B8 ; -- E9 EA EB EC ED EE EF
    B1EC:B8 C0 C4 C8        DFB $B8,$C0,$C4,$C8
    B1F0:F0 F1 CC D0        DFB $F0,$F1,$CC,$D0 ; -- -- F2 F3 F4 F5 F6 F7
    B1F4:D4 D8 DC E0        DFB $D4,$D8,$DC,$E0
    B1F8:F8 E4 E8 EC        DFB $F8,$E4,$E8,$EC ; -- F9 FA FB FC FD FE FF
    B1FC:F0 F4 F8 FC        DFB $F0,$F4,$F8,$FC

                                                ; Notes:
                                                ; * 4 bytes/nibble
                                                ; * 4'th byte not used -- used as padding since X*3 = x*2 + x = too slow
                                                ; * Sequence in psuedo-base 4: 0,2,1,3
    B200:00 00 00 96        DFB $00,$00,$00,$96 ; [00]
    B204:02 00 00 97        DFB $02,$00,$00,$97 ; [01]
    B208:01 00 00 9A        DFB $01,$00,$00,$9A ; [02]
    B20C:03 00 00 9B        DFB $03,$00,$00,$9B ; [03]
    B210:00 02 00 9D        DFB $00,$02,$00,$9D ; [04]
    B214:02 02 00 9E        DFB $02,$02,$00,$9E ; [05]
    B218:01 02 00 9F        DFB $01,$02,$00,$9F ; [06]
    B21C:03 02 00 A6        DFB $03,$02,$00,$A6 ; [07]
    B220:00 01 00 A7        DFB $00,$01,$00,$A7 ; [08]
    B224:02 01 00 AB        DFB $02,$01,$00,$AB ; [09]
    B228:01 01 00 AC        DFB $01,$01,$00,$AC ; [0A]
    B22C:03 01 00 AD        DFB $03,$01,$00,$AD ; [0B]
    B230:00 03 00 AE        DFB $00,$03,$00,$AE ; [0C]
    B234:02 03 00 AF        DFB $02,$03,$00,$AF ; [0D]
    B238:01 03 00 B2        DFB $01,$03,$00,$B2 ; [0E]
    B23C:03 03 00 B3        DFB $03,$03,$00,$B3 ; [0F]

    B240:00 00 02 B4        DFB $00,$00,$02,$B4 ; [10]
    B244:02 00 02 B5        DFB $02,$00,$02,$B5 ; [11]
    B248:01 00 02 B6        DFB $01,$00,$02,$B6 ; [12]
    B24C:03 00 02 B7        DFB $03,$00,$02,$B7 ; [13]
    B250:00 02 02 B9        DFB $00,$02,$02,$B9 ; [14]
    B254:02 02 02 BA        DFB $02,$02,$02,$BA ; [15]
    B258:01 02 02 BB        DFB $01,$02,$02,$BB ; [16]
    B25C:03 02 02 BC        DFB $03,$02,$02,$BC ; [17]
    B260:00 01 02 BD        DFB $00,$01,$02,$BD ; [18]
    B264:02 01 02 BE        DFB $02,$01,$02,$BE ; [19]
    B268:01 01 02 BF        DFB $01,$01,$02,$BF ; [1A]
    B26C:03 01 02 CB        DFB $03,$01,$02,$CB ; [1B]
    B270:00 03 02 CD        DFB $00,$03,$02,$CD ; [1C]
    B274:02 03 02 CE        DFB $02,$03,$02,$CE ; [1D]
    B278:01 03 02 CF        DFB $01,$03,$02,$CF ; [1E]
    B27C:03 03 02 D3        DFB $03,$03,$02,$D3 ; [1F]

    B280:00 00 01 D6        DFB $00,$00,$01,$D6 ; [20]
    B284:02 00 01 D7        DFB $02,$00,$01,$D7 ; [21]
    B288:01 00 01 D9        DFB $01,$00,$01,$D9 ; [22]
    B28C:03 00 01 DA        DFB $03,$00,$01,$DA ; [23]
    B290:00 02 01 DB        DFB $00,$02,$01,$DB ; [24]
    B294:02 02 01 DC        DFB $02,$02,$01,$DC ; [25]
    B298:01 02 01 DD        DFB $01,$02,$01,$DD ; [26]
    B29C:03 02 01 DE        DFB $03,$02,$01,$DE ; [27]
    B2A0:00 01 01 DF        DFB $00,$01,$01,$DF ; [28]
    B2A4:02 01 01 E5        DFB $02,$01,$01,$E5 ; [29]
    B2A8:01 01 01 E6        DFB $01,$01,$01,$E6 ; [2A]
    B2AC:03 01 01 E7        DFB $03,$01,$01,$E7 ; [2B]
    B2B0:00 03 01 E9        DFB $00,$03,$01,$E9 ; [2C]
    B2B4:02 03 01 EA        DFB $02,$03,$01,$EA ; [2D]
    B2B8:01 03 01 EB        DFB $01,$03,$01,$EB ; [2E]
    B2BC:03 03 01 EC        DFB $03,$03,$01,$EC ; [2F]

    B2C0:00 00 03 ED        DFB $00,$00,$03,$ED ; [30]
    B2C4:02 00 03 EE        DFB $02,$00,$03,$EE ; [31]
    B2C8:01 00 03 EF        DFB $01,$00,$03,$EF ; [32]
    B2CC:03 00 03 F2        DFB $03,$00,$03,$F2 ; [33]
    B2D0:00 02 03 F3        DFB $00,$02,$03,$F3 ; [34]
    B2D4:02 02 03 F4        DFB $02,$02,$03,$F4 ; [35]
    B2D8:01 02 03 F5        DFB $01,$02,$03,$F5 ; [36]
    B2DC:03 02 03 F6        DFB $03,$02,$03,$F6 ; [37]
    B2E0:00 01 03 F7        DFB $00,$01,$03,$F7 ; [38]
    B2E4:02 01 03 F9        DFB $02,$01,$03,$F9 ; [39]
    B2E8:01 01 03 FA        DFB $01,$01,$03,$FA ; [3A]
    B2EC:03 01 03 FB        DFB $03,$01,$03,$FB ; [3B]
    B2F0:00 03 03 FC        DFB $00,$03,$03,$FC ; [3C]
    B2F4:02 03 03 FD        DFB $02,$03,$03,$FD ; [3D]
    B2F8:01 03 03 FE        DFB $01,$03,$03,$FE ; [3E]
    B2FC:03 03 03 FF        DFB $03,$03,$03,$FF ; [3F]

                    RWTS_GetProDosSectors:
    B300:48                 PHA                 ; === T15S3 ===
    B301:29 07              AND #$07            ; A = 0000_0fgh
    B303:8D 19 B3           STA SectorProDOS+1  ; *** SELF-MODIFYING! ProDOS Block = Sectors XX and YY
    B306:68                 PLA                 ; A = abcd_efgh
    B307:6A                 ROR                 ; A = 0abc_defg C=h
    B308:4A                 LSR                 ; A = 00ab_cdef
    B309:4A                 LSR                 ; A = 000a_bcde
    B30A:C5 ED              CMP rwts_Track
    B30C:F0 0A              BEQ SectorProDOS    ;v $B318 On correct ProDOS Track
    B30E:48                 PHA
    B30F:20 33 B3           JSR RWTS_Read16
    B312:20 9E B3           JSR RWTS_UnloadSectors
    B315:68                 PLA
    B316:85 ED              STA rwts_Track

                    SectorProDOS:
    B318:A0 00              LDY #$00            ; *** SELF-MODIFIED by $B303
    B31A:Be B9 B3           LDX rwts_SectorsProDOS,Y    ; First Half of Block
    B31D:AD EA B3           LDA rwts_DestAddr+1
    B320:EE EA B3           INC rwts_DestAddr+1         ; store at next page
    B323:9D C9 B3           STA rwts_SectorLoadOrder,X
    B326:BE C1 B3           LDX rwts_SectorProDOS+8,Y   ; Second Half of Block
    B329:AD EA B3           LDA rwts_DestAddr+1
    B32C:EE EA B3           INC rwts_DestAddr+1
    B32F:9D C9 B3           STA rwts_SectorLoadOrder+8,X
    B332:60                 RTS

                    RWTS_Read16:
    B333:A2 0F              LDX #$0F            ; 16 sectors to load
                    MapLog2Phys:
    B335:BC A9 B3           LDY $B3A9,X         ; ProDOS Logical Sector
    B338:B9 C9 B3           LDA $B3C9,Y
    B33B:9D D9 B3           STA $B3D9,X
    B33E:CA                 DEX
    B33F:10 F4              BPL MapLog2Phys     ;^ $B335

    B341:A6 FD              LDX rwts_SlotX16
    B343:A9 40              LDA #$40
    B345:85 E8              STA rwts_ReadCount
    B347:D0 29              BNE                 ;v $B372 always, TODO: FIXME

                    AttemptRead:
    B349:C6 E8              DEC rwts_ReadCount  ; Exhausted read attempts?
    B34B:F0 31              BEQ ReadError       ;v $B37E
    B34D:20 26 B1           JSR RWTS_ReadPrologue
    B350:B0 F7              BCS AttemptRead     ;^ $B349
    B352:A5 E4              LDA $E4
    B354:C5 ED              CMP rwts_Track
    B356:D0 39              BNE                 ;v $B391
    B358:A4 E3              LDY rwts_SectorHave
    B35A:B9 D9 B3           LDA rwts_SectorDestAddr,Y
    B35D:F0 EA              BEQ AttemptRead     ;^ $B349, already read this sector?

    B35F:85 E7              STA rwts_LoadAddr+1
    B361:AD E9 B3           LDA rwts_DestAddr
    B364:85 E6              STA rwts_LoadAddr
    B366:20 11 B0           JSR RWTS_ReadSector
    B369:B0 DE              BCS AttemptRead     ;^ $B349

    B36B:A4 E3              LDY rwts_SectorHave
    B36D:A9 00              LDA #$00            ; Mark sector not loaded
    B36F:99 D9 B3           STA rwts_SectorDestAddr,Y
    B372:A0 0F              LDY #$0F
                    AreSectorsDone:
    B374:B9 D9 B3           LDA rwts_SectorDestAddr,Y
    B377:D0 D0              BNE AttemptRead     ;^ $B349
    B379:88                 DEY
    B37A:10 F8              BPL AreSectorsDone  ;^ $B374
    B37C:18                 CLC
    B37D:60                 RTS

                    ReadError
    B37E:38                 SEC
                    ReadSound:
    B37F:EA                 NOP                 ; *** SELF-MODIFIED to be RTS $60 @ $B464
    B380:A0 00              LDY #$00            ; Br0derbund "ZAP" sound
                    ^1
    B382:AD 30 C0           LDA SQUEEKER
    B385:98                 TYA
                    ^2
    B386:38                 SEC
    B387:E9 01              SBC #$01
    B389:D0 FB              BNE ^2              ;^ $B386
    B38B:88                 DEY
    B38C:D0 F4              BNE ^1              ;^ $B382
    B38E:4C 33 B3           JMP RWTS_Read16     ;^ $B333

    B391:A5 E4              LDA $E4             ; TODO
    B393:0A                 ASL
    B394:85 FF              STA rwts_HalfTrack_Have
    B396:A5 ED              LDA rwts_Track
    B398:20 E9 B0           JSR Do_Seek
    B39B:4C 33 B3           JMP Read16Sectors

                    RWTS_UnloadSectors              ; Called @ $B312
    B39E:A0 0F              LDY #$0F                ; 16 Sectors to load -- TODO: CALLED from $B312
    B3A0:A9 00              LDA #$00
                    MakeSectorUnloaded
    B3A2:99 C9 B3           STA rwts_SectorLoadOrder,Y
    B3A5:88                 DEY
    B3A6:10 FA              BPL MakeSectorUnloaded  ;^ $B3A2
    B3A8:60                 RTS

                    ; Map Logical Sectors -> Physical Sectors
                    rwts_SectorsDOS33:
    B3A9:00 07 0E 06        DFB $0,$7,$E,$6,$D,$5,$C,$4
    B3AD:0D 05 0C 04
    B3B1:0B 03 0A 02        DFB $B,$3,$A,$2,$9,$1,$8,$F
    B3B5:09 01 08 0F

                    ; Map Logical Blocks -> Physical Sectors
                    ;     const uint_8 BlockToSectorProDOS[16]
                    rwts_SectorsProDOS:
    B3B9:00 0D 0B 09        DFB $0,$D,$B,$9,$7,$5,$3,$1
    B3BD:07 05 03 01
    B3C1:0E 0C 0A 08        DFB $E,$C,$A,$8,$6,$4,$2,$F
    B3C5:06 04 02 0F

                    ; One of the above tables is copied here
                    rwts_SectorLoadOrder:
    B3C9:00 00 00 00
    B3CD:00 00 00 00
    B3D1:00 00 00 00
    B3D5:00 00 00 00

                    ; This is a scatter-gather read!
                    ; 00 = finished loading this page
                    ; xx = Dest Page to load at
                    ;   uint_8 DestAddr[ 16 ];
                    rwts_SectorDestAddr:
    B3D9:00 00 00 00
    B3DD:00 00 00 00
    B3E1:00 00 00 00
    B3E5:00 00 00 00

                    rwts_DestAddr:
    B3E9:00                 DFB $00
    B3EA:00                 DFB $00
    B3EB:00         rwts_B3EB   DFB $00         ; TODO
    B3EC:00         rwts_B3EC   DFB $00         ; TODO

                    ; ====================
                    ; Y = Track
                    ; JSR RWTS_B003
                    ;     DFB Load Address Sector 0
                    ;     DFB Load Address Sector 1
                    ;     DFB Load Address Sector 2
                    ;     DFB Load Address Sector 3
                    ;     DFB Load Address Sector 4
                    ;     DFB Load Address Sector 5
                    ;     DFB Load Address Sector 6
                    ;     DFB Load Address Sector 7
                    ;     DFB Load Address Sector 8
                    ;     DFB Load Address Sector 9
                    ;     DFB Load Address Sector A
                    ;     DFB Load Address Sector B
                    ;     DFB Load Address Sector C
                    ;     DFB Load Address Sector D
                    ;     DFB Load Address Sector E
                    ;     DFB Load Address Sector F
                    ; ====================
                    Do_LoadCode:
    B3ED:84 ED              STY rwts_Track
    B3EF:68                 PLA
    B3F0:85 EB              STA rwts_Return
    B3F2:68                 PLA
    B3F3:85 EC              STA rwts_Return+1
    B3F5:A2 00              LDX #$00
                    GetInterleave
    B3F7:20 0B B4           JSR GetNextAddr
    B3FA:9D C9 B3           STA rwts_SectorLoadOrder,X
    B3FD:E8                 INX
    B3FE:E0 10              CPX #$10
    B400:90 F5              BCC GetInterleave   ;^ $B3F7, === T15S4 ===

    B402:A5 EC              LDA rwts_Return     ; Restore caller
    B404:48                 PHA
    B405:A5 EB              LDA rwts_Return+1
    B407:48                 PHA
    B408:4C 33 B3           JMP RWTS_Read16     ;^ $B333

                    GetNextAddr:
    B40B:E6 EB              INC rwts_Return
    B40D:D0 02              BNE GetByte         ; roll over to next page?
    B40F:E6 EC              INC rwts_Return+1
                    GetByte:
    B411:A0 00              LDY #$00
    B413:B1 EB              LDY (rwts_Return),Y
    B415:60                 RTS

                    RWTS_Delay:
    B416:38                 SEC                 ; C=1 before subtract
                    BusyWait1
    B417:48                 PHA
                    BusyWait2:
    B418:E9 01              SBC #$01
    B41A:D0 FC              BNE BusyWait2       ;^ $B418
    B41C:68                 PLA
    B41D:E9 01              SBC #$01
    B41F:D0 F6              BNE BusyWait1       ;^ $B417
    B421:60                 RTS

                    ; ====================
                    ; Load @ $A,Y
                    ; A = Mem Hi
                    ; Y = Mem Lo
                    ; X = Num of ProDOS Blocks ?
                    ; ====================
                    Do_LoadBlocks:
    B422:8C E9 B3           STY rwts_DestAddr
    B425:8D EA B3           STA rwts_DestAddr+1
    B428:8E EB B3           STX rwts_B3EB
    B42B:20 9E B3           JSR RWTS_UnloadSectors
    B42E:A0 00              LDY #$00
    B430:8C EC B3           STY rwts_B3EC
                    ReadBlock
    B433:AC EC B3           LDY rwts_B3EC
    B436:E6 01              INC $01
    B438:B1 00              LDA ($00),Y         ; A=???
    B43A:4A                 LSR                 ; C=even
    B43B:C6 01              DEC $01
    B43D:B1 00              LDA ($00),Y
    B43F:B0 02              BCS                 ;v $B443 >= ?
    B441:F0 0B              BEQ                 ;v $B44E == done

    B443:20 00 B3           JSR RWTS_GetProDosSectors
    B446:EE EC B3           INC rwts_B3EC
    B449:CE EB B3           DEC rwts_B3EB
    B44C:D0 E5              BNE ReadBlock       ;^ $B433

                    JumpRead16:
    B44E:4C 33 B3           JMP RWTS_Read16

                    ; ====================
                    ; A = Dest Address
                    ; Y = Source Track
                    ; ====================
                    Do_ReadTrack:
    B451:84 ED              STY rwts_Track
    B453:A0 00              LDY #$00
                    NextSector:
    B455:99 C9 B3           STA rwts_SectorLoadOrder,Y
    B458:18                 CLC
    B459:69 01              ADC #$01            ; Fill memory in sequential sector order
    B45B:C8                 INY
    B45C:C0 10              CPY #$10            ; Finished 16 sectors?
    B45E:90 F5              BCC NextSector      ;^ $B455
    B460:B0 EC              BCS JumpRead16      ;^ $B44E, always

                    ; ====================
                    ; A = Dest Address
                    ; Y = Source Track
                    ; ====================
                    Do_ReadTrackQuiet:
    B462:A2 60              LDX #$60            ; $60 = RTS
    B464:8E 7F B3           STX ReadSound       ; Disable SFX ZAP for bad read
    B467:20 51 B4           JSR RWTS_B451
    B46A:A9 EA              LDA #$EA            ; $EA = NOP
    B46C:8D 7F B3           STA ReadSound       ; Re-enable SFZ ZAP for bad read
    B46F:60
                            DS $B500-*          ; unused/wasted
    B470:0 0 0 0 0 0 0 0
    B478:0 0 0 0 0 0 0 0
    B480:0 0 0 0 0 0 0 0
    B488:0 0 0 0 0 0 0 0
    B490:0 0 0 0 0 0 0 0
    B498:0 0 0 0 0 0 0 0
    B4A0:0 0 0 0 0 0 0 0
    B4A8:0 0 0 0 0 0 0 0
    B4B0:0 0 0 0 0 0 0 0
    B4B8:0 0 0 0 0 0 0 0
    B4C0:0 0 0 0 0 0 0 0
    B4C8:0 0 0 0 0 0 0 0
    B4D0:0 0 0 0 0 0 0 0
    B4D8:0 0 0 0 0 0 0 0
    B4E0:0 0 0 0 0 0 0 0
    B4E8:0 0 0 0 0 0 0 0
    B4F0:0 0 0 0 0 0 0 0
    B4F8:0 0 0 0 0 0 0 0

    B500:


    B568:60                 RTS
    B569:00 00 00
    B56C:00 00 00 00        DS $B7FF-*

    B600:0 0 0 0 0 0 0 0                        ; Unused/Wasted
    B608:0 0 0 0 0 0 0 0
    B610:0 0 0 0 0 0 0 0
    B618:0 0 0 0 0 0 0 0
    B620:0 0 0 0 0 0 0 0
    B628:0 0 0 0 0 0 0 0
    B630:0 0 0 0 0 0 0 0
    B638:0 0 0 0 0 0 0 0
    B640:0 0 0 0 0 0 0 0
    B648:0 0 0 0 0 0 0 0
    B650:0 0 0 0 0 0 0 0
    B658:0 0 0 0 0 0 0 0
    B660:0 0 0 0 0 0 0 0
    B668:0 0 0 0 0 0 0 0
    B670:0 0 0 0 0 0 0 0
    B678:0 0 0 0 0 0 0 0
    B680:0 0 0 0 0 0 0 0
    B688:0 0 0 0 0 0 0 0
    B690:0 0 0 0 0 0 0 0
    B698:0 0 0 0 0 0 0 0
    B6A0:0 0 0 0 0 0 0 0
    B6A8:0 0 0 0 0 0 0 0
    B6B0:0 0 0 0 0 0 0 0
    B6B8:0 0 0 0 0 0 0 0
    B6C0:0 0 0 0 0 0 0 0
    B6C8:0 0 0 0 0 0 0 0
    B6D0:0 0 0 0 0 0 0 0
    B6D8:0 0 0 0 0 0 0 0
    B6E0:0 0 0 0 0 0 0 0
    B6E8:0 0 0 0 0 0 0 0
    B6F0:0 0 0 0 0 0 0 0
    B6F8:0 0 0 0 0 0 0 0

    B700:0 0 0 0 0 0 0 0
    B708:0 0 0 0 0 0 0 0
    B710:0 0 0 0 0 0 0 0
    B718:0 0 0 0 0 0 0 0
    B720:0 0 0 0 0 0 0 0
    B728:0 0 0 0 0 0 0 0
    B730:0 0 0 0 0 0 0 0
    B738:0 0 0 0 0 0 0 0
    B740:0 0 0 0 0 0 0 0
    B748:0 0 0 0 0 0 0 0
    B750:0 0 0 0 0 0 0 0
    B758:0 0 0 0 0 0 0 0
    B760:0 0 0 0 0 0 0 0
    B768:0 0 0 0 0 0 0 0
    B770:0 0 0 0 0 0 0 0
    B778:0 0 0 0 0 0 0 0
    B780:0 0 0 0 0 0 0 0
    B788:0 0 0 0 0 0 0 0
    B790:0 0 0 0 0 0 0 0
    B798:0 0 0 0 0 0 0 0
    B7A0:0 0 0 0 0 0 0 0
    B7A8:0 0 0 0 0 0 0 0
    B7B0:0 0 0 0 0 0 0 0
    B7B8:0 0 0 0 0 0 0 0
    B7C0:0 0 0 0 0 0 0 0
    B7C8:0 0 0 0 0 0 0 0
    B7D0:0 0 0 0 0 0 0 0
    B7D8:0 0 0 0 0 0 0 0
    B7E0:0 0 0 0 0 0 0 0
    B7E8:0 0 0 0 0 0 0 0
    B7F0:0 0 0 0 0 0 0 0
    B7F8:0 0 0 0 0 0 0 0

Whew!

Boot Tracing Stage 2

Once the entire track $15 of Stage 2 is read in it is executed to read Stage 3.

                    TestBoot3:                  ; Boot Stage 2 done, do stage 3
    0873:A6 2B              LDX P5.SlotX16      ;
    0875:86 FD              STX rwts_SlotX16    ;
    0877:A9 BC              LDA #>LoadBoot3     ; Load T$22 4&4 "4 sectors" @ $BC00, $BD00, $BE00, $BF00
    0879:20 00 B5           JSR   ReadBoot3     ; Boot Stage 2 reads in encrypted Stage 3
    087C:B0 F5              BCS   TestBoot3     ;^ $0873 C=1 error, Hang if NibbleCheck1 fails
    087E:4C 00 BE           JMP   ExecBoot3     ;

Let's disassemble $B500 ...

                            COUNT      = $3E    ; 16-bit counter
                            DRIVE_DATA = $C08C  ; Read Disk Nibble

                    ReadBoot3:
    B500:8D 17 B5           STA _B516+1         ; *** SELF-MODIFYING *** DestAddr
                                                ; These 3 bytes are wasted
                                                ; Should just be STA $3D
                                                ; And we can delete $B516..$B518
    B503:A9 22              LDA #$22            ; TRACK_NIBBLE_CHECK
    B505:20 09 B0           JSR RWTS_Seek       ;
    B508:A6 FD              LDX rwts_SlotX16    ;
    B50A:A9 80              LDA #$80            ; Attempt to read $8080 disk nibbles
    B50C:85 3E              STA Count           ; Count
    B50E:85 3F              STA Count+1         ; 
    B510:A9 04              LDA #$04            ; Num "Sectors" or Pages to read
    B512:85 3B              STA NumSectors      ; 
    B514:A0 00              LDY #$00            ; Num disk nibbles read (actually is 1/2)
                    _B516:
    B516:A9 00              LDA #$00            ; *** SELF-MODIFIED @ $B500 *** Dest Addr to Load at
    B518:84 3C              STY $3C             ; 
    B51A:85 3D              STA DestPage        ; DestPage

                    NextCount:
    B51C:C6 3E              DEC Count           ; Force read counter
    B51E:D0 06              BNE DoNibbleCheck1  ; to read at least once
    B520:C6 3F              DEC Count+1         ; 
    B522:D0 02              BNE DoNibbleCheck1  ; 
    B524:38                 SEC                 ; FAIL nibble check 1
    B525:60                 RTS                 ; 

                    DoNibbleCheck1:
                    WaitNib1a:                  ;        ^
    B526:BD 8C C0           LDA DRIVE_DATA,X    ;        |
    B529:10 FB              BPl WaitNib1a       ;^ $B526 |
                    TestNib1a:                  ;        |
    B52B:C9 F5              CMP #$F5            ; $F5    | <--+
    B52D:D0 ED              BNE NextCount       ;^ $B51C +    |
                                                ;             |
                    WaitNib1b:                  ;             |
    B52F:BD 8C C0           LDA DRIVE_DATA,X    ;             |
    B532:10 FB              BPL WaitNib1b       ;^ $B52F      |
                    TestNib1b:                  ;             |
    B534:C9 F4              CMP #$F4            ; $F4         |
    B536:D0 F3              BNE TestNib1a       ;^ $B52B   ---+

                    WaitNib1c:
    B538:BD 8C C0           LDA DRIVE_DATA,X;
    B53B:10 FB              BPL WaitNib2b   ;^ $B538
                    TestNib1c:
    B53D:C9 CF              CMP #$CF        ; $CF
    B53F:D0 F3              BNE $B534

    B541:BD 8C C0   LDA $C08C,X     ;+
    B544:10 FB      BPL $B541       ;^
    B546:2A         ROL
    B547:85 3A      STA $3A
    B549:BD 8C C0   LDA $C08C,X     ;+
    B54C:10 FB      BPL $B549       ;^
    B54E:25 3A      AND $3A
    B550:91 3C      STA ($3C),Y
    B552:C8         INY
    B553:D0 EC      BNE $B541
    B555:0E FF FF   ASL $FFFF       ; //e = $C3, //c = $C8, Laser128 = ? TODO: FIXME:
    B558:BD 8C C0   LDA $C08C,X
    B55B:10 FB      BPL $B558       ;^
    B55D:C9 D5      CMP #$D5        ; $ D5
    B55F:D0 AF      BNE $B510       ;^
    B561:E6 3D      INC $3D         ; Inc Dest Page
    B563:C6 3B      DEC NumSectors  ;
    B565:D0 DA      BNE $B541       ;^
    B567:18         CLC             ; PASS nibble check 1
    B568:60         RTS

This first nibble check reads these disk nibbles on Track $22:

F5 F4 CF
[4&4 x256] [4&4 x256]
D5
[4&4 x256] [4&4 x256]
D5
[4&4 x256] [4&4 x256]
D5
[4&4 x256] [4&4 x256]
D5

This is one of the reasons you need a "Bit-Copier" to copy Track $22. There are no sectors as traditionally defined by D5 AA 96 and D5 AA ED!

Let's update our Boot Stages Diagram:

        ; Stage Addr TRK/S Load
        ; ---------------------
        ; 0     C600 T00S0 0800
        ; ---------------------
        ; 1     0801 T15
        ; ---------------------
        ; 2          T15S0 B000
        ; 2          T15SD B100
        ; 2          T15SB B200
        ; 2          T15S9 B300
        ; 2          T15S7 B400
        ; 2          T15S5 B500
        ; 2          T15S3 B600
        ; 2          T15S1 B700
        ; 2          T15SE 1800
        ; 2          T15SC 1900
        ; 2          T15SA 1A00
        ; 2          T15S8 1B00
        ; 2          T15S6 1C00
        ; 2          T15S4 1D00
        ; 2          T15S2 1E00
        ; 2          T15SF 1F00
        ; ---------------------
        ; 1     0873
        ; 2     B500 T22Sx BC00 Disk: $F5,$F4,$CF, [ 4&4:512,$D5 ] x4
        ; 2          T22Sx BD00
        ; 2          T22Sx BE00
        ; 2          T22Sx BF00
        ; ---------------------
        ; 3     BE00 ?     ?
        ; ---------------------

There are many different ways to remove the protection. The simpliest is to format track $22 normally, and modify $B500 to load them in.

Stupid Kids and their Graffiti

That is exactly what Black Bag's "Kracked" version of Fantavision does.

  • Black Bag Krack

On Track $15, Sector $5 @ $B500 it uses:

  • RWTS_ReadPrologue = $B126 and
  • RWTS_ReadSector = $B011

to read two sectors on a normal formatted track $22. That code looks like this:

                    ReadBoot3:
    B500:A9 22          LDA #$22                ; Nibble Check Track been converted to normal sector
    B502:20 09 B0       JSR RWTS_Seek           ;
    B505:A2 60          LDX #$60                ; BUG! Hard-Coded to Drive in Slot 6 !
                    FindSector0:                ; Should be: LDX rwts_SlotX16 -> LDX $FD
    B507:20 26 B1       JSR RWTS_Prologue       ;
    B50A:A5 E3          LDA rwts_Sector_Have    ;
    B50C:C9 00          CMP #$00                ; Logical Sector $0 -> Physical Sector $0
    B50E:D0 F7          BNE FindSector0         ;
    B510:85 E6          STA rwts_LoadAddr       ; Double Duty
    B512:A9 BE          LDA #$BE                ; Dest Addr = $BE00
    B514:85 E7          STA rwts_LoadAddr+1     ;
    B516:20 11 B0       JSR RWTS_ReadSector     ;
                    FindSectorD:                ;
    B519:20 26 B1       JSR RWTS_Prologue       ;
    B51C:A5 E3          LDA rwts_Sector_Have    ;
    B51E:C9 0D          CMP #$0D                ; Logical Sector $1 -> Physical Sector $D
    B520:D0 F7          BNE FindSectorD         ;
    B522:E6 E7          INC rwts_LoadAddr+1     ; DestAddr += $0100
    B522:20 11 B0       JSR RWTS_ReadSector     ;
    B527:18             CLC                     ; Signal $087C Nibble Check = PASS
    B528:60             RTS                     ;

Looks like this was an amateur rush job -- if you try to boot this version in Slot 5 it will fail.

Someone even patched the backup utility !

  • Logo

We will discuss this more when we remove the copy protection ourselves.

Boot Tracing Stage 3 $BE00

The boot sector code @ $087E calls Stage 3 $BE00:

    087E:4C 00 BE           JMP ExecBoot3

This is the disassembly:

                    ExecBoot3:
    BE00:4C 06 BE           JMP Main
    BE03:4C AD BE           JMP DoNibbleCheck2    ; Nibble Check #2: $D4,$D5,$DE,$A5,$xx,$yy,$E5,$AA
                    Main:
    BE06:20 16 BF           JSR Verify64K
    BE09:20 75 BE           JSR $BE75

    BE0C:A0 58              LDY #<$FF58
    BE0E:A9 FF              LDA #>$FF58
    BE10:84 36              STA CSW             ; CSWL = $36, Char Out Vector (Function Pointer)
    BE12:85 37              STA CSW+1

    BE14:A0 1F              LDY #<NewKSW
    BE16:A9 BE              LDA #>NewKSW
    BE18:84 38              STY KSW             ; KSWL = $38, Char In Vector (Function Pointer)
    BE1A:85 39              STA KSW+1
    BE1C:4C 00 E0           JMP BASIC           ; BASIC Cold Start
                    NewKSW:
    BE1F:91 28              STA (BAS),Y         ; BASL   = $28
    BE21:20 89 FE           JSR SETKBD          ; SETKBD = $FE89
    BE24:20 93 FE           JSR SETVID          ; SETVID = $FE93
    BE27:A9 16              LDA #$16            ; First Track to Load
    BE29:8D 33 BE           STA BE.NextTrack+1  ; *** SELF MODIFYING ***
    BE2C:20 09 B0           JSR RWTS_Seek
    BE2F:A9 08              LDA #$08            ; DestAddr
                    NextPage:
    BE31:48                 PHA                 ;+ Save DestAddr
                    BE.NextTrack:
    BE32:A0 00              LDY #$00            ; *** SELF-MODIFIED @ $BE27 @ $BE34 ***
    BE34:EE 33 BE           INC BE.NextTrack+1  ; *** SELF MODIFYING ***
    BE37:20 00 B0           JSR RTWS_LoadTrack
    BE3A:68                 PLA                 ; Restore DestAddr
    BE3B:18                 CLC
    BE3C:69 18              ADC #$18            ; We initially loaded at $0800
    BE3E:29 F0              AND #$F0            ; Force to load at start-of-page $2x00, $3x00, etc
    BE40:C9 A1              CMP #$A1            ; Load entire $Ax00 page then stop
    BE42:90 ED              BCC NextPage        ;^ $BE31

    BE44:AC 00 08           LDY $0800
    BE47:AD 04 08           LDA $0804
    BE4A:84 AF              STY $AF
    BE4C:85 B0              STA $B0
    BE4E:84 69              STY $69
    BE50:85 6A              STA $6A
    BE52:A9 00              LDY #$00
    BE54:8D 00 08           STA $0800
    BE57:8D 04 08           STA $0804

    BE5A:AD 00 C0           LDA KEYBOARD        ; Key pressed?
    BE5D:C9 9B              CMP #$1B + $80      ; ESC pressed?
    BE5F:F0 07              BEQ LoadBackup      ;v $BE68
    BE61:A6 FD              LDX $FD
    BE63:86 43              STX $43
    BE65:4C 00 40           JMP Main.2

                    LoadBackup:
    BE68:2C 10 C0           BIT KEYSTROBE
    BE6B:A0 20              LDY #$20            ; Y = Track of Backup Utility
    BE6D:A9 08              LDA #>Main.Backup   ; A = Address
    BE6F:20 00 B0           JSR RWTS_LoadTrack
    BE72:4C 00 08           JMP Main.Backup

    BE75:A2 20              LDX #$20            ; Double Duty: DstAddr, and Num Pages
    BE77:8E 7F BE           STX _ClearHGR+2     ; *** SELF-MODIFYING @ $BE7D
    BE7A:A0 00              LDY #$00
    BE7C:98                 TYA
                    _ClearHgr
    BE7D:99 00 20           STA $2000,Y         ; *** SELF_MODIFIED by $BE77, $BE83
    BE80:C8                 INy
    BE81:D0 FA              BNE _ClearHgr       ;^ $BE7D
    BE83:EE 7F BE           INC _ClearHGR+2
    BE86:CA                 DEX
    BE87:D0 F4              BNE _ClearHgr

    BE89:AD 52 C0           LDA $C052
    BE8C:AD 57 C0           LDA $C057
    BE8F:AD 50 C0           LDA $C050

    BE92:A9 17              LDA #$17            ; Logo on Track $17
    BE94:20 09 B0           JSR RWTS_Seek

    BE97:A9 20              LDA #$20            ; A = Addr = HGR top half
    BE99:A0 17              LDY #$17            ; Y = Track Logo Top
    BE9B:20 00 B0           JSR RWTS_LoadTrack
    BE9E:A9 30              LDA #$30            ; A = Addr = HGR bottom half
    BEA0:A0 18              LDY #$18            ; Y = Track Logo Bot
    BEA2:20 00 B0           JSR RWTS_LoadTrack

                            RTS

Here is the diassembly of the second nibble check on Track $22.

                            ; Normally $0200 is the keyboard buffer
                            ; But here it is used as an array of booleans.
                            ;
                            ;   bool MissingKey[ 256 ];
                            ;
                            False     = $00
                            True      = $FF

                            KeyBuf    = $0200
                            DriveData = $C08C

                    DoNibbleCheck2:
    BEAD:A0 00              LDY #$00
    BEAF:A9 FF              LDA #True       ; -1 = True
                    _Init200
    BEB1:99 00 02           STA KeyBuf,Y    ; MissingKey[ $00 .. $FF ] = True
    BEB4:C8                 INY
    BEB5:D0 FA              BNE _Init200    ;^ $BEB1

                    _Nib0
    BEB7:A6 FD              LDX rwts_SlotX16
                    _Read1:
    BEB9:BD 8C C0           LDX DriveData,X
    BEBC:10 FB              BPL _Read1      ;^ $BEB9
                    _Nib1:
    BEBE:C9 D4              CMP #$D4        ; Check1 = #$D4
    BEC0:D0 F5              BNE _Nib0       ;^ $BEB7
    BEC2:20 10 BF           JSR _NibRead
                    _Nib2:
    BEC5:C9 D5              CMP #$D5        ; Check2 = #$D5
    BEC7:D0 F5              BNE _Nib1       ;^ $BEBE
    BEC9:20 10 BF           JSR NibRead
                    _Nib3:
    BECC:C9 DE              CMP #$DE        ; Check3 = #$DE
    BECE:D0 F5              BNE _Nib2       ;^ $BEC5
    BED0:20 10 BF           JSR NibRead
                    _Nib4:
    BED3:C9 A5              CMP #$A5        ; Check4 = #$A5
    BED5:D0 F5              BNE _Nib3       ;^ $BECC
    BED7:EA                 NOP
                    _Nib5:
    BED8:BD 8C C0           LDA DriveData,X
    BEDB:10 FB              BPL _Nib5
    BEDD:2A                 ROL
    BEDE:85 26              STA #$26        ; Save CheckYa
                    _Nib6:
    BEE0:BD 8C C0           LDA DriveData,X
    BEE3:10 FB              BPL _Nib6
    BEE5:25 26              AND $26         ; Save CheckYb in 4&4 format
    BEE7:A8                 TAY             ; Y = Checksum
    BEE8:20 10 BF           JSR NibRead
    BEEB:C9 E5              CMP #$E5        ; Check7 = #$E5
    BEED:D0 C8              BNE _Nib0       ;^ BEB7

    BEEF:20 10 BF           JSR NibRead
    BEF2:C9 AA              CMP #$AA        ; Check8 = #$AA
    BEF4:D0 C1              BNE Nib0        ;^ $BEB7

    BEF6:B9 00 02           LDA KeyBuf,Y    ; if MissingKey[ Y ] > 0 cont. to next key
    BEF9:10 BC              BPL _Nib0       ;^ $BEB7
    BEFB:A9 00              LDA #False
    BEFD:99 00 02           STA KeyBuf,Y    ; else MissingKey[ Y ] = False
    BF00:AA                 TAX             ; X = 0
    BF01:A8                 TAY             ; Y = 0
                        _CountKey
    BF02:B9 00 02           LDA KeyBuf,Y    ; Search $0200 .. $02FF
    BF05:30 01              BMI _NextKey    ; if MissingKey[ Y ] < 0, don't count it
    BF07:E8                 INX             ; X = Num of Keys we have read so far
                        _NextKey
    BF08:C8                 INY
    BF09:D0 F7              BNE _CountKey
    BF0B:E0 A0              CPX #$A0        ; Keep reading until we have 160 keys
    BF0D:90 A8              BCC _Nib0       ;^ $BEB7
    BF0F:60                 RTS

                        NibRead:
    BF10:BD 8C C0           LDA DriveData,X
    BF13:10 FB              BPL NibRead
    BF15:60                 RTS

What this code is doing is checking for $A0 = 160 keys on disk of the form:

    D4 D5 DE A5  ?? ??  E5 AA   ?? ?? ??
    \_________/  \___/  \___/   \______/
     Prologue    KeyYY  Epilog  Random Sync Bytes

Recall that bytes in 4&4 format are stored as disk nibbles:

Hex 4&4
$00 AA AA
$01 AA AB
... ...
$D5 EA FF

The rest of the code is pretty straightforward.

                    Verify64K:
    BF16:2C 83 C0           BIT $C083
    BF19:2C 83 C0           BIT $C083
                            RTS
    TODO: FIXME:

Time to go LOCO, er LOGO

Logo -- no, not the language

  1. Boot our Fanta.Work disk

  2. Enter in this short ML (machine language program)

    1F01:A9 60              LDA #$60        ; "RTS"
    1F03:8D 46 08           STA $0846       ; 846:LDX $2B -> RTS
    1F06:8D 73 08           STA $0873       ; 873:LDX $2B -> RTS
    1F09:20 01 08           JSR $0801       ;
    1F0C:A9 17              LDA #$17        ; A = Track
    1F0E:A0 20              LDY #$20        ; Y = Dest
    1F10:20 20 1F           JSR $1F20
    1F13:A9 18              LDA #$18        ; A = Track
    1F15:A0 30              LDY #$30        ; Y = Dest
    1F17:20 20 1F           JSR $1F20
    1F1A:4C 69 FF           JMP $FF69

    1F20:8D 57 08           STA $857        ; LDA #Track
    1F23:A2 00              LDX #$00
    1F25:98                 TYA
    1F26:9D CE 08           STA $08CE,X
    1F29:C8                 INY             ; Dest Page
    1F2A:E8                 INX
    1F2B:E0 10              CPX #$10        ; 16 sectors
    1F2D:D0 F6              BNE $1F25
    1F2F:A6 2B              LDX $2B
    1F31:4C 48 08           JMP $0848

Via:

    1F01:A9 60 8D 46 08 8D 73 08
    1F09:20 01 08
    1F0C:A9 17 A0 20 20 20 1F
    1F13:A9 18 A0 30 20 20 1F
    1F1A:4C 69 FF
    1F20:8D 57 08 A2 00 98
    1F26:9D CE 08 C8 E8 E0 10
    1F2D:D0 F6 A6 2B 4C 48 08
  • Note: That A and Y are swapped comprared to RWTS_ReadTrack at $B000
  1. And save it:
    BSAVE GET_LOGO_1,A$1EEE,L$46
  1. Replace Slot 6, Drive 1 disk with the original Fantavision. And now for the moment of truth ...
    1EEEG
    C050 C052 C054 C057
  1. When you are done viewing ...
    C051

Logo Take 2

For some unknown reason there is actually a complete second copy of the logo on disk!?

    BLOAD GET_LOGO_1
    CALL-151
    1F0D:A
    1F14:B
    BSAVE GET_LOGO_2,A$1EEE,L$46
  • logo 2

Hmm, why is our logo messed up??

It turns out there are 4 tracks that are mirrored !?!?!?

Track Description
$08 Mirror of Track $1A, unused
$09 Mirror of Track $19, unused
$0A Mirror of Track $18 @ $3000
$0B Mirror of Track $17 @ $2000

This is easy enough to fix, we just need to swap the track load order. :-)

Boot our Fanta.Work disk ...

    BLOAD GET_LOGO_2
    CALL-151
    1F0D:B
    1F14:A
    BSAVE GET_LOGO_2,A$1EEE,L$46

You know the drill ... replace Slot 6, Drive 1 with Fantavision, and 1EEEG

  • logo

Logo Take 3 !

Track $20, Sectors $8 .. $F has yet a 3rd copy of the logo! (Bottom 1/4)

    BLOAD GET_LOGO_2
    CALL-151
    1F0D:20
    1F14:21
    BSAVE GET_LOGO_34,A$1EEE,L$46
  • logo 3

If we compare the original logo on Tracks $17 and $18 to this third copy we find:

  • Track $20, Sector $8 == Track $18, Sector $8 @ $3800

Logo Take 4 !!

And just when you thought it couldn't get any more loopy ... Track $21, Sectors $8 ..$F has yet a 4th copy of the logo! (Bottom 1/4)

If we compare the original logo on Tracks $17 and $18 we see that:

  • Track $21, Sector $8 == Track $18, Sector $8

Are you going loco yet?

In case you are curious how does Fantavision orignally loads the logo? That resides at $BE89:

    BE89:AD 52 C0       LDA $C052
    BE8C:AD 57 C0       LDA $C057
    BE8F:AD 50 C0       LDA $C050

    BE92:A9 17          LDA #$17        ; Track $17
    BE94:20 09 B0       JSR RWTS_Seek

    BE97:A9 20          LDA #$20        ; Load $2000 .. $2FFF
    BE99:A0 17          LDY #$17        ; First half of logo on track $17
    BE9B:20 00 B0       JSR RTWS_LoadTrack

    BE9E:A9 30          LDA #$20        ; Load $3000 .. $3FFF
    BEA0:A0 18          LDY #$18        ; Second half of logo on track $18
    BEA2:20 00 B0       JSR RTWS_LoadTrack

Here is a easy way to view these:

    CALL-151
    9600<C600.C6FFM
    96FA:9F
    9F00:60 60
    9600G

    FC58G
    2B:60
    880:9F

    BE86:AD 54 C0
    BEA5:60
    BE86G

Boot Tracing Stage 4

"What this, 'Byte-Code', you say young chap?" Harrumph.

Long before Java popularized the concept of byte-code in 1994 people were already using the concept at least 9 years earlier back in 1985!

  • Zork I is probably the most famous program to use byte-code -- back in 1980 -- predating even Fantavisio by 5 years.

  • UCSD Pascal is probably the second most famous example -- back in 1978 !

    • P-Code first appeared in 1966 !

Who knew that it would take almost 30 years for byte-code to go mainstream!

Backup Take 1

Track $20 has the built-in Backup Utility which is loaded in at $0800.

How do I know that?

  • $B500 decodes Track $22 in 4&4 format to $BC00 .. $BFFF
  • $BE00 has the relevent code that loads it:
                        Main.Backup = $0800

                        KEYBOARD    = $C000
                        KEYSTROBE   = $C010

                        ORG $BE00

BE00:4C 06 BE           JMP $BE06

BE06:20 16 BF   JSR Verify64K

BE5A:AD 00 C0           LDA KEYBOARD        ; Key pressed?
BE5D:C9 9B              CMP #$1B + $80      ; ESC pressed?
BE5F:F0 07              BEQ LoadBackup      ;v $BE68

                LoadBackup:
BE68:2C 10 C0           BIT KEYSTROBE
BE6B:A0 20              LDY #$20            ; Y = Track
BE6D:A9 08              LDA #>Main.Backup   ; A = Address
BE6F:20 00 B0           JSR RWTS_LoadTrack
BE72:4C 00 08           JMP Main.Backup

Backup Take 2

However on Track $21 there is yet another copy of the Backup Utility !

Let's write some code to load both of them. :)

  1. PR#5 to boot our Fanta.Work Disk
  2. BLOAD GET_LOGO_1
  3. CALL-151
    1F0D:20
    1F14:21
    BSAVE GET_BACKUP_T20_T21,A$1EEE,L$46
  1. Replace your Fantavision disk in slot 6
  2. 1EEEG to boot Fantavision
  3. Replace your Fanta-work disk in slot 5
  4. C500G to boot Fanta-work
  5. CALL-151
  6. 800<2000.27FFM
  7. BSAVE B5.BACKUP_T20_0800,A$800,L$800
  8. 800<3000.37FFM
  9. BSAVE B5.BACKUP_T21_0800,A$800,L$800
  10. BSAVE LOGO_3_T20,A$2800,L$800
  11. BSAVE LOGO_4_T21,A$3800,L$800

Backup Byte-Code Interpreter

Both backup versions share the same Byte-Code design, but Track $21 has an extra 2 bytes in the interpretor which causes the Array of Function Pointers to start at a higher location in memory.

    Track $20           Track $21
_800 JMP $0D37     1000: JMP $14DF

DecodeToken:
_803 PLA           1003: PLA
     STA $70             STA $70    ; Save ReturnAddrLo
     PLA                 PLA
     STA $71             STA $70    ; Save ReturnAddrHi
_809 JSR $086D     1009: JSR $106F
     JSR $0814           JSR $1014
     JMP $0809           JMP $1009

_812 LDA #$8D      1012: LDA #$8D   ; CR ?
_814 BPL $0834     1014: BPL $1036  ; Never

LiteralToken:
_816 CMP #$A0            CMP #$A0   ; < SPACE ?
     BCC $0824           BCC $1024
     BIT $32             BIT $32
     BMI $0824           BMI $1024
     CMP #$E0            CMP #$E0   ; > '``, one char before 'a'
     BCS $0824           BCS $1024
     AND #$3F            AND #$3F   ; Make Inverse
_824 ORA #$00      1024: ORA #$00   ;
     BIT $FBB3           LDY $FBB3
     BPL $0831           CPY #$06
                         BEQ $1033  ; <-- old version extra instruction
     CMP #$E0            CMP #$E0   ; < '`', one char before 'a'
     BCC $0831           BCC $1033
     AND #$DF            AND #$DF
_831 JMP $FDF0     1033: JMP $FDF0
_834 CMP #$20      1036: CMP #$20
     BNE $083A           BNE $103C
     LDA #$11            LDA #$11
_83A ASL                 ASL
     TAY                 TAY
     LDA $0846,Y         LDA $1048,Y
     PHA                 PHA
     LHA $0845,Y         LDA $1047,Y
_844 RTS           1046: RTS

GetNextToken:                       ; Leaves Token in A and X
_86D LDY #$00     106F: LDY #00
     INC $70            INC $70
     BNE $0875          BNE $1077
     INC $71            INC $71
_875 LDA ($70),Y  1077: LDA ($70),Y
     TAX                TAX
0878:RTS                RTS

We are interested in the table of function pointers.

Track Func Ptr
$20 $0845
$21 $1047

Comparing the two jump tables:

Token $20 $21
$00 $0885 $1087
$01 $FB35 $FB35
$02 $FC57 $FC57
$03 $088E $1090
$04 $0891 $1093
$05 $089B $109D
$06 $FC9B $FC9B
$07 $FC41 $FC41
$08 $08A1 $10A3
$09 $08A8 $10AA
$0A $08B0 $10B2
$0B $03E9 $03E9
$0C $FE92 $FE92
$0D $08BF $10C1
$0E $08C8 $10CA
$0F $08C8 $10CA
$10 $08C8 $10CA
$11 $08D6 $10D8
$12 $08EC $10EE
$13 $0901 $1106

We find they are identical (barring the +2 offset.)

Let's decode the byte code tokens:

    0845 FunctionJumpTable
    0845: [$00] CMD_DONE  $0886 Return to caller, like 6502: $00 BRK
    0847: [$01] CMD_TEXT  $FB36 SETTXT-3
    0849: [$02] CMD_HOME  $FC58 HOME
    084B: [$03] CMD_NORM  $088F Switch to Normal Text
    084D: [$04] CMD_INV   $0892 Switch to Inverse Text
    084F: [$05] CMD_05    $089C
    0851: [$06] CMD_EOL   $FC9C CLREOL
    0853: [$07] CMD_CLR   $FC42 CLREOP
    0855: [$08] CMD_HTAB  $08A2 xx -> $24 = x-1
    0857: [$09] CMD_VTAB  $08A9 -> Call $FB5B TABV yy+1
    0859: [$0A] CMD_REP   $08B1 xx yy Print char XX repeated YY times
    085B: [$0B] CMD_0B    $03EA
    085D: [$0C] CMD_PR0   $FE93 SETVID (PR#0)
    085F: [$0D] CMD_POKE  $08C0 lo hi val, like 6502: $8D STA $abs
    0861: [$0E] CMD_DUP_E $08C9 Duplicate of CMD_PTR$
    0863: [$0F] CMD_DUP_F $08C9 Duplicate of CMD_PTR$
    0865: [$10] CMD_PTR$  $08C9 Print Pointer to String
    0867: [$11] CMD_JSR2  $08D7 JSR address at next two bytes
    0869: [$12] CMD_INKEY $08ED Wait for keypress
    086B: [$13] CMD_BEEP  $0902 Soft Beep
          [$20] CMD_JSR -> remapped to [$11] JSR yy xx

Backup 1 Disasembly

Here is the diassembly of Backup 1:

                    ORG $0800

0800:4C 37 0D       JMP $0D37

DecodeToken:
0803:68         _803 PLA           
0804:                STA $70       ; Save ReturnAddrLo
0806:                PLA           
0807:                STA $71       ; Save ReturnAddrHi
0809:           _809 JSR $086D     
080C:20 14 08       JSR $0814     
080F:4C 09 08       JMP $0809     

                    _812 LDA #$8D      ; CR ?
                    _814 BPL $0834     ; Never

                    LiteralToken:
                    _816 CMP #$A0      ; < SPACE ?
                         BCC $0824     
                         BIT $32       
                         BMI $0824     
                         CMP #$E0      ; > '``, one char before 'a'
                         BCS $0824     
                         AND #$3F      ; Make Inverse
                    _824 ORA #$00      ;
                         BIT $FBB3     
                         BPL $0831     
                                       ; <-- old version extra instruction
                         CMP #$E0      ; < '`', one char before 'a'
                         BCC $0831     
                         AND #$DF      
                    _831 JMP $FDF0     
                    _834 CMP #$20      
                         BNE $083A     
                         LDA #$11      
                    _83A ASL           
                         TAY           
                         LDA $0846,Y   
                         PHA           
                         LHA $0845,Y   
                    _844 RTS           

                    GetNextToken:      ; Leaves Token in A and X
                    _86D LDY #$00     
                         INC $70       
                         BNE $0875     
                         INC $71       
                    _875 LDA ($70),Y  
                         TAX                TAX
0878:RTS                RTS




0879:           GetPointerByte
0879:20 6D 08       JSR GetNextToken
087C:85 72          STA $72
087E:20 6D 08       JSR GetNextToken
0881:85 73          STA $73
0883:B1 72          LDA ($72),Y         ; Y=0
0885:60             RTS


0896:68             PLA
0897:68

0896:85 32          STA $32
0898:8C 25 08       STY $0825
089B:60             RTS

089C:A9 7F          LDA #$7F
089E:A0 40          LDY #$40
08A0:D0 F4          BNE $0896           ; Always

08C0:           Cmd_Poke
08C0:20 79 08       JSR GetPointerByte  ; Addr -> $72,$73
08C3:20 6D 08       JSR GetNextToken    ; Val  -> A
08C6:91 72          STA ($72),Y         ; Poke Addr,Val
08C8:60             RTS

                Cmd_PTR$
08C9:20 79 08       JSR GetPointerByte
08CC:B1 72          LDA ($72),Y
08CE:F0 06          BEQ $08D6
08D0:20 16 08       JSR LiteralToken
08D3:C8             INY
08D4:D0 F6          BNE $08CC
08D6:60             RTS

08F9:           Seperator
08F9:               JSR DecodeToken
08FC:0A             DFB CMD_REP
08FD:AD                 DFB "-"
08FE:27                 DFB 39   ;
08FF:8D             DFB $8D  ; CR
0900:00             DFB CMD_END
0901:60             RTS

0918:           DriveOn
0918:A6 FD          LDX rwts_SlotX16    ; P5.SlotX16 = $2B -> rwts_SlotX16 = $FD
091A:BD 89 C0       LDA DRIVE_ON,X      ;
091D:A9 00          LDA #$00
091F:4C A8 FC       JMP WAIT

0922:           DiskLoadNibbleCheck
0922:20 18 09       JSR DriveOn     ; $0918
0925:24 6D          BIT $6D
0927:10 0B          BPL $0934
0929:A9 44          LDA #$44        ; Track $22
092B:85 FF          STA rwts_HalfTrack_Have
092D:A9 00          LDA #0          ; Track $00
092F:20 09 B0       JSR RWTS_Seek
0932:46 6D          LSR $6D
0934:A9 B8          LDA #$B8        ; Load @ $B800
0936:4C 00 B5       JMP ReadBoot3   ; Carry Clear = Original, Set = Copy

0939:20 22 09       JSR DiskLoadNibbleCheck
093C:B0 02          BCS $0940
093E:38             SEC
093F:60             RTS

0940:A9 40          LDA #$40
0942:85 60          STA $60
0944:20 44 19       JSR $1944
0947:90 04          BCC +2      ; $094D
0949:C6 60          DEC $60
094D:BD 88 C0       LDA DRIVE_OFF,X
0950:60             RTS

                GetKey
08ED:2C 10 C0       BIT KEYSTROBE
08F0:AD 00 C0       LDA KEYBOARD
08F3:10 FB          BNE $8F0
08F5:2C 10 C0       BIT KEYSTROBE
08F8:60             RTS

0AB7:           BottomSeperator
0AB7:20 03 08       JSR DecodeToken
0ABA:09             DFB CMD_VTAB
0ABB:18                 DFB #24
0ABC:08             DFB CMD_HTAB
0ABD:01                 DFB #1
0ABE:0A             DFB CMD_REP
0ABF:AD                 ASC "-"
0AC0:27                 DFB #39     ; 39 cols
0AC1:08             DFB CMD_HTAB
0AC2:01                 DFB #1
0AC3:00             DFB CMD_DONE
0AC4:60             RTS

                AlreadyCopied
0B90:20 03 08       JSR DecodeToken
0B93:02             DFB CMD_HOME
0B94:09             DFB CMD_VTAB
0B95:09                 DFB #9
0B96:D4 E8          ASC "This disk has already been used to"
0B98:E9 F3 A0 E4
0B9C:E9 F3 EB A0
0BA0:E8 E1 F3 A0
0BA4:E1 EC F2 E5
0BA8:E1 E4 F9 A0
0BAC:E2 E5 E5 EE
0BB0:A0 F5 F3 E5
0BB4:E4 A0 F4 EF
0BB8:               DFB CR
0BB8:               DFB CR
0BBA                ASC "make a backup."
0BC8:               DFB 00
0BC9:4C 05 0B       JMP $0B05

                 CheckAlreadyCopied
0BCC:20 22 09       JSR DiskLoadNibbleCheck
0BCF:B0 FB          BCS $0BCC           ; if C=1 then disk read error, keep trying
0BD1:AD 00 B8       LDA $B800           ; if DiskCopiedFlag == 00=Copied, FF=Not copied
0BD4:F0 BA          BEQ AlreadyCopied   ;^ $0B90
0BD6:60             RTS

0D1A:         TXT_FANTAVISION_NAME
0D1A:           ASC "Fantavision"
0D25:00         DFB CMD_DONE
0D26:         TXT_FANTAVISION_DISK
0D26:           ASC "Fantavision disk"
0D36:00         DFB CMD_DONE

0D37:A6 FD      LDX rwts_SlotX16
0D39:8E E9 1F   STX $1FE9   ;
0D3C:8E F7 1F   STX $1FF7   ;
0D3F:A9 00      LDA #$00
0D41:8D EB 1F   STA $1FEB
0D44:8D F0 1F   STA $1FF0
0D47:46 6B      LSR $6B
0D49:46 6C      LSR $6C
0D4B:20 03 08   JSR DecodeToken
0D4E:01         DFB CMD_TEXT
0D4F:02         DFB CMD_HOME
0D50:03         DFB CMD_NORM
0D51:20 F9 08   JSR Seperator
0D54:A0 A0 A0   ASC "   "
0D57:10         DFB CMD_PTR$
0D58:1A 0D          DA  TXT_FANTAVISION_NAME
0D5A:A0         ASC " (TM)   Backup Utility"
0D70:8D         DFB CR
0D71:20 F9 08   JSR Seperator
0D74:0D         DFB CMD_POKE
0D75:22 00          DA WNDTOP   ; $22 = Window.Top
0D77:04             DFB #4      ; 4 Lines
0D78:0D         DFB CMD_POKE
0D79:23 00          DA WNDBOT   ; $23 = Window.Bottom
0D7B:16             DFB #22     ; Normally $18 = 24 rows
0D7C:20 B7 0A   JSR BottomSeperator
0D7F:20 CC 0B   JSR CheckAlreadyCopied
0D82:02         DFB CMD_HOME
0D83:           ASC "This utility lets you make one backup"
0DA8:8D         DFB CR
0DA9:           ASC "of your "
0DB1:10         DFB CMD_PTR$
0DB2:26 0D          DA TXT_FANTAVISION_DISK
0DB4:AE         ASC "."
0DB5:8D         DFB CR
0DB6:8D         DFB CR
0DB7:04         DFB CMD_INV
0DB8:           ASC "IMPORTANT:"
0DC2:03         DFB CMD_NORM
0DC3:A0 D9      ASC " You may use this option"
0DDB:8D         DFB CR
0DDC:08         DFB CMD_HTAB
0DDD:0C             DFB #12
0DDE:           ASC "only once."
0DE8:8D         DFB CR
0DE9:8D         DFB CR
0DEA:D4         ASC "To make a backup, you will need"
0E09:8D         DFB CR
0E0A:           ASC "one blacnk disk."
0E19:8D         DFB CR
0E1A:8D         DFB CR
0E1B:           ASC "You will need to swap disks a number of "
0E42:8D         DFB CR
0E43:           ASC "times. The program will tell you when"
0E68:8D         DFB CR
0E69            ASC "to do this."
0E74:8D         DFB CR
0E75:8D         DFB CR
0E76:           ASC "To start, make sure your "
0E8F:10         DFB CMD_PTR$
0E90:1A 0D          DA  TXT_FANTAVISION_NAME
0E92:8D         DFB CR
0E93:           ASC "disk (label side up) is in"
0EAD:8D         DFB 8D
0EAE:20         JSR $0AC5 ; TODO: FIXME

To 'fake' the backup used:

BD1:A9
BD3:EA

800G

Backup 2 Disasembly

TODO: FIXME

Applesoft?!

  1. Boot our Fanta.Work disk
  2. BLOAD GET_LOGO_1
  3. CALL-151
    1F0D:16
    BSAVE GET_APPLESOFT_MENU,A$1EEE,L$46
  1. Replace your Fantavision disk in slot 6
  2. 1EEEG
  3. Replace your Fanta-work disk in slot 5
  4. C500G
  5. NEW
  6. CALL-151 NOTE: We DON'T want 67:0 to change start of Applesoft from $801 to $800
  7. 800<2000.2FFFM
  8. BSAVE B6.FANTASOFT.BAS_T16_0800,A$800,L$1000
  9. <CTRL-C>
  10. LIST
4864 ONERR GOTO 9000
1 REM Fantavision (C) 1985 by Scott Anderson - 9/26/85
3 HIMEM: 2 * 4096
5 POKE 1012,0
9 HOME: VTAB 23:HTAB 4:PRINT "PRESS THE SPACE BAR TO CHANGE THE":
  HTAB 3:PRINT "DRAWING TOOL OR TO MAKE A SHOW DISK.";
10 GOTO 5000
100 HTAB 11-2*(I>4): PRINT LEF$( I$(I),1)" - "I$(I):RETURN
150 HTAB 2:FOR I = 1 TO 38: PRINT " ";:NEXT: RETURN
180 HTAB 10 - (LEN( I$( ID ) ) ) / 2: PRINT "THE DRAWING TOOL IS A "I$(ID)".":RETURN
400 REM Get Input Device
410 TEXT: HOME: HTAB 9:PRINT "= [ FANTAVISION MENU ] =":PRINT:GOSUB 150
430 FOR I = 1 to NI: VTAB 4 + 2*I + 2*(I>4):GOSUB 100:NEXT
440 VTAB 22: CALL EB: PRINT: HTAB 13:PRINT "YOU CHOICE:";: GET A$: A=ASC(A$): A=A-32*(A>96):A$=CHR$(A):PRINT A$;
445 I = NI
450 IF A <> ASC( I$(I) ) THEN I=I-1:ON I > 0 GOTO 450:PRINT CHR$(7);:GOTO 440
460 ON I > 4 GOTO 490:ID = I: D = ND
470 IF  DB%(D,0) <> ID THEN D=D-1:ON D > 0 GOTO 470
480 IF D THEN GOSUB 500:ON SL > 0 GOTO 490:VTAB 23:HTAB 5:PRINT "WHAT SLOT IS THE " I$(ID)" IN? :";: GET A$:SL = VAL( A$ ):PRINT SL;:ON NOT SL GOTO 440
490 RETURN
500 B=1:SL=7
510 IF PEEK(12*4096 + SL*256 + DB%(0,B)) = DB%(D,B) THEN B=B+1: ON B < 5 GOTO 510:GOTO 580
520 B = 1: SL = SL - 1:ON SL > 0 GOTO 510
580 RETURN
600 L = DS*16 + 49292:X = PEEK( L - 3 ): X = PEEK( L - 1 ):REM Dr2 on
610 X = PEEK( L ): DD = 2:FOR I = 1 to 20: IF PEEK(L) = X THEN NEXT I:DD = 1
630 X = PEEK( L - 4 ):RETURN
800 REM
890 RETURN
900 REM
990 RETURN
5000 REM Start of program
5020 LS = 4*256 - 2: IL = LS + 1
5040 EB = 64578
5050 Z = 43008: IO = 48249: BF = 48896: CA = 776: CP = 789
5060 DS = PEEK( IO + 1 ) / 16
5070 GOSUB 600
5100 DATA   6,MOUSE,GRAPICS TABLET,KOALA PAD,JOY STICK,CREATE A SHOW DISK,QUIT THIS MENU
5110 READ NI:FOR I = 1 TO NI: READ I$(I):NEXT
5200 DATA 2
5201 DATA 0,  5,  7, 11, 12
5210 DATA 1, 56, 24,  1, 32
5211 DATA 2,120,255,200,160
5230 READ ND: FOR D = 0 TO ND: FOR B = 0 TO 4: READ DB%(D,B):NEXT:NEXT
5300 D = ND
5310 GOSUB 500: IF SL=0 THEN D=D-1:ON D > 0 GOTO 5310
5320 IF D THEN ID = DB%(D,0)
5340 ID = ID+4 * NOT ID
5600 VTAB 22:GOSUB 180
6000 A = PEEK( 49152 ): IF A > 127 THEN 6800
6010 W = W + 1: IF W < 150 GOTO 6000: GOTO 6900
6800 GOSUB 400:ON I = 5 GOTO 7000
6820 IF QF THEN 8000
6840 HOME: VTAB 15: GOSUB 180
6900 ID = ID - (ID = 4)
6920 POKE LS-1,255 * (DD=2): POKE LS,SL:POKE IL,ID-1
6940 CALL 768
7000 :TEXT:HOME:VTAB 3:HTAB 9:PRINT "=[ CREATE A SHOW DISK ]="
7010 VTAB 22:CALL EB: PRINT: PRINT " :::::::: PRESS 'ESC' TO EXIT ::::::::"
7020 VTAB 6:HTAB 3:PRINT "TO CREATE A SHOW DISK, THIS PROGRAM": HTAB 3:PRINT "MUST COPY SOME FILES FROM  THE MOVIE":HTAB 3:PRINT "MATINEED ON SIDE II OF FANTAVISION."
7030 GOSUB 150
7050 ON DD = 1 GOTO 7100:VTAB 13:HTAB 3:PRINT "INSERT THE MATINEE IN DRIVE 1,"
7060 HTAB 7:PRINT "AND A BLANK DISK IN DRIVE 2."
7070 PRINT:HTAB 9:PRINT "PRESS RETURN WHEN  READY:";:GET A$:ON A$ = CHR$(27) GOTO 7900:GOTO 7200
7100 VTAB 14:HTAB 8:PRINT "INSERT BLANK SHOW DISKETTE":HTAB 7:PRINT "IN DRIVE 1 AND PRESS RETURN:";:GET A$
7120 ON A$ = CHR$(27) GOTO 7900
7200 POKE ZP + 2, 2:POKE ZP + 5,96 + 128*(DD=2):POKE 0,0:POKE IO+2,DD:CALL CA:IF PEEK(0) THEN 7700
7220 B = PEEK( BF + 4 ):ON B < 241 or PEEK( BF + 2 ) <> 3 or PEEEK( BF + 36 ) <> 13 GOTO 7700
7230 B = B - 240: N$ = "": FOR I = 1 TO B: N$ = N$ + CHR$( PEEK( BF + 4 + I ) ):NEXT
7240 IF N$ = "FANTAVISION" THEN POKE 0, 144:GOTO 9900
7250 VTAB 18:HTAB 13 - B / 2: PRINT "DESTROY "N$"? (Y/N):";
7300 GET A$: ON A$ <> "Y" AND A$ <> "y" GOTO 7900:VTAB 18:HTAB 1:PRINT SPC(40)
7700 QF + 1: POKE 0,0: CALL CP: IF PEEK(0) THEN 9900
7900 GOTO 6800
8000 HOME: VTAB 10, HTAB 9:PRINT "INSERT THE DESIRED DISK,":PRINT:HTAB 12:PRINT "THEN PRESS RETURN:";:GET A$
8010 PRINT: CALL 12*4096 + DS*256
9000 Error trap
9010 RUN
9900 REM Dos Errors
9910 ER = PEEK( 0 ):POKE 0,0:VTAB 12:CALL EB:PRINT CHR$(7):FLASH
9920 IF ER=39 THEN HTAB 12:PRINT "UNABLE TO FORMAT.":GOTO 9990
9930 IF ER=43 OR ER=16 THEN HTAB 13:PRINT "WRITE PROTECTED.":GOTO 9990
9940 IF ER=51 THEN HTAB 13:PRINT "DRIVE TOO SLOW.":GOTO 9990
9950 IF ER=52 THEN HTAB 13:PRINT "DRIVE TOO FAST.":GOTO 9990
9970 IF ER=144 THEN HTAB 8:PRINT "CAN'T DESTROY FANTAVISION!":GOTO 9990
9980 HTAB 15:PRINT "DRIVE ERROR."
9990 NORMAL:HTAB 16:HTAB 9:PRINT "PRESS ANY KEY FOR MENU:";:GET A$:GOTO 5400

NOTES:

  • Line 0 has a bogus line number 4864 due to $0803:00 13
  • Line 3 changes the top of Applesoft variables to memory $2000, right before the HGR screen.
  • Line 5 changes the Reset Vector Checksum: 1012 = $3F4.
  • Line 6490 Exits the Applesoft program to $0300

Here is a disassembly of the first few bytes of the Applesoft program.

0800:08 0C 08 00 13 A5 AB 39 30 30 30 00
     \/ \___/ \___/ \/ \/ \/ \/ \/ \/ \/
   not  Addr  Line    GOTO 9 0  0  0  End-of-Line
  used  Next  Num  ONERR
        Statement

Let's pivot that table to make it easier to read:

Addr Byte(s) Meaning
0800 08 not used
0801 0C 08 Address of next line, $080C
0803 00 13 Line number, $1300
0805 A5 ONERR
0806 AB GOTO
0807 39 30 30 30 9000
080B 00 End-Of-Line sentinel

Applesoft Tokens

In case your knowledge of Applesoft is rusty, Bit-7 is used to desiginate BASIC "Tokens"

Quick review of Applesoft in ROM on the Apple.

  • $D000 is an Array of 16-bit Function Pointers
  • $D0D0 is the string (in DCI format) of all the token names.

Let's write a program to generate a nice table for us:

    ORG $300
    LDA #$D0    ; 0300:A9 D0
    STA $FE     ; 0302:85 FE
    STA $FF     ; 0304:85 FF
    LDA #$80    ; 0306:A9 80 Start @ Token $80
    STA $FC     ; 0308:85 FC

^0  JSR PRBYTE  ; 030A:20 DA FD
    LDA #$A0    ; 030D:A9 A0 space char
    JSR COUT    ; 030F:20 ED FD
^1  LDY #$00    ; 0312:A0 00
    LDA ($FE),Y ; 0314:B1 FE
    PHA         ; 0316:48
    INC $FE     ; 0317:E6 FE
    BNE ^2      ; 0319:D0 02
    INC $FF     ; 031B:E6 FF
^2  ORA #$80    ; 031D:09 80
    JSR COUT    ; 031F:20 ED FD
    PLA         ; 0322:68
    BPL ^1      ; 0323:10 ED
    JSR CROUT   ; 0325:20 8E FD   CROUT = $FD8E

    LDA $C000   ; 0328:AD 00 C0
    BPL ^4      ; 032B:10 0B
    STA $C010   ; 032D:8D 10 C0
^3
    LDA $C000   ; 0330:AD 00 C0
    BPL ^3      ; 0333:10 FB
    STA $C010   ; 0335:8D 10 C0
^4
    INC $FC     ; 0338:E6 FC
    LDA $FC     ; 033A:A5 FC Token
    CMP #$EB    ; 033C:C9 EB
    BNE ^0      ; 033E:D0 CA
    RTS         ; 0340:60

Enter in the raw machine code:

300:A9 D0 85 FE 85 FF A9 80
308:85 FC 20 DA FD A9 A0 20
310:ED FD A0 00 B1 FE 48 E6
318:FE D0 02 E6 FF 09 80 20
320:ED FD 68 10 ED 20 8E FD
328:AD 00 C0 10 0B 8D 10 C0
330:AD 00 C0 10 FB 8D 10 C0
338:E6 FC A5 FC C9 EB D0 CA
340:60

Which generates this table:

Hex Token
$80 END
$81 FOR
$82 NEXT
$83 DATA
$84 INPUT
$85 DEL
$86 DIM
$87 READ
$88 GR
$89 TEXT
$8A PR#
$8B IN#
$8C CALL
$8D PLOT
$8E HLIN
$8F VLIN
$90 HGR2
$91 HGR
$92 HCOLOR=
$93 HPLOT
$94 DRAW
$95 XDRAW
$96 HTAB
$97 HOME
$98 ROT=
$99 SCALE=
$9A SHLOAD
$9B TRACE
$9C NOTRACE
$9D NORMAL
$9E INVERSE
$9F FLASH
$A0 COLOR=
$A1 POP
$A2 VTAB
$A3 HIMEM:
$A4 LOMEM:
$A5 ONERR
$A6 RESUME
$A7 RECALL
$A8 STORE
$A9 SPEED=
$AA LET
$AB GOTO
$AC RUN
$AD IF
$AE RESTORE
$AF &
$B0 GOSUB
$B1 RETURN
$B2 REM
$B3 STOP
$B4 ON
$B5 WAIT
$B6 LOAD
$B7 SAVE
$B8 DEF
$B9 POKE
$BA PRINT
$BB CONT
$BC LIST
$BD CLEAR
$BE GET
$BF NEW
$C0 TAB(
$C1 TO
$C2 FN
$C3 SPC(
$C4 THEN
$C5 AT
$C6 NOT
$C7 STEP
$C8 +
$C9 -
$CA *
$CB /
$CC ^
$CD AND
$CE OR
$CF >
$D0 =
$D1 <
$D2 SGN
$D3 INT
$D4 ABS
$D5 USR
$D6 FRE
$D7 SCRN(
$D8 PDL
$D9 POS
$DA SQR
$DB RND
$DC LOG
$DD EXP
$DE COS
$DF SIN
$E0 TAN
$E1 ATN
$E2 PEEK
$E3 LEN
$E4 STR$
$E5 VAL
$E6 ASC
$E7 CHR$
$E8 LEFT$
$E9 RIGHT$
$EA MID$

There is very nice chart at Call APPLE Applesoft Tokens

To see Token 13

NEW
127 REM ABC
511 REM DEF
CALL-151
800.817

Boot stage 5

Boot stage 6

The Applesoft "Fantavision Menu" returns via CALL 768

0300:A9 60     LDA #$60
0302:8D 50 40  STA $4050
0306:4C 00 60  JMP $6000  FANTA_INIT
0309:20 26 03  JSR $0326
030B:A9 02     LDA #$02
030D:85 40     STA $40
030F:20 00 B3  JSR $B300
0312:4C 1B 03  JMP $031B

Boot Stage Memory Map

From $BE27 we can build this memory map

Track Address
$15a $B000 .. $B700
$15b $1800 .. $1FFF
$16 $0800 .. $17FF
$17 $2000 .. $2FFF
$18 $3000 .. $3FFF
$19 $4000 .. $4FFF
$1A $5000 .. $5FFF
$1B $6000 .. $6FFF
$1C $7000 .. $7FFF
$1D $8000 .. $8FFF
$1E $9000 .. $9FFF
$1F $A000 .. $AFFF

Disk Information

If one boots up Copy ][ 5.0 and uses the Sector Editor to read the original Fantavision disk you'll get an I/O Error.

Inspecting the disk nibbles via Copy > Bit Copy > Nibble Editor Tracks $00 through $21 we find the following disk nibbles for Track $00, Sector $0:

D5 AA 96 FF FE AA AA AA AA FF FE DE AB
\______/ \___/ \___/ \___/ \___/ \___/
 Address  Vol   Trk   Sec   XOR  Address
Prologue  254   $00   $0   Check Epilogue

...random sync bytes...

D5 AA AD ...342... DE AA EB
\______/           \______/
 Data               Data
Prologue           Epilogue

The standard Address bit-stream for a DOS 3.3 disk is:

D5 AA 96 vv vv tt tt ss ss cc cc DE AA EB
\______/ \___/ \___/ \___/ \___/ \______/
 Address Vol.  Track Sec.  Check Address
Prologue                         Epilogue

Everything looks kosher with Fantavision -- except Fantavision has changed the epilog disk nibbles from the standard DE AA EB to the non-standard DE AB

Returning to the Sector Editor we can press P to patch DOS 3.3 and use RETURN to cycle through the fields until we get to CHECK EPILOG?, N to toggle it, then ESC to return to the sector edit.

CUSTOM

                ADDRESS

  CHECK EPILOG? NO

Re-reading T00S0 ...

00- 01 A9 60 8D 01 08 A2 00
08- 86 FF B5 00 9D 00 10 E8
10- D0 F8 8D 08 C0 BD 00 10
18- 95 00 E8 D0 F8 A9 FF 8D
20- FB 04 8D F3 03 8D F4 03
28- 8D 00 C0 8D 0C C0 8D 0E
30- C0 8D 5F C0 8D 81 C0 20
38- 2F FB 20 58 FC 20 84 FE
40- 20 93 FE 20 89 FE A6 2B
48- 8A 4A 4A 4A 4A 09 C0 8D
50- 6E 08 A9 0F 85 52 A9 15
58- 85 41 0A 20 81 08 A5 52
60- B9 BE 08 85 3D B9 CE 08
68- F0 05 85 27 20 5C 00 C6
70- 52 10 EB A6 2B 86 FD A9
78- BC 20 00 B5 B0 F5 4C 00
80- BE 85 50 A5 FF 85 51 38
88- E5 50 F0 2C B0 04 E6 FF
90- 90 02 C6 FF 20 AD 08 20
98- B9 08 A5 51 29 03 0A 05
A0- 2B A8 B9 80 C0 20 B9 08
A8- F0 D9 20 B9 08 A5 FF 29
B0- 03 0A 05 2B A8 B9 81 C0
B8- 60 A9 28 4C A8 FC 00 0D
C0- 0B 09 07 05 03 01 0E 0C
C8- 0A 08 06 04 02 0F B0 B1
D0- B2 B3 B4 B5 B6 B7 18 19
D8- 1A 1B 1C 1D 1E 1F 00 00
E0- 00 00 00 00 00 00 00 00
E8- 00 00 00 00 00 00 00 00
F0- 00 00 00 00 00 00 00 00
F8- 00 00 00 00 00 00 00 00

AH-HA! Success!

Q. Why does the P5 PROM read these sectors but not DOS or ProDOS ?

A. We're getting ahead of ourselvs but the P5 PROM:

  • only checks the 3 disk nibbles od the Prolog fields, and
  • ignores the Epilig fields entirely!

COPY, eh?

To over-use a cliche, "With knowledge comes power."

With our new found knowledge we can use the old standby COPYA on a DOS 3.3 Master (disk) to copy all but the last track.

The only catch are:

  • we need to tell DOS 3.3 to ignore the Address Prologue (or use the correct one), and
  • we need to tell COPYA to skip track $22.

Both of these are pretty easy to do:

DOS 3.3 only checks for the disk nibble $DE epilog in one of two places:

  • Address Epilog at $B98B
  • Data Epilog at $B92F

The Data fields are read in DOS 3.3's function READ16 @ $B8DC but we are only interested in the Data Epilog portion @ $B92F:

392F:BD 8C C0       155 READ7       LDA Q6L,X
3932:10 FB          156             BPL READ7       *** NO PAGE CROSS! ***
3934:C9 DE          157             CMP #$DE        FIRST BIT SLIP MARK?
3936:D0 0A          158             BNE RDERR       (ERR IF NOT)
3938:EA             159             NOP             DELAY BETWEEN NIBLS.
3939:BD 8C C0       160 READ8       LDA Q6L,X
393C:10 FB          161             BPL READ8       *** NO PAGE CROSS! ***
393E:C9 AA          162             CMP #$AA        SECOND BIT SLIP MARK?
3940:F0 5C          163             BEQ RDEXIT      (DONE IF IT IS)
3942:38             164 RDERR       SEC             INDICATE 'ERROR EXIT'.
3943:60             165             RTS             FROM READ16 OR RDADR16.

The Address fields are read in DOS 3.3's function RDADR16 @ $B944 but we are only interested in the Address Epilog portions @ $ B98B:

398B:BD 8C C0       093 RDA6        LDA Q6L,X       FIRST BIT-SLIP NIBL.
398E:10 FB          094             BPL RDA6        *** NO PAGE CROSS! ***
3990:C9 DE          095             CMP #$DE
3992:D0 AE          096             BNE RDERR       ERROR IF NONMATCH.
3994:EA             097             NOP             DELAY BETWEEN NIBLS.
3995:BD 8C C0       098 RDA7        LDA Q6L,X       SECOND BIT-SLIP NIBL.
3998:10 FB          099             BPL RDA7        *** NO PAGE CROSS! ***
399A:C9 AA          100             CMP #$AA
399C:D0 A4          101             BNE RDERR       ERROR IF NONMATCH.
399E:18             102 RDEXIT      CLC             CLEAR CARRY ON
399F:60             103             RTS             NORMAL READ EXITS.

We need to change byte @ $B99B from $AA to $AB.

Technically we only need to change one of these.

  1. Boot a DOS 3.3 Master disk
  2. LOAD COPYA
  3. Make these changes to the Applesoft program
166 B=169:GOSUB 400:B=0:GOSUB 400`
167 ET=34
168 POKE 721,ET:POKE 722,ET:POKE 863,ET
400 POKE 47514,B:POKE 47422,B:RETURN
401 REM $B99A:C9 AA
402 REM $B93E:C9 AA
  1. Grab a blank disk, label it Fanta.COPYA
  2. Put your original Fantavision disk in Drive 1
  3. Put your blank Fanta.COPYA disk in Drive 2
  4. RUN

It will boot but fail to load due to missing the encrypted track $22. Let's fire up Copy ][ Sector Editor and make the modifications to skip reading track 22.

ProDOS Hybrid!?

If we boot ProDOS and try to CAT our Fanta.COPYA disk our efforts are rewarded with this:

    /FANTAVISION

    NAME            TYPE  BLOCKS  MODIFIED

    *FORMAT          BIN       7   4-SEP-85
     M.SPIDER        BIN       4  <NO DATE>
     M.CANCAN        BIN       4  <NO DATE>
     M.TREE          BIN       5  <NO DATE>
     M.OBJECTS       BIN       3  <NO DATE>
     M.DIVE          BIN       4  <NO DATE>
     M.FACE          BIN       3  <NO DATE>
     M.PEOPLE        BIN       4  <NO DATE>
     M.REDWHITEBLUE  BIN       3  <NO DATE>
     M.ENGLISHFONT   BIN      10  <NO DATE>
     M.SLANTFONT     BIN       4  <NO DATE>
     M.ORBITFONT     BIN       4  <NO DATE>
     M.DOTS2FONT     BIN       6  <NO DATE>
     M.CIRCUITFONT   BIN       5  <NO DATE>
     M.DOTSFONT      BIN       6  <NO DATE>
     M.OUTLINEFONT   BIN       8  <NO DATE>
     M.LAND          BIN       3  <NO DATE>

    BLOCKS FFREE:   78     BLOCKS USED: 202

Disk Notes

Notes

T00S8  n/a  ProDOS Block 3B ProDOS Volume Directory 2
                M.MORFIC
                M.VOLCANO
T00S9  n/a  ProDOS Block 3A ProDOS Volume Directory 2
                M.DOTS2FONT
                M.CIRCUITFONT
                M.DOTSFONT
                M.OUTLINEFONT
                M.LAND
                M.APPLE2
                M.SHADOWTRIXKS
T00SA  n/a  ProDOS Block 2B ProDOS Volume Directory 1
                M.FACE
                M.PEOPLE
                M.REDWHITEBLUE
                M.ENGLISHFONT
                M.SLANTFONT
                M.ORBITFONT
T00SB  n/a  ProDOS Block 2A ProDOS Volume Directory 1
                VOLUME: "FANTAVISION"
                FORMAT
                M.SPIDER
                M.CANCAN
                M.TREE
                M.OBJECTS
                M.DIVE

ProDOS Block Review

NOTES:

  • Block = ProDOS Block number (Dec)
  • Track (Hex)
  • Sector = Physical Sector (Hex)
  • Logical Sector = Copy ][+
Block Track Sectors Logical
0 00 0,2 0,E
1 00 4,6 D,C
2 00 8,A B,A
3 00 C,E 9,8
4 00 1,3 7,6
5 00 5,7 5,4
6 00 9,B 3,2
7 00 D,F 1,F
: : : :
279 22 D,F 1,F

Version 1

    0300:A9 00      LDA #00     ; Start Block
    0302:85 FF      STA $FF
    0304:20 10 03   JSR Block2TrackSector
    0307:E6 FF      INC $FF
    0309:A5 FF      LDA $FF
    030B:C9 20      CMP #$10    ; End Block
    030D:90 F5      BCC $304
    030F:60         RTS

Block2TrackSector:
    0310:48         PHA         ;+(1)
    0311:48         PHA         ;+(2)
                    JSR PRBYTE
                    LDA #'='    ; $BD
                    JSR COUT
                    PLA         ;-(2)

    0312:29 07      AND #7
    0314:C9 04      CMP #4
    0316:29 03      AND #3
    0318:08         PHP
    0319:0A         ASL
    031A:28         PLP
    031B:2A         ROL
    031C:85 FE      STA $FD     ; Sector First Half
    031E:A9 00      LDA #$00    ; Block+1
    0320:4A         LSR
    0321:68         PLA         ;-(2)
    0322:6A         ROR
    0323:4A         LSR
    0324:4A         LSR
    0324:85 FC      STA $FC     ; Track

    0327:A9 D4      LDA #$'T'   ;
    0329:20 ED FD   JSR COUT
    032C:A5 FC      LDA $FC     ; Track
    032E:20 DA FD   JSR PRBYTE  ; Print 2 hex digits = Register A
    0331:A9 D3      LDA #$'S'   ;
    0333:20 ED FD   JSR $FDED
    0336:A5 FD      LDA $FD     ; Sector
    0338:20 E3 FD   JSR PRHEX   ; Print 1 hex digit = Register A
    033B:A9 AC      LDA #$','
    033D:20 ED FD   JSR COUT
    0340:A4 FD      LDY $FD     ; Sector Second Half
    0342:C8         INY
    0343:C8         INY
    0344:98         TYA
    0345:20 E3 FD   JSR PRHEX
    0348:20 8E FD   JSR CROUT   ; $FD8E
    034B:68         PLA         ;-(1)
    034C:60         RTS

Disk Usage Summary

T/S Addr Block Offset Description
T00S0 $0800 0a n/a Boot Stage 1 - Boot Sector
T00S1 n/a 7a #0000 /FANTAVISION/FORMAT
T00S2 n/a 6b n/a ProDOS Volume Bitmap
T00S3 n/a 6a n/a ProDOS Volume Bitmap
T00S4 n/a 5b n/a ProDOS Volume Directory 4
T00S5 n/a 5a n/a ProDOS Volume Directory 4
T00S6 n/a 4b n/a ProDOS Volume Directory 3
T00S7 n/a 4a n/a ProDOS Volume Directory 3
T00S8 n/a 3b n/a ProDOS Volume Directory 2
T00S9 n/a 3a n/a ProDOS Volume Directory 2
T00SA n/a 2b n/a ProDOS Volume Directory 1
T00SB n/a 2a n/a ProDOS Volume Directory 1
T00SC n/a 1b n/a SOS Boot Part B
T00SD n/a 1a n/a SOS Boot Part A, JMP $A06E, ASC "SOS BOOT 1.1 .SOS.KERNEL"
T00SE n/a 0b n/a "PRODOS__________", "*** UNABLE TO LOAD PRODOS ***"
T00SF n/a 7b #0100 /FANTAVISION/FORMAT
--- --- -- --- ---
T01S0 n/a 8a #0200 /FANTAVISION/FORMAT
T01S1 n/a 15a #0200 /FANTAVISION/M.SPIDER
T01S2 n/a 14b #0100 /FANTAVISION/M.SPIDER
T01S3 n/a 14a #0000 /FANTAVISION/M.SPIDER 05 01 06 04 3B 08 FF 00
T01S4 n/a 13b #0D00 /FANTAVISION/FORMAT, Zero
T01S5 n/a 13a #0C00 /FANTAVISION/FORMAT, Mirror of T00SE, "PRODOS"
T01S6 n/a 12b #0B00 /FANTAVISION/FORMAT
T01S7 n/a 12a #0A00 /FANTAVISION/FORMAT, Mirror of T00SD, "SOS BOOT 1.1 KERNEL"
T01S8 n/a 11b #0900 /FANTAVISION/FORMAT
T01S9 n/a 11a #0800 /FANTAVISION/FORMAT, Subdirectory "MOVIES" ?
T01SA n/a 10b #0700 /FANTAVISION/FORMAT
T01SB n/a 10a #0600 /FANTAVISION/FORMAT
T01SC n/a 9b #0500 /FANTAVISION/FORMAT
T01SD n/a 9a #0400 /FANTAVISION/FORMAT
T01SE n/a 8b #0300 /FANTAVISION/FORMAT, Zero
T01SF n/a 15b #0300 /FANTAVISION/M.SPIDER
--- --- -- --- ---
T02S0 n/a #0400 /FANTAVISION/M.SPIDER #0400
T02S1 n/a n/a
T02S2 n/a n/a
T02S3 n/a n/a
T02S4 n/a #0700 /FANTAVISION/M.CANCAN, Zero
T02S5 n/a #0600 /FANTAVISION/M.CANCAN
T02S6 n/a #0500 /FANTAVISION/M.CANCAN
T02S7 n/a #0400 /FANTAVISION/M.CANCAN
T02S8 n/a #0300 /FANTAVISION/M.CANCAN, Zero
T02S9 n/a #0200 /FANTAVISION/M.CANCAN
T02SA n/a #0100 /FANTAVISION/M.CANCAN
T02SB n/a #0000 /FANTAVISION/M.CANCAN, 0000:03 03 04 04
T02SC n/a #0700 /FANTAVISION/M.SPIDER
T02SD n/a #0600 /FANTAVISION/M.SPIDER
T02SE n/a #0500 /FANTAVISION/M.SPIDER
T02SF n/a ??? /FANTAVISION/M.TREE, Zero
--- --- -- --- ---
T03S0 n/a ???
T03S1 n/a ???
T03S2 n/a ???
T03S3 n/a ???
T03S4 n/a ???
T03S5 n/a ???
T03S6 n/a ??? Zero
T03S7 n/a ???
T03S8 n/a ???
T03S9 n/a ???
T03SA n/a ???
T03SB n/a ???
T03SC n/a ???
T03SD n/a ???
T03SE n/a ???
T03SF n/a ??? Zero
--- --- -- --- ---
T04S0 n/a ???
T04S1 n/a ???
T04S2 n/a ???
T04S3 n/a ???
T04S4 n/a ??? Zero
T04S5 n/a ???
T04S6 n/a ??? Zero
T04S7 n/a ???
T04S8 n/a ??? Zero
T04S9 n/a ???
T04SA n/a ???
T04SB n/a ???
T04SC n/a ???
T04SD n/a ???
T04SE n/a ???
T04SF n/a ???
--- --- -- --- ---
|T05S0
|T05S1
|T05S2
|T05S3
|T05S4
|T05S5
|T05S6
|T05S7
|T05S8 Zero
|T05S9
|T05SA
|T05SB
|T05SC Zero
|T05SD
|T05SE
|T05SF Zero
| --- | --- |  -- | ---  | ---                          |
|T05S0|
|T05S1|
|T05S2|
|T05S3|
|T05S4|
|T05S5|
|T05S6|
|T05S7|
|T05S8|
|T05S9|
|T05S9|
|T05SA|
|T06SC| Zero
|T05SD|
|T06SE| Zero
|T05SF|
| --- | --- |  -- | ---  | ---                          |
|T07S8|     | Zero
|T07SF|     |Zero
| --- | --- |  -- | ---  | ---                          |

End of ProDOS Files

T/S Addr Description
T08Sx n/a Mirror of Track $1A
T09Sx n/a Mirror of Track $19
T0ASx n/a Mirror of Track $18 @ $3000
T0BSx n/a Mirror of Track $17 @ $2000
----- --- ---
T0CS0 n/a ???
T0CS1 n/a ???
T0CS2 n/a ???
T0CS3 n/a ???
T0CS4 n/a ???
T0CS5 n/a ???
T0CS6 n/a ???
T0CS7 n/a ???
T0CS8 n/a ???
T0CS9 n/a ???
T0CSA n/a ???
T0CSB n/a ???
T0CSC n/a ???
T0CSD n/a ???
T0CSE n/a Zero
----- --- ---
T0DS0 n/a 00:69, remaining Zero
T0DSx n/a ???
----- --- ---
T0ES0 n/a ???
T0ES1 n/a ???
T0ES2 n/a ???
T0ES3 n/a ???
T0ES4 n/a ???
T0ES5 n/a ???
T0ES6 n/a ???
T0ES7 n/a ???
T0ES8 n/a ???
T0ES9 n/a ???
T0ESA n/a ???
T0ESB n/a ???
T0ESC n/a ???
T0ESD n/a ???
T0ESE n/a ???
T0ESF n/a ???
----- --- ---
T0FSx n/a Blank / Not used
T10Sx n/a Blank / Not used
T11Sx n/a Blank / Not used
----- --- ---
T12S0 n/a $????
T13S0 n/a $????
T14S0 n/a $????
  • Boot Stage 2
T/S Addr Description
T15S0 $B000 RWTS
T15S1 $B100 RWTS
T15S2 $B200 RWTS 6-bit to 7-Bit Optimized 6&2 Decode Table
T15S3 $B300 RWTS
T15S4 $B400 RWTS
T15S5 $B500 RWTS
T15S6 $B600 Zero, Unused
T15S7 $B700 Zero, Unused
T15S8 $1800 FORMAT ???
T15S9 $1900
T15SA $1A00
T15SB $1B00
T15SC $1C00
T15SD $1D00
T15SE $1E00
T15SF $1F00
  • Fantavision Program
T/S Addr Description
T16S0 $0800 Applesoft Utility!! "= [ FANTAVISION MENU ] ="
T16S1 $0900
T16S2 $0A00
T16S3 $0B00
T16S4 $0C00 MOUSE,GRAPHICS TABLET,KOALA PAD,JOYSTICK,CREATE A SHOW DISK, QUIT THIS MENU
T16S5 $0D00
T16S6 $0E00
T16S7 $0F00 "INSERT THE MATINEE IN DRIVE 1,", "AND A BLANK DISK IN DRIVE 2."
T16S8 $1000 "DESTROY"
T16S9 $1100 "INSERT THE DESIRED DISK", "UNABLE TO FORMAT."
T16SA $1200 "DRIVE TOO FAST", "DRIVE TOO SLOW", "CAN'T DESTROY FANTAVISION!"
T16SB $1300 last 5 bytes: "5400",0
T16SC $1400 Blank / Unused
T16SD $1500 Blank / Unused
T16SE $1600 Blank / Unused
T16SF $1700 Blank - reserved for Applesoft variaibles, HIMEM: 2*4096
----- --- ----
T17S0 $2000 Logo
T17S1 $2100 Logo
T17S2 $2200 Logo
T17S3 $2300 Logo
T17S4 $2400 Logo
T17S5 $2500 Logo
T17S6 $2600 Logo
T17S7 $2700 Logo
T17S8 $2800 Logo
T17S9 $2900 Logo
T17SA $2A00 Logo
T17SB $2B00 Logo
T17SC $2C00 Logo
T17SD $2D00 Logo
T17SE $2E00 Logo
T17SF $2F00 Logo
----- --- ----
T18S0 $3000 Logo
T18S1 $3100 Logo
T18S2 $3200 Logo
T18S3 $3300 Logo
T18S4 $3400 Logo
T18S5 $3500 Logo
T18S6 $3600 Logo
T18S7 $3700 Logo
T18S8 $3800 Logo
T18S9 $3900 Logo
T18SA $3A00 Logo
T18SB $3B00 Logo
T18SC $3C00 Logo
T18SD $3D00 Logo
T18SE $3E00 Logo
T18SF $3F00 Logo
----- --- ----
T19S0 $4000 Main.2
T19S1 $4100
T19S2 $4200 "MOVIE MATINEE DISK BLANK SHOW DISKETTE"
T19S3 $4300 Source Assembly Fragment 1
T19S4 $4400 Table, Source Assembly Fragment 2
T19S5 $4500
T19S6 $4600
T19S7 $4700
T19S8 $4800
T19S9 $4900 "FANTAVISIONFORMAT"
T19SA $4A00
T19SB $4B00
T19SC $4C00
T19SD $4D00
T19SE $4E00
T19SF $4F00
----- --- ----
T1AS0 $5000
T1AS1 $5100
T1AS2 $5200
T1AS3 $5300
T1AS4 $5400
T1AS5 $5500
T1AS6 $5600
T1AS7 $5700
T1AS8 $5800
T1AS9 $5900 @ $4E: "(C) 1985 BY SCOTT ANDERSON"
T1ASA $5A00
T1ASB $5B00
T1ASC $5C00
T1ASD $5D00
T1ASE $5E00 @ $6F: "(C) 1984 BY SCOTT ANDERSON"
T1ASF $5F00
----- --- ----
T1BS0 $6000
T1BS1 $6100
T1BS2 $6200
T1BS3 $6300 @ $E2: "(C) 1984 BY SCOTT ANDERSON"
T1BS4 $6400
T1BS5 $6500 @ $8D: "(C) 1984 BY SCOTT ANDERSON"
T1BS6 $6600
T1BS7 $6700
T1BS8 $6800
T1BS9 $6900
T1BSA $6A00 Icons??
T1BSB $6B00 Icons??
T1BSC $6C00
T1BSD $6D00
T1BSE $6E00
T1BSF $6F00
----- --- ----
T1CS0 $7000
T1CS0 $7100
T1CS2 $7200 @59 HGR LookUP Y Table low byte
T1CS3 $7300 @00 HGR Lookup Y Table, Low byte and High Bytes
T1CS4 $7400
T1CS5 $7500
T1CS6 $7600
T1CS7 $7700
T1CS8 $7800
T1CS9 $7900 @74 Main menu: File, and Edit
T1CSA $7A00 Main menu: Goodies
T1CSB $7B00
T1CSC $7C00
T1CSD $7D00
T1CSE $7E00
T1CSF $7F00
----- --- ----
T1DS0 $8000
T1DS1 $8100
T1DS2 $8200
T1DS3 $8300
T1DS4 $8400
T1DS5 $8500
T1DS6 $8600
T1DS7 $8700
T1DS8 $8800
T1DS9 $8900
T1DSA $8A00
T1DSB $8B00
T1DSC $8C00
T1DSD $8D00
T1DSE $8E00
T1DSF $8F00
----- --- ----
T1ES0 $9000
T1ES1 $9100
T1ES2 $9200
T1ES3 $9300
T1ES4 $9400
T1ES5 $9500
T1ES6 $9600
T1ES7 $9700
T1ES8 $9800
T1ES9 $9900
T1ESA $9A00
T1ESB $9B00
T1ESC $9C00
T1ESD $9D00
T1ESE $9E00
T1ESF $9F00
----- --- ----
T1FS0 $A000
T1FS1 $A100
T1FS2 $A200
T1FS3 $A300
T1FS4 $A400
T1FS5 $A500
T1FS6 $A600 UNUSED Make Scripts
T1FS7 $A700 DOS 3.3: FTOC Track/Sector List: T1BS04 T1BS03 T1BS02
T1FS8 $A800 @ $2D: DOS3.3 FILENAME: "UP.D2"
T1FS9 $A900 DOS 3.3: FTOC Track/Sector List: T1CS0E
T1FSA $AA00 @ $82: DOS3.3 FILENAME: "PATCH"
T1FSB $AB00
T1FSC $AC00
T1FSD $AD00
T1FSE $AE00
T1FSF $AF00
----- --- ----
T20S0 $0800 Backup Version A
T20S1 $0900 Backup Version A
T20S2 $0A00 Backup Version A
T20S3 $0B00 Backup Version A
T20S4 $0C00 Backup Version A
T20S5 $0D00 Backup Version A
T20S6 $0E00 Backup Version A
T20S7 $0F00 Backup Version A
T20S8 $3800 Logo bottom 1/4, same as T18S8 and T21S8
T20S9 $3900 Logo bottom 1/4, same as T18S9 and T21S9
T20SA $3A00 Logo bottom 1/4, same as T18SA and T21SA
T20SB $3B00 Logo bottom 1/4, same as T18SB and T21SB
T20SC $3C00 Logo bottom 1/4, same as T18SC and T21SC
T20SD $3D00 Logo bottom 1/4, same as T18SD and T21SD
T20SE $3E00 Logo bottom 1/4, same as T18SE and T21SE
T20SF $3F00 Logo bottom 1/4, same as T18SF and T21SF
----- --- ----
T21S0 $0800 Backup Version B
T21S1 $0900 Backup Version B
T21S2 $0A00 Backup Version B
T21S3 $0B00 Backup Version B
T21S4 $0C00 Backup Version B
T21S5 $0D00 Backup Version B
T21S6 $0E00 Backup Version B
T21S7 $0F00 Backup Version B
T21S8 $3800 Logo bottom 1/4, same as T18S8
T21S9 $3900 Logo bottom 1/4, same as T18S9
T21SA $3A00 Logo bottom 1/4, same as T18SA
T21SB $3B00 Logo bottom 1/4, same as T18SB
T21SC $3C00 Logo bottom 1/4, same as T18SC
T21SD $3D00 Logo bottom 1/4, same as T18SD
T21SE $3E00 Logo bottom 1/4, same as T18SE
T21SF $3F00 Logo bottom 1/4, same as T18SF
----- --- ----
T22S- $BC00
T22S- $BD00
T22S- $BE00 Boot Stage 4
T22S- $BF00
  • NOTE: Track $22 has a single "BIG" sector of 4 pages.

Disk Usage Details

  • T1CS2 $7200 @59 HGR LookUP Y Table low byte
    • Relocated to LC: $FE00
        59: $00,$00,$00,$00,$00,$00,$00,$00 ;   0 ..   7
        61: $80,$80,$80,$80,$80,$80,$80,$80 ;   8 ..  15
        69: $00,$00,$00,$00,$00,$00,$00,$00 ;  16 ..  23
        71: $80,$80,$80,$80,$80,$80,$80,$80 ;  24 ..  31
        79: $00,$00,$00,$00,$00,$00,$00,$00 ;  32 ..  39
        81: $80,$80,$80,$80,$80,$80,$80,$80 ;  40 ..  47
        89: $00,$00,$00,$00,$00,$00,$00,$00 ;  48 ..  55
        91: $80,$80,$80,$80,$80,$80,$80,$80 ;  56 ..  63

        99: $28,$28,$28,$28,$28,$28,$28,$28 ;  64 ..  71
        A1: $A8,$A8,$A8,$A8,$A8,$A8,$A8,$A8 ;  72 ..  79
        A9: $28,$28,$28,$28,$28,$28,$28,$28 ;  80 ..  87
        B1: $A8,$A8,$A8,$A8,$A8,$A8,$A8,$A8 ;  88 ..  95
        B9: $28,$28,$28,$28,$28,$28,$28,$28 ;  96 .. 103
        C1: $A8,$A8,$A8,$A8,$A8,$A8,$A8,$A8 ; 104 .. 111
        C9: $28,$28,$28,$28,$28,$28,$28,$28 ; 112 .. 119
        D1: $A8,$A8,$A8,$A8,$A8,$A8,$A8,$A8 ; 120 .. 127

        D9: $50,$50,$50,$50,$50,$50,$50,$50 ; 128 .. 135
        E1: $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0 ; 136 .. 143
        E9: $50,$50,$50,$50,$50,$50,$50,$50 ; 144 .. 151
        F1: $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0 ; 152 .. 159
        F9: $50,$50,$50,$50,$50,$50,$50     ; 160 .. 166 *** spans sector
  • T1CS3 $7300 @00 HGR Lookup Y Table, Low byte and High Bytes
        00:                             $50 ;        167 *** spans sector
        01: $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0 ; 168 .. 175
        09: $50,$50,$50,$50,$50,$50,$50,$50 ; 176 .. 183
        11: $D0,$D0,$D0,$D0,$D0,$D0,$D0,$D0 ; 184 .. 191
HGR Lookup Y Table
Relocated to LC: $FF00
        59:00 04 08 0C 10 14 18 1C ;   0 ..   7
        61:00 04 08 0C 10 14 18 1C ;   8 ..  15
        69:01 05 09 0D 11 15 19 1D ;  16 ..  23
        71:01 05 09 0D 11 15 19 1D ;  24 ..  31
        79:02 06 0A 0E 12 16 1A 1E ;  32 ..  39
        81:02 06 0A 0E 12 16 1A 1E ;  40 ..  47
        89:03 07 0B 0F 13 17 1B 1F ;  48 ..  55
        91:03 07 0B 0F 13 17 1B 1F ;  56 ..  63

        99:00 04 08 0C 10 14 18 1C ;  64 ..  71
        A1:00 04 08 0C 10 14 18 1C ;  72 ..  79
        A9:01 05 09 0D 11 15 19 1D ;  80 ..  87
        B1:01 05 09 0D 11 15 19 1D ;  88 ..  95
        B9:02 06 0A 0E 12 16 1A 1E ;  96 .. 103
        C1:02 06 0A 0E 12 16 1A 1E ; 104 .. 111
        C9:03 07 0B 0F 13 17 1B 1F ; 112 .. 119
        D1:03 07 0B 0F 13 17 1B 1F ; 120 .. 127

        D9:00 04 08 0C 10 14 18 1C ; 128 .. 135
        E1:00 04 08 0C 10 14 18 1C ; 136 .. 143
        E9:01 05 09 0D 11 15 19 1D ; 144 .. 151
        F1:01 05 09 0D 11 15 19 1D ; 152 .. 159
        F9:02 06 0A 0E 12 16 1A    ; 160 .. 166 *** spans sector
T1CS4 $7400
    00:                     1E ;        167 *** spans sector
    01:02 06 0A 0E 12 16 1A 1E ; 168 .. 175
    09:03 07 0B 0F 13 17 1B 1F ; 176 .. 183
    11:03 07 0B 0F 13 17 1B 1F ; 184 .. 191

    LC:$FEC0:

    LC:$FEDB
        FEDD: BE C0 FE  LDX $FEC0,Y
        FEE0: BD 00 FB  LDA $FB00,X
        FEE3: 10 02     BPL $FEE7
        FEE5: A9 13     LDA #$13
        FEE7: 29 3F     AND #$3F
        FEE9: D0 02     BNE $FEED
        FEEB: A9 20     LDA #$20
        FEED: 99 00 04  STA $0400,Y
        FEF0: 88        DEY
        FEF1: 10 EA     BPL $FEDD
        FEF3: 60        RTS
  • T1CS9 $7900 @74 Main menu: File, and Edit
    • Note: K = Next key frame
        33: Pointer $DCF2
        35: Pointer $DCF3
        37: Pointer $0700
        39: Pointer $DCF7
        3B: Pointer $DCFB
        3D: Pointer $1C08
        3F: Pointer $DD71
        41: Pointer $DD75
        43: Pointer $4606
        45: Pointer $DDA5
        47: Pointer $DDAC
        49: Pointer $7005
        4B: Pointer $3FC0 TODO: HGR Screen Hole

        50: "File" MENU
        54: "L" @ $DD1B accelerator = File | Load Movie
        58: "S" @ $DD25 accelerator = File | Save Movie
        5C:0x18 @ $DD2F accelerator = File | Clear Movie (Ctrl-X), can use Y/N
        60: "D" @ $DD3A accelerator = File | Load Backdrop
        64: "B" @ $DD47 accelerator = File | Save Backdrop -> "Screen name:"
        68: "W" @ $DD54 accelerator = File | Clear Backdrop
        6C: "F" @ $DD62 accelerator = File | Format Disk
        70: "Q" @ $DD6D accelerator = File | Quit
        74: "Load Movie"
            "Save Movie"
            "Clear Movie"
            "Load Backdrop"
            "Save Backdrop"
            "Clear Backdrop"
            "Format Disk"
            "Quit"
            "Edit" MENU
        CE: "Z" @ $DD8D accelerator = Edit | Undo
        D2: "X" @ $DD91 accelerator = Edit | Cut
        D6: "C" @ $DD94 accelerator = Edit | Copy
        DA: "V" @ $DD98 accelerator = Edit | Paste
        DE: "K" @ $DD9D accelerator = Edit | Clone
        E2: "^" @ $DDA2 accelerator = Edit | Zap frame (Shift-6)
        E6: "Undo"
            "Cut"
            "Copy"
            "Paste"
            "Clone"
            "Zap"
            "Go" MENU
  • T1CSA $7A00 Main menu: Goodies
        00: "oodies"
        05:0x1A @ $DDC0 accelerator = Goodies | Zoom   (Ctrl-Z), top menu: Right/Left  Left/Right
        09:0x14 @ $DDC4 accelerator = Goodies | Turn   (Ctrl-T), top menu: Counter-Clockwise / Clockwise
        0D:0x0C @ $DDC8 accelerator = Goodies | Lean   (Ctrl-L), top menu: Left/Right
        11:0x06 @ $DDCC accelerator = Goodies | Flip   (Ctrl-F), top menu: Left/Right  Up/Down
        15:0x13 @ $DDD0 accelerator = Goodies | Squash (Ctrl-S), top menu: Up / Down"
        19: "Zoom"
            "Turn"
            "Lean"
            "Flip"
            "Squash"

Easter Eggs

Source Code

There are partial copies of the source code left on the disk! Track $19, Sectors 4 and 3 have these gems, respectively:

  • Track $19, Sector 4
pag


 DFB DialA+2
 DA  Mess9
 hex FE  ;yes/no
 hex 15  ;HTAB 4

 DFB DialA+2
 DA  Mess10
 h
  • Track $19, Sector 3
a

Dialog0
 DFB MenuA ;area
 DA Mess0
 DFB 0 ;input len
 hex 14 ;HTab 1*4,Vtab 4;
 DFB DialA
 DA Mess1
 DFB 0
 hex 14

 DFB BDrpA
 DA Mess2
 DFB 0
 hex 4C

 DFB DialA+2
 DA Mess3
 hex FF ;get button
 hex 44 ;H16,V4

 DFB DialA+2
 DA Mess4
 hex FF
 hex 14

Original Files Names

Track $1F, Sector $6 has this gem of a build script!

    600.160F <Ctrl-Y>
    ...
    BLOAD O:FLB000   ,A$1000
    BLOAD O:RDB500   ,A$1500
    BLOAD O:RWTS1800 ,A$1800
    1000<1500.150F <Ctrl-Y>
    ...
    BLOAD FANTAVISION,$1000
    41D6:18 60
    BLOAD F.PATCH,A$306C
    1000<1700.1F0F <Ctrl-Y>
    ...
    BLOAD O:BACKUP,A$2000
    2000<2000.200F <Ctrl-Y>
    ...<Ctrl-C> DONE <Ctrl-G>
    FP
    @
    BLOAD FANTA

NOTES:

  • There is no space after the ####.####F but I included one to make the Control characters easier to read

Here is a quick analysis on the filenames:

            Filename: FL = Fantavision Loader ???
            vv
    BLOAD O:FLB000,A$1000
             $B000
              ^^^^
              Load address

            Filename: RD = Read Disk ??? Roland's Disk protection???
            vv
    BLOAD O:RDB500,A$1500
             $B500
              ^^^^
              Load address
    BLOAD O:RWTS1800,A$1800

Easter Egg: Deleted Files!?

If we boot Copy ][+ 8.0 and choose UNDELETE FILES we find there are 4 deleted files on our Fanta.COPYA !

    M.APPLE2       BIN   4  <NO DATE>
    M.SHADOWTRIXKS BIN   8  <NO DATE>
    M.MORFIC       BIN   5  <NO DATE>
    M.VOLCANO      BIN   5  <NO DATE>

In Search of a Better Beep, or two

Some of us Apple fans take our BEEP pretty serious.

Fantavision has a nice "soft beep" instead of the classic "hard beep"

                            F8.Wait  = $FCA8
                            Squeeker = $C030 ; Technically it is a speaker but who are we kidding here. This is no SID chip.

                            ORG $0902
                    SoftBeep
    0902:A0 20              LDY #$20
                    SoftCycle
    0904:A9 02              LDA #$02        ;+
    0906:20 A8 FC           JSR F8.Wait
    0909:8D 30 C0           STA Squeeker

    090C:A9 24              LDA #$24
    090E:20 A8 FC           JSR F8.Wait
    0911:8D 30 C0           STA Squeeker

    0914:88                 DEY
    0915:D0 ED              BNE SoftCycle   ;^ $0904
    0917:60                 RTS

Fantavision also has Roland Gustafsson classic 'RW18' error "ZAP" @ $B380. (Track $15, Sector $3)

                    RWTS_Read16:
    B333:A2 0F              LDX #$0F            ; 16 sectors to load
                                                ; ...
    B34B:F0 31              BEQ ReadError       ; if failed to read disk ...

                    ReadError:
    B37E:38                 SEC
    B37F:EA                 NOP                 ; *** SELF-MODIFIED to be RTS $60
    B380:A0 00              LDY #$00            ; Br0derbund "ZAP" sound
                    ^1
    B382:AD 30 C0           LDA $C030
    B385:98                 TYA
                    ^2
    B386:38                 SEC
    B387:E9 01              SBC #$01
    B389:D0 FB              BNE ^2              ;^ $B386
    B38B:88                 DEY
    B38C:D0 F4              BNE ^1              ;^ $B382
    B38E:4C 33 B3           JMP RWTS_Read16     ;^ $B333