# [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen



## boss3D (13. Dezember 2012)

*[ASSEMBLER] Interrupt in Interrupt Tabelle einfügen*

Hi!

Wir hatten auf der Uni letzte Woche eine Übung zum Thema Betriebssysteme und mir ist der letzte Punkt der Aufgabenstellung für zuhause übergeblieben, weil sich's zeitlich nicht mehr ausging. Das Problem ist nur, ich komme alleine nicht wirklich klar mit der Aufgabe und könnte sehr gut Hilfe gebrauchen. 

Bei Aufgabe a ging's drum, ein "Mini-OS" zu schreiben, das per Color Text Mode "Hallo Welt" in weißer Schrift auf blauem Hintergrund ausgibt. --> Habe ich.
Bei Aufgabe b sollte Sektor 2 von der virtuellen Boot-Diskette an sect2dest:0 kopiert werden. --> Habe ich auch.
Bei Aufgabe c sollte "BSY1 via INT 10" mit Hilfe von BIOS Interrupt 10 ausgegeben werden. --> Auch erledigt.

^^ Ich habe dazu übrigens 2 virtuelle OSes im VMWare Player. Eines als "Entwicklungsumgebung" und eines um immer das Ergebnis testen zu können. Muss halt immer in der Entwicklungsumgebung eine virtuelle Boot-Diskette beschreiben und von dieser dann das Test-OS starten.

Bei Aufgabe d soll nun ein Interrupt 0x20 in die Interrupt-Tabelle eingefügt werden. Genaue Angabe siehe Screenshots:




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        

 


			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        



Soll außer der Ausgabe des kleinen Textes noch was passieren? Wie merke ich denn am Ende, ob der Interrupt-Eintrag tatsächlich in der Tabelle gelandet ist und ich nicht einfach nur die gewünschte Ausgabe produziert habe?

Mein erster Ansatz sieht jedenfalls so aus, aber allzuviel Sinn macht der wohl nicht?!

```
[BITS 16]
start:

    mov ax,0xB800
    mov es,ax
    
    mov AH, 02h
    mov BH,0
    int 0x10    

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

bsy1msg     db     13,10,"BSY1 via INT 10"
bsy1len     equ     $ - bsy1msg

[COLOR=royalblue][B]ISR20h:
start:

    mov ax,0xB800
    mov es,ax

    mov byte [es:0x00], 'I'
    mov byte [es:0x01], 00011111b

    mov byte [es:0x02], 'S'
    mov byte [es:0x03], 00011111b

    mov byte [es:0x04], 'R'
    mov byte [es:0x05], 00011111b

    mov byte [es:0x06], 'H'
    mov byte [es:0x07], 00011111b

    mov byte [es:0x08], '2'
    mov byte [es:0x09], 00011111b

    mov byte [es:0x0A], '0'
    mov byte [es:0x0B], 00011111b

    mov byte [es:0x0C], 'h'
    mov byte [es:0x0D], 00011111b
    
    jmp endloop[/B]
```
^^ Der schwarze Teil ist aus Aufgabe c übernommen. Den blauen Teil habe ich für Aufgabe d dazugefügt. 

Kann mir bei der Aufgabestellung bitte irgendjemand helfen, der sich mit Assembler und Registern auskennt? Ich stehe hier völlig im Dunkeln. Habe bei den Punkten a bis c schon mehr geraten und gegoogelt, als sonst was ...
_
PS: Habe nie Assembler gelernt, also bitte redet Deutsch mit mir._ 

Danke für jeden Tipp!


----------



## bingo88 (13. Dezember 2012)

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

Nein, das macht wirklich wenig Sinn 

Du musst deine ISR in der Interrupt Vector Table (IVT) registrieren. Das ist auf dem Arbeitsblatt diese Tabelle der Form

[int# | offset | segment]

Das Teil beginnt bei int 0 und erhöht dann immer um 1 bis 255 (also insgesamt 256 Einträge). Wenn du jetzt int 20h aufrufst, wird der 32. Eintrag aus dieser Tabelle geladen (20h = 2 * 16 = 32).

Fangen wir mal mit der ISR an. Du musst zuerst mal alle Register manuell sichern und wiederherstellen (pusha und Konsorten), da das erwartet wird (ein paar werden durch den int bzw. iret Aufruf gesichert/wiederhegestellt).


```
my_isr:
 ; Register sichern
 pusha
 push gs
 push fs
 push es
 push ds

 ; dein Code zur Ausgabe

 ; Register wiederherstellen
 pop ds
 pop es
 pop fs
 pop gs
 popa

 ; zurück aus ISR
 iret
```
Kommen wir nun zum setzen des Zeigers in der IVT:

```
; ax = 0
xor ax, ax
; ES mit ax = 0 initialisieren
mov es, ax

; Adresse deiner ISR holen
mov dx, my_isr
; Adresse der ISR setzen (Offset-Teil)
; Die IVT liegt ab Adresse 0 und wir sind am 32. Eintrag interessiert.
; Jeder Eintrag ist 4 Byte groß, also müssen wir 32 * 4 Byte weit springen
; Segment 0 : Offset 32 * 4 = Speicheradresse des Offset-Teils von int 20h
; 2 Byte weiter liegt dann die Segmentadresse (siehe unten)
mov [es:20h * 4], dx

; Code Segment Register in ax kopieren
mov ax, cs
; CS setzen (Segment-Teil)
mov [es:20h * 4 + 2], ax

; Deinen neuen Interrupt aufrufen
int 20h
```
Das ist jetzt aber ungetestet aus dem Kopf. Bräuchte ich jetzt auch eine Testumgebung für 

Edit: Das ganze geht aber auch über DOS Interrupts, aber ich weiß nicht, ob du die verwenden sollst/darfst.


----------



## boss3D (13. Dezember 2012)

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

Ok, vielen Dank vorerst! Wenn man Erklärungen dazu hat, ist das ganze durchaus verständlich ... 

Ich weiß halt jetzt nicht, ob ich in meinem Code eh alles in der richtigen Reihenfolge eingefügt habe. Ich denke aber schon, dass es so in etwa hinkommen müsste:

```
[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
    
    mov AL,1
    mov  BH,0
    mov  BL,0x1F
    mov CX,bsy1len
    mov ESI, 0x0500
    mov ES, ESI
    mov BP,bsy1msg
    
    int 0x10
    
    my_isr:
    ; Register sichern
    pusha
    push gs
    push fs
    push es
    push ds

    ; dein Code zur Ausgabe
    mov byte [es:0x00], 'I'
    mov byte [es:0x01], 00011111b

    mov byte [es:0x02], 'S'
    mov byte [es:0x03], 00011111b

    mov byte [es:0x04], 'R'
    mov byte [es:0x05], 00011111b

    mov byte [es:0x06], 'H'
    mov byte [es:0x07], 00011111b

    mov byte [es:0x08], '2'
    mov byte [es:0x09], 00011111b

    mov byte [es:0x0A], '0'
    mov byte [es:0x0B], 00011111b

    mov byte [es:0x0C], 'h'
    mov byte [es:0x0D], 00011111b

    ; Register wiederherstellen
    pop ds
    pop es
    pop fs
    pop gs
    popa

    ; zurück aus ISR
    iret
    
    ; ax = 0
    xor ax, ax
    ; ES mit ax = 0 initialisieren
    mov es, ax

    ; Adresse deiner ISR holen
    mov dx, my_isr
    ; Adresse der ISR setzen (Offset-Teil)
    ; Die IVT liegt ab Adresse 0 und wir sind am 32. Eintrag interessiert.
    ; Jeder Eintrag ist 4 Byte groß, also müssen wir 32 * 4 Byte weit springen
    ; Segment 0 : Offset 32 * 4 = Speicheradresse des Offset-Teils von int 20h
    ; 2 Byte weiter liegt dann die Segmentadresse (siehe unten)
    mov [es:20h * 4], dx

    ; Code Segment Register in ax kopieren
    mov ax, cs
    ; CS setzen (Segment-Teil)
    mov [es:20h * 4 + 2], ax

    ; Neuen Interrupt aufrufen
    int 20h

endloop:
    jmp endloop

bsy1msg     db     13,10,"BSY1 via INT 10"
bsy1len     equ     $ - bsy1msg
```
^^ Ich geh's gleich mal testen. Dann berichte ich.

*[EDIT]*
Irgendwas hat's! Ich kann die Bootdiskette für die Testumgebung MiniOS nicht schreiben ...




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        



Hier die übrigen Codes, die dafür nötig sind:
*Makefile:*


Spoiler





```
all : boot write disk

boot    : boot.asm sect2.asm
        nasm -o boot boot.asm
        nasm -o sect2 sect2.asm

disk    : write
        ./write boot sect2

write   : write.c
        cc write.c -o write

clean   :
        rm boot sect2 write
```



*write.c:*


Spoiler





```
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
        char bootsector[512];
        int floppy, bootcode;
        if (argc < 2) {
                printf("Syntax: %s codefile\n",argv[0]);
                return 0;
        }
        bootcode = open(argv[1], O_RDONLY);
        if( bootcode != -1 ) {
                read(bootcode, bootsector, 510);
                close(bootcode);
                bootsector[510] = 0x55;
                bootsector[511] = 0xaa;
                floppy = open("/dev/fd0", O_RDWR);
                if ( floppy != -1) {
                        lseek(floppy, 0, SEEK_SET);
                        write(floppy, bootsector, 512);
                        printf("%s auf FDD geschrieben\n", argv[1]);
                        close(floppy);
                } else {
                        printf("Fehler beim Schreiben\n");
                }
        } else {
                printf("Fehler beim Lesen\n");
        }
        
        bootcode = open(argv[2], O_RDONLY);
        if( bootcode != -1 ) {
                read(bootcode, bootsector, 512);
                close(bootcode);
                floppy = open("/dev/fd0", O_RDWR);
                if ( floppy != -1) {
                        lseek(floppy, 512, SEEK_SET);
                        write(floppy, bootsector, 512);
                        printf("%s auf FDD geschrieben\n", argv[2]);
                        close(floppy);
                } else {
                        printf("Fehler beim Schreiben\n");
                }
        } else {
                printf("Fehler beim Lesen\n");
        }
}
```



*boot.asm:*


Spoiler





```
[BITS 16]
org 0x7c00

start:
    mov ax,0xB800
    mov es,ax

    mov byte [es:0x00], 'H' 
    mov byte [es:0x01], 00011111b

    mov byte [es:0x02], 'a'
    mov byte [es:0x03], 00011111b

    mov byte [es:0x04], 'l'
    mov byte [es:0x05], 00011111b

    mov byte [es:0x06], 'l'
    mov byte [es:0x07], 00011111b

    mov byte [es:0x08], 'o'
    mov byte [es:0x09], 00011111b

    mov byte [es:0x0A], ' '
    mov byte [es:0x0B], 00011111b

    mov byte [es:0x0C], 'W'
    mov byte [es:0x0D], 00011111b

    mov byte [es:0x0E], 'e'
    mov byte [es:0x0F], 00011111b

    mov byte [es:0x10], 'l'
    mov byte [es:0x11], 00011111b

    mov byte [es:0x12], 't'
    mov byte [es:0x13], 00011111b

    mov ax,sect2dest
    mov es,ax
    mov ah,0x02
    mov al,1
    mov ch,0
    mov cl,2
    mov dh,0
    mov dl,0
    mov bx,0

    int 0x13

    sect2dest equ 0x0500

    jmp sect2dest
```



^^ Der Fehler wird aber in sect2.asm sein, oder? Write.c ist es ziemlich sicher nicht, weil ich das genau so schon in den vorigen Aufgaben verwendet habe (also in b und c) und da hat's auch gepasst.


----------



## bingo88 (13. Dezember 2012)

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

Eigentlich ist die ISR eine separate "Funktion" . In deinem geposteten Code rufst du die aber direkt auf und das ist nicht Sinn der Sache.


```
; Deine "main" Funktion
start:
 ; ...

 ; ISR in IVT einsetzen
 ; ...

 ; ISR aufrufen
 int 20h
  
 ; ...

endloop:
 ; Endlosschleife; Nachfolgender Code (my_isr) wird nicht mehr ausgeführt
 jmp endloop


; ISR für Interrupt 20h
my_isr:
 ; ISR implementierung
```

Edit: Dein write Programm schmeißt die Fehlermeldung, also musst du da gucken, was los ist. Die Binaries scheinen erstellt worden zu sein... Da du aber mit Linux arbeitest, kannst du auch einfach mit dd das Image auf die floppy schreiben


----------



## boss3D (13. Dezember 2012)

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

Hab's jetzt so umgebastelt, bekomme aber immer noch den Error 22; siehe Screenshot oben im EDIT:

```
[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
    
    mov AL,1
    mov BH,0
    mov BL,0x1F
    mov CX,bsy1len
    mov ESI, 0x0500
    mov ES, ESI
    mov BP,bsy1msg
    
    int 0x10
    
    int 20h

endloop:
    jmp endloop

my_isr:
    pusha
    push gs
    push fs
    push es
    push ds

    mov byte [es:0x00], 'I'
    mov byte [es:0x01], 00011111b

    mov byte [es:0x02], 'S'
    mov byte [es:0x03], 00011111b

    mov byte [es:0x04], 'R'
    mov byte [es:0x05], 00011111b

    mov byte [es:0x06], 'H'
    mov byte [es:0x07], 00011111b

    mov byte [es:0x08], '2'
    mov byte [es:0x09], 00011111b

    mov byte [es:0x0A], '0'
    mov byte [es:0x0B], 00011111b

    mov byte [es:0x0C], 'h'
    mov byte [es:0x0D], 00011111b

    pop ds
    pop es
    pop fs
    pop gs
    popa

    iret
    
    xor ax, ax
    mov es, ax

    mov dx, my_isr
    mov [es:20h * 4], dx

    mov ax, cs
    
    mov [es:20h * 4 + 2], ax
    
    jmp endloop
    
bsy1msg     db     13,10,"BSY1 via INT 10"
bsy1len     equ     $ - bsy1msg
```


----------



## bingo88 (13. Dezember 2012)

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

Ja wie ich sagte, der Fehler wird in deinem Write.c ausgegeben, wenn ich das jetzt so auf die Schnelle richtig gesehen habe, kann der dein Floppy net öffnen (/dev/fd0). Hast du es gemountet?


----------



## boss3D (13. Dezember 2012)

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

Laut VMWare Player Settings für das virtuelle OS ist die Floppy bereits eingebungen?! 




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        



Wenn ich in putty sage "mount /dev/fd0" kommt: "mount: you must specify the file system type" ...


----------



## bingo88 (13. Dezember 2012)

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

Ok, in VMware ist es gemountet (das meinte ich mit mounten ^^). Da du ja in write.c direkt mit /dev/fd0 zugreifst, brauchst du das fd0 eigentlich nicht zusätzlich mit mount mounten. Kannst du mal bei dem if (floppy != -1) mit errno den Fehlercode ermitteln? Der bekommt ja wohl das Device nicht geöffnet


----------



## boss3D (13. Dezember 2012)

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

Errno 6 ... vorrausgesetzt, ich habe wirklich das gemacht, was du wolltest ...




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        




```
#include <errno.h>

[...]

printf("Fehler beim Schreiben. Errno: %d\n", errno);
```


----------



## bingo88 (13. Dezember 2012)

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

Errno 6 aka ENXIO steht für "No such device or address" - also stimmt was mit dem Device nicht. Mal Neustarten bzw. Image-File neu mounten?

Update: Ich habe es gerade gesehen. Du hast keinen Haken bei "Connected" gesetzt, ergo ist das Floppy nicht verbunden


----------



## boss3D (13. Dezember 2012)

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

Habe jetzt mal sudo reboot eingegeben und mir damit einen schönen putty network error geholt ... 

Irgendwas läuft hier sowieso falsch! Wenn ich im VMWare Player das "Entwicklungsumgebungs-OS" (BSY1UE32) resette, zeigt er mir das an, was eigentlich nur im "Test-OS" (MiniOS) zu sehen sein sollte. Da greift wohl irgendwas vom einen auf was vom anderen zu, was nicht sein sollte:




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        



Irgendwie kommt mir das so vor, als würde BSY1UE32 selbst von der virtuellen Floppy starten, die eigentlich als virtuelles Bootmedium für MiniOS gedacht ist ...

Wenn ich aber in den Settings von BSY1UE32 anstelle von Floppy auto detect einstelle, kommt folgende Meldung:




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        

 


			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.


----------



## bingo88 (13. Dezember 2012)

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

Ich habe meinen letzen Post editiert, das Floppy war nicht eingebunden. Hast du bei der VM das Image gemountet? Der bootet vermutlich von Floppy.


----------



## boss3D (13. Dezember 2012)

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

^^ Zu dem selben Schluss bin ich auch gerade gekommen.   Was genau meinst du? Der VMWare Player startet doch das Image?! Siehe zweiter Screenshot oben ...


----------



## bingo88 (13. Dezember 2012)

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

Ja, wenn das Floppy beim booten gemountet ist, startet der evtl. vom Floppy. So wie du das bei deinem Rechner im BIOS auch einstellen kannst (Boot order).


----------



## boss3D (13. Dezember 2012)

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

Ja, aber die Bootreihenfolge kann ich in den VMWare Player Settings ja nicht ändern, oder? Ich kann ihm nur sagen, welches Image er starten soll und da ist eh schon korrekterweise das von BSY1UE32 eingestellt. K.A. wovon genau der bootet. Er hat halt sein Image ...  

Die Floppy ist jetzt bei BSY1UE32 jedenfalls weg (habe ja auf auto detect gestellt). Kommt aber trotzdem immer noch unser Errno 6 ...


----------



## bingo88 (13. Dezember 2012)

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

Zu VMware player kann ich jetzt nichts sagen, ich nutze selbst VirtualBox und da kann man das einstellen. Wenn du das Image erst mountest, wenn die Kiste schon läuft, müsste es aber auch gehen.


----------



## boss3D (13. Dezember 2012)

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

Hm ... ich könnte das BSY1UE32 noch einmal aus dem VMWare Player löschen. Dann eine neue virtuelle Maschine mit dem selben Namen erstellen, diese starten und dann vielleicht das Image einbinden. Muss ich probieren. Melde mich dann noch einmal ...

*[EDIT]*
Ne, da kommt auch nichts gescheites dabei raus.


----------



## bingo88 (13. Dezember 2012)

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

Ich glaube du kannst die Boot order im BIOS der VM einstellen (F2 beim Start?)


----------



## boss3D (13. Dezember 2012)

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

Rein kommen tue ich, aber ändern kann ich da nichts. Mit +/- sollte ich ja die Reihenfolge ändern können, aber es passiert nichts. Wie sollte es denn korrekterweise ausschauen? HardDisk ganz oben?!




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.


----------



## bingo88 (13. Dezember 2012)

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

Ja HDD ganz oben bei der Dev-VM. Hast du + auf dem Numpad oder auf der "normalen" Testatur versucht?


----------



## boss3D (13. Dezember 2012)

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

Nur auf der normalen Tastatur. Mit dem Numpad + habe ich jetzt die HDD ganz nach oben gebracht. Hat aber am Problem nichts geändert. Sobald ich "make" eingebe, fängt er an mit dem Erstellen der virtuellen Boot-Diskette für MiniOS an und bricht halt dann irgendwann wieder mit dem Errno 6 bzw. Error 22 ab ... 

In den Settings scheint jetzt zumindest das richtige File drinnen zu sein:




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        



Floppy auf "auto detect" stimmt?


----------



## bingo88 (13. Dezember 2012)

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

Du musst bei Floppy dann aber die Zieldatei auswählen, wenn du auf das Laufwerk zugreifen willst (connect nicht vergessen). Auto detect sucht AFAIK nach einem physischen Laufwerk (falls du noch ein Floppy hast ^^)


----------



## boss3D (14. Dezember 2012)

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

^^ Okay, danke! Das hat uns einen großen Schritt weitergebracht ...

Die virtuelle Boot-Diskette wird jetzt richtig geschrieben, aber die Ausgabe in der Testumgebung "MiniOS" stimmt noch nicht:




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        



Es soll ja am Ende dann statt dem Interrupt 10 Blabla das "ISR20h" ausgegeben werden. Das "Hallo Welt" und "Bsy bla" kann schon da bleiben. Das "ISR20h" fehlt jetzt eben noch. Vermutlich liegt's daran, dass ich im sect2.asm file immer noch ein Durcheinander habe und er nicht zur Ausgabe kommt?!

```
[BITS 16]
start:

    mov ax,0xB800
    mov es,ax
    
    mov AH, 02h
    mov BH,0
    int 0x10    

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

my_isr:
    pusha
    push gs
    push fs
    push es
    push ds

    mov byte [es:0x00], 'I'
    mov byte [es:0x01], 00011111b
    mov byte [es:0x02], 'S'
    mov byte [es:0x03], 00011111b
    mov byte [es:0x04], 'R'
    mov byte [es:0x05], 00011111b
    mov byte [es:0x06], 'H'
    mov byte [es:0x07], 00011111b
    mov byte [es:0x08], '2'
    mov byte [es:0x09], 00011111b
    mov byte [es:0x0A], '0'
    mov byte [es:0x0B], 00011111b
    mov byte [es:0x0C], 'h'
    mov byte [es:0x0D], 00011111b

    pop ds
    pop es
    pop fs
    pop gs
    popa

    iret
    
    xor ax, ax
    mov es, ax

    mov dx, my_isr
    mov [es:20h * 4], dx

    mov ax, cs
    
    mov [es:20h * 4 + 2], ax
    
    jmp endloop
    
bsy1msg     db     13,10,"BSY1 via INT 10"
bsy1len     equ     $ - bsy1msg
```
Muss ich nicht, wie in C, irgendwie nach int 20h auf die "Funktion" my_isr verweisen? Wenn er's der Reihe nach abarbeitet, würde er ja sonst in der Endlosschleife landen, bevor er die Funktion sieht, oder läuft das bei Assembler anders?

Hätte ja testweise probiert:

```
jmp my_isr
```
Aber dann hängt er an die Ausgabe in "MiniOS" nur ein "Operating System not found" an ...


----------



## bingo88 (14. Dezember 2012)

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

Mit dem Ausdruck int 20h rufst du deine ISR doch auf. int macht einen sog. Interrupt-Call und ruft die zur Nummer gehörende Funktion auf, da musst du nichts mit call oder jmp machen.

Dann hast du in deinem Code noch das Problem, dass du deine ISR zu spät registrierst, ich bin mir nicht mal sicher, ob dieser Code überhaupt ausgeführt wird. Du musst die Routine registrieren, bevor du den int 20h call machst, sonst klappt das natürlich nicht!


```
; Deine "main"
start:
    ; ...

    ; Register sichern (du hast ES mit einem anderen Wert initialisiert, den wollen wir nicht überschreiben)
    push ax
    push es

    ; ISR in IVT registrieren
    xor ax, ax
    mov es, ax

    mov dx, my_isr
    mov [es:20h * 4], dx
    mov ax, cs
    mov [es:20h * 4 + 2], ax

    ; register wiederherstellen
    pop es
    pop ax

    ; ISR aufrufen
    int 20h

    ; ...

endloop:
    jmp endloop



; Deine ISR
my_isr:
    ; ISR Code hierhin
    ; ...

    iret
```

Optional könnte man noch die alte ISR an 20h sichern, damit man die später wiederherstellen kann. War jetzt aber nicht gefordert, soweit ich das gesehen habe.


----------



## boss3D (14. Dezember 2012)

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

Mit folgendem Code erreiche ich aber nur (unabsichtlich), dass in der Ausgabe das "Hallo BSY" wegfällt, das aber dableiben soll:
	
	



```
[BITS 16]
start:

    mov ax,0xB800
    mov es,ax
    
    mov AH, 02h
    mov BH,0
    
    int 0x10    

    mov AH,13h
    
    mov AL,1
    mov BH,0
    mov BL,0x1F
    mov CX,bsy1len
    mov ESI, 0x0500
    mov ES, ESI
    mov BP,bsy1msg
    
    int 0x10
    
    pusha
    push gs
    push fs
    push es
    push ds
    
    xor ax, ax
    mov es, ax
    
    mov dx, my_isr
    mov [es:20h * 4], dx
    mov ax, cs    
    mov [es:20h * 4 + 2], ax
    
    pop ds
    pop es
    pop fs
    pop gs
    popa
    
    int 20h

endloop:
    jmp endloop

my_isr:
    mov byte [es:0x00], 'I'
    mov byte [es:0x01], 00011111b
    mov byte [es:0x02], 'S'
    mov byte [es:0x03], 00011111b
    mov byte [es:0x04], 'R'
    mov byte [es:0x05], 00011111b
    mov byte [es:0x06], 'H'
    mov byte [es:0x07], 00011111b
    mov byte [es:0x08], '2'
    mov byte [es:0x09], 00011111b
    mov byte [es:0x0A], '0'
    mov byte [es:0x0B], 00011111b
    mov byte [es:0x0C], 'h'
    mov byte [es:0x0D], 00011111b

    iret
    
bsy1msg     db     13,10,"BSY1 via INT 10"
bsy1len     equ     $ - bsy1msg
```
Wahrscheinlich überschreiben wir den Inhalt eines Registers, in dem das "Hallo BSY" gespeichert ist?! Von "ISRH20h" sehe ich aber weiterhin nichts ...

Kann es sein, dass ich die Ausgabe dafür auch so wie bsy1msg und bsy1len machen müsste, oder liegt der Fehler immer noch in der start Funktion? Nur push/pop ax und es, so wie in deinem Code oben, ist jedenfalls zuwenig. Da kommt eine komplett sinnlose Ausgabe mit Sonderzeichen. Wir brauchen offenbar schon alle Register, die du in einem deiner Postings auf der ersten Seite in den Code getan hast. So, wie eben jetzt bei mir hier. Aber irgendwas hat's wohl noch ...


----------



## bingo88 (14. Dezember 2012)

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

Der Teil hier

```
pusha
push gs
push fs
push es
push ds

pop ds
pop es
pop fs
pop gs
popa
```
muss in deine ISR, siehe eins meiner ersten Postings. Das hat beim Registrieren der ISR keinen Sinn. Ich habe in meinem letzen Posting nur Code weggelassen, weil ich nicht so viel schreiben wollte, der muss aber dahin.

Die Ausgabe könntest du mal mit der anderen Variante probieren, da ich mir auch nicht sicher bin, ob die Speicherbereiche noch stimmen. Wenn du ES zu Beginn (bei start) mit der (Segment)Adresse des Videospeichers initialisierst, darf die natürlich nicht verloren gehen, der Registerinhalt muss also beim Registrieren der ISR gesichert werden (siehe mein letzes Posting). Außerdem kann es sein, dass wenn du bei Offset 0 im Videospeicher anfängst zu schreiben, er den bereits vorhanden Inhalt einfach überschreibt.


----------



## boss3D (14. Dezember 2012)

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




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        



^^ Hat wieder nichts geändert. Ich glaube mittlerweile wirklich, dass es an der Ausgabe selbst scheitert ...

Stellt sich nur die Frage: Wie genau umschreiben? Wenn ich die ganzen mov byte Zeilen in der ISR lösche, fällt ja diese komplett weg?! Ohne Ausgabe-Code bringt mir ja auch das push/pop nichts?! Und das kann's ja auch nicht sein. Die ISR muss auf jeden Fall dableiben.

Das mit dem Videospeicher ist ja das mit [es:offset]?! Ok, da muss ich mir halt dann noch überlegen, ab welchem Offset die vorigen Ausgaben zu Ende sind und dann diesen Offset+1 als Startoffset für unser "ISRH20h" nehmen. Aber vorher will ich das zumindest mal irgendwo ausgegeben haben.


----------



## bingo88 (14. Dezember 2012)

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

Das hier ist ein (zumindest bei mir) funktionierender Code für das setzen und Aufrufen der ISR.

```
[BITS 16]

ORG 0x7C00

start:
    xor ax, ax
    ; IVT beginnt bei 0. Das ist wichtig, wenn du ES mit der Videospeicher-Adresse initialisierst.
    ; Diese dann vorher mit push es sichern und nach dem setzen der ISR mittels pop es wiederherstellen.
    mov es, ax
    
    ; ISR setzen
    ; Interrupts aus
    cli
    ; int Nummer 20h wollen wir bearbeiten
    mov bx, 0x20
    ; Adresse x 4 da 4 Byte pro IVT Eintrag
    shl bx, 2
    ; Offsetadresse der ISR in IVT setzen
    mov [es:bx], word my_isr
    ; Segmentadresse der ISR in IVT setzen
    mov [es:bx + 2], ds
    ; Interrupts an
    sti
    
    ; ISR aufrufen
    int 0x20
    
endloop:
    jmp endloop


; INT 20h handler
my_isr:
    ; Register sichern
    pusha
    push gs
    push fs
    push es
    push ds
    
    ; Ausgabe
    mov ax, 0xB800
    mov es, ax
    
    mov byte [es:00], 'I'
    mov byte [es:01], 0x1F
    mov byte [es:02], 'S'
    mov byte [es:03], 0x1F  
    mov byte [es:04], 'R'
    mov byte [es:05], 0x1F
    mov byte [es:06], '2'
    mov byte [es:07], 0x1F    
    mov byte [es:08], '0'
    mov byte [es:09], 0x1F
    mov byte [es:10], 'h'
    mov byte [es:11], 0x1F
    
    ; Register wiederherstellen
    pop ds
    pop es
    pop fs
    pop gs
    popa
    
    ; ISR verlassen
    iret
    
; Magic Bytes schreiben
times 510 - ($ - $$) db 0
dw 0xAA55
```
Wenn du das mit nasm -f bin -o isr.img isr.asm übersetzt, kannst du bei VMware direkt die isr.img als Boot-Floppy verwenden.




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        



Ich habe versucht, deinen Code da drum zu basteln, aber schon ohne ISR bekomme ich keine Ausgabe, sondern nur einen blauen Streifen. Also habe ich es weggelassen


----------



## boss3D (14. Dezember 2012)

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

Assemblieren kann ich's noch, aber wie genau boote ich dann MiniOS vom img? Es wird lokal nicht gespeichert, also kann ich nicht einfach in den VMWare Player Settings das img auswählen. Ich hätte noch probiert, das img auf die Diskette zu schreiben (./write sect2 --> bei mir heißt es sect2.img), aber da kommt nur "No such file or directory" ...

Wir haben zum Assemblieren übrigens diese komplett sinnlose Anleitung bekommen:




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        



^^ Aber mit dem nasm Befehl hier geht gar nichts (ja, habe natürlich auf sect2.asm geändert). Nur mit deinem konnte ich es assemblieren.


----------



## bingo88 (14. Dezember 2012)

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

Ah ihr habt ne fertige VM... okay. Also ich habe mir in VirtualBox eine simple DOS VM angelegt, ohne Festplatte/CD, nur mit Floppy. Dann gebe ich in den Einstellungen die img Datei für das virtuelle Floppy an und dann bootet der davon. Deswegen benötige ich in meinem Code auch diese Magic Byte Klamotte. Bei deinem write.c wird das manuell gesetzt (bootsector[510]...). Du müsstest den Code eigentlich in eine Datei packen können, die du dann als sect2 verwendest. Dann musst du aber meinen Magic Byte Code wieder rauswerfen, den brauchst du dann nicht.

Den Teil hier brauchst du auf jeden Fall.

```
; das muss in deine "Hauptroutine", die vom Bootloader aufgerufen wird (in deine vorhanden sect2?)
    xor ax, ax
    ; IVT beginnt bei 0. Das ist wichtig, wenn du ES mit der Videospeicher-Adresse initialisierst.
    ; Diese dann vorher mit push es sichern und nach dem setzen der ISR mittels pop es wiederherstellen.
    mov es, ax
    
    ; ISR setzen
    ; Interrupts aus
    cli
    ; int Nummer 20h wollen wir bearbeiten
    mov bx, 0x20
    ; Adresse x 4 da 4 Byte pro IVT Eintrag
    shl bx, 2
    ; Offsetadresse der ISR in IVT setzen
    mov [es:bx], word my_isr
    ; Segmentadresse der ISR in IVT setzen
    mov [es:bx + 2], ds
    ; Interrupts an
    sti
    
    ; ISR aufrufen
    int 0x20
    
endloop:
    jmp endloop


; INT 20h handler
my_isr:
    ; Register sichern
    pusha
    push gs
    push fs
    push es
    push ds
    
    ; Ausgabe
    mov ax, 0xB800
    mov es, ax
    
    mov byte [es:00], 'I'
    mov byte [es:01], 0x1F
    mov byte [es:02], 'S'
    mov byte [es:03], 0x1F  
    mov byte [es:04], 'R'
    mov byte [es:05], 0x1F
    mov byte [es:06], '2'
    mov byte [es:07], 0x1F    
    mov byte [es:08], '0'
    mov byte [es:09], 0x1F
    mov byte [es:10], 'h'
    mov byte [es:11], 0x1F
    
    ; Register wiederherstellen
    pop ds
    pop es
    pop fs
    pop gs
    popa
    
    ; ISR verlassen
    iret
```


----------



## boss3D (14. Dezember 2012)

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

Mit diesem Code bekomme ich genau "Hallo Welt" ausgegeben, weil das wahrscheinlich in irgendwelchen Registern gespeichert ist, die wir aufrufen, aber sonst nichts. Ich bin am Ende mit meinem Latein ...

*sect2.asm*

```
[BITS 16]

org 0x7C00

start:

    xor ax, ax
    ; IVT beginnt bei 0. Das ist wichtig, wenn du ES mit der Videospeicher-Adresse initialisierst.
    ; Diese dann vorher mit push es sichern und nach dem setzen der ISR mittels pop es wiederherstellen.
    mov es, ax
    
    ; ISR setzen
    ; Interrupts aus
    cli
    ; int Nummer 20h wollen wir bearbeiten
    mov bx, 0x20
    ; Adresse x 4 da 4 Byte pro IVT Eintrag
    shl bx, 2
    ; Offsetadresse der ISR in IVT setzen
    mov [es:bx], word my_isr
    ; Segmentadresse der ISR in IVT setzen
    mov [es:bx + 2], ds
    ; Interrupts an
    sti
    
    ; ISR aufrufen
    int 0x20
    
endloop:
    jmp endloop


; INT 20h handler
my_isr:
    ; Register sichern
    pusha
    push gs
    push fs
    push es
    push ds
    
    ; Ausgabe
    mov ax, 0xB800
    mov es, ax
    
    mov byte [es:00], 'I'
    mov byte [es:01], 0x1F
    mov byte [es:02], 'S'
    mov byte [es:03], 0x1F  
    mov byte [es:04], 'R'
    mov byte [es:05], 0x1F
    mov byte [es:06], '2'
    mov byte [es:07], 0x1F    
    mov byte [es:08], '0'
    mov byte [es:09], 0x1F
    mov byte [es:10], 'h'
    mov byte [es:11], 0x1F
    
    ; Register wiederherstellen
    pop ds
    pop es
    pop fs
    pop gs
    popa
    
    ; ISR verlassen
    iret
```
Ja, wir haben 2 fertige virtuelle Maschinen:
1. BSY1UE32 --> Entwicklungsumgebung
2. MiniOS --> Testumgebung (startet von der virtuellen Diskette, die in BSY1UE32 beschrieben wird)

Zum Beschreiben der Diskette werden immer die files boot.asm, Makefile, sect2.asm und write.c und der Befehl "make" verwendet.

Hier noch einmal der Inhalt:
*boot.asm*


Spoiler





```
[BITS 16]
org 0x7c00

start:
    mov ax,0xB800
    mov es,ax

    mov byte [es:0x00], 'H' 
    mov byte [es:0x01], 00011111b

    mov byte [es:0x02], 'a'
    mov byte [es:0x03], 00011111b

    mov byte [es:0x04], 'l'
    mov byte [es:0x05], 00011111b

    mov byte [es:0x06], 'l'
    mov byte [es:0x07], 00011111b

    mov byte [es:0x08], 'o'
    mov byte [es:0x09], 00011111b

    mov byte [es:0x0A], ' '
    mov byte [es:0x0B], 00011111b

    mov byte [es:0x0C], 'W'
    mov byte [es:0x0D], 00011111b

    mov byte [es:0x0E], 'e'
    mov byte [es:0x0F], 00011111b

    mov byte [es:0x10], 'l'
    mov byte [es:0x11], 00011111b

    mov byte [es:0x12], 't'
    mov byte [es:0x13], 00011111b

    mov ax,sect2dest
    mov es,ax
    mov ah,0x02
    mov al,1
    mov ch,0
    mov cl,2
    mov dh,0
    mov dl,0
    mov bx,0

    int 0x13

    sect2dest equ 0x0500

    jmp sect2dest
```



*Makefile.asm*


Spoiler





```
all : boot write disk

boot    : boot.asm sect2.asm
        nasm -o boot boot.asm
        nasm -o sect2 sect2.asm

disk    : write
        ./write boot sect2

write   : write.c
        cc write.c -o write

clean   :
        rm boot sect2 write
```



*write.c*


Spoiler





```
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char* argv[]) {
        char bootsector[512];
        int floppy, bootcode;
        if (argc < 2) {
                printf("Syntax: %s codefile\n",argv[0]);
                return 0;
        }
        bootcode = open(argv[1], O_RDONLY);
        if( bootcode != -1 ) {
                read(bootcode, bootsector, 510);
                close(bootcode);
                bootsector[510] = 0x55;
                bootsector[511] = 0xaa;
                floppy = open("/dev/fd0", O_RDWR);
                if ( floppy != -1) {
                        lseek(floppy, 0, SEEK_SET);
                        write(floppy, bootsector, 512);
                        printf("%s auf FDD geschrieben\n", argv[1]);
                        close(floppy);
                } else {
                        printf("Fehler beim Schreiben\n");
                }
        } else {
                printf("Fehler beim Lesen\n");
        }
        
        bootcode = open(argv[2], O_RDONLY);
        if( bootcode != -1 ) {
                read(bootcode, bootsector, 512);
                close(bootcode);
                floppy = open("/dev/fd0", O_RDWR);
                if ( floppy != -1) {
                        lseek(floppy, 512, SEEK_SET);
                        write(floppy, bootsector, 512);
                        printf("%s auf FDD geschrieben\n", argv[2]);
                        close(floppy);
                } else {
                        printf("Fehler beim Schreiben\n");
                }
        } else {
                printf("Fehler beim Lesen\n");
        }
}
```



^^ Aus der boot.asm kommt das "Hallo Welt", das ist klar. Aber wo das "ISR20h" bei mir bleibt? K.A. ...


----------



## bingo88 (14. Dezember 2012)

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

So, ich habe mir jetzt mal einen Gesamtüberblick verschafft. Meine Vermutung ist momentan, dass es am Laden des 2. Sektors scheitert. Ich versuche mal, ob ich da was auf die Schnelle zusammengebastelt bekomme...


----------



## boss3D (14. Dezember 2012)

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

Naja, bei Aufgabe b und c musste ich ja auch schon 2 Sektoren laden und da hat das mit dem exakt selben write.c file funktioniert. Sieht man ja auf den Bildern in den vorderen Thread-Seiten, dass da mehrer Sachen ausgegeben werden --> "Hallo Welt", "Hallo BSY", "Interrupt 10 blabla". Das steht ja nicht alles im selben Sektor. Wieso es jetzt bei Aufgabe d plötzlich nicht mehr klappt, verstehe ich nicht. Der müsste ja nur das "Interrupt 10 bla" mit "ISR20h" überschreiben (siehe erstes Bild hier). Das "Hallo Welt" aus boot.asm kann ruhig dableiben und das "Hallo BSY" würde auch nichts machen ...


----------



## bingo88 (15. Dezember 2012)

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

Der Lade-Code funktioniert doch, der hängt bei der ISR. Den Grund dafür habe ich aber noch nicht gefunden...


----------



## bingo88 (15. Dezember 2012)

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

Na geht doch 
Ich hatte beim sect2 Code mittels ORG meine Basisadresse verbastelt, deswegen hat der irgendwie die falsche ISR aufgerufen bzw. ist irgendwo im Speicher gelandet.

boot.asm

```
[BITS 16]
[ORG 0x7C00]

jmp 0x0:start

start:  
    ; Zweiten Sektor lesen
    mov ax, sect2dest    ; Segmentadresse
    mov es, ax
    xor bx, bx
    mov ah, 0x02        ; Sektor lesen
    mov al, 0x01        ; 1 Sektor lesen
    mov ch, 0x00        ; Spur 0
    mov cl, 0x02        ; Sektor 2
    mov dh, 0x00        ; Kopf 0
    mov dl, 0x00        ; Laufwerk 0
    int 0x13
    
    ; Umgebung für Sektor 2 anpassen
    mov ax, sect2dest
    mov es, ax
    mov ds, ax
    
    jmp sect2dest:0x0    ; Programm aus Sektor 2 ausführen
    
; Zieladresse
sect2dest equ 0x1000

; Magic Bytes
times 510 - ($ - $$) db 0
dw 0xAA55
```
sect2.asm

```
[BITS 16]

sect2:
    push es            ; ES sichern
    
    ; ISR setzen
    xor ax, ax                    ; AX = 0
    mov es, ax                    ; ES = 0 (für IVT)
    mov bx, 0x20                ; int 20h
    shl bx, 2                    ; Adresse x 4, da 4 Byte pro IVT Eintrag
    mov ax, isr_20h                ; Adresse der ISR in AX speichern
    cli                            ; Interrupts aus
    mov [es:bx], ax                ; Adresse der ISR in IVT setzen
    mov [es:bx + 2], cs            ; Segment der ISR in IVT setzen
    sti                            ; Interrupts an
    
    pop es        ; ES wiederherstellen
    
    int 0x20    ; ISR aufrufen
    
halt:
    cli            ; Interrupts aus
    hlt            ; Halt
    jmp halt    ; just in case


; INT 20h handler
isr_20h:
    ; Register sichern
    push ax
    push es
    
    ; Ausgabe
    mov ax, 0xB800
    mov es, ax
    
    mov byte [es:00], 'I'
    mov byte [es:01], 0x1F
    mov byte [es:02], 'S'
    mov byte [es:03], 0x1F
    mov byte [es:04], 'R'
    mov byte [es:05], 0x1F
    mov byte [es:06], '2'
    mov byte [es:07], 0x1F
    mov byte [es:08], '0'
    mov byte [es:09], 0x1F
    mov byte [es:10], 'h'
    mov byte [es:11], 0x1F
    
    ; Register wiederherstellen
    pop es
    pop ax
    
    ; ISR verlassen
    iret

; Rest des Sektors füllen (auf 512 Byte gesamt)
times 512 - ($ - $$) db 0
```
build.bat (im Prinzip was deine write.c macht; erzeugt das Floppy Image als minios.img)

```
@echo off

echo Erzeuge boot...
nasm -f bin -o boot boot.asm

echo Erzeuge sect2...
nasm -f bin -o sect2 sect2.asm

echo Erzeuge Image-Datei...
copy /B /Y boot + sect2 minios.img
```
Damit funktioniert es nun bei mir. Die build.bat brauchst du vermutlich nicht, die habe ich nur so der Vollständigkeit halber dazu gepackt.


----------



## boss3D (15. Dezember 2012)

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

Huch, wenn ich deine obige boot.asm und sect2.asm verwende, schließt sich das MiniOS Fenster gleich nach dem Start. Ich sehe die Ausgabe gar nicht ...

Ich versuche gerade zu verstehen, was an deiner jetzigen boot.asm so viel anders ist, als an meiner. Zeigst du nicht ziemlich wild im Speicher herum? Ich dachte, ich brauche nie mehr, als die 0xB800 Adresse für den Videospeicher und die 0x0500 für den Sektor 2?!

*[EDIT]*
Habe noch ein Bisschen herumgetestet: Mit meiner boot.asm kommt nur "Hallo Welt", ergo wird da wohl nie auf den Sektor 2 verwiesen?! Bei deiner boot.asm schließt sich bei mir sofort das MiniOS Fenster bevor ich überhaupt eine Ausgabe sehe. K.A., was da falsch ist. 

Ideal wäre es natürlich wenn wir einfach nur meine boot.asm um das nötige Zeug ergänzen könnten, anstatt einer komplett neuen zu verwenden, weil ja das "Hallo Welt" nicht verschwinden soll. Ich versuche jetzt mal, das "nötige", soweit ich es erkenne, aus deiner boot.asm zu nehmen und in meine reinzubasteln ...


----------



## bingo88 (15. Dezember 2012)

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

Dass ich 0x1000 statt 0x0500 geschrieben habe, hat eigentlich keinen speziellen Grund, ist vom Rumprobieren übrig geblieben ^^
Abgesehen davon müsste die boot.asm eigentlich ziemlich gleich geblieben sein, zumindest habe ich daran nicht viel gemacht (außer Kommentare + Reihenfolge geändert vielleicht).
Das jmp sieht noch anders aus, macht aber das gleiche wie bei dir. Ich wollte nur sichergehen, dass ich auch zur richtigen Adresse springe, also zu Segment 0x1000, Offset 0x0. Schließlich habe ich da ja den 2. Sektor hingeladen. Zu dem jmp sect2dest:0x0 - siehe meinen nächsten Post. Hätte mir eigentlich hier schon auffallen müssen ^^

An der sect2 habe ich mehr geändert. Kannst du vielleicht noch mal den Code posten, der jetzt nicht funktioniert? Ich habe zwischenzeitlich mal das Phänomen gehabt, dass die VM nach der Ausgabe direkt einen Neustart gemacht hat. Allerdings war da noch die falsche ISR beteiligt.

Edit: Hast du meine boot.asm mit deiner sect2.asm kombiniert? Du musst bei der sect2.asm das ORG entfernen, sonst funktioniert die ISR auf jeden Fall nicht. Das war zumindest bei mir der Fehler. Und man braucht bei der ISR wohl auch nur die Register sichern, die man tatsächlich nutzt (siehe meine sect2.asm).


----------



## boss3D (15. Dezember 2012)

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

Ausgabe: "Hallo Welt" (aus boot.asm) ...

*boot.asm*


Spoiler





```
[BITS 16]
org 0x7c00

start:
    mov ax,0xB800
    mov es,ax
    
    mov byte [es:0x00], 'H' 
    mov byte [es:0x01], 00011111b
    mov byte [es:0x02], 'a'
    mov byte [es:0x03], 00011111b
    mov byte [es:0x04], 'l'
    mov byte [es:0x05], 00011111b
    mov byte [es:0x06], 'l'
    mov byte [es:0x07], 00011111b
    mov byte [es:0x08], 'o'
    mov byte [es:0x09], 00011111b
    mov byte [es:0x0A], ' '
    mov byte [es:0x0B], 00011111b
    mov byte [es:0x0C], 'W'
    mov byte [es:0x0D], 00011111b
    mov byte [es:0x0E], 'e'
    mov byte [es:0x0F], 00011111b
    mov byte [es:0x10], 'l'
    mov byte [es:0x11], 00011111b
    mov byte [es:0x12], 't'
    mov byte [es:0x13], 00011111b

    mov ax,sect2dest
    mov es,ax
    mov ah,0x02
    mov al,1
    mov ch,0
    mov cl,2
    mov dh,0
    mov dl,0
    mov bx,0

    int 0x13

    sect2dest equ 0x0500

    jmp sect2dest
```



*sect2.asm*


Spoiler





```
[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   
    
halt:
    cli          
    hlt        
    jmp halt  

isr_20h:
    push ax
    push es
    
    mov ax, 0xB800
    mov es, ax
    
    mov byte [es:00], 'I'
    mov byte [es:01], 0x1F
    mov byte [es:02], 'S'
    mov byte [es:03], 0x1F
    mov byte [es:04], 'R'
    mov byte [es:05], 0x1F
    mov byte [es:06], '2'
    mov byte [es:07], 0x1F
    mov byte [es:08], '0'
    mov byte [es:09], 0x1F
    mov byte [es:10], 'h'
    mov byte [es:11], 0x1F
    
    pop es
    pop ax
    
    iret

times 512 - ($ - $$) db 0
```



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

Ausgabe: nicht sichtbar. Fenster der virtuellen Maschine "MiniOS" schließt sich sofort nach Start ...

*boot.asm*


Spoiler





```
[BITS 16]
[ORG 0x7C00]

jmp 0x0:start

start:  
    mov ax, sect2dest   
    mov es, ax
    xor bx, bx
    mov ah, 0x02      
    mov al, 0x01   
    mov ch, 0x00       
    mov cl, 0x02    
    mov dh, 0x00     
    mov dl, 0x00        
    int 0x13
    
    mov ax, sect2dest
    mov es, ax
    mov ds, ax
    
    jmp sect2dest:0x0    
    
sect2dest equ 0x1000

times 510 - ($ - $$) db 0
dw 0xAA55
```



*sect2.asm*


Spoiler





```
[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   
    
halt:
    cli          
    hlt        
    jmp halt  

isr_20h:
    push ax
    push es
    
    mov ax, 0xB800
    mov es, ax
    
    mov byte [es:00], 'I'
    mov byte [es:01], 0x1F
    mov byte [es:02], 'S'
    mov byte [es:03], 0x1F
    mov byte [es:04], 'R'
    mov byte [es:05], 0x1F
    mov byte [es:06], '2'
    mov byte [es:07], 0x1F
    mov byte [es:08], '0'
    mov byte [es:09], 0x1F
    mov byte [es:10], 'h'
    mov byte [es:11], 0x1F
    
    pop es
    pop ax
    
    iret

times 512 - ($ - $$) db 0
```


----------



## bingo88 (15. Dezember 2012)

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

Also dein geposteter Code funktionierte bei mir auch nicht, der blieb bei mir nach Hallo Welt hängen. Den Fehler habe ich aber gefunden, dein jmp in den geladenen Sektor funktionierte nicht (Segment-Offset-Adressierung sei dank ). Das habe ich auch leider irgendwie übersehen, sonst hätte ich schon früher was gesagt (mea culpa...). Mit

```
jmp sect2dest ; sect2dest = 0x0500
```
springst du an Adresse 0x0500, wir haben den 2. Sektor aber an Adresse 0x0500:0x0 geladen, d. h. 0x0500 * 0x10 + 0x0 = 0x5000 effektiv. Die Speicheradresse berechnet sich bei Segment-Offset-Adressierung nämlich zu Segment * 16 + Offset (16 dezimal = 10 Hex).

Wenn ich in deinem Code das jmp also durch

```
jmp sect2dest:0x0
```
ersetze, wird auch der Code aus sect2 aufgerufen.

Zum Fehler 2: Kannst du mal bitte das int 0x20 in sect2 auskommentieren? Damit können wir feststellen, ob der Fehler in der ISR selbst liegt. Wenn das nicht funktioniert, bitte mal folgendes probieren

```
halt:
    ;cli
    ;hlt
    jmp halt
```
Das ist die Endlosschleife von vorher (die 100% CPU Last erzeugt). Mit dem hlt Befehl wird die CPU "angehalten", bis ein Interrupt auftritt (was ich hier aber mit cli teilweise unterdrückt habe, NMIs können aber immer noch auftreten -> deswegen noch das jmp halt).


----------



## boss3D (16. Dezember 2012)

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

Mit folgenden Codes habe ich jetzt diese Ausgabe bekommen:




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        



^^  Wenn wir's jetzt nur noch schaffen würden, die Startausgabeadresse von "ISR20h" so zu ändern, dass er das unter "Hallo Welt" schreibt, anstatt dieses zu überschreiben, wäre ich komplett glücklich ...

*boot.asm*


Spoiler





```
[BITS 16]
org 0x7c00

start:
    mov ax,0xB800
    mov es,ax

    mov byte [es:0x00], 'H' 
    mov byte [es:0x01], 00011111b
    mov byte [es:0x02], 'a'
    mov byte [es:0x03], 00011111b
    mov byte [es:0x04], 'l'
    mov byte [es:0x05], 00011111b
    mov byte [es:0x06], 'l'
    mov byte [es:0x07], 00011111b
    mov byte [es:0x08], 'o'
    mov byte [es:0x09], 00011111b
    mov byte [es:0x0A], ' '
    mov byte [es:0x0B], 00011111b
    mov byte [es:0x0C], 'W'
    mov byte [es:0x0D], 00011111b
    mov byte [es:0x0E], 'e'
    mov byte [es:0x0F], 00011111b
    mov byte [es:0x10], 'l'
    mov byte [es:0x11], 00011111b
    mov byte [es:0x12], 't'
    mov byte [es:0x13], 00011111b

    mov ax,sect2dest
    mov es,ax
    mov ah,0x02
    mov al,1
    mov ch,0
    mov cl,2
    mov dh,0
    mov dl,0
    mov bx,0

    int 0x13

    jmp sect2dest:0x0
    
    sect2dest equ 0x0500
```



*sect2.asm*


Spoiler





```
[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:00], 'I'
    mov byte [es:01], 0x1F
    mov byte [es:02], 'S'
    mov byte [es:03], 0x1F
    mov byte [es:04], 'R'
    mov byte [es:05], 0x1F
    mov byte [es:06], '2'
    mov byte [es:07], 0x1F
    mov byte [es:08], '0'
    mov byte [es:09], 0x1F
    mov byte [es:10], 'h'
    mov byte [es:11], 0x1F
    
    pop es
    pop ax
    
    iret
```



Das int 0x20 musste ich gar nicht auskommentieren, meine Endlosschleife konnte ich auch drinnen lassen und die time Zeile am Ende von sect2.asm, was auch immer die machen hätte sollen, brauche ich scheinbar auch nicht. 

Vielen Dank schon mal soweit! Wie gesagt, würde ich nur noch gerne die Ausgabe ein Bisschen verändert haben, dass beides lesbar ist und dann hätte ich noch ein paar Verständnisfragen zum Code selbst.
Das mit der Ausgabe probiere ich eh gleich selber. Mal schauen, wie weit ich kommen ...


----------



## bingo88 (16. Dezember 2012)

*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).


----------



## boss3D (16. Dezember 2012)

*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:

```
[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 ...


----------



## bingo88 (16. Dezember 2012)

*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.

```
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.


----------



## boss3D (16. Dezember 2012)

*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:


Spoiler





```
[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":




			Dieser Inhalt steht nur eingeloggten Mitgliedern zur Verfügung.
        



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:

```
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?


----------



## bingo88 (16. Dezember 2012)

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



boss3D schrieb:


> 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.



boss3D schrieb:


> 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.



boss3D schrieb:


> 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).



boss3D schrieb:


> 3. Wie komme ich auf all das hier:
> 
> ```
> mov ah,0x02
> ...


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.



boss3D schrieb:


> 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  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  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.


----------



## boss3D (17. Dezember 2012)

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

Ok, vielen Dank!  

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.


----------



## bingo88 (17. Dezember 2012)

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

Alle klar


----------



## boss3D (12. Januar 2013)

*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. 

```
[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?! 
--------------------

```
[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


----------



## freecrac (12. Januar 2013)

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



boss3D schrieb:


> 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


----------



## boss3D (13. Januar 2013)

*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:

```
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 ...


----------



## bingo88 (14. Januar 2013)

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



boss3D schrieb:


> BTW: Kongratulation zu den 3k


 Danke 

Paging und Protected Mode - schön 
Ich versuche morgen mal was dazu zu schreiben, heute ist mir das zu spät, zumal ich auch von einer dicken Erkältung gequält werde


----------



## bingo88 (14. Januar 2013)

*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 


```
;<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>
```


----------



## freecrac (14. Januar 2013)

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



bingo88 schrieb:


> 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


----------



## bingo88 (14. Januar 2013)

*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!


----------



## boss3D (14. Januar 2013)

*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 ...

```
call startpaging 
... 
int 0x02
```


----------



## bingo88 (14. Januar 2013)

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



boss3D schrieb:


> 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:


```
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  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.


----------



## boss3D (14. Januar 2013)

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

Ah, ok. So ist das mit der Schleife klarer. 

Bzgl. Aufgabe 7 haben wir nur dieses 800 Seiten Intel PDF bekommen und irgendwo da drinnen wird's auch stehen, aber für mich ist das nur Fachchinesisch ...


----------



## bingo88 (15. Januar 2013)

*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.


----------

