OSDN Git Service

V300 beautified
[fast-forth/master.git] / forthMSP430FR_SD_RW.asm
1  ; -*- coding: utf-8 -*-
2 ; DTCforthMSP430FR5xxxSD_RW.asm
3
4 ; and only for FR5xxx and FR6xxx with RTC_B or RTC_C hardware if you want write file with date and time.
5
6 ; Tested with MSP-EXP430FR5969 launchpad
7 ; Copyright (C) <2015>  <J.M. THOORENS>
8 ;
9 ; This program is free software: you can redistribute it and/or modify
10 ; it under the terms of the GNU General Public License as published by
11 ; the Free Software Foundation, either version 3 of the License, or
12 ; (at your option) any later version.
13 ;
14 ; This program is distributed in the hope that it will be useful,
15 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ; GNU General Public License for more details.
18 ;
19 ; You should have received a copy of the GNU General Public License
20 ; along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22
23
24 ; ======================================================================
25 ; READ" primitive as part of OpenPathName
26 ; input from open:  S = OpenError, W = open_type, SectorHL = DIRsectorHL,
27 ;                   Buffer = [DIRsector], ClusterHL = FirstClusterHL
28 ;       from open(GetFreeHandle): Y = DIRentry, T = CurrentHdl
29 ; output: nothing else abort on error
30 ; ======================================================================
31
32 ; ----------------------------------;
33 OPEN_QREAD                          ;
34     CMP     #1,W                    ; open_type = READ" ?
35     JNZ     OPEN_QWRITE             ; no : goto next step
36 ; ----------------------------------;
37 OPEN_READ                           ;
38 ; ----------------------------------;
39     CMP     #0,S                    ; open file happy end ?
40     JNZ     OPEN_Error              ; no
41     mNEXT                           ;
42 ; ----------------------------------;
43
44 ;Z READ            -- f
45 ; sequentially read a file opened by READ".
46 ; sectors are loaded in SD_BUF and BufferLen leave the count of loaded bytes.
47 ; when the last sector of file is loaded in buffer, the handle is automatically closed and flag is true (<>0).
48
49 ; ----------------------------------;
50     FORTHWORD "READ"                ; -- fl     closed flag
51 ; ----------------------------------;
52 READ
53     SUB     #2,PSP                  ;
54     MOV     TOS,0(PSP)              ;
55     MOV     &CurrentHdl,TOS         ;
56     CALL    #Read_File              ;SWX
57 READ_END
58     SUB     &CurrentHdl,TOS         ; -- fl     if fl <>0 (if Z=0) handle is closed
59     mNEXT                           ;
60 ; ----------------------------------;
61
62
63 ;-----------------------------------------------------------------------
64 ; WRITE" (CREATE part) subroutines
65 ;-----------------------------------------------------------------------
66
67 ; parse all FAT sectors until free cluster is found 
68 ; this NewCluster is marked as the end's one (-1)
69
70
71 ; input : CurFATsector
72 ; use SWX registers
73 ; output: W = new FATsector, SD_BUF = [new FATsector], NewCluster
74 ;         SectorL is unchanged, FATS are not updated.
75 ;         S = 2 --> Disk FULL error
76 ; ----------------------------------;
77 SearchNewCluster                    ; <== CREATE file, WRITE_File
78 ; ----------------------------------;
79     MOV     #2,S                    ; preset disk full return error
80     PUSH    &CurFATsector           ; last known free cluster sector
81     MOV     &FATtype,Y              ;
82     ADD     Y,Y                     ;  Y = bytes size of Cluster number (2 or 4)
83 ; ----------------------------------;
84 LoadFATsectorInBUF                  ; <== IncrementFATsector
85 ; ----------------------------------;
86     MOV     @RSP,W                  ; W = FATsector
87     CMP     W,&FATSize              ;
88     JZ      OPW_Error               ; FATsector = FATSize ===> abort disk full
89     CALL    #ReadFAT1SectorW        ;SWX
90     MOV     #0,X                    ; init FAToffset
91 ; ----------------------------------;
92 SearchFreeClustInBUF                ; <== SearchNextCluster
93 ; ----------------------------------;
94     CMP     #2,Y                    ; FAT16 Cluster size ?
95     JZ      ClusterLowWordTest      ; yes
96 ClusterHighWordTest                 ;
97     CMP     #0,SD_BUF+2(X)          ; cluster address hi word = 0 ?
98     JNZ     SearchNextNewCluster    ;
99 ClusterLowWordTest                  ;
100     CMP     #0,SD_BUF(X)            ; Cluster address lo word = 0 ?
101     JZ      GNC_FreeClusterFound    ; 
102 SearchNextNewCluster                ;
103     ADD     Y,X                     ; increment SD_BUF offset by size of Cluster address
104     CMP     #BytsPerSec,X           ;
105     JNE     SearchFreeClustInBUF    ; loopback while X < BytsPerSec
106 IncrementFATsector                  ;
107     ADD     #1,0(RSP)               ; increment FATsector
108     JMP     LoadFATsectorInBUF      ; loopback
109 ; ----------------------------------;
110 GNC_FreeClusterFound                ; Y =  cluster number low word in SD_BUF = FATsector
111 ; ----------------------------------;
112     MOV     #0,S                    ; clear error
113     MOV.B   @RSP,W                  ; W = 0:FATsectorLo
114     MOV     #-1,SD_BUF(X)           ; mark NewCluster low word as end cluster (0xFFFF) in SD_BUF
115     CMP     #2,Y                    ; Y = FAT16 size of Cluster number ?
116     JZ      FAT16EntryToClusterNum  ; yes
117     MOV     #0FFFh,SD_BUF+2(X)      ; no: mark NewCluster high word as end cluster (0x0FFF) in SD_BUF
118 ; ----------------------------------;
119 FAT32EntryToClusterNum              ; convert FAT32 cluster address to cluster number
120 ; ----------------------------------;
121     RRA     X                       ; X = FATOffset>>1, FAToffset is byte size
122     SWPB    W                       ; W = FATsectorLo:0
123     ADD     W,X                     ; X = FATsectorLo:FATOffset>>1
124     MOV.B   1(RSP),W                ; W = FATsectorHi
125     RRA     W                       ; W = FATsectorHi>>1
126     RRC     X                       ; X = (FATsectorLo:FAToffset>>1)>>1 = FATsectorLo>>1:FAToffset>>2
127     MOV     W,&NewClusterH          ; NewClusterH =  FATsectorHi>>1
128     MOV     X,&NewClusterL          ; NewClusterL = FATsectorLo>>1:FAToffset>>2
129     JMP     SearchNewClusterEnd     ; max cluster = 7FFFFF ==> 1FFFFFFF sectors ==> 256 GB
130 ; ----------------------------------;
131 FAT16EntryToClusterNum              ; convert FAT16 address of Cluster in cluster number
132 ; ----------------------------------;
133     RRA     X                       ; X = Offset>>1, offset is word < 256
134     MOV.B   X,&NewClusterL          ; X = NewCluster numberLO (byte)
135     MOV.B   W,&NewClusterL+1        ; W = NewCluster numberHI (byte)
136     MOV     #0,&NewClusterH         ;
137 ; ----------------------------------;
138 SearchNewClusterEnd                 ;
139 ; ----------------------------------;
140     MOV     @RSP+,W                 ; W = FATsector
141     MOV     W,&CurFATsector         ; refresh CurrentFATsector
142     RET                             ;
143 ; ----------------------------------;
144
145
146 ; update FATs with SD_BUF content.
147 ; input : FATsector, FAToffset, SD_BUF = [FATsector]
148 ; use : SWX registers
149 ; ----------------------------------; else update FATsector of the old cluster
150 UpdateFATsSectorW                   ;
151 ; ----------------------------------;
152     PUSH    W                       ;
153     ADD     &OrgFAT1,W              ; update FAT#1
154     CALL    #WriteSectorW           ; SWX
155     MOV     @RSP+,W                 ;
156     ADD     &OrgFAT2,W              ; update FAT#2
157     MOV     #WriteSectorW,PC        ; then ret
158 ; ----------------------------------;
159
160
161
162 ; FAT16/32 format for date and time in a DIR entry
163 ; create time :     offset 0Dh = 0 to 200 centiseconds, not used.
164 ;                   offset 0Eh = 0bhhhhhmmmmmmsssss, with : s=seconds*2, m=minutes, h=hours
165 ; access time :     offset 14h = always 0, not used as date
166 ; modified time :   ofsset 16h = 0bhhhhhmmmmmmsssss, with : s=seconds*2, m=minutes, h=hours
167 ; dates :    offset 10, 12, 18 = 0byyyyyyymmmmddddd, with : y=year-1980, m=month, d=day
168
169 ; ----------------------------------; input:
170 GetYMDHMSforDIR                     ;X=date, W=TIME
171 ; ----------------------------------;
172     .IFDEF    LF_XTAL               ;
173     .IFNDEF   RTC                   ; RTC_B or RTC_C select
174 ; ----------------------------------;
175     BIT.B   #RTCHOLD,&RTCCTL1       ; rtc is running ?
176     JNZ     SD_RW_RET               ; no
177 WaitRTC                             ; yes
178     BIT.B   #RTCRDY,&RTCCTL1        ; rtc values are valid ?
179     JZ      WaitRTC                 ; no
180     MOV.B   &RTCSEC,W               ; yes
181     RRA.B   W                       ; 2 seconds accuracy time
182     MOV.B   &RTCDAY,X               ;
183     MOV.B   #32,&MPY                ; common MPY for minutes and months
184     MOV.B   &RTCMIN,&OP2            ;
185     ADD     &RES0,W                 ;
186     MOV.B   &RTCMON,&OP2            ;
187     ADD     &RES0,X                 ;
188     MOV.B   &RTCHOUR,&MPY           ;
189     MOV     #2048,&OP2              ;
190     ADD     &RES0,W                 ;
191     MOV     &RTCYEAR,&MPY           ;
192     SUB     #1980,&MPY              ;
193     MOV     #512,&OP2               ;
194     ADD     &RES0,X                 ;
195     .ELSEIF
196     MOV     #0,X                    ; X=DATE 
197     MOV     #0,W                    ; W=TIME
198     .ENDIF
199     .ENDIF
200 SD_RW_RET                           ;
201     RET                             ;
202 ; ----------------------------------;
203
204
205 ; when create filename, forbidden chars are skipped
206 ForbiddenChars ; 15 forbidden chars table + dot char
207     .byte '"','*','+',',','/',':',';','<','=','>','?','[','\\',']','|','.'
208
209 ; ----------------------------------;
210 OPWC_SkipDot                        ;
211 ; ----------------------------------;
212     CMP     #4,X                    ;
213     JL      FillDIRentryName        ; X < 4 : no need spaces to complete name entry
214     SUB     #3,X                    ;
215     CALL    #OPWC_CompleteWithSpaces; complete name entry 
216     MOV     #3,X                    ; 
217 ; ----------------------------------;
218
219 ; ----------------------------------;
220 FillDIRentryName                    ;SWXY use
221 ; ----------------------------------;
222     MOV.B   @T+,W                   ; W = char of pathname
223     MOV.B   W,SD_BUF(Y)             ;     to DIRentry
224 ;    CMP     #0,W                    ; end of stringZ ?
225 ;    JZ      OPWC_CompleteWithSpaces ;
226     CMP     T,&EndOfPath            ; EOS < PTR ?
227     JLO     OPWC_CompleteWithSpaces ; yes
228 ; ----------------------------------;
229 SkipForbiddenChars                  ;
230 ; ----------------------------------;
231     PUSH    IP                      ;3
232     MOV     #15,IP                  ;2 forbidden chars count
233     MOV     #ForbiddenChars,S       ;2 here, S is free
234 ForbiddenCharLoop                   ;
235     CMP.B   @S+,W                   ;2
236     JZ      FillDIRentryName        ;2 skip forbidden char
237     SUB     #1,IP                   ;1
238     JNZ     ForbiddenCharLoop       ;2
239     MOV     @RSP+,IP                ;2
240 ; ----------------------------------;
241     CMP.B   @S,W                    ;1 46 (0x2E)
242     JZ      OPWC_SkipDot            ;2 skip '.'
243 ; ----------------------------------;
244     SUB     #33,W                   ;
245     JL      FillDIRentryName        ; skip char =< SPACE char
246     ADD     #1,Y                    ; increment DIRentry ptr
247     SUB     #1,X                    ; decrement count of chars entry
248     JNZ     FillDIRentryName        ;
249 ; ----------------------------------;
250 OPWC_CompleteWithSpaces             ; 0 to n spaces !
251 ; ----------------------------------;
252     CMP     #0,X                    ; 
253     JZ      OPWC_CWS_End            ;
254 ; ----------------------------------;
255 OPWC_CompleteWithSpaceloop          ;
256 ; ----------------------------------;
257     MOV.B   #' ',SD_BUF(Y)          ; remplace dot by char space
258     ADD     #1,Y                    ; increment DIRentry ptr in buffer 
259     SUB     #1,X                    ; dec countdown of chars space
260     JNZ OPWC_CompleteWithSpaceloop  ;
261 OPWC_CWS_End                        ;
262     RET                             ;
263 ; ----------------------------------;
264
265
266
267
268 ; ======================================================================
269 ; WRITE" primitive as part of OpenPathName
270 ; input from open:  S = OpenError, W = open_type, SectorHL = DIRsectorHL,
271 ;                   Buffer = [DIRsector], ClusterHL = FirstClusterHL
272 ;       from open(GetFreeHandle): Y = DIRentry, T = CurrentHdl
273 ; output: nothing else abort on error
274 ; error 1  : PathNameNotFound
275 ; error 2  : NoSuchFile       
276 ; error 4  : DirectoryFull  
277 ; error 8  : AlreadyOpen    
278 ; error 16 : NomoreHandle   
279 ; ======================================================================
280
281 ; ----------------------------------;
282 OPEN_QWRITE                         ;
283     CMP     #2,W                    ; open_type = WRITE" ?
284     JNZ     OPEN_QDEL               ; no : goto next step
285 ; ----------------------------------;
286 ; 1 try to open                     ; done
287 ; ----------------------------------;
288 ; 2 select error "no such file"     ;
289 ; ----------------------------------;
290     CMP     #2,S                    ; "no such file" error ?
291     JZ      OPEN_WRITE_CREATE       ; yes
292     CMP     #0,S                    ; no open file error ?
293     JZ      OPEN_WRITE_APPEND       ; yes
294 ; ----------------------------------;
295 ; Write errors                      ;
296 ; ----------------------------------;
297 OPWC_InvalidPathname                ; S = 1
298 OPWC_DiskFull                       ; S = 2 
299 OPWC_DirectoryFull                  ; S = 4
300 OPWC_AlreadyOpen                    ; S = 8
301 OPWC_NomoreHandle                   ; S = 16
302 ; ----------------------------------;
303 OPW_Error                           ; set ECHO, type Pathname, type #error, type "< WriteError"; no return
304     mDOCOL                          ;
305     .word   XSQUOTE                 ;
306     .byte   12,"< WriteError",0     ;
307     .word   BRAN,SD_ERROR           ;
308 ; ----------------------------------;
309
310
311 ; ======================================================================
312 ; WRITE" (CREATE part) primitive as part of OpenPathName
313 ; input from open:  S = NoSuchFile, W = open_type, SectorHL = DIRsectorHL,
314 ;                   Buffer = [DIRsector], ClusterHL = FirstClusterHL
315 ; output: nothing else abort on error:
316 ; error 1  : InvalidPathname
317 ; error 2  : DiskFull       
318 ; error 4  : DirectoryFull  
319 ; error 8  : AlreadyOpen    
320 ; error 16 : NomoreHandle   
321 ; ======================================================================
322
323 ; ----------------------------------;
324 OPEN_WRITE_CREATE                   ;
325 ; ----------------------------------;
326 ; 3 get free cluster                ;
327 ; ----------------------------------; input: FATsector
328     CALL    #SearchNewCluster       ;SWXY output:  W = new FATsector loaded in buffer,NewCluster 
329     MOV     &NewClusterL,&ClusterL  ;
330     MOV     &NewClusterH,&ClusterH  ;
331     CALL    #UpdateFATsSectorW      ;SWX update FATs with buffer
332 ; ----------------------------------;
333     CALL    #ReadSector             ; reload DIRsector
334     MOV     &EntryOfst,Y            ; reload entry offset (first free entry in DIR)
335 ; ----------------------------------;
336 ; 4 init DIRentryAttributes         ;
337 ; ----------------------------------;
338 OPWC_SetEntryAttribute              ; (cluster=DIRcluster!)
339     MOV.B   #20h,SD_BUF+11(Y)       ; file attribute = file
340     CALL    #GetYMDHMSforDIR        ;WX  X=DATE,  W=TIME
341     MOV     #0,SD_BUF+12(Y)         ; nt reserved = 0 and centiseconds are 0
342     MOV     W,SD_BUF+14(Y)          ; time of creation
343     MOV     X,SD_BUF+16(Y)          ; date of creation      20/08/2001
344     MOV     X,SD_BUF+18(Y)          ; date of access        20/08/2001
345     MOV     &ClusterH,SD_BUF+20(Y)  ; as first Cluster Hi 
346     MOV     &ClusterL,SD_BUF+26(Y)  ; as first cluster LO   
347     MOV     #0,SD_BUF+28(Y)         ; file lenghtLO  = 0 
348     MOV     #0,SD_BUF+30(Y)         ; file lenghtHI  = 0 
349 ; ----------------------------------;
350 ; 5 create DIRentryName             ;
351 ; ----------------------------------;
352     MOV     #1,S                    ; preset pathname error
353     MOV     &Pathname,T             ; here, pathname is "xxxxxxxx.yyy" format
354 ;    CMP.B   #0,0(T)                 ; forbidden null string
355     CMP     T,&EndOfPath            ;
356     JZ      OPWC_InvalidPathname    ; write error 1
357     CMP.B   #'.',0(T)               ; forbidden "." in first
358     JZ      OPWC_InvalidPathname    ; write error 1
359     MOV     #11,X                   ; X=countdown of chars entry
360     CALL    #FillDIRentryName       ;STWXY
361 ; ----------------------------------;
362 ; 6 save DIRsector                  ;
363 ; ----------------------------------;
364     CALL    #WriteSector            ;SWX update DIRsector
365 ; ----------------------------------;
366 ; 7 Get free handle                 ;
367 ; ----------------------------------;
368     MOV     #2,W                    ;
369     CALL    #GetFreeHandle          ; output : S = handle error, CurCluster and CurSector are set
370 ; ----------------------------------;
371     CMP     #0,S                    ; no error ?
372     JNZ     OPWC_NomoreHandle       ; ==> abort with error 16
373     mNEXT                           ; --
374 ; ----------------------------------;
375
376 ;-----------------------------------------------------------------------
377 ; WRITE" subroutines
378 ;-----------------------------------------------------------------------
379
380 ; SectorL is unchanged
381 ; ----------------------------------;
382 OPWW_UpdateDirectory                ; <== CloseHandleT
383 ; ----------------------------------; Input : current Handle
384     MOV     HDLL_DIRsect(T),W       ;
385     MOV     HDLH_DIRsect(T),X       ;
386     CALL    #readSectorWX           ;SWX buffer = DIRsector
387     CALL    #GetYMDHMSforDIR        ; X=DATE,  W=TIME
388     MOV     HDLW_DIRofst(T),Y       ; Y = DirEntryOffset
389     MOV     X,SD_BUF+18(Y)          ; access date
390     MOV     W,SD_BUF+22(Y)          ; modified time
391     MOV     X,SD_BUF+24(Y)          ; modified date
392 OPWW_UpdateEntryFileSize            ;
393     MOV HDLL_CurSize(T),SD_BUF+28(Y); save new filesize
394     MOV HDLH_CurSize(T),SD_BUF+30(Y);
395     MOV     HDLL_DIRsect(T),W       ; 
396     MOV     HDLH_DIRsect(T),X       ;
397     MOV     #WriteSectorWX,PC       ;SWX then RET
398 ; ----------------------------------;
399
400 ; this subroutine is called by Write_File (bufferPtr=512) and CloseHandleT (0 =< BufferPtr =< 512)
401 ; ==================================; 
402 WriteBuffer                         ;SWXY input: T = CurrentHDL
403 ; ==================================; 
404     ADD &BufferPtr,HDLL_CurSize(T)  ; update handle CurrentSizeL
405     ADDC    #0,HDLH_CurSize(T)      ;
406 ; ==================================;
407 WriteSector                         ;SWX
408 ; ==================================;
409     MOV     &SectorL,W              ; Low
410     MOV     &SectorH,X              ; High
411     MOV     #WriteSectorWX,PC       ; ...then RET
412 ; ----------------------------------;
413
414
415
416 ; write sequentially the buffer in the post incremented SectorHL.
417 ; The first time, SectorHL is initialized by WRITE".
418 ; All used registers must be initialized.
419 ; ==================================;
420 Write_File                          ; <== WRITE, SD_EMIT, TERM2SD"
421 ; ==================================;
422     MOV     #BytsPerSec,&BufferPtr  ; write always all the buffer
423     MOV     &CurrentHdl,T           ;
424     CALL    #WriteBuffer            ; write SD_BUF and update Handle informations only for DIRentry update 
425     MOV     #0,&BufferPtr           ; reset buffer pointer
426 ; ----------------------------------;
427 PostIncrementSector                 ;
428 ; ----------------------------------;
429     ADD.B   #1,HDLB_ClustOfst(T)    ; increment current Cluster offset
430     CMP.B &SecPerClus,HDLB_ClustOfst(T) ; out of bound ?
431     JNZ     Write_File_End          ; no, 
432 ; ----------------------------------;
433 GetNewCluster                       ; input : T=CurrentHdl
434 ; ----------------------------------;
435     MOV.B   #0,HDLB_ClustOfst(T)    ; reset handle ClusterOffset
436     CALL #HDLCurClusToFAT1sectWofstY;WXY Output: W=FATsector, Y=FAToffset, Cluster=HDL_CurCluster
437     PUSH    Y                       ; push current FAToffset
438     PUSH    W                       ; push current FATsector
439     CALL    #SearchNewCluster       ;SWXY  output: W = new FATsector loaded in buffer, NewCluster 
440     CMP     @RSP,W                  ; current and new clusters are in same FATsector?
441     JZ      LinkClusters            ;     yes 
442 UpdateNewClusterFATs                ;
443     CALL    #UpdateFATsSectorW      ;SWX  no: UpdateFATsSectorW with buffer of new FATsector
444     MOV     @RSP,W                  ; W = current FATsector
445     CALL    #ReadFAT1SectorW        ;SWX  reload current FATsector in buffer to link clusters
446 LinkClusters                        ;
447     MOV     @RSP+,W                 ; W = current FATsector
448     MOV     @RSP+,Y                 ; pop current FAToffset
449     MOV     &NewClusterL,SD_BUF(Y)  ; store new cluster to current cluster address in current FATsector buffer
450     CMP     #1,&FATtype             ; FAT16?
451     JZ UpdatePreviousClusterFATs    ; yes
452     MOV     &NewClusterH,SD_BUF+2(Y);
453 UpdatePreviousClusterFATs           ;
454     CALL    #UpdateFATsSectorW      ;SWX update FATS with current FATsector buffer
455 UpdateHandleCurCluster              ;
456     MOV &NewClusterL,HDLL_CurClust(T)  ; update handle with new cluster
457     MOV &NewClusterH,HDLH_CurClust(T) ;
458 ;    CALL #ComputeHDLcurrentSector   ; set Cluster first Sector as next Sector to be written
459 ;    MOV #OPWW_UpdateDirectory,PC    ; update DIRentry to avoid cluster lost, then RET
460 Write_File_End
461     MOV #ComputeHDLcurrentSector,PC ; set current Cluster Sector as next Sector to be written then RET
462 ; ----------------------------------;
463
464 ;Z WRITE            -- 
465 ; sequentially write the entire SD_BUF in a file opened by WRITE"
466 ; ----------------------------------;
467     FORTHWORD "WRITE"               ;
468 ; ----------------------------------;
469     CALL    #Write_File             ;
470     mNEXT                           ;
471 ; ----------------------------------;
472
473 ;Z SD_EMIT  c --    output char c to a SD_CARD file opened as write
474 ; ----------------------------------;
475     FORTHWORD "SD_EMIT"             ;
476 ; ----------------------------------;
477 SD_EMIT                             ;
478     CMP     #BytsPerSec,&BufferPtr  ; 4 file buffer is full ?
479     JLO     SD_EmitNext             ; 2
480     CALL    #Write_File             ;   BufferPtr = 0
481 SD_EmitNext                         ;
482     MOV     &BufferPtr,Y            ; 3 
483     MOV.B   TOS,SD_BUF(Y)           ; 3
484     ADD     #1,&BufferPtr           ; 4
485     MOV     @PSP+,TOS               ; 2
486     mNEXT                           ; 4
487 ; ----------------------------------; 22~ for SD_EMIT, 22~ for EMIT
488
489
490
491
492 ; ======================================================================
493 ; WRITE" (APPEND part) primitive as part of OpenPathName
494 ; input from open:  S = OpenError, W = open_type, SectorHL = DIRsectorHL,
495 ;                   Buffer = [DIRsector], ClusterHL = FirstClusterHL
496 ;       from open(GetFreeHandle): Y = DIRentry, T = CurrentHdl
497 ; output: nothing else abort on error
498 ; error 2  : DiskFull       
499 ; ======================================================================
500
501 ; ----------------------------------;
502 OPEN_WRITE_APPEND                   ;
503 ; ----------------------------------;
504 ; 1- open file                      ; done
505 ; ----------------------------------;
506 ; 2- compute missing Handle infos   ;
507 ; ----------------------------------;
508 ; 2.1- Compute Sectors count        ; Sectors = HDLL_CurSize/512
509 ; ----------------------------------;
510     MOV.B   HDLL_CurSize+1(T),Y     ;Y = 0:CurSizeLOHi
511     MOV.B   HDLH_CurSize(T),X       ;X = 0:CurSizeHILo 
512     SWPB    X                       ;X = CurSizeHIlo:0 
513     ADD     Y,X                     ;X = CurSizeHIlo:CurSizeLOhi
514     MOV.B   HDLH_CurSize+1(T),Y     ;Y:X = CurSize / 256
515 ; ----------------------------------;
516 ; 2.2 Compute Clusters Count        ;
517 ; ----------------------------------;
518     MOV.B &SecPerClus,T             ;3 T = DIVISOR = SecPerClus = 0:SPClo
519 DIVSECPERSPC                        ;
520     MOV #0,W                        ;1 W = 0:REMlo = 0
521     MOV #8,S                        ;1 S = CNT
522 DIVSECPERSPC1                       ;
523     RRA Y                           ;1 0>0:SEC_HI>C
524     RRC X                           ;1 C>SEC_LO>C
525     RRC.B W                         ;1 C>REMlo>C
526     SUB #1,S                        ;1 CNT-1
527     RRA T                           ;1 0>SPChi:SPClo>C
528     JNC DIVSECPERSPC1               ;2 7~ loopback if carry clear
529 DIVSECPERSPC2                       ;
530     RRA W                           ;1 0>0:REMlo>C
531     SUB #1,S                        ;1 CNT-1
532     JGE DIVSECPERSPC2               ;2 4~ loopback     Wlo = OFFSET, X = CLU_LO, Y = CLU_HI
533 ; ----------------------------------;
534     MOV &CurrentHDL,T               ;3  reload Handle ptr  
535 ; ----------------------------------;
536 ; 2.3- Compute last Cluster         ; X = Clusters numberLO, Y = Clusters numberHI 
537 ; ----------------------------------;
538     ADD  HDLL_FirstClus(T),X        ;
539     ADDC HDLH_FirstClus(T),Y        ;
540     MOV X,HDLL_CurClust(T)          ;  update handle
541     MOV Y,HDLH_CurClust(T)          ;
542 ; ----------------------------------;
543 ; 2.4- Compute Sectors offset       ;
544 ; ----------------------------------;
545     MOV.B W,HDLB_ClustOfst(T)       ;3  update handle with W = REMlo = sectors offset in last cluster
546 ; ----------------------------------;
547 ; 3- load last sector in SD_BUF     ;
548 ; ----------------------------------;
549     MOV     HDLL_CurSize(T),W       ; example : W = 1013
550     BIC     #01FFh,HDLL_CurSize(T)  ; substract 13 from HDLL_CurSize, because loaded in buffer
551     AND     #01FFh,W                ; W = 13
552     MOV     W,&BufferPtr            ; init Buffer Pointer with 13
553     CALL    #LoadHDLcurrentSector   ;SWX
554     mNEXT                           ; BufferPtr = first free byte offset
555 ; ----------------------------------;
556
557
558 ; ======================================================================
559 ; DEL" primitive as part of OpenPathName
560 ; All "DEL"eted clusters are freed
561 ; If next DIRentry in same sector is free, DIRentry is freed, else hidden.
562 ; input from open:  S = OpenError, W = open_type, SectorHL = DIRsectorHL,
563 ;                   Buffer = [DIRsector], ClusterHL = FirstClusterHL
564 ;       from open(GetFreeHandle): Y = DIRentry, T = CurrentHdl
565 ; output: nothing (no message if open error)
566 ; ======================================================================
567
568
569 OPEN_QDEL                           ;
570 ;    CMP     #4,W                    ;   open_type = DEL"
571 ;    JNZ     OPEN_8W                 ;
572 ; ----------------------------------;
573 OPEN_DEL                            ;
574 ; ----------------------------------;
575 ; 1- open file                      ; done
576 ; ----------------------------------;
577     CMP     #0,S                    ; open file happy end ?
578     JNE     DEL_END                ; no: don't send message
579 ; ----------------------------------;
580 ; 2- Delete DIR entry               ; "delete" entry with 00h if next entry in same DIRsector is free, else "hide" entry with 05Eh
581 ; ----------------------------------;
582 SelectFreeEntry                     ; nothing to do: S = 0 ready for free entry!
583 ; ----------------------------------;
584     CMP     #BytsPerSec-32,Y        ; Entry >= last entry in DIRsector ?
585     JHS     SelectHideEntry         ; yes:  next DIR entry is out of sector
586     CMP.B   #0,SD_BUF+32(Y)         ; no:   next DIR entry in DIRsector is free?
587     JZ      WriteDelEntry           ;       yes
588 ; ----------------------------------;
589 SelectHideEntry                     ;       no
590 ; ----------------------------------;
591     MOV.B   #0E5h,S                 ;
592 ; ----------------------------------;
593 WriteDelEntry
594 ; ----------------------------------;
595     MOV.B   S,SD_BUF(Y)             ; 
596     CALL    #WriteSector            ;SWX  write SectorHL=DIRsector
597 ; ----------------------------------;
598 ; 3- free all file clusters         ; Cluster = FirstCluster
599 ; ----------------------------------;
600 ComputeClusterSectWofstY            ;     
601     CALL    #ClusterToFAT1sectWofstY;WXY    W = FATsector, Y=FAToffset
602     MOV     W,&CurFATsector         ; update CurrentFATsector
603 ; ----------------------------------;
604 LoadFAT1sector
605 ; ----------------------------------;
606     MOV     W,T                     ; T = W = current FATsector memory
607     CALL    #ReadFAT1SectorW        ;SWX
608 ; ----------------------------------;
609 GetAndFreeClusterLo                 ;
610 ; ----------------------------------;
611     MOV     SD_BUF(Y),W             ; get [clusterLO]
612     MOV     #0,SD_BUF(Y)            ; free CLusterLO
613 ClusterTestSelect                   ;
614     CMP     #1,&FATtype             ; FAT16 ?
615     JZ      ClusterLoTest           ; yes
616 GetAndFreeClusterHi                 ;
617     MOV     SD_BUF+2(Y),X           ; get [clusterHI]
618     MOV     #0,SD_BUF+2(Y)          ; free CLusterHI
619 ClusterHiTest
620     AND     #00FFFh,X               ; select 12 bits significant
621     CMP     #00FFFh,X               ; [ClusterHI] was = 0FFFh?
622     JNE     SearchNextCluster2free  ; no
623 ClusterLoTest                  
624     CMP     #-1,W                   ; [ClusterLO] was = FFFFh?
625     JZ      EndOfFileClusters       ; yes 
626 ; ----------------------------------;
627 SearchNextCluster2free
628 ; ----------------------------------;
629     MOV     W,&ClusterL             ;
630     MOV     X,&ClusterH             ;
631     CALL    #ClusterToFAT1sectWofstY;WXY
632     CMP     W,T                     ; new FATsector = current FATsector memory ?
633     JZ      GetAndFreeClusterLo     ; yes loop back
634     PUSH    W                       ; no: save new FATsector...
635     MOV     T,W                     ; ...before update current FATsector
636     CALL    #UpdateFATsSectorW      ;SWX
637     MOV     @RSP+,W                 ; restore new current FATsector
638     JMP     LoadFAT1sector          ; loop back with Y = FAToffset
639 ; ----------------------------------;
640 EndOfFileClusters                   ;
641 ; ----------------------------------;
642     MOV     T,W                     ; T = W = current FATsector
643     CALL    #UpdateFATsSectorW      ;SWX
644 ; ----------------------------------;
645 ; 3- Close Handle                   ;
646 ; ----------------------------------;
647     CALL    #CloseHandleT           ;
648 ; ----------------------------------;
649 DEL_END                             ;
650     mNEXT                           ;4
651 ; ----------------------------------;
652
653
654
655 ; first TERATERM sends the command TERM2SD" file.ext" to FastForth which returns XOFF at the end of the line.
656 ; then when XON is sent below, TERATERM sends "file.ext" up to XOFF sent by TERM2SD" (slices of 512 bytes),
657 ; then TERATERM sends char ETX that closes the file on SD_CARD.
658
659
660     FORTHWORD "TERM2SD\34"
661     mDOCOL
662     .word   DELDQ                   ;                   DEL file if already exist
663     .word   lit,2                   ; -- open_type
664     .word   HERE,COUNT              ; -- open_type addr cnt
665     .word   PARENOPEN               ;                   reopen same filepath but as write
666     FORTHtoASM                      ;
667     MOV     @RSP+,IP                ;
668     BIC     #UCRXIFG,&TERM_IFG      ;   clean up UCRX buffer  
669 ; ----------------------------------;
670 T2S_GetSliceLoop                    ;   tranfert by slices of 512 bytes terminal input to file on SD_CARD via SD_BUF 
671 ; ----------------------------------;
672     MOV     #0,Y                    ;1  reset Y = BufferPtr
673     CALL    #RXON                   ;   use no registers
674 ; ----------------------------------;
675 T2S_FillBufferLoop                  ;
676 ; ----------------------------------;
677     BIT     #UCRXIFG,&TERM_IFG      ;3 new char in TERMRXBUF ?
678     JZ      T2S_FillBufferLoop      ;2
679     MOV.B   &TERM_RXBUF,X           ;3
680     CMP.B   #4,X                    ;1 EOT sent by TERATERM ?
681     JZ      T2S_END                 ;2 yes
682     MOV.B   X,SD_BUF(Y)             ;3
683     ADD     #1,Y                    ;1
684     CMP     #BytsPerSec-1,Y         ;2
685     JLO     T2S_FillBufferLoop      ;2 Y<511    21 cycles char loop
686     JZ      T2S_XOFF                ;2 Y=511    send XOFF after RX 511th char
687 ; ----------------------------------;
688 T2S_WriteFile                       ;2 Y>511
689 ; ----------------------------------;
690     CALL    #Write_File             ;TSWXY write all the buffer
691     JMP     T2S_GetSliceLoop        ;2 
692 ; ----------------------------------;
693 T2S_XOFF                            ;  27 cycles between XON and XOFF
694 ; ----------------------------------;
695     CALL    #RXOFF                  ;4  use no registers
696     JMP     T2S_FillBufferLoop      ;2  loop back to get 512th char
697 ; ----------------------------------;
698 T2S_END                             ;
699 ; ----------------------------------;
700     CALL    #RXOFF                  ;4  use no registers
701     MOV     Y,&BufferPtr            ;3
702     CALL    #CloseHandleT           ;4
703 TERM2SD_END                         ;
704     mNEXT                           ;4
705 ; ----------------------------------;
706