Page 1 of 2
Protected mode problem
Posted: Mon Apr 02, 2007 6:10 pm
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...
Posted: Mon Apr 02, 2007 11:17 pm
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).
Re: Protected mode problem
Posted: Tue Apr 03, 2007 12:09 am
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.
Posted: Tue Apr 03, 2007 12:32 am
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?
Posted: Tue Apr 03, 2007 1:06 am
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
Posted: Tue Apr 03, 2007 1:44 am
by Nenad
OK, I suggest we stop discussing about my lazyness to hit the intel manual and completely review the GDT and lgdt sections...
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
Posted: Tue Apr 03, 2007 2:25 am
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.
Posted: Sun Apr 08, 2007 6:43 pm
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...
Posted: Mon Apr 09, 2007 3:33 am
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
Posted: Mon Apr 09, 2007 8:49 am
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
Posted: Mon Apr 09, 2007 9:44 am
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.
Posted: Mon Apr 09, 2007 10:08 am
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...
Posted: Mon Apr 09, 2007 10:33 am
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
Posted: Mon Apr 09, 2007 10:41 am
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
Posted: Mon Apr 09, 2007 11:09 am
by Nenad
Thanks, will give it a go!