【30天自制操作系统】 鼠标

Abstract: 《30天自制操作系统》鼠标显示
Keywords: 《30天自制操作系统》,Macbook,鼠标

啊哈,鼠标

今天看的有点纠结,因为竟然看困了,很少有这种情况,主要是有好多东西作者并没有做深入介绍,只是说这样是对的,至于为啥这样以后再说,这让我感觉很不爽。具体来说说。
首先,先解决了鼠标运动的问题,我感觉这个不是什么大问题,只是读取数据那需要点技巧,就是验证数据的有效性,以前用单片机发送串口数据时也用到过类似的做法,作者提出的是检查第一个字的低八位以及高两位,如果高两位是0,而低八位是8,那么数据没问题,但并不一定绝对没问题,比如发送的数据为 0x08 0x00 0x11下一组为0x08 0x08 0x01时也就是队列里面是0x08 0x00 0x11 0x08 0x08 0x01时如果红色字体丢失任意一组数据,其结果和下一组结果都是错误的,但发生的概率很小,我们也没必要要求硬件每发送一个数据都要加上校验位,那样冗余太大,也浪费资源,而且估计鼠标也不会总坏。。。
个人来讲,我不想将桌面作为系统开发的前期所应该做的(等我自己写的时候不会这么弄),一个黑漆漆的命令行而具有更强大的功能,我觉得更好,而且将桌面和系统剥离开,也就是linux那样,感觉更加自由。哈哈,这都是自己的想法,没有任何科学根据。接下来就到了让我纠结的地方了,就是作者解释了之前没解释的汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
; 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. 打开中断。

今天就这样。。明天继续看。。。谢谢收看。。

0%