2015-09-21 25 views
8

Tôi đang viết hệ điều hành nhỏ - để thực hành. Tôi bắt đầu với bộ nạp khởi động.
Tôi muốn tạo hệ thống lệnh nhỏ chạy ở chế độ thực 16 bit (hiện tại).
Tôi đã tạo bộ nạp khởi động để đặt lại ổ đĩa, sau đó tải sector sau bộ nạp khởi động.
Vấn đề là do sau jmp chức năng không có gì thực sự xảy ra.Trình tải khởi động không chuyển sang mã hạt nhân

Tôi không cố gắng tải khu vực tiếp theo tại 0x7E00 (tôi không hoàn toàn chắc chắn làm thế nào để trỏ địa chỉ bằng cách sử dụng es: bx để có thể là một vấn đề, tôi tin rằng địa chỉ của nó: bù đắp), ngay sau khi bộ nạp khởi động.

Đây là mã:

; 
; SECTOR 0x0 
; 

;dl is number of harddrive where is bootloader 
org 0x7C00 
bits 16 

;reset hard drive 
xor ah,ah 
int 0x13 
;read sectors 
clc 
mov bx,0x7E00 
mov es,bx 
xor bx,bx 
mov ah,0x02 ;function 
mov al,0x1 ;sectors to read 
mov ch,0x0 ;tracks 
mov cl,0x1 ;sector 
mov dh,0x0 ;head 
int 0x13 
;if not readed jmp to error 
jc error 
;jump to 0x7E00 - executed only if loaded 
jmp 0x7E00 
error: 
    mov si,MSGError 
    .loop: 
     lodsb 
     or al,al 
     jz .end 
     mov ah,0x0E 
     int 0x10 
     jmp .loop 
    .end: 
     hlt 
MSGError db "Error while booting", 0x0 
times 0x1FE - ($ - $$) db 0x0 
db 0x55 
db 0xAA 

; 
; SECTOR 0x1 
; 

jmp printtest 
;definitions 
MSGLoaded db "Execution successful", 0x0 
; 
; Print function 
; si - message to pring (NEED TO BE FINISHED WITH 0x0) 

printtest: 
    mov si,MSGLoaded 
    .loop: 
     lodsb 
     or al,al 
     jz .end 
     mov ah,0x0E 
     int 0x10 
     jmp .loop 
    .end: 
     hlt 

times 0x400 - ($-$$) db 0x0 

Tôi đã kiểm tra mã này sử dụng VirtualBox nhưng không thực sự xảy ra, Các lỗi đọc không thấy, cũng như thông điệp đó sẽ được in.

+0

Trước khi ra bất kỳ BIOS ngắt bạn cần phải thiết lập stack ('SS' và 'SP' registe rs). Bạn cũng cần phải gọi 'CLD' hoặc' STD' trước khi sử dụng các hàm lodsb và các hàm liên quan. Mã của bạn giả định tăng tự động nên sau khi thiết lập cuộc gọi ngăn xếp 'CLD'. Bạn cũng không có vẻ để thiết lập đăng ký 'DS' với một phân đoạn thích hợp (lodsb có khả năng sẽ không hoạt động nếu không). Nó không phải là rõ ràng từ những gì bạn đã trình bày những gì điểm gốc cho hình ảnh hạt nhân của bạn đã được thiết lập để. Makefile của bạn hoặc lệnh bạn sử dụng để liên kết, biên dịch, tạo hình ảnh đĩa sẽ có lợi trong câu hỏi của bạn. –

+0

'mov bx, 0x7E00'; 'mov es, bx',' xor bx, bx' có vẻ như nó có thể sai. Mã của bạn cho thấy bạn có ý định tải sector từ đĩa tại địa chỉ vật lý '0x0000: 0x7E00' =' 0x7E00', nhưng bạn đặt phân đoạn 'ES':' BX' thành '0x7E00' và' BX' thành 0 trong một địa chỉ vật lý của 0x7E00 << 4 + 0x0000 = '0x7E000'. Tôi nghĩ rằng bạn có nghĩa là để đặt 'ES' để' 0x07E0' mà sẽ mang lại một địa chỉ vật lý của 0x07E0 << 4 + 0x0000 = địa chỉ vật lý '0x7E00'. Bạn về cơ bản nạp hạt nhân của bạn cao hơn nhiều vào bộ nhớ hơn bạn dự định.Thử thay đổi '' mov bx, 0x7E00' thành 'mov bx, 0x07E0' –

+0

Ký quỹ của hạt nhân của tôi là sector 0x1. Tôi đang biên dịch với việc sử dụng 'nasm bootloader.asm -o bootloader.img '(bootloader.asm là mã nằm trong chủ đề chính). sau đó thiết lập bootloader.img làm đĩa mềm trong VirtualBox. về 'lodsb' nó đã làm việc khi thay vào đó' jc' tôi đã sử dụng 'jnc' để hiển thị thông báo lỗi khi đọc thực sự kết thúc thành công. – vakus

