Source Codes









next up previous
Nächste Seite: Betriebssystem - Generelles Aufwärts: Index Vorherige Seite: Index

http://www.talkortell.de/os4.html

http://www.talkortell.de/os5.html

http://www.talkortell.de/os6.html

http://www.talkortell.de/os7.html

http://www.talkortell.de/os8.html - die ersten wirklichen Code-Analysen, hier von ALLOC und FREE

http://www.talkortell.de/os9.html

http://www.talkortell.de/os10.html

http://www.talkortell.de/os11.html

So, wir schreiben an unserem Betriebssystem weiter

Quellen

Diese Seite bitte nicht vergessen:

https://www.henkessoft.de/OS_Dev/OS_Dev1.htm

Was wird hier beschriebe? Hier wird beschrieben, wie wir die Maschine in Gang kriegen. Das heißt beim Systemstart, erwartet uns ein schwarzer Bildschirm, oder aber, wenn wir ein Betriebssystem oder auch nur eine Startroutine programmiert haben, erwartet uns hier, eine Textaugabe, Konsole usw.
Das ist hier gut beschrieben.
Auf

http://www.talkortell.de/os/os.html

sind von mir sinnführende Schritte für ein Betriebssystem geschrieben. Eine Startroutine gibt Text auf dem Bildschirm aus. Oder fragt sogar nach Benutzereingaben. Was sie nicht tut, ist ein Dateisystem zu verwalten. Und Programme, zusammen mit Programmaufrufen. Das ist Teil eines Betriebssystems. Und dafür habe ich Schriite auf

http://www.talkortell.de/os/os.html

vorgestellt. Man glaubt es manchmal nicht gerne. Aber die wesentlichen Komponenten sind bei einem Betriebssytem, die Systemaufrufe: Das um zu setzen habe ich auf:

http://www.talkortell.de/os/os.html

begonnen. Dabei bin ich von folgenden entscheidenden Punkten ausgegangen: Ein Dateisystem kann sogar eine FAT haben, muss trotzdem kein FAT32, FAT16, ..., sein, denn es gibt eine Menge von Dateisystemen, die eine FAT erlauben, aber eine ganz eigene Implementierung sind. Das Dateisystem an sich, gibt es so oder so nicht. Zunächst gibt es erst Mal die bekannten: EXT, EXT2, EXT3, EXT4, SWAP, ReiserFS, FAT16, FAT32, NTFS, ... . Diese sind sehr bekannt und eine bestimmte Implementation. Nun gibt es aber eine Menge Dateisysteme. Bei den Bekannten alleine gibt es schon eine Menge, aber es gibt eine Menge Dateisysteme, die nicht implementiert wurden. Dabei muss man unterscheiden: Die Struktur. Eine FAT unterscheidet sich von anderen Dateisystemen, die keine FAT benutzen. Aber es gibt noch einen Unterschied. Bei der Implementierung eines Dateisystems, werden bestimmte Bytes und Konstanten verwendet, die an einer bestimmten Stelle stehen. Wie einer Datei Nutzinformationen enthalten sind. Man kann auch ein eigenes Dateisystem implementieren, was zum Beispiel eine FAT hat, aber ein anderes Dateisystem ist, weil bestimmte Nutzinformationen anders, gar nicht oder mehr gespeichert werden. Oder man implementiert vollkommen anderes Mir geht es jetzt darum, irgendein Dateisystem zu implementieren, das aber die Systemaurufe, wie oben beschrieben zulässt und eine Ordnerstruktur. D.h. hier werden Dateien in Ordnern, langfristig gespeichert:

Speichern eines Dateisystems

Ein Dateisystem lässt sich generell speichern, wie eine Datei. Vorsicht! Nicht unter Windows, aber unter Linux! Unter Linux lässt sich auch ein Windows-Dateisystem speichern, wie eine Datei.

Wie geht das? Na ja, die Daten einer Datei liegen auf den Bytes der Festplatte. Nun ist aber eine Festplatte, eine lange Anordnung von Bytes hintereinander.
Mit dem tool dd lassen sich unter Linux, Partitionen oder die gesamte Platte speichern oder kopieren. dd tut eines, es hat eine Ausgabe und eine Eingabe. Auf die Eingabe und Ausgabe lässt sich generell jedes Gerät und jede Datei anwenden.

dd if=/dev/sda1 of=/dev/sda2

Wenn ich nun an dem Dateisystem arbeite, dann muss ich nicht, beginnen, die Startroutine zur Verfügung haben. Sondern ich stelle anstatt des Dateisystems eine Datei zu Verfügung. Auf diese Datei wird mit C-Routinen zugegriffen. Diese stellen dieselbe Struktur des Dateisystem nach, in der Datei, die nachher mit einer Startroutine beim Betriebssystem verwendet wird. Ich kann diese Datei auch mit

dd if=/home/david/FileMitDateisystem of=/dev/sda2

auf ein Laufwerk kopieren. Das werde ich nachher auch machen. Dann kann ich auch dort schon ausführbare Dateien unterbringen, so dass ich beim Starten des Betriebssystems, diese zur Verfügung habe. Das Programm erzeugt gleich an richtiger Stelle, die Programme. Dies entspricht einer kurzen Installationsroutine eines Betriebssystems.

Diese Datei, die genauso Strukturiert ist, wie ein Dateisystem, das auf dem Computer läuft und mit einer Startroutine ein Betriebssytem darstellt, kann ich über die Virtual Box nutze. Sie bildet einen Computer ab. Alles, was auf ihr läuft, läuft auf dem Computer

32-Bit oder 16-Bit

Jetzt ist aber die Frage, ein 16-Bit-Betriebssystem oder ein 32-Bit-Betriebssystem: Lesen Sie hier
http://www.talkortell.de/osdescrp001.txt
Ich entscheide mich für ein 16-Bit-Betriebssystem.

Zwischenfrage: FAT-Dateisystem oder andere Struktur

Ich entscheide mich doch für eine FAT. Zunächst hatte ich ein Dateisystem, was wie folgt funktioniert: An jedem Ende eine Sektors, steht der nächste Sektor. Das ist nicht unbedingt gut und macht auch nicht alles automatisch einfacher. In einer FAT steht zu jedem Sektor, der durch seine Nummer identifiziert wird, in einer Tabelle, die als Indizes, die Adressen des Sektoren enthäalt den nächsten Sektor

Vorsicht wichtig!

Nicht vergessen: Interrupts. Wir werden das DOS-Interrupt: INT 0x21 verwenden. Das Interrupt ist die Stelle, über die, die Systemaufrufe aufgerufen werden. Schaut man sich das DOS-Interrupt INT 0x21 an, dann übernimmt dies alle die Systemaufrufe: Der entsprechende Aufruf wird über das Register ah mitgeteilt. Mit INT 0x21 springt man ins Betriebssystem, mit ah weiß das Betriebssystem, was zu tun ist. Installiert man ein Timer Interrupt, was ins Betriebssystem springt, ist Multitasking möglich.

32-Bit oder 16-Bit

Das ist hier die Frage: Ich nehm ein 16-Bit-Betriebssystem: Jetzt ist die Frage, wie gehe ich beim Dateisystem vor: Ich hatte auch schon in die Augen gefasst, das Dateisystem im Arbeitsspeicher zu lassen und nur beim Starten und Beenden, es auf der Platte zu lesen und speichern
Das Problem ist: Im 32-Bit-Modus steht uns der volle Funktionsumfang von IDE (Also Festplatte/Lesen von Sektoren/PATA...) zur Verfügung. Man braucht lediglich an bestimmte Stellen im Arbeitsspeicher zu schreiben und der Sektor wird gelesen oder geschrieben. Im 16-Bit-Modus steht nur das BIOS-Interrupt 0x10 zur Verfügung. Das verwendet auch Herr Henkes. Nälich für den Bootloader. Generell lässt sich damit schon eine Menge von Sektoren, lesen, die für uns verwertbar ist. So zum Beispiel eine 1,44MByte Diskette. Da wir so oder so im 16-Bit-Modus sind, ist das akzeptabel.
Zum Dateisystem habe ich jetzt überlegt: Soll ich nicht einfach mit dem Bootloader, wie bei Henkes verwendet, also mit dem Bios-Interrupt INT 0x10 am Anfang die gesamte Diskette in den Arbeitsspeicher lesen? Die Antwort lautet: Nein: Darum habe ich mich entschieden, Funktionen zu schreiben, die direkt auf die Platte schreiben und von ihr lesen.

Das Problem mit dem C-Kernel

Die Frage ist, wie geht das? Letzte Frage, letzte Überschrift. Wenn ich die gesamte Platte einlese, kann ich das in Assembler tun. Aber, wenn ich weiter gehe, dann brauche ich C. Denn solche Ausdrücke lassen sich nur in C so darstellen, dass es vernüftig ist.

C und das Betriebssystem

Nun nach Henkes sieht eine einfache Startroutine so aus:




  mov ax, 0x07C0 
  mov ds, ax
  mov es, ax

  mov si, welcome
  call print_string

loop: jmp loop


welcome db 'Welcome to http://www.talkortell.de', 0x0D, 0x0A, 0

print_string:
  lodsb    
  or al, al
  jz .done 
  mov ah, 0x0E
  int 0x10 
  jmp print_string
.done:
  ret

  times 510-($-$$) db 0
  db 0x55
  db 0xAA




os01.asm

Übersetzt wird das ganze mit

nasm kernel.asm -f bin -o kernel.bin

nasm erzeugt dabei eine Bin-Datei. Das ist wichtig. Das ist so zu sagen, das RAW-Format von ausführbaren Dateien. Man kann sich denken: Eine a.out unter Linux oder eine EXE unter Windows, die ausfühbare Dateien unter Linux und Windows sind, enthalten Kopfinformationen, die für das Betriebssystem sind. Diese Informationen sind genauso, wie die Befehle, irgendwelche Bytes. Blos werden sie nicht als Befehle interpretiert. Doch diese Kopfinformationen sind für das Betriebssystem wichtig und nur das entsprechende Betriebssystem weiß, was sie darstellen. Im MBR wollen wir einfach Befehle ausführen. Unsere Befehle. Vom ersten Moment an, ab erster Stelle. Die Befehle im MBR werden so ausgeführt, wie sie dastehen. Das heißt wir brauchen ein RAW-Binärformat: Schaut man sich einen Assemlberquelltext an:

_again:
mov ax, bx
inc ax
dec bx
jmp _again:


dann lassen sich diese Befehle direkt in Maschinenbefehle übersetzen. Jeder Befehl stellt dabei ein Byte dar, mit entsprechenden Operanden. Dabei lassen sich die Assembler-Befehle direkt in Bytes übersetzen und umgekehrt.
So einen Binärcode, schreiben wir nicht in Assembler, sondern direkt in Binärcode auf den MBR. Die Befehle werden nun beim Starten so ausgeführt, wie sie da stehen. Die Frage ist, wie bekommen wir einen solchen Binärcode auf den entsprechenden Datenträger?
Unter DOS mit:

partcopy kernel.bin 0 200 -f0

Unter Unixoiden Betriebssystemen mit: dd if=/dev/file of=/dev/fdd0 Wichtig ist für den MBR:


  org 0x7c00
  cli
  times 510-($-$$) hlt 
  db 0x55 
  db 0xAA  


Hier steht quasi die Erkennung für den MBR.


os02.asm




Nun führen wir einen Bootloader ein:


   org 0x7C00 

    
    mov ax, 0x9000 
    mov ss, ax     
    xor sp, sp     

    
    mov [bootdrive], dl 
    call load_kernel    
 
    
    jmp 0x1000:0x0000   
 
    bootdrive db 0      
    loadmsg db "bootloader message: loading kernel ...",13,10,0
 
    
print_string:
    lodsb 
    or al, al
    jz .done 
    mov ah, 0x0E
    int 0x10 
    jmp print_string
 .done:
    ret
 
  
load_kernel:
    mov dl,[bootdrive] 
    xor ax, ax         
    int 0x13          
    jc load_kernel     

load_kernel1:
    mov ax, 0x1000    
    mov es, ax         
    xor bx, bx         
 
    mov dl,[bootd rive] 
    mov al,10          ; read 10 sectors
    mov ch, 0          ; cylinder = 0
    mov cl, 2          ; sector   = 2
    mov dh, 0          ; head     = 0
    mov ah, 2          ; function "read"  
    int 0x13          
    jc load_kernel1    

         
    mov si,loadmsg
    call print_string 
    ret

    times 510-($-$$) hlt
    db 0x55
    db 0xAA



os03.asm




Was macht nun dieser Bootloader?

Das Ganze wird übersetzt mit:

nasm boot.asm -f bin -o boot.bin
nasm kernel.asm -f bin -o kernel.bin
copy /b boot.bin + kernel.bin MyOS.bin


(Unter Windows) und unter Linux mit

nasm boot.asm -f bin -o boot.bin
nasm kernel.asm -f bin -o kernel.bin
cat boot.bin kernel.bin > kernel.img


Dabei geschieht folgendes: Am Anfang der Binärdatei liegt boot.bin, danach folgt direkt angeschlossen kernel.bin. Mit cat werden unter Linux beide Dateien angehängt. Wichtig ist, wenn man die VirtualBox benutzt, zum Testen, dass die Datei, die endgültige, nicht *.bin heißt, sonden *.img und trotz dessen, dass der Kernel nicht direkt in die Sektorgrenzen passen muss, wie der Bootloader, die Datei auf eine 512-Byte = Sektorgrösse passt. Dies erreicht man durch erzeugen einer weiteren Datei:

dd if=/dev/zero of=/rest.bin bs=XBYTES count=1 und diese mit cat ebenso anhängt.

Nun, im Prinzip ist alles beim Alten, beim Bootloader:



  org 0x7c00
  cli
  times 510-($-$$) hlt 
  db 0x55 
  db 0xAA  



Dies gilt nach wie vor, nun für den Bootloader. Der Bootloader ließt nun den Kernel ein. Der Bootloader steht davor nach wie vor im MBR. Am Ende kommt die Kennung 0x55 und 0xAA. Das ist wichtig, damit signalisiert ist, von diesem Sektor aus kann man booten.
Der Bootloader selber bedient sich des Interrupts INT 0x10, welches im BIOS enthalten ist, um die entsprechenden Sektoren des Kernels zu lesen.
Dabei ließt der Bootloader die Sektoren, die beim Bootloader bei dem BIOS-Interrupt INT 0x10 angegeben sind ein. Der Kernel befindet sich auf diesen Sektoren, die nun eingelesen sind. Wichtig ist: Der Bootloader selber, muss im MBR stehen und er muss passen. Deswegen:


  times 510-($-$$) hlt 


Der MBR muss genau gefüt sein. Das heißt der Code vom Bootloader darf nicht kleiner und nicht grösser sein. Der Kernel muss nicht direkt in die Sektoren passen. Er muss am Anfang von irgendeinem Sektor stehen, damit beim Einsprung, die Routine direkt ausgeführt werden kann, die an erster Stelle steht. Aber er kann auf mehreren Sektoren stehen und sein Ende muss nicht mit dem Ende des Sektors übereinstimmen. Der Bootloader ließt die entsprechenden Sektoren einfach in den Arbeitsspeicher.

Die Entscheidende Stelle

Hat der Bootloader nun den Code vom Kernel aus den Sektoren gelesen, hat er ihn an eine bestimmte Stelle im Arbeitsspeicher abgelegt:


   org 0x7C00 

    
    mov ax, 0x9000 
    mov ss, ax     
    xor sp, sp     

    
    mov [bootdrive], dl 
    call load_kernel    
 
    
    jmp 0x1000:0x0000   
 
    bootdrive db 0      
    loadmsg db "bootloader message: loading kernel ...",13,10,0
 
    
print_string:
    lodsb 
    or al, al
    jz .done 
    mov ah, 0x0E
    int 0x10 
    jmp print_string
 .done:
    ret
 
  
load_kernel:
    mov dl,[bootdrive] 
    xor ax, ax         
    int 0x13          
    jc load_kernel     

load_kernel1:
    mov ax, 0x1000    
    mov es, ax         
    xor bx, bx         
 
    m ov dl,[bootdrive] 
    mov al,10          ; read 10 sectors
    mov ch, 0          ; cylinder = 0
    mov cl, 2          ; sector   = 2
    mov dh, 0          ; head     = 0
    mov ah, 2          ; function "read"  
    int 0x13          
    jc load_kernel1    

         
    mov si,loadmsg
    call print_string 
    ret

    times 510-($-$$) hlt
    db 0x55
    db 0xAA



An dieser Stelle im Arbeitsspeicher steht nur der Kernel, der Sektor für Sektor von der Platte eingelesen wurde, egal von welchen, eben allen, die mit INT 0x10 erreicht werden können. Der Code vom Bootloader, der nicht grösser sein kann, als ein Sektor, wird beim Systemstart automatisch vom Computer ausgeführt wird. In diesem Sektor stehen Befehle, die wie alle Befehle ausgeführt werden. Allerdings werden sie von der Platte ausgeführt. Wir sind es gewohnt, dass Befehle aus dem RAM ausgeführt werden. Doch diese Befehle stehen direkt auf der Platte und werden direkt von der Platte ausgeführt. Doch das ist normal. Normalweise werden Befehle aus dem RAM ausgführt, doch bei der Startroutine werden die Befehle standardmäig vom ersten Sektor ausgeführt. Irgendwo müssen ja die ersten Befehle stehen?!? Wie geht es jetzt weiter. Nachdem die Befehle vom MBR ausgeführt wurden und dort sicher etwas drin steht, welche Sektoren in den RAM gelesen werden - wie? Wie können nun die Befehle im RAM ausgeführt werden? Wir haben ja extra das BIOS-Interrupt INT 0x10 um weitere Sektoren ein zu lesen. Nun gut, es genügt ein Sprungbefehl, eben im MBR, wo beim Systemstart, die Befehle ausgeführt werden. Und wir hatten den Kernel an 0x1000:0x0000 geladen. Wie kommen wir an diese Stelle? Dazu genügt ein Sprungbefehl:


   org 0x7C00 

    
    mov ax, 0x9000 
    mov ss, ax     
    xor sp, sp     

    
    mov [bootdrive], dl 
    call load_kernel    
 
    
    jmp 0x1000:0x0000   
 
    bootdrive db 0      
    loadmsg db "bootloader message: loading kernel ...",13,10,0
 
    
print_string:
    lodsb 
    or al, al
    jz .done 
    mov ah, 0x0E
    int 0x10 
    jmp print_string
 .done:
    ret
 
  
load_kernel:
    mov dl,[bootdrive] 
    xor ax, ax         
    int 0x13          
    jc load_kernel     

load_kernel1:
    mov ax, 0x1000    
    mov es, ax         
    xor bx, bx         
 
    mov dl,[bootdrive] 
    mov al,10          ; read 10 sectors
    mov ch, 0          ; cylinder = 0
    mov cl, 2          ; sector   = 2
    mov dh, 0          ; head     = 0
    mov ah, 2          ; function "read"  
    int 0x13          
    jc load_kernel1    

         
    mov si,loadmsg
    call print_string 
    ret

    times 510-($-$$) hlt
    db 0x55
    db 0xAA

Der C-Kernel

Jetzt haben wir den wichtigen Teil erledigt. Die Frage ist nur, wie kann man einen C-Kernel einbauen.
Wir könnten nämlich theoretisch immer so weiter machen und einen Assembler-Kernel schreiben. Doch kann man nicht auch einen C-Kernel bauen?
Was müssen wir dafür wissen? Zunächst ein C-Kernel wird aus C genaus in Assembler übersetzt. Wenn wir einen Code in C haben, dann ist er am Ende genauso in Assemlber/Maschinensprache zu haben, wie direkt in Assembler geschrieben
Wir können also beide Teile, wenn sie erst übersetzt sind, problemlos zusammenbauen.
Doch, was müssen wir nun ausserdem wissen? Die Antwort lautet: Wir müssen die Einsprungsadresse von der main haben, oder bzw. überhaupt von allen C-Funktionen. Doch was ist eine Funktion?

Na ja, aus der Schule wissen wir: Eine Variable ist eine benannte Speicherstelle und hat neben, Name, Wert und Typ auch eine Adresse. Eine Variable ist eine benannte Speichertelle und hat eine Adresse. Dasselbe gilt aber auch für eine Funktion. Eine Funktion ist auch eine benannte Speicherstelle, blos wird hier Code ausgeführt.

Schaut man sich, nun Assembler an:

inc ax
call _function01
...

function01:
inc ax
dec bx
...
ret


Dann haben wir hier bei einem Funktionsaufruf, so eine benannten Speicherstelle von ausführbaren Code. Nämlich ein Label. Schaut man sich Assembler an, so wird man feststellen, der Unterschied zwischen Labels und Variablen ist nicht unbedingt besonders groß.
Wenn man eine C-Routine anschaut, dann sieht das in etwa so aus:

void _function01(void) {
    return;
}


Dieser Code könnte quasi in der vorherigen übersetzt werden. Dabei wird aus Funktionsnamen in C, ein Label in Assembler. Der Funktionsaufruf wird in einen call Befehl übersetzt. Am Ende einer Funktion müsste eigentlich immer return 0; stehen, hier kommt in Assembler der Rücksprungsbefehl ret eingesetzt.

Baut man nun einen Kernel in C, der zu einem Binärprogramm wird, dann könnte man rein theoretisch, da er danach ja quasi Assembler-Code ist, oder eigentlich Binärcode, diesen Code, als Binärcode genaus in die Sektoren legen, wie den Kernel in Assembler. Er wird von seinen Befehlen, in die er ja übersetzt wurde, genauso ausgeführt.

Das Problem ist, schreibt man so etwas, dann liegt es auf der Platte. Wir wollen diesen Kernel aber ausführen. Jetzt stehen wir vor demselben Problem, wie oben beim dem Bootloader mitsamt dem Kernel. Doch der Bootloader ließt den Kernel an eine bestimmte Stelle im Arbeitsspeicher und macht einen Sprung an diese Stelle. Ebenso, der C-Kernel genauso ein Code wird, könnte man diesen laden und einen Sprung an die Stelle des von C nach Assembler bzw. Binärcode übersetzten Kernel machen.
Dann stellt sich die Frage: Wo liegt die main-Funktion? Haben wir die Einsprungsadresse zur main-Funktion gefunden, müssen wir den Kernel nur laden und einen Sprung zur main-Funktion.

Dazu erst ein Mal, die Lösung von Henkes:
Die klassische Lösung, danach folgt meine Lösung. Diese Lösung verwende so ich. Vielleicht manch andere auch. Man mein so etwas schon ein Mal gehört zu haben. Aber trotzdem ich bin von selber darauf gekommen.

Der erste Schritt ist folgender:

[BITS 16]      ; 16 Bit Code
[extern _main] 

; Real Mode 

RealMode:

...


Dieser Code steht am Anfang in unserem Assembler-Kernel.
Ohne diesen Schritt (rot) kommen wir nicht weiter. Wir machen nälich gleich einen Funktionsaufruf:

  call _main ; ->-> C-Kernel
  jmp $


Unser Assembler-Kernel von vorher wird durch folgende Sequenzen ergänzt:

[BITS 16]      ; 16 Bit Code
[extern _main] 

; Real Mode 

RealMode:

...

...

  call _main ; ->-> C-Kernel
  jmp $


Also sieht der Kernel in etwa so aus:



[BITS 16]      ; 16 Bit Code
[extern _main] 

; Real Mode 

RealMode:

...

  mov ax, 0x07C0 
  mov ds, ax
  mov es, ax

  mov si, welcome
  call print_string

  call _main ; ->-> C-Kernel
  jmp $


loop: jmp loop


welcome db 'Welcome to http://www.talkortell.de', 0x0D, 0x0A, 0

print_string:
  lodsb    
  or al, al
  jz .done 
  mov ah, 0x0E
  int 0x10 
  jmp print_string
.done:
  ret

  times 510-($-$$) db 0
  db 0x55
  db 0xAA




Wichtig sind hierbei folgende Sachen: Das hätte ich oben schon erwähnen müssen, die Anweisung, an den Assembler

[BITS 16]      ; 16 Bit Code

Diese Anweisung ist kein Maschinenbefehl, sondern eine Anweisung an NASM, nälich um 16-Bit-Maschinencode zu erzeugen. Anders geht es nicht, sonst haben wir 32-Bit.
Jetzt, das C-Programm wird nachher dazu gelinkt. Wir müssen beim Übersetzen zwischen zwei Stufen unterscheiden: Das Übersetzen macht aus dem C-Programm, einen Binärcode bzw. einen Assemblerquelltext. Man kann einen C-Compiler auch die Anweisung geben, aus einem C-Programm, einen Assembler-Quelltext zu erzeugen, kein Binärprogramm. Beim Übersetzen, spielt eine Rolle, dass Ausdrücke nachher in richtigen Befehlen stehen. Wir kennen das von arithmetischen Ausdrücken. Siehe anfang der Seite: http://www.talkortell.de. Hier wird aus einer Sprache eine andere gemacht. Was wir allerdings nicht getan haben, ist: Die Label in Adressen um zu wandeln. Wir wissen Label, wie:

_function01:
_again:
.loop01:


sind eigentlich Adressenbezeichnung. Hinter jedem Label versteckt sich eine Adresse, ins Codesegment. Wenn wir übersetzt haben, haben wir diese Label nicht umgewandelt. Wir haben zwar einen Assembler-Code erzeugt, doch die Label stehen da. Was sich hinter dem Label verbindet, weiß man vor dem Linken noch nicht. Der Linker wandelt jedes Label in seine Adresse um. Das ist ein zweiter Schritt.

Zunächst ein Mal: Ein Programm kann richtig übersetzt sein und ließ sich richtig übersetzen und trotzdem ist ein Fehler drin. Denn wenn die Sprungmarken, die Label ins Leere verweisen, oder es sie nicht gibt, dann weiß man nach dem Compilen nichts davon. Der Compiler ersetzt die Label nicht durch Adressen und was sich dahinter verbirgt, bleibt unbekannt. So kann eine Funktion schreiben, die auf anderen Funktionen, semantisch angewiesen ist. Zum Beispiel gibt eine Funktion Text aus. Den Text erhält sie nicht von einer Funktion, geschweige denn, dass sie selbst erzeugt, sondern erhält ihn durch andere Funktionen. Diese Funktion ist also angewiesen, auf eine richtige Weitergabe von Zeichenketten. Wird die Funktion blos übersetzt, ist es egal, was sich hinter den Funktionen verbirgt, die Text liefern. Wenn diese Label zu Dingen führen, die es gar nicht gibt, übersetzbar ist das ganze. Erst nach dem Linken, sind die Adressen bekannt. Hier bleibt quasi eine leere Stelle.

Wozu ist das gut? (Nämlich, dass man Label nicht gleich ersetzt?) Na ja, wenn man ein Programm hat, das sich aus mehrere Dateien zusammensetzt und zwar mehreren C-Dateien, wenn wir ein Programm in C schreiben, dann wird jede C-Datei für sich übersetzt. Erst im letzten Schritt, dem Linken, werden alle Ergebnisse zusammengesetzt. Braucht eine Funktion einen Aufruf von einer anderen Datei, wird nun die Adresse richtig eingesetzt. Aber beim Übersetzen wird jede Datei einzeln übersetzt. Wenn sie einzeln übersetzt werden, dann ist nicht klar, was eigentlich hinter einem Label für eine C-Funktion in einer anderen Datei steht. Somit ist es besser, das Label nicht übersetzen.

So auch in diesem Falle: Wir haben einen Assembler-Kernel und einen C-Kernel. Den Assembler-Kernel, der den C-Kernel aufruft, habe ich soeben vorgestellt. Bei dem Call-Aufruf von main handelt es sich um ein main in einer anderen Datei, einer C-Datei.

int main(void) {

return 0;
}


Diese Funktion tut nicht viel, aber sie wird aus dem C-Kernel heraus aufgerufen.

Übersetzt wir das ganze nun mit:

nasm -O32 -f bin boot.asm -o boot.bin
nasm -O32 -f aout kernel.asm -o kernel.o
gcc -c ckernel.c -o ckernel.o
ld -T kernel.ld kernel.o ckernel.o
Was hier entscheidend ist: Wir sind wieder im 32-Bit-Modus, wie es Henkes fordert. Doch: Entscheidend ist auch, die Datei

kernel.ld

Diese sieht wie folgt aus:

OUTPUT_FORMAT("binary")
ENTRY(RealMode)
SECTIONS
{
  .text 0x8000 : { *(.text) }
  .data        : { *(.data) }
  .bss         : { *(.bss)  }
}


Das erste, was uns beim Übersetzten auffällt und ins Entsetzen führen sollte, ist das:

nasm -O32 -f aout kernel.asm -o kernel.o


Hatten wir nicht bin als Format? Nun, der Bootloader muss unbedingt im bin Format stehen, anders geht das nicht. Was hier aber steht ist der Kernel. Und der steht im aout Format. Das ist das typische Format, von dem Unix/Linux-ausführbare Dateien. Wenn man eine C-Datei in Linux übersetzt, dann erhält man a.out als ausführbares Programm. Aber, der Bootloader, der muss in bin bzw. RAW-ausfühbaren Format sein. Denn dieser steht im ersten Sektor der Platte und wird beim Starten ausgeführt genauso wie es da steht, da ist kein Platz, für Kopfinformationen, die der Computer nicht interpretieren kann.

