Nieuws:

Welkom, Gast. Alsjeblieft inloggen of registreren.
Heb je de activerings-mail niet ontvangen?

Auteur Topic: Timer / framerate capping in x86 Linux assembler (NASM)  (gelezen 1623 keer)

Offline profoX

  • Lid
    • wesley
    • Lionslink
Timer / framerate capping in x86 Linux assembler (NASM)
« Gepost op: 2009/07/26, 15:40:23 »
Ik ben een beetje met x86 Linux assembler / NASM aan het spelen.
Na wat zoeken heb ik de nodige zaken bij elkaar gepuzzeld om de GLUT-bibliotheek via assembler aan te roepen.
Nu heb ik dus - na porten van voorbeeldcode van NeHe.gamedev.net - enkele ronddraaiende objecten.
Nu is mijn vraag echter.. heeft iemand een idee hoe ik de framerate hiervan zou kunnen cappen naar bv. ~30fps?
Ik heb niet meteen bruikbare kernelfuncties hiervoor kunnen vinden.
Misschien kan ik wel ergens de microseconden sinds opstart vinden.. iemand hier ervaring mee?

Ik vind mijn data section trouwens ook niet zo netjes.
Is het echt niet mogelijk om floating points in NASM te gebruiken zonder deze vooraf te moeten declareren?
Als iemand hier in geslaagd is, let me know please :-*

Voor de geïnteresseerden, een port van de eerste lessenreeks naar assembler/GLUT:

; OpenGL example in x86 Linux assembler / NASM using Glut
; Author: Wesley Stessens [wesley@ubuntu.com]
; Original sample code for C/Windows by Jeff Molofee (NeHe)
;
; This code is released as sample code in the public domain
; Use it for whatever you want...

; Glut
extern glutInit, glutInitDisplayMode, glutInitWindowSize, glutInitWindowPosition
extern glutCreateWindow, glutMainLoop
extern glutDisplayFunc, glutIdleFunc, glutReshapeFunc, glutKeyboardFunc
extern glutSwapBuffers

; Glu
extern gluPerspective

; OpenGL
extern glClearColor, glClearDepth, glDepthFunc, glEnable, glShadeModel
extern glClear, glLoadIdentity, glMatrixMode, glViewport
extern glTranslatef, glRotatef, glBegin, glEnd, glVertex3f, glColor3f

; Macros
%macro _3call 4
push dword %4
push dword %3
push dword %2
call %1
add esp, 12
%endmacro
%macro _4call 5
push dword %5
push dword %4
push dword %3
push dword %2
call %1
add esp, 16
%endmacro

section .data
title: db 'OpenGL in x86 Linux assembler / NASM using Glut', 0
n7: dd -7.0
n6: dd -6.0
n1p5: dd -1.5
n1: dd -1.0
p0p4: dd 0.4
p0p5: dd 0.5
p1: dd 1.0
p1p5: dd 1.5
p3: dd 3.0
rottrm: dd 0.3
rotsqm: dd -0.2

q0p1: dq 0.1
q1: dq 1.0
q45: dq 45.0
q100: dq 100.0

section .bss
rottr: resd 1
rotsq: resd 1
aspr: resq 1

section .text
global _start

display:
; Prepare for drawing
push dword 4100h ; GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT
call glClear
add esp, 4
call glLoadIdentity

; Draw pyramid on the left of the screen
_3call glTranslatef, [n1p5], 0, [n6]
_4call glRotatef, [rottr], 0, [p1], 0
push dword 9h ; GL_POLYGON
call glBegin
add esp, 4
_3call glColor3f, [p1], 0, 0 ; Start drawing front face
_3call glVertex3f, 0, [p1], 0
_3call glColor3f, 0, [p1], 0
_3call glVertex3f, [n1], [n1], [p1]
_3call glColor3f, 0, 0, [p1]
_3call glVertex3f, [p1], [n1], [p1]
_3call glColor3f, [p1], 0, 0 ; Start drawing right face
_3call glVertex3f, 0, [p1], 0
_3call glColor3f, 0, 0, [p1]
_3call glVertex3f, [p1], [n1], [p1]
_3call glColor3f, 0, [p1], 0
_3call glVertex3f, [p1], [n1], [n1]
_3call glColor3f, [p1], 0, 0 ; Start drawing back face
_3call glVertex3f, 0, [p1], 0
_3call glColor3f, 0, [p1], 0
_3call glVertex3f, [p1], [n1], [n1]
_3call glColor3f, 0, 0, [p1]
_3call glVertex3f, [n1], [n1], [n1]
_3call glColor3f, [p1], 0, 0 ; Start drawing right face
_3call glVertex3f, 0, [p1], 0
_3call glColor3f, 0, 0, [p1]
_3call glVertex3f, [n1], [n1], [n1]
_3call glColor3f, 0, [p1], 0
_3call glVertex3f, [n1], [n1], [p1]
call glEnd

