ファイル操作関数

ファイル関連の機能は某所
でsdcc用のライブラリが公開されているが、そのままでは動かない。
なので必要なファイルを拾ってきて、適当に修正して使う事にする。

ここで作成するsdcc用のファイル操作関数は、MSXDOSで動作するもので、
ANSI-Cのファイル関数ではなく、DOSのシステムコールを呼び出すラッパとして
動作する。ANSI-Cに近い関数ライブラリのラッパを作る事も可能だろう。


まずヘッダをサルベージして適当に修正したものを使う。

dosfile.h
typedef struct {
	unsigned char	drive_no;
	unsigned char	name[8];
	unsigned char	ext[3];
	unsigned int	current_block;
	unsigned int	record_size;
	unsigned long	file_size;
	unsigned int	date;
	unsigned int	time;
	unsigned char	device_id;
	unsigned char	directory_location;
	unsigned int	start_cluster_no;
	unsigned int	last_access_cluster_no;
	unsigned int	cluster_offset;
	unsigned char	current_record;
	unsigned long	random_record;
} FCB;

typedef struct {
	unsigned char	name[8];
	unsigned char	ext[3];
	unsigned char	attribute;
	unsigned char	undel_char;			/* MSX-DOS2 */
	unsigned char	reserve[9];
	unsigned int	time;
	unsigned int	date;
	unsigned int	start_cluster_no;
	unsigned long	file_size;
} FCB_DIR;

typedef struct {
	unsigned char	drive_no;
	FCB_DIR			dirinfo;
} FCB_FIND;

/* return code */
#define FCB_SUCCESS	0x00

/* FCB_DIR::attribute */
#define	FCB_ATTR_READONLY	0x01		/* MSX-DOS2 */
#define FCB_ATTR_HIDDEN		0x02
#define	FCB_ATTR_SYSTEM		0x04		/* MSX-DOS2 */
#define FCB_ATTR_VOLUME		0x08		/* MSX-DOS2 */
#define	FCB_ATTR_DIR		0x10		/* MSX-DOS2 */
#define	FCB_ATTR_ARCHIVE	0x20		/* MSX-DOS2 */

extern unsigned char bdos_open(FCB *);
extern unsigned char bdos_close(FCB *);
extern void bdos_dta(unsigned int *);
extern unsigned char bdos_read(FCB *, unsigned int);
extern unsigned char bdos_seqread(FCB *);
extern unsigned char bdos_seqwrite(FCB *);
extern unsigned char bdos_newfile(FCB *);


次にDOSファンクションコールを呼び出すC関数インターフェースを
アセンブラで書く。(元のソースは動かないので)

fileio.asm
;
;	FCB Open
;	unsigned char bdos_open(FCB *fp)
;
_bdos_open::
	ld	hl,#2
	add	hl,sp
	;
	ld	e,(hl)		;FCB Pointer -> [DE]
	inc	hl
	ld 	d,(hl)
	;
	ld	c,#0xf		;Function call 0xF(openfile)
	call	0x05
	;
	ld	l,a		;return code
	ld	h,#0
	ret
;
;	FCB Close
;	unsigned char bdos_close(FCB *fp)
;
_bdos_close::
	ld	hl,#2
	add	hl,sp
	;
	ld	e,(hl)		;FCB Pointer -> [DE]
	inc	hl
	ld 	d,(hl)
	;
	ld	c,#0x10		;Function call 0x10(closefile)
	call	0x05
	;
	ld	l,a		;return code
	ld	h,#0
	ret
;
;	SET DTA(DMA) ADDRESS
;	void bdos_dta(unsigned int addr)
;
_bdos_dta::
	ld	hl,#2
	add	hl,sp
	;
	ld	e,(hl)		;DTA Address -> [DE]
	inc	hl
	ld	d,(hl)
	;
	ld	c,#0x1a		;Function call 0x1A (DTA)
	call	0x05
