├── c1541script.txt ├── start.cfg ├── README.md └── start.s /c1541script.txt: -------------------------------------------------------------------------------- 1 | format autostart,01 2 | write "start.prg" 3 | -------------------------------------------------------------------------------- /start.cfg: -------------------------------------------------------------------------------- 1 | MEMORY { 2 | # hack to get the load address as the first 2 bytes into the .PRG 3 | LOADADDR: start = $0188, size = 2; 4 | 5 | # the receive code, filled with $02s that overwrite the top few bytes of 6 | # the stack and make the KERNAL loader return to $0203 7 | PART2: start = $0188, size = $0065, fill = yes, fillval = $FF, file = %O; 8 | 9 | VECTOR: start = $01ED, size = $0011, fill = yes, fillval = $FF, file = %O; 10 | 11 | CMD: start = $01FE, size = $0005, fill = yes, fillval = $FF, file = %O; 12 | 13 | # entry point $0203 due to stack overwritten with $02s 14 | # code that transfers M-E 15 | START: start = $0203, size = $0003, fill = yes, fillval = $ff, file = %O; 16 | 17 | FCODE: start = $482, size = $007E, fill = yes, fillval = $ff, file = %O; 18 | } 19 | 20 | SEGMENTS { 21 | LOADADDR: load = LOADADDR, type = ro; 22 | START: load = START, type = ro; 23 | PART2: load = PART2, type = ro; 24 | CMD: load = CMD, type = ro; 25 | VECTOR: load = VECTOR, type = ro; 26 | FCODE: load = FCODE, type = ro; 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fastboot1541 2 | 3 | *fastboot1541* is an autostart fastloader for the Commodore 64 and 1541 that fits into a single sector. 4 | 5 | # Use 6 | 7 | This is the fastest way to chain-load a second-stage high-speed fastloader, since it only transfers about half a sector worth of data using the slow serial protocol, yet loads the next stage at ~10x speed - and it's an autostart! 8 | 9 | # Some Details 10 | 11 | * The file overwrites stack addresses to gain execution, which should make it compatible with most ROM modifications and cartridges. 12 | * The C64 part sits just above the stack. 13 | * The 1541 part executes in-place in the buffer that was written when loading the program for the C64. 14 | * The C64 and the 1541 part contain a simple bus protocol speeder that allows the screen to be on. 15 | * You should hand-edit the second byte of the resulting sector on disk so that only the C64-specific bytes are actually transfered. 16 | * Put this sector on track 18 to save some more time. 17 | * Choose the optimal interleave for the chainloaded data. Maybe you can fit it on track 18 as well. 18 | 19 | # More Details 20 | 21 | The blog post at [pagetable.com/?p=568](http://www.pagetable.com/?p=568) explains everything in detail. 22 | 23 | # License 24 | 25 | Do anything you want with it, but giving credit and emailing me about its use is very much appreciated. 26 | 27 | Improvements and corrections welcome! 28 | 29 | # Author 30 | 31 | Michael Steil 32 | -------------------------------------------------------------------------------- /start.s: -------------------------------------------------------------------------------- 1 | 2 | TARGET := $0400 3 | TRACK := 18 4 | 5 | DATA_OUT := $20 ; bit 5 6 | CLK_OUT := $10 ; bit 4 7 | VIC_OUT := $03 ; bits need to be on to keep VIC happy 8 | 9 | seccnt = 2 10 | 11 | ;---------------------------------------------------------------------- 12 | ; Hack to generate .PRG file with load address as first word 13 | ;---------------------------------------------------------------------- 14 | .segment "LOADADDR" 15 | .addr * 16 | 17 | ;---------------------------------------------------------------------- 18 | ; Send an "M-E" to the 1541 that jumps to floppy code. 19 | ; Then receive one block and run it. 20 | ; This code lives around $0190. 21 | ;---------------------------------------------------------------------- 22 | .segment "PART2" 23 | main: 24 | lda #$0f 25 | sta $b9 26 | sta $b8 27 | ldx #memory_execute 29 | lda #memory_execute_end - memory_execute 30 | jsr $fdf9 ; filnam 31 | jsr $f34a ; open 32 | 33 | sei 34 | lda #VIC_OUT | DATA_OUT ; CLK=0 DATA=1 35 | sta $DD00 ; we're not ready to receive 36 | 37 | ; wait until floppy code is active 38 | wait_fast: 39 | bit $DD00 40 | bvs wait_fast ; wait for CLK=1 (inverted read!) 41 | 42 | lda #sector_table_end - sector_table ; number of sectors 43 | sta seccnt 44 | ldy #0 45 | get_rest_loop: 46 | bit $DD00 47 | bvc get_rest_loop ; wait for CLK=0 (inverted read!) 48 | 49 | ; wait for raster 50 | wait_raster: 51 | lda $D012 52 | cmp #50 53 | bcc wait_raster_end 54 | and #$07 55 | cmp #$02 56 | beq wait_raster 57 | wait_raster_end: 58 | 59 | lda #VIC_OUT ; CLK=0 DATA=0 60 | sta $DD00 ; we're ready, start sending! 61 | pha ; 3 cycles 62 | pla ; 4 cycles 63 | bit $00 ; 3 cycles 64 | lda $DD00 ; get 2 bits into bits 6&7 65 | lsr 66 | lsr ; move down by 2 (bits 4&5) 67 | eor $DD00 ; get 2 more bits 68 | lsr 69 | lsr ; move everything down (bits 2-5) 70 | eor $DD00; get 2 more bits 71 | lsr 72 | lsr ; move everything down (bits 0-5) 73 | eor $DD00 ; get last 2 bits, now 0-7 are populated 74 | 75 | ldx #VIC_OUT | DATA_OUT ; CLK=0 DATA=1 76 | stx $DD00 ; not ready any more, don't start sending 77 | 78 | selfmod1: 79 | sta TARGET,y 80 | iny 81 | bne get_rest_loop 82 | 83 | inc selfmod1+2 84 | dec seccnt 85 | bne get_rest_loop 86 | 87 | inf: 88 | jmp inf 89 | 90 | .segment "VECTOR" 91 | ; these bytes will be overwritten by the KERNAL stack while loading 92 | ; let's set them all to "2" so we have a chance that this will work 93 | ; on a modified KERNAL 94 | .byte 2,2,2,2,2,2,2,2,2,2,2 95 | ; This is the vector to the start of the code; RTS will jump to $0203 96 | .byte 2,2 97 | ; These bytes are on top of the return value on the stack. We could use 98 | ; them for data; or, fill them with "2" so different versions of KERNAL 99 | ; might work 100 | .byte 2,2,2,2 101 | 102 | .segment "CMD" 103 | memory_execute: 104 | .byte "M-E" 105 | .word $0480 + 2 106 | memory_execute_end: 107 | 108 | ;---------------------------------------------------------------------- 109 | ; Jump to code that receives data. 110 | ;---------------------------------------------------------------------- 111 | .segment "START" 112 | jmp main 113 | 114 | ;---------------------------------------------------------------------- 115 | ;---------------------------------------------------------------------- 116 | ; C64 -> Floppy: direct 117 | ; Floppy -> C64: inverted 118 | ;---------------------------------------------------------------------- 119 | ;---------------------------------------------------------------------- 120 | 121 | .segment "FCODE" 122 | 123 | F_DATA_OUT := $02 124 | F_CLK_OUT := $08 125 | 126 | sec_index := $05 127 | 128 | start1541: 129 | lda #F_CLK_OUT 130 | sta $1800 ; fast code is running! 131 | 132 | lda #0 ; sector 133 | sta sec_index 134 | sta $f9 ; buffer $0300 for the read 135 | lda #TRACK 136 | sta $06 137 | read_loop: 138 | ldx sec_index 139 | lda sector_table,x 140 | inc sec_index 141 | bmi end 142 | sta $07 143 | cli 144 | jsr $D586 ; read sector 145 | sei 146 | 147 | send_loop: 148 | ; we can use $f9 as the byte counter, since we'll return it to 0 149 | ; so it holds the correct buffer number "0" when we read the next sector 150 | ldx $f9 151 | lda $0300,x 152 | 153 | ; first encode 154 | eor #3 ; fix up for receiver side (VIC bank!) 155 | pha ; save original 156 | lsr 157 | lsr 158 | lsr 159 | lsr ; get high nybble 160 | tax ; to X 161 | ldy enc_tab,x ; super-encoded high nybble in Y 162 | ldx #0 163 | stx $1800 ; DATA=0, CLK=0 -> we're ready to send! 164 | pla 165 | and #$0F ; lower nybble 166 | tax 167 | lda enc_tab,x ; super-encoded low nybble in A 168 | ; then wait for C64 to be ready 169 | wait_c64: 170 | ldx $1800 171 | bne wait_c64; needs all 0 172 | 173 | ; then send 174 | sta $1800 175 | asl 176 | and #$0F 177 | sta $1800 178 | tya 179 | nop 180 | sta $1800 181 | asl 182 | and #$0F 183 | sta $1800 184 | 185 | jsr $E9AE ; CLK=1 10 cycles later 186 | 187 | inc $f9 188 | bne send_loop 189 | beq read_loop 190 | 191 | end: 192 | jmp * 193 | 194 | enc_tab: 195 | .byte %1111, %0111, %1101, %0101, %1011, %0011, %1001, %0001 196 | .byte %1110, %0110, %1100, %0100, %1010, %0010, %1000, %0000 197 | 198 | sector_table: 199 | .byte 0,1,2,3,$FF 200 | sector_table_end: --------------------------------------------------------------------------------