羅左欣 BE STRONG TO BE USEFUL

20210518 [學習筆記] 30天作業系統自作入門筆記 (4)


  • 工作環境
    • Lubuntu 20.04
    • VMware Workstation 16.1.1

20210518 - Day_4

撰寫 Makefile

從撰寫原始檔、組/編譯程式,到了最後還要跑虛擬機。

如果每次都要一行行打指令會很辛苦。

所以要把這些指令寫成 Makefile,在往後的測試會相對容易和簡便。

-

Makefile 主要要做的事情有:

  • STEP 1 - 製作相關檔案
    • 處理開機磁區 - ipl10.asm
    • 包含作業系統本體 -asmhead.asmnasmfunc.asmbootpack.cos.lds
  • STEP 2 - 製作映像檔
    • 製作開機磁區映像檔
    • 將作業系統本體寫入映像檔

STEP 1 - 製作相關檔案

主要的指令有這些

nasm ipl10.asm -o ipl10.bin -l ipl10.lst

nasm asmhead.asm -o asmhead.bin -l asmhead.lst

nasm -g -f elf nasmfunc.asm -o nasmfunc.o

gcc -march=i486 -m32 -nostdlib -g -O0 -T os.lds bootpack.c nasmfunc.o -o bootpack.hrb -fno-pie

cat asmhead.bin bootpack.hrb > haribote.sys

STEP 2 - 製作映像檔

有兩種產生映像檔的寫法,這兩種都會用到 mcopy

  • 寫法 1 (主要指令 - mformatmcopy)

    mformat -f 1440 -C -B ipl10.bin -i haribote.img ::

    mcopy -i haribote.img haribote.sys ::

  • 寫法 2 (主要指令 - ddmcopy)

    dd if=/dev/zero of=haribote.img bs=512 count=2880

    dd if=ipl10.bin of=haribote.img conv=notrunc

    mcopy -i haribote.img haribote.sys ::

完整 Makefile 程式碼

將以上 STEP 1、STEP 2 裡的指令寫成 Makefile。 (以下為完整程式碼)

OSNAME := haribote

.DEFAULT_GOAL : all
.PHONY : all
all : img
	DEBUG_DIR := ./debug
	OBJDUMP_FLAGS := --full-contents --all-headers --target=binary --architecture=i386:intel --disassemble-all


#===============================================================================
ipl10.bin    : ipl10.asm
asmhead.bin  : asmhead.asm
nasmfunc.o   : nasmfunc.asm

%.bin : %.asm 
	@make make-debug-dir
	nasm $^ -o $@ -l ${DEBUG_DIR}/$*.lst

%.o : %.asm
	@make make-debug-dir
	nasm -f elf $^ -o $@ -l ${DEBUG_DIR}/$*.lst

BOOTPACK_FILES := bootpack.c nasmfunc.o
bootpack.hrb : ${BOOTPACK_FILES} os.lds
	@make make-debug-dir

gcc -march=i486 -m32 -nostdlib -g -O0 \
	-T os.lds \
	-o $@ \
	-fno-pie\
	${BOOTPACK_FILES}

objdump ${OBJDUMP_FLAGS} $@ > ${DEBUG_DIR}/$@.dasm


${OSNAME}.sys : asmhead.bin bootpack.hrb
%.sys :
	@make make-debug-dir
	cat $^ > $@

objdump ${OBJDUMP_FLAGS} $@ > ${DEBUG_DIR}/$@.dasm



${OSNAME}.img : ipl10.bin ${OSNAME}.sys

# ======方法 1======
    mformat -f 1440 -C -B ipl10.bin -i $@ ::
    mcopy -i $@ ${OSNAME}.sys ::
# ================

# ======方法 2======
# dd if=/dev/zero of=${OSNAME}.img bs=512 count=2880
# dd if=ipl10.bin of=${OSNAME}.img conv=notrunc
# mcopy -i $@ ${OSNAME}.sys ::
# =================


#===============================================================================


.PHONY : asm
asm :
	make ipl10.bin

.PHONY : img
img :
	make ${OSNAME}.img 

.PHONY : run
run :
	make img
	qemu-system-i386 -fda ${OSNAME}.img

.PHONY : make-debug-dir
        make-debug-dir :
	@if [ ! -d ${DEBUG_DIR} ]; then mkdir ${DEBUG_DIR}; fi


#===============================================================================


.PHONY : clean
clean :
	@rm *.img *.bin *.sys *.hrb *.o

.PHONY : debug
debug:
	make img
	qemu-system-i386 -fda haribote.img -gdb tcp::10000 -S

記得將 Makefile 與其他原始檔放在同一目錄下,未來如果要編譯直接載入 Makefile 指令即可。

