2016-08-08 11 views
0

다음 소스 파일은 별도로 (원시 바이너리로) 어셈블되어 가상 플로피의 섹터 1과 2에로드됩니다. 이 플로피는 qemu-system-i386 VM의 부팅 매체 역할을합니다.위치 독립적 코드, 16 비트 리얼 모드, 부트 로딩/플로피 읽기

"부트 로더"는 플로피의 섹터 2에서 "첫 번째 프로그램"을 읽은 다음 방금 읽은 코드가있는 메모리로 점프합니다. 다음 코드는 원하는대로 작동합니다 (예 : "첫 번째 프로그램"환영 메시지가 인쇄 됨). 그러나 "첫 번째 프로그램"(16 진 편집기에서 부트 로더 코드를 검사하여 얻음)의 소스에 ORG 0x001E을 지정해야했습니다. 0x001E는 temp 버퍼의 오프셋이며 플로피에서 읽은 코드를 보유합니다.

"부트 로더"

BITS 16 

bootloader_main: 
    mov bx, 0x07C0  ; Set data segment to bootloader's default segment 
    mov ds, bx 


    mov ah, 0x02  ; BIOS int13h "read sector" function 
    mov al, 1   ; Number of sectors to read 
    mov cl, 2   ; Sector to read 
    mov ch, 0   ; Cylinder/track 
    mov dh, 0   ; Head 
    mov dl, 0   ; Disk number (here, the floppy disk) 
    mov bx, 0x07C0  ; Segment containing the destination buffer 
    mov es, bx 
    mov bx, temp  ; Destination buffer offset 
    int 0x13 

    jmp temp 

    ret 
;end bootloader_main 




temp: times 60 db 17 

times 510-($-$$) db 0  ; Pad rest of sector and add bootloader 
dw 0xAA55      signature 

"첫 번째 프로그램"또는

BITS 16 
ORG 0x001E  ; Assume that this code will be located 0x001E bytes  
        after start of bootloader (in RAM) 

mov bx, string  ; Print a welcome string 
mov ah, 0x0E 
print_loop: 
    mov al, byte [bx] 
    int 0x10 
    inc bx 
    cmp byte [bx], 0 
    jne print_loop 
;end print_loop 


string: db "This is the first program.", 0 

, 나는 즉 직후 RAM에 프로그램을로드 (대신 temp의 버퍼 ORG 0x2000x200을 사용할 수 있습니다 부트 로더).하지만 유용한 운영 체제를 만들 때 이러한 해킹은 지속될 수 없습니다. 이런 종류의 주소 하드 코딩은 어떻게 피합니까?

+1

프로세서 아키텍처에 태그를하시기 바랍니다에게 선언 단지 NASM 기능입니다. – rjp

+0

기본 주소가 부트 로더와 함께 지정되어야 할 필요가 있다고 생각합니다. 이 분야에서 일한 대부분의 작업은 임베디드 시스템에서 수행되었으며, BIOS가 많은 도움이된다는 것을 알고 있습니다. 나에게는 부트 로더를 사용할 때 일반적으로 엔트리 포인트가 예상되는 위치를 알려주는 문서가있다. – rjp

+0

누군가가 플로피에로드되고 부트 로더가 읽는 프로그램을 어셈블하는 경우'ORG 512' (또는 다른 명확한 주소) 같은 것을 소스 코드에 추가하라는 메시지가 나타납니다. 그리고 부트 로더는 항상 프로그램을 메모리의 같은 부분에로드 할 것입니까? 현대 운영 체제가 어떻게 발전했는지 상상하기는 어렵지만 역사에 대해 더 자세히 읽어야합니다. – Vale132

답변

1

세그먼트를 사용하여 주소를 하드 코딩하지 않아도됩니다. 16의 배수 인 주소에 "첫 번째 프로그램"을로드하고 해당 세그먼트 (주소/16)로 DS를로드 한 다음 segment이 프로그램을로드 한 위치 인 segment:0으로 멀리 이동합니다. 로드 된 프로그램에서 ORG 0을 사용하십시오. 예를 들어

: 나는 mov dl, 0 명령을 제거했습니다

BITS 16 

bootloader_main: 
    mov ax, 0x07C0  ; Set data segment to bootloader's default segment 
    mov ds, ax 

    mov ah, 0x02  ; BIOS int13h "read sector" function 
    mov al, 1   ; Number of sectors to read 
    mov cl, 2   ; Sector to read 
    mov ch, 0   ; Cylinder/track 
    mov dh, 0   ; Head 
    mov bx, program_seg ; load program at program_seg:0 
    mov es, bx 
    xor bx, bx 
    int 0x13 

    mov ax, program_seg 
    mov ds, ax 
    mov ss, ax   ; set stack to end of program_seg 
    mov sp, 0 
    jmp program_seg:0 

