Unreal mode loading procedure

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
jtokarchuk
Member
Member
Posts: 26
Joined: Fri Oct 14, 2011 4:32 pm

Unreal mode loading procedure

Post by jtokarchuk »

How do I use fat12 to point to an unreal mode data location? I am using the information found in the Unreal Mode section of the wiki, and have that booting, but I want to use fat12 to find a kernel file, and jump to it at 1MB. Is this possible?

Code: Select all

[BITS 16]
[ORG 0x7C00]
jmp   boot
nop

OEMLabel		db "HARMONY"	; Disk label
BytesPerSector		dw 512		; Bytes per sector
SectorsPerCluster	db 1		; Sectors per cluster
ReservedForBoot		dw 1		; Reserved sectors for boot record
NumberOfFats		db 2		; Number of copies of the FAT
RootDirEntries		dw 224		; Number of entries in root dir
LogicalSectors		dw 2880		; Number of logical sectors
MediumByte		db 0F0h		; Medium descriptor byte
SectorsPerFat		dw 9		; Sectors per FAT
SectorsPerTrack		dw 18		; Sectors per track (36/cylinder)
Sides			dw 2		; Number of sides/heads
HiddenSectors		dd 0		; Number of hidden sectors
LargeSectors		dd 0		; Number of LBA sectors
DriveNo			dw 0		; Drive No: 0
Signature		db 41		; Drive signature: 41 for floppy
VolumeID		dd 00000000h	; Volume ID: any number
VolumeLabel		db "HARMONY OS "; Volume Label: any 11 chars
FileSystem		db "FAT12   "	; File system type: don't change!

wait_kbdbuf:
in   al, 0x64
test al, 0x02
loopnz  wait_kbdbuf
ret

enable_a20:
xor  cx, cx
call wait_kbdbuf
mov  al, 0xD1
out  0x64, al
call wait_kbdbuf
mov  al, 0xDF
out  0x60, al
call wait_kbdbuf
push ds
push es
xor  ax, ax
mov  ds, ax
dec  ax,
mov  es, ax
mov  ax,[es:0x10]
not  ax
push WORD  [0]
mov  [0], ax
mov  ax,[0]
cmp  ax,[es:0x10]
pop  WORD  [0]
pop  es
pop  ds
jz   .enable_a20_failed
ret
.enable_a20_failed:
jmp  reboot

go_unreal:
push ds
lgdt [gdtr]
mov  eax, cr0
or   al,0x01
mov  cr0, eax
mov  bx, 0x08
mov  ds, bx
and  al, 0xFE
mov  cr0, eax
pop  ds
ret

; ------------------------------------------------------------------
; BOOTLOADER SUBROUTINES

reboot:
	mov ax, 0
	int 19h				; Reboot the system

reset_floppy:		; IN: [bootdev] = boot device; OUT: carry set on error
	push ax
	push dx
	mov ax, 0
	mov dl, byte [bootdev]
	stc
	int 13h
	pop dx
	pop ax
	ret

	l2hts:			; Calculate head, track and sector settings for int 13h
			; IN: logical sector in AX, OUT: correct registers for int 13h
	push bx
	push ax
	mov bx, ax			; Save logical sector
	mov dx, 0			; First the sector
	div word [SectorsPerTrack]
	add dl, 01h			; Physical sectors start at 1
	mov cl, dl			; Sectors belong in CL for int 13h
	mov ax, bx
	mov dx, 0			; Now calculate the head
	div word [SectorsPerTrack]
	mov dx, 0
	div word [Sides]
	mov dh, dl			; Head/side
	mov ch, al			; Track
	pop ax
	pop bx
	mov dl, byte [bootdev]		; Set correct device
	ret

; ------------------------------------------------------------------
; STRINGS AND VARIABLES

	kern_filename	db "KERNEL  BIN"	; MikeOS kernel filename
	bootdev		db 0 	; Boot device number
	cluster		dw 0 	; Cluster of the file we want to load
	pointer		dw 0 	; Pointer into Buffer, for loading kernel

;Start of our boot process
boot:
cli
xor  ax, ax
mov  ds, ax
mov  es, ax
mov  ax, 0x9000
mov  ss, ax
mov  sp, 0xFFFF
call enable_a20
call go_unreal


floppy_ok:				
	mov ax, 19			
	call l2hts

	mov si, buffer			
	mov bx, ds
	mov es, bx
	mov bx, si

	mov ah, 2			
	mov al, 14			

	pusha			


read_root_dir:
	popa				; In case registers are altered by int 13h
	pusha

	stc				; A few BIOSes do not set properly on error
	int 13h				; Read sectors using BIOS

	jnc search_dir			; If read went OK, skip ahead
	call reset_floppy		; Otherwise, reset floppy controller and try again
	jnc read_root_dir		; Floppy reset OK?

	jmp reboot			; If not, fatal double error


search_dir:
	popa

	mov ax, ds			; Root dir is now in [buffer]
	mov es, ax			; Set DI to this info
	mov di, buffer

	mov cx, word [RootDirEntries]	; Search all (224) entries
	mov ax, 0			; Searching at offset 0


next_root_entry:
	xchg cx, dx			; We use CX in the inner loop...

	mov si, kern_filename		; Start searching for kernel filename
	mov cx, 11
	rep cmpsb
	je found_file_to_load		; Pointer DI will be at offset 11

	add ax, 32			; Bump searched entries by 1 (32 bytes per entry)

	mov di, buffer			; Point to next entry
	add di, ax

	xchg dx, cx			; Get the original CX back
	loop next_root_entry

	jmp reboot


found_file_to_load:			; Fetch cluster and load FAT into RAM
	mov ax, word [es:di+0Fh]	; Offset 11 + 15 = 26, contains 1st cluster
	mov word [cluster], ax

	mov ax, 1			; Sector 1 = first sector of first FAT
	call l2hts

	mov di, buffer			; ES:BX points to our buffer
	mov bx, di

	mov ah, 2			; int 13h params: read (FAT) sectors
	mov al, 9			; All 9 sectors of 1st FAT

	pusha				; Prepare to enter loop


read_fat:
	popa				; In case registers are altered by int 13h
	pusha

	stc
	int 13h				; Read sectors using the BIOS

	jnc read_fat_ok			; If read went OK, skip ahead
	call reset_floppy		; Otherwise, reset floppy controller and try again
	jnc read_fat			; Floppy reset OK?

	jmp reboot			; Fatal double error


read_fat_ok:
	popa
	mov edi, 0x100000
	mov ax, [ds:edi]			; Segment where we'll load the kernel
	mov es, ax
	mov bx, 0

	mov ah, 2			; int 13h floppy read params
	mov al, 1

	push ax				; Save in case we (or int calls) lose it

load_file_sector:
	mov ax, word [cluster]		; Convert sector to logical
	add ax, 31

	call l2hts			; Make appropriate params for int 13h

	mov ax, [ds:edi]			; Set buffer past what we've already read
	mov es, ax
	mov bx, word [pointer]

	pop ax				; Save in case we (or int calls) lose it
	push ax

	stc
	int 13h

	jnc calculate_next_cluster	; If there's no error...

	call reset_floppy		; Otherwise, reset floppy and retry
	jmp load_file_sector

calculate_next_cluster:
	mov ax, [cluster]
	mov dx, 0
	mov bx, 3
	mul bx
	mov bx, 2
	div bx				; DX = [cluster] mod 2
	mov si, buffer
	add si, ax			; AX = word in FAT for the 12 bit entry
	mov ax, word [ds:si]

	or dx, dx			; If DX = 0 [cluster] is even; if DX = 1 then it's odd

	jz even				; If [cluster] is even, drop last 4 bits of word
					; with next cluster; if odd, drop first 4 bits

odd:
	shr ax, 4			; Shift out first 4 bits (they belong to another entry)
	jmp short next_cluster_cont


even:
	and ax, 0FFFh			; Mask out final 4 bits


next_cluster_cont:
	mov word [cluster], ax		; Store cluster

	cmp ax, 0FF8h			; FF8h = end of file marker in FAT12
	jae end

	add word [pointer], 512		; Increase buffer pointer 1 sector length
	jmp load_file_sector


end:					; We've got the file to load!
	pop ax				; Clean up the stack (AX was pushed earlier)
	mov dl, byte [bootdev]		; Provide kernel with boot device info

	jmp 0000:0x100000	; Jump to entry point of loaded kernel!


gdtr:
dw   gdt_end - gdt_null - 1
dd   gdt_null

gdt_null:
db   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
gdt_data:
db   0xFF, 0xFF, 0x00, 0x00, 0x00, 10010010b, 11001111b, 0x00
gdt_end:

times 510-($-$$) db 0
dw 0xAA55

buffer:	
Here is my current code. Trying to modify a real mode fat12 loader to work. I am sure I am doing it improperly, however. Any help would be appreciated.
theseankelly
Posts: 20
Joined: Sat Oct 22, 2011 4:17 pm

Re: Unreal mode loading procedure

Post by theseankelly »

Hey, I'm still new to this too, but I've successfully done what you're trying to do.

