Pascal "absolute" Deklaration - mit Pointer-Array möglich?

Diskussion zum Thema Programmierung unter DOS (Intel x86)
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Pascal "absolute" Deklaration - mit Pointer-Array möglich?

Beitrag von zatzen »

Hallo zusammen!

Hab da mal ne Frage.

Um möglichst wenig Variablen im Datensegment zu verschwenden benutze ich sowas hier:

Code: Alles auswählen


type byte_array = array[0..1023] of byte;   { oder beliebiger Datentyp }


var
  datenfeld_pointer: pointer;

  datenfeld: ^byte_array absolute datenfeld_pointer;

Damit habe ich jetzt bequemen, durch eckige Klammern indizierbaren Zugriff auf "datenfeld^",
ohne das Ganze im Datensegment anzulegen. War mir neu, ich bedanke mich hier nochmal bei
DOSferatu, der mir das mal erklärte.

Jetzt hab ich aber ein Problem:
Ich brauche über 100 solcher Datenfelder, die ich dynamisch durch Pointer anlegen möchte
und auf die ich auch so komfortabel zugreifen möchte.
D.h. ich bräuchte ein Pointer-Array, meinetwegen 1...192, aber wie deklariere ich das dann
entsprechende Datenfelder mittels "absolute" ?
mov ax, 13h
int 10h

while vorne_frei do vor;
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von DOSferatu »

Ich habe hier eine selbstgemachte Unit (diese benutze ich in fast jedem meiner Programme), die dynamisch Pointer belegt oder freigibt. Man kann auch nachträglich die den Pointern zugewiesenen Speichergrößen ändern, der Speicher wird dann automatisch verschoben und die Pointer werden angeglichen... (Es merkt sich die Speicheradresse der Pointer-Variable, um deren Inhalt nachträglich ändern zu können.)
Übrigens können die jeweiligen einzelnen zugewiesenen Speicherbereiche auch größer als 64kB sein.

An sich ein wirklich ziemlich cooler Stuff. Deshalb benutz ich das auch so gerne.

Arbeitsweise: Es belegt zu Anfang mit mehreren GetMem-Befehlen eine frei wählbare Größe von Speicher (oder auch allen vorhandenen), indem direkt aufeinanderfolgende Speicherblöcke belegt werden, bis ein großer zusammenhängender Speicher belegt ist. Und es verteilt diesen Speicher dann selbst. An sich eine recht simple Idee - aber ich benutze es seit Jahren (Jahrzehnten) erfolgreich.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von zatzen »

Oh, ich wäre an dieser Unit interessiert, hatte dann aber gern den Quellcode und nicht nur die TPU, denn ich
möchte genau verstehen was ich da verwende. Wenn das okay wäre kannst Du mir ja ne PN schreiben.

Geniale Sache, das ist dann ja quasi der Unrealmode im Realmode, nur eben auf < 640K begrenzt.

Ich frag mich nur ob es da nicht Einbußen mit der Geschwindkeit gibt, d.h. wenn man auf
eine einzelne Variable zugreifen will, ob das dann wirklich genauso schnell ist als wenn
man einfach auf ein normales Array zugreift? Du schriebst mal sowas, dass Units Sachen
zwar modular und übersichtlich machen, aber u.U. langsamer sind als wenn man alles
direkt ins Hauptprogramm schreibt.

Und dann wäre da noch eine Frage mangels Erfahrung, können mehrere TPUs die ich benutze
jeweils diese Speicherverwaltungs TPU nutzen, ohne dass diese dann mehrmals in die EXE
einwandert, ist der Compiler so intelligent?

Die Sache mit dem "absoluten" Zugriff auf Daten, die auf bis 192 verschiedenen Pointern liegen,
löse ich bis jetzt einfach damit, dass ich einen Pointer definiere auf dem die "absolute"-Variable
definiert ist, und wenn ich auf diese oder jene Daten der 192 Pointer zugreifen will schreibe
ich vorher kurz sinngemäß " data_pointer := data_pointer_array[99] " oder welcher wert in der
Klammer auch immer.

Oh und noch eine Frage: Wie kann ich denn mittels einer Struktur wie
" datenfeld: ^datenfeldtype absolute daten_pointer "
auf einen Bereich > 65535 Byte zugreifen, wenn eine mit "type" definierte
Struktur nicht größer als 65535 Byte sein darf?
Löst Du das in der Unit durch eine Function mit einem longint als Eingangsparameter?
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von Dosenware »

