Hier ein umfangreiches Beispiel was man so alles optimieren kann. Code funktioniert zwar nicht mit PB 4.51 und ich weiß auf die schnelle nicht wordan es liegt, aber ich denke er ist trotzdem sehr veranschaulichend:
Code: Alles auswählen
;
; Small ASM tutorial
; Petit TUT ASM
;
; F.Weil 20040617
;
; This listing is a workshop about ASM insertion in PureBasic code. Nothing exhaustive but a possible first basis.
;
; The workshop is based on 'les taches de Martin', meaning each point of an image is set according to the average of it's neighbors.
; A limited color palette is necessary to render appropriate effect.
;
; I propose to use a regular 32 bits depth for the screen and to solve this by calculating the final color of each point.
;
; Focus is made on how to address the drawing buffer directly.
;
; Purpose of this workshop is not to show that it is possible to do much better than PureBasic as it demonstrates the opposite.
; Only in some cases using ASM will help, but most of the time not. This workshop shows how to code in PureBasic well, and how
; few improvements can be obtained using ASM coding.
;
; For a better user experience, it is possible to parse methods execution by using left / right keyboard arrows, and to change
; drawing area size using NumPad4 / NumPad6
;
; The algorithm name, frames per second obtained and drawing size are displayed in real time.
;================================================================================
; Ce listing a pour but de montrer comment étudier la transposition en ASM de certaines parties de code.
; Rien d'exhaustif mais une prise en main qui peut donner de bonnes bases.
;
; Reprise du principe des taches de Martin
;
; LÃ j'ai mis l'accent sur la gestion directe du DrawingBuffer
;
; L'algorithme proposé est de même principe que celui d'origine pour les taches de Martin, c'est à dire
; pour chaque point P(X, Y) on associe une couleur calculée à partir de la moyenne des couleurs des 4 points
; voisins + 1
; Soit couleur(P(X, Y)) = (couleur(P1(X, Y - 1)) + couleur(P2(X - 1, Y)) + couleur(P3(X + 1, Y)) + couleur(P4(X, Y + 1))) / 4 + 1
;
; Pour rendre l'effet escompté on doit travailler dans une palette limitée.
;
; Pour faire les choses sans passer par un screen en 8 bits ou autre chose, je prends le mode par défaut de l'écran en 32 bits, et
; je convertis en recalculant couleur
;
; couleur = couleur % 64 + 192
; couleur = couleur << 10 + couleur
;
; Le but de cette démonstration ne consiste pas à montrer des carences de PureBasic, bien au contraire. Il ressort que l'utilisation de l'ASM
; n'apporte que peu d'améliorations si le codage PureBasic est bien conçu par le développeur.
;
; Pour une meilleure expérimentation des méthodes ici, il est possible de naviguer sur les différents types de code en utilisant les
; flèches gauche / droite du clavier et de changer la taille de la zone de dessin avec les touches 4 / 6 du clavier numérique.
;
; Un affichage temps réel de l'algorithme utilisé, du nombre de trames par seconde et de la taille du dessin est effectué.
;
; This enumeration is made to make the different codes to executes easier to access
; Also the codes names are placed in strings to display stats
;
; Cette énumération est mise en place pour rendre l'accès aux différents algorithmes plus aisés.
; Les noms des codes à exécuter sont également placés dans un tableau de chaînes pour l'affichage des stats.
;
; Here is a chart of experimental results (inFPS) I found on my design PC
; Voici un tableau des résultats expérimentaux (en FPS) obtenus sur mon PC de développement
;
; FlipBuffers(1) 128x128 256x256 320x240 480x360 640x480 800x600
; PureBasic_Regular_Code 4 2 1 - - -
; PureBasic_Better_Code 60 30 30 21 10 8
; PureBasic_Optimized_Code 60 30 30 22 10 8
; PureBasic_LowLevel_Code 60 30 30 22 10 8
; PureBasic_Chewed_Code 60 30 30 19 9 7
; ASM_Code 60 30 30 23 10 8
; PureBasic_LowLevel_Code2 60 59 59 45 20 15
; ASM_LowLevel_Code2 60 60 59 50 22 16
;
; FlipBuffers(0) 128x128 256x256 320x240 480x360 640x480 800x600
; PureBasic_Regular_Code 5 2 1 - - -
; PureBasic_Better_Code 260 55 46 21 12 8
; PureBasic_Optimized_Code 251 50 49 22 11 8
; PureBasic_LowLevel_Code 251 50 49 22 11 8
; PureBasic_Chewed_Code 216 44 42 19 10 7
; ASM_Code 255 55 52 23 12 9
; PureBasic_LowLevel_Code2 604 126 100 45 27 17
; ASM_LowLevel_Code2 627 143 113 51 30 20
;
; Results measured on my 1.2GHz / 32MB graphic PC
; Résultats mesurés sur mon PC 1,2GHz / Graphique 32MO
;
Enumeration
#PureBasic_Regular_Code
#PureBasic_Better_Code
#PureBasic_Optimized_Code
#PureBasic_LowLevel_Code
#PureBasic_Chewed_Code
#ASM_Code
#PureBasic_LowLevel_Code2
#ASM_LowLevel_Code2
EndEnumeration
#LastCode = #ASM_LowLevel_Code2
Global tz.l, FPS.l, NFrames.l, AFrames.l, TFrames.l ; Stats
;
; ResetAll allows to clear the screen's buffers and thearrays used in the program
; ResetAll permet d'effacer les buffers et de remettre les tableaux utilisés à zéro.
Procedure ResetAll()
; FlipBuffers(1)
; ClearScreen(0)
; FlipBuffers(1)
; ClearScreen(0)
Dim DrawingArray.l(1024, 768)
Dim DrawingArea.l(1024 * 768)
tz = ElapsedMilliseconds()
FPS = 0
NFrames = 0
AFrames = 0
TFrames = 0
EndProcedure
ExecuteCode = #ASM_Code
Dim CodeType.s(10)
Dim DrawingWidth.l(10)
Dim DrawingHeight.l(10)
Global FontID1 = LoadFont(23, "Verdana", 8, #PB_Font_HighQuality | #PB_Font_Bold)
; A list of code parts names is placed in the DataSection
; Une liste des noms des parties du code est placée en DataSection.
For i = 0 To #LastCode
Read CodeType(i)
CodeType(i) + Space(40 - Len(CodeType(i)))
Next
LastSize = 5
WidthHeight = 3
; A list of drawing sizes is placed in DataSection
; Une liste des tailles de zone de dessin est donnée en DataSection
For i = 0 To LastSize
Read DrawingWidth(i)
Read DrawingHeight(i)
Next
;
; DrawingArray is a 2 dimensions array to store screen's x, y pixels in some part of the program
; DrawingArea is a single dimension array used in the same purpose in some other parts
;
; DrawinArray est un tableau à 2 dimensions utilisé pour mémoriser les pixels x, y de l'écran dans certaines parties du programme
; DrawingArea est un tableau à une seule dimension utilisé dans le même but, mais dans d'autres parties.
;
Dim DrawingArray.l(1024, 768)
Dim DrawingArea.l(1024 * 768)
; The current drawing width and heigth are set and xTop, yTop, xBottom, yBottom are bounds
; La largeur et la hauteur courante pour le dessin sont initalisées ainsi que les limites xTop, yTop, xBottom, yBottom
DrawingWidth = DrawingWidth(WidthHeight)
DrawingHeight = DrawingHeight(WidthHeight)
ScreenXSize = GetSystemMetrics_(#SM_CXSCREEN)
ScreenYSize = GetSystemMetrics_(#SM_CYSCREEN)
xTop = (ScreenXSize - DrawingWidth) / 2 + 1
yTop = (ScreenYSize - DrawingHeight) / 2 + 1
xBottom = xTop + DrawingWidth - 2
yBottom = yTop + DrawingHeight - 2
;
;
;
If InitKeyboard() And InitSprite() And OpenScreen(ScreenXSize, ScreenYSize, 32, "Taches de Matin")
StartDrawing(ScreenOutput())
DrawingFont(FontID1)
StopDrawing()
Repeat
;
; FlipBuffers is used with two possible argument value : 0 without buffer buffer sync which is shorter but
; may generate more flickering on the screen, or 1 to wait the buffer ready before to display it.
;
; Mode change is accesible using up / down arrows
;=========================================================================
; On utilise la commande FlipBuffers(0) (sans synchronisation plus rapide mais qui peut produire des scintillements
; ou FlipBuffers(1) qui attend que le buffer soit prêt avant affichage.
;
; Le changement de mode se fait en appuyant les flèches haut ou bas.
;
FlipBuffers(FlipBuffers)
StartDrawing(ScreenOutput())
Select ExecuteCode
;
; To start the following code just do what the top comments describe, using Point(x, y) to know a pixel value. Neighbors are
; injected in a formula and the given point set using Plot.
;=====================================================================================
; Pour commencer voici dans une écriture conventionnelle le code correspondant à la spécification donnée en commentaire ci-dessus.
;
; Pour l'ensemble des points à traiter, on calcule la somme des valeurs des 4 points adjacents que l'on injecte dans une formule.
;
; Le point courant est ensuite simplement dessiné à la position voulue.
;
Case #PureBasic_Regular_Code
For x = 1 To DrawingWidth - 1
For y = 1 To DrawingHeight - 1
Value = ((Point(x + xTop, y + yTop - 1) + Point(x + xTop, y + yTop + 1) + Point(x + xTop + 1, y + yTop) + Point(x + xTop - 1, y + yTop)) / 4 + 1) % 64 + 192
Color = Value << 10 + Value
Plot(x + xTop, y + yTop, Color)
Next
Next
;
; In #PureBasic_Better_Code, we do no more use Point(x, y) but an 2 dimensions array to store points values.
;
; This array contains elements corresponding to screen pixels.
;
; Using this makes the listing easy to understand but the performances are much better
;=======================================================================================
; Dans la version #PureBasic_Better_Code on ne va plus utiliser la fonction Point(x, y) mais un tableau qui permet de mémoriser les
; valeurs des points.
;
; Ce tableau est à deux dimensions et chaque élément x, y du tableau représente fidèlement un point de l'écran
;
; Par contre le tracé du point courant est fait avec la fonction Plot, dont les performances restent assez bonnes.
;
; De là l'écriture est encore assez lisible ... et les performances très différentes
;
Case #PureBasic_Better_Code
For x = 1 To DrawingWidth - 1
For y = 1 To DrawingHeight - 1
Value = ((DrawingArray(x, y - 1) + DrawingArray(x, y + 1) + DrawingArray(x - 1, y) + DrawingArray(x + 1, y)) / 4 + 1) & 63 + 192
Color = Value << 10 + Value
DrawingArray(x, y) = Color
Plot(x + xTop, y + yTop, Color)
Next
Next
;
; In #PureBasic_Optimized_Code a single dimension array is used.
;
; This makes the code a bit more difficult to understand but will make easier later optimization.
;=============================================================================
; Dans la version #PureBasic_Optimized_Code on utilise un tableau linéaire et non plus une matrice à deux dimensions.
;
; L'écriture du code est sensiblement plus lourde mais permettra d'aborder la phase d'optimisation suivante lus aisément.
;
Case #PureBasic_Optimized_Code
For x = 1 To DrawingWidth - 1
For y = 1 To DrawingHeight - 1
xy = y * DrawingWidth + x
Value = ((DrawingArea(xy - DrawingWidth) + DrawingArea(xy + DrawingWidth) + DrawingArea(xy - 1) + DrawingArea(xy + 1)) / 4 + 1) & 63 + 192
Color = Value << 10 + Value
DrawingArea(xy) = Color
Plot(x + xTop, y + yTop, Color)
Next
Next
;
; #PureBasic_LowLevel_Code uses both the array and a direct calculation of pixels addresses to the drawing buffer.
;
; This calculation will not make performances better right now, but will help to master direct copy of array values to pixels
;====================================================================================
; Dans #PureBasic_LowLevel_Code, on va utiliser simultanément un tableau pour mémoriser les points et un calcul de l'adresse des
; points dans le buffer d'écran.
;
; Ce calcul n'apportera pas directement d'améliorations de performances, mais permettra de maîtriser la copie directe d'une valeur du tableau sur l'écran.
;
Case #PureBasic_LowLevel_Code
DrawingBuffer = DrawingBuffer()
DrawingBufferPitch = DrawingBufferPitch()
For x = 1 To DrawingWidth - 1
X4 = DrawingBuffer + (x + xTop) << 2
For y = 1 To DrawingHeight - 1
xy = y * DrawingWidth + x
Value = ((DrawingArea(xy - DrawingWidth) + DrawingArea(xy + DrawingWidth) + DrawingArea(xy - 1) + DrawingArea(xy + 1)) / 4 + 1) & 63 + 192
Color = Value << 10 + Value
DrawingArea(xy) = Color
Color = (Color & $FF0000) >> 16 + (Color & $00FF00) + (Color & $0000FF) << 16
Address = DrawingBufferPitch * (y + yTop) + X4
PokeL(Address, Color)
Next
Next
;
; Here we try to "chew" the former code in order to get only simple low level instructions.
;
; Only one operation per line and no more than two variables in a single line.
;
; Thanks to PureBasic coding convention we can write :
;
; a + 1 instead of a = a + 1
; a + b instead of a = a + b
;
; etc
;
; c = a + b will be replaced by
; c = a
; c + b
;
; The resulting code will not be optimized as good as the compiler does, but it will be fast and close to machine level.
;===========================================================================
; Le but du code développé ici est de se rapprocher le plus possible des instructions les plus élémentaires.
;
; On essaye de proscrire toute écriture qui consiste à cumuler plusieurs opérations sur une même ligne
;
; Pour y parvenir il suffit d'ajouter des variables temporaires lorsque c'est nécessaire.
;
; Dans ce style d'écriture on utilisera de préférence la notation unaire de PureBasic, c'est à dire par exemple :
;
; a + 1 au lieu de a = a + 1
; a + b au lieu de a = a + b
;
; etc
;
; On s'interdit également d'utiliser trois variables sur une même ligne :
;
; c = a + b sera donc déployé en 2 lignes
; c = a
; c + b
;
; Cette écriture donnera un code très rapide, même si il n'est pas tout à fait aussi rapide que le modèle #PureBasic_LowLevel_Code précédent
;
; Cette étape est une manière réaliste de passer de l'optimisation langage évolué à l'optimisation assembleur. La tentative de traduire
; directement le code #PureBasic_Optimized_Code est généralement vouée à de malheureuses déconvenues et n'apporte jamais de meilleurs résultats
; que le code généré par le compilateur.
;
Case #PureBasic_Chewed_Code
DrawingBuffer = DrawingBuffer()
DrawingBufferPitch = DrawingBufferPitch()
For x = 1 To DrawingWidth - 1
X4 = x
X4 + xTop
X4 << 2
X4 + DrawingBuffer
For y = 1 To DrawingHeight - 1
xy = DrawingWidth
xy * y
xy + x
a = xy
a - DrawingWidth
Value = DrawingArea(a)
a = xy
a + DrawingWidth
Value + DrawingArea(a)
a = xy
a - 1
Value + DrawingArea(a)
a = xy
a + 1
Value + DrawingArea(a)
Value >> 2
Value + 1
Value & 63
Value + 192
Color = Value
Color << 10
Color + Value
DrawingArea(xy) = Color
Color1 = Color
Color1 & $FF0000
Color1 >> 16
Color2 = Color
Color2 & $00FF00
Color3 = Color
Color3 & $0000FF
Color3 << 16
Color = Color1
Color + Color2
Color + Color3
Address = y
Address + yTop
Address * DrawingBufferPitch
Address + X4
PokeL(Address, Color)
Next
Next
;
; Here we take the chewed code and translate it to ASM with some more optimization
;
; Especially we take care to avoid to store values in unnecessary variables.
;
; We can consider processor's registers as variables but with the limitation that any PureBasic instruction inserted between ASM lines
; may change registers values.
;
; So this is necessary to stay in ASM as far as possible to not loose registers values otherwise it is necessary to store registers all the time.
;=========================================================================================
; Le code #ASM_Code est la reprise du code #PureBasic_Chewed_Code avec quelques optimisations
;
; On s'attache en particulier à éliminer le report de valeurs de registres dans des variables temporaires.
;
; La limitation en nombre de registres oblige bien entendu à être très soigneux dans le choix des variables temporaires éliminées.
;
; On peut considérer pratiquement que les registres sont des variables, mais en conservant à l'esprit que l'exécution
; de toute instruction langage évolué remet en question l'état de tous les registres CPU.
;
; Il faut par conséquent rester en assembleur le plus longtemps possible pour éviter de risquer de perdre la valeur d'un registre en cours de route,
; ou inversement d'être contraint de sauvegarder tel ou tel registre dans une variable mémoire ce qui diminuerait la qualité d'optimisation.
;
Case #ASM_Code
DrawingBuffer = DrawingBuffer()
DrawingBufferPitch = DrawingBufferPitch()
X4.l
xy.l
For x = 1 To DrawingWidth - 1
! MOV eax, dword[v_x] ; eax = x
! ADD eax, dword[v_xTop] ; eax + xTop
! SAL eax, 2 ; eax << 2
! ADD eax, dword[v_DrawingBuffer] ; eax + DrawingBuffer
! MOV dword[v_X4], eax ; X4 = eax
For y = 1 To DrawingHeight - 1
! MOV eax, dword[v_DrawingWidth] ; eax = DrawingWidth
! IMUL eax, dword[v_y] ; eax * y
! ADD eax, dword[v_x] ; eax + x
! MOV ebp, dword[a_DrawingArea] ; ebp = @DrawingArea()
! MOV ecx, eax ; ecx = eax
! SUB ecx, dword[v_DrawingWidth] ; ecx - DrawingWidth
! SAL ecx, 2 ; ecx * 4
! MOV edi, dword[ebp+ecx] ; edi = PeekL(ebp + ecx)
! MOV ecx, eax ; ecx = eax
! ADD ecx, dword[v_DrawingWidth] ; ecx + DrawingWidth
! SAL ecx, 2 ; ecx * 4
! ADD edi, dword[ebp+ecx] ; edi + PeekL(ebp + ecx)
! MOV ecx, eax ; ecx = eax
! DEC ecx ; ecx - 1
! SAL ecx, 2 ; ecx * 4
! ADD edi, dword[ebp+ecx] ; edi + PeekL(ebp+ecx)
! ADD ecx, 8 ; ecx + 8 (soit ecx = ((eax - 1) + 2) * 4
! ADD edi, dword[ebp+ecx] ; edi + PeekL(ebp + ecx)
! SAR edi, 2 ; edi >> 2
! INC edi ; edi + 1
! And edi, 63 ; edi & 63
! ADD edi, 192 ; edi + 192
! MOV edx, edi ; edx = edi
! MOV ebx, edx ; ebx = edx
! SAL edx, 10 ; eax = edx << 10
! ADD edx, ebx ; edx = ebx
! MOV ecx, eax ; ecx = eax
! SAL ecx, 2 ; ecx * 4
! MOV [ebp+ecx], edx ; PokeL(ebp+ecx, edx)
! MOV ecx, dword[v_y] ; ecx = y
! ADD ecx, dword[v_yTop] ; ecx + yTop
! IMUL ecx, dword[v_DrawingBufferPitch] ; ecx * DrawingBufferPitch
! ADD ecx, dword[v_X4] ; ecx + X4
! BSWAP edx ; edx (00RRGGBB) => (BBGGRR00)
! SAR edx, 8 ; edx(BBGGRR00) => (00BBGGRR)
! MOV [ecx], edx ; PokeL(ecx, edx)
Next
Next
;
; In #PureBasic_LowLevel_Code2 the same idea is used than in former #PureBasic_LowLevel_Code but addressing is linearized
; Buffer addressing is no more x, y based but only a single variable is used.
;
; The address of the horizontal neighbor of a given point is the address of this point -/+ 1.
; The address of the vertical neighbor of a given point is the address of this point -/+ DrawingWidth.
;
; Using this method the point to draw is accessed the fastest.
;
; Meantime pixels values are stored in the array which is addressed using two variables to calculate unary index
; of the array easily, allowing to avoid the double address calculation.
;
; Obviously this does not look far from former code, it renders completely different results.
;
; In order to access to the best possible ASM optimization later, For / Next loops are replaced by Repeat / Until.
;
; Pixel value calculation is unchanged.
;=====================================================================
; Dans la version #PureBasic_LowLevel_Code2 on reprend le même principe mais on a linéarisé l'adressage
; Ici le buffer de l'écran est adressé avec une seule variable d'adresse qui est incrémentée de 4 octets pour passer
; point suivant et d'une ligne moins la DrawingWidth de tracé pour passer à la ligne suivante.
;
; Cette méthode permet d'adresser le pixel à tracer au plus vite.
;
; Dans le même temps les valeurs des points sont toujours stockées dans un tableau qui lui est adressé
; à partir de deux variables x et y en recalculant l'indice unaire du tableau pour éviter le double calcul d'adresse.
;
; La méthode paraît peu différente à priori, mais les résultats n'ont rien à voir.
;
; Pour amener à une meilleure optimisation ultérieure en assembleur, les boucles For / Next ont été remplacées par des Repeat / Until
;
; Le calcul de valeur d'un pixel reste inchangé.
;
Case #PureBasic_LowLevel_Code2
DrawingBuffer = DrawingBuffer()
DrawingBufferPitch = DrawingBufferPitch()
BufferAddress = DrawingBuffer + 4 * (xTop + 1) + yTop * DrawingBufferPitch
ArrayAddress = DrawingWidth + 1
y = 1
Repeat
x = 1
Repeat
Value = ((DrawingArea(ArrayAddress - DrawingWidth) + DrawingArea(ArrayAddress + DrawingWidth) + DrawingArea(ArrayAddress - 1) + DrawingArea(ArrayAddress + 1)) / 4 + 1) & 63 + 192
Color = Value << 10 + Value
DrawingArea(ArrayAddress) = Color
Color = (Color & $FF0000) >> 16 + (Color & $00FF00) + (Color & $0000FF) << 16
PokeL(BufferAddress, Color)
BufferAddress + 4
ArrayAddress + 1
x + 1
Until x > DrawingWidth - 1
ArrayAddress + 1
BufferAddress + DrawingBufferPitch - 4 * (DrawingWidth - 1)
y + 1
Until y > DrawingHeight - 1
;
; Here is the optimized ASM listing of the #PureBasic_LowLevel_Code2
;======================================================
; Voici une version assembleur optimisée pour le code #PureBasic_LowLevel_Code2
;
Case #ASM_LowLevel_Code2
DrawingBuffer = DrawingBuffer()
DrawingBufferPitch = DrawingBufferPitch()
! MOV ebx, dword[v_DrawingBuffer] ; ebx = DrawingBuffer
! MOV edi, dword[v_xTop] ; edi = xTop
! INC edi ; edi + 1
! SAL edi, 2 ; edi * 4
! ADD ebx, edi ; ebx + edi
! MOV edi, dword[v_yTop] ; edi = yTop
! IMUL edi, dword[v_DrawingBufferPitch] ; edi * DrawingBufferPitch
! ADD ebx, edi ; ebx + edi
! MOV dword[v_BufferAddress], ebx ; BufferAddress = ebx
! MOV ebx, dword[v_DrawingWidth] ; ebx = DrawingWidth
! INC ebx ; ebx + 1
! MOV dword[v_ArrayAddress], ebx ; ArrayAddress = ebx
! MOV dword[v_y], 1 ; y = 1
!_RepeatBN21: ; Repeat / Until return label
! MOV dword[v_x], 1 ; x = 1
!_RepeatBN22: ; Repeat / Until return label
! MOV ebp, dword[a_DrawingArea] ; ebp = DrawingArea
! MOV ecx, dword[v_ArrayAddress] ; ecx = @ArrayAddress
! MOV eax, ecx ; eax = ecx
! SUB eax, dword[v_DrawingWidth] ; eax - DrawingWidth
! SAL eax, 2 ; eax * 4
! MOV edi, dword[ebp+eax] ; edi = PeekL(ebp+eax]
! MOV eax, ecx ; eax = ecx
! ADD eax, dword[v_DrawingWidth] ; eax - DrawingWidth
! SAL eax, 2 ; eax * 4
! ADD edi, dword[ebp+eax] ; edi = PeekL(ebp+eax]
! MOV eax, ecx ; eax = ecx
! DEC eax ; eax - 1
! SAL eax, 2 ; eax * 4
! ADD edi, dword[ebp+eax] ; edi = PeekL(ebp+eax]
! ADD eax, 8 ; eax + 8
! ADD edi, dword[ebp+eax] ; edi = PeekL(ebp+eax]
! SAR edi, 2 ; edi / 4
! INC edi ; edi + 1
! And edi, 63 ; edi & 63
! ADD edi, 192 ; edi + 192
! MOV ebx, edi ; ebx = edi
! SAL ebx, 10 ; ebx << 10
! ADD ebx, edi ; ebx + edi
! MOV eax, ecx ; eax = ecx
! SAL eax, 2 ; eax * 4
! MOV dword[ebp+eax], ebx ; PokeL(ebp+eax, ebx)
! BSWAP ebx ; ebx (00RRGGBB) => (BBGGRR00)
! SAR ebx, 8 ; ebx(BBGGRR00) => (00BBGGRR)
! MOV eax,dword[v_BufferAddress] ; eax = @BufferAddress
! MOV [eax], ebx ; PokeL(eax, ebx)
! ADD dword[v_BufferAddress], 4 ; BufferAddress + 4
! INC dword[v_ArrayAddress] ; ArrayAddress + 1
! INC dword[v_x] ; x + 1
! MOV ebx, dword[v_x] ; ebx = x
! MOV edi, dword[v_DrawingWidth] ; edi = DrawingWidth
! DEC edi ; edi - 1
! CMP ebx, edi ; if ebx <= edi
! JLE _RepeatBN22 ; Goto RepeatBN22
! INC dword[v_ArrayAddress] ; ArrayAddress + 1
! MOV ebx, dword[v_DrawingWidth] ; ebx = DrawingWidth
! DEC ebx ; ebx - 1
! SAL ebx, 2 ; ebx / 4
! NEG ebx ; -ebx
! ADD ebx, dword[v_DrawingBufferPitch] ; ebx + DrawingBufferPitch
! ADD dword[v_BufferAddress], ebx ; BufferAddress + ebx
! INC dword[v_y] ; y + 1
! MOV edi, dword[v_DrawingHeight] ; edi = DrawingHeight
! ADD edi, -1 ; edi - 1
! MOV ebx, dword[v_y] ; ebx = y
! CMP ebx, edi ; if ebx <= edi
! JLE _RepeatBN21 ; Goto RepeatBN21
EndSelect
;
; Keyboard events part
;============================
; Section de gestion des évènements clavier
;
ExamineKeyboard()
; Change the code part to execute
; Modifie la section de code à exécuter
If KeyboardPushed(#PB_Key_Left)
ResetAll()
ExecuteCode - 1
If ExecuteCode < 0
ExecuteCode = 0
EndIf
Repeat
ExamineKeyboard()
Until KeyboardReleased(#PB_Key_Left)
EndIf
If KeyboardPushed(#PB_Key_Right)
ResetAll()
ExecuteCode + 1
If ExecuteCode > #LastCode
ExecuteCode = #LastCode
EndIf
Repeat
ExamineKeyboard()
Until KeyboardReleased(#PB_Key_Right)
EndIf
; Set / Unset the buffers synchronization
; Valide / invalide la synchronisation des buffers
If KeyboardPushed(#PB_Key_Up)
ResetAll()
FlipBuffers = 1 - FlipBuffers
Repeat
ExamineKeyboard()
Until KeyboardReleased(#PB_Key_Up)
EndIf
If KeyboardPushed(#PB_Key_Down)
ResetAll()
FlipBuffers = 1 - FlipBuffers
Repeat
ExamineKeyboard()
Until KeyboardReleased(#PB_Key_Down)
EndIf
; Change the drawing area size
; Modifie la taille de la zone de dessin
If KeyboardPushed(#PB_Key_Pad4)
ResetAll()
WidthHeight - 1
If WidthHeight < 0
WidthHeight = 0
EndIf
Repeat
ExamineKeyboard()
Until KeyboardReleased(#PB_Key_Pad4)
DrawingWidth = DrawingWidth(WidthHeight)
DrawingHeight = DrawingHeight(WidthHeight)
xTop = (ScreenXSize - DrawingWidth) / 2 + 1
yTop = (ScreenYSize - DrawingHeight) / 2 + 1
xBottom = xTop + DrawingWidth - 2
yBottom = yTop + DrawingHeight - 2
EndIf
If KeyboardPushed(#PB_Key_Pad6)
ResetAll()
WidthHeight + 1
If WidthHeight > LastSize
WidthHeight = LastSize
EndIf
Repeat
ExamineKeyboard()
Until KeyboardReleased(#PB_Key_Pad6)
DrawingWidth = DrawingWidth(WidthHeight)
DrawingHeight = DrawingHeight(WidthHeight)
xTop = (ScreenXSize - DrawingWidth) / 2 + 1
yTop = (ScreenYSize - DrawingHeight) / 2 + 1
xBottom = xTop + DrawingWidth - 2
yBottom = yTop + DrawingHeight - 2
EndIf
; Control the elapsed time since the last integer second
; Contrôle le temps écoulé depuis la dernière seconde entière
If ElapsedMilliseconds() - tz => 1000
tz = ElapsedMilliseconds()
FPS = NFrames
TFrames + FPS
NFrames = 0
AFrames + 1
EndIf
NFrames + 1
; Display statistics and parameters
; Affiche les statistiques et les paramètres
BackColor(0)
FrontColor(RGB(255, 255, 255))
DrawText(10, 40, "FPS " + Str(FPS) + " Average : " + StrF(TFrames / AFrames, 2))
DrawText(10, 60, CodeType(ExecuteCode) + " ")
DrawText(10, 80, "FlipBuffers(" + Str(FlipBuffers) + ")")
DrawText(10, 100, Str(DrawingWidth) + "x" + Str(DrawingHeight))
StopDrawing()
Until KeyboardReleased(#PB_Key_Escape)
EndIf
End
DataSection
Data.s "#PureBasic_Regular_Code", "#PureBasic_Better_Code", "#PureBasic_Optimized_Code", "#PureBasic_LowLevel_Code"
Data.s "#PureBasic_Chewed_Code", "#ASM_Code", "#PureBasic_LowLevel_Code2", "#ASM_LowLevel_Code2"
Data.l 128, 128
Data.l 256, 256
Data.l 320, 240
Data.l 480, 360
Data.l 640, 480
Data.l 800, 600
EndDataSection