Bootloader issue; int 13h succeeds but jump fails

Programming, for all ages and all languages.
Post Reply
Synon
Member
Member
Posts: 169
Joined: Sun Sep 06, 2009 3:54 am
Location: Brighton, United Kingdom

Bootloader issue; int 13h succeeds but jump fails

Post by Synon »

I have been writing stage1 of a three-stage bootloader for the past couple of days and for the past two, I've been trying to figure out why nothing happens when I try to jump to stage2.

I set up a stack and do some other setup stuff:
[NASM syntax]

Code: Select all

;==============================================================================;
; _start: do some initialization and then call main
_start:
	.setup:
		cli

		; Zero AX, DS, SS
		xor	ax,	ax
		mov	ds,	ax
		mov	ss,	ax
		; Set up the stack
		mov	ax,	0x9c00
		mov	sp,	ax
		; Store the boot disk
		mov	[bootdisk],	dl

		sti
	.load:
		call	main
		jmp	hang ; Hang the machine should main return
;------------------------------------------------------------------------------;
and then, 1k is fetched from the boot disk (at compile-time, my Makefile creates a 1.44MB .img file):

Code: Select all

; main: find and load stage2
main:
	print	"plxls1: load stage2...", 13, 10, 0

	; Load sectors from disk
	mov	al,	4 ; Read some sectors
	mov	cx,	2  ; Start from sector 2, cylinder 0
	xor	dh,	dh ; Read from head 0
	mov	dl,	[bootdisk]

	; Set up the buffer
	mov	bx,	0x1000
	mov	es,	bx
	xor	bx,	bx ; Data will be loaded at [ES:BX] == 1000h:0000h

	call	readsectors
	print	"plxls1: stage2 loaded, run stage2...", 13, 10, 0
	
	; Far jump to stage2
	jmp	0x1000:0x0000

	ret
;------------------------------------------------------------------------------;
but after the last message is printed ("plxls1: stage2 loaded, run stage2..."), nothing happens.

Full code:
stage1/plxs1.asm:

Code: Select all

;==============================================================================;
; plxls1.asm: main file for stage1; finds and loads stage2                     ;
;==============================================================================;

;==============================================================================;
%ifndef _PLXLS1_ASM
%define _PLXLS1_ASM
;------------------------------------------------------------------------------;
[bits	16]
[org	0x7c00]
jmp	_start
;------------------------------------------------------------------------------;
%include	"rmode/screen.asm"
%include	"rmode/diskio.asm"
;==============================================================================;

;==============================================================================;
; _start: do some initialization and then call main
_start:
	.setup:
		cli

		; Zero AX, DS, SS
		xor	ax,	ax
		mov	ds,	ax
		mov	ss,	ax
		; Set up the stack
		mov	ax,	0x9c00
		mov	sp,	ax
		; Store the boot disk
		mov	[bootdisk],	dl

		sti
	.load:
		call	main
		jmp	hang ; Hang the machine should main return
;------------------------------------------------------------------------------;
; main: find and load stage2
main:
	print	"plxls1: load stage2...", 13, 10, 0

	; Load sectors from disk
	mov	al,	4 ; Read some sectors
	mov	cx,	2  ; Start from sector 2, cylinder 0
	xor	dh,	dh ; Read from head 0
	mov	dl,	[bootdisk]

	; Set up the buffer
	mov	bx,	0x1000
	mov	es,	bx
	xor	bx,	bx ; Data will be loaded at [ES:BX] == 1000h:0000h

	call	readsectors
	print	"plxls1: stage2 loaded, run stage2...", 13, 10, 0
	
	; Far jump to stage2
	jmp	0x1000:0x0000

	ret
;------------------------------------------------------------------------------;
; hang: hang the machine indefinitely
hang:
	cli
	.halt:
		hlt
		jmp	.halt
;==============================================================================;

;==============================================================================;
bootdisk:		db	0x00
times	510 - ($ - $$)	db	0x00
bootsignature:		dw	0xaa55
;==============================================================================;

%endif ; ! _PLXLS1_ASM
include/rmode/screen.asm:

Code: Select all

;==============================================================================;
; screen.asm: access to screen-related functions such as putchar               ;
;==============================================================================;

;==============================================================================;
%ifndef	_SCREEN_ASM
%define	_SCREEN_ASM
;------------------------------------------------------------------------------;
[bits	16]
;------------------------------------------------------------------------------;
%macro	print	1+
	jmp	%%do_print
	%%str:
		db	%1,	0
	%%do_print:
		mov	si,	word %%str
		call	putstr
