30天自制操作系统第8天Word下载.docx

上传人:b****4 文档编号:17526155 上传时间:2022-12-07 格式:DOCX 页数:20 大小:1.18MB
下载 相关 举报
30天自制操作系统第8天Word下载.docx_第1页
第1页 / 共20页
30天自制操作系统第8天Word下载.docx_第2页
第2页 / 共20页
30天自制操作系统第8天Word下载.docx_第3页
第3页 / 共20页
30天自制操作系统第8天Word下载.docx_第4页
第4页 / 共20页
30天自制操作系统第8天Word下载.docx_第5页
第5页 / 共20页
点击查看更多>>
下载资源
资源描述

30天自制操作系统第8天Word下载.docx

《30天自制操作系统第8天Word下载.docx》由会员分享,可在线阅读,更多相关《30天自制操作系统第8天Word下载.docx(20页珍藏版)》请在冰豆网上搜索。

30天自制操作系统第8天Word下载.docx

}elseif(fifo8_status(&

mousefifo)!

mousefifo);

if(mouse_phase==0)/*等待鼠标的0xfa的状态*/

{

if(i==0xfa)

mouse_phase=1;

}

}elseif(mouse_phase==1){

/*等待鼠标的第一字节*/

mouse_dbuf[0]=i;

mouse_phase=2;

}elseif(mouse_phase==2){

/*等待鼠标的第二字节*/

mouse_dbuf[1]=i;

mouse_phase=3;

}elseif(mouse_phase==3){

mouse_dbuf[2]=i;

/*鼠标的三个字节都齐了,显示出来*/

%02X%02X%02X"

mouse_dbuf[0],mouse_dbuf[1],mouse_dbuf[2]);

scrnx,COL8_008484,32,16,32+8*8-1,31);

scrnx,32,16,COL8_FFFFFF,s);

实际上是将HariMain中for循环部分进行修改,首先把最初读到的0xfa舍弃掉。

之后,每次从鼠标那里送过来的数据都应该是3个字节一组的,所以每当数据累积到3个字节,就把他显示在屏幕上。

变量mouse_phase用来记住接受鼠标数据的工作进展到了什么阶段(phase)。

接受到的数据放在mouse_dbuf[0~2]内。

各种处理;

各种处理;

这部分就是对于不同的mouse_phase值,相应地做各种不同的处理。

显示结果如下(鼠标移动过):

屏幕上除了括号内的还有三字节数字,即mouse_dbuf[0],mouse_dbuf[1],mouse_dbuf[2]里的数据。

“08”部分0会在0~3的范围内变化,这里鼠标左移时显示1,下移时显示2,这是演讲的同学提出的,湛林莉她观察还真是厉害,不仅将上下左右,还把右下、坐下等也看了出来。

“8”只有在点击鼠标时才会有变化,值在8~F之间。

第二个字节与鼠标的左右移动有关,第三个字节与鼠标的上下移动有关。

这里的信息方便下面为鼠标的动作做出判断。

2.整理

在HariMain函数中出现的unsignedcharmouse_dbuf[3],mouse_phase;

声明可以放到函数前的结构体里:

structMOUSE_DEC{

unsignedcharbuf[3],phase;

};

123

并在这句话修改为:

structMOUSE_DECmdec;

1

创建的这个结构体MOUSE_DEC,DEC是decode的缩写,用这个结构日把鼠标所需要的变量都归总到一块儿。

然后将鼠标的解读从函数HariMain的接受信息处理中剥离出来,放到了mouse_decode函数。

3.鼠标解读

(2)

intx,y,btn;

intmouse_decode(structMOUSE_DEC*mdec,unsignedchardat)

