/* sd2iec - SD/MMC to Commodore serial bus interface/controller Copyright (C) 2007 Ingo Korb Copyright (C) 2007,2008 M.Kiesel Inspiration and low-level SD/MMC access based on code from MMC2IEC by Lars Pontoppidan et al., see sdcard.c|h and config.h. FAT filesystem access based on code from ChaN, see tff.c|h. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License only. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*! \file fastloader-ll.S * Low level fastloader transfer routines. * Side note: AVR - 8MHz. PAL C64DTV - 985246Hz. 1cyc DTV = 8.12cyc AVR. * Parameter mapping: http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage * Thanks to Jochen Adler for his Jiffy docs at http://www.nlq.de/ */ /* NOTE TO THE READER: * THIS IS AN OLD VERSION OF THE SD2IEC FIRMWARE JIFFY CODE. * IT IS NOT OPTIMIZED FOR SPACE BUT FOR READABILITY. * PLEASE READ IT IN CONJUNCTION WITH THE ROM DUMPS AVAILABLE AT NLQ.DE. */ #include "config.h" #include .section .text ;; =================================================================== ;; Utility routines ;; =================================================================== ;; Counts r18 down to zero ;; Takes (r18-1)*3 + 6 cycles, not including the rcall delayloop: dec r18 ; 1 brne delayloop ; 2/1 ret ; 4 ;; Including the rcall this will delay for 8 cycles delay1us: nop ; 1 ret ; 4 ;; ==================================================================== ;; JiffyDOS ;; ==================================================================== ;; ;; Receives a single byte using the JiffyDOS protocol ;; return uint8_t receivedbyte (r24) ;; .global jiffy_receive jiffy_receive: ;; Disable interrupts cli ;; Move pointer to EOF variable into Z movw r30,r24 ;; Set clock+data high cbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_CLOCK cbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_DATA ;; Wait until clock is high and emulate ATN-Ack 0: sbis _SFR_IO_ADDR(IEC_PIN), IEC_PIN_ATN sbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_DATA ; Data low if ATN is low sbis _SFR_IO_ADDR(IEC_PIN), IEC_PIN_CLOCK rjmp 0b ;; Wait for 13us from clock high -> 104 cycles ldi r18,32 ; 1 rcall delayloop ; 102 nop ; 1 ;; Read bit 5+4 in r0, _SFR_IO_ADDR(IEC_PIN) ; 1 ;; Move bits to the correct position swap r0 ; 1 mov r24, r0 ; 1 andi r24, 0x20 ; 1 - mask swapped data bit mov r25, r0 ; 1 andi r25, 0x40 ; 1 - mask swapped clock bit lsr r25 ; 1 - shift into position lsr r25 ; 1 or r24, r25 ; 1 - merge ;; Wait 13us -> 104-9=95 cycles ldi r18, 29 ; 1 rcall delayloop ; 93 nop ; 1 ;; Read bit 7+6 in r0, _SFR_IO_ADDR(IEC_PIN) ; 1 ;; Move to corrent position and merge swap r0 ; 1 mov r25, r0 ; 1 andi r25, 0x40 ; 1 - mask swapped clock bit or r24, r25 ; 1 - merge mov r25, r0 ; 1 andi r25, 0x20 ; 1 - mask swapped data bit lsl r25 ; 1 - shift into position lsl r25 ; 1 or r24, r25 ; 1 - merge ;; Wait 11us -> 88-10=78 cycles ldi r18, 23 ; 1 rcall delayloop ; 75 rjmp 1f ; 2 1: ;; Read bit 3+1 [sic] in r0, _SFR_IO_ADDR(IEC_PIN) ; 1 mov r25, r0 ; 1 andi r25, 0x04 ; 1 - mask clock bit lsl r25 ; 1 - shift into position or r24, r25 ; 1 - merge mov r25, r0 ; 1 andi r25, 0x02 ; 1 - mask data bit or r24, r25 ; 1 - merge ;; Wait 13us -> 104-8=96 cycles ldi r18, 29 ; 1 rcall delayloop ; 93 rjmp 1f ; 2 1: ;; Read Bit 2+0 in r0, _SFR_IO_ADDR(IEC_PIN) ; 1 mov r25, r0 ; 1 andi r25, 0x04 ; 1 - mask clock bit or r24, r25 ; 1 - merge mov r25, r0 ; 1 andi r25, 0x02 ; 1 - mask data bit lsr r25 ; 1 - shift into position or r24, r25 ; 1 clr r25 ; 1 - clear high byte of return value ;; Wait 13us -> 104-9=95 cycles ldi r18, 29 ; 1 rcall delayloop ; 93 nop ; 1 ;; Read EOI mark in r19, _SFR_IO_ADDR(IEC_PIN) ; 1 ;; Wait 6us -> 48-1=47 cycles ldi r18, 13 ; 1 rcall delayloop ; 45 ;; Data low sbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_DATA ; 1 ;; Post-process data and store last bus state ldi r18, 0xff eor r24, r18 ; invert received data (result) st Z, r19 ; store last bus state sei ; enable interrupts ret ;; ;; Sends a single byte using the Jiffy protocol ;; uint8_t prebus (r24), uint8_t value (r22), uint8_t eoi (r20) ;; return uint8_t atnactive (r24) ;; .global jiffy_send jiffy_send: cli ldi r21, 0xff ; constant for inverting bytes ;; J1541 FB0F delay 360uS? ;; J1541 FB5A - ATN check sbis _SFR_IO_ADDR(IEC_PIN), IEC_PIN_ATN jmp j_atnfound ;; J1541 FF88 ;; Set clock high/inactive - for C64 FBB9 cbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_CLOCK ;; Set data high/inactive (not in J1541 ROM, just to be sure) cbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_DATA ;; Wait until data is high/inactive (J1541 FFB8; C64 FBCB) 0: sbis _SFR_IO_ADDR(IEC_PIN), IEC_PIN_DATA rjmp 0b ;; J1541 at FFB8 (FFBB) here (6-2 cycles until write at FFBD) ;; C64 at FBCE now (12 cycles until read at FBD5) ;; We wait a bit longer than original J1541 to relax timing. ;; Wait 6/49 C64/AVR cycles. ldi r18,14 ; 1 rcall delayloop ; ((14-1)*3+6) + 3 = 48 ;; Send byte. First data in 2 C64 cycles on bus. mov r0,r22 ; 1 - move byte to output to r0 rcall j_sendbyte ; 265 ;; C64 at FBE7 here ;; C64 reads data at FBE9 so hold data for 6 cycles ;; C64 expects status at FBEF (in 14 cycles) ;; J1541 writes status in 11 cycles ;; wait 9/73 C64/AVR cycles ldi r18,22 ; 1 rcall delayloop ; ((22-1)*3+6) + 3 = 72 ;; J1541 at FFDB now (timing) ;; J1541 FFD6 (code) cpi r20,0 breq js_noeoi rcall js_eoi ;; J1541 FB74 (code) ;; wait 21/169 C64/AVR cycles to reach J1541 FB78 ldi r18,54 ; 1 rcall delayloop ; ((54-1)*3+6) + 3 = 168 ;; J1541 FB78/FFDB rcall j_sendok sei ret js_noeoi: ;; J1541 FFDB rcall j_sendok ;; J1541 FB72 branches so we leave here sei ret ;; ;; Jiffy helper routines ;; ;; End of byte routines. ;; 1541 FFD9 (not literally) js_eoi: ;; send single-byte EOI (DATA active, CLOCK inactive), return sbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_DATA ; set DATA active cbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_CLOCK ; set CLOCK inactive jmp j_checkatn j_sendok: ;; send single-byte/block OK (DATA inactive, CLOCK active) ;; watch ATN, wait for DATA active cbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_DATA ; set DATA inactive sbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_CLOCK ; set CLOCK active 0: sbis _SFR_IO_ADDR(IEC_PIN), IEC_PIN_ATN ; if ATN inactive: skip/check DATA jmp j_atnfound sbic _SFR_IO_ADDR(IEC_PIN), IEC_PIN_DATA ; if DATA active: skip/return, otherwise: loop rjmp 0b j_checkatn: sbis _SFR_IO_ADDR(IEC_PIN), IEC_PIN_ATN ; if ATN inactive: skip jmp j_atnfound ldi r24, 0 ; ATN inactive flag ret j_atnfound: ldi r24, 1 ; ATN active flag ret ;; Puts two bits from r0 on bus. ;; Takes 7 + 4 + 3 = 15 cycles including rcall. j_sendtwobits: clr r19 ; 1 - clear output register lsr r0 ; 1 rol r19 ; 1 lsr r0 ; 1 rol r19 ; 1 lsl r19 ; 1 out _SFR_IO_ADDR(IEC_DDR), r19 ; 1 - output the bit pair ret ; 4 ;; Sends a complete byte from r22. ;; First data is put on bus 15 AVR cycles after rcall. ;; Takes 265 AVR cycles including rcall. j_sendbyte: ;; C64 addresses apply for IECIN ;; J1541 FFBD - sets data for C64 FBD5 eor r0,r21 ; 1 - invert byte rcall j_sendtwobits ; 14 ;; - C64 at FBCE+10 cycles here = FBD4 ;; J1541 FFC4 - sets data for C64 FBDB ldi r18,20 ; 1 rcall delayloop ; ((20-1)*3+6) + 3 = 66 rcall j_sendtwobits ; 14 ;; - C64 at FBD4+10 cycles here = FBDA ;; J1541 FFCC - sets data for C64 FBE2 ldi r18,20 ; 1 rcall delayloop ; ((20-1)*3+6) + 3 = 66 rcall j_sendtwobits ; 14 ;; - C64 at FBDA+10 cycles here = FBE0 ;; J1541 FFD3 - sets data for C64 FBE9 ldi r18,20 ; 1 rcall delayloop ; ((20-1)*3+6) + 3 = 66 rcall j_sendtwobits ; 14 ;; - C64 at FBE0+10 cycles here = ~FBE7 ret ; 4 ;; ;; Sends a byte buffer using the Jiffy LOAD protocol ;; Expects interrupts to be disabled ;; uint8_t *buffer (r24/r25), uint8_t length (r22), uint8_t eoi (r20) ;; return uint8_t atnactive (r24) ;; .global jiffy_send_buffer jiffy_send_buffer: ;; J1541 FF35 (block loop) ldi r21, 0xff ; constant for inverting bytes movw r30,r24 ; output pointer ;; in case we got a zero byte block (sd2iec only) cpi r22,0 ; still bytes to transfer? breq jsb_eoi ; no, end transfer of this block ;; Set data low/active (J1541 FF9D, for C64 FB0C/FB11) sbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_DATA ;; Set clock high/inactive (J1541 FF9D, for C64 FB0C/FB0F) cbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_CLOCK ;; C64 at FAF0-FB0C here (RUN/STOP test...) ;; Wait 42++ (59) usec (J1541 FFA0) ldi r18,159 ; 1 rcall delayloop ; ((159-1)*3+6) + 3 = 483 AVR cycles ;; C64 heading for FB3E now jsb_byteloop: ;; J1541 FFA3 ;; Set data high/inactive (J1541 FFB5, for C64 FB3E) cbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_DATA ;; Set clock high/inactive (J1541 FFB5) cbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_CLOCK 0: ;; Wait until DATA or ATN is low/active (J1541 FFB8, set at C64 FB51) sbic _SFR_IO_ADDR(IEC_PIN), IEC_PIN_DATA ; 2 rjmp 1f rjmp 2f 1: sbic _SFR_IO_ADDR(IEC_PIN), IEC_PIN_ATN ; 2 rjmp 0b 2: ;; 16/130 C64/AVR cycles until data expected on lines now ;; C64 at FB54 now ;; wait 8/65 C64/AVR cycles ldi r18,19 ; 1 rcall delayloop ; ((19-1)*3+6) + 3 = 63 AVR cycles nop ;; C64 at FB5A now ;; 4/32 C64/AVR cycles until bus is read at C64 FB5D, write now to be early ld r0,Z+ ; 1 - move byte to output to r0 dec r22 ; decrement bytes left rcall j_sendbyte ; 265 ;; C64 at FB6F now ; wait for 11/88 C64/AVR cycles ldi r18,27 ; 1 rcall delayloop ; ((27-1)*3+6) + 3 = 57 ;; C64 at FB78 now, heading for FB44/FB54 cpi r22,0 ; still bytes to transfer? breq jsb_out ; no, end transfer of this block jmp jsb_byteloop jsb_out: ;; J1541 FFD9 rcall j_sendok ;; J1541 FFDB-FFE5 ;; returns at C64 FB54 ;; here: CLOCK active, DATA inactive (but pulled down by C64) cpi r24,1 ; bail out on ATN breq jsb_ret cpi r20,0 breq jsb_noeoi jsb_eoi: ;; reached eoi ;; J1541 FF60 ;; wait (J1541 FF65) ldi r18,250 rcall delayloop ;; Set clock high/inactive (J1541 FF65) cbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_CLOCK ;; wait (J1541 FF68) ldi r18,250 rcall delayloop ;; Set clock low/active (J1541 FF6B) sbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_CLOCK ;; wait (J1541 FF71) ldi r18,250 rcall delayloop ;; Set clock high/inactive (J1541 FF73) cbi _SFR_IO_ADDR(IEC_DDR), IEC_PIN_CLOCK ret jsb_noeoi: ;; J1541 FF5E, FF35 ;; make sure C64 FB57 branch to FAF0 is done ldi r18,25 rcall delayloop ;; C64 FAF0, FB06 (DATA=>inactive), waits until CLOCK inactive at FB0F jsb_ret: ret