;###############################################################################
;#
;# T642CRT - Convert T64 into Magic Desk Cartridges
;# Concept & Code: docbobo, Bugfixes and other contributions: TheJoker
;#
;# Please check www.dreisechzig.net/t64 for full instructions
;#
;# Feel free to use and improve, kindly notify me at boris@dreisechzig.net
;#
;# V6, December 2025
;# - HOMEBANK (Patchable after the fact at $8009 for MultiDesk, www.dreisechzig.net/multidesk)
;# - decreased resident RAM loader size by moving more code to ROM
;# - more flags for compile time, autobooting games with less error checking have smaller loader
;# - correct end adress settings
;# - no warnings during compile
;#
;###############################################################################

!TO "t64loader.bin", plain

; For Autobooting games you can set "EnableDirectory" to 0. This brings the binary to < 512 bytes
; RAMSTART usually is the tape buffer, some games require it at a different location

RAMSTART        = $0200         ; the RAM part of the loader goes here ($033C - Tape Buffer)
                
ENABLEDIRECTORY = 0             ; 1 = Enable directory listing ($) and BASIC Boot, switch off for Autoload
OBEYSECONDARY   = 0             ; 1 = If a secondary adress has been set, use instead of adress in PRG file
IGNORESAVE      = 0             ; 1 = Makes the SAVE vector do nothing to capture saves
FLICKER         = 0             ; 1 = Border Flicker every 256 transfered bytes.
HOMEBANK        = 0             ; Set the Bank number during Compile time. You also binary patch $8009 in first bank later

; Zeropage Variables (V1 Names)
FILENAME        = $bb           ; Pointer to filename for the LOAD, will be overwritten to pointer into or directory
ZPDIRENTRY      = $bb           ; pointer to our directory entry after we finished the search
ZPDIRSEARCH     = $02           ; BEFORE actual load re-use for directory search                
ZPNAMELENGTH    = $b7           ; length of filename
ZPTEMP          = ZPDIRSEARCH   ; Temp variable (reused)
ZPENDOFLOAD     = $ae           ; end of load adress, needs to be set for some programs after load

!IF OBEYSECONDARY = 1 {
ZPSECONDADDR    = $b9           ;Secondary Adress, use $c3 if not Zero
ZPLOADADRESS2   = $c3           ;adress to load to when secondary is 0 ($0801 or set by code)
}

; Vectors
LOADVECTOR      = $0330
!IF IGNORESAVE = 1 {
SAVEVECTOR      = $0332
}

; KERNAL Routines
IOINIT          = $FF84
RESTOR          = $FF8A
SETBOTTOMMEM    = $FD8C         ; to skip full RAM test but initialise memory pointers
INITBASIC       = $E453         ; initialises BASIC vectors
INITBASICRAM    = $E3BF         ; initialises BASIC RAM
INITSCREEN      = $E51B         ; init Screen without setting VIC registers
BASICBASIC      = $A7AE         ; Back to the Basics :-)
FILENOTFOUND    = $F704         ; File Not Found Error
!IF ENABLEDIRECTORY = 1 {
STROUT          = $AB1E         ; output text string
}
  
; Needed offsets in the T64 Directory
DIRECTORYSTART  = TAPESTART +$40 
NUMBEROFFILES   = TAPESTART +$22 ; Number of directory entries
OFFSETLOADADDR  = $02           ; Where to load the file
OFFSETENDADDR   = $04           ; End-address
OFFSETFILENAME  = $10           ; Filename starts here
OFFSETT64       = $08           ; Start of ROM in file

* = $8000                       ; ROM starts at $8000

!word COLDSTART
!word WARMSTART                 ; points to Loader Install

; Cartridge Signature
!byte $c3,$c2,$cd,$38,$30

; --------------------------------------------------------------
; PATCHABLE DATA BYTE: START BANK
; --------------------------------------------------------------
HOMEBANK_ROM: !byte HOMEBANK  ; Address $8009. Patch this byte!
; --------------------------------------------------------------