; Draw cube on the right of the screen
call glLoadIdentity
_3call glTranslatef, [p1p5], 0, [n7]
_4call glRotatef, [rotsq], [p1], [p0p4], [p0p5]
push dword 7h ; GL_QUADS
call glBegin
add esp, 4
_3call glColor3f, 0, [p1], 0 ; Start drawing top face
_3call glVertex3f, [p1], [p1], [n1]
_3call glVertex3f, [n1], [p1], [n1]
_3call glVertex3f, [n1], [p1], [p1]
_3call glVertex3f, [p1], [p1], [p1]
_3call glColor3f, [p1], [p0p5], 0 ; Start drawing bottom face
_3call glVertex3f, [p1], [n1], [p1]
_3call glVertex3f, [n1], [n1], [p1]
_3call glVertex3f, [n1], [n1], [n1]
_3call glVertex3f, [p1], [n1], [n1]
_3call glColor3f, [p1], 0, 0 ; Start drawing front face
_3call glVertex3f, [p1], [p1], [p1]
_3call glVertex3f, [n1], [p1], [p1]
_3call glVertex3f, [n1], [n1], [p1]
_3call glVertex3f, [p1], [n1], [p1]
_3call glColor3f, [p1], [p1], 0 ; Start drawing back face
_3call glVertex3f, [p1], [n1], [n1]
_3call glVertex3f, [n1], [n1], [n1]
_3call glVertex3f, [n1], [p1], [n1]
_3call glVertex3f, [p1], [p1], [n1]
_3call glColor3f, 0, 0, [p1] ; Start drawing left face
_3call glVertex3f, [n1], [p1], [p1]
_3call glVertex3f, [n1], [p1], [n1]
_3call glVertex3f, [n1], [n1], [n1]
_3call glVertex3f, [n1], [n1], [p1]
_3call glColor3f, [p1], 0, [p1] ; Start drawing right face
_3call glVertex3f, [p1], [p1], [n1]
_3call glVertex3f, [p1], [p1], [p1]
_3call glVertex3f, [p1], [n1], [p1]
_3call glVertex3f, [p1], [n1], [n1]
call glEnd

; Update rotation
fld dword [rottr]
fadd dword [rottrm]
fstp dword [rottr]
fld dword [rotsq]
fadd dword [rotsqm]
fstp dword [rotsq]

; Swap buffers and display
call glutSwapBuffers
ret

reshape:
push ebp
mov ebp, esp

; Prevent division by zero
cmp dword [ebp+8], 0
jne reshape_2
inc dword [ebp+8]

reshape_2:
fild dword [ebp+8]
fidiv dword [ebp+12]
fstp qword [aspr]

push dword [ebp+12]
push dword [ebp+8]
push dword 0
push dword 0
call glViewport
add esp, 16
push dword 1701h; GL_PROJECTION
call glMatrixMode
add esp, 4
call glLoadIdentity

push dword [q100+4]
push dword [q100]
push dword [q0p1+4]
push dword [q0p1]
push dword [aspr+4]
push dword [aspr]
push dword [q45+4]
push dword [q45]
call gluPerspective
add esp, 32

push dword 1700h; GL_MODELVIEW
call glMatrixMode
add esp, 4

pop ebp
ret

keyhandler:
ret

_start:
; Initialize glut
push dword [esp+8]
lea eax, [esp+8]
push eax
call glutInit
add esp, 8
push dword 1ah ; GLUT_RGBA|GLUT_DOUBLE|GLUT_ALPHA|GLUT_DEPTH
call glutInitDisplayMode
add esp, 4
push dword 480
push dword 640
call glutInitWindowSize
add esp, 8
push dword 0
push dword 0
call glutInitWindowPosition
add esp, 8

; Create window
push title
call glutCreateWindow
add esp, 4

; Register functions
push display
call glutDisplayFunc
call glutIdleFunc
add esp, 4
push reshape
call glutReshapeFunc
add esp, 4
push keyhandler
call glutKeyboardFunc
add esp, 4

