ROM file system source. By Lee Davison.

The source.


; constants for the file system

none		=	$00		; no flags

; flags:

deleted		=	$80		; file is deleted but not removed	
directory	=	$40		; file is really a directory

Self explanitory really, the deleted flag is not used yet.


; variables for the file system

; zero page

filesys_l	.ds	1		; file system pointer low byte
filesys_h	.ds	1		; file system pointer high byte

lensys_l	.ds	1		; file system length low byte
lensys_h	.ds	1		; file system length high byte

file_l		.ds	1		; current file pointer low byte
file_h		.ds	1		; current file pointer high byte

length_l	.ds	1		; current file length low byte
length_h	.ds	1		; current file length high byte

fileflags	.ds	1		; current file's flag byte

findname_l	.ds	1		; filename to find pointer low byte
findname_h	.ds	1		; filename to find pointer high byte	

filesys is the pointer returned by the find routine, it is only valid if a file was found, i.e. Cb is = 1.

lensys is the length of the returned file and like filesys is only valid if a file was found.

file and length are used internally by the find routine.

fileflags is used internally by the find routine.

findname points to the start of the null yerminated path/name of the file to find. Like the HTTP "GET" it must start with "/" and "/" is used to separate directories so "/",$00 is valid as is "/index.html",$00 but "o.html",$00 isn't.


; file format is ..

f_start		.ds	1		; flag byte
					; bit	function
					; ---	--------
					;  7	deleted
					;  6	directory
					; 5-0	unassigned
f_length	.ds	2		; file payload length word
f_name		.ds	1		; filename and null terminator byte
;		.ds	length		; here to here+length-1 is the payload	

This is used to define the header, f_name is used realtive to f_start and will remain correct even if other parameters, such as a parent pointer for reverse tree searches, are added to the header.


; find file, searches the file system for the filename pointed to by the find
; name pointer. returns with Cb = 1 and the pointer to the file payload in
; filesys, if found.
;
; file name to search for should be "/[path[path]][name]" where path is the
; directory name followed by a "/". the path may be many deep and both the
; path and the name may be null. if the name is null then the default_file
; name will be searched for the path given.

LAB_find
	LDA	root+1			; get root payload length low byte
	STA	lensys_l		; save payload length low byte
	LDA	root+2			; get root payload length high byte
	STA	lensys_h		; save payload length high byte

	LDA	#<root_b		; get root body pointer low byte
	STA	filesys_l		; set file system pointer low byte
	LDA	#>root_b		; get body pointer high byte
	STA	filesys_h		; set file system pointer high byte
	LDY	#$00			; clear index
	LDA	(findname_l),Y		; get the first byte of the find name
	CMP	#'/'			; compare with separator
	BNE	LAB_exit_n_found	; exit if not "/" at start

	JSR	LAB_directory		; search the root for the file
	BIT	fileflags		; get the flags for this file
	BVC	LAB_exit_find		; exit if it's a file

	JSR	LAB_find_default	; a directory so find the default file	
LAB_exit_find
	RTS

The routine above returns either a found file pointer and it's length or flags not found. If the match turns out to be a directory, e.g. "/images", then the search routine is re-entered at the find default file entry so the file searched becomes effectively "/images/index.html"


; flag file found and exit

LAB_exit_found
	BIT	fileflags		; test the flags, set Vb for a directory
	SEC				; flag found
	RTS


; flag file not found and exit

LAB_exit_n_foun
	CLV				; flag not a directory
	CLC				; flag not found
	RTS


; increment filesys to the next file pointer in the directory

LAB_nextfile
	CLC				; clear carry for add
	LDA	filesys_l		; get filesys low byte
	ADC	#$02			; increment to next pointer
	STA	filesys_l		; save filesys low byte
	BCC	ni_inc_h		; branch if no rollover

	INC	filesys_h		; else increment filesys high byte
ni_inc_h
	SEC				; set carry for subtract
	LDA	lensys_l		; get remaining length low byte
	SBC	#$02			; increment to next pointer
	STA	lensys_l		; save remaining length low byte
	BCS	LAB_comparefile		; branch if no rollunder

	DEC	lensys_h		; decerment remaining length high byte

LAB_comparefile
	LDA	lensys_l		; get remaining length low byte
	ORA	lensys_h		; OR remaining length high byte
	BEQ	LAB_exit_n_found	; exit if no more directory entries

	LDY	#$00			; clear index
	LDA	(filesys_l),Y		; get file pointer low byte
	STA	file_l			; save this file pointer low byte
	INY				; increment index
	LDA	(filesys_l),Y		; get file pointer high byte
	STA	file_h			; save this file pointer high byte

	DEY				; clear index
	LDA	(file_l),Y		; get the file's flags
	STA	fileflags		; save the file's flag byte
	INY				; point to payload length low byte
	LDA	(file_l),Y		; get file's payload length low byte
	STA	length_l		; save file's length low byte
	INY				; point to payload length high byte
	LDA	(file_l),Y		; get file's payload length high byte
	STA	length_h		; save file's length high byte

	CLC				; clear carry for add
	LDA	file_l			; get this file pointer low byte
	ADC	#f_name-f_start		; add offset to the file name
	STA	file_l			; save this file pointer low byte
	BCC	nf_inc_h		; branch if no rollover

	INC	file_h			; increment this file pointer high byte