{

if(mdec->

phase==0){

/*等待鼠标的0xfa的阶段*/

if(dat==0xfa){

mdec->

phase=1;

return0;

phase==1){

/*等待鼠标第一字节的阶段*/

if((dat&

0xc8)==0x08){

/*如果第一字节正确*/

buf[0]=dat;

phase=2;

phase==2){

/*等待鼠标第二字节的阶段*/

buf[1]=dat;

phase=3;

phase==3){

/*等待鼠标第三字节的阶段*/

buf[2]=dat;

btn=mdec->

buf[0]&

0x07;

x=mdec->

buf[1];

y=mdec->

buf[2];

if((mdec->

0x10)!

=0)

x|=0xffffff00;

0x20)!

=0){

y|=0xffffff00;

y=-mdec->

y;

/*鼠标的y方向与画面符号相反*/

return1;

return-1;

/*应该不可能到这里来*/

}

结构体里增加的几个变量用于存放解读结果,这几个变量是x、y和btn,分别用于存放移动信息和鼠标按键状态。

if(mdec->

phase==1)这个语句用于判断第一字节对移动有反应的部分是否在0~3的范围内;

同时还要判断第一字节对点击有反应的部分是否在8~F的范围内,如果不在以上数据范围内就被舍去。

这样做是因为鼠标连线可能会由接触不良,这样产生的数据就有错位,不能顺利解读。

phase==3)语句是解读处理的核心。

鼠标键的状态放在buf[0]的低3位,我们只取出这3位。

十六进制的0x07相当于二进制的00000111,通过与运算(&

)取出低3位。

x,y基本上直接使用buf[1]和buf[2],但是需要使用第一字节中对鼠标移动有反应的几位,将x和y的第8位及第8位以后全部都设成1,或全部都保留为0,就能正确解读x和y。

解读最后对y符号进行了取反操作是因为鼠标与屏幕的y方向正好相反,为了配合画面方向,就对y符号进行了取反操作。

鼠标数据解读完成之后接下来修改显示部分:

elseif(fifo8_status(&

if(mouse_decode(&

mdec,i)!

=0)/*3字节都凑齐了,所以把它们显示出来*/

[lcr%4d%4d]"

mdec.x,mdec.y);

if((mdec.btn&

0x01)!

s[1]='

L'

0x02)!

s[3]='

R'

0x04)!

s[2]='

C'

scrnx,COL8_008484,32,16,32+15*8-1,31);

4.移动鼠标指针

现在就是让鼠标指针在屏幕上动起来啦,感觉好激动,终于能动了(白眼)。

/*鼠标指针的移动*/

scrnx,COL8_008484,mx,my,mx+15*8-1,my+15);

/*隐藏鼠标*/

mx+=mdec.x;

my+=mdec.y;

if(mx<

x)

mx=0;

if(my<

0)

my=0;

if(mx>

binfo->

scrnx-16)

mx=binfo->

scrnx-16;

if(my>

scrny-16)

my=binfo->

scrny-16;

(%sd,%3d)"

mx,my);

scrnx,COL8_008484,0,0,79,15);

/*隐藏坐标*/

scrnx,0,0,COL8_FFFFFF,s);

/*显示坐标*/

putfonts8_8(binfo->

scrnx,16,16,mx,my,mcursor,16);

/*描画鼠标*/

先隐藏到鼠标指针,然后根据取得的鼠标数据解读得到的位移量,让鼠标显示在屏幕上。

mx+=mdec.x;

my+=mdec.y;

是为了防止鼠标指针跑到屏幕外进行的调整。

5.通往32位模式之路

这里讲解了asmhead.nas中的程序。

PIC关闭一切中断

根据AT兼容机的规格,如果要初始化PIC,

必须在CLI之前进行,否则有时会挂起,

随后进行PIC的初始化

MOVAL,0xff

OUT0x21,AL

NOP;

如果连续执行OUT指令,有些机种会无法正常运行

OUT0xa1,AL

CLI;

禁止CPU级别的中断

这段程序等同于一下内容的C程序。

io_out8(PIC0_IMR,0xff);

/*禁止主PIC的全部中断*/

io_out8(PIC1_IMR,0xff);

/*禁止从PIC的全部中断*/

Io_cli();

/*禁止CPU级别的中断*/

为了让CPU能够访问1MB以上的内存空间,设定A20GATE

CALLwaitkbdout

MOVAL,0xd1

OUT0x64,AL

MOVAL,0xdf;

enableA20

OUT0x60,AL

CALLwaitkbdout1234567

