Mirror of CollapseOS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

505 lines
11KB

  1. ; sdc
  2. ;
  3. ; Manages the initialization of a SD card and implement a block device to read
  4. ; and write from/to it, in SPI mode.
  5. ;
  6. ; Note that SPI can't really be used directly from the z80, so this part
  7. ; assumes that you have a device that handles SPI communication on behalf of
  8. ; the z80. This device is assumed to work in a particular way. See the
  9. ; "rc2014/sdcard" recipe for details.
  10. ;
  11. ; That device has 3 ports. One write-only port to make CS high, one to make CS
  12. ; low (data sent is irrelevant), and one read/write port to send and receive
  13. ; bytes with the card through the SPI protocol. The device acts as a SPI master
  14. ; and writing to that port initiates a byte exchange. Data from the slave is
  15. ; then placed on a buffer that can be read by reading the same port.
  16. ;
  17. ; It's through that kind of device that this code below is supposed to work.
  18. ; *** Defines ***
  19. ; SDC_PORT_CSHIGH: Port number to make CS high
  20. ; SDC_PORT_CSLOW: Port number to make CS low
  21. ; SDC_PORT_SPI: Port number to send/receive SPI data
  22. ; *** Consts ***
  23. .equ SDC_BLKSIZE 512
  24. ; *** Variables ***
  25. ; Where the block dev current points to. This is a byte index. Higher 7 bits
  26. ; indicate a sector number, lower 9 bits are an offset in the current SDC_BUF.
  27. .equ SDC_PTR SDC_RAMSTART
  28. ; Whenever we read a sector, we read a whole block at once and we store it
  29. ; in memory. That's where it goes.
  30. .equ SDC_BUF SDC_PTR+2
  31. ; Sector number currently in SDC_BUF. 0xff, it's initial value, means "no
  32. ; sector.
  33. .equ SDC_BUFSEC SDC_BUF+SDC_BLKSIZE
  34. ; Whether the buffer has been written to. 0 means clean. 1 means dirty.
  35. .equ SDC_BUFDIRTY SDC_BUFSEC+1
  36. .equ SDC_RAMEND SDC_BUFDIRTY+1
  37. ; *** Code ***
  38. ; Wake the SD card up. After power up, a SD card has to receive at least 74
  39. ; dummy clocks with CS and DI high. We send 80.
  40. sdcWakeUp:
  41. out (SDC_PORT_CSHIGH), a
  42. ld b, 10 ; 10 * 8 == 80
  43. ld a, 0xff
  44. .loop:
  45. out (SDC_PORT_SPI), a
  46. nop
  47. djnz .loop
  48. ret
  49. ; Initiate SPI exchange with the SD card. A is the data to send. Received data
  50. ; is placed in A.
  51. sdcSendRecv:
  52. out (SDC_PORT_SPI), a
  53. nop
  54. nop
  55. in a, (SDC_PORT_SPI)
  56. nop
  57. nop
  58. ret
  59. sdcIdle:
  60. ld a, 0xff
  61. jp sdcSendRecv
  62. ; sdcSendRecv 0xff until the response is something else than 0xff for a maximum
  63. ; of 20 times. Returns 0xff if no response.
  64. sdcWaitResp:
  65. push bc
  66. ld b, 20
  67. .loop:
  68. call sdcIdle
  69. inc a ; if 0xff, it's going to become zero
  70. jr nz, .end ; not zero? good, that's our command
  71. djnz .loop
  72. .end:
  73. ; whether we had a success or failure, we return the result.
  74. ; But first, let's bring it back to its original value.
  75. dec a
  76. pop bc
  77. ret
  78. ; Sends a command to the SD card, along with arguments and specified CRC fields.
  79. ; (CRC is only needed in initial commands though).
  80. ; A: Command to send
  81. ; H: Arg 1 (MSB)
  82. ; L: Arg 2
  83. ; D: Arg 3
  84. ; E: Arg 4 (LSB)
  85. ; C: CRC
  86. ;
  87. ; Returns R1 response in A.
  88. ;
  89. ; This does *not* handle CS. You have to select/deselect the card outside this
  90. ; routine.
  91. sdcCmd:
  92. ; Wait until ready to receive commands
  93. push af
  94. call sdcWaitResp
  95. pop af
  96. call sdcSendRecv
  97. ; Arguments
  98. ld a, h
  99. call sdcSendRecv
  100. ld a, l
  101. call sdcSendRecv
  102. ld a, d
  103. call sdcSendRecv
  104. ld a, e
  105. call sdcSendRecv
  106. ; send CRC
  107. ld a, c
  108. call sdcSendRecv
  109. ; And now we just have to wait for a valid response...
  110. jp sdcWaitResp ; return
  111. ; Send a command that expects a R1 response, handling CS.
  112. sdcCmdR1:
  113. out (SDC_PORT_CSLOW), a
  114. call sdcCmd
  115. out (SDC_PORT_CSHIGH), a
  116. ret
  117. ; Send a command that expects a R7 response, handling CS. A R7 is a R1 followed
  118. ; by 4 bytes. Those 4 bytes are returned in HL/DE in the same order as in
  119. ; sdcCmd.
  120. sdcCmdR7:
  121. out (SDC_PORT_CSLOW), a
  122. call sdcCmd
  123. ; We have our R1 response in A. Let's try reading the next 4 bytes in
  124. ; case we have a R3.
  125. push af
  126. ld a, 0xff
  127. call sdcSendRecv
  128. ld h, a
  129. ld a, 0xff
  130. call sdcSendRecv
  131. ld l, a
  132. ld a, 0xff
  133. call sdcSendRecv
  134. ld d, a
  135. ld a, 0xff
  136. call sdcSendRecv
  137. ld e, a
  138. pop af
  139. out (SDC_PORT_CSHIGH), a
  140. ret
  141. ; Initialize a SD card. This should be called at least 1ms after the powering
  142. ; up of the card. Sets result code in A. Zero means success, non-zero means
  143. ; error.
  144. sdcInitialize:
  145. push hl
  146. push de
  147. push bc
  148. call sdcWakeUp
  149. ; Call CMD0 and expect a 0x01 response (card idle)
  150. ; This should be called multiple times. We're actually expected to.
  151. ; Let's call this for a maximum of 10 times.
  152. ld b, 10
  153. .loop1:
  154. ld a, 0b01000000 ; CMD0
  155. ld hl, 0
  156. ld de, 0
  157. ld c, 0x95
  158. call sdcCmdR1
  159. cp 0x01
  160. jp z, .cmd0ok
  161. djnz .loop1
  162. ; Nothing? error
  163. jr .error
  164. .cmd0ok:
  165. ; Then comes the CMD8. We send it with a 0x01aa argument and expect
  166. ; a 0x01aa argument back, along with a 0x01 R1 response.
  167. ld a, 0b01001000 ; CMD8
  168. ld hl, 0
  169. ld de, 0x01aa
  170. ld c, 0x87
  171. call sdcCmdR7
  172. cp 0x01
  173. jr nz, .error
  174. xor a
  175. cp h ; H is zero
  176. jr nz, .error
  177. cp l ; L is zero
  178. jr nz, .error
  179. ld a, d
  180. cp 0x01
  181. jp nz, .error
  182. ld a, e
  183. cp 0xaa
  184. jr nz, .error
  185. ; Now we need to repeatedly run CMD55+CMD41 (0x40000000) until we
  186. ; the card goes out of idle mode, that is, when it stops sending us
  187. ; 0x01 response and send us 0x00 instead. Any other response means that
  188. ; initialization failed.
  189. .loop2:
  190. ld a, 0b01110111 ; CMD55
  191. ld hl, 0
  192. ld de, 0
  193. call sdcCmdR1
  194. cp 0x01
  195. jr nz, .error
  196. ld a, 0b01101001 ; CMD41 (0x40000000)
  197. ld hl, 0x4000
  198. ld de, 0x0000
  199. call sdcCmdR1
  200. cp 0x01
  201. jr z, .loop2
  202. or a ; cp 0
  203. jr nz, .error
  204. ; Success! out of idle mode!
  205. ; initialize variables
  206. ld hl, 0
  207. ld (SDC_PTR), hl
  208. ld a, 0xff
  209. ld (SDC_BUFSEC), a
  210. xor a
  211. ld (SDC_BUFDIRTY), a
  212. jr .end
  213. .error:
  214. ld a, 0x01
  215. .end:
  216. pop bc
  217. pop de
  218. pop hl
  219. ret
  220. ; Send a command to set block size to SDC_BLKSIZE to the SD card.
  221. ; Returns zero in A if a success, non-zero otherwise
  222. sdcSetBlkSize:
  223. push hl
  224. push de
  225. ld a, 0b01010000 ; CMD16
  226. ld hl, 0
  227. ld de, SDC_BLKSIZE
  228. call sdcCmdR1
  229. ; Since we're out of idle mode, we expect a 0 response
  230. ; We need no further processing: A is already the correct value.
  231. pop de
  232. pop hl
  233. ret
  234. ; Read block index specified in A and place the contents in (SDC_BUF).
  235. ; Doesn't check CRC. If the operation is a success, updates (SDC_BUFSEC) to the
  236. ; value of A.
  237. ; Returns 0 in A if success, non-zero if error.
  238. sdcReadBlk:
  239. push bc
  240. push hl
  241. out (SDC_PORT_CSLOW), a
  242. ld hl, 0 ; read single block at addr A
  243. ld d, 0
  244. ld e, a ; E isn't touched in the rest of the routine
  245. ; and holds onto our original A
  246. ld a, 0b01010001 ; CMD17
  247. call sdcCmd
  248. or a ; cp 0
  249. jr nz, .error
  250. ; Command sent, no error, now let's wait for our data response.
  251. ld b, 20
  252. .loop1:
  253. call sdcWaitResp
  254. ; 0xfe is the expected data token for CMD17
  255. cp 0xfe
  256. jr z, .loop1end
  257. cp 0xff
  258. jr nz, .error
  259. djnz .loop1
  260. jr .error ; timeout. error out
  261. .loop1end:
  262. ; We received our data token!
  263. ; Data packets follow immediately, we have 512 of them to read
  264. ld bc, SDC_BLKSIZE
  265. ld hl, SDC_BUF
  266. .loop2:
  267. call sdcIdle
  268. ld (hl), a
  269. cpi ; a trick to inc HL and dec BC at the same time.
  270. ; P/V indicates whether BC reached 0
  271. jp pe, .loop2 ; BC is not zero, loop
  272. ; Read our 2 CRC bytes
  273. call sdcIdle
  274. call sdcIdle
  275. ; success! Let's recall our orginal A arg and put it in SDC_BUFSEC
  276. ld a, e
  277. ld (SDC_BUFSEC), a
  278. xor a
  279. ld (SDC_BUFDIRTY), a
  280. jr .end
  281. .error:
  282. ; try to preserve error code
  283. or a ; cp 0
  284. jr nz, .end ; already non-zero
  285. inc a ; zero, adjust
  286. .end:
  287. out (SDC_PORT_CSHIGH), a
  288. pop hl
  289. pop bc
  290. ret
  291. ; Write the contents of (SDC_BUF) in sector number (SDC_BUFSEC). Unsets the
  292. ; (SDC_BUFDIRTY) flag on success.
  293. ; A returns 0 in A on success (with Z set), non-zero (with Z unset) on error.
  294. sdcWriteBlk:
  295. ld a, (SDC_BUFDIRTY)
  296. or a ; cp 0
  297. ret z ; return success, but do nothing.
  298. push bc
  299. push hl
  300. out (SDC_PORT_CSLOW), a
  301. ld a, (SDC_BUFSEC)
  302. ld hl, 0 ; write single block at addr A
  303. ld d, 0
  304. ld e, a
  305. ld a, 0b01011000 ; CMD24
  306. call sdcCmd
  307. or a ; cp 0
  308. jr nz, .error
  309. ; Before sending the data packet, we need to send at least one empty
  310. ; byte.
  311. ld a, 0xff
  312. call sdcSendRecv
  313. ; data packet token for CMD24
  314. ld a, 0xfe
  315. call sdcSendRecv
  316. ; Sending our data token!
  317. ld bc, SDC_BLKSIZE
  318. ld hl, SDC_BUF
  319. .loop:
  320. ld a, (hl)
  321. call sdcSendRecv
  322. cpi ; a trick to inc HL and dec BC at the same time.
  323. ; P/V indicates whether BC reached 0
  324. jp pe, .loop ; BC is not zero, loop
  325. ; Send our 2 CRC bytes. They can be anything
  326. call sdcIdle
  327. call sdcIdle
  328. ; Let's see what response we have
  329. call sdcWaitResp
  330. and 0b00011111 ; We ignore the first 3 bits of the response.
  331. cp 0b00000101 ; A valid response is "010" in bits 3:1 flanked
  332. ; by 0 on its left and 1 on its right.
  333. jr nz, .error
  334. ; good! Now, we need to let the card process this data. It will return
  335. ; 0xff when it's not busy any more.
  336. call sdcWaitResp
  337. xor a
  338. ld (SDC_BUFDIRTY), a
  339. jr .end
  340. .error:
  341. ; try to preserve error code
  342. or a ; cp 0
  343. jr nz, .end ; already non-zero
  344. inc a ; zero, adjust
  345. .end:
  346. out (SDC_PORT_CSHIGH), a
  347. pop hl
  348. pop bc
  349. ret
  350. ; Ensures that (SDC_BUFSEC) is in sync with (SDC_PTR), that is, that the current
  351. ; buffer in memory corresponds to where SDC_PTR points to. If it doesn't, loads
  352. ; the sector that (SDC_PTR) points to in (SDC_BUF) and update (SDC_BUFSEC).
  353. ; If the (SDC_BUFDIRTY) flag is set, we write the content of the in-memory
  354. ; buffer to the SD card before we read a new sector.
  355. ; Returns Z on success, not-Z on error (with the error code from either
  356. ; sdcReadBlk or sdcWriteBlk)
  357. sdcSync:
  358. ; SDC_PTR points to the character we're supposed to read or right now,
  359. ; but we first have to check whether we need to load a new sector in
  360. ; memory. To do this, we compare the high 7 bits of (SDC_PTR) with
  361. ; (SDC_BUFSEC). If they're different, we need to load a new block.
  362. push hl
  363. ld a, (SDC_BUFSEC)
  364. ld h, a
  365. ld a, (SDC_PTR+1) ; high byte has bufsec in its high 7 bits
  366. srl a
  367. cp h
  368. pop hl
  369. ret z ; equal? nothing to do
  370. ; We have to read a new sector, but first, let's write the current one
  371. ; if needed.
  372. call sdcWriteBlk
  373. ret nz ; error
  374. ; Let's read our new sector
  375. ld a, (SDC_PTR+1)
  376. srl a
  377. jp sdcReadBlk ; returns
  378. ; *** shell cmds ***
  379. sdcInitializeCmd:
  380. .db "sdci", 0, 0, 0
  381. call sdcInitialize
  382. jp sdcSetBlkSize ; returns
  383. ; Flush the current SDC buffer if dirty
  384. sdcFlushCmd:
  385. .db "sdcf", 0, 0, 0
  386. jp sdcWriteBlk ; returns
  387. ; *** blkdev routines ***
  388. ; Make HL point to (SDC_PTR) in current buffer
  389. _sdcPlaceBuf:
  390. call sdcSync
  391. ret nz ; error
  392. ld a, (SDC_PTR+1) ; high byte
  393. and 0x01 ; is first bit set?
  394. jr nz, .highbuf ; first bit set? we're in the "highbuf" zone.
  395. ; lowbuf zone
  396. ; Read byte from memory at proper offset in lowbuf (first 0x100 bytes)
  397. ld hl, SDC_BUF
  398. jr .read
  399. .highbuf:
  400. ; Read byte from memory at proper offset in highbuf (0x100-0x1ff)
  401. ld hl, SDC_BUF+0x100
  402. .read:
  403. ; HL is now placed either on the lower or higher half of SDC_BUF and
  404. ; all we need is to increase HL by the number in SDC_PTR's LSB (little
  405. ; endian, remember).
  406. ld a, (SDC_PTR) ; LSB
  407. call addHL ; returns
  408. xor a ; ensure Z
  409. ret
  410. sdcGetC:
  411. push hl
  412. call _sdcPlaceBuf
  413. jr nz, .error
  414. ; This is it!
  415. ld a, (hl)
  416. ; before we return A, we need to increase (SDC_PTR)
  417. ld hl, (SDC_PTR)
  418. inc hl
  419. ld (SDC_PTR), hl
  420. cp a ; ensure Z
  421. jr .end
  422. .error:
  423. call unsetZ
  424. .end:
  425. pop hl
  426. ret
  427. sdcPutC:
  428. push hl
  429. push af ; let's remember the char we put, _sdcPlaceBuf
  430. ; destroys A.
  431. call _sdcPlaceBuf
  432. jr nz, .error
  433. ; HL points to our dest. Recall A and write
  434. pop af
  435. ld (hl), a
  436. ; we need to increase (SDC_PTR)
  437. ld hl, (SDC_PTR)
  438. inc hl
  439. ld (SDC_PTR), hl
  440. ld a, 1
  441. ld (SDC_BUFDIRTY), a
  442. xor a ; ensure Z
  443. jr .end
  444. .error:
  445. pop af
  446. call unsetZ
  447. .end:
  448. pop hl
  449. ret
  450. sdcSeek:
  451. ld (SDC_PTR), hl
  452. ret
  453. sdcTell:
  454. ld hl, (SDC_PTR)
  455. ret