Trả lời

36

Những vấn đề chính với mã này là:

  1. ES: BX đã được trỏ đến phân khúc sai: bù đắp để nạp kernel vào
  2. ngành sai đã được nạp nên hạt nhân không phải là những gì đã được dự kiến ​​

Người đầu tiên là trong mã này:

mov bx,0x7E00 
mov es,bx 
xor bx,bx 

Câu hỏi muốn tải ngành từ đĩa đến 0x0000:0x7E00 (ES: BX). Mã này đặt ES2 ES: BX thành 0x7E00:0x0000 giải quyết đến địa chỉ thực của 0x7E000 ((0x7E00 < < 4) + 0x0000). Tôi nghĩ rằng ý định là để tải 0x07E0 vào ES sẽ mang lại địa chỉ thực của 0x7E00 ((0x07E0 < < 4) + 0x0000). Bạn có thể tìm hiểu thêm về 16:16 tính toán địa chỉ bộ nhớ here. Nhân các phân đoạn bằng 16 là giống như chuyển nó sang trái 4 bit.

Vấn đề thứ hai trong mã là ở đây:

mov ah,0x02 ;function 
mov al,0x1 ;sectors to read 
mov ch,0x0 ;tracks 
mov cl,0x2 ;sector number 
mov dh,0x0 ;head 
int 0x13 

Số cho ngành 512 khối thứ hai trên đĩa là 2, không 1. Vì vậy, để khắc phục các mã trên bạn cần phải thiết lập CL phù hợp:

mov cl,0x2 ;sector number 

Mẹo tổng thể phát triển Bootloader

các vấn đề khác có thể đi lên chạy mã trên giả lập khác nhau, Máy ảo và phần cứng vật lý thực sự mà cần được giải quyết là:

  1. Khi BIOS nhảy sang mã của bạn, bạn không thể dựa vào CS, DS, ES, SS, SP sổ đăng ký có giá trị hợp lệ hoặc dự kiến.Chúng sẽ được thiết lập một cách thích hợp khi bộ nạp khởi động của bạn bắt đầu. Bạn chỉ có thể được đảm bảo rằng bộ nạp khởi động của bạn sẽ được tải và chạy từ địa chỉ vật lý 0x00007c00 và số ổ đĩa khởi động được nạp vào thanh ghi DL.
  2. Đặt SS: SP vào bộ nhớ mà bạn biết sẽ không xung đột với hoạt động của mã của riêng bạn. BIOS có thể đã đặt con trỏ ngăn xếp mặc định của nó ở bất kỳ đâu trong megabyte đầu tiên của RAM có thể sử dụng và địa chỉ. Không có sự đảm bảo nào về vị trí đó và liệu nó có phù hợp với mã bạn viết hay không.
  3. Cờ chỉ đường được sử dụng bởi lodsb, movsb v.v ... có thể được đặt hoặc xóa. Nếu cờ hướng được đặt không đúng SI/DI sổ đăng ký có thể được điều chỉnh sai hướng. Sử dụng STD/CLD để đặt nó theo hướng bạn muốn (CLD = forward/STD = backwards). Trong trường hợp này, mã giả định chuyển động về phía trước, vì vậy, một mã nên sử dụng CLD. Hơn về vấn đề này có thể được tìm thấy trong một instruction set reference
  4. Khi nhảy đến một hạt nhân nó thường là một ý tưởng tốt để FAR JMP với nó để nó đúng cách đặt CS: IP đến giá trị mong đợi. Điều này có thể tránh các vấn đề với mã hạt nhân có thể làm tuyệt đối gầnJMPsGỌI SỐ trong cùng một phân đoạn.
  5. Nếu nhắm mục tiêu bộ tải khởi động của bạn cho mã 16 bit hoạt động trên bộ xử lý 8086/8088 (VÀ cao hơn), tránh sử dụng thanh ghi 32 bit trong mã lắp ráp. Sử dụng AX/BX/CX/DX/SI/DI/SP/BP thay vì EAX/EBX/ECX/EDX/ESI/EDI/ESP/EBP. Mặc dù không phải là một vấn đề trong câu hỏi này, nó đã là một vấn đề cho những người khác đang tìm kiếm sự giúp đỡ. Bộ vi xử lý 32 bit có thể sử dụng thanh ghi 32 bit ở chế độ thực 16 bit, nhưng 8086/8088/80286 không thể vì chúng là bộ vi xử lý 16 bit mà không truy cập vào thanh ghi 32 bit mở rộng.
  6. FSGS đăng ký phân đoạn đã được thêm vào 80386+ CPU. Tránh chúng nếu bạn có ý định nhắm mục tiêu 8086/8088/80286.