zatzen hat geschrieben: Oh und noch eine Frage: Wie kann ich denn mittels einer Struktur wie
" datenfeld: ^datenfeldtype absolute daten_pointer "
auf einen Bereich > 65535 Byte zugreifen, wenn eine mit "type" definierte
Struktur nicht größer als 65535 Byte sein darf?
deine Struktur ist nicht größer als 64k da deine Struktur nur auf andere Strukturen verweist.

Bleistift:

ein Pointer hat 4 byte d.h. es ist ein Array [0..16383] of Pointer möglich (in der Praxis etwas weniger, Pascal mag keine 64k Arrays)

mit Typisieren Pointern wäre das z.b. ein blah=Array [0..16383] of PChar; (Pchar ist seinerseits ein ^array 0..65535 of char)

Damit hast du eine 64k große Struktur die auf 16384 andere 64k Strukturen verweist: d.h du kannst mit dieser Struktur über blah^[index1]^[index2] 1MB byteweise addressieren. (musst dann halt etwas typcasten, damit der Compiler nicht meckert - im fertigen Programm ist das kein einziges zusätzliches byte Code)

PS. über eine Anpassung der feldgrößen z.b. 0..255 könntest du dann einfach ein (i=logint;index :array 0..3 of byte absolute i;) machen, d.h. du zerlegst intern ein logint in 4 bytes die du als index nutzt - nach außen hin könntest du dann einfach per longint addressieren.
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von zatzen »

Ok, danke, hatte mir schon gedacht dass so eine direkte Adressierung nur über sozusagen Pointer
auf Pointer möglich ist. Ich wusste nicht wie man das kombinieren kann, d.h. so: array1^[..]^array2[..].

Oder ich hab was falsch verstanden. Typisierte Pointer sind Neuland für mich.

Man müsste sich mal detaillierte Literatur zu dem Thema besorgen.

Das mit dem Longint ist eine schöne Sache, aber brauche ich wohl nicht solange ich keine
Speicherblöcke > 65528 anlegen kann. Aber nein, ich verstehe, man kann mit der Aufteilung
des Logints auf mehrere verteilte Elemente linear zugreifen.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von Dosenware »

zatzen hat geschrieben: Aber nein, ich verstehe, man kann mit der Aufteilung
des Logints auf mehrere verteilte Elemente linear zugreifen.
Richtig per "i:longint; index array [0..1] of word absolute i;"

machst du aus I:=$AFFEBABE; ein index[0]=$AFFE und index[1]=$BABE;

du musst nur etwas aufpassen wegen dem Lo und Hianteil der Variablen, die sind bei einigen Datentypen verdreht.

und Wie gesagt: PChar verweist auf einen Block aus 65536 byte - ist die einfachste Variante wenn man genau 64k braucht z.b. für $A000-AFFF
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von zatzen »

Danke! Das mit dem Pchar kann ich ja mal evtl. statt der Typendenklaration
" type blabla = array[0..12863] of byte " probieren, sehe da jetzt aber keinen
großen Unterschied, ausser dass ich bei Pchar dann ein Byte mehr adressieren könnte.

Passend zum Thema hier auch mal mein Code zum bitweisen Lesen.
Der müsste noch auf Geschwindigkeit optimiert werden, mit Assembler vielleicht:

Code: Alles auswählen


type byte_array = array[0..65534] of byte;

var 
  patterndata_pointer: pointer;
  patterndata: ^byte_array absolute patterndata_pointer;

  { das Datenfeld heisst hier beispielhaft und zufällig "patterndata" }
  { weil dieser Code ein Auszug meines "ZEM" Renderers ist }

  bitpointer: pointer;
  bitposition: longint;

procedure set_bitpointer(p: pointer);  { lese-anfangs-position auf einen speicherbereich setzen }
begin
  bitpointer := p;
  bitposition := 0;
end;


