Vesa und die Tücken 16 Bit Protected Mode

  • Grüße, weiter gehts mit Vesa und den Tücken des Protected Modes.


    Erstmal geht es um die Funktion AX=4F00 -> GET SuperVGA INFORMATION, die hier letztlich stellvertretend für so ziemlich alle Softwareinterrupts steht, die einen Zeiger für den Datenaustausch erwarten - in diesem Fall über die Register es:di.


    Da der Interrupt im Realmode arbeitet habe ich natürlich über int $31, ax $100 ALLOCATE DOS MEMORY BLOCK Speicher im Bereich unter 1MB reserviert, praktischerweise bekomme ich gleich noch die RealMode Segmentadresse zu dem reservierten Speicher mitgeliefert - was ich auch erfolgreich für die Soundausgabe (DMA) nutze.


    Tja und nun erstmal ein kleines Bild:



    Erklärung dazu:

    Hinter dem 'hi' steht ersteinmal die Adresse des Realmodesegments und darunter seht ihr jede Menge erfolgreich (79 (=$004F) bedeutet Int $10, AX $4F00 war erfolgreich) fehlgeschlagene ('blah' - eigentlich sollte da 'VESA' stehen) Aufrufe.


    Und einen erfolgreich erfolgreichen: offenbar ist das mit der Zeigerübergabe an einen RM Int nicht ganz so einfach - sprich: wenn ihr einen Interruptaufruf aus einem PM Programm tätigen wollt braucht ihr die Funktion INT $31, AX $0300 Simulate Real Mode Interrupt.

    Die Borland Pascal eigene Routine der Dos Unit funktioniert an der Stelle leider nicht.


    und nun ein wenig QT:

    erstmal das was geht


    Und nun die Gesamtheit:



    Von allen Dingen auf Erden ist die Intelligenz am gerechtesten verteilt: Jeder glaubt, er hätte genug davon.

  • Dosenware

    Hat den Titel des Themas von „Vesa und die tücken 16 Bit Protected Mode“ zu „Vesa und die Tücken 16 Bit Protected Mode“ geändert.
  • Typ/Zeigerkonvertierung:

    Hier erstmal etwas Vorgeplänkel, wenn ihr direkt zur ABSOLUTEN Konvertierung wollt, scrollt einfach zur ABSOLUTEN Konvertierung.


    Mal was kurzes worüber ihr auch stolpern könntet:


    Jetzt da ich Den VBE Informationsblock habe geht es damit weiter den richtigen Videomode zu finden: man sieht zwar immerwieder Modilisten, aber die waren nur bei VBE 1.X definiert.

    Seit VBE 2 gilt: ihr dürft selbst nach dem passenden Modus suchen (eben über die Funktion 00 (Modusnummern) und 01 (Informationen zu dem jeweiligen Modus) - auch wenn sich wohl die meisten Karten brav an die VBE 1.X Liste halten, ist es daher besser den Modus zu suchen.


    Nun zum Thema:


    Der Informationsblock sieht so aus:


    wichtig ist dabei SupportedModesPTR - eigentlich ein Pointer, aber den kann ich so nicht verwenden weshalb ich das ganze direkt in 2 Datenwörter zerlege, denn:


    Int $31, AX=$0002 nimmt keinen Pointer entgegen, sondern nur die Segmentadresse - und mittels des Arrays haben wir den RM Pointer direkt in Segment:Offset zerlegt (Ich glaube zwar nicht dass SEG(X) langsamer ist, aber egal)

    Also sieht die Zeigerkonvertierung erstmal so aus:

    Code
    DPMI_SegmentToDescriptor(t_Vesa^.SupportedModesPTR[1],Pointer(Modelist));


    Blöd nur dass jetzt der Offset fehlt, aber glücklicherweise funktionieren Deskriptoren auch mit den Offsets der Realmodeadressen, leider hat sich Pascal etwas eigen im Umgang mit Pointern, inc funktioniert nicht wie auch die meisten arithmetischen Operationen (z.b. Pointer:=Pointer+Offset), hier ist etwas recht hilfreich:


    Die ABSOLUTE Konvertierung


    Pascal unterstützt es mehrere Variablen auf derselben Speicheradresse unterzubringen, ihr erinnert euch an SupportedModesPTR? Der Zeiger den ich als 2 Worter gespeichert habe?


    Nun:

    Code
    var Modelist:^t_Modelist;
    var ModelistPTR_Help:array[0..1]of word absolute Modelist;

    Modelist ist ein Zeiger, und ModelistPTR_Help ist ein array aus 2 Wörtern welches auf derselben Adresse liegt wie der Zeiger Modelist - der Schlüssel hierzu ist der Zusatz "absolute Modelist".


    Jetzt haben wir also Variablen mit denen es sich sehr schön arbeiten lässt, die auf einer Variablen liegen mit der sich Pascal schwer tut. Super!

    Code
    ModelistPTR_Help[0]:=t_Vesa^.SupportedModesPTR[0];

    Und schon haben wir den Offset in unserem Pointer untergebracht und können gemütlich auf unsere Daten zugreifen.


    Absolute funktioniert nicht nur bei soetwas, man kann damit wunderbar einfach Typkonvertierungen jeder Art durchführen - praktisch für Puffervariablen. Stellt nur sicher dass eure größte Variable die Basis bildet, alles andere geht schief.

    (also ein byte auf ein word zu mappen geht Problemlos, aber zu sagen das ein 256 byte String auf der gleichen Adresse liegt wie ein 1byte shortint geht wahrscheinlich schief)


    Wolltet ihr z.b. mal ein 64k array anlegen?

    Bitte sehr:


    Code
    type wordarray=array [0..1] of word; {Bereichsüberprüfung sollte aus sein}
    var x:Pchar;                         {PChar ist ein 64k großer Datentyp, praktisch ein array of char, der durch das Zeichen $00 terminiert wird}
    var RS:^wordarray absolute x;
    var i:word;
    [..]
    new(x);
    for i:=0 to 32767 do RS^[i]:=$AFFE; {komplette 64k werden befüllt}



    Warum ich das ganze schreibe? Weil ich Drops vergessen habe absolute hinzuschreiben und mich ewig gewundert habe warum ich keine passende Modusliste bekomme, bis ich dann mal die Ausgabe auf Hexadezimal umgestellt habe und als erstes sah "AA55" * .... :Face

    *AA55 ist die Kennung für ein Optionrom (in diesem Falle das Grafikkartenbios), diese Kennung steht verständlicherweise direkt am Anfang.

    Von allen Dingen auf Erden ist die Intelligenz am gerechtesten verteilt: Jeder glaubt, er hätte genug davon.

    Einmal editiert, zuletzt von Dosenware ()

  • Aktueller Stand: Immernoch bei der Initialisierung des Videomodus:


    Part1, die Unit ist jetzt bereits zu lang... Die Initialisierung folgt in Part 2

    Von allen Dingen auf Erden ist die Intelligenz am gerechtesten verteilt: Jeder glaubt, er hätte genug davon.

    2 Mal editiert, zuletzt von Dosenware ()

  • Part 2 - DIe Initialisierungsroutine:

    Von allen Dingen auf Erden ist die Intelligenz am gerechtesten verteilt: Jeder glaubt, er hätte genug davon.

  • Im 16Bit Protected Mode habe ich immernoch 64k "Segmente" und afair max. 16MB ;)

    Von allen Dingen auf Erden ist die Intelligenz am gerechtesten verteilt: Jeder glaubt, er hätte genug davon.

  • Was denn? Wenn 640kb für jeden genug sind, sind 16MB doch das Schlaraffenland :D

    Von allen Dingen auf Erden ist die Intelligenz am gerechtesten verteilt: Jeder glaubt, er hätte genug davon.

  • enough for anybody ;)


    Außerdem gibts größere Sachen: mögliche bytelücken zwischen Zeilen (wobei dies vmtl eher 24bit Modi betrifft). Bibt es möglicherweise Lücken zwischen den Bildern, oder sind die Speicherbereiche direkt Zusammenhängend? Wie lässt sich das herausfinden?


    Mittels logical Scanlinelength dürfte ich zumindest die Lücken zwischen den Zeilen herauslesen können....

    Von allen Dingen auf Erden ist die Intelligenz am gerechtesten verteilt: Jeder glaubt, er hätte genug davon.

  • Dieser Moment in dem man merkt: array1:=array2 geht auch :Face



    Heute gings um die Datenübertragung in den Videospeicher:


    Ich arbeite mit 640x360x8 Bildern (640x120 Pixel wird das Interface sein, das muss ich also nicht ständig übertragen)

    Die Testroutine für die Datenübertragung sieht im wesentlichen so aus:


    VESA_WinByteAddress ist das Speicherfenster der Grafikkarte für den Banked Mode - liegt üblicherweise auf $A0000, p1-4 sind 64k Speicherhappen in denen sich das zu übertragende Bild befindet.

    Und damit die Zeitmessung etwas akkurater ist, wird das Bild 256 mal übertragen.


    Das ganze habe ich 3 mal gemacht:

    - byteweise (8 Bit) Übertragung

    - wordweise (16 Bit) Übertragung

    - doublewordweise (32 Bit) Übertragung


    Außerdem habe ich Assembler (rep movsw) versucht (dazu später mehr) und letztlich array1:=array2.

    Aufgrund einer Limitierung in Pascal sind nur 64k-1 (array [0..FFFE] of byte;) möglich, damit ich also nicht jedesmal mindestens 1 byte verliere muss ich die 64k in 2 32k aufteilen... was dannn so aussieht:



    Und hier die Messergebnisse (Dosenbox 8000 cycles - muss ich alles noch am 486er gegentesten)


    Code
    Pascal      Run1   Run2   Run3    fps     kb/s    Prozent
    byte        8179   8194   8217    3,12    936,97   100,00
    word        4839   4834   4833    5,29   1588,31   169,52
    long        2795   2796   2796    9,16   2747,11   293,19
    arr1:=arr2   758    752    758   33,86  10158,73  1084,22

    Jepp, array1:=array2 ist schnell... sehr schnell. Was ich aber auch interressant finde ist die Beschleunigung bei longint, Pascal nutzt 286er Code, ist also 16 Bit und kennt keinen 32Bit Move.



    Zu Assembler:

    Leider Bisher nicht zum laufen gebracht, das Problem scheint die Adressübergabe (Pascal zu Assembler, Protected) zu sein...


    gibt einen Seitenfehler, Ideen?


    /Grüße, Dose

    Von allen Dingen auf Erden ist die Intelligenz am gerechtesten verteilt: Jeder glaubt, er hätte genug davon.

  • Assembler und Probleme und eigene Blödheit:


    Erstmal das Gute: Ich habe Assembler zu laufen gebracht - und es ist schnell:


    Die Testroutine überträgt ein Bild 256 mal in den Videospeicher und misst die dafür benötigte Zeit (in hunderstel Sekunden, 8173 sind also 81,73 Sekunden).

    Code
    Pascal        Run1  Run2  Run3    fps     kb/s   Prozent
    byte          8173  8195  8206   3,13    937,58   100,00
    word          4833  4833  4833   5,30   1589,08   169,49
    long          2795  2796  2796   9,16   2747,11   293,00
    bytearr1:=arr 2758   758   758  33,77  10131,93  1080,65
    wordasm        390   379   390  66,26  19879,21  2120,28


    basierend auf den Zeiten würde ich sagen die Pascalvariante array of word:=array of word* - könnte die schnellste sein, oder zumindest mit wordasm gleichziehen - evtl. könnte sogar longintarray etwas schneller sein. (Pascal macht nur 16Bit code - daher könnte longint auch langsamer sein)


    *

    Code
    type x:=array[0..$7FFF] of Word;
    var x1,x2:x;
    [..]
    x1:=x2;


    Das es vorher mit rep nicht funktionierte lag wohl an rep selbst, es macht wohl bei erreichen von 0 einen extradurchlauf:

    Quasi:

    -Befehl ausführen

    -if cx=0 end else dec(cx)


    was dann zu einem illegalen Descriptor führte, weil movsw die Zeiger einmal zu oft in/decrementierte


    Probleme:


    Ich habe das ganze bisher immer direkt aus der IDE unter W3.11 in der Dosbox zu laufen gehabt und wollte das jetzt auf dem 486er gegentesten - mal davon ab, dass ich beim letzten mal wohl mit den Jumpern gespielt habe und der Rechner grad nicht auf 150MHz läuft, ist mir aufgefallen: unter der puren Dosbox, ohne W3.11 läufts nicht - der Absturz ist so hart dass selbst Dosbox mitabstürzt.


    #Hier Bitte eine Tirade euch angemessener Schimpfworte einfügen - ich selbst beschränke mich mal auf Knorkator#


    Ich habe nun eine Soundblasterunit die unter Dosbox funktioniert (aber nicht zusammen mit W3.11) und dazu eine Vesaunit die in der Dosbox nur mit W3.11 läuft... Perfekt.

    Es scheint sich schonmal nicht um meinen alten Feind "Parameterübergabe unter Pascal" *)2 zu handeln - auch aufgedröselt läufts nicht richtig.


    Ich werde jetzt wohl ein wenig Speicherverwaltung basteln:

    Die DPMI Funktion


    UND WÄHREND ICH DAS HIER SCHREIBE

    stolpere ich über das eigentliche Problem:


    :Face


    Na? Wer findet es?



    Offenbar läuft unter W3.11 ein anderer DPMI Server der etwas anders allokiert, mglw. reserviert er etwas mehr Speicher, oder ist fehlertoleranter.


    ______________________
    *)2 Variablen können unter Pascal auf 2 Arten übergeben werden:

    Procedure x (y:word); vs. Procedure x (var y:word);

    Im ersten Fall wird eine Kopie der Variablen erzeugt (leider sorgt das dafür dass man keinen Werte zurückliefern kann) im zweiten Fall wird ein Zeiger auf die Variable erzeugt.

    Für den zweiten Fall trickst Pascal etwas bei der Syntax und behandelt den Zeiger als normale Variable und nicht als Zeiger - spätestens bei Verwendung von Assembler führt das ganz gerne zu Problemen:

    Von allen Dingen auf Erden ist die Intelligenz am gerechtesten verteilt: Jeder glaubt, er hätte genug davon.

    2 Mal editiert, zuletzt von Dosenware ()

  • Ich kann Farbpaletten setzen und das Abspielen von Sound stört auch nicht:

    noname00_000_merged_new.mp4


    Nicht wundern, Dosbox macht wegen dem Auflösungswechsel 2 Dateien - musste etwas basteln um die zusammenzubekommen, Audio ist 4Bit Creative ADPCM mit 11kHz


    Als nächstes Framebuffer, Preloading, Animation und die dazugehörige Verwaltung.

    Sowie Palettenbverwaltung (ich werde am Ende bis zu glaube 6 Verschiedene Bilder mit jeweils eigener Farbpalette gleichzeitig darstellen).


    Und das Eigene Bildformat mit RLL? Encoding und multiplen Paletten für Tag/Nacht wechsel, sowie Animationsformat (wie Bildformat mit defferenzieller Codierung und Refframe) müssen auch noch implementiert werden.

    Sowie ein kleines Bildbearbeitungsprogramm um die Farbkonvertierung zu verbessern, gerade am Anfang sieht man jede menge unnützer Farben (vor allem das krieseln an den Kanten) die die effektive Farbtiefe nur reduzieren.


    UND Dann kann ich mich auch endlich an die Scriptengine machen... mir grauts jetzt schon davor....


    PS. und den arraytransfer kann ich leider nicht beschleunigen.... egal of byte,word, oder longint - ich lande immer bei etwa 7,5s

    Von allen Dingen auf Erden ist die Intelligenz am gerechtesten verteilt: Jeder glaubt, er hätte genug davon.

  • Hier nochmal richtigrum gedreht und mit korrektem Blockread (das liest nur 64k-1 byte - deshalb war oben alles etwas verschoben)


    Woher das Störgeräusch am Anfang kommt weiß ich allerdings nicht, real ist es nicht da - irgendwo beim zusammenbasteln der 3 Videoschnipsel (Auflösungswechsel==neue Datei) kam das wohl hinzu.


    dosbox_000_merged.mp4

    Von allen Dingen auf Erden ist die Intelligenz am gerechtesten verteilt: Jeder glaubt, er hätte genug davon.

Jetzt mitmachen!

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