2009年3月20日 星期五

●memtest86+教學 Part4


(爛豬腳‧‧續)_GLOBAL_OFFSET_TABLE_為基底位址的符號會
得到當前段的起始地址到global offset table的距離(offset),由於此
時已經是32bits的flat4G定址,因此引用這個符號相當等同於取得
.data segment及.bss區的啟始位址,因此我們比較一下為什麼setup.S
就不是用這種方式來計算offset值 ,因為那時還在真實模式,而現在
是在保護模式。
movl $(LOW_TEST_ADR + _GLOBAL_OFFSET_TABLE_), %esp
#LOW_TEST_ADR=0x2000=8k
所以這一行相當於,將堆疊指標暫存器設在.data區開始後的8k處。
/*********************************************************/
/* Load the GOT pointer */

call 0f        #為什要這樣call目的何在,目前I do'nt know.     
0: popl %ebx
addl $_GLOBAL_OFFSET_TABLE_+[.-0b], %ebx
上面這一行就是獲得標籤"0:",也就是popl %ebx這條指令的地址
,然後保存在%ebx中。
/*********************************************************/
/* Reload all of the segment registers */
leal gdt@GOTOFF(%ebx), %eax #取得gdt位址存入eax
movl %eax, 2 + gdt_descr@GOTOFF(%ebx)
#將gdtㄉ基底位址存到gdt_descr+2ㄉ位址
lgdt gdt_descr@GOTOFF(%ebx)
#看到這一行你就應該要知道上一行ㄉ目的 
leal flush@GOTOFF(%ebx), %eax
pushl $KERNEL_CS
pushl %eax
lret
#上面這三行是ㄍ很特殊ㄉ寫法,lret會pop出(cs)segment:offset
#所以相當於程式會繼續網下執行,但由於不能寫成如下:
#movl $KERNEL_CS, %eax
#movw %ax, %cs
#jmp flush 因為cs不可這樣搞

flush: movl $KERNEL_DS, %eax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss

/*
* Zero BSS 將_bss區清0
*/
cmpl $1, zerobss@GOTOFF(%ebx)
jnz zerobss_done
xorl %eax, %eax
leal _bss@GOTOFF(%ebx), %edi
leal _end@GOTOFF(%ebx), %ecx
subl %edi, %ecx
1: movl %eax, (%edi)
addl $4, %edi
subl $4, %ecx
jnz 1b
movl $0, zerobss@GOTOFF(%ebx)
zerobss_done:
此時你腦袋中應該要浮現出到目前為止程式在記
憶體中如何配置如上圖。
但這是沒有在dos環境下跑的流程:所以我們要找時間來看看這個mt86+_loader.asm檔,因此把它弄清楚就可以知道和正常的booting有何不同。

●memtest86+教學 Part3

在我們進入最佳男主角(爛豬腳)ㄉ主題之前,我希望先把makefile及三ㄍlds檔 memtest.bin.lds、memtest.lds、memtest_shared.lds稍加探討探討。若你還不知道什麼是makefile那先去溫習功課再來看這裡。makefile的寫法你去比較一下1.70版,稍有不同,但達成ㄉ效果是一樣的。一般我們很少會去寫lds檔,因為寫apㄉ人搞不好根本就不知道這是什麼東東,lds檔是給linux下gcc附的linker連結器LD所參考用的,若你不指定lds檔,LD還是會叫用預設lds檔,故名思義就是連結器專用的script(描述檔),畢竟memtest86+不是OS下ㄉapplication它等於是ㄍ小型的OS,因為從Boot到run實際應用都是要去和實際硬體溝通,因此lds檔當然要符合它自己ㄉ需求。先截取部分makefile內容:
memtest.bin: memtest_shared.bin bootsect.o setup.o memtest.bin.lds
$(LD) -T memtest.bin.lds bootsect.o setup.o -b binary \
memtest_shared.bin -o memtest.bin
以上敘述: bootsect.o setup.o連結時要參考memtest.bin.lds
以下是
memtest.bin.ldsㄉ內容:
OUTPUT_FORMAT("binary")
OUTPUT_ARCH("i386")

