Subversion Repositories Kolibri OS

Rev

Rev 2465 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2465 Serge 1
; Copyright (c) 2008-2009, diamond
2
; All rights reserved.
3
;
4
; Redistribution and use in source and binary forms, with or without
5
; modification, are permitted provided that the following conditions are met:
6
;       * Redistributions of source code must retain the above copyright
7
;       notice, this list of conditions and the following disclaimer.
8
;       * Redistributions in binary form must reproduce the above copyright
9
;       notice, this list of conditions and the following disclaimer in the
10
;       documentation and/or other materials provided with the distribution.
11
;       * Neither the name of the  nor the
12
;       names of its contributors may be used to endorse or promote products
13
;       derived from this software without specific prior written permission.
14
;
15
; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka  ''AS IS'' AND ANY
16
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
; DISCLAIMED. IN NO EVENT SHALL  BE LIABLE FOR ANY
19
; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
;*****************************************************************************
26
 
27
        org     0x7E00
28
; the KordOS FAT32 bootsector loads first cluster of this file to 0:7E00 and transfers control to here
29
; ss:bp = 0:7C00
30
; ds = 0
31
virtual at bp
32
                rb      3       ; BS_jmpBoot
33
                rb      8       ; BS_OEMName, ignored
34
                dw      ?       ; BPB_BytsPerSec
35
BPB_SecsPerClus db      ?
36
BPB_RsvdSecCnt  dw      ?
37
BPB_NumFATs     db      ?
38
BPB_RootEntCnt  dw      ?
39
                dw      ?       ; BPB_TotSec16
40
                db      ?       ; BPB_Media
41
                dw      ?       ; BPB_FATSz16 = 0 for FAT32
42
BPB_SecPerTrk   dw      ?
43
BPB_NumHeads    dw      ?
44
BPB_HiddSec     dd      ?
45
                dd      ?       ; BPB_TotSec32
46
BPB_FATSz32     dd      ?
47
BPB_ExtFlags    dw      ?
48
                dw      ?       ; BPB_FSVer
49
BPB_RootClus    dd      ?
50
filesize:
51
                dw      ?       ; BPB_FSInfo
52
                dw      ?       ; BPB_BkBootSec
53
                rb      12      ; BPB_Reserved
54
BS_DrvNum       db      ?
55
                db      ?       ; BS_Reserved1
56
                db      ?       ; BS_BootSig
57
                dd      ?       ; BS_VolID
58
;               rb      11      ; BS_VolLab
59
;               rb      5       ; BS_FilSysType, first 5 bytes
60
read_sectors32  dw      ?
61
read_sectors2   dw      ?
62
err_             dw      ?
63
noloader        dw      ?
64
cachelimit      dw      ?
65
fatcachehead    rw      2
66
fatcacheend     dw      ?
67
                rb      3       ; BS_FilSysType, last 3 bytes
68
curseg          dw      ?
69
num_sectors     dd      ?
70
cur_cluster     dd      ?
71
next_cluster    dd      ?
72
flags           dw      ?
73
cur_delta       dd      ?
74
end virtual
75
 
76
; procedures from boot sector
77
; LBA version
78
lba_read_sectors2 = 7CD6h
79
lba_err = 7CAAh
80
lba_noloader = 7CA7h    ; = lba_err - 3
81
; CHS version
82
chs_read_sectors2 = 7CD2h
83
chs_err = 7CA6h
84
chs_noloader = 7CA3h    ; = chs_err - 3
85
 
86
        push    eax cx          ; save our position on disk
87
; determine version of bootsector (LBA vs CHS)
88
        mov     [read_sectors2], chs_read_sectors2
89
        mov     bx, chs_err
90
        mov     [err_], bx
91
;       mov     [noloader], chs_noloader
92
        cmp     byte [bx], 0xE8         ; [chs_err] = 0xE8 for CHS version, 0x14 for LBA version
93
        jz      @f
94
        add     [read_sectors2], lba_read_sectors2 - chs_read_sectors2
95
        add     [err_], lba_err - chs_err
