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
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
.