Der Bootloader wiederum läadt weitere Sektoren. Er kann eine ganze Menge Sektoren laden. Dann kann er ja laden, was er will? Ja, eben auch ein aout-Code. Und jetzt kommt das entscheidende: Er kann zwar laden, was er will, auch ein Bild, damit kann man aber meistens nichts mit anfangen. Umgekehrt: Auch ein aout-Programm enthält ausführbaren Code. Es ist auch ein Programm. Und darin stehen genauso, Sprungmarken, Label, Funktionsnamen, sprich Adressen. Warum also den Bootloader nicht das laden lassen und ihn nachher zur richtigen Stelle springen lassen?

Nun besteht der Kernel aber aus zwei Teilen: Dem Assemlber-Code und dem C-Code. Das heißt, wir übersetzen beide Programm getrennt, wie vorher beschrieben. Dann linken wir sie. Sie werden ein Programm. Die Linkeranweisungen, in der kernel.ld

OUTPUT_FORMAT("binary")
ENTRY(RealMode)
SECTIONS
{
  .text 0x8000 : { *(.text) }
  .data        : { *(.data) }
  .bss         : { *(.bss)  }
}


sind wichtig, damit die Segmente an der richtigen Stelle stehen. Wir wollen den Bootloader ja nachher dahin springen lassen. Also müssen die Segmente stimmen. Dazu gehört, dass das Codesegment an erster Stelle steht. Wir wollen ja bequemerweise nicht irgendwo hin springen, sondern den Code, gleich ab der ersten Stelle ausführen.

Mein Schritt zu 16-Bit

Jetzt können wir zumindest C-Code unterbringen. Aber wir sind nicht mehr bei 16-Bit. Denn

gcc

arbeitet mit 32-Bit. Es gibt eine Menge Anweisungen, wie

-O16
-m16
-m elf32


mit denen sich Linker und Compiler deartig steuern lassen, 16-Bit, 32-Bit, 64-Bit Code zu erzeugen. Doch all das wird nicht funktionieren. Nasm selber lässt sich zum Beispiel über die Anweisung:

[BITS 16]      ; 16 Bit Code

16-Bit-Code zu erzeugen. Doch all das führt zu keinem Ergebnis.

Und jetzt: bcc

Es gibt ja eine Menge C-Compiler: gcc ist nur einer davon. Unter Windows und DOS gibt oder gab es den Borland-C-Compiler. Dieser nannte sich tcc oder tc. Nicht zu verwechseln mit bcc

Um bcc zu installieren, w&uum;rde ich auf jeden Fall, Debian Linux verwenden.

Debian-Linux verfügt zum installieren von Paketen über

apt-get

apt-get ist die Paketeverwaltung unter Debian, bzw. nicht die Verwaltung, sondern das Programm, über das Pakete installiert werden. Dabei greift es auf das Internet zu. Um ein Programm zu installieren muss man eingeben:

apt-get install Paket-Name

Der Paket-Name muss stimmen, sonst hat apt-get keinen Vorschlag. apt-get greift dabei auf das Internet zu. Es werden die Pakete, aus dem Internet geladen und installiert. Sie sind alle zusammen im Internet gehalten, die Pakete für Debian.

Wenn man bcc installieren will, gibt man ein:

apt-get install bcc

Nun hat man einen 16-Bit-Compiler für Linux. Aber nicht nur das, sondern: Die Programme, Assemlber und Linker für 16-Bit kommen gleich mit dem Compiler. Das 86 steht für 8086 dem 16-Bit-Prozessor von Intel, auf dem die weiteren 32-Bit-Prozessoren und 64-Bit-Prozessoren von Inteln gröstenteils aufbauen. 8086 ist ein 16-Bit-Prozessor.

Das Spiel beginnt von vorne

Wir legen wir eine

OUTPUT_FORMAT("binary")
ENTRY(RealMode)
SECTIONS
{
  .text 0x0800 : { *(.text) }
  .data        : { *(.data) }
  .bss         : { *(.bss)  }
}


Nur tun wir die Segmentgrenzen anders setzten. Die Segmentgrenzen, die hier beschrieben werden, stimmen mit denen der Adresse überein, an die der Bootloader nachher das Programm lädt. Ich finde es fragwürdig ob das notwendig ist. Das beschreibt nälich unter anderem die Lage im Programm und nicht Arbeitsspeicher.

