Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: kotzkroete am 18. May 2007, 11:20
-
Hi,
ich bin seit ein paar Tagen dabei, mir ein betriebssystem zu schreiben. Aber ich schaffs nicht, in den PMode zu kommen.
org 0x7C00 ; right after some BIOS stuff
start:
cli ; no interrupts
xor ax, ax
mov ds, ax ; set ds
mov es, ax ; and es
mov ax, 0x9000 ; this is our stack address
mov ss, ax
mov sp, 0x400 ; its 512 bytes big
sti ; allow interrupts
; save out bootdrive
mov [bootdrv], dl
; load the kernel
mov ax,0x1000 ; ES:BX = 0x10000
mov es,ax
mov bx, 0
call read_it
call kill_motor
;enter pmode
cli
; enable a20
call kbc
mov al,0xD1 ; write
out 0x64,al
call kbc
mov al,0xDF ; enable s20
out 0x60,al
call kbc
sti
; ok, this should be enabled...let's hope so
;lgdt [gdtr] ; load gdtr pointer
;mov eax,cr0 ; finally enter pmode
;or al,1
;mov cr0,eax
;jmp codesel:Pmode
;[BITS 32]
;Pmode:
;mov eax,datasel
;mov ds,eax
;mov ds,eax
;mov es,eax
;mov fs,eax
;mov gs,eax
;mov ss,eax
;mov esp,0x90000
mov si,loadmsg
putstr: ; print a string
lodsb ; load character
or al,al
jz short putstre ; if 0 >> end of string
mov ah,0x0E ; 0x0E
mov bx,0x0007 ; attribute
int 0x10 ; call the function
jmp putstr ; next character
putstre:
; jump to the kernel
;jmp codesel:0x100000
mov ax, 0x1000 ; the address of our kernel
mov es, ax ; update registers
mov ds, ax
push ax
mov ax, 0
push ax
retf
;--------------------------------------------
;--------------------------------------------
bootdrv db 0 ; bootdrive
loadmsg db "Loading the kernel...",13,10,0
; load the kernel (second sector)
read_it:
reset: ; floppydrive reset (Interrupt 13h, 0)
push ds ; save ds
mov ax, 0 ; reset
mov dl, [bootdrv] ; our bootdrive
int 13h
pop ds ; recover ds
jc reset ; try again if it didn't work
; read sector
read_sec:
mov ah, 2 ; read
mov al, 5 ; read 5 sectors
mov ch, 0 ; cylinder = 0
mov cl, 2 ; sector = 2
mov dx, 0 ; head=0, drive=0
int 13h
jc read_sec ; try again if it didn't work
retn
kill_motor: ; turns off the floppy drive's motor
push dx
mov dx,0x3f2
mov al,0x0c
out dx, al
pop dx
ret
kbc:
xor al, al ; set al to 0
in al, 0x64 ; get kbd status through input port 0x64
test al, 2 ; check if second bit is clear
jnz kbc
ret
; the GDT table
gdtr: ; desctiptortable
dw gdt_end-gdt-1 ; Limit
dd gdt ; baseaddress
gdt:
dd 0,0 ; 0 descriptor
codesel equ $-gdt
dw 0xFFFF ; segmentgoesse
dw 0x0000 ; Segmentadresse 0..15
db 0x00 ; Segmentadresse 16..23
db 0x9A ; Zugriffsberechtigung und Typ
db 0xCF ; Zusatzinformationen und Segmentgröße 16...19
db 0x00 ; Segmentadresse 24..31
datasel equ $-gdt
dw 0xFFFF ; segmentgroesse
dw 0x0000 ; Segmentadresse 0..15
db 0x00 ; Segmentadresse 16..23
db 0x92 ; Zugriffsberechtigung und Typ
db 0xCF ; Zusatzinformationen und Segmentgröße 16...19
db 0x00 ; Segmentadresse 24..31
gdt_end
times 512-($-$$)-2 db 0 ; to make sure the first sector is full
dw 0AA55h ; Bootsignature
So siehts aus. Und so geht es auch, aber nur weil ich nicht in den pmode gehe.
Den Code hab ich fast 1:1 aus der Ausgabe 7 genommen. Aber irgendwie geht es nicht. Ich hab das Gefuehl, dass dann irgendwas mit den Adressen falsch ist, aber ich weiss nicht, was.
Kleine Anmerkung:
An einer Stelle geb ich einen String aus. Wenn ich in den Pmode gehe, dann wird der nur ausgegeben, wenn er nach dem code steht, wo ich das bootlaufwerk speichere. Sprich: bei ";load the kernel" muss er sich irgendwie aufhaengen. Aber wo denn? Und warum haengt er da nur, wenn ich spaeter ich den pmode wechsel? Das weiss er doch noch nicht an dieser Stelle.
Hm...ich hoffe mal, jemand kann mir helfen.
Edit: Also ich hab jetzt gemerkt, dass er nicht haengt sondern rebootet. Im qemu sah's halt anders aus. Hm....ich verstehs nicht.
-
Was den String betrifft: Du machst deine Textausgabe über int 10h. Das geht im PMode nicht, du mußt direkt in den Videospeicher schreiben (ab 0xB8000).
-
Achso...ja...den PMode hab ich leider noch nicht so ganz verstanden....argh....
aber ist das dann der Grund, warum er einfach verreckt?
Leider schaff ich es auch nicht mir einen eigenen Interrupt zu bauen :/ Die code snippets in irgendeiner Ausgabe funktionieren naemlich nicht.
Edit: Wie macht denn eigentlich dann noch irgendwas, wenn man keine interrupts mehr benutzen kann? oder ist das nur der 0x10, der nicht mehr geht?
Edit2: Soll ich das mit dem Pmode nicht vielleicht einfach sein lassen? Das ist viel zu kompliziert.
-
aber ist das dann der Grund, warum er einfach verreckt?
Jo, Interrupts ohne IDT führen zum Tripple Fault und das heißt reboot.
Edit: Wie macht denn eigentlich dann noch irgendwas, wenn man keine interrupts mehr benutzen kann? oder ist das nur der 0x10, der nicht mehr geht?
Du musst selber direkt mit der Hardware "sprechen", zB bei 0xB8000 ist der VideoRAM (zumindest im Textmodues). Die meiste andere Hardware verbirgt sich aber hinter den I/O Adressen (ansprechbar über in/out).
Edit2: Soll ich das mit dem Pmode nicht vielleicht einfach sein lassen? Das ist viel zu kompliziert.
Im realmode wirst du nicht viel Spaß haben.
-
Och noe....
Das muss aber auch kompliziert sein.
Jo, Interrupts ohne IDT führen zum Tripple Fault und das heißt reboot.
Heisst das, dass ich mit IDT interrupts ausfuehren kann?
Im realmode wirst du nicht viel Spaß haben.
Na aber im Pmode hab ich ja noch weniger Spass....
-
Im 32Bit-PMode sehen die Instructions anders aus, daher kannst du im PM keinen 16-Bit Realmode Code mehr ausführen.
-
Jo, Interrupts ohne IDT führen zum Tripple Fault und das heißt reboot.
Heisst das, dass ich mit IDT interrupts ausfuehren kann?
Ja, aber nicht deine heißgeliebten BIOS Interrupts. Das kannst du nur mit einer noch um einigeres komplexeren Lösung hinbekommen (virtual-8086-mode), aber glaub mir, das ist auch nicht wirklich wünschenswert.
Im realmode wirst du wegen der 640Kilobyte maximal benutzbaren RAMs keinen Spaß haben und weil du wenn du dann eh in den pmode wechselst nichts von deinem Code wiederverwenden kannst.
-
Aber so weiss ich ja nichtmal, wie ich ein Zeichen schreiben oder lesen kann...
schreiben weiss ich ein bisschen, aber lesen z.B. garnicht.
Und wie lese ich denn Daten vom floppy, wenn mir 0x13 fehlt? Muss ich das alles ueber in/out machen?
-
Aber so weiss ich ja nichtmal, wie ich ein Zeichen schreiben oder lesen kann...
schreiben weiss ich ein bisschen, aber lesen z.B. garnicht.
Mit einem Tastaturtreiber. ;)
Und wie lese ich denn Daten vom floppy, wenn mir 0x13 fehlt? Muss ich das alles ueber in/out machen?
Genau, so ist es. Ein OS ist halt nichts, was man mal an einem Nachmittag kurz runterschreibt.
-
Argh....und muss ich das jetzt alles selber rausfinden, oder gibsts da vielleicht gewisse anlaufstellen?
-
*taljethzustimm*
aber es gibt genug tutorials, die das erklären...
kannst dich mal unter http://www.osdeveloper.net.tc/ anmelden und dir das buch (kostenlos) zuschicken lassen (als pdf)
PS: sorry an alle, die den autor des buches "nicht mögen", aber es ist einfach gut gemacht^^
-
Ich habe nichts gegen Toaster, aber das Buch ist höchstens als Nachschlagewerk für die Tabellen geeignet, wenn man eh schon weiß, was man machen muß.
Ich würde dir empfehlen, GRUB zu benutzen, dann bist du das ganze Realmode-Zeug, das du später eh nicht mehr brauchst, schonmal los. Eine PMode-Textausgabefunktion wäre noch nützlich, bevor du dann erstmal eine Weile damit beschäftigt bist, eine funktionierende GDT und IDT aufzusetzen und einen Interrupt zu überleben. ;)
Bevor du Interrupts hast, macht alles andere keinen Sinn.
-
Oder Geh mal auf http://www.osdever.net/, dort gibt es viele Tutorials, wenn auch in English. Zur Einführung würde ich dir Bran's Kernel Development Tutorial (http://www.osdever.net/bkerndev/index.php) raten, dort wird ein kleiner Kernel bis hin zur Tastatureingabe entwickelt und gut erklärt.
Und warum der PMode hier nicht funktioniert... Das Problem hatte ich mal. Also, zuerst würde ich dir raten, den PMode nach dem laden des Kernels an eine Speicherstelle einzuschalten, also eigentlich vor dem Sprung.
Dann musst du bedenken, dass du bei aktiviertem PMode im 32-Bit modus bist. Also muss ein Jmp auch eine physische Adresse erhalten.
Mit anderen Worten, nicht
jmp Pmode
Pmode:
sondern:
jmp dword Pmode
Pmode:
Der Thread sollte noch irgendwo sein, find den aber nicht mehr.
Auch will ich dir eher raten, GRUB zu benutzen, macht hier fast jeder, und es gibt noch andere Vorteile.
-
Hm....aber die schreiben alle in C.
Eigentlich war mein Grund fuer die ganze Sachen mit Betriebssystemen, assembler zu lernen.
Dann muss ich ja ganz viel umdenken.
Naja...vielleicht ueberleg ich mir auch nochmal, ein OS zu programmieren. Der Pmode scheint mir etwas zu kompliziert zu sein.
-
Du kannst dein OS auch in Assembler schreiben, aber zum Assembler lernen ist imho ein OS nicht das richtige Projekt. Du wirst sehr schnell feststellen, das du mit Assembler nicht produktiv arbeiten kannst. Zumindest war es meine Erfahrung, dass ich ab X Assemblerzeilen keine Ahnung mehr hab wo überhaupt welche lokale Variable steckt und was in Register sein sollte... :roll: Aber wie gesagt, für einen Assemblerprofi - der in Assembler denkt - mag das eine triviale Aufgabe sein.
Vllt. wäre es auch für dich geschickter Anwendungssoftware oder Treiber für ein bestehendes OS zu schreiben.
-
Nee, nee...das mit den Betriebssystemen find ich jetzt toll...
Aber Frustration gehoert sehr dazu.
Ich braeuchte nur mal einen (einfachen) bootloader, der:
in den pmode geht
a20 enabled
und dann zum kernel springt
(und den ganzen Kram, den der bootloader sowieso koennen muss)
GRUB will ich nicht nehmen, weil ich von dem den code vermutlich nicht verstehen werde.
Da haeng ich momentan noch am meisten. Ich komm zwar in den Pmode (obwohl ich mir da auch nicht ganz siche bin), aber dann komm ich nicht weiter. Ich hab keine Ahnung, wie ich dann zum Kernel komme.
-
GRUB will ich nicht nehmen, weil ich von dem den code vermutlich nicht verstehen werde.
Du musst den grub code nicht verstehen, es ist nämlich sehr genau festgelegt, was (http://www.gnu.org/software/grub/manual/multiboot/multiboot.html) grub alles für dich macht und was nicht.
-
Ich will den aber verstehen. Ich will doch wissen, wie der Bootloader funktioniert. Leider bin ich wieder einer der einzigen die so denken.
-
Ich will den aber verstehen. Ich will doch wissen, wie der Bootloader funktioniert. Leider bin ich wieder einer der einzigen die so denken.
Hehe, ich progge mein OS-Loader auch selber. Da der meiner Meinung nach zum OS zählt (sowie die libs) und ich ein eigenes OS proggen möchte. *duck*
bitmaster
-
Ich will den aber verstehen. Ich will doch wissen, wie der Bootloader funktioniert. Leider bin ich wieder einer der einzigen die so denken.
Ich wollte damit ja auch nur ausdrücken, dass das Verständnis jeder Codezeile und das Verständnis des Zusammenspiels der Codezeilen keine zwingende Voraussetzung zur Benutzung sind. Zum anderen wollte ich dir die sinnlose Folter eines eigenen Bootloaders ersparen. Ich mein dann hast du einen der auf Dateisystem XY und Datenträger Z funktioniert, aber sonst auch nirgendwo, klasse :wink:
bitmasters post ignoriere ich einfach mal, sonst würde das wiedermal in einen Flamewar ausarten. Aber mit solchen Kommentaren die grade dazu auffordern kannst du dich nie zurückhalten oder wie bitmaster?
-
Also ich stimme da bitmaster irgendwie zu, aber es ist ja auch erstmal egal.
Ich hab jetzt einen bootloader gefunden (und auch groesstenteils verstanden, ich muss mir trotzdem nochmal einen selbst bauen), aber der funktioniert noch nicht ganz. [bits 16]
[org 0x7C00]
jmp boot
;-------------Data
;-------------GDT Table
GDTR:
GDTsize DW GDT_END-GDT-1
GDTbase DD 0x500
GDT:
NULL_SEL EQU $-GDT ; null descriptor is required (64bit per entry)
DD 0x0
DD 0x0
CODESEL EQU $-GDT ; 4GB Flat Code at 0x0 with max 0xFFFFF limit
DW 0xFFFF ; Limit(2):0xFFFF
DW 0x0 ; Base(3)
DB 0x0 ; Base(2)
DB 0x9A ; Type: present,ring0,code,exec/read/accessed (10011000)
DB 0xCF ; Limit(1):0xF | Flags:4Kb inc,32bit (11001111)
DB 0x0 ; Base(1)
DATASEL EQU $-GDT ; 4GB Flat Data at 0x0 with max 0xFFFFF limit
DW 0xFFFF ; Limit(2):0xFFFF
DW 0x0 ; Base(3)
DB 0x0 ; Base(2)
DB 0x92 ; Type: present,ring0,data/stack,read/write (10010010)
DB 0xCF ; Limit(1):0xF | Flags:4Kb inc,32bit (11001111)
DB 0x0 ; Base(1)
GDT_END:
;-------------GDT Table End
;-------------Variables
start_s DB 0 ; starting sector [0x1-0x12]
total DB 0 ; number of sector [max 2880]
track DB 0 ; track number [max 160]
head DB 0 ; head number [max 2]
drive DB 0 ; boot drive number [usually 0]
bseg DB 0 ; memory address segment
boff DW 0 ; and offset to load into
loadmsg db "bootsector loaded",13,10,0
;-------------Variables End
;-------------Functions
print: ; print string
lodsb ; load character
or al, al ; check if 0
jz short print_e
mov ah, 0x0e ; print
mov bx, 0x0007
int 0x10
jmp print ; next char
print_e:
ret
wkc:
xor al, al ; set al to 0
in al, 0x64 ; get kbd status through input port 0x64
test al, 2 ; check if second bit is clear
jnz wkc
ret
wkf:
xor cx, cx
in al, 0x64 ; get kbd status
test al, 1 ; check if first bit is clear
jz wkf
ret
halt: ; halt on error
mov byte [gs:0], al
mov byte [gs:1], 0x04
cli
hlt
;-------------Funtions End
;-------------Data End
boot:
mov [drive], dl
cli ; no interrupts
mov ax, cs
mov ds, ax
mov es, ax
mov fs, ax
mov ax, 0x1d0 ; stack address
mov ss, ax ; align stack
mov sp, 0x200 ; 512 byte stack
mov ax,0xb800 ; setup video segment
mov gs,ax
sti ; enable interrupts
jmp init
init:
; enable a20
cli
call wkc ; wait for kbd buffer to clear
mov al, 0xd1 ; write 0xd1
out 0x64, al ; to status
call wkc
mov al, 0xdf ; set settings in kdb (to enable a20)
out 0x60, al ; send the value
call wkc
mov cx, 0x10
kbdwait:
xor ax, ax ; do something
out 0xe0, ax ; more useless stuff
loop kbdwait
; is a20 enabled?
mov al, 0xd0
out 0x64, al
call wkf
in al, 0x60
test al, 2
jnz a20on
mov al, 'A'
call halt
a20on:
sti
; load kernel.bin from floppy at to 0x100000
read:
xor ax, ax ; Floppt reset BIOS function
mov dl, [drive] ; select floppy that was booted from
int 0x13
jc read
mov ax, 0xffff
mov es, ax ; data buffer for file
mov bx, 0x10 ; start of segment
mov ah, 2 ; BIOS function
mov al, 17 ; number of sector to read
mov ch, 1 ; track
mov cl, 1 ; sector
mov dh, 1 ; head
int 0x13 ; call BIOS read dist function
mov dx, 0x3f2 ; stop the motor
mov al, 0x0c ; from spinning
out dx, al
; move GDT to 0x500
xor ax, ax
mov ds, ax
mov es, ax
mov si, GDT ; Move from [ds:si]
mov di, [GDTbase] ; Move to [es:di]
mov cx, [GDTsize] ; size of GDT
cld ; clear the direction flag
rep movsb ; move it
; enter pmode
cli
mov eax, cr0
or al, 1
mov cr0, eax
; load gdt
lgdt[GDTR]
; clear cs/ip/eip
jmp CODESEL:FLUSH
[BITS 32]
FLUSH:
; refresh all segment registers
mov eax, DATASEL
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
mov ss, eax
mov esp, 0xffff
mov eax, 0xb8000
mov byte [eax], 'b'
inc eax
mov byte [eax], 7
;jmp $
jmp CODESEL:0x100000
; stop here
;hlt
times 512-($-$$)-2 db 0
dw 0x0AA55
So sieht er aus.
Fast am Ende gebe ich 'b' aus. Aber wie springe ich jetzt in den Kernel? Der ist also genau nach dem bootsector.
Wenn mir das jetzt noch jemand sagt, hab ich endlich wieder ein Erfolgserlebnis und ich kann weitermachen.
Edit: Also ich hab irgendwie noch ein bisschen rumgespielt...es geht jetzt...Jetzt faengt die Arbeit an.
-
Argh....sobald ich den Bootloader aufm echten Computer ausfuehre, schreibt er in die obere linke ecke ein rotes A. Wenn ich mal sich den quellcode anguckt, findet man das an der Stelle, wo a20 enabled werden soll. Ist das wirklich dieses A? Und warum gunktionierts unter qemu problemlos?
-
zum enablen vom a20-gate gibt es tausende möglichkeiten und kein funktioniert wirklich auf jedem pc, deshalb rate ich dir mehrer methoden in deinen bootloader einzubauen und die dann da reinach aus zu probieren bis es geht.
weiß leider grad nicht genau wie das ging aber es geht
mit
BIOS-Int(noch bist du ja im RM und kanst die benutzen)
und Auf mehrer arten mit dem KBC
-
Aha...na ich hab noch ein paar andere bootloader....mal sehen, wie die das geloest haben. Danke....
Edit: Hab einfach mal das call halt rausgenommenl...jetzt gehts zwar, aber ob der a20 an is? Ich bin mir nicht sicher. Ich glaube, der ist an, aber code zur ueberfruefung stimmt nicht ganz.
-
AFAIK funktioniert die Methode über den Keyboard Controller auf jedem PC.
-
Ja...vermutlich geht die ueberpruefung nur nicht....naja....ich hab jetzt mein naechsted Problem:
undzwar will ich strings ausgeben. Das wuerde ich so machen: string deklarieren, zeichen fuer zeichen ausgeben. Aber das geht nicht.
mov al, [hello]
muesste eigentlich das erste Zeichen aus dem string speichern, oder?
Macht es aber nicht. Nichts wird augegeben.
hello db "hello, world\n", 0
damit deklariere ich den string. Aber wieso wird er nicht ausgegben?
Wenn ich ein Zeichen "einfach so" nach al schreibe und die Funktion aufrufe, wird es perfekt ausgegeben. Aber wieso nicht eins aus einem String?
-
Probiere mal [hello+1]. Da hello eine konstante Adresse hat, müsste es klappen. Ansonsten halt ein INC AL einfügen.
Um einen String auszugeben, musst du dann natürlich in einer Schleife zeichenweise ausgeben; eine Stringausgabefunktion gibt es nicht.
Auch, wenn es dir vielleicht nicht gefällt oder anstößig erscheint... ich habe vor kurzem den Sourcecode von DOS 3.3 gefunden. Der ist vollständig in Assembler (MASM-Syntax) und sehr ausführlich kommentiert. Dafür läuft es ausschließlich im Real Mode. Vielleicht fängst du damit an.
Gruß,
Svenska
-
Und wie genau hilft der DOS-Source mit dem Protected Mode? Mal abgesehen davon, daß es meines Wissens nicht nur anstößig, sondern auch illegal ist...
-
Mit dem PM direkt nicht. Eher mit dem Grundverständnis.
Und das es gegen das (c) von MS verstößt, weiß ich selber... ich behaupte aber, dass es einfacher ist, diesen Code zu verstehen als den Code von Linux 0.02. Aber das muss jeder mit sich selbst ausmachen, was ihm wichtiger ist. Es ist ein Vorschlag gewesen.