96
;       mov     [noloader], lba_noloader
97
@@:
98
        xor     bx, bx
99
; determine size of cache for folders
100
        int     12h             ; ax = size of available base memory in Kb
101
        sub     ax, 92000h / 1024
102
        jae     @f
103
nomem:
104
        mov     si, nomem_str
105
        jmp     [err_]
106
@@:
107
        shr     ax, 3
108
        mov     [cachelimit], ax        ; size of cache - 1
109
        mov     es, bx
110
; no folders in cache yet
111
        mov     di, foldcache_clus
112
        mov     cx, 8*4/2 + 1
113
        xor     ax, ax
3555 Serge 114
        rep stosw
2465 Serge 115
; bootsector code caches one FAT sector, [bp-14], in 6000:0000
116
; initialize our (more advanced) FAT caching from this
117
        mov     di, 8400h
118
        mov     cx, di
119
        lea     si, [fatcachehead]
120
        mov     [si], si                ; no sectors in cache:
121
        mov     [si+2], si              ; 'prev' & 'next' links point to self
122
        mov     [fatcacheend], di       ; first free item = 8400h
123
        stosw                   ; 'next cached sector' link
124
        stosw                   ; 'prev cached sector' link
125
        mov     eax, [bp-14]
126
        stosd                           ; first sector number in cache
127
        test    eax, eax
128
        js      @f
129
        mov     [si], cx                ; 'first cached sector' link = 8400h
130
        mov     [si+2], cx              ; 'next cached sector' link = 8400h
131
        mov     [fatcacheend], di       ; first free item = 8406h
132
@@:
133
; if cluster = sector, we need to read second part of our file
134
; (bootsector loads only first cluster of kordldr.f32)
135
        pop     cx eax          ; restore our position on disk
136
        cmp     cx, 1
137
        ja      kordldr_full
138
        sub     eax, [bp-10]
139
        inc     eax
140
        inc     eax             ; eax = first cluster of kordldr.f32
141
        call    get_next_cluster
142
        jc      @f
143
;       jmp     [noloader]
144
        mov     ax, [err_]
145
        sub     ax, 3
146
        jmp     ax
147
@@:
148
        dec     eax
149
        dec     eax
150
        push    0x800
151
        pop     es
152
        call    [read_sectors2]
153
kordldr_full:
154
; bootsector code has read some data of root directory to 8000:0000
155
; initialize our folder caching from this
156
        mov     eax, [BPB_RootClus]
157
        mov     [foldcache_clus], eax
158
        mov     cx, [curseg]
159
        mov     ax, 8000h
160
        sub     cx, ax          ; cx = size of data read in paragraphs (0x10 bytes)
161
        shr     cx, 1           ; cx = size of folder data read in entries (0x20 bytes)
162
        mov     [foldcache_size], cx
163
        shl     cx, 4
164
        push    ds
165
        mov     ds, ax
166
        push    0x9000
167
        pop     es
168
        xor     si, si
169
        xor     di, di
3555 Serge 170
        rep movsw
2465 Serge 171
        pop     ds
172
; ...continue loading...
173
        mov     di, secondary_loader_info
174
        call    load_file
175
        test    bx, bx
176
        mov     bx, [err_]
177
        jz      @f
178
        mov     si, aKernelNotFound
179
        jmp     bx
180
@@:
181
; for subsequent calls to callback function, hook error handler
182
; push hooked_err / ret
183
        mov     dword [bx], 0x68 + (hooked_err shl 8) + (0xC3 shl 24)
184
; set registers for secondary loader
185
        mov     ah, [bp-2]              ; drive id
186
        mov     al, 'f'
187
        btr     ax, 15
188
        jnc     @f
189
        mov     al, 'h'
190
@@:
191
        mov     bx, '32'
192
        mov     si, callback
193
        jmp     far [si+secondary_loader_info-callback]
194
 
195
nomem_str       db      'No memory',0
196
 
197
cluster2sector:
198
        sub     eax, 2
199
clustersz2sectorsz:
200
        movzx   ecx, [BPB_SecsPerClus]
