
;SDIO TEST PROGRAM tests the D16/M SD card interface module and any installed SD
;                  flash memory cards under Mode 1 (state-machine control).  It
;                  determines the presence/write protect status of the cards,
;                  and then writes and reads each logical block of 512 bytes on
;                  a selected unit.  It begins with block 0 and proceeds until
;                  a block write or read operation fails.  The largest block
;                  address permitted by this version of the program is FFFFH; a
;                  32 MB SD card "fails" at block address E980H.
;
		CPU	"D16.TBL"	;program is D16 assembly language
		HOF     "MOT16"		;output Motorola 16-bit hex format
		WDLN	2	 	;word length = 2 bytes (not 1)
;
;Constants, addresses, and definitions.
;
ETX             EQU	03H		;ASCII Ctrl-C
CR		EQU     0DH		;ASCII carriage return
LF		EQU	0AH		;ASCII line feed
SPACE		EQU	20H		;ASCII space
BACK		EQU	08H		;ASCII backspace, Ctrl-H
NUL		EQU	00H		;ASCII null character
ESC		EQU	1BH		;ASCII escape character
ZERO		EQU	30H		;ASCII 0
SDSTATUS	EQU	1000H		;SD card I/F STATUS port address
SDMODE		EQU	1001H		;SD card I/F MODE port address
SDCTRL		EQU	1002H		;SD card I/F CTRL port address
SD_RD		EQU     1003H           ;SD card I/F shift register read addr
SD_RW		EQU	1004H		;SD card I/F shift register R/W addr
SERAD		EQU     2000H		;Serial Port A Data
SERAS		EQU	2001H		;Serial Port A Status
;
;Initializations.
;
		ORG	9000H		;program origin in RAM
		DII			;explicitly disable hardware interrupts
		JMP	START		;and go
;
;Scratch memory and data storage.
;
SEED1		DFS	1		;seed no. 1 for random number generator
SEED2		DFS	1		;seed no. 2 for random number generator
BASE		DFS	1		;base address scratch
SDSTATVAL	DFS	1		;SD card interface STATUS value scratch
CHAR1		DFS	1		;first user input character
CHAR2		DFS	1		;second user input character
INITCTR		DFS	1		;counter word for SDINIT subroutine
RESPCTR		DFS	1		;counter word for SDRES subroutine
RWCTR		DFS	1		;counter word for READBLOCK, WRITEBLOCK
UNIT		DFS	1		;storage for logged SD card unit number
UNITEN		DFS	1		;storage for logged SD card enable word
BLOCK_NO	DFS	1		;storage for current test block number
BLOCK0		DFS	256		;storage for 256 word memory block 0
BLOCK1		DFS	256		;storage for 256 word memory block 1
;
;Text messages for display on the console.  The number of characters in any
;message (including the nulls) must be even, or the assembler will malfunction
;(software bug).
;
M1		DFB	"SD CARD/INTERFACE TEST PROGRAM", NUL, NUL
M2		DFB	"UNIT ", NUL
M3		DFB	" IS PRESENT.  IT IS WRITE ", NUL, NUL
M4		DFB	" IS ABSENT.", NUL
M5		DFB	"PROTECTED.", NUL, NUL
M6		DFB	"ENABLED.", NUL, NUL
M7		DFB	"ENTER UNIT TO TEST: ", NUL, NUL
M8		DFB	"UNIT ABSENT OR INVALID", NUL, NUL
M9		DFB	"TESTING BLOCK ", NUL, NUL
M10		DFB	"READ ERROR.", NUL
M11		DFB	"WRITE ERROR.", NUL, NUL
M12		DFB	"R/W COMPARE ERROR.", NUL, NUL
;
;Main program begins here.
;
START		LDS     0FFFFH		;initialize stack
		LDA	418		;initialize first RNG seed 
		STA	(SEED1)
		LDA	1279		;initialize second RNG seed (note odd)
		STA	(SEED2)
		CSR 	CRLF		;move down the screen a bit	
		CSR	CRLF
		LDA	M1		;display sign-on message
		CSR	MESSAGE
		CSR     CRLF
