I'm attempting to create an Operating System, I don't know every in and out of assembly language, I am mainly learning as I go. Here is the problem, I built a simple function to compare two strings (eax
and ebx
) the problem is that when I run the code to do this my system doesn't execute anything after the call... What did I do wrong?
compare:
xor ecx, ecx
.by_char:
mov dh, [eax+ecx]
mov dl, [ebx+ecx]
cmp dh, dl
inc ecx
je .zero_test
stc
jmp .done
.zero_test:
cmp dh, 0
je .done
jmp .by_char
.done:
ret
For reference here is the code I am calling this function from:
start:
mov esp, stack
mov si, msg_welcome
call print
mov eax, msg_welcome
mov ebx, msg_diskerr
call compare
jc j_aa
jmp j_bb
j_aa:
mov si, msg_strnequ
jmp part_b
j_bb:
mov si, msg_strrequ
part_b:
call print
mov eax, msg_booting
mov ebx, msg_booting
call compare
jc j_cc
jmp j_dd
j_cc:
mov si, msg_strnequ
jmp part_c
j_dd:
mov si, msg_strrequ
part_c:
call print
jmp halt
halt:
hlt
jmp halt
Here is where I am defining my variables:
bss:
msg_welcome: db "Welcome To Hypr Byte!", 10, 13, 10, 13, 0
msg_nokernl: db "FATAL: Missing or Corrupted Kernel. System Halted...", 10, 13, 10, 13, 0
msg_diskerr db "FATAL: An error occured while attempting to read the disk. Please go to https://www.instinct-loop.xyz/hypr/help to recieve support...", 0
msg_bterror db "Uh oh! An error occured while attempting to boot. Please go to https://www.instinct-loop.xyz/hypr/help to recieve support...", 0
msg_booting db "Attempting to load the kernel...", 10, 13, 10, 13, 0
msg_kreturn db "Oops! The kernel ran into a fatal error... System Halted!", 0
msg_strnequ db "Strings are Not Equal!", 10, 13, 0
msg_strrequ db "Strings are Equal!", 10, 13, 0
my OS is 16 bit, but when I try to use 16 bit registers in my compare function it tells me that I am giving it an invalid effective address.
16-bit addressing modes can only use [bx|bp + si|di + constant]
or a subset of that. Using 32-bit addressing modes is a valid workaround if you can't just pass pointers in si
and di
for addr modes like [si]
and [di]
like a normal person.
But only if you zero-extend the 16-bit addresses into the full 32-bit register, otherwise high garbage can lead to a segment limit violation. In real mode, segments implicitly have a 64k limit; offset > 65535 will fault.
You're probably not actually crashing VirtualBox itself, but you could be crashing the virtual guest machine with a triple fault or something.
mov eax, msg_welcome
does write the full register with the zero-extended address, unlike mov si, msg_welcome
Your loop will always exit after the first iteration, because inc ecx
/ je .zero_test
falls through. INC clears ZF because incrementing ECX from 0 to 1 leaves ECX != 0.
Presumably you should inc
before cmp
/je
if you want je
to read flags set by cmp
.
I'm not sure where your code would actually fault. Use a debugger to find out, e.g. by running it inside BOCHS instead of VirtualBox. BOCHS has a debugger built-in which understands segmentation, unlike attaching GDB to qemu or virtualbox as a GDB-remote.
Your loop is pretty inefficient, BTW. You can use cmp dl, [di]
or something and put a jne
at the bottom. If you break out of the loop with a cmp/jcc, you can put a test dl,dl
/jnz
as the loop branch at the bottom.
You should never write a jcc
over a jmp
, just write a JCC with the opposite condition that falls through. And here you can fall through to a RET instead of a jmp .done
. (The one exception is if you have to jump farther than -128..+127 bytes, and you're targeting an ancient CPU that doesn't support JCC rel16, only short JCC rel8.)
On an AMD CPU (no partial-register renaming), mov dh, [mem]
has a false dependency on mov dl, [mem]
, so there's extra latency for merging the load values before your cmp
can run. That's another reason for using a cmp-with-mem instead of 2 loads.