201
        mul     ecx
202
        ret
203
 
204
get_next_cluster:
205
; in: eax = cluster
206
; out: if there is next cluster: CF=1, eax = next cluster
207
; out: if there is no next cluster: CF=0
208
        push    di bx
209
        push    ds es
210
        push    ss
211
        pop     ds
212
        push    ss
213
        pop     es
214
        push    ax
215
        shr     eax, 7
216
; eax = FAT sector number; look in cache
217
        mov     di, 8400h
218
.cache_lookup:
219
        cmp     di, [fatcacheend]
220
        jae     .not_in_cache
221
        scasd
222
        scasd
223
        jnz     .cache_lookup
224
.in_cache:
225
        sub     di, 8
226
; delete this sector from the list
227
        push    si
228
        mov     si, [di]
229
        mov     bx, [di+2]
230
        mov     [si+2], bx
231
        mov     [bx], si
232
        pop     si
233
        jmp     @f
234
.not_in_cache:
235
; cache miss
236
; cache is full?
237
        mov     di, [fatcacheend]
238
        cmp     di, 8C00h
239
        jnz     .cache_not_full
240
; yes, delete the oldest entry
241
        mov     di, [fatcachehead]
242
        mov     bx, [di]
243
        mov     [fatcachehead], bx
244
        push    word [di+2]
245
        pop     word [bx+2]
246
        jmp     .cache_append
247
.cache_not_full:
248
; no, allocate new sector
249
        add     [fatcacheend], 8
250
.cache_append:
251
; read FAT
252
        mov     [di+4], eax
253
        pushad
254
        lea     cx, [di + 0x10000 - 0x8400 + (0x6000 shr (9-4-3))]      ; +0x10000 - for FASM
255
        shl     cx, 9-4-3
256
        mov     es, cx
257
        xor     bx, bx
258
        mov     cx, 1
259
        add     eax, [bp-6]     ; FAT start
260
        sub     eax, [bp-10]
261
        call    [read_sectors2]
262
        popad
263
@@:
264
; add new sector to the end of list
265
        mov     bx, di
266
        xchg    bx, [fatcachehead+2]
267
        push    word [bx]
268
        pop     word [di]
269
        mov     [bx], di
270
        mov     [di+2], bx
271
; get requested item
272
        lea     ax, [di + 0x10000 - 0x8400 + (0x6000 shr (9-4-3))]
273
        pop     di
274
        and     di, 0x7F
275
        shl     di, 2
276
        shl     ax, 9-4-3
277
        mov     ds, ax
278
        and     byte [di+3], 0x0F
279
        mov     eax, [di]
280
        pop     es ds
281
        pop     bx di
282
        ;and    eax, 0x0FFFFFFF
283
        cmp     eax, 0x0FFFFFF7
284
        ret
285
 
286
if $ > 0x8000
287
error 'get_next_cluster must fit in first sector of kordldr.f32!'
288
end if
289
 
290
load_file:
291
; in: ss:bp = 0:7C00
292
; in: ds:di -> information structure
293
;       dw:dw   address
294
;       dw      limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100)
295
;       ASCIIZ  name
296
; out: bx = status: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=2 - file not found
297
; out: dx:ax = file size (0xFFFFFFFF if file not found)
298
        mov     eax, [BPB_RootClus]     ; start from root directory
299
        or      dword [filesize], -1    ; initialize file size with invalid value
300
        lea     si, [di+6]
301
parse_dir_loop:
302
; convert name to FAT name
303
        push    di
304
        push    ax
305
        push    ss
306
        pop     es
307
; convert ASCIIZ filename to FAT name
308
filename equ bp
309
        mov     di, filename
310
        push    di
311
        mov     cx, 8+3
312
        mov     al, ' '
3555 Serge 313
        rep stosb
2465 Serge 314
        pop     di
315
        mov     cl, 8   ; 8 symbols per name
316
        mov     bl, 1
317
nameloop:
318
        lodsb
319
        test    al, al
320
        jz      namedone
321
        cmp     al, '/'
322
        jz      namedone
