ファイル関連の機能は
某所
で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コマンドは特殊な振る舞いをする例。
最終更新:2012年08月19日 19:39