boot.asm

本文分享了一个在DOS环境下自制的引导程序代码,通过读取磁盘根目录下的特定文件来实现系统的引导过程。文章详细展示了如何在实模式下进行磁盘操作,包括查找指定文件、读取扇区等步骤。

        经过一段时间的学习,把前四章都消化得差不多了,对进度和学习效果还是挺满意的。代码自己全部敲了几遍,没思路了再看看光盘上的代码,就这样下来掌握了最关键的思路。多亏了上学期学了汇编,感觉掌握得不错,呵呵,汇编非常有用,就是可读性太差。感觉底层很注重细节,稍微了哪里不对了全部死掉,用DEBUG查半天才查得到,呵呵。继续努力,至少在放寒假前看完上篇吧。争取把这个细菌操作系统当作毕业设计,哈哈。看完第四章只是迈了第一步,也是毕设的第一步吧。

        贴不了汇编代码,直接复制过来,这是晚上临时写的DOS测试,以后还要对细节修正。

;不在DOS中调试时,必须注释掉下面这行----------------------------------
%define _DEBUG_BOOT_
;---------------------------------------------------------------------
%ifdef _DEBUG_BOOT_
 org 0100h
%else
 org 7c00h
%endif
;---------------------------------------------------------------------
;---------------------------------------------------------------------
%ifdef _DEBUG_BOOT_
 Base_Of_Stack equ 100h  ;在DOS下调式时的栈基址
%else
 Base_Of_Stack equ 7c00h  ;在裸机上执行时的栈基址
%endif
;---------------------------------------------------------------------

 jmp short LABEL_BEGIN
 nop 

%include "include/FAT12hdc.inc"     
 
 Base_Of_Loader equ 9000h       ;LOADER缓冲区的段地址(实模式)
 Offset_Of_Loader equ 0100h      ;LOADER缓冲区的偏移地址(实模式)
 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_Loader_Name: db "LOADER  BIN"  ;LOADER文件名,用于比较
_w_Load_Offset: dw 0          ;控制131行的LOADER缓冲区的偏移
;显示的字符串
String_Len equ 9
_sz_Booting: db 'Booting  '
Booting_Message_Index equ 0
_sz_Ready:  db 'Ready    '
Ready_Message_Index equ 1
_sz_No_Loader: db 'No Loader'
No_Loader_Message_Index equ 2
 
LABEL_BEGIN:
 mov ax,cs
 mov ds,ax
 mov es,ax
 mov ss,ax
 mov sp,Base_Of_Stack
 
 ;显示BOOTING字符串
 mov dh,Booting_Message_Index
 call Disp_Str_In_Real_Mode
 
 ;编程思路:找到根目录下的LOADER.BIN
 ;根目录的范围是总扇区编号19到32,共占14个扇区
 ;分别读取每个根目录扇区,共14次
 ;每个扇区读16次
 ;每个扇区可以容纳512/32=16个文件信息
LABEL_SEARCH_LOADER_BEGIN:
 cmp word [_w_Root_Sector_For_Loop],0 ;比较要读取的根目录扇区数
 je LABEL_LOADER_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_Loader
 push ax
 mov ax,Base_Of_Loader
 push ax
 call Read_Sector
 add sp,8
 
 mov dx,File_Number_Per_Sector     ;初始化dx控制一个扇区循环次数为16次
 mov si,_sz_Loader_Name        ;ds:si指向LOADER的名字字符串
 mov ax,Base_Of_Loader         ;es:di指向缓冲区
 mov es,ax
 mov di,Offset_Of_Loader 
 
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,表示找到LOADER 
 je LABEL_LOADER_FOUND
 dec cx
 
 lodsb                 ;读取一个LOADER名字字符串到al
 cmp al,byte [es:di]          ;比较之
 jne LABEL_DIFF            ;有不同的直接进行下一个文件的比较 
 jmp LABEL_NEXT_CMP          ;相同则进行下一个字符的比较   
 
LABEL_DIFF:
 mov si,_sz_Loader_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_LOADER_BEGIN
 
LABEL_LOADER_NOT_FOUND:
 ;显示一些信息
 mov dh,No_Loader_Message_Index
 call Disp_Str_In_Real_Mode
 jmp LABEL_EXIT
 
LABEL_LOADER_FOUND:
 ; 显示一些信息
 mov dh,Ready_Message_Index
 call Disp_Str_In_Real_Mode
 
 and di,0ffe0h             ;此时找到LOADER,恢复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为LOADER的相应扇区在DATA区的总偏移
 
