[ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

Schön, das war jetzt auch mein Stand (die Ausgabe hatte ich nämlich auch nicht verändert). Da ist das Problem, dass der Videospeicher überschrieben wird. Wenn das im 25 Zeilen x 80 Spalten Modus läuft, müsstest du die Adresse mit (zeile * 80 + spalte) berechnen können, wobei du ja immer 2 Byte pro Zeichen brauchst. Zeile 1 (wenn wir bei 0 beginnen) müsste dann bei 1 * 80 + 0 = 80, x2 Byte = 160 Byte losgehen.

Und zu den Fragen, immer her damit ^^

Edit: Aso, diese times 510/512 - ($ - $$) db 0 Sachen brauchst du eigentlich auch nicht, da du deine Programme ja mit dem write Programm zusammenpackst. Die brauchte ich nur, um mittels copy die ersten beiden Sektoren eines Disketten-Images zu befüllen (mein Virtualbox mag keine Floppy-Images, die ungültige Sektoren haben).
 
Zuletzt bearbeitet:
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

K.A. in welchem Modus da was ausgegeben wird. Ich habe mir einfach gedacht, bei "BSY via int 10" in Aufgabe c hat's ja auch geklappt, dass das in der nächsten Zeile unter "Hallo Welt" gelandet ist. Also müsste ich doch nur schauen, was da die Start-Ausgabeadresse war und die dann einfach jetzt in Aufgabe d für "ISR20h" hernehmen?! ;)

Da bin ich gerade am Probieren ...

Irgendwo in den blauen Codezeilen steckt das drinnen, was ich jetzt auch für "ISR20h" bräuchte:
Code:
[BITS 16]
start:

    mov ax,0xB800
    mov es,ax    
    mov byte [es:20],'H'
    mov byte [es:21],0x1F
    mov byte [es:22],'A'
    mov byte [es:23],0x1F    
    mov byte [es:24],'L'
    mov byte [es:25],0x1F
    mov byte [es:26],'L'
    mov byte [es:27],0x1F
    mov byte [es:28],'O'
    mov byte [es:29],0x1F
    mov byte [es:30],' '
    mov byte [es:31],0x1F
    mov byte [es:32],'B'
    mov byte [es:33],0x1F
    mov byte [es:34],'S'
    mov byte [es:35],0x1F
    mov byte [es:36],'Y'
    mov byte [es:37],0x1F
    
    mov AH, 02h
    mov BH,0
    int 0x10    

    mov AH,13h
    
[COLOR=royalblue][B]    mov AL,1
    mov BH,0
    mov BL,0x1F
    mov CX,bsy1len
    mov ESI, 0x0500
    mov ES, ESI
    mov BP,bsy1msg[/B]    
    int 0x10

endloop:
    jmp endloop

[COLOR=royalblue][B]bsy1msg     db     13,10,"BSY1 via INT 10"
bsy1len     equ     $ - bsy1msg[/B]
Aber ich kann hier wieder nur herumraten und probieren. Wie gesagt, ich kann kein Assembler. Ich habe keine Ahnung, was die markierten Zeilen machen und wofür all die Werte stehen. Ok, 0x1F und 0x0500 sind halt irgendwelche Adressen, mov heißt, dass das Zeug auf den Stack geschoben wird (?), und das AL, BH, etc. sind wahrscheinlich Register?! Aber wie das jetzt alles zusammenhängt --> no idea ...
 
Zuletzt bearbeitet:
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

Ich habe es mal ausprobiert, mit der von mir angesprochenen Rechnerei kommt man in die nächste Zeile.
Code:
mov byte [es:160], 'I'
mov byte [es:161], 0x15
mov byte [es:162], 'S'
...
Wenn du die ISR so ergänzt, dass du den [es:xx] Teil ab 160 befüllst, müsste das eigentlich funktionieren.
 
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

Habe mir gerade vom Lehrer sagen lassen, dass es ihm egal ist, wie die Ausgabe aussieht, solange nur "ISR20h" irgendwo dasteht ... ;)

Hab's aber trotzdem noch so umgebastelt, dass das jetzt in der zweiten Zeile steht. Ist einfach optisch schöner. Woher hast du eigentlich das mit den 160+ gewusst? Ausprobiert? Oder ist eine Zeile im VMWare Player standardmäßig 160 Zeichen (0 bis 159) lang?

Davon abgesehen hätte ich vorerst folgende Fragen:
Hier noch einmal der Code:
Code:
[BITS 16]

sect2:
    push es            
    
    xor ax, ax                    
    mov es, ax                    
    mov bx, 0x20                
    shl bx, 2                    
    mov ax, isr_20h               
    cli                            
    mov [es:bx], ax                
    mov [es:bx + 2], cs           
    sti                            
    
    pop es       
    
    int 0x20   
    
endloop:
    jmp endloop

isr_20h:
    push ax
    push es
    
    mov ax, 0xB800
    mov es, ax
    
    mov byte [es:160], 'I'
    mov byte [es:161], [COLOR=royalblue][B]00011111b[/B]    mov byte [es:162], 'S'
    mov byte [es:163], 00011111b
    mov byte [es:164], 'R'
    mov byte [es:165], 00011111b
    mov byte [es:166], '2'
    mov byte [es:167], 00011111b
    mov byte [es:168], '0'
    mov byte [es:169], 00011111b
    mov byte [es:170], 'h'
    mov byte [es:171], 00011111b
    
    pop es
    pop ax
    
    iret
1. Wie ergibt sich die blau markierte Binärzahl? In dezimal ergibt das 31, aber das sagt mir jetzt auch nicht mehr. Von diesem Wert kommt die weiße Schrift auf blauem Hintergund, soviel weiß ich. Aber wie genau? Ich habe hier nur das als "Erklärung":

Capture.JPG

2. Wieso brauche ich in der boot.asm beim Ausgabeteil manchmal Hex-Werte für den Offset (z.B.: mov byte [es:0x0B], 00011111b) und hier in der sect2.asm konnte ich das jetzt ganz normal von 160 bis 171 runterschreiben? Oder ist eh das auch irgendwie Hex und ich seh's nicht?

3. Wie komme ich auf all das hier:
Code:
mov ah,0x02
mov al,1
mov ch,0
mov cl,2
mov dh,0
mov dl,0
mov bx,0
Du hast zwar irgendwo mal als Kommentar geschrieben "Lesekopf setzen" und dergleichen, aber ganz klar ist mir das trotzdem nicht. Hier kann ich mir zumindest anschauen, welche Register (?) nötig sind, um einen Sektor von einem Laufwerk zu lesen, aber wie komme ich auf die Zahlen? Und wo kommt das ch und cl her und was ist das?

4. Wir haben noch einen Übungsblock vor uns, wo wir wahrscheinlich wieder 4 so "kleine" Aufgaben erledigen müssen. Kannst du mir irgendwas empfehlen, das ich mir durchüberlegen sollte, um da ein Bisschen mehr Verständnis für das ganze Register/Assembler-Zeug zu entwickeln? Wie/Wo hast du das gelernt, wenn man fragen darf? :)
 
Zuletzt bearbeitet:
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

Hab's aber trotzdem noch so umgebastelt, dass das jetzt in der zweiten Zeile steht. Ist einfach optisch schöner. Woher hast du eigentlich das mit den 160+ gewusst? Ausprobiert? Oder ist eine Zeile im VMWare Player standardmäßig 160 Zeichen (0 bis 159) lang?
Das war mehr oder weniger geraten. 80 Spalten x 25 Zeilen ist so mehr oder weniger Standard-Textmodus. Und da der Videospeicher auch nur ein (großes) Array ist, habe ich es einfach mal nach der Formel (zeile * 80 + spalte) versucht, wobei zeile = 1 und spalte = 0 -> 80. Da aber nach jedem Zeichen-Byte noch so ein Options-Byte (Farbe) kam, musste ich das ganze noch mit 2 multiplizieren. So bin ich auf die 160 gekommen.