{ eigentlich geht's nur um folgende function: }

function read_bits(length: byte): byte;  { length ist die anzahl zu lesener bits }
  var
    buffer, bytepos, bitpos: word;
  label
    zero;
begin

  if length = 0 then
  begin
    buffer := 0;
    goto zero
  end;

  bytepos := bitposition shr 3;
  bitpos := bitposition - bytepos shl 3; { bitposition mod 8 }

  buffer := patterndata^[bytepos] + patterndata^[bytepos + 1] shl 8;
  buffer := (buffer shr bitpos) and (1 shl length - 1);

  inc(bitposition, length);

  zero:
  read_bits := buffer;

end;

Das Dingen ist im Moment nur darauf ausgerichtet dass es funktioniert, bis
für eine Bitbreite von max. 8.
Ich lese immer direkt ein Word ein, ich glaube das ist unterm Strich schneller,
als wenn ich jedesmal überprüfen würde, ob ein Byte oder ein Word
geladen werden muss. Ich gehe hier davon aus, dass es nichts ausmacht,
wenn man irgendwann am Ende des Datenfelds ein Byte einlesen sollte,
das nicht mehr zum Datenfeld gehört. Es gibt wohl Lesezugriffe, die
irgendwas auslösen, aber wohl nicht im Heap-Raum.
Mit Assembler kann man sicherlich Zeit gewinnen, indem man die
ganzen Zwischenrechnungen in Registern vollzieht. Ist nur die Frage
wie ich mit dem Longint umgehe, oder ob ich den irgendwie aufteile,
wären aber nur 3 Bit für den unteren Teil. Wahrscheinlich nimmt man
dann einfach für untenrum direkt 8 Bit und shiftet das bei den Berechnungen
nochmal um 5 Bit nach rechts, und obenherum sind noch dicke genug Bits
frei für eine 64K Adressierung.


Übrigens hab ich auch noch eine recht dringende Frage im Thread
"Trackermodul-Engine (sehr einfach)" - vielleicht kann da mal jemand reingucken!
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von Dosenware »

goto... sollte entfernt werden, ausserdem hast du eine unendliche Schleife.

label zero;
begin
if length = 0 then
begin
buffer := 0;
goto zero
end;
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von zatzen »

Sicher, das mit der unendlichen Schleife?

Oben wird das Label ja nur definiert, gesprungen wird unten hin.
Die function funktioniert, auch wenn man 0 als length reingibt.

Ich hab das auch in Erinnerung dass man in Pascal kein goto verwenden soll,
quasi schlechter Stil. Aber ich hatte es hier so für bequemer gehalten, als das
ganze mit einer IF-Abfrage zu umklammern. Naja irgendwie scheint mir das
einerlei.

In Anbetracht dessen, dass in Assembler Sprünge gang und gäbe sind,
und weil die function letztendes Assembler sein soll, hab ich das halt so
formuliert.

Es geht mir ja hier auch eher darum, was ich beim Umsetzen nach Assembler
beachten muss, bzw. wie ich z.B. die Longint Variable verarbeiten kann,
da ich ja nur 16 Bit Register habe. Und ob ich das besser anders mache,
nicht einen Longint für die direkte Bitposition, sondern aufbrechen in
Byteposition und Bit-Offset...
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von Dosenware »

ahh, stimmt... ist zu lang her das ich goto verwendet habe...
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von zatzen »

Dann gibt's da noch eine Sache...

Für die ASM Routine würde ich gern den Longint in zwei Words aufteilen.

Aber mit "absolute" kann ich nur ein Word auf die unteren beiden Byte setzen,
in ASM ist kein array erlaubt, und an die oberen komm ich so nicht ran!

Also... es ist halt praktisch, im Assembler-Teil direkt schreiben zu können
"mov ax, wordvar", und ich denke das geht auch schneller als wenn
ich erst Datenregister belade und dann einen Zugriff mache. Oder?

Ich könnte auch eax verwenden, mit nem db $66 davor. Aber die Anweisung
selbst würde weiterhin mov ax lauten und ein Longint ist dahiner nicht zulässig.

Ich finde aber auch, nur wegen einer einzigen 32 Bit Variable muss man nicht
unbedingt 32 Bit Code bemühen.
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von Dosenware »

zatzen hat geschrieben: Aber mit "absolute" kann ich nur ein Word auf die unteren beiden Byte setzen,
in ASM ist kein array erlaubt, und an die oberen komm ich so nicht ran!
hmm, du kannst über die Adresse des Arrays rankommen: Basisadresse +2byte
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von DOSferatu »

zatzen hat geschrieben:Oh, ich wäre an dieser Unit interessiert, hatte dann aber gern den Quellcode und nicht nur die TPU, denn ich möchte genau verstehen was ich da verwende. Wenn das okay wäre kannst Du mir ja ne PN schreiben.
Genau zu verstehen was man benutzt, finde ich lobenswert und unterstützenswert. Viel zu viele "Programmierer" benutzen Zeug, von dem sie keine Ahnung haben und können dann nicht damit umgehen, wenn das Programm/der Algorithmus usw auch nur die kleinste Änderung erfordert und dies das "Fertigbauteil" zufällig nicht in seiner Grundanordnung unterstützt.

Ich werde die Unit dazu noch ein wenig "ausmisten" müssen. (Meine Units haben manches alte - auskommentierte - Zeug drin, das nur "herumliegt".)

zatzen hat geschrieben:Geniale Sache, das ist dann ja quasi der Unrealmode im Realmode, nur eben auf < 640K begrenzt.
Würde ich so nicht sagen. Ich arbeite nur mit dem normalen Realmode. Ich weiß, Du beziehst Dich da wahrscheinlich darauf, daß Bereiche größer als 64kByte reservierbar sind. Aber an sich ist das kein Hexenwerk.
zatzen hat geschrieben:Ich frag mich nur ob es da nicht Einbußen mit der Geschwindkeit gibt, d.h. wenn man auf eine einzelne Variable zugreifen will, ob das dann wirklich genauso schnell ist als wenn man einfach auf ein normales Array zugreift? Du schriebst mal sowas, dass Units Sachen zwar modular und übersichtlich machen, aber u.U. langsamer sind als wenn man alles direkt ins Hauptprogramm schreibt.
Das hast Du falsch verstanden. Gemeint ist: Wenn man VÖLLIG OHNE UNTERPROGRAMME (also ohne procedures/functions) arbeitet, hat man einen SEHR GERINGFÜGIGEN Geschwindigkeitsvorteil.
Erklärung: Werden Subroutinen aufgerufen, wird ja ein CALL ausgeführt und bei Verlassen derselben ein RET. (oder bei FAR CALLs eben RETF.) Die paar Taktzyklen, die für das Ausführen dieser beiden Befehle benutzt werden, sind der "Geschwindigkeitsnachteil".
Ich hatte dies nur der Vollständigkeit halber erwähnt.

Ich bringe mal ein Beispiel, wo dieser Rechenzeitverlust ein Problem ist:
Eine Pixelroutine.
Nehmen wir an, man will eine "Kachel", ein Sprite, ein Bild o.ä. anzeigen.
Was macht man? Man macht zwei Schleifen:

Code: Alles auswählen

for y:=0 to maxY do
    for x:=0 to maxX do
         pixel(x,y,bild[x,y]);
Richtig? - FALSCH! Warum? Weil man (maxY+1)*(maxX+1) mal die Subroutine pixel(x,y,farbe) aufruft, (d.h. CALL und RET) dieser Subroutine jedesmal X- und Y- Koordinate übergeben werden, die Subroutine jedesmal die Position im Grafikspeicher ausrechnet....
Das ist MEGA DÄMLICH!
Warum? Wenn man etwas darstellt, wo die Pixel sowieso direkt neben-/untereinander liegen, braucht man nur den Startpunkt im Grafikspeicher "ausrechnen" (oder mit Hilfe von Tabelle, wenn mal Multiplikationen ganz vermeiden will). Und dann jeweils mit schlichten Additionen die Position erweitern. Und mit einer "Sprite-Routine" oder "Rechteck-Routine" usw... das ganze Objekt darstellen, anstatt tausendemale eine generalisierte Pixelroutine anzuspringen!
Das wäre ein Beispiel für die Verschwendung von Rechenzeit. Kann man sich ganz leicht merken: Je "innerer" die Schleife, umso weniger darf sie enthalten. Es multipliziert sich ja mit jeder Anzahl Schleifendurchläufe jeder übergeordneten Schleife. Angenommen: Spielbildschirm, 320x200 (64000 Pixel), soll mit 70Hz (MCGA-Frequenz für Mode 13h) Bildrate laufen. Sind also 64000*70 = 448000 Pixel pro Sekunde. Will man pro Sekunde 448000 Multiplikationen durchführen lassen und 448000 mal pro Sekunde eine generalisierte Routine aufrufen? Und das ist wirklich eine recht kleine Auflösung. Dann kommt ja noch dazu, daß die Daten noch irgendwo hergeholt werden müssen und daß im Programm (z.B. ein Spiel) ja auch noch andere Dinge passieren sollen, die auch noch "in dieser Sekunde" abgearbeitet werden sollen...

ANDERERSEITS:
Man kommt nicht ohne Subroutinen aus! Wenn ein Projekt ein wenig komplexer wird, kann man ja nicht alles hunderte Male in den Code reinbasteln, nur weil man kein Unterprogramm aufrufen will... Da wägt man dann eben wirklich ab. Es ist zwar möglich, auch ein total komplexes Programm völlig ohne Aufruf von Subroutinen, sondern allein mit Verzweigungen und Entscheidungsmatrizen o.ä. zu schreiben - aber im Endeffekt ist es fraglich, ob die "Selbstorganisation" eines solchen Programms nicht den (theoretisch vorhandenen) Geschwindigkeitsgewinn dann wieder auffrißt...

Es hat aber nichts damit zu tun, ob etwas in einer Unit oder direkt im Hauptprogramm steht.

zatzen hat geschrieben:Und dann wäre da noch eine Frage mangels Erfahrung, können mehrere TPUs die ich benutze
jeweils diese Speicherverwaltungs TPU nutzen, ohne dass diese dann mehrmals in die EXE einwandert, ist der Compiler so intelligent?
Ja, der Borland-Pascal-Compiler IST so intelligent! Wenn man die Units mehrmals benutzt (auch als "Unter-Unit" in eine andere eingebaut) wird diese nur einmalig eingefügt. Zusätzlich ist der Compiler auch SO intelligent, daß er auch wirklich nur DIE Konstanten, Variablen, Procedures und Functions (und auch alles andere) nur DANN einbaut, wenn es mindestens einmal irgendwo benutzt/aufgerufen wird!
zatzen hat geschrieben:Die Sache mit dem "absoluten" Zugriff auf Daten, die auf bis 192 verschiedenen Pointern liegen, löse ich bis jetzt einfach damit, dass ich einen Pointer definiere auf dem die "absolute"-Variable
definiert ist, und wenn ich auf diese oder jene Daten der 192 Pointer zugreifen will schreibe
ich vorher kurz sinngemäß " data_pointer := data_pointer_array[99] " oder welcher wert in der
Klammer auch immer.
Ja, so mache ich das auch oft. Man kann ja auch einfach das Segment erhöhen. Siehe nächster Absatz
zatzen hat geschrieben:Oh und noch eine Frage: Wie kann ich denn mittels einer Struktur wie
" datenfeld: ^datenfeldtype absolute daten_pointer "
auf einen Bereich > 65535 Byte zugreifen, wenn eine mit "type" definierte
Struktur nicht größer als 65535 Byte sein darf?
Löst Du das in der Unit durch eine Function mit einem longint als Eingangsparameter?
Naja, erstens benutze ich immer den Compilerschalter für offene Array-Grenzen.
D.h. wenn man ein

Code: Alles auswählen

var A:array[0..19]of byte;
deklariert, kann man trotzdem sowas machen:

Code: Alles auswählen

B:=1000; write(A[B]);
und bekommt dann den Wert an der Stelle angezeigt, die von A aus 1000 Bytes entfernt ist.
(Nur wenn man write(A[1000]); - also mit einer Konstante - schreibt, meckert der Compiler, weil außerhalb Arraygrenzen. Aber wenn man mit Variablen arbeitet, "weiß" der Compiler nicht, welchen Wert B zur Laufzeit des Programms haben wird und erlaubt es.
Man sollte sowieso alle Dinge, die Überläufe u.ä. testen, mit den entsprechenden Compilerschaltern abschalten. Aus 3 Gründen:
1.) Wenn ein Programm Dinge tut, die es nicht soll, gehört es debugged und nicht ge-watchdogged!
2.) Wenn diese "Watchdog" Routinen drin sind, wird ein Programm größer und langsamer.
3.) Mitunter WILL man diese Überläufe benutzen, da sie einem komplizierte Abfragen oder "Drumherumprogrammieren" ersparen.

