📄 fat32.asm
字号:
_REBOOT: ; 重启
MOV SI,MessageRestart
CALL ShowMessage
; 调用键盘中断,等待用户按键
MOV AH,00H
INT 16H
; 重启计算机
INT 19H
; 死循环
JMP $
;====================================================================
;
; 子过程
;
;====================================================================
;====================================================================
;
; 读取一个磁盘扇区
; 输入: 已经设置了DAP中相应的字段
; 限制: 不能读取超过一个簇的内容
;
;====================================================================
ReadSector:
PUSHA ; 保存寄存器
; 检查是否使用扩展方式
CMP BYTE [BP - DISK_EXT_SUPPORT],00H
JZ .NoDiskExtension
;====================================================================
; INT 13H AH = 42H 扩展磁盘调用
;====================================================================
; 每次读取一个扇区
MOV AH,42H ; 功能号
LEA SI ,[BP - DAP_PACKET_SIZE] ; 地址包地址
; 驱动器号
MOV DL ,[DriveNumber] ; 驱动器号
INT 13H
JC _DISK_ERROR ; 读取失败
JMP _READ_SECTOR_OK ; 读取成功
;====================================================================
;
; INT 13H
; AH = 2 柱面号:0 - 1023
; AL = 要读取的扇区数 磁头号:0 - 255
; CH = 柱面号低8位 扇区号:1 - 63
; CL = 柱面号高2位 : 6位扇区号
; DH = 磁头号
; DL = 驱动器号
;
; LBA = ( (cylinder * HeadsPerCylinder + heads ) * SectorsPerTrack ) + sector - 1
;
; Sector = LBA % SectorsPerTrack +1
; Head = ( LBA / SectorsPerTrack ) % HeadsPerCylinder
; Cylinder= ( LBA / SectorsPerTrack ) / HeadsPerCylinder
;
;====================================================================
.NoDiskExtension:
;===================================================================
; 首先需要将扇区号转换为CHS地址
;===================================================================
; 首先计算扇区号
MOV AX,WORD [ BP - DAP_SECTOR_LOW ]
MOV DX,WORD [ BP - DAP_SECTOR_LOW+2 ]
DIV WORD [SectorsPerTrack ] ; AX = LBA / SectorsPerTrack DX = LDA % SectorsPerTrack
MOV CX,DX
INC CX ; CL = Sector
AND CL,3FH ; 1-63
; 再计算磁头号和柱面号
XOR DX,DX ; DX:AX = LBA / SectorsPerTrack
DIV WORD [HeadsPerCylinder] ; DX = ( LBA/SectorsPerTrack ) % HeadsPerCylinder = Head
; AX = ( LBA/SectorsPerTrack ) / HeadsPerCylinder = Cylinder
MOV CH,AL ; 柱面号低8位
SHL AH,6
OR CL,AH ; CL = 柱面号高2位:6位扇区号
MOV DH,DL ; DL = 磁头号
; 准备读取磁盘
MOV AX,WORD[ BP - DAP_BUFFER_SEG ]
MOV ES,AX
MOV BX,WORD[ BP - DAP_BUFFER_OFF ]
MOV AX ,0201H ; 每次只读取一个扇区
; 驱动器号
MOV DL ,[DriveNumber] ; 驱动器号
INT 13H
JC _DISK_ERROR ; 读取失败
_READ_SECTOR_OK:
POPA ; 恢复寄存器
RET
;====================================================================
;
; 显示一个字符串
; 输入:
; DS:SI = 字符串的起始地址(以NULL结束)
;
;====================================================================
ShowMessage:
LODSB ; AL = DS:[SI] SI = SI+1
OR AL,AL ; 检测是否遇到NULL字符串
JZ _SHOW_END
MOV AH,0EH
MOV BX,07H
INT 10H
JMP ShowMessage
_SHOW_END:
RET
;====================================================================
; 数据区
;====================================================================
LoaderName db "FDOSLDR BIN" ; 第二阶段启动程序 FDOSLDR.BIN
MessageMissLoader db "NO FDOSLDR.BIN.",0DH,0AH,00H ; 没有找到装载程序
MessageDiskError db "Disk Error.",0DH,0AH,00 ; 磁盘错误消息
MessageRestart db "Press any key to restart." ,0DH,0AH,00 ; 提示重启消息
;====================================================================
; 扇区最后的标记字节(NASM不支持重复ORG)
;====================================================================
Padding TIMES 510-($-$$) db 00H
SectorSignature dw 0AA55H
;====================================================================
; 第二个扇区的代码(该代码位于分区的第四个扇区)
;====================================================================
;====================================================================
; 查找根目录,检查是否有 FDOSLDR.BIN文件
;====================================================================
_SEARCH_LOADER:
; 设置缓冲区
MOV WORD [ BP - DAP_BUFFER_OFF ], DATA_BUF_OFF ; 0000:1000H
; 根目录起始簇号
MOV EAX,DWORD[RootDirectoryStart]
MOV DWORD[ BP - CURRENT_CLUSTER ],EAX
; 检查下一个簇
_NEXT_ROOT_CLUSTER:
; 根据簇号计算扇区号(EAX-2)*SectorsPerCluster+DATA_START_SECTOR
DEC EAX
DEC EAX ; EAX = EAX - 2
XOR EBX,EBX
MOV BL, BYTE[ SectorsPerCluster]
MUL EBX
ADD EAX,DWORD[ BP- DATA_START_SECTOR]
MOV DWORD[ BP - DAP_SECTOR_LOW ], EAX
MOV DL,[SectorsPerCluster]
; 检查下一个扇区
_NEXT_ROOT_SECTOR:
; 依次读取每个根目录扇区,检查是否存在FDOSLDR.BIN文件
CALL ReadSector
; 检查该扇区内容
MOV DI,DATA_BUF_OFF
MOV BL,BYTE [ BP - DIR_PER_SECTOR]
; 检查每一个目录项
_NEXT_ROOT_ENTRY:
CMP BYTE [DI],DIR_NAME_FREE
JZ _MISSING_LOADER ; NO MORE DIR ENTRY
; 检查是否装载程序
PUSH DI ; 保存DI
MOV SI,LoaderName
MOV CX,11
REPE CMPSB
JCXZ _FOUND_LOADER ; 装载Loader并运行
; 是否还有下一个目录项(内层循环)
POP DI
ADD DI,DIR_ENTRY_SIZE
DEC BL
JNZ _NEXT_ROOT_ENTRY
; 检查是否还有下一个扇区可读(外层循环)
DEC DL
JZ _CHECK_NEXT_ROOT_CLUSTER
INC DWORD [ BP - DAP_SECTOR_LOW ] ; 增加扇区号
JMP _NEXT_ROOT_SECTOR
; 检查下一个簇
_CHECK_NEXT_ROOT_CLUSTER:
; 计算FAT所在的簇号和偏移
; FatOffset = ClusterNum*4
XOR EDX,EDX
MOV EAX,DWORD[BP - CURRENT_CLUSTER]
SHL EAX,2
XOR ECX,ECX
MOV CX,WORD [ BytesPerSector ]
DIV ECX ; EAX = Sector EDX = OFFSET
ADD EAX,DWORD [BP - FAT_START_SECTOR ]
MOV DWORD [ BP - DAP_SECTOR_LOW ], EAX
; 读取扇区
CALL ReadSector
; 检查下一个簇
MOV DI,DX
ADD DI,DATA_BUF_OFF
MOV EAX,DWORD[DI] ; EAX = 下一个要读的簇号
AND EAX,CLUSTER_MASK
MOV DWORD[ BP - CURRENT_CLUSTER ],EAX
CMP EAX,CLUSTER_LAST ; CX >= 0FFFFFF8H,则意味着没有更多的簇了
JB _NEXT_ROOT_CLUSTER
JMP _MISSING_LOADER
;====================================================================
; 装载FDOSLDR.BIN文件
;====================================================================
_FOUND_LOADER:
; 目录结构地址放在DI中
POP DI
XOR EAX,EAX
MOV AX,[DI+OFF_START_CLUSTER_HIGH] ; 起始簇号高32位
SHL AX,16
MOV AX,[DI+OFF_START_CLUSTER_LOW] ; 起始簇号低32位
MOV DWORD[ BP - CURRENT_CLUSTER ],EAX
MOV CX, OSLOADER_SEG ; CX = 缓冲区段地址
_NEXT_DATA_CLUSTER:
; 根据簇号计算扇区号
DEC EAX
DEC EAX ; EAX = EAX - 2
XOR EBX,EBX
MOV BL, BYTE[ SectorsPerCluster]
MUL EBX
ADD EAX,DWORD[ BP- DATA_START_SECTOR]
MOV DWORD[ BP - DAP_SECTOR_LOW ], EAX
MOV DL,[SectorsPerCluster]
; 设置缓冲区
MOV WORD [ BP - DAP_BUFFER_SEG ],CX
MOV WORD [ BP - DAP_BUFFER_OFF ],00H
; 每个簇需要读取的扇区数
MOV BL , BYTE [SectorsPerCluster]
_NEXT_DATA_SECTOR:
; 读取簇中的每个扇区(内层循环)
; 注意 : 通过检查文件大小,可以避免读取最后一个不满簇的所有大小
; 读取数据扇区
CALL ReadSector
; 更新地址,继续读取
MOV AX, WORD [BytesPerSector]
ADD WORD [BP - DAP_BUFFER_OFF],AX
INC DWORD [BP - DAP_SECTOR_LOW] ; 递增扇区号
DEC BL ; 内层循环计数
JNZ _NEXT_DATA_SECTOR
; 检查下一个簇
; 更新读取下一个簇的缓冲区地址
MOV CL,BYTE [ SectorsPerCluster ]
MOV AX ,WORD [BytesPerSector]
SHR AX ,4
MUL CL
ADD AX ,WORD [ BP - DAP_BUFFER_SEG ]
MOV CX,AX ; 保存下一个簇的缓冲区段地址
;====================================================================
;
; 检查是否还有下一个簇(读取FAT表的相关信息)
; LET N = 数据簇号
; THUS FAT_BYTES = N*4 (FAT32)
; FAT_SECTOR = FAT_BYTES / BytesPerSector
; FAT_OFFSET = FAT_BYTES % BytesPerSector
;
;====================================================================
; 计算FAT所在的簇号和偏移
MOV EAX,DWORD [BP - CURRENT_CLUSTER]
XOR EDX,EDX
SHL EAX,2
XOR EBX,EBX
MOV BX,WORD [ BytesPerSector ]
DIV EBX ; EAX = Sector EDX = Offset
; 设置缓冲区地址
ADD EAX,DWORD [BP - FAT_START_SECTOR ]
MOV DWORD [ BP - DAP_SECTOR_LOW ], EAX
MOV WORD [BP - DAP_BUFFER_SEG ], 00H
MOV WORD [BP - DAP_BUFFER_OFF ], DATA_BUF_OFF ; 0000:1000H
; 读取扇区
CALL ReadSector
; 检查下一个簇
MOV DI,DX
ADD DI,DATA_BUF_OFF
MOV EAX,DWORD[DI] ; EAX = 下一个要读的簇号
AND EAX,CLUSTER_MASK
MOV DWORD[ BP - CURRENT_CLUSTER ],EAX
CMP EAX,CLUSTER_LAST ; CX >= 0FFFFFF8H,则意味着没有更多的簇了
JB _NEXT_DATA_CLUSTER
;读取完毕
_RUN_LOADER:
; 运行FDOSLDR.BIN
MOV DL,[DriveNumber]
jmp 800h:0100h;JMP 00:OSLOADER_ADDR
;====================================================================
; 调试例程
;====================================================================
%IFDEF DEBUG
;====================================================================
;====================================================================
;
; 显示一个字符
; 输入: AL = 待显示字符
;
;====================================================================
PrintChar:
PUSH BX
MOV AH,0EH
MOV BX,7
INT 10H
POP BX
RET
;====================================================================
%ENDIF ; DEBUG
;====================================================================
;====================================================================
; 扇区最后的标记字节(NASM不支持重复ORG)
;====================================================================
SecondPadding TIMES 1022-($-$$) db 00H
SecondSignature DW 0AA55H
;====================================================================
; 代码结束
;====================================================================
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -