使用大S的原因:使用这样的后缀可以让 as 使用 GNU C 编译器的预处理功能,因此可以在汇编语言程序中包括"#include"、 “#if"等语句。本程序使用大写后缀主要是为了能在程序中使用”#include"语句来包含进 linux/config.h 头文件定义的常数。–引自《Linux内核完全注释》
部分代码解读:
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
SETUPLEN = 4 ! nr of setup-sectors
BOOTSEG = 0x07c0 ! original address of boot-sector
INITSEG = DEF_INITSEG ! we move boot here - out of the way
!下面先把自己移动到0x90000处,代码重定位
start:
mov ax,#BOOTSEG
mov ds,ax
mov ax,#INITSEG
mov es,ax
mov cx,#256
sub si,si
sub di,di
rep
movw
jmpi go,INITSEG !段间跳转
!这里go赋值给ip,ip是自start开始到标号go的段内偏移地址
!INITSEG = 0X9000赋值给cs
!代码重定位后,从这里开始执行,因为ip为go处这个偏移地址
go: mov ax,cs
mov dx,#0xfef4 ! arbitrary value >>512 - disk parm size
mov ds,ax
mov es,ax
push ax
mov ss,ax ! put stack at 0x9ff00 - 12.
mov sp,dx
!把cs = 0x9000这个段基址赋值给ds段,es段和ss段。
!栈偏移地址这里为0xfef4,可能0.11版本中给了0xff00
!这样setup在内存中的存放位置就设置好了,并且我们可以看到栈的大小也搞定了
!开始读setup模块的程序了。
!0x13是BIOS读磁盘扇区的中断:
! ah = 0x02 - 读磁盘扇区到内存; al = 需要读出的扇区数量;
! ch = 磁道(柱面)号的低 8 位; cl = 开始扇区(位 0-5),磁道号高2 位(位 6-7);
! dh = 磁头号; dl = 驱动器号(如果是硬盘则位 7 要置位);
! es:bx 它指向数据缓冲区; 如果出错则 CF 标志置位, ah 中是出错码。
load_setup:
xor dx, dx ! drive 0, head 0
mov cx,#0x0002 ! sector 2, track 0
mov bx,#0x0200 ! address = 512, in INITSEG
mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
int 0x13 ! read it
jnc ok_load_setup ! ok - continue
!后面读不懂
push ax ! dump error code
call print_nl
mov bp, sp
call print_hex
pop ax
xor dl, dl ! reset FDC
xor ah, ah
int 0x13
j load_setup
启动盘的程序放置顺序:
很简单,在第二个扇区开始读取4个扇区的内容。
! 这段代码利用 BIOS INT 0x13 功能 8 来取磁盘驱动器的参数。 实际是取每磁道扇区数,并保存在
! 位置 sectors 处。
! Get disk drive parameters, specifically nr of sectors/track
load_setup:
xor dl,dl
mov ah,#0x08 ! AH=8 is get drive parameters
int 0x13
xor ch,ch
seg cs
mov sectors,cx
mov ax,#INITSEG
mov es,ax
! 下面利用 BIOS INT 0x10 功能 0x03 和 0x13 来显示信息:“ 'Loading'+回车+换行”,显示包括
! 回车和换行控制字符在内共 9 个字符。
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10
mov cx,#9
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1
mov ax,#0x1301 ! write string, move cursor
int 0x10
!下面要读入系统了
mov ax,#SYSSEG !系统代码初始地址为0x1000
mov es,ax ! segment of 0x010000
call read_it !原来系统放在es段,这里调用的read_it应该是复制系统代码到0x1000处
jmpi 0,SETUPSEG ! 同前,0赋值给ip,这里因为要执行setup代码了,所以cs为SETUPSEG = 0X9020
下面是李老师的bootsect.s代码以及其完全注释,这部分代码的主要功能有两个。一是显示一段字符“Hello…”,用以与用户进行交互,而是将setup的代码从软驱中读入内存,放到512个字节的bootsect代码之后。最后,程序跳到setup代码中去执行。(TIPS:这里使用//是为了注释方便,该代码可能并无执行能力,仅供学习)
!BOOTSECT.S
SETUPLEN=2 //setup代码的长度为两个扇区,512个字节
SETUPSEG=0x07e0 //setup代码在内存中的执行地址,比0x7c0多0x20,这0x20可能是作为数据段
entry _start //可能是编译器要读的代码,给编译器解释了程序入口地址是_start
_start: //程序入口地址
mov ah,#0x03 //显示屏打印中断的寄存器赋值操作(这里是读入光标位置)
xor bh,bh //显示操作,字节型数据清零用xor(要记住)
int 0x10 //寄存器赋值完毕后的显示中断
mov cx,#36 //显示36个字节,显示的字节在msg1处,注意有要加上换行符和回车符
mov bx,#0x0007 //显示屏打印中断的寄存器赋值操作(这里是读入一些字符)
mov bp,#msg1 //bp是es段的偏移地址,这句指令表示字符串的偏移地址在msg1所代表的地址处
mov ax,#0x07c0 //0x07c0赋值要先赋值给ax是因为段寄存器一般不能直接赋值
mov es,ax //为了找到字符串的存放位置我们必须把es段与cs段重合,这样才好定位msg1的偏移地址
mov ax,#0x1301 //显示屏打印中断的寄存器赋值操作
int 0x10 //显示中断,执行完这一步,屏幕就能显示一段字符串了。
load_setup:
mov dx,#0x0000 //设置驱动器和磁头(drive 0, head 0): 软盘 0 磁头
mov cx,#0x0002 //设置扇区号和磁道(sector 2, track 0): 0 磁头、0 磁道、2 扇区
mov bx,#0x0200 //设置读入的内存地址:BOOTSEG+address = 512,偏移512字节
mov ax,#0x0200+SETUPLEN //SETUPLEN是读入的扇区个数,我们设置为2
int 0x13 //应用 0x13 号 BIOS 中断读入 2 个 setup.s扇区
jnc ok_load_setup //读入成功,跳转到 ok_load_setup: ok - continue
mov dx,#0x0000 //软驱、软盘有问题才会执行到这里。
mov ax,#0x0000 //这句和上一句都是reset软驱操作
int 0x13 //复位软驱 reset the diskette
jmp load_setup //跳到load_setup重复步骤,如果这部分一直不过就死机了啊><!!!
ok_load_setup:
jmpi 0,SETUPSEG //众所周知,段寄存器是不能随便赋值的,所以这里采用段间跳转
//这里0赋值给ip,INITSEG = 0X07e0赋值给cs(setup代码的执行地址)
msg1: //字符串存放在这儿
.byte 13,10 //1对回车、换行 (2字节)
.ascii "Hello OS world, my name is LZJ" //屏幕上显示的ascii字符 (30字节)
.byte 13,10,13,10 //2对回车、换行(4字节)
.org 510 //指定510地址,后面的程序或数据从这个地址值开始分配。相当于把代码长度拉满512Kbyte。
boot_flag: //bootsect的标识符地址,不清楚该标识符具体作用
.word 0xAA55 //该语句偏移地址为510,该语句占用2个字节,从而使bootsect代码正好为512字节。