跳到主要內容

尾聲-從硬碟開機

我們要將 Orange'S 安裝到硬碟上,並實現硬碟啟動。

回憶軟碟啟動的過程:
  1. BIOS 將開機磁區讀入記憶體 0000:7C00 處
  2. 跳轉到 0000:7C00 處開始執行開機程式碼
  3. 開機程式碼從軟碟中找到 loader.bin,並將其讀入記憶體
  4. 跳轉到 loader.bin 開始執行
  5. loader.bin 從軟碟中找到 kernel.bin,並將其讀入記憶體
  6. 跳轉到 kernel.bin 開始執行,到此可認為啟動過程結束
  7. 系統執行中
第 1 步中,係由 CMOS 來決定。第 3 步和第 5 步中,
  • 軟碟啟動:程式碼將在軟碟中尋找 loader.bin 和 kernel.bin
  • 硬碟啟動:需要讓開機磁區程式碼從硬碟中尋找 loader.bin 並讓 loader 從硬碟中尋找 kernel.bin
故我們必須重寫 boot.asm 和 loader.asm,讓它們讀取硬碟而不是軟碟。新的檔我們取名為 hdboot.asm 和 hdldr.asm。


程式碼

include/sys/proto.h

PUBLIC int  do_lseek();   // 新增

boot/hdboot.asm

; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                               hdboot.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                                                     Forrest Yu, 2008
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

org  0x7c00   ; bios always loads boot sector to 0000:7C00

 jmp boot_start

%include  "load.inc"

STACK_BASE  equ 0x7C00 ; base address of stack when booting
TRANS_SECT_NR  equ 2
SECT_BUF_SIZE  equ TRANS_SECT_NR * 512

disk_address_packet: db 0x10  ; [ 0] Packet size in bytes.
   db 0  ; [ 1] Reserved, must be 0.
   db TRANS_SECT_NR ; [ 2] Nr of blocks to transfer.
   db 0  ; [ 3] Reserved, must be 0.
   dw 0  ; [ 4] Addr of transfer - Offset
   dw SUPER_BLK_SEG ; [ 6] buffer.          - Seg
   dd 0  ; [ 8] LBA. Low  32-bits.
   dd 0  ; [12] LBA. High 32-bits.


err:
 mov dh, 3   ; "Error 0  "
 call disp_str  ; display the string
 jmp $

boot_start:
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ss, ax
 mov sp, STACK_BASE

 call clear_screen

 mov dh, 0   ; "Booting  "
 call disp_str  ; display the string

 ;; read the super block to SUPER_BLK_SEG::0
 mov dword [disk_address_packet +  8], ROOT_BASE + 1
 call read_sector
 mov ax, SUPER_BLK_SEG
 mov fs, ax

 mov dword [disk_address_packet +  4], LOADER_OFF
 mov dword [disk_address_packet +  6], LOADER_SEG

 ;; get the sector nr of `/' (ROOT_INODE), it'll be stored in eax
 mov eax, [fs:SB_ROOT_INODE]
 call get_inode

 ;; read `/' into ex:bx
 mov dword [disk_address_packet +  8], eax
 call read_sector

 ;; let's search `/' for the loader
 mov si, LoaderFileName
 push bx  ; <- save
.str_cmp:
 ;; before comparation:
 ;;     es:bx -> dir_entry @ disk
 ;;     ds:si -> filename we want
 add bx, [fs:SB_DIR_ENT_FNAME_OFF]
.1:
 lodsb    ; ds:si -> al
 cmp al, byte [es:bx]
 jz .2
 jmp .different  ; oops
.2:     ; so far so good
 cmp al, 0   ; both arrive at a '\0', match
 jz .found
 inc bx   ; next char @ disk
 jmp .1   ; on and on
.different:
 pop bx  ; -> restore
 add bx, [fs:SB_DIR_ENT_SIZE]
 sub ecx, [fs:SB_DIR_ENT_SIZE]
 jz .not_found

 mov dx, SECT_BUF_SIZE
 cmp bx, dx
 jge .not_found

 push bx
 mov si, LoaderFileName
 jmp .str_cmp
.not_found:
 mov dh, 2
 call disp_str
 jmp $
.found:
 pop bx
 add bx, [fs:SB_DIR_ENT_INODE_OFF]
 mov eax, [es:bx]  ; eax <- inode nr of loader
 call get_inode  ; eax <- start sector nr of loader
 mov dword [disk_address_packet +  8], eax
load_loader:
 call read_sector
 cmp ecx, SECT_BUF_SIZE
 jl .done
 sub ecx, SECT_BUF_SIZE ; bytes_left -= SECT_BUF_SIZE
 add word  [disk_address_packet + 4], SECT_BUF_SIZE ; transfer buffer
 jc err
 add dword [disk_address_packet + 8], TRANS_SECT_NR ; LBA
 jmp load_loader
.done:
 mov dh, 1
 call disp_str
 jmp LOADER_SEG:LOADER_OFF
 jmp $


;============================================================================
;字元串
;----------------------------------------------------------------------------
LoaderFileName  db "hdldr.bin", 0 ; LOADER 之檔案名
; 為簡化程式碼, 下面每個字元串的長度均為 MessageLength
MessageLength  equ 9
BootMessage:  db "BootingHD"; 9字元, 不夠則用空格補齊. 序號 0
Message1  db "HD Boot  "; 9字元, 不夠則用空格補齊. 序號 1
Message2  db "No LOADER"; 9字元, 不夠則用空格補齊. 序號 2
Message3  db "Error 0  "; 9字元, 不夠則用空格補齊. 序號 3
;============================================================================

clear_screen:
 mov ax, 0x600  ; AH = 6,  AL = 0
 mov bx, 0x700  ; 黑底白字(BL = 0x7)
 mov cx, 0   ; 左上角: (0, 0)
 mov dx, 0x184f  ; 右下角: (80, 50)
 int 0x10   ; int 0x10
 ret

