Reputation: 740
I want to create printl
function that allow me to print string in the ax
register. I am in 16-bit real mode and I can not find any way to print a message. I using int 0x10
to print a single letter.
I try pass argument (string to print) in bx
register, then in a loop print letter by letter and then go back using popa
and ret
. My code didn't really work -- either it created a infinite loop or printed a strange sign.
If you know more efficient way to do it then it's not a problem. I would also like to ask about comment your code if you gave any
This is my code
boot.asm:
start:
mov bx, welcome ;put argument to bx
call printl ;call printl function in sysf.asm
hlt ;halt cpu
welcome db 'Hello', 0
include 'sysf.asm'
times 510 - ($-$$) db 0
db 0x55
db 0xAA
sysf.asm:
;print function
; al is one letter argument (type Java:char)
;
print:
pusha
mov ah, 0x0e
int 0x10
popa
ret ; go back
;printl function
; bx is argument of type Java:String
;
printl:
pusha
jmp printl001
printl001:
lodsb ; I was working with si register but i would like to use bx register
or al,al
jz printl002
mov ah, 0x0e
int 0x10
jmp printl001
printl002:
popa
ret
Upvotes: 5
Views: 4964
Reputation: 39581
The lodsb
instruction loads the byte pointed to by the DS and SI registers but you haven't loaded either with a valid value. Since this a bootloader you also need to use the ORG directive, otherwise the assembler won't know where your code, and therefore the welcome
message, gets loaded into memory. Try changing the start of of your program to:
ORG 0x7c00
start:
push cs
pop ds
mov si, welcome
Upvotes: 4
Reputation: 39166
If, in your sysf.asm, you already had a single character print routine, why didn't you call it from within your printl routine?
Not that it matters, so read on...
BIOS loads your bootloader program in memory at the address 7C00h and the only register that BIOS passes on to your code is the BootDrive number in the DL
register. There are no other registers that you can trust to hold whatever value you would hope to find there!
Now, if you don't use the ORG
directive on bootloader code, then the assembler (FASM) will conclude that you want an implicit ORG 0
. You need to setup the segment register(s) accordingly. The code in the PrintString procedure depends on the DS
segment register. Its correct value will be 07C0h.
However, most people will prefer to start bootloader code with an explicit ORG 7C00h
(if only to make it recognizable in an instant). In this case the DS
segment register needs to be loaded with 0000h.
The BIOS.Teletype function expects the following arguments:
BL
GraphicsColor; this is only used when the display is in a graphics modeBH
DisplayPage; the number of display pages depends on the video modeAL
CharacterCode; the ASCII code of the character to displayAH
FunctionNumber; the number 0Eh (0x0E)Because the BL
and BH
registers are part of the BX
register, we should never use BX
to address the string when writing this kind of PrintString procedure!
And because, when in a bootloader, the display will normally be in display page 0 of the text video mode, we can omit the BL
GraphicsColor argument, but we should still set the BH
DisplayPage. If we don't, then the characters could land on any of the alternative display pages or even nowhere at all.
I want to create printl function that allow me to print string in the
ax
register.
A name like printl is hard to read! And a label like printl001 really hurts my eyes! Better use something like PrintString that conveys what is its purpose.
There's no benefit in passing the argument in the AX
register. SI
is generally the best choice in bootloader code because it facilitates the use of the lodsb
string primitive instruction. This can reduce the code's footprint. But note that it will be wise to use the cld
instruction once to be sure that the direction flag is reset so that SI
can increment the way we want it.
ORG 7C00h
xor ax, ax
mov ds, ax
cld ; So `lodsb` will increment SI
mov si, welcome
call PrintString
hlt
; --------------------------
; IN (si) OUT ()
PrintString:
pusha ; Preserving all registers
mov bh, 0 ; DisplayPage
jmp .While
.Do: mov ah, 0Eh ; BIOS.Teletype
int 10h
.While: lodsb
test al, al ; Test for the end of the zero-terminated string
jnz .Do ; Not yet
popa
ret
; --------------------------
welcome db 'Hello', 0
; --------------------------
times 510 - ($-$$) db 0
dw 0xAA55
Because this code must run in 512 bytes, it makes sense to preserve multiple registers using the 1-byte instructions pusha
/popa
. But if the program allows it, not preserving will shave off even those 2 bytes.
Below is an alternative way of passing strings that allows to not have to specify the address of the string explicitly, shaving off a further 3 bytes.
ORG 7C00h
xor ax, ax
mov ds, ax
cld ; So `lodsb` will increment SI
call PrintString ; -> (AX BX SI)
db 'Hello', 0
hlt
; --------------------------
; IN () OUT () MOD (ax,bx,si)
PrintString:
pop si ; -> SI is the address of the message 'Hello', 0
mov bh, 0 ; DisplayPage
jmp .While
.Do: mov ah, 0Eh ; BIOS.Teletype
int 10h
.While: lodsb
test al, al ; Test for the end of the zero-terminated string
jnz .Do ; Not yet
push si ; SI holds the address where execution resumes (here its `hlt`)
ret
; --------------------------
times 510 - ($-$$) db 0
dw 0xAA55
Be adviced: All this "shaving off" is fine in the one-time bootloader code where codesize is everything. It's not meant to be used in your everyday-coding.
Upvotes: 2
Reputation: 23
I'm totally new to this and I'm not sure if this is the efficient way to do this but it works for me
print_string:
pusha
mov ah, 0x0e
mov al, [bx]
loop:
cmp al, 0
je break
int 0x10
add bx, 0x01
mov al, [bx]
jmp loop
break:
popa
ret
Upvotes: 2
Reputation: 57774
According to the documentation for BIOS int 0x10:
Teletype output: AH=0Eh, AL = Character, BH = Page Number, BL = Color (only in graphic mode)
If BH
is not zero, it will write to a video page which is not displayed. Unless, of course, you have flipped to display whatever page is in BH
. Probably you will want to modify your print function:
print:
pusha
mov ah, 0x0e
xor bx, bx ; BX = 0
int 0x10
popa
ret ; go back
If your output causes the screen to scroll, BP
might be destroyed, though it should not cause a problem for your code because it preserves all the registers.
Upvotes: 3