NASM - The Netwide Assembler
NASM Forum => Example Code => Topic started by: Stino on May 15, 2011, 01:21:11 PM
-
Hi All,
yesterday evening I looked around if there in the web some useful examples how to program assembler in OSX. Long long time ago I coded a bit assembler for C64, DOS and Windows, but after my study ends I find not longer enough time assembler coding. Today I have a Mac and for unknown reason just yesterday I get the idea to code a bit assembler that runs under OSX.
Well, any start or restart is hard and so I search in web for some examples. Unfortunately there are only examples out there that shows how to put "Hallo World" to the console and explain something about an strange "16-byte-aglinment"? I start with them and they work, but I want to see some more graphic. Since each Mac is shipped with OpenGL I use this first.
I began with the OpenGL init code but always get those strange "wrong alinement" crashes of my program. I need a few hours until I understand the two problems my code has. Because gdb is really a crap debugger for assembler code! Anyone knows a better debugger for assembler code that runs with OSX? ???
First problem was that external functions not clean the stack of the used parameter ("push para1", "push para2",...) as I know this from other systems (e.g. Windows-API-calls returns with "ret 12", "ret 8" and "ret ..." to clean the stack). But OSX functions never clean the stack! Always the caller have to this by "pop register" or "add esp,4".
Second problem was the "16-byte-aglinment". If you call an external function your stack should have always a multiple of 16 byte on the stack. If not the external function call will crash! This is really a pain in the a*s! You will start in each function (like _main) with an alinement of zero. Just calling an external function now will crash since the "call" opcode it self pushed the 4-Byte back jump on the stack. But 4 is no multiple of 16 byte. So before doing the call you have to push 12 byte to stack with either 3x useless 4-byte "push" or 1x "sub esp,12" and than the call will be work fine. But after call finishes we have to clean those pushes with either 3x useless 4-byte "pop" or 1x "add esp,12". If your external call function has parameters you joust have to correct the difference of the parameter size to get always in time of call an multiple of 16 bytes. After call come back you have to clean these correction and also the parameters of the call it self (see first problem). Well naturally you can do both corrections with one "add esp,xx".
In my example I make in function "_main" these to corrections step by step to show how it works. In function "_display_func" I do these correction in one step.
Now I understand why there are only a few examples out there, but I hope this example will help others to understand the problems and will leeds to more assembler code for OSX.
Let us all hack and rock those Mac's! ;D
Here the example code that init GLUT and paint an colored triangle with OpenGL into the window. It is only 32-bit code because I have not learned yet the 64-bit opcodes.
;
; Basic OS X calls to GLUT and OpenGL
;
; Example by Stino / former ByTeGeiZ
;
; compile with:
; nasm -g -f macho32 bspGLUT.asm
; gcc -framework GLUT -framework OpenGL -m32 -o bspGLUT.out bspGLUT.o
;
%define GLUT_RGB 0
%define GLUT_SINGLE 0
%define GL_POLYGON 9
%define GL_COLOR_BUFFER_BIT 0x00004000
; GLUT stuff
extern _glutCreateWindow, _glutInit, _glutInitDisplayMode, _glutDisplayFunc, _glutMainLoop, _glutInitWindowPosition, _glutInitWindowSize, _glutSwapBuffers
; OpenGL stuff
extern _glClearColor, _glClear, _glBegin, _glEnd, _glFlush, _glVertex3f, _glColor3f
; static data
segment .data
window_name: db "OpenGL on OSX with nasm", 10, 0
fl_one: dd 1.0
fl_neg_one: dd -1.0
fl_zero: dd 0.0
fl_half: dd 0.5
fl_neg_half: dd -0.5
; code
segment .text
global _main
; the main function that init OpenGL and install the gl draw function (_display_func)
_main:
;alignment = 0
lea ecx, [esp+4] ;load adress of argc in stack to ecx
lea edx, [esp+8] ;load adress of argv in stack to edx
push ebp ; setup the frame ;alignment => 4
mov ebp, esp
push edx ;alignment => 8
push ecx ;alignment => 12
call _glutInit ;alignment => 16 -> alignment <= 12 (after backjump with "ret")
add esp, 8 ;alignment <= 4 (caller has to clean call paras (8 byte))
sub esp, 4 ;alignment => 8 (correction need to get to 16 at next call !)
mov eax, GLUT_RGB
or eax, GLUT_SINGLE
push eax ;alignment => 12
call _glutInitDisplayMode ;alignment => 16 -> alignment <= 12
add esp, 4 ;alignment <= 8 (caller has to clean call paras (4 byte))
add esp, 4 ;alignment <= 4 (clean last the allignment correction)
push dword 80 ;alignment => 8
push dword 80 ;alignment => 12
call _glutInitWindowPosition ;alignment => 16 -> alignment <= 12
add esp, 8 ;alignment <= 4 (caller has to clean call paras (8 byte))
push dword 300 ;alignment => 8
push dword 400 ;alignment => 12
call _glutInitWindowSize ;alignment => 16 -> alignment <= 12
add esp, 8 ;alignment <= 4 (caller has to clean call paras (8 byte))
sub esp, 4 ;alignment => 8 (correction need to get to 16 at next call !)
mov eax, dword window_name
push eax ;alignment => 12
call _glutCreateWindow ;alignment => 16 -> alignment <= 12
add esp, 4 ;alignment <= 8 (caller has to clean call paras (4 byte))
add esp, 4 ;alignment <= 4 (clean last the allignment correction)
sub esp, 4 ;alignment => 8 (correction need to get to 16 at next call !)
push dword _display_func ;alignment => 12
call _glutDisplayFunc ;alignment => 16 -> alignment <= 12
add esp, 4 ;alignment <= 8 (caller has to clean call paras (4 byte))
add esp, 4 ;alignment <= 4 (clean last the allignment correction)
sub esp, 8 ;alignment => 12 (correction need to get to 16 at next call !)
call _glutMainLoop ;alignment => 16 -> alignment <= 12
add esp, 8 ;alignment <= 4 (caller has to clean call paras (8 byte))
_pass_exit:
pop ebp ;alignment <= 0
ret
; the gl draw function (here is the content of OpenGL
_display_func:
sub esp,8
push dword GL_COLOR_BUFFER_BIT
call _glClear
add esp,12
sub esp,8
push dword GL_POLYGON
call _glBegin
add esp,12
push dword 0
push dword 0
push dword [fl_one]
call _glColor3f
add esp,12
push dword 0
push dword [fl_neg_half]
push dword [fl_neg_half]
call _glVertex3f
add esp,12
push dword 0
push dword [fl_one]
push dword 0
call _glColor3f
add esp,12
push dword 0
push dword [fl_neg_half]
push dword [fl_half]
call _glVertex3f
add esp,12
push dword [fl_one]
push dword 0
push dword 0
call _glColor3f
add esp,12
push dword 0
push dword [fl_half]
push dword 0
call _glVertex3f
add esp,12
sub esp,12
call _glEnd
add esp,12
sub esp,12
call _glFlush
add esp,12
ret
You need to create an running binary the "Xcode developer tools" (for "gcc", "ld", GLUT- and OpenGL- frameworks) and of curse "NASM" for Mac. ;)
Compile code with:
nasm -g -f macho32 bspGLUT.asm
gcc -framework GLUT -framework OpenGL -m32 -o bspGLUT.out bspGLUT.o
And run binary:
./bspGLUT.out
Instate of declare all the external functions and constants in one code file as I did in example it will be helpful to have GLUT and OpenGL in some include files (*.inc). Has someone still done the job for all the OpenGL API files? ???
Happy coding and greetings from Stino
(former ByTeGeiZ)
-
Yep, good work.
Most of the ABI specifics can be found HERE (developer.apple.com) (http://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/). For 64-bit, Mac follows the System V AMD64 ABI (http://www.x86-64.org/documentation/abi.pdf).
I have a 32-bit/64-bit Quartz (Core Graphics) based Hello, World! example HERE (http://www.asmcommunity.net/board/index.php?topic=29668.0) that may help you transition to 64-bit. It is fairly verbose (i.e. not optimized) for the sake of clarity.
You'll also see some syscalls... they are simply based on FreeBSD.
As for the GLUT/OpenGL inc files, while those can be fairly easily (but tediously) transcribed from equivalent C header files, I am not aware of anyone who has developed a complete equivalent for NASM. This is something that would be great fit for the NASMX Project.
-
You'll also see some syscalls... they are simply based on FreeBSD.
I was wondering why there exists examples that uses software interrupts like in DOS times (with "int" opcode) to call a system function while others examples often use the same functionality with normal "call" opcode inside libraries.
Thanks to you I know now that there are part of OSX FreeBSD base layer. :) Exists there an documentation for all these FreeBSD syscalls? ???
As for the GLUT/OpenGL inc files, while those can be fairly easily (but tediously) transcribed from equivalent C header files, I am not aware of anyone who has developed a complete equivalent for NASM. This is something that would be great fit for the NASMX Project.
That sounds to me like an hint to start the job. ::)
Well since I'm maybe the only person that is interested in this I did the job. It was really no hard job if you know how to use macros in VIM and whats this "search&replace" all about. ;)
The "typedef" are not longer useful with NASM, but I let them in the files as comments to give the programmer the possibility of a quick overview about the byte lengths of the data types.
gl.inc (http://stino.redio.de/asm/gl.inc)
glut.inc (http://stino.redio.de/asm/glut.inc)
Simply add them as includes to your code:
%include "gl.inc"
%include "glut.inc"
-
For 64-bit, Mac follows the System V AMD64 ABI (http://www.x86-64.org/documentation/abi.pdf).
Hey! Really cool demo how to use Quartz graphics! 8)
I read over this before since I was thinking this was just the link to the text based 64-bit hello-world-demo that I saw somewhere in web (that one uses "int 0x80" syscalls).
But I will need a while to understand the code. For me it was just possible to create a 32-bit binary. If I create a 64-bit binary it crashes at run time with an SIGSEGV. I have an 64-bit machine with SL. So I guess that apple has changed the ABI a bit... or I link it wrong...?
Addition:
I get the 64-bit running now! My problem was that I tried to link through the "gcc" instead of using "ld" directly as it was suggested in the comments of the file. ::)
-
A possible optimization that can be made when calling multiple cdecl-style functions (those that don't clean up the stack) is, at the beginning of your own function, simply subtract from esp the largest amount of bytes required by one of the subfunctions then not worry about popping or add exp, XX until the end of the function.
Of course, you have to use mov rather than push instructions.
For example:
;prologue
push ebp
mov ebp, esp
sub esp, 32 ; <-- make sure this is multiple of 16
.
.
mov [esp+4], edx
mov [esp], ecx
call SomeFunction
.
.
mov [esp+12], eax
mov [esp+8], ebx
mov [esp+4], ecx
mov [esp], edx
call SomeOtherFunction
;epilogue
add esp, 32
mov esp, ebp
pop ebp
ret
Please note that this same technique can work in both 32/64 bit environments ONLY IF your function does not call a pascal/fastcall/stdcall type function that executes a [/b]ret XX[/b].
This is something that I've implemented in NASMX but it is only useful in very specific situations.
And as Keith mentioned, having GLUT/GL headers available in NASMX would indeed be a great fit!
As Mach-O is one of the targets to be supported with NASMX perhaps you can help out with this as well? Feeling ambitious? ;D
-
A possible optimization that can be made when calling multiple cdecl-style functions (those that don't clean up the stack) is, at the beginning of your own function, simply subtract from esp the largest amount of bytes required by one of the subfunctions then not worry about popping or add exp, XX until the end of the function.
Of course, you have to use mov rather than push instructions.
Very interesting approach to get rid of all those problematic 16-bit-alinement stuff inside an sub function. So I was give it a try in one of my functions to see how it works.
One thing I saw was that it will not work if you make an stack frame with "sub esp,16/32/.." and "add esp,16/32/..". But this is just because each "call" opcode make his own 4-byte-push to stack (cleaned later automatic with "ret") and after this push we have to reach the multiple of 16. We have to correct the frame by subtract of 4 byte and your idea works like hell. 8) Stack frame start than with "sub esp,12/28/.." and ends with "add esp,12/28/..".
What I did with my OpenGL timer function:
_timer_func:
sub esp,28
mov eax, [window_handle]
mov [esp],eax
call _glutSetWindow
call _glutPostRedisplay
mov [esp+8], dword 0
mov [esp+4], dword _timer_func
mov [esp], dword 40
call _glutTimerFunc
add esp,28
ret
Thanks for the good idea!
-
I have another question. Does someone know how I can sync my OpenGL graphic draw code with vertical retrace? I was googling for this an it seems that OpenGL only support for Windows this functionality, but this is no generic function of OpenGL. Very strange cause this should be a basic of all graphic libraries to make clean drawings (without raster fragments) to a screen.
Or is there maybe an Quartz function of OSX I can use to sync my code with VBLANK? ???
-
One thing I saw was that it will not work if you make an stack frame with "sub esp,16/32/.." and "add esp,16/32/..". But this is just because each "call" opcode make his own 4-byte-push to stack (cleaned later automatic with "ret") and after this push we have to reach the multiple of 16. We have to correct the frame by subtract of 4 byte and your idea works like hell. 8) Stack frame start than with "sub esp,12/28/.." and ends with "add esp,12/28/..".
Thanks for the good idea!
Usually when a function makes multiple calls it will have a local stack area to maintain data. Thus, in my code above you will see the push ebp which makes the stack aligned to 8. Then once you've factored in all your local variables the final alignment is implemented with something like this:
; %$__nx_locnt context var contains total stack used calculated thus far
%assign %%__off (((%$__nx_locnt + 16) / 16) * 16)
%assign %%__tmp (%$__nx_locnt + 16)
%if (%%__off != %%__tmp)
%assign %$__nx_locnt %%__off
%endif
sub esp, %$__nx_locnt
Your code reminds me that I need to revisit the callstack optimization code to account for functions with no locals. So thank you for that. ;)
-
I solve missing VSYNC problem at least in OpenGL on OSX. :)
After I start my OpenGL demo I saw that the created GLUT window offers a menu to enable/disable vertical blank in GLUT.
I can be found under: "program name" -> "Preferences..." -> "Default Syncronize to Vertical Blank"
If you enable this value you will have VSYNC after close and restart of your program (it seems that GLUT read this value just at init time.
I trace my program with fseventer to see in which files it save its data and see it was in /Users/[your_homedir]/Library/Preferences/com.apple.glut.plist. A look into the config file shows quick that the interesting value is called GLUTSyncToVBLKey.
So now you can check or set this value on terminal.
Read it out with:
defaults read com.apple.glut GLUTSyncToVBLKey
Enable it with:
defaults write com.apple.glut GLUTSyncToVBLKey 1
Disable it with:
defaults write com.apple.glut GLUTSyncToVBLKey 0
Or enable it direct in your assembler program before initialize GLUT stuff:
extern _system
...
segment .data
enable_glut_vsync_command: db "defaults write com.apple.glut GLUTSyncToVBLKey 1", 10, 0
...
segment .text
mov [esp],dword enable_glut_vsync_command
call _system
...
[init OpenGL]
...
Anyway I'm still interest to know how VSYNC works with Quartz...
-
Time for OpenGL example number two.
It needs the two include files I offer for download in a former post her in thread:
gl.inc (http://stino.redio.de/asm/gl.inc)
glut.inc (http://stino.redio.de/asm/glut.inc)
This is more an HowTo demo with some comments to explain it. My finally goal was to initialize an good vsynced OpenGL window and gives an asm coder the possibility to write some own grafic code to screen. I allocate therefore an linear frame buffer that has 4 bytes per pixel. This buffer is drawn in each vertical retrace to screen.
Well the content I draw into this frame buffer is just filling it with an random color (random generator was coded by me years ago since random generator of DOS was not optimal). But at this just an placeholder and can be filled with any content you want. :)
;
; Basic OS X calls to GLUT and OpenGL
;
; Example by Stino / ByTeGeiZ
;
; compile with:
; nasm -g -f macho32 bspGLframBuff.asm
; gcc -framework GLUT -framework OpenGL -m32 -o bspGLframBuff.out bspGLframBuff.o
;
;inclut GLUT/OpenGL stuff
%include "gl.inc"
%include "glut.inc"
;glibc stuff
extern _clock, _malloc, _free, _usleep, _system
;different ways to use OpenGL (all should work)
;%define USE_TIMER_UPDATE 1
;%define USE_DEPTH_BUFFER 1
%define USE_DOUBLE_BUFFER 1
%define USE_VSYNC 1
; inside com.apple.glut.plist
; set GLUTSyncToVBLKey to true for vsync active in GLUT/OpenGL
;
; at terminal:
; defaults read com.apple.glut GLUTSyncToVBLKey
; defaults write com.apple.glut GLUTSyncToVBLKey 1
; frame buffer data
%define width 800
%define high 600
%define colordeep 4
; static data
segment .data
; enable/disable vertical retrace sync inside com.apple.glut.plist
; set GLUTSyncToVBLKey to true for vsync active in GLUT/OpenGL
;
; at terminal:
; defaults read com.apple.glut GLUTSyncToVBLKey
; defaults write com.apple.glut GLUTSyncToVBLKey 1
enable_glut_vsync_command: db "defaults write com.apple.glut GLUTSyncToVBLKey 1", 10, 0
disable_glut_vsync_command: db "defaults write com.apple.glut GLUTSyncToVBLKey 0", 10, 0
window_name: db "OpenGL frame buffer example", 10, 0
window_handle: dd 0
fl_one: dd 1.0
fl_neg_one: dd -1.0
fl_zero: dd 0.0
fl_half: dd 0.5
fl_neg_half: dd -0.5
; variables for random
seed1: dd 0
seed2: dd 0
; for pointer to frame buffer
fbuff: dd 0
; code
segment .text
global _main
; the main function that init OpenGL and install the gl draw function (_display_func)
_main:
lea ecx, [esp+4] ;load adress of argc in stack to ecx
lea edx, [esp+8] ;load adress of argv in stack to edx
push ebp ; setup the frame
mov ebp, esp
sub esp,24 ;add to get rid of 16-bit-alignment-problem (not 28 as normal since we allready push ebp)
%ifdef USE_VSYNC
;force enable vsync support of GLUT in OSX
mov [esp],dword enable_glut_vsync_command
call _system
%endif
;init OpenGL with GLUT
mov [esp+4],edx ;**argv
mov [esp],ecx ;&argc
call _glutInit
;Init Random numbers
call _randomize
;get memory for the gl-frame-buffer
mov eax,width*high*colordeep
mov [esp], eax
call _malloc
; check if the malloc failed
test eax, eax
jz _fail_exit
mov [fbuff],eax
;init display mode for OpenGL window
mov eax, GLUT_RGB
%ifdef USE_DOUBLE_BUFFER
or eax, GLUT_DOUBLE
%else
or eax, GLUT_SINGLE
%endif
%ifdef USE_DEPTH_BUFFER
or eax, GLUT_DEPTH
%endif
mov [esp], eax
call _glutInitDisplayMode
;define posion of OpenGL window
mov [esp+4],dword 80
mov [esp],dword 80
call _glutInitWindowPosition
;define OpenGL window size
mov [esp+4], dword high
mov [esp], dword width
call _glutInitWindowSize
;create OpenGL window
mov eax, dword window_name
mov [esp], eax
call _glutCreateWindow
mov dword [window_handle],eax
%ifdef USE_DEPTH_BUFFER
;enable depth buffer
mov [esp],dword GL_DEPTH_TEST
call _glEnable
%endif
;add own draw function as call back
mov [esp], dword _display_func
call _glutDisplayFunc
%ifdef USE_TIMER_UPDATE
;add call back that triggers draw update(timer based)
mov [esp+8], dword 0
mov [esp+4], dword _timer_func
mov [esp], dword 1
call _glutTimerFunc
%else
;add call back that triggers draw update(idle based)
mov [esp], dword _idle_func
call _glutIdleFunc
%endif
%ifdef USE_VSYNC
; disable glut vsync before main loop, since this loop will never terminate normaly
; it works if it was enabled while init of glut/OpenGL ;-)
mov [esp],dword disable_glut_vsync_command
call _system
%endif
;start OpenGL main loop
call _glutMainLoop
; free the malloc'd memory
mov eax, dword [fbuff]
mov [esp], eax
call _free
_pass_exit:
add esp,24
pop ebp
ret
_fail_exit:
%ifdef USE_VSYNC
;disable glut vsync in case we had an error
mov [esp],dword disable_glut_vsync_command
call _system
%endif
mov eax, 1
add esp,24
pop ebp
ret
;##############################################################################
; GL timer function to give GL draw function the command for redraw and also restart the timer
_timer_func:
sub esp,28
;select OpenGL window
mov eax, [window_handle]
mov [esp],eax
call _glutSetWindow
;start redraw of OpenGL
call _glutPostRedisplay
;restart timer
mov [esp+8], dword 0
mov [esp+4], dword _timer_func
mov [esp], dword 40 ;the timer
call _glutTimerFunc
add esp,28
ret
;##############################################################################
; GL idle function to give GL draw function the command for redraw
_idle_func:
sub esp,28
;just wait a few micro secs
mov [esp], dword 10
call _usleep
;select OpenGL window
mov eax,[window_handle]
mov [esp],eax
call _glutSetWindow
;start redraw of OpenGL
call _glutPostRedisplay
add esp,28
ret
;##############################################################################
; the gl draw function (here is the content of OpenGL
_display_func:
sub esp,28 ;add to get rid of 16-bit-alignment-problem
;fill frame buffer with some content
call _draw_to_frame_buff
;defiene color for clear screen
mov eax, dword [fl_one]
mov [esp+12],eax
mov eax, dword [fl_zero]
mov [esp+8],eax
mov [esp+4],eax
mov [esp],eax
call _glClearColor
;clear screen
mov eax, dword GL_COLOR_BUFFER_BIT
%ifdef USE_DEPTH_BUFFER
or eax, dword GL_DEPTH_BUFFER_BIT
%endif
mov [esp],eax
call _glClear
;set position were _glDrawPixels will start
mov eax,dword [fl_neg_one]
mov [esp+4],eax
mov [esp],eax
call _glRasterPos2f
;draw frame buffer to screen
mov eax, dword [fbuff]
mov [esp+16],eax
mov [esp+12],dword GL_UNSIGNED_BYTE
mov [esp+8],dword GL_RGBA
mov [esp+4],dword high
mov [esp],dword width
call _glDrawPixels
%ifdef USE_DOUBLE_BUFFER
;do all actions and than change the buffer
call _glutSwapBuffers
%else
;so all actions in single buffer mode
call _glFlush
%endif
add esp,28
ret
;##############################################################################
;here draw own content to frame buffer
_draw_to_frame_buff:
sub esp,28 ;add to get rid of 16-bit-alignment-problem
;get a random number
mov [esp],dword 0xFFFFFFFF
call _random ;eax has radom number
;fill complete frame buffer with a random color
mov edi,[fbuff]
mov ecx,width*high
rep stosd
add esp,28
ret
;##############################################################################
;Init the random number seeds with system time
_randomize:
;parameter:
;none
;return value:
;none
push eax
push edx
xor eax,eax
sub esp,4
call _clock ;nach eax
add esp,4
mov [seed1],eax
mov edx,eax
sub esp,4
call _clock ;nach eax
add esp,4
xor eax,edx
mul edx ;eax * edx nach edx:eax
mov [seed2],eax
pop edx
pop eax
ret
;##############################################################################
;create a new random number
_random:
;parameter:
;EAX = w, range of random number: 0 - (w-1)
;return value:
;EAX = new random number
push ebx
push edx
mov ebx,eax
mov eax,[seed1]
mov edx,[seed2]
mov [seed2],eax
add eax,edx
shld eax,edx,9
mov [seed1],eax
xor edx,edx
div ebx
mov eax,edx
pop edx
pop ebx
ret
-
Very nice, Stino. With your permission may I add that as a demo for NASMX?
Keith / Bryant - Before I add the GL headers referenced above can you take a quick look at the licenses to confirm redistribution?
Their short and appear amicable but a second pair of eyes never hurts.
-
Very nice, Stino. With your permission may I add that as a demo for NASMX?
Keith / Bryant - Before I add the GL headers referenced above can you take a quick look at the licenses to confirm redistribution?
Their short and appear amicable but a second pair of eyes never hurts.
The license looks OK to use, but IANAL and I'd rather avoid setting this precedent of including lengthy/cumbersome licenses in a project that still has my name attached to it. I have an inherent distrust of anything longer than a paragraph that resembles legalese.
I'd opt for the "clean room" approach of piecing together include files from official documentation, and contributing it to NASMX under our chosen license. This should be pretty much OK as interfaces/constants/standards and whatnot fall under fact and not implementation... and are thus not subject to copyright. So strict function, data and constant (i.e. interface) declarations should be OK to implement and license ourselves.
Mind you that my recommendation is merely in the best interests of our user base, in that they shouldn't have to think hard about the limitations of using any given part of NASMX... it is supposed to be gift, unencumbered and no strings attached.
Food for thought.
-
I can just speak for the demos I post here. The code was written by me and I allow everybody to copy, to modify and to uses them in any other software. In other words it is free.
Other thing are the OpenGL headers that I adopt to use them with NASM. "gl.h" is part of OpenGL and should be under License of OpenGL that can be found here: http://www.opengl.org/about/licensing/ The main message I understand is that application developers have not to license it. A bit more difficult seems "glut.h" to me since this is part of GLUT from Mark Kilgard. According to this Wikipedia article it is not so free as it seems. http://en.wikipedia.org/wiki/OpenGL_Utility_Toolkit
The arguments of Keith that headers not include any code and that they are free because they offer just the knowledge about an interface (function names and constants) sounds plausible for me. But actually I'm no jurist and have absolutely no idea how the juridical argumentation is? ???
Anyway If you decide not to include the headers to NASMX I will post here an patch for my second demo to make it running without the headers.
Remove:
;inclut GLUT/OpenGL stuff
%include "gl.inc"
%include "glut.inc"
And replace it by:
extern _glutInit, _glutInitDisplayMode, _glutInitWindowPosition, _glutInitWindowSize, _glutCreateWindow, _glEnable, _glutDisplayFunc, _glutTimerFunc, _glutIdleFunc, _glutMainLoop, _glutSetWindow, _glutPostRedisplay, _glClearColor, _glClear, _glRasterPos2f, _glDrawPixels, _glutSwapBuffers, _glFlush
%define GLUT_RGB 0
%define GLUT_DOUBLE 2
%define GLUT_SINGLE 0
%define GLUT_DEPTH 16
%define GL_DEPTH_TEST 0x0B71
%define GL_COLOR_BUFFER_BIT 0x00004000
%define GL_DEPTH_BUFFER_BIT 0x00000100
%define GL_UNSIGNED_BYTE 0x1401
%define GL_RGBA 0x1908
-
Mind you that my recommendation is merely in the best interests of our user base, in that they shouldn't have to think hard about the limitations of using any given part of NASMX... it is supposed to be gift, unencumbered and no strings attached.
I couldn't say it better myself. In fact, right off hand I can't tell yah what license NASMX is using much less if it conflicts with another license. I originally released NASM32 to the public domain, NASMX is more or less Keith's port of the project, his code and the code I contributed at that time is under whatever licenses it's under now.
As for the NASMX user base, I'm not sure including OGL/GLUT is really in their best interest. I always thought of NASMX as a more generic utility (which is why we attempted to support as many systems as we did). This kinda makes me think of a conversation I had on this board some time back. Apparently most of the user base wasn't even aware of the cross platform nature of the library because so much emphasis (and work) was done on the Win32/64 editions. I think if the NASMX project starts adding support for things like OGL/GLUT then it will just open the floodgates for users to misinterpret the projects capabilities again and might fuel rumors that "You can't do [insert graphics engine library] in NASMX 'cause it only supports OGL". I know, sounds silly but it happens. What's worse is, it makes NASMX look like it's taking sides on the whole OGL vs DX vs whatever (personally I'm an allegro fan but my graphics needs tend to be minimal at best) arguments that could easily be avoided by simply linking to Stino's page. As for NASMX revision of Stino's code, sounds like a good idea, however I would go as far as to say that game/graphics dev is popular enough that you might want to release it as a separate package. [NX4G - NASMX 4 Gamers]... Sounds interesting none the less. Maybe could get homer to help out with it after he finishes formalizing his skillset, he was picking up quite a lot of NASM before he went off to school. :)
-
Thank you
-
Just a quick update of this example, since the old example was not longer working with OSX 10.9:
;
; Basic OS X calls to GLUT and OpenGL
;
; Example by Stino / ByTeGeiZ
;
; compile with:
; nasm -g -f macho32 bspGLframBuff.asm
; xcrun gcc -framework GLUT -framework OpenGL -m32 -o bspGLframBuff.out bspGLframBuff.o
;
;inclut GLUT/OpenGL stuff
%include "gl.inc"
%include "glut.inc"
;glibc stuff
extern _clock, _malloc, _free, _usleep, _system
;different ways to use OpenGL (all should work)
;%define USE_TIMER_UPDATE 1
;%define USE_DEPTH_BUFFER 1
%define USE_DOUBLE_BUFFER 1
%define USE_VSYNC 1
; inside com.apple.glut.plist
; set GLUTSyncToVBLKey to true for vsync active in GLUT/OpenGL
;
; at terminal:
; defaults read com.apple.glut GLUTSyncToVBLKey
; defaults write com.apple.glut GLUTSyncToVBLKey 1
; frame buffer data
%define width 800
%define high 600
%define colordeep 4
; static data
segment .data
; enable/disable vertical retrace sync inside com.apple.glut.plist
; set GLUTSyncToVBLKey to true for vsync active in GLUT/OpenGL
;
; at terminal:
; defaults read com.apple.glut GLUTSyncToVBLKey
; defaults write com.apple.glut GLUTSyncToVBLKey 1
enable_glut_vsync_command: db "defaults write com.apple.glut GLUTSyncToVBLKey 1", 10, 0
disable_glut_vsync_command: db "defaults write com.apple.glut GLUTSyncToVBLKey 0", 10, 0
window_name: db "OpenGL frame buffer example", 10, 0
window_handle: dd 0
fl_one: dd 1.0
fl_neg_one: dd -1.0
fl_zero: dd 0.0
fl_half: dd 0.5
fl_neg_half: dd -0.5
; variables for random
seed1: dd 0
seed2: dd 0
; variables for argc/argv
argc: dd 0
argv: dd 0
; for pointer to frame buffer
fbuff: dd 0
; code
segment .text
global _main
; the main function that init OpenGL and install the gl draw function (_display_func)
_main:
lea ecx, [esp+4] ;load adress of argc in stack to ecx
lea edx, [esp+8] ;load adress of argv in stack to edx
push ebp ; setup the frame
mov ebp, esp
sub esp,24 ;add to get rid of 16-bit-alignment-problem (not 28 as normal since we allready push ebp)
%ifdef USE_VSYNC
mov [argv],edx
mov [argc],ecx
;force enable vsync support of GLUT in OSX
mov [esp],dword enable_glut_vsync_command
call _system
mov edx,[argv]
mov ecx,[argc] ;ecx=argc is not longer saved by _system and this will cause problems in _glutInit and so we need to save and restore it now
%endif
;init OpenGL with GLUT
mov [esp+4],edx ;**argv
mov [esp],ecx ;&argc
call _glutInit
;Init Random numbers
call _randomize
;get memory for the gl-frame-buffer
mov eax,width*high*colordeep
mov [esp], eax
call _malloc
; check if the malloc failed
test eax, eax
jz _fail_exit
mov [fbuff],eax
;init display mode for OpenGL window
mov eax, GLUT_RGB
%ifdef USE_DOUBLE_BUFFER
or eax, GLUT_DOUBLE
%else
or eax, GLUT_SINGLE
%endif
%ifdef USE_DEPTH_BUFFER
or eax, GLUT_DEPTH
%endif
mov [esp], eax
call _glutInitDisplayMode
;define position of OpenGL window
mov [esp+4],dword 80
mov [esp],dword 80
call _glutInitWindowPosition
;define OpenGL window size
mov [esp+4], dword high
mov [esp], dword width
call _glutInitWindowSize
;create OpenGL window
mov eax, dword window_name
mov [esp], eax
call _glutCreateWindow
mov dword [window_handle],eax
%ifdef USE_DEPTH_BUFFER
;enable depth buffer
mov [esp],dword GL_DEPTH_TEST
call _glEnable
%endif
;add own draw function as call back
mov [esp], dword _display_func
call _glutDisplayFunc
%ifdef USE_TIMER_UPDATE
;add call back that triggers draw update(timer based)
mov [esp+8], dword 0
mov [esp+4], dword _timer_func
mov [esp], dword 1
call _glutTimerFunc
%else
;add call back that triggers draw update(idle based)
mov [esp], dword _idle_func
call _glutIdleFunc
%endif
%ifdef USE_VSYNC
; disable glut vsync before main loop, since this loop will never terminate normaly
; it works if it was enabled while init of glut/OpenGL ;-)
mov [esp],dword disable_glut_vsync_command
call _system
%endif
;start OpenGL main loop
call _glutMainLoop
; free the malloc'd memory
mov eax, dword [fbuff]
mov [esp], eax
call _free
_pass_exit:
add esp,24
pop ebp
ret
_fail_exit:
%ifdef USE_VSYNC
;disable glut vsync in case we had an error
mov [esp],dword disable_glut_vsync_command
call _system
%endif
mov eax, 1
add esp,24
pop ebp
ret
;##############################################################################
; GL timer function to give GL draw function the command for redraw and also restart the timer
_timer_func:
sub esp,28
;select OpenGL window
mov eax, [window_handle]
mov [esp],eax
call _glutSetWindow
;start redraw of OpenGL
call _glutPostRedisplay
;restart timer
mov [esp+8], dword 0
mov [esp+4], dword _timer_func
mov [esp], dword 40 ;the timer
call _glutTimerFunc
add esp,28
ret
;##############################################################################
; GL idle function to give GL draw function the command for redraw
_idle_func:
sub esp,28
;just wait a few micro secs
mov [esp], dword 10
call _usleep
;select OpenGL window
mov eax,[window_handle]
mov [esp],eax
call _glutSetWindow
;start redraw of OpenGL
call _glutPostRedisplay
add esp,28
ret
;##############################################################################
; the gl draw function (here is the content of OpenGL
_display_func:
pusha ; we have to save all registers because newer versions of GLUT framework need a few registers (ebx, edx, esi ...) before and after call of our _display_func
sub esp,28 ;add to get rid of 16-bit-alignment-problem
;fill frame buffer with some content
call _draw_to_frame_buff
;defiene color for clear screen
mov eax, dword [fl_one]
mov [esp+12],eax
mov eax, dword [fl_zero]
mov [esp+8],eax
mov [esp+4],eax
mov [esp],eax
call _glClearColor
;clear screen
mov eax, dword GL_COLOR_BUFFER_BIT
%ifdef USE_DEPTH_BUFFER
or eax, dword GL_DEPTH_BUFFER_BIT
%endif
mov [esp],eax
call _glClear
;set position were _glDrawPixels will start
mov eax,dword [fl_neg_one]
mov [esp+4],eax
mov [esp],eax
call _glRasterPos2f
;draw frame buffer to screen
mov eax, dword [fbuff]
mov [esp+16],eax
mov [esp+12],dword GL_UNSIGNED_BYTE
mov [esp+8],dword GL_RGBA
mov [esp+4],dword high
mov [esp],dword width
call _glDrawPixels
%ifdef USE_DOUBLE_BUFFER
;do all actions and than change the buffer
call _glutSwapBuffers
%else
;so all actions in single buffer mode
call _glFlush
%endif
add esp,28
popa
ret
;##############################################################################
;here draw own content to frame buffer
_draw_to_frame_buff:
sub esp,28 ;add to get rid of 16-bit-alignment-problem
;get a random number
mov [esp],dword 0xFFFFFFFF
call _random ;eax has radom number
;fill complete frame buffer with a random color
mov edi,[fbuff]
mov ecx,width*high
rep stosd
add esp,28
ret
;##############################################################################
;Init the random number seeds with system time
_randomize:
;parameter:
;none
;return value:
;none
push eax
push edx
xor eax,eax
sub esp,4
call _clock ;nach eax
add esp,4
mov [seed1],eax
mov edx,eax
sub esp,4
call _clock ;nach eax
add esp,4
xor eax,edx
mul edx ;eax * edx nach edx:eax
mov [seed2],eax
pop edx
pop eax
ret
;##############################################################################
;create a new random number
_random:
;parameter:
;EAX = w, range of random number: 0 - (w-1)
;return value:
;EAX = new random number
push ebx
push edx
mov ebx,eax
mov eax,[seed1]
mov edx,[seed2]
mov [seed2],eax
add eax,edx
shld eax,edx,9
mov [seed1],eax
xor edx,edx
div ebx
mov eax,edx
pop edx
pop ebx
ret
There were two reasons:
1.) "call _system" destroys content of register ecx and so the next "call _glutInit" that needs this register as input fails with an "Segmentation fault: 11"
2.) Inside the "_display_func" where we draw our content into the GL frame buffer and that is called by GLUT framework, we need to save all registers with an "pusha" and restore them later with an "popa". In newer versions of GLUT framework the registers ebx, edx, esi and edi are used, before and after calling the users _display_func.
Furthermore with newer versions of XCode you should use the following sequence to assemble and to link the frameworks:
nasm -g -f macho32 bspGLframBuff.asm
xcrun gcc -framework GLUT -framework OpenGL -m32 -o bspGLframBuff.out bspGLframBuff.o