;----------------------------------------------------------------------------
; 函數名: disp_str
;----------------------------------------------------------------------------
; 作用:
; 顯示一個字元串, 函數開始時 dh 中應該是字元串序號(0-based)
disp_str:
 mov ax, MessageLength
 mul dh
 add ax, BootMessage
 mov bp, ax   ; `.
 mov ax, ds   ;  | ES:BP = 串位址
 mov es, ax   ; /
 mov cx, MessageLength ; CX = 串長度
 mov ax, 0x1301  ; AH = 0x13,  AL = 0x1
 mov bx, 0x7   ; 頁號為0(BH = 0) 黑底白字(BL = 0x7)
 mov dl, 0
 int 0x10   ; int 0x10
 ret

;----------------------------------------------------------------------------
; read_sector
;----------------------------------------------------------------------------
; Entry:
;     - fields disk_address_packet should have been filled
;       before invoking the routine
; Exit:
;     - es:bx -> data read
; registers changed:
;     - eax, ebx, dl, si, es
read_sector:
 xor ebx, ebx

 mov ah, 0x42
 mov dl, 0x80
 mov si, disk_address_packet
 int 0x13

 mov ax, [disk_address_packet + 6]
 mov es, ax
 mov bx, [disk_address_packet + 4]

 ret

;----------------------------------------------------------------------------
; get_inode
;----------------------------------------------------------------------------
; Entry:
;     - eax    : inode nr.
; Exit:
;     - eax    : sector nr.
;     - ecx    : the_inode.i_size
;     - es:ebx : inodes sector buffer
; registers changed:
;     - eax, ebx, ecx, edx
get_inode:
 dec eax   ; eax <-  inode_nr -1
 mov bl, [fs:SB_INODE_SIZE]
 mul bl   ; eax <- (inode_nr - 1) * INODE_SIZE
 mov edx, SECT_BUF_SIZE
 sub edx, dword [fs:SB_INODE_SIZE]
 cmp eax, edx
 jg err
 push eax

 mov ebx, [fs:SB_NR_IMAP_SECTS]
 mov edx, [fs:SB_NR_SMAP_SECTS]
 lea eax, [ebx+edx+ROOT_BASE+2]
 mov dword [disk_address_packet +  8], eax
 call read_sector

 pop eax   ; [es:ebx+eax] -> the inode

 mov edx, dword [fs:SB_INODE_ISIZE_OFF]
 add edx, ebx
 add edx, eax  ; [es:edx] -> the_inode.i_size
 mov ecx, [es:edx]  ; ecx <- the_inode.i_size

 ; es:[ebx+eax] -> the_inode.i_start_sect
 add ax, word [fs:SB_INODE_START_OFF]

 add bx, ax
 mov eax, [es:bx]
 add eax, ROOT_BASE  ; eax <- the_inode.i_start_sect
 ret


times  510-($-$$) db 0 ; 填充剩下的空間,使生成的二進制程式碼恰好為512字元
dw  0xaa55  ; 結束標誌

boot/hdldr.asm

; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                               loader.asm
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;                                                     Forrest Yu, 2005
; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


org  0100h

 jmp LABEL_START  ; Start

%include "load.inc"
%include "pm.inc"


TRANS_SECT_NR  equ 2
SECT_BUF_SIZE  equ TRANS_SECT_NR * 512

disk_address_packet: db 0x10  ; [ 0] Packet size in bytes. Must be 0x10 or greater.
   db 0  ; [ 1] Reserved, must be 0.
sect_cnt:  db TRANS_SECT_NR ; [ 2] Number of blocks to transfer.
   db 0  ; [ 3] Reserved, must be 0.
   dw KERNEL_FILE_OFF ; [ 4] Address of transfer buffer. Offset
   dw KERNEL_FILE_SEG ; [ 6]                             Seg
lba_addr:  dd 0  ; [ 8] Starting LBA address. Low  32-bits.
   dd 0  ; [12] Starting LBA address. High 32-bits.


; GDT ------------------------------------------------------------------------------------------------------------------------------------------------------------
;                                                段基址            段界限     , 属性
LABEL_GDT:   Descriptor             0,                    0, 0      ; 空描述符
LABEL_DESC_FLAT_C:  Descriptor             0,              0fffffh, DA_CR  | DA_32 | DA_LIMIT_4K   ; 0 ~ 4G
LABEL_DESC_FLAT_RW:  Descriptor             0,              0fffffh, DA_DRW | DA_32 | DA_LIMIT_4K   ; 0 ~ 4G
LABEL_DESC_VIDEO:  Descriptor  0B8000h,               0ffffh, DA_DRW                         | DA_DPL3 ; 显存首位址
; GDT ------------------------------------------------------------------------------------------------------------------------------------------------------------

GdtLen  equ $ - LABEL_GDT
GdtPtr  dw GdtLen - 1    ; 段界限
  dd LOADER_PHY_ADDR + LABEL_GDT  ; 基位址 (让基位址八字节对齐将起到优化速度之效果,目前懒得改)
; The GDT is not a segment itself; instead, it is a data structure in linear address space.
; The base linear address and limit of the GDT must be loaded into the GDTR register. -- IA-32 Software Developer’s Manual, Vol.3A


; GDT 选择子 ----------------------------------------------------------------------------------
SelectorFlatC  equ LABEL_DESC_FLAT_C - LABEL_GDT
SelectorFlatRW  equ LABEL_DESC_FLAT_RW - LABEL_GDT
SelectorVideo  equ LABEL_DESC_VIDEO - LABEL_GDT + SA_RPL3
; GDT 选择子 ----------------------------------------------------------------------------------


BaseOfStack equ 0100h


err:
 mov dh, 5   ; "Error 0  "
 call real_mode_disp_str ; display the string
 jmp $

LABEL_START:   ; <--- 从这里开始 *************
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ss, ax
 mov sp, BaseOfStack

 mov dh, 0   ; "Loading  "
 call real_mode_disp_str ; 显示字元串

 ; 得到内存数
 mov ebx, 0   ; ebx = 后续值, 开始时需为 0
 mov di, _MemChkBuf  ; es:di 指向一个位址范围描述符结构(Address Range Descriptor Structure)
.MemChkLoop:
 mov eax, 0E820h  ; eax = 0000E820h
 mov ecx, 20   ; ecx = 位址范围描述符结构的大小
 mov edx, 0534D4150h  ; edx = 'SMAP'
 int 15h   ; int 15h
 jc .MemChkFail
 add di, 20
 inc dword [_dwMCRNumber] ; dwMCRNumber = ARDS 的个数
 cmp ebx, 0
 jne .MemChkLoop
 jmp .MemChkOK
.MemChkFail:
 mov dword [_dwMCRNumber], 0
.MemChkOK:

 ;; get the sector nr of `/' (ROOT_INODE), it'll be stored in eax
 mov eax, [fs:SB_ROOT_INODE] ; fs -> super_block (see hdboot.asm)
 call get_inode

 ;; read `/' into es:bx
 mov dword [disk_address_packet +  8], eax
 call read_sector

 ;; let's search `/' for the kernel
 mov si, KernelFileName
 push bx  ; <- save
.str_cmp:
 ;; before comparation:
 ;;     es:bx -> dir_entry @ disk
 ;;     ds:si -> filename we want
 add bx, [fs:SB_DIR_ENT_FNAME_OFF]
.1:
 lodsb    ; ds:si -> al
 cmp al, byte [es:bx]
 jz .2
 jmp .different  ; oops
.2:     ; so far so good
 cmp al, 0   ; both arrive at a '\0', match
 jz .found
 inc bx   ; next char @ disk
 jmp .1   ; on and on
.different:
 pop bx  ; -> restore
 add bx, [fs:SB_DIR_ENT_SIZE]
 sub ecx, [fs:SB_DIR_ENT_SIZE]
 jz .not_found
 push bx
 mov si, KernelFileName
 jmp .str_cmp
.not_found:
 mov dh, 3
 call real_mode_disp_str
 jmp $
.found:
 pop bx
 add bx, [fs:SB_DIR_ENT_INODE_OFF]
 mov eax, [es:bx]  ; eax <- inode nr of kernel
 call get_inode  ; eax <- start sector nr of kernel
 mov dword [disk_address_packet +  8], eax
load_kernel:
 call read_sector
 cmp ecx, SECT_BUF_SIZE
 jl .done
 sub ecx, SECT_BUF_SIZE ; bytes_left -= SECT_BUF_SIZE
 add word  [disk_address_packet + 4], SECT_BUF_SIZE ; transfer buffer
 jc .1
 jmp .2
.1:
 add word  [disk_address_packet + 6], 1000h
.2:
 add dword [disk_address_packet + 8], TRANS_SECT_NR ; LBA
 jmp load_kernel
.done:
 mov dh, 2
 call real_mode_disp_str


; 下面准备跳入保护模式 -------------------------------------------

; 加载 GDTR
 lgdt [GdtPtr]

; 关中断
 cli

; 打开位址线A20
 in al, 92h
 or al, 00000010b
 out 92h, al

; 准备切换到保护模式
 mov eax, cr0
 or eax, 1
 mov cr0, eax

; 真正进入保护模式
 jmp dword SelectorFlatC:(LOADER_PHY_ADDR+LABEL_PM_START)


 jmp $  ; never arrive here

;============================================================================
;变量
;----------------------------------------------------------------------------
wSectorNo  dw 0  ; 要读取的扇区号
bOdd   db 0  ; 奇数还是偶数
dwKernelSize  dd 0  ; KERNEL.BIN 檔案大小

;============================================================================
;字元串
;----------------------------------------------------------------------------
KernelFileName  db "kernel.bin", 0 ; KERNEL.BIN 之檔案名
; 为简化代码, 下面每个字元串的长度均为 MessageLength
MessageLength  equ 9
LoadMessage:  db "Loading  "
Message1  db "         "
Message2  db "in HD LDR"
Message3  db "No KERNEL"
Message4  db "Too Large"
Message5  db "Error 0  "
;============================================================================

;============================================================================

clear_screen:
 mov ax, 0x600  ; AH = 6,  AL = 0
 mov bx, 0x700  ; 黑底白字(BL = 0x7)
 mov cx, 0   ; 左上角: (0, 0)
 mov dx, 0x184f  ; 右下角: (80, 50)
 int 0x10   ; int 0x10
 ret

;----------------------------------------------------------------------------
; 函数名: disp_str
;----------------------------------------------------------------------------
; 作用:
; 显示一个字元串, 函数开始时 dh 中应该是字元串序号(0-based)
real_mode_disp_str:
 mov ax, MessageLength
 mul dh
 add ax, LoadMessage
 mov bp, ax   ; ┓
 mov ax, ds   ; ┣ ES:BP = 串位址
 mov es, ax   ; ┛
 mov cx, MessageLength ; CX = 串长度
 mov ax, 0x1301  ; AH = 0x13,  AL = 0x1
 mov bx, 0x7   ; 页号为0(BH = 0) 黑底白字(BL = 0x7)
 mov dl, 0
 int 0x10   ; int 0x10
 ret

;----------------------------------------------------------------------------
; read_sector
;----------------------------------------------------------------------------
; before:
;     - fields disk_address_packet should have been filled
;       before invoking the routine
; after:
;     - es:bx -> data read
; registers changed:
;     - eax, ebx, dl, si, es
read_sector:
 xor ebx, ebx

 ;mov dword [disk_address_packet +  8], eax
 mov dword [disk_address_packet + 12], 0

 mov ah, 0x42
 mov dl, 0x80
 mov si, disk_address_packet
 int 0x13

 mov ax, [disk_address_packet + 6]
 mov es, ax
 mov bx, [disk_address_packet + 4]

 ret

;----------------------------------------------------------------------------
; get_inode
;----------------------------------------------------------------------------
; before:
;     - eax    : inode nr.
; after:
;     - eax    : sector nr.
;     - ecx    : the_inode.i_size
;     - es:ebx : inodes sector buffer
; registers changed:
;     - eax, ebx, ecx, edx
get_inode:
 dec eax    ; eax <-  inode_nr -1
 mov bl, [fs:SB_INODE_SIZE]
 mul bl    ; eax <- (inode_nr - 1) * INODE_SIZE
 mov edx, SECT_BUF_SIZE
 sub edx, dword [fs:SB_INODE_SIZE]
 cmp eax, edx
 jg err
 push eax

 mov ebx, [fs:SB_NR_IMAP_SECTS]
 mov edx, [fs:SB_NR_SMAP_SECTS]
 lea eax, [ebx+edx+ROOT_BASE+2]
 mov dword [disk_address_packet +  8], eax
 call read_sector

 pop eax    ; [es:ebx+eax] -> the inode

 mov edx, dword [fs:SB_INODE_ISIZE_OFF]
 add edx, ebx
 add edx, eax   ; [es:edx] -> the_inode.i_size
 mov ecx, [es:edx]   ; ecx <- the_inode.i_size

 add ax, word [fs:SB_INODE_START_OFF]; es:[ebx+eax] -> the_inode.i_start_sect

 add bx, ax
 mov eax, [es:bx]
 add eax, ROOT_BASE   ; eax <- the_inode.i_start_sect
 ret





; 从此以后的代码在保护模式下执行 ----------------------------------------------------
; 32 位代码段. 由实模式跳入 ---------------------------------------------------------
[SECTION .s32]

ALIGN 32

[BITS 32]

LABEL_PM_START:
 mov ax, SelectorVideo
 mov gs, ax
 mov ax, SelectorFlatRW
 mov ds, ax
 mov es, ax
 mov fs, ax
 mov ss, ax
 mov esp, TopOfStack

 call DispMemInfo
;;;  call DispReturn
;;;  call DispHDInfo ; int 13h 读出的硬盘 geometry 好像有点不对头,不知道为什么,干脆不管它了
 call SetupPaging

 ;mov ah, 0Fh    ; 0000: 黑底    1111: 白字
 ;mov al, 'P'
 ;mov [gs:((80 * 0 + 39) * 2)], ax ; 螢幕第 0 行, 第 39 列。

 call InitKernel

 ;jmp $
 mov dword [BOOT_PARAM_ADDR], BOOT_PARAM_MAGIC ; BootParam[0] = BootParamMagic;
 mov eax, [dwMemSize]    ;
 mov [BOOT_PARAM_ADDR + 4], eax   ; BootParam[1] = MemSize;
 mov eax, KERNEL_FILE_SEG
 shl eax, 4
 add eax, KERNEL_FILE_OFF
 mov [BOOT_PARAM_ADDR + 8], eax   ; BootParam[2] = KernelFilePhyAddr;

 ;***************************************************************
 jmp SelectorFlatC:KRNL_ENT_PT_PHY_ADDR ; 正式进入内核 *
 ;***************************************************************
 ; 記憶體看上去是這樣的:
 ;              ┃                                    ┃
 ;              ┃                 .                  ┃
 ;              ┃                 .                  ┃
 ;              ┃                 .                  ┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;              ┃■■■■■■Page  Tables■■■■■■┃
 ;              ┃■■■■■(大小由LOADER決定)■■■■┃
 ;    00101000h ┃■■■■■■■■■■■■■■■■■■┃ PAGE_TBL_BASE
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;    00100000h ┃■■■■Page Directory Table■■■■┃ PAGE_DIR_BASE  <- 1M
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃□□□□□□□□□□□□□□□□□□┃
 ;       F0000h ┃□□□□□□□System ROM□□□□□□┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃□□□□□□□□□□□□□□□□□□┃
 ;       E0000h ┃□□□□Expansion of system ROM □□┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃□□□□□□□□□□□□□□□□□□┃
 ;       C0000h ┃□□□Reserved for ROM expansion□□┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃□□□□□□□□□□□□□□□□□□┃ B8000h ← gs
 ;       A0000h ┃□□□Display adapter reserved□□□┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃□□□□□□□□□□□□□□□□□□┃
 ;       9FC00h ┃□□extended BIOS data area (EBDA)□┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;       90000h ┃■■■■■■■LOADER.BIN■■■■■■┃ somewhere in LOADER ← esp
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;       80000h ┃■■■■■■■KERNEL.BIN■■■■■■┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;       30000h ┃■■■■■■■■KERNEL■■■■■■■┃ 30400h ← KERNEL 入口 (KRNL_ENT_PT_PHY_ADDR)
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃                                    ┃
 ;        7E00h ┃              F  R  E  E            ┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃■■■■■■■■■■■■■■■■■■┃
 ;        7C00h ┃■■■■■■BOOT  SECTOR■■■■■■┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃                                    ┃
 ;         500h ┃              F  R  E  E            ┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃□□□□□□□□□□□□□□□□□□┃
 ;         400h ┃□□□□ROM BIOS parameter area □□┃
 ;              ┣━━━━━━━━━━━━━━━━━━┫
 ;              ┃◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇◇┃
 ;           0h ┃◇◇◇◇◇◇Int  Vectors◇◇◇◇◇◇┃
 ;              ┗━━━━━━━━━━━━━━━━━━┛ ← cs, ds, es, fs, ss
 ;
 ;
 ;  ┏━━━┓  ┏━━━┓
 ;  ┃■■■┃ 我們使用  ┃□□□┃ 不能使用的記憶體
 ;  ┗━━━┛  ┗━━━┛
 ;  ┏━━━┓  ┏━━━┓
 ;  ┃      ┃ 未使用空間 ┃◇◇◇┃ 可以覆蓋的記憶體
 ;  ┗━━━┛  ┗━━━┛
 ;
 ; 注:KERNEL 的位置實際上是很靈活的,可以通過同時改變 LOAD.INC 中的 KRNL_ENT_PT_PHY_ADDR 和 MAKEFILE 中參數 -Ttext 的值來改變。
 ;     比如,如果把 KRNL_ENT_PT_PHY_ADDR 和 -Ttext 的值都改為 0x400400,則 KERNEL 就會被加載到記憶體 0x400000(4M) 處,入口在 0x400400。
 ;




; ------------------------------------------------------------------------
; 顯示 AL 中的數字
; ------------------------------------------------------------------------
DispAL:
 push ecx
 push edx
 push edi

 mov edi, [dwDispPos]

 mov ah, 0Fh   ; 0000b: 黑底    1111b: 白字
 mov dl, al
 shr al, 4
 mov ecx, 2
.begin:
 and al, 01111b
 cmp al, 9
 ja .1
 add al, '0'
 jmp .2
.1:
 sub al, 0Ah
 add al, 'A'
.2:
 mov [gs:edi], ax
 add edi, 2

 mov al, dl
 loop .begin
 ;add edi, 2

 mov [dwDispPos], edi

 pop edi
 pop edx
 pop ecx

 ret
; DispAL 結束-------------------------------------------------------------


; ------------------------------------------------------------------------
; 顯示一個整形數
; ------------------------------------------------------------------------
DispInt:
 mov eax, [esp + 4]
 shr eax, 24
 call DispAL

 mov eax, [esp + 4]
 shr eax, 16
 call DispAL

 mov eax, [esp + 4]
 shr eax, 8
 call DispAL

 mov eax, [esp + 4]
 call DispAL

 mov ah, 07h   ; 0000b: 黑底    0111b: 灰字
 mov al, 'h'
 push edi
 mov edi, [dwDispPos]
 mov [gs:edi], ax
 add edi, 4
 mov [dwDispPos], edi
 pop edi

 ret
; DispInt 結束------------------------------------------------------------

; ------------------------------------------------------------------------
; 顯示一個字元串
; ------------------------------------------------------------------------
DispStr:
 push ebp
 mov ebp, esp
 push ebx
 push esi
 push edi

 mov esi, [ebp + 8] ; pszInfo
 mov edi, [dwDispPos]
 mov ah, 0Fh
.1:
 lodsb
 test al, al
 jz .2
 cmp al, 0Ah ; 是ENTER嗎?
 jnz .3
 push eax
 mov eax, edi
 mov bl, 160
 div bl
 and eax, 0FFh
 inc eax
 mov bl, 160
 mul bl
 mov edi, eax
 pop eax
 jmp .1
.3:
 mov [gs:edi], ax
 add edi, 2
 jmp .1

.2:
 mov [dwDispPos], edi

 pop edi
 pop esi
 pop ebx
 pop ebp
 ret
; DispStr 結束------------------------------------------------------------

; ------------------------------------------------------------------------
; 換行
; ------------------------------------------------------------------------
DispReturn:
 push szReturn
 call DispStr   ;printf("\n");
 add esp, 4

 ret
; DispReturn 結束---------------------------------------------------------


; ------------------------------------------------------------------------
; 記憶體拷貝,仿 memcpy
; ------------------------------------------------------------------------
; void* MemCpy(void* es:pDest, void* ds:pSrc, int iSize);
; ------------------------------------------------------------------------
MemCpy:
 push ebp
 mov ebp, esp

 push esi
 push edi
 push ecx

 mov edi, [ebp + 8] ; Destination
 mov esi, [ebp + 12] ; Source
 mov ecx, [ebp + 16] ; Counter
.1:
 cmp ecx, 0  ; 判斷計數器
 jz .2  ; 計數器為零時跳出

 mov al, [ds:esi]  ; ┓
 inc esi   ; ┃
     ; ┣ 逐字元移動
 mov byte [es:edi], al ; ┃
 inc edi   ; ┛

 dec ecx  ; 計數器減一
 jmp .1  ; 循環
.2:
 mov eax, [ebp + 8] ; 返回值

 pop ecx
 pop edi
 pop esi
 mov esp, ebp
 pop ebp

 ret   ; 函數結束,返回
; MemCpy 結束-------------------------------------------------------------




; 顯示記憶體訊息 --------------------------------------------------------------
DispMemInfo:
 push esi
 push edi
 push ecx

 push szMemChkTitle
 call DispStr
 add esp, 4

 mov esi, MemChkBuf
 mov ecx, [dwMCRNumber] ;for(int i=0;i<[MCRNumber];i++) // 每次得到一個ARDS(Address Range Descriptor Structure)結構
.loop:     ;{
 mov edx, 5   ; for(int j=0;j<5;j++) // 每次得到一個ARDS中的成員,共5個成員
 mov edi, ARDStruct  ; {   // 依次顯示:BaseAddrLow,BaseAddrHigh,LengthLow,LengthHigh,Type
.1:     ;
 push dword [esi]  ;
 call DispInt   ;  DispInt(MemChkBuf[j*4]); // 顯示一個成員
 pop eax   ;
 stosd    ;  ARDStruct[j*4] = MemChkBuf[j*4];
 add esi, 4   ;
 dec edx   ;
 cmp edx, 0   ;
 jnz .1   ; }
 call DispReturn  ; printf("\n");
 cmp dword [dwType], 1 ; if(Type == AddressRangeMemory) // AddressRangeMemory : 1, AddressRangeReserved : 2
 jne .2   ; {
 mov eax, [dwBaseAddrLow] ;
 add eax, [dwLengthLow] ;
 cmp eax, [dwMemSize] ;  if(BaseAddrLow + LengthLow > MemSize)
 jb .2   ;
 mov [dwMemSize], eax ;   MemSize = BaseAddrLow + LengthLow;
.2:     ; }
 loop .loop   ;}
     ;
 call DispReturn  ;printf("\n");
 push szRAMSize  ;
 call DispStr   ;printf("RAM size:");
 add esp, 4   ;
     ;
 push dword [dwMemSize] ;
 call DispInt   ;DispInt(MemSize);
 add esp, 4   ;

 pop ecx
 pop edi
 pop esi
 ret
; ---------------------------------------------------------------------------


;;; ; 顯示記憶體訊息 --------------------------------------------------------------
;;; DispHDInfo:
;;;  push eax

;;;  cmp dword [dwNrHead], 0FFFFh
;;;  je .nohd

;;;  push szCylinder
;;;  call DispStr   ; printf("C:");
;;;  add esp, 4

;;;  push dword [dwNrCylinder]  ; NR Cylinder
;;;  call DispInt
;;;  pop eax

;;;  push szHead
;;;  call DispStr   ; printf(" H:");
;;;  add esp, 4

;;;  push dword [dwNrHead]  ; NR Head
;;;  call DispInt
;;;  pop eax

;;;  push szSector
;;;  call DispStr   ; printf(" S:");
;;;  add esp, 4

;;;  push dword [dwNrSector]  ; NR Sector
;;;  call DispInt
;;;  pop eax

;;;  jmp .hdinfo_finish

;;; .nohd:
;;;  push szNOHD
;;;  call DispStr   ; printf("No hard drive. System halt.");
;;;  add esp, 4
;;;  jmp $   ; 沒有硬碟,死在這裡

;;; .hdinfo_finish:
;;;  call DispReturn

;;;  pop eax
;;;  ret
;;; ; ---------------------------------------------------------------------------


; 啟動分頁機制 --------------------------------------------------------------
SetupPaging:
 ; 根據記憶體大小計算應初始化多少PDE以及多少頁表
 xor edx, edx
 mov eax, [dwMemSize]
 mov ebx, 400000h ; 400000h = 4M = 4096 * 1024, 一個頁表對應的記憶體大小
 div ebx
 mov ecx, eax ; 此時 ecx 為頁表的個數,也即 PDE 應該的個數
 test edx, edx
 jz .no_remainder
 inc ecx  ; 如果餘數不為 0 就需增加一個頁表
.no_remainder:
 push ecx  ; 暫存頁表個數

 ; 為簡化處理, 所有線性位址對應相等的物理位址. 並且不考慮記憶體空洞.

 ; 首先初始化頁目錄
 mov ax, SelectorFlatRW
 mov es, ax
 mov edi, PAGE_DIR_BASE ; 此段首位址為 PAGE_DIR_BASE
 xor eax, eax
 mov eax, PAGE_TBL_BASE | PG_P  | PG_USU | PG_RWW
.1:
 stosd
 add eax, 4096  ; 為了簡化, 所有頁表在記憶體中是連續的.
 loop .1

 ; 再初始化所有頁表
 pop eax   ; 頁表個數
 mov ebx, 1024  ; 每個頁表 1024 個 PTE
 mul ebx
 mov ecx, eax  ; PTE個數 = 頁表個數 * 1024
 mov edi, PAGE_TBL_BASE ; 此段首位址為 PAGE_TBL_BASE
 xor eax, eax
 mov eax, PG_P  | PG_USU | PG_RWW
.2:
 stosd
 add eax, 4096  ; 每一頁指向 4K 的空間
 loop .2

 mov eax, PAGE_DIR_BASE
 mov cr3, eax
 mov eax, cr0
 or eax, 80000000h
 mov cr0, eax
 jmp short .3
.3:
 nop

 ret
; 分頁機制啟動完畢 ----------------------------------------------------------



; InitKernel ---------------------------------------------------------------------------------
; 將 KERNEL.BIN 的內容經過整理對齊後放到新的位置
; --------------------------------------------------------------------------------------------
InitKernel: ; 遍歷每一個 Program Header,根據 Program Header 中的訊息來確定把什麼放進記憶體,放到什麼位置,以及放多少。
 xor esi, esi
 mov cx, word [KERNEL_FILE_PHY_ADDR + 2Ch] ; ┓ ecx <- pELFHdr->e_phnum
 movzx ecx, cx     ; ┛
 mov esi, [KERNEL_FILE_PHY_ADDR + 1Ch] ; esi <- pELFHdr->e_phoff
 add esi, KERNEL_FILE_PHY_ADDR  ; esi <- OffsetOfKernel + pELFHdr->e_phoff
.Begin:
 mov eax, [esi + 0]
 cmp eax, 0    ; PT_NULL
 jz .NoAction
 push dword [esi + 010h]  ; size ┓
 mov eax, [esi + 04h]  ; ┃
 add eax, KERNEL_FILE_PHY_ADDR ; ┣ ::memcpy( (void*)(pPHdr->p_vaddr),
 push eax    ; src ┃  uchCode + pPHdr->p_offset,
 push dword [esi + 08h]  ; dst ┃  pPHdr->p_filesz;
 call MemCpy    ; ┃
 add esp, 12    ; ┛
.NoAction:
 add esi, 020h   ; esi += pELFHdr->e_phentsize
 dec ecx
 jnz .Begin

 ret
; InitKernel ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^


; SECTION .data1 之開始 ---------------------------------------------------------------------------------------------
[SECTION .data1]

ALIGN 32

LABEL_DATA:
; 實模式下使用這些符號
; 字元串
_szMemChkTitle:   db "BaseAddrL BaseAddrH LengthLow LengthHigh   Type", 0Ah, 0
_szRAMSize:   db "RAM size: ", 0
;;; _szCylinder   db "HD Info : C=", 0
;;; _szHead    db " H=", 0
;;; _szSector   db " S=", 0
;;; _szNOHD    db "No hard drive. System halt.", 0
_szReturn:   db 0Ah, 0
;; 變量
;;; _dwNrCylinder   dd 0
;;; _dwNrHead   dd 0
;;; _dwNrSector   dd 0
_dwMCRNumber:   dd 0 ; Memory Check Result
_dwDispPos:   dd (80 * 7 + 0) * 2 ; 螢幕第 7 行, 第 0 列。
_dwMemSize:   dd 0
_ARDStruct:   ; Address Range Descriptor Structure
 _dwBaseAddrLow:  dd 0
 _dwBaseAddrHigh: dd 0
 _dwLengthLow:  dd 0
 _dwLengthHigh:  dd 0
 _dwType:  dd 0
_MemChkBuf: times 256 db 0
;
;; 保護模式下使用這些符號
szMemChkTitle  equ LOADER_PHY_ADDR + _szMemChkTitle
szRAMSize  equ LOADER_PHY_ADDR + _szRAMSize
;;; szCylinder  equ LOADER_PHY_ADDR + _szCylinder
;;; szHead   equ LOADER_PHY_ADDR + _szHead
;;; szSector  equ LOADER_PHY_ADDR + _szSector
;;; szNOHD   equ LOADER_PHY_ADDR + _szNOHD
szReturn  equ LOADER_PHY_ADDR + _szReturn
;;; dwNrCylinder  equ LOADER_PHY_ADDR + _dwNrCylinder
;;; dwNrHead  equ LOADER_PHY_ADDR + _dwNrHead
;;; dwNrSector  equ LOADER_PHY_ADDR + _dwNrSector
dwDispPos  equ LOADER_PHY_ADDR + _dwDispPos
dwMemSize  equ LOADER_PHY_ADDR + _dwMemSize
dwMCRNumber  equ LOADER_PHY_ADDR + _dwMCRNumber
ARDStruct  equ LOADER_PHY_ADDR + _ARDStruct
 dwBaseAddrLow equ LOADER_PHY_ADDR + _dwBaseAddrLow
 dwBaseAddrHigh equ LOADER_PHY_ADDR + _dwBaseAddrHigh
 dwLengthLow equ LOADER_PHY_ADDR + _dwLengthLow
 dwLengthHigh equ LOADER_PHY_ADDR + _dwLengthHigh
 dwType  equ LOADER_PHY_ADDR + _dwType
MemChkBuf  equ LOADER_PHY_ADDR + _MemChkBuf


; 堆疊就在資料段的末尾
StackSpace: times 1000h db 0
TopOfStack equ LOADER_PHY_ADDR + $ ; 堆疊頂
; SECTION .data1 之結束 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

command/Makefile

# commands/Makefile

#ENTRYPOINT = 0x1000
HD  = ../80m.img

ASM  = nasm
DASM  = objdump
CC  = gcc
LD  = ld
ASMFLAGS = -I ../include/ -f elf
CFLAGS  = -I ../include/ -m32 -c -fno-builtin -fno-stack-protector -Wall
LDFLAGS  = -Ttext 0x1000 -s -melf_i386
DASMFLAGS = -D
LIB  = ../lib/orangescrt.a
BIN  = echo pwd

# All Phony Targets
.PHONY : everything final clean realclean disasm all install

# Default starting position
everything : $(BIN)

install : all clean
 cp ../boot/hdldr.bin ./ -v
 cp ../kernel.bin ./ -v
 tar vcf inst.tar kernel.bin $(BIN) hdldr.bin
 dd if=inst.tar of=$(HD) seek=`echo "obase=10;ibase=16;(\`egrep -e '^ROOT_BASE' ../boot/include/load.inc | sed -e 's/.*0x//g'\`+\`egrep -e '#define[[:space:]]*INSTALL_START_SECT' ../include/sys/config.h | sed -e 's/.*0x//g'\`)*200" | bc` bs=1 count=`ls -l inst.tar | awk -F " " '{print $$5}'` conv=notrunc

all : realclean everything

final : all clean

clean :
 rm -f *.o

realclean :
 rm -f $(BIN) *.o

kernel.bin :
 cp ../kernel.bin ./

start.o : start.asm
 $(ASM) $(ASMFLAGS) -o $@ $<

echo.o: echo.c ../include/type.h ../include/stdio.h
 $(CC) $(CFLAGS) -o $@ $<

echo : echo.o start.o $(LIB)
 $(LD) $(LDFLAGS) -o $@ $?

pwd.o: pwd.c ../include/type.h ../include/stdio.h
 $(CC) $(CFLAGS) -o $@ $<

pwd : pwd.o start.o $(LIB)
 $(LD) $(LDFLAGS) -o $@ $?

Makefile

#########################
# Makefile for Orange'S #
#########################

# Entry point of Orange'S
# It must have the same value with 'KernelEntryPointPhyAddr' in load.inc!
ENTRYPOINT = 0x1000

FD  = a.img
HD  = 80m.img

# Programs, flags, etc.
ASM  = nasm
DASM  = objdump
CC  = gcc
LD  = ld
ASMBFLAGS = -I boot/include/
ASMKFLAGS = -I include/ -I include/sys/ -f elf
CFLAGS  = -I include/ -I include/sys/ -m32 -c -fno-builtin -fno-stack-protector
LDFLAGS  = -s -melf_i386 -Ttext $(ENTRYPOINT) -Map krnl.map
DASMFLAGS = -D
ARFLAGS  = rcs

# This Program
ORANGESBOOT = boot/boot.bin boot/hdboot.bin boot/loader.bin boot/hdldr.bin
ORANGESKERNEL = kernel.bin
LIB  = lib/orangescrt.a

OBJS  = kernel/kernel.o kernel/start.o kernel/main.o\
   kernel/clock.o kernel/keyboard.o kernel/tty.o kernel/console.o\
   kernel/i8259.o kernel/global.o kernel/protect.o kernel/proc.o\
   kernel/systask.o kernel/hd.o\
   kernel/kliba.o kernel/klib.o\
   lib/syslog.o\
   mm/main.o mm/forkexit.o mm/exec.o\
   fs/main.o fs/open.o fs/misc.o fs/read_write.o\
   fs/link.o\
   fs/disklog.o
LOBJS  =  lib/syscall.o\
   lib/printf.o lib/vsprintf.o\
   lib/string.o lib/misc.o\
   lib/open.o lib/read.o lib/write.o lib/close.o lib/unlink.o\
   lib/getpid.o lib/stat.o\
   lib/lseek.o\
   lib/fork.o lib/exit.o lib/wait.o lib/exec.o
DASMOUTPUT = kernel.bin.asm

# All Phony Targets
.PHONY : everything final image clean realclean disasm all buildimg

# Default starting position
nop :
 @echo "why not \`make image' huh? :)"

everything : $(ORANGESBOOT) $(ORANGESKERNEL)

all : realclean everything

image : realclean everything clean buildimg app vdi

clean :
 rm -f $(OBJS) $(LOBJS)
 cd ./command/; make clean

realclean :
 rm -f $(OBJS) $(LOBJS) $(LIB) $(ORANGESBOOT) $(ORANGESKERNEL)
 cd ./command/; make realclean

disasm :
 $(DASM) $(DASMFLAGS) $(ORANGESKERNEL) > $(DASMOUTPUT)

# We assume that "a.img" exists in current folder
buildimg :
 dd if=boot/boot.bin of=$(FD) bs=512 count=1 conv=notrunc
 dd if=boot/hdboot.bin of=$(HD) bs=1 count=446 conv=notrunc
 dd if=boot/hdboot.bin of=$(HD) seek=510 skip=510 bs=1 count=2 conv=notrunc
 #dd if=boot/hdboot.bin of=$(HD) seek=`echo "obase=10;ibase=16;\`egrep -e '^ROOT_BASE' boot/include/load.inc | sed -e 's/.*0x//g'\`*200" | bc` bs=1 count=446 conv=notrunc
 #dd if=boot/hdboot.bin of=$(HD) seek=`echo "obase=10;ibase=16;\`egrep -e '^ROOT_BASE' boot/include/load.inc | sed -e 's/.*0x//g'\`*200+1FE" | bc` skip=510 bs=1 count=2 conv=notrunc
 mkdir tmp
 sleep 1
 sudo mount -o loop a.img tmp
 sleep 1
 sudo cp -fv boot/loader.bin tmp
 sudo cp -fv kernel.bin tmp
 sleep 1
 sudo umount tmp
 sleep 1
 rmdir tmp

app:
 cd ./command/; make install

vdi:
 rm 80m.vdi
 VBoxManage convertfromraw -format VDI 80m.img 80m.vdi
 VBoxManage internalcommands sethduuid 80m.vdi ccea0f68-01ea-4b9e-8160-e43ef528ba6e

debug:
 rm t.img
 VBoxManage clonehd -format RAW ccea0f68-01ea-4b9e-8160-e43ef528ba6e  t.img
 xxd -u -a -g 1 -c 16 -s 0xAD9C00 -l 20000 t.img
 

boot/boot.bin : boot/boot.asm boot/include/load.inc boot/include/fat12hdr.inc
 $(ASM) $(ASMBFLAGS) -o $@ $<

boot/hdboot.bin : boot/hdboot.asm boot/include/load.inc boot/include/fat12hdr.inc
 $(ASM) $(ASMBFLAGS) -o $@ $<

boot/loader.bin : boot/loader.asm boot/include/load.inc boot/include/fat12hdr.inc boot/include/pm.inc
 $(ASM) $(ASMBFLAGS) -o $@ $<

boot/hdldr.bin : boot/hdldr.asm boot/include/load.inc boot/include/fat12hdr.inc boot/include/pm.inc
 $(ASM) $(ASMBFLAGS) -o $@ $<

$(ORANGESKERNEL) : $(OBJS) $(LIB)
 $(LD) $(LDFLAGS) -o $(ORANGESKERNEL) $^

$(LIB) : $(LOBJS)
 $(AR) $(ARFLAGS) $@ $^

kernel/kernel.o : kernel/kernel.asm
 $(ASM) $(ASMKFLAGS) -o $@ $<

lib/syscall.o : lib/syscall.asm
 $(ASM) $(ASMKFLAGS) -o $@ $<

kernel/start.o: kernel/start.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/main.o: kernel/main.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/clock.o: kernel/clock.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/keyboard.o: kernel/keyboard.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/tty.o: kernel/tty.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/console.o: kernel/console.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/i8259.o: kernel/i8259.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/global.o: kernel/global.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/protect.o: kernel/protect.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/proc.o: kernel/proc.c
 $(CC) $(CFLAGS) -o $@ $<

lib/printf.o: lib/printf.c
 $(CC) $(CFLAGS) -o $@ $<

lib/vsprintf.o: lib/vsprintf.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/systask.o: kernel/systask.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/hd.o: kernel/hd.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/klib.o: kernel/klib.c
 $(CC) $(CFLAGS) -o $@ $<

lib/misc.o: lib/misc.c
 $(CC) $(CFLAGS) -o $@ $<

kernel/kliba.o : kernel/kliba.asm
 $(ASM) $(ASMKFLAGS) -o $@ $<

lib/string.o : lib/string.asm
 $(ASM) $(ASMKFLAGS) -o $@ $<

lib/open.o: lib/open.c
 $(CC) $(CFLAGS) -o $@ $<

lib/read.o: lib/read.c
 $(CC) $(CFLAGS) -o $@ $<

lib/write.o: lib/write.c
 $(CC) $(CFLAGS) -o $@ $<

lib/close.o: lib/close.c
 $(CC) $(CFLAGS) -o $@ $<

lib/unlink.o: lib/unlink.c
 $(CC) $(CFLAGS) -o $@ $<

lib/getpid.o: lib/getpid.c
 $(CC) $(CFLAGS) -o $@ $<

lib/syslog.o: lib/syslog.c
 $(CC) $(CFLAGS) -o $@ $<

lib/fork.o: lib/fork.c
 $(CC) $(CFLAGS) -o $@ $<

lib/exit.o: lib/exit.c
 $(CC) $(CFLAGS) -o $@ $<

lib/wait.o: lib/wait.c
 $(CC) $(CFLAGS) -o $@ $<

lib/exec.o: lib/exec.c
 $(CC) $(CFLAGS) -o $@ $<

lib/stat.o: lib/stat.c
 $(CC) $(CFLAGS) -o $@ $<

lib/lseek.o: lib/lseek.c
 $(CC) $(CFLAGS) -o $@ $<

mm/main.o: mm/main.c
 $(CC) $(CFLAGS) -o $@ $<

mm/forkexit.o: mm/forkexit.c
 $(CC) $(CFLAGS) -o $@ $<

mm/exec.o: mm/exec.c
 $(CC) $(CFLAGS) -o $@ $<

fs/main.o: fs/main.c
 $(CC) $(CFLAGS) -o $@ $<

fs/open.o: fs/open.c
 $(CC) $(CFLAGS) -o $@ $<

fs/read_write.o: fs/read_write.c
 $(CC) $(CFLAGS) -o $@ $<

fs/link.o: fs/link.c
 $(CC) $(CFLAGS) -o $@ $<

fs/disklog.o: fs/disklog.c
 $(CC) $(CFLAGS) -o $@ $<

執行畫面(最後成果)
執行畫面(直接從硬碟啟動,尚未從硬碟啟動)
執行畫面(必要動作,從軟碟安裝作業系統至硬碟)

注意事項

第一次必須從軟碟開機:因為硬碟裡面沒有 loader 和 kernel(類似於安裝作業系統至硬碟)。軟碟作兩件事:
  • 透過 mkfs() 將硬碟的相應分區做成 Orange'S FS
  • 將 cmd.tar 解開,這時 FS 中就有 hdldr.bin 和 kernel.bin

目錄結構

.
├── 80m.img
├── 80m.vdi
├── a.img
├── boot
│   ├── boot.asm
│   ├── hdboot.asm
│   ├── hdldr.asm
│   ├── include
│   │   ├── fat12hdr.inc
│   │   ├── load.inc
│   │   └── pm.inc
│   └── loader.asm
├── command
│   ├── echo.c
│   ├── hdldr.bin
│   ├── inst.tar
│   ├── kernel.bin
│   ├── Makefile
│   ├── pwd.c
│   └── start.asm
├── fs
│   ├── disklog.c
│   ├── link.c
│   ├── main.c
│   ├── misc.c
│   ├── open.c
│   └── read_write.c
├── include
│   ├── stdio.h
│   ├── string.h
│   ├── sys
│   │   ├── config.h
│   │   ├── console.h
│   │   ├── const.h
│   │   ├── fs.h
│   │   ├── global.h
│   │   ├── hd.h
│   │   ├── keyboard.h
│   │   ├── keymap.h
│   │   ├── proc.h
│   │   ├── protect.h
│   │   ├── proto.h
│   │   ├── sconst.inc
│   │   └── tty.h
│   └── type.h
├── kernel
│   ├── clock.c
│   ├── console.c
│   ├── global.c
│   ├── hd.c
│   ├── i8259.c
│   ├── kernel.asm
│   ├── keyboard.c
│   ├── kliba.asm
│   ├── klib.c
│   ├── main.c
│   ├── proc.c
│   ├── protect.c
│   ├── start.c
│   ├── systask.c
│   └── tty.c
├── lib
│   ├── close.c
│   ├── exec.c
│   ├── exit.c
│   ├── fork.c
│   ├── getpid.c
│   ├── lseek.c
│   ├── misc.c
│   ├── open.c
│   ├── printf.c
│   ├── read.c
│   ├── stat.c
│   ├── string.asm
│   ├── syscall.asm
│   ├── syslog.c
│   ├── unlink.c
│   ├── vsprintf.c
│   ├── wait.c
│   └── write.c
├── Makefile
└── mm
    ├── exec.c
    ├── forkexit.c
    └── main.c

留言

這個網誌中的熱門文章

用 C# 批次控制 Word 合併列印

前由 我有全區的電話資料,問題在於我要依不同里別來製作出電話簿。結果如下圖: 單純採用合併列印無法達成我的需求。解決方法係用「功能變數」儲存上一個里別,與現在里別進行比較:若不同,則換頁。不過,這樣功能變數還蠻長的。最後,我還是採用 C# 來解決。 解決方案 用 C# 控制 WORD 中合併列印的「資料來源 Data Source」,給予不同里別的「sqlstatement」。迴圈處理不同的里別即可。但可預見其處理過程會很慢,不過還好,我可以不用在意它,有跑出結果即可。 程式碼 IList<string> areas = new List<string>() { "後壁", "侯伯", "嘉苳", "土溝", "嘉田", "嘉民", "菁豊", "崁頂", "後廍", "墨林", "菁寮", "新嘉", "頂長", "平安", "仕安", "竹新", "新東", "長安", "頂安", "福安", "烏樹" }; string root = @"D:\"; // 根目錄 string data = root + @"\data.docm"; // 資料檔(即資料來源) string template = root + @"\template.docx"; // 已設定好格式與合併欄位的 Word 檔 string output = @"d:\Final"; // 輸出之資料夾 object oMissing = System.Reflection.Missing.Va...

VLC c# 順利編譯

原文網址: http://www.cnblogs.com/haibindev/archive/2011/12/21/2296173.html 原文作者: haibindev 原文標題:c#万能视频播放器 本文的重點在於修正 class VlcPlayer,使其能順利在 VC# Express 2010 .Net Framework 4 下順利編譯。 修正重點在於 CallingConvention = CallingConvention. StdCall 改成 CallingConvention = CallingConvention. Cdecl using System; using System.Runtime.InteropServices; using System.Security; using System.Text; namespace VlcDotNet { class VlcPlayer { private IntPtr libvlc_instance_; private IntPtr libvlc_media_player_; private double duration_; public VlcPlayer(string pluginPath) { string plugin_arg = "--plugin-path=" + pluginPath; string[] arguments = { "-I", "dummy", "--ignore-config", "--no-video-title", plugin_arg }; libvlc_instance_ = LibVlcAPI.libvlc_new(arguments); libvlc_media_player_ = LibVlcAPI.libvlc_media_player_new(libvlc_instance_); } public ...

[Symfony+Doctrine] 透過非 Id 來使用 Pessimistic Lock

根據 文件 ,Doctrine 有 Pessimistic Lock,又分為兩種: LockMode::PESSIMISTIC_WRITE:對應至 MySQL 的 Select FOR UPDATE LockMode::PESSIMISTIC_READ:對應至 MySQL 的 Select LOCK IN SHARE MODE 差別在於 LOCK IN SHARE MODE 會將在 row 的資料鎖定(row-level lock),在非同一交易(Transaction)下,不給寫入,其他交易可以讀取, 且可以繼續 Select LOCK IN SHARE MODE 。而 FOR UPDATE 不僅鎖定該資料,在非同一交易下,不給寫入,其它交易可以讀取, 但不能 Select LOCK IN SHARE MODE 。MySQL 文件有更詳細的比較與情境使用的說明,參考 網址 。 現在問題是,我們要完全採用 ORM 來處理資料。Doctrine 的文件提到 EntityManager::find 可以採用 Pessimistic Lock, 但 find 是透過 id 來處理 。而其他 find 系列函數(包括:findAll, findBy, findOneBy)皆不支援 LockMode。 因此,勢必要有方法來「透過非 id 來使用 Pessimistic Lock」。透過查看原始碼,簡單的方法是有的,解法之範例如下: 19 public function depositAction() 20 { 21 22 $em = $this->getDoctrine()->getManager(); 23 24 $em->transactional(function ($em) { 25 $entityName = 'AcmeTrainingBundle:Account'; 26 $lockMode = LockMode::PESSIMISTIC_READ; 27 $orderBy = null; 28 $...