;
;	RANDOM READ
;	unsigned char bdos_read(FCB *fp,unsigned int rnumber)
;
_bdos_read::
	ld	hl,#2
	add	hl,sp
	;
	ld	e,(hl)		;FCB Pointer -> [DE]
	inc	hl
	ld	d,(hl)
	inc	hl
	;
	ld	c,(hl)		;Record number -> [BC] -> [HL]
	inc	hl
	ld	b,(hl)
	push	bc
	pop	hl
	;
	ld	c,#0x27		;Function call 0x27 (Random read)
	call	0x05
	;
	ld	l,a		;Return code
	ld	h,#0
	ret
;
;	SEQUENTIAL READ
;	unsigned char bdos_seqread(FCB *fp)
;
_bdos_seqread::
	ld	hl,#2
	add	hl,sp
	;
	ld	e,(hl)		;FCB Pointer -> [DE]
	inc	hl
	ld 	d,(hl)
	;
	ld	c,#0x14		;Function call 0x14(sequentialread)
	call	0x05
	;
	ld	l,a		;return code
	ld	h,#0
	ret
;
;	SEQUENTIAL WRITE 
;	unsigned char bdos_seqwrite(FCB *);
;
_bdos_seqwrite::
	ld	hl,#2
	add	hl,sp
	;
	ld	e,(hl)		;FCB Pointer -> [DE]
	inc	hl
	ld 	d,(hl)
	;
	ld	c,#0x15		;Function call 0x15(sequentialwrite)
	call	0x05
	;
	ld	l,a		;return code
	ld	h,#0
	ret
;
;	CREATE FILE
;	unsigned char bdos_newfile(FCB *);
;
_bdos_newfile::
	ld	hl,#2
	add	hl,sp
	;
	ld	e,(hl)		;FCB Pointer -> [DE]
	inc	hl
	ld 	d,(hl)
	;
	ld	c,#0x16		;Function call 0x16(create new)
	call	0x05
	;
	ld	l,a		;return code
	ld	h,#0
	ret

fileio.asmはアセンブルするとオブジェクトファイルが作成されるのでコンパイラの
リンク時に指定すればよい。

>sdasz80 -o fileio.rel fileio.asm

今回作成するファイルI/O関数は、DOSファンクションコールを呼ぶもので低レベルI/O。
これらの関数はアセンブラで書かれているが、基本的にはDOSファンクションコールを
呼び出す手続きしか書かれていない。
WindowsのCランタイムライブラリのファイル関数ではなく低レベルファイル関数と等しい


unsigned char bdos_open(FCB *) ファイルを開く
unsigned char bdos_close(FCB *) ファイルを閉じる
void bdos_dta(unsigned int *) DTA(DMA)アドレスを設定
unsigned char bdos_read(FCB *, unsigned int) ランダムデータ読み込み
unsigned char bdos_seqread(FCB *) シーケンシャル読み込み
unsigned char bdos_seqwrite(FCB *) シーケンシャル書き込み
unsigned char bdos_newfile(FCB *) 新規作成


  • 使用例

C言語のサンプル例を以下に示す。

file1.c
#include "dosfile.h"

void main(void){

	//Create pFCB
	FCB *fp;

	//Setting Default FCB address
	fp=(FCB *)0x005c;

	//Open
	bdos_open(fp);

	//Setting buffer address
	bdos_dta(0x300);

	//Setting recordsize
	fp->record_size=8;
	fp->random_record=0;

	//random read buffer
	bdos_read(fp,1);

	//Close
	bdos_close(fp);
}

コンパイル方法は以下
>sdcc -mz80 -c file1.c
>sdld -b _CODE=0x100 -b _DATA=0x300 -i file1.ihx file1.rel fileio.rel -l z80.lib
>makebin -s 4096 file1.ihx > file1.bin
>perl binout.pl file1.bin > file1.com

実行時はコマンド上でファイル名を指定する。a.txtから読み込む場合の例
A:>file1 a.txt

このサンプルはDOSのファンクションコールを利用してランダムアクセスリードにより
ファイルから8byteデータをバッファへ読み込む。

DOSのファイル構造体(FCB、いわゆるファイルディスクリプタ)はデフォルトで0x5Cから始まる
数バイトに確保されている。
このアドレスをFCB*としてFCB構造体へのポインタでアクセスする。
デフォルトのFCB構造体アドレスを使う場合はコマンド引数が利用出来る。

DTA(DMA)はファンクションコールで、ディスクからデータを読み書きする場合のバッファメモリのアドレスを指定する。
ファイルを開いた後に必ず指定する。

