~vdupras/duskos

ref: 2e0d6f71ebc7994b028ddd77772c007a68deed34 duskos/fs/fs/boot.fs -rw-r--r-- 5.3 KiB
2e0d6f71Virgil Dupras fs/boot: add findpath 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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
\ 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
here const )fnbuf

: upcase ( c -- c ) dup 'a' - 26 < if $df and then ;
: fnbufclr fnbuf( FNAMESZ SPC fill ;

\ we assume a 8.3 name. Don't call this with an inadequate name.
: _tofnbuf ( fname -- )
  A>r >A Ac@+ >r fnbufclr fnbuf( begin ( a )
    Ac@+ dup '.' = if ( a c ) 2drop fnbuf( 8 + else upcase swap c!+ then
    next drop r>A ;

: _findindir ( -- direntry )
  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 ) ;

\ Search in directory that is currently loaded in dirbuf.
\ Return address of directory entry, abort if not found.
: findindir ( fname -- direntry ) _tofnbuf _findindir ;

\ 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 ;


: findpath ( path -- direntry )
  A>r fnbufclr fnbuf( >A c@+ >r readroot begin ( a )
    c@+ dup emit case
      '.' of = fnbuf( 8 + >A endof
      '/' of = _findindir readdir fnbufclr fnbuf( >A endof
      r@ upcase Ac!+ A> )fnbuf = if abort" filename too long!" then
    endcase
  next drop
  \ at the point, the correct subdir is read and the final filename is in fnbuf(
  _findindir r>A ;

\ 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! ;