%endmacro
;==============================================================================;

;==============================================================================;
;------------------------------------------------------------------------------;
; putchar: put a character on the screen
; @AL: the character to print
putchar:
	mov	ah,	0x0E
	int	0x10
	ret
;------------------------------------------------------------------------------;
; putstr: print a NULL-terminated ASCII string to the screen. The string should
; be pointed to by SI
putstr:
	.next_char:
		; Load the character
		lodsb

		; Check it to see if it's NUL (EOL marker)
		cmp	al,	0
		je	.done ; AL == 0: done

		; Print the char if AL != 0
		call	putchar
		jmp	.next_char
	.done:
		ret
;==============================================================================;

%endif ; ! _SCREEN_ASM
include/rmode/diskio.asm:

Code: Select all

;==============================================================================;
; diskio.asm: access to the disk through the BIOS                              ;
;==============================================================================;

;==============================================================================;
%ifndef	_DISKIO_ASM
%define	_DISKIO_ASM
;------------------------------------------------------------------------------;
[bits	16]
;==============================================================================;

;==============================================================================;
; readsectors: read sectors from disk
; @AL: sector count
; @CH: starting track
; @CL: starting sector
; @DH: head/side no.
; @DL: drive no.
; @ES:BX: buffer
; Returns 0 in AL on success, or 1 on error
;
; NOTE: Due to some BIOS' limitations, we have to read one sector at a time.
readsectors:
	mov	[count],	al


	.read:
		call	resetdisk
		mov	ah,	0x02
		mov	al,	0x01
		int	0x13

		jnc	.check ; CF is 0 if the read succeeded
	
		print	"disk read error", 13, 10, 0
		jmp	.read ; CF is not 0: error; try again without changing count

		.check:
			dec	byte [count]
			cmp	byte [count],	0
			je 	.done ; count = 0: no more sectors left
			jmp	.read ; read more secotrs

	.done:
		ret
;------------------------------------------------------------------------------;
; resetdisk: reset a disk
; @DL: drive no.
; Returns 0 in AL on success, or 1 on error
resetdisk:
	xor	ah,	ah
	int	0x13
	jnc	.done ; CF is 0 if the read succeeded
	
	print	"disk reset error", 13, 10, 0
	jmp	resetdisk

	.done:
		ret
;==============================================================================;

;==============================================================================;
count:	db	0
;==============================================================================;

%endif ; ! _DISKIO_ASM
stage2/main.c:

Code: Select all

int main()
{
        asm("cli");

        char* buffer = "Hello, world!",
            * vmem   = (char*)0xB8000;
        
        while (*buffer) {
                *vmem++ = *buffer++;
                *vmem++ = 0x07;
        }
        
        for (;;)
                asm("hlt");

        return 0;
}
I'm not sure why "Hello, world!" is not printed to the screen. I've tried a lot of different things like changing parameters to readsectors and things, but nothing has worked. If you need to to see anything else, it's yours, my friend; as long as you have enough rubies.

[I think this goes here; it's related to OS development, but it isn't an OS.]
Gigasoft
Member
Member
Posts: 855
Joined: Sat Nov 21, 2009 5:11 pm

Re: Bootloader issue; int 13h succeeds but jump fails

Post by Gigasoft »

The main program must be 16-bit and it must be a flat binary based at offset 0.

char *vmem = (char*)0xB8000; will only work in a 32 bit program. In a 16 bit program, it should be char far*vmem=(char far*)0xb8000000L;
Synon
Member
Member
Posts: 169
Joined: Sun Sep 06, 2009 3:54 am
Location: Brighton, United Kingdom

Re: Bootloader issue; int 13h succeeds but jump fails

Post by Synon »

Gigasoft wrote:The main program must be 16-bit and it must be a flat binary based at offset 0.

char *vmem = (char*)0xB8000; will only work in a 32 bit program. In a 16 bit program, it should be char far*vmem=(char far*)0xb8000000L;
Oh, of course. I wasn't thinking. I shall try it in assembly. Can you mix 16- and 32-bit code? I would like to have my stage2 load, collect some information from the BIOS and then jump to protected mode. Is it easier to do it like this, or have the stage2 load a third stage (as I originally planned) which then loads the kernel?
Post Reply