Beispielprogramm:
Ich habe mal angenommen einem Pointer Ptemp mit meiner Unit 262144 Bytes (256kByte) Speicher zugewiesen.
Wie greife ich im Realmode darauf zu? (Jetzt mal ganz einfach, um es zu zeigen)

Code: Alles auswählen

function GetByteFromPointer(P:Pointer;Address:longint):byte;
var PZ:record O,S:word;end absolute P;
var AZ:record L,H:word;end absolute Address;
type TM=array[0..$FFFE]of byte;
var M:^TM absolute P;
begin 
with PZ do with AZ do 
   begin inc(S,H shl 12);GetByteFromPointer:=M^[L];end;
end;
Und mit GetByteFromPointer(Ptemp,87654) erhält man dann das Byte, das in Ptemp an der 87654. Stelle steht (von 0 an gezählt).

Man kann natürlich auch mal ganze Ketten von Bytes lesen:

Code: Alles auswählen

const ADDER:array[false..true]of word=(0,4096);
var P:Pointer;
var PZ:record O,S:word;end absolute P;
var AZ:record L,H:word;end absolute Address;
var B:^byte absolute P;
var KETTE:array[0..1023];
var K:word;
begin
with PZ do with AZ do 
   begin K:=O;inc(O,L);inc(H,ord(O<K));inc(S,H shl 12);
   for K:=0 to 1023 do
        begin KETTE[K]:=B^;inc(O);inc(S,ADDER[O=0]);end;
   end;
