123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836 |
- ; |==================|
- ; | 这是loader程序 |
- ; |==================|
- ; Created by longjin, 2022/01/17
- ; 由于实模式下,物理地址为CS<<4+IP,而从boot的定义中直到,loader的CS为0x1000, 因此loader首地址为0x10000
- org 0x10000
- jmp Label_Start
- %include 'fat12.inc' ; 将fat12文件系统的信息包含进来
- Base_Of_Kernel_File equ 0x00
- Offset_Of_Kernel_File equ 0x100000 ; 设置内核文件的地址空间从1MB处开始。(大于实模式的寻址空间)
- Base_Tmp_Of_Kernel_Addr equ 0x00
- Offset_Tmp_Of_Kernel_File equ 0x7e00 ; 内核程序的临时转存空间
- Memory_Struct_Buffer_Addr equ 0x7e00 ; 内核被转移到最终的内存空间后,原来的临时空间就作为内存结构数据的存储空间
- ; ==== 临时的全局描述符表 =====
- [SECTION gdt]
- LABEL_GDT: dd 0,0
- LABEL_DESC_CODE32: dd 0x0000FFFF,0x00CF9A00 ; 代码段和数据段的段基地址都设置在0x00000000处, 把段限长设置为0xffffffff,可以索引32位地址空间
- LABEL_DESC_DATA32: dd 0x0000FFFF,0x00CF9200
- GdtLen equ $ - LABEL_GDT
- ; GDTR寄存器是一个6B的结构,低2B保存GDT的长度, 高4B保存GDT的基地址
- GdtPtr dw GdtLen - 1
- dd LABEL_GDT
- ; 这是两个段选择子,是段描述符在GDT表中的索引号
- SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
- SelectorData32 equ LABEL_DESC_DATA32 - LABEL_GDT
- ; === IA-32e模式的临时gdt表
- [SECTION gdt64]
- LABEL_GDT64: dq 0x0000000000000000
- LABEL_DESC_CODE64: dq 0x0020980000000000
- LABEL_DESC_DATA64: dq 0x0000920000000000
- GdtLen64 equ $ - LABEL_GDT64
- GdtPtr64 dw GdtLen64-1,
- dd LABEL_GDT64
- SelectorCode64 equ LABEL_DESC_CODE64 - LABEL_GDT64
- SelectorData64 equ LABEL_DESC_DATA64 - LABEL_GDT64
- [SECTION .s16] ;定义一个名为.s16的段
- [BITS 16] ; 通知nasm,将要运行在16位宽的处理器上
- Label_Start:
- mov ax, cs
- mov ds, ax ; 初始化数据段寄存器
- mov es, ax ; 初始化附加段寄存器
- mov ax, 0x00
- mov ss, ax ;初始化堆栈段寄存器
- mov sp, 0x7c00
- ;在屏幕上显示 start Loader
- mov ax, 0x1301
- mov bx, 0x000f
- mov dx, 0x0100 ;在第2行显示
- mov cx, 23 ;设置消息长度
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, Message_Start_Loader
- int 0x10
- ;jmp $
- ; 使用A20快速门来开启A20信号线
- push ax
- in al, 0x92 ; A20快速门使用I/O端口0x92来处理A20信号线
- or al, 0x02 ; 通过将0x92端口的第1位置1,开启A20地址线
- out 0x92, al
- pop ax
- cli ; 关闭外部中断
- db 0x66
- lgdt [GdtPtr] ; LGDT/LIDT - 加载全局/中断描述符表格寄存器
- ; 置位CR0寄存器的第0位,开启保护模式
- mov eax, cr0
- or eax, 1
- mov cr0, eax
- ; 为fs寄存器加载新的数据段的值
- mov ax, SelectorData32
- mov fs, ax
- ; fs寄存器加载完成后,立即从保护模式退出。 这样能使得fs寄存器在实模式下获得大于1MB的寻址能力。
- mov eax, cr0
- and al, 11111110b ; 将第0位置0
- mov cr0, eax
- sti ; 开启外部中断
- ; =========在文件系统中搜索 kernel.bin==========
- mov word [SectorNo], SectorNumOfRootDirStart ;保存根目录起始扇区号
- Label_Search_In_Root_Dir_Begin:
- cmp word [RootDirSizeForLoop], 0 ; 比较根目录扇区数量和0的关系。 cmp实际上是进行了一个减法运算
- jz Label_No_KernelBin ; 等于0,不存在kernel.bin
- dec word [RootDirSizeForLoop]
- mov ax, 0x00
- mov es, ax
- mov bx, 0x8000
- mov ax, [SectorNo] ;向函数传入扇区号
- mov cl, 1
- call Func_ReadOneSector
- mov si, Kernel_FileName ;向源变址寄存器传入Loader文件的名字
- mov di, 0x8000
- cld ;由于LODSB的加载方向与DF标志位有关,因此需要用CLD清零DF标志位
- mov dx, 0x10 ; 每个扇区的目录项的最大条数是(512/32=16,也就是0x10)
- Label_Search_For_LoaderBin:
- cmp dx, 0
- jz Label_Goto_Next_Sector_In_Root_Dir
- dec dx
- mov cx, 11 ; cx寄存器存储目录项的文件名长度, 11B,包括了文件名和扩展名,但是不包括 分隔符'.'
- Label_Cmp_FileName:
- cmp cx, 0
- jz Label_FileName_Found
- dec cx
- lodsb ; 把si对应的字节载入al寄存器中,然后,由于DF为0,si寄存器自增
- cmp al, byte [es:di] ; 间接取址[es+di]。 也就是进行比较当前文件的名字对应字节和loader文件名对应字节
- jz Label_Go_On ; 对应字节相同
- jmp Label_Different ; 字节不同,不是同一个文件
- Label_Go_On:
- inc di
- jmp Label_Cmp_FileName
- Label_Different:
- and di, 0xffe0 ;将di恢复到当前目录项的第0字节
- add di, 0x20 ;将di跳转到下一目录项的第0字节
- mov si, Kernel_FileName
- jmp Label_Search_For_LoaderBin ;继续搜索下一目录项
- Label_Goto_Next_Sector_In_Root_Dir:
- add word [SectorNo], 1
- jmp Label_Search_In_Root_Dir_Begin
- Label_No_KernelBin:
- ; 在屏幕上显示 [ERROR] No Kernel Found.
- mov ax, 0x1301
- mov bx, 0x000c ; 红色闪烁高亮黑底
- mov dx, 0x0200 ; 显示在第3行(前面已经显示过2行了)
- mov cx, 24 ; 字符串长度
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, Message_No_Loader
- int 0x10
- jmp $
- ; ========= 找到了 kernel.bin ===========
- ; 将内核加载到内存中
- Label_FileName_Found:
- mov ax, RootDirSectors
- ; 先取得目录项DIR_FstClus字段的值(起始簇号)
- and di, 0xffe0
- add di, 0x1a
- mov cx, word [es:di]
- push cx
- add cx, ax
- add cx, SectorBalance
- mov eax, Base_Tmp_Of_Kernel_Addr ; 内核放置的临时地址
- mov es, eax ;配置es和bx,指定kernel.bin在内存中的起始地址
- mov bx, Offset_Tmp_Of_Kernel_File
- mov ax, cx
- Label_Go_On_Loading_File:
- push ax
- push bx
- ; 显示字符.
- mov ah, 0x0e
- mov al, "."
- mov bl, 0x0f
- int 0x10
- pop bx
- pop ax
- ; 读取一个扇区
- mov cl, 1
- call Func_ReadOneSector
- pop ax
- ; ======逐字节将内核程序复制到临时空间,然后转存到内核空间===
- push cx
- push eax
- push fs
- push edi
- push ds
- push esi
- mov cx, 0x0200 ; 指定计数寄存器的值为512, 为后面循环搬运这个扇区的数据做准备
- mov ax, Base_Of_Kernel_File
- mov fs, ax ; 这样在物理机上是行不通的,因为这样移动的话,fs就失去了32位寻址能力
- mov edi, dword [OffsetOfKernelFileCount] ; 指定目的变址寄存器
- mov ax, Base_Tmp_Of_Kernel_Addr
- mov ds, ax
- mov esi, Offset_Tmp_Of_Kernel_File ; 指定来源变址寄存器
- Label_Move_Kernel:
- ; 真正进行数据的移动
- mov al, byte [ds:esi] ; 移动到临时区域
- mov byte [fs:edi], al ; 再移动到目标区域
- inc esi
- inc edi
- loop Label_Move_Kernel
- ; 当前扇区数据移动完毕
- mov eax, 0x1000
- mov ds, eax
- mov dword [OffsetOfKernelFileCount], edi ; 增加偏移量
- pop esi
- pop ds
- pop edi
- pop fs
- pop eax
- pop cx
- call Func_GetFATEntry
- cmp ax, 0x0fff
- jz Label_File_Loaded
- push ax
- mov dx, RootDirSectors
- add ax, dx
- add ax, SectorBalance
- ; 继续读取下一个簇
- jmp Label_Go_On_Loading_File
- Label_File_Loaded:
- ;在屏幕上显示 kernel loaded
- mov ax, 0x1301
- mov bx, 0x000f
- mov dx, 0x0200 ;在第3行显示
- mov cx, 20 ;设置消息长度
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, Message_Kernel_Loaded
- int 0x10
- ; ======直接操作显示内存=======
- ; 从内存的0x0B800开始,是一段用于显示字符的内存空间。
- ; 每个字符占用2bytes,低字节保存要显示的字符,高字节保存样式
- mov ax, 0xB800
- mov gs, ax
- mov ah, 0x0F ;黑底白字
- mov al, '.'
- mov [gs:((80 * 2 + 20) * 2)], ax ;在屏幕第0行,39列
- Label_Kill_Motor:
- ; =====关闭软驱的马达======
- ; 向IO端口0x03f2写入0,关闭所有软驱
- push dx
- mov dx, 0x03F2
- mov al, 0
- out dx, al
- pop dx
- ; =====获取物理地址空间====
- ; 显示 正在获取内存结构
- mov ax, 0x1301
- mov bx, 0x000F
- mov dx, 0x0300 ; 在第四行显示
- mov cx, 34
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, Message_Start_Get_Mem_Struct
- int 0x10
- mov ax, 0x00
- mov es, ax
- mov di, Memory_Struct_Buffer_Addr ; 设置内存结构信息存储的地址
- mov ebx, 0 ;第一次调用0x15的时候,ebx要置为0 ebx存储的是下一个待返回的ARDS (Address Range Descriptor Structure)
- Label_Get_Mem_Struct:
- ;==== 获取内存物理地址信息
- ; 使用0x15中断程序的功能号0xe820来获取内存信息
- ; 返回信息在[es:di]指向的内存中
- ; 一共要分5次才能把20个字节的信息获取完成
- ; 这些信息在内核初始化内存管理单元的时候,会去解析它们。
- mov eax, 0xe820
- mov ecx, 20 ; 指定ARDS结构的大小,是固定值20
- mov edx, 0x534d4150 ; 固定签名标记,是字符串“SMAP”的ASCII码
- int 0x15
- jc Label_Get_Mem_Fail ; 若调用出错,则CF=1
- add di, 20
- cmp ebx, 0
- jne Label_Get_Mem_Struct ; ebx不为0
- jmp Label_Get_Mem_OK ; 获取内存信息完成
- Label_Get_Mem_Fail:
- ; =====获取内存信息失败====
- ; 显示 正在获取内存结构
- mov ax, 0x1301
- mov bx, 0x000c
- mov dx, 0x0400 ; 在第5行显示
- mov cx, 33
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, Message_Get_Mem_Failed
- int 0x10
- jmp $
- Label_Get_Mem_OK:
- ; ==== 成功获取内存信息 ===
- mov ax, 0x1301
- mov bx, 0x000f
- mov dx, 0x0400 ; 在第5行显示
- mov cx, 38
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, Message_Get_Mem_Success
- int 0x10
- jmp Label_Get_SVGA_Info
- Label_Get_SVGA_Info:
- ; ==== 获取SVGA芯片的信息
- mov ax, 0x1301
- mov bx, 0x000f
- mov dx, 0x0500 ; 在第6行显示
- mov cx, 34
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, Message_Start_Get_SVGA_VBE_Info
- int 0x10
- ; 使用INT0x10的主功能号0x4F00获取SVGA VBE信息
- ; For more information, please visit: https://longjin666.top/?p=1321
- mov ax, 0x00
- mov es, ax
- mov di, 0x8000
- mov ax, 0x4F00
- int 0x10
- cmp ax, 0x004F ; 获取成功
- jz Label_Get_SVGA_VBE_Success
- Label_Get_SVGA_VBE_Failed:
- ; 获取SVGA VBE信息失败
- mov ax, 0x1301
- mov bx, 0x008c
- mov dx, 0x0600 ; 在第7行显示
- mov cx, 33
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, Message_Get_SVGA_VBE_Failed
- int 0x10
- jmp $
- Label_Get_SVGA_VBE_Success:
- mov ax, 0x1301
- mov bx, 0x000f
- mov dx, 0x0600 ; 在第7行显示
- mov cx, 38
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, Message_Get_SVGA_VBE_Success
- int 0x10
- Label_Get_SVGA_Mode_Info:
- ; ====== 获取SVGA mode信息 ======
- mov ax, 0x1301
- mov bx, 0x000f
- mov dx, 0x0700 ; 在第8行显示
- mov cx, 35
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, Message_Start_Get_SVGA_Mode_Info
- int 0x10
- mov ax, 0x00
- mov es, ax
- mov si, 0x800e ; 根据文档可知,偏移量0Eh处, DWORD pointer to list of supported VESA and OEM video modes
- ;(list of words terminated with FFFFh)
- mov esi, dword [es:si]
- mov edi, 0x8200
- Label_SVGA_Mode_Info_Get:
- mov cx, word [es:esi]
- ; ===========显示SVGA mode的信息
- ;push ax
- ;mov ax, 0x00
- ;mov al, ch
- ;call Label_DispAL
- ;mov ax, 0x00
- ;mov al, cl
- ;call Label_DispAL
- ;pop ax
- ;============
- ; 判断是否获取完毕
- cmp cx, 0xFFFF
- jz Label_SVGA_Mode_Info_Finish
- mov ax, 0x4f01 ; 使用4f01功能,获取SVGA的模式
- int 0x10
- cmp ax, 0x004f ; 判断是否获取成功
- jnz Label_SVGA_Mode_Info_Fail
- add esi, 2
- add edi, 0x100 ; 开辟一个 256-byte 的 buffer
- jmp Label_SVGA_Mode_Info_Get
- Label_SVGA_Mode_Info_Fail:
- ; === 获取信息失败 ===
- mov ax, 0x1301
- mov bx, 0x008c
- mov dx, 0x0800 ; 在第9行显示
- mov cx, 34
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, Message_Get_SVGA_Mode_Failed
- int 0x10
- jmp $
- Label_SVGA_Mode_Info_Finish:
- ; === 成功获取SVGA mode信息 ===
- mov ax, 0x1301
- mov bx, 0x000f
- mov dx, 0x0800 ; 在第9行显示
- mov cx, 39
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, Message_Get_SVGA_Mode_Success
- int 0x10
- jmp Label_Set_SVGA_Mode
- Label_SET_SVGA_Mode_VESA_VBE_FAIL:
- ; 设置SVGA显示模式失败
- mov ax, 0x1301
- mov bx, 0x008c
- mov dx, 0x0800 ; 在第10行显示
- mov cx, 29
- push ax
- mov ax, ds
- mov es, ax
- pop ax
- mov bp, Message_Set_SVGA_Mode_Failed
- int 0x10
- jmp $
- Label_Set_SVGA_Mode:
- ; ===== 设置SVGA芯片的显示模式(VESA VBE) ===
- mov ax, 0x4f02 ; 使用int0x10 功能号AX=4f02设置SVGA芯片的显示模式
- mov bx, 0x4180 ; 显示模式可以选择0x180(1440*900 32bit)或者0x143(800*600 32bit)
- int 0x10
- cmp ax, 0x004F
- jnz Label_SET_SVGA_Mode_VESA_VBE_FAIL
- ; ===== 初始化GDT表,切换到保护模式 =====
- cli ; 关闭外部中断
- db 0x66
- lgdt [GdtPtr]
- db 0x66
- lidt [IDT_POINTER]
- mov eax, cr0
- or eax, 1 ; 启用保护模式
- mov cr0, eax
- ; 跳转到保护模式下的第一个程序
- jmp dword SelectorCode32:GO_TO_TMP_Protect
- [SECTION .s32]
- [BITS 32]
- GO_TO_TMP_Protect:
- ; ==== 切换到长模式 =====
- mov ax, 0x10
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov ss, ax
- mov esp, 0x7e00 ; 将栈指针设置在实模式获取到的数据的基地址上
- call support_long_mode ; 检测是否支持长模式
- test eax, eax ; 将eax自身相与,检测是否为0(test指令不会把结果赋值回去eax)
- jz no_support ; 不支持长模式
- ; 初始化临时页表, 基地址设置为0x90000
- ; 设置各级页表项的值(页表起始地址与页属性组成)
- mov dword [0x90000], 0x91007
- mov dword [0x90004], 0x00000
- mov dword [0x90800], 0x91007
- mov dword [0x90804], 0x00000
- mov dword [0x91000], 0x92007
- mov dword [0x91004], 0x00000
- mov dword [0x92000], 0x000083
- mov dword [0x92004], 0x000000
- mov dword [0x92008], 0x200083
- mov dword [0x9200c], 0x000000
- mov dword [0x92010], 0x400083
- mov dword [0x92014], 0x000000
- mov dword [0x92018], 0x600083
- mov dword [0x9201c], 0x000000
- mov dword [0x92020], 0x800083
- mov dword [0x92024], 0x000000
- mov dword [0x92028], 0xa00083
- mov dword [0x9202c], 0x000000
- ; === 加载GDT ===
- db 0x66
- lgdt [GdtPtr64] ; 加载GDT
- ; 把临时gdt的数据段加载到寄存器中(cs除外)
- mov ax, 0x10
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov gs, ax
- mov ss, ax
- mov esp, 0x7e00
- ; ====== 开启物理地址扩展 =====
- ; 通过bts指令,将cr4第5位置位,开启PAE
- mov eax, cr4
- bts eax, 5
- mov cr4, eax
- ; 将临时页目录的地址设置到CR3控制寄存器中
- mov eax, 0x90000
- mov cr3, eax
- ; ==== 启用长模式 ===
- ; 参见英特尔开发手册合集p4360 volume4, chapter2 页码2-60 Vol. 4
- ; IA32_EFER寄存器的第8位是LME标志位,能启用IA-32e模式
- mov ecx, 0xC0000080
- rdmsr
- bts eax, 8
- wrmsr
- ; === 开启分页机制 ===
- mov eax, cr0
- bts eax, 0 ; 再次开启保护模式
- bts eax, 31 ; 开启分页管理机制
- mov cr0, eax
- ; === 通过此条远跳转指令,处理器跳转到内核文件进行执行,正式进入IA-32e模式
- jmp SelectorCode64:Offset_Of_Kernel_File
- support_long_mode:
- ; ===== 检测是否支持长模式 ====
- mov eax, 0x80000000
- cpuid ; cpuid指令返回的信息取决于eax的值。当前返回到eax中的是最大的输入参数值。 详见:英特尔开发人员手册卷2A Chapter3 (Page 304)
- cmp eax, 0x80000001
- setnb al ; 当cmp结果为不低于时,置位al
- jb support_long_mode_done ; 当eax小于0x80000001时,跳转
- mov eax, 0x80000001
- cpuid ; 获取特定信息,参照开发人员手册卷2A p304
- bt edx, 29 ; 将edx第29位的值移到CF上。该位指示了CPU是否支持IA-32e模式
- ; Bit 29: Intel® 64 Architecture available if 1.
- setc al ; 若支持则al置位
- support_long_mode_done:
- movzx eax, al ; 将al,零扩展为32位赋值给eax
- ret
- no_support:
- ; 不支持长模式
- jmp $
- [SECTION .s16lib]
- [BITS 16]
- ; 从软盘读取一个扇区
- ; AX=待读取的磁盘起始扇区号
- ; CL=读入的扇区数量
- ; ES:BX=>目标缓冲区起始地址
- Func_ReadOneSector:
- push bp
- mov bp, sp
- sub esp, 2
- mov byte [bp-2], cl
- push bx
- mov bl, [BPB_SecPerTrk]
- div bl ;用AX寄存器中的值除以BL,得到目标磁道号(商:AL)以及目标磁道内的起始扇区号(余数:AH)
- inc ah ; 由于磁道内的起始扇区号从1开始计数,因此将余数+1
- mov cl, ah
- mov dh, al
- shr al, 1 ;计算出柱面号
- mov ch, al
- and dh, 1;计算出磁头号
- pop bx
- mov dl, [BS_DrvNum]
- ;最终,dh存储了磁头号,dl存储驱动器号
- ; ch存储柱面号,cl存储起始扇区号
- Label_Go_On_Reading:
- ; 使用BIOS中断服务程序INT13h的主功能号AH=02h实现软盘读取操作
- mov ah, 2
- mov al, byte [bp-2]
- int 0x13
- jc Label_Go_On_Reading ;当CF标志位被复位时,说明数据读取完成,恢复调用现场
- add esp, 2
- pop bp
- ret
- ; 解析FAT表项,根据当前FAT表项索引出下一个FAT表项
- Func_GetFATEntry:
- ; AX=FAT表项号(输入、输出参数)
- ; 保存将要被修改的寄存器
- push es
- push bx
- push ax
- ; 扩展段寄存器
- mov ax, 00
- mov es, ax
- pop ax
- mov byte [Odd], 0 ;将奇数标志位置0
- ; 将FAT表项号转换为总的字节号
- mov bx, 3
- mul bx
- mov bx, 2
- div bx
- cmp dx, 0
- jz Label_Even ; 偶数项
- mov byte [Odd], 1
- Label_Even:
- xor dx, dx ;把dx置0
- ; 计算得到扇区号(商)和扇区内偏移(余数)
- mov bx, [BPB_BytesPerSec]
- div bx
- push dx
- ; 读取两个扇区到[es:bx]
- mov bx, 0x8000
- add ax, SectorNumOfFAT1Start
- mov cl, 2 ; 设置读取两个扇区,解决FAT表项跨扇区的问题
- call Func_ReadOneSector
- pop dx
- add bx, dx
- mov ax, [es:bx]
- cmp byte [Odd], 1
- jnz Label_Even_2 ;若是偶数项,则跳转
- shr ax, 4 ; 解决奇偶项错位问题
- Label_Even_2:
- and ax, 0x0fff ; 确保表项号在正确的范围内 0x0003~0x0fff
- pop bx
- pop es
- ret
- ; ==== 显示AL中的信息 ===
- Label_DispAL:
- push ecx
- push edx
- push edi
- mov edi, [DisplayPosition]
- mov ah, 0x0F
- mov dl, al ; 为了先显示al的高4位,因此先将al暂存在dl中,然后把al往右移动4位
- shr al, 4
- mov ecx, 2 ; 计数为2
- .begin:
- and al, 0x0F
- cmp al, 9
- ja .1 ; 大于9,跳转到.1
- add al, '0'
- jmp .2
- .1:
- sub al, 0x0a
- add al, 'A'
- .2:
- ; 移动到显示内存中
- mov [gs:edi], ax
- add edi, 2
- mov al, dl
- loop .begin
- mov [DisplayPosition], edi
- pop edi
- pop edx
- pop ecx
- ret
- ; === 临时的中断描述符表 ===
- ; 为临时的IDT开辟空间。
- ; 由于模式切换过程中已经关闭了外部中断,只要确保模式切换过程中不产生异常,就不用完整的初始化IDT。甚至乎,只要没有异常产生,没有IDT也可以。
- IDT:
- times 0x50 dq 0
- IDT_END:
- IDT_POINTER:
- dw IDT_END - IDT - 1
- dd IDT
- ;==== 临时变量 =====
- RootDirSizeForLoop dw RootDirSectors
- SectorNo dw 0
- Odd db 0
- OffsetOfKernelFileCount dd Offset_Of_Kernel_File
- DisplayPosition dd 0
- ; 要显示的消息文本
- Message_Start_Loader: db "[DragonOS] Start Loader"
- Message_No_Loader: db "[ERROR] No Kernel Found."
- Message_Kernel_Loaded: db "[INFO] Kernel loaded"
- Message_Start_Get_Mem_Struct: db "[INFO] Try to get memory struct..."
- Message_Get_Mem_Failed: db "[ERROR] Get memory struct failed."
- Message_Get_Mem_Success: db "[INFO] Successfully got memory struct."
- Message_Start_Get_SVGA_VBE_Info: db "[INFO] Try to get SVGA VBE info..."
- Message_Get_SVGA_VBE_Failed: db "[ERROR] Get SVGA VBE info failed."
- Message_Get_SVGA_VBE_Success: db "[INFO] Successfully got SVGA VBE info."
- Message_Start_Get_SVGA_Mode_Info: db "[INFO] Try to get SVGA mode info..."
- Message_Get_SVGA_Mode_Failed: db "[ERROR] Get SVGA Mode info failed."
- Message_Get_SVGA_Mode_Success: db "[INFO] Successfully got SVGA Mode info."
- Message_Set_SVGA_Mode_Failed: db "[ERROR] Set SVGA Mode failed."
- Kernel_FileName: db "KERNEL BIN", 0
|