;
;Examine SD card interface status to determine which cards are inserted, and
;whether they are write protected or write enabled.  Display results.
;
SDIOCHECK	CSR	CRLF
		INP     (SDSTATUS)	;read the SD card status
		STA	(SDSTATVAL)	;save it
		LDA	0		;preset the unit number to 0
		STA	(UNIT)
;
PRESENTLOOP	CSR     CRLF
		LDA     M2		;display "UNIT"
         	CSR	MESSAGE
		LDA	(UNIT)		;display unit number
		CSR	BIN2ASCII
		CSR	CHOUT
		LDA	(SDSTATVAL)	;get SD card status
		AND	0001		;mask off all but /CARD INSERTED bit
		JOZ	PRESENT
ABSENT		LDA	M4		;if bit is not zero, display "IS ABSENT"
		CSR	MESSAGE	
		LDA     (SDSTATVAL)	;and go check the next one
		JMP	NEXTCHECK
PRESENT		LDA	M3		;but if not, display "IS PRESENT"
		CSR	MESSAGE
;
PROTCHECK	LDA	(SDSTATVAL)	;get status again
		AND	0002		;mask off all but /WRITE ENABLED bit
		JOZ	ENABLED		
PROTECTED	LDA	M5		;if bit isn't zero, display "PROTECTED" 
		CSR	MESSAGE
		LDA	(SDSTATVAL)
		JMP     NEXTCHECK	
ENABLED		LDA	M6		;else display "ENABLED"
		CSR	MESSAGE
		LDA	(SDSTATVAL)
;
NEXTCHECK	LSR			;advance to next unit		
		LSR
		STA     (SDSTATVAL)
		LDA	(UNIT)		
		INC
		STA	(UNIT)
		SUB	4		;have we completed unit 3?  Then
		JOZ	DONECHECK	;recover status for later use, and quit
		JMP	PRESENTLOOP	;otherwise, check next unit
;
DONECHECK	INP	(SDSTATUS)	;save status again for subsequent code
		STA	(SDSTATVAL)
;	
;Next, ask which unit to test.  
;
WHATUNIT	CSR	CRLF
		CSR 	CRLF
		LDA	M7		;ask for unit number
		CSR	MESSAGE		
;
FIRSTCHAR	CSR	CHIN		;get first character from the console
		STA	(CHAR1)		;save it
		SUB	ETX		;Ctrl-C?
		JOZ     BAILOUT		;then return to the console monitor
		LDA	(CHAR1)
		SUB	BACK		;backspace?
		JOZ	FIRSTCHAR	;then go get another
		LDA	(CHAR1)		
		SUB	CR		;carriage return?
		JOZ	SET0		;then just assume unit 0
		LDA	(CHAR1)		
		SUB	ESC		;ESCAPE?
		JOZ	START		;then restart the program
		LDA	(CHAR1)		;if anything else, echo to the console
		CSR	CHOUT
SECONDCHAR	CSR	CHIN		;now, get a second character
		STA	(CHAR2)		;save it
		SUB	ETX		;Ctrl-C?
		JOZ     BAILOUT		;then return to the console monitor
		LDA	(CHAR2)
		SUB	BACK		;backspace?
		JNZ	CHKCR
		LDA	BACK		;if so, backspace and erase first char
		CSR	CHOUT
		LDA	SPACE	
		CSR     CHOUT
		LDA     BACK
		CSR     CHOUT
		JMP	FIRSTCHAR	;and go get a new one
CHKCR		LDA	(CHAR2)		
		SUB	CR		;carriage return?
		JOZ	SDSETUP		;then done.  Go set up
		LDA	(CHAR2)		
		SUB	ESC		;ESCAPE?
		JOZ	START		;then restart the program
;
		JMP	SECONDCHAR	;if anything else, ignore, keep looking
;
SET0		LDA	ZERO		;set default unit 0
		STA	(CHAR1)
;
;we have a user-input unit number now. We log the selected SD card and set up
;the UNITEN word for the WRITEBLOCK and SDEN subroutines.
;
SDSETUP		LDA	(CHAR1)		;get the input character (unit number)
		CSR	LOGUNIT		;log the unit
		JNC	SDINITLOOP	;if no error, go initialize SD card
		CSR	CRLF		;otherwise display "INVALID UNIT"
		LDA	M8
		CSR	MESSAGE
		JMP	WHATUNIT	;and ask for a new unit number		