以下為常用的指令 (在 Makefile 和其他原始檔皆存在的狀況下):

  • 編譯原始檔

    make

  • 編譯並執行 (以 QEMU 模擬)

    make run

  • 清除原始檔以外的檔案

    make clean

一、讓螢幕顯示白色

1. 在 VRAM (Video RAM, 顯示畫面用的記憶體) 寫入指令,相關程式碼如下:

  • bootpack.c (../01_source1_white/bootpack.c)
/* 告知C語言編譯器,有使用其他檔案所製作的函數 */

void io_hlt(void);
void write_mem8(int addr, int data);
/* 雖然是函數的宣告,卻沒有 {},而且馬上就以; 字元結束。
   這是表示它存在於其他的檔案裡,請到該處去進行讀取。 */

void HariMain(void)
{
    int i;  /* 宣告變數。稱為 i 的變數是 32 位元的整數型別 */

    for (i = 0xa0000; i <= 0xaffff; i++)     {
        write_mem8(i, 15);      // 螢幕會顯示白色
        // write_mem8(i, i & 0x0f);     // 螢幕會顯示條紋色
    }

    for (;;) {
        io_hlt(); /* 這樣在 nasmfunc.asm 的 io_hlt 將會執行 */
    }	
}

  • nasmfunc.asm (../01_source1_white/nasmfunc.asm)
; nasmfunc
; TAB=4

; ===========================================
section .text
    GLOBAL	io_hlt
	GLOBAL	write_mem8
; ===========================================


; ===========================================
io_hlt:	; void io_hlt(void);
		HLT
		RET
; ===========================================


write_mem8:    ; void write_mem8(int addr, int data);
        MOV     ECX, [ESP+4]    ; 因為在 [ESP+4] 裡有 addr,所以將該值讀入到 ECX
        MOV     AL, [ESP+8]     ; 因為在 [ESP+8] 裡有 data,所以將該值讀入 AL
        MOV     [ECX], AL
        RET

2. 進行編譯並執行

make run

【執行結果】 螢幕顯示白色

二、讓螢幕顯示條紋

1. 在 VRAM (Video RAM, 顯示畫面用的記憶體) 寫入指令,相關程式碼如下:

  • bootpack.c (../01_source2_stripes/bootpack.c)
/* 告知C語言編譯器,有使用其他檔案所製作的函數 */

void io_hlt(void);
void write_mem8(int addr, int data);
/* 雖然是函數的宣告,卻沒有 {},而且馬上就以; 字元結束。
   這是表示它存在於其他的檔案裡,請到該處去進行讀取。 */

void HariMain(void)
{
    int i;  /* 宣告變數。稱為 i 的變數是 32 位元的整數型別 */

    for (i = 0xa0000; i <= 0xaffff; i++)     {
        write_mem8(i, 15);      // 螢幕會顯示白色
        // write_mem8(i, i & 0x0f);     // 螢幕會顯示條紋色
    }

    for (;;) {
        io_hlt(); /* 這樣在 nasmfunc.asm 的 io_hlt 將會執行 */
    }	
}

2. 進行編譯並執行

make run

【執行結果】 螢幕顯示條紋色

三、矩形的繪製與處理

1. 修改主要程式碼

2. 要修改 Linker Script - os.lds,程式碼如下:

  • os.lds (…/01_source3_rectangle/os.lds)
OUTPUT_FORMAT(binary)
OUTPUT_ARCH(i386)

SECTIONS {
     .head 0x0 :
     {
         LONG((ADDR(.bss) + SIZEOF(.bss) + 0xfff) & ~ 0xfff)
         BYTE(0x4f) BYTE(0x53) BYTE(0x4c) BYTE(0x69)
         LONG(0x0)
         LONG(ADDR(.data))
         LONG(SIZEOF(.data))
         LONG(LOADADDR(.data))
         LONG(0xe9000000)
         LONG(HariMain - 0x20)
         LONG((ADDR(.bss) + SIZEOF(.bss) + 0xf) & ~ 0xf)
     }

     .text ADDR(.head) + SIZEOF(.head) :
         SUBALIGN (1)
     {
         *(.text)
     }

     .data 0x00000400 :
         AT ( LOADADDR(.text) + SIZEOF(.text) ) SUBALIGN (4)
     {
         *(.data)
         *(.rodata*)
     }

     .bss  :
         AT ( LOADADDR(.data) + SIZEOF(.data) ) SUBALIGN (4)
     {
        *(.bss)
     }

     /DISCARD/ : { *(*) }
}

3. 進行編譯並執行

make run

【執行結果】 螢幕顯示矩形

四、螢幕顯示出狀態列圖樣

1. 修改主要程式碼

2. 進行編譯並執行

make run

【執行結果】 螢幕顯示出狀態列圖樣


Similar Posts

Comments