今天看的有点纠结,因为竟然看困了很少有这种情况,主要是有好多东西作者并没有做深入介绍,只是说这样是对的,至于为啥这样以后再说,这让我感觉很不爽。具体来说说。
首先,先解决了鼠标运动的问题,我感觉这个不是什么大问题,只是读取数据那需要点技巧,就是验证数据的有效性,以前用单片机发送串口数据时也用到过类似的做法,作者提出的是检查第一个字的低八位以及高两位,如果高两位是0,而低八位是8,那么数据没问题,但并不一定绝对没问题,比如发送的数据为 0x08 0x00 0x11下一组为0x08 0x08 0x01时也就是队列里面是0x08 0x00 0x11 0x08 0x08 0x01时如果红色字体丢失任意一组数据,其结果和下一组结果都是错误的,但发生的概率很小,我们也没必要要求硬件每发送一个数据都要加上校验位,那样冗余太大,也浪费资源,而且估计鼠标也不会总坏。。。
个人来讲,我不想将桌面作为系统开发的前期所应该做的(等我自己写的时候不会这么弄),一个黑漆漆的命令行而具有更强大的功能,我觉得更好,而且将桌面和系统剥离开,也就是linux那样,感觉更加自由。哈哈,这都是自己的想法,没有任何科学根据。接下来就到了让我纠结的地方了,就是作者解释了之前没解释的汇编代码:
; haribote-os boot asm
; TAB=4
BOTPAK EQU 0x00280000 ; c语言编写部分的入口位置
DSKCAC EQU 0x00100000 ; 启动区将要被复制到的地方
DSKCAC0 EQU 0x00008000 ; dskcaco启动区存储地址,其实里面没啥,知识为了预留,这也是之前纠结的地方
; BOOT_INFO関係
CYLS EQU 0x0ff0 ;
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ;
SCRNX EQU 0x0ff4 ;
SCRNY EQU 0x0ff6 ;
VRAM EQU 0x0ff8 ;
ORG 0xc200 ;
; 显示设置,中断号10H具体中断用法参照google
MOV AL,0x13 ;
MOV AH,0x00
INT 0x10
MOV BYTE [VMODE],8 ;
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000
; 设置键盘中断,中断号16H,自行百度
MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL
;
; PIC必须在CLI之前设置好
; 有些机型不能连续使用OUT指令,中间要停一下
; PIC初始化
; CLI指令后CPU屏蔽所有中断
MOV AL,0xff
OUT 0x21,AL
NOP ;
OUT 0xa1,AL
CLI ;
; 开启A20设置,这里的具体原理见下文。。
CALL waitkbdout
MOV AL,0xd1
OUT 0x64,AL
CALL waitkbdout
MOV AL,0xdf ; enable A20
OUT 0x60,AL
CALL waitkbdout
; 这就是个简单设置并等待反馈的过程,书上说可以同时读取鼠标和键盘。。。我没看出来
[INSTRSET "i486p"] ; 指令集,不知道酷睿2应该咋表示0.0
LGDT [GDTR0] ; 设定GDT
MOV EAX,CR0
AND EAX,0x7fffffff ;
OR EAX,0x00000001 ; 将EAX设置成二进制 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxx1的形式
MOV CR0,EAX ;CR0很高端的寄存器,只有操作系统可以使用
JMP pipelineflush
pipelineflush:
MOV AX,1*8 ; 1*8不知道是啥意思。。。。。
MOV DS,AX
MOV ES,AX
MOV FS,AX
MOV GS,AX
MOV SS,AX
; bootpack
MOV ESI,bootpack ; 复制的原始地址
MOV EDI,BOTPAK ; 复制的目标地址
MOV ECX,512*1024/4 ;DWORD是单位
CALL memcpy
MOV ESI,0x7c00 ; 同上
MOV EDI,DSKCAC ; 同上
MOV ECX,512/4
CALL memcpy
; 残り全部
MOV ESI,DSKCAC0+512 ;
MOV EDI,DSKCAC+512 ;
MOV ECX,0
MOV CL,BYTE [CYLS]
IMUL ECX,512*18*2/4 ;
SUB ECX,512/4 ; 真心不知道这步干啥用
CALL memcpy
; bootpack开启
MOV EBX,BOTPAK
MOV ECX,[EBX+16]
ADD ECX,3 ; ECX += 3;
SHR ECX,2 ; ECX /= 4;
JZ skip ; 如果哦ECX>>2==0,则跳转
MOV ESI,[EBX+20] ; 还是复制。。
ADD ESI,EBX
MOV EDI,[EBX+12] ;
CALL memcpy
skip:
MOV ESP,[EBX+12] ; 不懂在干啥。。。
JMP DWORD 2*8:0x0000001b
waitkbdout:
IN AL,0x64
AND AL,0x02
JNZ waitkbdout ; 读入0x64的响应和0x02做与运算,非零则跳转
RET
memcpy:
MOV EAX,[ESI]
ADD ESI,4
MOV [EDI],EAX
ADD EDI,4
SUB ECX,1
JNZ memcpy ; 复制过程比较简单
RET
ALIGNB 16
GDT0:
RESB 8 ;
DW 0xffff,0x0000,0x9200,0x00cf ;
DW 0xffff,0x0000,0x9a28,0x0047 ; 不知道在干嘛。。写了这么多奇怪的地址
DW 0
GDTR0:
DW 8*3-1 ;依旧不知道8*3哈意思
DD GDT0
ALIGNB 16
bootpack:
好吧,我的理解就只能到上面这个地步,不知道再继续往下看还能多领悟点不了。。
为什么是向键盘发送指令来控制A20GATE呢?百度到的原因是为了控制是否使用超过1M以上的内存,并和16位兼容,IBM的老爷爷们使用键盘控制器剩下的一根信号线来控制A20,A20不是控制是否进入保护模式。
原始16位处理器访问最高地址为0xFFFF:0xFFFF=0xFFFF0+0xFFFF=0x10EFFEF,很明显多于1M对于多出来的0xEFFEF的访问就需要另外的地址线。但是系统的做法是当程序员访问多于1M的地址时,CPU将地址按1M取模。。这样就不会超过1M了,这种技术被称为wrap-around
到了80286出现了一些问题,当程序员试图访问1M到0x10EFFEF时系统并没有循环回去而是直接访问,这导致了和之前产品不兼容,于是为了兼容,设计了第21根信号线就是上面提到的键盘控制器多余的这根,称为A20来控制是否访问1M以后的地址,当设置为打开时可以访问到多于1M的地址,关闭时则只能循环回0按8086的方式访问。
以上都是实模式下的,在保护模式下,CPU访问的内存增加,如果这个20位的控制线不被打开,那我们的第20号位地址将被视为无效,内存将会被切割成小碎片,系统将只能访问以基数兆的内存。。所以必须要打开A20才能完整访问。至于A20和键盘的关系。。。就是没啥关系,只不过用一个控制器。。。
一下内容来自互联网(没有验证准确性):
多数PC都使用键盘控制器(8042芯片)来处理A20Gate。
从理论上讲,打开A20Gate的方法是通过设置8042芯片输出端口(64h)的2nd-bit,但事实上,当你向8042芯片输出端口进行写操作的时候,在键盘缓冲区中,或许还有别的数据尚未处理,因此你必须首先处理这些数据。
流程如下:
1. 禁止中断;
2. 等待,直到8042 Inputbuffer为空为止;
3. 发送禁止键盘操作命令到8042Input buffer;
4. 等待,直到8042 Inputbuffer为空为止;
5. 发送读取8042 OutputPort命令;
6. 等待,直到8042 Outputbuffer有数据为止;
7. 读取8042 Outputbuffer,并保存得到的字节;
8. 等待,直到8042 Inputbuffer为空为止;
9. 发送Write 8042Output Port命令到8042 Input buffer;
10. 等待,直到8042 Inputbuffer为空为止;
11. 将从8042 OutputPort得到的字节的第2位置1(OR 2),然后写入8042 Input buffer;
12. 等待,直到8042 Inputbuffer为空为止;
13. 发送允许键盘操作命令到8042Input buffer;
14. 打开中断。
今天就这样。。明天继续看。。。谢谢收看。。