2-bit transfer protocol in diskdrive IRQ-loaders by Cadaver ----------------------------------------------------------- This is a continuation of the previous IRQ-loader rant, that focused on a 1-bit transfer protocol. The loader discussed here is very much like the previous one, so only the changes in the protocol are explained in detail. 2-bit transfer, as mentioned in the previous rant, requires good synchronization between the C64 and diskdrive. Once the transfer of a byte is fired up, the 1541 sends 2 bits at a time on both the CLK & DATA lines of the serial bus without any waiting or handshaking. What gives problems is that synchronization between the C64 & diskdrive can't be cycle-exact; usually the diskdrive is waiting in a loop for the C64 to reply before firing up the strictly timed transfer, so the timing might be off as much as the length of this waiting loop. The other problem is that the diskdrive CPU runs at 1Mhz, but the C64 runs either slower (PAL) or faster (NTSC). This has to be taken into account also. In fact it seems that when the transfer routine is coded in a certain way, the difference between correct PAL & NTSC timing is only 1 clock cycle on the C64 side; the disk drive code doesn't have to be adjusted at all. The drawbacks with this kind of 2-bit transfer are: - Interrupts will be disabled during the transfer of a byte, so any raster effects might be displaced. - Sprites are not allowed onscreen (would steal CPU cycles and mess with the timing.) - Hitting RESTORE while loading will cause an NMI interrupt and also mess the timing. There are two ways I can think of to handle this: Extend the transfer protocol to have a resend option (the NMI interrupt will set a resend flag) or disable NMIs by triggering a one-shot CIA2 interrupt but never acknowledging the NMI. None of these are in use in the loader, it will just load incorrectly if NMIs are occurring. I thank Marko M??kel?? for his C64->diskdrive asynchronous protocol, drive init code and original main drive loop, K.M/TABOO for inspiration on the badline detection and MuOn for testing the transfer routines on a real NTSC machine. The 2bit transfer code in this loader is coded by me but heavily inspired by various game loadersystems; for example Technocop. 2-bit loader disk image and source ;COVERT BITOPS 2bit fastloader for Rant #9 ;Based on: ;Marko M??kel??'s IRQ-loader ;- Drive init code ;- Drive main loop ;- Asynchronous C64->drive communication ;Technocop loader and other game loadersystems ;- Drive->C64 2-bit communication ;K.M/Taboo's 2bit loaders ;- Badline detection status = $90 ;Kernal zeropage variables messages = $9d fa = $ba bufferstatus = $02 stackptrstore = $03 ciout = $ffa8 ;Kernal routines listen = $ffb1 second = $ff93 unlsn = $ffae acptr = $ffa5 chkin = $ffc6 chkout = $ffc9 chrin = $ffcf chrout = $ffd2 ciout = $ffa8 close = $ffc3 open = $ffc0 setmsg = $ff90 setnam = $ffbd setlfs = $ffba clrchn = $ffcc getin = $ffe4 load = $ffd5 save = $ffd8 processor 6502 org 2049 ;Example main program. Inits the fastloader and loads a file using it. After- ;wards the drive can be used normally. sys: dc.b $0b,$08 ;Address of next instruction dc.b $0a,$00 ;Line number(10) dc.b $9e ;SYS-token dc.b $32,$30,$36,$31 ;2061 as ASCII dc.b $00 dc.b $00,$00 ;Instruction address 0 terminates ;the basic program start: jsr initfastload jsr initmusicplayback ;Now that we can play music while ldx #"D" ;loading, let's not forget it :-) ldy #"A" jsr fastload jsr stopmusicplayback rts initmusicplayback: sei lda #<raster sta $0314 lda #>raster sta $0315 lda #50 ;Set low bits of raster sta $d012 ;position lda $d011 and #$7f ;Set high bit of raster sta $d011 ;position (0) lda #$7f ;Set timer interrupt off sta $dc0d lda #$01 ;Set raster interrupt on sta $d01a lda $dc0d ;Acknowledge timer interrupt lda #$00 jsr $1000 cli rts stopmusicplayback: sei lda #<$ea31 sta $0314 lda #>$ea31 sta $0315 lda #$00 sta $d01a lda #$81 sta $dc0d inc $d019 lda #$00 sta $d418 cli rts raster: inc $d020 jsr $1003 dec $d020 dec $d019 jmp $ea31 ;INITFASTLOAD ; ;Uploads the fastloader to disk drive memory and starts it. ;This routine is Marko M??kel??'s work, except for the 2-bit transfer ;preparations. ; ;Parameters: - ;Returns: - ;Modifies: A,X,Y AMOUNT = 32 ;Bytes in one M-W command The fastloader initialization code starts with PAL/NTSC detection. I didn't want to rely on the value of $02a6 memory location so I implemented it with raster-line based detection. This code measures the highest rasterline number on the screen, and draws conclusions from that. initfastload: sei lda #$00 il_detectntsc: ldx $d011 ;Get the biggest rasterline in the bmi il_detectntsc ;area >= 256 to detect NTSC/PAL il_detectntsc2: ldx $d011 bpl il_detectntsc2 il_detectntsc3: cmp $d012 bcs il_detectntsc4 lda $d012 il_detectntsc4: ldx $d011 bmi il_detectntsc3 cli cmp #$20 ;PAL has 312 lines, but this check is bcc il_isntsc ;somewhere in the middle... For a PAL machine, the BPL instruction in the getbyte delay code (3 cycles, takes the branch) is replaced with a BMI instruction (2 cycles, doesn't take the branch) lda #$30 ;Adjust 2-bit fastload transfer sta fastload_delay ;delay for PAL The rest of the initialization is like in the 1-bit loader. il_isntsc: lda #<drvprog ;Initialize selfmodifying code sta il_mwbyte+1 lda #>drvprog sta il_mwbyte+2 lda #<drive sta mwcmd+2 lda #>drive sta mwcmd+1 il_mwloop: jsr il_device ;Set drive to listen ldx #lmwcmd - 1 il_sendmw: lda mwcmd,x ;Send M-W command jsr ciout dex bpl il_sendmw ldx #0 il_mwbyte: lda drvprog,x ;Send AMOUNT bytes of drive jsr ciout ;code inx cpx #AMOUNT bne il_mwbyte jsr unlsn ;Unlisten starts the command lda mwcmd+2 clc adc #AMOUNT sta mwcmd+2 bcc il_nohigh inc mwcmd+1 il_nohigh: lda il_mwbyte+1 clc ;Move pointers adc #AMOUNT sta il_mwbyte+1 tax bcc il_nohigh2 inc il_mwbyte+2 il_nohigh2: lda il_mwbyte+2 cpx #<drvprogend sbc #>drvprogend bcc il_mwloop jsr il_device ;Set drive to listen again ldx #lmecmd - 1 il_sendme: lda mecmd,x ;Send M-E command jsr ciout dex bpl il_sendme jmp unlsn ;Unlisten starts the command il_device: lda fa jsr listen lda #$6f jmp second ;FASTLOAD ; ;Loads a file with fastloader. INITFASTLOAD must have been called first. ;Any normal KERNAL disk operations will cause the fastloader drive code to ;exit (as ATN line goes low) and after that, INITFASTLOAD has to be called ;again. ; ;Parameters: X: First letter of filename, Y: Second letter of filename ;Returns: C=0 OK, C=1 error ;Modifies: A,X,Y fastload: stx filename sty filename+1 sta $d07a ;SCPU to slow mode tsx ;Store stackpointer, needed when stx stackptrstore ;finishing loading lda #$00 ;Reset buffer status sta bufferstatus ldx #$01 ;Byte counter. fastload_sendouter: ldy #$08 ;Bit counter fastload_sendinner: lsr filename,x ;Rotate byte to be sent lda $dd00 and #$ff-$30 ora #$10 bcc fastload_zerobit eor #$30 fastload_zerobit: sta $dd00 lda #$c0 ;Wait for CLK & DATA low fastload_sendack: bit $dd00 bne fastload_sendack ...
shogunpl