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.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-$


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM