Stuck on BIOS INT10 WriteString!!

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
indiocolifa
Member
Member
Posts: 41
Joined: Sat May 24, 2008 12:41 pm
Location: La Plata, Argentina

Stuck on BIOS INT10 WriteString!!

Post by indiocolifa »

I'm trying to start creating some toy OS just for fun. All from scratch, so I began writing the bootloader of course. My first test was to write a BOOTING... message in red, at top of the cleared screen, this is my primitive code:

Code: Select all

	
;; ITCHIES/OS
	;;
	;; Boot loader
	;; =============================================================

	format binary
	use16

	SECTOR_SIZE = 512

	org	0x7c00
	jmp	start

	;; -- message strings -----------------------------------------

	bootmsg		db	'BOOTING...'
	bootmsglen	equ	$-bootmsg

	;; -- boot code -----------------------------------------------
	
start:	mov	ax, 0003h
	int	10h		; clear screen
	mov	ax, 1300h	; moving cursor
	mov	bx, 0004h	; page 0 / redcolor
	xor	dx, dx		; print at (0,0)
	mov	cx, bootmsglen	; string length
	mov	bp, bootmsg	; es:bp --> msg (ES=0 at boot)
	int	10h		; call BIOS
	hlt

	;; -- boot signature ------------------------------------------
	
	db	SECTOR_SIZE-2-($-$$) dup 0
	dw 	0xaa55

The problem is that CX is loaded with wrong count of characters (0x17) so I get the message plus garbage. I don't know where the error is, this is FASM code and the bootmsglen calculation is OK, I think.

Note that I'm assuming that ES is set at 0000 by the BIOS, it's correct to assume this?

Thx for your help.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Hi,

Code: Select all

mov cx, bootmsglen
Moves the memory address 'bootmsglen' in to cx.

Code: Select all

mov cx, WORD [bootmsglen]
Moves the data pointed to by 'bootmsglen'.

Cheers,
Adam
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Oh, and by ending your boot message with a NULL terminator, you don't need to store the message length.

HTH,
Adam
indiocolifa
Member
Member
Posts: 41
Joined: Sat May 24, 2008 12:41 pm
Location: La Plata, Argentina

Post by indiocolifa »

AJ wrote:Hi,

Code: Select all

mov cx, bootmsglen
Moves the memory address 'bootmsglen' in to cx.

Code: Select all

mov cx, WORD [bootmsglen]
Moves the data pointed to by 'bootmsglen'.

Cheers,
Adam
Hey Adam, thanks for your reply.

But EQU does reserve some memory? How you can take the address of a symbolic constant?

From what I understand (I know assembler but not how FASM works in particular), FASM does:

Pass 1: Calculates CURRENT_OFFSET - BOOTMSG address
Pass 2: Replaces this offset diff in the mov instruction (number of chars) and that's what CX expects...

Can you give me some light on this "address of equates" thing? :wink:
indiocolifa
Member
Member
Posts: 41
Joined: Sat May 24, 2008 12:41 pm
Location: La Plata, Argentina

Post by indiocolifa »

What I'm trying to say is that it's not bad to use

MOV cx, bootmsg_1

since if I define

bootmsg_1 equ 10 ; (correct value=10 chars)

the message works properly. I don't know why FASM calculates 0x17 as the resulting message length!
:twisted:
indiocolifa
Member
Member
Posts: 41
Joined: Sat May 24, 2008 12:41 pm
Location: La Plata, Argentina

Post by indiocolifa »

Ha! I solved it using FASM constants:

BOOTMSGLEN = $-bootmsg

but I don't understand why this not works with:

BOOTMSGLEN equ $-bootmsg :?
svdmeer
Member
Member
Posts: 87
Joined: Tue May 06, 2008 9:32 am
Location: The Netherlands

Post by svdmeer »

indiocolifa wrote:Ha! I solved it using FASM constants:

BOOTMSGLEN = $-bootmsg

but I don't understand why this not works with:

BOOTMSGLEN equ $-bootmsg :?
It's not hard to understand why you got 0x17 as length instead of 10.

Your definition behaved like a macro. On the place where you use it, $ is calulated. So $ is not calculated from the position directly after the string, but in the middle of your code.

Code: Select all

bootmsg      db   'BOOTING...'               [10 bytes]
   bootmsglen   equ   $-bootmsg 

   ;; -- boot code ----------------------------------------------- 
    
start:   mov   ax, 0003h                      [ 3 bytes ]
   int   10h      ; clear screen                [2 bytes]
   mov   ax, 1300h   ; moving cursor     [3 bytes] 
   mov   bx, 0004h   ; page 0 / redcolor   [3 bytes]
   xor   dx, dx      ; print at (0,0)          [2 bytes]
mov   cx, bootmsglen   ; string length
Before the instruction with bootmsglen there are after the label bootmsg 10 bytes of data and 13 bytes of code. 10 + 13 = 23 = 0x17.
svdmeer
Member
Member
Posts: 87
Joined: Tue May 06, 2008 9:32 am
Location: The Netherlands

Post by svdmeer »

