OSDN Git Service

la der de der
[fast-forth/master.git] / MSP430-FORTH / test / SD_LOAD_2022-09-26-15-38-34.f
1 \ -*- coding: utf-8 -*-
2
3 SD_LOAD.f
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 !
10
11
12 \ =====================================================================
13 \ goal : accept 64 MB up to 64 GB SD_CARD
14 \ =====================================================================
15 \ thus FAT and RootClus logical sectors are word addressable.
16
17 \ FAT is a little endian structure.
18 \ CMD frame is sent as big endian.
19
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.
24
25 \ ref. https://en.wikipedia.org/wiki/Extended_boot_record
26 \ ref. https://en.wikipedia.org/wiki/Partition_type
27
28 \ Formatage FA16 d'une SDSC Card 2GB
29 \ First sector of physical drive (sector 0) content :
30 \ ---------------------------------------------------
31 \ dec@| HEX@
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
36
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
41
42
43 \ Partition type Description
44 \ 0         empty / unused
45 \ 1         FAT12
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
54
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)
67
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
76
77 \ ====================================================================================
78
79 \ Formatage FA32 d'une SDSC Card 8GB
80 \ First sector of physical drive (sector 0) content :
81 \ ---------------------------------------------------
82 \ dec@| HEX@
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
87
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
92
93
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)
102
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)
110
111
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
120
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
123
124
125     MARKER {SD_APP}
126 \ CFA = DODOES
127 \ PFA = MARKER_DOES
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 ....
137
138
139
140 \   ====================================\
141     HDNCODE SPI_GET                     \ PUT(FFh) one time, output : W = received byte, X = 0
142 \   ====================================\
143     MOV #1,X                            \
144 \   ====================================\
145 \   SPI_X_GET                           \ PUT(FFh) X times, output : W = last received byte, X = 0
146 \   ====================================\
147     MOV #-1,W                           \
148 \   ====================================\
149 \   SPI_PUT                             \ PUT(W) X times, output : W = last received byte, X = 0
150 \   ====================================\
151     BEGIN
152         SWPB W                          \ 1 
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 ?
155         0<> IF                          \ no
156             BEGIN
157                 BIT #RX_SD,&SD_IFG      \ 3
158             0<> UNTIL
159                 CMP.B #0,&SD_RXBUF      \ 3 clear RX_BUF flag
160         THEN
161 \        NOP                             \  NOPx adjusted to avoid SD error
162         SUB #1,X                        \ 1
163     0= UNTIL                            \ 2 12~ loop
164     MOV.B &SD_RXBUF,W                   \ 3
165     MOV @RSP+,PC                        \ 4 X=0
166     ENDCODE
167 \   ------------------------------------\
168
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 ?
175     0<> IF                              \
176         MOV #COLD,PC                    \ no: force COLD
177     THEN                                \ yes
178 \   ------------------------------------\ input = logical sector...
179     ADD &BS_FirstSectorL,W              \ 3
180     ADDC &BS_FirstSectorH,X             \ 3
181 \   ------------------------------------\ ...output = physical sector
182 \   Compute CMD                         \
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
188         ADDC.B  X,X                     \ 1
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)
193         SWPB    W                       \ 1
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)
196         SWPB    X                       \ 1
197         MOV.B   X,&SD_CMD_FRM+4         \ 3 $(01 ll LL hh HH CMD)
198     THEN    
199 \   ====================================\
200 BW1 \   WaitIdleBeforeSendCMD           \ <=== CMD41, CMD1, CMD16 (forthMSP430FR_SD_INIT.asm)
201 \   ====================================\
202     BEGIN                               \
203         CALL #SPI_GET                   \
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 \   ------------------------------------\
217     BEGIN
218         MOV.B   SD_CMD_FRM(X),&SD_TXBUF \ 5 
219         CMP     #0,&SD_BRW              \ 3 full speed ?
220         0<> IF                          \ no
221             BEGIN                       \  case of low speed during memCardInit
222                 BIT #RX_SD,&SD_IFG      \ 3
223                 JZ  Send_CMD_Loop       \ 2
224             0<> UNTIL
225             CMP.B #0,&SD_RXBUF          \ 3 to clear UCRXIFG
226         THEN    
227 \        NOP                             \ 0 NOPx adjusted to avoid SD error
228         SUB.B   #1,X                    \ 1
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
237     BEGIN
238         SUB #1,X                        \ 1
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 ?
242         0<> IF                          \
243             BEGIN                       \  case of low speed during memCardInit (CMD0,CMD8,ACMD41,CMD16)
244                 BIT #RX_SD,&SD_IFG      \ 3
245             0<> UNTIL                   \ 2
246         THEN
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
253
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
258     GOTO BW2                            \
259 \   ------------------------------------\
260
261
262 \ SD Error n°
263 \ High byte
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
270
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
279
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
288
289 \ ----------------------------------\
290 CODE ABORT_SD                       \ <=== OPEN file errors from forthMSP430FR_SD_LOAD.asm
291 \ ----------------------------------\
292     SUB #2,PSP                      \
293     MOV TOS,0(PSP)                  \
294     MOV #10h,&BASE                  \ select hex
295     MOV S,TOS                       \
296 \    MOV #TIB_ORG,&CIB_ADR           \               restore TIB as Current Input Buffer
297 \    MOV #BODYACCEPT,&PFAACCEPT      \               restore default ACCEPT
298     LO2HI                           \
299     U. 
300     HI2LO
301     MOV #ABORT_TERM,PC              \ no return...
302 ENDCODE
303 \ ----------------------------------\
304
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
312     COLON                           \
313     S" < SD Error!"                 \ don't use S register
314     ABORT_SD
315     ;
316
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 \   ------------------------------------\
327         CALL #SPI_GET                   \
328         ADD.B #2,W                      \ 1 FEh expected value
329     0= UNTIL
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
335         ADD     #1,X                    \ 1
336         CMP     #BytsPerSec+1,X         \ 2
337     0<> WHILE
338         MOV.B   &SD_RXBUF,SD_BUF-1(X)   \ 5
339     REPEAT
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
347     MOV @RSP+,PC                        \
348     ENDCODE
349 \   ------------------------------------\
350
351 \    .IFDEF SD_CARD_READ_WRITE
352
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
365         ADD     #1,X                    \ 1
366         CMP     #BytsPerSec,X           \ 2
367     0= UNTIL
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
376     ENDCODE
377 \ ----------------------------------\
378
379 \    .ENDIF \ SD_CARD_READ_WRITE
380
381 \ ===========================================================
382 \ Init SD_Card
383 \ ===========================================================
384 \ ----------------------------------\
385     CODE INIT_SD
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
394         0= IF                           \ yes
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 \ ----------------------------------\
402             MOV #SD_LEN,X               \                      
403             BEGIN                       \ case of MSP430FR57xx : SD datas are in FRAM not initialized by RESET. 
404                 SUB #2,X                \ 1
405                 MOV #0,SD_ORG(X)        \ 3 
406             0= UNTIL                    \ 2
407 \ ----------------------------------\
408 \ SD_POWER_ON
409 \ ----------------------------------\
410             MOV #8,X                    \ send 64 clk on SD_clk
411             CALL #SPI_X_GET             \
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)
425             THEN
426 \ ----------------------------------\ see forthMSP430FR_SD_lowLvl.asm
427 \ INIT_CMD8                           \ mandatory if SD_Card >= V2.x     [11:8]supply voltage(VHS)
428 \ ----------------------------------\
429             BEGIN
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
433             0= UNTIL
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
443             CALL #SPI_X_GET             \WX
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 \ ----------------------------------\
456             BEGIN
457 \ INIT_CMD55                          \
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
468                 0= IF
469                     MOV #SD_ERROR,PC    \ ReturnError on time out : unusable card  (or insufficient Vdd SD)
470                 THEN
471             REPEAT                          \
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
480             0<> IF
481                 MOV #SD_ERROR,PC        \ if W = R1 <> 0, ReturnError = $20R1 \ send command ko
482             THEN                        \
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 \ ----------------------------------\
492             MOV #0,X
493             CALL #readSectorWX           \ read physical first sector
494             MOV #SD_BUF,Y               \
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 \ ----------------------------------\
499 \ TestPartitionID                   \
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 ?
504             0<> IF                  \ no
505 \ FAT16_LBA_Test                    \
506                 SUB.B #8,W              \ ID=0Eh Partition FAT16 using LBA ?
507                 0<> IF                  \ no
508 \ ----------------------------------\
509                     MOV #2,&FATtype         \ set FAT32
510 \ FAT32_LBA_Test                            \
511                     ADD.B #2,W              \ ID=0Ch Partition FAT32 using LBA ?
512                     0<> IF                  \ no
513 \ FAT32_CHS_LBA_Test                            \
514                         ADD.B #1,W              \ ID=0Bh Partition FAT32 using CHS & LBA ?
515                         0<> IF                  \ no
516                             ADD.B #4,W              \ ID=07h assigned to FAT 32 by MiniTools Partition Wizard....
517                             0<> IF                  \ no
518                                 ADD #0$200B,W           \
519                                 MOV W,S                 \
520                                 MOV #SD_CARD_ID_ERROR,PC    \ S = ReturnError = $20xx with xx = partition ID 
521                             THEN
522                         THEN
523                     THEN
524                 THEN
525             THEN
526 \ ----------------------------------\ see: https://en.wikipedia.org/wiki/Partition_type
527 \ Read_MBR_FirstSector              \ read first logical sector
528 \ ----------------------------------\ W = 0
529             MOV #0,X
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 ?
539             0= IF                       
540                 MOV 36(Y),W             \ W = BPB_FATSz32
541             THEN
542 \ Set_FATsize                           \
543             MOV W,&FATSize              \ limited to 16384 sectors....
544             ADD W,X                     \
545             MOV X,&OrgFAT2              \ X = OrgFAT1 + FATsize = OrgFAT2
546             ADD W,X                     \ X = OrgFAT2 + FATsize = FAT16 OrgRootDir | FAT32 OrgDatas
547             CMP #2,&FATtype             \ FAT32?
548             0<> IF
549 \ FAT16_SetRootCluster                  \
550                 MOV X,&OrgRootDIR       \ only FAT16 use, is a sector used by CLS_SCT
551                 ADD #32,X               \ OrgRootDir + RootDirSize = OrgDatas
552             THEN
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
557         THEN                            \
558     THEN                                \
559     MOV @RSP+,PC                        \ RET
560     ENDCODE
561 \ ----------------------------------\
562
563 \ ----------------------------------\ 
564     HDNCODE RST_ABORT_SD            \ common part of ?ABORT|RST
565 \ ----------------------------------\
566     CALL #RET_ADR                   \ which does nothing
567 \ ----------------------------------\
568     MOV &CurrentHdl,T               \
569     GOTO FW1
570     BEGIN
571         MOV.B #0,HDLB_Token(T)      \
572         MOV @T,T                    \
573 FW1     CMP #0,T                    \
574     0= UNTIL
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)
577     MOV @RSP+,PC                    \ RET
578     ENDCODE
579 \ ----------------------------------\
580
581 \-----------------------------------------------------------------------
582 \ SD card OPEN, LOAD subroutines
583 \-----------------------------------------------------------------------
584
585 \ used variables : BufferPtr, BufferLen
586
587 \ rules for registers use
588 \ S = error
589 \ T = CurrentHdl, pathname
590 \ W = SectorL, (RTC) TIME
591 \ X = SectorH, (RTC) DATE
592 \ Y = BufferPtr, (DIR) EntryOfst, FAToffset
593
594
595     HDNCODE CLS_FAT
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?
607     0= IF                               \ yes
608 \    JZ CTF1S_end                    \ 2 yes
609
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
621         SWPB Y   
622 \ CTF1S_end
623     THEN
624     ADD Y,Y                         \  Y = 0:ClusterLoLo << 1
625     MOV @RSP+,PC                    \ 4
626     ENDCODE
627 \ ----------------------------------\
628
629
630 \ use no registers
631     HDNCODE CLS_SCT
632 \ ----------------------------------\ Input : Cluster, output: Sector = Cluster_first_sector
633 \   ComputeClusFrstSect                 \ If Cluster = 1 ==> RootDirectory ==> SectorL = OrgRootDir
634 \ ----------------------------------\ Output: SectorL of Cluster
635     MOV     #0,&SectorH             \
636     MOV     &OrgRootDir,&SectorL    \
637     CMP.B   #0,&ClusterH            \ clusterH <> 0 ?
638     0= IF    
639         CMP     #1,&ClusterL            \ clusterHL = 1 ? (FAT16 specificity)
640         0= IF                           \ yes, sectorL for FAT16 OrgRootDIR is done
641             MOV @RSP+,PC
642         THEN
643     THEN
644
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
652     GOTO FW1                        \
653     BEGIN                           \
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
659     ADDC #0,Y                       \ 1
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 \ ----------------------------------\
674     [THEN]
675 \ ----------------------------------\32~ + 5~ by 2* shift
676     MOV @RSP+,PC                    \
677 \ ----------------------------------\
678     ENDCODE
679
680
681     HDNCODE CUR_SCT
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       \
689     ADD W,&SectorL                  \
690     ADDC #0,&SectorH                \
691     MOV @RSP+,PC                    \
692 \ ----------------------------------\
693     ENDCODE
694
695     HDNCODE LOAD_SCT
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)
703 \    JC       CLOSE_HDL              \ yes
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 \ ----------------------------------\
713     ENDCODE
714
715     HDNCODE CLOSE_HDL
716 \ ==================================\
717 \ CloseHandleT                      \ <== CLOSE, Read_File, TERM2SD", OPEN_DEL
718 \ ==================================\
719 MOV &CurrentHdl,T                   \
720 CMP #0,T                            \ no handle?
721 0<> IF                              \
722     CMP.B #2,HDLB_Token(T)          \ opened as write (updated) file ?
723     0= IF
724         CALL #WriteBuffer               \SWXY
725         CALL #OPWW_UpdateDirectory      \SWXY
726     ELSE
727         CMP.B #-1,HDLB_Token(T)     \ token type = LOAD? 
728         0= IF
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
741                 PUSH #ECHO  
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)
744             ELSE
745                 PUSH #NOECHO
746             THEN
747         THEN
748     THEN
749     MOV.B #0,HDLB_Token(T)          \ release the handle
750     MOV @T,T                        \ T = previous handle
751     MOV T,&CurrentHdl               \ becomes current handle
752     CMP #0,T                        \
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
759         BIC #Z,SR                       \ 
760     THEN
761 THEN
762     MOV @RSP+,PC                    \ Z = 1 if no more handle
763 \ ----------------------------------\
764     ENDCODE
765
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. 
771
772     HDNCODE READ_FILE
773 \ ==================================\
774 \ Read_File                         \ <== SD_ACCEPT, READ
775 \ ==================================\
776     MOV &CurrentHdl,T               \
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 ?
785     JNC LOAD_SCT   \ no
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
791     ADD &OrgFAT1,W                  \
792     MOV #0,X
793     CALL    #ReadSectorWX           \SWX (< 65536)
794     MOV     #0,HDLH_CurClust(T)     \
795     MOV SD_BUF(Y),HDLL_CurClust(T)  \
796     CMP     #1,&FATtype             \ FAT16?
797     JZ LOAD_SCT    \
798     MOV SD_BUF+2(Y),HDLH_CurClust(T) \
799     MOV LOAD_SCT,PC
800     ENDCODE
801
802
803
804 \ if first open_load token, save DefaultInputStream
805 \ if other open_load token, decrement token, save previous context
806
807 \ OPEN subroutine
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
811
812     HDNCODE NEW_HDL
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
817 MOV #FirstHandle,T                  \
818 MOV #0,X                            \ X = init previous handle as 0
819 \ ----------------------------------\
820 \ SearchHandleLoop                    \
821 \ ----------------------------------\
822 BEGIN
823     CMP.B #0,HDLB_Token(T)          \ free handle ?
824 0<> WHILE                           \ no
825 \ AlreadyOpenTest                     \
826     CMP &ClusterH,HDLH_FirstClus(T) \
827     0= IF
828         CMP &ClusterL,HDLL_FirstClus(T) \
829         0= IF
830             MOV @RSP+,PC            \ error 8: file already Open abort ===>
831         THEN
832     THEN
833 \ SearchNextHandle                    \
834     MOV T,X                         \ handle is occupied, keep it in X as previous handle
835     ADD #HandleLenght,T             \
836     CMP #HandleEnd,T                \
837     0= IF
838         ADD S,S                     \ 16 = no more handle error
839         MOV @RSP+,PC                \ abort ===>
840     THEN
841 REPEAT
842 \ ----------------------------------\
843 \FreeHandleFound                     \ T = new handle, X = previous handle
844 \ ----------------------------------\
845 MOV #0,S                            \ prepare Happy End (no error)
846 MOV T,&CurrentHdl                   \
847 MOV X,HDLW_PrevHDL(T)               \ link to previous handle
848 \ ----------------------------------\
849 \ CheckCaseOfPreviousToken          \
850 \ ----------------------------------\
851 CMP #0,X                            \ existing previous handle?
852 0<> IF                              \ yes
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) ?
858     S< IF                           \ yes
859         CMP.B #0,HDLB_Token(X)      \ previous token is negative? (open_load type)
860         S< IF                       \ yes
861             ADD.B HDLB_Token(X),W   \ LOAD token = previous LOAD token -1
862         THEN
863     THEN
864 THEN
865 \ ----------------------------------\
866 \ InitHandle                          \
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)        \ 
872 MOV &EntryOfst,Y                    \
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?
882 0= IF
883     MOV CUR_SCT,PC                  \ = 2, is a WRITE file
884 THEN
885 S>= IF                              \ > 2, is a file to be deleted
886     MOV @RSP+,PC                    \ RET
887 THEN
888 MOV #0,HDLW_BUFofst(T)              \ < 2, is a READ or a LOAD file
889 CMP.B #-1,W                         \
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
893 THEN
894 S>= IF
895 MOV LOAD_SCT,PC                     \ case of READ file
896 THEN
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
906     ENDCODE
907
908     HDNCODE NAME_BL
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
912 CMP #0,X                            \
913 0<> IF 
914     BEGIN
915         CMP.B #32,SD_BUF(Y)         \ SPACE ? 
916     0<> WHILE    
917         ADD #1,Y                    \   inc pointer
918         SUB #1,X                    \   dec countdown_of_spaces
919     0= UNTIL
920     THEN
921 THEN
922 MOV @RSP+,PC                        \ 
923 \ ----------------------------------\ 
924     ENDCODE
925
926
927 CODE OPEN_ERROR
928 BW1
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
936 S" < OpenError"                 \
937 ABORT_SD                        \ to insert S error as flag, no return
938 ;
939
940
941 \ ======================================================================
942 \ OPEN FILE primitive
943 \ ======================================================================
944 \ Open_File               --
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.
956
957     HDNCODE OPEN_FILE
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 \ ----------------------------------\
965 \ OPN_PathName                        \
966 \ ----------------------------------\
967 MOV #1,S                            \ error 1
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 ?
974     0= IF
975         ADD #2,rDOCON                \ yes : skip drive because not used, only one SD_card
976     THEN
977     CMP.B #'\',0(rDOCON)            \ "\" as first char ?
978     0<> ?GOTO FW1
979 \    JNZ     OPN_SearchDirSector     \ no
980     ADD     #1,rDOCON               \ yes : skip '\' char
981     MOV     &FATtype,&ClusterL      \       FATtype = 1 as FAT16 RootDIR, FATtype = 2 = FAT32RootDIR
982     MOV     #0,&ClusterH            \
983 \   OPN_EndOfStringTest             \ <=== dir found in path
984 BW2 CMP     rDOCON,TOS              \ PTR = EOS ? (end of pathname ?)
985     0= ?GOTO FW3
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
993     0<> IF
994         CMP     &ClusterL,&FATtype      \ FAT16 AND RootDIR ?
995 \        JZ      OPN_LoadDIRsector       \ yes
996         0= ?GOTO FW1
997 \   OPN_SetDirSectors                   \
998     THEN
999     MOV     &SecPerClus,rDODOES     \
1000 \   OPN_LoadDIRsector                   \ <=== Dir Sector loopback
1001 BW2
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
1006 BW3 MOV     W,Y                     \ 1
1007     RLAM    #4,Y                    \             --> * 16
1008     ADD     Y,Y                     \ 1           --> * 2
1009     MOV     Y,&EntryOfst            \ EntryOfst points to first free entry
1010     CMP.B   #0,SD_BUF(Y)            \ free entry ? (end of entries in DIR)
1011 \    JZ      OPN_NoSuchFile
1012     0= ?GOTO BW1                    \ error 2 NoSuchFile, used by create ===>
1013     MOV     #8,X                    \ count of chars in entry name
1014 \   OPN_CompareName8chars 
1015     BEGIN                           \
1016         CMP.B   @rDOCON+,SD_BUF(Y)      \ compare Pathname(char) with DirEntry(char)
1017     0= WHILE
1018 \        JNZ     OPN_FirstCharMismatch   \
1019         ADD     #1,Y                    \
1020         SUB     #1,X                    \
1021 \        JNZ     OPN_CompareName8chars   \ loopback if chars 1 to 7 of string and DirEntry are equal
1022     0= UNTIL
1023     ADD     #1,rDOCON               \ 9th char of Pathname is always a dot
1024     THEN
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          \
1033         0= ?GOTO FW1
1034         CMP     rDOCON,TOS              \ EOS exceeded ?
1035 \        JNC     OPN_EntryFound          \ yes
1036         U< ?GOTO FW2
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
1042         0<> ?GOTO BW3
1043         ADD     #1,&SectorL             \
1044         ADDC    #0,&SectorH             \
1045         SUB     #1,rDODOES              \ dec count of Dir sectors
1046         JNZ     OPN_LoadDIRsector       \ ===> loopback for search next DIR sector
1047         MOV     #4,S                    \
1048         GOTO BW1                        \ error 4 ===> 
1049     THEN
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
1055     MOV     #3,X                    \
1056 \   OPN_CompareExt3chars                \
1057     CMP.B   @rDOCON+,SD_BUF(Y)      \ compare string(char) with DirEntry(char)
1058     JNZ     OPN_ExtNotEqualChar     \
1059     ADD     #1,Y                    \
1060     SUB     #1,X                    \
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)
1071 FW1 
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
1077     JZ      OPN_FileFound           \
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 
1091     JMP     OPN_Dir
1092 \   OPN_FileFound                       \ -- open_type ptr
1093     MOV     @PSP,W                  \   
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
1101 OPN_Dir
1102     MOV     #xdodoes,rDODOES        \                   restore rDODOES
1103     MOV     #xdocon,rDOCON          \                   restore rDODOES
1104     MOV     @PSP+,W                 \ -- ptr            W = open_type
1105     MOV     @PSP+,TOS               \ --
1106 \ ----------------------------------\ then go to selected OpenType subroutine (OpenType = W register)
1107 \OPEN_QDIR                           \
1108 \ ----------------------------------\
1109     CMP     #0,W                    \
1110     JZ      OPEN_LOAD_END           \ nothing to do
1111 \ ----------------------------------\
1112 \OPEN_QLOAD                          \
1113 \ ----------------------------------\
1114     .IFDEF SD_CARD_READ_WRITE       \
1115     CMP.B   #-1,W                   \ open_type = LOAD"
1116     JNZ     OPEN_QREAD              \ next step
1117     .ENDIF                          \
1118 \ ----------------------------------\ here W is free
1119 \OPEN_LOAD                           \
1120 \ ----------------------------------\
1121 MOV @IP+,PC                         \
1122     ENDCODE
1123
1124 \-----------------------------------------------------------------------
1125 \ SD_CARD_LOADER FORTH word
1126 \-----------------------------------------------------------------------
1127
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
1138
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 \ ======================================================================
1146     
1147     [UNDEFINED] S_ 
1148     [IF]
1149     CODE S_             \           Squote alias with blank instead quote separator
1150     MOV #0,&CAPS        \           turn CAPS OFF
1151     COLON
1152     XSQUOTE ,           \           compile run-time code
1153     $20 WORD            \ -- c-addr (= HERE)
1154     HI2LO
1155     MOV.B @TOS,TOS      \ -- len    compile string
1156     ADD #1,TOS          \ -- len+1
1157     BIT #1,TOS          \           C = ~Z
1158     ADDC TOS,&DP        \           store aligned DP
1159     MOV @PSP+,TOS       \ --
1160     MOV @RSP+,IP        \           pop paired with push COLON
1161     MOV #$20,&CAPS      \           turn CAPS ON (default state)
1162     MOV @IP+,PC         \ NEXT
1163     ENDCODE IMMEDIATE
1164     [THEN]
1165
1166 \ ----------------------------------\
1167     CODE LOAD                       \ immediate
1168 \ ----------------------------------\
1169     MOV.B   #-1,W                   \ W = OpenType
1170 \ ----------------------------------\
1171 BW1 SUB #4,PSP                          \
1172     MOV TOS,2(PSP)                      \
1173     MOV W,0(PSP)                        \ -- Open_type (0=LOAD", 1=READ", 2=WRITE", 4=DEL")
1174     MOV &STATE,TOS                      \
1175     COLON                               \ if exec state
1176     IF 
1177         20 WORD COUNT                   \ -- open_type addr u
1178     ELSE                                \ compile OPEN_FILE
1179         LITERAL
1180         S_ [ 20 WORD DROP ]
1181     THEN
1182     OPEN_FILE
1183     ; IMMEDIATE
1184
1185 \   .IFDEF SD_CARD_READ_WRITE
1186
1187 \-----------------------------------------------------------------------
1188 \ SD_READ_WRITE FORTH words
1189 \-----------------------------------------------------------------------
1190
1191 \Z READ          --
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".
1200
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.
1203
1204 \ if pathname is a directory, change current directory.
1205 \ if an error is encountered, no handle is set, error message is displayed.
1206
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
1211
1212 \ to close all files type : WARM (or COLD, RESET)
1213
1214 \ ----------------------------------\
1215     CODE READ                           \ "            
1216     MOV.B   #1,W                    \ W = OpenType
1217     GOTO BW1                        \
1218     ENDCODE IMMEDIATE
1219
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 \ ----------------------------------\
1227     CODE WRITE                          \ "
1228     MOV.B   #2,W                    \ W = OpenType
1229     GOTO BW1                        \
1230     ENDCODE IMMEDIATE
1231
1232
1233 \Z DEL" pathame"   --       immediate
1234 \ compile state : compile DEL" pathname"
1235 \ exec state : DELETE entry selected by pathname
1236
1237 \ ----------------------------------\
1238     CODE DEL                            \ "
1239 \ ----------------------------------\
1240     MOV.B   #4,W                    \ W = OpenType
1241     GOTO BW1                        \
1242     ENDCODE IMMEDIATE
1243
1244
1245 \Z CLOSE      --     
1246 \ close current handle
1247 \ ----------------------------------\
1248     CODE CLOSE                         \
1249 \ ----------------------------------\
1250     CALL    #CLOSE_HDL           \
1251     MOV @IP+,PC                     \
1252     ENDCODE
1253
1254 \    .ENDIF \ SD_CARD_READ_WRITE
1255
1256
1257
1258
1259 \        .IFDEF BOOTLOADER
1260 \ https://forth-standard.org/standard/core/Equal
1261 \ =      x1 x2 -- flag         test x1=x2
1262     [UNDEFINED] = 
1263     [IF]
1264     CODE =
1265     SUB @PSP+,TOS   \ 2
1266     0<> IF          \ 2
1267         AND #0,TOS  \ 1
1268         MOV @IP+,PC \ 4
1269     THEN
1270     XOR #-1,TOS     \ 1 flag Z = 1
1271     MOV @IP+,PC     \ 4
1272     ENDCODE
1273     [THEN]
1274
1275 \ https://forth-standard.org/standard/core/DUP
1276 \ DUP      x -- x x      duplicate top of stack
1277     [UNDEFINED] DUP
1278     [IF]
1279     CODE DUP
1280     BW1 SUB #2,PSP      \ 2  push old TOS..
1281         MOV TOS,0(PSP)  \ 3  ..onto stack
1282         MOV @IP+,PC     \ 4
1283     ENDCODE
1284
1285 \ https://forth-standard.org/standard/core/qDUP
1286 \ ?DUP     x -- 0 | x x    DUP if nonzero
1287     CODE ?DUP
1288     CMP #0,TOS      \ 2  test for TOS nonzero
1289     0<> ?GOTO BW1    \ 2
1290     MOV @IP+,PC     \ 4
1291     ENDCODE
1292 [THEN]
1293
1294 \ https://forth-standard.org/standard/core/EVALUATE
1295 \ EVALUATE          \ i*x c-addr u -- j*x  interpret string
1296     [UNDEFINED] EVALUATE 
1297     [IF]
1298     CODE 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
1304     LO2HI
1305     INTERPRET
1306     HI2LO
1307     MOV @RSP+,&TOIN         \ 4
1308     MOV @RSP+,&SOURCE_ORG   \ 4
1309     MOV @RSP+,&SOURCE_LEN   \ 4
1310     MOV @RSP+,IP 
1311     MOV @IP+,PC
1312     ENDCODE
1313     [THEN]
1314
1315 \ BOOT          RSTIV_MEM --        \ bootstrap on SD_CARD\BOOT.4th file
1316 \                                   \ called by WARM
1317 \  to enable bootstrap type: ' BOOT IS WARM
1318 \ to disable bootstrap type: ' BOOT [PFA] IS WARM
1319     CODE BOOT
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 ?
1323     U< IF                   \ yes
1324         MOV @RSP+,PC        \ if RSTIV_MEM U< 2, return to BODYWARM
1325     THEN
1326     BIT.B #CD_SD,&SD_CDIN   \ SD_memory in SD_Card socket ?
1327     0<> IF                  \
1328         MOV 2(X),PC         \ if no, goto previous INIT: INIT TERMINAL only then ret to PFAWARM
1329     THEN
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 \---------------------------------------------------------------------------------
1335 \ END OF RESET
1336 \---------------------------------------------------------------------------------
1337     MOV #PSTACK-2,PSP       \ to avoid error "Stack empty!"
1338     MOV #0,&STATE           \ )
1339     MOV #LSTACK,&LEAVEPTR   \ > same as QUIT
1340     MOV #RSTACK,RSP         \ )
1341     LO2HI                   \
1342     S_ LOAD" BOOT.4TH"        \ LOAD BOOT.4TH issues error 2 if no such file...
1343     EVALUATE                \ to interpret this string
1344     ;