ENTRY(_main);
SECTIONS {
. = 0;
.bootsect : { *(.bootsect) }
.setup : { *(.setup) }
.memtest : {
_start = . ;
*(.data)
_end = . ;
}
_syssize = (_end - _start + 15) >> 4;
}
若沒有全部看懂,先懂一半也行。
其他的makefile內容依此類推,應該難不倒大家。

爛豬腳:head.S
.code32
.globl startup_32
startup_32:
cld
cli
‧‧‧
看到沒,它是32位元的code,而且它沒有.section ㄉ宣告,因為它和Cㄉ部分是整合為同一區段,如下所示:
下面ㄉOBJS除了head.o是組合語言寫ㄉ,其餘都是C寫ㄉ,但這本來就無關緊要,重要ㄉ是這些物件檔被連結成
memtest_shared(依照memtest_shared.ldsㄉ敘述)
OBJS= head.o reloc.o main.o test.o init.o lib.o patn.o screen_buffer.o \
config.o linuxbios.o memsize.o pci.o controller.o random.o extra.o \
spd.o error.o dmi.o

memtest_shared: $(OBJS) memtest_shared.lds Makefile
$(LD) --warn-constructors --warn-common -static -T memtest_shared.lds \
-o $@ $(OBJS) && \
$(LD) -shared -Bsymbolic -T memtest_shared.lds -o $@ $(OBJS)

我們還是看一下
memtest_shared.ldsㄌㄌ等(台語)
OUTPUT_FORMAT("elf32-i386");
OUTPUT_ARCH(i386);

ENTRY(startup_32);
SECTIONS {
. = 0;
.text : {
_start = .;
*(.text)
*(.text.*)
*(.plt)
_etext = . ;
} = 0x9090
.rodata : {
*(.rodata)
*(.rodata.*)
}
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.hash : { *(.hash) }
.dynamic : { *(.dynamic) }

.rel.text : { *(.rel.text .rel.text.*) }
.rel.rodata : { *(.rel.rodata .rel.rodata.*) }
.rel.data : { *(.rel.data .rel.data.*) }
.rel.got : { *(.rel.got .rel.got.*) }
.rel.plt : { *(.rel.plt .rel.plt.*) }

. = ALIGN(4);
.data : {
_data = .;
*(.data)
*(.data.*)
}
.got : {
*(.got.plt)
*(.got)
_edata = . ;
}
. = ALIGN(4);
.bss : {
_bss = .;
*(.dynbss)
*(.bss)
*(.bss.*)
*(COMMON)
/* _end must be at least 256 byte aligned */
. = ALIGN(256);
_end = .;
}
/DISCARD/ : { *(*) }
}

若是看ㄌ一頭霧水,至少你也看到startup_32,它就是head.Sㄉ頭,至於_start和 _end 請看:
memtest: memtest_shared.bin memtest.lds
$(LD) -s -T memtest.lds -b binary memtest_shared.bin -o $@
以下是 memtest.lds內容 0x10000=64k
OUTPUT_FORMAT("elf32-i386");
OUTPUT_ARCH(i386);

ENTRY(_start);
SECTIONS {
. = 0x10000;
_start = . ;
.data : {
*(.data)
}
}

所以startup_32=_startyou know!

2009年3月19日 星期四

●桌面圖示陰影(背景)








這ㄍ問題解法相當簡單:




●memtest86+教學 Part2


mt86+_loader.asm這ㄍ檔案就是我上一篇文章賣的關子,有ㄌ它你可以將memtest86+製作成在Dos底下run的執行檔:
exeh: db "MZ"
dw fullsize % 512 ; how much to load from
dw (fullsize + 511) / 512 ; .exe to RAM
dw 0 ; no relocations used
dw 2 ; header size is 2 * 16 bytes
dw stackpara ; minimum heap is 128 * 16 bytes, for stack
dw stackpara ; we do not need more heap either
dw (fullsize + 15) / 16 ; SS is after file
; segment offsets are relative to PSPseg+10h
; initial DS and ES point to PSPseg, and file
; except headers is loaded to PSPseg+10h.

