assemblyiokernelx86-16real-mode

16-bit x86 Assembly Kernel getting stuck when calling input function


Well, I'm writing my own OS just for fun, and I thought it would have been cool to add a bytecode "language" interpreted by the kernel itself. I took inspiration mainly from Java, which is translated into a bytecode interpreted by the JVM. So basically I'm just adding basic I/O functions that I can use to write a basic program that asks your name and cheers you using the input value. My problem is that the code is getting stuck after inputting my name: the cursor just stays there without doing anything. I tried revising my functions but the problem still persists. If this information helps, I'm using NASM to compile both boot.asm and kern.asm (nasm -f bin filename.asm -o filename.bin), which are then linked using dd (dd if=filename.bin of=filename.img) and concatenated using cat in a unique file (cat boot.img kern.img > main.img). main.img is then ran in QEMU. All the OS code follows:

boot.asm:

[org 0x7c00]
[bits 16]

    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax

    ; Set up the stack.
    mov ss, ax
    mov sp, 0x7c00

    mov ah, 0x01
    mov cx, 0x0007
    int 0x10

    mov si, ntgos_bootld_msg
    call out

    mov si, bootld_started_msg
    call out

    mov si, disk_reading_msg
    call out

    ; Read two sectors from the disk.
    mov si, KERN_ADDR
    call disk

    ; Handle disk reading error.
    jc disk_error

    mov si, disk_done_msg
    call out

    mov si, kern_starting_msg
    call out

    ; Jump to kernel.
    jmp KERN_ADDR

    jmp $

disk_error:
    mov si, disk_error_msg
    call out

    jmp $

%include "boot-libs/out.asm"
%include "boot-libs/disk.asm"

ntgos_bootld_msg:
    db "ENTEEG OS BOOTLOADER", 0x0a, 0x0d, 0x00
bootld_started_msg:
    db "    Started.", 0x0a, 0x0d, 0x00
disk_reading_msg:
    db "    Reading and loading disk at address 0x7e00...", 0x0a, 0x0d, 0x00
disk_done_msg:
    db "    Disk reading done successfully.", 0x0a, 0x0d, 0x00
disk_error_msg:
    db "    (!) ERROR! Error reading disk. Cannot continue booting. Please restart the computer.", 0x0a, 0x0d, 0x00
kern_starting_msg:
    db "    Starting kernel...", 0x0a, 0x0d, 0x00

KERN_ADDR equ 0x7e00

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

boot-libs/out.asm:

out:
    pusha
    mov ah, 0x0e

out_loop:
    mov al, [si]
    cmp al, 0x00
    je out_end
    int 0x10

    inc si
    jmp out_loop

out_end:
    popa
    ret

boot-libs/disk.asm:

disk:
    pusha
    mov ah, 0x02

disk_loop:
    mov al, 0x02
    mov ch, 0x00
    mov dh, 0x00
    mov cl, 0x02
    mov bx, 0x0000
    mov es, bx
    mov bx, si
    int 0x13

    jnc disk_end

disk_err_:
    popa
    stc
    ret

disk_end:
    popa
    ret

kern.asm:

[org 0x7e00]
[bits 16]

    mov si, ntgos_kern_msg
    call out

    mov si, kern_started_msg
    call out

    ; Clear the screen.
    mov ah, 0x00
    mov al, 0x03
    int 0x10

    mov si, code
    call exec

    jmp $

%include "kern-libs/ioutils.asm"
%include "exec.asm"

ntgos_kern_msg:
    db "ENTEEG OS KERNEL", 0x0a, 0x0d, 0x00
kern_started_msg:
    db "    Started.", 0x0a, 0x0d, 0x00

INPUT_ADDR equ 0xd000

code:
    db 0x25, "Type in your name:", 0x0a, 0x0d, 0x00, 0x27, 0x25, "Hi, ", 0x00, 0x26, 0xd0, 0x00, 0x00

times 1024-($-$$) db 0x00

exec.asm:

exec:
    mov al, [si]
    cmp al, 0x25 ; Out
    je _0x25
    cmp al, 0x26 ; Outaddr
    je _0x26
    cmp al, 0x27 ; In
    je _0x27
    cmp al, 0x00 ; Exit program
    je _0x00

    jmp exec

_0x25:
    call print_out_prompt
    inc si
    call out
    jmp exec_ret

_0x26:
    call print_out_prompt
    inc si
    call outaddr
    jmp exec_ret

_0x27:
    pusha
    mov si, in_prompt
    call out
    popa
    call in
    jmp exec_ret

_0x00:
    ret


exec_ret:
    inc si
    jmp exec

print_out_prompt:
    pusha
    mov si, out_prompt
    call out
    popa
    ret

in_prompt:
    db "{?} ", 0x00
out_prompt:
    db "{.}     ", 0x00

kern-libs/ioutils.asm:

out:
    mov ah, 0x0e

out_loop:
    mov al, [si]
    cmp al, 0x00
    je out_end
    int 0x10
    inc si

    jmp out_loop

out_end:
    ret

outaddr:
    mov ah, 0x0e
    mov bl, [si]
    inc si
    mov bh, [si]

outaddr_loop:
    mov al, [bx]
    cmp al, 0x00
    je outaddr_end
    int 0x10
    inc bx

    jmp outaddr_loop

outaddr_end:
    ret

in:
    mov cx, 0x004c

in_loop:
    mov ah, 0x00
    int 0x16
    mov ah, 0x0e
    int 0x10
    cmp cx, 0x0000
    je in_full_buff
    cmp al, 0x0d
    je in_end
    mov [si], al
    inc si
    dec cx

    jmp in_loop

in_full_buff:
    stc

in_end:
    mov byte [si], 0x00
    mov al, 0x0a
    int 0x10
    ret

Solution

  • INPUT_ADDR equ 0xd000
    
    code:
        db 0x25, "Type in your name:", 0x0a, 0x0d, 0x00, 0x27, 0x25, "Hi, ", 0x00, 0x26, 0xd0, 0x00, 0x00
    

    Your program does not get stuck while inputting, but rather while trying to display the inputted string afterwards. It is fine that you have devised a versatile i/o format that eg. can incorporate a string address following the escape code 0x26, but, and this is crucial, x86 is a little endian machine and so the 16-bit address 0xD000 must have its low byte first and its high byte last. In 0x26, 0xd0, 0x00 you have it the other way round!

    INPUT_ADDR equ 0xD000
    
    code:
        db 0x25, "Type in your name:", 0x0D, 0x0A, 0x00, 0x27, 0x25, "Hi, ", 0x00, 0x26, 0x00, 0xD0, 0x00
    

    Please note that it is more usual to specify the newline as 0x0D followed by 0x0A, meaning first carriage return (13) and then linefeed (10).

    Also note that eventhough you had to specify the address as two separate bytes because of the db directive, you can read both bytes together as in mov bx, [si]. So you can replace:

    mov bl, [si]
    inc si
    mov bh, [si]
    

    by:

    mov bx, [si]
    inc si
    

    EDIT

    On closer inspection, your program does get stuck while inputting because it overwrites the format string (since SI is still pointing at it)! I think it would be best (most versatile) to configure the 0x27 command just like the 0x26 command. So add the buffer address for it too: 0x27, 0x00, 0xD0.