这里的waitbdout等同于wait_KBC_sendread,等同于C语言中的:

#defineKEYCMD_WRITE_OUTPORT0xd1

#defineKBC_OUTPORT_A20G_ENABLE0xdf

/*A20GATE的设定*/

Wait_KBC_sendready();

Io_out8(PORT_KEYCMD,KEYCMD_WRITE_OUTPORT);

Waite_KBC_sendready();

Io_out8(PORT_KEYDATA,KBC_OUTPORT_A20G_ENABLE);

/*这句话是为了等待完成执行指令*/

程序的基本结构与init_keyboard完全相同,功能仅仅是往键盘控制电路发送指令。

这里发送的指令,是指令键盘控制电路的附属端口输出0xdf。

这个附属端口,连接着主板上的很多地方,通过这个端口发送不同的指令,就可以实现各种各样的控制功能。

这次输出0xdf所要完成的功能,是让A20GATE信号线变成ON的状态。

这条信号线的作用是使内存的1MB以上的部分变成可使用状态。

Waite_KBC_sendready();

是多余的,在此之后,虽然不会往键盘送命令,但仍然要等到下一个命令能够送来为止。

这是为了等待A20GATE的处理切实完成。

切换到保护模式

[INSTRSET"

i486p"

];

“想要使用486指令”的叙述

LGDT[GDTR0];

设定临时GDT

MOVEAX,CR0

ANDEAX,0x7fffffff;

设bit31为0(为了禁止颁)

OREAX,0x00000001;

设bit0为1(为了切换到保护模式)

MOVCR0,EAX

JMPpipelineflush

pipelineflush:

MOVAX,1*8;

可读写的段32bit

MOVDS,AX

MOVES,AX

MOVFS,AX

MOVGS,AX

MOVSS,AX

INSTRSET指令,是为了能够使用386以后的LGDT,EAX,CR0等关键字。

LGDT指令将暂定的GDT读进来。

然后将CR0这一特殊的32寄存器的值带入EAX,并将最高位置为0,最低位置为1,再将这个值返回给CR0寄存器。

这样就完成了模式转换,进入到不用颁的保护模式。

CR0,也就是controlregister0,是一个非常重要的寄存器,只有操作系统才能操作它。

保护模式是指操作系统受到CPU的保护,应用程序既不能随便改变段的设定,又不能使用操作系统专用的段。

通过带入CR0而切换到保护模式时,要马上执行JMP指令。

因为变成保护模式后,机器语言的解释要发生变化。

CPU为了加快指令的执行速度而使用了管道(piprline)这一机制。

也就是说,前一条指令还在执行的时候就开始解释下一条甚至再下一条指令。

因为模式变了,就要重新解释一遍,所以加入了JMP指令。

并且在进入保湿模式以后,段寄存器的意思也变了(不再是乘以16后再加算的意思了),除了CS以外所有段寄存器的值都从0x0000变成了0x0008.CS保持原状是因为如果CS也变了,会造成混乱,所以只有CS要放到后面再处理。

0x0008,相当于“gdt+1”的段。

bootpack的传送

MOVESI,bootpack;

传送源

MOVEDI,BOTPAK;

传送目的地

MOVECX,512*1024/4

CALLmemcpy

磁盘数据最终转送到它本来的位置去

首先从启动扇区开始

MOVESI,0x7c00;

MOVEDI,DSKCAC;

MOVECX,512/4

所有剩下的

MOVESI,DSKCAC0+512;

MOVEDI,DSKCAC+512;

MOVECX,0

MOVCL,BYTE[CYLS]

IMULECX,512*18*2/4;

从柱面数变换为字节数/4

SUBECX,512/4;

减去IPL

这部分程序只是在调用memcpy函数,同样可以理解成C语言形式(写法可能不正确,但中心思想是类似的):

memcpy(bootpack,BOTPAK,512*1024/4);

memcpy(0x7c00,DSKCAC,512/4);

memcpy(DSKCAC0+512,DSKCAC+512,cyls*512*18*2/4-512/4);

函数mencpy是赋值内存的函数,语法如下:

memcpy(转送源地址,转送目的地址,转送数据的大小);

