EINFÜHRUNG:

Als ich den Suchbegriff "switch to real mode" an Google schickte, bekam ich 7,5 million Treffer. Ich konnte aber keine genaue Beschreibung finden, wie das zu tun wäre. Jeder der gefundenen Hinweise war entweder nicht brauchbar, oder nicht bezogen auf die "Architektur bedingten" Details der Intel-CPU, Typ i486, oder sogar falsch. Sogar das dicke und teure Buch (vor 30 Jahren geschrieben), das ich normalerweise als Ausgangspunkt nehme, wenn ich unter die Haube meiner Maschinen gehe, enthält eine teilweise falsche und nicht richtig begründete Beschreibung (ausdrücklich auf Intel-Dokumentation basierend!)
So machte ich selber, was offenbar für Millionen Menschen interessant ist. Und ich beschreibe hier den komplizierten Weg zum ersehnten Zustand "real mode". Ich mache es online lesbar in .html, damit jedermann leicht zur Lektüre kommt und beanspruche kein anderes copyright als GNU. Jedem steht völlig frei, meine Geistesfrüchte zu kosten!
Der nötige Assembler-code wird im NASM-Dialekt und meinem ASMat-Dialekt gezeigt. Beide Dialekte sind unter meinem Assembler Betriebssystem ASMOS brauchbar, das NICHT im page mode, aber im protected mode arbeitet.

Sich mit ASMOS vertraut machen:

  • meine homepage www.rcfriz.de

    ÜBERBLICK:

    Der erste Schritt muss sein, den page mode los zu werden und zurück in den normalen protected mode zu kommen, wo Segment-Deskriptoren und Selektoren, die sie adressieren, zu benutzen sind, um Basis-Adressen zu definieren, unter denen Basis-Adressen =0 von Programmen nutzbar und verschieblich sind
    Dieser Schritt ist mit folgenden zwei Assembler-Kommandos getan:

    mov eax,1 Das PE-bit =Einschalten d.protected mode mit bit1 Das PG-bit =Einschalten d.page mode mit bit31 mov cr0,eax

    Dieser Schritt kann nicht getan werden, wenn ein Programm nicht in einem Segment mit Privileg-Stufe 0 läuft. Und natürlich muss man wissen, wo die "Global Descriptor Table" (="GDT") im Arbeitsspeicher liegt. Das muss eine absolute Adresse sein, die man mit folgenden Kommandos erfährt:

    mov eax,b b=Adresse von Länge und Basis-Adresse der GDT (2*DD) sgdt .eax speichere Deskriptor der GDT in Variable<br>

    Die Variable "b" enthält danach die Länge in bits0-15 und die Basis-Adresse in Bits16-47. Das gleiche Format wird für den IDT-Deskriptor benutzt, wobei die Bits48-31 immer =0 sein müssen. Auch diese Kommandos können nicht ohne die Privilegstufe 0 gegeben werden!
    Diese Schritte müssen unter ASMOS nicht getan werden, wo jedes Programm jedes beliebige Kommando enthalten kann (ausser einigen nicht ursprünglichen i486 Kommandos). Das muss im Hinterkopf gehalten werden, wenn man unten den Beispiel-Code liest!
    Wenn man in Privilegstufe 0 und protected mode ist und weiss wo nicht nur die GDT, sondern auch die IDT liegen, dann kann man weitere Schritte tun, wenn man auch weiss, wie das eigene Betriebssystem den Interrupt-Controler initialisiert hat und wo die zugehörigen Interrupt-Vektoren in der IDT abgelegt wurden.
    Diese sehr wichtige Tabelle ist immer in Gebrauch! Aber die CPU macht nach dem Reset eine Besetzung für den IDT-Deskriptor, die dem originalen AT-Design entspricht: Länge in Bits0-15 =3FFh und Basis-Adresse =0 .Das ist der nicht geänderte Ausgangspunkt für das BIOS.
    Da im protected mode die ersten Interrupt-Vektoren (unterste Adressen und Nummern) von der CPU benutzt werden ("faults",traps" usw.), muss jedes Betriebssystem zuerst nicht nur die Basis-Adresse der IDT ändern, sondern auch die Interrupt-vektoren - insbesondere jene, die der Interrupt-Controler braucht.
    Diese Details muss wissen, wer zurück in den protected mode will, während man den originalen, vom BIOS gemachten Zustand kennen muss, bevor man wirklich in den real mode gelangen kann.
    Bevor man irgendwelchen real-mode-Code in den Speicher unter 1M schreiben kann, muss man alle Daten, die dort liegen, retten. Andernfalls kann man weder in den real mode noch zurück in den protected mode! Meistens wird man neue Deskriptoren für GDT und IDT definieren müssen, die im Bereich unter 1M liegen und ohne neue Basis-Adressen nicht mehr brauchbar sind - die sind zur Programmierzeit nicht bekannt, sondern ergeben sich nur zur Laufzeit.
    Der nächste Schritt besteht in der Definition von einem neuen Deskriptor in der verschobenen GDT - er beschreibt ein 16-Bit-protected-mode-Segment mit der gleichen Basis-Adresse wie das beabsichtigte real-mode-Segment. In meinem Beispiel unten ist dies die absolute Adresse 80000h. Und man muss im im real-mode-Segment noch ein weiteres Segment für den stack definieren!
    Das zu schreibende Binary muss abhängig vom Assembler-Dialekt, den man benutzen will, programmiert werden. Es gibt viele verschiedene Wege, um Binaries aus gemischtem Code für drei verschiedene Zustände der Maschine zu schreiben, die zu durchschreiten sind. Ich werde das unten im Detail erklären...
    Das fertig geschriebene Programm hat schliesslich zur Laufzeit weitere Schritte zu tun, um den Teil, der unterhalb 1M arbeiten soll, fertig zum Gebrauch zu machen. Mindestens die neuen Deskriptoren für GDT und IDT sind in den Code zu schreiben. Und natürlich die Zieladresse für den Rücksprung in das protected-mode-Programm!
    Wenn das zu verschiebende Binary komplettiert ist, kann es ab einer absoluten Adresse unterhalb 1M geschrieben und mit einem FAR-Sprung im 32-Bit-protected-mode gestartet werden. Dieses Kommando hat den neuen Selektor für das 16-Bit-protected-mode-Segment zu enthalten, das zuvor gemacht wurde. Man kann auf keinen Fall mit einem FAR-Sprung in das real-mode-Segment kommen, weil die CPU, nachdem vor dem Sprung der protected mode ausgeschaltet wurde, eine Segment-Adresse im Kommando erwartet. Im protected mode werden Offset-Adressen in 32 bits benutzt, die NICHT dem Format im real mode entsprechen. Das ist der Grund, warum man den 16-Bit-Deskriptor braucht, der einen 16-Bit-Offset erzwingt für den weiteren Sprung in den real mode.
    Im 16-bit-protected-mode-Segment unter 1M hat man vor dem Sprung in den real mode nicht mehr zu tun als den protected mode abzuschalten. Man muss nicht Segment-Register-Inhalte oder irgendeinen Stack umdefinieren. Aber den protected mode innerhalb eines 16-bit-Segmentes abzuschalten, ist ein besonderes Problem, das ich unten im Detail beschreibe (und als Erster).
    Innerhalb des real modes kann man die gut bekannten Schritte tun, um zurück in den protected mode zu kommen (jeder Bootloader enthält sie). Weil aber nur eine eine 16-bit-Offset-Adresse im Sprung-Kommando erlaubt ist, kann man Probleme bekommen beim Rücksprung, deren Lösung ich unten zeige.

    IM DETAIL:

    Meine Aufgabe war ein Programm, das VBE-Funktionen mit Werten in Registern zu rufen erlaubt. So rufe ich in meinem Programm eine Prozedur als wenn ich einen BIOS-int benutzte.
    Da ASMOS GDT, IDT, stack und andere wichtige Dinge zwischen den Adressen 1000h und 9FFFFh stapelt, ist die real-mode-int-Tabelle zwar unberührt, alles andere bis zum Video-Bereich aber zu retten. Das ist leicht getan, weil ASMOS auf absoluter Adresse die Adresse verfügbaren Speichers über 1M enthält. Diese Adresse muss im Programm aufbewahrt werden, während die ASMOS-Variable neu definiert werden muss, weil andere Programmteile freien Platz oberhalb des geretteten Speicherinhalts benötigen können.
    Dieses Beispiel ist im ASMat-Dialekt geschrieben. Da gilt: Präfix ":" leitet einen Namen eines Labels ein, Präfix " " markiert die folgende Zeichenkette als Kommando bis zum ersten " " nach erkanntem Code, Kommentare können getrennt durch " " einem Kommando oder einem Label folgen, oder ohne irgendein Präfix am Zeilenanfang stehen (Keine Kommentarzeichen!), Operanden-Adressen in Kommandos stehen nach Präfix ".", der alberne Klammern ersetzt.

    -----------------------------------anderer Code mov edi,.es:100024h Ziel-Adresse="membegin" in ASMOS ...becomes now base-adress of moved low mem! mov .lowMEM,edi mov esi,0 call P_Move_mem -----------------------------------anderer Code :P_Move_mem call with base-adress of source in esi, of destination in edi mov ecx,28000h Anzahl der zu schreibenden Doppelworte :Move_mem dec ecx mov eax,.es:esi+ecx*4 mov .es:edi+ecx*4,eax jne Move_mem ret

    Zu sehen ist, dass ich die Adress-Kalkulation im protected mode nutze, die C-Programmierern fremd ist und kaum im page-mode nutzbar ist (Man muss absolute Adressen, Basis-Adressen in Deskriptoren und Selektoren in Segment-Registern handhaben können - unter ASMOS ist in "es" normalerweise der Überalles-Selektor mit Basis-Adresse =0 .Ich mache diesen Schritt vor allen anderen unter 1M und restauriere erst am Ende des Programms. So können GDT, IDT und Stack umdefiniert werden mit Adressen in Variablen innerhalb des Programms und dem unter 1M zu versetzenden Binary. Solche Programmierung kann nicht innerhalb des page modes eines Betriebssystems veranstaltet werden!
    Während der Laufzeit dieses Programms benutze ich die verpflanzte GDT, IDT und Stack, nachdem ich noch folgendes getan habe:

    -----------------------------------anderer Code mov eax,.lowMEM add esp,eax neuer stackpointer / selector =overall-selector =8 add eax,1000h abs.Basis-Adresse der normalen GDT in ASMOS mov .gdt1,eax 17-48 DWord aus absoluter Adresse add eax,10000h abs.Basis-Adresse der normalen IDT in ASMOS mov .idt1,eax 17-48 DWord aus absoluter Adresse mov eax,gdt0 erste von 4 Byte-Adressen des Deskriptors lgdt .eax mov eax,idt0 erste von 4 Byte-Adressen des Deskriptors lidt .eax Definiere die Basis von noch freiem Puffer für weitere Aufgaben: add edi,A0000h letztes DD des versetzten Codes =9FFFCh, der Stack mov .es:100024h,edi =neues "membegin" -----------------------------------anderer Code


    Der wie ein BIOS-Int zu rufenden Prozedur steht Code voran, der separat zu übersetzen ist und als Binärcode eingefügt werden muss, bevor dieses Programm übersetzt werden kann. Während das Programm mit ASMat zu übersetzen ist, wird der zu versetzende Code mit ASMnr übersetzt, das nur real-mode-Code übersetzt und den selben Dialekt wie NASM benutzt. Meine Übersetzungsprogramme erlauben keinen gemischten Code (macht sie schneller). Normalerweise braucht man Ausdrücke wie "USE 16" oder "BIN 16" oder irgend ähnliches vor solchem Code. Im ASMat-Dialekt ist das folgende Kommentar, der kopiert, übersetzt und als Binary wieder kopiert und weiter unten als binäre Konstante eingefügt wird. Unter ASMOS sind diese Aktionen sehr einfach zu handhaben.

    _Wir sind zunächst im 16-bit-protected-mode-Segment. _ jmp START _ nop _;Diese Pufferzone speichert Basis-Adressen von GDT und IDT und die vom Rufer _;gegebenen Registerinhalte. Nach Abläufen im real mode können die Register _;umdefiniert sein und ihre neuen Inhalte können hier wieder auf absoluten, _;dem Rufer zugänglichen Adressen gespeichert werden. _RegAX: DW 0 ; Adresse:4 / ...in je 2 Bytes ax,bx,cx,dx _RegBX: DW 0 _RegCX: DW 0 _RegDX: DW 0 _IDT0: DW 0FFFFh ; Adresse:0Ch / IDT im protected mode / 1-16 Länge _IDT1: DW 0 ; Adresse:0Eh / 48-bit-pointer auf IDT im oberen Speicher _IDT2: DW 0 _IDT3: DW 0 ; immer =0 _IDTrm0: DW 003FFh ; Adresse:14h / IDT im real mode / 1-16 Länge _IDTrm1: DW 0 ; Adresse:16h / 48-bit-pointer to IDT in low memory _IDTrm2: DW 0 _IDTrm3: DW 0 ; immer =0 _START: _;Hier sind 16-bit-register-Teile normal, man braucht ein "operand-size-praefix _;=66h um "xor ax,ax" wie "xor eax,eax" wirken zu lassen, was genau anders rum _;im 32-bit-protected-mode geht. Dann fügen die meisten Assembler-Übersetzungs- _;Programme automatisch das operand-size-praefix vor "xor ax,ax" ein, während _;der Opcode der selbe wie bei "xor eax,eax" ist. Das muss interessieren, wenn _;man eine Übersetzung für den real mode hier macht. _;Es gibt keinen Grund hier irgendwelche Segment-Register zu laden, weil wir _;sofort in den real mode wechseln. Wir schalten nur den protected mode (und -;den page mode) ab. _;WARNUNG! _;Das Kommando "lmsw" arbeitet NICHT im 16-bit-protected mode !!!!! _;Da die meisten Übersetzungs-Programme (wie mein ASMnr) für den real mode weder _;das Kommando "mov cr0,eax" übersetzen noch "mov eax,60000000h" mit richtiger _;Länge von 32 Bit für den immediate Wert, definiere ich die Opcodes binär als _;Konstanten (Natürlich müssen solche Werte in der richtigen Abfolge von _;Kommandos stehen!). _Rmop_mov: DW B866h ; operand-size-praefix 66h erzwingt Laden von eax statt _ ; des "normalen" ax im 16-Bit-Pmode. "B8h" is the opcode. _Rmimm_movL: DW 0 ;="mov eax,60000000h" _Rmimm_movH: DW 6000h ; Wert in eax schaltet den cache und das PE-bit aus _Rmop: DB 0FH ; Opcode von "mov cr0,eax" / benutzt alle 32 Bits _ ; unabhängig von der Operanden-Breite! _;Nach diesem Schalten erwartet die CPU eine Segment-Adresse in cs ! _;Da Ich weiss, wo das Segment sein soll, kann ich es vor der Laufzeit geben _;und die Übersetzung den Rest machen lassen... _ jmp 8000h:Realmode ; real-mode-Segment und KEIN Selektor! _;Der Offset wird in 16 Bits stehen, weil für den real mode übersetzt wird. _;Erst nach Benutzung von Segment-Registern wird tatsächlich von der CPU der _;gewechselte Zustand erkannt! Dann haben alle anderen Register ebenfalls _;Segment-Adressen zu enthalten statt Selektoren. _Realmode: _ mov ax,8000h _ mov ds,ax _ mov ax,8800h ; Puffer-Zone für video-BIOS, lesbar für den _ ; Rufer auf abs.Adresse 88000h _ mov es,ax ; die meisten BIOSints erwarten die Puffer- _ mov di,0 ; Definition in es:di _ mov ax,9000h _ mov ss,ax _ mov sp,8000h ; wird dekrementiert... _;Definiere die real mode IDT als BIOS-default: _ mov si,IDTrm0 _ lidt [si] _;Initialisiere den Interrupt-Controller wie BIOS (KEIN eoi wie unter ASMOS) _;Interrupt-Tabelle und Vektoren ab Adresse=0 bleiben unter ASMOS unberührt! _;Maskiere alle ints: ; Nicht alle sind unter ASMOS maskiert _ mov al,FFh ; De-maskieren mit 0, maskieren mit 1 / Bits sind auf _ ; IRQ# bezogen _ out 21h,al ; Master _ out 0A1h,al ; Slave _;Init, 1.Parameter: _ mov eax,00010001b _ out 20h,al;Master _ out 0A0h,al ; Slave _;Init, 2.Parameter: (BIOS default ist unter ASMOS geändert) _ mov al,8 ; setze INT# auf IRQ#0 des Masters _ out 21h,al _ mov eax,70 ; dezimal! _ out 0A1h,al ; setze INT# auf IRQ#0 des Slave _;Init,3.Parameter: _ mov eax,00000100b _ out 21h,al ; IRQ# des Slave, bit-orientiert an Master _ mov al,00000010b _ out 0A1h,al ; IRQ# des Slave an Master-Inputs, dual codiert an Slave _;Init, 4.Parameter: ( Bit1: 1=auto, 0=eoi explizit)! _ mov al,1 ; means: Intel-Umgebung _ out 21h,al ; BIOS default an Master _ mov al,1 _ out 0A1h,al ; BIOS default an Slave _;Mache einen "unspecific end of interrupt": (im real mode nach jedem Int nötig) _ mov al,20h _ out A0h,al _ out 20h,al _;De-maskiere alle ints: (meistens maskiert unter ASMOS!) _ mov al,0 ; De-maskieren mit 0, maskieren mit 1 / Bits sind auf _ ; IRQ# bezogen _ out 21h,al ; Master _ out 0A1h,al ; Slave _;Restaurieren der Inhalte von Registern, die der Rufer definierte: _ mov ax,[RegAX] _ mov bx,[RegBX] _ mov cx,[RegCX] _ mov dx,[RegDX] _ sti _ int 10h ; Nur ein Beispiel....Beliebig anderes darf sein. _ mov [RegAX],ax _ mov [RegBX],bx _ mov [RegCX],cx _ mov [RegDX],dx _ push 0 _ popf ; clear interrupts vor der Rückkehr... _;Zurück zum Rufer im 32-bit-protected mode mit ASMOS-Standard: _;Maskiere ints: _ mov al,10001b _ out 21h,al _ out 0A1h,al _;Init, 1.Parameter: _ mov eax,10001b _ out 20h,al _ out 0A0h,al _;Init, 2.Parameter: _ mov al,0F0h _ out 21h,al _ mov eax,0F8h _ out 0A1h,al _;Init,3.Parameter: _ mov eax,100b _ out 21h,al _ mov al,10b _ out 0A1h,al _;Init, 4.Parameter: ( bit1=1=auto-eoi)! _ mov al,11b _ out 21h,al _ mov al,11b _ out 0A1h,al _;Etabliere IDT: _ mov si,IDT0 _ lidt [si] _ mov ax,[Selector32] ; hol den Programm-Selektor für direkten Sprung _ mov [FARjmpSELrm],ax _;Schalte zum protected mode (cli, switch PE-bit im machine status word und _;springe FAR auf folgenden Code): _;Da dies für den real mode übersetzt wird, arbeitet dort auch folgendes _;Kommando... _ mov ax,1 _ lmsw ax ; set PE-Bit in cr0 _;FAR Sprung mit Selektor:Offset-argument. Erst dieser Sprung schaltet den _;protected mode wegen der Benutzung von Segment-Registern wirklich ein! _;Ein indizierter Sprung würde hier nur arbeiten, wenn auch noch ein passender _;Selektor in ds geladen würde! _;Einfacher ist ein direkter Sprung, gemacht aus Konstanten. _FARjmpOPrm: DB EAh ; Opcode des direkten FAR jump _FARjmpOFrm: DW 32Pmode ; Offset 0-15 des Sprungzieles _FARjmpSELrm: DW 0 ; Selektor zum 32-bit-Deskriptor _32Pmode: ; Nun sind wir im 32-bit-protected-mode-Segment _ mov ax,8 _ mov ds,ax ; overall-segment _ mov es,ax ; overall-segment _ mov fs,ax ; overall-segment _ mov gs,ax ; overall-segment _ mov ss,ax ; overall-segment _;Erst jetzt kann der FAR-Sprung zum 32-bit-protected mode über 1M getan werden. _;Da das Kommando von ASMnr mit einer Offset-Adresse in 16 Bit statt der _;nötigen 32 Bit übersetzt würde, wird hier der Opcode als Konstante gegeben. _FARjmpre: DB 66h ; operand-size-praefix 66h erlaubt 32 bit Offset _FARjmpOP: DB 0EAh ; Opcode des direkten FAR-Sprunges _FARjmpOF0: DW 0 ; Offset 0-15 der Sprungziel-Adresse _FARjmpOF1: DW 0 ; Offset 16-31 der Sprungziel-Adresse _FARjmpSEL: DW 0 ; Selektor zum 32-Bit-Deskriptor _Selector32: DW 0 ; Selektor zum 32-Bit-Deskriptor, der das zum _ ; real-mode-Segment kongruente Segment definiert. _PROGAMMend: DB "END " ; Diese Zeichen braucht man unter ASMOS, um das _;Kopieren zu vereinfachen. Erstes nicht zu kopierendes Zeichen ist das "E". Nach der Übersetzung des obigen Codes wird das Binary in die unten stehende Konstante kopiert - ein spezieller Typ, der von ASMn und ASMat erkannt wird. Wer andere Übersetzungs-Programme und Assembler-Dialekte zur Programmierung dieses Ablaufes benutzen will, braucht einen äquivalenten Typ einer Konstante, die das Binary bei einem Label beginnen und exakt vor dem folgenden enden lässt. Auf diese Weise werden Adressen im Binary zur Laufzeit unten adressierbar - das ist in diesem Zusammenhang unverzichtbar! Der originale NASM-Assembler bietet als ziemlich ähnlichen Typ "INCBIN"... :16-bit-PROTECTEDM i 63h first byte of binary at "f", last at "d" of end Das folgende Label der Prozedur bezeichnet die nächste Adresse über dem "d". Sie wird gerufen nach Definition von Registern in Bits 0-15 und nach versetzen des Codes unter 1M (Vor und nach diesem Ruf mit Nutzung der oben gezeigten Prozedur "P_Move_mem". :P_VBE-call mov .callerESP,esp originalen stack-pointer retten In es:di zu definierende Puffer-Zone ist immer bei abs. 88000h (es=8800h,edi=0) mov esi,16-bit-PROTECTEDM Vom Rufer definierte Register-Inhalte retten. Kein push&pop ist hier erlaubt, weil sich der Stack ändert! mov .esi+4,eax mov .esi+6,ebx mov .esi+8,ecx mov .esi+Ah,dx Definiere Konstanten im Code für 16-bit-protected & real mode: Die folgenden Adressen sind bezogen auf das Label am Beginn des übersetzten Binary! mov eax,.lowMEM add eax,11000h Adresse der neuen IDT im oberen Speicher mov .esi+Eh,eax Die folgenden Adressen sind bezogen auf das Label hinter dem Ende des übersetzten Binary! mov edi,P_VBE-call Definiere Pointer im FAR-Sprung zurück zum 32-bit-protected-mode: mov .edi-8h,eax mov eax,cs Selektor im Opcode hier mov .edi-4,ax Wir machen nun ein Programm-Segment unter 1M mit Operanden- und Adress-Breite von 16 Bit. Ein Programm-Daten-Segment ist NICHT nötig. Basis-Adresse des Segmentes ist 80000h. Die Abfolge der Deskriptoren im Stapel der GDT steht im Raster von 8 Byte-Adressen. Innerhalb von ASMOS gibt es eine Variable namens "GDTtop", die die aktuelle Offset-Adresse der ersten verfügbaren enthält, über den bereits definierten Deskriptoren. Dies ist tatsächlich auch der Wert eines Selektors, wenn die Bits0-2 =0 sind. Sie definieren Privilegstufen und werden unter ASMOS nicht genutzt, sondern sind immer =0 Man braucht den neuen Deskriptor nicht in der Abfolge der vorhandenen. Man muss nur sicher gehen, dass man nicht bereits definierte überschreibt! Der hier gemachte Deskriptor kann nämlich vergessen werden bei der Rückkehr von dieser Prozedur... Die absolute Adresse der neuen GDT ist oben bereits definiert... mov edx,.gdt1 absolute Adresse d. GDT oberhalb 1M mov eax,.es:100038h ="GDTtop" cmp eax,FFF8h jc Progdescon Das folgende bewirkt unter ASMOS eine Fehlerausgabe auf dem Bildschirm. mov ebx,2 "OUT OF RANGE" stc Vom Rufer der Prozedur zu verwerten... ret :Progdescon add edx,eax Selektor von 16-bit-Programm-Deskriptor mov w.es:edx,FFFFh 1-16 Länge mov w.es:edx+2,0 1-16 Segment-Adresse mov b.es:edx+4,8 17-24 Segment-Adresse mov b.es:edx+5,10011010b Bitfeld mov b.es:edx+6,0 definiert 16-bit-Deskriptor mov b.es:edx+7,0 Dieser Deskriptor ist vergessen nach Rückkehr von der Prozedur, weil "GDTtop" nicht inkrementiert wird. Wir brauchen aber den aktuellen Wert, der der Selektor des eben gefertigten Programm-Deskriptors für ein 16-bit-Segment ist. mov eax,.es:100038h ="GDTtop" Programm-Selektor im FAR-Sprung zum 16-bit-protected mode mov .FARjmpS,ax mov .edi-2,ax Wert von "FARjmpSELrm" im Binary Versetzen des Binaries in den unteren Speicher-Bereich: mov ecx,edi mov edi,80000h Ziel der Versetzung Länge des zu versetzenden Codes kalkulieren: sub ecx,esi shr ecx,2 inc ecx :MoveRM dec ecx mov eax,.esi+ecx*4 mov .es:edi+ecx*4,eax jne MoveRM Puffer für BIOS vorbesetzen mit 0 : mov edi,88000h Puffer-Basis-Adresse xor eax,eax mov ecx,8000h :ClearRM dec ecx mov .es:edi+ecx*4,eax jne ClearRM push 0 popfd Reset EEFLAGS, meint auch "cli" mov eax,60000001h der Wert schaltet den Cache aus, berührt aber nicht das PE-bit (immer noch im protected mode). mov cr0,eax jmp f.FARjmpO :FARjmpO d 0 offset 0-32 of destination label :FARjmpS w 0 selector of 16-bit-descriptor a align with nops.... :BackAgain Wir sind zurück im 32-bit-protected mode über 1M.... mov eax,cs add eax,8 Standard unter ASMOS: Der kongruente Daten-Programm-Deskriptor ist stets über dem Programm-Deskriptor definiert. mov ds,eax Selektor des Programm-Daten-Segmentes mov esp,.callerESP hält die Rücksprung-Adresse d.Prozedur! mov eax,1 Setting in cr0: PE=H=an, CD,NW=L=cache an mov cr0,eax sti wieder Interrupts erlauben ret

    Wenn das Betriebssystem im page mode läuft, muss man noch weitere Schritte zurück zu jener Steinzeit tun... Der page mode wurde einst in die Welt gesetzt, weil damals Arbeitsspeicher noch sehr klein und sehr teuer war. Tatsächlich reduziert er die Ausführungszeit aller Programme um mindestens die Hälfte. Auch multi-tasking wurde nur in die Welt gesetzt, weil die CPUs noch sehr langsam waren. Multi-tasking reduziert die Ausführungszeit noch weiter. Ganz schlimm wird's bei swapping... Moderne Computer enthalten bis zum Faktor 1000 schnellere CPUs und bis zum Faktor 2000 mehr Platz im Arbeitsspeicher. Das war einst unerschwinglich oder garnicht machbar. Wer also heute page mode und multi-tasking als Vorteil preist, ist nicht bei Verstand...