DOS(CPM)のファイル操作は128byteを単位とするが、ランダムアクセスでは指定バイト単位での
リードライトが可能。
データサイズは予めレコードサイズで指定しておき、関数の引数のレコードでリードライトする。

シーケンシャルにリードする場合はbdos_seqread()を使う。このときデータは128byte単位となる。

	//Setting buffer address(pass pointer to the arrary)
	bdos_dta(0x300);

	//sequential read buffer(every 128bytes)
	bdos_seqread(fp);

	//Close
	bdos_close(fp);

デフォルトのファイルディスクリプタ(FCB)領域を使用せず、プログラム内で確保する例を
以下に示す。
文字列は左寄せで先頭から書き込み、ファイル名は8文字未満の場合はスペースで埋め空白とする。

#include "dosfile.h"
#include <string.h>

void main(void){
	//Create FCB struct
	FCB fp;

	//Setting filename
	strcpy(fp.name,"ABCDEFG1");
	strcpy(fp.ext,"TXT");

	fp.current_block=0;
	fp.record_size=128;
	fp.current_record=0;
	fp.random_record=0;

	//Create file
	bdos_newfile(&fp);

	//Close
	bdos_close(&fp);
}

バッファーを用いてシーケンシャルアクセスで書き込む場合の例。

#include "dosfile.h"
#include <string.h>

void main(void){
	int i;
	unsigned char fdata[128];

	//Create FCB struct
	FCB fp;

	//Setting filename
	strcpy(fp.name,"BBBBBBB1");
	strcpy(fp.ext,"TXT");

	//Setting parameter
	fp.current_block=0;
	fp.record_size=128;
	fp.current_record=0;
	fp.random_record=0;

	//make file
	bdos_newfile(&fp);

	//make data
	for(i=0; i<128; i++){
		fdata[i]=i;
	}

	//Setting DTA(data transfer address,//every 128bytes)
	bdos_dta((unsigned int *)&fdata);

	//Write data
	bdos_seqwrite(&fp);

	//additional writing,(after the second)
	bdos_seqwrite(&fp);

	//Close
	bdos_close(&fp);
}


  • コマンドラインパラメータ

DOSのコマンド実行時に、コマンドと共にファイル名などを指定すると自動的にファイル
ディスクリプタ部分にもファイル名が格納される。

>doscmd filename.txt

コマンド引数のファイル名が与えられると、0x5Cからはじまるファイルディスクリプタ(FCB)
へ名前と拡張子が保存される。
0x5Cから始まるFCB領域はデフォルトでCOMMAND.COMがファイル名を自動で設定する。
プログラムはFCB構造体を指定しファンクションコールを開くだけでよい。

DOSコマンドの引数パラメータ文字列は0x80-0xFFのデータエリアに記録される。
通常この部分はCPMファンクションコールを利用するファイルアクセスの一時的なバッファ領域
として用いられるが、MSXDOSではコマンド引数の文字列が格納される。

コマンドで与えた文字列、ファイル名、オプションなどを参照する場合は、0x80から始まる文字列
を読む。
最初の1byteは文字数、最後に0xDが現われるまでの文字列がコマンドオプションである。
これらのパラメータを用いる事で、args,argvなどの引数をCから参照することができる。
プログラムはこれをargs,argvとしてそのまま利用することができる。

0x0-0x54 CPU割り込みベクトルなどジャンプテーブル
0x5C-0x7f デフォルトのファイルポインタ(FCB)構造体
0x80-0xff バッファエリア兼コマンド文字列バッファ
0x100- *.COM実行ファイルロード開始アドレス

COMMAND.COMの内部コマンドであるTYPEはファイルを表示するが、このコマンドは
0x80から始まるアドレスをバッファエリアとして使う。
0x100は外部コマンドなどの実行ファイルがロードされるアドレスだが、
TYPEコマンドは内部命令なので、0x80-0x3000などのアドレス空間をバッファとして使う。
TYPEコマンドは特殊な振る舞いをする例。

タグ:

+ タグ編集
  • タグ:

このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー利用規約 が適用されます。

最終更新:2012年08月19日 19:39
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。