bootsect.s文件阅读问题集
? 這個是我學習bootsect.s文件時,看完了本版關于bootsect文件后匯集了一下。
有些問題實在是書上很明顯的地方,我就沒有摘錄上去
有些問題實在和bootsect這個代碼沒有關系,我也沒有摘錄上去
都是大家問的問題,也是自己最想問的問題。我看了幾遍依然還是有很多問題。
其實很多問題,以前大家都問過了,確實是這樣的。
查閱很方便,都是按照行號來的!
----------------------------------------
關于bootsect.s中出現的移位指令
-------------------------------------------------------
操作數的長度用加在指令后的符號表示b(byte, 8-bit), w(word, 16-bits), l(long, 32-bits),如“movb %al, %bl”,“movw %ax, %bx”,“movl %eax, %ebx ”。
如果沒有指定操作數長度的話,編譯器將按照目標操作數的長度來設置。
比如指令“mov %ax, %bx”,由于目標操作數bx的長度為word,那么編譯器將把此指令等同于“movw %ax, %bx”,指令“mov $4, %ebx”等同于指令“movl $4, %ebx”,“push %al”等同于“pushb %al”。
對于沒有指定操作數長度,但編譯器又無法猜測的指令,編譯器將會報錯,比如指令“push $4”。
======================================
L25 .globl begtext,begdata,begbbs,endtext,enddata,endbbs
L26 .text
L27 begintext:
L28 .data
L29 begdata:
L30 .bss
L31 begbbs:
L32 .text
...
.....
.......
L255 .text
L256 endtext:
L257 .data
L258 enddata:
L259 .bss
L260 endbbs:
Q:
可以看出.text .data .bss重疊的,我的疑問是在模塊連接時,.text 和其他模塊的.text合并,.data和其他模塊的.data合并,那不會出現問題?
還有就是語法問題,為什么.text會出現3次?
-------------------------------------------------------------------------------
A:
重疊段的用法通常只用于單個文件。
前后兩個.text開始text段并起定義段“標號”的用途,這樣在帶符號調試時可以分清。bootsect和setup都單獨編譯鏈接,不與其他目標文件合并。在組建內核Image文件時,tools/build.c文件會直接把他們去除各自的頭結構后順序組合在一起。.text就定義其后面的代碼在.text段中。若后面又出現其他.XXXX,則表示.XXXX后面開始是XXXXX段中的代碼或數據。
###########################################################################
最近我在學習as匯編,了解了分段方面的知識。但在閱讀bootsect.s文件時產生了疑問
整個文件的結構是這樣的
.globl begtext,begdata,begbbs,endtext,enddata,endbbs
.text
begintext:
.data
begdata:
.bss
begbbs:
.text
...
.....
.......
.text
endtext:
.data
enddata:
.bss
endbbs:
Q:我不理解這種分段的方法,難道linus為了把代碼段和數據段編譯在一個段內?
即便如此,為什么最后還要有endtext等標示符?
-----------------------------------------------------------
A: bootsect程序都統統放在512字節之內。在這么小的地方沒有必要分離各個sections。
標號begtext和endtext可分別用來指明text段的開始和結束。
##########################################################################
25 .globl begtext, begdata, begbss, endtext, enddata, endbss
26 .text
27 begtext:
28 .data
29 begdata:
30 .bss
31 begbss:
32 .text
Q:我看了一下MASM的語法,然后又看了一下as86的幫助,但是不知道上面幾句話到底起什么作用,在MASM中有定義段的語句,正好對應段寄存器,as86的幫助上說.text,.data是置當前段(set current segment)我想跟MASM中的某些語句相當,但是.text和.bss對應什么段寄存器呢?另外as86中好像沒有看到像MASM那樣在源代碼中分段,是不是as86在匯編的時候自動根據源代碼來劃分相應段?
A:這些偽指令是供編譯器使用的。這些標號供ld86使用。
.text指明程序中的代碼段;.data是數據段;.bss是未初始化的數據區。
注意,上面的“段”的含義并不是段寄存器的意思,它只是指明目標文件或執行文件中的代碼或數據區(塊)。其中對于硬盤上的執行文件來講不含bss,只有到了執行文件被加載到內存中時才會為其分配bss段(區),并且位于data段的后面。
鏈接程序(例如ld)會使用這些偽指令把所有鏈接模塊中的這些段都分別組合在一起,從而在輸出文件中形成已經組合過的代碼和數據部分。這方面的內容可以參考:
http://www.oldlinux.org/cgi-bin/LB5000XP/topic.cgi?forum=1&topic=1139&show=25
########################################################################
Q: 在啟動的文件中定義的幾個globl變量沒有使用是不是可以不要啊
我在閱讀過程中看不出這幾個globl變量有什么用啊
----------------------------------------------------------------------
A: 一般情況下或者需要使用你自己指定的全局變量時不能不要.global。但是我你的所有段都是重疊的并且不使用除了鏈接器使用的幾個全局變量時,就可以省略它們。例如,你可以把bootsect.s和setup.s中的.global定義都去掉。
#######################################################################
Q:下面代碼和上面代碼的效果等價么?
.globl begtext,begdata,begbbs,endtext,enddata,endbbs
.text
begintext:
begdata:
begbbs:
...
.....
.......
endtext:
enddata:
endbbs:
A: 你還需要在.text后給出.data、.bss
==============================================================================
3.
L43? ROOT_DEV=0X306 !指定文件系統設備是第2個硬盤的第一個分區。
Q:
如果內核程序和文件系統安裝在同一個硬盤里時,是否需要把ROOT_DEV=0X301 /dev/hda1?換成第一個硬盤的第一個分區.
還有程序中load setup、取每個磁道扇區數等時,也需要把驅動器號改成讀硬盤標識?
A:
1. 如果要安裝在同一個分區中,那么就需要使用專用獨立的能從文件系統中取得內核Image文件并加載的引導軟件。例如,shoelace,grub,lilo等。本站有人已經自己做了一個專門的。請參考第2個分論壇中(精華區)。
2. 你說的沒錯。很多(基本上全部:)都沒用,可刪除。不過從這點可以看出Linus當時也在不斷學習其他人的東東
L43? ROOT_DEV = 0x306?? ! 0x306-/dev/hd6-第2個硬盤的第1個分區
Q:
為什么ROOT_DEV要指向第2個硬盤的第1個分區,而不是第一個硬盤或者其他分區?有什么特別的道理嗎?
A:
因為當時Linus的機器上有兩個硬盤,而他使用第2個硬盤來開發Linux系統,第1個硬盤上安裝的是MINIX系統。
==============================================================================
L51?? mov cx #256
?????? sub si si
?????? sub di di
?????? rep
?????? movw
Q:
將bootsect.s的模塊移到0x90200處,請問怎么知道bootsect.s匯編好后是256個字?
A:
在bootsect.s最后有:
249L? .org 508
表示249行后面的代碼從508個字節處開始,再往下:
.word ROOT_DEV
.word 0xAA55
共4個字節,所以整個文件大小就是508+4=512個字節。
Linus在這里用了.org也可以采用填充0的方法,不論怎樣,總要使512字節的最后兩個字節 是55 AA(用word表是就是AA55了)
==============================================================================
L56 jmpi go,INITSEG
L57 go: mov ax,cs
jmpi的語法是jmpi 段值,段內偏移
Q:
在匯編里好象標號指的是段內偏移,所以此處的標號go應該是一個段內偏移,感覺是不是有點問題啊??
A:
“jmpi的語法是jmpi 段值,段內偏移”--沒錯,這是as86的語法。
==============================================================================
L77? j?? load_setup
Q:
是否應為:? jmp load_Setup?
A:
不是印刷錯誤,LD86中就有j這條指令,等價于JMP
==============================================================================
L81 <注釋> 利用INT 0x13的第8號子功能讀取磁盤驅動器參數,入口參數ah=0x08,dl=驅動器號(如果是硬盤則要置位7為1)
Q:
驅動器號是誰分配的,又是怎么分配的,為啥硬盤要置位7為1?
A:
是IBM PC機BIOS就這樣規定設計的。
==============================================================================
L87 seg cs? !表示下一條語句的操作數在cs段寄存器所指的段中
Q:
在bootsect.s中定義了sectors, root_dev, sread,head,track等標志符,但是在操作數中含有sectors, root_dev時,需要seg cs, 而操作數含有sread,head,track時,之前卻不用加seg cs,why?
A:
你仔細看一下會發現,其實所有"seg cs"指令都無用。因為這個程序被連接在一個段中,即代碼、數據段都在一起。Linus當時開發時所用的讀盤程序可能是單獨編寫的,或者是從網上下載后修改的。
L87? seg cs
L88? mov sectors,cx
Q:
語句的意思很好理解,但是這時DS與CS段是一樣的,是否這里不加87行語句也是可以的呢?
A:
seg cs可以不用。
==============================================================================
L94 mov ah,0x03
L95xor bh,bh
L96int ox10
A:
第94到96行的語句,是否是顯示串前必須做的呢?我以前在核心的代碼里加調試語句時是沒有先讀光標位置的。是否是第一次顯示時讀一次就可以了
Q:
94-96是必須的。用于設置int 0x10調用功能0x13使用的行列值(dx中,串開始位置)。
若調用串顯示函數時al的最低比特位=1(al=1或3),則光標會在顯示串后被設置在串的結尾處。否則若al=0或2,則在顯示后光標位置不動
==============================================================================
L99 mov bx,#0x0007? !page 0,attribute 7 (normal)
Q:bl=0x07,有什么作用?
A:這是顯示參數,請參考VGA顯示卡資料。
==============================================================================
L153? test ax,#0x0fff
Q:
(1)#0x0fff為啥不是#0x1000呢?
(2)這一步是不是多余呀,因為ax肯定是0x1000,為什么還要測試?出了什么錯才會導致ax和es不等于0x1000?
A:
(1)TEST指令:兩操作數相與不保存結果,只保存特征條件碼(影響Flag標志)。
這句測試所有三個0xf代表的12個比特位應該都為零。若都為0則段值肯定是0xN000,對應段的地址肯定是0xN0000,也即位于64KB邊界上。
(2)因為這是個子程序,它有自己的接口要求。加上這種判斷是良好的編程習慣
==============================================================================
L170xor ax,ax?
L171sub ax,bx
L172shr ax,#9
Q:
170行的代碼有疑問,程序跳轉到read_it 子程序里時,要完成把磁盤里的數據讀入到內存中0x10000地址開始的區域中,整個讀的過程是先檢測本磁道中剩下的扇區所包含的字節總數是不是可以完全放到當前的64K內存段里,如果當前的64K內存段里放不下,就會執行到170行這個地方,170行來了句
xor ax,ax?
sub ax,bx
shr ax,#9
這幾行代碼是想獲得可以讀到當前的64K內存段的扇區個數,存放在al里
對這個地方,我有一點不解,如果是第一次讀,bx就應該是0了,那ax也是0,結果最后al里面也是0了,那下面緊接著就調用INT 13號中斷讀磁盤,al 在13號中斷里的含義是從磁盤讀出的扇區的個數,當al=0時,不知道這個中斷是怎么執行的??
我查了手冊,手冊上也沒講INT 13號中斷的2號子功能調用,在當al=0時調用的結果是什么,手冊上只是說了,al的值在1-128之間是有效的,我就不知道上面講的那種情況INT 13號中斷是怎么處理的了?
A:
第1次讀時bx確實是0,但ax不是0。因為sread最初是1+SETUPLEN,即5。因此(80 - 5)*512 = 38400 = 37.5KB 沒有超過64KB,所以第1次不會執行170行開始的代碼。由于16位寄存器最大只能表示64KB -1,因此這里0表示最大值64KB(已進位)。
軟盤上一個磁道上有80個扇區,硬盤上一個磁道上最多有63個扇區,
這樣的話,一個磁道上的字節總數是肯定小于64K的,也就是說不會出現,第一次讀磁盤就執行到 170行處的:
xor ax,ax
sub ax,bx
shr ax,#9
==============================================================================
L180mov ax,#1
L181sub ax,head
L182jne ok4_read
L183inc track
Q:
當0磁頭的當前道沒有數據可讀后則去讀1磁頭上的數據,想問這種情況是適用于哪種軟驅的?我想過去是個雙面軟驅。如果不是雙面軟驅,則在read_track里則會死循環吧。
A:
PC機使用的軟盤驅動器都是雙頭(雙面)的。沒有見過哪種PC機用的的是單面的
==============================================================================
read_track:
199???????? push ax
200???????? push bx
201???????? push cx
202???????? push dx
203???????? mov dx,track
204???????? mov cx,sread
205???????? inc cx
206???????? mov ch,dl
207???????? mov dx,head
208???????? mov dh,dl
209???????? mov dl,#0
210???????? and dx,#0x0100
211???????? mov ah,#2
212???????? int 0x13
213???????? jc bad_rt <--我用Bochs返匯編出來的是jb... 這個是不是代碼錯了?
214???????? pop dx
215???????? pop cx
216???????? pop bx
217???????? pop ax
218???????? ret
Q:
(1)是不是因為Linux0.11每個段就給了64KB得長度限制?
(2)還有就是bootsect.c里面read_it這部分,jc bad_rt <--我用Bochs返匯編出來的是jb... 這個是不是代碼錯了?
A:
沒有限制在64KB,而是每次讀時最多64KB。
另外,查一下手冊看看jb 是否就是 jc 。
?
?
?
?
總結
以上是生活随笔為你收集整理的bootsect.s文件阅读问题集的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 区分那些是属于构架方面的C++功能
- 下一篇: __VA_ARGS__宏