dw stacksize-4 ; initial SP value
dw 0 ; no checksum
dw 100h ; initial IP
dw -10h ; initial CS relative to PSPseg+10h
dw 0 ; no relocation table, "offset 0 in file"
dw 0 ; this is not an overlay
db "MEMT" ; padding to a multiple of 16 bytes
以上這些資訊是從mt86+_loader.asm截取下來,若你看不明白可以參考這份資訊,其他的看完有問題再說。因為到目前為止都還沒進入主題,所以先進入memtest86+殿堂看看究竟‧‧‧。
註:本文使用版本是memtest86+-2.01;首先看一下上圖有哪些檔案:不好意思好像不是頂清楚。首先我們看我圈選起來的那幾個檔案,先說明main.c吧!不要被檔案名稱給誤導ㄌ,學Cㄉ時候是要你知道main這ㄍroutine而不是檔名,所以這ㄍ檔案根本就不是程式ㄉentry(進入點)所在。
另外三ㄍ副檔名為大寫"S"的檔案bootsect.S、head.S、setup.S才是主角(注意,不要在windows將memtest86+-2.01.tar.gz解壓縮,因為windows不區分檔名大小寫),我並不想針對這三ㄍ檔案涉入太深,否則一樣會廢話一堆,但玩過Linux Kernelㄉ大大們對這三個檔案一定不陌生,網路上也有粉多這方面ㄉ討論,但是這三個檔案到底還是很重要,若你完全看不懂,我想你可能要花一段不算短ㄉ時間將它搞懂,因為你若不懂,則接下來的C語言你會Trace的很吃力,也不踏實,雖然說網路上有針對這三ㄍ檔案ㄉ論述,但畢竟memtest86已經修改過因此我會稍為作說明:
這三個檔案是GAS的語法:你必須自己搞懂GAS(Assembly),但mt86+_loader.asm又是nasm的語法‧‧‧‧這些主題若是我精力過旺,或許以後再另闢文章。
1.bootsect.S:先把自己512Bytes從0x7c00搬移到0x90000,並繼續執行;繼續執行的第一ㄍ動做就是movw %cs, %ax #讓cs值也等於0x9000,接著處理一些磁碟參數問題後將setup.S從1.44mbㄉ磁片讀到0x92000,然後是# we want to load the system (at 0x10000):將整ㄍ程式主體從磁碟讀到0x10000處,這個動作就是call read_it,並從0x90200處開始執行,這ㄍ位址就是setup.Sㄉ啟始處。所以bootsect.S所完成的工作就是將程式從磁碟讀到記憶體後再接著去執行setup.S。
所以若你不作成dos版本ㄉ執行檔,原則上執行ㄉ流程是從bootsect.S開始,但相反ㄉ若作成dos版本ㄉ執行檔則bootsect.S幾乎算是ㄍ廢物,因為mt86+_loader執行完就會跳到setup.S
2.setup.S:這部份就是準備進入保護模式ㄉ一些動作,但它不搞cr0,它用lmsw這ㄍ指令進保護模式,反正道理都一樣,若你有認真去看完setup.S,你會發現它只是單純ㄉ進入保護模式,但這樣ㄉ話也可以在head.S作啊,其實說穿ㄌ它用這ㄍ檔主要是啟用a20位址線,由於這部份方法實作有三個,而它(setup.S)通通作,為什麼?請看我postㄉ文章"Fast A20 和92H,及8042到底是什麼關係";其他(改天再詳述)。
3.head.S:這ㄍ檔案才是最佳男主角‧‧‧待續。

●memtest86+教學 Part1