; Initialize OpenGL
push dword 0
push dword 0
push dword 0
push dword 0
call glClearColor
add esp, 16
push dword [q1+4]
push dword [q1]
call glClearDepth
add esp, 8
push dword 203h; GL_LEQUAL
call glDepthFunc
add esp, 4
push dword 0b71h; GL_DEPTH_TEST
call glEnable
add esp, 4
push dword 1d01h; GL_SMOOTH
call glShadeModel
add esp, 4

; Enter main loop
call glutMainLoop

; Exit (return with error code 0)
mov eax, 1
mov ebx, 0
int 80h

Compileren en linken kan met: nasm -felf filename.asm && ld -lglut -s -I/lib/ld-linux.so.2 -o filename filename.o

Het was toch wel een karwij om alles bij elkaar te puzzelen. Er is verrassend weinig informatie te vinden wat betreft NASM / Linux.
Men verwacht meestal dat je vroeger al met assembler in DOS hebt gewerkt...
Human Knowledge Belongs To The World -- Antitrust (2001)
Nederlandstalige Ubuntu documentatie van Ubuntu-NL (wiki)

Offline profoX

  • Lid
    • wesley
    • Lionslink
Re: Timer / framerate capping in x86 Linux assembler (NASM)
« Reactie #1 Gepost op: 2009/07/27, 06:07:45 »
Ik heb het zo min of meer opgelost :) de huidige implementatie zit wel niet zo mooi in elkaar en pas na de overschakeling van een seconde (max. 1 seconde dus) is de framerate fatsoenlijk afgekapt, maar het werkt goed genoeg om op verschillende computers en op verschillende venstergroottes min of meer dezelfde snelheid te krijgen :)

Ik heb de low-level sys_gettimeofday kernel call gebruikt, in combinatie met usleep.. implementatie is als volgt:

; Needed for framerate control
extern usleep

section .data
    ; Data for framerate control
    mspf: dd 20000 ; ms per frame

section .bss
    ; Vars for framerate control
    time: resd 2
    usecs: resd 1
    oldslp: resd 1

section .text
fpsmanager:
    push eax
    push ebx
    push ecx
    push edx

    mov eax, 78 ; sys_gettimeofday
    mov ebx, time
    mov ecx, 0
    int 80h

    mov eax, [time + 4]
    sub eax, [usecs]
    mov ebx, [mspf]
    sub ebx, eax

    test eax, eax
    js keep_sleep
    test ebx, ebx
    js keep_sleep

    push ebx
    call usleep
    add esp, 4
    mov dword [oldslp], ebx
    jmp done_sleep

keep_sleep:
    push dword [oldslp]
    call usleep
    add esp, 4

done_sleep:
    mov eax, [time + 4]
    mov dword [usecs], eax

    pop edx
    pop ecx
    pop ebx
    pop eax
    ret

Na glutSwapBuffers roep ik dan deze fpsmanager aan.

Voor de geïnteresseerden, het volledige programma is dus nu:

; OpenGL example in x86 Linux assembler / NASM using Glut
; Author: Wesley Stessens (wesley@ubuntu.com)
;
; OpenGL calls to create pyramid and cube are from Jeff Molofee's OpenGL tutorial
; See: http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=05
;
; Features:
;  - Calling C functions by cdecl conventions
;  - Using GLUT and OpenGL calls to perform 3D graphics
;  - Low-level framerate control
;
; Sample compile instructions:
; nasm -felf ogl.asm && ld -lglut -s -I/lib/ld-linux.so.2 -o ogl ogl.o
;
; This code is released as sample code in the public domain. Use it for whatever you want.
; Feel free to let me know if you found this code useful in any way.

; Glut
extern glutInit, glutInitDisplayMode, glutInitWindowSize, glutInitWindowPosition
extern glutCreateWindow, glutMainLoop
extern glutDisplayFunc, glutIdleFunc, glutReshapeFunc, glutKeyboardFunc
extern glutSwapBuffers

; Glu
extern gluPerspective

; OpenGL
extern glClearColor, glClearDepth, glDepthFunc, glEnable, glShadeModel
extern glClear, glLoadIdentity, glMatrixMode, glViewport
extern glTranslatef, glRotatef, glBegin, glEnd, glVertex3f, glColor3f

; Needed for framerate control
extern usleep

; Macros
%macro _3call 4
    push dword %4
    push dword %3
    push dword %2
    call %1
    add esp, 12
