30天自制操作系统(NASM+GCC版)

配置环境

  • VSCODE + x86 and x86_64 Assembly插件 + hexdump for VSCode插件
  • NASM
  • QEMU
  • Windows 10 + MingW64 8.1.0(MSYS2)
C:WINDOWSsystem32>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 FilesNASM)加入系统环境变量 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.imghelloos.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, 0label1 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-$

原文地址:https://www.cnblogs.com/yucloud/p/13054367.html