简体   繁体   中英

NASM far jump / far call in real mode and ASM code conventions

I tried to make a bootloader the last few days and this is the result:

BITS 16


;CONSTANTS
BOOTSEG     equ 07C0h
STACKSEG    equ 1BC0h ; BOOTSEG + 512 Byte (bootloader) + 512 Byte (second stage) + 4096 Byte (buffer) = 1BC0h
STACKSIZE   equ 0400h ; 1KB stack



; INIT
    mov AX, BOOTSEG     
    mov DS, AX      ; set data segment to adress where bootloader will be loaded to
    mov AX, STACKSEG
    cli         ; disable interrupts while set up stack
    mov SS, AX  
    mov SP, STACKSIZE   ; set up stack
    sti         ; restore interrupts
    mov [bootdev], DL   ; save boot device number

;START
    mov SI, string      ; get the adress of the string to print into SI 
    call _printstring

;LOAD 2nd STAGE
    mov AH, 02h     ; int 13h subfunction ah=02
    mov AL, 01h     ; read 1 sector
    mov CX, 02h     ; begin read at track 0, sector 2
    mov DH, 00h     ; head = 0 ????
    mov DL, [bootdev]   ; read from boot device
    mov BX, BOOTSEG     
    add BX, 512
    mov ES, BX      ; write second stage right after first stage
    mov BX, 00h


    int 13h         ; do it
    jc fail
    mov SI, success     ; OK
    call _printstring


    jmp 09C0h:0000h     ; jump to second stage, execute it
                ; does not work:
                    ; 1)    jmp ES:BX
                    ;
                    ; 2)    push ES
                    ;   push BX
                    ;   retf



fail:   
    mov SI,error        ; error on reading second stage
    call _printstring



loop:   
    jmp loop        ; infinite loop at the end


_printhex:
; AX: hex value to print
; Modifies: AX, BX, DX, CX

    mov CX, 4
start:  mov DX, 00h
    mov BX, 10h
    div BX
    cmp DX, 9
    jg letter
    add DL, 30h
    jmp store

letter: add DL, 37h

store:  push DX
    dec CX
    jnz start
    mov CX,4

print:  pop AX
    call _printchar
    dec CX
    jnz print 
    mov AL, 13
    call _printchar
    mov Al, 10
    call _printchar 
    ret






_printchar:
; print char in AL
; Modifies: AX, BX

    mov AH, 0Eh
    mov BX, 07h
    int 10h
    ret




_printstring:
; SI : start adress of string
; Modifies: AX , BX , SI

m1: lodsb           ; Loads [SI] into AL and increases SI by one    
    or AL, AL       ; check if AL = 0
    jz finish       ; then finish
    call _printchar     ; else print charakter
    jmp m1

finish: 
    ret         ; return from the printstring call






;DATA
    string db 'Started my first Bootloader', 13, 10, 0
    success db 'Success', 13, 10, 0
    error db 'Error', 13, 10, 0
    bootdev db 0




; MAKE BOOTSECTOR
    times 510-($-$$) db 0       ; fill up the sector to 512 - 2 = 510 bytes
    dw 0AA55h           ; set the two bootsector identifying bytes





; SECOND STAGE
    add BX, 10
    mov AL, 'A'
    mov AH, 0Eh 
    mov BX, 07h
    int 10h     ; test output

loop2:
    jmp loop2   ; infinite loop at the end

Now I have two questions:

  1. In line 43 I have to use a jump with an absolute adress, but I want to use the values of ES and BX . So I tried the two alternatives you can see in the comments but they did not work for me. What am I doing wrong?

  2. I am quite new to low-level programming. Are there some major or minor mistakes in my code? Are there code style conventions I did not consider?

I do not want to format the code, so here is the link to the asm file and you can read it with your favourite editor: https://www.dropbox.com/s/i3jpprf66nlmzz2/mybootloader.asm?m

jmp 09C0h:0000h

and

push ES
push BX
retf

should work equally well (provided, of course, ES =9C0h and BX =0 in the latter case).

And jmp ES:BX is not a valid instruction, or, at least, it's not going to do what you might be expecting from it.

Possible problems:

  • 9C0h:0 is the wrong address
  • you don't have the expected code at 9C0h:0 (the read has failed or you asked the BIOS to store the read data at a different location)
  • the code at 9C0h:0 was not compiled to begin its execution at offset 0 (remember that x86 machine code is generally not position-independent)
  • something else, perhaps a code/data corruption or uninitialized variable/register

I found the bug. A very silly mistake: The BX register was overwritten by the _printstring function bevor the interrupt was triggered. I fixed it and now the jump does exactly work like described by Alexey Frunze - thank you:

push ES

push BX

retf

Moreover I´ve made a mistake in segment adressing (also thanks to Alexey Frunze): I wanted to load the second stage right after the 512 byte long first stage. So referring to segment/offset adressing (physical adress = 16*segment + offset) I have to add 512/16 = 32 = 20H to 07C0. So the values have to be: ES = 07E0H and BX = 0000H

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM