OSDN Git Service

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