nf_inc_h
	LDY	#$FF			; set so first increment clears index

; compare this file's name, pointed to by file, with the find file
; name pointed to by findname. exits with Y indexed to the next byte
; in the name if the whole name matched.

LAB_comparename
	INY				; increment index
	LDA	(file_l),Y		; get next byte of name to test
	BEQ	LAB_cnameexit		; exit if end of name (match)

	EOR	(findname_l),Y		; compare with next byte of name to find
	BEQ	LAB_comparename		; loop if character match

	BNE	LAB_nextfile		; branch if not this file

LAB_cnameexit
	LDA	(findname_l),Y		; get next byte of name to find
	BEQ	LAB_end_find		; branch if end of name to find

	CMP	#'/'			; compare with separator
	BNE	LAB_nextfile		; branch if not end of name to find

LAB_end_find
	PHA				; save next byte of name to find

					; name matched so update lensys
	LDA	length_l		; get length low byte
	STA	lensys_l		; save length low byte
	LDA	length_h		; get length high byte
	STA	lensys_h		; save length high byte

					; name matched so update filesys
	SEC				; set carry for add +1
	TYA				; copy offset
	ADC	file_l			; add this file pointer low byte
	STA	filesys_l		; save as file system pointer low byte
	LDA	file_h			; get this file pointer high byte
	ADC	#$00			; add in the carry
	STA	filesys_h		; save as file system pointer high byte

	PLA				; restore next byte of name to find
	BEQ	LAB_exit_found		; branch if end of name to find

	BIT	fileflags		; else test the flags byte
	BVC	LAB_exit_n_found	; branch if not a directory


; searches the directory pointed to by the file system pointer for the
; filename pointed to by the find name pointer. the routine is entered
; with filesys pointing to the first directory byte after the file header
; and lensys holding the remaining directory size

LAB_directory
	SEC				; set carry for add +1
	TYA				; copy offset
	LDY	#$00			; clear index
	ADC	findname_l		; add offset to findname low byte
	STA	findname_l		; save findname low byte
	TYA				; set $00 for add carry
	ADC	findname_h		; add findname high byte
	STA	findname_h		; save findname high byte

	LDA	(findname_l),Y		; get next byte of name to find
	BNE	LAB_comparefile		; go compare file if not null filename

LAB_find_default
	LDA	#<default_file		; get default filename pointer low byte
	STA	findname_l		; set pointer of name to find low byte
	LDA	#>default_file		; get default filename pointer high byte
	STA	findname_h		; set pointer of name to find high byte
	BNE	LAB_comparefile		; go compare file (branch always)

The routine above is the main file tree search routine. The path/name to find is compared byte at a time with each filename in turn until a file is found that is a complete match to it's end. Once there the find string is tested and if it's at a separator, "/", a directory change is made (assuming the found file was a directory), the search then continues.

If the find string ends in "/" then the pointer is changed to point to the default file name "index.html" and this file is searched for in the target directory. So "/" as a find string will search for, and return, the pointer and size for "/index.html", where as "/images/" as a find string will search for, and fail to find, "/images/index.html" as there is no index.html file in the images directory.


default_file
	.byte	"index.html",$00	; default filename for "/xxx/" finds	

Finally the test filesystem files. There is one additional, zero payload length, file "index" which is to test that matches with partial names don't trigger false matches.


; this is the test data for the filesystem. it is made up of the HTML
; pages and other files for the Vic 20 mini web server plus a dummy
; file to test the file match.

; structure for this trial is ..

; root+--errors+--404			; error 404, really an html file
;	|
;	+--images+--e.gif		; small .gif image
;	|
;	+--b.html			; button page
;	+--index			; dummy file for test
;	+--index.html			; default page
;	+--o.html			; other page


; *********************************************************************		
; the root file is always a directory

root
	.byte	directory		; flags
	.word	root_end-root_b		; file payload length
	.byte	$00			; filename, null in this case
root_b
	.word	errors			; errors directory
	.word	images			; images directory
					; done directories, now do files
	.word	b_html			; the button page
	.word	index_html		; default web page
	.word	o_html			; other web page
root_end


; *********************************************************************
; dummy file for test

