# 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? * ProDOS Hybrid!? * ProDOS Block Review * Disk Usage Details * Disk Usage Summary * 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](https://en.wikipedia.org/wiki/Adobe_Flash) became ubiquitous back in 1985 [Brøderbund](https://en.wikipedia.org/wiki/Broderbund) released a fantastic animation program called `Fantavision` * ![Logo](pics/logo.png) * 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 ``` 4. 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](pics/backup_used.png) What we want is this picture: * ![Backup available](pics/backup_available.png) 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 9600LoadBoot3 ; 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: ```asm 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. ```asm 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, ```asm 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: ```asm 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 4000Physical Sectors ; ProDOS order?? TODO 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 rwts_Logical2Physical_B ; Map Logical->Physical Sectors ; DOS order?? TODO 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 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! rwts_SectorDestAddr: B3D9:00 00 00 00 B3DD:00 00 00 00 B3E1:00 00 00 00 B3E5:00 00 00 00 rwts_DestAddrLow: B3E9:00 DFB $00 ; ==================== ; Y = Track ; JSR RWTS_B003 ; DFB Load Address 0 ; DFB Load Address 1 ; DFB Load Address 2 ; DFB Load Address 3 ; DFB Load Address 4 ; DFB Load Address 5 ; DFB Load Address 6 ; DFB Load Address 7 ; DFB Load Address 8 ; DFB Load Address 9 ; DFB Load Address A ; DFB Load Address B ; DFB Load Address C ; DFB Load Address D ; DFB Load Address E ; DFB Load Address F ; ==================== Do_LoadCode: B3ED:84 ED STY rwts_ED B3EF:68 PLA B3F0:85 EB STA rwts_Return B3F2:68 PLA B3F3:85 EC STA rwts_Return+1 B3F5:A2 00 LDX #$00 RWTS_Load B3F7:20 0B B4 JSR NextByte B3FA:9D C9 B3 STA $B3C9,X B3FD:E8 INX B3FE:E0 10 CPX #$10 B400:90 F5 BCC ;^ $B3F7 B402:A5 EC LDA rwts_Return B404:48 PHA B405:A5 EB LDA rwts_Return+1 B407:48 PHA B408:4C 33 B3 JMP RWTS_Read16 ;^ $B333 NextByte: 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 ; ==================== ; ; ==================== B451: 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 ``` # Boot Tracing Stage 2 Once the entire track $15 of Stage 2 is read in it is executed to read Stage 3. ```asm 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 ... ```asm 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 $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](pics/logo_blackbag.png) 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: ```asm 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](pics/backup_blackbag.png) 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: ```asm 087E:4C 00 BE JMP ExecBoot3 ``` This is the disassembly: ```asm 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 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. ```asm ; 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. ```asm 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) ```asm 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: ```asm 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 3. And save it: ``` BSAVE GET_LOGO_1,A$1EEE,L$46 ``` 4. Replace Slot 6, Drive 1 disk with the original Fantavision. And now for the moment of truth ... ```asm 1EEEG C050 C052 C054 C057 ``` 5. When you are done viewing ... ```asm 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](pics/logo_2.png) 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](pics/logo.png) ## 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](pics/logos_34.png) 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: ```asm 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 '``, 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: ```asm 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_SLOT16 ; $2B -> $FD 091A:BD 89 C0 LDA $C089,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 ; TODO: CLEANUP 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_MOTON_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 $FD ; 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 ```asm ``` # Applesoft?! 1. Boot our `Fanta.Work` disk 2. `BLOAD GET_LOGO_1` 3. `CALL-151` ```asm 1F0D:16 BSAVE GET_APPLESOFT_MENU,A$1EEE,L$46 ``` 4. Replace your Fantavision disk in slot 6 5. `1EEEG` 6. Replace your Fanta-work disk in slot 5 7. `C500G` 8. `NEW` 9. `CALL-151` NOTE: We DON'T want `67:0` to change start of Applesoft from $801 to $800 10. `800<2000.2FFFM` 11. `BSAVE B6.FANTASOFT.BAS_T16_0800,A$800,L$1000` 12. `` 13. `LIST` ```Basic 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. ```asm 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: ```asm 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](http://www.callapple.org/vintage-apple-computers/apple-ii/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 ```asm 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: ```asm 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: ```asm 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. ## 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 M.CANCAN BIN 4 M.TREE BIN 5 M.OBJECTS BIN 3 M.DIVE BIN 4 M.FACE BIN 3 M.PEOPLE BIN 4 M.REDWHITEBLUE BIN 3 M.ENGLISHFONT BIN 10 M.SLANTFONT BIN 4 M.ORBITFONT BIN 4 M.DOTS2FONT BIN 6 M.CIRCUITFONT BIN 5 M.DOTSFONT BIN 6 M.OUTLINEFONT BIN 8 M.LAND BIN 3 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 ```asm 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| /FANTAVISION/M.SPIDER #0400 |T02S1| |T02S2| |T02S3| |T02S4| /FANTAVISION/M.CANCAN #0700, Zero |T02S5| /FANTAVISION/M.CANCAN #0600 |T02S6| /FANTAVISION/M.CANCAN #0500 |T02S7| /FANTAVISION/M.CANCAN #0400 |T02S8| /FANTAVISION/M.CANCAN #0300, Zero |T02S9| /FANTAVISION/M.CANCAN #0200 |T02SA| /FANTAVISION/M.CANCAN #0100 |T02SB| /FANTAVISION/M.CANCAN #0000, 0000:03 03 04 04 ... |T02SC| /FANTAVISION/M.SPIDER #0700 |T02SD| /FANTAVISION/M.SPIDER #0600 |T02SE| /FANTAVISION/M.SPIDER #0500 |T02SF| /FANTAVISION/M.TREE #????? Zero |T03S0| |T03S1| |T03S2| |T03S3| |T03S4| |T03S5| |T03S6| Zero |T03S7| |T03S8| |T03S9| |T03SA| |T03SB| |T03SC| |T03SD| |T03SE| |T03SF| Zero T04S4 Zero T04S6 Zero T04S8 Zero T05S8 Zero T05SC Zero T05SF Zero T06SC Zero T06SE Zero T07S8 Zero T07SF Zero T08Sx Mirror of Track $1A T09Sx Mirror of Track $19 T0ASx Mirror of Track $18 @ $3000 T0BSx Mirror of Track $17 @ $2000 T0CSx ??? T0CSE Zero T0DS0 00:69, remaining Zero T0FSx Blank / Not used T10Sx Blank / Not used T11Sx Blank / Not used T12S0 $???? T13S0 $???? T14S0 $???? ``` T15S0 $B000 RWTS ``` NOTES: * $02AA .. $02FF used as disk nibbles decode buffer for $56 bytes B000: JMP $B451 ; RWTS_ReadTrack ; Y = Track, A = Addr B003: JMP $B3ED ; RWTS_ ; TODO B006: JMP $B422 ; RWTS_ ; TODO B009: JMP $B0E9 ; RWTS_Seek ; A = Track B00C: JMP $B462 B00F: SEC RTS ; Error B051: CMP #$D5 B05B: CMP #$AA B065: CMP #$AD B069: Decode #$56 nibbles into $2AA..$2FF B0DC: CMP #$DE B0E9: CHECKSUM = $E1 HALF_TRACK_PREV = $EC (mirror of $FF) HALF_TRACK_WANT = $EB HALF_TRACK_HAVE = $FF ; TODO: FIXME: RWTS_HALFTRACK ``` T15S1 $B100 @ $B126:read sector prologue D5 AA 96 [xx yy zz] DE ``` B136:CMP #$D5 B140:CMP #$AA B14B:CMP #$96 B172:CMP #$DE 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 ; -- -- -- -- -- -- 96 97 B198:98 99 08 0C 9C 10 14 18 ; -- -- 9A 9B -- 9D 9E 9F B1A0:A0 A1 A2 A3 A4 A5 1C 20 ; -- -- -- -- -- -- A6 A7 B1A8:A8 A9 AA 24 28 2C 30 34 ; -- -- -- AB AC AD AE AF B1B0:B0 B1 38 3C 40 44 48 4C ; -- -- B2 B3 B4 B5 B6 B7 B1B8:B8 50 54 58 5C 60 64 68 ; -- B9 BA BB BC BD BE BF B1C0:C0 C1 C2 C3 C4 C5 C6 C7 ; -- -- -- -- -- -- -- -- B1C8:C8 C9 CA 6C CC 70 74 78 ; -- -- -- CB -- CD CE CF B1D0:D0 D1 D2 7C D4 D5 80 84 ; -- -- -- D3 -- -- D6 D7 B1D8:D8 88 8C 90 94 98 9C A0 ; -- D9 DA DB DC DD DE DF B1E0:E0 E1 E2 E3 E4 A4 A8 AC ; -- -- -- -- -- E5 E6 E7 B1E8:E8 B0 B4 B8 B8 C0 C4 C8 ; -- E9 EA EB EC ED EE EF B1F0:F0 F1 CC D0 D4 D8 DC E0 ; -- -- F2 F3 F4 F5 F6 F7 B1F8:F8 E4 E8 EC F0 F4 F8 FC ; -- F9 FA FB FC FD FE FF T15S2 $B200 Optimized 6&2 Decode Table 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 ; [00] B204:02 00 00 97 ; [01] B208:01 00 00 9A ; [02] B20C:03 00 00 9B ; [03] B210:00 02 00 9D ; [04] B214:02 02 00 9E ; [05] B218:01 02 00 9F ; [06] B21C:03 02 00 A6 ; [07] B220:00 01 00 A7 ; [08] B224:02 01 00 AB ; [09] B228:01 01 00 AC ; [0A] B22C:03 01 00 AD ; [0B] B230:00 03 00 AE ; [0C] B234:02 03 00 AF ; [0D] B238:01 03 00 B2 ; [0E] B23C:03 03 00 B3 ; [0F] B240:00 00 02 B4 ; [10] B244:02 00 02 B5 ; [11] B248:01 00 02 B6 ; [12] B24C:03 00 02 B7 ; [13] B250:00 02 02 B9 ; [14] B254:02 02 02 BA ; [15] B258:01 02 02 BB ; [16] B25C:03 02 02 BC ; [17] B260:00 01 02 BD ; [18] B264:02 01 02 BE ; [19] B268:01 01 02 BF ; [1A] B26C:03 01 02 CB ; [1B] B270:00 03 02 CD ; [1C] B274:02 03 02 CE ; [1D] B278:01 03 02 CF ; [1E] B27C:03 03 02 D3 ; [1F] B280:00 00 01 D6 ; [20] B284:02 00 01 D7 ; [21] B288:01 00 01 D9 ; [22] B28C:03 00 01 DA ; [23] B290:00 02 01 DB ; [24] B294:02 02 01 DC ; [25] B298:01 02 01 DD ; [26] B29C:03 02 01 DE ; [27] B2A0:00 01 01 DF ; [28] B2A4:02 01 01 E5 ; [29] B2A8:01 01 01 E6 ; [2A] B2AC:03 01 01 E7 ; [2B] B2B0:00 03 01 E9 ; [2C] B2B4:02 03 01 EA ; [2D] B2B8:01 03 01 EB ; [2E] B2BC:03 03 01 EC ; [2F] B2C0:00 00 03 ED ; [30] B2C4:02 00 03 EE ; [31] B2C8:01 00 03 EF ; [32] B2CC:03 00 03 F2 ; [33] B2D0:00 02 03 F3 ; [34] B2D4:02 02 03 F4 ; [35] B2D8:01 02 03 F5 ; [36] B2DC:03 02 03 F6 ; [37] B2E0:00 01 03 F7 ; [38] B2E4:02 01 03 F9 ; [39] B2E8:01 01 03 FA ; [3A] B2EC:03 01 03 FB ; [3B] B2F0:00 03 03 FC ; [3C] B2F4:02 03 03 FD ; [3D] B2F8:01 03 03 FE ; [3E] B2FC:03 03 03 FF ; [3F] T15S3 $B300 T15S4 $B400 T15S5 $B500 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 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 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 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 T1CS5 $7500 T1CS6 $7600 T1CS7 $7700 T1CS8 $7800 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" 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 sector of 4 pages. # 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 ```Assembly pag DFB DialA+2 DA Mess9 hex FE ;yes/no hex 15 ;HTAB 4 DFB DialA+2 DA Mess10 h ``` * Track $19, Sector 3 ```Assembly 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 ... BLOAD O:FLB000 ,A$1000 BLOAD O:RDB500 ,A$1500 BLOAD O:RWTS1800 ,A$1800 1000<1500.150F ... BLOAD FANTAVISION,$1000 41D6:18 60 BLOAD F.PATCH,A$306C 1000<1700.1F0F ... BLOAD O:BACKUP,A$2000 2000<2000.200F ... DONE 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 M.SHADOWTRIXKS BIN 8 M.MORFIC BIN 5 M.VOLCANO BIN 5 ``` # In Search of a Better Beep, or two Some of us Apple fans take our BEEP pretty serious. * [Apple IIc Plus – Fixing the Beep](http://quinndunki.com/blondihacks/?p=2471) Fantavision has a nice "soft beep" instead of the classic "hard beep" ```asm 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) ```asm Read16Sec: B333:A2 0F LDX #$0F ; 16 sectors to load from track ; ... 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 $B333 ;^ $B333 ```