mov bp, bootmsg ; es:bp --> msg (ES=0 at boot)
I don't think it's safe to assume the value of segment-registers at boot. You'd better initialize them.

From my bootsector:

Code: Select all

xor ax,ax		; ax=0
cli
mov ss,ax       	; ss=0
mov sp,7C00h   ; Top of stack
sti
mov bp,sp ; bp=7c00h for access to filesystem info in the first 127 bytes 
                ; of the bootsector (like FAT parameters) with smaller code
mov ds,ax		; ds=0
mov es,ax		; es=0
...
indiocolifa
Member
Member
Posts: 41
Joined: Sat May 24, 2008 12:41 pm
Location: La Plata, Argentina

Post by indiocolifa »

Thanks, I thought that addresses present on EQU definitions were calculated and later replaced literally, seems that works like a plain literal text replacement.

I fixed some "assumptions", hehe, like segment registers. Now it's working ok, the only question is that I left SS at 0 (it's OK) but I did not specify stack pointer. This is OK?
svdmeer
Member
Member
Posts: 87
Joined: Tue May 06, 2008 9:32 am
Location: The Netherlands

Post by svdmeer »

indiocolifa wrote:Thanks, I thought that addresses present on EQU definitions were calculated and later replaced literally, seems that works like a plain literal text replacement.

I fixed some "assumptions", hehe, like segment registers. Now it's working ok, the only question is that I left SS at 0 (it's OK) but I did not specify stack pointer. This is OK?
No. You can't set SS to a value without knowing the value of SP. SS=0, it's the same segment as the MBR/bootsector. In the worst case the stack overwrites your code. So set SS and SP both like in my example.
It doesn't matter where you place the stack (outside code and important data, so keep also away from the first 1536 bytes in RAM), but you should know where it is.
SS and SP both zero is also ok, your stack grows down from 0000:FFFF.
I use SP 7C00h, stack is right under the bootsector/MBR code and grows down, in the unused memory space between realmode interrupt/bios data and the bootsectorMBR code.
chezzestix
Member
Member
Posts: 118
Joined: Mon May 05, 2008 5:51 pm

Post by chezzestix »

Is using this Interrupt function really all that worth it? I wrote a custom string out function in nine lines... its as short as what its taking to set up the variables for the int function. All you have to do is set di to the address of the string you want to print and call the function.

Code: Select all

sb_strout:
mov al,[di]
cmp al,0
je sbstrout_end
mov ah,0Eh
int 10h
add di,1h
jmp sb_strout
sbstrout_end:
ret
svdmeer
Member
Member
Posts: 87
Joined: Tue May 06, 2008 9:32 am
Location: The Netherlands

Post by svdmeer »

chezzestix wrote:Is using this Interrupt function really all that worth it? I wrote a custom string out function in nine lines... its as short as what its taking to set up the variables for the int function. All you have to do is set di to the address of the string you want to print and call the function.

Code: Select all

sb_strout:
mov al,[di]
cmp al,0
je sbstrout_end
mov ah,0Eh
int 10h
add di,1h
jmp sb_strout
sbstrout_end:
ret
Number of lines is not important. Important is the amount of code it generates, especially when it's part of the bootsector/mbr with very limited space. Instead of "mov al,[di]" and "add di,1h" you can use "lodsb".

Your function has a bug: It doesn't set BX for color attribute and page. Value of BX is uncertain in your function, so textcolor will be unpredicable.

This is my small and safe version, specially written for bootsectors:

Code: Select all

; **********************
; *** Function Print ***
; **********************
;
;  Usage: The string must follow directly after the call
;         to the function "Print".
;
;  For example: 
;               call Print	; Call printfunction
;               db 'Put your message here...',0
;               nop             ; Program execution continues..
;
Print:          cld                     ; Directionflag=0, for lodsb
                pop si                  ; ds:si -> string
                push bp                 ; BP on stack, buggy bioses
                lodsb                   ; Char from string in AL
NextChar:       mov bx,7                ; Color 7 (ordinary light gray)
                mov ah,0x0e             ; Function 0x0e: Teletype output
                int 0x10                ; Call video-BIOS with interrupt 10h
                lodsb                   ; Char from string in AL
                or al,al                ; Value 0 ?
                jnz NextChar            ; If not, print this char
                pop bp                  ; Restore BP
                push si                 ; Put the right value of IP on the stack
                ret                     ; Return from function
The way of calling is somethign different: Instead of setting SI or another register for addressing the string, you have to put the string directly after the function call. That save a MOV SI,... instruction (3 bytes of code). The function has a PUSH SI and POP SI instruction to manage the pointer to the string and a goot return from the function, but it makes a call to the function take less (3 bytes) of code macause MOV SI,.. is not necessary.
indiocolifa
Member
Member
Posts: 41
Joined: Sat May 24, 2008 12:41 pm
Location: La Plata, Argentina

Post by indiocolifa »

I've already used LODSB instruction in a loop in my boot sector. It's really tight. I've also wrote a function to output integers using STOSB (needs AX as a parameter) but that's another story.
Post Reply