System模块在磁盘上的位置紧接着setup.s所在扇区,即0磁道0磁头3扇区。该模块被读到内存地址0x10000开始处,模块长度0x30000。读入该模块的代码被封装到了read_it函数中。
由于实模式下段偏移寄存器只有16位,最多寻址0x10000内的地址,所以无法在不改变段寄存器的情况下读入整个system模块。程序选择的方法是每次读入0x10000,然后将段寄存器加上这个偏移,反复执行直到读完。
在一次大小为0x10000读取中,程序要比较剩余需要读取的扇区数a和当前磁道、磁头下剩余可读扇区数b的大小。如果a>b,则读取b个扇区;否则读取a个扇区。一次读取完成后,要重新设置当前磁道号、磁头号和扇区号,以及内存缓冲区位置,包括段寄存器和段偏移。
循环过程中各关键变量的名字如下:track=当前磁道号,head=当前磁头号,sread=当前已读扇区数,ES:bx=缓冲区头部位置,al=一次读取中需要读取的扇区数。
函数的关键流程图如下:
附上汇编代码:
read_it: !在进入函数之前es被初始化为0x1000
mov ax,es
test ax,#0x0fff !测试es低12位是否为0,实模式下ES还有四位隐藏位。实际测试低16位是否为0,即测试es是否位于64kb边界。test指令对两个操作数位与,若结果为0则ZF标志位置位。
die: jne die ! es must be at 64kB boundary 若ZF=0,死机;否则初始化段偏移bx=0
xor bx,bx ! bx is starting address within segment
rp_read: !开始进入读循环,循环终止条件为es=ENDSEG(0x4000)
mov ax,es
cmp ax,#ENDSEG ! have we loaded all yet?
jb ok1_read
ret
ok1_read:
seg cs !表示下一条指令的操作数sectors(每磁道最大扇区数)地址在代码段中
mov ax,sectors
sub ax,sread
mov cx,ax !cx=sectors-sread,求得当前磁道剩余未读扇区数
shl cx,#9 !cx左移9位,即cx*512转化成字节数
add cx,bx !检测cx+bx是否超过了64kb(16位寄存器)。若超过,即无法一次性将磁盘剩余扇区全部读进,需重新计算可读扇区
jnc ok2_read !两条指令判断若cx+bx<=64kb,则直接读取磁盘剩余扇区,ax=磁盘剩余扇区数
je ok2_read
xor ax,ax !若cx+bx>64kb,则重新计算可读扇区数ax=(64kb-bx)>>9
sub ax,bx !ax-bx,这里ax=0,所以等价于64kb-bx。无符号数相减的神奇规则!作者在许多地方都使用了这种巧妙的处理,比如在管道中计算可读数据大小。
shr ax,#9
ok2_read:
call read_track !实际的读操作函数,利用了bios int13中断。
mov cx,ax
add ax,sread !读完之后更新sread
seg cs
cmp ax,sectors !sread=sectors?当前磁道扇区是否已经读完?
jne ok3_read
mov ax,#1
sub ax,head !若磁道扇区已经读完,则试图将磁头号递增,并判断磁头号是否越界。软盘最多两个磁头号0,1。所以判断当前磁头号是否为1可知磁头号是否到达边界。无论哪种情况执行该指令后ax中存放的是应该更新的磁头号
jne ok4_read !若磁头号未越界,则递增磁头号;否则递增磁道号,并重置磁头号
inc track
ok4_read:
mov head,ax
xor ax,ax
ok3_read:
mov sread,ax !这三条指令更新bx的值,bx<-bx+cx,cx是上次读取的字节数
shl cx,#9
add bx,cx
jnc rp_read !若bx+cx>=64kb,则要递增段寄存器ES,并重置段偏移bx
mov ax,es
add ax,#0x1000
mov es,ax
xor bx,bx
jmp rp_read
read_track:
push ax
push bx
push cx
push dx
mov dx,track
mov cx,sread
inc cx
mov ch,dl
mov dx,head
mov dh,dl
mov dl,#0
and dx,#0x0100
mov ah,#2
int 0x13
jc bad_rt
pop dx
pop cx
pop bx
pop ax
ret
bad_rt: mov ax,#0
mov dx,#0
int 0x13
pop dx
pop cx
pop bx
pop ax
jmp read_track