The concept of a IO control block

IO Control Block

The SD-Card IO routines and the FAT library use various control blocks. Both evolved from the SD-Card IO control blocks and up to now they all share the same header. This is quite convenient because when the FAT library needs to access the SD-Card it only needs to set some fields in the control block and then passes the same control block to the IO routines.

Some control blocks are allocated permanently to a specific function and are placed as sections in the assembler source. However as we always use a pointer to the control block when calling a function it is also possible to dynamically allocate control blocks and use them as the parameter block when calling the functions. See the description of the malloc and free functions.

Basic IO Control Block Header

It all started with the general IO control block. This control block only consists of the general IO control block header and is used when we need to directly access the SD-Card for various tasks.

;--------------------------------------------------------------------------
;	IO Control Blocks
;--------------------------------------------------------------------------
;
;	Paramterblock for general IO to read/write individual sectors like
;	MBR, VBR, Extended Partition, and general Sectors
;
pcluster:	.byte	4		; current cluster / next sector
psector:	.byte	4		; Sector number for sector read/write routine
paddress:	.byte	2		; Pointer to Buffer
pflag:		.byte	1		; Used when the SD-Card is inserted.
.equ	Part_Next	= 5		; Analyze next extended partition table
.equ	Part_Fat	= 6		; FAT Volume Mapped
.equ	Part_Ext	= 7		; Current MBR has Extended Partition
pnumsect:	.byte	1		; Used to walk the sectors in a cluster


pextended:	.byte	4		; Sector of primary extended partition
;
; Offsets in standard parameter block
;
.equ	P_Cluster	= pcluster-pcluster
.equ	P_Sector	= psector-pcluster
.equ	P_Address	= paddress-pcluster
.equ	P_Flag		= pflag-pcluster
.equ	P_NumSect	= pnumsect-pcluster
.equ	P_Extended	= pextended-pcluster

The blocks start with a 4-byte field that normally contains the cluster with which we are working at the moment. It also my be used as temporary buffer for other 32-bit integers often used to process clusters, sectors and linked clusters

The next 4-byte field is used for the sector number, in our case this is the equivalent to a block of 512 bytes.

The third 2-byte field points to the IO buffer itself.

The forth and the fifth 1-byte fields are generally used as a flags field and a value to support clusters which consist of multiple sectors/blocks.

Other fields that follow the numsect field are specific to the use of the control block.

IO Control Block used to access the FAT

One of the permanently allocated control blocks is used to read and write individual sectors/blocks from FAT

;--------------------------------------------------------------------------
;
;	The volume access primitives use fixed parameter blocks which are pointed
;	to by the 16-bit Y register (yl:yh). The parameter blocks have a fixed 
;	header that is valid for all data structures accessed. Depending on the
;	use of the parameter block fields are appended.
;
;--------------------------------------------------------------------------
;
;	Parameterblock used to read FAT, we always keep the last FAT sector in a dedicated
;	buffer to improve the performance when building the fragmentlists note the fat
;	control block has no extension and is used as the prototype for the control
;	block header for IO control blocks, it is important that this never chagnes
;
pfatcluster:	.byte	4
pfatsector:	.byte	4
pfataddress:	.byte	2
pfatflag:	.byte	1		; 
pfatnumsect:	.byte	1		; reserved
;

Directory Read IO Control Block

The control block that is used to read the directory of the volume is the first control block that has extensions. This control block structure is used to open and walk through a directory.

;--------------------------------------------------------------------------
;
;	Directory funtions, this parameter block is used to access directories. Normally
;	we only need one parameter block for this purpose as we need it only to find files
;	by name.
;
pdircluster:.byte	4		; active directory cluster
pdirsector:	.byte	4		; current sector of active directory
pdiraddress:.byte	2
pdirflag:	.byte	1		; Flags for directory processing
.equ	D_Linked	= 0		; Directory is a linked list of clusters
.equ	D_Long		= 1		; We have a long filename
pdirnumsect:.byte	1		; current sector in directory, FAT16: in root directory, FAT32: in current directory cluster
;
pdircount:	.byte	1		; current entry in active directory sector
		.byte	1		; reserved
pdirpointer:.byte	2		; Pointer to directory entry
pdirlfn:	.byte	2		; pointer to long filename buffer
pdirstartc:	.byte	4
pdirname:	.byte	2		; Pointer in input path in case we want update the filenames
;
;	Offset definitions
;
.equ	P_Count		= pdircount-pdircluster
.equ	P_DirPointer= pdirpointer-pdircluster
.equ	P_LongFileN	= pdirlfn-pdircluster
.equ	P_StartClust= pdirstartc-pdircluster
.equ	P_DirName	= pdirname-pdircluster

File IO Control Block

Based on the above control blocks and the experience using these control blocks I recognized that it would be easier to maintain all symbols via macros. Unfortunately the AVR Assembler is not very capable, especially when someone is used to the features and tools available on PDP-11 using MACRO-11.

A set of macros helped to organize the offsets of the file control block. Most likely I will use them to redefine the above control blocks to be consistent throughout the whole program.

For files that are accessed block-wise I decided to keep the list of fragments as a link list and attach them dynamically to the file IO control block when the file is opened. This is similar to the window control blocks in RSX-11M. In my case I decided that a file can only be opened successfully if there is enough memory to hold all fragments in a list of fragment descriptor blocks. This allows to access any block of the file without going to the FAT to retrieve new file windows. In my case this is not a problem as most files used in the RLV12 emulator are small RL01/02 diskimages and on a fresh FAT volumen no fragmentation occurs. Files that are accessed block-wise can be opened read-only and read-write. For the moment files will not grow so I do not need to be able to allocate new clusters to a file.

Files that are read sequentially use a different control block. I only remember the position within the current cluster and read consecutive sectors. When the end of the cluster is reached the next cluster will be evaluated using the FAT.

;--------------------------------------------------------------------------
;
;	Parameter Block for FILE IO	(File Control Block)
;
;--------------------------------------------------------------------------
;
;
;
.set recordlength = -1
.macro	recordstart
.set recordlength = 0
.endmacro

.macro	record
.equ	@0##_##@1 = recordlength
.set recordlength = recordlength + @2
.endmacro	

.macro	recordend
.equ	@0##_##@1 = recordlength
.endmacro	


recordstart	fcb
record		fcb, cluster, 4
record		fcb, sector, 4
record		fcb, address, 2
record		fcb, flag, 1
.equ	F_Readonly		= 0		; File is open read-only
.equ	F_Direct		= 1		; File is open for direct block IO
.equ	F_Sequential		= 2		; File is open for sequential access
.equ	F_Image			= 3		; File is a valid diskimage
record		fcb, secinclst, 1
record		fcb, drvtab, 2
record		fcb, filename, 2
record		fcb, fraglist, 2
record		fcb, position, 4
record		fcb, filesize, 4
record		fcb, byteinsec, 2
recordend	fcb, length

The fragments of a block IO file are kept in a linked list of contiguous blocks, this control block structure is dynamically allocated as many times as there are fragments in a file.


recordstart	Fr
record		Fr, List, 2			; Linked List Head
record		Fr, Size, 4			; Size of Fragment (or 0 if end if list)
record		Fr, Start, 4			; Start sector of fragment on device
recordend	Fr, Entry			; Length of fragment list entry