Adventures in FAT32 with 65c02

Programming the 6502 microprocessor and its relatives in assembly and other languages.
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Adventures in FAT32 with 65c02

Post by BigDumbDinosaur »

barnacle wrote:
Seek past the end of a file?  Return an error message, set the pointer to the last byte.  Sparse files aren't as far as I know part of the FAT system and I have no intention of forcing them in.
In the not-too-distant past, I had done a dive on sparse files as part of my research into implementing my 816NIX filesystem.  As I recall, all the references were to POSIX or Windows NTFS filesystem mechanics...but not a single mention of FAT.  Given the manner in which FAT works, I can’t see how a seek beyond a file’s end could turn out well.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: Adventures in FAT32 with 65c02

Post by barnacle »

If you exceeded the file end but not the cluster, you'd read whatever is in the rest of the cluster; possibly the remains of an older but deleted file. If you moved into another cluster, you'd have to find where the cluster was, only to discover it wasn't...

Neil

edit: (that is: to calculate an fseek(), you'd need to follow the cluster chain and see if you get to the offset before you ran out of cluster links in the FAT. If you find a FAT record with $0FFFFF8 or greater, either you'd return an error, or you'd have to allocate a new cluster (as if you were writing sequentially), but in either case you'd be looking at unwritten (by you) areas of the disk. It seems to me there's no actual use case to try and seek past the end of the file, so better to return an error).
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: Adventures in FAT32 with 65c02

Post by barnacle »

Ooh, decisions, decisions... of the five DIP 28C256 eeproms in my bits box, I find that (a) two have only 27 legs, (b) one doesn't program, and (c) one appears to work but gets suspiciously hot... time to go shopping.

In which I find they are ten euros each at Digikey, ten euros each at Mouser, not available at Farnell, twenty-one euros at Conrad, sixteen euros at Reichelt... all add postage to that unless I spend sixty bucks or so (and VAT on top of that).

Or about six euros from dubious suppliers in China. These I have came from China, sold as new but actually board pulls, locked, and containing data... no doubt the next batch will be the same.

Neil
GlennSmith
Posts: 162
Joined: 26 Dec 2002
Location: Occitanie, France

Re: Adventures in FAT32 with 65c02

Post by GlennSmith »

barnacle wrote:
In which I find they are ten euros each at Digikey, ten euros each at Mouser, not available at Farnell, twenty-one euros at Conrad, sixteen euros at Reichelt... all add postage to that unless I spend sixty bucks or so (and VAT on top of that).
Since Brexit Mouser and Farnell have become VERY expensive in continental Europe. I recently wanted to re-stock on the 40-pin DIP sockets for our favorite uProc. 6Euros (before VAT) at Mouser! I spend more time hunting for 'reasonably priced' components for customer repairs than the total time spent diagnosing and repairing!! And now with the Russia embargo (and the Chinese valve/tube factory that burnt down), It's become near impossible to find some of the less-known valves at affordable prices. I used to really enjoy my work...
Sorry, gripe over. :lol:
Glenn-in-France
jgharston
Posts: 181
Joined: 22 Feb 2004

Re: Adventures in FAT32 with 65c02

Post by jgharston »

BigDumbDinosaur wrote:
The JSR - RTS combination costs 4 bytes and 12 cycles.  In-lining the CF_WAIT function via a macro could save quite a few clock cycles during a block read/write, at the minor expense of 8 extra bytes per call.
Checking my IDE driver all the wait-ready code is inlined, and once there is data ready you just transfer all the data all in one go.

Code: Select all

 6480 LDA IDEstatus:BMI P%-3                   :\ Wait until IDE not busy
 6490 LDA #64:STA IDEcount:STA IDEsector       :\ 64 sectors per track
 6500 LDA #15:JSR IDESetHead                   :\ 16 heads
 6510 LDA #&91:STA IDEcommand                  :\ Set geometry
etc.
 6880 AND #&10:ORA #&20:STA IDEcommand         :\ Create IDE command byte
 6890 .IDEWait
 6900 LDA IDEstatus:BMI P%-3                   :\ Wait until IDE not busy
 6910 TAY:AND #&21:BNE IDEError                :\ Error occured
 6990 TYA:AND #8:BEQ IDEWait:LDY #0            :\ Wait for DRQ
 7000 BCC IDEIORead                            :\ Read or Write
 7010 :
 7020 .IDEIOWrite                              :\ Write 256 byte from I/O
 7030 LDA (addr),Y:STA IDEdata:INY:BNE IDEIOWrite:BEQ IDENext
 7040 .IDEIORead                               :\ Read 256 to I/O
 7050 LDA IDEdata:STA (addr),Y:INY:BNE IDEIORead:BEQ IDENext
 7130 :
 7140 .IDENext
 7150 LDA IDEstatus:AND #&21:BNE IDEError      :\ Error occured
electricdawn
Posts: 34
Joined: 23 Nov 2025

Re: Adventures in FAT32 with 65c02

Post by electricdawn »

This will be my go-to thread for when I start trying to implement my own mass-storage solution. Thanks, folks!
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: Adventures in FAT32 with 65c02

Post by barnacle »

Yay! You are in a maze of twisty little passages, all alike... :mrgreen:

Neil
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Adventures in FAT32 with 65c02

Post by BigDumbDinosaur »

barnacle wrote:
Yay!  You are in a maze of twisty little passages, all alike... :mrgreen:
Do you, at times, feel as though you taken on a vague resemblance to Rattus norvegicus as you stumble about in the FAT32 maze?  :lol:
x86?  We ain't got no x86.  We don't NEED no stinking x86!
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: Adventures in FAT32 with 65c02

Post by barnacle »

I lack the necessary yellow quarantine flag.

Neil
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: Adventures in FAT32 with 65c02

Post by barnacle »

Getting closer to the treasure - two steps forward, one step back

( I should point out that the code I've been writing has undergone major changes, mostly factoring, isolating 32 bit arithmetic and helper, and the like, in the interest of code size. And sanity. I'm not going to go through all the gory details here, but I will post a finished listing at the end of this series. Which is looking as far away as it was when I started!)

Our hero has been cheerfully following the yellow bricks in the road, but he's run into a problem. He's got to the end of a cluster before he's reached the end of the directory. There are more files but he doesn't know anything about them. Somehow, in fs_find_next he has to advance to the next cluster.

The problem is, it almost certainly isn't the _next_ cluster. By the time the first directory cluster is filled up, there are potentially dozens of files written before the new directory cluster is required, each using at least one cluster.

We could, potentially, log every cluster of interest as we go along, but that sounds a painful way to do it (though it might be worth it). Instead, we'll use a generic routine to get the next cluster. Other routines will find this handy.

The only thing we know is the sector we're currently in. That's in LBA when we need it, filled in by each iteration through fs_get_next. In there, we're keeping track of how many records we've looked at, grabbing a new sector whenever we get to the end of one (the sectors in a cluster are always sequential). When we get to the eighth sector, we need a new cluster... which is where we left things last time.

Here's the bit that's doing the work:

Code: Select all

fs_find_next:
	; else get the next record
	inc fs_dir_number		; have we reached record $10?
	lda fs_dir_number
	cmp #$10
	bne fs_ff_01			; try the next entry
		inc fs_dir_sector		; otherwise we need the next sector
		lda fs_dir_sector	
		cmp #$8					; have we run out of cluster?
		bne fs_fn_12
			jsr fs_next_cluster		; if so, we need more cluster in lba
			stz fs_dir_number		; clear the record and sector count
			stz fs_dir_sector
			bra fs_fn_15			; and round we go
fs_fn_12:
		; increment the lba
which it does by handing the problem to fs_next_cluster...

Code: Select all

	bss
fat_record:		ds 2
	code
fs_next_cluster:					; the current sector is in lba	
	jsr sec_to_cluster				; so find out the sector number
	jsr clus_to_fat_rec				; convert it to a FAT record
									; remembering the offset by two!
	; so now we have the FAT sector and an index into transient pointing
	; to the FAT record we need, so get the sector on board
	jsr cf_set_lba
	jsr cf_read
	; now we can read the FAT record from Transient
	ldy #0
fs_nc_1:
	lda (fat_record),y
	sta lba,y
	iny
	cpy #4
	bne fs_nc_1
	; we're on the home straight, all we have to do is convert this
	; record number to a sector number
	jsr clus_to_1st_sec				; leaving the sector number in lba
	rts
That takes a deep breath and gets on with things. First it asks sec_to_cluster to tell it which cluster it's in. That it does by applying this equation:

Code: Select all

cluster = (sector - cluster_begin_lba)  / sectors_per_cluster
(and in my lazy code, it assumes that sectors_per_cluster is always 8 ).

Now it knows the cluster, it can find its entry in the FAT. It needs to get the correct sector into transient, and an offset into the transient area to see the right data. clus_to_fat_record is moderately complex:

Code: Select all

clus_to_fat_rec:
	LYA two
	jsr u32_add				; lba = lba + 2 = record number, but that
							; could be more than fits in a sector
	; there are 128 records per sector (four bytes each)
	stz fat_record+1
	lda lba
	and #$7f				; extract lower seven bits
	; multiply by four
	asl a					; this doesn't affect the high byte
	asl a					; but this does
	sta fat_record
	rol fat_record+1		
	clc						; now add transient to it
	lda fat_record
	adc # lo transient
	sta fat_record
	lda fat_record+1
	adc # hi transient
	sta fat_record+1		; so we're pointing to the right place in
							; transient, even though we haven't loaded
							; the sector
	; now sort out the sector where the record is
	; sector = (lba >> 7) + fat_begin_lba
	ldy #7
fs_sf_1:
	lsr lba+3
	ror lba+2
	ror lba+1
	ror lba+0
	dey
	bne fs_sf_1				; not the speediest, but small
	LYA fat_begin_lba
	jsr u32_add
	rts	
We start by adding two - remember the offset between the clusters and their pointers in the FAT, and then, as we have four bytes per entry, multiply by four. Then we add the address of transient to generate a pointer to the record (though it's not valid yet; we haven't loaded the sector).

To decide the correct sector to load, we divide the current contents of the LBA (the cluster plus 2) by 128, and add fat_begin_lba. Then we can return with LBA holding a sector number and fat_record containing an offset into the FAT.

When we return, we can load the sector directly, read the value of the FAT record, and from there use clus_to_1st_sec to convert our cluster number to the first sector:

Code: Select all

sector = ((record - 2) * sectors_per_cluster) + cluster_begin_lba
And when we finally find our way back fs_get_next, we already have the directory sector's next clusters' first sector loaded, and it can proceed as normal.

And here's what we get. I haven't done anything significant about the actual data in the directory yet; that will be next time. But this output shows the sector number between each block of entries; you can see where it suddenly leaps from 00000FC7 to 00046650; that's how far away the second cluster of the root directory is.

Code: Select all

00000800                                                                        
00000820                                                                        
00000FC0                                                                        
$0220  45 43 55 20 20 20 20 20 43 20 20 20 00 8A 21 60 ECU     C   ..!`         
$0240  E5 4E 54 49 54 4C 7E 31 20 20 20 10 00 3E 08 60 .NTITL~1   ..>.`         
$0280  54 45 53 54 20 20 20 20 20 20 20 10 00 3E 08 60 TEST       ..>.`         
$02C0  45 45 50 52 4F 4D 20 20 43 20 20 20 00 96 21 60 EEPROM  C   ..!`         
$0300  47 52 41 50 48 49 43 53 43 20 20 20 00 A1 21 60 GRAPHICSC   ..!`         
$0340  49 31 38 4E 20 20 20 20 43 20 20 20 00 AC 21 60 I18N    C   ..!`         
$0380  4D 41 49 4E 20 20 20 20 43 20 20 20 00 B8 21 60 MAIN    C   ..!`         
$03C0  53 45 52 49 41 4C 20 20 43 20 20 20 00 C3 21 60 SERIAL  C   ..!`         
00000FC1                                                                        
$0200  53 53 44 31 33 30 39 20 43 20 20 20 00 07 22 60 SSD1309 C   .."`         
$0240  53 59 53 4D 45 4D 20 20 43 20 20 20 00 12 22 60 SYSMEM  C   .."`         
$02A0  46 41 54 5F 46 49 7E 31 50 44 46 20 00 7B 1D 77 FAT_FI~1PDF .{.w         
$0300  53 4C 49 44 45 53 7E 31 50 44 46 20 00 87 1D 77 SLIDES~1PDF ...w         
$0380  55 4E 49 54 31 30 7E 31 50 44 46 20 00 9C 1D 77 UNIT10~1PDF ...w         
$03E0  53 4C 49 44 45 53 7E 32 50 44 46 20 00 AD 1D 77 SLIDES~2PDF ...w         
00000FC2                                                                        
$0260  55 4E 49 54 31 30 7E 32 50 44 46 20 00 C1 1D 77 UNIT10~2PDF ...w         
$02E0  53 52 41 4D 5F 42 7E 31 50 44 46 20 00 0A 20 77 SRAM_B~1PDF .. w         
$0340  43 46 5F 34 34 5F 7E 31 50 44 46 20 00 22 20 77 CF_44_~1PDF ." w         
$03A0  43 46 5F 34 34 5F 7E 32 50 44 46 20 00 2E 20 77 CF_44_~2PDF .. w         
00000FC3                                                                        
$0200  36 35 30 32 5F 43 7E 31 50 44 46 20 00 3A 20 77 6502_C~1PDF .: w         
$0260  43 46 5F 42 4F 41 7E 31 50 44 46 20 00 4E 20 77 CF_BOA~1PDF .N w         
$02E0  36 35 30 32 41 4E 7E 31 50 44 46 20 00 5E 20 77 6502AN~1PDF .^ w         
$0340  4E 45 4F 4E 36 35 7E 31 41 53 4D 20 00 6A 20 77 NEON65~1ASM .j w         
$0380  49 44 45 20 20 20 20 20 50 44 46 20 00 74 20 77 IDE     PDF .t w         
$03E0  46 41 54 33 32 46 7E 31 50 44 46 20 00 82 20 77 FAT32F~1PDF .. w         
00000FC4                                                                        
$0220  46 20 20 20 20 20 20 20 50 44 46 20 00 95 20 77 F       PDF .. w         
$0280  48 45 58 43 4F 4E 7E 31 50 44 46 20 00 AA 20 77 HEXCON~1PDF .. w         
$02C0  31 20 20 20 20 20 20 20 50 44 46 20 00 C0 20 77 1       PDF .. w         
$0320  36 35 30 32 5F 43 7E 32 50 44 46 20 00 04 21 77 6502_C~2PDF ..!w         
$0380  5A 38 30 2D 44 4F 7E 31 50 44 46 20 00 18 21 77 Z80-DO~1PDF ..!w         
$03C0  57 36 35 43 32 32 20 20 50 44 46 20 00 27 21 77 W65C22  PDF .'!w         
00000FC5                                                                        
$0200  49 53 36 32 43 32 35 36 50 44 46 20 00 3E 21 77 IS62C256PDF .>!w         
$0260  53 59 4E 45 52 54 7E 31 50 44 46 20 00 4A 21 77 SYNERT~1PDF .J!w         
$02A0  53 4E 37 34 48 43 7E 31 50 44 46 20 00 7D 21 77 SN74HC~1PDF .}!w         
$0320  56 49 56 41 4C 44 7E 31 44 45 42 20 00 B5 21 77 VIVALD~1DEB ..!w         
$0360  38 31 34 30 34 39 20 20 50 44 46 20 00 92 2B 77 814049  PDF ..+w         
$03C0  41 50 58 38 32 33 7E 31 50 44 46 20 00 A8 2B 77 APX823~1PDF ..+w         
00000FC6                                                                        
$0200  42 55 34 38 58 58 7E 31 50 44 46 20 00 C4 2B 77 BU48XX~1PDF ..+w         
$0260  44 41 54 41 53 48 7E 31 50 44 46 20 00 19 2C 77 DATASH~1PDF ..,w         
$02E0  44 41 54 41 53 48 7E 32 50 44 46 20 00 3D 2C 77 DATASH~2PDF .=,w         
$0360  36 35 30 32 2D 4B 7E 31 5A 49 50 20 00 5B 2C 77 6502-K~1ZIP .[,w         
$03C0  31 30 31 44 53 45 7E 31 50 44 46 20 00 67 2C 77 101DSE~1PDF .g,w         
00000FC7                                                                        
$0220  31 30 31 44 2D 54 7E 31 50 44 46 20 00 78 2C 77 101D-T~1PDF .x,w         
$0280  46 43 49 2D 31 30 7E 31 50 44 46 20 00 88 2C 77 FCI-10~1PDF ..,w         
$02E0  31 30 31 44 53 45 7E 32 50 44 46 20 00 96 2C 77 101DSE~2PDF ..,w         
$0360  4E 45 57 53 4C 45 7E 31 50 44 46 20 00 A6 2C 77 NEWSLE~1PDF ..,w         
$03E0  5A 47 36 35 30 32 7E 31 50 44 46 20 00 0C 2D 77 ZG6502~1PDF ..-w         
00046650                                                                        
$0260  48 49 52 4F 53 45 7E 31 50 44 46 20 00 18 2D 77 HIROSE~1PDF ..-w         
$02C0  36 39 33 31 32 30 7E 31 50 44 46 20 00 2C 2D 77 693120~1PDF .,-w         
$0320  4A 41 45 49 53 30 7E 31 50 44 46 20 00 49 2D 77 JAEIS0~1PDF .I-w         
$0360  4D 49 32 30 2D 32 7E 31 50 44 46 20 00 59 2D 77 MI20-2~1PDF .Y-w         
$03A0  54 53 32 30 38 33 20 20 50 44 46 20 00 6A 2D 77 TS2083  PDF .j-w         
$03E0  43 32 30 34 31 35 7E 31 50 44 46 20 00 92 2D 77 C20415~1PDF ..-w         
00046651                                                                        
$0240  31 30 31 44 2D 54 7E 32 50 44 46 20 00 9F 2D 77 101D-T~2PDF ..-w         
$0280  43 46 53 20 20 20 20 20 50 44 46 20 00 AF 2D 77 CFS     PDF ..-w         
$+
We are close to the treasure, but before that, next time I'll take a look at the directory entry in more detail.

Neil
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: Adventures in FAT32 with 65c02

Post by barnacle »

As an aside, it occurs to me that there is no way to use the FAT to move backwards in a file (SEEK_SET): all the links are forwards only.

Your only option is to start again at the beginning of the chain, if you need to move to a cluster before the one you're in, or to keep track of how you got there.

Neil
jgharston
Posts: 181
Joined: 22 Feb 2004

Re: Adventures in FAT32 with 65c02

Post by jgharston »

barnacle wrote:
We are close to the treasure, but before that, next time I'll take a look at the directory entry in more detail.
Summary overview here if it's any use.
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: Adventures in FAT32 with 65c02

Post by BigDumbDinosaur »

barnacle wrote:
As an aside, it occurs to me that there is no way to use the FAT to move backwards in a file (SEEK_SET): all the links are forwards only.

Your only option is to start again at the beginning of the chain, if you need to move to a cluster before the one you're in, or to keep track of how you got there.
Welp, that’s going to make your FSEEK() function an interesting bit of coding, eh?  :twisted:
x86?  We ain't got no x86.  We don't NEED no stinking x86!
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: Adventures in FAT32 with 65c02

Post by barnacle »

The thought had occurred to me...

Neil
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: Adventures in FAT32 with 65c02

Post by barnacle »

Dissecting the directory
Every directory entry is 32 bytes long. Not all the entries are strictly required, though they're nice to have. In particular, those relating to time are going to be tricky, since my system has no clock. If you do happen to have that luxury, then by all means fill 'em in, otherwise they're zero. All your files will be dated January 1st 1980.

Those of us old enough will remember 8086 machines that didn't have real time clocks; the first thing you did after turning them on was to set the date and time.

Knowing the date and time a file was created, written, and accessed is handy for a number of functions: perhaps you need to know if a file was created after the last build of a compilation, to avoid recompiling lots of files unnecessarily? Or you just want to list files in terms of age - oldest to newest, or whatever.

But without that time and date information from my system, I can't do it. I will probably get around to building some sort of interrupt driven real time clock, but it's not necessary just to put a file on the drive or to read it. And we're nowhere near writing files yet.

Here are the fields to be found within a directory record (in decimal offsets). Each file has one, and only one, record. I really wish that there was a table format available...

Code: Select all

------------------------------------------------------------------------
| Offset | Name       | Function                                       |
|--------|------------|------------------------------------------------|
| 0      | DIR_NAME   | The 8.3 name of the file, directory, or volume |
|        |            | The name is upper case and may not start with a|
|        |            | space. The period is implicit between the      |
|        |            | eight and ninth character.                     |
|        |            | Note: only one volume reference may exist in a |
|        |            | file system, and it must be in the root dir, if|
|        |            | present.                                       |
|        |            | Note: a file name with e.g. lower case may be  |
|        |            | created, but will not be readable on MSDOS.    |
|--------|------------|------------------------------------------------|
| 11     | DIR_ATTR   | The attribute byte. When bit is set:           |
|        |            | 0: attr_ro - read only                         |
|        |            | 1: attr_hid - file will be hidden on MSDOS     |
|        |            | 2: attr_sys - system file                      |
|        |            | 3: attr_vol - volume label                     |
|        |            | 4: attr_dir - this file is a directory         |
|        |            | 5: attr_arc - archive. Bit set when file is    |
|        |            |    create or written to, cleared by when       |
|        |            |    backup occurs                               |
|        |            | 6: not used; zero                              |
|        |            | 7: not used; zero                              |
|        |            | 0+1+2+3 all set: long filename fragment        |
|--------|------------|------------------------------------------------|
| 12     | reserved   | One byte: reserved for NT OS; should be zero   |
|--------|------------|------------------------------------------------|
| 13     | crt_tenth  | One byte: The tenths of a second count of the  |
|        |            | file creation time, 0-199                      |
|--------|------------|------------------------------------------------|
| 14     | crt_time   | Two bytes: the time the file was created       |
|--------|------------|------------------------------------------------|
| 16     | crt_date   | Two bytes: the date the file was created       |
|--------|------------|------------------------------------------------|
| 18     | acc_date   | Two bytes: the date the file was last accessed |
|--------|------------|------------------------------------------------|
| 20     | DIR_FCH    | Two bytes: the high word of the file's first   |
|        |            | cluster                                        |
|--------|------------|------------------------------------------------|
| 22     | DIR_TIME   | Two bytes: the time at which the file was last |
|        |            | written, or was created                        |
|--------|------------|------------------------------------------------|
| 24     | DIR_DATE   | Two bytes: the date at which the file was last |
|        |            | written, or was created                        |
|--------|------------|------------------------------------------------|
| 26     | DIR_FCL    | Two bytes: the low word of the file's first    |
|        |            | cluster                                        |
|--------|------------|------------------------------------------------|
| 28     | DIR_SIZE   | Four bytes: the length of the file in bytes    |
------------------------------------------------------------------------
 
The entries with DIR_XXXX format names are those about which we're concerned. Starting at the beginning:
  • DIR_NAME - original FAT names were in 8.3 format, with the latter three letters (the 'extension') used as a file type marker: .com, .exe, .xls, .asm, etc. I am ignoring long file names; a file can be identified with the 8.3 name only, and an 8.3 file with no long file name information is recognised by MS systems (and Linux).
    The name stored at DIR_NAME is upper case only, filled with blanks, and ignores the period.
    So, for example, "main.c" would be represented as "MAIN----C--"; "basic.asm" would be "BASIC---ASM" (using '-' to represent spaces).
    There are special values for the first byte of the name: if it is $E5, the file has been deleted. If it is $05, then the filename/OS are using the Kanji codepages and it should be read as $E5. If it is $00, then there are no more entries in the directory.
  • DIR_ATTR - this bit describes what the file name represents. The values for each bit are shown in the table.
    • attr_ro - the file may not be written to or deleted
    • attr_hi - the file should be hidden (or at least, will not be listed on an MS system unless instructed to show hidden files. Note that hidden files in Linux are traditionally indicated by a leading period in the filename, which isn't legal on FAT systems.)
    • attr_sys - on MSDOS, certain important system files were expected to held in specific places in the file structure or on the disk. This bit is an instruction to defragmention programs _not_ to move the file while it shuffled other files around.
    • attr_vol - a bit indicating the volume name. There may be no more than one volume name on a partition, and if it exists (it's optional) it must be in the root directory.
    • attr_dir - this is the name of a sub-directory
    • attr_arc - the archive bit. When the file is created, written, or added to, this bit is set so that any archiving or backup program can tell which files have changed since it was last run
  • crt_tenth - Microsoft's specification says: Millisecond stamp at file creation time. This field actually contains a count of tenths of a second. The granularity of the seconds part of DIR_CrtTime is 2 seconds so this field is a count of tenths of a second and its valid value range is 0-199 inclusive. which makes no sense at all. I _think_ it contains hundredths of a second. I ignore it.
  • crt_time; crt_date; acct_date - the date and time the file was created, and the date it was last accessed.
  • DIR_FCH and DIR_FCL - the high and low words giving the cluster on the disc where the file begins
  • DIR_SIZE - the file's size in bytes. For reasons unknown, it's interpreted by MS as a signed 32 bit variable, so the maximum file size is 2GB.
Date and Time
The date and time fields encode their data into sixteen bits. For dates, bits 0-4 are the day of the month, 1-31; bits 5-8 are the month, 1-12; and bits 9-15 the year count (0 to 127), to which is added the MS epoch of 1980. Which means you're going to have issues after 2107...

Time has a problem; to get HMS it needs seventeen bits, so it reduces the resolution of the seconds count: bits 15-11 are the hours 0-23; bits 10-5 are the minutes 0-59, and bits 4-0 are two-second counts.

Layers on layers...
At the moment, all my code is in one huge blob. That's why you haven't seem most of it :mrgreen:
But it has different meanings and hierarchy in the system...

At the bottom, what we might call the bios: the code that initialises the serial port and handles data in and out. On a system with video generation on board and a keyboard permanently attached, that would be handled at this level, too.

Next, the code which handles the basic access to the CF card. This might be considered at the same level as above; it's basic input/output. Routines working at this level are prefixed cf_

The level above this is where we are working now. This is code which understands the file system and is able to move around, as required, between the various sectors, FATs, and clusters of the disc to isolate specific data on the disc. This code has prefix fs_ for file system.

The next level is where we will install familiar file operations. What happens _to_ data on the disc is a function of the operating system, so when we see a software to list a directory, (e.g. dir) or to open a file or read a byte from it (fopen, fread) it will be considered operating system and prefixed os_ for the supporting functions. As yet, there is nothing in this area, but it should be clear how the simpler functions might be implemented.

In the next episode, I'll look at a simple directory listing program.

Neil
Post Reply