Ben moi je vous ai fait ça pour broder un peu autour des tâches et de l'assembleur ...
Code : Tout sélectionner
;
; 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, 0, 0)
FlipBuffers(1)
ClearScreen(0, 0, 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)
; 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(LoadFont(23, "Verdana", 8, #PB_Font_HighQuality | #PB_Font_Bold))
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, 0, 0)
FrontColor(255, 255, 255)
Locate(10, 40)
DrawText("FPS " + Str(FPS) + " Average : " + StrF(TFrames / AFrames, 2))
Locate(10, 60)
DrawText(CodeType(ExecuteCode) + " ")
Locate(10, 80)
DrawText("FlipBuffers(" + Str(FlipBuffers) + ")")
Locate(10, 100)
DrawText(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