Protected mode problem

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.
Nenad
Posts: 12
Joined: Fri Mar 23, 2007 7:01 pm

Protected mode problem

Post by Nenad »

Hello!

I'm having lots of begginer trouble with getting into Pmode...

My bootloader is loading my kernel at 0x0100.
This is my simple kernel code that is supposed to boost me into pmode just like "that"...

Code: Select all

[BITS 16]
[global start]
[extern _kernel]

start:
    cli

    mov ax, 0x0100      ;location where kernel is loaded
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    
    mov ax, 0x0100
    mov ss, ax          ;stack segment
    mov sp, 0x009C0      ;stack pointer

    sti

jmp begin

;=========================================
begin:


;-------------------- PMODE -------------------


cli

  xor ebx,ebx             ; now in real mode (16-bit)
	mov bx,cs
	shl ebx,4
	mov eax,ebx		; EAX=EBX=CS<<4
	lea eax,[ebx]
	mov [gdt2 + 2],ax	; set descriptor base address=EAX
	mov [gdt3 + 2],ax
	shr eax,16
	mov [gdt2 + 4],al
	mov [gdt3 + 4],al
	mov [gdt2 + 7],ah
	mov [gdt3 + 7],ah
	lea eax,[ebx + gdt]	; point gdt_ptr to the gdt
	mov [gdt_ptr + 2],eax	; EAX=linear address of gdt
	;push dword 0		; zero EFLAGS (interrupts off,
	;popfd			;  IOPL=0, NT bit=0)
	o32 lgdt [gdt_ptr]
	mov eax,cr0
	or al,1
	mov cr0,eax
	jmp SYS_CODE_SEL:do_pm
[BITS 32]
do_pm:  
	mov ax,SYS_DATA_SEL
	mov ds,eax
	mov ss,eax
        nop
	mov es,eax
	mov fs,eax
	mov gs,eax
	xor eax,eax
	mov ax,sp
	mov esp,eax

;===========================================================

sti
hlt
call _kernel

cli  ; stop interrupts
hlt ; halt the CPU

;================= DATA ====================

gdt:	dw 0			; limit 15:0
	dw 0			; base 15:0
	db 0			; base 23:16
	db 0			; type
	db 0			; limit 19:16, flags
	db 0			; base 31:24
; linear data segment descriptor
LINEAR_SEL	equ	$-gdt
        dw 0xFFFF               ; limit 0xFFFFF (1 meg? 4 gig?)
	dw 0			; base for this one is always 0
	db 0
	db 0x92			; present,ring 0,data,expand-up,writable
        db 0xCF                 ; page-granular (4 gig limit), 32-bit
	db 0
; code segment descriptor
SYS_CODE_SEL	equ	$-gdt
gdt2:	dw 0xFFFF
	dw 0			; (base gets set above)
	db 0
	db 0x9A			; present,ring 0,code,non-conforming,readable
	db 0xCF
	db 0
; data segment descriptor
SYS_DATA_SEL	equ	$-gdt
gdt3:	dw 0xFFFF
	dw 0			; (base gets set above)
	db 0
	db 0x92			; present,ring 0,data,expand-up,writable
	db 0xCF
	db 0
gdt_end:

gdt_ptr:
	dw gdt_end - gdt - 1	; GDT limit
	dd gdt			; linear, physical address of GDT
But... I am getting triple fault errors as soon as something gets executed after the cr0 has been set to 1 :(

The above is basically not my code, but something I mixed up from a hell of a lot of examples and tutorials. I've stripped out 80% of my kernel code that is not currently needed right now to save space and time, so you now have a GDT descriptor (that I'm not quite completely familiar with) and the code that gets me in Pmode. I'm pretty much stuck right now. What's wrong with "my" code? Where should I focus my attention to?

The "_kernel" is a function in an external C file that is nothing more than a piece of code invoking an endless loop.

Tools I'm using:
NASM, GCC, LD, Virtual PC and Bochs for testing purposes

Hope my question is clear enough...
User avatar
muisei
Member
Member
Posts: 79
Joined: Sat Sep 23, 2006 2:10 pm
Location: Bulgaria
Contact:

Post by muisei »

Wellcome to the forum Nenand.

Remove the 'sti' instruction.Before to enable the interrupts you must set Interrupt Descritor Table Register and you must have interrupt handlers in the Interrupt Descriptor Table(at least for the clock).
User avatar
B.E
Member
Member
Posts: 275
Joined: Sat Oct 21, 2006 5:29 pm
Location: Brisbane Australia
Contact:

Re: Protected mode problem

Post by B.E »

Nenad wrote:The above is basically not my code, but something I mixed up from a hell of a lot of examples and tutorials.
I seguest you read and understand the tutorials that you copied the code from, that's the whole point of tutorials.
Image
Microsoft: "let everyone run after us. We'll just INNOV~1"
Nenad
Posts: 12
Joined: Fri Mar 23, 2007 7:01 pm

Post by Nenad »

I understand the tutorials, otherwise I wouldn't be here with a pmode question... The main problem is the GDT, I get confused when i see that nice GDT table in a tutorial get turned into a lump of asm code that gets "somehow" used by the lgdt command...

Btw... Isn't the IDT optional?
User avatar
crackers
Member
Member
Posts: 27
Joined: Wed Nov 15, 2006 6:31 am

Post by crackers »

Nenad wrote:I understand the tutorials, otherwise I wouldn't be here with a pmode question... The main problem is the GDT, I get confused when i see that nice GDT table in a tutorial get turned into a lump of asm code that gets "somehow" used by the lgdt command...

Btw... Isn't the IDT optional?
Sorry to tell you this but the only thing you can do is learn a little bit about asm if you don't know how this part of code works.
As muisei said you have to delete this sti instruction - your timer is trying to access interrupt descriptor which does not exist.
IDT is not neseccary if you don't want to use interrupts but sooner or later you will need it.
I think it could be a good idea to call your kernel function before executing hlt instruction :wink:
Nenad
Posts: 12
Joined: Fri Mar 23, 2007 7:01 pm

Post by Nenad »

OK, I suggest we stop discussing about my lazyness to hit the intel manual and completely review the GDT and lgdt sections... :oops:

So, I should setup an IDT? Alright...
I think it could be a good idea to call your kernel function before executing hlt instruction
Yeah, I know ;)
That's the good ol' CLI + HLT debug combination (with the CLI missing right now ;) ) to see where my code is crashing (I thought that the part that where I was calling _kernel had the problem, but I was wrong, tre triple fault happens before that).

Just to let you know, I'm not at home right now, so I'll try to implement your suggestions as soon as I get there :)
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Post by Combuster »

This code looks like it has been taken from the DOS protected mode tutorials. You should be aware of the fact that a bootloader does not run under DOS, and that cs is a constant.
Also, when linking with high level languages, you will have to keep in mind that they assume that CS points to the same area as DS, ES and SS.
In short, you picked the tutorial that is bound to give the most trouble later on.

Also, try to familiarize yourself with bochs' debugger.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Nenad
Posts: 12
Joined: Fri Mar 23, 2007 7:01 pm

Post by Nenad »

Nope, nothing... My new code follows:

Code: Select all

[BITS 16]
[global start]
[extern _kernel]

start:
    cli

    mov ax, 0x0100      ;location where kernel is loaded
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    
    mov ax, 0x0100
    mov ss, ax          ;stack segment
    mov sp, 0x09C0      ;stack pointer

    sti

jmp begin

  Int10Handler:            ; Dummy INT 0x10 Handler
	nop
    IRET                   ; Return from the Trap
    
;=========================================
read:
mov ah,00h
int 16h
ret                                        
;=========================================
print:
push ax

.prn:
lodsb
or al,al
jz .end

mov ah,0eh
int 10h
jmp .prn

.end:
pop ax
ret
;=========================================
begin:

mov si,pmode ;print nifty message
call print

call read ;wait for a keypress

;-------------------- PMODE -------------------

        cli                     ; Disable interrupts, we want to be alone

		mov si,pmodenotice
		call print
		
		call read ;wait for a keypress... again
		
		xor ax, ax
        mov ds, ax              ; Set the DS-register to 0 - used by lgdt
        
        lgdt [gdt_desc]         ; Load the GDT descriptor
        lidt [IDT]				; Load the IDT
        
        mov eax, cr0
		or eax, 1
		mov cr0, eax            ; Copy the contents of EAX into CR0
		        
        jmp 08h:clear_pipe      ; (Far) Jump to code segment, offset clear_pipe
		

[BITS 32]                       ; We now need 32-bit instructions
clear_pipe:
        mov eax, gdt_data        ; Save data segment identifyer
        mov ds, eax              ; Move a valid data segment into the data segment register
        mov ss, eax              ; Move a valid data segment into the stack segment register
        mov esp, 090000h        ; Move the stack pointer to 090000h

        hlt ;halt... just for testing purposes, currently, since jumping to clear_pipe makes a big boom...
		sti
;===========================================================


call _kernel

cli  ; stop interrupts if kernel returns for some reason
hlt ; halt the CPU

;================= DATA ====================
pmode db 13,10,13,10,'Switching to protected mode...',13,10,0
pmodenotice db 13,10,'Setting cr0 to enable protected mode...',13,10,0

gdt:                    ; Address for the GDT

gdt_null:               ; Null Segment
        dd 0
        dd 0

gdt_code:               ; Code segment, read/execute, nonconforming
        dw 0FFFFh
        dw 0
        db 0
        db 10011010b
        db 11001111b
        db 0

gdt_data:               ; Data segment, read/write, expand down
        dw 0FFFFh
        dw 0
        db 0
        db 10010010b
        db 11001111b
        db 0

gdt_end:                ; Used to calculate the size of the GDT



gdt_desc:                       ; The GDT descriptor
        dw gdt_end - gdt - 1    ; Limit (size)
        dd gdt                  ; Address of the GDT
        
        
        
  IDT:                     ; Beginning of the IDT
    TIMES   0x10 DD 0 , 0  ; 16 empty descriptors
    DW      0x0000         ; Low order Word of the handler
    DW      0x0008         ; Code segment selector in GDT
    DW      0x8F00         ; Trap Gate
    DW      0x0000         ; High order Word of the handler
  IDT_END:                 ; End of the IDT
  ;------------------------------
  IDTR:                    ; Limits of the IDT
    DW (IDT_END - IDT) - 1 ; Integer multiple of 8 minus 1
    DD IDT                 ; Starting point of the IDT
Added a primitive IDT and changed the GDT (it looks fine to me, as I reviewed it and checked it with the tables in most tutorials), but still I get a triple fault as soon as I do a far jump to "clear_pipe"... I'm assuming "08h:clear_pipe" is a good spot to jump to, since 08h should point to the code segment inside the GDT? Sorry if I seem lost, but I am (there's something pretty obvious I'm missing here, but I can't find out what) :)

If you take a peek at the attachments I posted here, you will see the boobootloader I'm using and the bochs screenshot when the triple fault occurs...
Attachments
bochs2.jpg
bochs2.jpg (111.14 KiB) Viewed 3279 times
bootloader.asm
(10.63 KiB) Downloaded 83 times
Last edited by Nenad on Mon Apr 09, 2007 10:13 am, edited 1 time in total.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Hi,

looks to me like you're trying to load a code segment in to the DS register. Try 0x10 instead of 0x08.

There could be more, but just had a quick glance...

HTH
Adam
Nenad
Posts: 12
Joined: Fri Mar 23, 2007 7:01 pm

Post by Nenad »

Tried 0x10, but I still have the problem...

Bochs debug dump:

Code: Select all

00073225287e[CPU0 ] fetch_raw_descriptor: GDT: index (17)2 > limit (0)
00073225287i[CPU0 ] protected mode
00073225287i[CPU0 ] CS.d_b = 16 bit
00073225287i[CPU0 ] SS.d_b = 16 bit
00073225287i[CPU0 ] | EAX=00000011  EBX=00000007  ECX=0000000d  EDX=00000fff
00073225287i[CPU0 ] | ESP=000009c0  EBP=00000000  ESI=000002cf  EDI=00000005
00073225287i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00073225287i[CPU0 ] | SEG selector     base    limit G D
00073225287i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00073225287i[CPU0 ] |  CS:0100( 1e00| 0|  0) 00001000 0000ffff 0 0
00073225287i[CPU0 ] |  DS:0000( 0000| 0|  0) 00000000 0000ffff 0 0
00073225287i[CPU0 ] |  SS:0100( 0000| 0|  0) 00001000 0000ffff 0 0
00073225287i[CPU0 ] |  ES:0100( 0000| 0|  0) 00001000 0000ffff 0 0
00073225287i[CPU0 ] |  FS:0100( 0000| 0|  0) 00001000 0000ffff 0 0
00073225287i[CPU0 ] |  GS:0100( 0000| 0|  0) 00001000 0000ffff 0 0
00073225287i[CPU0 ] | EIP=0000014b (0000014b)
00073225287i[CPU0 ] | CR0=0x00000011 CR1=0 CR2=0x00000000
00073225287i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00073225287i[CPU0 ] >> jmp far 0010:0150 : EA50011000
00073225287e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown
 status is 00h, resetting
00073225287i[SYS  ] bx_pc_system_c::Reset(SOFTWARE) called
00073225287i[APIC0] local apic in CPU 0 initializing
00073229027i[BIOS ] $Revision: 1.166 $ $Date: 2006/08/11 17:34:12 $
00073543040i[KBD  ] reset-disable command received
User avatar
~
Member
Member
Posts: 1228
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

Post by ~ »

It looks like you are trying to make a "bootloader" and mixing the kernel into it. It isn't even a boot record, because it isn't aligned to 512-bytes to copy in the Master Boot Record, neither it's loaded at absolute physical address 0x7C00 (where all the bootstraps get loaded by the BIOS).

Also, you state that you want to load your kernel at absolute physical address 0x00000100. But, by loading the DS, ES, FS or GS segment registers with 0x100 in Real Mode, you will end up actually pointing to absolute address 0x1000 because the value of the segment register is always shifted 4 bits to the left and then the 16-bit offset into it is added from a general-purpose register.

I'd suggest you to use a true bootstrap and start making a small kernel that does nothing, to begin with, and also, you should make a list of what want your boot loader to do; otherwise, you'll sadly and unavoidably throw the results of a big effort right to the trash can by having done a product build from misunderstud parts.

It could be good to start clearing out what you exactly want your bootloader to do, where exactly load your kernel (forget about segments by now, just a lineal address), and whether you want it to be a 512-byte program for the MBR or a true bootloader. I recommend you to start with the 512-byte program or dedicate a good deal of time making a stand-alone boot loader program.

----------------------------------------------
----------------------------------------------
----------------------------------------------
----------------------------------------------

I have attached a boot disk image along with the source code of the bootstrap (tell if you don't know how to copy it to the MBR of the floppy or how to write this image file using RAWRITE to help out).

This bootstrap truly loads your 32-bit kernel into the address 0x100, and this boot disk image contains a dummy "kernel" (let's call it so) that just prints a "diamond" character in the first space of the text screen and then hangs in an infinite loop.
Attachments
test_img.zip
(5.9 KiB) Downloaded 99 times
Nenad
Posts: 12
Joined: Fri Mar 23, 2007 7:01 pm

Post by Nenad »

I'm not sure if I understand you correctly... The bootloader is located inside the "bootloader.asm" file that is attached in my previous post, and it contains all the code necessary to make it 512b long and writable to the MBR. The bootloader.asm loads the kernel (displayed in text, in my previous post) at 0x0100.

I already tried creating a "OS" in 512 bytes that I wrote into the MBR... It could go into an endless loop displaying some text over and over again while beeping, reset itself, shutdown, hang etc. depending on which key was pressed (0, 1, 2, 3, 4 or 5).

I'm still keeping it just for the sake of it:

Code: Select all

[ORG 0x7C00]
start:
jmp short begin

times 0x3B db 0

end:
mov ax, 0040h
mov ds, ax
jmp 0ffffh:0000h

warm:
int 19h

hang:
mov si,hangout
call print
cli
jmp $

gonuts:
mov si,nuts
call print
jmp gonuts

runx:
mov si,tom
call print
jmp smallshell
;=========================================
print:
push ax
push ds
push si

mov ax,0
mov ds, ax

.prn:
lodsb
or al,al
jz .end

mov ah,0eh
int 10h
jmp .prn
.end:
pop si
pop ds
pop ax
ret
;=========================================
read:
mov ah,00h
int 16h
ret
;=========================================

begin:
mov si,startup
call print
mov si,greeting
call print
mov si,ask
call print

smallshell:
mov si,prompt
call print
call read
cmp al,'0'
je end
cmp al,'1'
je runx
cmp al,'2'
je runx
cmp al,'3'
je gonuts
cmp al,'4'
je warm
cmp al,'5'
je hang
jmp smallshell

startup db 'MyOS v0.1',13,10,0
greeting db 'Blah blah blah',13,10,0
nuts db 'All work and no play makes Johnny a dull boy ',7,0
hangout db 13,10,"I'm stuck in an infinite loop! :)",13,10,0
tom db "Sorry, maybe tomorrow :(",13,10,0
ask db 13,10,'Select operation:',13,10,'0 - Reboot',13,10,'1 - Run operating system',13,10,'2 - Shutdown',13,10,'3 - Go nuts',13,10,'4 - Execute warm boot (reload MyOS)',13,10,'5 - Hang',13,10,0
prompt db 13,10,'>',0
times 512-($-$$)-2 db 0
dw 0AA55h
I got over that phase a while ago, and now I need more than 512b to work with, pmode and all the "thingies" that come along with it :) So, my ultimate goal for now is not only to "go" into pmode, but also to stay in it without triple faulting ;)

Edit: It seems like you have attached something and edited your previous post, so please excuse me if my post doesn't look like a proper response...
Nenad
Posts: 12
Joined: Fri Mar 23, 2007 7:01 pm

Post by Nenad »

Your attached file "test_img.zip" contains only a 950KB sized file called "test_image.img", which is not bootable. No source code though :(
User avatar
~
Member
Member
Posts: 1228
Joined: Tue Mar 06, 2007 11:17 am
Libera.chat IRC: ArcheFire

Post by ~ »

You need RAWWRITE to write it to a standard floppy. That image contains the source to a 512-byte bootstrap:

RAWWRITE: http://uranus.it.swin.edu.au/~jn/linux/rawwrite.htm

A copy to the dummy kernel source (NOT in floppy, and when compiled must be named KERNEL.BIN):

Code: Select all

bits 32
org 100h

mov byte[0xB8000],4
jmp $


And the bootstrap (must be copied to the MBR of the floppy):

Code: Select all

org 7C00h

__MyKernelPlainEntry__ equ 0x100


_jmp: jmp short start
_nop: nop
_OEMid              db    "        "
_bytesPerSect       dw         0200h
_sectsPerClus       db          001h
_resrvedSects       dw         0001h
_numOfFATs          db          002h
_numRootDirEntries  dw         00E0h
_numSectors         dw         0B40h
_mediaType          db          0F0h
_numFATsectors      dw         0009h
_sectorsPerTrack    dw         0012h
_numHeads           dw         0002h
_numHiddenSects     dd     00000000h
_numSectorsHuge     dd     00000000h
_driveNumber        db           00h
_reserved           db           00h
_signature          db           29h
_volumeID           db        "    "
_volumeLabel        db "           "
_FSType             db    "FAT12   "


;INIT: 448 free bytes:
;INIT: 448 free bytes:
;INIT: 448 free bytes:
 ;>   cs = 0
 ;>>  dl = drive we were booted from
 ;INIT: enable protected mode (time 1 out of 2) to load GDT
 ;INIT: enable protected mode (time 1 out of 2) to load GDT
 ;INIT: enable protected mode (time 1 out of 2) to load GDT

  start:  cli                             ;{0}
          lgdt    [cs:GDT]                ;Load GDT.
            mov     ecx, CR0              ;Switch to protected mode
            inc     cx
            mov     CR0, ecx          ;{5}
 ;END:  enable protected mode (time 1 out of 2) to load GDT
 ;END:  enable protected mode (time 1 out of 2) to load GDT
 ;END:  enable protected mode (time 1 out of 2) to load GDT


;;INIT: enable A20
;;INIT: enable A20
;;INIT: enable A20
  ;registers modified in this INIT--END section:
   ;AX (EAX)
     ;registers wich values to reuse in the next INIT--END:
      ;AH (will be 0)

  .5:     in      al, 0x64              ;Enable A20 {4A}.
          test    al, 2     ;see if bit 1 returned from port
                            ;0x64 is 1, which means that
                            ;controller is not ready
          jnz     .5    ;repeat until bit 1 from 0x64 is cleared to 0,
                        ;indicating the keyboard controller is ready

        mov     al, 0xD1    ;*WRITE OUTPUT PORT* command
          out     0x64, al    ;send it

.6:     in      al, 0x64    ;read port 0x64
        and     ax, byte 2   ;see if bit 1 is set to 0
                             ;NOTE: it will leave AH set to 0 at once
                             ;      to be used in the next INIT--END block.
          jnz     .6           ;repeat until keyboard controller is ready
                               ;(bit 1 cleared to 0)


        mov     al, 0xDF    ;configure keyb. controller bit-set
            out     0x60, al   ;send it to data port, to enable A20
;;END:  enable A20
;;END:  enable A20
;;END:  enable A20




 ;;INIT: register/memory parameters configuration
 ;;INIT: register/memory parameters configuration
 ;;INIT: register/memory parameters configuration
  ;registers to modify in this INIT--END section:
   ;AX (EAX) 0
   ;DS 0
   ;ES 0x800
   ;SS 0
   ;SP 0x800

     ;registers with values to reuse in the next INIT--END block:
      ;apparently ALL of the above

 ;>   ah = 0
 ;>   dl = drive we were booted from

        mov     al,Data32
        mov     ds, ax                  ;{2} Extend limit for ds.
        mov     es, ax                  ;Extend limit for es.

        dec     cx                      ;Switch back to and into (Un)Real mode.
	mov	CR0, ecx		;{5}


  ;Here we are in (Un)Real Mode:
  ;Here we are in (Un)Real Mode:
  ;Here we are in (Un)Real Mode:
        mov     [_jmp], dl         ;Save drive number we came from.
        mov     sp, 0x800              ;Configure stack end to 0x800

        xor     eax, eax                ;Segment. 32 bits cleared to 0
        mov     ds, ax                  ;DS (Data Segment) cleared to 0
        mov     ss, ax                  ;SS (Stack Segment) cleared to 0
        mov     es, sp                  ;Read directory at 800:0 {1C}. ES
                                        ;(Extra Segment) 0x800.
 ;;END:  register/memory parameters configuration
 ;;END:  register/memory parameters configuration
 ;;END:  register/memory parameters configuration




 ;;INIT: configure floppy information
 ;;INIT: configure floppy information
 ;;INIT: configure floppy information
  ;>   eax = 00000000

  ;We keep in Unreal Mode from the previous INIT--END:
  ;We keep in Unreal Mode from the previous INIT--END:
  ;We keep in Unreal Mode from the previous INIT--END:
        mov     al, [_numOfFATs]      ;Number of FATs
        mul     byte [_numFATsectors] ;Times size of FAT |(in sectors?)
        add     ax, [_resrvedSects]   ;Plus Sectors before first FAT
                 ;(_numOfFATs*_numFATsectors)+_resrvedSects
					;eax = LBN of Root directory

        movzx   edi,word [_numRootDirEntries] ;Root directory entries
	push	di			; used again later
	dec	di			;Convert to number of sectors

        shr     di, 4               ;16 directory entries per sector
	inc	di

        call    read_sectors    ;call forward
 ;;END:  configure floppy information
 ;;END:  configure floppy information
 ;;END:  configure floppy information



 ;;INIT: look for 8.3 name in the floppy root directory
 ;;INIT: look for 8.3 name in the floppy root directory
 ;;INIT: look for 8.3 name in the floppy root directory
  ;>  eax  = LBN of root directory
  ;>  edi  = length of root directory in sectors
  ;>  [sp] = length of root directory in entries
  ;>  esi  = 00000000

  ;We keep here in Unreal Mode from the 2 previous INIT--END blocks:
  ;We keep here in Unreal Mode from the 2 previous INIT--END blocks:
  ;We keep here in Unreal Mode from the 2 previous INIT--END blocks:
	lea	ebp, [eax+edi]		;ebp = LBN of cluster 2

        pop     bx                      ;Root directory entries.

        xor     di, di               ;Point at directory {1C}
  .20:  mov     si, kernelFile       ;Name of file we want.

	xor	ecx, ecx
        mov     cl, 11       ;bytes in string "KERNEL  BIN"
        a32 rep cmpsb                   ;Found the file?.
                                        ;we use the 11 in CL
	je	found			;Yes
	add	cl, 21			;Offset to next directory entry
	add	edi, ecx		;Advance to next entry
	dec	bx			;Loop through all entries
        jnz     .20


	;Couldn't find file in directory
  boot_error:
  disk_error: 
	mov	ax, 0xE07		;{3}
        int     10h          ;Ring the bell on error...
        jmp short $     ;end with infinite loop
 ;;END:  look for 8.3 name in the floppy root directory
 ;;END:  look for 8.3 name in the floppy root directory
 ;;END:  look for 8.3 name in the floppy root directory



 ;;INIT: read file according what the previous INIT--END found
 ;;INIT: read file according what the previous INIT--END found
 ;;INIT: read file according what the previous INIT--END found
  ;>> ecx   = 00000000
  ;> es     = 800
  ;> es:edi = Directory entry of file
  ;> ebp    = LBN of cluster 2
  ;> eax    = 0000????

  found:  push    word [es:edi+0xF]        ;Starting cluster of file
          mov     di, [_numFATsectors] ;Size of FAT (in sectors)
          mov     ax, [_resrvedSects]  ;LBN of FAT
            call    read_sectors    ;call forward

	mov	bx, 0x4000
	mov	es, bx			;es = 0x4000
	mov	edi, __MyKernelPlainEntry__-0x40000	;{1D}{4B} One megabyte minus ES base
 ;;END:  read file according what the previous INIT--END found
 ;;END:  read file according what the previous INIT--END found
 ;;END:  read file according what the previous INIT--END found




 ;;INIT: final processes and control transfer to kernel entry point
 ;;INIT: final processes and control transfer to kernel entry point
 ;;INIT: final processes and control transfer to kernel entry point
  .10:

  ;>>    ecx = 0000????
  ;>    [sp] = Next cluster of file
  ;>     esi = 0000????
  ;>>    edx = 0000????
  ;>  es:edi = Destination address
  ;>     ebp = LBN of cluster 2
  ;>      ds = 0

	xor	eax, eax
	pop	si			;Next cluster of file
	mov	bx, si
	cmp	si, 0xFF8		;Valid cluster?
	jae	eof			;No: assume end of file
					;Yes: (c-bit set)
	rcr	bx, 1			;bx = 0x8000 + cluster/2
	mov	bx, [bx+si]		;Get word containing FAT entry
	jnc	.11			;Entry is low 12 bits
	shr	bx, 4			;Entry was high 12 bits
  .11:  and     bh, 0xF                 ;Mask to just 12 bits
	push	bx			;Save cluster after next
	push	di			;Save destination address {7}
        mov     al, [_sectsPerClus]     ;Size of each cluster
	mov	di, ax			;  (in sectors)
	dec	si
	dec	si
	mul	esi			;Times cluster number minus 2
	add	eax, ebp		;Plus LBN of cluster 2	
	call	read_sectors		;Read that cluster

  ;>     ecx = 0000????
  ;>>    edx = 0000????
  ;>      di = Clustersize in sectors
  ;>     esi = 0
  ;>>    ebp = LBN of cluster 2
  ;>    [sp] = Bottom 16-bits of destination address {7}
  ;>> [sp+2] = Following cluster
  ;>      ds = 0
  ;>      es = 4000

	mov	cx, di			;Cluster size in sectors
	xchg	ch, cl			;Cluster size in words
	pop	di			;Restore destination address {7}
	es a32 rep movsw
	jmp short .10			;Loop until end of file

  ;>     eax = 0
  ;>     edx = 0000????
  ;>      bx = 0FF?

  eof:
.20:
	int	8			;{8}
	loop	.20

	cli				;{6}

	mov	eax, CR0		;Turn on protected mode
	or	eax, 0x00000001
	mov	CR0, eax
        mov     cl, Data32              ;Setup ds and es
	push	cx			;{5}
	pop	ds
	mov	es, cx
     a32   jmp dword Code32:__MyKernelPlainEntry__   ;Go

 ;;END:  final processes and control transfer to kernel entry point
 ;;END:  final processes and control transfer to kernel entry point
 ;;END:  final processes and control transfer to kernel entry point




 ;;INIT: read_sectors
 ;;INIT: read_sectors
 ;;INIT: read_sectors
  read_sectors:
  ; Input:
  ;       EAX = LBN
  ;       DI  = sector count
  ;       ES = segment
  ; Output:
  ;       EBX high half cleared
  ;       DL = drive #
  ;       EDX high half cleared
  ;       ESI = 0
  ; Clobbered:
  ;       BX, CX, DH

          push    eax
          push    di
          push    es

  .10:    push    eax             ;LBN

          cdq                     ;edx = 0
          movzx   ebx, byte [_sectorsPerTrack]
          div     ebx             ;EAX=track ;EDX=sector-1
          mov     cx, dx          ;CL=sector-1 ;CH=0
          sub     bl, dl          ;BX = max transfer before end of track
          cmp     di, bx          ;Do we want more than that?
          ja      .20             ;Yes, do just this much now
          mov     bx, di          ;No, do it all now
  .20:    mov     esi, ebx        ;Save count for this transfer.

          inc     cx              ;CL=Sector number
          xor     dx, dx
          mov     bl, [_numHeads]
          div     ebx             ;EAX=cylinder ;EDX=head

          mov     dh, dl          ;Head
          mov     dl, [_jmp]  ;Drive number
          xchg    ch, al          ;CH=Low 8 bits of cylinder number; AL=0
          shr     ax, 2           ;AL[6:7]=High two bits of cylinder
          or      cl, al          ;CX = Cylinder and sector

          mov     ax, si          ;Sector count
          mov     ah, 2           ;Read
          xor     bx, bx
          push    ax
          int     13h
          pop     ax
          jnc     .30

          int     13h             ;If at second you don't succeed, give up
          jc near disk_error

  .30:    pop     eax
          add     eax, esi        ;Advance LBN

          push    si
          shl     si, 5
          mov     bx, es
          add     bx, si          ;Advance segment
          mov     es, bx
          pop     si

          sub     di, si
          ja      .10

          pop     es
          pop     di
          pop     eax
          xor     si, si
          ret 
 ;;END:  read_sectors
 ;;END:  read_sectors
 ;;END:  read_sectors


 kernelFile db 'KERNEL  BIN'


 ;INIT: GDT
 ;INIT: GDT
 ;INIT: GDT
   GDT:
   _SELNulo equ 0   ;WARNING: this sector, besides being the GDT pointer,
      GDT_size:     ;         is the NULL selector.
        dw GDTsize
      GDT_actualptr:
        dd GDT
       dw 0x0000

   Code32 equ 8
     dw 0FFFFh       ; bits 0-15 length
     dw 00000h       ; bits 0-15 base addr
     db 0            ; bits 16-23 base addr
     db 10011010b    ; bits P,DPL,DT & type
     db 11001111b    ; bits G,D & bits 16-19 length
     db 0            ; bits 24-31 base addr

   Data32 equ 16
     dw 0FFFFh       ; bits 0-15 length
     dw 00000h       ; bits 0-15 base addr
     db 0            ; bits 16-23 base addr
     db 10010010b    ; bits P,DPL,DT & type
     db 11001111b    ; bits G,D & bits 16-19 length
     db 0            ; bits 24-31 base addr
   GDT_end:

   GDTsize equ (GDT_end-GDT)-1
 ;END:  GDT
 ;END:  GDT
 ;END:  GDT

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

;END:  448 free bytes
;END:  448 free bytes
;END:  448 free bytes


dw 0xAA55
Nenad
Posts: 12
Joined: Fri Mar 23, 2007 7:01 pm

Post by Nenad »

Thanks, will give it a go! :)
Post Reply