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!
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)