;
;now we initialize the SD card.
;
SDINITLOOP	CSR	SDINIT		;initialize the SD card for normal ops
		NOP
		JOC	SDINITLOOP	;if initialization fails, try it again
;
;then, proceed with test.
;
		CLR			;set starting block addr of 0000H
		STA	(BLOCK_NO)
                CSR	CRLF		;move down the page
		LDA	M9		;display "TESTING BLOCK "
		CSR	MESSAGE
;	
MAIN_LOOP	LDA	(BLOCK_NO)	;get the current block number
		AND	0F000H		;isolate nybble 3
		LSR			;place it into nybble 0
		LSR
		LSR
		LSR
		BSR
		CSR	BIN2ASCII	;convert it to ASCII
		CSR	CHOUT		;and display it
;
		LDA	(BLOCK_NO)	;isolate nybble 2
		AND	0F00H
		BSR			;place it into nybble 0
		CSR     BIN2ASCII	;convert
		CSR	CHOUT		;display
;
		LDA	(BLOCK_NO)	;isolate nybble 1
		AND	00F0H		
		LSR			;place it into nybble 0
		LSR
		LSR
		LSR
		CSR	BIN2ASCII	;convert
		CSR	CHOUT		;display
;
		LDA	(BLOCK_NO)	;isolate nybble 0
		AND	000FH
		CSR     BIN2ASCII	;convert
		CSR	CHOUT		;display
;
;now we initialize two blocks of memory
;
		LDX	BLOCK0		;fill memory block 0 with pseudo-
		LDY	256		;random numbers
		CSR	FILLR
;
		LDX	BLOCK1		;fill memory block 1 with FFFFH
		LDY	256		
		LDA	0FFFFH
		CSR	FILL
;
;write the contents of BLOCK0 to the SD card, at the current block address
;
		LDY	BLOCK0
		LDX	(BLOCK_NO)
		CSR	WRITEBLOCK
		JNC	READIT		;if write OK, go to read routine 
		CSR	CRLF		;otherwise, print WRITE error message
		CSR	CRLF
		LDA	M11
		CSR	MESSAGE
		JMP     BAILOUT
;
;and read it back, into BLOCK1
;
READIT		LDY	BLOCK1
		LDX	(BLOCK_NO)
		CSR	READBLOCK
		JNC	COMPIT		;if write OK, go to compare routine 
		CSR	CRLF		;otherwise, print READ error message
		CSR	CRLF
		LDA	M10
		CSR	MESSAGE
		JMP     BAILOUT
;
;now, we compare the blocks to see if we read what we wrote
;
COMPIT		LDX	BLOCK0		;compare block 0 with block 1
		LDY	BLOCK1
		LDA	256
		CSR	COMPARE
		JNC	FAIL	
;
;if we get here, the compare is presumed to be good, and we proceed to the
;next logical block.
;
		LDA	(BLOCK_NO)      ;increment the block number
		INC	
		STA	(BLOCK_NO)
		LDA	BACK		;send four backspaces to the console
		CSR     CHOUT
		CSR     CHOUT
		CSR	CHOUT
		CSR 	CHOUT
		JMP	MAIN_LOOP	;and loop again!
;			
FAIL		CSR     CRLF		;if comparison fails, error
		CSR	CRLF	
		LDA	M12		
		CSR     MESSAGE
;
BAILOUT		JMP 	0000H		;return to console monitor
;
;PROGRAM SUBROUTINES begin here.
;
;ASCII2BIN SUBROUTINE accepts an ASCII character in bits 6-0 of the Accumulator,
;                     presumed to be representative of a hex digit from 0 to F,
;                     and converts it to binary.  It then returns the binary
;                     number in bits 3-0 of the Accumulator, with bits 15-4 and
;                     the Carry flag cleared to zero.  The subroutine checks to
;                     ensure that the ASCII input ranges from 0 to F; the alphas 
;		      may be either upper or lower case.  If the input is out of
;                     range, the subroutine returns the original character, with
;                     the Carry flag set.
;
ASCII2BIN      	AND	007FH		;zero out all but bits 6-0
		PSA			;save the result
