这个是我学习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 。