!IF ENABLEDIRECTORY {
TEXTSTRING:     !text "T64CRT"    
                !byte $00
}

COLDSTART:      sei
                ldx #$ff
                txs                                 
                cld                                 
                
                lda #8                              ; Enable VIC
                sta $d016
                
                lda #$0                             ; Clear RAM $0002-$03FF
                tax
-               sta $0002, x                        
                sta $0100, x
                sta $0200, x
                sta $0300, x
                dex
                bne -

                lda #$37                            ; Init memory map
                sta $01
                lda #$2f                            ; Init DDR
                sta $00
                
                jsr IOINIT                          ; Init I/O & CIA
                                   
                ldy #$a0
                jsr SETBOTTOMMEM                       
                jsr INITBASIC                          
                jsr INITBASICRAM                           
                jsr RESTOR                           
                jsr INITSCREEN                           
                
                lda #$69                            ; patch BRK to not restore LOAD
                sta $0316

                jsr COPYLOADER                      ; Installs the resident RAM portion of the loader
  
!IF ENABLEDIRECTORY {
                lda #<TEXTSTRING
                ldy #>TEXTSTRING
                jsr STROUT                          ; Print "T64CRT" message when Directory function active
}

                lda DIRECTORYSTART + OFFSETFILENAME ; Check for Autostart '!' (first file only)
                cmp #$21                            
                beq AUTOSTART               

                jmp EXIT                            ; No Autostart? Then we can go back to Basic!

; -----------------------------------------------------------------
; AUTOSTART for first file named "!1234" where 1234 is a JMP adress
; -----------------------------------------------------------------

AUTOSTART
                ; --- Autostart Parsing (4 Petsci to address) ---
                
                ldy #3                              ; Parse Low Byte
                jsr PETSCIITOBYTE_HEX
                sta JUMPAWAY + 1
                
                ldy #1                              ; Parse High Byte
                jsr PETSCIITOBYTE_HEX
                sta JUMPAWAY + 2
                
                lda #$00                            ; Filename length Zero tells Loader we are in Autostart
                sta ZPNAMELENGTH
                
                jmp ($0330)                         ; Launch Loader (LOADVECTOR)

; Helper: Reads 2 PETSCII chars at Y and Y+1, converts to 1 Hex Byte in A
PETSCIITOBYTE_HEX: 
                lda DIRECTORYSTART + OFFSETFILENAME, Y
                jsr PETSCIITONIBBLE
                asl
                asl
                asl
                asl
                sta ZPTEMP
                lda DIRECTORYSTART + OFFSETFILENAME + 1, Y
                jsr PETSCIITONIBBLE
                ora ZPTEMP
                rts

PETSCIITONIBBLE: sec                                ; Convert PETSCII '0'-'F' to $0-$F
                sbc #$30
                cmp #$0a
                bcc +
                sbc #$07
+               rts

;----------------------------------------------------------------------
; WARMSTART - if you need to re-init the Loader for whatever reason
; Cartridge Warmstart Vector points here.
;----------------------------------------------------------------------
WARMSTART:

COPYLOADER:     ldy #ENDOFLOADER - 1 - RAMSTART
-               lda LOADERROMADDR,Y
                sta RAMSTART,Y
                dey
                bpl -

                ; Make the loader know which bank is the "home" bank (for MultiDesk)
                lda HOMEBANK_ROM
                sta HOMEBANK_RAM

                ; Set the LOAD vector to the start routine in RAM
                lda #<RAMSTART                  
                sta LOADVECTOR
                lda #>RAMSTART
                sta LOADVECTOR+1
                
!IF IGNORESAVE = 1 {
                lda #<JUMPAWAY                  
                sta SAVEVECTOR
                lda #>JUMPAWAY
                sta SAVEVECTOR+1
}                
                rts
                
;########################################################################################
; ROM LOADER (everything that is not self modifying)
;########################################################################################

