~vdupras/duskos

ref: 18d3f30bc4df7070cbd1aa68cce850255a11fb40 duskos/fs/fs/boot.fs -rw-r--r-- 4.8 KiB
18d3f30bVirgil Dupras fs/boot: implement fat16close 2 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
\ boot filesystem implementation

\ This is a subset of a FAT16 implementation. It is designed to be embedded
\ right after "boot.fs" and provide the means to continue bootstrapping on.

\ Its goal is to provide fopen and fgetc, nothing more. The rest of the FAT16
\ implementation is in fs/fat16.

\ This unit has access to a very small set of words, that it, words implemented
\ by boot.fs as well as the "drive" protocol, which is implemented by a driver
\ that is inserted between boot.fs and this unit.

$18 const BPBSZ \ we only care about a part of the BPB
create bpb BPBSZ allot
0 drv@ drvbuf( bpb BPBSZ move

: BPB_BytsPerSec bpb $0b + w@ ;
: BPB_SecPerClus bpb $0d + c@ ;
: BPB_RsvdSecCnt bpb $0e + w@ ;
: BPB_NumFATs bpb $10 + c@ ;
: BPB_RootEntCnt bpb $11 + w@ ;
: BPB_TotSec16 bpb $13 + w@ ;
: BPB_FATSz16 bpb $16 + w@ ;
: RootDirSectors
  BPB_RootEntCnt 32 * BPB_BytsPerSec /mod ( r q ) swap if 1+ then ;
: FirstDataSector BPB_RsvdSecCnt BPB_NumFATs BPB_FATSz16 * + RootDirSectors + ;
: FirstSectorOfCluster ( n -- sec ) 1- 1- BPB_SecPerClus * FirstDataSector + ;
: FirstRootDirSecNum BPB_RsvdSecCnt BPB_NumFATs BPB_FATSz16 * + ;
: ClusterSize BPB_SecPerClus BPB_BytsPerSec * ;

\ read multiple sectors in buf
: readsectors ( sec u buf -- )
  A>r swap >r swap >A begin ( buf )
    A> over (drv@) A+ drvblksz + next ( buf ) drop r>A ;

create FAT16( BPB_BytsPerSec BPB_FATSz16 * allot
here value )FAT16
: readFAT BPB_RsvdSecCnt BPB_FATSz16 FAT16( readsectors ;

32 const DIRENTRYSZ
11 const FNAMESZ
: DIR_Name ( direntry -- sa sl ) FNAMESZ ;
: DIR_Cluster ( direntry -- cluster ) 26 + w@ ;
: DIR_FileSize ( direntry -- sz ) 28 + @ ;

\ A buffer where dir entries are copied before we search in them. It's big
\ enough to hold the root dir entries. This means that no directory in the FS
\ can have more than BPB_RootEntCnt entries.
create dirbuf( RootDirSectors BPB_BytsPerSec * allot
here const )dirbuf
create fnbuf FNAMESZ allot

: upcase ( c -- c ) dup 'a' - 26 < if $df and then ;
\ we assume a 8.3 name. Don't call this with an inadequate name.
: _tofnbuf ( fname -- )
  A>r >A Ac@+ >r fnbuf FNAMESZ SPC fill fnbuf begin ( a )
    Ac@+ dup '.' = if ( a c ) 2drop fnbuf 8 + else upcase swap c!+ then
    next drop r>A ;
\ Search in directory that is currently loaded in dirbuf.
\ Return address of directory entry, abort if not found.
: findindir ( fname -- direntry ) _tofnbuf
  dirbuf( begin ( a )
    dup )dirbuf < while ( a )
    fnbuf over DIR_Name []= not while ( a ) DIRENTRYSZ + repeat
    ( success ) else ( not found ) abort" file not found" then ( a ) ;

\ Make the current dir the root
: readroot FirstRootDirSecNum RootDirSectors dirbuf( readsectors ;

: EOC? ( cluster -- f ) $fff8 and $fff8 = ;

\ get cluster following this one in the FAT
: nextcluster ( cluster -- nextcluster ) << FAT16( + w@ ;

: readcluster ( cluster dst -- )
  over << BPB_BytsPerSec BPB_FATSz16 * >= if abort" cluster out of range" then
  swap FirstSectorOfCluster ( dst sec ) swap BPB_SecPerClus swap readsectors ;

\ Read specified "direntry" in dirbuf(
\ Errors out if it has more entries that BPB_RootEntCnt
: readdir ( direntry -- )
  DIR_Cluster ( cluster ) dirbuf( begin ( cluster buf )
    over EOC? not while
    2dup readcluster
    ClusterSize + swap nextcluster swap repeat ( cluster buf )
  2drop ;

\ File cursor
\ 2b first cluster 0=free cursor
\ 2b current cluster in buf
\ 4b cur pos (offset from beginning of file)
\ 4b file size
\ Xb current cluster X=ClusterSize
4 const FCURSORCNT \ maximum number of opened files
: FCursorSize ClusterSize 12 + ;
: FCUR_cluster0 ( fcur -- n ) w@ ;
: FCUR_cluster ( fcur -- n ) 2 + w@ ;
: FCUR_cluster! ( n fcur -- ) 2 + w! ;
: FCUR_pos ( fcur -- n ) 4 + @ ;
\ return pos and post-inc it
: FCUR_pos+ ( fcur -- n ) 4 + dup @ 1 rot +! ;
: FCUR_size ( fcur -- n ) 8 + @ ;
: FCUR_buf( ( fcur -- a ) 12 + ;

create fcursors( FCursorSize FCURSORCNT * allot0

: findfreecursor ( -- fcursor )
  FCURSORCNT >r fcursors( begin ( a )
    dup FCUR_cluster0 not if ( found! ) r~ exit then FCursorSize + next
  abort" out of file cursors!" ;

\ Open the specified "direntry" into one of the free cursors and return that
\ cursor.
: openfile ( direntry -- fcursor )
  findfreecursor >r
  dup DIR_Cluster ( dirent cluster ) dup r@ FCUR_buf( readcluster
  ( dirent cluster ) dup r@ w! r@ FCUR_cluster! ( dirent )
  0 r@ 4 + ! DIR_FileSize r@ 8 + ! ( ) r> ;

: fat16getc ( fcursor -- c-or-0 )
  dup FCUR_pos over FCUR_size = if drop 0 exit then
  dup FCUR_pos+ ClusterSize mod over FCUR_buf( + c@ ( fc c )
  over FCUR_pos ClusterSize mod not if ( fc c ) \ end of cluster, read next
    over FCUR_cluster nextcluster ( fc c cluster )
    dup EOC? if drop else
      dup 2 < if abort" cluster out of range" then
      rot 2dup FCUR_cluster! ( c cluster fc )
      tuck FCUR_buf( readcluster ( c fc ) swap then
  then ( fc c ) nip ;

: fat16close ( fcursor ) 0 swap w! ;