上一次我们已经说到,操作系统的代码从硬盘复制到内存里了。
今天我们一起来看看,setup.s 都做了啥?
_start:
mov %cs,%ax
mov %ax,%ds
mov %ax,%es
#
##print some message
#
mov $0x03, %ah
xor %bh, %bh
int $0x10
mov %dx, %ds:0 # it from 0x90000.
这里又发起了一个10号中断, 上一次我们提到 13号是BIOS提供的读磁盘中断程序。0x10号中断调用显示服务的中断处理程序。
把寄存器ah赋值成0x03是为了给显示服务里,读取光标位置的子服务。
0x10号中断程序结束时,会在dx寄存器里存储光标的位置:
高八位 dh 存储行号,
低八位 dl 存储列号。
注意:计算机加电自检后会自动初始化为文字模式, 一屏25行,每行80列,一列一个字符。
接下来的mov 指令就是把光标位置存储在这个内存地址 给到ds:0的地址处。因为ds现在是0x9000, 所以0x90000里存的就是光标的位置了。
感觉中断和我们平时调用函数 有点类似,只不过用寄存器当作入参和返回值了。
接下来的一坨代码和上面逻辑差不多。调用中断程序获取点信息,存在内存里。
# Get memory size (extended mem, kB)
mov $0x88, %ah
int $0x15
mov %ax, %ds:2
# Get video-card data:
mov $0x0f, %ah
int $0x10
mov %bx, %ds:4 # bh = display page
mov %ax, %ds:6 # al = video mode, ah = window width
# check for EGA/VGA and some config parameters
mov $0x12, %ah
mov $0x10, %bl
int $0x10
mov %ax, %ds:8
mov %bx, %ds:10
mov %cx, %ds:12
# Get hd0 data
mov $0x0000, %ax
mov %ax, %ds
lds %ds:4*0x41, %si
mov $INITSEG, %ax
mov %ax, %es
mov $0x0080, %di
mov $0x10, %cx
rep
movsb
# Get hd1 data
mov $0x0000, %ax
mov %ax, %ds
lds %ds:4*0x46, %si
mov $INITSEG, %ax
mov %ax, %es
mov $0x0090, %di
mov $0x10, %cx
rep
movsb
经过上面的代码处理,现在内存地址里存储的数据大致如下图:
后面马上就要轮到C语言上场了,汇编语言和C语言之间的数据交互,虽然也可以用变量的形式进行传递。对于这种大量的数据,双方约定了一个内存地址(类似一个公用银行卡号),我存你取。
存储好数据之后,用cli 关闭中断.
之所以关闭中断,是为了要写上我们自己的中断向量表,所以在这期间是不允许中断进来的。
接着看
# first we move the system to it's rightful place
mov $0x0000, %ax
cld # 'direction'=0, movs moves forward
do_move:
mov %ax, %es # destination segment
add $0x1000, %ax
cmp $0x9000, %ax
jz end_move
mov %ax, %ds # source segment
sub %di, %di
sub %si, %si
mov $0x8000, %cx
rep
movsw
jmp do_move
# then we load the segment descriptors
最后 rep movsw 和之前从0x7c00复制到0x90000是一样的, 只不过这一次是从0x10000 到 0x90000的内容都复制到0的位置。
现在内存布局大致是这个样子的:
可以看到从0到0x80000的这512K里是除了bootsect和setup的操作系统的代码。这么一来就把 之前启动时的 0x7c00那些内容都覆盖了。
0x7c00已经是前任了,感觉忘掉吧!
下一次,我们一起迎接新的篇章,模式转换