Aber siehe da, :-( Erst funktionierte es tatsälich. Mit 16-Bit. Ich hatte einen funktionierenden Kernel, mit C-Code und 16-Bit. Aber die Sache tat nicht lange und es versagte.

Die Idee

Ich kam auf eine andere Idee: Wenn der Assembler eine C-Funktion aufruft, die gerade, oder weil sie ein Label hat, das auf eine Adresse verweist, über die Adresse aufruft, dann muss man eben diese main-Funktion aufrufen. Umgekehrt, unser C-Compiler muss gar keinen Objektcode erzeugen. Sondern er kann auch Assembler Coder erzeugen.

Lesen Sie dazu vielleicht erst Mal folgende Datei:

osdescrp2.txt

Ich möchte jetzt an dieser Stelle gar nicht lange abschweifen und stelle folgenden Code vor:

void c_print_string();

void main(void) {
    c_print_string();
return;
}


void c_print_string(void) {

return;
}


printstring.c



  mov ax, #0x400 
  mov ds, ax
  mov es, ax

  mov si, #welcome
  call _print_string
  call _main


  
_again1: jmp _again1



_print_string:
    lodsb             
    or al, al         
    jz _done          
    mov ah, #0x0E
    int #0x10          
    jmp _print_string
 _done:
    ret

welcome: .asciz "Welcome to http://www.talkortell.de!!!"


! 1 
! 1 # 1 "printstring.c"
! 1 void c_print_string();
!BCC_EOS
! 2 
! 3 void main()
! 4 # 3 "printstring.c"
! 3 {
export _main
_main:
! 4     c_print_string();
push bp
mov bp,sp
push di
push si
! Debug: func () void = c_print_string+0 (used reg = )
call _c_print_string
!BCC_EOS
! 5 return;
pop si
pop di
pop bp
ret
!BCC_EOS
! 6 }
! 7 
! 8 
! 9  ;void c_print_string()
! 10 # 9 "printstring.c"
! 9 {
export _c_print_string
_c_print_string:
! 10 
! 11 return;
push bp
mov bp,sp
push di
push si

  mov si, #welcome
  call _print_string

    mov si, #welcome
  call _print_string

    mov si, #welcome
  call _print_string


pop si
pop di
pop bp
ret
!BCC_EOS
! 12 }
! 13 
.data
.bss

! 0 errors detected




prinstring.s



  mov ax, #0x400 
  mov ds, ax
  mov es, ax

  mov si, #welcome
  call _print_string
  call _main


  
_again1: jmp _again1



_print_string:
    lodsb             
    or al, al         
    jz _done          
    mov ah, #0x0E
    int #0x10          
    jmp _print_string
 _done:
    ret

welcome: .asciz "Welcome to http://www.talkortell.de!!!"


! 1 
1 # 1 "printstring.c"
! 1 void c_print_string();
!BCC_EOS
! 2 
! 3 void main()
! 4 # 3 "printstring.c"
! 3 {
export _main
_main:
! 4     c_print_string();
push bp
mov bp,sp
push di
push si
! Debug: func () void = c_print_string+0 (used reg = )
call _c_print_string
!BCC_EOS
! 5 return;
pop si
pop di
pop bp
ret
!BCC_EOS
! 6 }
! 7 
! 8 
!  ;9 void c_print_string()
! 10 # 9 "printstring.c"
! 9 {
export _c_print_string
_c_print_string:
! 10 
! 11 return;
push bp
mov bp,sp
push di
push si

  mov si, #welcome
  call _print_string

    mov si, #welcome
  call _print_string

    mov si, #welcome
  call _print_string


pop si
pop di
pop bp
ret
!BCC_EOS
! 12 }
! 13 
.data
.bss

! 0 errors detected




Schaut man sich nun das Rot-geschriebene an, dann erkennt man hier den Assember-Kernel von ganz oben.

  mov ax, #0x400 
  mov ds, ax
  mov es, ax

  mov si, #welcome
  call _print_string
  call _main


  
_again1: jmp _again1



_print_string:
    lodsb             
    or al, al         
    jz _done          
    mov ah, #0x0E
    int #0x10          
    jmp _print_string
 _done:
    ret

welcome: .asciz "Welcome to http://www.talkortell.de!!!"


! 1 
1 # 1 "printstring.c"
! 1 void c_print_string();
!BCC_EOS
! 2 
! 3 void main()
! 4 # 3 "printstring.c"
! 3 {
export _main
_main:
! 4     c_print_string();
push bp
mov bp,sp
push di
push si
! Debug: func () void = c_print_string+0 (used reg = )
call _c_print_string
!BCC_EOS
! 5 return;
pop si
pop di
pop bp
ret
!BCC_EOS
! 6 }
! 7 
! 8 
! 9 void c_print_string()
! 10 # 9 "printstring.c"
! 9 {
export _c_print_string
_c_print_string:
! 10 
! 11 return;
push bp
mov bp,sp
push di
push si

  mov si, #welcome
  call _print_string

    mov si, #welcome
  call _print_string

    mov si, #welcome
  call _print_string


pop si
pop di
pop bp
ret
!BCC_EOS
! 12 }
! 13 
.data
.bss

! 0 errors detected


Schaut man sich nun das nächste rot-geschriebene an, erkennt man den Code, wenn man die

printstring.c



übersetzt. Das ist nämlich:

void c_print_string();

void main(void) {
    c_print_string();
return;
}


void c_print_string(void) {

return;
}
in Assembler.

In diesen Assembler-Code habe ich die Funktionaufrufe eingebaut:

Schaut man sich nun das Rot-geschriebene an, dann erkennt man hier den Assember-Kernel von ganz oben.

  mov ax, #0x400 
  mov ds, ax
  mov es, ax

  mov si, #welcome
  call _print_string
  call _main


  
_again1: jmp _again1



_print_string:
    lodsb             
    or al, al         
    jz _done          
    mov ah, #0x0E
    int #0x10          
    jmp _print_string
 _done:
    ret

welcome: .asciz "Welcome to http://www.talkortell.de!!!"


! 1 
! 1 # 1 "printstring.c"
! 1 void c_print_string();
!BCC_EOS
! 2 
! 3 void main()
! 4 # 3 "printstring.c"
! 3 {
export _main
_main:
! 4     c_print_string();
push bp
mov bp,sp
push di
push si
! Debug: func () void = c_print_string+0 (used reg = )
call _c_print_string
!BCC_EOS
! 5 return;
pop si
pop di
pop bp
ret
!BCC_EOS
! 6 }
! 7 
! 8 
! 9  ;void c_print_string()
! 10 # 9 "printstring.c"
! 9 {
export _c_print_string
_c_print_string:
! 10 
! 11 return;
push bp
mov bp,sp
push di
push si

  mov si, #welcome
  call _print_string

    mov si, #welcome
  call _print_string

    mov si, #welcome
  call _print_string



pop si
pop di
pop bp
ret
!BCC_EOS
! 12 }
! 13 
.data
.bss

! 0 errors detected


Schaut man sich nun das nächste rot-geschriebene an, erkennt man den Code, wenn man die, die Assembler-Routinen aufrufen. Diese habe ich an der Stelle eingebaut. Übersetzt wir das ganze mit:

printstring.c





bcc -ansi -S printstring.c

Nun erhält man

  mov ax, #0x400 
  mov ds, ax
  mov es, ax

  mov si, #welcome
  call _print_string
  call _main


  
_again1: jmp _again1



_print_string:
    lodsb             
    or al, al         
    jz _done          
    mov ah, #0x0E
    int #0x10          
    jmp _print_string
 _done:
    ret

welcome: .asciz "Welcome to http://www.talkortell.de!!!"


! 1 
1 # 1 "printstring.c"
! 1 void c_print_string();
!BCC_EOS
! 2 
! 3 void main()
! 4 # 3 "printstring.c"
! 3 {
export _main
_main:
! 4     c_print_string();
push bp
mov bp,sp
push di
push si
! Debug: func () void = c_print_string+0 (used reg = )
call _c_print_string
!BCC_EOS
! 5 return;
pop si
pop di
pop bp
ret
!BCC_EOS
! 6 }
! 7 
! 8 
!  ;9 void c_print_string()
! 10 # 9 "printstring.c"
! 9 {
export _c_print_string
_c_print_string:
! 10 
! 11 return;
push bp
mov bp,sp
push di
push si

  mov si, #welcome
  call _print_string

    mov si, #welcome
  call _print_string

    mov si, #welcome
  call _print_string



pop si
pop di
pop bp
ret
!BCC_EOS
! 12 }
! 13 
.data
.bss

! 0 errors detected


Dieses rot-geschriebene, schreibt man nachher dazu.

Nun übersetzt man die boot.asm und die prinstring.s mit:

all:
nasm -f bin boot.asm -o boot.bin
as86  -0 main.s -b main.bin
cat boot.bin main.bin > kernel.bin

    


Hier der Makefile dazu:

makefile

So sieht das Ergebnis aus:





Besonders wichtig, ist die Anweisung: bcc -ansi -S printstring.c Hier sagen wir bcc, aus dem C-Quelltext, Assemblerquelltext zu erzeugen.
Das geht, wie üblicherweise auch für gcc üblich, über die Anweisung
-S
Das
-ansi
ist für bcc nötig, damit wir wie gewohnt in ansi-C schreiben können, was unter gcc normal ist.

Wir bauen C-Funktionen ein, die etwas tun

Wir schreiben folgenden C-Code:

void c_print_string();

void main(void) {
    char *buf = "Hallo Welt";
    c_print_string(buf);
return;
}


void c_print_string(char *str) {
    char *ptr1;
    char *ptr2;
    char *ptr3;
    
    ptr1 = str;
    ptr2 = str;
    ptr3 = str;

return;
}


Neu im Gegensatz, zu:

void c_print_string();

void main(void) {
    c_print_string();
return;
}


void c_print_string(void) {

return;
}


Sind:

void c_print_string();

void main(void) {
     char *buf = "Hallo Welt";
    c_print_string(buf);
return;
}


void c_print_string(char *str) {
    char *ptr1;
    char *ptr2;
    char *ptr3;
    
    ptr1 = str;
    ptr2 = str;
    ptr3 = str;


return;
}


Warum machen wir das - was wollen wir mit diesen Variablen? Nun gut, C ohne Variablen und wir können uns das Ganze ersparen. Allerdings übersetzen wir den C-Quelltext ja nach Assembler. Dann haben wir einen Assemblerquelltext vorliegen. Nur leider ist die Variablenverwaltung bei Funktionsaufrufen nicht so einfach, wenn man das nach Assembler übersetzte C-Programm anschaut. In C sind wir es gewohnt, dass wir Variablen über den Funktionskopf übergeben. Das sieht Recht logisch aus. Doch es bleibt ja nicht bei dem C-Programm, es wird in Assembler übersetzt. Was geschieht jetzt mit den Variablennamen, die wir übergeben haben?

(PS: Das müssen wir wissen, weil wir in den von C nach Assembler übersetzten Quelltext, nachher Variablenwerte usw. über Assembler abrufen wollen.)

void c_print_string();

void main(void) {
    char *buf = "Hallo Welt";
    c_print_string(buf);
return;
}


void c_print_string(char *str) {
    char *ptr1;
    
    ptr1 = str;
return;
}


Dieser Code eignet sich deswegen für unsere Zwecke, weil hier einerseits Variablen beim Aufruf einer Funktion übergeben werden, ausserdem wir die eine weitere Variable mit den Übergabeparameter initialisiert. Wir können also die Übergabe studieren.

Übersetzen wir das ganze, erhalten wir:

! 1 
! 1 # 1 "printstring3.c"
! 1 void c_print_string();
!BCC_EOS
! 2 
! 3 void main()
! 4 # 3 "printstring3.c"
! 3 {
export _main
_main:
! 4     char *buf = "Hallo Welt";
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq [$B] char = .1+0 to * char buf = [S+8-8] (used reg = )
mov bx,#.1
mov -6[bp],bx
!BCC_EOS
! 5     c_print_string(buf);
! Debug: list * char buf = [S+8-8] (used reg = )
push -6[bp]
! Debug: func () void = c_print_string+0 (used reg = )
call _c_print_string
inc sp
inc sp
!BCC_EOS
! 6 return;
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 7 }
! 8 
! 9 
! 10 void c_print_string(str)
! Register BX used in function main
! 11 # 10 "printstring3.c"
! 10 char *str;
export _c_print_string
_c_print_string:
!BCC_EOS
! 11 # 10 "printstring3.c"
! 10 {
!&n bsp;11     char *ptr1;
!BCC_EOS
! 12     
! 13     ptr1 = str;
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq * char str = [S+8+2] to * char ptr1 = [S+8-8] (used reg = )
mov bx,4[bp]
mov -6[bp],bx
!BCC_EOS
! 14 return;
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 15 }
! 16 
! Register BX used in function c_print_string
.data
.1:
.2:
.ascii "Hallo Welt"
.byte 0
.bss

! 0 errors detected


Was uns bei der Variablenverwaltug auffällt, ist:

! 1 
! 1 # 1 "printstring3.c"
! 1 void c_print_string();
!BCC_EOS
! 2 
! 3 void main()
! 4 # 3 "printstring3.c"
! 3 {
export _main
_main:
! 4     char *buf = "Hallo Welt";
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq [$B] char = .1+0 to * char buf = [S+8-8] (used reg = )
mov bx,#.1
mov -6[bp],bx
!BCC_EOS
! 5     c_print_string(buf);
! Debug: list * char buf = [S+8-8] (used reg = )
push -6[bp]
! Debug: func () void = c_print_string+0 (used reg = )
call _c_print_string
inc sp
inc sp
!BCC_EOS
! 6 return;
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 7 }
! 8 
! 9 
! 10 void c_print_string(str)
! Register BX used in function main
! 11 # 10 "printstring3.c"
! 10 char *str;
export _c_print_string
_c_print_string:
!BCC_EOS
! 11 # 10 "printstring3.c"
! 10 {
!&n bsp;11     char *ptr1;
!BCC_EOS
! 12     
! 13     ptr1 = str;
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq * char str = [S+8+2] to * char ptr1 = [S+8-8] (used reg = )
mov bx,4[bp]
mov -6[bp],bx
!BCC_EOS
! 14 return;
inc sp
inc sp

pop si
pop di
pop bp
ret
!BCC_EOS
! 15 }
! 16 
! Register BX used in function c_print_string
.data
.1:
.2:
.ascii "Hallo Welt"
.byte 0
.bss

! 0 errors detected


Ansonsten ist der Code, scheinbar identisch, wie ohne die Variablen:

! 1 
! 1 # 1 "printstring4.c"
! 1 void c_print_string();
!BCC_EOS
! 2 
! 3 void main()
! 4 # 3 "printstring4.c"
! 3 {
export _main
_main:
! 4 
! 5 return;
push bp
mov bp,sp
push di
push si
pop si
pop di
pop bp
ret
!BCC_EOS
! 6 }
! 7 
! 8 
! 9 void c_print_string(str)
! 10 # 9 "printstring4.c"
! 9 char *str;
export _c_print_string
_c_print_string:
!BCC_EOS
! 10 # 9 "printstring4.c"
! 9 {
! 10 
! 11 return;
push bp
mov bp,sp
push di
push si
pop si
pop di
pop bp
ret
!BCC_EOS
! 12 }
! 13 
.data
.bss

! 0 errors detected


Jetzt machen, wir die ganze Schickane noch ein Mal von vorne:

bcc -ansi -S printstring.c

EDITIEREN

...
all:
nasm -f bin boot.asm -o boot.bin
as86  -0 main.s -b main.bin
cat boot.bin main.bin > kernel.bin

    


(Wir übersetzen alles erneut)
und, wenn wir das Programm starten, siehe da: Es funktioniert nicht. Jetzt vergleichen wir, die Stellen, die nicht übereinstimmen, zwischen C-Code mit Variablen und C-Code ohne Variablen. Und kommentieren scheinbar alle aus, die im Assemblerquelltext vom C-Code ohne Variablen nicht auftauchen:

! 1 
! 1 # 1 "printstring3.c"
! 1 void c_print_string();
!BCC_EOS
! 2 
! 3 void main()
! 4 # 3 "printstring3.c"
! 3 {
export _main
_main:
! 4     char *buf = "Hallo Welt";
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq [$B] char = .1+0 to * char buf = [S+8-8] (used reg = )
mov bx,#.1
mov -6[bp],bx
!BCC_EOS
! 5     c_print_string(buf);
! Debug: list * char buf = [S+8-8] (used reg = )
push -6[bp]
! Debug: func () void = c_print_string+0 (used reg = )
call _c_print_string
inc sp
inc sp
!BCC_EOS
! 6 return;
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 7 }
! 8 
! 9 
! 10 void c_print_string(str)
! Register BX used in function main
! 11 # 10 "printstring3.c"
! 10 char *str;
export _c_print_string
_c_print_string:
!BCC_EOS
! 11 # 10 "printstring3.c"
! 10 {
!&n bsp;11     char *ptr1;
!BCC_EOS
! 12     
! 13     ptr1 = str;
push bp
mov bp,sp
push di
push si
!dec sp
!dec sp
! Debug: eq * char str = [S+8+2] to * char ptr1 = [S+8-8] (used reg = )
!mov bx,4[bp]
!mov -6[bp],bx
!BCC_EOS
! 14 return;
!inc sp
!inc sp

pop si
pop di
pop bp
ret
!BCC_EOS
! 15 }
! 16 
! Register BX used in function c_print_string
.data
.1:
.2:
.ascii "Hallo Welt"
.byte 0
.bss

! 0 errors detected


Und wir probieren es erneut aus und es funktioniert immer noch nicht. Wo liegt der Fehler: Wir haben jetzt alles dafür getan, dass der Assembler-Quelltext vom C-Programm mit Variablen so aussieht, wie der ohne. Und trotzdem es funktionierrt nicht. Wie kann das sein? Wenn wir alles entsprechend auskommentiert haben, sind die Quelltexte doch identisch? Wo liegt der Fehler? Und wir finden ihn:

! 1 
! 1 # 1 "printstring3.c"
! 1 void c_print_string();
!BCC_EOS
! 2 
! 3 void main()
! 4 # 3 "printstring3.c"
! 3 {
export _main
_main:
! 4     char *buf = "Hallo Welt";
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq [$B] char = .1+0 to * char buf = [S+8-8] (used reg = )
mov bx,#.1
mov -6[bp],bx
!BCC_EOS
! 5     c_print_string(buf);
! Debug: list * char buf = [S+8-8] (used reg = )
push -6[bp]
! Debug: func () void = c_print_string+0 (used reg = )
call _c_print_string
inc sp
inc sp
!BCC_EOS
! 6 return;
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 7 }
! 8 
! 9 
! 10 void c_print_string(str)
! Register BX used in function main
! 11 # 10 "printstring3.c"
! 10 char *str;
export _c_print_string
_c_print_string:
!BCC_EOS
! 11 # 10 "printstring3.c"
! 10 {
!&n bsp;11     char *ptr1;
!BCC_EOS
! 12     
! 13     ptr1 = str;
push bp
mov bp,sp
push di
push si
!dec sp
!dec sp
! Debug: eq * char str = [S+8+2] to * char ptr1 = [S+8-8] (used reg = )
!mov bx,4[bp]
!mov -6[bp],bx
!BCC_EOS
! 14 return;
!inc sp
!inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 15 }
! 16 
! Register BX used in function c_print_string
.data
.1:
.2:
.ascii "Hallo Welt"
.byte 0

.bss

! 0 errors detected


Was man hier sieht, sind die Segmente (Code-Segment, Daten-Segment, Stack-Segment). Und bei Intel schleicht sich hier schnell ein Fehler ein. Greift man zum Beispiel aus einem Code Segment auf Daten zu, die im Datensegment sind, ist aber das DS-Register nicht richtig initialisiert, dann führt das schnell zu Fehlern. Umgekehrt kann man Variablen genauso gut im Code Segment deklarieren und das machen wir jetzt:

! 1 
! 1 # 1 "printstring3.c"
! 1 void c_print_string();
!BCC_EOS
! 2 
! 3 void main()
! 4 # 3 "printstring3.c"
! 3 {
export _main
_main:
! 4     char *buf = "Hallo Welt";
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq [$B] char = .1+0 to * char buf = [S+8-8] (used reg = )
mov bx,#.1
mov -6[bp],bx
!BCC_EOS
! 5     c_print_string(buf);
! Debug: list * char buf = [S+8-8] (used reg = )
push -6[bp]
! Debug: func () void = c_print_string+0 (used reg = )
call _c_print_string
inc sp
inc sp
!BCC_EOS
! 6 return;
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 7 }
! 8 
! 9 
! 10 void c_print_string(str)
! Register BX used in function main
! 11 # 10 "printstring3.c"
! 10 char *str;
export _c_print_string
_c_print_string:
!BCC_EOS
! 11 # 10 "printstring3.c"
! 10 {
!&n bsp;11     char *ptr1;
!BCC_EOS
! 12     
! 13     ptr1 = str;
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq * char str = [S+8+2] to * char ptr1 = [S+8-8] (used reg = )
mov bx,4[bp]
mov -6[bp],bx
!BCC_EOS
! 14 return;
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 15 }
! 16 
! Register BX used in function c_print_string
.1:
.2:
.ascii "Hallo Welt"
.byte 0
.data

.bss

! 0 errors detected


Wir haben .data nach ganz unten gelegt und siehe da: Es funktioniert.

Wo werden die Variablen übergeben?

! 1 
! 1 # 1 "printstring3.c"
! 1 void c_print_string();
!BCC_EOS
! 2 
! 3 void main()
! 4 # 3 "printstring3.c"
! 3 {
export _main
_main:
! 4     char *buf = "Hallo Welt";
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq [$B] char = .1+0 to * char buf = [S+8-8] (used reg = )
mov bx,#.1
mov -6[bp],bx
!BCC_EOS
! 5     c_print_string(buf);
! Debug: list * char buf = [S+8-8] (used reg = )
push -6[bp]
! Debug: func () void = c_print_string+0 (used reg = )
call _c_print_string
inc sp
inc sp
!BCC_EOS
! 6 return;
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 7 }
! 8 
! 9 
! 10 void c_print_string(str)
! Register BX used in function main
! 11 # 10 "printstring3.c"
! 10 char *str;
export _c_print_string
_c_print_string:
!BCC_EOS
! 11 # 10 "printstring3.c"
! 10 {
!&n bsp;11     char *ptr1;
!BCC_EOS
! 12     
! 13     ptr1 = str;
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq * char str = [S+8+2] to * char ptr1 = [S+8-8] (used reg = )
mov bx,4[bp]
mov -6[bp],bx

!BCC_EOS
! 14 return;
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 15 }
! 16 
! Register BX used in function c_print_string
.data
.1:
.2:
.ascii "Hallo Welt"
.byte 0
.bss

! 0 errors detected


Wir werden nachher sehen, die Variablen weren tatsäber BX übergeben.

Die erste Quasi-Konsole



int c_strcmp(char *str1, char *str2) {
    char *ptr1;
    char *ptr2;
    
    for(ptr1 = str1, ptr2 = str2;   (((*ptr1) != 0) && ((*ptr2) != 0));  ptr1++, ptr2++) {
        if(*ptr1 != *ptr2)
            return 1;
    }
return 0;
}

void c_print_str(char *str) {
    char *ptr;
    
return;    
}

void c_get_str(char *str) {
    char *ptr;
    

return;
}

void main(void) {
    char buf[256];
    char *welcome_str = "Welcome to http://www.talkortell.de - OS\n\n";
    char *new_line_str = "\n";
    char *prompt_str = "> ";
    char *str_smiley_text = "smiley";
    char *str_smiley_sym = ":-)";
    char *str_fullstop_text = "fullstop";
   &n bsp;char *str_fullstop_sym = ".";
    char *str_help = "Possible Commands are:\n\n smiley\n:-)\nfullstop\n.\nhelp\nexit\n\n";
    char *str_unknown_cmd = "Unknown Command!!!\nType \"help\" to list commands\n\n";
    
    c_print_str(welcome_str);
    while(1) {
        c_print_str(prompt_str);
        c_get_str(buf);
        c_print_str(new_line_str);
        if(c_strcmp(buf, "exit") == 0)
            break;
        else if(c_strcmp(buf, str_smiley_text) == 0)
            c_print_str(str_smiley_sym);
        else if(c_strcmp(buf, str_smiley_sym ) == 0)
            c_print_str(str_smiley_text);
        else if(c_strcmp(buf, str_fullstop_text) == 0)
            c_print_str(str_ fullstop_sym);
        else if(c_strcmp(buf, str_fullstop_sym) == 0)
            c_print_str(str_fullstop_text);
        else if(c_strcmp(buf, str_help) == 0) 
            c_print_str(str_help);
        else
            c_print_str(str_unknown_cmd);
        c_print_str(new_line_str);
        c_print_str(new_line_str);
    }
return;
}


Und in Assembler machen wir daraus:

  mov ax, #0x400 
  mov ds, ax
  mov es, ax
  mov si, #welcome
  call _print_string
  call _main
_again1: jmp _again1



_get_string:
  xor cl, cl
_get_string_loop:
  xor ah, ah   
  int #0x16    
  cmp al, #13    
  je _get_string_done
  cmp cl, #31    
  je _get_string_loop
  mov ah, #0x0E
  int #0x10     
  stosb  
  inc cl
  jmp _get_string_loop
_get_string_done:
  mov al, #0
  stosb
  mov ax, #0x0E0D
  int #0x10
  mov al, #0x0A
  int #0x10
  ret


_print_string:
    lodsb             
    or al, al         
    jz _print_string_done          
    mov ah, #0x0E
    int #0x10          
    jmp _print_string
_print_string_done:
    ret

welcome: .asciz "Welcome to http:/ /www.talkortell.de!!!"
.byte 0


! 1 
! 1 # 1 "main.c"
! 1 int c_strcmp(str1,str2)
! 2 # 1 "main.c"
! 1 char *str1;
export _c_strcmp
_c_strcmp:
!BCC_EOS
! 2 # 1 "main.c"
! 1 char *str2;
!BCC_EOS
! 2 # 1 "main.c"
! 1 {
! 2     char *ptr1;
!BCC_EOS
! 3     char *ptr2;
!BCC_EOS
! 4     
! 5     for(ptr1 = str1,ptr2 = str2;   (((*ptr1)!= 0)&& ((*ptr2)!= 0));  ptr1++,ptr2++) {
push bp
mov bp,sp
push di
push si
add sp,*-4
! Debug: eq * char str1 = [S+$A+2] to * char ptr1 = [S+$A-8] (used reg = )
mov bx,4[bp]
mov -6[bp],bx
! Debug: eq * char str2 = [S+$A+4] to * char ptr2 = [S+$A-$A] (used reg = )
mov bx,6[bp]
mov -8[bp],bx
!BCC_EOS
!BCC_EOS
jmp .3
.4:
! 6         if(*ptr1 != *ptr2)
mov bx,-8[bp]
mov si,-6[bp]
! Debug: ne char = [bx+0] to char = [si+0] (used reg = )
mov al,[si]
cmp al,[bx]
je   .5
.6:
! 7             return 1;
mov ax,*1
add sp,*4
pop si
pop di
pop bp
ret
!BCC_EOS
! 8     }
.5:
! 9 return 0;
.2:
! Debug: postinc * char ptr1 = [S+$A-8] (used reg = )
mov bx,-6[bp]
inc bx
mov -6[bp],bx
! Debug: postinc * char ptr2 = [S+$A-$A] (used reg = )
mov bx,-8[bp]
inc bx
mov -8[bp],bx
.3:
mov bx,-6[bp]
! Debug: ne int = const 0 to char = [bx+0] (used reg = )
mov al,[bx]
test al,al
je   .7
.8:
mov bx,-8[bp]
! Debug: ne int = const 0 to char = [bx+0] (used reg = )
mov al,[bx]
test al,al
jne .4
.7:
.1:
xor ax,ax
add sp,*4
pop si
pop di
pop bp
ret
!BCC_EOS
! 10 }
! 11 
! 12 void c_print_ ;str(str)
! Register BX SI used in function c_strcmp
! 13 # 12 "main.c"
! 12 char *str;
export _c_print_str
_c_print_str:
!BCC_EOS
! 13 # 12 "main.c"
! 12 {
! 13     char *ptr;
!BCC_EOS
! 14     
! 15     ptr = str;
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq * char str = [S+8+2] to * char ptr = [S+8-8] (used reg = )
mov bx,4[bp]
mov -6[bp],bx

mov si, bx
call _print_string


!BCC_EOS
! 16 return;    
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 17 }
! 18 
! 19 void c_get_str(str)
! Register BX used in function c_print_str
! 20 # 19 "main.c"
! 19 char *str;
export _c_get_str
_c_get_str:
!BCC_EOS
! 20 # 19 "main.c"
! 19 {
! 20     char *ptr;
!BCC_EOS
! 21     
! 22     ptr = str;
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug:  eq * char str = [S+8+2] to * char ptr = [S+8-8] (used reg = )
mov bx,4[bp]
mov -6[bp],bx

mov di, bx
call _get_string


!BCC_EOS
! 23 return;
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 24 }
! 25 
! 26 void main()
! Register BX used in function c_get_str
! 27 # 26 "main.c"
! 26 {
export _main
_main:
! 27     char buf[256];
!BCC_EOS
! 28     char *welcome_str = "Welcome to http://www.talkortell.de - OS\r\n\r\n";
push bp
mov bp,sp
push di
push si
add sp,#-$102
! Debug: eq [$2D] char = .9+0 to * char welcome_str = [S+$108-$108] (used reg = )
mov bx,#.9
mov -$106[bp],bx
!BCC_EOS
! 29     char *new_line_str = "\r\n";
dec sp
dec sp
! Debug: eq [3] char = .A+0 to * char new_line_str = [S+$10A-$10A] (used reg = )
mov bx,#.A
mov -$108[bp],bx
!BCC_EOS
! 30     char *prompt_str = "> ";
dec sp
dec sp
! Debug: eq [3] char = .B+0 to * char prompt_str = [S+$10C-$10C] (used reg = )
mov bx,#.B
mov -$10A[bp],bx
!BCC_EOS
! 31     char *str_smiley_text = "smiley";
dec sp
dec sp
! Debug: eq [7] char = .C+0 to * char str_smiley_text = [S+$10E-$10E] (used reg = )
mov bx,#.C
mov -$10C[bp],bx
!BCC_EOS
! 32     char *str_smiley_sym = ":-)";
dec sp
dec sp
! Debug: eq [4] char = .D+0 to * char str_smiley_sym = [S+$110-$110] (used reg = )
mov bx,#.D
mov -$10E[bp],bx
!BCC_EOS
! 33     char *str_fullstop_text = "fullstop";
dec sp
dec sp
! Debug: eq [9] char = .E+0 to * char str_fullstop_text = [S+$112-$112] (used reg = )
mov bx,#.E
mov -$110[bp],bx
!BCC_EOS
! 34     char *str_fullstop_sym = ".";
dec sp
dec sp
! Debug: eq [2] char = .F+0 to * char str_fullstop_sym = [S+$114-$114] (used reg = )
mov bx,#.F
mov -$112[bp],bx
!BCC_EOS
! 35     char *str_help = "help";
dec sp
dec sp
! Debug: eq [5] char = .10+0 to * char str_help = [S+$116-$116] (used reg = )
mov bx,#.10
mov -$114[bp],bx
!BCC_EOS
! 36     char *str_help_out = "Possible Commands are:\r\n\n smiley\r\n:-)\r\nfullstop\r\n.\r\nhelp\r\nexit\r\n\r\n";
dec sp
dec sp
! Debug: eq [$43] char = .11+0 to * char str_help_out = [S+$118-$118] (used reg = )
mov bx,#.11
mov -$116[bp],bx
!BCC_EOS
! 37     char *str_unknown_cmd = "Unknown Command!!!\r\nType \"help\" to list commands\r\n\r\n";
dec sp
dec sp
! Debug: eq [$35] char = .12+0 to * char str_unknown_cmd = [S+$11A-$11A] (used reg = )
mov bx,#.12
mov -$118[bp],bx
!BCC_EOS
! 38     
! 39     c_print_str(welcome_str);
! Debug: list * char welcome_str = [S+$11A-$108] (used reg = )
push -$106[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 40     while(1) {
.15:
! 41         c_print_str(prompt_str);
! Debug: list * char prompt_str = [S+$11A-$10C] (used reg = )
push -$10A[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 42         c_get_str(buf);
! Debug: list * char buf = S+$11A-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () void = c_get_str+0 (used reg = )
call _c_get_str
inc sp
inc sp
!BCC_EOS
! 43         c_print_str(new_line_str);
! Debug: list * char new_line_str = [S+$11A-$10A] (used reg = )
push -$108[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 44         if(c_strcmp(buf,"exit")== 0)
! Debug: list * char = .17+0 (used reg = )
mov bx,#.17
push bx
! Debug: list * char buf = S+$11C-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .16
.18:
! 45             break;
br  .13
!BCC_EOS
! 46         else if(c_strcmp(buf,str_smiley_text)== 0)
br  .19
.16:
! Debug: list * char str_smiley_text = [S+$11A-$10E] (used reg = )
push -$10C[bp]
! Debug: list * char buf = S+$11C-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .1A
.1B:
! 47             c_print_str(str_smiley_sym);
! Debug: list * char str_smiley_sym = [S+$11A-$110] (used reg = )
push -$10E[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 48         else if(c_strcmp(buf,str_smiley_sym )== 0)
br  .1C
.1A:
! Debug: list * char str_smiley_sym = [S+$11A-$110] (used reg = )
push -$10E[bp]
! Debug: list * char buf = S+$11C-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .1D
.1E:
! 49             c_print_str(str_smiley_text);
! Debug: list * char str_smiley_text = [S+$11A-$10E] (used reg = )
push -$10C[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 50         else if(c_strcmp(buf,str_fullstop_text)== 0)
jmp .1F
.1D:
! Debug: list * char str_fullstop_text = [S+$11A-$112] (used reg = )
push -$110[bp]
! Debug: list * char buf = S+$11C-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .20
.21:
! 51             c_print_str(str_fullstop_sym);
! Debug: list * char str_fullstop_sym = [S+$11A-$114] (used reg = )
push -$112[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 52         else if(c_strcmp(buf,str_fullstop_sym)== 0)
jmp .22
.20:
! Debug: list * char str_fullstop_sym = [S+$11A-$114] (used reg = )
push -$112[bp]
! Debug: list * char buf = S+$11C-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .23
.24:
! 53             c_print_str(str_fullstop_text);
! Debug: list * char str_fullstop_text = [S+$11A-$112] (used reg = )
push -$110[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 54          else if(c_strcmp(buf,str_help)== 0) 
jmp .25
.23:
! Debug: list * char str_help = [S+$11A-$116] (used reg = )
push -$114[bp]
! Debug: list * char buf = S+$11C-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .26
.27:
! 55             c_print_str(str_help);
! Debug: list * char str_help = [S+$11A-$116] (used reg = )
push -$114[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 56         else
! 57             c_print_str(str_unknown_cmd);
jmp .28
.26:
! Debug: list * char str_unknown_cmd = [S+$11A-$11A] (used reg = )
push -$118[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 58         c_print_str(new_line_str);
.28:
.25:
.22:
.1F:
.1C:
.19:
! Debug: list * char new_line_str = [S+$11A-$10A] (used reg = )
push -$108[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 59         c_print_str(new_line_str);
! Debug: list * char new_line_str = [S+$11A-$10A] (used reg = )
push -$108[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 60     }
! 61 return;
.14:
br  .15
.29:
.13:
lea sp,-4[bp]
pop si
pop di
pop bp
ret
!BCC_EOS
! 62 }
! 63 
! Register BX used in function main
.17:
.2A:
.ascii "exit"
.byte 0
.12:
.2B:
.ascii "Unknown Command!!!"
.byte $D,$A
.ascii "Type \"help\" to list commands"
.byte $D,$A,$D,$A
.byte 0
.11:
.2C:
.ascii "Possible Commands are:"
.byte $D,$A,$A
.ascii " smiley"
.byte $D,$A
.ascii ":-)"
.byte $D,$A
.ascii "fullstop"
.byte $D,$A
.ascii "."
.byte $D,$A
.ascii "help"
.byte $D,$A
.ascii "exit"
.byte $D,$A,$D,$A
.byte 0
.10:
.2D:
.ascii "help"
.byte 0
.F:
.2E:
.ascii "."
.byte 0
.E:
.2F:
.ascii "fullstop"
.byte 0
.D:
.30:
.ascii ":-)"
.byte 0
.C:
.31:
.ascii "smiley"
.byte 0
.B:
.32:
.ascii "> "
.byte 0
.A:
.33:
.byte $D,$A
.byte 0
.9:
.34:
.ascii "Welcome to http://www.talkortell.de - OS"
.byte $D,$A,$D,$A
.byte 0
.data
.bss

! 0 errors detected


Der erste fertige Schritt

Wir schreiben eine READ_SECTOR und WRITE_SECTOR

So, ich habe ein funktionierendes WRITE_SECTOR und das READ_SECTOR wird dementsprechend auch stimmen.


int c_strcmp(char *str1, char *str2) {
    char *ptr1;
    char *ptr2;
    
    for(ptr1 = str1, ptr2 = str2;   (((*ptr1) != 0) && ((*ptr2) != 0));  ptr1++, ptr2++) {
        if(*ptr1 != *ptr2)
            return 1;
    }
return 0;
}

void c_print_str(char *str) {
    char *ptr;
    
    ptr = str;
return;    
}

void c_get_str(char *str) {
    char *ptr;
    
    ptr = str;
return;
}

void read_sector(int sector, int *buf) {
    int head;
    int cylinder;
    int sector_per_track;

    cylinder = (sector / (18*2));
    head = mod(sector, (18*2))/18;
    sector_per_track = mod(mod(sector, (18*2)),18);
    
    cylinder = 0;
    head = 0;
    sector_per_track = 0;
    
    buf = 0;
    
return;   
}

write_sector(int sector, int *buf) {
    int head;
    int cylinder;
    int sector_per_track;

    cylinder = (sector / (18*2));
    head = mod(sector, (18*2))/18;
    sector_per_track = mod(mod(sector, (18*2)),18);
    
    cylinder = 0;
    head = 0;
    sector_per_track = 0;
        
    buf = 0;

return;    
}

int mod(int y, int x) {
    int x2;
    
    for(x2 = x;  x2 < y;  x2+=x);
    if(x2 > y)
        x2-=x;
return y-x2;
}




void main(void) {
    char buf[256];
    char *welcome_str = "Welcome to http://www.talkortell.de - OS\r\n\r\n";
    char  *new_line_str = "\r\n";
    char *prompt_str = "> ";
    char *str_smiley_text = "smiley";
    char *str_smiley_sym = ":-)";
    char *str_fullstop_text = "fullstop";
    char *str_fullstop_sym = ".";
    char *str_help = "help";
    char *str_help_out = "Possible Commands are:\r\n\n smiley\r\n:-)\r\nfullstop\r\n.\r\nhelp\r\nexit\r\n\r\n";
    char *str_unknown_cmd = "Unknown Command!!!\r\nType \"help\" to list commands\r\n\r\n";
    char sec_buf[512];
    
    int i;
    
    for(i = 0;  i < 512;  i+=3) {
        sec_buf[i+0] = 'A';
        sec_buf[i+1] = 'B';
        sec_buf[i+2] = 'C';
    }
    
    write_sector(4, sec_buf);
    c_print_str(welcome_str);
    while(1) {
        c_print_str(prompt_str);
        c_get_str(buf);
        c_print_str(new_line_str);
        if(c_strcmp(buf, "exit") == 0)
            break;
        else if(c_strcmp(buf, str_smiley_text) == 0)
            c_print_str(str_smiley_sym);
        else if(c_strcmp(buf, str_smiley_sym ) == 0)
            c_print_str(str_smiley_text);
        else if(c_strcmp(buf, str_fullstop_text) == 0)
            c_print_str(str_fullstop_sym);
        else if(c_strcmp(buf, str_fullstop_sym) == 0)
            c_print_str(str_fullstop_text);
        else if(c_strcmp(buf, str_help) == 0) 
            c_print_str(str_help);
        else
            c_print_str(str_unknown_cmd);
        c_print_str(new_line_str);
        c_print_str(new_line_str);
    }
return;
}


mainx01.c
Und der Assembler-Quelltext dazu


  mov ax, #0x400 
  mov ds, ax
  mov es, ax
  mov si, #welcome
  call _print_string
  call _main
_again1: jmp _again1



_get_string:
  xor cl, cl
_get_string_loop:
  xor ah, ah   
  int #0x16    
  cmp al, #13    
  je _get_string_done
  cmp cl, #31    
  je _get_string_loop
  mov ah, #0x0E
  int #0x10     
  stosb  
  inc cl
  jmp _get_string_loop
_get_string_done:
  mov al, #0
  stosb
  mov ax, #0x0E0D
  int #0x10
  mov al, #0x0A
  int #0x10
  ret


_print_string:
    lodsb             
    or al, al         
    jz _print_string_done          
    mov ah, #0x0E
    int #0x10          
    jmp _print_string
_print_string_done:
    ret

welcome: .asciz "Welcome to http:/ /www.talkortell.de!!!"
.byte 0


! 1 
! 1 # 1 "main.c"
! 1 int c_strcmp(str1,str2)
! 2 # 1 "main.c"
! 1 char *str1;
export _c_strcmp
_c_strcmp:
!BCC_EOS
! 2 # 1 "main.c"
! 1 char *str2;
!BCC_EOS
! 2 # 1 "main.c"
! 1 {
! 2     char *ptr1;
!BCC_EOS
! 3     char *ptr2;
!BCC_EOS
! 4     
! 5     for(ptr1 = str1,ptr2 = str2;   (((*ptr1)!= 0)&& ((*ptr2)!= 0));  ptr1++,ptr2++) {
push bp
mov bp,sp
push di
push si
add sp,*-4
! Debug: eq * char str1 = [S+$A+2] to * char ptr1 = [S+$A-8] (used reg = )
mov bx,4[bp]
mov -6[bp],bx
! Debug: eq * char str2 = [S+$A+4] to * char ptr2 = [S+$A-$A] (used reg = )
mov bx,6[bp]
mov -8[bp],bx
!BCC_EOS
!BCC_EOS
jmp .3
.4:
! 6         if(*ptr1 != *ptr2)
mov bx,-8[bp]
mov si,-6[bp]
! Debug: ne char = [bx+0] to char = [si+0] (used reg = )
mov al,[si]
cmp al,[bx]
je   .5
.6:
! 7             return 1;
mov ax,*1
add sp,*4
pop si
pop di
pop bp
ret
!BCC_EOS
! 8     }
.5:
! 9 return 0;
.2:
! Debug: postinc * char ptr1 = [S+$A-8] (used reg = )
mov bx,-6[bp]
inc bx
mov -6[bp],bx
! Debug: postinc * char ptr2 = [S+$A-$A] (used reg = )
mov bx,-8[bp]
inc bx
mov -8[bp],bx
.3:
mov bx,-6[bp]
! Debug: ne int = const 0 to char = [bx+0] (used reg = )
mov al,[bx]
test al,al
je   .7
.8:
mov bx,-8[bp]
! Debug: ne int = const 0 to char = [bx+0] (used reg = )
mov al,[bx]
test al,al
jne .4
.7:
.1:
xor ax,ax
add sp,*4
pop si
pop di
pop bp
ret
!BCC_EOS
! 10 }
! 11 
! 12 void c_print_str(str)
! Register BX SI used in function c_strcmp
! 13 # 12 "main.c"
! 12 char *str;
export _c_print_str
_c_print_str:
!BCC_EOS
! 13 # 12 "main.c"
! 12 {
! 13     char *ptr;
!BCC_EOS
! 14     
! 15     ptr = str;
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq * char str = [S+8+2] to * char ptr = [S+8-8] (used reg = )
mov bx,4[bp]
mov -6[bp],bx

mov si, bx
call _print_string


!BCC_EOS
! 16 return;    
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 17 }
! 18 
! 19 void c_get_str(str)
! Register BX used in function c_print_str
! 20 # 19 "main.c"
! 19 char *str;
export _c_get_str
_c_get_str:
!BCC_EOS
! 20 # 19 "main.c"
! 19 {
! 20     char *ptr;
!BCC_EOS
! 21     
! 22     ptr = str;
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq * char str = [S+8+2] to * char ptr = [S+8-8] (used reg = )
mov bx,4[bp]
mov -6[bp],bx

mov di, bx
call _get_string


!BCC_EOS
! 23 return;
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 24 }
! 25 
! 26 void read_sector(sector,buf)
! Register BX used in function c_get_str
! 27 # 26 "main.c"
! 26 int sector;
export _read_sector
_read_sector:
!BCC_EOS
! 27 # 26 "main.c"
! 26 int *buf;
!BCC_EOS
! 27 # 26 "main.c"
! 26 {
! 27     int head;
!BCC_EOS
! 28     int cylinder;
!BCC_EOS
! 29     int sector_per_track;
!BCC_EOS
! 30 
! 31     cylinder = (sector / (18*2));
push bp
mov bp,sp
push di
push si
add sp,*-6
! Debug: div int = const $24 to int sector = [S+$C+2] (used reg = )
mov ax,4[bp]
mov bx,*$24
cwd
idiv bx
! Debug: eq int = ax+0 to int cylinder = [S+$C- 6;A] (used reg = )
mov -8[bp],ax
!BCC_EOS
! 32     head = mod(sector,(18*2))/18;
! Debug: list int = const $24 (used reg = )
mov ax,*$24
push ax
! Debug: list int sector = [S+$E+2] (used reg = )
push 4[bp]
! Debug: func () int = mod+0 (used reg = )
call _mod
add sp,*4
! Debug: div int = const $12 to int = ax+0 (used reg = )
mov bx,*$12
cwd
idiv bx
! Debug: eq int = ax+0 to int head = [S+$C-8] (used reg = )
mov -6[bp],ax
!BCC_EOS
! 33     sector_per_track = mod(mod(sector,(18*2)),18);
! Debug: list int = const $12 (used reg = )
mov ax,*$12
push ax
! Debug: list int = const $24 (used reg = )
mov ax,*$24
push ax
! Debug: list int sector = [S+$10+2] (used reg = )
push 4[bp]
! Debug: func () int = mod+0 (used reg = )
call _mod
add sp,*4
! Debug: list int = ax+0 (used reg = )
push ax
! Debug: func () int = mod+0 (used reg = )
call _mod
add sp,*4
! Debug: eq int = ax+0 to int sector_per_track = [S+$C-$C] (used reg = )
mov -$A[bp],ax
!BCC_EOS
! 34     
! 35     cylinder = 0;
! Debug: eq int = const 0 to int cylinder = [S+$C-$A] (used reg = )
!xor ax,ax
!mov -8[bp],ax
!BCC_EOS
! 36     head = 0;
! Debug: eq int = const 0 to int head = [S+$C-8] (used reg = )
!xor ax,ax
!mov -6[bp],ax
!BCC_EOS
! 37     sector_per_track = 0;
! Debug: eq int = const 0 to int sector_per_track = [S+$C-$C] (used reg = )
!xor ax,ax
!mov -$A[bp],ax
!BCC_EOS
! 38     
! 39     buf = 0;
! Debug: eq int = const 0 to * int buf = [S+$C+4] (used reg = )
!xor ax,ax
!mov 6[bp],ax

mov al, #1
mov bx, 6[bp]
mov dl, #0
mov ch, -8[bp] !Zylinder
mov cl, -$A[bp] !Sector
mov dh, -6[bp] !Head
mov ah, #2
int #0x13

!BCC_EOS
! 40     
! 41 return;   
add sp,*6
pop si
pop di
pop bp
ret
!BCC_EOS
! 42 }
! 43 
! 44 write_sector(sector,buf)
! Register BX used in function read_sector
! 45 # 44 "main.c"
! 44 int sector;
export _write_sector
_write_sector:
!BCC_EOS
! 45 # 44 "main.c"
! 44 int *buf;
!BCC_EOS
! 45 # 44 "main.c"
! 44 {
! 45     int head;
!BCC_EOS
! 46     int cylinder;
!BCC_EOS
! 47     int sector_per_track;
!BCC_EOS
! 48 
! 49     cylinder = (sector / (18*2));
push bp
mov bp,sp
push di
push si
add sp,*-6
! Debug: div int = const $24 to int sector = [S+$C+2] (used reg = )
mov ax,4[bp]
mov bx,*$24
cwd
idiv bx
! Debug: eq int = ax+0 to int cylinder = [S+$C-$A] (used reg = )
mov -8[bp],ax
!BCC_EOS
! 50     head = mod(sector,(18*2))/18;
! Debug: list int = const $24 (used reg = )
mov ax,*$24
push ax
! Debug: list int sector = [S+$E+2] (used reg = )
push 4[bp]
! Debug: func () int = mod+0 (used reg = )
call _mod
add sp,*4
! Debug: div int = const $12 to int = ax+0 (used reg = )
mov bx,*$12
cwd
idiv bx
! Debug: eq int = ax+0 to int head = [S+$C-8] (used reg = )
mov -6[bp],ax
!BCC_EOS
! 51     sector_per_track = mod(mod(sector,(18*2)),18);
! Debug: list int = const $12 (used reg = )
mov ax,*$12
push ax
! Debug: list int = const $24 (used reg = )
mov ax,*$24
push ax
! Debug: list int sector = [S+$10+2] (used reg = )
push 4[bp]
! Debug: func () int = mod+0 (used reg = )
call _mod
add sp,*4
! Debug: list int = ax+0 (used reg = )
push ax
! Debug: func () int = mod+0 (used reg = )
call _mod
add sp,*4
! Debug: eq int = ax+0 to int sector_per_track = [S+$C-$C] (used reg = )
mov -$A[bp],ax
!BCC_EOS
! 52     
! 53     cylinder = 0;
! Debug: eq int = const 0 to int cylinder = [S+$C-$A] (used reg = )
!xor ax,ax
!mov -8[bp],ax
!BCC_EOS
! 54     head = 0;
! Debug: eq int = const 0 to int head = [S+$C-8] (used reg = )
!xor ax,ax
!mov -6[bp],ax
!BCC_EOS
! 55     sector_per_track = 0;
! Debug: eq int = const 0 to int sector_per_track = [S+$C-$C] (used reg = )
!xor ax,ax
!mov -$A[bp],ax
!BCC_EOS
! 56         
! 57     buf = 0;
! Debug: eq in t = const 0 to * int buf = [S+$C+4] (used reg = )
!xor ax,ax
!mov 6[bp],ax

mov al, #1
mov bx, 6[bp]
mov dl, #0
mov ch, -8[bp] !Zylinder
mov cl, -$A[bp] !Sector
mov dh, -6[bp] !Head
mov ah, #0x03
int #0x13

!BCC_EOS
! 58 
! 59 return;    
add sp,*6
pop si
pop di
pop bp
ret
!BCC_EOS
! 60 }
! 61 
! 62 int mod(y,x)
! Register BX used in function write_sector
! 63 # 62 "main.c"
! 62 int y;
export _mod
_mod:
!BCC_EOS
! 63 # 62 "main.c"
! 62 int x;
!BCC_EOS
! 63 # 62 "main.c"
! 62 {
! 63     int x2;
!BCC_EOS
! 64     
! 65     for(x2 = x;  x2 < y;  x2+=x);
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq int x = [S+8+4] to int x2 = [S+8-8] (used reg = )
mov ax,6[bp]
mov -6[bp],ax
!BCC_EOS
!BCC_EOS
jmp .B
.C:
!BCC_EOS
! 66     if(x2 > y)
.A:
! Debug: addab int x = [S+8+4] to int x2 = [S+8-8] (used reg = )
mov ax,-6[bp]
add ax,6[bp]
mov -6[bp],ax
.B:
! Debug: lt int y = [S+8+2] to int x2 = [S+8-8] (used reg = )
mov ax,-6[bp]
cmp ax,4[bp]
jl  .C
.D:
.9:
! Debug: gt int y = [S+8+2] to int x2 = [S+8-8] (used reg = )
mov ax,-6[bp]
cmp ax,4[bp]
jle  .E
.F:
! 67         x2-=x;
! Debug: subab int x = [S+8+4] to int x2 = [S+8-8] (used reg = )
mov ax,-6[bp]
sub ax,6[bp]
mov -6[bp],ax
!BCC_EOS
! 68 return y-x2;
.E:
! Debug: sub int x2 = [S+8-8] to int y = [S+8+2] (used reg = )
mov ax,4[bp]
sub ax,-6[bp]
! Debug: cast int = const 0 to int = ax+0 (used reg = )
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 69 }
! 70 # 74
! 74 void main()
! 75 # 74 "main.c"
! 74 {
export _main
_main:
! 75     char buf[256];
!BCC_EOS
! 76     char *welcome_str = "Welcome to http://www.talkortell.de - OS\r\n\r\n";
push bp
mov bp,sp
push di
push si
add sp,#-$102
! Debug: eq [$2D] char = .10+0 to * char welcome_str = [S+$108-$108] (used reg = )
mov bx,#.10
mov -$106[bp],bx
!BCC_EOS
! 77     char *new_line_str = "\r\n";
dec sp
dec sp
! Debug: eq [3] char = .11+0 to * char new_line_str = [S+$10A-$10A] (used reg = )
mov bx,#.11
mov -$108[bp],bx
!BCC_EOS
! 78     char *prompt_str = "> ";
dec sp
dec sp
! Debug: eq [3] char = .12+0 to * char prompt_str = [S+$10C-$10C] (used reg = )
mov bx,#.12
mov -$10A[bp],bx
!BCC_EOS
! 79     char *str_smiley_text = "smiley";
dec sp
dec sp
! Debug: eq [7] char = .13+0 to * char str_smiley_text = [S+$10E-$10E] (used reg = )
mov bx,#.13
mov -$10C[bp],bx
!BCC_EOS
! 80     char *str_smiley_sym = ":-)";
dec sp
dec sp
! Debug: eq [4] char = .14+0 to * char str_smiley_sym = [S+$110-$110] (used reg = )
mov bx,#.14
mov -$10E[bp],bx
!BCC_EOS
! 81     char *str_fullstop_text = "fullstop";
dec sp
dec sp
! Debug: eq [9] char = .15+0 to * char str_fullstop_text = [S+$112-$112] (used reg = )
mov bx,#.15
mov -$110[bp],bx
!BCC_EOS
! 82     char *str_fullstop_sym = ".";
dec sp
dec sp
! Debug: eq [2] char = .16+0 to * char str_fullstop_sym = [S+$114-$114] (used reg = )
mov bx,#.16
mov -$112[bp],bx
!BCC_EOS
! 83     char *str_help = "help";
dec sp
dec sp
! Debug:  eq [5] char = .17+0 to * char str_help = [S+$116-$116] (used reg = )
mov bx,#.17
mov -$114[bp],bx
!BCC_EOS
! 84     char *str_help_out = "Possible Commands are:\r\n\n smiley\r\n:-)\r\nfullstop\r\n.\r\nhelp\r\nexit\r\n\r\n";
dec sp
dec sp
! Debug: eq [$43] char = .18+0 to * char str_help_out = [S+$118-$118] (used reg = )
mov bx,#.18
mov -$116[bp],bx
!BCC_EOS
! 85     char *str_unknown_cmd = "Unknown Command!!!\r\nType \"help\" to list commands\r\n\r\n";
dec sp
dec sp
! Debug: eq [$35] char = .19+0 to * char str_unknown_cmd = [S+$11A-$11A] (used reg = )
mov bx,#.19
mov -$118[bp],bx
!BCC_EOS
! 86     char sec_buf[512];
!BCC_EOS
! 87     
! 88     int i;
!BCC_EOS
! 89     
! 90     for(i 
add sp,#-$202
! 90 = 0;  i < 512;  i+=3) {
! Debug: eq int = const 0 to int i = [S+$31C-$31C] (used reg = )
xor ax,ax
mov -$31A[bp],ax
!BCC_EOS
!BCC_EOS
jmp .1C
.1D:
! 91         sec_buf[i+0] = 'A';
! Debug: add int = const 0 to int i = [S+$31C-$31C] (used reg = )
mov ax,-$31A[bp]
! Debug: ptradd int = ax+0 to [$200] char sec_buf = S+$31C-$31A (used reg = )
mov bx,bp
add bx,ax
! Debug: eq int = const $41 to char = [bx-$318] (used reg = )
mov al,*$41
mov -$318[bx],al
!BCC_EOS
! 92         sec_buf[i+1] = 'B';
! Debug: add int = const 1 to int i = [S+$31C-$31C] (used reg = )
mov ax,-$31A[bp]
! Debug: ptradd int = ax+1 to [$200] char sec_buf = S+$31C-$31A (used reg = )
inc ax
mov bx,bp
add bx,ax
! Debug: eq int = const $42 to char = [bx-$318] (used reg = )
mov al,*$42
mov -$318[bx],al
!BCC_EOS
! 93         sec_buf[i+2] = 'C';
! Debug: add int = const 2 to int i = [S+$31C-$31C] (used reg = )
mov ax,-$31A[bp]
! Debug: ptradd int = ax+2 to [$200] char sec_buf = S+$31C-$31A (used reg = )
inc ax
inc ax
mov bx,bp
add bx,ax
! Debug: eq int = const $43 to char = [bx-$318] (used reg = )
mov al,*$43
mov -$318[bx],al
!BCC_EOS
! 94     }
! 95     
! 96     write_sector(4,sec_buf);
.1B:
! Debug: addab int = const 3 to int i = [S+$31C-$31C] (used reg = )
mov ax,-$31A[bp]
add ax,*3
mov -$31A[bp],ax
.1C:
! Debug: lt int = const $200 to int i = [S+$31C-$31C] (used reg = )
mov ax,-$31A[bp]
cmp ax,#$200
jl  .1D
.1E:
.1A:
! Debug: list * char sec_buf = S+$31C-$31A (used reg = )
lea bx,-$318[bp]
push bx
! Debug: list int = const 4 (used reg = )
mov ax,*5
push ax
! Debug: func () int = write_sector+0 (used reg = )
call _write_sector

!mov al, #1
!mov bx, 6[bp]
!mov dl, #0
!mov ch, #0
!mov cl, #5
!mov dh, #0
!mov ah, #3
!int #0x13



add sp,*4
!BCC_EOS
! 97     c_print_str(welcome_str);
! Debug: list * char welcome_str = [S+$31C-$108] (used reg = )
push -$106[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 98     while(1) {
.21:
! 99         c_print_str(prompt_str);
! Debug: list * char prompt_str = [S+$31C-$10C] (used reg = )
push -$10A[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 100         c_get_str(buf);
! Debug: list * char buf = S+$31C-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () void = c_get_str+0 (used reg = )
call _c_get_str
inc sp
inc sp
!BCC_EOS
! 101         c_print_str(new_line_str);
! Debug: list * char new_line_str = [S+$31C-$10A] (used reg = )
push -$108[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 102         if(c_strcmp(buf,"exit")== 0)
! Debug: list * char = .23+0 (used reg = )
mov bx,#.23
push bx
! Debug: list * char buf = S+$31E-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .22
.24:
! 103             break;
br  .1F
!BCC_EOS
! 104         else if(c_strcmp(buf,str_smiley_text)== 0)
br  .25
.22:
! Debug: list * char str_smiley_text = [S+$31C-$10E] (used reg = )
push -$10C[bp]
! Debug: list * char buf = S+$31E-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .26
.27:
! 105             c_print_str(str_smiley_sym);
! Debug: list * char str_smiley_sym = [S+$31C-$110] (used reg = )
push -$10E[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 106         else if(c_strcmp(buf,str_smiley_sym )== 0)
br  .28
.26:
! Debug: list  * char str_smiley_sym = [S+$31C-$110] (used reg = )
push -$10E[bp]
! Debug: list * char buf = S+$31E-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .29
.2A:
! 107             c_print_str(str_smiley_text);
! Debug: list * char str_smiley_text = [S+$31C-$10E] (used reg = )
push -$10C[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 108         else if(c_strcmp(buf,str_fullstop_text)== 0)
jmp .2B
.29:
! Debug: list * char str_fullstop_text = [S+$31C-$112] (used reg = )
push -$110[bp]
! Debug: list * char buf = S+$31E-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .2C
.2D:
! 109             c_print_str(str_fullstop_sym);
! Debug: list * char str_fullstop_sym = [S+$31C-$114] (used reg = )
push -$112[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 110         else if(c_strcmp(buf,str_fullstop_sym)== 0)
jmp .2E
.2C:
! Debug: list * char str_fullstop_sym = [S+$31C-$114] (used reg = )
push -$112[bp]
! Debug: list * char buf = S+$31E-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .2F
.30:
! 111             c_print_str(str_fullstop_text);
! Debug: list * char str_fullstop_text = [S+$31C-$112] (used reg = )
push -$110[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 112         else if(c_strcmp(buf,str_help)== 0) 
jmp .31
.2F:
! Debug: list * char str_help = [S+$31C-$116] (used reg = )
push -$114[bp]
! Debug: list * char buf = S+$31E-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .32
.33:
! 113             c_print_str(str_help);
! Debug: list * char str_help = [S+$31C-$116] (used reg = )
push -$114[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 114         else
! 115             c_print_str(str_unknown_cmd);
jmp .34
.32:
! Debug: list * char str_unknown_cmd = [S+$31C-$11A] (used reg = )
push -$118[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 116         c_print_str(new_line_str);
.34:
.31:
.2E:
.2B:
.28:
.25:
! Debug: list * char new_line_str = [S+$31C-$10A] (used reg = )
push -$108[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 117         c_print_str(new_line_str);
! Debug: list * char new_line_str = [S+$31C-$10A] (used reg = )
push -$108[bp]
! Debug: func () void = c_pri nt_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 118     }
! 119 return;
.20:
br  .21
.35:
.1F:
lea sp,-4[bp]
pop si
pop di
pop bp
ret
!BCC_EOS
! 120 }
! 121 
! Register BX used in function main
.23:
.36:
.ascii "exit"
.byte 0
.19:
.37:
.ascii "Unknown Command!!!"
.byte $D,$A
.ascii "Type \"help\" to list commands"
.byte $D,$A,$D,$A
.byte 0
.18:
.38:
.ascii "Possible Commands are:"
.byte $D,$A,$A
.ascii " smiley"
.byte $D,$A
.ascii ":-)"
.byte $D,$A
.ascii "fullstop"
.byte $D,$A
.ascii "."
.byte $D,$A
.ascii "help"
.byte $D,$A
.ascii "exit"
.byte $D,$A,$D,$A
.byte 0
.17:
.39:
.ascii "help"
.byte 0
.16:
.3A:
.ascii "."
.byte 0
.15:
.3B:
.ascii "fullstop"
.byte 0
.14:
.3C:
.ascii ":-)"
.byte 0
.13:
.3D:
.ascii "smiley"
.byte 0
.12:
.3E:
.ascii "> "
.byte 0
.11:
.3F:
.byte $D,$A
.byte 0
.10:
.40:
.ascii "Welcome to http://www.talkortell.de - OS"
.byte $D,$A,$D,$A
.byte 0
.data
.bss

! 0 errors detected



mainxxx01.s
mod habe ich deswegen derartig verwendet, weil mod nicht im Assembler für zumindest große Zahlen vorhanden ist. Stattdessen wird eine Routine aufgerufen, die zu einem Unterprogramm führt, dass in einer Bibliothek des C-Programms ist. Linkt man aber das C-Programm nicht, erhält man im Assemblerprogramm diese Routine nicht.
Das zweite die Ausgabe, die belegt, dass die Sache ihren Dienst erfüllt. Ich habe den 5. Sektor beschrieben. Sektor 1 bis 4, sind belegt. Sektor 5 wurde beschrieben. Ich habe mir die IMG-Datei vom Kernel und Bootloader im Hexeditor angeschaut, heraus kam das:



Es funktioniert also. Vor dem Start des Kernels steht, statt ABC 000, ...

Das Dateisystem

So, jetzt schreiben wir das Dateisystem:
Das Dateisystem, wird ... Zu den Funktionsaufrufen READ, WRITE, OPEN, CLOSE, CREATE, MKDIR, RM, RMDIR kommt ein weiterer Befehl: MAKEFS. Dieser legt ein leeres Dateisystem an.


Ich habe es geschafft, ich habe den grundlegenden Pfeiler für ein FAT-Dateisystem erschaffen.
Es gibt bisher: Eine FAT, die von der Struktur funktioniert. READ_SECTOR, WRITE_SECTOR und ein READ und ein WRITE. Was noch nicht da ist, ist: Ein funktionierender Filehandler. Das ist nötig. Ein Filehandler bietet: Zunächst das mehrere Dateien geöffnet werden können. Das alleine ist immens wichtig. Denn: Man kann nicht erwarten, dass man nur eine Datei geöffnet hat. Umgekehrt: Ließt man aus einer Datei, dann ist es absolut normal, die Datei nicht vom Anfang bis zum Ende zu lesen oder zu schreiben. In diesem Falle hat man aber eine Position in der Datei. Wenn ich auf den Filehandler und seine zwei Kriterien eingehe, dann meine ich zum einen, dass man tatsächlich mehrere Dateien geöffnet hat, zum anderen aber, dass man sich die Position in der Datei merken muss. Der bisherige Quelltext erlaubt noch keine Speicherung der Position. Gehen wir zur Position über, ist aber noch etwas von Bedeutung: Nämlich: READ und WRITE greifen auf READ_SECTOR und WRITE_SECTOR zurück. Trotzdem bieten sie weit mehr. Denn, sie arbeiten bereits mit einem Dateisystem. Würde man sagen READ und WRITE bauen auf READ_SECTOR und WRITE_SECTOR auf, bieten aber nicht mehr, als, dass sie in Anführungsstrichen mehrere Sektoren einlesen, wäre das immer noch nicht automatisch ein Dateisystem. Man könnte READ und WRITE, auf 10 aufeinander folgende Sektoren anwenden. Das tun sie aber in diesem Falle nicht, sondern sie arbeiten tatsächlich bereits mit einer FAT, aber trotzdem, obwohl die FAT bereits bestens implementiert ist, und daran nichts aus zu setzen ist, das Ziel erfüllt ist, muss das ganze so eingerichtet werden, dass es ab einer Position lesbar ist. Das ist aber eine verhältnismäßig kleine Aufgabe.
Das zweite ist, was implementiert werden muss: Die Ordnerstruktur. Es fehlt an Verzeichnissen. In diesem Fall, kann man sich ein Verzeichnis vorstellen, wie eine Datei. Diese enthält in 16 Byte Blöcken, jeweils den Namen und die Anfangsadresse einer Datei oder eines Verzeichnis. Es ist nicht gut, zunächst zur Ordnerstruktur über zu gehen. Klar ein Wurzelverzeichnis muss implementiert werden. Trotzdem braucht zumindest jedes weitere Verzeichnis ein READ und WRITE. Natürlich kann ein MKDIR auf READ und WRITE zugreifen, da es sich bei einem Verzeichnis um eine Datei handelt. Aber es ist bequemer READ und WRITE schon zu haben. Außerdem: Bisher fängt jede Datei ab einem bestimmte Startsektor an. Ich habe die Sache bereits so implementiert, dass eine neue Datei nicht ab einem ausgesuchten Startsektor beginnt, sondern ab dem ersten freien. Aber jede Datei enthält bisher einen Startsektor, doch dieser liegt so zu sagen in der Luft. Ein Verzeichnis ist nötig, um an den Dateinamen den Startsektor zu finden, damit die Dateien, Dateinamen und Startsektoren tatsächlich verzeichnet sind.

fs01.c


#include <stdio.h>

#define SECTOR_SIZE         512
#define N_SECTOR            2880
#define FAT_EOF             4096

char fs_buf[SECTOR_SIZE*N_SECTOR];
char fat[N_SECTOR*4];

void read_sector(int sector, char *buf) {
    int i;
    int j;
    int r;
    
    r = sector * SECTOR_SIZE;

    for(i = r, j  = 0;  j < SECTOR_SIZE;  j++, i++)
        buf[j] = fs_buf[i];    
return;
}

void write_sector(int sector, char *buf) {
    int i;
    int j;
    int r;
    
    r = sector * SECTOR_SIZE;
    
    for(i = r, j  = 0;  j < SECTOR_SIZE;  j++, i++)
        fs_buf[i] = buf[j];
return;
}


int getnextfreefatentry(void) {
    int i;
        
    for(i = 0;  i < N_SECTOR*4;   i += 4) {
        if((fat[i+0x00] == 0) && (fat[i+0x01] == 0) && (fat[i+0x02] == 0) && (fat[i+0x03] == 0)) {
            return i/4;
        }
    }
        
    
return -1;
}

void writefatentry(int sector1, int sector2, int length) {
    fat[sector1*4+0x00] = ((sector2 & 0xff00) >> 8) & 0x00ff;
    fat[sector1*4+0x01] = sector2 & 0x00ff;
    fat[sector1*4+0x02] = ((length & 0xff00) >> 8) & 0x00ff;
    fat[sector1*4+0x03] = length & 0x00ff; 
return;
}

void makefreefat(void) {
    int i;
    
    for(i = 0;  i < N_SECTOR*4;  i++)
        fat[i] = 0;
return;
}

int write(int n, char *buf) {
    char sec_buf[SECTOR_SIZE];
    int i;
    int j;
    int sector1, sector2, sectorret;
    int t;
    
    
    /*
    sectorret = sector1 = getnextfreefatentry();
    writefatentry(sector1, EOF, 512);
    for(i = 0;  i < 7;  i++) {
        sector2 = sector1;
        sector1 = getnextfreefatentry();
        writefatentry(sector1, EOF, 512);
        writefatentry(sector2, sector1, 512);        
    }
    */
    
    sectorret = sector1 = getnextfreefatentry();
    writefatentry(sector1, EOF, 512);
    for(j = 0;  (j < SECTOR_SIZE) && (j < n);  j++) 
        sec_buf[j] = buf[j];
    for(; j < SECTOR_SIZE;  j++)
        sec_buf[j] = 0x00;
    write_sector(sector1, sec_buf);
    for(i = SECTOR_SIZE;  i < n;  i += SECTOR_SIZE) {
        sector2 = sector1;
        sector1 = getnextfreefatentry();
        writefatentry(sector1, EOF, 512);
        writefatentry(sector2, sector1, 512);
        for(j = 0;  (j < SECTOR_SIZE) && ((j+i) < n);  j++) 
            sec_buf[j] = buf[j+i];
        for(; j < SECTOR_SIZE;  j++)
            sec_buf[j] = 0x00;     
        write_sector(sector1, sec_buf);
    }

return sectorret;
}

int fatgetnext(int i) {
    i = i*4;
    return(((8 << fat[i+0x00]) & 0xff00) | (fat[i+0x01] & 0x00ff));
}

void read(int start_sector, int n, char *buf) {
    char sec_buf[SECTOR_SIZE];
    int i;
    int j;
    int k;
    
    i = 0;
    j = 0;
    while(i < n) {
        read_sector(start_sector, sec_buf);
        for(k = 0;  k < SECTOR_SIZE;  j++, k++)
            buf[j] = sec_buf[k];
        start_sector = fatgetnext(start_sector);
        i = i+SECTOR_SIZE;
    }
    
return;
}

int main(void) {
    char buf[SECTOR_SIZE*4+512], buf2[SECTOR_SIZE*4];
    int i, t;
    
    for(i = 0;  i < SECTOR_SIZE*4;  i+=3) {
        buf[i+0x00] = 'A';
        buf[i+0x01] = 'B';
        buf[i+0x02] = 'C';
    }

    makefreefat();
    writefatentry(0, FAT_EOF, 512);
    writefatentry(1, FAT_EOF, 512);
    writefatentry(3, FAT_EOF, 512);
    writefatentry(5, FAT_EOF, 512);
    writefatentry(6, FAT_EOF, 512);
    writefatentry(8, FAT_EOF, 512);

    for(i = 0;  i < SECTOR_SIZE*4;  i+=3) {
        buf[i+0x00] = 'D';
        buf[i+0x01] = 'E';
        buf[i+0x02] = 'F';
    }
    

    write(2048, buf);

    for(i = 0;  i < 80; i+=4)
        printf("%i\t", fat[i+1]);
    
    for(i = 0;  i < SECTOR_SIZE*10;  i++) {
        if((i % 512) == 0)
            printf("\n\n%i\n\n", i / 512);
        printf("%i\t", fs_buf[i]);
    }
    
    printf("\n\n");
    read(2, 2048, buf2);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf2[i]);
    
return 0;
}



OPEN, CREATE, CLOSE, SOFT_READ, SOFT_WRITE,...

Also, jetzt ist die Sache so:
Unter den OPEN, CREATE, CLOSE, READ, WRITE, RM, MKDIR, RMDIR, CHDIR fehlt noch ein Systemaufruf: SEEK.
Das wird jetzt wichtig, wo es um die Dateiposition geht. Umgekehrt. Ich habe gesagt, OPEN, CREATE, CLOSE arbeiten automatisch mit einem Filehandler. Auch, weil jede Datei nicht nur über eine Nummer und einen Startsektor identifiziert wird, sondern auch über eine Position. Ich werde jetzt so tun, als gäbe es nur eine Datei und speichere global eine Position. Aber: Ich schreiben ein SEEK. Das wird aber kein SEEK in diesem Sinne, sondern ein SECTOR_SEEK. Dieses SECTOR_SEEK wechselst in den Sektor, in dem die Dateiposition an zu siedeln ist. Vor jedem Aufruf von READ werde ich SECTOR_SEEK anwenden und der Sektor wird ermittelt. Die Position innerhalb dieses Sektors wird von READ selbst ermittelt und dementsprechend verwendet.
Was das WRITE betrifft, wird die Sache ganz anders. Es gibt zunächst drei Arten von WRITE: Das überschreiben, das anhängend (append) und das darüber-schreiben. Ich werde einfach ein WRITE schreiben, was immer die Datei vollkommen neu überschreibt. Wenn eine Datei überschreibt und will allerdings ans Ende anhängen, kann der User auch so vorgehen, dass er die Datei erst einließt, im Puffer ans Ende etwas anhängt und die Datei vollkommen neu schreibt. Wenn das der Benutzer kann, kann ich das auch: Ich werde meine Version von WRITE, die anhängt einfach so schreiben, dass sie selber die Datei ließt, dann im Puffer anhängt und dann mit WRITE diese Datei neu schreibt. Das wird eine neue WRITE-Funktion, die sich aus bereits vorhandenen Funktionen wie READ und WRITE zusammensetzt. Umgekehrt: Wenn man schreibt, gibt es einfaches Hilfsmittel. Man legt einen Dateipuffer an. Man schreibt in diesen Dateipuffer und schreibt nicht alles gleich auf die Diskette/Festplatte/... . Und: Am Ende, wenn CLOSE aufgerufen wird, wird der Dateipuffer geschrieben. Das heißt es gibt ein weiteres WRITE, welches auf den Dateipuffer stattfindet und am Ende, wenn CLOSE aufgerufen wird, wird das echte WRITE aufgerufen. Und die Datei wird geschrieben. Wenn man EXIT, im Bereich von EXEC, Prozesse aufruft, werden alle Funktionen wie CLOSE, zum Schreiben der Dateipuffer automatisch aufgerufen.
Um sich nun die Arbeit zu ersparen, könnte man auch sagen, man ließt die Datei zum Lesen einfach in den Dateipuffer und damit ist die Sache relativ gegessen.

So, jetzt habe ich das so gemacht: Ich habe die Funktionen READ und WRITE gar nicht in RAW_WRITE und RAW_READ umgetauft, sondern ich habe eingefügt: Die Aufgabe des angehängt Schreiben, übernimmt SOFT_APPEND.
Jetzt werde ich doch gleich, ohne Umwege, ein System mit Filehandlern bauen. Dabei: Bei WRITE gilt, beim Aufruf von CLOSE wird die Datei tatsächlich geschrieben. Umgekehrt, wie ist es bei READ? Hier muss die Datei, beim Dateiöffnen, komplett in den Puffer gelesen werden. Also, der umgekehrte Schritt.

So, ich habe jetzt eine funktionierende Version.
Ich habe eine Struktur Filehandler. Diese enthält: Den Startsektor. Da ich bisher noch keine Einträge in ein Verzeichnis habe, arbeite ich mit dem Startsektor. Im Verzeichnis sind Dateinamen und dahinter der Startsektor einer Datei, warum nicht experimentativ mit dem Startsektor arbeiten. In der Struktur ist des weiteren die Position, so wie FLAGS. Diese FLAGS sind notwendig. Sie enthalten, ob eine Datei zum Lesen oder Schreiben geöffnet wurde. Wurde die Datei zum Schreiben geöffnet, muss sie bei CLOSE geschrieben werden. Ob die Datei am Anfang eingelesen wird, entscheidet der Systemaufruf OPEN oder CREATE. Das ist nicht dasselbe. Eine Datei, die mit OPEN aufgerufen wird, wird zum Lesen geöffnet, eine Datei, die mit CREATE geöffnet wird, wird zum Schreiben geöffnet.
Des weiteren habe ich eben OPEN und CREATE implementiert, des weiteren ein SOFT_WRITE und SOFT_READ.


#include <stdio.h>

#define SECTOR_SIZE         512
#define N_SECTOR            2880
#define FAT_EOF             4096
#define MAX_OPEN_FILE       12
#define MAX_OPEN_SECTORS    64
#define OPEN_READ           0x01
#define OPEN_WRITE          0x02

char fs_buf[SECTOR_SIZE*N_SECTOR];
char fat[N_SECTOR*4];
char file_buffer[MAX_OPEN_FILE][SECTOR_SIZE*MAX_OPEN_SECTORS];

int file_handler_i = 0;

typedef struct t_file_handler {
    int pos;
    int start_sector;
    int flags;
} t_file_handler;

t_file_handler file_handler[MAX_OPEN_FILE];

int open(int start_sector) {
    
    file_handler[file_handler_i].pos = 0;
    file_handler[file_handler_i].start_sector = start_sector;
    file_handler[file_handler_i].flags = OPEN_READ;
    
    read(start_sector, SECTOR_SIZE*MAX_OPEN_SECTORS, file_buffer[file_handler_i]);
    
    file_handler_i++;
    
return file_handler_i-1;
}

int create(void) {
    
    file_handler[file_handler_i].pos = 0;
    file_handler[file_handler_i].start_sector = getnextfreefatentry();
    file_handler[file_handler_i].flags = OPEN_WRITE;
    
    file_handler_i++;
return file_handler_i-1;
}

void close(int f_hndl) {
    int i;
    if(file_handler[f_hndl].flags == OPEN_WRITE) {
        write(file_handler[f_hndl].start_sector, file_handler[f_hndl].pos, file_buffer[f_hndl]);
    }
return;
}

int pos_byte;

void read_sector(int sector, char *buf) {
    int i;
    int j;
    int r;
    
    r = sector * SECTOR_SIZE;

    for(i = r, j  = 0;  j < SECTOR_SIZE;  j++, i++)
        buf[j] = fs_buf[i];    
return;
}

void write_sector(int sector, char *buf) {
    int i;
    int j;
    int r;
    
    r = sector * SECTOR_SIZE;
    
    for(i = r, j  = 0;  j < SECTOR_SIZE;  j++, i++)
        fs_buf[i] = buf[j];
return;
}


int getnextfreefatentry(void) {
    int i;
        
    for(i = 0;  i < N_SECTOR*4;   i += 4) {
        if((fat[i+0x00] == 0) && (fat[i+0x01] == 0) && (fat[i+0x02] == 0) && (fat[i+0x03] == 0)) {
            return i/4;
        }
    }
        
    
return -1;
}

void writefatentry(int sector1, int sector2, int length) {
    fat[sector1*4+0x00] = ((sector2 & 0xff00) >> 8) & 0x00ff;
    fat[sector1*4+0x01] = sector2 & 0x00ff;
    fat[sector1*4+0x02] = ((length & 0xff00) >> 8) & 0x00ff;
    fat[sector1*4+0x03] = length & 0x00ff; 
return;
}

void makefreefat(void) {
    int i;
    
    for(i = 0;  i < N_SECTOR*4;  i++)
        fat[i] = 0;
return;
}

int write(int sectorx1, int n, char *buf) {
    char sec_buf[SECTOR_SIZE];
    int i;
    int j;
    int sector1, sector2, sectorret;
    int t;

    /*
    sectorret = sector1 = getnextfreefatentry();
    writefatentry(sector1, EOF, 512);
    for(i = 0;  i < 7;  i++) {
        sector2 = sector1;
        sector1 = getnextfreefatentry();
        writefatentry(sector1, EOF, 512);
        writefatentry(sector2, sector1, 512);        
    }
    */
    sectorret = sector1 = sectorx1;
    writefatentry(sector1, EOF, 512);
    for(j = 0;  (j < SECTOR_SIZE) && (j < n);  j++) 
        sec_buf[j] = buf[j];
    for(; j < SECTOR_SIZE;  j++)
        sec_buf[j] = 0x00;
    write_sector(sector1, sec_buf);
    for(i = SECTOR_SIZE;  i < n;  i += SECTOR_SIZE) {
        sector2 = sector1;
        sector1 = getnextfreefatentry();
        writefatentry(sector1, EOF, 512);
        writefatentry(sector2, sector1, 512);
        for(j = 0;  (j < SECTOR_SIZE) && ((j+i) < n);  j++) 
            sec_buf[j] = buf[j+i];
        for(; j < SECTOR_SIZE;  j++)
            sec_buf[j] = 0x00;     
        write_sector(sector1, sec_buf);
    }
    
return sectorret;
}

void soft_write(int f_hndl, int n, char *buf) {
    int i;
    
    for(i = 0;  i < n;  i++) {
        file_buffer[f_hndl][i] = buf[i];
    }
    file_handler[f_hndl].pos = n;
return; 
}

void soft_append(int f_hndl, int n, char *buf) {
    int i = pos_byte;
    int j;
    
    for(j = 0;  j < n;  j++, i++)
        file_buffer[f_hndl][i] = buf[j];
    pos_byte = i;
return;
}

void soft_read(int f_hndl, int n, char *buf) {
    int i;
    int j;
    
    for(i = pos_byte, j = 0;  j < n;  j++, i++)
        buf[j] = file_buffer[f_hndl][i];
    pos_byte = i;
return;
}

int fatgetnext(int i) {
    i = i*4;
    return(((8 << fat[i+0x00]) & 0xff00) | (fat[i+0x01] & 0x00ff));
}

void read(int start_sector, int n, char *buf) {
    char sec_buf[SECTOR_SIZE];
    int i;
    int j;
    int k;
    
    i = 0;
    j = 0;
    while(i < n) {
        read_sector(start_sector, sec_buf);
        for(k = 0;  k < SECTOR_SIZE;  j++, k++)
            buf[j] = sec_buf[k];
        start_sector = fatgetnext(start_sector);
        i = i+SECTOR_SIZE;
    }
    
return;
}

int main(void) {
    char buf[SECTOR_SIZE*4+512], buf2[SECTOR_SIZE*4], buf3[SECTOR_SIZE*4];
    int i, t;
    int j;
    int sec1;
    
    for(i = 0;  i < SECTOR_SIZE*4;  i+=3) {
        buf[i+0x00] = 'A';
        buf[i+0x01] = 'B';
        buf[i+0x02] = 'C';
    }

    makefreefat();
    writefatentry(0, FAT_EOF, 512);
    writefatentry(1, FAT_EOF, 512);
    writefatentry(3, FAT_EOF, 512);
    writefatentry(5, FAT_EOF, 512);
    writefatentry(6, FAT_EOF, 512);
    writefatentry(8, FAT_EOF, 512);

    for(i = 0;  i < SECTOR_SIZE*4;  i+=3) {
        buf[i+0x00] = 'D';
        buf[i+0x01] = 'E';
        buf[i+0x02] = 'F';
    }
    

    //write(2048, buf);

    /*for(i = 0;  i < 80; i+=4)
        printf("%i\t", fat[i+1]);*/
    
    /*for(i = 0;  i < SECTOR_SIZE*10;  i++) {
        if((i % 512) == 0)
            printf("\n\n%i\n\n", i / 512);
        printf("%i\t", fs_buf[i]);
    }
    
    printf("\n\n");
    read(2, 2048, buf2);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf2[i]);*/
    
    printf("--->\n\n\n\n");
    
    j = create();
    sec1 = file_handler[j].start_sector;
    soft_write(j, 2048, buf);
    close(j);
    
    read(sec1, 2048, buf3);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf3[i]);
    
    printf("\n\n\n");
    
    j = open(sec1);
    soft_read(j, 2048, buf2);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf2[i]);
    
    
    
return 0;
}


fs02.c


Verzeichnisstruktur, Wurzelvereichnis

#include <stdio.h>

#define SECTOR_SIZE                 512
#define N_SECTOR                    2880
#define FAT_EOF                     4096
#define MAX_OPEN_FILE               12
#define MAX_OPEN_SECTORS            64
#define OPEN_READ                   0x01
#define OPEN_WRITE                  0x02
#define ROOT_DIR_FIRST_SECTOR       0x00

char fs_buf[SECTOR_SIZE*N_SECTOR];
char fat[N_SECTOR*4];
char file_buffer[MAX_OPEN_FILE][SECTOR_SIZE*MAX_OPEN_SECTORS];

int file_handler_i = 0;

typedef struct t_file_handler {
    int pos;
    int start_sector;
    int flags;
} t_file_handler;

t_file_handler file_handler[MAX_OPEN_FILE];

int open(char *name) {
    int i;
    int j;
    int start_sector;
    char name2[0x10];
    
    for(i = 0;  (name[i] != 0x00) && (i < 14);  i++)
        name2[i] = name[i];
    for(; i < 14;  i++)
        name2[i] = ' ';
    for(i = ROOT_DIR_FIRST_SECTOR;   i < (SECTOR_SIZE + ROOT_DIR_FIRST_SECTOR);   i += 16) {
        for(j = 0;  (j < 14) && (fs_buf[i+j] == name2[j]);  j++);
        if(j == 14)
            break;
    }
    if(!(i < (SECTOR_SIZE+ROOT_DIR_FIRST_SECTOR)))
        return -1;
    start_sector = (8 << fs_buf[i+0x0e]) & 0xff00;
    start_sector = start_sector | (fs_buf[i+0x0f] & 0x00ff); 
        
    file_handler[file_handler_i].pos = 0;
    file_handler[file_handler_i].start_sector = start_sector;
    file_handler[file_handler_i].flags = OPEN_READ;
    
    read(start_sector, SECTOR_SIZE*MAX_OPEN_SECTORS, file_buffer[file_handler_i]);
    
    file_handler_i++;
    
return file_handler_i-1;
}

int create(char *name) {
    int i;
    int j;
    int start_sector;
    char name2[0x10];
    
    for(i = 0;  (name[i] != 0x00) && (i < 14);  i++)
        name2[i] = name[i];
    for(; i < 14;  i++)
        name2[i] = ' ';
    for(i = ROOT_DIR_FIRST_SECTOR;   i < (SECTOR_SIZE + ROOT_DIR_FIRST_SECTOR);   i += 16) {
        if(fs_buf[i] == 0x00)
            break;
    }
    if(!(i < (SECTOR_SIZE+ROOT_DIR_FIRST_SECTOR)))
        return -1;
    
    start_sector = getnextfreefatentry();
    
    for(j = 0;  j < 14;  j++)
        fs_buf[i+j] = name2[j];
    fs_buf[i+0x0e] = (8 << start_sector) & 0xff00;
    fs_buf[i+0x0f] = start_sector & 0x00ff;         
    
    file_handler[file_handler_i].pos = 0;
    file_handler[file_handler_i].start_sector = start_sector;
    file_handler[file_handler_i].flags = OPEN_WRITE;
    
    file_handler_i++;
return file_handler_i-1;
}

void close(int f_hndl) {
    int i;
    if(file_handler[f_hndl].flags == OPEN_WRITE) {
        write(file_handler[f_hndl].start_sector, file_handler[f_hndl].pos, file_buffer[f_hndl]);
    }
return;
}

int pos_byte;

void read_sector(int sector, char *buf) {
    int i;
    int j;
    int r;
    
    r = sector * SECTOR_SIZE;

    for(i = r, j  = 0;  j < SECTOR_SIZE;  j++, i++)
        buf[j] = fs_buf[i];    
return;
}

void write_sector(int sector, char *buf) {
    int i;
    int j;
    int r;
    
    r = sector * SECTOR_SIZE;
    
    for(i = r, j  = 0;  j < SECTOR_SIZE;  j++, i++)
        fs_buf[i] = buf[j];
return;
}


int getnextfreefatentry(void) {
    int i;
        
    for(i = 0;  i < N_SECTOR*4;   i += 4) {
        if((fat[i+0x00] == 0) && (fat[i+0x01] == 0) && (fat[i+0x02] == 0) && (fat[i+0x03] == 0)) {
            return i/4;
        }
    }
        
    
return -1;
}

void writefatentry(int sector1, int sector2, int length) {
    fat[sector1*4+0x00] = ((sector2 & 0xff00) >> 8) & 0x00ff;
    fat[sector1*4+0x01] = sector2 & 0x00ff;
    fat[sector1*4+0x02] = ((length & 0xff00) >> 8) & 0x00ff;
    fat[sector1*4+0x03] = length & 0x00ff; 
return;
}

void makefreefat(void) {
    int i;
    
    for(i = 0;  i < N_SECTOR*4;  i++)
        fat[i] = 0;
return;
}

int write(int sectorx1, int n, char *buf) {
    char sec_buf[SECTOR_SIZE];
    int i;
    int j;
    int sector1, sector2, sectorret;
    int t;

    /*
    sectorret = sector1 = getnextfreefatentry();
    writefatentry(sector1, EOF, 512);
    for(i = 0;  i < 7;  i++) {
        sector2 = sector1;
        sector1 = getnextfreefatentry();
        writefatentry(sector1, EOF, 512);
        writefatentry(sector2, sector1, 512);        
    }
    */
    sectorret = sector1 = sectorx1;
    writefatentry(sector1, EOF, 512);
    for(j = 0;  (j < SECTOR_SIZE) && (j < n);  j++) 
        sec_buf[j] = buf[j];
    for(; j < SECTOR_SIZE;  j++)
        sec_buf[j] = 0x00;
    write_sector(sector1, sec_buf);
    for(i = SECTOR_SIZE;  i < n;  i += SECTOR_SIZE) {
        sector2 = sector1;
        sector1 = getnextfreefatentry();
        writefatentry(sector1, EOF, 512);
        writefatentry(sector2, sector1, 512);
        for(j = 0;  (j < SECTOR_SIZE) && ((j+i) < n);  j++) 
            sec_buf[j] = buf[j+i];
        for(; j < SECTOR_SIZE;  j++)
            sec_buf[j] = 0x00;     
        write_sector(sector1, sec_buf);
    }
return sectorret;
}

void soft_write(int f_hndl, int n, char *buf) {
    int i;
    
    for(i = 0;  i < n;  i++) {
        file_buffer[f_hndl][i] = buf[i];
    }
    file_handler[f_hndl].pos = n;
return; 
}

void soft_append(int f_hndl, int n, char *buf) {
    int i = pos_byte;
    int j;
    
    for(j = 0;  j < n;  j++, i++)
        file_buffer[f_hndl][i] = buf[j];
    pos_byte = i;
return;
}

void soft_read(int f_hndl, int n, char *buf) {
    int i;
    int j;
    
    for(i = pos_byte, j = 0;  j < n;  j++, i++)
        buf[j] = file_buffer[f_hndl][i];
    pos_byte = i;
return;
}

int fatgetnext(int i) {
    i = i*4;
    return(((8 << fat[i+0x00]) & 0xff00) | (fat[i+0x01] & 0x00ff));
}

void read(int start_sector, int n, char *buf) {
    char sec_buf[SECTOR_SIZE];
    int i;
    int j;
    int k;
    
    i = 0;
    j = 0;
    while(i < n) {
        read_sector(start_sector, sec_buf);
        for(k = 0;  k < SECTOR_SIZE;  j++, k++)
            buf[j] = sec_buf[k];
        start_sector = fatgetnext(start_sector);
        i = i+SECTOR_SIZE;
    }
    
return;
}

void make_root_dir() {
    char fs_buf[SECTOR_SIZE];
    int i;
    
    for(i = 0;  i < SECTOR_SIZE;   i++)
        fs_buf[i] = 0;
    fs_buf[0x00] = '.';
    fs_buf[0x0e] = ((ROOT_DIR_FIRST_SECTOR & 0xff00) >> 8) & 0x00ff;
    fs_buf[0x0f] = ROOT_DIR_FIRST_SECTOR & 0x00ff;
    fs_buf[0x10] = '.';
    fs_buf[0x11] = '.';
    fs_buf[0x1e] = ((ROOT_DIR_FIRST_SECTOR & 0xff00) >> 8) & 0x00ff;
    fs_buf[0x1f] = ROOT_DIR_FIRST_SECTOR & 0x00ff;
    writefatentry(ROOT_DIR_FIRST_SECTOR, EOF, 512);
}

void make_dir() {
    
    
}

int main(void) {
    char buf[SECTOR_SIZE*4+512], buf2[SECTOR_SIZE*4], buf3[SECTOR_SIZE*4];
    int i, t;
    int j;
    int sec1;
    /*
    for(i = 0;  i < SECTOR_SIZE*4;  i+=3) {
        buf[i+0x00] = 'A';
        buf[i+0x01] = 'B';
        buf[i+0x02] = 'C';
    }

    makefreefat();
    writefatentry(0, FAT_EOF, 512);
    writefatentry(1, FAT_EOF, 512);
    writefatentry(3, FAT_EOF, 512);
    writefatentry(5, FAT_EOF, 512);
    writefatentry(6, FAT_EOF, 512);
    writefatentry(8, FAT_EOF, 512);
*/
    for(i = 0;  i < SECTOR_SIZE*4;  i+=3) {
        buf[i+0x00] = 'D';
        buf[i+0x01] = 'E';
        buf[i+0x02] = 'F';
    }
    

    //write(2048, buf);

    /*for(i = 0;  i < 80; i+=4)
        printf("%i\t", fat[i+1]);*/
    
    /*for(i = 0;  i < SECTOR_SIZE*10;  i++) {
        if((i % 512) == 0)
            printf("\n\n%i\n\n", i / 512);
        printf("%i\t", fs_buf[i]);
    }
    
    printf("\n\n");
    read(2, 2048, buf2);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf2[i]);*/
    
    printf("--->\n\n\n\n");
    makefreefat();
    make_root_dir();
    
    j = create("test");
    sec1 = file_handler[j].start_sector;

    for(i = 0;  i < 512;  i++)
        printf("%i ", fs_buf[i]);
    

    soft_write(j, 2048, buf);
    close(j);
    
    for(i = 0;  i < 2048;  i++)
        printf("%i ", fs_buf[i]);

    
    read(sec1, 2048, buf3);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf3[i]);
    
    printf("\n\n\n");
    j = open("test");
    soft_read(j, 2048, buf2);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf2[i]);


    for(i = 0;  i < SECTOR_SIZE*4;  i+=3) {
        buf[i+0x00] = 'A';
        buf[i+0x01] = 'A';
        buf[i+0x02] = 'A';
    }
    

    j = create("hallo");
    sec1 = file_handler[j].start_sector;

    for(i = 0;  i < 512;  i++)
        printf("%i ", fs_buf[i]);
    

    soft_write(j, 2048, buf);
    close(j);
    
    printf("\n\n\n");

    
    read(sec1, 2048, buf3);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf3[i]);
    
    
    
return 0;
}




fs03.c


Oh je, ich habe da gestern ein paar Fehler gemacht. OPEN, CREATE, MKDIR arbeiten bisher direkt mit fs_buf. Das ist nicht gut. Zwar kann man das Wurzelverzeichnis immer im Arbeitsspeicher lassen, aber trotzdem. Doch der Fehler ist schnell behoben, man benutzt READ und WRITE und zwar, die RAW-Version und ließt in einen Puffer, der nur in der Funktion vorhanden ist.

fs04.c


#include <stdio.h>

#define SECTOR_SIZE                 512
#define N_SECTOR                    2880
#define FAT_EOF                     4096
#define MAX_OPEN_FILE               12
#define MAX_OPEN_SECTORS            64
#define OPEN_READ                   0x01
#define OPEN_WRITE                  0x02
#define ROOT_DIR_FIRST_SECTOR       0x00

char fs_buf[SECTOR_SIZE*N_SECTOR];
char fat[N_SECTOR*4];
char file_buffer[MAX_OPEN_FILE][SECTOR_SIZE*MAX_OPEN_SECTORS];

int file_handler_i = 0;

typedef struct t_file_handler {
    int pos;
    int start_sector;
    int flags;
} t_file_handler;

t_file_handler file_handler[MAX_OPEN_FILE];

int open(char *name) {
    int i;
    int j;
    int start_sector;
    char name2[0x10];
    char buf[SECTOR_SIZE];
    
    read(ROOT_DIR_FIRST_SECTOR, SECTOR_SIZE, buf);
    
    for(i = 0;  (name[i] != 0x00) && (i < 14);  i++)
        name2[i] = name[i];
    for(; i < 14;  i++)
        name2[i] = ' ';
    for(i = 0;   i < SECTOR_SIZE;   i += 16) {
        for(j = 0;  (j < 14) && (buf[i+j] == name2[j]);  j++);
        if(j == 14)
            break;
    }
    if(!(i < (SECTOR_SIZE+ROOT_DIR_FIRST_SECTOR)))
        return -1;
    start_sector = (8 << fs_buf[i+0x0e]) & 0xff00;
    start_sector = start_sector | (fs_buf[i+0x0f] & 0x00ff); 
        
    file_handler[file_handler_i].pos = 0;
    file_handler[file_handler_i].start_sector = start_sector;
    file_handler[file_handler_i].flags = OPEN_READ;
    
    read(start_sector, SECTOR_SIZE*MAX_OPEN_SECTORS, file_buffer[file_handler_i]);
    
    file_handler_i++;
    
return file_handler_i-1;
}

int create(char *name) {
    int i;
    int j;
    int start_sector;
    char name2[0x10];
    char buf[SECTOR_SIZE];
    
    read(ROOT_DIR_FIRST_SECTOR, SECTOR_SIZE, buf); 
    
    for(i = 0;  (name[i] != 0x00) && (i < 14);  i++)
        name2[i] = name[i];
    for(; i < 14;  i++)
        name2[i] = ' ';
    for(i = 0;   i < SECTOR_SIZE;   i += 16) {
        if(buf[i] == 0x00)
            break;
    }
    if(!(i < SECTOR_SIZE))
        return -1;
    
    start_sector = getnextfreefatentry();
    
    for(j = 0;  j < 14;  j++)
        buf[i+j] = name2[j];
    buf[i+0x0e] = (8 << start_sector) & 0xff00;
    buf[i+0x0f] = start_sector & 0x00ff;
    
    write(ROOT_DIR_FIRST_SECTOR, SECTOR_SIZE, buf);
    
    file_handler[file_handler_i].pos = 0;
    file_handler[file_handler_i].start_sector = start_sector;
    file_handler[file_handler_i].flags = OPEN_WRITE;
    
    file_handler_i++;
return file_handler_i-1;
}

void close(int f_hndl) {
    int i;
    if(file_handler[f_hndl].flags == OPEN_WRITE) {
        write(file_handler[f_hndl].start_sector, file_handler[f_hndl].pos, file_buffer[f_hndl]);
    }
return;
}

int pos_byte;

void read_sector(int sector, char *buf) {
    int i;
    int j;
    int r;
    
    r = sector * SECTOR_SIZE;

    for(i = r, j  = 0;  j < SECTOR_SIZE;  j++, i++)
        buf[j] = fs_buf[i];    
return;
}

void write_sector(int sector, char *buf) {
    int i;
    int j;
    int r;
    
    r = sector * SECTOR_SIZE;
    
    for(i = r, j  = 0;  j < SECTOR_SIZE;  j++, i++)
        fs_buf[i] = buf[j];
return;
}


int getnextfreefatentry(void) {
    int i;
        
    for(i = 0;  i < N_SECTOR*4;   i += 4) {
        if((fat[i+0x00] == 0) && (fat[i+0x01] == 0) && (fat[i+0x02] == 0) && (fat[i+0x03] == 0)) {
            return i/4;
        }
    }
        
    
return -1;
}

void writefatentry(int sector1, int sector2, int length) {
    fat[sector1*4+0x00] = ((sector2 & 0xff00) >> 8) & 0x00ff;
    fat[sector1*4+0x01] = sector2 & 0x00ff;
    fat[sector1*4+0x02] = ((length & 0xff00) >> 8) & 0x00ff;
    fat[sector1*4+0x03] = length & 0x00ff; 
return;
}

void makefreefat(void) {
    int i;
    
    for(i = 0;  i < N_SECTOR*4;  i++)
        fat[i] = 0;
return;
}

int write(int sectorx1, int n, char *buf) {
    char sec_buf[SECTOR_SIZE];
    int i;
    int j;
    int sector1, sector2, sectorret;
    int t;

    /*
    sectorret = sector1 = getnextfreefatentry();
    writefatentry(sector1, EOF, 512);
    for(i = 0;  i < 7;  i++) {
        sector2 = sector1;
        sector1 = getnextfreefatentry();
        writefatentry(sector1, EOF, 512);
        writefatentry(sector2, sector1, 512);        
    }
    */
    sectorret = sector1 = sectorx1;
    writefatentry(sector1, EOF, 512);
    for(j = 0;  (j < SECTOR_SIZE) && (j < n);  j++) 
        sec_buf[j] = buf[j];
    for(; j < SECTOR_SIZE;  j++)
        sec_buf[j] = 0x00;
    write_sector(sector1, sec_buf);
    for(i = SECTOR_SIZE;  i < n;  i += SECTOR_SIZE) {
        sector2 = sector1;
        sector1 = getnextfreefatentry();
        writefatentry(sector1, EOF, 512);
        writefatentry(sector2, sector1, 512);
        for(j = 0;  (j < SECTOR_SIZE) && ((j+i) < n);  j++) 
            sec_buf[j] = buf[j+i];
        for(; j < SECTOR_SIZE;  j++)
            sec_buf[j] = 0x00;     
        write_sector(sector1, sec_buf);
    }
return sectorret;
}

void soft_write(int f_hndl, int n, char *buf) {
    int i;
    
    for(i = 0;  i < n;  i++) {
        file_buffer[f_hndl][i] = buf[i];
    }
    file_handler[f_hndl].pos = n;
return; 
}

void soft_append(int f_hndl, int n, char *buf) {
    int i = pos_byte;
    int j;
    
    for(j = 0;  j < n;  j++, i++)
        file_buffer[f_hndl][i] = buf[j];
    pos_byte = i;
return;
}

void soft_read(int f_hndl, int n, char *buf) {
    int i;
    int j;
    
    for(i = pos_byte, j = 0;  j < n;  j++, i++)
        buf[j] = file_buffer[f_hndl][i];
    pos_byte = i;
return;
}

int fatgetnext(int i) {
    i = i*4;
    return(((8 << fat[i+0x00]) & 0xff00) | (fat[i+0x01] & 0x00ff));
}

void read(int start_sector, int n, char *buf) {
    char sec_buf[SECTOR_SIZE];
    int i;
    int j;
    int k;
    
    i = 0;
    j = 0;
    while(i < n) {
        read_sector(start_sector, sec_buf);
        for(k = 0;  k < SECTOR_SIZE;  j++, k++)
            buf[j] = sec_buf[k];
        start_sector = fatgetnext(start_sector);
        i = i+SECTOR_SIZE;
    }
    
return;
}

void make_root_dir() {
    char buf[SECTOR_SIZE];
    int i;
    
    for(i = 0;  i < SECTOR_SIZE;   i++)
        buf[i] = 0;
    buf[0x00] = '.';
    buf[0x0e] = ((ROOT_DIR_FIRST_SECTOR & 0xff00) >> 8) & 0x00ff;
    buf[0x0f] = ROOT_DIR_FIRST_SECTOR & 0x00ff;
    buf[0x10] = '.';
    buf[0x11] = '.';
    buf[0x1e] = ((ROOT_DIR_FIRST_SECTOR & 0xff00) >> 8) & 0x00ff;
    buf[0x1f] = ROOT_DIR_FIRST_SECTOR & 0x00ff;
    writefatentry(ROOT_DIR_FIRST_SECTOR, EOF, 512);
    write(ROOT_DIR_FIRST_SECTOR, SECTOR_SIZE, buf);
return;
}
/*
void make_dir(char *name, int sector_nr_dir_above) {
    char buf[SECTOR_SIZE];
    int i;
    int sector1;
    char name2[0x0f];
    
    sector1 = getnextfreefatentry();
    
    for(i = 0;  i < SECTOR_SIZE;   i++)
        buf[i] = 0;
    buf[0x00] = '.';
    buf[0x0e] = ((ROOT_DIR_FIRST_SECTOR & 0xff00) >> 8) & 0x00ff;
    buf[0x0f] = ROOT_DIR_FIRST_SECTOR & 0x00ff;
    buf[0x10] = '.';
    buf[0x11] = '.';
    buf[0x1e] = ((ROOT_DIR_FIRST_SECTOR & 0xff00) >> 8) & 0x00ff;
    buf[0x1f] = ROOT_DIR_FIRST_SECTOR & 0x00ff;
    
    write(sector1, SECTOR_SIZE, buf);
    read(sector_nr_dir_above, SECTOR_SIZE, buf);
    
    for(i = 0;  (name[i] != 0x00) && (i < 14);  i++)
        name2[i] = name[i];
    for(; i < 14;  i++)
        name2[i] = ' ';
    for(i;   i < SECTOR_SIZE;   i += 16) {
        //for(j = 0;  (j < 14) && (fs_buf[i+j] == name2[j]);  j++);
        if(fs_buf[i] == 0x00;
            break;
    }
    if(!(i < (SECTOR_SIZE+ROOT_DIR_FIRST_SECTOR)))
        return -1;
    
    
    
return;
}*/

int main(void) {
    char buf[SECTOR_SIZE*4+512], buf2[SECTOR_SIZE*4], buf3[SECTOR_SIZE*4];
    int i, t;
    int j;
    int sec1;
    /*
    for(i = 0;  i < SECTOR_SIZE*4;  i+=3) {
        buf[i+0x00] = 'A';
        buf[i+0x01] = 'B';
        buf[i+0x02] = 'C';
    }

    makefreefat();
    writefatentry(0, FAT_EOF, 512);
    writefatentry(1, FAT_EOF, 512);
    writefatentry(3, FAT_EOF, 512);
    writefatentry(5, FAT_EOF, 512);
    writefatentry(6, FAT_EOF, 512);
    writefatentry(8, FAT_EOF, 512);
*/
    for(i = 0;  i < SECTOR_SIZE*4;  i+=3) {
        buf[i+0x00] = 'D';
        buf[i+0x01] = 'E';
        buf[i+0x02] = 'F';
    }
    

    //write(2048, buf);

    /*for(i = 0;  i < 80; i+=4)
        printf("%i\t", fat[i+1]);*/
    
    /*for(i = 0;  i < SECTOR_SIZE*10;  i++) {
        if((i % 512) == 0)
            printf("\n\n%i\n\n", i / 512);
        printf("%i\t", fs_buf[i]);
    }
    
    printf("\n\n");
    read(2, 2048, buf2);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf2[i]);*/
    
    printf("--->\n\n\n\n");
    makefreefat();
    make_root_dir();
    
    j = create("test");
    sec1 = file_handler[j].start_sector;

    for(i = 0;  i < 512;  i++)
        printf("%i ", fs_buf[i]);
    

    soft_write(j, 2048, buf);
    close(j);
    
    for(i = 0;  i < 2048;  i++)
        printf("%i ", fs_buf[i]);

    
    read(sec1, 2048, buf3);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf3[i]);
    
    printf("\n\n\n");
    j = open("test");
    soft_read(j, 2048, buf2);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf2[i]);


    for(i = 0;  i < SECTOR_SIZE*4;  i+=3) {
        buf[i+0x00] = 'A';
        buf[i+0x01] = 'A';
        buf[i+0x02] = 'A';
    }
    

    j = create("hallo");
    sec1 = file_handler[j].start_sector;

    for(i = 0;  i < 512;  i++)
        printf("%i ", fs_buf[i]);
    

    soft_write(j, 2048, buf);
    close(j);
    
    printf("\n\n\n");

    
    read(sec1, 2048, buf3);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf3[i]);
    
    
    
return 0;
}



Ich habe jetzt eine funktionierende Version geschrieben, mitsamt Verzeichnis. Bei OPEN und CREATE fehlt noch die Angabe über ein Verzeichnis. Aber es funktioniert bestens.

fs05.c




#include <stdio.h>

#define SECTOR_SIZE                 512
#define N_SECTOR                    2880
#define FAT_EOF                     4096
#define MAX_OPEN_FILE               12
#define MAX_OPEN_SECTORS            64
#define OPEN_READ                   0x01
#define OPEN_WRITE                  0x02
#define ROOT_DIR_FIRST_SECTOR       0x00

char fs_buf[SECTOR_SIZE*N_SECTOR];
char fat[N_SECTOR*4];
char file_buffer[MAX_OPEN_FILE][SECTOR_SIZE*MAX_OPEN_SECTORS];

int file_handler_i = 0;

typedef struct t_file_handler {
    int pos;
    int start_sector;
    int flags;
} t_file_handler;

t_file_handler file_handler[MAX_OPEN_FILE];

int open(char *name) {
    int i;
    int j;
    int start_sector;
    char name2[0x10];
    char buf[SECTOR_SIZE];
    
    read(ROOT_DIR_FIRST_SECTOR, SECTOR_SIZE, buf);
    
    for(i = 0;  (name[i] != 0x00) && (i < 14);  i++)
        name2[i] = name[i];
    for(; i < 14;  i++)
        name2[i] = ' ';
    for(i = 0;   i < SECTOR_SIZE;   i += 16) {
        for(j = 0;  (j < 14) && (buf[i+j] == name2[j]);  j++);
        if(j == 14)
            break;
    }
    if(!(i < (SECTOR_SIZE+ROOT_DIR_FIRST_SECTOR)))
        return -1;
    start_sector = (8 << fs_buf[i+0x0e]) & 0xff00;
    start_sector = start_sector | (fs_buf[i+0x0f] & 0x00ff); 
        
    file_handler[file_handler_i].pos = 0;
    file_handler[file_handler_i].start_sector = start_sector;
    file_handler[file_handler_i].flags = OPEN_READ;
    
    read(start_sector, SECTOR_SIZE*MAX_OPEN_SECTORS, file_buffer[file_handler_i]);
    
    file_handler_i++;
    
return file_handler_i-1;
}

int create(char *name) {
    int i;
    int j;
    int start_sector;
    char name2[0x10];
    char buf[SECTOR_SIZE];
    
    read(ROOT_DIR_FIRST_SECTOR, SECTOR_SIZE, buf); 
    
    for(i = 0;  (name[i] != 0x00) && (i < 14);  i++)
        name2[i] = name[i];
    for(; i < 14;  i++)
        name2[i] = ' ';
    for(i = 0;   i < SECTOR_SIZE;   i += 16) {
        if(buf[i] == 0x00)
            break;
    }
    if(!(i < SECTOR_SIZE))
        return -1;
    
    start_sector = getnextfreefatentry();
    
    for(j = 0;  j < 14;  j++)
        buf[i+j] = name2[j];
    buf[i+0x0e] = (8 << start_sector) & 0xff00;
    buf[i+0x0f] = start_sector & 0x00ff;
    
    write(ROOT_DIR_FIRST_SECTOR, SECTOR_SIZE, buf);
    
    file_handler[file_handler_i].pos = 0;
    file_handler[file_handler_i].start_sector = start_sector;
    file_handler[file_handler_i].flags = OPEN_WRITE;
    
    file_handler_i++;
return file_handler_i-1;
}

void close(int f_hndl) {
    int i;
    if(file_handler[f_hndl].flags == OPEN_WRITE) {
        write(file_handler[f_hndl].start_sector, file_handler[f_hndl].pos, file_buffer[f_hndl]);
    }
return;
}

int pos_byte;

void read_sector(int sector, char *buf) {
    int i;
    int j;
    int r;
    
    r = sector * SECTOR_SIZE;

    for(i = r, j  = 0;  j < SECTOR_SIZE;  j++, i++)
        buf[j] = fs_buf[i];    
return;
}

void write_sector(int sector, char *buf) {
    int i;
    int j;
    int r;
    
    r = sector * SECTOR_SIZE;
    
    for(i = r, j  = 0;  j < SECTOR_SIZE;  j++, i++)
        fs_buf[i] = buf[j];
return;
}


int getnextfreefatentry(void) {
    int i;
        
    for(i = 0;  i < N_SECTOR*4;   i += 4) {
        if((fat[i+0x00] == 0) && (fat[i+0x01] == 0) && (fat[i+0x02] == 0) && (fat[i+0x03] == 0)) {
            return i/4;
        }
    }
        
    
return -1;
}

void writefatentry(int sector1, int sector2, int length) {
    fat[sector1*4+0x00] = ((sector2 & 0xff00) >> 8) & 0x00ff;
    fat[sector1*4+0x01] = sector2 & 0x00ff;
    fat[sector1*4+0x02] = ((length & 0xff00) >> 8) & 0x00ff;
    fat[sector1*4+0x03] = length & 0x00ff; 
return;
}

void makefreefat(void) {
    int i;
    
    for(i = 0;  i < N_SECTOR*4;  i++)
        fat[i] = 0;
return;
}

int write(int sectorx1, int n, char *buf) {
    char sec_buf[SECTOR_SIZE];
    int i;
    int j;
    int sector1, sector2, sectorret;
    int t;

    /*
    sectorret = sector1 = getnextfreefatentry();
    writefatentry(sector1, EOF, 512);
    for(i = 0;  i < 7;  i++) {
        sector2 = sector1;
        sector1 = getnextfreefatentry();
        writefatentry(sector1, EOF, 512);
        writefatentry(sector2, sector1, 512);        
    }
    */
    sectorret = sector1 = sectorx1;
    writefatentry(sector1, EOF, 512);
    for(j = 0;  (j < SECTOR_SIZE) && (j < n);  j++) 
        sec_buf[j] = buf[j];
    for(; j < SECTOR_SIZE;  j++)
        sec_buf[j] = 0x00;
    write_sector(sector1, sec_buf);
    for(i = SECTOR_SIZE;  i < n;  i += SECTOR_SIZE) {
        sector2 = sector1;
        sector1 = getnextfreefatentry();
        writefatentry(sector1, EOF, 512);
        writefatentry(sector2, sector1, 512);
        for(j = 0;  (j < SECTOR_SIZE) && ((j+i) < n);  j++) 
            sec_buf[j] = buf[j+i];
        for(; j < SECTOR_SIZE;  j++)
            sec_buf[j] = 0x00;     
        write_sector(sector1, sec_buf);
    }
return sectorret;
}

void soft_write(int f_hndl, int n, char *buf) {
    int i;
    
    for(i = 0;  i < n;  i++) {
        file_buffer[f_hndl][i] = buf[i];
    }
    file_handler[f_hndl].pos = n;
return; 
}

void soft_append(int f_hndl, int n, char *buf) {
    int i = pos_byte;
    int j;
    
    for(j = 0;  j < n;  j++, i++)
        file_buffer[f_hndl][i] = buf[j];
    pos_byte = i;
return;
}

void soft_read(int f_hndl, int n, char *buf) {
    int i;
    int j;
    
    for(i = pos_byte, j = 0;  j < n;  j++, i++)
        buf[j] = file_buffer[f_hndl][i];
    pos_byte = i;
return;
}

int fatgetnext(int i) {
    i = i*4;
    return(((8 << fat[i+0x00]) & 0xff00) | (fat[i+0x01] & 0x00ff));
}

void read(int start_sector, int n, char *buf) {
    char sec_buf[SECTOR_SIZE];
    int i;
    int j;
    int k;
    
    i = 0;
    j = 0;
    while(i < n) {
        read_sector(start_sector, sec_buf);
        for(k = 0;  k < SECTOR_SIZE;  j++, k++)
            buf[j] = sec_buf[k];
        start_sector = fatgetnext(start_sector);
        i = i+SECTOR_SIZE;
    }
    
return;
}

void make_root_dir() {
    char buf[SECTOR_SIZE];
    int i;
    
    for(i = 0;  i < SECTOR_SIZE;   i++)
        buf[i] = 0;
    buf[0x00] = '.';
    buf[0x0e] = ((ROOT_DIR_FIRST_SECTOR & 0xff00) >> 8) & 0x00ff;
    buf[0x0f] = ROOT_DIR_FIRST_SECTOR & 0x00ff;
    buf[0x10] = '.';
    buf[0x11] = '.';
    buf[0x1e] = ((ROOT_DIR_FIRST_SECTOR & 0xff00) >> 8) & 0x00ff;
    buf[0x1f] = ROOT_DIR_FIRST_SECTOR & 0x00ff;
    writefatentry(ROOT_DIR_FIRST_SECTOR, EOF, 512);
    write(ROOT_DIR_FIRST_SECTOR, SECTOR_SIZE, buf);
return;
}

int make_dir(char *name, int sector_nr_dir_above) {
    char buf[SECTOR_SIZE];
    int i;
    int j;
    int sector1;
    char name2[0x0f];
    
    sector1 = getnextfreefatentry();
    
    for(i = 0;  i < SECTOR_SIZE;   i++)
        buf[i] = 0;
    buf[0x00] = '.';
    buf[0x0e] = ((sector1 & 0xff00) >> 8) & 0x00ff;
    buf[0x0f] = sector1 & 0x00ff;
    buf[0x10] = '.';
    buf[0x11] = '.';
    buf[0x1e] = ((sector_nr_dir_above & 0xff00) >> 8) & 0x00ff;
    buf[0x1f] = sector_nr_dir_above & 0x00ff;
    
    write(sector1, SECTOR_SIZE, buf);
    read(sector_nr_dir_above, SECTOR_SIZE, buf);
    
    for(i = 0;  (name[i] != 0x00) && (i < 14);  i++)
        name2[i] = name[i];
    for(; i < 14;  i++)
        name2[i] = ' ';
    for(i = 0;   i < SECTOR_SIZE;   i += 16) {
        if(buf[i] == 0x00)
            break;
    }
    if(!(i < SECTOR_SIZE))
        return -1;
    for(j = 0; j < 14;  j++)
        buf[i+j] = name2[j];
    buf[i+0x0e] = ((sector1 & 0xff00) >> 8) & 0x00ff;
    buf[i+0x0f] = sector1 & 0x00ff;
    
    write(sector_nr_dir_above, SECTOR_SIZE, buf);
    
return sector1;
}

int chsubdir(char *name, int sector_nr_dir_above) {
    char buf[SECTOR_SIZE];
    int i;
    int j;
    int sector1;
    char name2[0x0f];

    printf("%s\n\n", name);
    
    read(sector_nr_dir_above, SECTOR_SIZE, buf);
    
    for(i = 0;  (name[i] != 0x00) && (i < 14);  i++)
        name2[i] = name[i];
    for(; i < 14;  i++)
        name2[i] = ' ';
    for(i = 0;   i < SECTOR_SIZE;   i += 16) {
        for(j = 0;  (j < 14) && (buf[i+j] == name2[j]);  j++);
        if(j == 14)
            break;
    }
    if(i < SECTOR_SIZE)
        return (((8 << (buf[i+0x0e] & 0x00ff)) & 0xff00) | (buf[i+0x0f] & 0xff));
    
return -1;
}

int chdir(char *name, int sector_nr_dir_above) {
    char name2[256], name3[0x10];
    int i, j;
    
    for(i = 0;  i < 256;  i++)
        name2[i] = name[i];
    name2[i] = 0;
    
    i = 0;
    
    if(name2[0] == '/') {
        sector_nr_dir_above = ROOT_DIR_FIRST_SECTOR;
        i++;
    }
    
    for(j = 0;  (i < 256) && (name2[i] != 0);  i++, j++) {
        if(name2[i] != '/')
            name3[j] = name2[i];
        else if(name2[i] == '/') {
            name3[j] = 0x00;
            printf("%s\n", name3);
            if((sector_nr_dir_above = chsubdir(name3, sector_nr_dir_above)) == -1)
                break;
            j = -1;
        }
        
    }
    
return sector_nr_dir_above;
}

int main(void) {
    char buf[SECTOR_SIZE*4+512], buf2[SECTOR_SIZE*4], buf3[SECTOR_SIZE*4];
    int i, t;
    int j;
    int sec1, sec2, sec3, sec4;
    
    /*
    for(i = 0;  i < SECTOR_SIZE*4;  i+=3) {
        buf[i+0x00] = 'A';
        buf[i+0x01] = 'B';
        buf[i+0x02] = 'C';
    }

    makefreefat();
    writefatentry(0, FAT_EOF, 512);
    writefatentry(1, FAT_EOF, 512);
    writefatentry(3, FAT_EOF, 512);
    writefatentry(5, FAT_EOF, 512);
    writefatentry(6, FAT_EOF, 512);
    writefatentry(8, FAT_EOF, 512);
*/
    for(i = 0;  i < SECTOR_SIZE*4;  i+=3) {
        buf[i+0x00] = 'D';
        buf[i+0x01] = 'E';
        buf[i+0x02] = 'F';
    }
    

    //write(2048, buf);

    /*for(i = 0;  i < 80; i+=4)
        printf("%i\t", fat[i+1]);*/
    
    /*for(i = 0;  i < SECTOR_SIZE*10;  i++) {
        if((i % 512) == 0)
            printf("\n\n%i\n\n", i / 512);
        printf("%i\t", fs_buf[i]);
    }
    
    printf("\n\n");
    read(2, 2048, buf2);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf2[i]);*/
    
    printf("--->\n\n\n\n");
    makefreefat();
    make_root_dir();
    
    j = create("test");
    sec1 = file_handler[j].start_sector;

    for(i = 0;  i < 512;  i++)
        printf("%i ", fs_buf[i]);
    

    soft_write(j, 2048, buf);
    close(j);
    
    for(i = 0;  i < 2048;  i++)
        printf("%i ", fs_buf[i]);

    
    read(sec1, 2048, buf3);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf3[i]);
    
    printf("\n\n\n");
    j = open("test");
    soft_read(j, 2048, buf2);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf2[i]);


    for(i = 0;  i < SECTOR_SIZE*4;  i+=3) {
        buf[i+0x00] = 'A';
        buf[i+0x01] = 'A';
        buf[i+0x02] = 'A';
    }
    

    j = create("hallo");
    sec1 = file_handler[j].start_sector;

    for(i = 0;  i < 512;  i++)
        printf("%i ", fs_buf[i]);
    

    soft_write(j, 2048, buf);
    close(j);
    
    printf("\n\n\n");

    
    read(sec1, 2048, buf3);
    for(i = 0;  i < 2048;  i++)
        printf("%i ", buf3[i]);
    
    sec1 = make_dir("ABER", ROOT_DIR_FIRST_SECTOR);
    sec2 = make_dir("JETZT", ROOT_DIR_FIRST_SECTOR);
    
    sec3 = make_dir("XXX", sec1);

    read(ROOT_DIR_FIRST_SECTOR, 512, buf3); 
    for(i = 0;  i < 512;  i++)
        printf("%i %c, ", buf3[i], buf3[i]);
    printf("\n\n");
    read(sec1, 512, buf3); 
    for(i = 0;  i < 512;  i++)
        printf("%i %c, ", buf3[i], buf3[i]);
    read(sec2, 512, buf3); 
    for(i = 0;  i < 512;  i++)
        printf("%i %c, ", buf3[i], buf3[i]);
    printf("---> %i %i\n", sec3, chdir("/ABER/XXX/", ROOT_DIR_FIRST_SECTOR));
    
    sec4 = make_dir("HAHAHA", sec3);
    printf("---> %i %i\n", sec4, chdir("/ABER/XXX/HAHAHA/", ROOT_DIR_FIRST_SECTOR));
    
return 0;
}



Interrupt-Handler/Interrupt-Routine 0x21

So, als nächstes kommt der Interrupt 0x21.
Es funktioniert auch schon, wie wir im folgenden Code gleich sehen werden.
Von früher weiß ich es, und einfach bitte Code testen:
Die Interrupt-Vektor-Tabelle steht an der Stelle:

0x0000:0x0000

Das heißt, das Segmentregister = 0x0000 und der Offset ist 0x0000. Die Interrupts werden so abgespeichert, dass in den ersten zwei Byte der Offset, der Interruptroutine/Interrupthandler steht, in den darauffolgenden Bytes, das Segment.
Jeder Eintrag in der Interrupt-Vektor-Tabelle ist vier Byte groß: Eben 16 Bit für den Offset des Interrupthandlers und 16 Bit für die Segmentadresse des Interrupthandlers.
Wenn man nun das Interrupt 0x21 implementieren will, muss man an die Adresse 0x84 schreiben, denn jeder Eintrag ist ja vier Byte groß und das 21h Interrupt und 21h*4 = 84h.
Jetzt zunächst zur Interruptroutine, diese sieht in etwa wie folgt aus:

_print_string2:
 call _print_string
 iret


Diese hat ein Label _print_string2. Dieses bildet den Offset innerhalb des Codesegments. Natürlich ist auch noch das Codesegment vorhanden. Dies ist das Codesegment des Kernels. Jetzt ist die Frage, wie an die Adresse in der Interrupt-Vektor-Tabelle laden?
Ich weiß, wie man unter NASM zum Beispiel mit Segment und Offset eine Angabe macht, etwa
mov ebx [ds:di]
mov ebx [0x2000:00ff]


Aber unter as86 weiß ich es nicht, aber keine Panik, es funktioniert! Und es funktioniert so: Wir laden in das DS, die Segmentadresse, 0x0000, hier steht die Interrupt-Vektor-Tabelle. Und dann laden wir in DI, den Offset von _print_string2. Das sieht, dann so aus:


  mov ax, #0x400 
  mov ds, ax
  mov es, ax
  mov si, #welcome
  
  mov ax, #0x0000
  mov ds, ax
  mov di, #0x0084
  mov [di], #_print_string2
  inc di
  inc di
  mov [di], #0x0400
  mov ax, #0x400
  mov ds, ax
  
  mov si, #welcome
  int #0x21
  mov si, #welcome
  int #0x21
  mov si, #welcome
  int #0x21

  ;mov si, #welcome
  ;call _print_string
_again1: jmp _again1
  call _main
  



_print_string2:
 call _print_string
 iret

_get_string:
  xor cl, cl
_get_string_loop:
  xor ah, ah   
  int #0x16    
  cmp al, #13    
  je _get_string_done
  cmp cl, #31    
  je _get_string_loop
  mov ah, #0x0E
  int #0x10     
  stosb  
  inc cl
  jmp _get_string_loop
_get_string_done:
  mov al, #0
  stosb
  mov ax, #0x0E0D
  int #0x10
  mov al, #0x0A
  int #0x10
  ret


_print_string:
    lodsb             
    or al, al         
    jz _print_string_done          
    mov ah, #0x0E
    int #0x10          
    jmp _print_string
_print_string_done:
    ret

welcome: .asciz "Welcome to http://www.talkortell.de!!!"
.byte 0
...




mainxxx02.asm


So, sieht der vollständige Code aus:

  mov ax, #0x400 
  mov ds, ax
  mov es, ax
  mov si, #welcome
  
  mov ax, #0x0000
  mov ds, ax
  mov di, #0x0084
  mov [di], #_print_string2
  inc di
  inc di
  mov [di], #0x0400
  mov ax, #0x400
  mov ds, ax
  
  mov si, #welcome
  int #0x21
  mov si, #welcome
  int #0x21
  mov si, #welcome
  int #0x21

  ;mov si, #welcome
  ;call _print_string
_again1: jmp _again1
  call _main
  



_print_string2:
 call _print_string
 iret

_get_string:
  xor cl, cl
_get_string_loop:
  xor ah, ah   
  int #0x16    
  cmp al, #13    
  je _get_string_done
  cmp cl, #31    
  je _get_string_loop
  mov ah, #0x0E
  int #0x10     
  stosb  
  inc cl
  jmp _get_string_loop
_get_string_done:
  mov al, #0
  stosb
  mov ax, #0x0E0D
  int #0x10
  mov al, #0x0A
  int #0x10
  ret


_print_string:
    lodsb             
    or al, al         
    jz _print_string_done          
    mov ah, #0x0E
    int #0x10          
    jmp _print_string
_print_string_done:
    ret

welcome: .asciz "Welcome to http://www.talkortell.de!!!"
.byte 0


! 1 
! 1 # 1 "main.c"
! 1 int c_strcmp(str1,str2)
! 2 # 1 "main.c"
! 1 char *str1;
export _c_strcmp
_c_strcmp:
!BCC_EOS
! 2 # 1 "main.c"
! 1 char *str2;
!BCC_EOS
! 2 # 1 "main.c"
! 1 {
! 2     char *ptr1;
!BCC_EOS
! 3     char *ptr2;
!BCC_EOS
! 4     
! 5     for(ptr1 = str1,ptr2 = str2;   (((*ptr1)!= 0)&& ((*ptr2)!= 0));  ptr1++,ptr2++) {
push bp
mov bp,sp
push di
push si
add sp,*-4
! Debug: eq * char str1 = [S+$A+2] to * char ptr1 = [S+$A-8] (used reg = )
mov bx,4[bp]
mov -6[bp],bx
! Debug: eq * char str2 = [S+$A+4] to * char ptr2 = [S+$A-$A] (used reg = )
mov bx,6[bp]
mov -8[bp],bx
!BCC_EOS
!BCC_EOS
jmp .3
.4:
! 6         if(*ptr1 != *ptr2)
mov bx,-8[bp]
mov si,-6[bp]
! Debug: ne char = [bx+0] to char = [si+0] (used reg = )
mov al,[si]
cmp al,[bx]
je   .5
.6:
! 7             return 1;
mov ax,*1
add sp,*4
pop si
pop di
pop bp
ret
!BCC_EOS
! 8     }
.5:
! 9 return 0;
.2:
! Debug: postinc * char ptr1 = [S+$A-8] (used reg = )
mov bx,-6[bp]
inc bx
mov -6[bp],bx
! Debug: postinc * char ptr2 = [S+$A-$A] (used reg = )
mov bx,-8[bp]
inc bx
mov -8[bp],bx
.3:
mov bx,-6[bp]
! Debug: ne int = const 0 to char = [bx+0] (used reg = )
mov al,[bx]
test al,al
je   .7
.8:
mov bx,-8[bp]
! Debug: ne int = const 0 to char = [bx+0] (used reg = )
mov al,[bx]
test al,al
jne .4
.7:
.1:
xor ax,ax
add sp,*4
pop si
pop di
pop bp
ret
!BCC_EOS
! 10 }
! 11 
! 12 void c_print_str(str)
! Register BX SI used in function c_strcmp
! 13 # 12 "main.c"
! 12 char *str;
export _c_print_str
_c_print_str:
!BCC_EOS
! 13 # 12 "main.c"
! 12 {
! 13     char *ptr;
!BCC_EOS
! 14     
! 15     ptr = str;
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq * char str = [S+8+2] to * char ptr = [S+8-8] (used reg = )
mov bx,4[bp]
mov -6[bp],bx

mov si, bx
call _print_string


!BCC_EOS
! 16 return;    
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 17 }
! 18 
! 19 void c_get_str(str)
! Register BX used in function c_print_str
! 20 # 19 "main.c"
! 19 char *str;
export _c_get_str
_c_get_str:
!BCC_EOS
! 20 # 19 "main.c"
! 19 {
! 20     char *ptr;
!BCC_EOS
! 21     
! 22     ptr = str;
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq * char str = [S+8+2] to * char ptr = [S+8-8] (used reg = )
mov bx,4[bp]
mov -6[bp],bx

mov di, bx
call _get_string


!BCC_EOS
! 23 return;
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 24 }
! 25 
! 26 void read_sector(sector,buf)
! Register BX used in function c_get_str
! 27 # 26 "main.c"
! 26 int sector;
export _read_sector
_read_sector:
!BCC_EOS
! 27 # 26 "main.c"
! 26 int *buf;
!BCC_EOS
! 27 # 26 "main.c"
! 26 {
! 27     int head;
!BCC_EOS
! 28     int cylinder;
!BCC_EOS
! 29     int sector_per_track;
!BCC_EOS
! 30 
! 31     cylinder = (sector / (18*2));
push bp
mov bp,sp
push di
push si
add sp,*-6
! Debug: div int = const $24 to int sector = [S+$C+2] (used reg = )
mov ax,4[bp]
mov bx,*$24
cwd
idiv bx
! Debug: eq int = ax+0 to int cylinder = [S+$C-$A] (used reg = )
mov -8[bp],ax
!BCC_EOS
! 32     head = mod(sector,(18*2))/18;
! Debug: list int = const $24 (used reg = )
mov ax,*$24
push ax
! Debug: list int sector = [S+$E+2] (used reg = )
push 4[bp]
! Debug: func () int = mod+0 (used reg = )
call _mod
add sp,*4
! Debug: div int = const $12 to int = ax+0 (used reg = )
mov bx,*$12
cwd
idiv bx
! Debug: eq int = ax+0 to int head = [S+$C-8] (used reg = )
mov -6[bp],ax
!BCC_EOS
! 33     sector_per_track = mod(mod(sector,(18*2)),18);
! Debug: list int = const $12 (used reg = )
mov ax,*$12
push ax
! Debug: list int = const $24 (used reg = )
mov ax,*$24
push ax
! Debug: list int sector = [S+$10+2] (used reg = )
push 4[bp]
! Debug: func () int = mod+0 (used reg = )
call _mod
add sp,*4
! Debug: list int = ax+0 (used reg = )
push ax
! Debug: func () int = mod+0 (used reg = )
call _mod
add sp,*4
! Debug: eq int = ax+0 to int sector_per_track = [S+$C-$C] (used reg = )
mov -$A[bp],ax
!BCC_EOS
! 34     
! 35     cylinder = 0;
! Debug: eq int = const 0 to int cylinder = [S+$C-$A] (used reg = )
!xor ax,ax
!mov -8[bp],ax
!BCC_EOS
! 36     head = 0;
! Debug: eq int = const 0 to int head = [S+$C-8] (used reg = )
!xor ax,ax
!mov -6[bp],ax
!BCC_EOS
! 37     sector_per_track = 0;
! Debug: eq int = const 0 to int sector_per_track = [S+$C-$C] (used reg = )
!xor ax,ax
!mov -$A[bp],ax
!BCC_EOS
! 38     
! 39     buf = 0;
! Debug: eq int = const 0 to * int buf = [S+$C+4] (used reg = )
!xor ax,ax
!mov 6[bp],ax

mov al, #1
mov bx, 6[bp]
mov dl, #0
mov ch, -8[bp] !Zylinder
mov cl, -$A[bp] !Sector
mov dh, -6[bp] !Head
mov ah, #2
int #0x13

!BCC_EOS
! 40     
! 41 return;   
add sp,*6
pop si
pop di
pop bp
ret
!BCC_EOS
! 42 }
! 43 
! 44 write_sector(sector,buf)
! Register BX used in function read_sector
! 45 # 44 "main.c"
! 44 int sector;
export _write_sector
_write_sector:
!BCC_EOS
! 45 # 44 "main.c"
! 44 int *buf;
!BCC_EOS
! 45 # 44 "main.c"
! 44 {
! 45     int head;
!BCC_EOS
! 46     int cylinder;
!BCC_EOS
! 47     int sector_per_track;
!BCC_EOS
! 48 
! 49     cylinder = (sector / (18*2));
push bp
mov bp,sp
push di
push si
add sp,*-6
! Debug: div int = const $24 to int sector = [S+$C+2] (used reg = )
mov ax,4[bp]
mov bx,*$24
cwd
idiv bx
! Debug: eq int = ax+0 to int cylinder = [S+$C-$A] (used reg = )
mov -8[bp],ax
!BCC_EOS
! 50     head = mod(sector,(18*2))/18;
! Debug: list int = const $24 (used reg = )
mov ax,*$24
push ax
! Debug: list int sector = [S+$E+2] (used reg = )
push 4[bp]
! Debug: func () int = mod+0 (used reg = )
call _mod
add sp,*4
! Debug: div int = const $12 to int = ax+0 (used reg = )
mov bx,*$12
cwd
idiv bx
! Debug: eq int = ax+0 to int head = [S+$C-8] (used reg = )
mov -6[bp],ax
!BCC_EOS
! 51     sector_per_track = mod(mod(sector,(18*2)),18);
! Debug: list int = const $12 (used reg = )
mov ax,*$12
push ax
! Debug: list int = const $24 (used reg = )
mov ax,*$24
push ax
! Debug: list int sector = [S+$10+2] (used reg = )
push 4[bp]
! Debug: func () int = mod+0 (used reg = )
call _mod
add sp,*4
! Debug: list int = ax+0 (used reg = )
push ax
! Debug: func () int = mod+0 (used reg = )
call _mod
add sp,*4
! Debug: eq int = ax+0 to int sector_per_track = [S+$C-$C] (used reg = )
mov -$A[bp],ax
!BCC_EOS
! 52     
! 53     cylinder = 0;
! Debug: eq int = const 0 to int cylinder = [S+$C-$A] (used reg = )
!xor ax,ax
!mov -8[bp],ax
!BCC_EOS
! 54     head = 0;
! Debug: eq int = const 0 to int head = [S+$C-8] (used reg = )
!xor ax,ax
!mov -6[bp],ax
!BCC_EOS
! 55     sector_per_track = 0;
! Debug: eq int = const 0 to int sector_per_track = [S+$C-$C] (used reg = )
!xor ax,ax
!mov -$A[bp],ax
!BCC_EOS
! 56         
! 57     buf = 0;
! Debug: eq int = const 0 to * int buf = [S+$C+4] (used reg = )
!xor ax,ax
!mov 6[bp],ax

mov al, #1
mov bx, 6[bp]
mov dl, #0
mov ch, -8[bp] !Zylinder
mov cl, -$A[bp] !Sector
mov dh, -6[bp] !Head
mov ah, #0x03
int #0x13

!BCC_EOS
! 58 
! 59 return;    
add sp,*6
pop si
pop di
pop bp
ret
!BCC_EOS
! 60 }
! 61 
! 62 int mod(y,x)
! Register BX used in function write_sector
! 63 # 62 "main.c"
! 62 int y;
export _mod
_mod:
!BCC_EOS
! 63 # 62 "main.c"
! 62 int x;
!BCC_EOS
! 63 # 62 "main.c"
! 62 {
! 63     int x2;
!BCC_EOS
! 64     
! 65     for(x2 = x;  x2 < y;  x2+=x);
push bp
mov bp,sp
push di
push si
dec sp
dec sp
! Debug: eq int x = [S+8+4] to int x2 = [S+8-8] (used reg = )
mov ax,6[bp]
mov -6[bp],ax
!BCC_EOS
!BCC_EOS
jmp .B
.C:
!BCC_EOS
! 66     if(x2 > y)
.A:
! Debug: addab int x = [S+8+4] to int x2 = [S+8-8] (used reg = )
mov ax,-6[bp]
add ax,6[bp]
mov -6[bp],ax
.B:
! Debug: lt int y = [S+8+2] to int x2 = [S+8-8] (used reg = )
mov ax,-6[bp]
cmp ax,4[bp]
jl  .C
.D:
.9:
! Debug: gt int y = [S+8+2] to int x2 = [S+8-8] (used reg = )
mov ax,-6[bp]
cmp ax,4[bp]
jle  .E
.F:
! 67         x2-=x;
! Debug: subab int x = [S+8+4] to int x2 = [S+8-8] (used reg = )
mov ax,-6[bp]
sub ax,6[bp]
mov -6[bp],ax
!BCC_EOS
! 68 return y-x2;
.E:
! Debug: sub int x2 = [S+8-8] to int y = [S+8+2] (used reg = )
mov ax,4[bp]
sub ax,-6[bp]
! Debug: cast int = const 0 to int = ax+0 (used reg = )
inc sp
inc sp
pop si
pop di
pop bp
ret
!BCC_EOS
! 69 }
! 70 # 74
! 74 void main()
! 75 # 74 "main.c"
! 74 {
export _main
_main:
! 75     char buf[256];
!BCC_EOS
! 76     char *welcome_str = "Welcome to http://www.talkortell.de - OS\r\n\r\n";
push bp
mov bp,sp
push di
push si
add sp,#-$102
! Debug: eq [$2D] char = .10+0 to * char welcome_str = [S+$108-$108] (used reg = )
mov bx,#.10
mov -$106[bp],bx
!BCC_EOS
! 77     char *new_line_str = "\r\n";
dec sp
dec sp
! Debug: eq [3] char = .11+0 to * char new_line_str = [S+$10A-$10A] (used reg = )
mov bx,#.11
mov -$108[bp],bx
!BCC_EOS
! 78     char *prompt_str = "> ";
dec sp
dec sp
! Debug: eq [3] char = .12+0 to * char prompt_str = [S+$10C-$10C] (used reg = )
mov bx,#.12
mov -$10A[bp],bx
!BCC_EOS
! 79     char *str_smiley_text = "smiley";
dec sp
dec sp
! Debug: eq [7] char = .13+0 to * char str_smiley_text = [S+$10E-$10E] (used reg = )
mov bx,#.13
mov -$10C[bp],bx
!BCC_EOS
! 80     char *str_smiley_sym = ":-)";
dec sp
dec sp
! Debug: eq [4] char = .14+0 to * char str_smiley_sym = [S+$110-$110] (used reg = )
mov bx,#.14
mov -$10E[bp],bx
!BCC_EOS
! 81     char *str_fullstop_text = "fullstop";
dec sp
dec sp
! Debug: eq [9] char = .15+0 to * char str_fullstop_text = [S+$112-$112] (used reg = )
mov bx,#.15
mov -$110[bp],bx
!BCC_EOS
! 82     char *str_fullstop_sym = ".";
dec sp
dec sp
! Debug: eq [2] char = .16+0 to * char str_fullstop_sym = [S+$114-$114] (used reg = )
mov bx,#.16
mov -$112[bp],bx
!BCC_EOS
! 83     char *str_help = "help";
dec sp
dec sp
! Debug: eq [5] char = .17+0 to * char str_help = [S+$116-$116] (used reg = )
mov bx,#.17
mov -$114[bp],bx
!BCC_EOS
! 84     char *str_help_out = "Possible Commands are:\r\n\n smiley\r\n:-)\r\nfullstop\r\n.\r\nhelp\r\nexit\r\n\r\n";
dec sp
dec sp
! Debug: eq [$43] char = .18+0 to * char str_help_out = [S+$118-$118] (used reg = )
mov bx,#.18
mov -$116[bp],bx
!BCC_EOS
! 85     char *str_unknown_cmd = "Unknown Command!!!\r\nType \"help\" to list commands\r\n\r\n";
dec sp
dec sp
! Debug: eq [$35] char = .19+0 to * char str_unknown_cmd = [S+$11A-$11A] (used reg = )
mov bx,#.19
mov -$118[bp],bx
!BCC_EOS
! 86     char sec_buf[512];
!BCC_EOS
! 87     
! 88     int i;
!BCC_EOS
! 89     
! 90     for(i 
add sp,#-$202
! 90 = 0;  i < 512;  i+=3) {
! Debug: eq int = const 0 to int i = [S+$31C-$31C] (used reg = )
xor ax,ax
mov -$31A[bp],ax
!BCC_EOS
!BCC_EOS
jmp .1C
.1D:
! 91         sec_buf[i+0] = 'A';
! Debug: add int = const 0 to int i = [S+$31C-$31C] (used reg = )
mov ax,-$31A[bp]
! Debug: ptradd int = ax+0 to [$200] char sec_buf = S+$31C-$31A (used reg = )
mov bx,bp
add bx,ax
! Debug: eq int = const $41 to char = [bx-$318] (used reg = )
mov al,*$41
mov -$318[bx],al
!BCC_EOS
! 92         sec_buf[i+1] = 'B';
! Debug: add int = const 1 to int i = [S+$31C-$31C] (used reg = )
mov ax,-$31A[bp]
! Debug: ptradd int = ax+1 to [$200] char sec_buf = S+$31C-$31A (used reg = )
inc ax
mov bx,bp
add bx,ax
! Debug: eq int = const $42 to char = [bx-$318] (used reg = )
mov al,*$42
mov -$318[bx],al
!BCC_EOS
! 93         sec_buf[i+2] = 'C';
! Debug: add int = const 2 to int i = [S+$31C-$31C] (used reg = )
mov ax,-$31A[bp]
! Debug: ptradd int = ax+2 to [$200] char sec_buf = S+$31C-$31A (used reg = )
inc ax
inc ax
mov bx,bp
add bx,ax
! Debug: eq int = const $43 to char = [bx-$318] (used reg = )
mov al,*$43
mov -$318[bx],al
!BCC_EOS
! 94     }
! 95     
! 96     write_sector(4,sec_buf);
.1B:
! Debug: addab int = const 3 to int i = [S+$31C-$31C] (used reg = )
mov ax,-$31A[bp]
add ax,*3
mov -$31A[bp],ax
.1C:
! Debug: lt int = const $200 to int i = [S+$31C-$31C] (used reg = )
mov ax,-$31A[bp]
cmp ax,#$200
jl  .1D
.1E:
.1A:
! Debug: list * char sec_buf = S+$31C-$31A (used reg = )
lea bx,-$318[bp]
push bx
! Debug: list int = const 4 (used reg = )
mov ax,*5
push ax
! Debug: func () int = write_sector+0 (used reg = )
call _write_sector

!mov al, #1
!mov bx, 6[bp]
!mov dl, #0
!mov ch, #0
!mov cl, #5
!mov dh, #0
!mov ah, #3
!int #0x13



add sp,*4
!BCC_EOS
! 97     c_print_str(welcome_str);
! Debug: list * char welcome_str = [S+$31C-$108] (used reg = )
push -$106[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 98     while(1) {
.21:
! 99         c_print_str(prompt_str);
! Debug: list * char prompt_str = [S+$31C-$10C] (used reg = )
push -$10A[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 100         c_get_str(buf);
! Debug: list * char buf = S+$31C-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () void = c_get_str+0 (used reg = )
call _c_get_str
inc sp
inc sp
!BCC_EOS
! 101         c_print_str(new_line_str);
! Debug: list * char new_line_str = [S+$31C-$10A] (used reg = )
push -$108[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 102         if(c_strcmp(buf,"exit")== 0)
! Debug: list * char = .23+0 (used reg = )
mov bx,#.23
push bx
! Debug: list * char buf = S+$31E-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .22
.24:
! 103             break;
br  .1F
!BCC_EOS
! 104         else if(c_strcmp(buf,str_smiley_text)== 0)
br  .25
.22:
! Debug: list * char str_smiley_text = [S+$31C-$10E] (used reg = )
push -$10C[bp]
! Debug: list * char buf = S+$31E-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .26
.27:
! 105             c_print_str(str_smiley_sym);
! Debug: list * char str_smiley_sym = [S+$31C-$110] (used reg = )
push -$10E[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 106         else if(c_strcmp(buf,str_smiley_sym )== 0)
br  .28
.26:
! Debug: list * char str_smiley_sym = [S+$31C-$110] (used reg = )
push -$10E[bp]
! Debug: list * char buf = S+$31E-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .29
.2A:
! 107             c_print_str(str_smiley_text);
! Debug: list * char str_smiley_text = [S+$31C-$10E] (used reg = )
push -$10C[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 108         else if(c_strcmp(buf,str_fullstop_text)== 0)
jmp .2B
.29:
! Debug: list * char str_fullstop_text = [S+$31C-$112] (used reg = )
push -$110[bp]
! Debug: list * char buf = S+$31E-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .2C
.2D:
! 109             c_print_str(str_fullstop_sym);
! Debug: list * char str_fullstop_sym = [S+$31C-$114] (used reg = )
push -$112[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 110         else if(c_strcmp(buf,str_fullstop_sym)== 0)
jmp .2E
.2C:
! Debug: list * char str_fullstop_sym = [S+$31C-$114] (used reg = )
push -$112[bp]
! Debug: list * char buf = S+$31E-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .2F
.30:
! 111             c_print_str(str_fullstop_text);
! Debug: list * char str_fullstop_text = [S+$31C-$112] (used reg = )
push -$110[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 112         else if(c_strcmp(buf,str_help)== 0) 
jmp .31
.2F:
! Debug: list * char str_help = [S+$31C-$116] (used reg = )
push -$114[bp]
! Debug: list * char buf = S+$31E-$106 (used reg = )
lea bx,-$104[bp]
push bx
! Debug: func () int = c_strcmp+0 (used reg = )
call _c_strcmp
add sp,*4
! Debug: logeq int = const 0 to int = ax+0 (used reg = )
test ax,ax
jne  .32
.33:
! 113             c_print_str(str_help);
! Debug: list * char str_help = [S+$31C-$116] (used reg = )
push -$114[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 114         else
! 115             c_print_str(str_unknown_cmd);
jmp .34
.32:
! Debug: list * char str_unknown_cmd = [S+$31C-$11A] (used reg = )
push -$118[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 116         c_print_str(new_line_str);
.34:
.31:
.2E:
.2B:
.28:
.25:
! Debug: list * char new_line_str = [S+$31C-$10A] (used reg = )
push -$108[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 117         c_print_str(new_line_str);
! Debug: list * char new_line_str = [S+$31C-$10A] (used reg = )
push -$108[bp]
! Debug: func () void = c_print_str+0 (used reg = )
call _c_print_str
inc sp
inc sp
!BCC_EOS
! 118     }
! 119 return;
.20:
br  .21
.35:
.1F:
lea sp,-4[bp]
pop si
pop di
pop bp
ret
!BCC_EOS
! 120 }
! 121 
! Register BX used in function main
.23:
.36:
.ascii "exit"
.byte 0
.19:
.37:
.ascii "Unknown Command!!!"
.byte $D,$A
.ascii "Type \"help\" to list commands"
.byte $D,$A,$D,$A
.byte 0
.18:
.38:
.ascii "Possible Commands are:"
.byte $D,$A,$A
.ascii " smiley"
.byte $D,$A
.ascii ":-)"
.byte $D,$A
.ascii "fullstop"
.byte $D,$A
.ascii "."
.byte $D,$A
.ascii "help"
.byte $D,$A
.ascii "exit"
.byte $D,$A,$D,$A
.byte 0
.17:
.39:
.ascii "help"
.byte 0
.16:
.3A:
.ascii "."
.byte 0
.15:
.3B:
.ascii "fullstop"
.byte 0
.14:
.3C:
.ascii ":-)"
.byte 0
.13:
.3D:
.ascii "smiley"
.byte 0
.12:
.3E:
.ascii "> "
.byte 0
.11:
.3F:
.byte $D,$A
.byte 0
.10:
.40:
.ascii "Welcome to http://www.talkortell.de - OS"
.byte $D,$A,$D,$A
.byte 0
.data
.bss

! 0 errors detected



Der Einbau der C-Funktionen und des Dateisystems in den Kernel

Ich poste die nächsten Codes, jetzt nicht ausführlich. Den Code kann man herunterladen. Ich habe unten Links zu den Dateien.
In dem C-Quelltext waren noch ein paar kleine Fehler, die mir erste aufgefallen sind, als ich den Code im Kernel testete. Zum Beispiel war die Dateiposition als globale Variable angegeben. Diese Fehler konnte ich alle entfernen. Jetzt ist es mir gelungen, die Funktionen und den Code zum Dateisystem mit dem Kernel zu vereinigen. Die Auführung führte zu keinen Absturz. Das kann man daran sehen, dass ja die _main Funktion aufgerufen wird, doch vorher wird eine Funktion von der Dateiverwaltung aufgerufen. Hier kam es zu keim Absturz, so, dass die Funktion _main erfolgreich ausgeführt werden konnte

So, ich habe es mit einem kleinen Trick hinbekommen. Der Assemblerkernel führt die Funktionen zum Dateizugriff erfolgreich aus. Ich habe ein entsprechendes Dump vom Image des Kernels gemacht und das heißt, vom Kernel aus, wird in das Dateisystem geschrieben.
Ich hatte ein Problem: Ich lege ja die FAT im Arbeitsspeicher an. Im Arbeitsspeicher hat diese eine Größe von 512*24 Byte. Der Dateipuffer hat eine Größe von 512*25 Byte. Das Problem war, dass, nachdem ich diese Variablen bzw. Arrays im C-Programm definiert hatte, sie nicht im Code-Segment zu finden waren, sondern in .comm. Dasselbe Problem hatten wir bereits mit .data.
Jetzt war meine Frage, wie das übersetzen? Und meine erste Idee war, dass man weiterhin Assembler verwendet, aber ein C-Programm schreibt, welches 25*512 bzw. 24*512, $0, nach _fat: .byte schreibt. Allerdings krepierte der Assembler daran. Dann war meine Idee, die ich allerdings zuerst ausführte, doch zu der Methode zurück zu kehren, wie sie Henkes verwendet. Er verwendet C-Programme und Assembler-Programme so, dass erst das Assemblerprogramm zum Objektfile übersetzt wird, dann das C-Programm und am Ende beides gelinkt.
Das würde jetzt gehen, da ich ja nicht NASM mit bcc mische. Denn ich habe ja as86 als Assembler. Was mit nasm geht, geht auch mit as86. Dann könnte ich das Linkerskript mit as86 statt mit nasm verwenden. Allerdings funktionierte das ganze wieder nicht.
Jetzt war die Frage, wie kann ich einen 25*512 bzw. 24*512 Byte großen Bereich anlegen. Die Antwort lautet schlicht und ergreifend, ich verwende die Direktive für as86: .space. Hinter .space kann man eine Anzahl von Byte angeben, die reserviert werden. .space BYTE, heißt es werden BYTE-Anzahl Byte resveriert. .space 12800 es werden 12800 Byte in Anspruch genommen. Das habe ich verwendet und funktioniert mit 512*25 bzw. 512*24 Byte. Nun hat der Kernel eine Größe von ca. 30kByte. Im 16-Bit-Modus ist der Arbeitsspeicher maximal 1MByte groß. Ein Segment allerdings ist maximal 64kByte groß. Der Kernel liegt alleine im Code-Segment. Das haben wir ja eben gerade hier die ganze Zeit gemacht, wir haben und wollten .data und .comm umgangen und umgehen wollen. Also ist der Kernel ausschließlich im Code-Segment. Er kann aber nicht größer als 64kByte werden. Da der Kernel aber jetzt, so wie er sein soll, so gut wie fertig ist, und nicht mehr viel Funktionen kommen, und der Kernel gerade Mal 30kByte einnimmt, wobei man bedenken muss, dass das wenigste Code ist, ca. 26kByte alleine sind der Puffer für die Dateien und für die FAT, passt wohl noch genügend Code rein, etwa für EXEC.

Ein Fehler ist noch drin:
Das WRITE, mit CLOSE und SOFT_WRITE hat die Anfangssektoren des Image überschrieben. Aber der Fehler findet sich einfach. Nicht etwa bei den Definitionen von ROOT_DIR_FIRST_SECTOR usw. Das stimmt. Aber auffällig ist, dass der 0. Sektor gleich überschrieben wird. Eine Datei ist etwa 4 Sektoren weiter geschrieben worden. Der Fehler ist aber einfach zu finden: Nämlich getnextfreefatentry(); Bisher fanden die Operationen auf den Puffer im Arbeitsspeicher statt, nämlich fs_buf. In diesem Puffer, der das Dateisystem simulierte, wurde von 0 angefangen zu zählen. Jetzt muss bei getnextfreefatentry() beim Rückgabewert ROOT_DIR_FIRST_SECTOR = 24+10 addiert werden.

Nein, Entschuldigung, das war nicht der einzige der Fehler, da stand von einem Test, noch das Beschreiben eines Sektors. Das ist aber kein Problem. Die Routinen für das Dateisystem funktionieren generell. Man kann sie auch aufrufen, ohne, dass das Programm abstürzt. Tatsächlich wurde der erste Sektor überschrieben und das stammt nicht vom Test. Das war ein Test mit writesector.

Was man jetzt in den Programmquelltexten sieht in mainnow.s, ist am Ende, die Definition von der Puffer mittels .space

Es beginnt zu funktionieren

Was die Variablenwerte im Quelltext betrifft, mit .space mag dies etwas für Verwirrung gesorgt haben. Sie wurden von as86 unter .comm definiert, scheinbar als dynamische Elemente. Die gesamte Syntax sieht etwas anders aus. .space wird gar nicht verwendet. Trotzdem, werden sie als Label definiert, stehen sie wie sie müssen zur Verfügung. Jetzt ist ein Fehler im Code. Er schreibt irgendeinen Sektor voll. Ich mache jetzt aber heute oder ein paar Tage Pause. Wenn ich damit fertig bin und der Fehler ausgemärtzt ist, kommt die Implementierung des INT 0x21 und dann EXEC. Danach wäre die erste Version fertig.

So, jetzt fange ich heute doch an, den Fehler zu finden. Ich beginne in 10 Minuten. Das kann spannend werden. Von weiteren Tests des Dateisystems sehe ich erst Mal ab.
Wenn das geklappt habe, sage ich "Ok, gut, ...". Dann wir die Sache spannend, aber gar nicht so schwer. Der Interrupt INT 0x21, wird über eine Assemblerroutine, die in Assembler programmiert wird, das AH Register abfragen. CMP AH, Funktionsnummer, JZ ... Dort werden alle Einsprungsadressen zu den Routinen READ, WRITE, OPEN, CREATE, CLOSE, ... drin stehen.
Was dann auch noch ein Mal spannend wird, ist die Funktion EXEC, am Besten mitsamt EXIT. Da muss ich mich noch ein Mal schlau machen, wie man in as86 einen FAR-JUMP ausführt. Hat man den Dateikopf der EXE-Datei richtig interpretiert, fehlt eigentlich nichts. Dabei kommt es unter Umständen gar nicht darauf an, den Dateikopf der EXE-Datei wirklich zu 100% aus zu werten und jedes Byte oder Wort, eindeutig 100%ig zu verwenden. Denn im Prinzip genügt schon der Einsprung ins Code-Segment, ab der ersten Speicherstelle, wenn man den Abstand vom Code zum CS berücksichtigt und DS und ES und SS richtig lädt. Was ich an der Stelle noch sagen möchte, ist, dass es einigen Vorteil bietet, dass ich den as86 hier so verwende, wie ich es tue. Denn ich habe Mal geschaut: Der as86 bietet tatsächlich keine Möglichkeit im Inline-Assembler, also Assembler in einer C-Routine, C-Variablen zu verwenden. Das heißt, man kann zwar aus dem Assembler-Programm C-Code aufrufen, aber nicht so gut, aus dem C-Code Assembler. Hier würden sich wieder die Methoden anbieten, da ja bei dem Aufruf einer C-Funktion die Variablen über BP übergeben werden, dies so verwenden, wie ich es mit dem as86 im vollständigen Assembler-Quelltext der mit bcc übersetzt wurde, bisher schon mache. Doch das ist ebenso wenig bequem. Dass dem tatsächlich so ist, dass im Inline-Assembler keine C-Variablen verwendet werden können, sogar auch bei gcc und nasm, wobei ich mir vorstellen kann, dass es hier doch ginge, finde ich zeigt sogar Henkes. Denn Henkes hat ja einen C-Kernel geschrieben. In diesem C-Kernel braucht man genauso wie überall, auch im Assemblerprogramm eine Routine zur Ein- und Ausgabe über Tastatur und Bildschirm. Doch ich habe die Ausgabefunktion betrachtet und diese enthält kein Stück Code in Assembler, sondern stattdessen beschreibt sie den Videopuffer im 32-Bit-Modus. Beschreibt man den Video-Puffer im 32-Bit-Modus in dem man an eine bestimmte Stelle im Arbeitsspeicher schreibt, ist das natürlich kein Problem.
Des weiteren, was ich noch anmerken möchte: Ist der Kernel erst ein Mal so weit fertig, dass er Programme zur Ausführung im Single-Tasking-Modus bringt, ist es relativ einfach in den Multitasking-Modus zu wechseln. Und das ist dann echtes Multitasking. Das ist keine Vorgaukelung eines Multitaskings. Die Sache funktioniert so, dass wir eine verkettete Liste mit Prozessen haben. Diese Liste ist am Ende zu einem Ring geschlossen. Jedes Element der Liste enthält einen Eintrag der Prozesstabelle. Es muss keine Liste sein, es kann auch ein Array sein, dann wäre der Index zum Beispiel die PID. Trotzdem macht es natürlich Spaß einen solchen Eintrag zu implementieren. Hier findet man dann AX, BX, CX, DX, SI, DI, BP, SP und so weiter. Außerdem braucht man ein Timer-Interrupt was eine entsprechende Routine aufruft. Das ist spannend. Interessant wird dann auch, wenn man natürlich mehrere Konsolen realisiert. Ohne mehrere Konsolen macht die Sache natürlich keinen Spaß. Hier wäre interessant den Wechsel der Konsolen ein zu leiten, dass könnte man aber ganz unelegant machen, etwa über den Befehl:

console 1
console 4


in der aktuellen Konsole. Nur: Bei diesem Multitasking muss die Ausgabe jeweils in einen Puffer geschrieben werden und es darf nur der Puffer einer Konsole gleichzeitig angezeigt werden.
Der Schritt der dann folgt, ist ein sehr interessanter Schritt, denn man könnte sich mit oder ohne Umweg das Interrupt 0x21 zum Interrupt 0x80 oder wenigstens zu einem Teil davon, sich dem Konzept der Devices nähern. Wir wissen, dass die Devices unter Linux im Verzeichnis
/dev/
enthalten sind.
Also

/dev/sda1
/dev/sda2
/dev/hda0
/dev/com1
/dev/video0

usw. Wenn wir Linux heutzutage aufrufen sind wir es gewohnt, dass die Devices alle gleich angezeigt werden. Doch eines weiß, /dev können auch alle erst mal installiert werden müssen.
Sobald man bei dem Prinzip der Devices angekommen ist, muss man sich zwangsläufig auf irgendeine Art und Weise mit dem Prinzip des Treibers auseinandersetzen. Doch Vorsicht! Die Nöte sind geringer als man denkt, denn schaut man sich zum Beispiel das Lesen einer IDE-Festplatte mit ihrem IDE-Befehlssatz an, dann sind die Probleme weit aus schneller gelöst, als man denkt. Der Treiber nimmt einen das Problem des Protokolls ab. Auch eine IDE-Festplatte hat ein Protokoll, nämlich, wann und in welcher Reihenfolge, Befehle bzw. Daten übertragen werden. Was hier dann eine Rolle spielt, ist diese, wenn es auch jeweils keine übermäßige Menge an Programmieraufwand ist, alle für zumindest bestimmte Geräte zur Verfügung zu stellen.
Die Devices sind nach außen alle gleich, dahinter versteckt sich jeweils eine eigene Eigenschaft. So stelle ich mir das vor. An der Stelle werde ich mich allerdings Andrews S. Tanenbaums Minix wirklich bedienen.
Ab diesem Zeitpunkt wird es interessant, was die Konzepte von Betriebssystemen betrifft. Ein typisches Konzept von Betriebssystemen sind Pipes und Semaphore. Semaphore sind Methoden die Interprozesskommunikation zu ermöglichen, ohne, dass es zu Zusammenstößen kommt. Schaut man sich aber zum Beispiel die Bash an,so ist es typisch, die Ausgabe des einen Programms in ein anders um zu lenken. Hier wird Interprozesskommunikation interessant.

Ich habe es hinbekommen, der Fehler ist weg. Es waren zwei, nachdem ich die Fehler ausgemärtzt hatte, von denen ich schon sprach. Die da waren: Dass bei READ und WRITE an einigen Stellen in den Puffer geschrieben wurde.
Trotzdem gab es ein paar sehr gravierende Fehler: Ich habe nämlich an vielen Stellen WRITE statt WRITE-SECTOR und READ statt READ-SECTOR verwendet. Das ist natürlich ein Problem. Tatsächlich wird WRITE und READ nur an jeweils einer Stelle verwendet: WRITE bei CLOSE und READ bei OPEN. Das ist auch gut. Denn: Hier bewährt sich die Sache ja, über ihren Dateipuffer. Ein READ und WRITE allerdings, dass ja automatisch mit einer FAT und Struktur arbeitet, da zu verwenden, wo die Struktur erst angelegt wird, ist natürlich fatal!
Trotzdem gab es noch einen Fehler. Trotz der Verbesserung, die eine Notwendigkeit darstellt, gab es noch einen Fehler, so, dass immer noch der erste Sektor des Images überschrieben wurde. Und der ist mir bereits aufgefallen. Jetzt allerdings habe ich ihn erneut ausgemärtzt, mit dem Resultat, dass die Sache funktioniert.
Wenn man nämlich getnextfatentry() anschaut, die den nächsten freien Sektor sucht, dann liefert sie ihn als Sektornummer von Typ Ganzzahl zurück. Doch hier wird anders gerechnet: Der erste Eintrag ist 0, der zweite 1 usw. Und das ist auch. Schreibt man aber an diese Stelle direkt auf die Festplatte wird natürlich der erste Sektor überschrieben. Deswegen gilt beim Schreiben eines Sektors, immer ROOT-DIR-START-SECTOR addieren. Gerechnet wird weiter mit den ganzen Zahlen, relativ, auch bei getnextfatentry(). Das ist auch gut. Umgekehrt muss man sehen. Dieser Fehler schlug bei dem Test im Arbeitsspeicher nicht zu Buche, denn hier gab es einen eigenen Filesystem-Puffer, bei dem man durchaus, wenn man die FAT nicht in diesem Puffer abbildete, was man hätte korrekt auch tun könnte, aber nicht so sehr ins Gewicht fällt.

Den C-Code habe ich mit
bcc
entsprechend in Assembler übersetzt und die fehlenden Funktionen entsprechend in Assembler ersetzt, also:

-c-print-str
-c-get-str
-write-sector
-read-sector

Und den Anfang, also den ursprünglich in Assembler geschrieben Teil des Kernels. Außerdem die Variablen, wie fat und Datei-Puffer, die Sache mit dem .space.
Ich poste das ganze gleich auf meiner Website.
Außerdem hier noch mal den Assembler-Quelltext.
Außerdem ist zu sagen: Es wurde an einer Stelle nicht richtig geschrieben. Dabei handelt es sich um einen minimalen Fehler. Denn: Bevor ich die Korrekturen durchgenommen habe, wurden wie gesagt die ersten 8 Sektoren des Images überschrieben. Das funktionierte fabelhaft. Nur ist das überhaupt nicht das Ziel, die ersten 8 Sektoren zu überschreiben. Das Schreiben funktioniert einwandfrei. Daran ist nicht zu zweifeln. Nachdem ich jetzt noch mehr verbessert hatte, ist noch ein klein wenig mehr fehlerhaftes aufgetaucht. Aber das sind minimale Fehler, die sich auch dadurch auszeichnen, dass sie eben auf die Korrektur hin sich veränderten. Somit ist das Problem gelöst. Außerdem möchte ich nicht die ersten 8 Sektoren überschreiben. Da dies beeinflussbar ist, ist somit das Problem so gut, wie gelöst.

Was man hier sehen kann, letzter Beitrag, ist, dass hier im Wurzelverzeichnis test2 steht. Dies steht aber an richtiger Stelle. Betrachtet man den Offset (siehe Adressen linke Seite und am unteren Rand), dann steht das an richtiger Stelle. Der Kernel blieb unberührt. Umgekehrt: Ich hatte test und test2 erzeugt. Bevor ich das Problem gelöst hatte, wurde test und test2 geschrieben. Jetzt nachdem das Problem gelöst wurde, ist dieses Problem aufgetaucht. Beide Probleme bestehen also in einem Zusammenhang. Wobei: Das Problem das gelöst wurde, wiegt viel viel schwerer, lässt sich aber auch beseitigen. Was jetzt kommt, ist mit wenig Anstrengung zu beseitigen und lässt sich dann auch dementsprechend demonstrieren.

main0001.c
main0001.s
fs08.c



Lösung bzw. Bug gefunden

Es ist mir gelungen die Dateien als Sequenz von Bytes zu schreiben.
Allerdings muss man bedenken: Die Puffer, die bei der Funktion func übergeben werden, sind wieder lokale Variablen, die dynamisch übergeben werden. Hier ist nicht ausdrücklich Platz reserviert und der Zeiger, der auf sie zeigt, benutzt eventuell das Segmentregister DS. Doch DS befindet sich unter .data und damit hatten wir bereits unsere Schwierigkeiten. Ich habe etwas anderes gemacht. Da, wo fat definiert wurde, habe ich ein Label untergebracht. Hinter diesem Label steht: .ascii "ABCABC..." .space 2000. Also 2048 Bytes. Übergibt man diese Variable anstelle dem vorherigen, wird etwas korrekt geschrieben. Ich habe es auf dem Image zwei Mal gefunden. Allerdings gibt es jetzt wieder ein Problem, nämlich mit der Geometrie. Das Programm hat nur den Namen der einen Datei platziert, die Frage ist nur wo? Sie liegt nämlich gar nicht auf den 512 Byte Grenzen. Da das Programm, dies als reines C-Programm im Arbeitsspeicher vollzog, nehme ich an, der Fehler liegt nicht eigentlich in der Struktur generell. Das kann andere Gründe haben. Zum Beispiel wird der Sektor auf der Diskette mit 1 angefangen zu zählen. Derartige Sachen können zu so einem Fehler führen. Ich möchte noch Mal sagen: Sollte das Problem da liegen, dass ich überall mit dem ROOT_DIRECTORY anfange zu zählen (ROOT_SECTOR_START... wie auch immer,), würde mich das wundern lassen. Denn: Das ROOT_DIRECTORY ist eine Datei. Und wenn diese angelegt wird, wird automatisch der Platz von einem Sektor belegt. Zwar könnte man meinen, dass dieser Zusammenhang durch die READ_SECTOR und WRITE_SECTOR verloren geht, doch: Ich habe vor jedem Anlegen eines Sektors getnextfreefatentry() verwendet und das belegt den Platz in der FAT. Und der erste und nächste Sektor, der von CREATE verwendet wird, wird ebenfalls mit getnextfreefatentry() ermittelt.

Tatsächlich. Verwendet man nicht close() funktioniert die Sache. Verwendet man close nicht, dann wird der Puffer nicht geschrieben. Nun ist das Wurzelverzeichnis ordentlich angelegt. Es sind dort test und test2. Allerdings gibt es nun einen zweiten Fehler, fällt mir auf. Vielleicht liegt es ja an dem. In test und test2 sind als Angabe hinter dem Dateinamen zu dem Sektor, an dem die Datei beginnt, gar keine Werte, die auf den ersten Sektor zeigen, sondern wieder 0. Das kann stimmen, werde ich jetzt prüfen, weil ich ja so oder so richtig nummeriere und den Offset dann dazu addieren, was allerdings eindeutig falsch ist, ist: Beide Verzeichnisse zeigen auf den selben Sektor. Außerdem: Bei 0 beginnt wenn schon überhaupt, das Wurzelverzeichnis. Das stimmt also auf keinen Fall.
Der Fehler muss aber durch die Veränderungen geschehen, weil beim Test im Arbeitsspeicher spielte er keine Rolle.

void make_root_dir() {
    char buf[SECTOR_SIZE];
    int i;
    
    for(i = 0;  i < SECTOR_SIZE;   i++)
        buf[i] = 0;
    for(i = 0;  i < 14;  i++)
        buf[i] = ' ';
    for(i = 0x10;  i < 14+0x10;  i++)
        buf[i] = ' ';
    buf[0x00] = '.';
    buf[0x0e] = ((ROOT_DIR_FIRST_SECTOR & 0xff00) >> 8) & 0x00ff;
    buf[0x0f] = ROOT_DIR_FIRST_SECTOR & 0x00ff;
    buf[0x10] = '.';
    buf[0x11] = '.';
    buf[0x1e] = ((ROOT_DIR_FIRST_SECTOR & 0xff00) >> 8) & 0x00ff;
    buf[0x1f] = ROOT_DIR_FIRST_SECTOR & 0x00ff;
    writefatentry(ROOT_DIR_FIRST_SECTOR, FAT_EOF, 512);
    write_sector(ROOT_DIR_FIRST_SECTOR, buf);
return;
}


Und hin gehört:

void make_root_dir() {
    char buf[SECTOR_SIZE];
    int i;
    
    for(i = 0;  i < SECTOR_SIZE;   i++)
        buf[i] = 0;
    for(i = 0;  i < 14;  i++)
        buf[i] = ' ';
    for(i = 0x10;  i < 14+0x10;  i++)
        buf[i] = ' ';
    buf[0x00] = '.';
    buf[0x0e] = ((ROOT_DIR_FIRST_SECTOR & 0xff00) >> 8) & 0x00ff;
    buf[0x0f] = ROOT_DIR_FIRST_SECTOR & 0x00ff;
    buf[0x10] = '.';
    buf[0x11] = '.';
    buf[0x1e] = ((ROOT_DIR_FIRST_SECTOR & 0xff00) >> 8) & 0x00ff;
    buf[0x1f] = ROOT_DIR_FIRST_SECTOR & 0x00ff;
    writefatentry(0, FAT_EOF, 512);
    write_sector(ROOT_DIR_FIRST_SECTOR, buf);
return;
}


Es geht um die Zeile:

writefatentry(0, FAT_EOF, 512);

bzw.

writefatentry(ROOT_DIR_FIRST_SECTOR, FAT_EOF, 512);


writefatentry bzw. die FAT selber arbeitet ja mit einer Nummerierung, die allgemein gilt. Ich habe das ganze wieder im Arbeitsspeicher getestet und auch hier zeigte sich alles, wie jetzt gedacht.

Es funktioniert wirklich, auch in der Praxis, ich habe ein dump erzeugt, von dem Image und es sieht gut aus. Sowohl das root-Verzeichnis, als auch die erste Datei und die zweite wurden richtig geschrieben.






main0002.s
fs09.c


Der Interrupt-Handler wird installiert

So, ich habe es jetzt so weit: Der Interrupt-Handler ist quasi schon da und er tut auch mit _print_string.

Wie gewohnt beim Interrupt INT 0x21
Zeichenkette ausgeben:

ah = 0x09
dx = ANFANGSADRESSE VOM AUS ZU GEBENDEN STRING...


main0003.s
newsave0001.s
kernel0002.img

Einbau von CREATE, ... in den Interrupt-Handler

Also, es hat funktioniert. Von nun an funktioniert mein Kernel auch mit dem Schreiben in das Dateisystem. Also mit OPEN, CREATE, CLOSE, SOFT_WRITE, SOFT_READ. Ich habe OPEN, CREATE, CLOSE, SOFT_WRITE, SOFT_READ als Betriebssystemaufrufe implementiert und es funktioniert. Ich werde jetzt die "dumps" zu dem so entstandenen Dateisystem posten. Danach den neu entstandenen Assembler-Quelltext.

  mov ax, #0x400 
  mov ds, ax
  mov es, ax
  mov si, #welcome
  call _print_string
  mov si, #welcome
  call _print_string
  call _install_int_0x21
  mov ax, #0x400
  mov ds, ax
  
  mov dx, #welcome 
  mov ah, #0x09
  int #0x21

  mov dx, #welcome 
  mov ah, #0x09
  int #0x21
  
  mov dx, #welcome 
  mov ah, #0x09
  int #0x21
  
  mov dx, #welcome 
  mov ah, #0x09
  int #0x21
  

  call _func
  call _main
_again1: jmp _again1

_install_int_0x21:
mov ax, #0x0000
mov ds, ax
mov di, #0x0084
mov [di], #_interrupt_handler_int_0x21
inc di
inc di
mov [di], #0x0400
mov ax, #0x0400
mov ds, ax
ret 

_interrupt_handler_int_0x21:

! Funktionen
! 0x00 Program terminate (Later)
! 0x01 Character input
! 0x02 Character output
! 0x09 Display string 
! 0x0a buffered keyboard input 
! 0x0f open file 
! 0x10 close file
! 0x39 create subdirectory 
! 0x3b change current directory
! 0x3d open file 
! 0x3e close file 
! 0x3f read file or device 
! 0x40 write file or device 
! 0x4b execute program (Later)
! 0x5b create new file 

cmp ah, #0x00 !Program terminate
jnz _int_0x21_not_0x00
!call _exit
!jmp _int_0x21_end2
mov ah, #0x00
_int_0x21_not_0x00:


cmp ah, #0x01 !Character Input
jnz _int_0x21_not_0x01
call _get_char
!jmp _int_0x21_end
mov ah, #0x01
_int_0x21_not_0x01:


cmp ah, #0x02 !Character Output
jnz _int_0x21_not_0x02
call _print_char
jmp _int_0x21_end
_int_0x21_not_0x02:


cmp ah, #0x09 !Display string
jnz _int_0x21_not_0x09
call _print_string2
jmp _int_0x21_end
_int_0x21_not_0x09:


cmp ah, #0x0a !buffered keyboard input
jnz _int_0x21_not_0x0a
call _get_string
jmp _int_0x21_end
_int_0x21_not_0x0a:


cmp ah, #0x0f !open file
jnz _int_0x21_not_0x0f
call _open
jmp _int_0x21_end
_int_0x21_not_0x0f:


cmp ah, #0x10 !close file
jnz _int_0x21_not_0x10
call _close
jmp _int_0x21_end
_int_0x21_not_0x10:


cmp ah, #0x39 !create subdirectory
jnz _int_0x21_not_0x39
!call _mkdir
jmp _int_0x21_end
_int_0x21_not_0x39:


cmp ah, #0x3b !change subdirectory
jnz _int_0x21_not_0x3b
call _chdir
jmp _int_0x21_end
_int_0x21_not_0x3b:


cmp ah, #0x3d !open file
jnz _int_0x21_not_0x3d
call _open
jmp _int_0x21_end
_int_0x21_not_0x3d:


cmp ah, #0x3e !close file
jnz _int_0x21_not_0x3e
call _close
jmp _int_0x21_end
_int_0x21_not_0x3e:


cmp ah, #0x3f !read file or device
jnz _int_0x21_not_0x3f
call _soft_read
jmp _int_0x21_end
_int_0x21_not_0x3f:


cmp ah, #0x40 !write file or device
jnz _int_0x21_not_0x40
call _soft_write
jmp _int_0x21_end
_int_0x21_not_0x40:


cmp ah, #0x4b !execute program 
jnz _int_0x21_not_0x4b
!call _exec
jmp _int_0x21_end
_int_0x21_not_0x4b:


cmp ah, #0x5b !create new file
jnz _int_0x21_not_0x5b
call _create
jmp _int_0x21_end
_int_0x21_not_0x5b:

_int_0x21_could_not_handle:
mov si, #could_not_handle_int_0x21_string
call _print_string

_int_0x21_end:
iret


_get_string:
  mov si, dx
  xor cl, cl
_get_string_loop:
  xor ah, ah   
  int #0x16    
  cmp al, #13    
  je _get_string_done
  cmp cl, #31    
  je _get_string_loop
  mov ah, #0x0E
  int #0x10     
  stosb  
  inc cl
  jmp _get_string_loop
_get_string_done:
  mov al, #0
  stosb
  mov ax, #0x0E0D
  int #0x10
  mov al, #0x0A
  int #0x10
  ret


_print_string2:
    mov si, dx
_print_string:
    lodsb             
    or al, al         
    jz _print_string_done          
    mov ah, #0x0E
    int #0x10          
    jmp _print_string
_print_string_done:
    ret
    
_get_char:
    xor ah, ah
    int #0x16
    ret

_print_char:
    mov ah, #0x0E
    int #0x10
    ret

could_not_handle_int_0x21_string: .asciz "Could not handle interrupt" 
.byte 0
welcome: .asciz "Welcome to http://www.talkortell.de!!!"
.byte 0




  mov ax, #0x400 
  mov ds, ax
  mov es, ax
  mov si, #welcome
  call _print_string
  mov si, #welcome
  call _print_string
  call _install_int_0x21
  mov ax, #0x400
  mov ds, ax
  
  mov dx, #welcome 
  mov ah, #0x09
  int #0x21

  mov dx, #welcome 
  mov ah, #0x09
  int #0x21
  
  mov dx, #welcome 
  mov ah, #0x09
  int #0x21
  
  mov dx, #welcome 
  mov ah, #0x09
  int #0x21

  call _func  
  
  mov dx, #test_fname
  mov ah, #0x5b
  int #0x21
  mov ah, #0x40
  mov bx, #0x02
  mov cx, #2048
  mov dx, #_hallohallo
  int #0x21
  mov ah, #0x3e
  mov bx, #0x02
  int #0x21
  
  call _main
_again1: jmp _again1

_install_int_0x21:
mov ax, #0x0000
mov ds, ax
mov di, #0x0084
mov [di], #_interrupt_handler_int_0x21
inc di
inc di
mov [di], #0x0400
mov ax, #0x0400
mov ds, ax
ret 

_interrupt_handler_int_0x21:

! Funktionen
! 0x00 Program terminate (Later)
! 0x01 Character input
! 0x02 Character output
! 0x09 Display string 
! 0x0a buffered keyboard input 
! 0x0f open file 
! 0x10 close file
! 0x39 create subdirectory 
! 0x3b change current directory
! 0x3d open file 
! 0x3e close file 
! 0x3f read file or device 
! 0x40 write file or device 
! 0x4b execute program (Later)
! 0x5b create new file 

cmp ah, #0x00 !Program terminate
jnz _int_0x21_not_0x00
!call _exit
!jmp _int_0x21_end2
jmp _int_0x21_end_between
_int_0x21_not_0x00:


cmp ah, #0x01 !Character Input
jnz _int_0x21_not_0x01
call _get_char
!jmp _int_0x21_end
jmp _int_0x21_end_between
_int_0x21_not_0x01:


cmp ah, #0x02 !Character Output
jnz _int_0x21_not_0x02
call _print_char
jmp _int_0x21_end_between
_int_0x21_not_0x02:


cmp ah, #0x09 !Display string
jnz _int_0x21_not_0x09
call _print_string2
jmp _int_0x21_end_between
_int_0x21_not_0x09:

cmp ah, #0x0a !buffered keyboard input
jnz _int_0x21_not_0x0a
call _get_string
jmp _int_0x21_end_between
_int_0x21_not_0x0a:

jmp _int_0x21_end_between2

_int_0x21_end_between:
jmp _int_0x21_end

_int_0x21_end_between2:


cmp ah, #0x0f !open file
jnz _int_0x21_not_0x0f
push dx
call _open
pop dx
jmp _int_0x21_end
_int_0x21_not_0x0f:


cmp ah, #0x10 !close file
jnz _int_0x21_not_0x10
push bx
call _close
pop bx
jmp _int_0x21_end
_int_0x21_not_0x10:


cmp ah, #0x39 !create subdirectory
jnz _int_0x21_not_0x39
!call _mkdir
jmp _int_0x21_end
_int_0x21_not_0x39:


cmp ah, #0x3b !change subdirectory
jnz _int_0x21_not_0x3b
call _chdir
jmp _int_0x21_end
_int_0x21_not_0x3b:


cmp ah, #0x3d !open file
jnz _int_0x21_not_0x3d
push dx
call _open
pop dx
jmp _int_0x21_end
_int_0x21_not_0x3d:


cmp ah, #0x3e !close file
jnz _int_0x21_not_0x3e
push bx
call _close
pop bx
jmp _int_0x21_end
_int_0x21_not_0x3e:


cmp ah, #0x3f !read file or device
jnz _int_0x21_not_0x3f
push dx
push cx
push bx
call _soft_read
pop bx
pop cx
pop dx
jmp _int_0x21_end
_int_0x21_not_0x3f:


cmp ah, #0x40 !write file or device
jnz _int_0x21_not_0x40
push dx
push cx
push bx
call _soft_write
pop bx
pop cx
pop dx
jmp _int_0x21_end
_int_0x21_not_0x40:


cmp ah, #0x4b !execute program 
jnz _int_0x21_not_0x4b
!call _exec
jmp _int_0x21_end
_int_0x21_not_0x4b:


cmp ah, #0x5b !create new file
jnz _int_0x21_not_0x5b
push dx
call _create
pop dx
jmp _int_0x21_end
_int_0x21_not_0x5b:

_int_0x21_could_not_handle:
mov si, #could_not_handle_int_0x21_string
call _print_string

_int_0x21_end:
iret


_get_string:
  mov si, dx
  xor cl, cl
_get_string_loop:
  xor ah, ah   
  int #0x16    
  cmp al, #13    
  je _get_string_done
  cmp cl, #31    
  je _get_string_loop
  mov ah, #0x0E
  int #0x10     
  stosb  
  inc cl
  jmp _get_string_loop
_get_string_done:
  mov al, #0
  stosb
  mov ax, #0x0E0D
  int #0x10
  mov al, #0x0A
  int #0x10
  ret


_print_string2:
    mov si, dx
_print_string:
    lodsb             
    or al, al         
    jz _print_string_done          
    mov ah, #0x0E
    int #0x10          
    jmp _print_string
_print_string_done:
    ret
    
_get_char:
    xor ah, ah
    int #0x16
    ret

_print_char:
    mov ah, #0x0E
    int #0x10
    ret

could_not_handle_int_0x21_string: .asciz "Could not handle interrupt" 
.byte 0
welcome: .asciz "Welcome to http://www.talkortell.de!!!"
.byte 0
test_fname: .asciz "testfnam"
.byte 0










main0004.s
newsave0002.s

Die Lösung für EXEC

Meine erste Frage, die ich mir stellte, war wie laden wir CS und IP. Wenn wir ein Multitasking-Betriebssystem mit PCB (Process Control Block) verwenden, dann müssen wir eine variable Einsprungsadresse verwenden. Denn das Programm liegt immer an anderer Stelle, also etwas wie:

jmp cs:dx

Allerdings fehlt mir dafür die Anweisung in as86.

Was geht, ist eine feste Einsprungsadresse, etwas wie

jmp #0x0400:#0x0020

Doch wie lädt man bei variablen CS und IP die Register - Codesegment und Instruction-Pointer. Na ja, das Code-Segment zu laden ist ganz einfach:

mov ax, ADRESSE mov cs, ax

Ab dieser Stelle, wo das CS geladen wurde, wird der Code ab der Stelle ausgeführt, die als Codesegment CS hat. Jetzt ist die Frage, wie laden wir den IP. Das geschieht über einen Sprung:

jmp #0x00

Jetzt ist die Frage: Was initialisieren wir zuerst: Den IP oder CS. Eine Möglichkeit wäre es, einen Sprung zu 0x00 zu machen und an dieser Stelle das CS zu initialisieren. Also

jmp 0x00

Und an der Stelle 0x00 steht:

mov ax, ADRESSE mov cs, ax

Nachteil wäre, dass der Instruction Pointer nicht mehr auf 0x00 zeigt. Das br&auuml;chten wir, denn, in die ausführbare Datei soll bei IP := 0x00 gesprungen werden. Eine Möglichkeit wäre die Verwendung eines zwischen Code-Segments. In diesem Code-Segment steht eine weitere Sprunganweisung.

All das ersparen wir uns aber, wenn wir ein Single-Tasking-Betriebssystem verwenden und immer an eine feste Stelle springen:

jmp #0x0400:#0x0020

Der Code des ausführbaren Programms wird einfach immer an eine feste Stelle geladen. Zum Beispiel an #0x0400:#0x0020.
Die zweite Frage, die uns beschäftigt ist die Frage, mit dem Laden. Wenn wir EXEC schreiben, besteht dieses quasi aus zwei Teilen. Das erste, wäre das Problem von gerade eben. Wir haben das Programm nach #0x0400:#0x0020 geladen. Initialisieren entsprechend CS und IP. Umgekehrt müssen wir das Programm zunächst laden.

Wenn wir allerdings gerade dabei sind die Stelle des Programms mit jmp #0x0400:#0x0020 zu testen, steht uns das Laden im Wege. Denn auch beim Laden können Fehler geschehen. Außerdem müssen wir das Programm auf dem Image unterbringen. Gerade bei diesem auf dem Image unterbringen müssen wir Entwicklungsarbeit leisten.
Dazu gehört: Wir müssen das Programm, da wir wie in DOS arbeiten zunächst unter Windows/DOS übersetzen. Ich habe kein Windows und kein DOS und meine Idee war, Windows 2000 in die Virtual Box zu installieren. Dann TASM von Borland als Assembler zu verwenden. Doch das wollte ich nicht. Ich habe nasm von Linux verwendet um mit Crosscompiling, eine Windows-Exe im 16-Bit-Format zu erzeugen. Das geht mit dem Befehl:

nasm -fobj test.asm -o test.exe

Jetzt ist das Problem: Wie bekommen wir das Programm in das Image. Die erste Möglichkeit wäare es, dieses von außen unter zu bringen. Wir verwenden sämtliche Routinen für das Datei-System, in einem C-Programm, das unter Linux übersetzt wird und auf das Image angewendet wird. Dieses bringt das Programm unter Linux in dem Image unter. Eine andere Möglichkeit wäre es, diese Datei erst in dem Kernel zu erzeugen. Dazu würde man den Programmcode, der bei der Datei test.exe steht in dem Kernel unterbringen und ihn als Datei auf die Festplatte schreiben lassen.
Sagte ich nicht Programmcode? Genau das ist es: Wir ersparen uns bei EXEC das Laden der Datei in den Arbeitsspeicher. Wir übersetzen einfach die Datei unter Linux in eine DOS-ausfühbare Datei und legen diese an einer bestimmten Stelle im Arbeitsspeicher ab. Dann führen wir einen Sprung dorthin aus.

Dies ist die hauptsächliche Idee: Wir übersetzen die ausführbare Datei und bringen sie an einer Stelle im Arbeitsspeicher im Kernel unter. Wir verzichten auf das Laden der Datei, bei EXEC - zu Testzwecken - und führen einen Sprung dorthin aus.

Für EXEC habe ich mir schon etwas ausgedacht.
Zunächst Mal auf der Seite:
http://www.delorie.com/djgpp/doc/exe/
Sieht man, das meiste ist nicht so wichtig. Wichtig ist beim Code, die richtige Einsprungsadresse zu finden.
Wenn man sich jetzt den JMP anschaut, kann man ruhig springen, zu:

jmp #32768:#0x0058

Das geht. Mit Registern geht bei mir im as86 noch nicht. Aber man kann an eine feste Adresse mit Codesegment:Offset springen.
Ich habe jetzt ein Programm übersetzt. Den Quelltext habe ich mir in Hexcode als DUMP anzeigen lassen. In den Kernel habe ich diesen Hexcode übernommen. Ich habe dabei bei der Funktion EXEC , noch nicht die Komponente programmiert, die das Programm in den Arbeitsspeicher lädt. Sondern ich habe den Hex-Code direkt in den Kernel geschrieben. Beim Kernel habe ich darauf geachtet, dass er auf die 512 Byte Grenze passt, denn das Segmentregister, muss auf eine 16-Byte Grenze passen.
Ich habe vermutet, dass das Programm bei dem Offset #0x0058 beginnt, scheint aber nicht so zu sein. Das werde ich jetzt ausprobieren.

Doch jetzt ist die Frage, wie bekommen wir den Hexcode in den Kernel?

Na ja, wir erzeugen die EXE-Datei und schauen sie im Hexeditor an. Den Hexcode übernehmen wir Byteweise in den Kernel.



Dies schreiben wir so in den Kernel:


_dummy: .space 789
_exec_program_code: .byte $80, $8, $0, $6, $74, $65, $73, $74, $2e, $73, $11, $88, $21, $0, $0, $0, $1d, $54, $68, $65, $20, $4e, $65, $74, $77, $69, $64, $65, $20, $41, $73, $73, $65, $6d, $62, $6c, $65, $72, $20, $32, $2e, $31, $32, $2e, $30, $31, $f9, $96, $f, $0, $0, $c, $5f, $5f, $4e, $41, $53, $4d, $44, $45, $46, $53, $45, $47, $b4, $98, $7, $0, $28, $39, $0, $2, $1, $1, $fc, $88, $4, $0, $40, $a2, $1, $91, $a0, $3d, $0, $1, $0, $0, $8c, $c8, $8e, $d8, $b4, $9, $8d, $16, $11, $0, $cd, $21, $b8, $0, $4c, $cd, $21, $48, $65, $6c, $6c, $6f, $20, $77, $6f, $72, $6c, $64, $2c, $20, $49, $27, $6d, $20, $31, $36, $62, $69, $74, $20, $44, $6f, $73, $20, $41, $73, $73, $65, $6d, $62, $6c, $79, $20, $21, $21, $21, $24, $9f, $9c, $5, $0, $c4, $8, $54, $1, $3e, $8a, $2, $0, $0, $74


Das .dummy ist nachher notwendig. Denn: Wir laden ja das CS Das CS kann aber nur bei allen 16-Byte-Paragraphen anfangen. Also muss der Kernel um die Länge ergänzt werden, dass das Programm nachher auf einer 16-Byte oder gleich 512-Byte-Grenze liegt.

...

cmp ah, #0x4c !exit program 
jnz _int_0x21_not_0x4c
mov ax, #0x400
mov ds, ax
call _main
jmp _int_0x21_end
_int_0x21_not_0x4c:

...

mov ax, #32768
mov ds, ax
jmp #32768:#0x0058

...

_dummy: .space 784
_exec_program_code: .byte $80, $8, $0, $6, $74, $65, $73, $74, $2e, $73, $11, $88, $21, $0, $0, $0, $1d, $54, $68, $65, $20, $4e, $65, $74, $77, $69, $64, $65, $20, $41, $73, $73, $65, $6d, $62, $6c, $65, $72, $20, $32, $2e, $31, $32, $2e, $30, $31, $f9, $96, $f, $0, $0, $c, $5f, $5f, $4e, $41, $53, $4d, $44, $45, $46, $53, $45, $47, $b4, $98, $7, $0, $28, $39, $0, $2, $1, $1, $fc, $88, $4, $0, $40, $a2, $1, $91, $a0, $3d, $0, $1, $0, $0, $8c, $c8, $8e, $d8, $b4, $9, $8d, $16, $11, $0, $cd, $21, $b8, $0, $4c, $cd, $21, $48, $65, $6c, $6c, $6f, $20, $77, $6f, $72, $6c, $64, $2c, $20, $49, $27, $6d, $20, $31, $36, $62, $69, $74, $20, $44, $6f, $73, $20, $41, $73, $73, $65, $6d, $62, $6c, $79, $20, $21, $21, $21, $24, $9f, $9c, $5, $0, $c4, $8, $54, $1, $3e, $8a, $2, $0, $0, $74 

.data
.bss

! 0 errors detected


Jetzt stehe ich vor einem anderen Problem. Nämlich. Wo ist bei einer EXE-Datei die Einsprungsadresse. Diese muss gefunden werden und zwar nicht vom Segment, sondern vom Offset. Und dazu fällt mir folgende Lösung ein:

So, meine Damen und Herren. Ich habe des Rätsels Lösung gefunden. Wir waren ja jetzt so weit: Wir haben gesagt: EXEC lädt eine ausführbare Datei in den Arbeitsspeicher. Danach wird ein Sprung zu der Einsprungsadresse in das Programm gemacht. Wenn das Programm aber an eine bestimmte Stelle in den Arbeitsspeicher geladen wird und dann ausgeführt wird, warum dann nicht zu Testzwecken, gleich den Programmcode in den Assembler-Kernel einfügen? Zu Testzwecken ersparen wir uns das Laden des Programms in den Arbeitsspeicher und bringen den Binärcode des Programms gleich in den Kernel ein. Und springen dahin. Dazu haben wir die ausführbare Datei in eine DOS-EXE-Datei übersetzt und den Code in dem Kernel angebracht.

Jetzt war die Frage: Wo ist in einer EXE-Datei der erste Programmcode der ausgeführt wird? Und jetzt ist die Antwort einfach. Erinnern wir uns an das FAT-System: Wir haben irgendein FAT-Dateisystem implementiert. Warum machen wir das nicht mit der EXE ? Die Frage ist, in welches Format sollen wir denn den Programmcode des Programms übersetzen? Ganz einfach: Wie auch den Kernel in "bin". In Binärcode. Wenn wir das machen, haben wir den reinen Binärcode. Nun können wir diesen an die Adresse einfügen. Allerdings, wenn wir den Binärcode einfügen, dann können wir auch gleich den Assembler-Code einfügen.

Und, wenn wir dann an später zurückdenken, dann können wir einfach an eine Adresse im Arbeitsspeicher, die durch ein Label bezeichnet ist, das Programm laden. Und dieses Programm wird mit einem Assembler nicht nach EXE oder in eine Linux-ausführbare-Datei übersetzt, sondern in Binärcode. Und an die Stelle, an der jetzt das Programm direkt in den Kernel eingefügt wurde, wird nachher das Programm geladen.

Zwei Probleme stellen sich uns da in den Weg. Zum einen könnte man der JMP -Anweisung in den Code auch über eine CALL -Anweisung nachdenken. Das zweite ist: Die JMP-Anweisung meldet "out of border" oder bound oder wie auch immer. Das ist es gerade: Aber wir können eine FAR-JMP -Anweisung machen: Wie geht das? Na, wir geben das Segment einfach an:

JMP 0x0400:LabelZumAusfuehrbarenCode.

Die Frage ist, was ist das Segment, bei dieser FAR-JMP -Anweisung: Ganz einfach: Das Code-Segment, in dem auch der Kernel ist. Denn der Programmcode, der nachher von der Platte geladen wird, der steht ja jetzt an einer Einsprungsadresse, die im Kernel ist. Nachher wird an dieser Einsprungsadresse ein leerer Buffer stehen, in den das Programm geladen wird.
Wenn wir die Anweisung

JMP 0x0400:LabelZumAusfuehrbarenCode.

funktioniert das mit dem Sprung. Und: Bei diesem Sprung ist zu berücksichtigen: Er arbeitet nicht mit Variablen Werten. Das heißt: Das Programm steht immer an derselben Stelle, sowohl was Offset betrifft, als auch was Segment betrifft.
Und: Etwas ist noch zu berücksichtigen: Das Code und Datensegment. Aber wir sind ja im Kernel. Also bleibt das Code-Segment dasselbe. Dann ist die Frage, warum JMP 0x0400:LabelZumAusfuehrbarenCode.
Ganz einfach: Wir brauchen einen FAR-Jump.
Das zweite ist das Datensegment. Aber wieder: Wir sind ja im Kernel. Aber dem nicht genug: Der Kernel verwendet ja bereits kein Extra-Datensegment. Der Kernel verwendet dasselbe Datensegment. Dasselbe gilt auch für das Programm, dass dann im Kernel steht.
Umgekehrt: Es ist mir bereits gelungen, so ein Programm aus zu führen. Es ist über mov ah, 0x4c int 0x21 bereits zurückgesprungen. Es wurde immer wieder main aufgerufen. Das lag daran, dass ich mov ah, 0x4c int 0x21 so implementiert habe, dass es in die main-Funktion zurückspringt. Was noch nicht geklappt hat, war die Ausgabe von einer Zeichenkette vom Programm aus.

Wir übersetzen die Datei einfach nicht eine EXE-Datei, sondern wir verwenden für unser Betriebssystem bin-Dateien.

...  
  jmp 0x0400:_exec_program_code
...
_exec_program_code: 
        mov   ah,#0x09
        mov   dx, #message
        int   #0x21
        
        mov   ax,#0x4c
        int   #0x21

message: .ascii "Hello world, I'm 16bit Dos Assembly !!!" 
.byte 0   

.data
.bss

! 0 errors detected


main0005.s

Und, weiter geht es, ...

http://www.talkortell.de/os4.html

http://www.talkortell.de/os5.html


next up previous
Nächste Seite: Betriebssystem - Generelles Aufwärts: Index Vorherige Seite: Index
David Vajda 2019-03-01