fl { This driver is in part a refactor from caskaz's work. It is intended to abstract the low level functions necessary to initialize the sd card, and read and write 512 byte blocks to/from the card. It is thread safe, as long as the routines below are used for access. This means mulitple cogs can use the sd card at the same time. Each cog must call sd_init, calling sd_init multiple times is ok. For read modify write operations sd_lock and sd_unlock should be used to ensure no other cog does anything with the sd card. Lock 3 is used for the sd card. sd_init - initializes the card, should be ok for version 2+ Standard and High Capacity. Only tested with a 2 GB Kingston standard card. and 8GB no name phone cards sd_blockread - reads up to a 512 byte block to cog memory as 128 longs, the starting address is defined by sd_cogbuf sd_blockwrite - writes a 512 byte block from cog memory as 128 longs, the starting address is defined by sd_cogbuf The longs are read off the disks Most Significant Bit first. When reading a long as a long it means the long is coming in as a big endian long. The prop uses little endian longs. If reading data that is exchanged with other systems, or system data off the card this must be compensated for. sd_lock - locks the card for use by this cog sd_unlock - unlocks the card sd_cogbuf - the starting address of the data in cog memory, 128 longs sd_cogbufclr - zeros out the cog buffer References: REF1: SD Specifications Part 1 Physical Layer Simplified Specification Version 3.01 May 18, 2010 Available at: http://www.sdcard.org/developers/tech/sdcard/pls/simplified_specs/ REF2: http://elm-chan.org/docs/mmc/mmc_e.html REF3: http://elm-chan.org/docs/mmc/sdinit.png } hex 1 wconstant build_sddriver \ aallot ( n1 -- ) add n1 to coghere, allocates space in the cog or release it, n1 is # of longs [ifndef aallot : aallot coghere W+! coghere W@ par >= if 10 ERR then ; ] \ cog, ( x -- ) allocate 1 long in the cog and copy x to that location [ifndef cog, : cog, coghere W@ COG! 1 aallot ; ] \ lasm ( addr -- ) expects an address pointing to a structure in the following form \ empty long, long upper address of the assembler routine, long lower address of the assembler routine \ a series of longs which are the assembler codes [ifndef lasm : lasm 4+ dup L@ swap 4+ swap over L@ dup coghere W! do 4+ dup L@ cog, loop drop ; ] \ \ variable ( -- ) skip blanks parse the next word and create a variable, allocate a long, 4 bytes [ifndef variable : variable lockdict create $C_a_dovarl w, 0 l, forthentry freedict ; ] \ \ constant ( x -- ) skip blanks parse the next word and create a constant, allocate a long, 4 bytes [ifndef constant : constant lockdict create $C_a_doconl w, l, forthentry freedict ; ] \ \ CONFIG PARAMETERS BEGIN \ \ definitions for io pins connecting to the sd card 0 wconstant _sd_cs 1 wconstant _sd_di 2 wconstant _sd_clk 3 wconstant _sd_do \ \ comment out this variable if debugging is not required \ wvariable _sd_debug FF _sd_debug W! \ \ CONFIG PARAMETERS END \ 16B wconstant v_currentdir 160 asmlabel a_sd_shift_inlong 15E asmlabel a_sd_shift_in 155 asmlabel a_sd_shift_outlong 152 asmlabel a_sd_shift_out 151 wconstant v_sd_clk 150 wconstant v_sd_di 14F wconstant v_sd_do lockdict variable def_sdasm 016C l, 014F l, 0 l, 0 l, 0 l, A0FE1C08 l, 2CFE1018 l, 5C7C0156 l, A0FE1C20 l, 35FE1001 l, 70BFE950 l, 68BFE951 l, 64BFE951 l, E4FE1D56 l, 64BFE950 l, 5CFDF0F1 l, 5C7C0075 l, A0FE1C08 l, 5C7C0161 l, A0FE1C20 l, 5CFDCEE0 l, A0FE1000 l, 68BFE950 l, 68BFE951 l, 613E9FF2 l, 34FE1001 l, 64BFE951 l, E4FE1D64 l, 64BFE950 l, 5C7C0075 l, 0 l, freedict \ \ set the data area for the buffer at the end of the assembler code, the allocation is done in sd_init \ v_currentdir 1+ wconstant sd_cogbuf \ \ this variable determines whether we use the assembler routines to drive the SD card or the \ forth routines. During card startup we use the forth routines which are slow enough to be in \ for card startup, then we use the assembler routines \ for testing purposes the assembler routines can be individually enabled/disabled \ bit 0 - _sd_shift_out \ bit 1 - _sd_shift_in \ bit 2 - _sd_shift_outlong \ bit 3 - _sd_shift_inlong \ \ wvariable _sd_asm 0 _sd_asm W! wvariable _sd_initialized 0 _sd_initialized W! \ sd_lock ( -- ) : sd_lock 3 lock ; \ sd_unlock ( -- ) : sd_unlock 3 unlock ; \ sd_cogbufclr ( -- ) initialize the buffer to zeros : sd_cogbufclr sd_cogbuf 80 bounds do 0 i COG! loop ; \ \ basic pin functions to drive the sd card \ 1 _sd_cs lshift constant _sd_csm 1 _sd_di lshift constant _sd_dim 1 _sd_clk lshift constant _sd_clkm 1 _sd_do lshift constant _sd_dom : _sd_cs_out _sd_cs pinout ; : _sd_di_out _sd_di pinout ; : _sd_clk_out _sd_clk pinout ; : _sd_do_in _sd_do pinin ; : _sd_cs_out_l _sd_csm _maskoutlo ; : _sd_cs_out_h _sd_csm _maskouthi ; : _sd_di_out_l _sd_dim _maskoutlo ; : _sd_di_out_h _sd_dim _maskouthi ; : _sd_clk_out_l _sd_clkm _maskoutlo ; : _sd_clk_out_h _sd_clkm _maskouthi ; \ \ REF1: p 104 / 92 5.1 OCR register \ \ \ this constant is a reflection of the ccs bit in the OCR \ \ register, if 0, we send byte addresses \ \ ( block aligned) to the card, otherwise block addresses, \ \ only used internally in the driver \ \ initialized by sd_init wvariable _sd_ccs 0 _sd_ccs W! \ \ REF1: p 107 / 95 5.3 CSD Register \ \ \ this variable is a reflection of the CSD structure \ \ version bits in the CSD register, \ \ if 0, it is a Standard card, otherwise a High or \ \ Extended Capacity card \ \ initialized by sd_init wvariable _sd_hc 0 _sd_hc W! \ \ Card Size calculation \ \ REF1: p 111 / 99 SIZE_C \ \ \ this vairable is the maximum block number for the card \ \ initialized by sd_init (card capacity is this * 512) variable _sd_maxblock 0 _sd_maxblock L! \ \ Low level Routines to read and write bytes and longs to the sd card \ \ \ _sd_shift_out ( c1 -- ) shift out one char most siginficant bit first : _sd_shift_out _sd_asm W@ 1 and if a_sd_shift_out else 80 8 0 do 2dup and if _sd_di_out_h else _sd_di_out_l then 1 rshift _sd_clk_out_l _sd_clk_out_h loop 2drop _sd_clk_out_l _sd_di_out_l then ; \ \ _sd_shift_outlong ( n1 -- ) shift out one long most siginficant bit first : _sd_shift_outlong _sd_asm W@ 4 and if a_sd_shift_outlong else dup 18 rshift _sd_shift_out dup 10 rshift _sd_shift_out dup 8 rshift _sd_shift_out _sd_shift_out then ; \ \ _sd_shift_in ( -- c1 ) shift in one char, most siginificant bit first : _sd_shift_in _sd_asm W@ 2 and if a_sd_shift_in else _sd_di_out_h 0 8 0 do 1 lshift _sd_clk_out_l _sd_clk_out_h _sd_dom _maskin if 1 or then loop _sd_clk_out_l _sd_di_out_l then ; \ _sd_shift_inlong ( -- n1 ) shift in one long, most siginificant bit first : _sd_shift_inlong _sd_asm W@ 8 and if a_sd_shift_inlong else _sd_shift_in 8 lshift _sd_shift_in or 8 lshift _sd_shift_in or 8 lshift _sd_shift_in or then ; [ifdef _sd_debug \ 8 times we can record lockdict variable _times 1c allot freedict ] \ \ REF1: p 128 / 116 7.2.3 Data Read \ REF1: p 144 / 132 7.3.3 Control Tokens \ \ _sd_readdata( n1 -- ) n1 number of bytes to read, n1 must be a multilpe of 4, and it does not include the crc, \ the crc is discarded by this routine. The data is read to sd_cogbuf : _sd_readdata [ifdef _sd_debug _sd_debug W@ 2 and if cr ." _sd_readdata readlen: " dup . then cnt COG@ _times 8 + L! ] \ \ divide by 4 to get the long count 200 min 2/ 2/ \ \ wait for the data control token for 4000 loops 0 4000 0 do _sd_shift_in FE = if drop -1 leave then loop [ifdef _sd_debug cnt COG@ _times C + L! ] if \ \ valid data control token \ \ read in the data sd_cogbuf swap bounds do _sd_shift_inlong i COG! loop [ifdef _sd_debug cnt COG@ _times 10 + L! ] \ \ read the crc + 2 extra bytes \ \ make sure the read is finished _sd_shift_inlong FFFF and FFFF <> if \ \ we are getting more data \ \ something is very wrong A2 ERR then else \ \ read timed out A3 ERR then [ifdef _sd_debug cnt COG@ _times 14 + L! _sd_debug W@ 2 and if sd_cogbuf 80 cogdump then ] ; \ \ REF1: p 129 / 117 7.2.4 Data Write \ REF1: p 144 / 132 7.3.3 Control Tokens \ \ _sd_writedata( n1 -- ) n1 number of bytes to write, n1 must be a multilpe of 4, and it does not include the crc, \ the crc is written as FF by this routine. The data is written from sd_cogbuf : _sd_writedata [ifdef _sd_debug _sd_debug W@ 2 and if cr ." _sd_writedata writelen: " dup . sd_cogbuf 80 cogdump then cnt COG@ _times 8 + L! ] 200 min 4 max 2/ 2/ FE _sd_shift_out sd_cogbuf swap bounds do i COG@ _sd_shift_outlong loop -1 dup _sd_shift_out _sd_shift_out 10000 0 do _sd_shift_in dup FF <> if leave else drop then loop 7 and 5 <> if \ \ some kind of write error A4 ERR then [ifdef _sd_debug cnt COG@ _times C + L! ] 10000 0 do _sd_shift_in dup FF = if leave else drop then loop FF <> [ifdef _sd_debug cnt COG@ _times 10 + L! ] if \ \ still busy, something is very wrong A5 ERR then ; \ \ REF1: p 135 / 123 7.3.1.3 Detailed Command Description \ REF1: p 141 / 129 7.3.2 Responses \ REF1: p 70 / 58 Command Format \ \ \ _sd_cmdr8 ( crc arg cmd -- n1 ) send the command, wait for response, n1 - the 8 bit response \ n1 is set to all 1's (FFFFFFFF or -1) in the case of a timeout : _sd_cmdr8 [ifdef _sd_debug _sd_debug W@ 1 and if cr ." _sd_cmdr8 CMD: " dup . ." ARG: " over . then ] \ \ Seems a number of cards need the a couple of clock pulses before the start bit for a command is \ sent. Not yet able to dig up anything in the specs to verify this, but without this, \ a number of cards do not work properly, this gives l->h clock transitions. With limited tesing \ only a couple of clock pulses seem necessary, but it is just as easy to to do a get a byte, \ issues 8 clocks, instead of 3 pulses \ \ \ _sd_di_out_h \ _sd_clk_out_l _sd_clk_out_h \ _sd_clk_out_l _sd_clk_out_h \ _sd_clk_out_l \ _sd_shift_in drop 3F and 40 or _sd_shift_out _sd_shift_outlong _sd_shift_out -1 10 0 do _sd_shift_in dup FF <> if nip leave else drop then loop [ifdef _sd_debug _sd_debug W@ 1 and if ." RESPONSE: " dup . cr then ] ; \ \ REF1: p 135 / 123 7.3.1.3 Detailed Command Description \ REF1: p 141 / 129 7.3.2 Responses \ REF1: p 70 / 58 Command Format \ \ \ _sd_cmdr16 ( crc arg cmd -- n1 ) send the command, wait for response, n1 - the 16 bit response \ n1 is set to all 1's (FFFFFFFF or -1) in the case of a timeout : _sd_cmdr16 [ifdef _sd_debug _sd_debug W@ 1 and if cr ." _sd_cmdr16 CMD: " dup . ." ARG: " over . then ] _sd_cmdr8 8 lshift _sd_shift_in or [ifdef _sd_debug _sd_debug W@ 1 and if ." RESPONSE: " dup . cr then ] ; \ \ REF1: p 135 / 123 7.3.1.3 Detailed Command Description \ REF1: p 141 / 129 7.3.2 Responses \ REF1: p 70 / 58 Command Format \ \ \ _sd_cmdr8data ( datalen crc arg cmd -- n1 ) send the command, wait for response, the read the data into _sd_buf, \ n1 - the 8 bit response n1 is set to all 1's (FFFFFFFF or -1) in the case of a timeout : _sd_cmdr8data [ifdef _sd_debug _sd_debug W@ 1 and if cr ." _sd_cmdr8data readlen: " >r >r >r dup . r> r> r> then cnt COG@ _times 4 + L! ] _sd_cmdr8 dup 0= if swap _sd_readdata then ; \ \ REF1: p 135 / 123 7.3.1.3 Detailed Command Description \ REF1: p 141 / 129 7.3.2 Responses \ REF1: p 70 / 58 Command Format \ \ \ _sd_cmdr40 ( crc arg cmd -- n1 n2 ) send the command, wait for response, n1 - the 8 bit response, \ n2 - 32 bit response, n1 and n2 are set to all 1's (FFFFFFFF or -1) in the case of a timeout : _sd_cmdr40 [ifdef _sd_debug _sd_debug W@ 1 and if cr ." _sd_cmdr40 CMD: " dup . ." ARG: " over . then ] _sd_cmdr8 _sd_shift_inlong [ifdef _sd_debug _sd_debug W@ 1 and if ." RESPONSE: " over . dup . cr then ] ; \ \ Should support SD v2 standard, H & X \ ***** only tested with a Kingston 2G card V2, SDSC, and an 8G no name phone card SDHC \ \ _sd_init ( -- ) initialize the SD card, this routine is only called once for the propeller : _sd_init [ifdef _sd_debug _sd_debug W@ 1 and if cr ." _sd_init ENTER" cr then ] \ \ make sure we are using the forth _sd_shiftXXXXXX routines \ \ necessary for lo clock speed through initialization 0 _sd_asm W! \ \ REF1: p 123 \ REF2: Power On \ \ \ >74 clock pulses, reset \ \ in development, when there was a bug, sometimes \ \ the sd card would be in a bad state \ \ 74 clock pulses did not seem to reset it \ \ 1000 hex did (doesn't take much longer) \ \ we will retry the whole initialize up to 8 times -1 8 0 do _sd_di_out_h _sd_clk_out_h _sd_cs_out_h 1000 0 do _sd_clk_out_l _sd_clk_out_h loop \ \ select the card forever _sd_cs_out_l \ REF1: p 135 / 123 7.3.1.3 Detailed Command Description \ REF1: p 141 / 129 7.3.2 Responses \ REF1: p 126 / 7.2.1 Mode Selection and Initialization \ REF2: Software Reset \ REF3 \ \ \ CMD0 0 - GO_IDLE_STATE 95 0 0 _sd_cmdr8 1 = if drop 0 leave then loop if \ \ card did not respond A6 ERR then \ \ REF1: p 135 / 123 7.3.1.3 Detailed Command Description \ REF1: p 141 / 129 7.3.2 Responses \ REF1: p 126 / 114 7.2.1 Mode Selection and Initialization \ REF1: p 63 / 51 4.3.13 Send Interface Condition Command (CMD8) \ REF1: p 85 / 73 4.9.6 R7 (Card interface condition) \ REF2: How to support SDC Ver2 and high capacity cards \ REF3 \ \ -1 8 0 do \ \ CMD8 1AA - SEND_IF_COND - the card send the \ \ interface condition 87 1AA 8 _sd_cmdr40 1AA = swap 1 = and if drop 0 leave then loop \ \ checking for valid SD ver2 or higher, supports \ \ 2.7 - 3.6 volts if \ \ it is something else, MMC support or sd version \ \ 1 could be added here \ A7 ERR \ SDSC (512Mbyte 256Mbyte) \ CMD55 - CMD41 1 100 0 do 1 0 37 _sd_cmdr8 drop \ CMD55 69 _sd_shift_out \ CMD41 0 _sd_shift_outlong 1 _sd_shift_out [ifdef _sd_debug _sd_debug W@ 1 and if ." CMD: 41 ARG: 0" cr then ] 5 0 do _sd_shift_in [ifdef _sd_debug _sd_debug W@ 1 and if dup ." RESPONSE: " . cr then ] 0= if drop 0 leave then loop dup 0= if leave then loop if A7 ERR then \ then else \ A \ \ REF1: p 135 / 123 7.3.1.3 Detailed Command Description \ REF1: p 141 / 129 7.3.2 Responses \ REF1: p 126 / 114 7.2.1 Mode Selection and Initialization \ REF3 \ \ try ACMD41 40000000 (CMD55 - CMD41) \ \ SD_SEND_OP_COND, initialize \ \ the sd card, telling it we support hi capacity -1 100 0 do 1 0 37 _sd_cmdr8 drop 1 40000000 29 _sd_cmdr8 0= if drop 0 leave then loop if \ \ card did not initialize, doesn't support \ \ hi capacity host? \ \ try ACMD41 0 (CMD55 - CMD41) SD_SEND_OP_COND, \ \ initialize \ \ the sd card, telling it we do not support hi \ \ capacity -1 100 0 do 1 0 37 _sd_cmdr8 drop 1 0 29 _sd_cmdr8 0= if drop 0 leave then loop if \ \ card did not initialize A8 ERR then then then \ A \ \ REF1: p 135 / 123 7.3.1.3 Detailed Command Description \ REF1: p 141 / 129 7.3.2 Responses \ REF1: p 126 / 114 7.2.1 Mode Selection and Initialization \ REF1: p 104 / 92 5.1 OCR register \ \ CMD58 0 - SEND_OCR, get the OCR register, \ \ make sure we are powered up \ \ and find out if the ccs bit is set, \ \ if yes the card is SDHC or SDXC and \ \ can only be addressed as 512 byte blocks 1 0 3A _sd_cmdr40 swap 0<> over 80000000 and 0= or if \ \ card did not indicate ready, or the \ \ power up status was not set A9 ERR then \ \ set the ccs flag accordingly 40000000 and 0<> _sd_ccs W! \ \ REF3 \ REF1: p 135 / 123 7.3.1.3 Detailed Command Description \ REF1: p 141 / 129 7.3.2 Responses \ REF1: p 126 / 114 7.2.1 Mode Selection and Initialization \ REF1: p 104 / 92 5.1 OCR register \ \ CMD16 512 SET_BLOCKLEN, set the block length \ \ to 512 bytes _sd_ccs W@ 0= if 1 200 10 _sd_cmdr8 if \ \ setting the block length to 512 bytes failed AA ERR then then \ \ REF1: p 135 / 123 7.3.1.3 Detailed Command Description \ REF1: p 141 / 129 7.3.2 Responses \ REF1: p 107 / 95 5.3 CSD Register \ \ CMD9 0 - SEND_CSD, get the CSD register, \ \ check the structure version 10 1 0 9 _sd_cmdr8data if AB ERR then sd_cogbuf COG@ 40000000 and if 1 _sd_hc W! \ \ Card Size calculation \ \ REF1: p 116 / 104 SIZE_C \ sd_cogbuf 1+ COG@ 3F and 10 lshift sd_cogbuf 2+ COG@ 10 rshift or 1+ A lshift _sd_maxblock L! else 0 _sd_hc W! \ \ Card Size calculation \ \ REF1: p 111 / 99 SIZE_C \ \ \ READ_BL_LEN , this is the blocksize \ \ for size calc \ _sd_buf 5 + C@ F and >m sd_cogbuf 1+ COG@ 10 rshift F and >m \ \ C_SIZE + 1 \ _sd_buf 6 + dup C@ 3 and 8 lshift over st? \ 1+ C@ or 2 lshift swap st? \ 2+ C@ C0 and 6 rshift or 1+ st? sd_cogbuf 1+ COG@ 3ff and 2 lshift sd_cogbuf 2+ COG@ 1e rshift or 1+ \ \ 2 exp C_SIZE_MULT +2 \ _sd_buf 9 + dup C@ 3 and 1 lshift \ swap 1+ C@ 7 rshift or 2+ >m sd_cogbuf 2+ COG@ F rshift 7 and 2+ >m \ \ the maximum block number u* u* 200 u/ _sd_maxblock L! then [ifdef _sd_debug _sd_debug W@ 1 and if _sd_hc W@ if ." SH" else ." SD" then ." SC initialize success # of Blocks: " _sd_maxblock L@ . cr cr ." _sd_init EXIT" cr then ] ; \ sd_init ( -- ) call for every cog using the sd card : sd_init [ifdef _sd_debug _sd_debug W@ 1 and if cr ." sd_init ENTER" cr lock? then ] \ \ Set up the pins connected to the SD card _sd_di_out_l _sd_di_out _sd_clk_out_l _sd_clk_out _sd_do_in _sd_cs_out_l _sd_cs_out def_sdasm lasm _sd_dim v_sd_di COG! _sd_dom v_sd_do COG! _sd_clkm v_sd_clk COG! \ \ allocate 128 longs in the cog as the data buffer 80 aallot sd_cogbufclr lockdict _sd_initialized W@ 0= if sd_lock _sd_init -1 _sd_initialized W! sd_unlock \ \ switch to fast assembler routines for _sd_shiftXXXXXXX -1 _sd_asm W! then freedict [ifdef _sd_debug _sd_debug W@ 1 and if cr ." sd_init EXIT" cr then ] ; \ \ REF1: p 135 / 123 7.3.1.3 Detailed Command Description \ REF1: p 141 / 129 7.3.2 Responses \ \ sd_blockread ( n1 -- ) n1 - the block number. Reads a 512 byte block into _sd_buf : sd_blockread [ifdef _sd_debug _sd_debug W@ 1 and if cr ." sd_blockread ENTER Block: " dup . cr then cnt COG@ _times L! ] sd_lock \ \ CMD17 0 - READ_SINGLE_BLOCK, Read 512 bytes 200 1 \ \ _sd_ccs = 0 means we use byte adressing to \ \ the card rot _sd_ccs W@ 0= if 9 lshift then 11 _sd_cmdr8data if \ \ block read error AC ERR then sd_unlock [ifdef _sd_debug cnt COG@ _times 18 + L! cnt COG@ _times 1C + L! _sd_debug W@ 1 and if cr ." sd_blockread EXIT" cr lock? then ] ; \ \ REF1: p 135 / 123 7.3.1.3 Detailed Command Description \ REF1: p 141 / 129 7.3.2 Responses \ \ sd_blockwrite ( n1 -- ) n1 - the block number. Writes 512 byte block from _sd_buf : sd_blockwrite [ifdef _sd_debug _sd_debug W@ 1 and if cr ." sd_blockwrite ENTER Block: " dup . cr then cnt COG@ _times L! ] sd_lock \ \ CMD24 0 - WRITE_BLOCK, write 512 bytes \ \ + 2 byte crc 200 1 \ \ _sd_ccs = 0 means we use byte adressing \ \ to the card rot _sd_ccs W@ 0= if 9 lshift then 18 [ifdef _sd_debug cnt COG@ _times 4 + L! ] _sd_cmdr8 if \ \ block write error AD ERR else _sd_writedata then \ \ get status to make sure all was ok \ \ CMD13 0 - SEND_STATUS, get the status reg, \ \ make sure there were no errors [ifdef _sd_debug cnt COG@ _times 14 + L! ] 1 0 D _sd_cmdr16 if \ \ one of the error bits was on AE ERR then sd_unlock [ifdef _sd_debug cnt COG@ _times 18 + L! cnt COG@ _times 1C + L! _sd_debug W@ 1 and if cr ." sd_blockwrite EXIT" cr then ] ; { hex forget v_sd_do 14f coghere W! :asm \ bitmasks for the various control pins v_sd_do 0 v_sd_di 0 v_sd_clk 0 a_sd_shift_out mov _treg6 , # 8 shl _sttos , # 18 jmp # __1 a_sd_shift_outlong \ send the bits in _sttos mov _treg6 , # 20 __1 \ hi bit to the carry flag rcl _sttos , # 1 wc \ set data out accordingly muxc outa , v_sd_di \ toggle clock or outa , v_sd_clk andn outa , v_sd_clk \ loop djnz _treg6 , # __1 andn outa , v_sd_di spop jnext a_sd_shift_in mov _treg6 , # 8 jmp # __3 a_sd_shift_inlong mov _treg6 , # 20 __3 spush mov _sttos , # 0 or outa , v_sd_di __2 \ clock high or outa , v_sd_clk \ read in bit test v_sd_do , ina wc rcl _sttos , # 1 \ clock low andn outa , v_sd_clk \ loop djnz _treg6 , # __2 andn outa , v_sd_di jnext v_currentdir 0 ;asm } [ifdef _sd_debug \ test_cogbufdump ( -- ) dumps the contents of sd_cogbuf : test_cogbufdump cr sd_cogbuf 80 cogdump ; \ \ test_debuglevel ( n1 -- ) : test_debuglevel dup _sd_debug W! ." Debug level: " . cr ; \ \ test_patternfill ( n1 -- ) : test_patternfill dup dup 8 lshift or dup 10 lshift or sd_cogbuf 80 bounds do dup i COG! 1+ loop drop ." Buffer filled starting with: " . cr ; \ \ test_rndpattern ( -- ) : test_rndpattern rnd test_patternfill ; \ \ test_sduninit ( -- ) : test_sduninit 0 _sd_initialized W! ." Uninitialzed" cr ; \ test_hex ( -- ) : test_hex hex ." Hex mode" cr ; \ test_decimal ( -- ) : test_decimal decimal ." Decimal mode" cr ; \ test_dumptimes ( -- ) : test_dumptimes 8 1 do ." Times " i 4* <# # # #> .cstr ." + " _times i 4* + dup L@ swap 4 - L@ - clkfreq f4240 u/ u/ . ." usec" cr loop ." Total time " _times 1C + L@ _times L@ - clkfreq f4240 u/ u/ . ." usec" cr ; \ \ test_setasmflags ( n1 -- ) : test_setasmflags dup _sd_asm W! ." ASM flags: " . cr ; \ \ test_asmflags? ( -- ) : test_asmflags? _sd_asm W@ ." ASM flags: " . cr ; \ stackdepth ( -- n1 ) : stackdepth _sttop _stptr COG@ - 3 - ; \ test_help ( -- ) : test_help cr ." ~009~009a # - set the debug level" cr ." ~009~009b # - read a block" cr ." ~009~009c # - pattern fill buffer" cr ." ~009~009d # - write block" cr ." ~009~009e - initialize" cr ." ~009~009f - zero out buffer" cr ." ~009~009g - random fill buffer" cr ." ~009~009h - dump buffer" cr ." ~009~009i - sd_lock" cr ." ~009~009j - sd_unlock" cr ." ~009~009k - lock?" cr ." ~009~009l - uninitialize" cr ." ~009~009m - Hex mode" cr ." ~009~009n - Decimal mode" cr ." ~009~009o - Dump times" cr ." ~009~009p - Dump Asm flags" cr ." ~009~009p # - Set Asm flags" cr ." ~009~009q - quit" cr ; \ create a menu structure of the test routines, lockdict herewal here W@ 0 w, \ the size of the structure in bytes \ the address (test_addr) #parameters (#params) the menu key (menuchar) ' test_debuglevel w, 1 c, 61 c, ' sd_blockread w, 1 c, 62 c, ' test_patternfill w, 1 c, 63 c, ' sd_blockwrite w, 1 c, 64 c, ' sd_init w, 0 c, 65 c, ' sd_cogbufclr w, 0 c, 66 c, ' test_rndpattern w, 0 c, 67 c, ' test_cogbufdump w, 0 c, 68 c, ' sd_lock w, 0 c, 69 c, ' sd_unlock w, 0 c, 6a c, ' lock? w, 0 c, 6b c, ' test_sduninit w, 0 c, 6c c, ' test_hex w, 0 c, 6d c, ' test_decimal w, 0 c, 6e c, ' test_dumptimes w, 0 c, 6f c, ' test_setasmflags w, 1 c, 70 c, ' test_asmflags? w, 0 c, 70 c, \ \ update the structure size here W@ over 2+ - over W! \ \ the address of the structure wconstant test_structure freedict \ \ exec_test ( params #params menuchar -- t/f ) executes the menu in the test_jumptable, \ if the # params match, true if test executed : exec_test 8 lshift or 0 >r 0 test_structure 2+ test_structure W@ bounds do \ \ ( params #params/menuchar 0 == 0 ) over i 2+ W@ = if drop i leave then 4 +loop \ \ ( params #params/menuchar struct_addr/0 == 0 ) swap FF and swap dup if \ \ ( params #params struct_addr/0 == 0 ) \ \ ( params #params struct_addr/0 == 0 ) swap >r W@ \ \ ( params #params test_addr == 0 #params ) stackdepth >r \ \ ( params #params test_addr == 0 #params stackdepth ) dup 0= if \ \ test routine address in structure was zero \ \ undefined routine? EE ERR then execute \ \ ( - == 0 #params stackdepth ) r> stackdepth - r> <> if \ \ test routine consumed the wrong number \ \ of parameters from the stack EF ERR then \ \ ( - == 0 ) r> drop -1 >r \ \ ( - == -1 ) then \ \ ( params #params struct_addr/0 == 0 ) r> if \ \ ( - == - ) -1 else \ \ ( params #params 0 == 0 ) drop dup 1+ 0 do drop loop 0 then \ \ ( 0/-1 == - ) ; \ test_getnumber ( -- n1 t/f) : test_getnumber parsenw \ \ ( cstr/0 == - ) dup if \ \ ( cstr/0 == - ) dup C@++ isnumber \ \ ( cstr t/f == - ) if \ \ got a valid number C@++ number -1 \ \ ( n1 -1 == - ) else \ \ print an error message _udf dup .cstr cr 0 \ \ ( cstr 0 == - ) then else 0 \ \ ( 0 0 == - ) then ; \ test_params ( -- n1 .. ncount count) : test_params 0 >r \ \ ( == count ) begin test_getnumber \ \ ( ... n1/ctr -1/0 == count ) if \ ( ... n1 == count ) r> 1+ >r 0 \ ( ... n1 0 == count+1 ) else \ \ ( ... -1 == count ) drop -1 then until r> \ \ ( ... count == - ) ; \ test ( -- ) : test test_help begin \ \ get a line from the input and parse the first word pad padsize accept drop 0 >in W! parsenw dup if \ \ ( cstr == - ) C@++ 1 = \ \ ( cstr+1 length == - ) if C@ dup 71 = if \ \ ( char == - ) drop -1 \ \ ( -1 == - ) else \ \ ( char == - ) >r test_params r> \ \ ( params #params menuchar == - ) exec_test \ ( 0/-1 == - ) 0= if test_help then 0 \ \ ( 0 == - ) then else \ \ ( cstr == - ) drop test_help 0 \ \ ( 0 == - ) then else \ ( cstr == - ) drop test_help 0 \ \ ( 0 == - ) then until ; ] decimal