pics | ||
README.md |
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 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
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.
-
Boot your favorite "Fast" DOS such as ProntoDOS or Diversi-DOS.
-
Replace the disk with a blank floppy.
-
Enter these lines:
NEW
10 ?CHR$(4);"CATALOG"
INIT HELLO
- 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:
What we want is this picture:
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
-
If you computer isn't already on, turn it on and press
CTRl-RESET
-
Insert the original Fantavision disk in Slot 6, Drive 1
-
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
, andRead 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.B7FFM
Boot to our Fanta.Work
disk ...
C500G
... so we can save it:
BSAVE B2.FANTAVISION_T15_B000,A$4000,L$800
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
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
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
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 $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.
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 !
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
-
Boot our
Fanta.Work
disk -
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
- And save it:
BSAVE GET_LOGO_1,A$1EEE,L$46
- Replace Slot 6, Drive 1 disk with the original Fantavision. And now for the moment of truth ...
1EEEG
C050 C052 C054 C057
- 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
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 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
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.
- The Z-Machine was created in 1979.
-
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. :)
PR#5
to boot ourFanta.Work
DiskBLOAD GET_LOGO_1
CALL-151
1F0D:20
1F14:21
BSAVE GET_BACKUP_T20_T21,A$1EEE,L$46
- Replace your Fantavision disk in slot 6
1EEEG
to boot Fantavision- Replace your Fanta-work disk in slot 5
C500G
to boot Fanta-workCALL-151
800<2000.27FFM
BSAVE B5.BACKUP_T20_0800,A$800,L$800
800<3000.37FFM
BSAVE B5.BACKUP_T21_0800,A$800,L$800
BSAVE LOGO_3_T20,A$2800,L$800
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?!
- Boot our
Fanta.Work
disk BLOAD GET_LOGO_1
CALL-151
1F0D:16
BSAVE GET_APPLESOFT_MENU,A$1EEE,L$46
- Replace your Fantavision disk in slot 6
1EEEG
- Replace your Fanta-work disk in slot 5
C500G
NEW
CALL-151
NOTE: We DON'T want67:0
to change start of Applesoft from $801 to $800800<2000.2FFFM
BSAVE B6.FANTASOFT.BAS_T16_0800,A$800,L$1000
<CTRL-C>
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 | |
$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.
- Boot a DOS 3.3 Master disk
LOAD COPYA
- 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
- Grab a blank disk, label it
Fanta.COPYA
- Put your original
Fantavision
disk in Drive 1 - Put your blank
Fanta.COPYA
disk in Drive 2 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
Disk Usage Details
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
; Disk Nibbles: Volume, Track, Sector, Checksum
CHECKSUM = $E1
HAVE_SECTOR = $E3
T15S1 $B100 @ $B126:read sector prologue D5 AA 96 [xx yy zz] DE
T15S2 $B200 Optimized 6&2 Decode Table
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
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