323
        cmp     al, '.'
324
        jz      namedot
325
        dec     cx
326
        js      badname
327
        cmp     al, 'a'
328
        jb      @f
329
        cmp     al, 'z'
330
        ja      @f
331
        sub     al, 'a'-'A'
332
@@:
333
        stosb
334
        jmp     nameloop
335
namedot:
336
        inc     bx
337
        jp      badname
338
        add     di, cx
339
        mov     cl, 3
340
        jmp     nameloop
341
badname:        ; do not make direct js/jp to notfound_pop:
342
                ; this generates long forms of conditional jumps and results in longer code
343
        jmp     notfound_pop
344
namedone:
345
; scan directory
346
        pop     ax      ; eax = cluster of directory
347
                        ; high word of eax is preserved by operations above
348
        push    ds
349
        push    si
350
; read a folder sector-by-sector and scan
351
; first, try to use the cache
352
        push    ss
353
        pop     ds
354
        mov     di, foldcache_mark
355
        xor     bx, bx
356
        mov     cx, [cachelimit]
357
@@:
358
        lea     si, [di+bx]
359
        mov     edx, dword [foldcache_clus+si-foldcache_mark+bx]
360
        cmp     edx, eax
361
        jz      cacheok
362
        test    edx, edx
363
        jz      cacheadd        ; the cache has place for new entry
364
        inc     bx
365
        inc     bx
366
        dec     cx
367
        jns     @b
368
; the folder is not present in the cache, so add it
369
; the cache is full; find the oldest entry and replace it with the new one
370
        mov     bx, -2
371
        mov     dx, [cachelimit]
372
@@:
373
        inc     bx
374
        inc     bx
375
        cmp     word [di+bx], dx        ; marks have values 0 through [cachelimit]
376
        jnz     @b
377
        lea     si, [di+bx]
378
cacheadd:
379
        or      word [di+bx], 0xFFFF    ; very big value, it will be changed soon
380
        and     [foldcache_size+di-foldcache_mark+bx], 0        ; no folder items yet
381
        mov     dword [foldcache_clus+si-foldcache_mark+bx], eax
382
cacheok:
383
; update cache marks
384
        mov     dx, [di+bx]
385
        mov     cx, [foldcache_size+di-foldcache_mark+bx]
386
        mov     di, [cachelimit]
387
        add     di, di
388
cacheupdate:
389
        cmp     [foldcache_mark+di], dx
390
        adc     [foldcache_mark+di], 0
391
        dec     di
392
        dec     di
393
        jns     cacheupdate
394
        and     [foldcache_mark+bx], 0
395
; done, bx contains (position in cache)*2
396
        ;mov    dx, bx
397
        ;shl    dx, 8           ; dx = (position in cache)*0x2000/0x10
398
        ;add    dx, 0x9000
399
        lea     dx, [bx + 0x90]
400
        xchg    dl, dh
401
        mov     ds, dx
402
        mov     si, filename    ; ss:si -> filename in FAT style
403
        call    scan_for_filename
404
        jz      lookup_done
405
; cache miss, read folder data from disk
406
        mov     bx, cx
407
        shr     bx, 4
408
        shl     cx, 5
409
        mov     di, cx          ; es:di -> free space in cache entry
410
; external loop: scan clusters
411
folder_next_cluster:
412
; internal loop: scan sectors in cluster
413
        push    eax
414
        call    cluster2sector
415
folder_next_sector:
416
; skip first bx sectors
417
        dec     bx
418
        jns     folder_skip_sector
419
        push    cx
420
        push    es di
421
        push    0x8000
422
        pop     es
423
        xor     bx, bx
424
        mov     cx, 1
425
        push    es
426
        push    eax
427
        call    [read_sectors2]
428
        pop     eax
429
; copy data to the cache...
430
        pop     ds
431
        pop     di es
432
        cmp     di, 0x2000      ; ...if there is free space, of course
433
        jae     @f
434
        pusha
435
        mov     cx, 0x100
436
        xor     si, si
3555 Serge 437
        rep movsw
2465 Serge 438
        mov     di, es
