Linear Frame Buffer bei BP7

Diskussion zum Thema Programmierung unter DOS (Intel x86)
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Linear Frame Buffer bei BP7

Beitrag von Dosenware »

Hohe Videomodi ergeben viel Bildmaterial und so ist es zweckmäßig ein Speicherbereich zu verwenden, wo das Bild aufbereitet und berechnet wird und wenn es fertig berechnet ist, von dort aus zum LFB übertragen werden kann.
Jepp, zumal am Bild immer wieder etwas geändert wird - ich habe auch vor das Bild etwas zu "partitionieren" und nur die geänderten Bereiche zu übertragen.
go32
Kommandozeilenfetischist
Beiträge: 174
Registriert: Sa 24. Okt 2015, 22:51

Re: Linear Frame Buffer bei BP7

Beitrag von go32 »

Ich lese hier grad mit und da kam mir folgende Idee, die Adressierung wie LFB zu realisieren:

Ist noch nicht getestet, ist nur ein erster Ansatz.

Code: Alles auswählen

unit LFB;

interface

uses
  Dos,Objects;

type
  TLFB = object(TMemoryStream)
    procedure SetBytePixel(Color: Byte; LinAddr: Longint); virtual;
    procedure SetWordPixel(Color: Word; LinAddr: Longint); virtual;
    procedure SetLongPixel(Color: Longint; LinAddr: Longint); virtual;
    function GetBytePixel(LinAddr: Longint): Byte; virtual;
    function GetWordPixel(LinAddr: Longint): Word; virtual;
    function GetLongPixel(LinAddr: Longint): Longint; virtual;
  end;


implementation

procedure TLFB.SetBytePixel(Color: Byte; LinAddr: Longint);
var
  lfbSeg: Word;
  lfbOfs: Word;
begin
  lfbSeg := LinAddr div 65536;
  lfbOfs := LinAddr mod 65536;
  MEM[SegA000+lfbSeg:lfbOfs] := Color;
end;

procedure TLFB.SetWordPixel(Color: Word; LinAddr: Longint);
var
  lfbSeg: Word;
  lfbOfs: Word;
begin
  lfbSeg := LinAddr div 65536;
  lfbOfs := LinAddr mod 65536;
  MEMW[SegA000+lfbSeg:lfbOfs] := Color;
end;

procedure TLFB.SetLongPixel(Color: Longint; LinAddr: Longint);
var
  lfbSeg: Word;
  lfbOfs: Word;
begin
  lfbSeg := LinAddr div 65536;
  lfbOfs := LinAddr mod 65536;
  MEML[SegA000+lfbSeg:lfbOfs] := Color;
end;

function TLFB.GetBytePixel(LinAddr: Longint): Byte;
var
  lfbSeg: Word;
  lfbOfs: Word;
begin
  lfbSeg := LinAddr div 65536;
  lfbOfs := LinAddr mod 65536;
  GetBytePixel := MEM[SegA000+lfbSeg:lfbOfs];
end;

function TLFB.GetWordPixel(LinAddr: Longint): Word;
var
  lfbSeg: Word;
  lfbOfs: Word;
begin
  lfbSeg := LinAddr div 65536;
  lfbOfs := LinAddr mod 65536;
  GetWordPixel := MEMW[SegA000+lfbSeg:lfbOfs];
end;

function TLFB.GetLongPixel(LinAddr: Longint): Longint;
var
  lfbSeg: Word;
  lfbOfs: Word;
begin
  lfbSeg := LinAddr div 65536;
  lfbOfs := LinAddr mod 65536;
  GetLongPixel := MEML[SegA000+lfbSeg:lfbOfs];
end;

end.
LinAddr, die lineare Adresse würde sich dann aus X + Y*XResolution ergeben.

Ihr könnte das ja mal testen. Ich experimentiere auch damit.
go32
Kommandozeilenfetischist
Beiträge: 174
Registriert: Sa 24. Okt 2015, 22:51

Re: Linear Frame Buffer bei BP7

Beitrag von go32 »

Zu ergänzen wäre noch, dass der MemoryStream das Bild vorher im Speicher halten könnte.

