News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Enigma with NeHe Tut

Started by Mark Jones, August 24, 2006, 07:00:38 PM

Previous topic - Next topic

Mark Jones

Hello, in the old Neon Helium OpenGL tutorials, Scalp provides the MASM code. In the file "include.def" some of the macros are defined as follows:


_glClearDepth MACRO t                   ;this is not defined in hardcode's include files
    gl_dpush t                          ;so here it is.
    mov eax, eax
    mov ecx, ecx
    call glClearDepth
ENDM

_gluPerspective MACRO a,b,c,d
    gl_dpush d
    gl_dpush c
    gl_dpush b
    gl_dpush a
    mov eax, eax ;without this crap, it f---s up
    mov ecx, ecx ;don't ask me why...
    call gluPerspective
ENDM
...


When the MOVs are commented out, compilation crashes. However, if I replace those two MOVs with four NOPs (the same number of bytes), it will compile and run normally:


_glClearDepth MACRO t                   ;this is not defined in hardcode's include files
    gl_dpush t                          ;so here it is.
nop
nop
nop
nop
    call glClearDepth
ENDM

_gluPerspective MACRO a,b,c,d
    gl_dpush d
    gl_dpush c
    gl_dpush b
    gl_dpush a
nop
nop
nop
nop
    call gluPerspective
ENDM
...


This behavior is repeatable. 3 (or less) NOPS will cause the exception, while 4 (or more) will not. Align does nothing. The FPU is not used. OllyDbg reveals nothing, just an undefined crash 0xC0000005 after the call, at 0x00510014. Why on Earth are these NOPs needed?

Here are the other macros used, from gl.def. Note that sometimes the MOVs are present here too, along with a very long IF-ELSE statement:


;====> Better close your eyes... ADULTS ONLY!

gl_fpush MACRO numb
LOCAL prmstr,prmlen,x,n
prmstr EQU <numb>
prmlen SIZESTR prmstr
IF (prmlen LE 7)
  ;; constant or varname
  x SUBSTR prmstr,prmlen,1
  IFIDNI x,<f>
   x SUBSTR prmstr,1,prmlen-1
   push 12345678h
   ORG $-4
   real4 &x
  ELSE
   push GLfloat ptr prmstr
  ENDIF
ELSE
x SUBSTR prmstr,1,7
IFIDNI x,<(float)>
  x SUBSTR prmstr,8
  n=&x
  x TEXTEQU %n
  x CATSTR x,<.0>
  push 12345678h
  ORG $-4
  real4 &x
ELSE
  x SUBSTR prmstr,prmlen,1
  IFIDNI x,<f>
   x SUBSTR prmstr,1,prmlen-1
   push 12345678h
   ORG $-4
   real4 &x
  ELSE
   push GLfloat ptr prmstr
  ENDIF
ENDIF
ENDIF
ENDM


; version without auto-convertion
_gl_fpush MACRO numb
LOCAL prmstr,prmlen,x
prmstr EQU <numb>
prmlen SIZESTR prmstr
x SUBSTR prmstr,prmlen,1
IFIDNI x,<f>
  x SUBSTR prmstr,1,prmlen-1
  push 12345678h
  ORG $-4
  real4 &x
ELSE
  push GLfloat ptr prmstr
ENDIF
ENDM



; ugly, ugly, ugly... but yet works (from time to time)
IF 0 ; this version is more stable, but generated code much more close to insane
gl_dpush MACRO numb
LOCAL loc1,dat,prmstr,prmlen,x
prmstr EQU <numb>
prmlen SIZESTR prmstr
x SUBSTR prmstr,prmlen,1
IFIDNI x,<f>
  x SUBSTR prmstr,1,prmlen-1
  jmp loc1
   ALIGN DWORD
   dat real8 &x
  loc1:
   push dword ptr dat[4]
   push dword ptr dat
ELSE
  x CATSTR prmstr,<[4]>
  push dword ptr x
  push dword ptr prmstr
ENDIF
ENDM

_glFrustum MACRO l,r,b,t,zNear,zFar
gl_dpush zFar
gl_dpush zNear
gl_dpush t
gl_dpush b
gl_dpush r
gl_dpush l
call glFrustum
ENDM

ELSE

gl_dpush MACRO numb
LOCAL prmstr,prmlen,x
prmstr EQU <numb>
prmlen SIZESTR prmstr
x SUBSTR prmstr,prmlen,1
IFIDNI x,<f>
x SUBSTR prmstr,1,prmlen-1
real8 &x
db 68h
real8 &x
ORG $-8-1-8
db 8Dh,40h,0 ; lea eax,[eax+0]
db 68h
ORG $+4+1+4
ELSE
x CATSTR prmstr,<[4]>
mov eax,eax
mov ebx,ebx
push dword ptr x
push dword ptr prmstr
ENDIF
ENDM

