[ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

B

boss3D

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

Capture1.JPG Capture2.JPG

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?!
Code:
[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!
 
Zuletzt bearbeitet:
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).

Code:
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:
Code:
; 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.
 
Zuletzt bearbeitet:
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:
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
    
    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 ...

Capture.JPG

Hier die übrigen Codes, die dafür nötig sind:
Makefile:
Code:
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:
Code:
#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:
Code:
[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.
 
Zuletzt bearbeitet:
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.

Code:
; 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
 
Zuletzt bearbeitet:
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:
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
    
    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
 
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?
 
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

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

Capture.JPG

Wenn ich in putty sage "mount /dev/fd0" kommt: "mount: you must specify the file system type" ...
 
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
 
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

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

Capture.JPG

Code:
#include <errno.h>

[...]

printf("Fehler beim Schreiben. Errno: %d\n", errno);
 
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 :ugly:
 
Zuletzt bearbeitet:
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

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

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:

Capture.JPG

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:

Capture2.JPG Capture1.JPG
 
Zuletzt bearbeitet:
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.
 
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

^^ Zu dem selben Schluss bin ich auch gerade gekommen. :ugly: Was genau meinst du? Der VMWare Player startet doch das Image?! Siehe zweiter Screenshot oben ...
 
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).
 
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 ... :huh:

Die Floppy ist jetzt bei BSY1UE32 jedenfalls weg (habe ja auf auto detect gestellt). Kommt aber trotzdem immer noch unser Errno 6 ...
 
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.
 
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.
 
Zuletzt bearbeitet:
AW: [ASSEMBLER] Interrupt in Interrupt Tabelle einfügen

Ich glaube du kannst die Boot order im BIOS der VM einstellen (F2 beim Start?)
 
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?!

Capture.JPG
 
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?
 
Zurück