Code: Alles auswählen

procedure TLFB.Mem_set_pixel(Color: TColor; LinAddr: Longint);
begin
   Seek(LinAddr);
   Write(Color, Sizeof(Color));
end;

Wobei hier TColor von der Farbtiefe (8,16,24,32) Bits per Pixel abhängt.

Das Screiben in den Bildspeicher sähe dann ungefähr so aus:

Code: Alles auswählen

procedure TLFB.UpdateScreen;
begin
  Seek(0);
  case FBitsPerPixl of
    4: Mem[SegA000:0] := self;
    8: Mem[SegA000:0] := self;
   15: MemW[SegA000:0] := self;
   16: MemW[SegA000:0] := self;
   24: MemL[SegA000:0] := self;
   32: MemL[SegA000:0] := self;
  end;
end;
Oder das Pixel müsste zuerst gelesen und dann wie oben zu sehen an MEMX[] zugewiesen werden, wenn es so wie oben nicht klappt mit der Zuweisung.

Code: Alles auswählen

procedure TLFB.UpdateScreen;
var
  ScrBuf: array[0..ScreenSize] of Byte;
begin
 Seek(0); 
 case FBitsPerPixl of
    8: 
    begin
       Read(ScrBuf, SizeOf(ScrBuf));
       Mem[SegA000:0] := ScrBuf;
    end;
    16: ...;
  end;
end;
Dann könnte man das Bild zuerst im Speicher zeichnen und dann in einem Rutsch auf dem Bildschirm ausgeben.
Für Bewegungen auf dem Bildschirm braucht es aber wohl doch noch Bit Block Transfer Algos.

Diese Idee kam mir spontan. Muss daher selber auch noch testen, ob das so funktioniert.
go32
Kommandozeilenfetischist
Beiträge: 174
Registriert: Sa 24. Okt 2015, 22:51

Re: Linear Frame Buffer bei BP7

Beitrag von go32 »

Muss leider dazu sagen, dass meine Version eher ein LFB Emulator ist. Unterhalb meines Codes auf Hardwareebene erfolgt so nach wie vor Bank Switching. Was mein Code bewirkt, ist, die lineare Adressierung des Bildspeichers auf Softwareebene. Intern werden die Adressen in SEG:OFS umgerechnet, um damit dann den Bilspeicher anzusprechen.
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Linear Frame Buffer bei BP7

Beitrag von DOSferatu »

Naja, das "Problem" bei echtem LFB ist, daß er natürlich nur bei 32-bit-Zugriffen Sinn macht. Im reinen RealMode oder Virtual V86 Mode ist das dann natürlich keine brauchbare Option. Hier hilft dann nur entweder dieser 4G Flat Trick oder gleich Protected Mode. Ansonsten ist man in den 16bit Modi mit dem Banked Mode wahrscheinlich besser dran.
go32
Kommandozeilenfetischist
Beiträge: 174
Registriert: Sa 24. Okt 2015, 22:51

Re: Linear Frame Buffer bei BP7

Beitrag von go32 »

@DOSferatu:

Das käme bezüglich der Verarbeitungszeit auf einen Test an.

Allerdings habe ich mit meinem Konstrukt die Möglichkeit, den Bildspricher linear zu addressieren, auch wenn darunter wieder nach 16 Bit zurück gerechnet wird. @freecrac sagt in einem Beitrag hier, dass dieses Umrechnen wohl recht schnell geht.

Ich lese aus den Beiträgen außerdem raus, dass es mit $66 als Befehlspräfix für die Assembleranweisungen möglich ist, auch mit Turbo Pascal die 32 Bit Versionen der ASM Anweisungen zu verwenden, womit es ja dann evtl. möglich wäre auch unterhalb meiner Objekte 32 Bit Zugriff zu verwenden.

Für meine Objekte wären zum Zufriff auf den Bildspeicher 32Bittige Funktionen ideal, die dann in den Bildspeicher schreiben und bei Grafikkkarten, die LFB unterstützen, diesen auch nutzen.