_glFrustum MACRO l,r,b,t,zNear,zFar
gl_dpush zFar
gl_dpush zNear
gl_dpush t
gl_dpush b
gl_dpush r
gl_dpush l
mov eax,eax
mov ebx,ebx
call glFrustum
ENDM

_glOrtho MACRO l,r,b,t,zNear,zFar
gl_dpush zFar
gl_dpush zNear
gl_dpush t
gl_dpush b
gl_dpush r
gl_dpush l
mov eax,eax
mov ebx,ebx
call glOrtho
ENDM

_glDepthRange MACRO zNear,zFar
gl_dpush zFar
gl_dpush zNear
mov eax,eax
mov ebx,ebx
call glDepthRange
ENDM


ENDIF


The only thing I can think of is the ORG directive is causing a contention. But how?...

Finally, there are a lot of definitions like:


_glTranslatef MACRO x,y,z
gl_fpush z
gl_fpush y
gl_fpush x
call glTranslatef
ENDM

_glRotatef MACRO angle,x,y,z
gl_fpush z
gl_fpush y
gl_fpush x
gl_fpush angle
call glRotatef
ENDM

_glNormal3f MACRO nx,ny,nz
gl_fpush nz
gl_fpush ny
gl_fpush nx
call glNormal3f
ENDM
...


A REAL4 is four bytes, right? Why all the special pushing code? And why no MOVs required here? Thanks for any insight! :bg
"To deny our impulses... foolish; to revel in them, chaos." MCJ 2003.08

hitchhikr

Consider the while macro generation as using a caret (positioned by the org statement), when it's generated, it have to extract both part of the 64bits real8 and put each part after a 32bits 0x68 (push instruction) in reverse order so what the macro does is:


1. real8 &x                       << generate a first 64 bits number.
2. db 68h                         << generate the second push after the 1st number.
3. real8 &x                       << generate the second argument for the second push (it'll only use the first 32 bits of this one).
4. ORG $-8-1-8                    << jump backward to the first byte of the first 64bits number generated above.
5. db 8Dh,40h,0 ; lea eax,[eax+0] << replace the higher 24 bits of the first generated number with a phony instruction
    db 68h                        << place a push instruction from the 24th to 32th bit of the 1st number.
6. ORG $+4+1+4                    << jump forward to the end of the lower 32 bits of the 2nd generated number.


At the end of that sequence any following code will be generated right in the middle of the 2nd number (at the lower 32 bits) that's why it requires 4 bytes to overwrite these extra datas from the executed code & to avoid to run it as an instruction.

The _gl_fpush macro permits to convert a float into a 32bits pushed integer (to be able to pass a floating point as argument).

THAT's a fugly piece of code :bg

zooba

Wouldn't the call statement overwrite the rest of the number though? The call should be at least 5 bytes I would have thought.

hitchhikr

Yes but for some reason it doesn't.

If i had a guess i'd say that this is a multi-passes problem and masm expand the macros in-between, the address may be marked/pre-generated as being a call with a reference datas written at the memory address itself in the first pass (like a call with an address to the internal symbols table of masm) and then the macro overwrite that place so when the assembler tries to retrieve/resolve the marker it left at that particular address it's invalid or different and it generates crap in the second pass (it generates a correct call instruction but with an invalid or unknown address).

Just a guess but it could be a solution.

cmdprompt

Try This:

dpush MACRO arg:REQ
local arg2

IFDEF arg
push dword ptr arg[4]
push dword ptr arg
EXITM
ENDIF

IF @InStr(1,<arg>,<[>)
push dword ptr arg[4]
push dword ptr arg
EXITM
ENDIF

IF @InStr(1,<arg>,<.>)
arg2 textequ <arg>
ELSE
arg2 textequ @CatStr(arg, <.0>)
ENDIF

IFIDN <arg2>, <0.0>
push 0
push 0
ELSEIFIDN <arg2>, <-1.0>
push 0BFF00000h
push 0
ELSEIFIDN <arg2>, <1.0>
push 3FF00000h
push 0
ELSE
mov eax, 0FFh
org $-4
dq arg2
org $-4
dq arg2
org $-8
db 8Dh,49h,00h ; lea ecx, [ecx+0] or any 3 byte instruction
db 068h ; push imm32
org $+4 ; [mov eax][0][1][2][3][lea ecx][ecx][+0][push][4][5][6][7]
push eax
ENDIF

ENDM

dedndave

welcome to the forum, cmdprompt
(this thread is only 4 years old   :bg )

vanjast

It was a delayed interrupt !!!!