439
        shr     di, 8
440
        add     [ss:foldcache_size+di-0x90], 0x10       ; 0x10 new entries in the cache
441
        popa
442
@@:
443
        push    es
444
        mov     cl, 0x10        ; ch=0 at this point
445
        call    scan_for_filename
446
        pop     es
447
        pop     cx
448
        jz      lookup_done_pop
449
folder_skip_sector:
450
        inc     eax
451
        loop    folder_next_sector
452
        pop     eax     ; eax = current cluster
453
        call    get_next_cluster
454
        jc      folder_next_cluster
455
        stc
456
        push    eax
457
lookup_done_pop:
458
        pop     eax
459
lookup_done:
460
        pop     si
461
; CF=1 <=> failed
462
        jnc     found
463
        pop     ds
464
notfound:
465
        pop     di
466
notfound2:
467
        mov     bx, 2   ; file not found
468
        mov     ax, 0xFFFF
469
        mov     dx, ax  ; invalid file size
470
        ret
471
notfound_pop:
472
        pop     ax
473
        jmp     notfound
474
found:
475
        mov     eax, [di+20-2]
476
        mov     edx, [di+28]
477
        mov     ax, [di+26]     ; get cluster
478
        test    byte [di+11], 10h       ; directory?
479
        pop     ds
480
        pop     di
481
        jz      regular_file
482
        cmp     byte [si-1], 0
483
        jz      notfound2       ; don't read directories as regular files
484
; ok, we have found a directory and the caller requested a file into it
485
        jmp     parse_dir_loop  ; restart with new cluster in ax
486
regular_file:
487
        cmp     byte [si-1], 0
488
        jnz     notfound2       ; file does not contain another files
489
; ok, we have found a regular file and the caller requested it
490
; save file size
491
        mov     [filesize], edx
492
        mov     si, [di+4]      ; [ds:di+4] = limit in 4K blocks
493
        shl     si, 3
494
        push    si
495
        les     bx, [di]        ; es:bx -> buffer
496
clusloop:
497
; eax = first cluster, top of stack contains limit in sectors
498
        mov     esi, eax        ; remember current cluster
499
        xor     ecx, ecx        ; ecx will contain number of consecutive clusters
500
        mov     [cur_delta], ecx
501
        mov     edi, eax
502
clusfind:
503
        inc     edi
504
        inc     ecx
505
        call    get_next_cluster
506
        jnc     clusread
507
        cmp     eax, edi
508
        jz      clusfind
509
        stc
510
clusread:
511
        pop     di      ; limit in sectors
512
        movzx   edi, di
513
        push    eax     ; save next cluster
514
        pushf           ; save flags
515
; read cx clusters, starting from si
516
; calculate number of sectors
517
        xchg    eax, ecx
518
        call    clustersz2sectorsz
519
        mov     [num_sectors], eax
520
        jmp     @f
521
continue_load_file:
522
        les     bx, [di]        ; es:bx -> buffer
523
        movzx   edi, word [di+4]        ; di = limit in 4K blocks
524
        shl     di, 3   ; now di = limit in sectors
525
        mov     eax, [num_sectors]
526
        mov     esi, [cur_cluster]
527
        push    [next_cluster]
528
        push    [flags]
529
        test    eax, eax
530
        jz      nextclus
531
@@:
532
; eax = number of sectors; compare with limit
533
        cmp     eax, edi
534
        seta    dl
535
        push    dx      ; limit was exceeded?
536
        jbe     @f
537
        mov     eax, edi
538
@@:
539
        sub     di, ax  ; calculate new limit
540
        sub     [num_sectors], eax
541
        mov     [cur_cluster], esi
542
; calculate starting sector
543
        push    ax
544
        xchg    eax, esi
545
        call    cluster2sector
546
        pop     cx
547
        add     eax, [cur_delta]
548
        add     [cur_delta], ecx
549
; read
550
        call    [read_sectors2]
551
        pop     dx
552
; next cluster?
553
nextclus:
554
        popf
555
        pop     eax
556
        mov     [next_cluster], eax
