Unlike include
in high level languages (where the compiler decides about where the external contents go), this %include
will insert the contents of your print.asm file right where you have written the line. The true source code then becomes:
[BITS 16]
[ORG 0x7c00]
mov al, 65
call print_char
print_char:
mov ah, 0x0e
mov bh, 0x00
mov bl, 0x07
int 0x10
ret
jmp $
times 512-($-$$) db 0
dw 0xaa55
As you can see, once the call print_char
returns, the code falls through in the print_char routine and thus executes the same instructions a second time (producing "AA").
Additionally, because the second time, the ret
instruction has no sensible return address on the stack, the computer will crash, but in a dangerous way because there's no telling about where execution will go to!
If you want, the jmp $
is also a crash but at least it's a 'safe' one.
This is what your code should have been:
[BITS 16]
[ORG 0x7C00]
mov al, 65
call print_char
cli
hlt
jmp $-2
%include "print.asm"
times 510-($-$$) db 0
dw 0xAA55
times 512-($-$$) db 0
(512 instead of 510) was wrong because it forces the mandatory signature bytes out of the 512-bytes bootsector. I don't understand why your emulator even launches this code without a valid signature where it belongs!
jmp $
is not wrong, but the preferred way is using cli
hlt
jmp $-2
.