LOADERINROM:        ldy ZPNAMELENGTH                    ; Name length check - if Zero we are in Autostart!
                bne NOAUTOSTART
                lda #"!"
                inc ZPNAMELENGTH
                bne STOREFILENAMECOPY
                
NOAUTOSTART:                
                lda #$60                            ; Patch the Autostart/Basic Jump to become an RTS
                sta JUMPAWAY                 
                lda FILENAMECOPY                    ; Check the first letter for $ or *
                
!IF ENABLEDIRECTORY {
                cmp #"$"                            ; Directory requested?
                beq DIRPRINT
}
                cmp #"*"                            ; Wildcard?
                bne +
                lda DIRECTORYSTART + OFFSETFILENAME ; Reget the first letter of the first filename

STOREFILENAMECOPY:
                sta FILENAMECOPY                    ; Now we have 1 letter filename that matches either ! or first file
+

;----------------------------------------------------------
; Abbreviated Directory Printer (only first page, 7 files)
;----------------------------------------------------------

!IF ENABLEDIRECTORY {
                bne NOTDIR                          ; Moved here because not necessary outside
DIRPRINT:       ldy #$00
LOOPDIR:        ldx #$00
-               lda DIRECTORYSTART + OFFSETFILENAME,Y
                sta DIRECTORYSTRING,X
                inx
                iny
                tya
                cmp #$e0                            ; Hard check to stay in the first page of the directory
                bcs DIRDONE
                pha
                txa 
                cmp #$0f
                bne -
                lda #<DIRECTORYSTRING
                ldy #>DIRECTORYSTRING
                jsr STROUT                          ; PRINTSTRING
                pla
                clc
                adc #$11
                tay
                bne LOOPDIR
DIRDONE:        jmp BASICBASIC                      ; Back to BASIC
NOTDIR:
}

;---------------------------------------------------------
; Search for the file in the directory
;---------------------------------------------------------

                ldx NUMBEROFFILES                   
                lda #>DIRECTORYSTART
                sta ZPDIRSEARCH+1
                lda #<DIRECTORYSTART
                clc
                adc #$10
                sta ZPDIRSEARCH
                bcc +
                inc ZPDIRSEARCH+1
+ 
NAMECHECK:      ldy ZPNAMELENGTH             
                dey
-               lda (ZPDIRSEARCH), Y
                cmp FILENAMECOPY, Y
                bne NEXTFILE
                dey
                bpl -
                jmp FOUNDFILE
  
NEXTFILE:       
                dex                                 
                beq FILENOTFOUNDEXIT                
                
                lda ZPDIRSEARCH
                clc
                adc #$20
                sta ZPDIRSEARCH
                bcc +
                inc ZPDIRSEARCH+1
+               jmp NAMECHECK

FILENOTFOUNDEXIT:
                lda #$37                            ; Turn on Kernal/Basic
                sta $01
                lda #$80                            ; Disable MagicDesk
                sta $DE00    
                jmp FILENOTFOUND                    ; KERNALFILENOTFOUND

;--------------------------------------------------------------
; Found File, now init start and end adress and position in ROM
;--------------------------------------------------------------
  
FOUNDFILE:      lda ZPDIRSEARCH     
                sec 
                sbc #$10
                sta ZPDIRENTRY
                lda ZPDIRSEARCH + 1
                sbc #$00
                sta ZPDIRENTRY + 1

                ldy #OFFSETLOADADDR       
                lda (ZPDIRENTRY), Y
                sta TORAM + 1
                iny
                lda (ZPDIRENTRY), Y
                sta TORAM + 2

                ldy #OFFSETENDADDR        
                lda (ZPDIRENTRY), Y
                sta ZPENDOFLOAD
                iny
                lda (ZPDIRENTRY), Y
                sta ZPENDOFLOAD + 1