转送数据大小是以双字节位单位的,所以数据大小用字节数除以4来指定。

memcpy(0x7c00,DSKCAC,512/4);

DSKCAC是0x00100000,所以上面这句话就是从0x7c00复制512字节到0x00100000。

这正好是将启动扇区复制到1MB以后的内存去。

它的意思是将始于0x00008200的磁盘内容,复制到0x00100200那里。

上文中“转送数据大小”的计算有点复杂,因为它是以柱面数来计算的,所以需要减去启动区的那一部分长苏。

IMUL是乘法运算,SUB是减法运算。

bootpack是asmhead,nas的最后一个标签,haribote.hrb连接起来而生成的,所以asmhead结束的地方,紧接着串联着bootpack.hrb最前面的部分。

→从bootpack的地址开始的512KB内容复制到0x00280000号地址去

必须有asmhead来完成的工作,至此全部完毕

以后就变由bootpack来完成

bootpack的启动

MOVEBX,BOTPAK

MOVECX,[EBX+16]

ADDECX,3;

ECX+=3;

SHRECX,2;

ECX/=4;

JZskip;

没有要转送的东西时

MOVESI,[EBX+20];

转送源

ADDESI,EBX

MOVEDI,[EBX+12];

转送目的地

skip:

MOVESP,[EBX+12];

栈初始值

JMPDWORD2*8:

0x0000001b

这里仍然是在做memcpy,它对bootpack.hrb的header进行解析,将执行必需的数据传送过去。

EBX里带入的是BOTPAK,所以值如下:

[EBX+16]……bootpack.hrb之后的第16号地址。

值是0x11a8

[EBX+20]……bootpack.hrb之后的第20号地址。

值是0x10c8

[EBX+12]……bootpack.hrb之后的第12号地址。

值是0x00310000

上面这些值是通过二进制编辑器,打开harib05d的bootpack.hrb后确认的。

这些值因harib的版本不同而有所变化。

SHA指令是向右移位指令,相当于“ECX>

>

=2;

”,JZ时条件转移指令,来自英文jumpifzero,根据前一个计算结果是否为0来决定是否跳转。

最终这个memcpy的作用是将bootpack.hrb第0x10c8字节开始的0x11a8字节复制到0x00310000号地址去。

最后将0x310000代入到ESP里,然后用一个特别的JMP指令,将2*8代入到CS里,同时移动到0x1b号。

这里的0x1b号地址是指第2个段的0x1b号地址。

第2个段的基地址是0x280000,所以实际上是从0x28001b开始执行的。

也就是bootpack.hrb的0x1b号地址。

然后是这个我们制作的这个“纸娃娃系统”的内存分布图:

0x00000000-0x000fffff:

虽然在启动中会多次使用,但之后就变空。

(1MB)

0x00100000-0x00267fff:

用于保存软盘的内容。

(1440KB)

0x00268000-0x0026f7ff:

空(30KB)

0x0026f800-0x0026ffff:

IDT(64KB)

0x00270000-0x0027ffff:

GDT(64KB)

0x00280000-0x002fffff:

bootpack.hrb(512KB)

0x00300000-0x003ffff:

栈及其他(1MB)

0x00400000-:

 

waitkbdout:

INAL,0x64

ANDAL,0x02

INAL,0x60;

空读(为了清空数据接收缓冲区中的垃圾数据)

JNZwaitkbdout;

AND的结果如果不是0,就跳到waitkbdout

RET

waitbdout与wait_KBC_sendready相同,但也添加了部分处理,就是从0x60号设备进行IN的处理。

如果控制器里有键盘代码,或者已经积累了鼠标数据,就顺便把它们读取出来。

JNC与JZ相反,意思是“jumpifnotzero”

memcpy:

MOVEAX,[ESI]

ADDESI,4

MOV[EDI],EAX

ADDEDI,4

SUBECX,1

JNZmemcpy;

减法运算的结果如果不是0,就跳转到memcpy

复制内存的程序。

ALIGNB16

GDT0:

RESB8;

NULLsele

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 总结汇报 > 学习总结

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1