下面就来添加读写硬盘的功能,首先修改Proc_Hard_Disk:
kernel/hd.c
- case HD_READ:
- case HD_WRITE:
- Read_And_Write_HD(&m);
- break;
下面来添加Read_And_Write函数,还把等待某端口的某位是否为0的功能抽象为Wait_For_Reg函数,相应的HD_Cmd_Out函数中也要调用,这里,很简单不细说:
kernel/hd.c
- /*-------------------------------------------------------------Read_And_Write_HD
- 读写硬盘函数
- */
- static void Read_And_Write_HD(Message *m)
- {
- int device = m->i1; /* 取出设备号 */
- /*
- 取出相应分区的起始扇区号
- 如果是设备号代表的是整个硬盘和主分区,则到primary中寻找
- 如果是扩展分区的逻辑分区则到logical中寻找
- */
- int prt_begin_sec = (device < 5) ?
- hd_prt_info.primary[device].base
- : hd_prt_info.logical[device - 16].base;
- int read_begin_sec = prt_begin_sec + m->i2; /* 取出要读写的第一个扇区号 */
- int read_bytes = m->i3; /* 取出要读写的字节数 */
- void *ret_buf = (void*)m->r1; /* 取出含有要读写的数据的指针 */
- /* 设置正确的读取硬盘的扇区数 */
- int sc;
- if(read_bytes < SECTOR_SIZE)
- {
- sc = 1;
- }
- else if((read_bytes % SECTOR_SIZE) == 0)
- {
- sc = read_bytes / SECTOR_SIZE;
- }
- else
- {
- sc = read_bytes / SECTOR_SIZE + 1;
- }
- /* 填充准备发出的命令 */
- HD_Command hd_cmd;
- hd_cmd.features = 0;
- hd_cmd.sector_count = sc;
- hd_cmd.lba_low = read_begin_sec & 0xff;
- hd_cmd.lba_mid = (read_begin_sec >> 8) & 0xff;
- hd_cmd.lba_high = (read_begin_sec >> 16) & 0xff;
- hd_cmd.device = MAKE_DEVICE_REG(1,0,(read_begin_sec >> 24) & 0xf);
- hd_cmd.command = (m->msg_type == HD_READ) ? ATA_READ_HD : ATA_WRITE_HD; /* 读还是写? */
- HD_Cmd_Out(&hd_cmd);
- /* 如果是读,则等待硬盘驱动器把数据读到REG_DATA中 */
- if(m->msg_type == HD_READ)
- {
- Wait_Int();
- }
- int left_bytes = read_bytes;
- int copy_bytes = SECTOR_SIZE;
- /* 一次读写一个扇区,如果还有剩余则继续 */
- while(left_bytes > 0)
- {
- /* 读来到这 */
- if(m->msg_type == HD_READ)
- {
- /* 如果成立,则说明读完这次就结束了 */
- if(left_bytes < SECTOR_SIZE)
- {
- copy_bytes = left_bytes;
- /* 由于从REG_DATA一次读2个字节,则如果copy_bytes为奇数,则要自增1 */
- if((copy_bytes % 2) != 0)
- {
- copy_bytes++;
- }
- }
- /* 从REG_DATA把数据读到hd_buf缓冲区中,copy_bytes / 2是因为一次读2字节 */
- Read_Port_2_Buffer(REG_DATA,hd_buf,copy_bytes / 2);
- Memory_Copy(ret_buf,hd_buf,copy_bytes); /* 把数据再从缓冲区读到返回的缓冲区 */
- }
- /* 写来到这 */
- else
- {
- /*
- 写硬盘的话如果是写一个扇区的话,必须填充REG_DATA256次。
- 所以这里如果写的数据少于一个扇区的话,则把数据量设为一个扇区。
- */
- if(left_bytes < SECTOR_SIZE)
- {
- copy_bytes = SECTOR_SIZE;
- }
- /* 如果REG_STATUS最高位不为0,说明硬盘尚未准备好,不能写数据 */
- Wait_For_Reg(REG_STATUS,STATUS_GO_ON,100);
- /* 写一个扇区的数据到REG_DATA中 */
- Write_Buffer_2_Port(REG_DATA,ret_buf,copy_bytes / 2);
- Wait_Int(); /* 写完一个扇区后,要等待硬盘工作完成,才可写下一个扇区 */
- }
- ret_buf += SECTOR_SIZE; /* 消息的返回数据缓冲区指针指向下一个512字节 */
- left_bytes -= SECTOR_SIZE; /* 要写的数据字节数减512 */
- }
- }
- /*------------------------------------------------------------------Wait_For_Reg
- 等待某个端口的某位为0,如果不为0则死等,milli_sec是查询的间隔。
- 中断处理函数中不可调用,因为调用了Milli_Delay ?
- */
- static void Wait_For_Reg(u16 port,u8 mask,int milli_sec)
- {
- do
- {
- u8 cur_status = In_Byte(port);
- if((cur_status & mask) == 0)
- {
- break;
- }
- Milli_Delay(milli_sec);
- }while(1);
- }
其中用到了Write_Buffer_2_Port函数,功能跟Read_Port_2_Buffer函数功能相反。。不同之处一个用si表示缓冲区地址另一个用di。。
lib/lib_in_asm.asm:
- ;------------------------------------------------------------Write_Buffer_2_Port
- Write_Buffer_2_Port:
- ;outsw为串输出指令,以字为单位,该指令的功能是从dx指定的端口写入一个字节
- ;从es:si指定的内存单元读取
- mov edx,[esp + 4] ;端口号送edx
- mov esi,[esp + 8] ;缓冲区偏移送edi
- mov ecx,[esp + 12] ;要复制的字的个数送ecx
- cld ;clear df
- rep outsw
- ret
在proto.h中添加原型则不消说了。
使用到的相关宏定义如下:
include/const.h
- #define HD_READ 3 /* 读硬盘的功能号 */
- #define HD_WRITE 4 /* 写硬盘的功能号 */
include/hd.h
- #define ATA_READ_HD 0x20 /* 读取硬盘的送给硬盘驱动器寄存器的值 */
- #define ATA_WRITE_HD 0x30 /* 写硬盘的送给硬盘驱动器寄存器的值 */
- #define SECTOR_SIZE 512 /* 一个扇区的大小 */
- #define STATUS_GO_ON 0x80 /* 状态寄存器的测试BUSY位是否为0的MASK */
下面我们就来做测试了,先读系统分区表的内容,就是0扇区的0x1be偏移处的64个字节,修改FS进程的执行体:
kernel/fs.c
- /*--------------------------------------------------------------Proc_File_System
- 文件系统进程的执行体
- */
- static u8 buf[1024]; /* 读写硬盘的缓冲区 */
- void Proc_File_System()
- {
- Message m;
- m.msg_type = HD_INFO;
- Send_Receive_Shell(BOTH,PROC_HD_PID,&m);
- m.msg_type = HD_READ;
- m.i1 = 0;
- m.i2 = 0;
- m.i3 = 512;
- m.r1 = (void*)buf;
- Send_Receive_Shell(BOTH,PROC_HD_PID,&m);
- int i = 0;
- while(i < 16 * 4)
- {
- Printf("%c ",buf[0x1be + i++]);
- }
- Printf("PROC FS LOOP/n");
- while(1);
- }
OK,make,bochs,结果如图:
跟实际情况是否一致呢,看看硬盘里的对应部分的数据:
我们再来看看写功能,先在设备号为33的逻辑扇区的起始扇区写一个512的数据,再把这个地儿的数据的前3个字节读出来并显示出来,代码如下:
kernel/fs.c
- /*--------------------------------------------------------------Proc_File_System
- 文件系统进程的执行体
- */
- static u8 buf[1024]; /* 读写硬盘的缓冲区 */
- void Proc_File_System()
- {
- buf[0] = 'a';
- buf[1] = 'b';
- buf[2] = 'c';
- int j = 4;
- for(;j < 1024;j++){
- buf[j] = 0;
- }
- Message m;
- m.msg_type = HD_INFO;
- Send_Receive_Shell(BOTH,PROC_HD_PID,&m);
- m.msg_type = HD_WRITE;
- m.i1 = 33;
- m.i2 = 0;
- m.i3 = 3;
- m.r1 = (void*)buf;
- Send_Receive_Shell(BOTH,PROC_HD_PID,&m);
- j = 0;
- for(;j < 1024;j++)
- {
- buf[j] = 0;
- }
- m.msg_type = HD_READ;
- m.i1 = 33;
- m.i2 = 0;
- m.i3 = 512;
- m.r1 = (void*)buf;
- Send_Receive_Shell(BOTH,PROC_HD_PID,&m);
- int i = 0;
- while(i < 3)
- {
- Printf("%c/n",buf[i++]);
- }
- Printf("PROC FS LOOP/n");
- while(1);
- }
OK,make,bochs,结果如图所示:
再来看看硬盘里的情况,偏移字节数为:0xec7f * 512 = 0x 1d8fe00,如图所示:
嗯,很好,成功了。。