首先廢話一篇:我一直很渴望能從事bios相關行業,因為我確定很多你我不了解的規範specification是如何實作他們最清楚,即使從事那一行沒有在ami或award、phoenix這種外商大公司,一樣可以學習到很多東西。也因此我的學習格外牛步;但憑藉"新聞挖挖哇"的精神,從一套看似不起眼的軟體,一樣可以精進你的專業,學習memtest86+就等於是在學習如何玩弄記憶體,然而這也是跟底層很近的起點,畢竟資訊軟體業說破ㄌ就真的是在玩弄記憶體,只是玩法不同便衍生出不同的技術、演算法‧‧‧‧‧真不好意思,用"玩弄"兩ㄍ字對自己好像也不是很敬業。
言歸正傳,memtest86+及memtest86有什麼不同,memtest86+發行版本是base on memtest86,並作些修正因此release的版本較慢吧!關於作者Memtest86+ is written by Samuel Demeulemeester ,the original author of memtest86 is Chris Brady 。但"+"就是有加入一些東西ㄉ意思。加入ㄌ什麼,先賣ㄍ關子請往下看‧‧
首先你必須具備什麼技能: 1會使用linux(不用很厲害,只要你可以安裝完成linux,其他慢慢來.)2你要會看懂C及Assembly.3對X86架構要了解(這部份要時間ㄉ累積,以及你棄而不捨ㄉ精神,誰叫你我都是門外漢又找不到高高手來做朋友)。我個人是用Microsoft virtual PC或Sun xVM VirtualBox安裝linux,然後掛起samba sever,在windows用source Insight來編輯code,這樣很方便,也是我ㄍ人的習慣,畢竟windows有些好用的tools,但你若是linux高手就當我是在廢話。以上的畫面是我ㄉvirtual PC 啟動ubuntu linux的畫面。

●Fast A20 和92H,及8042到底是什麼關係

