Search code examples
assemblyx86nasmbootloaderbios

how to load more sector and call a function in the loaded sector


I assembled the following code with

nasm -f bin option

and put it into floppy image and then run with virtualbox

my goal is to call print_word

but how can I call print_word here?

To learn how to call a function which is in extra sector,

I put print_word into second sector with intension.

Thanks in advance.

main.asm:

org 0x7c00
start:
    mov ax, 0x0206
    mov cx, 0x0002
    mov dx, 0x0000

    mov bx, 0x7e00
    push bx
    pop es

    mov bx, 0
.try_again:
    int 13h
    jc .try_again

    mov ax, 0xabcd
    push ax
    jmp 0x7e00


jmp $

times 510 - ($-$$) db 0
dw 0xaa55

more:
    %include "./common/print_word.asm"

print_word.asm:

;print_word(word)
print_word:
    push bp
    mov bp, sp

    push word [bp+4]
    call print_word_1

    push word [bp+4]
    call print_word_2
    push word [bp+4]
    call print_word_3
    push word [bp+4]
    call print_word_4
    mov sp, bp
    pop bp
    ret


print_word_1:
    push bp
    mov bp, sp

    mov ah, 0x0e
    mov byte al, [bp+4]
    shr al, 4
    cmp al, 0x0a
    jl .num
    sub al, 0x0a
    jmp .char
.num:
    add al, '0' 
    jmp .put
.char:
    add al, 'A'
    jmp .put
.put:
    int 10h
    mov sp, bp
    pop bp
    ret

print_word_2:
    push bp
    mov bp, sp

    mov ah, 0x0e
    mov byte al, [bp+4]
    shl al, 4
    shr al, 4
    cmp al, 0x0a
    jl .num
    sub al, 0x0a
    jmp .char
.num:
    add al, '0' 
    jmp .put
.char:
    add al, 'A'
    jmp .put
.put:
    int 10h
    mov sp, bp
    pop bp
    ret


print_word_3:
    push bp
    mov bp, sp

    mov ah, 0x0e
    mov byte al, [bp+5]
    shr al, 4
    cmp al, 0x0a
    jl .num
    sub al, 0x0a
    jmp .char
.num:
    add al, '0' 
    jmp .put
.char:
    add al, 'A'
    jmp .put
.put:
    int 10h
    mov sp, bp
    pop bp
    ret

print_word_4:
    push bp
    mov bp, sp

    mov ah, 0x0e
    mov byte al, [bp+5]
    shl al, 4
    shr al, 4
    cmp al, 0x0a
    jl .num
    sub al, 0x0a
    jmp .char
.num:
    add al, '0' 
    jmp .put
.char:
    add al, 'A'
    jmp .put
.put:
    int 10h
    mov sp, bp
    pop bp
    ret

Solution

  • Use a regular CALL instruction. Your assembler will assemble a CALL NEAR with a displacement rather an absolute address. Something like this:

    mov ax,0xabcd
    push ax
    call print_word
    add sp,2  ;to restore the stack
    
    jmp $
    

    Also, note that your print_word routine, when calling print_word_1, print_word_2, etc, doesn't restore the stack properly. Your calling convention requires the caller to remove the arguments it pushed on the stack, so after each call print_word_N instruction, you have to restore the stack, either by issuing an add sp,2 instruction, or by popping the last stack element into an unused register. For example, like this:

    As the value passed to the different print_word_N routines is always the same, and as your routines don't change the value of the parameter, you can optimize code size a litte by assuming the argument is already on the stack for the second and subsequent calls:

    print_word:
        push bp
        mov bp, sp
    
        push word [bp+4]
        call print_word_1
        call print_word_2
        call print_word_3
        call print_word_4
        mov sp, bp
        pop bp
        ret
    

    BTW: Don't forget this is a little endian machine. You are converting to hex and printing the byte at [ebp+4], then the byte at [ebp+5]. This will result in CDAB printed on screen. Two left-most digits of your hex number are located in the byte at [ebp+5] so you should print them first, then digits stored at byte [ebp+4].

    Ah! About loading sectors: your mistake is how you have considered the segment and offset of the block where the sector is loaded. You want to load the next sector at 0000:7E00, that is, next to the first 512 bytes of code loaded. Instead, you load it at 7E00:0000, which is not the same address at all.

    The updated and fixed code is as follows (the part that loads the next sector):

    org 0x7c00
    start:
        mov ax, 0   ;segment is 0
        mov ds, ax
        mov es, ax
    
    .try_again:
        mov ax, 0x0201  ;load 1 sector
        mov cx, 0x0002  ;located at sector 2
        mov dx, 0x0000
        mov bx, 0x7e00  ;into 0000:7E00
        int 13h
        jnc .ok
    .errorsector:
        mov ax,0   ;reset floppy controller ir error
        int 13h
        jmp .try_again
    
    .ok:
        mov ax, 0xabcd
        push ax
        call print_word
        add sp,2
    
        jmp $