1. Wie ergibt sich die blau markierte Binärzahl? In dezimal ergibt das 31, aber das sagt mir jetzt auch nicht mehr. Von diesem Wert kommt die weiße Schrift auf blauem Hintergund, soviel weiß ich. Aber wie genau? Ich habe hier nur das als "Erklärung":
Du hast immer ein Byte für das Zeichen und ein Byte für Darstellungsoptionen. In dem zweiten Byte ist unter anderem die Vorder- und Hintergrundfarbe bitweise kodiert (als Rotanteil R, Grünanteil G und Blauanteil B). Das ist in dem Bild eigentlich ganz gut zu erkennen.

2. Wieso brauche ich in der boot.asm beim Ausgabeteil manchmal Hex-Werte für den Offset (z.B.: mov byte [es:0x0B], 00011111b) und hier in der sect2.asm konnte ich das jetzt ganz normal von 160 bis 171 runterschreiben? Oder ist eh das auch irgendwie Hex und ich seh's nicht?
Das sind alles nur Zahlen. Der CPU ist es egal, ob du die nun Dezimal, Hex oder Binär hinschreibst - zumindest wenn sie den gleichen Wert darstellen. Es macht z. B. keinen Unterschied, ob ich 0x02, 2 oder 10b schreibe. Die Angabe der Binärzahl soll hier vielmehr verdeutlichen, welche Flags des Darstellungsbytes gesetzt sind (schreib mal die Bitfolge unter die roten Kästchen in dem Bild, da siehst du dann, welche Flags gesetzt sind).

3. Wie komme ich auf all das hier:
Code:
mov ah,0x02
mov al,1
mov ch,0
mov cl,2
mov dh,0
mov dl,0
mov bx,0
Du hast zwar irgendwo mal als Kommentar geschrieben "Lesekopf setzen" und dergleichen, aber ganz klar ist mir das trotzdem nicht. Hier kann ich mir zumindest anschauen, welche Register (?) nötig sind, um einen Sektor von einem Laufwerk zu lesen, aber wie komme ich auf die Zahlen? Und wo kommt das ch und cl her und was ist das?
Dazu musst du wissen, wie zum Beispiel eine Diskette aufgebaut ist. Normalerweise müsste man die Grenzen dieser Zahlen auch programmtechnisch ermitteln. Es gab ja schließlich Disketten in unterschiedlichen Größen und deshalb auch unterschiedlicher Geometrie (das ist der Fachbegriff dafür). Bei Festplatten ist die ganze Sache ähnlich (Zylinder, Köpfe, Sektoren [CHS] schon mal gehört?).

Wie kommt man nun zu CL/CH? Da wir im 16 Bit Realmode arbeiten, gibt es nur 16 Bit große Register, z. B. AX, BX, CX, DX. Das sind die sog. General Purpose Register. Du kannst aber jeweils auf die oberen und unteren 8 Bit getrennt zugreifen. So enthält CH die oberen 8 Bit und CL die unteren. Das sind aber keine separaten Register, wenn du CL/CH änderst, ändert sich auch der Wert in CX beispielsweise. Im 32 Bit Modus gibt es dann wieder größere Register (z. B. EAX, EBX, ECX, EDX), bei denen das ähnlich funktioniert. CX sind dann die unteren 16 Bit aus ECX, CL die unteren 8 aus CX. Es gibt hier allerdings keinen Registernamen für die oberen 16 Bit.

4. Wir haben noch einen Übungsblock vor uns, wo wir wahrscheinlich wieder 4 so "kleine" Aufgaben erledigen müssen. Kannst du mir irgendwas empfehlen, das ich mir durchüberlegen sollte, um da ein Bisschen mehr Verständnis für das ganze Register/Assembler-Zeug zu entwickeln? Wie/Wo hast du das gelernt, wenn man fragen darf? :)
Hilfreich ist auf jeden Fall auch das NASM Handbuch. Anders als bei Hochsprachen wie C gibt es teils große Unterschiede zwischen den einzelnen Assemblern. Die Grundsyntax der Mnemonics (so heißen diese Befehle wie mov, push, int) ist meist ähnlich, aber das drumherum variiert stark (Makro-Funktionen, if-else-Konstrukte, Schleifen, ...). Deshalb ist das Portieren von Code, der in Assembler 1 geschrieben wurde, nach Assembler 2 eine mitunter ziemlich frustrierende und langwierige Aufgabe. Eigentlich ist ziemlich viel, was mit Assembler zu tun hat, frustrierend und langwierig :ugly: Allein wenn du für eine andere CPU-Architektur entwickelst, kannst du oft einfach alles neu schreiben. Bei C ist das alles einfacher ;)

Das hier ist ein ganz gutes (Linux-)Tutorial zu Assemblerprogrammierung. Vor allem weil der Autor auch mit NASM arbeitet, was aus dem oben genannten Grund für dich sicherlich einfacher sein wird. Da lernst du jetzt zwar nicht, wie du ein Betriebssystem schreibst, aber die ASM Grundlagen werden meiner Meinung nach ganz gut rübergebracht.

Ich habe mir mein Wissen selbst angeeignet (wie eigentlich alle meine Programmierfertigkeiten). Vor ein paar Jahren habe ich mich mal intensiver mit OS Entwicklung beschäftigt. Mit Assembler hatte ich aber vorher schon öfters gearbeitet. Es ist halt alles etwas mühselig, aber nunja, Übung macht den Meister :ugly: Wobei ich jetzt nicht sagen würde, ich könnte fließend Assemblercode schreiben - geschweige denn guten (was noch mal ein ganz anderes komplexes Thema ist). Lesen klappt aber eigentlich ganz gut. Es ist halt wirklich low-level und nicht so abstrakt wie eine höhere Programmiersprache.
 
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

Ok, vielen Dank! :daumen:

Werde mir das alles noch einmal durchüberlegen und natürlich die Codes noch entsprechend kommentieren. Wenn ich für den zweiten Übungsteil im Jänner noch einmal Hilfe brauche, melde ich mich wieder. :)
 
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

So, gestern war der zweite Übungsblock ...

7 "kleine" Aufgaben, 5 davon haben wir im Teamwork geschafft. Für 6 und 7 bräuchte ich bitte wieder Hilfe und wenn das erledigt ist, hätte ich noch ein paar Fragen zu den Aufgaben 1 - 5. :)
Code:
[COLOR=seagreen];<AUFGABE 6>[COLOR=seagreen];Analysieren Sie folgenden Code. Was passiert hier? Nutzen Sie die
;Erkenntnisse in AUFGABE 7startpaging:
pagedir equ 0x80000
pagetab equ 0x81000
tabsize equ 0x400
    
    mov ebx,tabsize            [COLOR=seagreen];Zero PageDirectoryzeropagedir:
    dec ebx
    mov dword [ebx*4+pagedir],0
    cmp ebx,0
    jne zeropagedir
    mov eax,pagetab
    or  eax,1
    mov dword [pagedir],eax    
    mov dword [pagedir+8],eax

    mov ebx,tabsize           [COLOR=seagreen] ;Map PDE1 (virt = phys)fillpagetable:
    dec ebx
    mov eax, ebx
    shl eax,12
    or  eax,3
    mov dword [ebx*4+pagetab],eax
    cmp ebx,0
    jne fillpagetable

    mov eax,pagedir           [COLOR=seagreen] ;PDE -> CR3    mov cr3,eax
    
    mov eax,cr0            [COLOR=seagreen];Set PG-Bit    or eax,0x80000000
    mov cr0,eax
    retn
[COLOR=seagreen];</AUFGABE6>
Unter den meisten Assembler Befehlen kann ich mir schon ungefähr was vorstellen ...
mov = irgendwas an eine Adresse setzen?!
dec = dekrementieren um 1
or = logische OR Verknüpfung?!
retn = return irgendwohin
jne = ein Jump-Befehl
cmp = die beiden Parameter vergleichen
equ = gleichsetzen bzw. das links auf das rechts setzen
shl = sagt mir nichts
dword = sagt mir jetzt auch nicht viel

Aber was mir so gar nicht wirklich klar ist, was das alles hier macht bzw. warum. Bei startpaging werden wohl die Startadressen für den Vorgang festgelegt?! Bei fillpagetable wird diese pagetable wohl mit Inhalt geladen?!
--------------------
Code:
[COLOR=seagreen];<AUFGABE7>[COLOR=seagreen];Aktivieren Sie Paging indem sie das Kommentar vor der nachfolgenden
;Call-Anweisung entfernen. Der Interrupthandler für Interrupt 2 ist
;auch auf einer Adresse > 4MB zu erreichen. Modifizieren Sie den
;Interrupt-Gate-Deskriptor für Interrupt 2 so, dass der Interrupthandler
;von dieser Adresse gestartet wird. Rufen Sie anschließend Interrupt 2 auf.
    call startpaging

[COLOR=seagreen];INSERT CODE HERE
[COLOR=seagreen];</AUFGABE 7>
Ok, das Komma habe ich bereits entfernt und somit wohl paging aktiviert. Wahrscheinlich braucht man nicht mehr als 5, vielleicht 6 kurze Code Zeilen um das zu lösen (mehr hat man auch für 1 - 5 nicht gebraucht), nur wie die ausschauen müssen, weiß ich nicht so recht. Ich werde wahrscheinlich einen Interrupt 2 brauchen (int 0x02?) und dann wohl irgendsowas: mov ax, isr und mov [es:bx], ax. In bx muss dann wohl die neue gewünschte Adresse rein?! So ungefähr stelle ich mir das vor.

Danke für baldige Hilfe!

BTW: Kongratulation zu den 3k ;)
 
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

mov = irgendwas an eine Adresse setzen?!
Mit dem mov-Befehl kann man Werte in eine Adresse, oder in ein CPU-Register kopieren und auch aus Adressen oder Registern Werte laden.
Beispiele intel Syntax:
mov eax, 1 ; Wert in das eax-Register schreiben
mov DWORD [Adresse], 1 ; 4 Byte-Wert in eine Adresse schreiben
mov eax, ebx : Wert vom ebx-Register in das eax-Register schreiben
mov [Adresse], eax ; Wert vom eax-Register in eine Adresse schreiben
mov eax, [Adresse] ; Wert aus einer Adresse in das eax-Register schreiben


retn = return irgendwohin
Gewöhnlicher Weise wird an die Stelle zurückgesprungen, von wo der Call-Befehl(zum Aufruf von Subroutinen) aufgerufen wurde und der Programmcounter auf den dort nachfolgenden Befehl gesetzt.
Durch den Call-Befehl wird die Rücksprungadresse auf unseren Stack gelegt und der ret-Befehl holt diese Adresse vom Stack und springt zurück.

jne = ein Jump-Befehl
Das ist ein bedingter Sprungbefehl der das Zeroflag im Flagregister auswertet und nur dann springt, wenn dieses Flag gesetzt, oder in unserem Fall mit jne(jump not equal) nicht gesetzt ist.
Das Zeroflag wird durch einen vorherigen Befehl verändert, wie z.B durch den cmp-Befehl, der zum Vergleichen von Werten benutzt werden kann.

shl = sagt mir nichts
shl = shift left
damit werden alle spezifizierten Bits nach links/oben geschoben und das unterste Bit wird mit einer 0 aufgefüllt und das höchste Bit geht verloren
Die Bits von einem Byte in Reihe betrachtet von rechts nach links aufsteigend: 76543210

dword = sagt mir jetzt auch nicht viel
double word sind 4 Bytes, oder auch 32 Bit
Ein DWORD, WORD, oder BYTE muss immer dann mit angegeben werden, wenn wir auf den Speicher zugreifen und wir einen direckten (immediate) Wert im Befehl mit angeben möchten.
Weil sonst weiss der Assembler nicht auf wieviele Bytes sich unsere Befehl beziehen soll und gibt eine Felhermeldung darüber aus.
Im wesentlichen werden die CPU-Register zusammen mit den vorhandenen Befehlen und den Flags im Flagregister dazu verwendet ein Programmablauf aufzubauen und zu steuern.