以下是從DOS编程技术這ㄍ大陸博客寫ㄉ資料放ㄌ上來,它裡面還有介紹一些好東西。您可以慢慢參考、品嘗。
早期的PC,控制鍵盤有一個單獨的單片機8042,現如今這個晶片已經給集成到了其它大片子中,但其功能和使用 方法還是一樣,當PC機剛剛出現A20 Gate的時候,估計實在找不到控制它的地方了,同時為這點小事也不值得增加晶片,於是工程師使用這個8042鍵盤控制器來控制A20 Gate,但A20 Gate真的和鍵盤一點關係也沒有,我們先從軟體的角度簡單介紹一下8042這個晶片。
8042有4個寄存器
• 1個8-bit長的Input buffer;Write-Only;
• 1個8-bit長的Output buffer; Read-Only;
• 1個8-bit長的Status Register;Read-Only;
• 1個8-bit長的Control Register;Read/Write。
有兩個埠位址:60h和64h。
• 讀60h埠,讀output buffer
• 寫60h埠,寫input buffer
• 讀64h埠,讀Status Register
對Control Register的操作相對要複雜一些,首先要向64h埠寫一個命令(20h為讀命令,60h為寫命令),然後根據命令從60h埠讀出Control Register的資料或者向60h埠寫入Control Register的資料(64h埠還可以接受許多其它的命令)。
先來看看Status Register的定義,我們後面要用bit 0和bit 1:
bit meaning
-----------------------------------------------------------------------
0 output register (60h) 中有數據
1 input register (60h/64h) 有數據
2 系統標誌(上電重定後被置為0)
3 data in input register is command (1) or data (0)
4 1=keyboard enabled, 0=keyboard disabled (via switch)
5 1=transmit timeout (data transmit not complete)
6 1=receive timeout (data transmit not complete)
7 1=even parity rec'd, 0=odd parity rec'd (should be odd)
除了這些資源外,8042還有3個內部埠:Input Port、Outport Port和Test Port,這三個埠的操作都是通過向64h發送命令,然後在60h進行讀寫的方式完成,其中本文要操作的A20 Gate被定義在Output Port的bit 1上,所以我們有必要對Outport Port的操作及埠定義做一個說明。
• 讀Output Port
向64h發送0d0h命令,然後從60h讀取Output Port的內容
• 寫Output Port
向64h發送0d1h命令,然後向60h寫入Output Port的資料
另外我們還應該介紹兩個命令:
• 禁止鍵盤操作命令
向64h發送0adh
• 打開鍵盤操作命令
向64h發送0aeh
有了這些命令和知識,我們可以考慮操作A20 Gate了,有關8042晶片更詳細的資料,請參考該晶片的Data Sheet。
如何打開和關閉A20 Gate。
理論上講,我們只要操作8042晶片的輸出埠(64h)的bit 1,就可以控制A20 Gate,但實際上,當你準備向8042的輸入緩衝區裡寫資料時,可能裡面還有其它資料沒有處理,所以,我們要首先禁止鍵盤操作,同時等待資料緩衝區中沒 有資料以後,才能真正地去操作8042打開或者關閉A20 Gate。打開A20 Gate的具體步驟大致如下:
1.關閉中斷;
2.等待8042 Input buffer為空;
3.發送禁止鍵盤操作命令;
4.等待8042 Input buffer為空;
5.發送讀取8042 Output Port命令;
6.等待8042 Output buffer有數據;
7.讀取8042 Output buffer,並保存得到的位元組;
8.等待8042 Input buffer為空;
9.發送Write 8042 Output Port命令到8042 Input buffer;
10.等待8042 Input buffer為空;
11.將從8042 Output Port得到的位元組的第2位置1(或清0),然後寫入8042 Input buffer;
12.等待,直到8042 Input buffer為空為止;
13.發送允許鍵盤操作命令到8042 Input buffer;
14. 打開中斷。
下面是完成打開A20 Gate的代碼:
A20Enable:
cli ;1.關閉中斷
call WaitInbufEmpty ;2.等待8042 Input buffer為空;
mov al, 0adh
mov dx, 64h
out dx, al ;3.發送禁止鍵盤操作命令
call WaitInbufEmpty ;4.等待8042 Input buffer為空;
mov al, 0d0h
mov dx, 64h
out dx, al ;5.發送讀取8042 Output Port命令;
call WaitOutbufFull ;6.等待8042 Output buffer有數據;
mov dx, 60h
in al, dx ;7.讀取8042 Output buffer
push ax ;保存讀取的資料
call WaitInbufEmpty ;8.等待8042 Input buffer為空;
mov al, 0d1h
mov dx, 64h
out dx, al ;9.發送寫 8042 Output Port命令
call WaitInbufEmpty ;10.等待8042 Input buffer為空
pop ax
or al, 02h ;11.將從8042 Output Port得到的位元組的bit 1置1
mov dx, 60h
out dx, al ;寫入Output Port
call WaitInbufEmpty ;12.等待8042 Input buffer為空
mov al, 0aeh
mov dx, 64h
out dx, al ;13.發送允許鍵盤操作命令
sti ;開中斷
ret

WaitInbufEmpty:
mov dx, 64h
in al, dx ;讀取Status Register
test al, 02h
jnz WaitInbufEmpty
ret

