Mode X und EGA Parrallax

  • ...genau mein Programm schnappt einen Handle und reserviert einfach einen 4MB Block.

    Dann starte ich Borland, das schnappt sich alles was zu bekommen ist.

    Jetzt muss man nur noch mit dem 1. Programm was man startet den speicher Handle wieder frei geben und ich habe 4MB XMS wieder frei.

  • ich habe heute einen ersten Teslauf auf dem 386 gemacht mit einem Testprogramm.


    Das Testprogramm ist das Spiel ...wobei bei jedem Frame zusätzlich noch 2 Texturen 2048 Byte vom XMS in den unteren Speicher bereich kopiert werden. Ich hätte hier jetzt eigentlich mit frame einbrüchen gerechnet zumal das Programm normalerweise 36-37 FPS macht wenn kein NPC durchs Bild läuft und der Char steht... was soll ich sagen, wenn ich bei jedem Frame die 2 Texturen im XMS verschiebe verliere ich KEIN FPS!!!

    Also können locker 36-37 Aufrufe an den XMS Manager gesendet werden zum kopieren von jeweils 2 Texturen = 74 Texturen innerhalb einer Sekunde ohne das das System merklich an geschwindigkeit verliert.

    Testrechner war wie fast immer der 386DX40 mit 8MB Ram wovon 1MB mit XMS genutz werden!


    Ich hätte hier schon erwartet das die geschwindigkeit irgendwo einbricht... dem ist nicht so!

  • Jetzt musst du den Sprite-Anzeigecode so lange optimieren, bis der XMS doch zum Flaschenhals wird ;)


    Je nach Compiler ist es auch immer interessant, Profiling zu machen. Dazu kompiliert man das Programm in einer speziellen Profiling-Version, benutzt es eine Weile, und bekommt am Ende Statistiken in eine Datei, wie viel Prozent der Zeit es in welchen Funktionen verbraucht hat und welche Funktionen wie oft aufgerufen wurden :)


    Man kann das Profiling ggf. auf exakt (verlangsamt das Programm evtl. merklich) oder auf Stichproben einstellen. Bei Stichproben wird in regelmässigen Abständen geschaut, welche Funktion in dem Moment läuft, was das Programm während dem Testlauf kaum verlangsamt.

  • Ich werde jetzt wohl erstmal eine Funktionssammlung zusammen stellen, welche ich dann Programmübergreifend nutzen kann.

    Danach muss ich dann schauen das ich den MAP Editor anpasse damit ich dann Texturen frei wählen kann nach lust und laune.

    Und dan brauche ich noch Funktionen die den Speicher verwalten in dem Texturen innerhalb der unteren 640kb abgelegt werden die ständig genutzt werden.


    Mir wird nicht langweilig...

  • Der Compiler ärgert mich mal wieder wo er nur kann.


    ich muss einer Funktion eine Variable übergeben damit die Funktion auf dessen Adresse zugreift.


    Wenn ich das über Pointer versuche, wird die Adresse der Pointer übergeben.

    Kann machen was ich will es Funktioniert nicht.


    Ich bin jetzt so weit das ich den Adresswert als HEX oder dezimal übergebe, weil es anders komplett unmöglich erscheint.


    Nur finde ich bei der Lösung keinen Wert die Adresse einer Variable in einer anderen Variable als unsigned long Wert zu übergeben.




    mir fällt einfach keine Lösung ein und ich finde im Netzt absolut garnichts wie man an die Adresse als zahlen Wert kommen kann.

  • habe ich noch nicht gemacht... c++ ist halt ziemliches Neuland für mich.


    ...hatte gestern 6 Stunden an dem Code gebastelt und am ende dann festgestellt das der Compiler sich irgendwo was verstellt hatte und deshalb immer alles abstürzte.


    Wenn du ein Beispiel hast immer gerne her damit.. sonst muss ich wieder suchen.

  • Wenn du ein Beispiel hast immer gerne her damit.. sonst muss ich wieder suchen

    Sieht man in meinen Videos. Kurz gesagt: MK_FP(seg, off) baut dir einen long pointer aus Segment und Offset, z.B. wenn ein INT dir das in ES:CX oder so zurückliefert. Mit FP_SEG(pointer_var) und FP_OFF(pointer_var) wiederum kriegst du Segment und Offset aus einer deiner far pointer Variablen. Die kannst du dann "normal" in deine Register laden. Beide Makros müssen im C++ Teil sein.

    root42 auf YouTube


    80486DX@33 MHz, 16 MiB RAM, Tseng ET4000 1 MiB, GUSar Lite & TNDY & SnarkBarker, PC MIDI Card + SC55 + MT-32, XT CF Lite, OSSC 1.6

  • Ich bin dann mal so frei.


    Ich habe jetzt soweit alle wichtigen Routinen in diese Funktions Sammlung gesteckt.


    Damit kann man alles wichtige machen.


    Unten in der Main ist dann ein Beispielprogramm welches folgendes macht.


    XMS Treiber prüfen

    XMS Manager Adresse abfragen


    XMS Handle reservieren


    Die ersten 10 Byte des Char Array Text1 in den reservierten XMS Handle kopieren

    Die 10 byte vom reservierten XMS Handle kopieren in das Char Array Text2


    Den reservierten XMS handle wieder frei geben


    ENDE!


    Man kann das Programm so nicht von den meisten compilern starten, weil diese während der Ausführung meist selber den kompletten speicher reservieren!


    ...ich umgehe das Problem indem ich vor dem Ausführen XMS reserviere und dann vom compiler aus den Handler mit einem toll wieder frei gebe, dann kann man auch vom compiler aus den Code ausführen.

    ...testen und experimentieren auf eigene Gefahr.

    Einmal editiert, zuletzt von Markus () aus folgendem Grund: ..kleines Code update Funktion XMS(Kbyte) ergänzt

  • Jo Borland ist halt Borland.

    Die hatten immer eine brauchbare IDE und auch sonst läuft der compiler schön stabil.


    Wenn man XMS / EMS nutzen will, muss man vorm start einfach reservieren... wüsste jetzt nicht das man den compiler mit hausmitteln daran hindern kann einfach alles beim start zu reservieren was frei ist.


  • Man kann das Programm so nicht von den meisten compilern starten, weil diese während der Ausführung meist selber den kompletten speicher reservieren!

    Du meinst nicht Compiler, sondern du meinst glaube ich IDE? Und du meinst nicht die meisten, du meinst nur die von Borland, nehme ich an? ;)

  • das hatte ich gestern schon versucht, irgendwie reserviert er immer alles egal was ich da eingebe.



    Momentan ärgert mich auch der Code ein wenig, der umbau vom Map Editor funktioniert bisher reibungslos. Aber im Spiel wird der Inhalt der Variable XMShandle ab und an verändert obwohl es keinen befehlt gibt der das macht.

    Das Problem tritt immer bei kopieren auf, wenn die Quelladresse gewisse Werte hat.


    Wenn ich als Quelle den richtigen Handle wähle und als ersten Byte im Speicher 142614 >> müsste dann Offset 11542 / Segment 2 sein. Dann tritt der Fehler auf. Bei niedrigeren Adressen und teilweise bei höheren tritt der Fehler nicht auf.

    Merkwürdig das der Fehler auftritt wenn ich die Quelladresse ändere, die Zieladresse bleibt immer gleich. Also dürfte ja eigentlich nichts überschrieben werden, weil ich ja nur wo anders lese.


    der Reservierte XMS Speicher ist 1,5MB groß wovon lediglich 300kb belegt sind.


    Merkwürdig ist, wenn ich die Variable XMShandle später initialisiere (ein Array von 909 Chars habe ich jetzt einfach davor zuteilen lassen) dann tritt der Fehler nicht auf.

    Hierdurch ändert sich ja die Adresse der Variable... also scheint irgendwas die Adresse gezielt zu überschreiben.


  • Wenn ich als Quelle den richtigen Handle wähle und als ersten Byte im Speicher 142614 >> müsste dann Offset 11542 / Segment 2 sein. Dann tritt der Fehler auf. Bei niedrigeren Adressen und teilweise bei höheren tritt der Fehler nicht auf.

    Merkwürdig das der Fehler auftritt wenn ich die Quelladresse ändere, die Zieladresse bleibt immer gleich. Also dürfte ja eigentlich nichts überschrieben werden, weil ich ja nur wo anders lese.


    Merkwürdig ist, wenn ich die Variable XMShandle später initialisiere (ein Array von 909 Chars habe ich jetzt einfach davor zuteilen lassen) dann tritt der Fehler nicht auf.

    Hierdurch ändert sich ja die Adresse der Variable... also scheint irgendwas die Adresse gezielt zu überschreiben.

    Also zunächst mal... wenn du XMSBlock[142614] zugreifen willst, ist 142614 einfach ein 32-bit Offset. Die obere Hälfte ist dann kein Segment, sondern nur die oberen 16 Bit vom 32-bit Wert.


    Ich zitiere mal aus RBIL, Int 2F Funktion 4310 etc. XMS:



    Interessanterweise sind die real-mode Adressen dabei allerdings normale Far Pointer. Wenn du also Arbeitsspeicher[142614] zugreifen willst, also Arbeitsspeicher[0x22d16] dann wäre das Handle 0 und "Offset" 0x22d10006 damit es als Far Pointer 0x22d1:0x0006 interpretiert wird. Wie du den Pointer "normalisierst" ist dabei dir überlassen, "Offset" 0x20002d16 zeigt auf die gleiche Speicheradresse.


    XMSBlock[142614] musst du andererseits als Handle XMSHandle, Offset 0x00022d16 ansprechen, also untere 16 Bit 0x2d16, obere 16 Bit 0x0002.


    Es könnte auch interessant sein, deinem Programm einen Nicht-Grafik-Testmodus zu geben, bei dem der PC im Textmodus bleibt (die Zugriffe auf den Grafikspeicher auf 0xa000:nnnn laufen dann halt ins Leere) damit du viele wissenswerte Debug-Meldungen über printf ausgeben kannst. Zum Beispiel die Parameter der XMS-Zugriffe, um zu sehen, ob sie deinen Erwartungen entsprechen.


    Sich versehentlich irgendwelche Speicherinhalte zu zerstören ist leider in C-Programmen ein häufiges Problem. Du könntest zwar Breakpoints setzen und dann per Debugger Speicherinhalte anschauen oder schrittweise durch die Assembler- bzw. Maschinencode-Befehle gehen, um zu sehen, wo Dinge schiefgehen, aber da verzettelt man sich sehr schnell. Ein paar Zeilen C-Code können sehr viele Zeilen Assembler sein.


    Wenn deine IDE bzw. dein Debugger genügend Power haben, kannst du auch logische Breakpoints setzen. Sowas wie "Programm anhalten und anzeigen, wo im Quelltext es gerade war, sobald der Wert von XMSHandle sich ändert". Das Programm läuft dann zwar sehr langsam, während der Debugger es beobachtet, aber es bleibt dann exakt an der Stelle stehen, an der dein XMSHandle zerstört wird.

  • momentan schaut es so aus.


    Texturen in den speicher laden


    Diese Funktion gibt die kopier anweisung



    Das ist die aufgerufene Funktion. ...es funktioniert halt irgendwie fast immer bei gewissen Adressen schreibt es aber den XMShandle um...


    was komisch ist, ich schreibe immer in die selben Ziel adressen, aber manchmal schreibt er scheinbar wo anders rein.


    Anscheinend tritt der Fehler ab Textur 139 bis 154 auf...danach 155 Funktioniert wieder...alles ziemlich merkwürdig. Ich verstehs nicht wirklich.

    Einmal editiert, zuletzt von Markus () aus folgendem Grund: nachtrag

  • ....wenn ich beim initialisieren der Variable =0 weg lasse dann Funktioniert es ohne Fehler.



    wenn ich im Compiler Register Variables auf none stelle, dann funktioniert es auch.


    Ist das zufall oder hat hier der Compiler meine Variable geschreddert?

  • Ich schlage vor, du lässt XMScopy die tatsächlich empfangenen Werte anzeigen, damit du drüber schauen kannst, ob sich irgendwo irgendwas verrechnet.


    Statt unsigned int könntest du auch einen extra zu dem Zweck definierten uint16 Typ verwenden, um deutlicher zu machen, was wo übergeben wird.


    XMScopy könnte statt ZWEI 16-bit Werten (entweder Segment und Offset im DOS Speicher oder obere und untere 16 Bit des XMS-Offsets) auch EINEN 32-bit Wert annehmen. Far Pointer sind sowieso 32 Bit und der 32 Bit Offset auch. Dann sparst du dir in beiden Fällen die Umrechnung. Noch eleganter wäre eine union, die die gleichen 4 Bytes einmal als 16 Bit DOS Segment und 16 Bit DOS Offset und einmal als einen 32 Bit Offset interpretiert, für bessere Lesbarkeit.


    Code
    unsigned int XMSbl=XMSb;
    unsigned int XMSbh=XMSb>>16;

    Das ist eine eher unleserliche Methode, 32 Bit "XMSb" zu zerstückeln. Lass das lieber an einem 32 Bit Stück. Ich vermisse auch ein XMSbl = XMSb & 0xffff um zumindest explizit zu machen, dass die oberen 16 Bit der 32 Bit Variable bewusst verworfen werden, wenn du die unteren in eine 16 Bit Variable rüberkopierst.


    Es ist natürlich auch ein wenig eine Glaubensfrage, aber willst du dir wirklich den Schmerz mit den vielen 16-Bit Stücken von 32-Bit Variablen antun, damit dein Spiel auf 286 lauffähig bleibt? Wenn du einen 386 als Mindestanforderung nimmst, kannst du bequem 32 Bit am Stück in einem Register wie EAX transportieren. Und auf älteren als 286 PC läuft das Spiel sowieso nicht, weil es dort kein XMS geben kann. Mal ganz abgesehen von der Geschwindigkeit ;)


    Code
    ... (DCa<<16)>>16,DCa>>16, ...

    Auch hier würde ich wieder sagen: Übergib lieber 32 Bit am Stück, statt den DCa (seltsamer Name für "Quelloffset der gewünschten Textur im XMS Block") auf seltsame Weise in zwei 16-Bit Teile zu zerlegen. Und wieso machst du diesmal "32 Bit Variable um 16 Bit nach links schieben, dann wieder 16 Bit nach rechts, dann in 16 Bit Argument kopieren", während du bei XMSbl überhaupt nicht geschoben hast? Anders gesagt, du könntest es so machen:

    Code
    ... DCa & 0xffff, DCa >> 16, ...

    oder eben noch besser mit weniger, aber dafür 32 Bit Argumenten:


    Dass dein XMS Handle den festgelegten Wert 1 genau in dem Moment bekommt, zu dem getsprite ihn wissen will, kann eigentlich auch nicht sein. Der Handle sollte vom Treiber beantragt werden, Handle 1 kann wer weiss was sein. Wenn du den Eindruck hast, dass der Handle Wert manchmal verloren geht, mach lieber was in der Richtung von "if XMShandle < 1 then gibt Fehlermeldung aus, dass XMS Handle auf einmal auf DOS Speicher zeigt, und bricht das Programm an der Stelle ganz ab, oder brich wenigstens getsprite ab".


    Wegen diesem "Buche den Handle bevor die Borland IDE startet und gib ihn dann wieder frei, damit das Spiel ihn verwenden kann", kannst du ja trotzdem deine "Besorge dir einen 4 MB XMS Handle" Funktion zu Testzwecken immer 1 returnen lassen, aber die 1 (oder besser ein einstellbarer Wert, evtl. per Command Line Argument deines Spiels einstellbar) sollte nur an dieser einen einzigen Stelle im Quelltext vorkommen und nicht erst mitten in getsprite auftauchen.


    Wie ist in dem Zusammenhang das hier gemeint?

    Code
    if (XMShandle!=1) Xprint(28,10,14,"XMSHANDLE");//Speicherbedarf am Anfang anzeigen
    if (XMShandle!=1) Xprint(28,30,12,TNr);//Speicherbedarf am Anfang anzeigen

    Es scheint in die Richtung von "wenn XMShandle noch nicht gebucht ist, gib irgendwas aus" zu gehen, aber das ist wie gesagt gar nicht die richtige Stelle, um den Handle zu buchen. Ausserdem ist es seltsam, dass Xprint sowohl Strings als auch Integers wie TNr ausgeben kann, wie macht es das?

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!