http://hi.baidu.com/xshaonan/blog/item/6a3fca976ec5e26755fb965d.html
;********************************************
; A20.inc
; --- Enable A20 address line
; 方法一: 系统控制端口A
; 方法二: BIOS中断例程
; 方法三: 键盘控制器
; 方法四: 键盘控制器输出端口
;********************************************
%ifndef __A20_INC_67343546FDCC56AAB872_INCLUDED__
%define __A20_INC_67343546FDCC56AAB872_INCLUDED__
bits 16
;---------------------------------------
; 方法一: (system control)系统控制IO端口0x92,可用来实现对A20的开启与关闭,但因为0x92端口的细节与功能
; 和制造商有很多关系,所以这种方法移植性比较差
; Bit 0 - Setting to 1 causes a fast reset (Used to switch back to real mode)
; Bit 1 - 0: disable A20; 1: enable A20
; Bit 2 - Manufacturer defined
; Bit 3 - power on password bytes (CMOS bytes 0x38-0x3f or 0x36-0x3f). 0: accessible, 1: inaccessible
; Bits 4-5 - Manufacturer defined
; Bits 6-7 - 00: HDD activity LED off; any other value is "on"
;-----------------------------------------
;----------------------
; 打开地址线
;----------------------
EnableA20_SysControlA:
push ax
in al, 92h
or al, 00000010b
out 92h, al
pop ax
ret
;----------------------
; 关闭地址线
;----------------------
DisableA20_SysControlA:
push ax
in al, 92h
and al, 11111101b
out 92h, al
pop ax
ret
;-----------------------------------------
;方法二: 使用BIOS INT 0x15
; ax=2400h --- 关闭A20地址线
; ax=2401h --- 开启A20地址线
; ax=2402h --- 获取A20当前状态
; ax=2403h --- 查询A20
;-----------------------------------------
;----------------------
; 打开地址线
;----------------------
EnableA20_Bios:
pusha
mov ax, 2401h
int 0x15
popa
ret
;----------------------
; 关闭地址线
;----------------------
DisableA20_Bios:
pusha
mov ax, 2400h
int 0x15
popa
ret
;-----------------------------------------
;方法三: 使用键盘控制器(KBC)(这里指的都是8043 keyboard Controller), 这个方法用得比较好,可移植性比较高
; Note: 设备控制器位于CPU与设备之间,它既要与CPU通信,又要与设备通信,还具有按照CPU所发来的
; 命令去控制设备工作的功能,因此,现有的大多数控制器有以下三部分:
; 1)设备控制器与处理机的接口
; 2)设备控制器与设备的接口
; 3)I/O控制逻辑
;为了更好地理解控制器,必须先知道控制器内部IO端口所包含的寄存器及其端口映射地址
; *************************
; 端口 读/写 描述
; 0x60 读 读输入缓冲
; 0x60 写 写输出缓冲
; 0x64 读 读状态寄存器
; 0x64 写 发送命令到控制器
; **************************
; 通过写0x64端口命令字节来发生命令给控制器. 如果这命令带有参数,则被发送到0x60端口.
;-----------------------------------------
;-----------------------------------------
; 从0x64端口读取控制器的状态信息
;Bit 0: Output Buffer Status
; 0: Output buffer empty, dont read yet
; 1: Output buffer full, please read me :)
;Bit 1: Input Buffer Status
; 0: Input buffer empty, can be written
; 1: Input buffer full, dont write yet
;-----------------------------------------
; Note:在操作系统课程中,CPU与外设之间交换信息即I/O控制方式的宗旨就
; 是: 尽量减少主机对I/O控制的干预,把主机从繁杂的I/O控制事务中
; 解脱出来(因为CPU的运行速度比I/O设备的运行速度远快得多),以便
; 更多地去完成数据处理的任务.
wait_input:
in al, 0x64 ;读取状态寄存器
test al, 0x10 ;测试输入缓冲
jnz wait_input ;如果输入缓冲非空,则继续等待
ret
wait_output:
in al, 0x64
test al, 0x01 ;测试输出缓冲
jz wait_output ;如果输出缓冲非空,则继承等待
ret
;---------------------------
; 方法三: 通过控制器
;---------------------------
EnableA20_KKbrd:
cli
push ax
mov al, 0xdd ;comand 0xdd: 开启a20
out 0x64, al ;发送command到控制器
pop ax
ret
;---------------------------
; 方法四: 通过控制器的输出端口
;----
; 键盘控制器的命令
; 0xD0 Read Output Port
; 0xD1 Write Output Port
; 0xDD Enable A20 Address Line
; 0xDF Disable A20 Address Line
;Note: I/O控制方式有: 程序I/O方式(即程序查询); 中断驱动; DMA方式; I/O通道
;---------------------------
EnableA20_KKbrd_Out:
cli
pusha
call wait_input
mov al, 0xad
out 0x64, al ; CPU发送关闭键盘命令给I/O控制器
call wait_input
mov al, 0xd0 ; CPU发送命令让控制器去读输出端口
out 0x64, al
call wait_output
in al, 0x60
push eax ;获取输出端口数据并存放在堆栈中
call wait_input
mov al, 0xd1
out 0x64, al ; 让控制器去写输出端口
call wait_input
pop eax
or al, 2
out 0x60, al
call wait_input
mov al, 0xae ; 开启键盘
out 0x64, al
call wait_input
popa
sti
ret
%endif