WaitOutbufFull:
mov dx, 64h
in al, dx
test al, 01 ;讀取Status Register
jz WaitOutbufFull
ret
後來,由於感覺使用8042控制A20運行太慢了(確實,那麼長的代碼,中間還要若干次的wait),所以後來又出現了所謂的Fast A20,實際上,現在的大多數機器都是Fast A20,Fast A20使用92h埠控制A20,同時BIOS裡提供了一個軟中斷來控制A20:
入口:ah=24h
al=0 關閉A20
1 打開A20
2 讀取A20狀態
int 15h
返回:如果BIOS支援此功能,CF=0,否則CF=1
CF=0時,AX返回當前A20狀態,1=打開,0=關閉
像8042中的Output Port中的定義一樣,92h埠的bit 1控制著A20,為1時打開,為0時關閉,從92h中讀一個byte可以看a20的當前狀態,所以對92h的操作如下:
• 讀A20狀態
mov dx, 92h
in al, dx
如果al的bit 1為1表示a20打開,否則為0
• 打開A20
mov dx, 92h
mov al, 02
out dx, al
• 關閉A20
mov dx, 92h
mov al, 0
out dx, al
特別要注意的是,大家從這篇文章的文字中可能也能感覺到,A20 Gate的設計本身就讓人感覺很彆扭,不是那麼流暢,所以和A20有關的事情就難免也會有相同的感覺,很奇怪的是,上面介紹的三種方法並不是在每台機器上 都適用,所以如果你要做一個商務軟體其中要操作A20,那一定要三種方式聯合使用才比較穩妥,否則會有意想不到的結果,LINUX公開的啟動代碼中就是這 麼做的
在DOS下有時我們會在config.sys中寫上一句:dos=high,這句就會把駐留的DOS放到高端記憶體區域去,怎麼放的呢?關鍵點就是打開 A20,然後把DOS從常設記憶體搬到100000h起始的區域去,並把在常設記憶體中佔用的記憶體釋放掉,當然說起來容易,實際做的時候還有很多細節要處理。
說到DOS=HIGH,就不得不提醒大家另一件事,如果在config.sys中有dos=high這一句,那麼恐怕92h的方法和BIOS的方法都會不 完全靈驗(至於操作8042的方法靈不靈我沒有試),這是DOS做了手腳,因為DOS被放到了高端記憶體中,為了保證DOS能正常運行,它不允許你把A20 給關掉,遇到這種情況不要驚慌,不是我寫得不對,確實是DOS太狡猾了。
還有最後一點要特別注意,92h的bit 0是給機器發重定信號的(8042 Output Port的bit 0也是),所以在向92h寫資料時,千萬不要讓bit 0為1,否則機器會重新啟動,如果你的應用程式需要重新開機機器,這也是方法之一,比jmp 0ffffh:0來的還要乾脆。
在其它我們介紹保護模式的文章中,我們會用到上面提到的打開A20的方法,屆時可能就不會做更多的解釋了。
另外,涉及操作A20的資料其實很少,有些資料我手裡也很缺乏,比如92h除bit 0和bit 1以外的定義是什麼我至今也不知道。

更多參考:Gate A20與保護模式

2009年3月18日 星期三

●8042 Keyboard Controller

IO Port 60h為鍵盤的輸入暫存器 、61h為控制暫存器、64h狀態暫存器
8042 - Keyboard Controller
8042 Status Register (port 64h read)
8042 Status Register
Bit0|------output register (60h) has data for system
Bit1|------input register (60h/64h) has data for 8042
Bit2|------system flag (set to 0 after power on reset)
Bit3|------data in input register is command (1) or data (0)
Bit4|------1=keyboard enabled, 0=keyboard disabled (via switch)
Bit5|------1=transmit timeout (data transmit not complete)
Bit6|------1=receive timeout (data transmit not complete)
Bit7|------1=even parity rec'd, 0=odd parity rec'd (should be odd)

Port Mode Description

64h read 8042 status register. Can be read at any time. See table above for more information.
64h write 8042 command register. Writing this port sets Bit3 of the status register to 1 and the byte is treated as a controller command. Devices attached to the 8042 should be disabled before issuing commands that return data since data in the output register will be overwritten.
60h read 8042 output register (should only be read if Bit 0 of status port is set to 1)
60h write 8042 data register. Data should only be written if Bit 1 of the status register is zero (register is empty).
When this port is written Bit 3 of the status register is set to zero and the byte is treated as a data. The 8042 uses this byte if it's expecting data for a previous command, otherwise the data is written directly to the keyboard. See ~KEYBOARD COMMANDS~ for information on
programming the actual keyboard hardware.

8042 Commands Related to PC Systems (Port 64h)

Command Description