;
TEST1		SUB	0030H   	;is number >= 30H?
                JMI	OUT     	;if not, out of range
                SUB     000AH   	;is number >= 3AH?
                JMI     _0TO9_1 	;if not, number is 30H to 39H
;
TEST2		ADD     003AH 		;A to F? Recover original character
		AND	005FH   	;force upper case
		SUB     0041H   	;is number >= 41H?
                JMI     OUT		;if not, out of range
                SUB     0006H  		;is number >= 47H?
                JMI     ATOF    	;if not, number is 41H to 46H
                JMP     OUT	    	;but if it is, out of range
;
_0TO9_1		POA			;get original character
                SUB     0030H   	;subtract 30H to get binary
                CCF             	;clear Carry
                RTN			;done
;
ATOF            POA             	;get original character
		AND     005FH		;force upper case
                SUB     0037H   	;subtract 37H to get binary
                CCF             	;clear Carry
                RTN             	;done
;         
OUT             POA             	;restore original character
                SCF             	;assert Carry
                RTN			;done
;
;BIN2ASCII SUBROUTINE accepts the contents of the Accumulator and converts the
;                     least-significant four bits to ASCII.  It then returns the
;                     ASCII character in bits 6-0 of the Accumulator, with bits
;                     15-7 cleared to zero.
;
BIN2ASCII	AND 	000FH		;zero out all but LS nybble
		SUB     10		;test--
		JMI     _0TO9_2       	;if less than 10, must be 0 to 9
		ADD     7		;if A to F, add 55 total to orig value
_0TO9_2		ADD     58		;if 0 to 9, add 48 total to orig value
                RTN                     ;done
;  
;CHIN SUBROUTINE reads an ASCII character from Serial Port A, and returns it in
;                the Accumulator, with the MS byte cleared.
;
CHIN		INP	(SERAS)		;get Serial Port A status
                AND     0008H		;mask all but Data Received bit
		JOZ	CHIN		;if data not available, keep looking
		INP	(SERAD)		;otherwise, get character
		AND     007FH		;mask all but least sig 7 bits
		RTN			;done
;
;CHOUT SUBROUTINE writes an ASCII character, in the LS byte of the Accumulator,
;		  to Serial Port A.
;
CHOUT		PSA			;save input character
CHOU1		INP	(SERAS)		;UART output buffer empty?
                AND	0010H
		JOZ	CHOU1
		POA			;when it is, recover the character
		AND	007FH		;mask all but least sig 7 bits
		OUT	(SERAD)		;and transmit
		RTN
;
;COMPARE SUBROUTINE compares the contents of two identically-sized blocks of
;                   memory.  It accepts the starting address of block 1 in IX,
;                   of block 2 in IY, and the number of words in each block in
;                   the Accumulator.  If the blocks are the same, the subroutine
;                   returns with the Carry flag set; if any pair of words is
;                   found to be different, the subroutine aborts and returns
;                   with the Carry flag clear.  In either case, the index
;                   registers will contain the addresses of the last words
;                   checked, and the Accumulator will contain the number of
;                   words remaining in each block. 
;
COMPARE		PSA			;save the word count
		LDA	(0+IX)		;get value from block 1
		SUB	(0+IY)		;compare to value from block 2
		JNZ	COMPARE1	;quit if they're different
		POA			;but if the same, get word count
		DEC			;subtract one
		JOZ	COMPARE2	;zero?
		INX			;if not, point to next words
		INY
		JMP	COMPARE		;and keep comparing
;
COMPARE1	POA			;get word count (restoring stack)
		DEC			;subtract one 
		CCF			;flag no compare
		RTN			;and quit immediately	
;
COMPARE2	SCF			;flag valid compare (stack is OK)
		RTN
;
;CRLF SUBROUTINE sends a carriage return and a line-feed to the console.  It
;		 calls the CHOUT subroutine.
;
CRLF            LDA	CR		;send a carriage return
		CSR	CHOUT
		LDA	LF		;send a line-feed
		CSR	CHOUT
		RTN			;that's it
