;-------------------------------------------------------------------------- ; VIA Code ; Coding: Mark Hindsbo ; (C) Arcane Productions 2020 ;-------------------------------------------------------------------------- !ZONE VIA VIA1_PORTB = $F100 ; address of port a VIA1_PORTA = $F101 ; address of port b VIA1_DDRB = $F102 ; data direction register for port a VIA1_DDRA = $F103 ; data direction register for port b VIA1_T1CL = $F104 ; timer 1 counter low (writes to latch low) VIA1_T1CH = $F105 ; timer 1 counter high VIA1_T1LL = $F106 ; timer 1 latch low VIA1_T1LH = $F107 ; timer 1 latch high VIA1_T2CL = $F108 ; timer 2 counter low (writes to latch low) VIA1_T2CH = $F109 ; timer 2 counter high VIA1_SR = $F10A ; shift register VIA1_ACR = $F10B ; auxiliary control register (bit 0 = PA, bit 1 = PB, bit 2-4 = SR, bit 5 = T2, bit 6-7 = T1) VIA1_PCR = $F10C ; peripheral control register (bit 0 = CA1, bit 1-3 = CA2, bit 4 = CB1, bit 5-7 = CB2) VIA1_IFR = $F10D ; interrupt flag register VIA1_IER = $F10E ; interrupt enable register VIA1_ORA = $F10F ; same as port A, except "no handshake" ;-------------------------------------------------------------------------- ; via routines ;-------------------------------------------------------------------------- via.init lda #%01111111 sta VIA1_IER ; disable all VIA interrupts sta VIA1_IFR ; clear flag register lda #$FF sta VIA1_DDRA ; set port A to output sta VIA1_DDRB ; set port B to output rts ;-------------------------------------------------------------------------- ; SPI on port B of VIA. SD card = SS1 ; CB1 = CLK, CB2 = MOSI/MISO, PB0 = OUTPUT, PB1 = INPUT, PB2-4 = S1-S4 ;-------------------------------------------------------------------------- SPI_CLKN = 0 ; freq = clock_freq / 2*(count + 2) SPI_OUTPUT = %00000001 ; set PB0 = 1 for output on CB2 (MOSI) SPI_INPUT = %00000010 ; set PB1 = 1 for input on CB2 (MISO) SPI_S1 = %00000100 ; S1 / CS (SD card select) SPI_S2 = %00001000 ; S2 SPI_S3 = %00010000 ; S3 SPI_S4 = %00100000 ; S4 SPI_NOSS = %00111100 ; deselect all slaves SPI_SS1 = SPI_NOSS ~ SPI_S1 ; SS1 / CS (SD card select) SPI_SS2 = SPI_NOSS ~ SPI_S2 ; SS2 (SSX = select slave X ... this slave bit = 0 all other high) SPI_SS3 = SPI_NOSS ~ SPI_S3 ; SS3 SPI_SS4 = SPI_NOSS ~ SPI_S4 ; SS4 SD_RETRY = 3 ; number of times to retry SD operation SD_MAX_FILESIZE = $8000 ; 32K size limit SD_INIT_ERR = 0 ; SD error codes SD_FILE_ERR = 1 SD_LOAD_ERR = 2 ;-------------------------------------------------------------------------- ; SPI routines ;-------------------------------------------------------------------------- spi.init lda #%00000100 sta VIA1_IER ; disable shift-register interrupt lda #$FF ; set port B to output sta VIA1_DDRB lda #SPI_CLKN sta VIA1_T2CL ; set period for SPI clock signal (in number of processor clock signals) rts ;-------------------------------------------------------------------------- ; initialize SD card. carry set on success ;-------------------------------------------------------------------------- sd.init jsr spi.set_output ; setup for SPI output lda #(SPI_OUTPUT+SPI_NOSS) ; set SR to output and SD card CS = 1 (special first time init) sta VIA1_PORTB ldx #10 ; send 80 clock signals to initialize SPI mode for SD card @init0 lda #$FF ; MOSI = 1 and CS = 1 jsr spi.send_byte dex bne @init0 ldy #100 ; number of retries to get card in idle state @cmd0 lda #0 ; command 0 (go to idle) - send without waiting for idle byte! sta sd_cmd_dat+4 sta sd_cmd_dat+3 sta sd_cmd_dat+2 sta sd_cmd_dat+1 ldx #$95 ; crc for cmd0 stx sd_cmd_dat+0 ; store at start of command data (we send from end due to dex) ora #$40 ; set the command bit sta sd_cmd_dat+5 ; store at end of command data (we send from end due to dex) jsr sd.send_cmdx ; call send command w/o checking idle cmp #$01 beq @cmd8 ; returns 1 when i idle state (bne .init_sd.error) dey ; retry cmd0 bne @cmd0 beq .init_sd.error @cmd8 lda #8 ; command 8 = send if cond jsr sd.send_cmd ;cmp #$01 ;bne @error jsr spi.get_byte ; response should be $00, $00, $01, $AA jsr spi.get_byte jsr spi.get_byte jsr spi.get_byte cmp #$AA bne .init_sd.error ldy #200 ; number of retries until card is ready @cmd55 lda #$00 ; command arguments sta sd_cmd_dat+4 sta sd_cmd_dat+3 sta sd_cmd_dat+2 sta sd_cmd_dat+1 lda #55 ; command 55 = application command jsr sd.send_cmd ;cmp #$01 ;bne @error @cmd41 lda #$40 ; command arguments sta sd_cmd_dat+4 ;lda #$00 ;sta sd_cmd_dat+3 ;sta sd_cmd_dat+2 ;sta sd_cmd_dat+1 lda #41 ; AMC41 when following command 55 jsr sd.send_cmd beq @cmd58 dey ; retry until card is ready bne @cmd55 .init_sd.error clc ; returns carry clear on error rts @cmd58 lda #$00 sta sd_cmd_dat+4 ;sta sd_cmd_dat+3 ;sta sd_cmd_dat+2 ;sta sd_cmd_dat+1 lda #58 ; command 58 = read OCR jsr sd.send_cmd bne .init_sd.error jsr spi.get_byte ; dont use this response for anything jsr spi.get_byte ; is used to check that voltage is ok for this card jsr spi.get_byte jsr spi.get_byte @cmd16 ;lda #$00 ;sta sd_cmd_dat+4 ;sta sd_cmd_dat+3 ;sta sd_cmd_dat+1 lda #$02 ; 512 bytes size = $00000200 sta sd_cmd_dat+2 lda #16 ; command 16 = set block size jsr sd.send_cmd bne .init_sd.error ; load MBR for this SD card ;-------------------------------------------------------------------------- sd.load_mbr lda #$00 ; load block 0 on SD card = MBR sta sd_cmd_dat+4 sta sd_cmd_dat+3 sta sd_cmd_dat+2 sta sd_cmd_dat+1 jsr sd.get_blockx @boot_adr lda #$00 ; calculate & store boot sector address (little indean) sta sd_boot_sector+0 ; address = LBA * $200 (sector size) lda sd_data_buffer+$1BE+8 ; location of first partition info ($1BE) + LBA (#sectors between MBR and first sector in partition) asl sta sd_boot_sector+1 lda sd_data_buffer+$1BE+9 rol sta sd_boot_sector+2 lda sd_data_buffer+$1BE+10 rol sta sd_boot_sector+3 ; load boot sector and initialize values for this SD card ;-------------------------------------------------------------------------- sd.load_bsctr ldx #sd_boot_sector jsr sd.get_block bcc .stlb_end ; carry clear = failure @sector_size lda sd_data_buffer+$0B ; check that sector size is 512 bne .init_sd.error lda sd_data_buffer+$0C cmp #$02 bne .init_sd.error @reserved lda sd_data_buffer+$0E ; sectors to first FAT (number of reserved sectors) sta zp_temp+0 lda sd_data_buffer+$0F sta zp_temp+1 lda #$00 sta zp_temp+2 jsr m.mult512 ; convert from sector length to address length ldx #sd_fat_sector jsr m.add2w ldx #3 @fat_length lda sd_data_buffer+$24,x ; sector length of FAT (for FAT32) sta sd_fat_length,x sta zp_temp,x dex bpl @fat_length jsr m.mult512 ; convert from sector length to address length ldx #sd_root_sector jsr m.add2w dec zp_temp+5 bne @add_fat_length ;lda #$FF ; mask to calculate remainding bytes if cluster size = 256 ;sta sd_remain_mask+0 ;lda #$00 ;sta sd_remain_mask+1 ;sta sd_remain_mask+2 ;sta sd_remain_mask+3 @cluster_size lda sd_data_buffer+$0D ; sectors / cluster sta sd_cluster_size ;@remainder sec ; rol sd_remain_mask+1 ; shift mask for each power of 2 sector/cluster ; lsr ; note: 1 extra since only set default mask to 256 so one shift set it for 512 ; bcc @remainder ldx #3 @root_cluster lda sd_data_buffer+$2C,x ; root cluster number (typically 2, but is theoretically a 2word) sta sd_root_cluster,x dex bpl @root_cluster ; load FAT sector from SD (to find EOF marker) ;-------------------------------------------------------------------------- sd.load_fat ldx #sd_fat_sector jsr sd.get_block bcc .stlb_end ; carry clear = failure ldy #3 @eof lda sd_data_buffer,y ; end of file marker sta sd_eof_marker,y dey bpl @eof @success sec ; set carry to indicate success .stlb_end rts ;-------------------------------------------------------------------------- ; load file (must be in root sector of sd card). carry set on success ; pointer to file name in sd_file_name ;-------------------------------------------------------------------------- sd.load_file jsr sd.load_root ; load first root sector bcc .stlb_end ; carry clear = failure @check_sector ldx #(SD_SECTOR_SIZE/F32_ENTRY_SIZE) ; number of entries per sector (is 16 assuming 512 byte sectors) lda #sd_data_buffer sta zp_source+1 @cmp_attribute ldy #11 ; file attribute lda (zp_source),y and #%00011000 ; check volume labe or directory (will include LFN) bne @next_entry ; ... then skip dey @cmp_file_name lda sd_file_name,y ; check short name only (11 chars) cmp (zp_source),y bne @next_entry dey bpl @cmp_file_name bmi @found_file ; if all letters match file name we have the right entry @next_entry clc ; advance to the next entry in the root lda zp_source+0 adc #F32_ENTRY_SIZE sta zp_source+0 bcc @move_entry_end inc zp_source+1 @move_entry_end dex ; have we checked this entire sector? bne @cmp_attribute @load_sector jsr sd.load_rootx ; load next sector bcs @check_sector ; carry clear = failure or end of root .stlb_error lda #SD_FILE_ERR ; return file error code sta sd_cmd_dat+0 clc ; clear carry to indicate failure rts ; found file name in one of the root entries ;-------------------------------------------------------------------------- @found_file ldy #$1F ; file size ldx #3 @get_file_size lda (zp_source),y sta sd_file_size,x dey dex bpl @get_file_size lda sd_file_size+3 ; check if file size is over max limit ora sd_file_size+2 ; high word must be zero bne .stlb_error @get_file_pages ldx sd_file_size+1 ; file size in number of pages lda sd_file_size+0 beq @set_file_pages ; do we have remaining bytes? inx cpx #(>SD_MAX_FILESIZE)+1 ; more pages than max size allows? bcs .stlb_error @set_file_pages stx sd_file_pages ; number of pages in file (remaining to load) @first_cluster ldy #$1A ; first cluster of file lda (zp_source),y sta zp_temp+0 iny lda (zp_source),y sta zp_temp+1 ldy #$14 ; first cluster of file lda (zp_source),y sta zp_temp+2 ; start of FAT chain stored in buffer iny lda (zp_source),y sta zp_temp+3 @get_fchain jsr sd.get_fchain ; get fat chain of clusters for this file bcc .sdlf_end ; error if carry is clear @load_file sty zp_temp+6 ; index to last entry in fat-chain table (eof) sty zp_temp+4 ; set zp_temp+4 != 0 to get load address from first 2 bytes of file ldy #0 ; first entry @get_cluster ldx #0 ; get next cluster number @copy_cn lda sd_fat_chain,y sta zp_temp,x iny inx cpx #4 bne @copy_cn sty zp_temp+7 ; store/update index to next entry jsr sd.load_cluster bcc .sdlf_end ; error if carry clear @next_cluster ldy zp_temp+7 ; index to current entry in fat chain cpy zp_temp+6 ; did we just get the last cluster? bcc @get_cluster ; ... if not get the next one (carry se when equal) ; sec ; set carry to indicate success .sdlf_end rts ;----------------------------------------------------------------------------- ; load root into sd_data_buffer. call load_root for first sector ; subsequently call load_rootx for next sector. returns carry set on success ; uses zp_temp 5-7 to keep track of sector number, etc. ;----------------------------------------------------------------------------- sd.load_root ldx #3 @root_cluster lda sd_root_cluster,x ; copy cluster number of root to zp_temp 0-3 sta zp_temp,x dex bpl @root_cluster jsr sd.get_fchain ; get fat chain for root folder bcc .sdlf_end ; error if carry is clear sty zp_temp+6 ; index to last entry in fat-chain table (eof) ldy #0 ; first cluster .sdlr_sector1 ldx #0 ; get next cluster number @copy_cn lda sd_fat_chain,y sta zp_temp,x iny inx cpx #4 bne @copy_cn sty zp_temp+7 ; store/update index to next entry lda sd_cluster_size ; number of sectors to load per cluster sta zp_temp+5 jsr .cluster_adr ; convert cluster number to address (address in zp_temp) @load_fsector ldx #sd_file_sector jmp sd.get_block ; rts ; loads next sector of root. assumes sd_cmd_dat structure is set up correctly ; returns carry clear at end of FAT. cpy zp_temp+6 to see if we are done ;----------------------------------------------------------------------------- sd.load_rootx dec zp_temp+5 ; load next sector of root. bne @next_sector ldy zp_temp+7 ; index to current entry in fat chain cpy zp_temp+6 ; did we just get the last cluster? bcc .sdlr_sector1 clc ; return error at end of root rts @next_sector clc lda sd_cmd_dat+2 ; add 512 to sector address adc #$02 sta sd_cmd_dat+2 bcc @load_sector inc sd_cmd_dat+3 bne @load_sector inc sd_cmd_dat+4 @load_sector jmp sd.get_blockx ; load next sector ; rts ;----------------------------------------------------------------------------- ; get full cluster from SD and copy to memory. cluster number in zp_temp 0-3 ; if zp_temp+4 = 0 then memory destination address is in sd_cluster_dest ; ... otherwise memory destination address from first 2 bytes in cluster/file ; returns carry set on success ;----------------------------------------------------------------------------- sd.load_cluster lda sd_cluster_size ; has to be less than 128 sectors/cluster sta zp_temp+5 jsr .cluster_adr ; convert cluster number to address (address in zp_temp) @load_fsector ldx #sd_file_sector jsr .sd_block_cmd ; send 'read block' command to SD card (but dont read data) beq @sd_ready @error lda #SD_LOAD_ERR ; return file error code sta sd_cmd_dat+0 clc ; clear carry to indicate error rts @sd_ready ldx #0 ; high byte counter ldy zp_temp+4 ; if not 0 then we need to get load address from file beq @set_dest @dest_from_file jsr spi.get_byte ; load address in two first bytes sta sd_file_start+0 jsr spi.get_byte sta sd_file_start+1 sec ; decrease destination address with 2 to account for starting w YR = 2 lda sd_file_start+0 sbc #2 sta sd_cluster_dest+0 lda sd_file_start+1 sbc #0 sta sd_cluster_dest+1 lda #$FE ; store -2 in zp_temp sta zp_temp+0 lda #$FF sta zp_temp+1 sta zp_temp+2 sta zp_temp+2 ldx #sd_file_size jsr m.add2w ldx #0 ldy #2 ; set YR = 2 to indicate we already read two bytes stx zp_temp+4 ; clear zp_temp+1 so we only get destinatioon address from the first cluster @set_dest lda sd_cluster_dest+0 ; set memory destination addresss sta zp_dest+0 lda sd_cluster_dest+1 sta zp_dest+1 @get_data lda VIA1_IFR ; check IRQ flag and #%00000100 ; check for SR flag beq @get_data lda VIA1_SR ; get data sta (zp_dest),y iny bne @get_data dec sd_file_pages ; are we done? beq @end_of_file inc zp_dest+1 ; next page of destination inx cpx #2 bne @get_data jsr spi.get_byte ; get CRC jsr spi.get_byte @next_sector ldx #sd_file_sector jsr m.add2w_512 @save_dest_end lda zp_dest+0 ; update memory destination address to where this cluster ended sta sd_cluster_dest+0 lda zp_dest+1 sta sd_cluster_dest+1 dec zp_temp+5 ; next sector in cluster beq @end_ok ; loaded all sectors? jmp @load_fsector @end_of_file inx ; last page of file loaded and moved to RAM cpx #2 beq @final_crc ; did we get all data in this block? @final_page jsr spi.get_byte ; ... if not get remaining (second) page of block iny bne @final_page @final_crc jsr spi.get_byte ; get final CRC jsr spi.get_byte clc lda sd_file_start+0 ; update memory destination addresss to precise end of file adc sd_file_size+0 sta sd_cluster_dest+0 lda sd_file_start+1 adc sd_file_size+1 sta sd_cluster_dest+1 @end_ok sec ; return OK rts ;-------------------------------------------------------------------------- ; get FAT chain. start cluster number in zp_temp 0-3 ; returns carry set on success and chain of clusters in sd_fat_chain ;-------------------------------------------------------------------------- sd.get_fchain ldy #4 ; copy first cluster number into fat chain @first_entry lda zp_temp-1,y sta sd_fat_chain-1,y dey bne @first_entry sty zp_temp+6 ; index to entry in fat-chain table @fat_sector ldx #2 jsr m.shift2w ; multiply by 4 to get address ofset to the right fat (4 bytes per cluster entry) lda zp_temp+0 ; index into sector where this cluster numbers entry is located sta zp_temp+4 ; temp+4/temp+5 hi/lo range 0-508 lda zp_temp+1 and #%00000001 sta zp_temp+5 lda #$00 ; truncate to get offset to address of start of fat sector sta zp_temp+0 lda zp_temp+1 and #%11111110 sta zp_temp+1 ldx #sd_file_sector jsr m.add2w @load_fat ldx #sd_file_sector jsr sd.get_block ldx zp_temp+4 ; low byte of index into FAT sector ldy zp_temp+6 ; index to entry in fat-chain table @next_fc_index tya ; move index to next entry in fat-chain clc adc #4 tay bne @get_next_clst ; overflow? @overflow ldy #3 ; if overflow we have an issue lda sd_eof_marker,y ; store EOF marker in first entry to signal this @eof_marker sta sd_fat_chain,y dey bpl @eof_marker clc ; clear carry to indicate error @end rts @get_next_clst lda zp_temp+5 ; high byte of index into FAT sector bne @next_256 ; are we fetching from upper or lower half of current FAT sector @first_256 lda sd_data_buffer,x ; copy next cluster number sta sd_fat_chain+0,y inx lda sd_data_buffer,x sta sd_fat_chain+1,y inx lda sd_data_buffer,x sta sd_fat_chain+2,y inx lda sd_data_buffer,x sta sd_fat_chain+3,y jsr .chk_fat_entry ; check for eof marker bcs @end inx ; next cluster number bne @next_fc_index ; did we cross the boundary to the lower inc zp_temp+5 ; ... if so increase high byte bne @next_fc_index ; jmp @next_256 lda sd_data_buffer+$100,x ; copy next cluster number sta sd_fat_chain+0,y inx lda sd_data_buffer+$100,x sta sd_fat_chain+1,y inx lda sd_data_buffer+$100,x sta sd_fat_chain+2,y inx lda sd_data_buffer+$100,x sta sd_fat_chain+3,y jsr .chk_fat_entry ; check for eof marker bcs @end inx ; next cluster number bne @next_fc_index ; do we need to load next FAT sector? @next_fat_sctr stx zp_temp+4 ; set FAT sector index to start of sector (XR = 0) stx zp_temp+5 sty zp_temp+6 ; index to entry in fat-chain table ldx #sd_file_sector jsr m.add2w_512 jmp @load_fat ; ... and load next sector of the FAT ; check fat entry for reserved values. index in YR. sets carry if eof ;-------------------------------------------------------------------------- .chk_fat_entry lda sd_fat_chain+3,y ; sd_eof_marker or something else? could be bad cluster cmp sd_eof_marker+3 ; do we have a reserved value (high byte >= $0F) bcc @chk_end lda sd_fat_chain+0,y ; sd_eof_marker or something else? could be bad cluster cmp sd_eof_marker+0 ; bad cluster marker = $F7 in low byte bcs @chk_end @bad_cluster tya ; skip a bad cluster sbc #3 ; note: carry clear due to cmp/branch above, so subtracts one more tay clc @chk_end rts ; sets carry if we have eof ;-------------------------------------------------------------------------- ; calculate cluster address. cluster number in zp_temp 0-3 ; address returned in sd_file_sector ;-------------------------------------------------------------------------- .cluster_adr sec ; cluster number - root cluster number lda zp_temp+0 sbc sd_root_cluster+0 sta zp_temp+0 lda zp_temp+1 sbc sd_root_cluster+1 sta zp_temp+1 lda zp_temp+2 sbc sd_root_cluster+2 sta zp_temp+2 lda zp_temp+3 sbc sd_root_cluster+3 sta zp_temp+3 ldx #8 ; 512 = 2 power 9 (loop always adds 1) lda sd_cluster_size ; calculate power of 2 for sectors/cluster ora #%10000000 ; make sure loop can exit even if custers/sector is accidentally erazed @power2 inx lsr bcc @power2 jsr m.shift2w ; offset address to this cluster (multiply w 512 * clusters/sector) ldx #sd_file_sector jmp m.add2w ; rts ;; copy cluster number to zp_temp 0-3 ;;-------------------------------------------------------------------------- ; ;.copy_cluster ldx #3 ;@copy_clust lda sd_file_sector,x ; copy cluster number to tempz ; sta zp_temp,x ; dex ; bpl @copy_clust ; rts ;-------------------------------------------------------------------------- ; load block into buffer. pointer l/h to block address in XR/YR ; returns carry set on success ;-------------------------------------------------------------------------- sd.get_block jsr .sd_block_cmd ; send 'read block' command to SD card bne .sdgb_error .sd_get_bdata ldx #0 @first_256 lda VIA1_IFR ; check IRQ flag and #%00000100 ; check for SR flag beq @first_256 lda VIA1_SR ; get data sta sd_data_buffer,x inx bne @first_256 ; ldx #0 ; read last 256 bytes into buffer @last_256 lda VIA1_IFR ; check IRQ flag and #%00000100 ; check for SR flag beq @last_256 lda VIA1_SR ; get data sta sd_data_buffer+$100,x inx bne @last_256 jsr spi.get_byte ; get CRC jsr spi.get_byte sec ; set carry on success rts sd.get_blockx jsr .sd_block_cmdx ; address already set in in sd_cmd_dat structure beq .sd_get_bdata .sdgb_error lda #SD_FILE_ERR ; return file error code sta sd_cmd_dat+0 clc ; clear carry on failure rts ; send read block (cmd 17) but dont read any actual data ; returns 'equal' on success ;-------------------------------------------------------------------------- .sd_block_cmd stx zp_source+0 ; low byte in XR sty zp_source+1 ; high byte in YR ldy #3 ; move block address to command buffer @set_cmd_data lda (zp_source),y sta sd_cmd_dat+1,y dey bpl @set_cmd_data .sd_block_cmdx lda #17 ; entry point if sd_cmd_dat structure set directly jsr sd.send_cmd ; command 17 = read a single block (arg = block number) bne @end ldy #0 @wait_data jsr spi.get_byte ; wait until card is ready to send block data cmp #$FF bne @return dey bne @wait_data @return cmp #$FE ; did we get 'ok' token ($FE) or error? @end rts ;-------------------------------------------------------------------------- ; send command to sd card. command number in AR. Args in sd_cmd_dat 1-4 ; returns first byte of repsonse in AR. negative value = error ;-------------------------------------------------------------------------- sd.send_cmd ora #$40 ; set first bit of command sta sd_cmd_dat+5 ; store at end of command data (we send from end due to dex) @set_crc ldx #$FF ; standard CRC for most commands cmp #$48 ; cmd8 has a different CRC bne @store_crc lda #$00 sta sd_cmd_dat+4 sta sd_cmd_dat+3 lda #$01 sta sd_cmd_dat+2 lda #$AA ; pattern for response sta sd_cmd_dat+1 ldx #$87 ; crc for cmd8 @store_crc stx sd_cmd_dat+0 ; store at start of command data (we send from end due to dex) @check_idle jsr spi.set_input jsr spi.get_byte eor #$FF ; idle = $FF beq @wait rts @wait lda VIA1_IFR ; check IRQ flag and #%00000100 ; check for SR flag beq @wait ; send a command to SD card ;-------------------------------------------------------------------------- sd.send_cmdx jsr spi.set_output ; setup SPI to output ldx #5 @send_cmd lda sd_cmd_dat,x jsr spi.send_byte dex bpl @send_cmd ; get response after command was sent. returns negative on error ;-------------------------------------------------------------------------- sd.get_response jsr spi.set_input ldx #8 ; wait for response @get_byte jsr spi.get_byte bpl @return ; first bit of first byte of response will always be 0 dex ; retry bne @get_byte @return sta sd_cmd_dat+0 ; save return value for later retrieval rts ; set SPI port to input or output ;-------------------------------------------------------------------------- spi.set_input lda VIA1_ACR and #%11100011 ; mask out SR control bits ; ora #%00000100 ; bit 2-4: 001 = SR shift in under control of T2 ora #%00001000 ; bit 2-4: 001 = SR shift in under control of PHI2 sta VIA1_ACR ; enable SR control by T2 with clock out on CB1 lda #(SPI_INPUT+SPI_SS1) ; set SR to input and SD card CS = 0 sta VIA1_PORTB rts spi.set_output lda VIA1_ACR and #%11100011 ; mask out SR control bits ; ora #%00010100 ; bit 2-4: 101 = shift out under control of T2 ora #%00011000 ; bit 2-4: 101 = shift out under control of PHI2 sta VIA1_ACR ; enable SR control by T2 with clock out on CB1 lda #(SPI_OUTPUT+SPI_SS1) ; set SR to output and SD card CS = 0 sta VIA1_PORTB rts ;-------------------------------------------------------------------------- ; send and receive byte (non optimized since send waits after sending) ;-------------------------------------------------------------------------- spi.send_byte sta VIA1_SR ; send data to shift register (also clears SR flag in IFR) @wait lda VIA1_IFR ; check IRQ flag and #%00000100 ; check for SR flag beq @wait rts spi.get_byte lda VIA1_IFR ; check IRQ flag and #%00000100 ; check for SR flag beq spi.get_byte ; wait until SR flag is set (when previous shift operation is completed) lda VIA1_SR ; get data (also clears SR flag in IFR) rts ;-------------------------------------------------------------------------- ; data ;--------------------------------------------------------------------------