7 dir命令(harib15g)
好了,现在我们已经可以自己来尽情添加命令了,不过作为一个操作系统,我们的目标还是制作可执行文件(比如.exe)来让它运行。笔者很想马上进入这个话题,不过在此之前,我们先来制作一个显示磁盘内文件名称的命令吧。通过这个命令的制作,我们可以学习对磁盘中文件信息的操作方法,为明天制作可执行文件打好基础。
那么dir命令到底是干什么用的呢?大家在Windows的命令行窗口中输入“dir”执行一下就明白了,除了会显示文件名,还会显示文件的日期和大小。在Linux中与dir对应的命令是ls。接下来我们就来实现这个命令的功能。
■■■■■
怎样才能让系统执行dir命令呢?要显示文件名等信息,我们需要读取磁盘的内容,这得借助BIOS的帮助。当然,不通过BIOS来读写磁盘的方法也是有的,但是较难实现,所以这里就不介绍了。
不过,现在“纸娃娃系统”正处于32位模式,想使用BIOS也不行。BIOS不能用,不用BIOS读取磁盘的方法又不讲,这样的话你要让我怎么来读磁盘啊!其实大家不用担心。没错,其实在进入32位模式之前,我们不是已经从磁盘读了很多内容了吗?有10个柱面那么多呢!实在想不起来的话,请再翻到3.4节看一看吧。
说句题外话,笔者也重新读了读3.4节,哇,真的好怀念呢。仅仅过了15天,差异竟然如此之大!不,应该说是进步如此之快吧~(做感慨状)。
那么已经读出来的这些数据,存放在内存中的什么地方呢?在8.5节中写得很清楚,是0x00100000~0x00267fff。其中存放文件名的地方又在哪里呢?其实我们也已经说过了,不过大家可能都不记得了吧,参考3.5节从0柱面、0磁头、1扇区开始的0x002600之后,也就是内存地址的0x00102600开始写入。
■■■■■
这些数据的具体内容,在这里需要详细讲解一下。作为试验,我们在磁盘映像中加入了haribote.sys、ipl10.nas和make.bat这3个文件(加入其他文件也可以,这里暂以这3个文件为例)我们对Makefile稍作修改。
本次的Makefile节选
haribote.img : ipl10.bin haribote.sys Makefile
$(EDIMG) imgin:../z_tools/fdimg0at.tek \
wbinimg src:ipl10.bin len:512 from:0 to:0 \
copy from:haribote.sys to:@: \
copy from:ipl10.nas to:@: \
copy from:make.bat to:@: \
imgout:haribote.img
然后我们make一下,查看磁盘映像中0x002600字节以后的部分,内容如下。
磁盘映像的内容
+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F 0123456789ABCDEF
+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F 0123456789ABCDEF
002600 48 41 52 49 42 4F 54 45 53 59 53 20 00 00 00 00 HARIBOTESYS ….
002610 00 00 00 00 00 00 18 74 FF 32 02 00 68 6B 00 00 …….t.2..hk..
002620 49 50 4C 31 30 20 20 20 4E 41 53 20 00 00 00 00 IPL10 NAS ….
002630 00 00 00 00 00 00 59 7A 42 41 38 00 95 0B 00 00 ……YzB58…..
002640 4D 41 4B 45 20 20 20 20 42 41 54 20 00 00 00 00 MAKE BAT ….
002650 00 00 00 00 00 00 F6 10 81 30 3E 00 2E 00 00 00 ………0>…..
002660 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………….
看起来这里的内容是以32个字节为单位循环的,这32个字节的结构如下。
struct FILEINFO {
unsigned char name[8], ext[3], type;
char reserve[10];
unsigned short time, date, clustno;
unsigned int size;
};
开始的8个字节是文件名。文件名不足8个字节时,后面用空格补足。文件名超过8个字节的情况比较复杂,我们在这里先只考虑不超过8个字节的情况吧,一上来就挑战高难度的话,很容易产生挫败感呢。再仔细观察一下,我们发现所有的文件名都是大写的。
如果文件名的第一个字节为0xe5,代表这个文件已经被删除了;文件名第一个字节为0x00,代表这一段不包含任何文件名信息。从磁盘映像的0x004200就开始存放文件haribote.sys了,因此文件信息最多可以存放224个。
接下来3个字节是扩展名,和文件名一样,不足3个字节时用空格补足,如果文件没有扩展名,则这3个字节都用空格补足。扩展名和文件名一样,也全部使用了大写字母。
后面1个字节存放文件的属性信息。我们这3个文件的属性都是0x20。一般的文件不是0x20就是0x00,至于其他的值,我们来看下面的说明。
0x01
……只读文件(不可写入)
0x02
……隐藏文件
0x04
……系统文件
0x08
……非文件信息(比如磁盘名称等)
0x10
……目录
当一个文件既是只读文件又是隐藏文件时,将上面的对应值加算即可,即0x03。
到此为止的总计12个字节,基本上存放的都是字符编码信息,不会出现负数,因此在数据结构声明中使用unsigned类型。
接下来的10个字节为保留,也就是说,是为了将来可能会保存更多的文件信息而预留的,在我们的磁盘映像中都是0x00。话说,这个磁盘格式是由Windows的开发商微软公司定义的,因此,这段保留区域以后要如何使用,也是由微软公司来决定的。其他人要自行定义的话也可以,只不过将来可能会和Windows产生不兼容的问题。
下面2个字节为WORD整数,存放文件的时间。因此即便文件的内容都一样,这里大家看到的数值也可能是因人而异的。再下面2个字节存放文件的日期。这些数值虽然怎么看都不像是时间和日期,但只要用微软公司的公式计算一下,就可以转换为时、分、秒等信息了。
接下来的2个字节也是WORD整数,代表这个文件的内容从磁盘上的哪个扇区开始存放。变量名clustno本来是“簇号”(cluster number)的缩写,“簇”这个词是微软的专有名词,在这里我们先暂且理解为和“扇区”是一码事就好了。
最后的4个字节为DWORD整数,存放文件的大小。
■■■■■
有了上面这些信息,感觉dir命令应该可以实现了,我们来做做看吧。
本次的bootpack.h节选
/* asmhead.nas */
struct BOOTINFO { /* 0x0ff0-0x0fff */
(中略)
};
#define ADR_BOOTINFO 0x00000ff0
#define ADR_DISKIMG 0x00100000 /*这里! */
本次的bootpack.c节选
struct FILEINFO {
unsigned char name[8], ext[3], type;
char reserve[10];
unsigned short time, date, clustno;
unsigned int size;
};
void console_task(struct SHEET *sheet, unsigned int memtotal)
{
(中略)
struct FILEINFO *finfo = (struct FILEINFO *) (ADR_DISKIMG + 0x002600); /*这里!*/
(中略)
for (;;) {
io_cli();
if (fifo32_status(&task->fifo) == 0) {
(中略)
} else {
(中略)
if (256 <= i && i <= 511) { /*键盘数据(通过任务A) */
if (i == 8 + 256) {
/*退格键*/
(中略)
} else if (i == 10 + 256) {
/*回车键*/
(中略)
/*执行命令*/
if (strcmp(cmdline, "mem") == 0) {
/* mem命令*/
(中略)
} else if (strcmp(cmdline, "cls") == 0) {
/* cls命令*/
(中略)
/*从此开始*/ } else if (strcmp(cmdline, "dir") == 0) {
/* dir命令 */
for (x = 0; x < 224; x++) {
if (finfo[x].name[0] == 0x00) {
break;
}
if (finfo[x].name[0] != 0xe5) {
if ((finfo[x].type & 0x18) == 0) {
sprintf(s, "filename.ext %7d", finfo[x].size);
for (y = 0; y < 8; y++) {
s[y] = finfo[x].name[y];
}
s[ 9] = finfo[x].ext[0];
s[10] = finfo[x].ext[1];
s[11] = finfo[x].ext[2];
putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF,
COL8_000000, s, 30);
cursor_y = cons_newline(cursor_y, sheet);
}
}
}
/*到此结束*/ cursor_y = cons_newline(cursor_y, sheet);
} else if (cmdline[0] != 0) {
/*不是命令,也不是空行*/
(中略)
}
(中略)
} else {
/*一般字符*/
(中略)
}
}
(中略)
}
}
}
先大概写成这个样子,可以显示文件名、扩展名和文件大小。到底能不能成功呢?我们来“make run”,然后“dir”。
哇,出来啦!
哇,显示出来了哦!话说回来,不显示出来的话才让人抓狂,运行成功了真的很开心呢。
对了,如果你不喜欢Windows风格的dir命令,觉得Linux风格的ls命令更好的话,没问题,当然可以把这个命令给改成ls哦。至于应该修改哪里嘛,对于已经读到这里的人,应该不用再解释了吧?
好了,今天就到这里,明天再见啦。