6502 ROM file system

From the pages by Lee Davison.

filesys
6502 ROM file system
This is a simple ROMable file system for 6502 based computers. It was designed to hold all the web content for my Vic 20 web server and so only has the features needed to support that.
Requirements.
As it’s intended for use in embedded type applications the filesystem needs to be small and require few system resources. The system presented here uses only four bytes, apart from the filename to describe each file and directory. The routine to find a file needs only eleven bytes of RAM, apart from the filename to be searched for, only six of which need to be in page zero and none of which need to be maintained between calls.
File system description.
Files and directories are basically the same with just one bit changed in the header to tell them apart. There are no blocks or block mapping, no file dates or times, there aren’t even the pointers to reverse traverse the file tree, though they could fairly easily be added.

Files consist of a header and the payload. The header is from four to 259 bytes long and is made up from a flag byte, a length word and a null terminated filename. File names are case sensitive, can be up to 255 characters long and can include most characters, only “/” and $00 are expressly forbidden.

The payload is just binary data and can be any size, including zero bytes.

A directory is the same in form as a file except b6 in the flag byte is set. The payload is a list of word size pointers to the headers for each of the files and directories within the directory.

There are no end markers or guard bytes or any special way of marking the start and end of each file. The only point that must be known is the start of the root directory from which the pointers to all other files and directories can be found. Files do not need to be in contiguous memory and can be placed as and where is convenient.

The header.
The header can be as short as four and as long as 259 bytes, this is because the filename is variable in length and can be from zero to 255 bytes long (excluding the null treminator byte)

f_start		.ds	1		; flags
f_length	.ds	2		; file payload length
f_name		.ds	n+1		; filename and null terminator		

The first byte in the header, the flag byte, only has two bits defined and of those only one is used at present. b7 is the deleted bit, if this bit is set then this file or directory has been deleted. b6 is used to distinguish a directory file from a non directory file, if this bit is set then this file is a directory entry and as such is treated slightly differently by the filesystem.

 bit	function
 ---	--------
  7	deleted
  6	directory	
  5	unused
  4	unused
  3	unused
  2	unused
  1	unused
  0	unused

The header flag byte has only b7 and b6 defined, all other bits are available for any use such as hidden, system, executable, read-only or archive bits.

The deleted flag is not used as such and, with a small code change, could also be assigned to other uses.

The second and third bytes in the header make up the file payload length word, this is length of the file’s contents. The file payload length is limited only by the space available but in practice will usually be small enough to fit in a single ethernet packet.

The fourth byte onwards of the header is the null terminated filename. There are few restrictions on the name aside from the maximum length which, excluding terminator, is 255 bytes and the character “/” and the byte $00 are not allowed. No other characters are expressly forbidden, but the use of non-printing characters is not recommended.

Directories.
Directories are distinguished from files only by the state of the directory bit in the flag byte. This means no special code is needed to search for directories instead of files as, during searching, both are treated the same. The down side to this is that you can’t have a file and a directory with the same name, only the first listed in the directory would ever be found.

;	the root file should always be a directory

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

The payload in a directory is a list of absolute word pointers pointing to the start of each of the files and directories to which the current directory is the parent. No end marker is needed as the end can be dreived from the length so there is no restriction on the location of files. Though the entries in the example above have been arranged in directory then file alphabetic order this is not a requirement and any order is as valid as any other. No significance is placed on file ordering except where duplicate filenames are concerned. In this case only the first occurance of the duplicate filename will ever be found in a search.
The root directory.
The root directory is exactly the same in form as any other directory, what makes it special is that it is not pointed to by any pointer in any other directory and so it’s location can only be refered to explicitly. For this reason a name is not needed, it can never be searched for, but could be used as a volume name or distinguishing information in a system where more than one volume may exist or the volume is held in removable media – a flashcard for example.

If the root is always held in the same relative place it makes handling multiple or removable volumes much easier.

Files.

The header of a file is the same as that of a directory except the directory bit in the flag byte is clear. The payload is just binary data and there is no end-of-file marker needed as the end of file can be derived from the length.

Because of this there is no restriction on what the payload may contain and there is no significance in the filename or it’s extension (if any). All file payloads are of equal significance, i.e. none, as far as the filesystem is concerned.

;	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

Software.
The only software provided is a routine to search the file system tree for a particular file. The filename to search for should be the complete path from the root, start with a “/” character and be null terminated. Directories are separated with the “/” character. As this code is intended for a small web server there is the feature that any search terminating in a directory will automatically append “index.html” to the string and search that filename.
Source.
The code presented only allows searching of the file tree and there is no code to add or delete files or directories.
You can either download the source as zipped text or view it as html.

Expansion.
If 64 kilobytes, the limit for this system as it stands, is not enough the filesystem can quite easily be expanded by extending the directory pointers and payload length to values big enough to handle the required size. With 32 bit words up to 4GB of paged or externally stored data could be handled with a minimal increase in code complexity, more than enough for most 8 bit microprocessor systems.

; 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

; 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=mailto:leeedavison@lycos.co.uk> 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=mailto:leeedavison@lycos.co.uk> 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=mailto:leeedavison@lycos.co.uk> 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=mailto:leeedavison@lycos.co.uk> 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