end;
(Man kann auch statt inc(S,ADDER[O=0]); schreiben: if O=0 then inc(S,4096); und die ADDER-Konstante weglassen...)

Das mit dem Array KETTE ist jetzt nur ein Beispiel.

So oder so ähnlich mache ich das auch, wenn ich da in Hochsprache etwas baue.
Das liegt einfach an diesem segmentierten Speichermodell im Realmode. Ein Segment fängt immer an einer 16er Adresse an. Oder andersherum: 65536 Bytes (64kByte) sind 4096 Segmente. D.h. wenn man durch 65536 Bytes (Offset) durch ist, muß man das Segment um 4096 erhöhen (also $1000).
Man kann das auch völlig anders programmieren, das sind nur Beispiele.

in assembler kann man z.B. den Segmentwechsel so machen:
(angenommen, das Segment ist in ES und der Offset in DI:

inc DI;jz @INCSEG;
:
:
@INCSEG: mov AX,ES;add AH,$10;mov ES,AX;jump... {wieder an Schleifenanfang ...}

Oder wenn man um mehr als 1 erhöht (z.B. 5)

add DI,5; jc @INCSEG; ... usw. {Bei add funktioniert das Carry-Flag für den Überlauf}

Der Möglichkeiten gibt es viele. Ich verwende teilweise recht raffinierte Methoden, um platz- und speichersparende Dinge mit diesem Kram zu tun... Manches passe ich direkt auf den jeweiligen Zweck an.

Ich WEIß natürlich, daß hier ein FLAT Zugriff schneller und einfacher wäre. Dann brauche ich aber auch ein restliches "Konstrukt drumherum", das auch mit diesem FLAT Mode (oder wenn gewünscht auch Protected Mode) umgehen kann. Und das normale Borland-Pascal hat das eben nicht. Ich schätze dafür andere Dinge daran - wie direktes Einfügen von ASM-Routinen/Befehle in den Sourcecode oder die Möglichkeit der direkten Übernahme von Variablen(Adressen) in den ASM-Source-Teil. Wenn man sich darauf einläßt, kann man mit der Kombination Pascal / Assembler eine Menge Dinge tun. Die nicht-386er-Code-Fähigkeit habe ich mittlerweile gelernt zu umgehen, indem ich entsprechende Befehle (oder Präfix-Bytes) selbst in den Code einsetze. Man kann sich dran gewöhnen. (Immer noch besser, als unter Windows programmieren zu müssen...)

Es kann auch sein, daß ich in o.g. Codeschnipsel irgendwelche Fehler drin hab, hab jetzt einfach aus dem Kopf irgendwas schnell hingetackert. Habe es nicht getestet.

Man muß auch nicht mit Variablen umgehen, wenn es sowieso nur normale Typen sind.


Ob man oben B^ benutzt oder gleich mem[S:O]; spielt wahrscheinlich keine große Rolle, da B^ in diesem Fall auf CPU-Ebene sowieso aus Segment S, Offset O geholt werden muß.

Ich weiß nicht GANZ GENAU, wie Pascal das dann macht. In ASSEMBLER hat man ja die Chance, den Wechsel eines Segmentregisters "so selten wie möglich" durchzuführen (da Segmentregisterwechsel etwas Zeit kosten).

Besser wäre oben übrigens ohne AX zu arbeiten, sondern zu Anfang das Segmentregister an eine Speicherstelle als Word zu speichern, dann im @INCSEG: Teil nur diese Speicherstelle (also ihr Highbyte) um $10 (16) zu erhöhen und ES daraus zu laden. (Aus Speicherstellen können Segmentregister "direkt" geladen werden, brauchen dann nicht den "Umweg" über ein Basisregister.)

Naja, usw. Würde jetzt zu weit führen.
Methoden wie diese und ähnliche verwende ich eben in meinen Programmen - wenn ich sie auf <640kB beschränke. Mittlerweile habe ich aber auch die HIMEM.SYS Routinen "liebgewonnen", d.h. ich verwende auch ganz gern mal XMS. Und, daß das ganze angeblich sooooo langsam sein soll, kann ich - zumindest auf meinem Rechner - nicht bestätigen. Ich finde es erstaunlich schnell, wie das Ganze da "hin-und-her-kopiert" wird. Man sollte es natürlich trotzdem intelligent genug programmieren, um sich auf "blockweises" Kopieren einzustellen und nicht "byteweise" zwischen "Realmode-Speicher" und XMS so Zeug hin- und herzuschaufeln...

Ich habe übrigens auch eine "XMS"-Unit gemacht, die fast 100% ASM ist, allerdings mit Procedures/Functions mit Pascal-Headern - so daß man mit einfachen Pascal-Procedures/Functions die Funktionen von HIMEM.SYS nutzen kann (XMS reservieren/freigeben, innerhalb der Bereiche kopieren/verschieben... Außerdem habe ich auch dafür etwas gemacht, das ähnlich funktioniert wie meine SpeicherUnit (mit den Pointerzuweisungen), nur daß als "Pointer" hier dann einfache Longints genutzt werden (die die Offset-Adresse im XMS-Handle enthalten).

Naja, wahrscheinlich schon wieder "too much information"...
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von zatzen »

Zu viel Information nur insofern, als ich nach jetzt ca. 3-4 Wochen jeden Tag programmieren mal
wieder etwas abschweife zum Lautsprecherbau. Aber wenn's wieder ans Eingemachte geht werd ich
die Infos sehr zu schätzen wissen!

Mein neues Musikformat steht übrigens, der Converter, daher auch weiter oben mein Verweis auf den
Thread, da hab ich halt auch noch ne Frage wegen Fixkomma.

Ich hab jetzt beim Versuch, die Bitlese-Routine nach Assembler zu übersetzen statt dem Longint
die Brüder byteposition (word) und bitoffset (byte) genommen.

Ob das überhaupt einen Gewinn an Geschwindigkeit bringt?
Werd demnächst mal was ASM'en und hier posten...
mov ax, 13h
int 10h

while vorne_frei do vor;
Benutzeravatar
zatzen
DOS-Guru
Beiträge: 518
Registriert: Di 17. Apr 2012, 05:10
Wohnort: bei Köln
Kontaktdaten:

Re: Pascal "absolute" Deklaration - mit Pointer-Array möglic

Beitrag von zatzen »

Dann hab ich noch eine Frage:

Welche Register muss ich auf dem Stack sichern, wenn ich sie in einer
Pascal-Assembler-Prozedur bzw. Function verändere?

Also für folgende Beispielfälle:

Code: Alles auswählen


procedure blabla;
  var bla: word
begin
  asm
  ...
  end;
end;


procedure laber; assembler;
asm
...
end;


function blubb: word;
begin
  asm
  ...
  end;
end;


function blablubb: word; assembler;
asm
...
end;


Was mir noch schleierhaft ist, wie ich aus dem ASM Teil heraus einer Function einen
Rückwert gebe! Das wäre mir noch wichtig zu wissen! Und ob es technisch d.h. kompiliert
einen Unterschied macht ob ich die Routinen direkt mit "assembler" hinter dem Namen
deklariere oder "normal" und dann nur asm innerhalb der Routine verwende!
mov ax, 13h
int 10h

while vorne_frei do vor;
Antworten