20 Read 8042 Command Byte: current 8042 command byte is placed in port 60h.
60 Write 8042 Command Byte: next data byte written to port 60h is placed in 8042 command register. Format:

8042 Command Byte
Bit0|------1=enable output register full interrupt
Bit1|------ should be 0
Bit2|------1=set status register system, 0=clear
Bit3|------ 1=override keyboard inhibit, 0=allow inhibit
Bit4|------disable keyboard I/O by driving clock line low
Bit5|------ disable auxiliary device, drives clock line low
Bit6|------IBM scancode translation 0=AT, 1=PC/XT
Bit7|------reserved, should be 0

A4 Password Installed Test: returned data can be read from port 60h; FA=password installed, F1=no password
A5 Load Security: bytes written to port 60h will be read until a null (0) is found.
A6 Enable Security: works only if a password is already loaded
A7 Disable Auxiliary Interface: sets Bit 5 of command register stopping auxiliary I/O by driving the clock line low
A8 Enable Auxiliary Interface: clears Bit 5 of command register
A9 Auxiliary Interface Test: clock and data lines are tested; results placed at port 60h are listed below:

00 no error
01 keyboard clock line is stuck low
02 keyboard clock line is stuck high
03 keyboard data line is stuck low
04 keyboard data line is stuck high

AA Self Test: diagnostic result placed at port 60h, 55h=OK
AB Keyboard Interface Test: clock and data lines are tested; results placed at port 60h are listed above with command A9
AC Diagnostic Dump: sends 16 bytes of 8042's RAM, current input port state, current output port state and 8042 program status
word to port 60h in scan-code format.
AD Disable Keyboard Interface: sets Bit 4 of command register stopping keyboard I/O by driving the clock line low
AE Enable Keyboard Interface: clears Bit 4 of command register enabling keyboard interface.
C0 Read Input Port: data is read from its input port (which is inaccessible to the data bus) and written to output register at port 60h; output register should be empty before call.

8042 Input Port
Bit0-3|------undefined
Bit4|------ 1=enable 2nd 256K of motherboard RAM, 0=disable
Bit5|------1=manufacturing jumper not installed, 0=installed
Bit6|------ 1=primary display is MDA, 0=primary display is CGA
Bit7|------1=keyboard not inhibited, 0=keyboard inhibited

C1 Poll Input Port Low Bits: Bits 0-3 of port 1 placed in status Bits 4-7
C2 Poll Input Port High Bits: Bits 4-7 of port 1 placed in status Bits 4-7
D0 Read Output Port: data is read from 8042 output port (which is inaccessible to the data bus) and placed in output register;
the output register should be empty. (see command D1 below)
D1 Write Output Port: next byte written to port 60h is placed in the 8042 output port (which is inaccessible to the data bus)

8042 Output Port
Bit0|------system reset line
Bit1|------gate A20
Bit2-3|------undefined
Bit4|------ output buffer full
Bit5|------input buffer empty
Bit6|------keyboard clock (output)
Bit7|------keyboard data (output)

D2 Write Keyboard Output Register: on PS/2 systems the next data
byte written to port 60h input register is written to port 60h
output register as if initiated by a device; invokes interrupt
if enabled
D3 Write Auxiliary Output Register: on PS/2 systems the next data
byte written to port 60h input register is written to port 60h
output register as if initiated by a device; invokes interrupt
if enabled
D4 Write Auxiliary Device: on PS/2 systems the next data byte
written to input register a port at 60h is sent to the
auxiliary device
E0 Read Test Inputs: 8042 reads its T0 and T1 inputs; data is
placed in output register; Bit 0 is T0, Bit 1 is T1:

Test Input Port Bits
Bit0|------keyboard clock
Bit1|------keyboard data

Fx Pulse Output Port: Bits 0-3 of the 8042 output port can be pulsed low for 6 us(微秒);Bits 0-3 of command indicate which Bits should be pulsed; 0=pulse, 1=don't pulse; pulsing
Bit 0 results in CPU reset since it is connected to system reset line.