;
;FILL SUBROUTINE fills a block of memory with a value. It accepts the starting
;                address of the block in IX, the number of words in IY, and the
;                filling value in the Accumulator.
;
FILL		STA	(0+IX)		;store value in current block address
		INX			;point to the next block address
		PSA			;set the value aside
		MYA			;get the word count
		DEC			;subtract one
		JOZ	FILL1		;zero?
		MAY			;if not, put count back
		POA			;recover the value
		JMP	FILL		;and fill the next word in the block
;
FILL1		POA			;done:  correct the stack
		RTN			;before returning
;
;FILLR SUBROUTINE is just like the FILL subroutine, except that it fills the 
;                 block with pseudorandom numbers instead of a fixed value.
;                 It calls the RANDOM subroutine.
;
FILLR		CSR	RANDOM		;generate a random number
		STA	(0+IX)		;store it in current block address
		INX			;point to the next block address
		MYA			;get the word count
		DEC			;subtract one
		JOZ	FILLR1		;zero?
		MAY			;if not, put count back
		JMP	FILLR		;and fill the next word in the block
;
FILLR1		RTN	
;
;LOGUNIT SUBROUTINE logs a specific SD card unit for reading and writing.  It
;                   sets up the unit enable word for the SDEN subroutine, as
;		    well as the write protect status.  It accepts an ASCII unit
;                   number, taken from the command tail, and if the character
;                   does not represent a valid unit or if the unit is not 
;                   present, the subroutine returns with the Carry flag set.
;
LOGUNIT		CSR	ASCII2BIN	;convert unit char to binary unit number
		PSA			;set it aside
		JNZ	LOGU1		;zero?
		LDA	(SDSTATVAL)	;yes 
		AND	01H		;mask off all but presence bit
		JNZ	LOGU4		;error exit if unit not present
		LDA	(SDSTATVAL)	;but if it is there, then
		AND	02H		;isolate write protect bit
		BSL			;and put it in MS byte of select word
		ORA	03H		;set "zero" select
		JMP	LOGU5		;and go save the select word
LOGU1		SUB	1		;one?
		JNZ	LOGU2
		LDA	(SDSTATVAL)	 
		AND	04H		
		JNZ	LOGU4	
		LDA	(SDSTATVAL)	
		AND	08H		
		BSL			
		ORA	05H		;set "one" select
		JMP	LOGU5		
LOGU2		SUB	1		;two?
		JNZ	LOGU3
		LDA	(SDSTATVAL)	 
		AND	10H		
		JNZ	LOGU4	
		LDA	(SDSTATVAL)	
		AND	20H		
		BSL			
		ORA	09H		;set "two" select
		JMP	LOGU5		
LOGU3		SUB	1		;three?
		JNZ	LOGU4
		LDA	(SDSTATVAL)	 
		AND	40H		
		JNZ	LOGU4	
		LDA	(SDSTATVAL)	
		AND	80H		
		BSL			
		ORA	11H		;set "three" select
		JMP	LOGU5		
;
LOGU4		SCF			;not valid unit, or absent.  Flag error
		POA			;restore stack
		RTN			;and quit
;
LOGU5		STA	(UNITEN)	;save completed select word
		POA			;recover unit number and store it
		STA	(UNIT)	
		RTN			;done
;
;MESSAGE SUBROUTINE sends a text message, whose starting address is in the 
;                   Accumulator, to the console.  It stops when it reaches the               
;                   ASCII NUL character placed at the end of the string in
;                   the DFB statement.  This subroutine uses the scratch word
;                   BASE, and calls the CHOUT subroutine.
;
MESSAGE		STA	(BASE)		;set the base address
		LDX	0		;set the index
MESS1		LDA	[BASE+IX]	;get the first (MS) character
		BSR			
		JOZ	MESS2		;zero?  Then we're done
		CSR	CHOUT		;if not, output the character
		LDA	[BASE+IX]	;now get the second (LS) character
		AND	00FFH
		JOZ	MESS2		;if zero we're done
		CSR	CHOUT		;else output the character
		INX			;then point to next character pair
		JMP	MESS1		;and keep going
MESS2		RTN
;
;PROMPT SUBROUTINE outputs a prompt (CR,LF,>) to the console.  It calls the
;                  CHOUT subroutine.
;
PROMPT		LDA     CR		;send a carriage return
		CSR     CHOUT
		LDA	LF		;send a line feed
		CSR	CHOUT
		LDA     ">"		;display the prompt
		CSR	CHOUT
		RTN	