Để giải quyết mục đầu tiên và thứ hai mã này có thể được sử dụng gần sự bắt đầu của bộ nạp khởi động:

xor ax,ax  ; We want a segment of 0 for DS for this question 
mov ds,ax  ;  Set AX to appropriate segment value for your situation 
mov es,ax  ; In this case we'll default to ES=DS 
mov bx,0x8000 ; Stack segment can be any usable memory 

cli   ; Disable interrupts to circumvent bug on early 8088 CPUs 
mov ss,bx  ; This places it with the top of the stack @ 0x80000. 
mov sp,ax  ; Set SP=0 so the bottom of stack will be @ 0x8FFFF 
sti   ; Re-enable interrupts 

cld   ; Set the direction flag to be positive direction 

Một vài điều cần lưu ý. Khi bạn thay đổi giá trị của thanh ghi SS (trong trường hợp này là qua MOV), bộ xử lý giả sử tắt các ngắt cho lệnh đó và giữ chúng cho đến sau hướng dẫn sau đây. Thông thường, bạn không cần phải lo lắng về việc tắt ngắt nếu bạn cập nhật SS theo sau ngay lập tức bằng cách cập nhật SP.Có một lỗi trong bộ vi xử lý 8088 rất sớm, nơi điều này đã không được vinh danh vì vậy nếu bạn đang nhắm mục tiêu các môi trường rộng nhất có thể nó là một đặt cược an toàn để vô hiệu hóa rõ ràng và kích hoạt lại chúng. Nếu bạn không có ý định làm việc trên một lỗi 8088 thì có thể xóa các hướng dẫn CLI/STI trong mã ở trên. Tôi biết về lỗi này đầu tay với công việc tôi đã làm vào giữa những năm 80 trên máy tính gia đình của tôi.

Điều thứ hai cần lưu ý là cách tôi thiết lập ngăn xếp. Đối với những người mới đến 8088/8086 lắp ráp 16 bit, ngăn xếp có thể được thiết lập vô số cách. Trong trường hợp này, tôi đặt phần trên cùng của ngăn xếp (phần thấp nhất trong bộ nhớ) tại 0x8000 (SS). Sau đó tôi đặt con trỏ ngăn xếp (SP) thành 0. Khi bạn push something on the stack trong chế độ thực 16 bit, bộ xử lý đầu tiên sẽ giảm con trỏ ngăn xếp xuống 2 và sau đó đặt 162 bit WORD tại vị trí đó. Do đó, lần đẩy đầu tiên vào ngăn xếp sẽ là 0x0000-2 = 0xFFFE (-2). Sau đó, bạn sẽ có một số SS: SP trông giống như 0x8000:0xFFFE. Trong trường hợp này, ngăn xếp chạy từ 0x8000:0x0000 đến 0x8000:0xFFFF.

Khi xử lý ngăn xếp chạy trên 8086 (không áp dụng cho bộ xử lý 80286,80386+), nên đặt con trỏ ngăn xếp (SP) thành số chẵn. Trên 8086 ban đầu nếu bạn đặt SP thành số lẻ, bạn sẽ phải trả 4 clock cycle penalty cho mọi quyền truy cập vào không gian ngăn xếp. Kể từ khi 8088 có một bus dữ liệu 8 bit, hình phạt này không tồn tại, nhưng tải 162 bit từ trên 8086 mất 4 chu kỳ đồng hồ trong khi nó mất 8 chu kỳ đồng hồ trên 8088 (hai bộ nhớ 8 bit đọc).

Cuối cùng, nếu bạn muốn thiết lập một cách rõ ràng CS: IP để CS được thiết lập đúng vào thời điểm các JMP là đầy đủ (để kernel) sau đó nó được khuyến khích để làm một FAR JMP (Xem Các hoạt động ảnh hưởng đến đăng ký phân khúc/FAR Jump). Trong NASM cú pháp JMP sẽ trông như thế này:

jmp 0x07E0:0x0000 

Một số (ví dụ: MASM/MASM32) lắp ráp không có hỗ trợ trực tiếp để mã hóa một FAR Jmp vì vậy một cách thức mà nó có thể được thực hiện bằng tay là như thế này:

db 0x0ea  ; Far Jump instruction 
dw 0x0000 ; Offset 
dw 0x07E0 ; Segment 

Nếu sử dụng GNU lắp ráp nó sẽ trông giống như:

ljmpw $0x07E0,$0x0000 
+1

Khi một người sử dụng GNU assembler cho x86, nó giống như '.intel_syntax noprefix'' jmp 0x07E0: 0x0000' –

Các vấn đề liên quan