LABEL_READ_AGAIN:
 mov bx,1               ;把LOADER的相应一个扇区读到指定的位置
 push bx
 push ax
 mov ax,Offset_Of_Loader
 add ax,[_w_Load_Offset]
 push ax
 mov ax,Base_Of_Loader
 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_LOADED_COMPLETE
 push ax
 
 add ax,Root_Dir_Sector_Index   
 add ax,Root_Dir_Sector_Number
 sub ax,2   
 add word [_w_Load_Offset],Byte_Number_Per_Sector
 
 jmp LABEL_READ_AGAIN

LABEL_LOADED_COMPLETE:
 jmp Base_Of_Loader:Offset_Of_Loader  ;把控制权交给LOADER

LABEL_EXIT:
 %ifdef _BOOT_DEBUG_
  mov ax,4c00h
  int 21h
 %else
  jmp $
 %endif
 
;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------------------------------------------------
Disp_Str_In_Real_Mode:
 push es
 push bx
 push cx
 push dx
 push bp
 
 mov ax,String_Len
 mul dh
 add ax,_sz_Booting
 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 es
 
 ret
;end of Disp_Str_In_Real_Mode-----------------------------------------

times 510 - ($ - $$) db 0
dw 0xaa55

<think>好的,我现在需要帮助用户解决如何用NASMboot.asm编译成boot.bin文件,然后用于制作ISO镜像的问题。用户之前已经询问过使用UltraISO和boot.bin制作ISO镜像的步骤,现在他们的需求更具体到编译部分,并且引用了之前的一些资料,可能需要结合这些引用内容来完善回答。 首先,我需要确保用户正确安装了NASM。根据引用[4],用户可能需要从官网下载NASM,但用户现在可能是在Windows环境下操作,因为之前的问题提到的是Windows 11下使用UltraISO。所以需要确认NASM在Windows下的安装步骤。用户提供的引用[4]提到的是Linux下的RPM包,但用户现在可能在Windows上,所以需要指导他们如何在Windows安装NASM,比如下载Windows版本的NASM并添加到系统路径中。 接下来是编译命令。用户提供的示例中使用了nasm boot.asm -f bin -o boot.bin,但根据引用[1],代码里有一个org 07c00h的指令,所以需要确保编译时正确设置起始地址。可能需要提醒用户,在编写boot.asm时,必须包含正确的起始地址和引导扇区结束标志0xAA55,否则生成的bin文件可能无法被正确识别为引导扇区。 然后,用户提到制作ISO镜像的问题,之前回答中使用的是UltraISO加载boot.bin作为引导文件。但根据引用[1]和[2],用户可能需要创建一个虚拟软盘映像,并将boot.bin写入到该映像的第一个扇区。不过用户现在的问题可能更倾向于直接制作ISO,而不是软盘映像。需要区分这两种情况,但根据用户当前的提问,他们可能希望直接生成ISO,所以需要结合之前的UltraISO步骤,同时确保编译的boot.bin正确无误。 另外,引用[2]提到将loader.bin复制到FAT12文件系统的虚拟软盘,这可能涉及到挂载镜像文件,但用户现在可能不需要文件系统,因为只是显示字符串的极简ISO。因此,重点应放在如何正确编译boot.asm生成boot.bin,并确保其结构符合引导扇区的要求(512字节,结束标志0xAA55)。 用户可能遇到的常见问题包括编译后的文件大小不正确,或者缺少结束标志,导致ISO无法启动。需要提醒用户检查生成的boot.bin文件是否符合这些要求,比如使用Hex编辑器查看最后两个字节是否为55 AA(注意字节顺序)。此外,可能还需要指导用户如何验证编译后的文件是否正确,比如通过虚拟机测试。 最后,用户可能需要将boot.bin作为引导文件加载到ISO中,所以在回答中需要将编译步骤和之前的ISO制作步骤连接起来,确保流程连贯。可能需要强调在UltraISO中正确加载引导文件,并保存为ISO,然后测试。</think>### **使用 NASM 编译 boot.asm 生成 boot.bin 的完整步骤** #### **1. NASM 环境配置** - **Windows 系统安装 NASM**: 1. 访问 [NASM 官网](https://www.nasm.us/),下载 Windows 版本(如 `nasm-2.16.01-win64.zip`)。 2. 解压文件到任意目录(例如 `C:\nasm`)。 3. 将 NASM 可执行文件路径(如 `C:\nasm`)添加到系统环境变量 `PATH` 中[^4]。 4. 验证安装:打开命令提示符,输入 `nasm -v`,若显示版本号(如 `NASM version 2.16.01`)则成功。 --- #### **2. 编写 boot.asm 引导程序** - **示例代码**(保存为 `boot.asm`): ```nasm org 0x7C00 ; 指定加载地址为 0x7C00(BIOS 默认引导扇区加载地址) bits 16 ; 16 位实模式 start: mov ax, 0x03 ; 清屏功能(AH=0x06, AL=0x00) int 0x10 mov si, msg ; 将字符串地址存入 SI 寄存器 call print_str ; 调用打印函数 jmp $ ; 无限循环挂起 print_str: lodsb ; 从 SI 加载字符到 AL or al, al ; 判断是否到字符串结尾(0) jz done mov ah, 0x0E ; BIOS 打印字符功能(AH=0x0E) int 0x10 jmp print_str done: ret msg db 'Hello, Custom ISO!', 0 times 510-($-$$) db 0 ; 填充剩余空间至 510 字节 dw 0xAA55 ; 引导扇区结束标志(小端格式:0x55 0xAA) ``` - **关键说明**: - `org 0x7C00`:指定代码在内存中的加载地址,确保 BIOS 正确执行指令[^1]。 - `times 510-($-$$) db 0`:填充至 510 字节,保证引导扇区总长度为 512 字节。 - `dw 0xAA55`:引导扇区结束标志,必须位于第 511-512 字节[^1]。 --- #### **3. 编译 boot.asmboot.bin** 1. 打开命令提示符,进入 `boot.asm` 所在目录。 2. 执行编译命令: ```bash nasm boot.asm -f bin -o boot.bin ``` - `-f bin`:指定输出为二进制格式。 - `-o boot.bin`:定义输出文件名。 3. **验证编译结果**: - 检查文件大小是否为 **512 字节**。 - 使用 **Hex 编辑器**(如 [HxD](https://mh-nexus.de/en/hxd/))打开 `boot.bin`,确认最后两个字节为 `55 AA`(小端存储)。 --- #### **4.boot.bin 集成到 ISO 镜像** 1. **使用 UltraISO**(参考[用户方案](https://example.com)): - 新建空白 ISO 项目:**文件 → 新建 → 数据光盘映像**。 - 加载引导文件:**启动 → 加载引导文件**,选择 `boot.bin`。 - 保存 ISO:**文件 → 另存为**,格式选 `ISO 9660`,勾选 `Joliet` 以支持长文件名[^3]。 2. **替代方案(DD 命令,Linux/Mac)**: ```bash dd if=boot.bin of=image.iso bs=512 count=1 ``` --- #### **5. 虚拟机测试 ISO 镜像** - **VirtualBox 示例**: 1. 新建虚拟机,类型选 `Other`,版本选 `DOS`。 2. 挂载 ISO:**设置 → 存储 → 控制器 IDE → 选择虚拟光驱**,加载 `image.iso`。 3. 启动虚拟机,若成功将显示字符串: ``` Hello, Custom ISO! ``` --- ### **常见问题解决** #### **Q1: 编译时报错“invalid combination of opcode and operands”** - **原因**:汇编语法错误(如寄存器使用不当)。 - **解决**:检查代码中指令与寄存器的兼容性,例如 `bits 16` 模式下不可使用 32 位寄存器。 #### **Q2: ISO 启动后无显示或卡死** - **原因**:引导代码未正确处理 BIOS 中断或缺少挂起逻辑。 - **解决**: - 确保 `jmp $` 指令用于挂起程序。 - 验证 `int 0x10` 中断调用参数(如 AH=0x0E 为字符打印功能)[^2]。 #### **Q3: boot.bin 文件大小不足 512 字节** - **原因**:填充指令 `times 510-($-$$) db 0` 计算错误。 - **解决**:手动计算代码长度,确保填充至 510 字节,并添加结束标志。 --- ### **扩展应用** - **多扇区加载**:修改引导代码,从 ISO 中加载更多扇区数据(需扩展汇编代码)。 - **交互功能**:通过 `int 0x16` 实现键盘输入检测(如按下按键后显示文本)。 --- ### **相关问题** 1. 如何通过 BIOS 中断在引导扇区中读取磁盘内容? 2. 如何为 ISO 镜像添加 FAT12 文件系统以支持文件加载? 3. 如何在引导扇区中实现彩色文本显示? [^1]: 引导扇区必须严格遵循 512 字节长度和 `0xAA55` 结束标志,否则无法被 BIOS 识别为有效引导程序。 [^2]: BIOS 中断 `int 0x10` 的功能号需根据 AH 寄存器设置,例如 AH=0x0E 为逐字符打印。 [^3]: Joliet 文件系统扩展允许在 ISO 镜像中使用长文件名和 Unicode 字符,但需虚拟机支持。 [^4]: 若 NASM 未加入环境变量,需通过绝对路径调用(如 `C:\nasm\nasm.exe boot.asm -f bin -o boot.bin`)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值