bootloader_end: 
program_seg equ (bootloader_end - bootloader_main + 0x7c00 + 15)/16 

times 510-($-$$) db 0  ; Pad rest of sector and add bootloader 
dw 0xAA55     ; signature 
BITS 16 
ORG 0 

mov bx, string  ; Print a welcome string 
mov ah, 0x0E 
print_loop: 
    mov al, byte [bx] 
    int 0x10 
    inc bx 
    cmp byte [bx], 0 
    jne print_loop 
;end print_loop 


string: db "This is the first program.", 0 

당신이 코드이 값을 하드 안하기 때문이다. BIOS는 DL에 부팅 장치의 드라이브 번호를 전달하므로 변경할 필요가 없습니다.

+0

또 다른 일반적인 트릭은 부트 로더 코드를 다른 곳으로 복사하는 것입니다 (어디에서 알 수 있도록 복사하고 있습니다). 원래의 부트 섹터에 두 번째 섹터를로드하여 다시 알려줍니다. 많은 접근법. –

+0

@DavidHoelzer 질문은 하드 코딩되지 않은 "알려진"주소에서 프로그램을로드하는 방법과 관련이있는 것 같습니다. 로드 세그먼트를 선택하는 방법 외에도이 예제는 본질적으로 MS-DOS가 .COM 파일로드를 처리하는 방법입니다. –

0

이 예에서는 jmp Load_Buffer & Load_Buffer 사이에 임의의 양의 코드를 입력 할 수 있습니다. 부트 섹터의 마지막 4 바이트를 초과하지 않는 한.

부트 로더

BOOTSEG  equ 0x7c0 
    LOW_MEM  equ 18 
    DISKIO  equ 19 

는 사용 가능한 얼마나 많은 4K 페이지 결정하고 세그먼트 주소로이 변환합니다. 제 경우에는 0x8FC0입니다.

xor  cx, cx 
mov  cl, 64 
int  LOW_MEM    ; Get # of 4k segments to top of memory 
sub  ax, cx 
shl  ax, 6      ; 

스택 포인터를 수정할 때 항상 인터럽트를 비활성화하십시오.이 CS가 0이 될 수 또는 바이오스 공급 업체에 따라 0x7c0 수이 시점에서 64K

cli 
mov  ss, ax 
xor  sp, sp 
sti 

적은 메모리의 상단에 매우 안전한 장소에 스택을두고,하지만 그건별로 중요하지 않습니다 만 ES 때문에 : BX를 우리의 경우 ES = BOOTSEG + Load_Buffer/16

xor  bx, bx 
int  DISKIO     ; Read sector 2 
jmp  Load_Buffer 
을에 그러나, 여기에 대안이 예에서와 같이 Load_Buffer와 0x7c0 및 BX와 ES를로드했을

mov  ax, Load_Buffer 
shr  ax, 4 
add  ax, BOOTSEG 
mov  es, ax 
mov  ax, 0x201     ; Read one sector 
mov  cx, 2      ; Starting @ cylinder 0, sector 2 
xor  dh, dh     ; Head 0 and BIOS has already passed device. 

이 시점에서 중요하다

페이지 경계에 정렬하면 Load_Buffer 위에 아무리 많이 추가 되더라도 로더는 부팅 섹터의 마지막 4 바이트를 초과 실행하지 않는 한 항상 작동합니다. 정렬은 페이지 경계에 있어야하기 때문에 16 개가 올바르게 작동했을 것입니다.

align 32 
Load_Buffer: 

은 NOP의 넣다 코드 부문을 채우기 위해 좋은 아이디어는 세그먼트 오류가 없을 것, 멀리 아마 실행됩니다입니다. LODSB를 사용하려고 먼저 프로그램

times 508 - ($-$$) db 144  ; NOP's 0x90 
int  25      ; Incase codes runs away we'll reboot 
dw  0xAA55     ; Boot sig (no required by some emulators) 

는하지 그 당신의 방법보다 더 정확하지만 단지 적은 코드를 사용합니다.

mov  si, string 
push es 
pop  ds      ; DS:SI now points to string 
mov  ah, 14     ; TTY output 

Loop: 
lodsb       ; Read from DS:SI 
test al, 255    ; Test if these any of these bits are on. 
jz  .Done 
int  16 
jmp  Loop 

라벨 이전 기간은 지역 라벨 조립 질문을 게시 할 때

.Done: 
hlt 
jmp  $ 

string db 'This is the first program', 0