1

I want to create a print function i x86 Assembly(NASM)to print a string to the terminal without using any OS(i.e. without any syscall).

I wrote the following code so far:
main.asm

[org 0x7c00] ; load our boot sector here

%include "print_function.asm" ; the print function is declared outside this file

mov bx, msg ; mov the message to BX
call print ; call the print function

jmp $ ; hang 

msg:
   db "Hello World!", 0 ; our string

times 510-($-$$) db 0 ; padding
dw 0xaa55 ; magic number

print_function.asm

print:
   pusha ; push all register to the stack before anything lese
   mov ah, 0x0e ; tty mode
   int 0x10 ; print to the screen(with an interrupt)
   popa ; pop all the values 
   ret ; return to the caller

The problem is that when i open run the binary file it prints a character('U') and nothing more.
So what is the problem with this code?
And what can i do to fix it?

beep
  • 1,057
  • 2
  • 11
  • 23
  • 1
    the `%include` will put print function code ahead of your "main" code, but the bootloader is still executed from the beginning, so your print function is executed first, with invalid `bx`, and then the `ret` goes somewhere into the wild, probably finding random machine code elsewhere in memory and having fun there... – Ped7g Jul 10 '18 at 15:31
  • @Ped7g i've tried to put the include statement after the jmp but nothing has changed – beep Jul 10 '18 at 15:35
  • 1
    You also don't set up the `ds` register which may be enough to "fix" it for friendly environment (probably some QEMU BIOS), but generally to make it work on 99% of PCs you would have to harden your code lot more against many more BIOS quirks and differences, you may check source code of some well debugged bootloader like grub or osdev wiki https://wiki.osdev.org/Main_Page where are many things and common pitfalls described. (BTW, you should have testbed where you can actually debug your bootloader, to increase your development speed a lot) – Ped7g Jul 10 '18 at 15:39
  • And BTW2, it's sort of pointless to study all those quirks and problems, unless you are planning to seriously work on some real bootloader code, if you are just trying to improve your programming skills, you should rather focus on user-land assembly programming under some modern OS, like linux/etc, having sort of stable and well defined API, so you can focus on actually learning some programming techniques, and not that there's like ~1% of PC BIOSes which launch your bootloader as `07C0:0000` instead of common `0000:7C00`. – Ped7g Jul 10 '18 at 15:42
  • @Ped7g This is just an assignment...obviously i don't want to write the next Linux or GRUB, btw i don't see how this stuff would be more pointless than using a system API. My actual scope is to learn low level programming, not how Linux or Windows works. Apart that, what you mean by 'set up ds'? What i have to do with it? And what are used for in this specific case? – beep Jul 10 '18 at 15:52
  • 1
    What he means is that the DS segment register value can't be relied on to be what you expect. If you use an org of 0x7c00 you should be setting the _DS_ register to 0. I have bootloader tips in this SO answer: https://stackoverflow.com/a/32705076/3857942 . Failure to set _DS_ properly will cause problems. – Michael Petch Jul 10 '18 at 16:51
  • 2
    You have another serious problem. [int 10h/ah=0eh](http://www.ctyme.com/intr/rb-0106.htm) prints a single character (in AL), not an entire string. – Michael Petch Jul 10 '18 at 16:55
  • `mov bx, msg` doesn't move the string to BX, it moves the address of `msg` to BX – Michael Petch Jul 10 '18 at 16:57
  • @MichaelPetch so I have to loop until i get the 0? Right? And I think I have to do: mov bx, [msg] – beep Jul 10 '18 at 17:08
  • You'd have to loop to read each character and you'd have to mov each character into AL (not BX). And yes, you'd loop until you reach a 0 character. – Michael Petch Jul 10 '18 at 17:22
  • @Ped7g : On a side note, it isn't just real hardware. BOCHS does something similar to some old BIOSes as well. In BOCHs if you boot via floppy the BIOS transfrs to the bootloader through 0x0000:0x7c00, but if you boot via CDROM is uses 0x07c0:0000. BIOSes did this because of the way the original El Torito spec was written that suggested the traditional segment was 0x07c0. – Michael Petch Jul 10 '18 at 17:44
  • @MichaelPetch I known this is pointless, but can i use the BL register instead of AL? – beep Jul 10 '18 at 18:07
  • If you use `int 10h/ah=0eh` which is documented here: http://www.ctyme.com/intr/rb-0106.htm you have to pass the character in AL. That is the requirement of that particular interrupt. The documentation in Ralph Brown's interrupt list tells you what needs to be in each register. – Michael Petch Jul 10 '18 at 18:09
  • calling API itself is quite trivial and uninteresting, although from your questions it looks you have still lot of gaps in basics too, like segmentation of memory in 16 bit real mode, etc (and these things are not that trivial, that's another point why learning for example 32 bit mode under linux first is easier, 16b real mode is actually a bit confusing due to only 16b register being available for 20 bit address calculation). I would even recommend to use clib and not bother with API too much, rather work on programming tasks. Assignment of obsolete non-trivial knowledge :/ ... weird choice. – Ped7g Jul 10 '18 at 20:35
  • @Ped7g Yeah, maybe the assignment is a bit weird. But it's used to teach concepts like kernels, bootloaders and low level programming. Btw i'm searching about memory segmentation right now...i just following a list of arguments. – beep Jul 10 '18 at 22:17

0 Answers0