index
	.byte	none			; flags
	.word	$0000			; file payload length
	.byte	"index",$00		; filename, null terminator

; *********************************************************************
; default web page, served for "GET / "

index_html
	.byte	none			; flags
	.word	index_end-index_html_b	; file payload length
	.byte	"index.html",$00	; filename, null terminator
					; rest is file payload
index_html_b
	.byte	"HTTP/1.0 200 OK",$0D,$0A,"Content-Type: text/html"
	.byte	$0D,$0A,$0D,$0A
	.byte	"<HTML><HEAD><TITLE>Vic 20 WEB Server</TITLE>"
	.byte	"</HEAD><BODY BGCOLOR=#CC6502 TEXT=WHITE>"
	.byte	"<TABLE ALIGN=CENTER WIDTH=80% HEIGHT=90%>"
	.byte	"<TR><TD VALIGN=CENTER>"
	.byte	"<FONT SIZE=+1>Vic 20 WEB Server </FONT>"
	.byte	"- <A HREF=b.html>The button page</A>"
	.byte	" - <A HREF=o.html>The other page</A>"
	.byte	"<P><FONT SIZE=+4><B>Welcome</B></FONT>"
	.byte	"<FONT FACE=Courier> - to the default page</FONT>"
	.byte	"<P>"
	.byte	"<A HREF=http://forum.6502.org/viewtopic.php?f=5&t=3024>&nbsp;e-mail "
	.byte	"me <IMG SRC=images/e.gif BORDER=0 ALT=e-mail></A>"
	.byte	"</TD></TR></TABLE></BODY></HTML>"
index_end
 
; *********************************************************************
; other web page, served for "GET /o.html"

o_html
	.byte	none			; flags
	.word	o_html_end-o_html_b	; file payload length
	.byte	"o.html",$00		; filename, null terminator
					; rest is file payload
o_html_b
	.byte	"HTTP/1.0 200 OK",$0D,$0A,"Content-Type: text/html"
	.byte	$0D,$0A,$0D,$0A
	.byte	"<HTML><HEAD><TITLE>Vic 20 WEB Server</TITLE>"
	.byte	"</HEAD><BODY BGCOLOR=#65CC02 TEXT=WHITE>"
	.byte	"<TABLE ALIGN=CENTER WIDTH=80% HEIGHT=90%>"
	.byte	"<TR><TD VALIGN=CENTER>"
	.byte	"<FONT SIZE=+1>Vic 20 WEB Server </FONT>"
	.byte	"- <A HREF=>The home page</A>"
	.byte	" - <A HREF=b.html>The button page</A>"
	.byte	"<P>"
	.byte	"<FONT SIZE=+4><B>Welcome</B></FONT>"
	.byte	"<FONT FACE=Courier> - to the other page</FONT>"
	.byte	"<P>"
	.byte	"<A HREF=http://forum.6502.org/viewtopic.php?f=5&t=3024>&nbsp;e-mail "
	.byte	"me <IMG SRC=images/e.gif BORDER=0 ALT=e-mail></A>"
	.byte	"</TD></TR></TABLE></BODY></HTML>"
o_html_end


; *********************************************************************
; the button page, served for "GET /b.html"

b_html
	.byte	none			; flags
	.word	b_html_end-b_html_b	; file payload length
	.byte	"b.html",$00		; filename, null terminator
					; rest is file payload
b_html_b
	.byte	"HTTP/1.0 200 OK",$0D,$0A,"Content-Type: text/html"
	.byte	$0D,$0A,$0D,$0A
	.byte	"<HTML><HEAD><TITLE>Vic 20 WEB Server</TITLE>"
	.byte	"</HEAD><BODY BGCOLOR=#065C02 TEXT=WHITE>"
	.byte	"<TABLE ALIGN=CENTER WIDTH=90% HEIGHT=90%>"
	.byte	"<TR><TD VALIGN=CENTER>"
	.byte	"<FONT SIZE=+1>Vic 20 WEB Server </FONT>"
	.byte	"- <A HREF=>The home page</A>"
	.byte	" - <A HREF=o.html>The other page</A>"
	.byte	"<P>"
	.byte	"<FONT SIZE=+4><B>Welcome</B></FONT>"
	.byte	"<FONT FACE=Courier> - to the button page</FONT>"
	.byte	"<P>"
	.byte	"<A HREF=http://forum.6502.org/viewtopic.php?f=5&t=3024>&nbsp;e-mail "
	.byte	"me <IMG SRC=images/e.gif BORDER=0 ALT=e-mail></A></TD>"
	.byte	"<TD WIDTH=20% ALIGN=CENTER>Are you easily confused?"
	.byte	"<FORM METHOD=GET ACTION=b.html>"
	.byte	"<PRE>"
	.byte	"yes <INPUT TYPE=RADIO NAME=4 VALUE=0>",$0D,$0A
	.byte	"no  <INPUT TYPE=RADIO NAME=4 VALUE=1>",$0D,$0A
	.byte	"fish<INPUT TYPE=RADIO NAME=4 VALUE=2 CHECKED>"
	.byte	"</PRE>"
	.byte	"<INPUT TYPE=SUBMIT VALUE=Submit>"
	.byte	"</FORM></TD></TR></TABLE></BODY></HTML>"