After a very quick look at your code (i have to run somewhere now), I think you have the basic right idea. However, the problem is that even though you CAN access memory above 1mb in unreal mode, bios int13 can't. So what has to happen is you use int13 to load the info somewhere in lower memory, and then use the command movsb (or some variant) to move it up above 1mb. You can also prefix movsb with "rep" which repeats the command the specified number of times.

Info on rep movsb:
http://faydoc.tripod.com/cpu/movsb.htm
jtokarchuk
Member
Member
Posts: 26
Joined: Fri Oct 14, 2011 4:32 pm

Re: Unreal mode loading procedure

Post by jtokarchuk »

Thanks, I will do some research into this. If you don't mind, can you post me your working solution?
theseankelly
Posts: 20
Joined: Sat Oct 22, 2011 4:17 pm

Re: Unreal mode loading procedure

Post by theseankelly »

Sure -- I was planning on doing that when I got back. Which I just did. So here's the relevant subroutine from my bootloader (16 bit assembly in nasm):

Code: Select all

; loadkernel
; loads kernel to 1mb mark (0x100000)
; BIOS can't write above 1mb, so we have to write to 0x1000
; and then move it up to 0x100000 (because we're in unreal mode)
;
; So naturally, this procedure requires that we be in unreal mode before entering.
;
; Returns 0 in ax on success, nonzero on error
loadkernel:
            reset_drive:        ; have to reset the drive
            mov   ah,0
            int   13h
            or    ah,ah 
            jnz   reset_drive   ; if errorcode (ah) is not zero, try again
            mov   ax, 0
            mov   es, ax        ; base addr is zero, we're in real
            mov   ebx, 1000h    ; place to load kernel - has to be in realmem
            mov   ah, 02h       ; command to read
            mov   al, 03h       ; number of sectors to read
            mov   ch, 0         ; cylinder
            mov   cl, 02h       ; sector to start (addr starts at 1)
            mov   dh, 0         ; disk head
                                ; dl = drive, already set by bios
            int   13h           ; do it!
            or    ah, ah        ; check to see if we succeeded
            jz    .succeed
            mov   ax,1          ; error
            jmp  .end
            .succeed:
            mov   esi,00001000h   ; now we move it to 1mb
            mov   edi,00100000h   ; we can do this, because of unreal mode!
            mov   ax,ds           ; moving with a base of ds b/c in real mode
            mov   es,ax
            mov   cx,384           ; 384*4 = 1536 = 3 sectors
            a32 rep movsd         ; 'a32' tells it to use esi/edi instead of si/di
            mov   ax,0              ; success
            .end:
            ret
And the code that switches to pmode: [EDIT] I mean unreal mode [/EDIT]

Code: Select all

      push  ds            ; switch to unreal mode
      mov   eax,cr0       ; because we need to put kernel at 1mb 
      or    al,1          ; basically, switch to pmode 
      mov   cr0, eax
      mov   bx, 08h       ; load a selector 
      mov   ds, bx   
      and   al,0xFE
      mov   cr0,eax       ; switch back to "unreal"
      pop   ds            ; restore segment
Please note that this is sloppy code; it's my first bootloader. I just went in guns blazing to see if I could get things to work. I've learned a good bit since writing this and plan to fix it up, but the general concept is there.

For example, I kind of arbitrarily loaded the kernel at 0x1000 without really considering how much space I have to work with. Also, I arbitrarily load 3 sectors because that's how big my kernel happens to be right now; you should add some smarts somehow to calculate the size of the kernel. Otherwise you'll hit a problem I did when I was loading 2 sectors: Suddenly the kernel image size crept passed the 1024byte mark and nothing worked. Took me a while to realize I wasn't loading the whole thing anymore, and it wasn't a problem with the C code I just added.

And of course, if your kernel eventually grows to be larger than the space you have available in the lower 1mb of memory, you'll have to do some looping to load/move, or implement it another way. My kernel hasn't hit that size yet, so I've ignored that problem for now :) (don't do that. I'm designing for it now that I'm designing a proper two stage loader even though that day is a long way off, because one day you'll add some functionality and your kernel won't run anymore. You'll spend a LONG time figuring out that the problem is in your bootloader rather than that new code that made the kernel JUST too big.)

Sounds like you'd just do the same as what I've done, except you'll have to parse FAT12 in order to find the sector and number of sectors to load, whereas I hard coded them.
Last edited by theseankelly on Tue Oct 25, 2011 4:16 pm, edited 1 time in total.
jtokarchuk
Member
Member
Posts: 26
Joined: Fri Oct 14, 2011 4:32 pm

Re: Unreal mode loading procedure

Post by jtokarchuk »

Excellent, much thanks for the info, I learn more visually.

I will continue to forge on thanks to you.
Post Reply