Meine Objekte können aber schon mal lineare Adressen übergeben, auch wenn sie direkt darunter schon wieder nach 16 BIt Seg:Ofs umgerechnet werden. Dies ist nicht ganz und gar sinnlos, sofern diese Umrechnung schnell genug ist für schnell bewegte Grafiken.

Segment := Linaddr div 65520
Offset := Linaddr mod 65520

65520 ist die letze nutzbare Bytenummer im Segment.

Ich bin jedenfalls gerade dabei, die LFB Unit zu testen. Werde sie in der Final Version allerdings umbenennen, das sie ja nur ein Interface für lineare Adressierung des Bildspeichers bereit stellt. Echter LFB ZUgriff erfolgt ja erst auf der Grafikkarte, unterhalb meiner MEMX[Seg:Ofs] Funktionen. Selbige, die dann auch hardwaremäßig LFB Zugriff nutzen, wären hier jetzt ideal. Dann wären diese Objekte eine gute Gesamtlösung.
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Linear Frame Buffer bei BP7

Beitrag von DOSferatu »

go32 hat geschrieben: Ich lese aus den Beiträgen außerdem raus, dass es mit $66 als Befehlspräfix für die Assembleranweisungen möglich ist, auch mit Turbo Pascal die 32 Bit Versionen der ASM Anweisungen zu verwenden, womit es ja dann evtl. möglich wäre auch unterhalb meiner Objekte 32 Bit Zugriff zu verwenden.
Hier muß man unterscheiden:
Präfix $66 macht aus einem 16-bit-OPERANDEN einen 32-bit-OPERANDEN.
(D.h. z.B. aus AX wird EAX, aus einem direkten Wert wird ein 32bit-Wert, d.h. man muß die restlichen (oberen) 16 noch mit dw XXXX hinter den Befehl schreiben (um ihn auf die 32bit zu erweitern), usw... Kenne mich ziemlich gut mit diesem Zeug aus inzwischen, da ich ASM unter TurboPascal nutze, und alles, was über 286er Code hinausgeht, mit direkten Bytes (Words, etc) direkt in den Source schreibe. Der ASM in Borland/Turbo-Pascal wurde bekanntlich nur maximal bis 286er-Support entwickelt.

Präfix $67 macht aus einer 32-bit-ADRESSE eine 32-bit-ADRESSE (bzw Offset zum Segment). Das ist das, was Du gebrauchen könntest - ABER! Aus Gründen der Abwärtskompatibilität werden im Real-Address-Mode (also dem 16-Bit-Mode) standartmäßig die oberen 16 bit einer Adresse (eines Offsets) "ausgefiltert", d.h. =0 gesetzt, so daß sich die Befehle weiterhin wie für 16-bit verhalten. Hier muß dann der von mir angesprochene "Flat-4G-Trick" angewendet werden, um "das Limit zu erhöhen" - ansonsten kommt man auch weiterhin nicht über die 1MB (plus 64kB minus 16, sofern Gate A20 offen) hinaus.
Die Linear-Framebuffer-"Adressen" werden aber immer am Ende des 4GB-Bereichs (also am Ende des mit 32bit adressierbaren Speicherbereichs) virtuell eingeblendet, müssen dann also so angesprochen werden, als würde man z.B. auf Adressen 3,75 GB bis 4GB (minus 1) zugreifen wollen. Ohne Erweiterung des LImits für ein oder mehr Segmentregister wäre dies vom RealMode aus nicht möglich. (Man setzt die Limits für die Segmentregister, d.h. Adressierungen mit einem solchen Segmentregister wäre dann bis 4 GB (minus 1) möglich. Daß man die Limits den Segmentregistern zuordnet, hat etwas damit zu tun, daß man hier eigentlich Dinge des Protected Modes (32bit) "mißbraucht", der die Segmentregister komplett anders benutzt als im Real-Address-Mode (16bit). Der Grund, wieso das überhaupt funktioniert, ist, daß ab 80386ern (eigentlich ab 286ern) der Protected Mode quasi der Mode ist, in dem die CPU arbeitet und der Real-Address-Mode quasi von diesem abwärtskompatibel "simuliert" wird, indem die Eigenschaften des PM "weggelassen", bzw ausgeblendet werden. (Ja, diese Erklärung ist sehr simpel, will das nicht so auswalzen.)

Und ja - HIMEM.SYS und solche Erweiterungen erlauben natürlich den Zugriff auf Speicher über 1 MB. Sie dienen quasi als Möglichkeit, diesen Speicher auch vom Real-Address-Mode aus zu nutzen und bedienen sich dabei ähnlicher Mechanismen wie der Flat-4G-(Pseudo)Mode. Da der normale Zugriff im Real-Address-Mode sich aber weiterhin auf 16bit-Segmente und 16bit-Offsets beschränkt, kann dieser Zugriff nur indirekt erfolgen, d.h. durch hoch-/runterkopieren von/zum Speicher über 1MB. Direkte Zugriffe/Operationen/Berechnungen u.ä. durch die CPU können aber im Real-Address-Mode (16bit) weiterhin nur im Bereich unter 1MB erfolgen, weshalb diese HIMEM.SYS-"Kopier-Orgien" erforderlich werden.

Ich kann mir zwar nicht direkt vorstellen, wie man HIMEM.SYS oder ähnliche "Treiber" dazu bringen sollte, direkt auf festgelegte "obere Adressen" zugreifen zu lassen - da man sich in HIMEM.SYS ein "Handle" holt und mit diesem einen zusammenhängenden Speicherbereich "irgendwo" zugewiesen bekommt, den man aber selbst mit Offset 0 bis Speichergröße anspricht. Wo genau dann dieser Bereich liegt, weiß man eigentlich nicht.

Hinzu kommt, daß HIMEM.SYS natürlich nur Speicher zuweist, der auch physisch vorhanden ist. Die (virtuellen) Adressen für den Linear-Frame-Buffer werden aber absichtlich auf das Ende des 4GB-Bereichs gelegt (dort "eingeblendet"), um nicht wirklichen, physisch vorhandenen RAM, der eventuell "darunter" liegt, für den User unbenutzbar zu machen. Beim PC (im Gegensatz z.B. zum C64) ist das "Übereinander-Stapeln" von Speicher so nicht vorgesehen - d.h. man kann nicht mit derselben Adresse einmal auf Grafikkartenspeicher und einmal auf normalen "darunterliegenden" RAM zugreifen, da es hier keinen "Umschalter" gibt.
(Und ja - ich bin mir des Vorhandenseins von EMS bewußt - das spielt aber für das Thema Linearen Framebuffer jetzt keine Rolle.)

Außerdem - selbst WENN man HIMEM.SYS "überreden" und so verbiegen könnte, daß das zugewiesene Handle wirklich einen direkten Zugriff auf den (virtuellen) Linearen Framebuffer Bereich abdeckt - ergäbe sich weiterhin die elende HEAP<->XMS Kopiererei durch HIMEM.SYS-Funktionen. Und ob das dann wirklich mehr performt, da melde ich persönliche Zweifel an.

Das heißt, ich wüßte nicht, wie man ohne Flat-4G-Trick vom Real-Address-Mode aus den Linearen Framebuffer sinnvoll nutzbar machen könnte.

Ich lasse mich aber natürlich gern eines Besseren belehren. Ich bin ebenfalls sozusagen "Grafikprogrammierer" - und der Zugriff auf Grafikspeicher (vor allem bei höheren Modi als die normalen VGA) sind immer ein ziemlicher Hemmschuh, was Performance, Einfachheit und Eleganz angeht. Vielleicht habe ich hier nutzbare Möglichkeiten bisher übersehen.

Kleiner Nachsatz: Ich rede hier NICHT davon, DOS-Maschinen mit 4GHz-CPUs und 4GB RAM und AGP8x Grafikkarten auszurüsten, um die durch Umständlichkeit verlorene Performance durch Hardware-Materialschlacht zu kompensieren.
Wer SOWAS saubere Programmierung nennt, der programmiert auch DOOM3 in Interpreter-BASIC, läßt es auf 'nem Quantenrechner laufen und sagt dann: "Performance ist doch ganz OK."
Antworten