30天自制操作系统(NASM+GCC版)
配置环境
VSCODE
+x86 and x86_64 Assembly
插件 +hexdump for VSCode
插件- NASM
- QEMU
- Windows 10 + MingW64 8.1.0(MSYS2)
C:\WINDOWS\system32>gcc --version
gcc (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
...
安装 NASM 并把 nasm.exe 所在目录(默认C:\Program Files\NASM
)加入系统环境变量 PATH 里
安装 QEMU 并同样将其安装目录加入 PATH
然后关闭 VSCODE 软件再打开,使得环境变量刷新。
首先写一个 Day1 的 NASM 版源码
**展开查看源码**
; hello-os
; TAB=4
; 标准FAT12格式软盘专用的代码 Stand FAT12 format floppy code
DB 0xeb, 0x4e, 0x90
DB "HELLOIPL" ; 启动扇区名称(8字节)
DW 512 ; 每个扇区(sector)大小(必须512字节)
DB 1 ; 簇(cluster)大小(必须为1个扇区)
DW 1 ; FAT起始位置(一般为第一个扇区)
DB 2 ; FAT个数(必须为2)
DW 224 ; 根目录大小(一般为224项)
DW 2880 ; 该磁盘大小(必须为2880扇区1440*1024/512)
DB 0xf0 ; 磁盘类型(必须为0xf0)
DW 9 ; FAT的长度(必须是9扇区)
DW 18 ; 一个磁道(track)有几个扇区(必须为18)
DW 2 ; 磁头数(必须是2)
DD 0 ; 不使用分区,必须是0
DD 2880 ; 重写一次磁盘大小
; 书中作者说原因不明的两行代码我查到了,see https://www.ntfs.com/fat-partition-sector.htm
DB 0 ; BPB_Physical_Disk_Number DB (This is related to the BIOS physical disk number. Floppy drives are numbered starting with 0x00 for the A disk. Physical hard disks are numbered starting with 0x80. The value is typically 0x80 for hard disks, regardless of how many physical disk drives exist, because the value is only relevant if the device is the startup disk.)
DB 0 ; BPB_Current_Head DB (Not used by FAT file system)
DB 0x29 ; BPB_Signature DB (Must be either 0x28 or 0x29 in order to be recognized by Windows NT.)
DD 0xffffffff ; BPB_Volume_Serial_Number DD
DB "HELLO-OS " ; 磁盘的名称(必须为11字节,不足填空格)
DB "FAT12 " ; 磁盘格式名称(必须是8字节,不足填空格)
TIMES 18 DB 0 ; 先空出18字节
; 程序主体
DB 0xb8, 0x00, 0x00, 0x8e, 0xd0, 0xbc, 0x00, 0x7c
DB 0x8e, 0xd8, 0x8e, 0xc0, 0xbe, 0x74, 0x7c, 0x8a
DB 0x04, 0x83, 0xc6, 0x01, 0x3c, 0x00, 0x74, 0x09
DB 0xb4, 0x0e, 0xbb, 0x0f, 0x00, 0xcd, 0x10, 0xeb
DB 0xee, 0xf4, 0xeb, 0xfd
; 信息显示部分
DB 0x0a, 0x0a ; 换行两次
DB "hello, world"
DB 0x0a ; 换行
DB 0
TIMES 0x1fe-($-$$) DB 0x00 ; 填写0x00直到0x001fe
DB 0x55, 0xaa
; 启动扇区以外部分输出
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 4600 DB 0
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 1469432 DB 0
; 只是把 RESB 20 改成了 TIMES 20 DB 0
测试
编译命令,VSCODE->New Terminal
输入
nasm -f bin day1.asm -o day1.img
-f 参数指定输出格式为 bin,即二进制文件。 详见 nasm -h 帮助命令
拿 day1.img
和 helloos.img
十六进制对比一下(工具 Beyond Compare),发现文件内容完全一致
然后用 QEMU(32位虚拟) 测试一下 qemu-system-i386 day1.img
参考:制作可启动IMG镜像并用QEMU虚拟机测试
另外我写了个脚本命名为 run.bat
当然你也可以搭配 VSCode 的 Code Runner 插件使用
@echo OFF
nasm -f bin %1.asm -o %1.img
qemu-system-i386 %1.img
放在源码同目录下(如和 day1.asm
放在一起),然后执行命令 run day1
即可编译生成img并通过 QEMU 运行。
FAT32 版的启动镜像
初步写好了 FAT32 格式的 BPB
资料参考:Fat32 白皮书 及 https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc776720(v=ws.10)#fat32-boot-sector
对了,NASM 语法中的标签是可以不加冒号的,也就说label1: MOV AX, 0
和 label1 MOV AX, 0
是一样的
; hello-os
; TAB=4
; author: https://www.cnblogs.com/yucloud/p/10943215.html#Reference
; see1: Hardware White Paper - Microsoft Extensible Firmware Initiative FAT32 File System Specification (Version 1.03, December 6, 2000)
; see2: https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2003/cc776720(v=ws.10)
; Offset Length Name
; 0x00 3 B Jump instruction
; 0x03 8 B OEM ID
; 0x0B 53 B BPB
; 0x40 26 B Extended BPB
; 0x5A 420B Bootstrap code
; 0x01FE 2 B End of sector marker
; B(Bytes) b(bits) 1B=8bits
; 存储容量 = 磁头数 × 磁道(柱面)数 × 每道扇区数 × 每扇区字节数
; 扇区(sector) 柱面(cylinders) 磁道(Track) 磁头(head) 卷/分区(Volume) BPB()
; 簇/块(cluster)由多个扇区组成,是操作系统所使用的逻辑概念,而非磁盘的物理特性。
; DB (Byte 1B) 8bit
; DW (DWord 2B) 16bits
; DD (DDoubleWord 4B) 32bits
ORG 0x7c00
; FAT32 Boot Sector {0th sector}
; jump instruction [0x00-0x02]
BS_jmpBoot
JMP SHORT entry
NOP
; DB 0xeb, 0x5A, 0x90 ; (3B) 启动区跳转,短跳转只需要 4bit 地址,所以用 0x90(NOP,即空指令)。长跳转需要 8bit 地址
; OEM ID [0x03-0x0A]
BS_OEMName DB "HELLOIPL" ; [ 8_Bytes] OEM_ID 启动扇区名称
; BPB (BIOS Paramter Block, 53 Bytes) [0x0B-0x3F]
BPB_BytsPerSec DW 512 ; Bytes_Per_Sector (only 512, 1024, 2048, 4096)
BPB_SecPerClus DB 1 ; Sector_Per_Cluster
BPB_RsvdSecCnt DW 0x03F8 ; BPB_Reserved_Sectors_Counts
BPB_NumFATs DB 2 ; Number_of_FATs
BPB_RootEntCnt DW 0 ; Root_Entries_Counts (FAT12/FAT16 only, Fat32=0)
BPB_TotSec16 DW 0 ; old 16-bit total count of sectors Small_Sectors (FAT12/FAT16 only, Fat32=0)
BPB_Media DB 0xf8 ; Media_Descriptor (HardDisk=f8)
BPB_FatSz16 DW 0 ; Sectors_Per_FAT (FAT12/FAT16 only, Fat32=0)
BPB_SecPerTrk DW 0x3F ; Sectors_Per_Track
BPB_NumHeads DW 2 ; Number_of_Heads
BPB_HiddSec DD 0 ; Hidden_Sectors
BPB_TotSec32 DD 2880 ; 32-bit total count of sectors on the volume, Large_Sectors (卷中的扇区总数)
; offset 36(0x24)
BPB_FATSz32 DD 9 ; Sectors_Per_FAT_32 (Fat32 only)
BPB_ExtFlags DW 0 ; Extended_Flags 标志活跃的 FAT 文件分配表,Not_Used_By_WindowsServer2003
BPB_FSVer DW 0 ; File_System_Version (Fat32 only)
BPB_RootClus DD 2 ; Root_Cluster_Number (Fat32 only)
BPB_FSInfo DW 1 ; (Fat32 only, File_System_Information_Sector_Number)
BPB_BkBootSec DW 6 ; Backup_Boot_Sector (FAT32 only)
BPB_Reserved DW 0,0,0 ; [12_Bytes] (FAT32 only)
; Extended BPB (26 Bytes)[0x40-0x59]
BS_DrvNum DB 0x80 ; Physical_Disk_Number (HD=0x80)
BS_Reserved1 DB 0 ; (Fat32=0)
BS_BootSig DB 29 ; Extended_Boot_Signature (0x28 or 0x29 to be recognized by WinServer 2003)
BS_VolID DD 0xffffffff ; Volume_Serial_Number
BS_VolLab DB "MYOS-VOLUME" ; [11_Bytes] Volume_Label (default="NO NAME ")
BS_FilSysType DB "FAT32 " ; [ 8_Bytes] System_ID (分区的格式, the fileSystem ID such as "FAT32 ")
; Bootstrap Code [0x5A ~ 0x01FD]
TIMES 18 DB 0 ; 先空出18字节
; 程序主体
entry:
MOV AX, 0
MOV SS, AX
MOV SP, 0x7c00
MOV DS, AX
MOV ES, AX
MOV SI, msg
putloop:
MOV AL, [SI]
ADD SI, 1
CMP AL, 0
JE fin
MOV AH, 0x0e
MOV BX, 15
INT 0x10
JMP SHORT putloop
fin:
HLT
JMP SHORT fin
; 信息显示部分
msg:
DB 0x0a, 0x0a ; 换行两次
DB "hello, world"
DB 0x0a ; 换行
DB 0
TIMES 0x1fe-($-$$) DB 0 ; 这里要使用 0x1fe(即十进制数510)(NASK的是0x7dfe,具体不清楚)
; EndOfSector Marker [0x01FE]
DB 0x55, 0xaa
; FAT32 Data Structure (the first data cluster is 2, and not 0 or 1)
; RootDirSectors = ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec – 1)) / BPB_BytsPerSec;
; Note that on a FAT32 volume, the BPB_RootEntCnt value is always 0;
; so on a FAT32 volume, RootDirSectors is always 0.
; Next, we determine the count of sectors in the data region of the volume:
; DataSec = TotSec – (BPB_ResvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors);
; CountofClusters = DataSec / BPB_SecPerClus;
; 启动扇区以外部分输出
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 4600 DB 0
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 1469432 DB 0
; 只是把 RESB 20 改成了 TIMES 20 DB 0
把编译得到的 img 用 磁盘精灵/WinHex模板 加载一下
发现还没有文件系统... 一定是哪里出错了,下午再改
这里的难度在于 BPB 中磁盘参数的计算和分配
NASM - NASK 语法对照
NASM TIMES 0x1fe-($-$$) db 0
NASK RESB 0x1fe-$