557
        pushf
558
        pop     [flags]
559
        jnc     @f      ; no next cluster => return
560
        mov     dl, 1
561
        test    di, di
562
        jz      @f      ; if there is next cluster but current limit is 0 => return: limit exceeded
563
        push    di
564
        jmp     clusloop        ; all is ok, continue
565
hooked_err:
566
        mov     sp, 7C00h-14-2  ; restore stack
567
        mov     dl, 3           ; return: read error
568
@@:
569
        mov     bl, dl
570
        mov     bh, 0
571
        mov     ax, [filesize]
572
        mov     dx, [filesize+2]
573
        ret
574
 
575
scan_for_filename:
576
; in: ss:si -> 11-bytes FAT name
577
; in: ds:0 -> part of directory data
578
; in: cx = number of entries
579
; in: bh = 0
580
; out: if found: CF=0, ZF=1, es:di -> directory entry
581
; out: if not found, but continue required: CF=1 and ZF=0
582
; out: if not found and zero item reached: CF=1 and ZF=1
583
        push    ds
584
        pop     es
585
        xor     di, di
586
        push    cx
587
        jcxz    snoent
588
sloop:
589
        cmp     byte [di], bh
590
        jz      snotfound
591
        test    byte [di+11], 8         ; volume label?
592
        jnz     scont                   ; ignore volume labels
593
        pusha
594
        mov     cx, 11
3555 Serge 595
        repz cmps byte [ss:si], byte [es:di]
2465 Serge 596
        popa
597
        jz      sdone
598
scont:
599
        add     di, 0x20
600
        loop    sloop
601
snoent:
602
        inc     cx      ; clear ZF flag
603
snotfound:
604
        stc
605
sdone:
606
        pop     cx
607
lrdret:
608
        ret
609
 
610
; Callback function for secondary loader
611
callback:
612
; in: ax = function number; only functions 1 and 2 are defined for now
613
; save caller's stack
614
        mov     dx, ss
615
        mov     cx, sp
616
; set our stack (required because we need ss=0)
617
        xor     si, si
618
        mov     ss, si
619
        mov     sp, 7C00h-10
620
        mov     bp, 7C00h
621
        push    dx
622
        push    cx
623
; call our function
624
        stc     ; unsupported function
625
        dec     ax
626
        jz      callback_readfile
627
        dec     ax
628
        jnz     callback_ret
629
; function 2: continue loading file
630
; can be called only after function 1 returned value bx=1 (only part of file was loaded)
631
; in: ds:di -> information structure
632
;       dw:dw   address
633
;       dw      limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100)
634
; out: bx=0 - ok, bx=1 - still only part of file was loaded, bx=3 - read error
635
; out: dx:ax = file size
636
        call    continue_load_file
637
        jmp     callback_ret_succ
638
callback_readfile:
639
; function 1: read file
640
; in: ds:di -> information structure
641
;       dw:dw   address
642
;       dw      limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100)
643
;       ASCIIZ  name
644
; out: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=2 - file not found, bx=3 - read error
645
; out: dx:ax = file size (0xFFFFFFFF if file was not found)
646
        call    load_file
647
callback_ret_succ:
648
        clc     ; function is supported
649
callback_ret:
650
; restore caller's stack
651
        pop     cx
652
        pop     ss
653
        mov     sp, cx
654
; return to caller
655
        retf
656
 
657
secondary_loader_info:
658
        dw      0, 0x1000
659
        dw      0x30000 / 0x1000
660
        db      'kernel.mnt',0
661
aKernelNotFound db      'Fatal error: cannot load the kernel',0
662
 
663
;if $ > 0x8200
664
;error 'total size of kordldr.f32 must not exceed 1024 bytes!'
665
;end if
666
 
667
;foldcache_clus dd      0,0,0,0,0,0,0,0 ; start with no folders in cache
668
;foldcache_mark dw      0
669
;               rw      7
670
;foldcache_size rw      8
671
foldcache_clus  rd      8
672
foldcache_mark  rw      8
673
foldcache_size  rw      8