1 \ -*- coding: utf-8 -*-
4 \ ===========================================================
5 \ ABOUT INIT SD_CARD AND HOW TO SELECT FAT16/FAT32 FORMAT
6 \ ===========================================================
7 \ FAT16/FAT32 selection is made via the ID of partition in EBP
8 \ because SD must be always FAT16 and SDHC must be always FAT32
9 \ this is automatically done when we format the SD_Card !
12 \ =====================================================================
13 \ goal : accept 64 MB up to 64 GB SD_CARD
14 \ =====================================================================
15 \ thus FAT and RootClus logical sectors are word addressable.
17 \ FAT is a little endian structure.
18 \ CMD frame is sent as big endian.
20 \ we assume that SDSC Card (up to 2GB) is FAT16 with a byte addressing
21 \ and that SDHC Card (4GB up to 64GB) is FAT32 with a sector addressing (sector = 512 bytes)
22 \ for SDHC Card = 64 GB, cluster = 64 sectors ==> max clusters = 20 0000h ==> FAT size = 16384 sectors
23 \ ==> FAT1 and FAT2 can be addressed with a single word.
25 \ ref. https://en.wikipedia.org/wiki/Extended_boot_record
26 \ ref. https://en.wikipedia.org/wiki/Partition_type
28 \ Formatage FA16 d'une SDSC Card 2GB
29 \ First sector of physical drive (sector 0) content :
30 \ ---------------------------------------------------
32 \ 446 |0x1BE : partition table first record ==> logical drive 0
33 \ 462 |0x1CE : partition table 2th record ==> logical drive 1
34 \ 478 |0x1DE : partition table 3th record ==> logical drive 2
35 \ 494 |0x1EE : partition table 4th record ==> logical drive 3
37 \ partition of first record content :
38 \ ---------------------------------------------------
39 \ 450 |0x1C2 = 0x0E : type FAT16 using LBA addressing
40 \ 454 |0x1C6 = 89 00 00 00 : FirstSector (of logical drive 0) BS_FirstSector = 137
43 \ Partition type Description
46 \ 4 FAT16 for partitions <= 32 MiB
47 \ 5 extended partition
48 \ 6 FAT16 for partitions > 32 MiB
49 \ 11 FAT32 for partitions <= 2 GiB
50 \ 12 Same as type 11 (FAT32), but using LBA addressing, which removes size constraints
51 \ 14 Same as type 6 (FAT16), but using LBA addressing
52 \ 15 Same as type 5, but using LBA addressing
53 \ ref. https://www.compuphase.com/mbr_fat.htm#BOOTSECTOR
55 \ FirstSector of logical drive (sector 0) content :
56 \ -------------------------------------------------
57 \ dec@| HEX@ = HEX decimal
58 \ 11 | 0x0B = 00 02 : 512 bytes/sector BPB_BytsPerSec = 512
59 \ 13 | 0x0D = 40 : 64 sectors/cluster BPB_SecPerClus = 64
60 \ 14 | 0x0E = 01 00 : 2 reserved sectors BPB_RsvdSecCnt = 1
61 \ 16 | 0x10 = 02 : 2 FATs BPB_NumFATs = 2 (always 2)
62 \ 17 | 0x11 = 00 02 : 512 entries/directory BPB_RootEntCnt = 512
63 \ 19 | 0x13 = 00 00 : BPB_TotSec16 (if < 65535) BPB_TotSec16 = 0
64 \ 22 | 0x16 = EB 00 : 235 sectors/FAT (FAT16) BPB_FATSize = 235
65 \ 32 | 0x20 = 77 9F 3A 00 : 3841911 total sectors BPB_TotSec32 = 3841911
66 \ 54 | 0x36 = "FAT16" BS_FilSysType (not used)
68 \ all values below are evaluated in logical sectors
69 \ FAT1 = BPB_RsvdSecCnt = 1
70 \ FAT2 = BPB_RsvdSecCnt + BPB_FATSz32 = 1 + 235 = 236
71 \ OrgRootDirL = BPB_RsvdSecCnt + (BPB_FATSize * BPB_NumFATs) = 471
72 \ RootDirSize = BPB_RootEntCnt * 32 / BPB_BytsPerSec = 32 sectors
73 \ OrgDatas = OrgRootDir + RootDirSize = 503
74 \ OrgCluster = OrgRootDir - 2*BPB_SecPerClus = 375 (virtual value)
75 \ FirstSectorOfCluster(n) = OrgCluster + n*BPB_SecPerClus ==> cluster(3) = 705
77 \ ====================================================================================
79 \ Formatage FA32 d'une SDSC Card 8GB
80 \ First sector of physical drive (sector 0) content :
81 \ ---------------------------------------------------
83 \ 446 |0x1BE : partition table first record ==> logical drive 0
84 \ 462 |0x1CE : partition table 2th record ==> logical drive 1
85 \ 478 |0x1DE : partition table 3th record ==> logical drive 2
86 \ 494 |0x1EE : partition table 4th record ==> logical drive 3
88 \ partition record content :
89 \ ---------------------------------------------------
90 \ 450 |0x1C2 = 0x0C : type FAT32 using LBA addressing
91 \ 454 |0x1C6 = 00 20 00 00 : FirstSector (of logical drive 0) = BS_FirstSector = 8192
94 \ FirstSector of logical block (sector 0) content :
95 \ -------------------------------------------------
96 \ dec@| HEX@ = HEX decimal
97 \ 11 | 0x0B = 00 02 : 512 bytes/sector BPB_BytsPerSec = 512
98 \ 13 | 0x0D = 08 : 8 sectors/cluster BPB_SecPerClus = 8
99 \ 14 | 0x0E = 20 00 : 32 reserved sectors BPB_RsvdSecCnt = 32
100 \ 16 | 0x10 = 02 : 2 FATs BPB_NumFATs = 2 (always 2)
101 \ 17 | 0x11 = 00 00 : 0 BPB_RootEntCnt = 0 (always 0 for FAT32)
103 \ 32 | 0x20 = 00 C0 EC 00 : BPB_TotSec32 BPB_TotSec32 = 15515648
104 \ 36 | 0x24 = 30 3B 00 00 : BPB_FATSz32 BPB_FATSz32 = 15152
105 \ 40 | 0x28 = 00 00 : BPB_ExtFlags BPB_ExtFlags
106 \ 44 | 0x2C = 02 00 00 00 : BPB_RootClus BPB_RootClus = 2
107 \ 48 | 0x30 = 01 00 : BPB_FSInfo BPB_FSInfo = 1
108 \ 50 | 0x33 = 06 00 : BPB_BkBootSec BPB_BkBootSec = 6
109 \ 82 | 0x52 = "FAT32" : BS_FilSysType BS_FilSysType (not used)
112 \ all values below are evaluated in logical sectors
113 \ FAT1 = BPB_RsvdSecCnt = 32
114 \ FAT2 = BPB_RsvdSecCnt + BPB_FATSz32 = 32 + 15152 = 15184
115 \ OrgRootDirL = BPB_RsvdSecCnt + BPB_FATSz32 * BPB_NumFATs = 32 + 15152*2 = 30336
116 \ OrgCluster = OrgRootDir - 2*BPB_SecPerClus = 30320
117 \ RootDirSize = BPB_RootEntCnt * 32 / BPB_BytsPerSec = 0
118 \ OrgDatas = OrgRootDir + RootDirSize = 30336
119 \ FirstSectorOfCluster(n) = OrgCluster + n*BPB_SecPerClus ==> cluster(6) = 30368
121 \ SPI_GET and SPI_PUT are adjusted for SD_CLK = MCLK
122 \ PUT value must be a word or byte:byte because little endian to big endian conversion
128 \ BODY = DP value before MARKER definition
129 \ BODY+2 = VOClink value before MARKER definition
130 \ BODY+4 = RET_ADR: by default MARKER_DOES does a call to RET_ADR (does nothing)
131 10 ALLOT \ make room for:
132 \ {SD_APP}+10 = content of previous ....
133 \ {SD_APP}+12 = content of previous ....
134 \ {SD_APP}+14 = content of previous ....
135 \ {SD_APP}+16 = content of previous ....
136 \ {SD_APP}+18 = content of previous ....
140 \ ====================================\
141 HDNCODE SPI_GET \ PUT(FFh) one time, output : W = received byte, X = 0
142 \ ====================================\
144 \ ====================================\
145 \ SPI_X_GET \ PUT(FFh) X times, output : W = last received byte, X = 0
146 \ ====================================\
148 \ ====================================\
149 \ SPI_PUT \ PUT(W) X times, output : W = last received byte, X = 0
150 \ ====================================\
153 MOV.B W,&SD_TXBUF \ 3 put W high byte then W low byte and so forth, that performs little to big endian conversion
154 CMP #0,&SD_BRW \ 3 full speed ?
157 BIT #RX_SD,&SD_IFG \ 3
159 CMP.B #0,&SD_RXBUF \ 3 clear RX_BUF flag
161 \ NOP \ NOPx adjusted to avoid SD error
163 0= UNTIL \ 2 12~ loop
164 MOV.B &SD_RXBUF,W \ 3
167 \ ------------------------------------\
169 \ in SPI mode CRC is not required, but CMD frame must be ended with a stop bit
170 \ ====================================\
171 HDNCODE REWR_CMD \ WX <=== CMD17 or CMD24 (read or write Sector CMD)
172 \ ====================================\
173 BIC.B #CS_SD,&SD_CSOUT \ set Chip Select low
174 BIT.B #CD_SD,&SD_CDIN \ test Card Detect: memory card present ?
176 MOV #COLD,PC \ no: force COLD
178 \ ------------------------------------\ input = logical sector...
179 ADD &BS_FirstSectorL,W \ 3
180 ADDC &BS_FirstSectorH,X \ 3
181 \ ------------------------------------\ ...output = physical sector
183 \ ------------------------------------\
184 MOV #1,&SD_CMD_FRM \ 3 $(01 00 xx xx xx CMD) set stop bit in CMD frame
185 CMP #1,&FATtype \ 3 FAT16 ?
186 0= IF \ 2 yes : CMD17/24 byte address = Sector * BPB_BytsPerSec
187 ADD W,W \ 1 shift left one Sector
189 MOV W,&SD_CMD_FRM+2 \ 3 $(01 00 ll LL xx CMD)
190 MOV.B X,&SD_CMD_FRM+4 \ 3 $(01 00 ll LL hh CMD)
191 ELSE \ FAT32 : CMD17/24 sector address
192 MOV.B W,&SD_CMD_FRM+1 \ 3 $(01 ll xx xx xx CMD)
194 MOV.B W,&SD_CMD_FRM+2 \ 3 $(01 ll LL xx xx CMD)
195 MOV.B X,&SD_CMD_FRM+3 \ 3 $(01 ll LL hh xx CMD)
197 MOV.B X,&SD_CMD_FRM+4 \ 3 $(01 ll LL hh HH CMD)
199 \ ====================================\
200 BW1 \ WaitIdleBeforeSendCMD \ <=== CMD41, CMD1, CMD16 (forthMSP430FR_SD_INIT.asm)
201 \ ====================================\
204 ADD.B #1,W \ expected value = FFh <==> MISO = 1 = SPI idle state
205 0= UNTIL \ loop back if <> FFh
206 \ ====================================\ W = 0 = expected R1 response = ready, for CMD41,CMD16, CMD17, CMD24
207 BW2 \ sendCommand \ sendCommand = WaitIdleBeforeSendCMD+8
208 \ ====================================\
209 \ input : SD_CMD_FRM : {CRC,byte_l,byte_L,byte_h,byte_H,CMD}
210 \ W = expected return value
211 \ output W is unchanged, flag Z is positionned
212 \ reverts CMD bytes before send : $(CMD hh LL ll 00 CRC)
213 MOV #5,X \ X = SD_CMD_FRM ptr AND countdown
214 \ ------------------------------------\
215 \ Send_CMD_PUT \ performs little endian --> big endian conversion
216 \ ------------------------------------\
218 MOV.B SD_CMD_FRM(X),&SD_TXBUF \ 5
219 CMP #0,&SD_BRW \ 3 full speed ?
221 BEGIN \ case of low speed during memCardInit
222 BIT #RX_SD,&SD_IFG \ 3
225 CMP.B #0,&SD_RXBUF \ 3 to clear UCRXIFG
227 \ NOP \ 0 NOPx adjusted to avoid SD error
229 U< UNTIL \ 2 don't skip SD_CMD_FRM(0) !
230 \ host must provide height clock cycles to complete operation
231 \ here X=255, so wait for CMD return expected value with PUT FFh 256 times
232 \ MOV #4,X \ to pass made in PRC SD_Card init
233 \ MOV #16,X \ to pass Transcend SD_Card init
234 \ MOV #32,X \ to pass Panasonic SD_Card init
235 \ MOV #64,X \ to pass SanDisk SD_Card init
236 \ ------------------------------------\ expect W = return value during X = 255 times
239 0>= WHILE \ 2 if out of loop, error on time out with flag Z = 0
240 MOV.B #-1,&SD_TXBUF \ 3 PUT FFh
241 CMP #0,&SD_BRW \ 3 full speed ?
243 BEGIN \ case of low speed during memCardInit (CMD0,CMD8,ACMD41,CMD16)
244 BIT #RX_SD,&SD_IFG \ 3
247 \ NOP \ NOPx adjusted to avoid SD_error
248 CMP.B &SD_RXBUF,W \ 3 return value = ExpectedValue ?
249 0= UNTIL \ 2 16~ full speed loop
250 THEN \ WHILE resolution
251 MOV @RSP+,PC \ W = expected value, unchanged
252 \ ------------------------------------\ flag Z = 1 <==> Returned value = expected value
254 \ ------------------------------------\
255 HDNCODE CMD_IDLE \ <=== CMD0, CMD8, CMD55: W = 1 = R1 expected response = idle (forthMSP430FR_SD_INIT.asm)
256 \ ------------------------------------\
257 MOV #1,W \ expected R1 response (first byte of SPI R7) = 01h : idle state
259 \ ------------------------------------\
264 \ 1 = CMD17 read error
265 \ 2 = CMD24 write error
266 \ 4 = CMD0 time out (GO_IDLE_STATE)
267 \ 8 = ACMD41 time out (APP_SEND_OP_COND)
268 \ $10 = CMD16 time out (SET_BLOCKLEN)
269 \ $20 = not FAT16/FAT32 media, low byte = partition ID
271 \ low byte, if CMD R1 response : %0xxx_xxxx
272 \ 1th bit = In Idle state
273 \ 2th bit = Erase reset
274 \ 3th bit = Illegal command
275 \ 4th bit = Command CRC error
276 \ 5th bit = erase sequence error
277 \ 6th bit = address error
278 \ 7th bit = parameter error
280 \ Data Response Token
281 \ Every data block written to the card will be acknowledged by a data response token.
282 \ It is one byte long and has the following format:
283 \ %xxxx_sss0 with bits(3-1) = Status
284 \The meaning of the status bits is defined as follows:
285 \'010' - Data accepted.
286 \'101' - Data rejected due to a CRC error.
287 \'110' - Data Rejected due to a Write Error
289 \ ----------------------------------\
290 CODE ABORT_SD \ <=== OPEN file errors from forthMSP430FR_SD_LOAD.asm
291 \ ----------------------------------\
294 MOV #10h,&BASE \ select hex
296 \ MOV #TIB_ORG,&CIB_ADR \ restore TIB as Current Input Buffer
297 \ MOV #BODYACCEPT,&PFAACCEPT \ restore default ACCEPT
301 MOV #ABORT_TERM,PC \ no return...
303 \ ----------------------------------\
305 \ ----------------------------------\
306 CODE SD_ERROR \ <=== SD_INIT errors 4,8,$10
307 \ ----------------------------------\
308 SWPB S \ High Level error in High byte
309 ADD &SD_RXBUF,S \ add SPI(GET) return value as low byte error
310 BW3 \ SD_CARD_ID_ERROR \ <=== SD_INIT error $20 from forthMSP430FR_SD_LowLvl.asm
311 BIS.B #CS_SD,&SD_CSOUT \ Chip Select high
313 S" < SD Error!" \ don't use S register
317 \ ==================================\
318 CODE READ_SWX \ Read Sector
319 \ ==================================\
320 BIS #1,S \ preset sd_read error
321 MOV.B #51h,&SD_CMD_FRM+5 \ CMD17 = READ_SINGLE_BLOCK
322 CALL #REWR_CMD \ which performs logical sector to physical sector then little endian to big endian conversion
323 0<> ?GOTO BW3 \ SD_ERROR \ time out error if R1 <> 0
324 \ ------------------------------------\
325 BEGIN \ wait SD_Card response FEh
326 \ ------------------------------------\
328 ADD.B #2,W \ 1 FEh expected value
330 \ ------------------------------------\
331 BEGIN \ get 512+1 bytes, write 512 bytes in SD_BUF
332 \ ------------------------------------\
333 MOV.B #-1,&SD_TXBUF \ 3 put FF
334 NOP \ 1 NOPx adjusted to avoid read SD_error
336 CMP #BytsPerSec+1,X \ 2
338 MOV.B &SD_RXBUF,SD_BUF-1(X) \ 5
340 \ ------------------------------------\
341 MOV.B #-1,&SD_TXBUF \ 3 put only one FF because first CRC byte is already received...
342 \ ------------------------------------\
343 \ ReadWriteHappyEnd \ <==== WriteSector
344 \ ------------------------------------\
345 BW2 BIC #3,S \ reset read and write errors
346 BIS.B #CS_SD,&SD_CSOUT \ Chip Select high
349 \ ------------------------------------\
351 \ .IFDEF SD_CARD_READ_WRITE
353 \ ====================================\
354 CODE WRITE_SWX \ Write Sector
355 \ ====================================\
356 BIS #2,S \ preset sd_write error
357 MOV.B #058h,SD_CMD_FRM+5 \ CMD24 = WRITE_SINGLE_BLOCK
358 CALL #CMD_RW \ which performs logical sector to physical sector then little endian to big endian conversions
359 0<> ?GOTO BW3 \ ReturnError = 2
360 MOV #2,X \ to put 16 bits value
361 CALL #SPI_PUT \ which performs little endian to big endian conversion
362 BEGIN \ 11 cycles loop write, starts with X = 0
363 MOV.B SD_BUF(X),&SD_TXBUF \ 5
364 NOP \ 1 NOPx adjusted to avoid write SD_error
366 CMP #BytsPerSec,X \ 2
368 \ ------------------------------------\ CRC16 not used in SPI mode
369 MOV #3,X \ PUT 2 bytes to skip CRC16
370 CALL #SPI_X_GET \ + 1 byte to get data token in W
371 \ ------------------------------------\ CheckWriteState
372 BIC.B #0E1h,W \ apply mask for Data response
373 CMP.B #4,W \ data accepted
374 0= ?GOTO BW2 \ goto ReadWriteHappyEnd
375 GOTO BW3 \ goto SD_ERROR
377 \ ----------------------------------\
379 \ .ENDIF \ SD_CARD_READ_WRITE
381 \ ===========================================================
383 \ ===========================================================
384 \ ----------------------------------\
386 \ ----------------------------------\
387 CALL #INIT_TERM \ which activates all previous I/O settings and set TOS = RSTIV_MEM.
388 \ ----------------------------------\
389 CMP #0,TOS \ RSTIV_MEM = WARM ?
390 0<> IF \ init if RSTIV_MEM <> WARM
391 \ ----------------------------------\
392 BIT.B #CD_SD,&SD_CDIN \ SD_memory in SD_Card module ?
393 \ JNZ INI_SD_END \ no
395 \ ----------------------------------\
396 MOV #$0A981,&SD_CTLW0 \ UCxxCTL1 = CKPH, MSB, MST, SPI_3, SMCLK + UCSWRST
397 MOV #FREQUENCY*3,&SD_BRW \ UCxxBRW init SPI CLK = 333 kHz ( < 400 kHz) for SD_Card initialisation
398 BIS.B #CS_SD,&SD_CSDIR \ SD Chip Select as output high
399 BIS #BUS_SD,&SD_SEL \ Configure pins as SIMO, SOMI & SCK (PxDIR.y are controlled by eUSCI module)
400 BIC #1,&SD_CTLW0 \ release eUSCI from reset
401 \ ----------------------------------\
403 BEGIN \ case of MSP430FR57xx : SD datas are in FRAM not initialized by RESET.
407 \ ----------------------------------\
409 \ ----------------------------------\
410 MOV #8,X \ send 64 clk on SD_clk
412 BIC.B #CS_SD,&SD_CSOUT \ preset Chip Select output low to switch in SPI mode
413 \ ----------------------------------\
414 \ INIT_CMD0 \ all SD area is 0 filled
415 \ ----------------------------------\
416 MOV #4,S \ preset error 4R1 for CMD0
417 MOV #$95,&SD_CMD_FRM \ $(95 00 00 00 00 00)
418 MOV #$4000,&SD_CMD_FRM+4 \ $(95 00 00 00 00 40)\ send CMD0
419 \ ----------------------------------\
420 \ SEND_CMD0 \ CMD0 : GO_IDLE_STATE expected SPI_R1 response = 1 = idle state
421 \ ----------------------------------\
422 CALL #sendCommandIdleRet \ X
423 0<> IF \ INIT_CMD8 \ if no idle state
424 MOV #SD_ERROR,PC \ ReturnError = $04R1, case of defectuous card (or insufficient SD_POWER_ON clk)
426 \ ----------------------------------\ see forthMSP430FR_SD_lowLvl.asm
427 \ INIT_CMD8 \ mandatory if SD_Card >= V2.x [11:8]supply voltage(VHS)
428 \ ----------------------------------\
430 CALL #SPI_GET \ (needed to pass SanDisk ultra 8GB "HC I")
431 CMP.B #-1,W \ FFh expected value <==> MISO = high level
432 \ JNE INIT_CMD8 \ loop back while yet busy
434 MOV #$0AA87,&SD_CMD_FRM \ $(87 AA ...) (CRC:CHECK PATTERN)
435 MOV #1,&SD_CMD_FRM+2 \ $(87 AA 01 00 ...) (CRC:CHECK PATTERN:VHS set as 2.7to3.6V:0)
436 MOV #$4800,&SD_CMD_FRM+4 \ $(87 AA 01 00 00 48)
437 \ ----------------------------------\
438 \ SEND_CMD8 \ CMD8 = SEND_IF_COND\ expected R1 response (first byte of SPI R7) = 01h : idle state
439 \ ----------------------------------\
440 CALL #sendCommandIdleRet \X time out occurs with SD_Card V1.x (and all MMC_card)
441 \ ----------------------------------\
442 MOV #4,X \ skip end of SD_Card V2.x type R7 response (4 bytes), because useless
444 \ ----------------------------------\
445 INIT_ACMD41 \ no more CRC needed from here
446 \ ----------------------------------\
447 MOV #1,&SD_CMD_FRM \ $(01 00 ... set stop bit
448 MOV #0,&SD_CMD_FRM+2 \ $(01 00 00 00 ...
449 \ MOV.B #16,Y \ init 16 * ACMD41 repeats (power on fails with SanDisk ultra 8GB "HC I" and Transcend 2GB)
450 \ MOV.B #32,Y \ init 32 * ACMD41 repeats ==> ~400ms time out
451 MOV.B #-1,Y \ init 255 * ACMD41 repeats ==> ~3 s time out
452 MOV #8,S \ preset error 8R1 for ACMD41
453 \ ----------------------------------\
454 \ SEND_ACMD41 \ send CMD55+CMD41
455 \ ----------------------------------\
458 MOV #$7700,&SD_CMD_FRM+4 \ $(01 00 00 00 00 77)
459 \ SEND_CMD55 \ CMD55 = APP_CMD\ expected SPI_R1 response = 1 : idle
460 CALL #sendCommandIdleRet \X
461 \ SEND_CMD41 \ CMD41 = APP OPERATING CONDITION
462 MOV #$6940,&SD_CMD_FRM+4 \ $(01 00 00 00 40 69) (30th bit = HCS = High Capacity Support request)
463 CALL #WaitIdleBeforeSendCMD \ wait until idle (needed to pass SanDisk ultra 8GB "HC I") then send Command CMD41
464 \ JZ SetBLockLength \ if SD_Card ready (R1=0)
465 0<> WHILE \ if SD_Card not ready (R1<>0)
466 SUB.B #1,Y \ else decr time out delay
467 \ JNZ INIT_CMD55 \ then loop back while count of repeat not reached
469 MOV #SD_ERROR,PC \ ReturnError on time out : unusable card (or insufficient Vdd SD)
472 \ ----------------------------------\
473 \ setBLockLength \ set block = 512 bytes (buffer size), usefull only for FAT16 SD Cards
474 \ ----------------------------------\
475 ADD S,S \ preset error $10 for CMD16
476 \ SEND_CMD16 \ CMD16 = SET_BLOCKLEN
477 MOV #$02,&SD_CMD_FRM+2 \ $(01 00 02 00 ...)
478 MOV #$5000,&SD_CMD_FRM+4 \ $(01 00 02 00 00 50)
479 CALL #WaitIdleBeforeSendCMD \ wait until idle then send CMD16
481 MOV #SD_ERROR,PC \ if W = R1 <> 0, ReturnError = $20R1 \ send command ko
483 \ ----------------------------------\ W = R1 = 0
484 \ SwitchSPIhighSpeed \ end of SD init ==> SD_CLK = SMCLK
485 \ ----------------------------------\
486 BIS #1,&SD_CTLW0 \ Software reset
487 MOV #0,&SD_BRW \ UCxxBRW = 0 ==> SPI_CLK = MCLK
488 BIC #1,&SD_CTLW0 \ release from reset
489 \ ----------------------------------\
490 \ Read_EBP_FirstSector \ W=0, BS_FirstSectorHL=0
491 \ ----------------------------------\
493 CALL #readSectorWX \ read physical first sector
495 MOV 454(Y),&BS_FirstSectorL \ so, sectors become logical
496 MOV 456(Y),&BS_FirstSectorH \
497 MOV.B 450(Y),W \ W = partition ID
498 \ ----------------------------------\
500 \ ----------------------------------\
501 MOV #1,&FATtype \ preset FAT16
502 \ FAT16_CHS_LBA_Test \
503 SUB.B #6,W \ ID=06h Partition FAT16 > 32MB using CHS & LBA ?
506 SUB.B #8,W \ ID=0Eh Partition FAT16 using LBA ?
508 \ ----------------------------------\
509 MOV #2,&FATtype \ set FAT32
511 ADD.B #2,W \ ID=0Ch Partition FAT32 using LBA ?
513 \ FAT32_CHS_LBA_Test \
514 ADD.B #1,W \ ID=0Bh Partition FAT32 using CHS & LBA ?
516 ADD.B #4,W \ ID=07h assigned to FAT 32 by MiniTools Partition Wizard....
520 MOV #SD_CARD_ID_ERROR,PC \ S = ReturnError = $20xx with xx = partition ID
526 \ ----------------------------------\ see: https://en.wikipedia.org/wiki/Partition_type
527 \ Read_MBR_FirstSector \ read first logical sector
528 \ ----------------------------------\ W = 0
530 CALL #READ_SWX \ ...with the good CMD17 bytes/sectors frame ! (good switch FAT16/FAT32)
531 \ ----------------------------------\
532 \ FATxx_SetFileSystem \
533 \ ----------------------------------\
534 MOV.B 13(Y),&SecPerClus \
535 MOV 14(Y),X \ 3 X = BPB_RsvdSecCnt
536 MOV X,&OrgFAT1 \ 3 set OrgFAT1
537 MOV 22(Y),W \ W = BPB_FATsize
538 CMP #0,W \ BPB_FATsize = 0 ?
540 MOV 36(Y),W \ W = BPB_FATSz32
543 MOV W,&FATSize \ limited to 16384 sectors....
545 MOV X,&OrgFAT2 \ X = OrgFAT1 + FATsize = OrgFAT2
546 ADD W,X \ X = OrgFAT2 + FATsize = FAT16 OrgRootDir | FAT32 OrgDatas
547 CMP #2,&FATtype \ FAT32?
549 \ FAT16_SetRootCluster \
550 MOV X,&OrgRootDIR \ only FAT16 use, is a sector used by CLS_SCT
551 ADD #32,X \ OrgRootDir + RootDirSize = OrgDatas
553 SUB &SecPerClus,X \ OrgDatas - SecPerClus*2 = OrgClusters
554 SUB &SecPerClus,X \ no borrow expected
555 MOV X,&OrgClusters \ X = virtual cluster 0 address (clusters 0 and 1 don't exist)
556 MOV &FATtype,&DIRClusterL \ init DIRcluster as RootDIR
561 \ ----------------------------------\
563 \ ----------------------------------\
564 HDNCODE RST_ABORT_SD \ common part of ?ABORT|RST
565 \ ----------------------------------\
566 CALL #RET_ADR \ which does nothing
567 \ ----------------------------------\
571 MOV.B #0,HDLB_Token(T) \
575 MOV #TIB_ORG,&CIB_ADR \ restore TIB as Current Input Buffer for next line (next QUIT)
576 MOV #ACCEPT+4,&ACCEPT+2 \ restore default ACCEPT for next line (next QUIT)
579 \ ----------------------------------\
581 \-----------------------------------------------------------------------
582 \ SD card OPEN, LOAD subroutines
583 \-----------------------------------------------------------------------
585 \ used variables : BufferPtr, BufferLen
587 \ rules for registers use
589 \ T = CurrentHdl, pathname
590 \ W = SectorL, (RTC) TIME
591 \ X = SectorH, (RTC) DATE
592 \ Y = BufferPtr, (DIR) EntryOfst, FAToffset
596 \ ----------------------------------\
597 \ HDLCurClusToFAT1sectWofstY \WXY Input: T=currentHandle, Output: W=FATsector, Y=FAToffset, Cluster=HDL_CurCluster
598 \ ----------------------------------\
599 MOV HDLL_CurClust(T),&ClusterL \
600 MOV HDLH_CurClust(T),&ClusterH \
601 \ ----------------------------------\
602 \ ClusterToFAT1sectWofstY \WXY Input : Cluster \ Output: W = FATsector, Y = FAToffset
603 \ ----------------------------------\
604 MOV.B &ClusterL+1,W \ 3 W = ClusterLoHI
605 MOV.B &ClusterL,Y \ 3 Y = ClusterLoLo
606 CMP #2,&FATtype \ 3 FAT32?
608 \ JZ CTF1S_end \ 2 yes
610 \ input : Cluster n, max = 7FFFFF (SDcard up to 256 GB)
611 \ ClusterLoLo*4 = displacement in 512 bytes sector ==> FAToffset
612 \ ClusterHiLo&ClusterLoHi +C << 1 = relative FATsector + orgFAT1 ==> FATsector
613 \ ----------------------------------\
614 MOV.B &ClusterH,X \ X = 0:ClusterHiLo
615 SWPB X \ X = ClusterHiLo:0
616 ADD X,W \ W = ClusterHiLo:ClusterLoHi
617 \ ----------------------------------\
618 SWPB Y \ Y = ClusterLoLo:0
619 ADD Y,Y \ 1 Y = ClusterLoLo:0 << 1 + carry for FATsector
620 ADDC W,W \ W = ClusterHiLo:ClusterLoHi << 1 = ClusterHiLo:ClusterL / 128
624 ADD Y,Y \ Y = 0:ClusterLoLo << 1
627 \ ----------------------------------\
632 \ ----------------------------------\ Input : Cluster, output: Sector = Cluster_first_sector
633 \ ComputeClusFrstSect \ If Cluster = 1 ==> RootDirectory ==> SectorL = OrgRootDir
634 \ ----------------------------------\ Output: SectorL of Cluster
636 MOV &OrgRootDir,&SectorL \
637 CMP.B #0,&ClusterH \ clusterH <> 0 ?
639 CMP #1,&ClusterL \ clusterHL = 1 ? (FAT16 specificity)
640 0= IF \ yes, sectorL for FAT16 OrgRootDIR is done
645 TLV_ORG 4 + @ $81F3 U<
646 $81EF TLV_ORG 4 + @ U< = \ MSP430FR413x subfamily without hardware_MPY
647 [IF] \ Cluster24<<SecPerClus --> ClusFrstSect\ SecPerClus = {1,2,4,8,16,32,64}
648 PUSHM #3,W \ 5 PUSHM W,X,Y
649 MOV.B &SecPerClus,W \ 3 SecPerClus(5-1) = multiplicator
650 MOV &ClusterL,X \ 3 Cluster(16-1) --> MULTIPLICANDlo
651 MOV.B &ClusterH,Y \ 3 Cluster(24-17) --> MULTIPLICANDhi
654 ADD X,X \ 1 (RLA) shift one left MULTIPLICANDlo16
655 ADDC Y,Y \ 1 (RLC) shift one left MULTIPLICANDhi8
656 FW1 RRA W \ 1 shift one right multiplicator
657 U>= UNTIL \ 2 C = 0 loop back
658 ADD &OrgClusters,X \ 3 OrgClusters = sector of virtual_cluster_0, word size
660 MOV X,&SectorL \ 3 low result
661 MOV Y,&SectorH \ 3 high result
662 POPM #3,W \ 5 POPM Y,X,W
663 \ ----------------------------------\
664 [ELSE] ; hardware MPY, the general case
665 \ ----------------------------------\
666 MOV &ClusterL,&MPY32L \ 3
667 MOV &ClusterH,&MPY32H \ 3
668 MOV &SecPerClus,&OP2 \ 5+3
669 MOV &RES0,&SectorL \ 5
670 MOV &RES1,&SectorH \ 5
671 ADD &OrgClusters,&SectorL \ 5 OrgClusters = sector of virtual cluster 0, word size
672 ADDC #0,&SectorH \ 3 32~
673 \ ----------------------------------\
675 \ ----------------------------------\32~ + 5~ by 2* shift
677 \ ----------------------------------\
682 \ ----------------------------------\
683 \ ComputeHDLcurrentSector \ input: currentHandle, output: Cluster, Sector
684 \ ----------------------------------\
685 MOV HDLL_CurClust(T),&ClusterL \
686 MOV HDLH_CurClust(T),&ClusterH \
687 CALL #CLS_SCT \ Cluster --> its first sector
688 MOV.B HDLB_ClustOfst(T),W \
692 \ ----------------------------------\
696 \ ==================================\
697 \ SetBufLenAndLoadCurSector \WXY <== previous handle reLOAD with BufferPtr<>0
698 \ ==================================\
699 MOV #bytsPerSec,&BufferLen \ preset BufferLen
700 CMP #0,HDLH_CurSize(T) \ CurSize > 65535 ?
701 JNZ LoadHDLcurrentSector \ yes
702 \ CMP HDLL_CurSize(T),&BufferPtr \ BufferPtr >= CurSize ? (BufferPtr = 0 or see RestorePreviousLoadedBuffer)
704 CMP #bytsPerSec,HDLL_CurSize(T) \ CurSize >= 512 ?
705 JC LoadHDLcurrentSector \ yes
706 MOV HDLL_CurSize(T),&BufferLen \ no: adjust BufferLen
707 \ ==================================\
708 \ LoadHDLcurrentSector \ <=== OPEN_WRITE_APPEND
709 \ ==================================\
710 CALL #CUR_SCT \ use no registers
711 MOV #READ_SECT,PC \ SWX then RET
712 \ ----------------------------------\
716 \ ==================================\
717 \ CloseHandleT \ <== CLOSE, Read_File, TERM2SD", OPEN_DEL
718 \ ==================================\
720 CMP #0,T \ no handle?
722 CMP.B #2,HDLB_Token(T) \ opened as write (updated) file ?
724 CALL #WriteBuffer \SWXY
725 CALL #OPWW_UpdateDirectory \SWXY
727 CMP.B #-1,HDLB_Token(T) \ token type = LOAD?
729 \ ----------------------------------\
730 \ RestoreSD_ACCEPTContext \
731 \ ----------------------------------\
732 MOV HDLW_PrevLEN(T),TOS \
733 MOV HDLW_PrevORG(T),0(PSP) \ -- org len
734 \ ----------------------------------\
735 \ RestoreReturnOfSD_ACCEPT \
736 \ ----------------------------------\
737 ADD #6,RSP \ R-- QUIT3 empties return stack
738 MOV @RSP+,IP \ skip return to SD_ACCEPT
739 CMP #0,HDLW_PrevHDL(T) \
740 0= IF \ no more token
742 MOV #TIB_ORG,&CIB_ADR \ restore TIB as Current Input Buffer for next line (next QUIT)
743 MOV #ACCEPT+4,&ACCEPT+2 \ restore default ACCEPT for next line (next QUIT)
749 MOV.B #0,HDLB_Token(T) \ release the handle
750 MOV @T,T \ T = previous handle
751 MOV T,&CurrentHdl \ becomes current handle
753 0<> IF \ if more handles
754 \ ----------------------------------\
755 \ RestorePreviousLoadedBuffer \
756 \ ----------------------------------\
757 MOV HDLW_BUFofst(T),&BufferPtr \ restore previous BufferPtr
758 CALL #LOAD_SCT \ then reload previous buffer
762 MOV @RSP+,PC \ Z = 1 if no more handle
763 \ ----------------------------------\
766 \ sequentially load in SD_BUF bytsPerSec bytes of a file opened as read or as load
767 \ if new bufferLen have a size <= BufferPtr, closes the file then RET.
768 \ if previous bufferLen had a size < bytsPerSec, closes the file and reloads previous LOADed file if exist.
769 \ HDLL_CurSize leaves the not yet read size
770 \ All used registers must be initialized.
773 \ ==================================\
774 \ Read_File \ <== SD_ACCEPT, READ
775 \ ==================================\
777 MOV #0,&BufferPtr \ reset BufferPtr (the buffer is already read)
778 \ ----------------------------------\
779 CMP #bytsPerSec,&BufferLen \
780 JNZ CLOSE_HDL \ because this last and incomplete sector is already read
781 SUB #bytsPerSec,HDLL_CurSize(T) \ HDLL_CurSize is decremented of one sector lenght
782 SUBC #0,HDLH_CurSize(T) \
783 ADD.B #1,HDLB_ClustOfst(T) \ current cluster offset is incremented
784 CMP.B &SecPerClus,HDLB_ClustOfst(T) \ Cluster Bound reached ?
786 \ ----------------------------------\
787 \SearchNextCluster \ yes
788 \ ----------------------------------\
789 MOV.B #0,HDLB_ClustOfst(T) \ reset Current_Cluster sectors offset
790 CALL #CLS_FAT\WXY Output: W=FATsector, Y=FAToffset, Cluster=HDL_CurCluster
793 CALL #ReadSectorWX \SWX (< 65536)
794 MOV #0,HDLH_CurClust(T) \
795 MOV SD_BUF(Y),HDLL_CurClust(T) \
796 CMP #1,&FATtype \ FAT16?
798 MOV SD_BUF+2(Y),HDLH_CurClust(T) \
804 \ if first open_load token, save DefaultInputStream
805 \ if other open_load token, decrement token, save previous context
808 \ Input : EntryOfst, Cluster = EntryOfst(HDLL_FirstClus())
809 \ init handle(HDLL_DIRsect,HDLW_DIRofst,HDLL_FirstClus,HDLL_CurClust,HDLL_CurSize)
810 \ Output: Cluster = first Cluster of file, X = CurrentHdl
813 \ ----------------------------------\ input : Cluster, EntryOfst
814 \ GetFreeHandle \STWXY init handle(HDLL_DIRsect,HDLW_DIRofst,HDLL_FirstClus = HDLL_CurClust,HDLL_CurSize)
815 \ ----------------------------------\ output : T = new CurrentHdl
816 MOV #8,S \ prepare file already open error
818 MOV #0,X \ X = init previous handle as 0
819 \ ----------------------------------\
821 \ ----------------------------------\
823 CMP.B #0,HDLB_Token(T) \ free handle ?
826 CMP &ClusterH,HDLH_FirstClus(T) \
828 CMP &ClusterL,HDLL_FirstClus(T) \
830 MOV @RSP+,PC \ error 8: file already Open abort ===>
834 MOV T,X \ handle is occupied, keep it in X as previous handle
835 ADD #HandleLenght,T \
838 ADD S,S \ 16 = no more handle error
839 MOV @RSP+,PC \ abort ===>
842 \ ----------------------------------\
843 \FreeHandleFound \ T = new handle, X = previous handle
844 \ ----------------------------------\
845 MOV #0,S \ prepare Happy End (no error)
847 MOV X,HDLW_PrevHDL(T) \ link to previous handle
848 \ ----------------------------------\
849 \ CheckCaseOfPreviousToken \
850 \ ----------------------------------\
851 CMP #0,X \ existing previous handle?
853 ADD &TOIN,HDLW_BUFofst(X) \ in previous handle, add interpret offset to Buffer offset
854 \ ----------------------------------\
855 \ CheckCaseOfLoadFileToken \
856 \ ----------------------------------\
857 CMP.B #0,W \ open_type is LOAD (-1) ?
859 CMP.B #0,HDLB_Token(X) \ previous token is negative? (open_load type)
861 ADD.B HDLB_Token(X),W \ LOAD token = previous LOAD token -1
865 \ ----------------------------------\
867 \ ----------------------------------\
868 MOV.B W,HDLB_Token(T) \ marks handle as open type: <0=LOAD, 1=READ, 2=WRITE, 4=DEL
869 MOV.B #0,HDLB_ClustOfst(T) \ clear ClustOfst
870 MOV &SectorL,HDLL_DIRsect(T) \ init handle DIRsectorL
871 MOV &SectorH,HDLH_DIRsect(T) \
873 MOV Y,HDLW_DIRofst(T) \ init handle SD_BUF offset of DIR entry
874 MOV SD_BUF+26(Y),HDLL_FirstClus(T) \ init handle firstcluster of file (to identify file)
875 MOV SD_BUF+20(Y),HDLH_FirstClus(T)
876 MOV SD_BUF+26(Y),HDLL_CurClust(T) \ init handle CurrentCluster
877 MOV SD_BUF+20(Y),HDLH_CurClust(T)
878 MOV SD_BUF+28(Y),HDLL_CurSize(T) \ init handle LOW currentSizeL
879 MOV SD_BUF+30(Y),HDLH_CurSize(T) \
880 MOV #0,&BufferPtr \ reset BufferPtr all type of files
881 CMP.B #2,W \ is a WRITE file handle?
883 MOV CUR_SCT,PC \ = 2, is a WRITE file
885 S>= IF \ > 2, is a file to be deleted
888 MOV #0,HDLW_BUFofst(T) \ < 2, is a READ or a LOAD file
890 0= IF \ case of first loaded file: ReplaceInputBuffer
891 MOV #SDIB_ORG,&CIB_ADR \ set SD Input Buffer as Current Input Buffer before return to QUIT
892 MOV #SD_ACCEPT,&ACCEPT+2 \ redirect ACCEPT to SD_ACCEPT before return to QUIT
895 MOV LOAD_SCT,PC \ case of READ file
897 \ ----------------------------------\
898 \ SaveBufferContext \ (see CLOSE_HDL)
899 \ ----------------------------------\
900 MOV &SOURCE_LEN,HDLW_PrevLEN(T) \ = CPL
901 SUB &TOIN,HDLW_PrevLEN(T) \ PREVLEN = CPL - >IN
902 MOV &SOURCE_ORG,HDLW_PrevORG(T) \ = CIB
903 ADD &TOIN,HDLW_PrevORG(T) \ PrevORG = CIB + >IN
904 \ ----------------------------------\
905 MOV LOAD_SCT,PC \ then RET
909 \ ----------------------------------\ input : X = countdown_of_spaces, Y = name pointer in buffer
910 \ ParseEntryNameSpaces \XY
911 \ ----------------------------------\ output: Z flag, Y is set after the last space char
915 CMP.B #32,SD_BUF(Y) \ SPACE ?
917 ADD #1,Y \ inc pointer
918 SUB #1,X \ dec countdown_of_spaces
923 \ ----------------------------------\
929 \ S = Error 1 : PathNameNotFound \
930 \ S = Error 2 : NoSuchFile \
931 \ S = Error 4 : DIRisFull \
932 \ S = Error 8 : alreadyOpen \
933 \ S = Error 16 : NomoreHandle \
934 \ ----------------------------------\
935 COLON \ set ECHO, type Pathname, type #error, type "< OpenError"\ no return
937 ABORT_SD \ to insert S error as flag, no return
941 \ ======================================================================
942 \ OPEN FILE primitive
943 \ ======================================================================
945 \ primitive for LOAD" READ" CREATE" WRITE" DEL"
946 \ store OpenType on TOS,
947 \ compile state : compile OpenType, compile SQUOTE and the string of provided pathname
948 \ exec state : open a file from SD card via its pathname
949 \ convert counted string found at HERE in a String then parse it
950 \ media identifiers "A:", "B:" ... are ignored (only one SD_Card),
951 \ char "\" as first one initializes rootDir as SearchDir.
952 \ if file found, if not already open and if free handle...
953 \ ...open the file as read and return the handle in CurrentHdl.
954 \ if the pathname is a directory, change current directory, no handle is set.
955 \ if an error is encountered, no handle is set, an error message is displayed.
958 \ ----------------------------------\
959 \ Open_File \ -- open_type HERE HERE as pathname ptr
960 \ ----------------------------------\
961 MOV @PSP+,rDOCON \ rDOCON = addr = pathname PTR
962 ADD rDOCON,TOS \ TOS = EOS (End Of String) = pathname end
963 MOV TOS,&EndOfPath \ for WRITE CREATE part
964 \ ----------------------------------\
966 \ ----------------------------------\
968 MOV &DIRClusterL,&ClusterL \
969 MOV &DIRclusterH,&ClusterH \
970 CMP rDOCON,TOS \ PTR = end of pathname ?
971 \ JZ OPN_NoPathName ;
972 0= ?GOTO BW1 \ yes: error 1 ===>
973 CMP.B #':',1(rDOCON) \ A: B: C: ... in pathname ?
975 ADD #2,rDOCON \ yes : skip drive because not used, only one SD_card
977 CMP.B #'\',0(rDOCON) \ "\" as first char ?
979 \ JNZ OPN_SearchDirSector \ no
980 ADD #1,rDOCON \ yes : skip '\' char
981 MOV &FATtype,&ClusterL \ FATtype = 1 as FAT16 RootDIR, FATtype = 2 = FAT32RootDIR
983 \ OPN_EndOfStringTest \ <=== dir found in path
984 BW2 CMP rDOCON,TOS \ PTR = EOS ? (end of pathname ?)
986 \ JZ OPN_SetCurrentDIR \ yes
987 \ OPN_SearchDirSector \
988 FW1 MOV rDOCON,&Pathname \ save Pathname ptr
989 CALL #CLS_SCT \ output: SectorHL
990 MOV #32,rDODOES \ preset countdown for FAT16 RootDIR sectors
991 CMP #2,&FATtype \ FAT32?
992 JZ OPN_SetDirSectors \ yes
994 CMP &ClusterL,&FATtype \ FAT16 AND RootDIR ?
995 \ JZ OPN_LoadDIRsector \ yes
997 \ OPN_SetDirSectors \
999 MOV &SecPerClus,rDODOES \
1000 \ OPN_LoadDIRsector \ <=== Dir Sector loopback
1002 FW1 CALL #READ_SECT \SWX
1003 MOV #2,S \ prepare no such file error
1004 MOV #0,W \ init entries count
1005 \ OPN_SearchDIRentry \ <=== DIR Entry loopback
1007 RLAM #4,Y \ --> * 16
1009 MOV Y,&EntryOfst \ EntryOfst points to first free entry
1010 CMP.B #0,SD_BUF(Y) \ free entry ? (end of entries in DIR)
1012 0= ?GOTO BW1 \ error 2 NoSuchFile, used by create ===>
1013 MOV #8,X \ count of chars in entry name
1014 \ OPN_CompareName8chars
1016 CMP.B @rDOCON+,SD_BUF(Y) \ compare Pathname(char) with DirEntry(char)
1018 \ JNZ OPN_FirstCharMismatch \
1021 \ JNZ OPN_CompareName8chars \ loopback if chars 1 to 7 of string and DirEntry are equal
1023 ADD #1,rDOCON \ 9th char of Pathname is always a dot
1025 \ OPN_FirstCharMismatch \
1026 CMP.B #'.',-1(rDOCON) \ FirstNotEqualChar of Pathname = dot ?
1027 0<> IF \ OPN_DotNotFound
1028 ADD #3,X \ for next cases not equal chars of entry until 11 must be spaces
1029 CALL #NAME_BL \ for X + 3 chars
1030 JNZ OPN_DIRentryMismatch \ if a char entry <> space
1031 CMP.B #'\',-1(rDOCON) \ FirstNotEqualChar of Pathname = "\" ?
1032 \ JZ OPN_EntryFound \
1034 CMP rDOCON,TOS \ EOS exceeded ?
1035 \ JNC OPN_EntryFound \ yes
1037 \ OPN_DIRentryMismatch \
1038 MOV &pathname,rDOCON \ reload Pathname
1039 ADD #1,W \ inc entry
1040 CMP #16,W \ 16 entry in a sector
1041 JNZ OPN_SearchDIRentry \ ===> loopback for search next DIR entry
1045 SUB #1,rDODOES \ dec count of Dir sectors
1046 JNZ OPN_LoadDIRsector \ ===> loopback for search next DIR sector
1048 GOTO BW1 \ error 4 ===>
1050 \ OPN_DotFound \ not equal chars of entry name until 8 must be spaces
1051 CMP.B #'.',-2(rDOCON) \ LastCharEqual = dot ?
1052 JZ OPN_DIRentryMismatch \ case of first DIR entry = "." and Pathname = "..\"
1053 CALL #NAME_BL \ parse X spaces, X{0,...,7}
1054 JNZ OPN_DIRentryMismatch \ if a char entry <> space
1056 \ OPN_CompareExt3chars \
1057 CMP.B @rDOCON+,SD_BUF(Y) \ compare string(char) with DirEntry(char)
1058 JNZ OPN_ExtNotEqualChar \
1061 JNZ OPN_CompareExt3chars \ nothing to do if chars equal
1062 JMP OPN_EntryFound \
1063 \ OPN_ExtNotEqualChar \
1064 CMP rDOCON,TOS \ EOS exceeded ?
1065 JC OPN_DIRentryMismatch \ no, loop back
1066 CMP.B #'\',-1(rDOCON) \ FirstNotEqualChar = "\" ?
1067 JNZ OPN_DIRentryMismatch \
1068 CALL #NAME_BL \ parse X spaces, X{0,...,3}
1069 JNZ OPN_DIRentryMismatch \ if a char entry <> space, loop back
1070 \ OPN_EntryFound \ Y points on the file attribute (11th byte of entry)
1072 FW2 MOV &EntryOfst,Y \ reload DIRentry
1073 MOV SD_BUF+26(Y),&ClusterL \ first clusterL of file
1074 MOV SD_BUF+20(Y),&ClusterH \ first clusterT of file, always 0 if FAT16
1075 \ OPN_EntryFoundNext
1076 BIT.B #10h,SD_BUF+11(Y) \ test if Directory or File
1078 \ OPN_DIRfound \ entry is a DIRECTORY
1079 CMP #0,&ClusterH \ case of ".." entry, when parent directory is root
1080 JNZ OPN_DIRfoundNext \
1081 CMP #0,&ClusterL \ case of ".." entry, when parent directory is root
1082 JNZ OPN_DIRfoundNext \
1083 MOV &FATtype,&ClusterL \ set cluster as RootDIR cluster
1084 \ OPN_DIRfoundNext \
1085 CMP rDOCON,TOS \ EOS exceeded ?
1086 JC OPN_EndOfStringTest \ no: (we presume that FirstNotEqualChar = "\") ==> loop back
1087 \ OPN_SetCurrentDIR \ -- open_type ptr
1088 FW3 MOV &ClusterL,&DIRClusterL \
1089 MOV &ClusterH,&DIRclusterH \
1090 MOV #0,0(PSP) \ -- open_type ptr open_type = 0
1092 \ OPN_FileFound \ -- open_type ptr
1094 CALL #NEW_HDL \STWXY init handle(HDLL_DIRsect,HDLW_DIRofst,HDLL_FirstClus = HDLL_CurClust,HDLL_CurSize)
1095 \ --------------------------------\ output : T = CurrentHdl*, S = ReturnError, Y = DIRentry offset
1096 \ OPN_NomoreHandle \ S = error 16
1097 \ OPN_alreadyOpen \ S = error 8
1098 \ OPN_EndOfDIR \ S = error 4
1099 \ OPN_NoSuchFile \ S = error 2
1100 \ OPN_NoPathName, S = error 1
1102 MOV #xdodoes,rDODOES \ restore rDODOES
1103 MOV #xdocon,rDOCON \ restore rDODOES
1104 MOV @PSP+,W \ -- ptr W = open_type
1106 \ ----------------------------------\ then go to selected OpenType subroutine (OpenType = W register)
1108 \ ----------------------------------\
1110 JZ OPEN_LOAD_END \ nothing to do
1111 \ ----------------------------------\
1113 \ ----------------------------------\
1114 .IFDEF SD_CARD_READ_WRITE \
1115 CMP.B #-1,W \ open_type = LOAD"
1116 JNZ OPEN_QREAD \ next step
1118 \ ----------------------------------\ here W is free
1120 \ ----------------------------------\
1124 \-----------------------------------------------------------------------
1125 \ SD_CARD_LOADER FORTH word
1126 \-----------------------------------------------------------------------
1128 \Z LOAD" pathame" -- immediate
1129 \ compile state : compile LOAD" pathname"
1130 \ exec state : open a file from SD card via its pathname
1131 \ see Open_File primitive for pathname conventions
1132 \ the opened file becomes the new input stream for INTERPRET
1133 \ this command is recursive, limited only by the count of free handles (up to 8)
1134 \ LOAD" acts also as dos command "CD" :
1135 \ - LOAD" \misc\" set a:\misc as current directory
1136 \ - LOAD" \" reset current directory to root
1137 \ - LOAD" ..\" change to parent directory
1139 \ ======================================================================
1140 \ LOAD" primitive as part of Open_File
1141 \ input from open: S = OpenError, W = open_type, SectorHL = DIRsectorHL,
1142 \ Buffer = [DIRsector], ClusterHL = FirstClusterHL
1143 \ from open(GetFreeHandle): Y = DIRentry, T = CurrentHdl
1144 \ output: nothing else abort on error
1145 \ ======================================================================
1149 CODE S_ \ Squote alias with blank instead quote separator
1150 MOV #0,&CAPS \ turn CAPS OFF
1152 XSQUOTE , \ compile run-time code
1153 $20 WORD \ -- c-addr (= HERE)
1155 MOV.B @TOS,TOS \ -- len compile string
1156 ADD #1,TOS \ -- len+1
1158 ADDC TOS,&DP \ store aligned DP
1160 MOV @RSP+,IP \ pop paired with push COLON
1161 MOV #$20,&CAPS \ turn CAPS ON (default state)
1166 \ ----------------------------------\
1167 CODE LOAD \ immediate
1168 \ ----------------------------------\
1169 MOV.B #-1,W \ W = OpenType
1170 \ ----------------------------------\
1173 MOV W,0(PSP) \ -- Open_type (0=LOAD", 1=READ", 2=WRITE", 4=DEL")
1175 COLON \ if exec state
1177 20 WORD COUNT \ -- open_type addr u
1178 ELSE \ compile OPEN_FILE
1185 \ .IFDEF SD_CARD_READ_WRITE
1187 \-----------------------------------------------------------------------
1188 \ SD_READ_WRITE FORTH words
1189 \-----------------------------------------------------------------------
1192 \ parse string until " is encountered, convert counted string in String
1193 \ then parse string until char '0'.
1194 \ media identifiers "A:", "B:" ... are ignored (only one SD_Card),
1195 \ char "\" as first one initializes rootDir as SearchDir.
1196 \ if file found, if not already open and if free handle...
1197 \ ...open the file as read and return the handle in CurrentHdl.
1198 \ then load first sector in buffer, bufferLen and bufferPtr are ready for read
1199 \ currentHdl keep handle that is flagged as "read".
1201 \ to read sequentially next sectors use READ word. A flag is returned : true if file is closed.
1202 \ the last sector so is in buffer.
1204 \ if pathname is a directory, change current directory.
1205 \ if an error is encountered, no handle is set, error message is displayed.
1207 \ READ" acts also as CD dos command :
1208 \ - READ" a:\misc\" set a:\misc as current directory
1209 \ - READ" a:\" reset current directory to root
1210 \ - READ" ..\" change to parent directory
1212 \ to close all files type : WARM (or COLD, RESET)
1214 \ ----------------------------------\
1216 MOV.B #1,W \ W = OpenType
1220 \Z WRITE" pathame" -- immediate
1221 \ open or create the file designed by pathname.
1222 \ an error occurs if the file is already opened.
1223 \ the last sector of the file is loaded in buffer, and bufferPtr leave the address of the first free byte.
1224 \ compile state : compile WRITE" pathname"
1225 \ exec state : open or create entry selected by pathname
1226 \ ----------------------------------\
1228 MOV.B #2,W \ W = OpenType
1233 \Z DEL" pathame" -- immediate
1234 \ compile state : compile DEL" pathname"
1235 \ exec state : DELETE entry selected by pathname
1237 \ ----------------------------------\
1239 \ ----------------------------------\
1240 MOV.B #4,W \ W = OpenType
1246 \ close current handle
1247 \ ----------------------------------\
1249 \ ----------------------------------\
1254 \ .ENDIF \ SD_CARD_READ_WRITE
1260 \ https://forth-standard.org/standard/core/Equal
1261 \ = x1 x2 -- flag test x1=x2
1270 XOR #-1,TOS \ 1 flag Z = 1
1275 \ https://forth-standard.org/standard/core/DUP
1276 \ DUP x -- x x duplicate top of stack
1280 BW1 SUB #2,PSP \ 2 push old TOS..
1281 MOV TOS,0(PSP) \ 3 ..onto stack
1285 \ https://forth-standard.org/standard/core/qDUP
1286 \ ?DUP x -- 0 | x x DUP if nonzero
1288 CMP #0,TOS \ 2 test for TOS nonzero
1294 \ https://forth-standard.org/standard/core/EVALUATE
1295 \ EVALUATE \ i*x c-addr u -- j*x interpret string
1296 [UNDEFINED] EVALUATE
1299 MOV #SOURCE_LEN,X \ 2
1300 MOV @X+,S \ 2 S = SOURCE_LEN
1301 MOV @X+,T \ 2 T = SOURCE_ORG
1302 MOV @X+,W \ 2 W = TOIN
1303 PUSHM #4,IP \ 6 PUSHM IP,S,T,W
1308 MOV @RSP+,&SOURCE_ORG \ 4
1309 MOV @RSP+,&SOURCE_LEN \ 4
1315 \ BOOT RSTIV_MEM -- \ bootstrap on SD_CARD\BOOT.4th file
1317 \ to enable bootstrap type: ' BOOT IS WARM
1318 \ to disable bootstrap type: ' BOOT [PFA] IS WARM
1320 MOV #INIT_SD,X \ X = INIT_SD
1321 BIC #LOCKLPM5,&PM5CTL0 \ activate all previous I/O settings, mandatory for QSD_MEM.
1322 CMP #2,TOS \ RSTIV_MEM <> WARM ?
1324 MOV @RSP+,PC \ if RSTIV_MEM U< 2, return to BODYWARM
1326 BIT.B #CD_SD,&SD_CDIN \ SD_memory in SD_Card socket ?
1328 MOV 2(X),PC \ if no, goto previous INIT: INIT TERMINAL only then ret to PFAWARM
1330 \---------------------------------------------------------------------------------
1331 \ RESET 6: if RSTIV_MEM <> WARM, init TERM, init SD
1332 \---------------------------------------------------------------------------------
1333 CALL X \ init TERM UC first then init SD card, TOS = RSTIV_MEM
1334 \---------------------------------------------------------------------------------
1336 \---------------------------------------------------------------------------------
1337 MOV #PSTACK-2,PSP \ to avoid error "Stack empty!"
1339 MOV #LSTACK,&LEAVEPTR \ > same as QUIT
1342 S_ LOAD" BOOT.4TH" \ LOAD BOOT.4TH issues error 2 if no such file...
1343 EVALUATE \ to interpret this string