Verständnisproblem mit dynamischem Speicher
-
Hallo zusammen
Ich habe nun erste Erfahrungen mit Assembler sammeln können, habe aber nun so einige Fragen in Bezug auf die Speicherverwaltung!Frage 1:
Ich möchte eine verkettete Liste in Assembler implementieren. Aber wie kann ich denn das machen, ich meine, es gibt ja keine Funktion nahmens malloc oder etwas vergleichbares? Wie kann ich zur Laufzeit mehr Speicher anfordern?Frage 2:
Wenn ich Variablen in einem Datensegment habe, dann füllt der Assembler diese mit nullen! Nun habe ich aber viele Hilfsvariablen, welche erst zur Laufzeit gefüllt werden und ich möchte nicht, dass durch diese das Programm unnötigerweise aufgebläht wird.In einem anderen Thread habe ich folgendes gelesen:
Der Speicherbereich zwischen 07C00 und 98000 steht dir zur freien Verfuegung. Wo du fuer dein Programm Code, Daten und Stack hinlegst, haengt allein von dir, bzw. (sofern vorhanden) auch dem DOS ab. Da du dein Programm scheinbar ohne irgendein DOS praktisch als eigenes OS konzipierst, solltest du schon grob wissen, wie sich dein Programm ueber den Speicher verteilt, und wo da noch Platz ist. Dein PONG wird doch wohl kaum die ganzen unteren 640kByte einnehmen.
Also suchst du dir entweder auf diese Weise selbst einen freien Speicherbereich, oder laesst deinen Assembler das machen, indem du ein extra Datensegment definierst und da ein 64kByte grosses Array reinpackst.a) Was liegt denn unterhalb von 07c00h bzw. über 98000h ? Gibs vielleicht irgendwo Resourcen im Internet, wo die Speicherverteilung der 8086 Architektur beschrieben ist?
b) Wenn ich ein extra Datensegment mache und dort ein Array reinpacke, dann ist das Programm anschliessend 64K grösser! Kann ich das nicht verhindern?
c) Was ich gar nicht verstehe: jedes Assemblerprograam braucht doch dringend ein Codesegment und da ich nicht wissen kann, wie gross das ist, kann man doch nicht einfach pauschal sagen, dass ich zwischen 07c00h und 98000h alles benutzen darf, weil sich jenes Codesegment ja IRGENDWO in diesem Speicherfeld angelegt ist? Ähnlich verhält es sich doch mit dem Stacksegment, wenn ich einfach sage ".STACK 256" dann habe ich doch keine Ahnung, wo dieser Stackspeicher liegt?
Ich weis, dass sind sehr viele Fragen, aber es sind alles Fragen, welche mir unser Prof. nicht beantworten konnte!
Mfg Ishildur
P.S.
Alle diese Frage beziehen sich auf 16-Bit Assembler, also den Realmode!
-
Hi.
Ishildur schrieb:
Frage 1:
Ich möchte eine verkettete Liste in Assembler implementieren. Aber wie kann ich denn das machen, ich meine, es gibt ja keine Funktion nahmens malloc oder etwas vergleichbares? Wie kann ich zur Laufzeit mehr Speicher anfordern?Das ist Aufgabe des Betriebssystems. Jedes Betriebssystem bietet verschiedene "malloc"-Funktionen an, die du benutzen kannst (die letztendlich wohl auch C, etc. benutzen). Wenn du kein Betriebssystem hast, musst du dir eben selbst ueberlegen, wie du deinen Speicher dynamisch verwaltest. Letztendlich ist das im RealMode nichts weiter als ein ~640kByte grosses Array... Aber bevor wir dieses komplexe Thema anschneiden, will ich doch nochmal sicher gehen, ob du wirklich ohne OS arbeitest.
Ishildur schrieb:
Frage 2:
Wenn ich Variablen in einem Datensegment habe, dann füllt der Assembler diese mit nullen! Nun habe ich aber viele Hilfsvariablen, welche erst zur Laufzeit gefüllt werden und ich möchte nicht, dass durch diese das Programm unnötigerweise aufgebläht wird.Jeder x86-Assembler, den ich kenne, bietet eine Moeglichkeit uninitialisierten Speicher zu reservieren. Je nach Programmformat musst du dir dazu natuerlich ueberlegen, dass uninitialisierter Speicher idR. nur hinter den letzten initialisierten Daten folgen kann. Alle uninitialisierten Daten, auf die initialisierte Daten (oder eben Code) folgen, werden mit 0 aufgefuellt.
Ishildur schrieb:
In einem anderen Thread habe ich folgendes gelesen:
[...]
a) Was liegt denn unterhalb von 07c00h bzw. über 98000h ? Gibs vielleicht irgendwo Resourcen im Internet, wo die Speicherverteilung der 8086 Architektur beschrieben ist?Unter 07C00h liegt die BiosDataArray (Uhrzeit, Tastaturpuffer und verschiedenster Schlunz vom BIOS) und die InterruptVectorTable (Far pointer auf die 256 Interrupt-Handler).
Ueber 98000h liegt uA. Grafikspeicher und ansonsten meist BIOS shadows - nichts, wo man einfach so reinschreiben will...
Und ja: Dazu gibt's auch einige Tabellen. Habe aber gerade auch nichts zur Hand, deshalb kann ich nur auf die FAQ - OSDev-Links oder die Suchmaschine deiner Wahl verweisen.Ishildur schrieb:
b) Wenn ich ein extra Datensegment mache und dort ein Array reinpacke, dann ist das Programm anschliessend 64K grösser! Kann ich das nicht verhindern?
Siehe oben. Pass auf, wie du deine Segmente und die Daten darin anordnest. Die Reihenfolge der Definition spielt durchaus eine Rolle!
Ishildur schrieb:
c) Was ich gar nicht verstehe: jedes Assemblerprograam braucht doch dringend ein Codesegment und da ich nicht wissen kann, wie gross das ist, kann man doch nicht einfach pauschal sagen, dass ich zwischen 07c00h und 98000h alles benutzen darf, weil sich jenes Codesegment ja IRGENDWO in diesem Speicherfeld angelegt ist?
Doch, kannst du pauschal schon so sagen. Wenn du dabei den gerade laufenden Code plattsaebelst: selbst schuld.
Hindert dich aber keiner dran.Ishildur schrieb:
Ähnlich verhält es sich doch mit dem Stacksegment, wenn ich einfach sage ".STACK 256" dann habe ich doch keine Ahnung, wo dieser Stackspeicher liegt?
Daran solltest du besser was aendern. Das spielt naemlich vor allem eine zentrale Rolle, wenn du kein OS hast, das die Speicherverwaltung fuer dich uebernimmt.
Wenn es aber wirklich mal noetig werden sollte, weil du die Adresse deines Stacks und Codes nicht kennst, diese zur Laufzeit zu bestimmen, kannst du das ueber cs und ss und geschickt im Quellcode platzierte Labels angehen (zB. am Anfang und Ende des Codes, usw.).
-
@NabuoT
Erst mal vielen Dank für deine Zeit!
Ich denke, das meisste habe ich verstanden, allerdings etwas bleibt mir nach wie vor schleierhaft:Wenn ich nun ein Code, Stack sowie mehrere Datensegmente habe, dann wird ja ein bestimmer des mir zur Verfügung stehenden Speichers zwischen 07c00h und 98000h für jene Segmente benutzt. Wenn ich nun also zur Laufzeit zusätzlichen Arbeitsspeicher benötige, woher kann ich denn nun wissen, welcher Teil zwischen 07c00h und 98000h noch benutzt werden darf? Der Stack bspw. startet ja AFAIK bei der höchsten Adresse und breitet sich anschliessend in die tieferen Adressen aus. Kann es also sein, dass der Stack bei 98000h anfängt? Dann könnte ich das ESP Register abfragen, um zu wissen, wo die obere Grenze liegt. Doch woher kann ich nun wissen, wo die Untere Grenze liegt?
x_unten = 07c00h+Codesegment+3Datensegmente
x_oben = ESPP.S.
Ja, ohne Betriebssystem
-
Nobuo T schrieb:
Ishildur schrieb:
Frage 1:
Ich möchte eine verkettete Liste in Assembler implementieren. Aber wie kann ich denn das machen, ich meine, es gibt ja keine Funktion nahmens malloc oder etwas vergleichbares? Wie kann ich zur Laufzeit mehr Speicher anfordern?Das ist Aufgabe des Betriebssystems. Jedes Betriebssystem bietet verschiedene "malloc"-Funktionen an, die du benutzen kannst (die letztendlich wohl auch C, etc. benutzen).
Das Betriebssystem ist zwar letztendlich dafür zuständig, einem Prozess Speicher zur Verfügung zu stellen, aber nicht in der Granularität, die man z.B. für eine verkettete Liste benötigt. malloc macht auch nicht jedesmal einen Systemaufruf. Das Zerteilen der vom OS zugewiesen Speicherbrocken in mundgerechte Stücke ist Aufgabe des Programms bzw. wenn man z.B. in C programmiert der Standardlibrary. Wenn man so eine Bibliothek nicht zur Verfügung hat, wird man sich wohl selbst was über Allokationsalgorithmen anlesen müssen.
-
Ishildur schrieb:
@NabuoT
(irgendwie scheint mein Name echt schwierig zu lesen zu sein - *gg)
Ishildur schrieb:
Wenn ich nun ein Code, Stack sowie mehrere Datensegmente habe, dann wird ja ein bestimmer des mir zur Verfügung stehenden Speichers zwischen 07c00h und 98000h für jene Segmente benutzt. Wenn ich nun also zur Laufzeit zusätzlichen Arbeitsspeicher benötige, woher kann ich denn nun wissen, welcher Teil zwischen 07c00h und 98000h noch benutzt werden darf? Der Stack bspw. startet ja AFAIK bei der höchsten Adresse und breitet sich anschliessend in die tieferen Adressen aus. Kann es also sein, dass der Stack bei 98000h anfängt? Dann könnte ich das ESP Register abfragen, um zu wissen, wo die obere Grenze liegt. Doch woher kann ich nun wissen, wo die Untere Grenze liegt?
Ich gehe mal davon aus, dass du fuer einen PC programmierst?
Wenn du ohne OS arbeitest, wird dein 512Byte Bootsektor vom BIOS nach 7C00h (wie diese phys. Adresse im RM sich auf Segment:Offset verteilt kann je nach BIOS verschieden sein!) geladen. Wenn du einen anderen Bootloader benutzt evtl. auch woanders hin - das musst du der Doku entnehmen.
Am Anfang deines Codes ist im Grunde alles undefiniert. Das Einzige was sicher ist, sind die Funktionen des BIOS, und die Adresse deines Codes.
Zuerst wirst du deinen Stack anlegen muessen - dessen Adresse ist am Anfang naemlich auch undefiniert. Praktischerweise wirst du den vielleicht erstmal zB. nach 7C00:0000 (ber erste gepushte Wert wird dann bei 7C00:FFFE landen) packen. Da du weisst, dass dein Code bei 07C00-07E00 liegt, hast du dann also 512 Byte Stack zur verfuegung - von 07E00-08000.
Nun wirst du als naechstes wohl den Rest deines Codes nachladen wollen. Den packst du dann vielleicht nach 08000 bis (wie gross auch immer dein Code evtl. mit Datensegmenten wird) 18000. Wenn du dann in deinen nachgeladenen Code springst, kannst du deinen von BIOS zuerst geladenen Code ueberschreiben lassen und hast dann 1024Byte Stack. ... usw.
Also wie gesagt: Haengt wirklich alles von dir ab, es gibt keine Vorgaben.Ishildur schrieb:
x_oben = ESP
Im RealMode wird allerdings nur SP zum adressieren benutzt.
Bashar schrieb:
Das Betriebssystem ist zwar letztendlich dafür zuständig, einem Prozess Speicher zur Verfügung zu stellen, aber nicht in der Granularität, die man z.B. für eine verkettete Liste benötigt.
Naaaja, praktisch vielleicht nicht dazu gedacht, aber moeglich waere es (notfalls halt mit etwas Speicherverschnitt).
Mal ganz davon ab, wie genau die Listenelemente aussehen, wie gross sie sind, bzw. ob es in diesem Szenario ueberhaupt Sinn macht, ein in Hochsprachen idR. doch eher abstrakt verwendetes Konstrukt wie eine verkettete Liste zu benutzen...Bashar schrieb:
Wenn man so eine Bibliothek nicht zur Verfügung hat, wird man sich wohl selbst was über Allokationsalgorithmen anlesen müssen.
Da stimme ich zu. Ohne OS macht das wohl erst recht Sinn. Dazu muesste sich eigentlich auch noch was in der OS-Dev-Ecke finden.
Ansonsten sind neue gute Links natuerlich auch gern gesehen. :xmas1:
-
@Nobuo<whitespace>T :p
Ok, das habe ich glaube ich kapiert! Nun nur noch 2 Dinge: Mir ist noch überhaupt nicht klar, wie ich in den anderen Code springen kann.
Ich habe 2 Dateien: boot.bin sowie pong.bin. Diese habe ich mit MagicIso in ein Image gepackt und auf eine Diskette geschrieben.
Nun weiss ich ja, dass das BIOS den Code aus boot.bin in die Adresse 07c00h kopiert ok! Aber doch bloss die 512 Bytes des ersten Sektors. Die restlichen Daten befinden sich doch noch auf der Diskette und nicht im Arbeitsspeicher?! Wie kann ich dann einfach in dahin springen?2. Ich habe da noch ein Problem: Es ladet nicht einmal dieser verflixte Bootloader! Dies ist mein code:
ORG 7C00h start: mov ax,03h int 10h mov ah,0eh mov al,'S' int 10h jmp $ TIMES 510-($-$$) DB 0 DW 0AA55h
Aber es kommt immer folgender Fehler:
Disk formatted with MagicISO 4.70 (c) 2001-04 MagicISO, Inc.
Bootsector form C.H. HochstätterNo Systemdisk. Booting from harddisk
Also ich bin schon sehr überrascht, wie es möglich ist, dass an dieser Stelle Text von MagicIso steht?! Das kann doch eigentlich nur bedeuten, dass MagicSchrott da seinen eigenen Bootloader reingehackt hat?! Ein sehr ähnliches Problem hatte ich min WinImage! Gibt es irgendeine Software, die einfach mal das tut, was sie soll?
-
Ich habe das fertige Image mit einem Hex-Editor analysiert! Dieses Scheiss MagicIso überschreib meinen Programmcode komplett! :p
-
Ishildur schrieb:
Nun weiss ich ja, dass das BIOS den Code aus boot.bin in die Adresse 07c00h kopiert ok! Aber doch bloss die 512 Bytes des ersten Sektors. Die restlichen Daten befinden sich doch noch auf der Diskette und nicht im Arbeitsspeicher?! Wie kann ich dann einfach in dahin springen?
Wie ich bereits schrieb, musst du den restlichen Code natuerlich zuerst von der Diskette laden, bevor du reinspringen kannst.
Dazu bietet es sich an, die BIOS Funktionen des int 13h zu benutzen - weitere Infos in FAQ, Forensuche, Internetsuche ... gibt wirklich genug zu dem Thema - auch Beispielcodes.Ishildur schrieb:
2. Ich habe da noch ein Problem: Es ladet nicht einmal dieser verflixte Bootloader! Dies ist mein code:[...]
Dazu sei noch angemerkt, dass auch Interrupt-Aufrufe den Stack verwenden. Wenn du keinen eingerichtet hast, kann das uU. zum Crash fuehren. Daher sollte wirklich das Erste, was du in deinem Bootloader machst, das Einrichten des Stacks und der Segmentregister sein.
Ishildur schrieb:
Dieses Scheiss MagicIso überschreib meinen Programmcode komplett!
Versuch's mal mit RawRite o.Ae.? Das sieht ueberhaupt so aus, als wuerdest du versuchen, ein von CD bootbares Diskettenimage zu fabrizieren.
Dabei sollte dir schon klar sein, dass ein CD-ROM keine Diskette ist und entsprechend auch komplett anders funktioniert. Mit einschlaegigen ISO-/Brennprogrammen fabrizierte Images verwenden daher einen speziellen Bootloader, der ein Diskettenlaufwerk simuliert und dann deinen Code spaeter nachlaedt.
-
RawWrite kann AFAIK eben nur fertige Images auf Disketten schreiben oder ein Image aus einer bestehenden Diskette erzeugen! Was ich aber doch brauche ist ein Programm, welches ein neues Image erstellen kann?! Mehrere Dateien als Input, ein Imagefile als Output?! Ich bin nun seit Stunden nach so einem Programm am suchen, aber ohne Erfolg!
-
Ehrlich gesagt verstehe ich nicht ganz, was du vor hast, wozu du ein tolles Programm zum Image-Erstellen brauchst?
Entweder linkst du deine Dateien geschickt zusammen, oder du haengst sie einfach per HexEditor (oder wenn gewusst wie mit dem copy-Befehl) hintereinander. Die so erzeugte Datei kopierst du mittels Rawrite auf Diskette - fertig.
Selbst deinen 512Byte grossen Bootloader kannst du wie er ist mittels Rawrite in den Sektor 0 einer Diskette schreiben, um ihn auszuprobieren. Dabei rate ich allerdings zur Vorsicht, da du dir so deine wertvollen Disketten zerstoeren kannst.
Zum ersten Rumspielen empfehle ich daher sowas wie bochs. Dem kannst du AFAIR auch deinen 512Byte Bootloader als Disketten-Image verfruehstuecken.
-
Hmmm... Wieso können denn dabei die Disketten kaputtgehen?
-
AFAIK: Weil der Bootsektor normalerweise in den ersten Bytes auch Informationen ueber die Diskettengeometrie enthaelt (leider gerade wieder kein Beispiel zur Hand). Meiner Erfahrung nach reagieren viele Systeme nicht gut darauf, wenn diese zB. mit deinem Code ueberschrieben werden.
-
Und wie kann ich denn das verhindern?
2. Ich habe mir inzwischen mal die Speicheraufteilung angesehen:http://www.in4mation.de/services/memory.html#konventionellerspeicher
Da ist nichts davon zu erkennen, dass sich im Speicher unterhalb von 07c00h irgendetwas vom BIOS befinden würde. Ausserdem ist daraus ersichtlich, dass der Bildschirmspeicher bei a0000h anfängt, wieso also kann ich den Speicher nur bis zur Adresse 98000h benutzen?
Ach ja noch was, wieso kann das Programm überhaupt funktionieren, ich habe doch gar kein Codesegment definiert, aber aus irgendeinem Grund funktionierts trotzdem!
Ich verwirrt!
-
Ishildur schrieb:
Und wie kann ich denn das verhindern?
Indem du diese Daten in deinen Bootsektor einbaust. Schau dir mal hier den Teil zum BPB an (und den Rest vielleicht auch noch
).
Ishildur schrieb:
2. Ich habe mir inzwischen mal die Speicheraufteilung angesehen:http://www.in4mation.de/services/memory.html#konventionellerspeicher
Da ist nichts davon zu erkennen, dass sich im Speicher unterhalb von 07c00h irgendetwas vom BIOS befinden würde.
KA, warum das da nicht drin steht, aber es ist so.
Die BDA geht auch nicht unbedingt genau bis 7C00h - wenn du darunter aber einfach nichts rumschreibst, ohne genau zu wissen was du tust, bist du auf der sicheren Seite.Ishildur schrieb:
Ausserdem ist daraus ersichtlich, dass der Bildschirmspeicher bei a0000h anfängt, wieso also kann ich den Speicher nur bis zur Adresse 98000h benutzen?
Wieder gilt: Vorsicht ist die Mutter der Porzellankiste.
Duerfte aber eigentlich auch bis A0000 problemlos klappen. Musst da oben nur mit der Segmentierung aufpassen, damit du nicht aus versehen in den Grafikspeicher kommst.
-
Hmmmm. Irgendetwas klappte mit meinem Stack nicht! Wie muss ich den Stackpointer initialisieren?
Im Internet habe ich folgendes gefunden:
Danach startet der eigentliche Bootloader. Zuerst basteln wir uns einen Stack, dessen Adresse wir auf 0x9000 legen. Den Stackpointer setzen wir dabei auf 0. Während wir unseren Stack zusammenbauen, dürfen wir KEINE Interrupts verwenden!
Code:
start:
cli ; Keine Interrupts verwenden!
mov ax, 0x9000 ; Adresse des Stack speichern
mov ss, ax ; Stackadresse festlegen
mov sp, 0 ; Stackpointer auf 0 setzen
sti ; Jetzt lassen wir wieder Interrupts zu[/quote]
Ich habe das ein wenig abgeändert:
cli ; disable all interrupts as there is still no stack available mov ax,MEM_STK ; copy the address of the stack into ax mov ss,ax ; copy ax into the stacksegment xor sp,sp ; init the stack pointer by zero sti ; enable all interupts
Also, leider funktioniert dass nicht, sobald ich xor sp,sp oder mov sp,00h mache, startet das Programm anschliessend nicht mehr! Was ich sowieso nicht so richtig verstehen kann: Ist es nicht so, dass der Stackpointer dekrementiv ist, müsste man diesen dann nicht wennschon mit bspw. 512 initialisieren?
Lg Ishildur
-
Ishildur schrieb:
Hmmmm. Irgendetwas klappte mit meinem Stack nicht! Wie muss ich den Stackpointer initialisieren?[...]
Was ich sowieso nicht so richtig verstehen kann: Ist es nicht so, dass der Stackpointer dekrementiv ist, müsste man diesen dann nicht wennschon mit bspw. 512 initialisieren?Habe ich eigentlich auch schon geschrieben: Stimmt, aber 0 - 2 ist im 16Bit-Register FFFE. Dahin wird auch der erste Wert dann gespeichert.
Warum dein Code nicht startet, kannst du dir zB. im Bochs debugger anzeigen lassen. Ansonsten kann ich auch nur raten - welchen Wert hat denn zB. MEM_STK?Noch eine Anmerkung: AFAIK wird beim Setzen von SS automatisch das Interrupt-Flag bis zur Ausfuehrung des uebernaechsten Befehls geloescht. Ein cli/sti um die 2 Befehle zur Stack-Einrichtung ist also unnoetig.
-
Hmmm, OK, das Problem mit dem Stack konnte ich lösen, ich dachte eben, dass die CPU den Stackpointer vom definierten Stackpointer herunterzählt...
Irgendwie kriege ich das mit der Diskette lesen nicht gebacken! Woher weiss ich denn, von welchem Track und aus welchem Sektor ich lesen muss? Ist die Sektorgrösse nicht von der Formatierung abhängig?
Ausserdem, wenn ich nun 15 KBytes auslesen, will, woher weiss ich dann wieviele Tracks und Sektoren dass sind?Gibts vielleicht irgendwo ein gutes Tutorial in Bezug auf das lesen von Diskette ohne Dateisystem?
Lg Ishildur
-
Ishildur schrieb:
Irgendwie kriege ich das mit der Diskette lesen nicht gebacken! Woher weiss ich denn, von welchem Track und aus welchem Sektor ich lesen muss?
Du weisst, in welcher Reihenfolge die Daten/Programmteile in deinem Image liegen. Weisst du das nicht, solltest du dir darueber erstmal klar werden.
Fuer Disketten wird dann AFAIK immer die chs-Adressierung verwendet...
Wenn du dir dazu noch den Artikel ueber LBA durchliest, wird die Sache hoffentlich klarer... Die Werte fuer die Diskettengeometrie stehen im BPB.Ishildur schrieb:
Ist die Sektorgrösse nicht von der Formatierung abhängig?
Nein. Von der Laufwerksgeometrie. AFAIK ist das aber eigentlich immer mit der Blockgroesse gleichzusetzen, welche eigentlich auch immer 512 Byte betraegt (mir ist zumindest keine Ausnahme bekannt).
Ishildur schrieb:
Ausserdem, wenn ich nun 15 KBytes auslesen, will, woher weiss ich dann wieviele Tracks und Sektoren dass sind?
Teile die 15kiloByte durch die Blockgroesse. Die so gewonnene Anzahl von Bloecken kannst du mit Hilfe der Laufwerksgeometrie-Daten und der Umrechnungsformel LBA/chs von Wikipedia weiter umrechnen.
Ishildur schrieb:
Gibts vielleicht irgendwo ein gutes Tutorial in Bezug auf das lesen von Diskette ohne Dateisystem?
Kenne keines. Vielleicht bei den OS-Dev-Links.