之前的boot启动镜像文件在需要改变后继文件(kernel.img)时要用partcopy,并且要计算文件的长度,使用起来不方便,现在我们可以引入微软的软盘FAT格式,如果我们使用软盘来作为测试的介质,那么在使用起来会很方便。
;; 文件:boot.asm
;; 作用:从7c00h处启动,显示载入系统信息"Loading System..."
;; 没有文件系统,1.44M 512bits/80sec 软盘启动,
;; 创建日期:2004/01/30 flyback
;; fly-back@163.com
;; ===================================
%define loadpoint 9000h ; 载入点,初始化程序载入到9000h的地方
%define loadoffset 0000h
bits 16
ORG 0x0 ; 启动入口地址
main:
jmp short start ; 跳转到开始程序入口
nop ;
; 引导区文件系统数据
;----------------------------------------------------------------------------
brOEM DB ' -My-0S.' ; 0003h - 引导程序的名字
brBPS DW 0x200 ; 000Bh - 每扇区的字节数 512
brSPC DB 0x01 ; 000Dh - 每簇扇区数
brResCount DW 0x0001 ; 000Eh - 保留扇区数
brFATs DB 0x02 ; 0010h - FAT 备份数
brRootEntries DW 0x00e0 ; 0011h - 根目录入口数
brSectorCount
DW 2880 ; 0013h - 磁盘容量扇区数< 32MB
brMedia DB 240 ; 0015h - 媒体描述符
brSPF DW 9 ; 0016h - 每FAT扇区数
brSPH DW 18 ; 0018h - 每磁道扇区数
brHPC DW 2 ; 001Ah - 盘面数
brHidden DD 0 ; 001Ch - 隐藏扇区数
brSectors DD 0 ; 0020h - 如果大于32m的扇区总数
DB 0 ; 0024h - 物理驱动器号
DB 0 ; 0025h - 系统保留
DB 29H ; 0026h - 扩展扇区标记(包含29h)
brSerialNum DD 00000006H ; 0027h - 卷ID
brLabel DB 'teachosdisk' ; 002Bh - 卷标
brFSID DB 'FAT12 ' ; 0036h - 系统保留
;------------------------------------------------------------------------
start:
cli ; 关中断,防止意外中断打断程序执行
mov ax, 0x7c0 ;
mov ds, ax ; 设置数据段
mov es, ax ;
mov ax, 0x0000; 设置堆栈段
mov ss, ax
mov sp, 0ffffh ; 堆栈入口
sti ; 开中断
mov si, brOEM ; system info
call pntchr
mov si, CRLF
call pntchr
mov si, loadmsg ; 调用显示载入信息
call pntchr
; 把磁盘目录信息载入到7c00:0200的地方
loadroot:
mov cx, 0
mov dx, 0
mov ax, 0x0020 ; 32个字节/文件
mul WORD [brRootEntries] ; 32*224(文件数) = 7168 (最多所有文件所占的字节数)
div WORD [brBPS] ; 7168/512 = 14 (Root dir) 文件的目录描述字节占用的扇区数14
xchg ax, cx ; CX = 14
mov al, [brFATs] ; 2
mul word [brSPF] ; 9 * 2 = 18
add ax, word [brResCount] ; 18 + 1 now AX = 19
mov WORD[datasector], ax ; 目录起始的扇区 19
add WORD[datasector], cx ; 数据区起始扇区 33
; 目录放入0x0200内存
mov bx, 0x0200
call ReadSec
; 比较目录中是否有init.img文件存在
mov cx, WORD [brRootEntries] ; CX:224 在目录的所有文件中寻找
mov di, 0x0200 ; 从目录入口处开始 0x200
.LOOP:
push cx ; 保护 CX = 224
mov cx, kernellen ; 8个文件名称和3个扩展名称
mov si, kernelname ;
push di
repe cmpsb ; stop compare if cx >0 or the
; first unequal is met(zf=1)!
pop di
je loadfiledec ;if zf = 1, jmp LOAD_FAT!(Seek OK!)
pop cx
add di, 0x0020 ; 跳到下一个目录入口
loop .LOOP ; cx 自动减
jmp failure
; 载入文件描述子节
loadfiledec:
; 把启动的镜像文件*.img文件的起始单元保存起来
mov si, CRLF
call pntchr
mov si, loadfat
call pntchr
mov si, CRLF
call pntchr
mov dx, WORD [di + 0x001A] ;the file's first cluster
mov WORD [cluster], dx ; store the first cluster
; 计算fat的大小并保存到07c00:0x0200(ds:bx)
xor ax, ax
mov al, BYTE [brFATs] ; al:2
mul WORD [brSPF] ; AH:18 = 9*2
mov cx, ax ; CX:18
; 读取数据放入到0x7c00:0x0200 ds:bx
mov ax, WORD [brResCount] ; AX:1
mov bx, 0x0200 ;
call ReadSec ; AX:1 CX:18 BX:0200
; 读取img 放入 9000:0x0000处
mov ax, loadpoint ; destination of image CS
mov es, ax
mov bx, loadoffset ; destination for image IP
push bx
LOAD_IMAGE:
mov ax, WORD [cluster] ; cluster to read
pop bx ; buffer to read into
call ClusterLBA ; convert cluster to LBA
; AX:[cluster] ES:BX[9000:0000](dest)
xor cx, cx
mov cl, BYTE [brSPC] ; CL:1
call ReadSec ; AX:LBA CX:1 BX:0000 ES:0100
push bx
; 计算下一个单元
mov ax, WORD [cluster] ; identify current cluster
mov cx, ax ; copy current cluster
mov dx, ax ; copy current cluster
shr dx, 0x0001 ; divide by two
add cx, dx ; sum for (3/2)
mov bx, 0x200 ; location of FAT in memory
add bx, cx ; index into FAT
mov dx, WORD [bx] ; read two bytes from FAT
test ax, 0x0001
jnz .ODD_CLUSTER
.EVEN_CLUSTER:
and dx, 0000111111111111b ; take low twelve bits
jmp .DONE
.ODD_CLUSTER:
shr dx, 0x0004 ; take high twelve bits
.DONE:
mov WORD [cluster], dx ; store new cluster
cmp dx, 0x0FF0 ; test for end of file,>=0x0FF0: end or bad!
jb LOAD_IMAGE ; if dx<0x0ff0 (CF=1), jmp Load_image
DONE: ; this Label is not used!
; push WORD loadpoint ;--->pop CS (second)
; push WORD loadoffset ;--->pop IP (first)
; retf
mov si, CRLF
call pntchr
jmp word loadpoint:loadoffset
failure:
mov si, CRLF
call pntchr
mov si, loadfail
call pntchr
hlt
jmp $
;-------------------- 数据区-------------------------------
loadmsg db 'Loading System:',0 ; 要显示的字符窜以0结尾
loadfail db 'Load init Failure!',0 ; 载入失败信息
Sector db 0x00
Head db 0x00
Track db 0x00
datasector dw 0x0000 ; 33 数据区起始扇区号
rootaccess equ 0x0200 ; 存放目录数据的偏移地址
cluster dw 0x0000
kernelname db "KERNEL IMG" ; 11 chars
kernellen equ $ - kernelname ; 长度
CRLF db 13,10,0
Progress db ".", 0
; msgFailure db "ERROR!", 0x00
; loaddir db 'Load Dir:', 0 ;字符串,回车,换行,0
loadfat db 'Load FAT:', 0 ;字符串,回车,换行,0
;readsec db 'read sec', 13,10,0
; ----------------子程序区-------------------------
;*********************显示字符串**************
pntchr:
pusha
.pnt:
lodsb ; 从DS:SI装载一个字符到AL
or al,al ;
jz endpntchr ; 如果 al = 0, 返回
;
mov ah,0x0E ;
mov bx,0x004a ;
int 0x10 ; 调用bios中断显示字符
jmp .pnt ;
;
endpntchr: ;
popa
ret ; 返回
;*************************************************************************
; PROCEDURE ReadSec
; 从ax+1的地方把cx个扇区载入到es:bx的内存
; 注意扇区是从2开始,1扇区已经读入了
;*************************************************************************
ReadSec:
.Main:
;mov si, readsec
;call pntchr
;mov di, 0x0005
.secloop:
push ax
push bx
push cx ;protect ax, bx, cx
call LBACHS ; 调用转换
mov ah, 0x02 ; BIOS 读取扇区命令
mov al, 0x01 ; 一个扇区
mov ch, BYTE [Track] ; track
mov cl, BYTE [Sector] ; sector
mov dh, BYTE [Head] ; head
mov dl, 0 ; 因为是a:所以为0
int 0x13 ; 调用中断
jnc .SUCCESS ; test for read error
xor ax, ax
;int 0x13
;dec di
pop cx
pop bx
pop ax
jnz .secloop
jmp failure ; 错误则显示出错信息并停止
hlt
.SUCCESS
mov si, Progress
call pntchr
pop cx
pop bx
pop ax
add bx, WORD [brBPS] ; 读取下一个扇区的内容
inc ax
loop .Main ; cx -= 1, if cx != 0, jmp .main
ret
;*************************************************************************
; PROCEDURE LBACHS
; 转换逻辑块访问为读取磁盘所使用的磁道,盘面,扇区
; 相对扇区 = (逻辑扇区 / 每磁道扇区数) + 1
; 相对盘面 = (逻辑扇区 / 每磁道扇区数) MOD 盘面数
; 相对磁道 = 逻辑扇区 / (每磁道扇区数 * 盘面数)
;*************************************************************************
LBACHS:
xor dx, dx ; dx = 0
div WORD [brSPH] ; div m16: ax/18 -> 商:ax 余数:dx
inc dl ;
mov BYTE [Sector], dl ;sector No relative to the Track
xor dx, dx ; dx = 0
div WORD [brHPC] ; ax/2 -> 商:ax 余数:dx
mov BYTE [Head], dl ;
mov BYTE [Track], al ;
ret
;*************************************************************************
; PROCEDURE ClusterLBA
; 转换单元访问到直接扇区访问
; LBA = (cluster - 2) * sectors per cluster
;*************************************************************************
ClusterLBA:
sub ax, 0x0002 ; zero base cluster number
xor cx, cx
mov cl, BYTE [brSPC] ; convert byte to word
mul cx
add ax, WORD [datasector] ; base data sector
ret
; -----------------------------------------------------------------
times 510 - ($ - $$) db 0 ; 保证boot区有512个字节
dw 0AA55h ; boot区标记
上面是改进后的boot.asm编译后用partcopy 放入软盘开始的512字节
这时,我们只要把我们的其它程序编译成独立的镜像文件命名为KERNEL.IMG拷贝到软盘(软盘文件)就可以了
;-------------------------------------------------
;; 文件:显示初始化信息
;; KERNEL.ASM
;; 作用:显示初始化信息
;; fly-back@163.com
;;-------------------------------------------------
start:
mov ax, cs
mov ds, ax
mov es, ax
lea si, [prompt] ; press a key to clear screen
call pntchr
; 等待按下键盘
;mov ah, 0x10
;int 0x16
; cls
mov ah, 0x06
mov al, 0x00
mov bh,0x07
mov cx, 0x0
mov dx,0x184f
int 0x10
lea si, [start2msg]
call pntchr
; 显示图形功能
; 保存原来的显示方式
mov ah, 0x0f
int 0x10
mov [gmode], ax
; 设置图形模式640X480
mov ah,0x0
mov al, 0x12
int 0x10
; 设置调色板
mov ah, 0x0b
mov bh, 0x0
mov bl, 0x11
int 0x10
; 显示字符串
lea bp, [start2msg] ; es:bp
mov ah, 0x13
mov al, 0x1
mov bh, 0
mov bl, 0x71
mov dx, 0x00
mov cx, msgsize
int 0x10
; 划线
mov bx, 0x0
mov cx, 64
mov dx,70
.pix:
mov ah,0x0c
mov al, bl
int 0x10
inc cx
cmp cx,576
jne .pix
mov cx,64
inc bl
inc dx
cmp dx, 280
jne .pix
; 等待键盘
mov ah, 0x10
int 0x16
; 恢复原来的图形
mov ax, [gmode]
mov ah, 0x0
int 0x10
mov si, retmode
call pntchr
jmp $
nop
;----------------显示字符串----------------------
pntchr:
lodsb ; 从DS:SI装载一个字符到AL
or al,al ;
jz endpntchr ; 如果 al = 0, 返回
;
mov ah,0x0E ;
mov bx,0x0007 ;
int 0x10 ; 调用bios中断显示字符
jmp pntchr ;
;
endpntchr: ;
ret ; 返回
start2msg db 'Display 640X480 Video mode:',13,10,0
msgsize equ ($-start2msg)
prompt db 'Press any key...',13,10,0
prosize equ ($ - prompt)
retmode db 'Return to Text Mode.', 13,10,0
gmode dw 0
把上面文件编译成KERNEL.IMG(大写)拷贝到做好的软盘里,然后用它启动。