[BITS 16]
start: .....

mov ESI, 0x0500
mov ES, ESI
Das ES-Segmentregister ist nur 16 Bit gross.
Das "mov ES, ESI" muss daher in "mov ES, SI" geändert werden.

Dirk
 
Zuletzt bearbeitet:
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

Ok, danke ... jetzt sind zumindest die Befehle klar. ABER: Warum muss ich das alles machen, was im Code von Aufgabe 6 steht? Was bewirkt all das? Warum genau die Register, Werte, etc.?

Und wie genau muss der Code für Aufgabe 7 ausschauen. Zwei Zeile schauen wohl so aus:
Code:
call startpaging
...
int 0x02
Aber dazwischen muss noch das rein:
Modifizieren Sie den Interrupt-Gate-Deskriptor für Interrupt 2 so, dass der Interrupthandler von dieser Adresse gestartet wird.
Und da weiß ich nicht, wie ich das machen soll ...
 
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

Zu Aufgabe 7 habe ich mir noch keine Gedanken machen können, aber bei Aufgabe 6 habe ich mal den Code kommentiert. Ob das alles so 100% stimmt, kann ich allerdings nicht garantieren ;)

Code:
;<AUFGABE 6>
;Analysieren Sie folgenden Code. Was passiert hier? Nutzen Sie die
;Erkenntnisse in AUFGABE 7
startpaging:
pagedir equ 0x80000                ; Startadresse des Page Directories
pagetab equ 0x81000                ; Startadresse der Page Table (4096 Byte hinter PD)
                                ; Die beiden Tabellen sind je 4096 Byte groß (1024 Einträge * 4 Byte)
tabsize equ 0x400                ; Tabellengröße (1024 Einträge)
    
                                ; Page Directory initialisieren (mit 0 füllen)
    mov ebx,tabsize                ; EBX mit tabsize (1024) laden (Anzahl Einträge)
zeropagedir:
    dec ebx                        ; EBX = EBX - 1
    mov dword [ebx*4+pagedir],0    ; DWORD (4 Byte int) an der berechneten Adresse auf 0 setzen
    cmp ebx,0                    ; Ist EBX = 0? (Abbruchbedingung der Schleife)
    jne zeropagedir                ; Falls nicht, weiter mit dem nächsten Eintrag
                                ; Der ganze Block ergibt daher eine Schleife der Form i = 1023; i >= 0; --i
                                
    mov eax,pagetab                ; Startadresse der Page Table in EAX laden
    or  eax,1                    ; EAX = 0x81001 (Bit 0 setzen)
    mov dword [pagedir],eax        ; 1. Eintrag im Page Directory setzen (Page Directory Entry - PDE)
                                ; Adresse der Page Table steht in den Bits 12 bis 31 (müsste hier jetzt auf 0x81 stehen...)
                                ; Bit 0 = 1 bedeutet, die Seite befindet sich momentan im physischen Speicher (Present-Bit)
    mov dword [pagedir+8],eax    ; Und nochmal für den 3. Eintrag (2 * 4 Byte weiter)
                                ; Was das jetzt allerdings soll, erschließt sich mir momentan nicht...

                                ; Map PDE1 (virt = phys)
                                ; Hier wird ein 1:1 Mapping der Form virtuelle Adresse = physische Adresse
                                ; durchgeführt (bei Paging ist es i. A. nicht so, dass die virtuelle Adresse
                                ; auch der physischen entspricht).
    mov ebx,tabsize                ; Schleife wie oben
fillpagetable:
    dec ebx
    mov eax, ebx
    shl eax,12                    ; EAX = EAX << 12 (Shift nach Links um 12 Bit = Multiplikation mit 4096)
                                ; Das hat mit der Struktur der Page Table zu tun.
                                ; Die physische Page Adresse beginnt erst ab dem 12. Bit, die restlichen Bits
                                ; werden für andere Zwecke benutzt (z. B. das Present-Bit von oben). Im Gegensatz zum PDE
                                ; muss die Adresse hier auf einen 4 KB Block physischen Speicher zeigen.
                                ; Die PDEs sind im wesentlichen ziemlich ähnlich aufgebaut, haben nur ein paar Flags anders
                                ; (und das Adressfeld bedeutet natürlich was anderes).
    or  eax,3                    ; Bit 0 (Present Bit) und 1 (Read/Write Bit) setzen
    mov dword [ebx*4+pagetab],eax    ; Page Table Eintrag schreiben
    cmp ebx,0                    ; Abbruchbedingung der Schleife
    jne fillpagetable            ; ggf. wiederholen

                                ; Nun wird Paging aktiviert
    mov eax,pagedir                ; Adresse des Page Directories in EAX laden
    mov cr3,eax                    ; Adresse des PDs in CR3 (Control Register 3) laden
    
    mov eax,cr0                    ; Control Register 0 in EAX laden
    or eax,0x80000000            ; Paging-Bit setzen
    mov cr0,eax                    ; In Control Register 0 zurückschreiben (aktiviert Paging)
    retn                        ; zurück zum Aufrufer
                                ; Hierbei handelt es sich um ein return near, bei dem sich das Sprungziel
                                ; im gleichen Codesegment befinden muss. Es gibt auch ein retf, was für
                                ; return far steht (Ziel muss nicht im gleichen Codesegement liegen).
                                ; Wenn man nur ret schreibt, entscheidet der Assembler, ob retn oder retf.
;</AUFGABE6>
 
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

Wie kommt man nun zu CL/CH? Da wir im 16 Bit Realmode arbeiten, gibt es nur 16 Bit große Register, z. B. AX, BX, CX, DX. Das sind die sog. General Purpose Register. Du kannst aber jeweils auf die oberen und unteren 8 Bit getrennt zugreifen. So enthält CH die oberen 8 Bit und CL die unteren. Das sind aber keine separaten Register, wenn du CL/CH änderst, ändert sich auch der Wert in CX beispielsweise. Im 32 Bit Modus gibt es dann wieder größere Register (z. B. EAX, EBX, ECX, EDX), bei denen das ähnlich funktioniert. CX sind dann die unteren 16 Bit aus ECX, CL die unteren 8 aus CX. Es gibt hier allerdings keinen Registernamen für die oberen 16 Bit.
Schon vor dem Umschalten in den 32 Bit Modus sind alle 32 Bitregister(ab 80386+) bereits verfügbar und können ebenfalls im 16 Bit Modus verwendet werden. Ebenso können die 64 Bit MMX und die 128 Bit SSE-Register im 16 Bit Modus verwendet werden, insofern die verwendete CPU solche Erweitungen schon mit integriert hat. Der einzige Unterschied zwischen dem 32 Bit - und dem 16 Bit-Modus ist die Bedeutung und Verwendung der Operandsize- und Adresssize Prefixe. Wobei im 16 Bit-Modus solche Prefixe vor dem jeweiligen Befehl plaziert sein müssen, wenn wir 32 Bit-Operanden, oder 32 Bit Adressen verwenden möchten. Ohne solche Prefixe werden nur 16 Bit-Operanden und/oder 16 Bit Adressen verwendet.

Im 32 Bit-Modus ändert sich die Bedeutung und Verwendung dieser Prefixe grundlegend, so dass wenn wir 32 Bit-Operanden, oder 32 Bit-Adressen verwenden möchten, diese Prefixe weglassen müssen und nur wenn wir 16 Bit-Operanden und/oder 16 Bit-Adressen verwenden möchten, dann müssen wir solche Prefixe verwenden. Genau umgekehrt zum 16 Bit - Modus. Mit der adressierbaren Speichemenge hat das aber rein gar nichts zu tun, weil die Segmentgrössen nur in den Segmenteinträgen einer bestehenden GDT/LDT/Pagetable dafür zuständig, andernfall defaultmäßig auf 64KB begrenzt sind. So läßt sich auch im 16 Bit-PM der gesamte 4 GB-Adressbereich schon auf einem 80386+ addressieren und der gesamte freie (und physikalisch auch vorhandene) Speicher davon (regulär) verwenden.

@bingo88: Uns hats auch erwischt, gute Besserung.

Dirk
 
Zuletzt bearbeitet:
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

Ah okay, das war mir so nicht klar. Habe bis jetzt immer nur 16 Bit Code ohne EAX und Konsorten gesehen. Wieder was gelernt ^^

@bingo88: Uns hats auch erwischt, gute Besserung.
Danke, dir auch!
 
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

@ bingo88
Vielen Dank für die Erklärung! Da wird einiges klarer. :) Wieder mal ist es so, dass ich die Dinge jetzt verstehe, aber nicht wüsste, wie ich da hätte von selber drauf kommen sollen ... Keine Ahnung, warum die uns solche Aufgaben geben, wo wir doch nie Assembler gelernt haben. In C/Jave hätte ich z.B. sofort gesehen, dass da eine Schleife drinnen steckt, aber wie ich das in Assembler hätte erkennen sollen?!

Ich bin im Moment ziemlich damit beschäftigt den Klausurstoff für Betriebssysteme zusammenzufassen (nein, da kommt zum Glück nichts mit Assembler oder Registern) und kann mir das ganze wahrscheinlich erst morgen oder übermorgen so richtig durchdenken. Wenn dann noch Fragen auftauchen, melde ich mich wieder. Im Moment habe ich nur eine: Kann es sein, dass sich dein "; Schleife wie oben" Kommentar eigentlich auf dec ebx bezieht? Dann würde der 2 Zeilen zu weit oben stehen ...

Und ja, für Aufgabe 7 bräuchte ich noch ein Bisschen Hilfe, bitte. Die erste und die letzte Zeile hätte ich ja (?), aber was dazwischen rein muss, da habe ich nur eine vage Idee ...
Code:
call startpaging 
... 
int 0x02
 
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

Kann es sein, dass sich dein "; Schleife wie oben" Kommentar eigentlich auf dec ebx bezieht? Dann würde der 2 Zeilen zu weit oben stehen ...
Ich meine damit, dass der Block halt ähnlich aufgebaut ist wie zuvor. Es wird wieder mit 1024 initialsiert, dann beginnt die Schleife mit dem erste Dekrement-Operator, usw. So wird es evtl. klarer:

Code:
mov ebx,tabsize    ; i = 1024 
fillpagetable:
    dec ebx           ; --i
    
    ...                   ; hier steht, was in der Schleife passieren soll

    cmp ebx,0        ; i == 0?
    jne fillpagetable ; Falls i != 0 wiederholen
Die Labels heißen jetzt anders und ich meinte ja auch nicht, dass es die gleiche Schleife ist, sondern nur, dass die gleich aufgebaut ist.

Zu Aufgabe 7:
Ah ja, der Protected Mode :ugly: Da bin ich jetzt ehrlich gesagt nicht ganz so fit drinnen, weil ich damals mehr im Real Mode unterwegs war. Diese Interrupt Gate Deskriptoren bzw. die Interrupt Descriptor Table (IDT) zusammen mit der Global Descriptor Table (GDT) sind das Analogon zu der IVT die wir schon mal hatten, nur ist das hier ein wesentlich komplexeres Gebilde... Da müsste ich mich jetzt auch erst mal schlau machen, wie die Dinger aufgebaut sind. Meine Vermutung ist momentan, dass nur die Adresse im IDT Eintrag für Interrupt 2 angepasst werden muss und der Rest kann mehr oder weniger so bleiben.
 
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

Ja, den Schinken kenn ich schon, gibt es noch ein paar andere von ;-)
Der hier benötigte Teil beginnt wohl so ab Kapitel 6.10 (IDT). Ich seh es mir morgen noch mal an.
 
Zurück