b_html_end


; *********************************************************************
; errors directory

errors
	.byte	directory		; flags
	.word	errors_end-errors_b	; file payload length
	.byte	"errors",$00		; filename, null terminator
errors_b
	.word	e_404			; error 404
errors_end


; error 404, served for not found files

e_404
	.byte	none			; flags
	.word	e_404_end-e_404_b	; file payload length
	.byte	"404",$00		; filename, null terminator
					; rest is file payload
e_404_b
	.byte	"HTTP/1.0 200 OK",$0D,$0A,"Content-Type: text/html"
	.byte	$0D,$0A,$0D,$0A
	.byte	"<HTML><HEAD><TITLE>Error 404</TITLE>"
	.byte	"</HEAD><BODY BGCOLOR=#CC0265 TEXT=WHITE>"
	.byte	"<TABLE ALIGN=CENTER WIDTH=80% HEIGHT=90%>"
	.byte	"<TR><TD VALIGN=CENTER>"
	.byte	"<FONT SIZE=+1>Vic 20 WEB Server </FONT>"
	.byte	"- <A HREF=>The home page</A>"
	.byte	"<P>"
	.byte	"<FONT SIZE=+4><B>Error 404</B></FONT>"
	.byte	"<FONT FACE=Courier> - File not found</FONT>"
	.byte	"<P>"
	.byte	"<A HREF=http://forum.6502.org/viewtopic.php?f=5&t=3024>&nbsp;e-mail "
	.byte	"me <IMG SRC=images/e.gif BORDER=0 ALT=e-mail></A>"
	.byte	"</TD></TR></TABLE></BODY></HTML>"
e_404_end


; *********************************************************************
; images directory

images
	.byte	directory		; flags
	.word	images_end-images_b	; file payload length
	.byte	"images",$00		; filename, null terminator
images_b
	.word	e_gif			; small image
images_end


; small image, served for "GET /email.gif"

e_gif
	.byte	none			; flags
	.word	e_gif_end-e_gif_b	; file payload length
	.byte	"e.gif",$00		; filename, null terminator
					; rest is file payload
e_gif_b
	.byte	"HTTP/1.0 200 OK",$0D,$0A,"Content-Type: image/gif",$0D,$0A
	.byte	$0D,$0A,$47,$49,$46,$38,$39,$61,$10,$00,$10,$00,$C4,$00,$00
	.byte	$F7,$00,$5F,$33,$03,$9A,$33,$03,$CA,$03,$03,$83,$5F,$5F,$F7
	.byte	$33,$63,$FA,$63,$9B,$FB,$63,$CB,$FB,$03,$9B,$CB,$63,$FA,$FA
	.byte	$FA,$FA,$CB,$97,$00,$00,$CB,$03,$03,$FA,$FA,$FA,$63,$63,$63
	.byte	$03,$03,$03,$FF,$FF,$FF,$00,$00,$00,$00,$00,$00,$00,$00,$00
	.byte	$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	.byte	$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	.byte	$00,$00,$00,$00,$00,$00,$21,$F9,$04,$01,$00,$00,$10,$00,$2C
	.byte	$00,$00,$00,$00,$10,$00,$10,$00,$00,$05,$69,$20,$24,$8E,$C6
	.byte	$91,$24,$63,$9A,$96,$49,$11,$18,$AA,$28,$40,$85,$59,$40,$46
	.byte	$A0,$3A,$8E,$20,$20,$07,$01,$6F,$38,$7C,$38,$1A,$3F,$84,$40
	.byte	$C1,$6C,$36,$8D,$C8,$02,$62,$E0,$64,$02,$18,$0A,$E8,$60,$BB
	.byte	$AD,$32,$16,$D9,$63,$A3,$51,$2D,$43,$C9,$E5,$EA,$B9,$2C,$28
	.byte	$B8,$A9,$D0,$B4,$42,$70,$82,$3B,$E4,$F3,$7A,$F8,$C1,$EF,$F3
	.byte	$05,$26,$5B,$7C,$2A,$2F,$10,$02,$25,$37,$03,$31,$10,$04,$01
	.byte	$05,$06,$06,$37,$8B,$23,$01,$03,$6D,$31,$21,$00,$3B
e_gif_end


Last page update: 24th February, 2004. e-mail me