星期日

回到Assembler

好了,已經不可以再用「簡單工具」了,沒有Assembler,真的很難做下去。

這幾天K了2本有關Assembly的書,及一個教寫OS的網頁(這個)。

呼~ 上次認真用Assembly來寫code是何時呢? 我想已經10年之前的事了,天啊~ 真是太可怖了,太難了吧(相對於PHP? XD),連bug都是非常low level的,寫了一整天,才可以在螢幕上Print出一句話 @@"

OK,說回正題,這次還是用上了NASM來compile,首先是loader.asm,已經在loader把Protect Mode轉好了:
---------- loader.asm ----------
;*********************************************
;   ABCOS - Bootloader
;*********************************************

%define KERNEL_ADDR 0x1000

   bits 16 ;Tell the assembler it is in 16 bit
   org 0x7c00 ;Tell the assembler we start at 0x7C00

;Reset the drive first
reset_drive:
   MOV AH, 0
   INT 13h
   OR AH, AH
   JNZ reset_drive

;Loading the kernel from disk to memory
   MOV AX, 0
   MOV ES, AX
   MOV BX, KERNEL_ADDR ;copy the kernel to 0000:1000
   MOV AL, 02h ;Number of sectors to read
   MOV CH, 0 ;Disk cylinder
   MOV CL, 02h ;Disk sector (starts with 1, not 0)
   MOV DH, 0 ;Disk head
   MOV AH, 02h ;Command - 02h for 'Read sector from disk'
   INT 13h
   OR AH, AH
   JNZ reset_drive ;any problem, back to reset_drive label and try again

   CLI ;no interrupts for now
   LGDT [gdt_desc] ;***load GDT into GDTR, GDT set done

   ;***to Protect Mode (set CR0 bit 0 to 1)
   MOV EAX, CR0
   OR EAX, 1
   MOV CR0, EAX

   ;make it becomes Descriptor:Address memory model by a far jump
   jmp 08h:p_mode ;08h because the code selector at 08h of the GDT

   bits 32 ;tell assembler we are in 32 bits mode now
p_mode:
   MOV AX, 10h ; set data segments to data selector (0x10) (as they are incorrect after enter to PMode)
   MOV DS, AX
   MOV SS, AX
   MOV ES, AX

   ;Method 3.1: Enables A20 through keyboard controller
   MOV AL, 0xdd ; command 0xdd: enable a20
   OUT 64h, AL ; send command to controller

   JMP KERNEL_ADDR ;jump to kernel

;Construct Global Descriptor Table
;Offset 0 in GDT: Descriptor code=0
gdt_data:
   dd 0 ; null descriptor
   dd 0

; Offset 0x8 bytes from start of GDT: Descriptor code therfore is 8
; gdt code: ; code descriptor
   dw 0FFFFh ; limit low
   dw 0 ; base low
   db 0 ; base middle
   db 10011010b ; access
   db 11001111b ; granularity
   db 0 ; base high

; Offset 16 bytes (0x10) from start of GDT. Descriptor code therfore is 0x10.
; gdt data: ; data descriptor
   dw 0FFFFh ; limit low (Same as code)
   dw 0 ; base low
   db 0 ; base middle
   db 10010010b ; access
   db 11001111b ; granularity
   db 0 ; base high

;...Other descriptors begin at offset 0x18. Remember that each descriptor is 8 bytes in size?
; Add other descriptors for Ring 3 applications, stack, whatever here...

end_of_gdt:

gdt_desc:
   dw end_of_gdt - gdt_data - 1 ; limit (Size of GDT)
   dd gdt_data ; base of GDT


   times 510 - ($-$$) db 0 ; We have to be 512 bytes. Clear the rest of the bytes with 0
   dw 0xAA55 ; Boot Signiture
---------- loader.asm ----------

> nasm d:\os\loader.asm -f bin -o d:\os\loader.bin

跟著是kernel.asm,只是在螢幕上Print一句話:
---------- kernel.asm ----------
;*********************************************
;   ABCOS - Kernel
;*********************************************

   bits 32
   org 0x1000
   
   MOV SI, msg
   MOV BH, 23
   MOV BL, 70
   CALL PrintString

   HLT