%endmacro
%macro _4call 5
    push dword %5
    push dword %4
    push dword %3
    push dword %2
    call %1
    add esp, 16
%endmacro

section .data
    title: db 'OpenGL in x86 Linux assembler / NASM using Glut', 0
    ; Data for FP math with 4 bytes
    n7: dd -7.0
    n6: dd -6.0
    n1p5: dd -1.5
    n1: dd -1.0
    p0p4: dd 0.4
    p0p5: dd 0.5
    p1: dd 1.0
    p1p5: dd 1.5
    p3: dd 3.0
    rottrm: dd 4.5
    rotsqm: dd -3.0

    ; Data for FP math with 8 bytes
    q0p1: dq 0.1
    q1: dq 1.0
    q45: dq 45.0
    q100: dq 100.0

    ; Data for framerate control
    mspf: dd 20000 ; ms per frame

section .bss
    ; Vars for rotation
    rottr: resd 1
    rotsq: resd 1
    ; Var for aspect ratio
    aspr: resq 1
    ; Vars for framerate control
    time: resd 2
    usecs: resd 1
    oldslp: resd 1

section .text
    global _start

fpsmanager:
    push eax
    push ebx
    push ecx
    push edx

    mov eax, 78 ; sys_gettimeofday
    mov ebx, time
    mov ecx, 0
    int 80h

    mov eax, [time + 4]
    sub eax, [usecs]
    mov ebx, [mspf]
    sub ebx, eax

    test eax, eax
    js keep_sleep
    test ebx, ebx
    js keep_sleep

    push ebx
    call usleep
    add esp, 4
    mov dword [oldslp], ebx
    jmp done_sleep

keep_sleep:
    push dword [oldslp]
    call usleep
    add esp, 4

done_sleep:
    mov eax, [time + 4]
    mov dword [usecs], eax

    pop edx
    pop ecx
    pop ebx
    pop eax
    ret

display:
    ; Prepare for drawing
    push dword 4100h ; GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT
    call glClear
    add esp, 4
    call glLoadIdentity

    ; Draw pyramid on the left of the screen
    _3call glTranslatef, [n1p5], 0, [n6]
    _4call glRotatef, [rottr], 0, [p1], 0
    push dword 9h ; GL_POLYGON
    call glBegin
    add esp, 4
    _3call glColor3f, [p1], 0, 0 ; Start drawing front face
    _3call glVertex3f, 0, [p1], 0
    _3call glColor3f, 0, [p1], 0
    _3call glVertex3f, [n1], [n1], [p1]
    _3call glColor3f, 0, 0, [p1]
    _3call glVertex3f, [p1], [n1], [p1]
    _3call glColor3f, [p1], 0, 0 ; Start drawing right face
    _3call glVertex3f, 0, [p1], 0
    _3call glColor3f, 0, 0, [p1]
    _3call glVertex3f, [p1], [n1], [p1]
    _3call glColor3f, 0, [p1], 0
    _3call glVertex3f, [p1], [n1], [n1]
    _3call glColor3f, [p1], 0, 0 ; Start drawing back face
    _3call glVertex3f, 0, [p1], 0
    _3call glColor3f, 0, [p1], 0
    _3call glVertex3f, [p1], [n1], [n1]
    _3call glColor3f, 0, 0, [p1]
    _3call glVertex3f, [n1], [n1], [n1]
    _3call glColor3f, [p1], 0, 0 ; Start drawing right face
    _3call glVertex3f, 0, [p1], 0
    _3call glColor3f, 0, 0, [p1]
    _3call glVertex3f, [n1], [n1], [n1]
    _3call glColor3f, 0, [p1], 0
    _3call glVertex3f, [n1], [n1], [p1]
    call glEnd

    ; Draw cube on the right of the screen
    call glLoadIdentity
    _3call glTranslatef, [p1p5], 0, [n7]
    _4call glRotatef, [rotsq], [p1], [p0p4], [p0p5]
    push dword 7h ; GL_QUADS
    call glBegin
    add esp, 4
    _3call glColor3f, 0, [p1], 0 ; Start drawing top face
    _3call glVertex3f, [p1], [p1], [n1]
    _3call glVertex3f, [n1], [p1], [n1]
    _3call glVertex3f, [n1], [p1], [p1]
    _3call glVertex3f, [p1], [p1], [p1]
    _3call glColor3f, [p1], [p0p5], 0 ; Start drawing bottom face
    _3call glVertex3f, [p1], [n1], [p1]
    _3call glVertex3f, [n1], [n1], [p1]
    _3call glVertex3f, [n1], [n1], [n1]
    _3call glVertex3f, [p1], [n1], [n1]
    _3call glColor3f, [p1], 0, 0 ; Start drawing front face
    _3call glVertex3f, [p1], [p1], [p1]
    _3call glVertex3f, [n1], [p1], [p1]
    _3call glVertex3f, [n1], [n1], [p1]
    _3call glVertex3f, [p1], [n1], [p1]
    _3call glColor3f, [p1], [p1], 0 ; Start drawing back face
    _3call glVertex3f, [p1], [n1], [n1]
    _3call glVertex3f, [n1], [n1], [n1]
    _3call glVertex3f, [n1], [p1], [n1]
    _3call glVertex3f, [p1], [p1], [n1]
    _3call glColor3f, 0, 0, [p1] ; Start drawing left face
    _3call glVertex3f, [n1], [p1], [p1]
    _3call glVertex3f, [n1], [p1], [n1]
    _3call glVertex3f, [n1], [n1], [n1]
    _3call glVertex3f, [n1], [n1], [p1]
    _3call glColor3f, [p1], 0, [p1] ; Start drawing right face
    _3call glVertex3f, [p1], [p1], [n1]
    _3call glVertex3f, [p1], [p1], [p1]
    _3call glVertex3f, [p1], [n1], [p1]
    _3call glVertex3f, [p1], [n1], [n1]
    call glEnd

    ; Update rotation
    fld dword [rottr]
    fadd dword [rottrm]
    fstp dword [rottr]
    fld dword [rotsq]
    fadd dword [rotsqm]
    fstp dword [rotsq]

    call glutSwapBuffers ; Swap buffers and display
    call fpsmanager ; Cap framerate if necessary
    ret