;
;RANDOM SUBROUTINE generates a pseudorandom number using a Fibonacci sequence.
;                  It operates on numbers stored in memory locations SEED1 and
;                  SEED2, and it returns the 16-bit number in the Accumulator.
;                  WARNING:  This generator is useful for electronic testing,
;                  but it is not suitable for critical applications such as
;                  cryptography.
;
RANDOM		LDA     (SEED1)		;get the first seed
	        PSA			;set it aside for a moment
		LDA	(SEED2)		;get the second seed
		STA	(SEED1)		;save it as SEED1 for next run
		POA			;recover the first seed again
		SHL			;rotate left (not through CF) 4 times
		ADC	0		;(multiplying by 16)
		SHL
		ADC	0
		SHL
		ADC 	0
		SHL
		ADC	0	
		ADD	(SEED2)		;add to SEED2
		STA	(SEED2)		;and save as new SEED2 for next run
		RTN			;random number is still in Accumulator
;
;READBLOCK SUBROUTINE reads a block from the selected SD card.  It accepts the
;                     logical sector number (0-FFFFH) in IX and the address of
;                     the 256-word memory buffer in IY.  If the read was
;                     successful, it returns with the Carry flag clear.
;
READBLOCK	PSA			;save registers
		PSX	
		PSY			;(must be pushed last)
;
		CSR	SDEN            ;enable the SD card
		LDA	17		;issue block READ command to SD card
		CSR	SDCMD		;(17,<block addr>*512,95H)
		ADD	0FFFFH		;error exit if response was not 00H
		JOC	RESTORE
;
		CSR	SDRES		;read the start byte, FEH
;
		LDA	256		;and then, for 256 words--
		STA	(RWCTR)
READ01		CSR     READBYTE	;read a byte from the SD card
                BSL                     ;pack it into the MS byte of the word
                STA     (0+IY)          
		CSR	READBYTE        ;read a second byte
		ORA	(0+IY)		;pack it into the LS byte
		STA     (0+IY)		;store completed word into the buffer
		INY	                ;point to next word
		DSZ	(RWCTR)		
		JMP     READ01
;
		CSR	READBYTE	;read 16-bit checksum and ignore it
		CSR	READBYTE
		CCF			;indicate successful read
;
		JMP	RESTORE		;return via SDINIT RESTORE routine
;
;READBYTE SUBROUTINE receives a byte from the SD card and returns it in the
;                    low Accumulator.
;
READBYTE	SET			;send a dummy byte (FF)
	        OUT	(SD_RW)		  			
		INP	(SD_RD)		;read card response byte
		AND	0FFH
		RTN
;
;SDCMD SUBROUTINE sends a command to the SD card and returns a response.  It 
;                 accepts the command in AC low and the argument (a sector or
;                 block address) in IX. It "falls into" the SDRES subroutine,
;                 which returns the response byte in AC low.
;
SDCMD		ORA	0040H		;fit "start bits" to command byte
                CSR	WRITEBYTE	;and send it
;
		MXA			;get the block address
		SHL			;multiply by 2
		MAX			;store it back
		CLR			;then, if the multiply op carried,
		ADC	0		;send 01H as arg0
		CSR	WRITEBYTE	;but if not, send 00H as arg0
;
		MXA			;send high byte of IX as arg1
		BSR
		CSR	WRITEBYTE
;
		MXA			;send low byte of IX as arg2
		CSR	WRITEBYTE
;
		CLR			;send 00H as arg3 (thus, we have 
		CSR	WRITEBYTE	;multiplied the block address by 512)
;
		LDA	0095H		;send CRC byte (actual for power-up init,
		CSR     WRITEBYTE        ;dummy for subsequent commands)
;
;SDRES SUBROUTINE receives the response from the SD card, which may arrive at up
;                 at up to 8 bytes after the command.  Its code is executed on 
;                 every SDCMD call, as well as on SDRES calls.
;
SDRES		LDA	64		;but, loop up to 64 times!
		STA	(RESPCTR)
SDRES01		CSR	READBYTE	;read a response byte
		SUB     0FFH		;exit loop if response is any but FFH
                JNZ	SDRES02
		DSZ	(RESPCTR)
		JMP	SDRES01
