| |
|
|
Sven Bader | Das Jonglieren mit Datentypen kann im Alltag nerven. Zum besseren Verständnis für mich selbst habe ich etwas mit Rückgabewerten von OpenGL herumgespielt. Dankenswerterweise kann man dort immer zwischen Float, Double und Integer wählen. Mit 32-Bit Floats können ältere XProfan Versionen erst einmal nichts anfangen aber auch das bekommt man hin!
Im Beispiel frage ich die maximale Größe für einen gezeichneten Punkt ab. Zurück kommt die Adresse eines Arrays mit zwei Werten, mich interessiert jeweils der 2. Wert. Bei meiner Grafikkarte übrigens 189.875
Ich habe Procs und auch Assembler Funktionen geschrieben, um das interne Float() und Long() zu ersetzen. Letztendlich tue ich m Falle von Float 7x das gleiche auf unterschiedlichen Wegen.
Es ist mein erster Ausflug zu Assembler, weshalb die Kommentare am Code noch etwas unbeholfen sind. Letztendlich ist es genau für solche Speicher-Schubsereien ideal. Schneller als ihre Vorbilder long() und float() sind die Funktionen NICHT geworden.
Fazit: Über den Lerneffekt hinaus sind folgende Wege die einfachsten und auch nicht langsamer als der ASM Code:
Verarbeitung eines 32-Bit Floats aus einem externen Aufruf mithilfe von long und double: float! = double(integer&) bzw. als Bereich float! = double(long(bereich#,0)) Alternativ ab XProfan X2, mit dem Datentyp Single arbeiten.
Verarbeitung eines 64-Bit Floats (Double) aus einem externen Aufruf: Direkt, ohne Konvertierung bzw. als Bereich float! = float(bereich#,0)
Verarbeitung eines 32-Bit Integers aus einem externen Aufruf: Direkt, ohne Konvertierung bzw. als Bereich integer& = long(bereich#,0)
Jede Variable und auch Arrays können wie ein Bereich verwendet werden mit Addr(): long(Addr(integer&[0],4) ist das gleiche wie integer&[1], also ein Integer bei Adresse des ersten Array-Elements + 4 Byte.
Mit Speicheradressen (Bereichsvariablen # und addr()) kann gerechnet werden: long(bereich#,4) ist das gleiche wie long(bereich# + 4,0)
Beim Herumspielen mit Speicheradressen muss man selbstverständlich aufpassen, da man einerseits schnell über deklarierte Bereiche hinausschießt oder sogar Werte mit Speicheradressen verwechelt und irgendwo im Speicher landet.
Cls
Declare sizeRangeFloat#, sizeRangeInteger#, float!,sizeRangeArray![2],integerArray&[2]
Declare single singleArray[2]' das geht erst seit X2
Dim sizeRangeFloat#, 16'2 Werte je 8 Byte
Dim sizeRangeInteger#, 8'2 Werte je 4 Byte
oGL("Init", %hWnd, 1,1,1,1)
oGL("glGetDoublev", ~GL_POINT_SIZE_RANGE, Addr(sizeRangeArray![0]))
oGL("glGetDoublev", ~GL_POINT_SIZE_RANGE, sizeRangeFloat#)
oGL("glGetFloatv", ~GL_POINT_SIZE_RANGE, Addr(singleArray[0]))'das geht erst seit X2
oGL("glGetFloatv", ~GL_POINT_SIZE_RANGE, Addr(integerArray&[0]))'Trick für ältere Profan-Versionen, es folgt double()
oGL("glGetIntegerv", ~GL_POINT_SIZE_RANGE, sizeRangeInteger#)
oGL("Done")
Proc procLong
Parameters a#, shift&
'in Tests nicht immer korrekt
Return ( (Byte(a#+shift&,3) << 24) | (Byte(a#+shift&,2) << 16) | (Byte(a#+shift&,1) << 8) | (Byte(a#+shift&,0)) )
EndProc
ASM "asmFloat2", 3
mov ecx, par1'par1 nach ecx
mov ebx, par2'par2 nach ebx
mov eax, [ebx]'par2 nach eax
mov [ecx], eax' eax nach par1
add ecx, 4' par1 Adresse + 4
mov edx, par3' par2 nach edx
mov eax, [edx]' par3 nach eax
mov [ecx], eax' eax nach par1
ENDASM
ASM "asmFloat", 2
mov eax, par2'par2 nach eax
mov edx, [eax]'4-Byte par2 (offset 0) nach edx schieben
mov eax, par1'par1 nach eax (Speicheradresse der Ziel Float-Variable)
mov [eax], edx
mov eax, par2'par2 nach eax
add eax, 4'Adresse + 4
mov edx, [eax]
mov eax, par1
add eax, 4'Adresse + 4
mov [eax], edx' eax (jetzt+4) in par1 speichern
ENDASM
'Diese Funktion tut eigentlich nichts:
'sie gibt das heraus, was man hineingeworfen hat
ASM "asmLong", 1
mov ecx, par1'par 1 nach ecx
mov eax, [ecx]'und unverändert ausgeben
ENDASM
'Diese Funktion tut eigentlich nichts:
'sie gibt das heraus, was man hineingeworfen hat
ASM "asmLong2", 2
mov ecx, par1'par 1 nach ecx
mov ebx, par2'par 2 nach ebx
add ecx, par2'par 1 (ecx) und par2 addieren
mov eax, [ecx]'ausgeben
ENDASM
Def MoveMemory(3) !"kernel32.dll","RtlMoveMemory"
Proc procFloatMoveMemory
Parameters dest!, source#
MoveMemory(addr(dest!),source#,8)
Return dest!
EndProc
Proc procFloatLongLong
Parameters dest!, source#
Long addr(dest!),0 = long(source#,8),long(source#,12)
EndProc
Print "\nFloats:"
'Offset jeweils bei Byte 8, das ist der zweite Array-Wert
'Float() Assembler-Variante
asmFloat(addr(float!), sizeRangeFloat# + 8)'direkte Zuweisung geht leider Aufgrund von 32-Bit Paremetern nicht
Print float!
'Float() als interne XProfan-Funktion
Print Float(sizeRangeFloat#,8)
'Float() als Proc mit zwei long()
procFloatLongLong(float!,sizeRangeFloat#+8)'Direkte Rückgabe nicht möglich, außer mit float() und das sollte ja eben ersetzt werden ;-)
print str$(float!)
'Float() als Proc mit MoveMemory
Print procFloatMoveMemory(float!, sizeRangeFloat#+8)'Zuweisung möglich aber die Variable im 1. Paremeter wird in jedem Fall auch beschrieben
'Wert aus Array (1 = zweiter Wert, da es von 0 startet)
Print sizeRangeArray![1]'Zuweisung möglich aber die Variable im 1. Paremeter wird in jedem Fall auch beschrieben
'Wert aus Single-Array (32-Bit), Datentyp ab X2 verfügbar
Print singleArray[1]
'Wert aus Integer-Array (32-Bit) als Double intepretiert. Das geht auch mit älteren Profan Versionen
Print double(integerArray&[1])
Print "\nIntegers:"
'Long() als Proc
Print procLong(sizeRangeInteger#,4)'nicht immer korrekt...
'Long() als interne XProfan-Funktion
Print Long(sizeRangeInteger#,4)
'Long() Assembler-Variante in einem Parameter (Adress-Offset muss selbst addiert werden)
Print asmLong(sizeRangeInteger# + 4)
'Long() Assembler-Variante mit 2 Parametern, Offset kann wie in der originalen Funktion übergeben werden
Print asmLong2(sizeRangeInteger#, 4)
Dispose sizeRangeFloat#
Dispose sizeRangeInteger#
WaitKey
End
|
|
|
| |
|
|