reshape:
    push ebp
    mov ebp, esp

    ; Prevent division by zero
    cmp dword [ebp+8], 0
    jne reshape_2
    inc dword [ebp+8]

reshape_2:
    fild dword [ebp+8]
    fidiv dword [ebp+12]
    fstp qword [aspr]

    push dword [ebp+12]
    push dword [ebp+8]
    push dword 0
    push dword 0
    call glViewport
    add esp, 16
    push dword 1701h; GL_PROJECTION
    call glMatrixMode
    add esp, 4
    call glLoadIdentity

    push dword [q100+4]
    push dword [q100]
    push dword [q0p1+4]
    push dword [q0p1]
    push dword [aspr+4]
    push dword [aspr]
    push dword [q45+4]
    push dword [q45]
    call gluPerspective
    add esp, 32

    push dword 1700h; GL_MODELVIEW
    call glMatrixMode
    add esp, 4

    pop ebp
    ret

keyhandler:
    ret

_start:
    ; Initialize glut
    push dword [esp+8]
    lea eax, [esp+8]
    push eax
    call glutInit
    add esp, 8
    push dword 1ah ; GLUT_RGBA|GLUT_DOUBLE|GLUT_ALPHA|GLUT_DEPTH
    call glutInitDisplayMode
    add esp, 4
    push dword 480
    push dword 640
    call glutInitWindowSize
    add esp, 8
    push dword 0
    push dword 0
    call glutInitWindowPosition
    add esp, 8

    ; Create window
    push title
    call glutCreateWindow
    add esp, 4

    ; Register functions
    push display
    call glutDisplayFunc
    call glutIdleFunc
    add esp, 4
    push reshape
    call glutReshapeFunc
    add esp, 4
    push keyhandler
    call glutKeyboardFunc
    add esp, 4

    ; Initialize OpenGL
    push dword 0
    push dword 0
    push dword 0
    push dword 0
    call glClearColor
    add esp, 16
    push dword [q1+4]
    push dword [q1]
    call glClearDepth
    add esp, 8
    push dword 203h; GL_LEQUAL
    call glDepthFunc
    add esp, 4
    push dword 0b71h; GL_DEPTH_TEST
    call glEnable
    add esp, 4
    push dword 1d01h; GL_SMOOTH
    call glShadeModel
    add esp, 4

    ; Frame rate cap initialization
    mov dword [oldslp], 0

    ; Enter main loop
    call glutMainLoop

    ; Exit (return with error code 0)
    mov eax, 1 ; sys_exit
    mov ebx, 0
    int 80h
Human Knowledge Belongs To The World -- Antitrust (2001)
Nederlandstalige Ubuntu documentatie van Ubuntu-NL (wiki)