SDRES02		ADD	00FFH		;restore accumulator
		RTN
;
;SDDIS SUBROUTINE negates the chip enable on the SD card.
;
SDDIS	        LDA	01H		;program SD card disabled, MODE 1
		OUT  	(SDMODE)
		RTN
;
;SDEN SUBROUTINE asserts the chip enable on the SD card.
;		
SDEN		LDA	(UNITEN)	;program SD card enabled, MODE 1
		OUT	(SDMODE)
		RTN	
;
;SDINIT SUBROUTINE initializes the SD card for use after power-up.  If the
;                  process is successful, it returns with the Carry flag clear.
;                  Its RESTORE routine is shared with other subroutines
;                  (READBLOCK and WRITEBLOCK), which jump to it rather than 
;                  incorporating a copy themselves.
;		
;
SDINIT		PSA			;save registers
		PSX	
		PSY			;(must be pushed last)
;
		CSR	SDDIS		;de-select the SD card
		LDA	10		;transmit 80 dummy clock cycles
                STA	(INITCTR)
SDINIT01	CSR	READBYTE
		DSZ	(INITCTR)
		JMP	SDINIT01
		CSR	SDEN		;re-select the SD card
;	
		CLR			;send command 0 to SD card (select SPI)
              	LDX	0000H
		CSR	SDCMD		;(00,00,00,00,00,95H)
		DEC			;if response is not 01H then exit with
		SCF			;carry flag set to indicate error
		JNZ	RESTORE
;
SDINIT02	CSR     READBYTE	;generate 8 dummy clocks on SD card
		LDA	01H		;send command 1 (args, CRC don't matter)
		CSR	SDCMD           ;(01,00,00,XX,XX,95) 
		DEC			;loop until response not 01H
		JOZ	SDINIT02
		INC			;clear Carry flag if response was 00H
		ADD     0FFFFH		;else set it to indicate error
;
RESTORE	        PSF			;save Carry flag
		CSR	SDDIS		;disable the SD card
		CSR	READBYTE	;generate 8 dummy clock cycles
		POF			;restore Carry flag
;		
		POY			;recover registers
		POX
		POA
		RTN
;
;WRITEBLOCK SUBROUTINE writes a block to the selected SD card. It accepts the
;                     logical sector number (0-FFFFH) in IX and the address of
;                     the 256-word memory buffer in IY.  If the write was
;                     successful, it returns with the Carry flag clear.
;
WRITEBLOCK	PSA			;save registers
		PSX			
		PSY			;(must be pushed last)
;
		LDA	(UNITEN)	;are we write protected? 
		AND	0FF00H
		JOZ	WRIT01		;if not, proceed
		SCF			;otherwise, flag error
		JMP	RESTORE		;and exit
WRIT01		CSR	SDEN
		LDA	24		;issue SD card block WRITE command
		CSR	SDCMD		;(24,<block addr>*512,95H)
		ADD	0FFFFH		;error exit if response is not 00H
		JOC	RESTORE
		CSR	READBYTE	;send 8 dummy clock cycles
		LDA	00FEH		;send start byte to SD card
		CSR	WRITEBYTE
;
		LDA     256             ;for 256 words--
		STA	(RWCTR)
WRIT02		LDA     (0+IY)		;get word from buffer
		BSR			;send MS byte to SD card
		CSR	WRITEBYTE	
		LDA	(0+IY)		;get word again
		CSR	WRITEBYTE	;and send the LS byte to the card
		INY			;point to the next buffer word
		DSZ	(RWCTR)
		JMP	WRIT02
;
		CSR	READBYTE	;send dummy checksum (FFFFH)
		CSR	READBYTE
;
		CSR	READBYTE	;read data response byte (05H)
;
WRIT03		CSR	READBYTE	;and loop until no longer busy
		JOZ	WRIT03		;(could take a while)
		CCF			;indicate successful write op
		JMP	RESTORE		;and return via SDINIT RESTORE code

;
;WRITEBYTE SUBROUTINE accepts a byte in low Accumulator and sends it to the
;                     SD card interface.
;          
WRITEBYTE	OUT	(SD_RW)		;send the byte
		RTN
;
		END			;end of program code
