loader.asm
来源:互联网 发布:ubuntu 选择性能模式 编辑:程序博客网 时间:2024/04/29 04:20
org 0100h
jmp LABEL_BEGIN
%include "include/pm.inc"
%include "include/FAT12hdc.inc"
Base_Of_Loader equ 9000h ;LOADER缓冲区的段地址(实模式)
Offset_Of_Loader equ 0100h ;LOADER缓冲区的偏移地址(实模式)
Base_Of_Kernel equ 8000h ;内核缓冲区的段地址(实模式)
Offset_Of_Kernel equ 0000h ;内核缓冲区的偏移地址(实模式)
Page_Dir_Base equ 100000h ;页目录物理地址
Page_Tbl_Base equ 101000h ;页表物理地址
Loader_Phy_Address equ Base_Of_Loader * 16 ;LOADER的物理段地址
Kernel_Phy_Address equ Base_Of_Kernel * 16 ;KERNEL的物理段地址
Kernel_Entry_Point equ 30400h ;KERNEL的入口物理地址
;gdt------------------------------------------------------------------
[section .gdt]
LABEL_DESC_GDT: Descriptor 0,0,0
LABEL_DESC_FLAT_RW: Descriptor 0,0fffffh,DA_DRW + DA_LIMIT_4K
LABEL_DESC_FLAT_C: Descriptor 0,0fffffh,DA_CR + DA_32 + DA_LIMIT_4K
LABEL_DESC_DATA: Descriptor 0,Data_Len - 1,DA_DRW
LABEL_DESC_STACK32: Descriptor 0,Stack32_Len,DA_DRWA + DA_32
LABEL_DESC_VIDEO: Descriptor 0b8000h,0ffffh,DA_DRW
Selector_Flat_RW equ LABEL_DESC_FLAT_RW - LABEL_DESC_GDT
Selector_Flat_C equ LABEL_DESC_FLAT_C - LABEL_DESC_GDT
Selector_Data equ LABEL_DESC_DATA - LABEL_DESC_GDT
Selector_Stack32 equ LABEL_DESC_STACK32 - LABEL_DESC_GDT
Selector_Video equ LABEL_DESC_VIDEO - LABEL_DESC_GDT
Gdt_Len equ $ - LABEL_DESC_GDT
Gdt_Ptr: dw Gdt_Len - 1
dd (Loader_Phy_Address + LABEL_DESC_GDT)
;end of gdt-----------------------------------------------------------
[section .stack32]
[bits 32]
LABEL_STACK32:
times 512 db 0
Stack32_Len equ $ - LABEL_STACK32 - 1
;end of stack32-------------------------------------------------------
;data section---------------------------------------------------------
[section .data]
LABEL_DATA:
;显示的字符串
String_Len equ 9
_sz_Loading: db 'Loading '
Loading_Message_Index equ 0
_sz_Ready: db 'Ready '
Ready_Message_Index equ 1
_sz_No_Kernel: db 'No Kernel'
No_Kernel_Message_Index equ 2
;加载Kernel有关的数据
LOADER_NAME equ 11 ;LOADER文件名的长度
;一堆变量
_w_Current_Root_Sector: dw Root_Dir_Sector_Index ;控制根目录当前读取的扇区号
_w_Root_Sector_For_Loop: dw Root_Dir_Sector_Number ;控制读取根目录的扇区数
_b_Odd: db 0 ;控制加载的文件在FAT中的相对偏移字节的奇偶
_sz_Kernel_Name: db "KERNEL BIN" ;LOADER文件名,用于比较
_w_Kernel_Offset: dw 0 ;控制131行的LOADER缓冲区的偏移
;实模式下使用这些符号
;字符串
_Memory_Info_Title: db 'BaseAddrL BaseAddrH LengthL LengthH Type',0ah,0
_Ram_Size: db 'Ram Size:',0
_Return: db 0ah,0
_Disp_Pos: dd (80 * 4) * 2
_Some_Int: dd 098ff22eeh
_ARD_Buffer: times 256 db 0
_ARD_Number: dd 0
_dwRam_Size dd 0
_ARD_Temp:
_Base_Low: dd 0
_Base_High: dd 0
_Len_Low: dd 0
_Len_High: dd 0
_Type: dd 0
Memory_Info_Title equ _Memory_Info_Title - $$
Ram_Size equ _Ram_Size - $$
Return equ _Return - $$
Disp_Pos equ _Disp_Pos - $$
Some_Int equ _Some_Int - $$
ARD_Buffer equ _ARD_Buffer - $$
ARD_Number equ _ARD_Number - $$
dw_Ram_Size equ _dwRam_Size - $$
ARD_Temp equ _ARD_Temp - $$
Base_Low equ _Base_Low - $$
Base_High equ _Base_High - $$
Len_Low equ _Len_Low - $$
Len_High equ _Len_High - $$
Type equ _Type - $$
Data_Len equ $ - LABEL_DATA
;end of data section--------------------------------------------------
[section .code16]
[bits 16]
LABEL_BEGIN:
mov ax,cs
mov ds,ax
mov ax,0
mov ss,ax ;Boot中所占的区域已经作废,当来作实模式下的栈
mov sp,7c00h
mov dh,Loading_Message_Index
call Disp_Str_In_Real_Mode
;编程思路:找到根目录下的KERNEL.BIN
;根目录的范围是总扇区编号19到32,共占14个扇区
;分别读取每个根目录扇区,共14次
;每个扇区读16次
;每个扇区可以容纳512/32=16个文件信息
LABEL_SEARCH_KERNEL_BEGIN:
cmp word [_w_Root_Sector_For_Loop],0 ;比较要读取的根目录扇区数
je LABEL_KERNEL_NOT_FOUND ;如果为0表示没有找到
dec word [_w_Root_Sector_For_Loop]
mov ax,1 ;读取当前扇区至es:bx处
push ax
mov ax,[_w_Current_Root_Sector]
push ax
mov ax,Offset_Of_Kernel
push ax
mov ax,Base_Of_Kernel
push ax
call Read_Sector
add sp,8
mov dx,File_Number_Per_Sector ;初始化dx控制一个扇区循环次数为16次
mov si,_sz_Kernel_Name ;ds:si指向KERNEL的名字字符串
mov ax,Base_Of_Kernel ;es:di指向缓冲区
mov es,ax
mov di,Offset_Of_Kernel
LABEL_NEXT_FILE:
cmp dx,0
je LABEL_READ_NEXT_SECTOR ;如果为0表示当前扇区比较结束,读下一个扇区
dec dx
LABEL_CMP_BEGIN:
mov cx,11 ;初始化cx控制比较次数为最多为11次
LABEL_CMP_GO_ON:
cmp cx,0 ;如果cx为0,表示找到KERNEL
je LABEL_KERNEL_FOUND
dec cx
lodsb ;读取一个KERNEL名字字符串到al
cmp al,byte [es:di] ;比较之
jne LABEL_DIFF ;有不同的直接进行下一个文件的比较
jmp LABEL_NEXT_CMP ;相同则进行下一个字符的比较
LABEL_DIFF:
mov si,_sz_Kernel_Name ;恢复si,使ds:si重新指向字符串
and di,0ffe0h
add di,File_Item_Byte_Number ;恢复di,使es:di指向下个根目录
jmp LABEL_NEXT_FILE
LABEL_NEXT_CMP:
inc di
jmp LABEL_CMP_GO_ON
LABEL_READ_NEXT_SECTOR: ;做读下一个扇区的准备工作
inc word [_w_Current_Root_Sector]
jmp LABEL_SEARCH_KERNEL_BEGIN
LABEL_KERNEL_NOT_FOUND:
;显示一些信息
mov dh,No_Kernel_Message_Index
call Disp_Str_In_Real_Mode
jmp $
LABEL_KERNEL_FOUND:
; 显示一些信息
mov dh,Ready_Message_Index
call Disp_Str_In_Real_Mode
and di,0ffe0h ;此时找到KERNEL,恢复di指向名字字段的首字节
add di,FAT_Offset_In_File_Item ;此时di指向此文件在FAT的序号,记录序号的段占两字节
mov ax,[es:di] ;取出准备调用Get_FAT_Entry
push ax ;临时保存之,等待日后调用函数时使用
add ax,Root_Dir_Sector_Index
add ax,Root_Dir_Sector_Number
sub ax,2 ;此时ax为KERNEL的相应扇区在DATA区的总偏移
LABEL_READ_AGAIN:
mov bx,1 ;把KERNEL的相应一个扇区读到指定的位置
push bx
push ax
mov ax,Offset_Of_Kernel
add ax,[_w_Kernel_Offset]
push ax
mov ax,Base_Of_Kernel
push ax
call Read_Sector
add sp,8
pop ax
push ax
call Get_FAT_Entry
add sp,2
cmp ax,0fffh ;不等于0FFFH的话说明已是最后一个扇区了
je LABEL_EXIT
push ax
add ax,Root_Dir_Sector_Index
add ax,Root_Dir_Sector_Number
sub ax,2
add word [_w_Kernel_Offset],Byte_Number_Per_Sector
jmp LABEL_READ_AGAIN
LABEL_EXIT:
mov ax,Base_Of_Loader
mov ds,ax
mov es,ax
;获得内存信息,存放在缓冲区中
mov ebx,0
mov di,_ARD_Buffer
LABEL_CHK_MEMORY:
mov eax,0e820h
mov ecx,20
mov edx,0534D4150h
int 15h
jc LABEL_CHK_ERROR
add di,20
inc dword [_ARD_Number]
cmp ebx,0
je LABEL_EXIT_CHK_MEMORY
jmp LABEL_CHK_MEMORY
LABEL_CHK_ERROR:
mov dword [_ARD_Number],0
LABEL_EXIT_CHK_MEMORY:
;填充数据段描述符
xor eax,eax
mov eax,Loader_Phy_Address
add eax,LABEL_DATA
mov word [LABEL_DESC_DATA + 2],ax
shr eax,16
mov byte [LABEL_DESC_DATA + 4],al
mov byte [LABEL_DESC_DATA + 7],ah
;填充栈段描述符
xor eax,eax
mov eax,Loader_Phy_Address
add eax,LABEL_STACK32
mov word [LABEL_DESC_STACK32 + 2],ax
shr eax,16
mov byte [LABEL_DESC_STACK32 + 4],al
mov byte [LABEL_DESC_STACK32 + 7],ah
lgdt [Gdt_Ptr]
cli
in al,92h
or al,00000010h
out 92h,al
mov eax,cr0
or al,1
mov cr0,eax
jmp dword Selector_Flat_C:(Loader_Phy_Address + LABEL_CODE32)
;Read_Sector----------------------------------------------------------
;压栈顺序 1:欲读取的扇区个数
; 2:欲读取的扇区总号
; 3:读取的扇区的缓冲区的偏移地址
; 4:读取的扇区的缓冲区的段地址
;---------------------------------------------------------------------
;入口参数
; ah=02h
; al:扇区数
; ch:柱面
; cl:扇区
; dh:磁头
; dl:驱动器,00H~7FH:软盘;80H~0FFH:硬盘
; es:bx=缓冲区的地址
;出口参数
; cf=0——操作成功,ah=00H,al=传输的扇区数,否则,ah=状态代码,参见功能号01H中的说明
;----------------------------------------------------------------------
;求解磁头号,柱面号,相对扇区数的算法如下
; 设扇区号为 x
; ┌ 柱面号 = y >> 1
; x ┌ 商 y ┤
; -------------- => ┤ └ 磁头号 = y & 1
; 每磁道扇区数 │
; └ 余 z => 起始扇区号 = z + 1
Read_Sector:
push bp
mov bp,sp
push ax
push bx
push cx
push dx
push es
mov ax,[bp + 8] ;扇区总数送ax
mov bl,18 ;除数是18,一个柱面有18个扇区
div bl ;商送al,余数送ah
mov ch,al
shr ch,1 ;求得柱面
mov dh,al
and dh,1 ;求得磁头
mov cl,ah
inc cl ;求得相对扇区偏移
mov dl,0 ;读取A盘
mov ax,[bp + 4]
mov es,ax ;设置缓冲区段地址
mov bx,[bp + 6] ;设置缓冲区偏移地址
LABEL_RETRY:
mov ax,[bp + 10] ;设置读取扇区的个数
mov ah,2 ;设置功能号
int 13h ;中断调用
jc LABEL_RETRY ;如果读取不成功,再继续读取
pop es
pop dx
pop cx
pop bx
pop ax
pop bp
ret
;end of Read_Sector---------------------------------------------------
;Get_FAT_Entry--------------------------------------------------------
;压栈顺序 1:一个文件在FAT中的序号
;返回值:在此FAT中序号的值,保留在ax中
Get_FAT_Entry:
push bp
mov bp,sp
push bx
push dx
push di
push es
mov ax,[bp + 4] ;求出在FAT中偏移的字节数,乘以1.5倍
mov bx,3
mul bx
mov bx,2
div bx
mov byte [_b_Odd],0
cmp dx,0
je LABEL_EVEN ;余数等于0的话,就为偶字节
mov byte [_b_Odd],1 ;余数等于1的话,就为奇字节
LABEL_EVEN:
xor dx,dx
mov bx,Byte_Number_Per_Sector ;字节数除以512,得到在FAT中的偏移扇区
div bx
inc ax ;得到总的扇区偏移,用来调用Read_Sector
;dx中保存相对扇区偏移的字节数
mov bx,2 ;读两个扇区,以免出现跨扇区的情况
push bx
push ax
mov ax,Offset_Of_Loader
push ax
mov ax,Base_Of_Loader - 100h ;换个地儿存放缓冲区
push ax
call Read_Sector
add sp,8
mov ax,Base_Of_Loader - 100h ;es:di指向要读取的FAT的首字节
mov es,ax
mov di,Offset_Of_Loader
add di,dx
mov ax,[es:di]
cmp byte [_b_Odd],0
je LABEL_EVEN2
shr ax,4 ;奇数的策略
LABEL_EVEN2:
and ax,0fffh ;偶数的策略
pop es
pop di
pop dx
pop bx
pop bp
ret
;end of Get_FAT_Entry-------------------------------------------------
;Disp_Str_In_Real_Mode------------------------------------------------
;入口参数:dh代表在实模式下字符串的偏移
;功能13h
;功能描述:在Teletype模式下显示字符串
;入口参数:ah=13h
;bh=页码
;bl=属性(若AL=00H或01H)
;cx=显示字符串长度
;(dh、dl)=坐标(行、列)
;es:bp=显示字符串的地址 al=显示输出方式
;0——字符串中只含显示字符,其显示属性在bl中。显示后,光标位置不变
;1——字符串中只含显示字符,其显示属性在bl中。显示后,光标位置改变
;2——字符串中含显示字符和显示属性。显示后,光标位置不变
;3——字符串中含显示字符和显示属性。显示后,光标位置改变
;出口参数:无
Disp_Str_In_Real_Mode:
push es
push ax
push bx
push cx
push dx
push bp
mov ax,String_Len
mul dh
add dh,2
add ax,_sz_Loading
mov bp,ax
mov ax,ds
mov es,ax
mov cx,String_Len
mov ax, 01301h ; AH = 13, AL = 01h
mov bx, 0007h ; 页号为0(BH = 0) 黑底白字(BL = 07h)
mov dl, 0
int 10h ; int 10h
pop bp
pop dx
pop cx
pop bx
pop ax
pop es
ret
;end of Disp_Str_In_Real_Mode-----------------------------------------
;end of loader_entry--------------------------------------------------
[section .code32] ;跳入保护模式
[bits 32]
LABEL_CODE32:
mov ax,Selector_Data
mov ds,ax
mov es,ax
mov ax,Selector_Video
mov gs,ax
mov ax,Selector_Stack32
mov ss,ax
mov esp,Stack32_Len
push Memory_Info_Title ;显示内存头信息
call Disp_Str
call Disp_Mem_Size ;显示内存信息
mov ax,Selector_Flat_RW
mov es,ax
call Setup_Paging ;启动分页,但线性地址还是等价于物理地址
mov ah,0ch
mov al,'P'
mov [gs:(80 * 20) * 2],ax
mov ax,Selector_Flat_RW
mov ds,ax
mov ax,es
call Init_Kernel
jmp Selector_Flat_C:Kernel_Entry_Point
;Disp_Mem_Size--------------------------------------------------------
Disp_Mem_Size:
push eax
push ecx
push esi
push edi
mov esi,ARD_Buffer
mov ecx,[ARD_Number]
.w:
push ecx
mov ecx,5
mov edi,ARD_Temp
.n:
lodsd
stosd
mov [Some_Int],eax
push Some_Int
call Disp_32Bit_Int
push edi
mov edi,[Disp_Pos]
xor ax,ax
mov ah,0ch
mov al,'h'
mov [gs:edi],ax
add edi,2
mov al,' '
mov [gs:edi],ax
add edi,2
mov [Disp_Pos],edi
pop edi
loop .n
cmp dword [Type],1
jne .skip
mov eax,[Base_Low]
add eax,[Len_Low]
cmp eax,[dw_Ram_Size]
jb .skip
mov [dw_Ram_Size],eax
.skip:
pop ecx
call Disp_Return
loop .w
push Ram_Size
call Disp_Str
push dw_Ram_Size
call Disp_32Bit_Int
push edi
mov edi,[Disp_Pos]
xor ax,ax
mov ah,0ch
mov al,'h'
mov [gs:edi],ax
add edi,2
mov [Disp_Pos],edi
pop edi
pop edi
pop esi
pop ecx
pop eax
ret
;end of Disp_Mem_Size-------------------------------------------------
;Setup_Paging---------------------------------------------------------
Setup_Paging:
push eax
push ebx
push ecx
push edx
push edi
xor edx,edx
mov eax,[dw_Ram_Size]
mov ebx,400000h
div ebx
cmp edx,0
jne .1
inc eax
.1:
push eax
mov ecx,eax
mov eax,Page_Tbl_Base | PG_P | PG_RWW | PG_USU
mov edi,Page_Dir_Base
.2:
stosd
add eax,4096
loop .2
pop eax
mov ebx,1024
mul ebx
mov ecx,eax
mov eax,PG_P | PG_RWW | PG_USU
mov edi,Page_Tbl_Base
.3:
stosd
add eax,4096
loop .3
mov eax,Page_Dir_Base
mov cr3,eax
mov eax,cr0
or eax,80000000h
mov cr0,eax
pop edi
pop edx
pop ecx
pop ebx
pop eax
ret
;end of Setup_Paging--------------------------------------------------
;Init_Kernel----------------------------------------------------------
Init_Kernel:
push eax
push ecx
push edx
push esi
mov cx,[Kernel_Phy_Address + 2ch] ;取得Program Section Table的个数
movzx ecx,cx
mov esi,[Kernel_Phy_Address + 1ch] ;取得Program Section Table的在文件的偏移字节
mov dx,[Kernel_Phy_Address + 2ah] ;取得一个程序描述符的长度
movzx edx,dx
add esi,Kernel_Phy_Address ;指向表中的第一个程序描述符的起始位置
.begin:
cmp dword [esi],0 ;类型如果为0,跳到下一项
je .next
mov eax,[esi + 10h] ;压入程序的长度
push eax
mov eax,[esi + 8] ;压入目的指针
push eax
mov eax,[esi + 4]
add eax,Kernel_Phy_Address ;压入源指针
push eax
call Memory_Copy
.next:
add esi,edx
dec ecx
jnz .begin
pop esi
pop edx
pop ecx
pop eax
ret
;end of Init_Kernel---------------------------------------------------
%include "diy.inc"
;end of code32--------------------------------------------------------
- loader.asm
- boot/loader.asm
- boot/loader.asm
- Loader.asm大体思路
- loader.asm 注释
- 自己动手写操作系统 ( chapter5/c/loader.asm完全注释 )
- asm
- ASM
- asm
- asm
- asm
- ASM
- asm
- ASM
- ASM
- ASM
- ASM
- Loader
- 关于手动更新ubuntu 的内核
- LDR指令和LDR伪指令
- 为爱,种下一束罂粟花(转载)
- 成为情绪的主人(10月19日)
- diy.inc
- loader.asm
- 连载6:数组循环移位(《编程之美》第2.17节)
- FAThdc.inc
- pm.inc
- 深度·自信·计划——《我是一只IT小小鸟》读书笔记(4)
- 大学生活应该这样度过之计算机专业与实训——《程序员羊
- 直接插入排序
- struts2 的doubleSelect标签的使用
- 这样的《软件工程》课,真实,透明,清晰,强悍!