!IF OBEYSECONDARY = 1 {
; if secondary adress is set, we need to change start and end adress
; we calculate the new end adress first

  LDA ZPSECONDADDR
  BEQ GETSTART   
  
  SEC
  LDA ZPENDOFLOAD
  SBC TORAM + 1
  TAX
  LDA ZPENDOFLOAD + 1
  SBC TORAM + 2
  TAY
  TXA
  CLC
  ADC ZPLOADADRESS2
  STA ZPENDOFLOAD
  TYA
  ADC ZPLOADADRESS2 + 1
  STA ZPENDOFLOAD + 1
  
; now we replace the load adress with the one given in by kernal/call
  LDA ZPLOADADRESS2
  STA TORAM + 1
  LDA ZPLOADADRESS2 + 1
  STA TORAM + 2  
  
GETSTART:
} 

; --- Get Start Adress (and Bank) of file in ROM ---

                ldy #OFFSETT64                    
                lda (ZPDIRENTRY), Y
                sta FROMCRT + 1    

                iny                             
                lda (ZPDIRENTRY), Y
                clc
                adc #>(TAPESTART - $8000)       ; Loader Size Offset
                pha                             

                ror                             ; Division / 32 with carry
                lsr
                lsr
                lsr
                lsr

                clc
                sta ZPTEMP                     
                
                pla                             

                and #$1f
                ora #$80
                sta FROMCRT + 2             

                iny                             
                lda (ZPDIRENTRY), Y
                asl                             
                asl
                asl

                clc                             
                adc ZPTEMP                     
                adc HOMEBANK_RAM                ; MultiDesk: Add Home Bank
                tax                             

                rts                             ; This RTS jumps back into the RAM Loader (used to be a JMP)

;########################################################################################
; RAM LOADER
;########################################################################################

LOADERROMADDR:  !PSEUDOPC RAMSTART {

                php
                pla
                and #$ee
                pha                     ; Safe Flags
                
                sei
                lda $01                 ; Safe $01
                pha

                ldy ZPNAMELENGTH
                beq +
-               lda (FILENAME), y                   ; Copy filename to RAM
                sta FILENAMECOPY, y
                dey
                bpl -                     

+               lda #$37
                sta $01
                
                lda HOMEBANK_RAM        ; Multidesk: Get Home Bank
                sta $de00               ; Force switch to Start Bank
                
                jsr LOADERINROM             ; do stuff that can be done in ROM, receive Bank in X
                
                stx $de00               ; Switch to Calculated Bank for data copy      
COPYLOOP:           
FROMCRT:        lda $8000               ; Self-modifying Read

                inc $01                 ; $37 to $38
TORAM:          sta $0801               ; Self-modifying Write
                dec $01                 ; $38 to $37

                inc FROMCRT + 1     
                bne +

!IF FLICKER = 1 {
                inc $d020               ; Border Color every 256 bytes
}

                inc FROMCRT + 2

; Check if we need to switch banks

                lda FROMCRT + 2         ; Check end of 8k bank
                cmp #$a0
                bne +

; Switch banks and go back to $8000
                                    
                inx                     ; Next Bank
                stx $de00
                
                lda #$80                ; Reset Read Ptr to $8000
                sta FROMCRT + 2

; Increase write adress and check for end address
+               inc TORAM + 1    
                bne +
                inc TORAM + 2

+               lda TORAM + 1    
                cmp ZPENDOFLOAD 
                bne COPYLOOP
                lda TORAM + 2
                cmp ZPENDOFLOAD + 1             
                bne COPYLOOP

; All bytes copied

EXIT:                                   ; Can be called to exit to BASIC when nothing was loaded
                lda #$80                ; Cart Off
                sta $DE00    
                pla
                sta $01                 
                plp                     
JUMPAWAY:       jmp BASICBASIC          ; Initial: Jump into Basic. JMP overwritten by Autostart or RTS.

ENDOFLOADER:
FILENAMECOPY:   
!IF ENABLEDIRECTORY {
DIRECTORYSTRING:
}
                !tx "                "  
                !byte $0d, $00
                
; New location for Start Bank in RAM, value unimportant HERE, will be set during copy
HOMEBANK_RAM: !byte $00
                }

                !realign $100, $FF
TAPESTART:
