OSDN Git Service

raz
[fast-forth/master.git] / forthMSP430FR_SD_LowLvl.asm
1 ; -*- coding: utf-8 -*-
2 ; forthMSP430FR_SD_lowLvl.asm
3
4 ; Copyright (C) <2017>  <J.M. THOORENS>
5 ;
6 ; This program is free software: you can redistribute it and/or modify
7 ; it under the terms of the GNU General Public License as published by
8 ; the Free Software Foundation, either version 3 of the License, or
9 ; (at your option) any later version.
10 ;
11 ; This program is distributed in the hope that it will be useful,
12 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ; GNU General Public License for more details.
15 ;
16 ; You should have received a copy of the GNU General Public License
17 ; along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19 ; =====================================================================
20 ; goal : accept 64 MB up to 64 GB SD_CARD
21 ; =====================================================================
22 ; thus FAT and RootClus logical sectors are word addressable.
23
24 ; FAT is a little endian structure.
25 ; CMD frame is sent as big endian.
26
27 ; we assume that SDSC Card (up to 2GB) is FAT16 with a byte addressing
28 ; and that SDHC Card (4GB up to 32GB) is FAT32 with a sector addressing (sector = 512 bytes)
29
30 ; ref. https://en.wikipedia.org/wiki/Extended_boot_record
31 ; ref. https://en.wikipedia.org/wiki/Partition_type
32
33 ; Formatage FA16 d'une SDSC Card 2GB
34 ; First sector of physical drive (sector 0) content :
35 ; ---------------------------------------------------
36 ; dec@| HEX@ =  HEX                                                        decimal
37 ; 446 |0x1BE          : partition table first record  ==> logical drive 0       
38 ; 446 |0x1CE          : partition table 2th record    ==> logical drive 1
39 ; 446 |0x1DE          : partition table 3th record    ==> logical drive 2
40 ; 446 |0x1EE          : partition table 4th record    ==> logical drive 3
41
42 ; partition record content :
43 ; ---------------------------------------------------
44 ; dec@|HEX@ =  HEX                                                        decimal
45 ; 0   |0x00 =  0x00     : not bootable
46 ; 1   |0x01 =  02 0C 00 : Org Cylinder/Head/Sector offset (CHS-addressing) = not used
47 ; 4   |0x04 =  0x0E     : type FAT16 using LBA addressing                  = 14 ==> FAT16
48 ; 5   |0x05 =  ED 3F EE : End Cylinder/Head/Sector offset (CHS-addressing) = not used
49 ; 8   |0x08 =  00 20 00 00 : sector offset of logical drive                = 8192
50 ; 12  |0x0C =  00 40 74 00 : sector size of logical drive                  = 7618560 sectors
51
52 ; 450 |0x04 =  0x0E     : type FAT16 using LBA addressing                  = 14 ==> set FATtype = FAT16 with byte CMD addressing
53 ; 454 |0x1C6 = 89 00    : FirstSector (of logical drive 0) BS_FirstSector  = 137
54
55
56 ; ref. https://www.compuphase.com/mbr_fat.htm#BOOTSECTOR
57
58 ; FirstSector of logical drive (sector 0) content :
59 ; -------------------------------------------------
60 ; dec@| HEX@ =  HEX                                                         decimal
61 ; 11  | 0x0B = 00 02        : 512 bytes/sector          BPB_BytsPerSec  = 512
62 ; 13  | 0x0D = 40           : 64 sectors/cluster        BPB_SecPerClus  = 64
63 ; 14  | 0x0E = 01 00        : 2 reserved sectors        BPB_RsvdSecCnt  = 1
64 ; 16  | 0x10 = 02           : 2 FATs                    BPB_NumFATs     = 2 (always 2)
65 ; 17  | 0x11 = 00 02        : 512 entries/directory     BPB_RootEntCnt  = 512
66 ; 19  | 0x13 = 00 00        : BPB_TotSec16 (if < 65535) BPB_TotSec16    = 0
67 ; 22  | 0x16 = EB 00        : 235 sectors/FAT (FAT16)   BPB_FATSize     = 235
68 ; 32  | 0x20 = 77 9F 3A 00  : ‭3841911‬ total sectors     BPB_TotSec32    = ‭3841911‬
69 ; 54  | 0x36 = "FAT16"                                  BS_FilSysType   (not used)
70
71 ; all values below are evaluated in logical sectors
72 ; FAT1           = BPB_RsvdSecCnt = 1
73 ; FAT2           = BPB_RsvdSecCnt + BPB_FATSz32 = 1 + 235 = 236
74 ; OrgRootDirL    = BPB_RsvdSecCnt + (BPB_FATSize * BPB_NumFATs) = 471
75 ; RootDirSize    = BPB_RootEntCnt * 32 / BPB_BytsPerSec         = 32 sectors
76 ; OrgDatas       = OrgRootDir + RootDirSize                     = 503
77 ; OrgCluster     = OrgRootDir - 2*BPB_SecPerClus                = 375 (virtual value)
78 ; FirstSectorOfCluster(n) = OrgCluster + n*BPB_SecPerClus       ==> cluster(3) = 705
79
80 ; ====================================================================================
81
82 ; Formatage FA32 d'une SDSC Card 8GB
83 ; First sector of physical drive (sector 0) content :
84 ; ---------------------------------------------------
85 ; dec@| HEX@ =  HEX                                                        decimal
86 ; 446 |0x1BE          : partition table first record  ==> logical block 0       
87 ; 446 |0x1CE          : partition table 2th record    ==> logical block 1
88 ; 446 |0x1DE          : partition table 3th record    ==> logical block 2
89 ; 446 |0x1EE          : partition table 4th record    ==> logical block 3
90
91 ; partition record content :
92 ; ---------------------------------------------------
93 ; dec@|HEX@ =  HEX                                                        decimal
94 ; 0   |0x00 =  0x00     : not bootable
95 ; 1   |0x01 =  82 03 00 : Org CHS offset (Cylinder/Head/Sector)         = not used
96 ; 4   |0x04 =  0x0C     : type FAT32 using LBA addressing               = 12 ==> set FATtype = FAT32 with sector CMD addressing
97 ; 5   |0x05 =  82 03 00 : End offset (Cylinder/Head/Sector offset)      = not used
98 ; 8   |0x08 =  00 20 00 00 : sector offset of logical block             = 8192
99 ; 12  |0x0C =  00 40 74 00 : sector size of logical block               = 7618560
100
101 ; 454 |0x1C6 = 00 20 00 00 : FirstSector (of logical drive 0) BS_FirstSector  = 8192
102
103
104 ; FirstSector of logical block (sector 0) content :
105 ; -------------------------------------------------
106 ; dec@| HEX@ =  HEX                                                     decimal
107 ; 11  | 0x0B = 00 02        : 512 bytes/sector          BPB_BytsPerSec  = 512
108 ; 13  | 0x0D = 08           : 8 sectors/cluster         BPB_SecPerClus  = 8
109 ; 14  | 0x0E = 20 00        : 32 reserved sectors       BPB_RsvdSecCnt  = 32
110 ; 16  | 0x10 = 02           : 2 FATs                    BPB_NumFATs     = 2 (always 2)
111 ; 17  | 0x11 = 00 00        : 0                         BPB_RootEntCnt  = 0 (always 0 for FAT32)
112
113 ; 32  | 0x20 = 00 C0 EC 00  : BPB_TotSec32              BPB_TotSec32    = 15515648
114 ; 36  | 0x24 = 30 3B 00 00  : BPB_FATSz32               BPB_FATSz32     = 15152
115 ; 40  | 0x28 = 00 00        : BPB_ExtFlags              BPB_ExtFlags 
116 ; 44  | 0x2C = 02 00 00 00  : BPB_RootClus              BPB_RootClus    = 2
117 ; 48  | 0x30 = 01 00        : BPB_FSInfo                BPB_FSInfo      = 1
118 ; 50  | 0x33 = 06 00        : BPB_BkBootSec             BPB_BkBootSec   = 6
119 ; 82  | 0x52 = "FAT32"      : BS_FilSysType             BS_FilSysType   (not used)
120
121
122 ; all values below are evaluated in sectors
123 ; FAT1           = BS_FirstSector + BPB_RsvdSecCnt = 8192 + 32 = 8224
124 ; FAT2           = BS_FirstSector + BPB_RsvdSecCnt + BPB_FATSz32 = 8192 + 32 + 15152 = 23376
125 ; OrgRootDirL    = BS_FirstSector + BPB_RsvdSecCnt + (BPB_FATSz32 * BPB_NumFATs) = 8192 + 32 + 15152*2 = 38528
126 ; OrgCluster     = OrgRootDir - 2*BPB_SecPerClus = 38512
127 ; RootDirSize    = BPB_RootEntCnt * 32 / BPB_BytsPerSec         = 0
128 ; OrgDatas       = OrgRootDir + RootDirSize                     = 38512
129 ; FirstSectorOfCluster(n) = OrgCluster + n*BPB_SecPerClus       ==> cluster(6) = 38560
130
131
132
133 BytsPerSec      .equ 512
134
135 ; all sectors are computed as logical, then physically translated at last time by RW_Sector_CMD
136
137 ; in SPI mode CRC is not required, but CMD frame must be ended with a stop bit
138 ; ==================================;
139 RW_Sector_CMD                       ;WX <=== CMD17 or CMD24 (read or write Sector CMD)
140 ; ==================================;
141     BIC.B   #SD_CS,&SD_CSOUT        ; SD_CS low
142     BIT.B   #SD_CD,&SD_CDIN         ; memory card present ?
143     JZ      ComputePhysicalSector   ; yes
144     MOV     #COLD,PC                ; no: force COLD
145 ; ----------------------------------;
146 ComputePhysicalSector               ;
147 ; ----------------------------------;
148     ADD     &BS_FirstSectorL,W      ;3
149     ADDC    &BS_FirstSectorH,X      ;3
150 ; ----------------------------------;
151     MOV     #1,&SD_CMD_FRM          ;3 $(01 00 xx xx xx CMD) (set stop bit)
152     CMP     #2,&FATtype             ;3 FAT32 ?          
153     JZ      FAT32_CMD               ;2 yes
154 FAT16_CMD                           ;  FAT16 : CMD17/24 byte address = Sector * BPB_BytsPerSec
155     ADD     W,W                     ;  shift left one SectorL
156     ADDC.B  X,X                     ;
157     MOV     W,&SD_CMD_FRM+2         ;  $(01 00 ll LL xx CMD)
158     MOV.B   X,&SD_CMD_FRM+4         ;  $(01 00 ll LL hh CMD) 
159     JMP     SDbusyLoop
160 FAT32_CMD                           ;  FAT32 : CMD17/24 sector address
161     MOV.B   W,&SD_CMD_FRM+1         ;3 $(01 ll xx xx xx CMD)
162     SWPB    W                       ;1
163     MOV.B   W,&SD_CMD_FRM+2         ;3 $(01 ll LL xx xx CMD)
164     MOV.B   X,&SD_CMD_FRM+3         ;3 $(01 ll LL hh xx CMD)
165     SWPB    X                       ;1
166     MOV.B   X,&SD_CMD_FRM+4         ;3 $(01 ll LL hh HH CMD)
167 ; ==================================;
168 SDbusyLoop                          ; <=== CMD41, CMD1, CMD16 (R1 expected response = 0 = ready)
169 ; ==================================;
170     CALL #SPI_GET                   ;
171     CMP.B   #-1,W                   ; FFh expected value <==> MISO = 1 = not busy
172     JNE SDbusyLoop                  ; loop back while yet busy
173     MOV     #0,W                    ; W = expected R1 response = ready = 0, for CMD41, CMD1, CMD16, CMD17, CMD24
174
175 ; ==================================;
176 sendCommand                         ;X <=== CMD0, CMD8, CMD55 (W = R1 expected response = 1 = idle)
177 ; ==================================;
178                                     ; input : SD_CMD_FRM : {CRC,byte_l,byte_L,byte_h,byte_H,CMD} 
179                                     ;         W = expected return value
180                                     ; output  W is unchanged, flag Z is positionned
181                                     ; reverts CMD bytes before send : $(CMD hh LL ll 00 CRC)
182     MOV     #5,X                    ; X = SD_CMD_FRM index AND countdown
183 ; ----------------------------------;
184 Send_CMD_PUT                        ; performs little endian --> big endian conversion
185 ; ----------------------------------;
186     MOV.B   SD_CMD_FRM(X),&SD_TXBUF ;5 
187     CMP     #0,&SD_BRW              ;3 full speed ?
188     JZ      FullSpeedSend           ;2 yes
189 Send_CMD_Loop                       ;  no: case of low speed during memCardInit
190     BIT     #UCRXIFG,&SD_IFG        ;3
191     JZ      Send_CMD_Loop           ;2
192     CMP.B   #0,&SD_RXBUF            ;3 to clear UCRXIFG
193 FullSpeedSend                       ;
194 ;   NOP                             ;0 NOPx adjusted to avoid SD error
195     SUB.B   #1,X                    ;1
196     JHS     Send_CMD_PUT            ;2 U>= : don't skip SD_CMD_FRM(0) !
197
198                                     ; host must provide height clock cycles to complete operation
199                                     ; here X=255, so wait for CMD return expected value with PUT FFh 256 times
200
201 ;    MOV     #4,X                    ; to pass made in PRC SD_Card init 
202 ;    MOV     #16,X                   ; to pass Transcend SD_Card init
203 ;    MOV     #32,X                   ; to pass Panasonic SD_Card init
204 ;    MOV     #64,X                   ; to pass SanDisk SD_Card init
205 ;    MOV     #1000,X                 ; max value
206 ; ----------------------------------;
207 Wait_Command_Response               ; expect W = return value during X = 255 delay time
208 ; ----------------------------------;
209     SUB     #1,X                    ;1
210     JN      SPI_WAIT_RET            ;2 error on time out with SR(Z) = 0
211     MOV.B   #-1,&SD_TXBUF           ;3 PUT FFh
212     CMP     #0,&SD_BRW              ;3 full speed ?
213     JZ      FullSpeedGET            ;2 yes
214 cardResp_Getloop                    ;  no: case of low speed during memCardInit
215     BIT     #UCRXIFG,&SD_IFG        ;3
216     JZ      cardResp_Getloop        ;2
217 FullSpeedGET                        ;
218 ;    NOP2                           ;2 NOPx adjusted to avoid SD_error
219     CMP.B   &SD_RXBUF,W             ;3 return value = ExpectedValue ?
220     JNZ     Wait_Command_Response   ;2
221 SPI_WAIT_RET                        ; SR(Z) = 1 <==> Return value = expected value
222     RET                             ; expected value = W is unchanged
223 ; ----------------------------------;
224
225
226 ; SPI_GET and SPI_PUT are adjusted for SD_CLK = MCLK
227 ; PUT value must be a word or  byte:byte because little endian to big endian conversion
228
229 ; ==================================;
230 SPI_GET                             ; PUT(FFh)
231 ; ==================================; output : W = received byte, X = 0 always
232     MOV #1,X                        ;1
233 ; ==================================;
234 SPI_X_GET                           ; PUT(FFh) X time
235 ; ==================================; output : W = last received byte, X = 0
236     MOV #-1,W                       ;1
237 ; ==================================;
238 SPI_PUT                             ; PUT(W) X time
239 ; ==================================; output : W = last received byte, X = 0
240             SWPB W                  ;1
241             MOV.B W,&SD_TXBUF       ;3 put W high byte then W low byte and so forth that performs little to big endian conversion
242             CMP #0,&SD_BRW          ;3 full speed ?
243             JZ FullSpeedPut         ;2 
244 SPI_PUTWAIT BIT #UCRXIFG,&SD_IFG    ;3
245             JZ SPI_PUTWAIT          ;2
246             CMP.B #0,&SD_RXBUF      ;3 reset RX flag
247 FullSpeedPut
248 ;           NOP                     ;0 NOPx adjusted to avoid SD error
249             SUB #1,X                ;1
250             JNZ SPI_PUT             ;2
251 SPI_PUT_END MOV.B &SD_RXBUF,W       ;3
252             RET                     ;4
253 ; ----------------------------------;
254
255 ; ==================================;
256 readFAT1SectorW                     ; read a FAT1 sector
257 ; ==================================;
258     ADD     &OrgFAT1,W              ;
259 ; ==================================;
260 readSectorW                         ; read a logical sector up to 65535 (case of FAT1,FAT2,RootDIR)
261 ; ==================================;
262     MOV     #0,X                    ;
263 ; ==================================;
264 readSectorWX                        ; read a logical sector
265 ; ==================================;
266     BIS     #1,S                    ; preset sd_read error
267     MOV.B   #51h,&SD_CMD_FRM+5      ; CMD17 = READ_SINGLE_BLOCK
268     CALL    #RW_Sector_CMD          ; which performs logical sector to physical sector then little endian to big endian conversions
269     JNE     SD_CARD_ERROR           ; time out error if R1 <> 0 
270 ; ----------------------------------;
271 WaitFEhResponse                     ; wait SD_Card response FEh
272 ; ----------------------------------;
273     CALL #SPI_GET                   ;
274     CMP.B   #-2,W                   ; FEh expected value
275     JNZ WaitFEhResponse             ;
276 ; ----------------------------------;
277 ReadSectorLoop                      ; 16 cycles loop read byte, starts with X = 0
278 ; ----------------------------------;
279     MOV.B   #-1,&SD_TXBUF           ; 3 put FF
280     NOP3                            ; 3 NOPx adjusted to avoid read SD_error
281     ADD     #1,X                    ; 1
282     CMP     #BytsPerSec,X           ; 2
283     MOV.B   &SD_RXBUF,BUFFER-1(X)   ; 5
284     JNZ     ReadSectorLoop          ; 2
285 ; ----------------------------------;
286 ReadSkipCRC16                       ; not used in SPI mode
287 ; ----------------------------------;
288     MOV     #2,X                    ;
289     CALL    #SPI_X_GET              ;
290 ; ----------------------------------;
291 ReadWriteHappyEnd                   ;
292 ; ----------------------------------;
293     BIC     #3,S                    ; reset read and write errors
294     BIS.B   #SD_CS,&SD_CSOUT        ; SD_CS = high  
295     RET                             ; 
296 ; ----------------------------------;
297
298     .IFDEF SD_CARD_READ_WRITE
299
300 ; ==================================;
301 WriteSectorW                        ; write a logical sector up to 65535 (FAT1,FAT2,RootDIR)
302 ; ==================================;
303     MOV     #0,X                    ;
304 ; ==================================;
305 WriteSectorWX                       ; write a logical sector
306 ; ==================================;
307     BIS     #2,S                    ; preset sd_write error
308     MOV.B   #058h,SD_CMD_FRM+5      ; CMD24 = WRITE_SINGLE_BLOCK
309     CALL    #RW_Sector_CMD          ; which performs logical sector to physical sector then little endian to big endian conversions
310     JNE     SD_CARD_ERROR           ; ReturnError = 2
311     MOV     #0FFFEh,W               ; PUT FFFEh as preamble requested for sector write
312     MOV     #2,X                    ; to put 16 bits value
313     CALL    #SPI_PUT                ; which performs little endian to big endian conversion
314 ; ----------------------------------;
315 WriteSectorLoop                     ; 11 cycles loop write, starts with X = 0
316 ; ----------------------------------;
317     MOV.B   BUFFER(X),&SD_TXBUF     ; 5
318     NOP                             ; 1 NOPx adjusted to avoid write SD_error
319     ADD     #1,X                    ; 1
320     CMP     #BytsPerSec,X           ; 2
321     JNZ     WriteSectorLoop         ; 2
322 ; ----------------------------------;
323 WriteSkipCRC16                      ; not used in SPI mode
324 ; ----------------------------------;
325     MOV     #3,X                    ; PUT 2 bytes to skip CRC16
326     CALL    #SPI_X_GET              ; + 1 byte to get data token in W
327 ; ----------------------------------;
328 CheckWriteState                     ;
329 ; ----------------------------------;
330     BIC.B   #0E1h,W                 ; apply mask for Data response
331     CMP.B   #4,W                    ; data accepted
332     JZ      ReadWriteHappyEnd       ;
333 ; ----------------------------------;
334
335     .ENDIF ; SD_CARD_READ_WRITE
336
337 ; SD Error n°
338 ; High byte
339 ; 1  = CMD17    read error
340 ; 2  = CMD24    write error 
341 ; 4  = CMD0     time out (GO_IDLE_STATE)
342 ; 8  = CMD1     time out (SEND_OP_COND), reserved for MMC_Card
343 ; 10 = ACMD41   time out (APP_SEND_OP_COND)
344 ; 20 = CMD16    time out (SET_BLOCKLEN)
345 ; 40 = not FAT16/FAT32 media, low byte = partition ID
346
347 ; low byte, if CMD R1 response : |0|7|6|5|4|3|2|1|
348 ; 1th bit = In Idle state
349 ; 2th bit = Erase reset
350 ; 3th bit = Illegal command
351 ; 4th bit = Command CRC error
352 ; 5th bit = erase sequence error
353 ; 6th bit = address error
354 ; 7th bit = parameter error
355
356 ; ----------------------------------;
357 SD_CARD_ERROR                       ; <=== SD_INIT errors 4,8,10,20,40
358 ; ----------------------------------;
359     BIS.B #SD_CS,&SD_CSOUT          ; SD_CS = high
360     SWPB S                          ; High Level error in High byte
361     ADD &SD_RXBUF,S                 ; add SPI(GET) return value to high level error
362     mDOCOL                          ;
363     .word   XSQUOTE                 ;
364     .byte   11,"< SD Error!"        ;
365 ; ----------------------------------;
366 SD_QABORTYES                        ; <=== OPEN/READ and WRITE errors
367 ; ----------------------------------;
368     FORTHtoASM                      ;
369     SUB #4,PSP                      ;
370     MOV TOS,2(PSP)                  ;
371     MOV &BASE,0(PSP)                ;
372     MOV #10h,&BASE                  ; select hex
373     MOV S,TOS                       ;
374     ASMtoFORTH                      ;
375     .word   UDOT                    ;
376     .word   FBASE,STORE             ; restore base
377     .word   QABORTYES               ;
378 ; ----------------------------------;
379