现在的位置: 首页 > 综合 > 正文

《30天自制操作系统》学习笔记–第8天

2017年11月20日 ⁄ 综合 ⁄ 共 3476字 ⁄ 字号 评论关闭

       今天看的有点纠结,因为竟然看困了大哭很少有这种情况,主要是有好多东西作者并没有做深入介绍,只是说这样是对的,至于为啥这样以后再说,这让我感觉很不爽。具体来说说。

       首先,先解决了鼠标运动的问题,我感觉这个不是什么大问题,只是读取数据那需要点技巧,就是验证数据的有效性,以前用单片机发送串口数据时也用到过类似的做法,作者提出的是检查第一个字的低八位以及高两位,如果高两位是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. 打开中断。

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

抱歉!评论已关闭.