msg db 'ha1234', 10, 13, 'this is next line', 0

; *** Define screen constant ***
%define VIDMEM 0xB8000 ; video memory start address
%define VIDMEM_END 0xB8FA0 ; video memory last address
%define SCR_COLS 80
%define SCR_ROWS 25

; *** Set cursor position ***
;    BH = Y (row) position
;    BL = X (column) position
SetCursor:
   MOV AX, SCR_COLS
   MUL BH
   XOR BH, BH
   ADD AX, BX
   MOV BX, AX ; BX = BH * SCR_COLS + BL

   MOV AL, 0x0F ; Cursor location low byte index
   MOV DX, 0x03D4 ; Write it to the CRT index register
   OUT DX, AL
   MOV AL, BL ; The current location is in EBX. BL contains the low byte, BH high byte
   MOV DX, 0x03D5 ; Write it to the data register
   OUT DX, AL ; low byte

   MOV AL, 0x0E ; Cursor location high byte index
   MOV DX, 0x03D4 ; Write to the CRT index register
   OUT DX, AL
   MOV AL, BH ; the current location is in EBX. BL contains low byte, BH high byte
   MOV DX, 0x03D5 ; Write it to the data register
   OUT DX, AL ; high byte
   RET
; *** End of Set cursor position ***


; *** Print character in text mode ***
;    DL = Ascii code of character
;    BH = Y (row) position
;    BL = X (column) position
PrintChar:
   MOV EAX, SCR_COLS
   MUL BH ; AX = 80d or 50h, BH max = 24d or 18h
   XOR BH, BH
   ADD AX, BX ; AX max = 1920d or 780h, result AX max = 2000d or 7D0h
   ADD AX, AX ; result AX max = 4000d or FA0h
   MOV EDI, EAX ; DI = ( BH * SCR_COLS + BL ) * 2
   ADD EDI, VIDMEM
   MOV DH, 0x7 ; character attribute
   MOV [EDI], DX ; print character
   RET
; *** End of Print character in text mode ***


; *** Print String in text mode ***
;    SI = Address of String, string end by 0 (eg. MOV SI, msg)
;    BH = Y (row) start position
;    BL = X (column) start position
PrintString:
   MOV EAX, SCR_COLS
   MUL BH ; AX = 80d or 50h, BH max = 24d or 18h
   XOR BH, BH
   ADD AX, BX ; AX max = 1920d or 780h, result AX max = 2000d or 7D0h
   ADD AX, AX ; result AX max = 4000d or FA0h
   MOV EDI, EAX ; DI = ( BH * SCR_COLS + BL ) * 2
   ADD EDI, VIDMEM
PrintString_loop:
   MOV AL, [SI]
   CMP AL, 0
   JE PrintString_end
   MOV AH, 0x7 ; character attribute
   MOV [EDI], AX ; print character that BL point to
   INC SI
   INC EDI
   INC EDI
   CMP EDI, VIDMEM_END
   JE PrintString_end
   JMP PrintString_loop
PrintString_end:
   RET
; *** End of Print String in text mode ***


; *** Clear Screen in text mode ***
ClearScreen:
   MOV EDI, VIDMEM
   MOV CX, 2000
ClearScreen_loop:
   MOV word [EDI], 0x0720
   ADD EDI, 2
   LOOP ClearScreen_loop
   RET
; *** End of Clear Screen in text mode ***
---------- kernel.asm ----------

> nasm d:\os\kernel.asm -f bin -o d:\os\kernel.bin

> copy /b /y loader.bin+kernel.bin boot.img

跟著想做的,就是盡量把hardware的資料拿出來。

ps. 這幾天有一個感想,在電腦科技發展的數十年,我覺得從來都是量的改變,質是沒有改變過的,現在大部份有關的知識理論,都是當時的想法,大家都沒法跳出來想似的?

2 則留言:

匿名 說...

一切都是換了包裝
內裏做法大都不外如是
boot 了機,都是window 式介面 ( 不論你是微軟, Linux or Mac )
一大堆software, 不論你說何種方言( Basic , C .. ) ,都走不出執行的先後次序,創意何在!

bonep 說...

對呢~ 真難搞~ 可能要做的,就是先由改變自己的思維模式開始~~~