OSDN Git Service

V4.0
[fast-forth/master.git] / forthMSP430FR_SD_LOAD.asm
1 ; -*- coding: utf-8 -*-
2 ; forthMSP430FR_SD_LOAD.asm
3
4 ; Tested with MSP-EXP430FR5994 launchpad
5 ; Copyright (C) <2019>  <J.M. THOORENS>
6 ;
7 ; This program is free software: you can redistribute it and/or modify
8 ; it under the terms of the GNU General Public License as published by
9 ; the Free Software Foundation, either version 3 of the License, or
10 ; (at your option) any later version.
11 ;
12 ; This program is distributed in the hope that it will be useful,
13 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ; GNU General Public License for more details.
16 ;
17 ; You should have received a copy of the GNU General Public License
18 ; along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 ; used variables : BufferPtr, BufferLen
21
22 ;-----------------------------------------------------------------------
23 ; SD card OPEN, LOAD subroutines
24 ;-----------------------------------------------------------------------
25
26 ; ==================================;
27 ReadFAT1SectorW                     ;SWX (< 65536)
28 ; ==================================;
29     ADD     &OrgFAT1,W              ;
30     MOV     #0,X                    ; FAT1_SectorHI = 0
31     JMP     ReadSectorWX            ;SWX read FAT1SectorW, W = 0
32 ; ----------------------------------;
33
34    .IFDEF SD_CARD_READ_WRITE
35
36 ; this subroutine is called by Write_File (bufferPtr=512) and CloseHandle (0 =< BufferPtr =< 512)
37 ; ==================================;
38 WriteSD_Buf                         ;SWX input: T = CurrentHDL
39 ; ==================================;
40     ADD &BufferPtr,HDLL_CurSize(T)  ; update handle CurrentSizeL
41     ADDC    #0,HDLH_CurSize(T)      ;
42 ; ==================================;
43 WriteSectorHL                       ;SWX
44 ; ==================================;
45     MOV     &SectorL,W              ; Low
46     MOV     &SectorH,X              ; High
47     JMP     WriteSectorWX           ; ...then RET
48 ; ----------------------------------;
49
50     .ENDIF
51
52 ; rules for registers use
53 ; S = error
54 ; T = CurrentHdl, pathname
55 ; W = SectorL, (RTC) TIME
56 ; X = SectorH, (RTC) DATE
57 ; Y = BufferPtr, (DIR) DIREntryOfst
58
59 ; ==================================;
60 HDLcurClus2FATsecWofstY             ;WXY Input: T=Handle, HDL_CurClustHL  Output: ClusterHL, FATsector, W = FATsector, Y = FAToffset
61 ; ==================================;
62     MOV HDLL_CurClust(T),&ClusterL  ;
63     MOV HDLH_CurClust(T),&ClusterH  ;
64 ; ==================================;
65 ClusterHLtoFAT1sectWofstY           ;WXY Input : ClusterHL   Output: ClusterHL, FATsector, W = FATsector, Y = FAToffset
66 ; ==================================;limited to $10000 sectors ==> $800000 clusters ==> 32GB for 4k clusters
67     MOV.B &ClusterL+1,W             ;3 W = ClusterLoHI
68     MOV.B &ClusterL,Y               ;3 Y = ClusterLOlo
69 ; input : Cluster n, max = 7FFFFF,  (SD_card up to 256 GB with 64k clusters)
70 ; ClusterLoLo*4 = displacement in 512 bytes sector   ==> FAToffset
71 ; ClusterHiLo&ClusterLoHi +C  << 1 = relative FATsector + orgFAT1       ==> FATsector
72 ; ----------------------------------;
73     MOV.B &ClusterH,X               ;3 X = 0:ClusterHIlo
74     SWPB X                          ;1 X = ClusterHIlo:0
75     BIS X,W                         ;1 W = ClusterHIlo:ClusterLOhi
76 ; ----------------------------------;
77     SWPB Y                          ;1 Y = ClusterLOlo:0
78     ADD Y,Y                         ;1 Y = ClusterLOlo:0 << 1  (carry report for FATsector)
79     ADDC W,W                        ;1 FATsector = W = ClusterHIlo:ClusterLOhi<<1 + Carry
80     SWPB Y                          ;1 Y = 0:ClusterLOlo
81     ADD Y,Y                         ;1 FAToffset = Y = 0:ClusterLOlo<<2 for FAT32
82     MOV @RSP+,PC                    ;4
83 ; ----------------------------------;
84
85 ; use no registers
86 ; ==================================;
87 ClusterHLtoFrstSectorHL             ; Input : ClusterHL, output: first SectorHL of ClusterHL
88 ; ==================================;
89     .IFDEF MPY                      ; general case
90 ; ----------------------------------;
91     MOV     &ClusterL,&MPY32L       ;3
92     MOV     &ClusterH,&MPY32H       ;3
93     MOV     &SecPerClus,&OP2        ;5+3
94     MOV     &RES0,&SectorL          ;5
95     MOV     &RES1,&SectorH          ;5
96     ADD     &OrgClusters,&SectorL   ;5 OrgClusters = sector of virtual cluster 0, word size
97     ADDC    #0,&SectorH             ;3 32~
98 ; ----------------------------------;
99     .ELSEIF                         ; case of no hardware multiplier
100 ; ----------------------------------; Cluster24<<SecPerClus --> ClusFrstSect; SecPerClus = {1,2,4,8,16,32,64}
101     PUSHM  #3,W                     ;5 PUSHM W,X,Y
102     MOV.B &SecPerClus,W             ;3 SecPerClus(5-1) = multiplicator
103     MOV &ClusterL,X                 ;3 Cluster(16-1) --> MULTIPLICANDlo
104     MOV.B &ClusterH,Y               ;3 Cluster(24-17) -->  MULTIPLICANDhi
105     JMP CCFS_ENTRY                  ;
106 CCFS_LOOP                           ;
107     ADD X,X                         ;1 (RLA) shift one left MULTIPLICANDlo16
108     ADDC Y,Y                        ;1 (RLC) shift one left MULTIPLICANDhi8
109 CCFS_ENTRY
110     RRA W                           ;1 shift one right multiplicator
111     JNC CCFS_LOOP                   ;2 C = 0 loop back
112 CCFS_NEXT                           ;  C = 1, it's done
113     ADD &OrgClusters,X              ;3 OrgClusters = sector of virtual_cluster_0, word size
114     ADDC #0,Y                       ;1
115     MOV X,&SectorL                  ;3 low result
116     MOV Y,&SectorH                  ;3 high result
117     POPM  #3,W                      ;5 POPM Y,X,W
118 ; ----------------------------------;32~ + 5~ by 2* shift
119     .ENDIF ; MPY
120 ; ----------------------------------;
121 CCFS_RET                            ;
122     MOV @RSP+,PC                    ;
123 ; ----------------------------------;
124
125 ; ==================================;
126 HDLCurClusPlsOfst2sectorHL          ;SWX input: HDL (CurClust, ClustOfst) output: SectorHL
127 ; ==================================;
128     MOV HDLL_CurClust(T),&ClusterL  ;
129     MOV HDLH_CurClust(T),&ClusterH  ;
130 ; ==================================;
131 ClusterHL2sectorHL                  ;W input: ClusterHL, ClustOfst output: SectorHL
132 ; ==================================;
133     CALL #ClusterHLtoFrstSectorHL   ;
134     MOV.B HDLB_ClustOfst(T),W       ; byte to word conversion
135     ADD W,&SectorL                  ;
136     ADDC #0,&SectorH                ;
137     MOV @RSP+,PC                    ;
138 ; ----------------------------------;
139
140 ; if first open_load token, save DefaultInputStream
141 ; if other open_load token, decrement token, save previous context
142
143 ; OPEN subroutine
144 ; Input : DIREntryOfst, Cluster = DIREntryOfst(HDLL_FirstClus())
145 ; init handle(HDLL_DIRsect,HDLW_DIRofst,HDLL_FirstClus,HDLL_CurClust,HDLL_CurSize)
146 ; Output: Cluster = first Cluster of file, X = CurrentHdl
147 ; ==================================; input : Cluster, DIREntryOfst
148 GetFreeHandle                       ;STWXY init handle(HDLL_DIRsect,HDLW_DIRofst,HDLL_FirstClus = HDLL_CurClust,HDLL_CurSize)
149 ; ==================================; output : T = new CurrentHdl
150     MOV #4,S                        ; prepare file already open error
151     MOV #FirstHandle,T              ;
152     MOV #0,X                        ; X = init previous handle as 0
153 ; ----------------------------------;
154 SearchHandleLoop                    ;
155 ; ----------------------------------;
156     CMP.B   #0,HDLB_Token(T)        ; free handle ?
157     JZ      FreeHandleFound         ; yes
158 AlreadyOpenTest                     ; no
159     CMP     &ClusterH,HDLH_FirstClus(T);
160     JNE     SearchNextHandle        ;
161     CMP     &ClusterL,HDLL_FirstClus(T);
162     JZ      OPEN_Error              ; error 4: Already Open abort ===>
163 SearchNextHandle                    ;
164     MOV     T,X                     ; handle is occupied, keep it in X as previous handle
165     ADD     #HandleLenght,T         ;
166     CMP     #HandleEnd,T            ;
167     JNZ     SearchHandleLoop        ;
168     MOV     #8,S                    ;
169     JMP     OPEN_Error              ; error 8 = no more handle error, abort ===>
170 ; ----------------------------------;
171 FreeHandleFound                     ; T = new handle, X = previous handle
172 ; ----------------------------------;
173     MOV     #0,S                    ; prepare Happy End (no error)
174     MOV     T,&CurrentHdl           ;
175     MOV     X,HDLW_PrevHDL(T)       ; link to previous handle
176 ; ----------------------------------;
177 CheckCaseOfPreviousToken            ;
178 ; ----------------------------------;
179     CMP     #0,X                    ; existing previous handle?
180     JZ      InitHandle              ; no
181     ADD     &TOIN,HDLW_BUFofst(X)   ; in previous handle, add interpret offset to Buffer offset
182 ; ----------------------------------;
183 CheckCaseOfLoadFileToken            ;
184 ; ----------------------------------;
185     CMP.B   #0,W                    ; open_type is LOAD (-1) ?
186     JGE     InitHandle              ; W>=0, no
187     CMP.B   #0,HDLB_Token(X)        ; previous token is negative? (open_load type)
188     JGE     InitHandle              ; no
189     ADD.B   HDLB_Token(X),W         ; LOAD token = previous LOAD token -1
190 ; ----------------------------------;
191 InitHandle                          ;
192 ; ----------------------------------;
193     MOV.B   W,HDLB_Token(T)         ; marks handle as open type: <0=LOAD, 1=READ, 2=DEL, 4=WRITE, 8=APPEND
194     MOV.B   #0,HDLB_ClustOfst(T)    ; clear ClustOfst
195     MOV     &SectorL,HDLL_DIRsect(T); init handle DIRsectorL
196     MOV     &SectorH,HDLH_DIRsect(T);
197     MOV     &DIREntryOfst,Y         ;
198     MOV     Y,HDLW_DIRofst(T)       ; init handle SD_BUF offset of DIR entry
199     MOV SD_BUF+26(Y),HDLL_FirstClus(T); init handle firstcluster of file (to identify file)
200     MOV SD_BUF+20(Y),HDLH_FirstClus(T); = 0 if new DIRentry (create write file)
201     MOV SD_BUF+26(Y),HDLL_CurClust(T); init handle CurrentCluster
202     MOV SD_BUF+20(Y),HDLH_CurClust(T); = 0 if new DIRentry (create write file)
203     MOV SD_BUF+28(Y),HDLL_CurSize(T); init handle LOW currentSizeL
204     MOV SD_BUF+30(Y),HDLH_CurSize(T); = 0 if new DIRentry (create write file)
205     MOV     #0,&BufferPtr           ; reset BufferPtr all type of files
206     CMP.B   #2,W                    ; del file request (2) ?
207     JZ      InitHandleRET           ;
208     JGE HDLCurClusPlsOfst2sectorHL  ; set ClusterHL and SectorHL for all WRITE requests
209 ; ----------------------------------;
210     MOV     #0,HDLW_BUFofst(T)      ; < 2, is a READ or a LOAD request
211     CMP.B   #-1,W                   ;
212     JZ      ReplaceInputBuffer      ; case of first loaded file
213     JL      SaveBufferContext       ; case of other loaded file
214     JMP     SetBufLenLoadCurSector  ; case of READ file
215 ; ----------------------------------;
216 ReplaceInputBuffer                  ;
217 ; ----------------------------------;
218     MOV #SDIB_ORG,&CIB_ORG          ; set SD Input Buffer as Current Input Buffer before return to QUIT
219     MOV #SD_ACCEPT,&PFAACCEPT       ; redirect ACCEPT to SD_ACCEPT before return to QUIT
220 ; ----------------------------------;
221 SaveBufferContext                   ; (see CloseHandle)
222 ; ----------------------------------;
223     MOV &SOURCE_LEN,HDLW_PrevLEN(T) ; = CPL
224     SUB &TOIN,HDLW_PrevLEN(T)       ; PREVLEN = CPL - >IN
225     MOV &SOURCE_ORG,HDLW_PrevORG(T) ; = CIB
226     ADD &TOIN,HDLW_PrevORG(T)       ; PrevORG = CIB + >IN
227     JMP SetBufLenLoadCurSector      ; then RET
228 ; ----------------------------------;
229 InitHandleRET                       ;
230 ; ----------------------------------;
231     MOV @RSP+,PC                    ;
232 ; ----------------------------------;
233
234
235 ; sequentially load in SD_BUF bytsPerSec bytes of a file opened as read or as load
236 ; if new bufferLen have a size <= BufferPtr, closes the file then RET.
237 ; if previous bufferLen had a size < bytsPerSec, closes the file and reloads previous LOADed file if exist.
238 ; HDLL_CurSize leaves the not yet read size
239 ; All used registers must be initialized.
240 ; ==================================;
241 Read_File                           ; <== SD_ACCEPT, READ
242 ; ==================================;
243     MOV     &CurrentHdl,T           ;
244     MOV     #0,&BufferPtr           ; reset BufferPtr (the buffer is already read)
245 ; ----------------------------------;
246     CMP     #bytsPerSec,&BufferLen  ;
247     JNZ     CloseHandle             ; because this last and incomplete sector is already read
248     SUB #bytsPerSec,HDLL_CurSize(T) ; HDLL_CurSize is decremented of one sector lenght
249     SUBC    #0,HDLH_CurSize(T)      ;
250     ADD.B   #1,HDLB_ClustOfst(T)    ; current cluster offset is incremented
251     CMP.B &SecPerClus,HDLB_ClustOfst(T) ; Cluster Bound reached ?
252     JNC     SetBufLenLoadCurSector  ; no
253 ; ----------------------------------;
254 ;SearchNextClusterInFAT1            ;
255 ; ----------------------------------;
256     MOV.B   #0,HDLB_ClustOfst(T)    ; reset Current_Cluster sectors offset
257     CALL    #HDLcurClus2FATsecWofstY;WXY  Output: FATsector W=FATsector, Y=FAToffset
258     CALL    #ReadFAT1SectorW        ;SWX (< 65536)
259     MOV     #0,HDLH_CurClust(T)     ; preset HDLH_CurClust(T)=0 for FAT16
260     MOV SD_BUF(Y),HDLL_CurClust(T)  ;
261     MOV SD_BUF+2(Y),HDLH_CurClust(T); set HDLH_CurClust(T)=0 for FAT32
262 ; ==================================;
263 SetBufLenLoadCurSector              ;WXY <== previous handle reLOAD with BufferPtr<>0
264 ; ==================================;
265     MOV     #bytsPerSec,&BufferLen  ; preset BufferLen
266     CMP     #0,HDLH_CurSize(T)      ; CurSize > 65535 ?
267     JNZ     LoadCurSectorHL         ; yes
268     CMP HDLL_CurSize(T),&BufferPtr  ; BufferPtr >= CurSize ? (BufferPtr = 0 or see RestorePreviousLoadedBuffer)
269     JC      CloseHandle             ; yes
270     CMP #bytsPerSec,HDLL_CurSize(T) ; CurSize >= 512 ?
271     JC      LoadCurSectorHL         ; yes
272     MOV HDLL_CurSize(T),&BufferLen  ; no: adjust BufferLen
273 ; ==================================;
274 LoadCurSectorHL                     ;
275 ; ==================================;
276     CALL #HDLCurClusPlsOfst2sectorHL;SWX
277 ; ==================================;
278 ReadSectorHL                        ;
279 ; ==================================;
280     MOV     &SectorL,W              ; Low
281     MOV     &SectorH,X              ; High
282     JMP     ReadSectorWX            ; SWX then RET with W = 0
283 ; ----------------------------------;
284
285
286 ; ==================================;
287 CloseHandle                         ; <== CLOSE, Read_File, TERM2SD", OPEN_DEL
288 ; ==================================;
289     MOV &CurrentHdl,T               ;
290     CMP #0,T                        ; no handle?
291     JZ CloseHandleRet               ; RET
292 ; ----------------------------------;
293     .IFDEF SD_CARD_READ_WRITE
294     CMP.B #4,HDLB_Token(T)          ; WRITE file ?
295     JL TestClosedToken              ; no, case of DEL READ LOAD file
296 ;; ----------------------------------; optionnal
297 ;    MOV &BufferPtr,W                ;
298 ;RemFillZero                         ;the remainder of sector
299 ;    CMP     #BytsPerSec,W           ;2 buffer full ?
300 ;    JZ      UpdateWriteSector       ;2 remainding of buffer is full filled with 0
301 ;    MOV.B   #0,SD_BUF(W)            ;3
302 ;    ADD     #1,W                    ;1
303 ;    JMP     RemFillZero             ;2
304 ;; ----------------------------------;
305 UpdateWriteSector
306     CALL #WriteSD_Buf               ;SWX
307 ; ----------------------------------;
308 ;Load Update Save DirEntry          ;SWXY
309 ; ----------------------------------;
310     MOV     HDLL_DIRsect(T),W       ;
311     MOV     HDLH_DIRsect(T),X       ;
312     CALL    #ReadSectorWX           ;SWX SD_buffer = DIRsector
313     MOV     HDLW_DIRofst(T),Y       ; Y = DirEntryOffset
314     CALL    #GetYMDHMSforDIR        ; X=DATE,  W=TIME
315     MOV     X,SD_BUF+18(Y)          ; access date
316     MOV     W,SD_BUF+22(Y)          ; modified time
317     MOV     X,SD_BUF+24(Y)          ; modified date
318     MOV HDLL_CurSize(T),SD_BUF+28(Y); save new filesize
319     MOV HDLH_CurSize(T),SD_BUF+30(Y);
320     MOV     HDLL_DIRsect(T),W       ;
321     MOV     HDLH_DIRsect(T),X       ;
322     CALL    #WriteSectorWX          ;SWX
323 ; ----------------------------------;
324     .ENDIF                          ;
325 ; ----------------------------------;
326 TestClosedToken                     ;
327 ; ----------------------------------;
328     CMP.B #0,HDLB_Token(T)          ;
329 ; ----------------------------------;
330 CaseOfAnyReadWriteDelFileIsClosed   ; token >= 0
331 ; ----------------------------------;
332     JGE CloseHandleT                ; then RET
333 ; ----------------------------------;
334 CaseOfAnyLoadedFileIsClosed         ; -- org' len'   R-- QUIT3 dst_ptr dst_len SD_ACCEPT
335 ; ----------------------------------;
336 RestoreSD_ACCEPTContext             ;
337 ; ----------------------------------;
338     MOV HDLW_PrevLEN(T),TOS         ;
339     MOV HDLW_PrevORG(T),0(PSP)      ; -- org len
340 ; ----------------------------------;
341 ReturnOfSD_ACCEPT                   ;
342 ; ----------------------------------;
343     ADD #6,RSP                      ; R-- QUIT3     empties return stack
344     MOV @RSP+,IP                    ;               skip return to SD_ACCEPT
345 ; ----------------------------------;
346     PUSH #CheckFirstLoadedFile      ; defines the RETurn of CloseHandleT
347 ; ----------------------------------;
348 CloseHandleT                        ;
349 ; ----------------------------------;
350     MOV.B #0,HDLB_Token(T)          ; release the handle
351     MOV @T,T                        ; T = previous handle
352     MOV T,&CurrentHdl               ; becomes current handle
353     CMP #0,T                        ;
354     JZ CloseHandleRet               ; if no more handle
355 ; ----------------------------------;
356 RestorePreviousLoadedBuffer         ;
357 ; ----------------------------------;
358     MOV HDLW_BUFofst(T),&BufferPtr  ; restore previous BufferPtr
359     CALL    #SetBufLenLoadCurSector ; then reload previous buffer
360     BIC #Z,SR                       ;
361 ; ----------------------------------;
362 CloseHandleRet                      ;
363     MOV @RSP+,PC                    ; Z = 1 if no more handle, then RET
364 ; ----------------------------------;
365 CheckFirstLoadedFile                ;
366 ; ----------------------------------;
367     JZ RestoreDefaultACCEPT         ;
368     MOV #NOECHO,PC                  ; -- org len    if return to SD_ACCEPT
369 ; ----------------------------------;
370 RestoreDefaultACCEPT                ;               if no more handle, first loaded file is closed...
371 ; ----------------------------------;
372     MOV #TIB_ORG,&CIB_ORG           ;               restore TIB as Current Input Buffer for next line (next QUIT)
373     MOV #BODYACCEPT,&PFAACCEPT      ;               restore default ACCEPT for next line (next QUIT)
374     MOV #ECHO,PC                    ; -- org len    if return to Terminal ACCEPT
375 ; ----------------------------------;
376
377    .IFDEF SD_CARD_READ_WRITE
378
379 ; ==================================;
380 HDLFrstClus2FATsecWofstY            ;WXY Input: T=Handle, HDL_CurClustHL  Output: ClusterHL, FATsector, W = FATsector, Y = FAToffset
381 ; ==================================;
382     MOV HDLL_FirstClus(T),&ClusterL ;
383     MOV HDLH_FirstClus(T),&ClusterH ;
384     JMP ClusterHLtoFAT1sectWofstY   ;
385 ; ----------------------------------;
386
387 ;-----------------------------------------------------------------------
388 ; SD_READ_WRITE FORTH words
389 ;-----------------------------------------------------------------------
390
391 ;Z READ"         --
392 ; parse string until " is encountered, convert counted string in String
393 ; then parse string until char '0'.
394 ; media identifiers "A:", "B:" ... are ignored (only one SD_Card),
395 ; char "\" as first one initializes rootDir as SearchDir.
396 ; if file found, if not already open and if free handle...
397 ; ...open the file as read and return the handle in CurrentHdl.
398 ; then load first sector in buffer, bufferLen and bufferPtr are ready for read
399 ; currentHdl keep handle that is flagged as "read".
400
401 ; to read sequentially next sectors use READ word. A flag is returned : true if file is closed.
402 ; the last sector so is in buffer.
403
404 ; if pathname is a directory, change current directory.
405 ; if an error is encountered, no handle is set, error message is displayed.
406
407 ; READ" acts also as CD dos command :
408 ;     - READ" a:\misc\" set a:\misc as current directory
409 ;     - READ" a:\" reset current directory to root
410 ;     - READ" ..\" change to parent directory
411
412 ; to close all files type : WARM (or COLD, RESET)
413
414 ; ==================================;
415     FORTHWORDIMM "READ\34"          ; immediate
416 ; ==================================;
417 READDQ
418     MOV.B   #1,W                    ; W = READ request
419     JMP     Open_File               ;
420 ; ----------------------------------;
421
422 ;Z DEL" pathame"   --       immediate
423 ; ==================================;
424     FORTHWORDIMM "DEL\34"           ; immediate
425 ; ==================================;
426 DELDQ
427     MOV.B   #2,W                    ; W = DEL request
428     JMP     Open_File               ;
429 ; ----------------------------------;
430
431 ;Z WRITE" pathame"   --       immediate
432 ; if file exist, free all clusters then switch handle to WRITE
433 ; if "no such file", open a write handle
434 ; ==================================;
435     FORTHWORDIMM "WRITE\34"         ; immediate
436 ; ==================================;
437 WRITEDQ
438     MOV.B   #4,W                    ; W = WRITE request
439     JMP     Open_File               ;
440 ; ----------------------------------;
441
442 ;Z APPEND" pathame"   --       immediate
443 ; open the file designed by pathname.
444 ; the last sector of the file is loaded in buffer, and bufferPtr leave the address of the first free byte.
445 ; ==================================;
446     FORTHWORDIMM "APPEND\34"        ; immediate
447 ; ==================================;
448 APPENDQ
449     MOV.B   #8,W                    ; W = APPEND request
450     JMP     Open_File               ;
451 ; ----------------------------------;
452
453 ;Z CLOSE      --
454 ; close current handle
455 ; ==================================;
456     FORTHWORD "CLOSE"               ;
457 ; ==================================;
458     CALL    #CloseHandle            ;
459     MOV @IP+,PC                     ;
460 ; ----------------------------------;
461
462     .ENDIF ; SD_CARD_READ_WRITE
463
464 ;-----------------------------------------------------------------------
465 ; SD_CARD_LOADER FORTH word
466 ;-----------------------------------------------------------------------
467
468 ;Z LOAD" pathame"   --       immediate
469 ; compile state : compile LOAD" pathname"
470 ; exec state : open a file from SD card via its pathname
471 ; see Open_File primitive for pathname conventions
472 ; the opened file becomes the new input stream for INTERPRET
473 ; this command is recursive, limited only by the count of free handles (up to 8)
474 ; LOAD" acts also as dos command "CD" :
475 ;     - LOAD" \misc\" set a:\misc as current directory
476 ;     - LOAD" \" reset current directory to root
477 ;     - LOAD" ..\" change to parent directory
478
479 ; ==================================;
480     FORTHWORDIMM "LOAD\34"          ; immediate
481 ; ==================================;
482     MOV.B   #-1,W                   ; W = LOAD request
483 ; ----------------------------------;
484
485 ; ======================================================================
486 ; OPEN FILE primitive
487 ; ======================================================================
488 ; Open_File               --
489 ; primitive for LOAD" READ" CREATE" WRITE" DEL"
490 ; store OpenType on TOS,
491 ; compile state : compile OpenType, compile SQUOTE and the string of provided pathname
492 ; exec state :  open a file from SD card via its pathname
493 ;               convert counted string found at HERE in a String then parse it
494 ;                   char "\" as first one initializes rootDir as SearchDir.
495 ;               if file found, if not already open and if free handle...
496 ;                   ...open the file as read and return the handle in CurrentHdl.
497 ;               if the pathname is a directory, change current directory, no handle is set.
498 ;               if an error is encountered, no handle is set, an error message is displayed.
499 ; ==================================;
500 Open_File                           ; --
501 ; ==================================;
502     SUB     #2,PSP                  ;
503     MOV     TOS,0(PSP)              ;
504     MOV     W,TOS                   ; -- Open_type (-1=LOAD", 1=READ", 2=DEL", 4=WRITE", 8=APPEND")
505     CMP     #0,&STATE               ;
506     JZ      OPEN_EXEC               ;
507 ; ----------------------------------;
508 OPEN_COMP                           ;
509     mDOCOL                          ; if compile state                              R-- LOAD"_return
510     .word   lit,lit,COMMA,COMMA     ; compile open_type as literal
511     .word   SQUOTE                  ; compile string_exec + string
512     .word   lit,ParenOpen,COMMA     ; compile (OPEN)
513     .word   EXIT                    ;
514 ; ----------------------------------;
515 OPEN_EXEC                           ;
516     mDOCOL                          ; if exec state
517     .word   lit,'"',WORDD,COUNT     ; -- open_type addr cnt
518     mNEXTADR                        ;
519     MOV     @RSP+,IP                ;
520 ; ----------------------------------;
521 ParenOpen                           ; -- open_type addr cnt
522 ; ----------------------------------;
523     MOV #0,S                        ;
524 Q_SD_present                        ;
525     BIT.B #CD_SD,&SD_CDIN           ;                               SD_memory in SD_Card module ?
526     JZ Q_SD_not_init                ;                               yes
527     BIC #BUS_SD,&SD_SEL             ;                               no, hide SIMO, SOMI & SCK pins (SD not initialized memory)
528 Q_SD_not_init                       ;          
529     BIT #BUS_SD,&SD_SEL             ;                               is SD init by SYS ? 
530     JNZ OPEN_LetUsGo                ;                               no --> with TOS = -1 does abort
531     MOV #NO_SD_CARD,PC              ;                               S = 0 --> error 0
532 ; ----------------------------------;
533 OPEN_LetUsGo                        ;
534 ; ----------------------------------;
535     MOV     #1,S                    ;                       error 1
536     CMP     #0,TOS                  ;                       cnt = 0 ?
537     JZ      OPEN_Error              ;                       yes: error 1 ===>
538     MOV     @PSP+,rDOCON            ; -- open_type cnt      rDOCON = addr = pathname PTR
539     ADD     rDOCON,TOS              ; -- open_type EOS      TOS = EOS (End Of String) = pathname end
540     .IFDEF SD_CARD_READ_WRITE       ;
541     MOV     TOS,&PathName_END       ; for WRITE CREATE part
542     .ENDIF
543     MOV     &DIRClusterL,&ClusterL  ; set DIR cluster
544     MOV     &DIRClusterH,&ClusterH  ;
545 ; ----------------------------------;
546 OPN_AntiSlashFirstTest              ;
547 ; ----------------------------------;
548     CMP.B   #'\\',0(rDOCON)         ; "\" as first char ?
549     JNZ     OPN_SearchInDIR         ; no
550     ADD     #1,rDOCON               ; yes : skip '\' char
551     MOV     #0,&ClusterH            ;
552     JMP     OPN_AntiSlashFirstNext  ;
553 ; ----------------------------------;
554 OPN_SearchInDIR                     ; <=== dir found in path
555 ; ----------------------------------;
556     MOV     rDOCON,&PathName_PTR    ; save Pathname ptr
557 ; ----------------------------------;
558 OPN_LoadDIRcluster                  ; <=== next DIR cluster loopback
559 ; ----------------------------------;
560     CALL    #ClusterHLtoFrstSectorHL; output: first Sector of this cluster
561     MOV     &SecPerClus,rDODOES     ; set sectors count down
562 ; ----------------------------------;
563 OPN_LoadDIRsector                   ; <=== next DIR Sector loopback
564 ; ----------------------------------;
565     CALL    #ReadSectorHL           ;SWX,
566     MOV     #2,S                    ; prepare error 2
567 ; ----------------------------------; W = 0 = DIREntryOfst
568 OPN_SearchDIRentry                  ; <=== next DIR_Entry loopback
569 ; ----------------------------------;
570     MOV     W,&DIREntryOfst         ; update DIREntryOfst
571     CMP.B   #0,SD_BUF(W)            ; free entry ?
572     JZ      OPN_NoSuchFile          ; NoSuchFile error = 2 ===>
573     MOV     W,Y                     ; 1         W = DIREntryOfst, Y = Entry_name pointer
574     MOV     #8,X                    ; count of chars in entry name
575 ; ----------------------------------;
576 OPN_CompareName                     ;
577 ; ----------------------------------;
578     MOV.B   @rDOCON+,T              ;
579     CMP.B   T,SD_BUF(Y)             ; compare Pathname with DirEntry1to8, char by char
580     JNZ     OPN_CompareNameNext     ;
581     ADD     #1,Y                    ;
582     SUB     #1,X                    ;
583     JNZ     OPN_CompareName         ;
584     MOV.B   @rDOCON+,T              ; 9th char of Pathname should be '.'
585     JZ      OPN_CompareNameDone     ; if X = 0
586 ; ----------------------------------;
587 OPN_CompareNameNext                 ; remainder of 8 chars of DIR_entry name must be spaces
588 ; ----------------------------------;
589     CMP.B   #32,SD_BUF(Y)           ; parse DIR entry up to 8th chars
590     JNZ     OPN_DIRentryMismatch    ; if a char of DIR entry name <> space
591     ADD     #1,Y                    ;
592     SUB     #1,X                    ;
593     JNZ     OPN_CompareNameNext     ;
594 ; ----------------------------------;
595 OPN_CompareNameDone                 ; T = "." or FirstNotEqualChar
596 ; ----------------------------------;
597     CMP.B   #'\\',T                 ; FirstNotEqualChar of Pathname = "\" ?
598     JZ      OPN_EntryFound          ;
599 ; ----------------------------------;
600     MOV     #3,X                    ; to compare 3 char extension
601     CMP.B   #'.',T                  ; FirstNotEqualChar of Pathname = dot ?
602     JNZ     OPN_CompExtensionNext   ; if not
603 ; ----------------------------------;
604 OPN_CompareExtension                ;
605 ; ----------------------------------;
606     CMP.B   @rDOCON+,SD_BUF(Y)      ; compare Pathname_ext(char) with DirEntry9to11(char)
607     JNZ     OPN_CompExtensionNext   ;
608     ADD     #1,Y                    ;
609     SUB     #1,X                    ;
610     JNZ     OPN_CompareExtension    ;
611     JZ      OPN_CompExtensionDone   ;
612 ; ----------------------------------;
613 OPN_CompExtensionNext               ; remainder of 8 chars of DIR_entry extension must be spaces
614 ; ----------------------------------;
615     CMP.B   #32,SD_BUF(Y)           ; parse DIR entry up to 11th chars
616     JNZ     OPN_DIRentryMismatch    ; if a char of DIR entry extension <> space
617     ADD     #1,Y                    ;
618     SUB     #1,X                    ;
619     JNZ     OPN_CompExtensionNext   ;
620 ; ----------------------------------;
621 OPN_CompExtensionDone               ;
622 ; ----------------------------------;
623     CMP.B   #'.',-2(rDOCON)         ; LastCharEqual = dot ? (case of Pathname = "..\" which matches with first DIR entry = ".")
624     JZ      OPN_DIRentryMismatch    ; to compare with 2th DIR entry, the good one.
625     CMP     TOS,rDOCON              ; EOS reached ?
626     JC      OPN_EntryFound          ; yes
627 ; ----------------------------------;
628 OPN_DIRentryMismatch                ;
629 ; ----------------------------------;
630     MOV     &PathName_PTR,rDOCON    ; reload PathName_PTR as it was at last OPN_SearchInDIR
631     ADD     #32,W                   ; W = DIREntryOfst + DIRentrySize
632     CMP     #512,W                  ; out of sector bound ?
633     JNZ     OPN_SearchDIRentry      ; no, loopback for search next DIR entry in same sector
634 ; ----------------------------------;
635     ADD     #1,&SectorL             ;
636     ADDC    #0,&SectorH             ;
637     SUB     #1,rDODOES              ; count of Dir sectors reached ?
638     JNZ     OPN_LoadDIRsector       ; no, loopback to load next DIR sector in same cluster
639 ; ----------------------------------;
640     CALL #ClusterHLtoFAT1sectWofstY ; load FATsector in SD_Buffer, set Y = FAToffset
641     CMP     #-1,0(Y)                ; last DIR cluster ?
642     JNZ     OPN_SetNextDIRcluster   ;
643     CMP     #0FFFh,2(Y)             ;
644     .IFNDEF SD_CARD_READ_WRITE      ;
645     JZ      OPN_NoSuchFile          ; yes, NoSuchFile error = 2 ===>
646     .ELSE                           ;
647     JNZ     OPN_SetNextDIRcluster   ; no
648 OPN_QcreateDIRentry                 ; -- open_type EOS
649     CMP     #4,0(PSP)               ;               open type = WRITE" or APPEND" ?
650     JNC     OPN_NoSuchFile          ; no: NoSuchFile error = 2 ===>
651 OPN_AddDIRcluster                   ; yes
652     PUSH    #OPN_LoadDIRcluster     ; as RETurn of GetNewCluster: ===> loopback to load this new DIR cluster
653 ; ==================================;
654 GetNewCluster                       ; called by Write_File
655 ; ==================================;
656     PUSH    Y                       ; push previous FAToffset
657     PUSH    W                       ; push previous FATsector
658     CALL    #SearchMarkNewClusterHL ;SWXY input: W = FATsector Y = FAToffset, output: ClusterHL, W = FATsector of New cluster
659     CMP     @RSP,W                  ; previous and new clusters are in same FATsector?
660     JZ      LinkClusters            ;     yes
661 ; ----------------------------------;
662 UpdateNewClusterFATs                ;
663 ; ----------------------------------;
664     MOV     @RSP,W                  ; W = previous FATsector
665     CALL    #ReadFAT1SectorW        ;SWX  reload previous FATsector in buffer to link clusters
666 ; ----------------------------------;
667 LinkClusters                        ;
668 ; ----------------------------------;
669     MOV     @RSP+,W                 ; W = previous FATsector
670     MOV     @RSP+,Y                 ; Y = previous FAToffset
671     MOV     &ClusterL,SD_BUF(Y)     ; store new cluster to current cluster address in previous FATsector buffer
672     MOV     &ClusterH,SD_BUF+2(Y)   ;
673     JMP     SaveSectorWtoFATs       ;SWXY update FATs from SD_BUF to W = previous FATsector, then RET
674 ; ==================================;
675     .ENDIF ; SD_CARD_READ_WRITE     ;
676 ; ----------------------------------;
677 OPN_SetNextDIRcluster               ;
678 ; ----------------------------------;
679     MOV     @Y+,&ClusterL           ;
680     MOV     @Y,&ClusterH            ;
681     JMP     OPN_LoadDIRcluster      ; ===> loop back to load this new DIR cluster
682 ; ----------------------------------;
683
684 ; ----------------------------------;
685 OPN_EntryFound                      ; Y points on the file attribute (11th byte of entry)
686 ; ----------------------------------;
687 ;    MOV     W,&DIREntryOfst         ;
688     MOV     SD_BUF+14H(W),&ClusterH ; first clusterH of file
689     MOV     SD_BUF+1Ah(W),&ClusterL ; first clusterL of file
690 OPN_EntryFoundNext
691     BIT.B   #10h,SD_BUF+0Bh(W)      ; test if Directory or File
692     JZ      OPN_FileFound           ; is a file
693 ; ----------------------------------;
694 OPN_DIRfound                        ; entry is a DIRECTORY
695 ; ----------------------------------;
696     CMP     #0,&ClusterH            ; case of ".." entry, when parent directory is root
697     JNZ     OPN_DIRfoundNext        ;
698     CMP     #0,&ClusterL            ; case of ".." entry, when parent directory is root
699     JNZ     OPN_DIRfoundNext        ;
700 OPN_AntiSlashFirstNext
701     MOV     #2,&ClusterL            ; set clusterL as RootDIR cluster
702 OPN_DIRfoundNext                    ;
703     CMP     TOS,rDOCON              ; EOS reached ?
704     JNC     OPN_SearchInDIR         ; no: (rDOCON points after "\") ==> loop back
705 ; ----------------------------------;
706 OPN_SetCurrentDIR                   ; -- open_type ptr  PathName_PTR is set on name of this DIR
707 ; ----------------------------------;
708     MOV     &ClusterL,&DIRClusterL  ;
709     MOV     &ClusterH,&DIRclusterH  ;
710     MOV     #0,0(PSP)               ; -- open_type ptr      set open_type = 0 = DIR
711     JMP     OPN_Dir
712 ; ----------------------------------;
713 OPN_FileFound                       ; -- open_type ptr  PathName_PTR is set on name of file
714 ; ----------------------------------;
715     MOV     @PSP,W                  ;
716     CALL    #GetFreeHandle          ;STWXY init handle(HDLL_DIRsect,HDLW_DIRofst,HDLL_FirstClus = HDLL_CurClust,HDLL_CurSize)
717 ; ----------------------------------; output : T = CurrentHdl*, S = ReturnError, Y = DIRentry offset
718 OPN_NoSuchFile                      ; S = error 2
719 OPN_Dir                             ;
720     MOV     #xdodoes,rDODOES        ;                   restore rDODOES
721     MOV     #xdocon,rDOCON          ;                   restore rDODOES
722     MOV     @PSP+,W                 ; -- ptr            W = open_type
723     MOV     @PSP+,TOS               ; --
724 ; ----------------------------------; then go to selected OpenType subroutine (OpenType = W register)
725
726
727 ; ======================================================================
728 ; LOAD" primitive as part of Open_File
729 ; input from open:  S = OpenError, W = open_type, SectorHL = DIRsectorHL,
730 ;                   Buffer = [DIRsector], ClusterHL = FirstClusterHL
731 ;       from open(GetFreeHandle): Y = DIRentry, T = CurrentHdl
732 ; output: nothing else abort on error
733 ; ======================================================================
734
735 ; ----------------------------------;
736 OPEN_QDIR                           ;
737 ; ----------------------------------;
738     CMP     #0,W                    ;
739     JNZ     OPEN_QLOAD              ; nothing else to do
740     MOV @IP+,PC                     ;
741 ; ----------------------------------;
742 OPEN_QLOAD                          ;
743 ; ----------------------------------;
744     .IFDEF SD_CARD_READ_WRITE       ;
745     CMP.B   #-1,W                   ; open_type = LOAD"
746     JNZ     OPEN_1W                 ; next step
747     .ENDIF                          ;
748 ; ----------------------------------; here W is free
749 OPEN_LOAD                           ;
750 ; ----------------------------------;
751     CMP     #0,S                    ; open file happy end ?
752     JNZ     OPEN_Error              ; no
753 OPEN_LOAD_END                       ;
754     MOV #NOECHO,PC                  ;
755 ;    MOV @IP+,PC                     ;
756 ; ----------------------------------;
757
758 ; ----------------------------------;
759 OPEN_Error                          ; S= error
760 ; ----------------------------------;
761 ; Error 1  : PathNameNotFound       ; S = error 1
762 ; Error 2  : NoSuchFile             ; S = error 2
763 ; Error 4  : alreadyOpen            ; S = error 4
764 ; Error 8  : NomoreHandle           ; S = error 8
765 ; ----------------------------------;
766     MOV #SD_CARD_FILE_ERROR,PC      ;
767 ; ----------------------------------;
768
769     .IFDEF BOOTLOADER
770 ; to enable bootstrap: BOOT
771 ; to disable bootstrap: NOBOOT
772
773 ; XBOOT          [SYSRSTIV|USERSTIV] --
774 ; here we are after INIT_FORTH
775 ; performs bootstrap from SD_CARD\BOOT.4th file, ready to test SYSRSTIV|USERSYS value
776 XBOOT       CALL &HARD_APP          ; WARM first calls HARD_APP (which includes INIT_HARD_SD)
777             MOV #PSTACK-2,PSP       ; preserve SYSRSTIV|USERSYS in TOS for BOOT.4TH tests
778             MOV #0,0(PSP)           ; set TOS = 0 for the next of XBOOT
779             mASM2FORTH              ;
780             .word XSQUOTE           ; -- SYSRSTIV|USERSYS addr u
781             .byte 15,"LOAD\34 BOOT.4TH\34"  ; LOAD" BOOT.4TH" issues error 2 if no such file...
782             .word BRAN,QUIT4        ; to interpret this string, then loop back to QUIT1/QUIT2
783 ; ----------------------------------;
784
785 ; ==================================;
786             FORTHWORD "BOOT"        ; to enable BOOT
787 ; ==================================;
788             MOV #XBOOT,&PUCNEXT     ; inserts XBOOT in PUC chain.
789             MOV @IP+,PC
790
791 ; ==================================;
792             FORTHWORD "NOBOOT"      ; to disable BOOT
793 ; ==================================;
794 NOBOOT      MOV #WARM,&PUCNEXT      ; removes XBOOT from PUC chain.
795             MOV @IP+,PC             ;
796     .ENDIF