type.bmx: * Fixed bug with LExposedType#Compare where any type other than an LExposedType would result in a crash.  Now handles TTypeIds as it should have.

SuperStrict

Import "field.bmx"
Import "method.bmx"
Import "metadata.bmx"

Import Brl.LinkedList

Private

Global _exposedTypes:TList = New TList

Public

Type LExposedType
	Field exposed%
	Field static%
	Field noclass%
	Field hidefields%
	Field nonew%

	Field typeid:TTypeId
	Field methods:TList			' TList<LExposedMethod>
	Field fields:TList			' TList<LExposedField>
	Field constructor$
	Field name$

	' Returns an LExposedType for a typeid - recommended to use this instead of creating a new LExposedType and initializing it
	Function ForType:LExposedType(typeid:TTypeId)
		If typeid = ObjectTypeId Or typeid = StringTypeId Or typeid._class = ArrayTypeId._class Then
			Return Null
		EndIf

		Local link:TLink = _exposedTypes.FindLink(typeid)
		If link Then
			Return LExposedType(link.Value())
		EndIf
		Return New LExposedType.InitWithTypeID(typeid)
	End Function

	' Compares LExposedTypes based on the TTypeId associated with each LExposedType
	Method Compare:Int(other:Object)
		Local ot:LExposedType = LExposedType(other)
		If other = Null Then
			Return 1
		ElseIf TTypeId(other) Then
			Return typeid.Compare(other)
		ElseIf other And Not ot Then
			Throw "Invalid type to compare LExposedType against"
		Else
			Return typeid.Compare(ot.typeid)
		EndIf
	End Method

	' Returns a list of exposed types to iterate over
	Function EnumTypes:TList()
		Return _exposedTypes.Copy()
	End Function

	' Initializes an LExposedType with information for the type passed
	' The object returned may not be the one you originally sent the initialize message to
	' If there is already an instance for this type, that instance will be returned instead
	' of proceeding with intialization
	Method InitWithTypeID:LExposedType(tid:TTypeId)
		If tid = ObjectTypeId Or typeid = StringTypeId Or typeid._class = ArrayTypeId._class Then
			Return Null
		EndIf

		Local lnk:TLink = _exposedTypes.FindLink(tid)
		If lnk Then
			Return LExposedType(lnk.Value())
		EndIf

		exposed = typeid.Metadata(LUGI_META_EXPOSE).ToInt()>0
		static = typeid.Metadata(LUGI_META_STATIC).ToInt()>0
		noclass = typeid.Metadata(LUGI_META_NOCLASS).ToInt()>0
		hidefields = typeid.Metadata(LUGI_META_HIDEFIELDS).ToInt()>0
		nonew = typeid.Metadata(LUGI_META_DISABLECONSTRUCTOR).ToInt()>0

		name = typeid.Metadata(LUGI_META_RENAME).Trim()
		If Not name Then
			name = typeid.Name()
		EndIf

		If Not (nonew Or (static and noclass)) Then
			constructor = typeid.Metadata(LUGI_META_CONSTRUCTOR).Trim()
			If Not constructor Then
				' Force New[A-Z_].* as default
				constructor = "New"+name[0..1].ToUpper()+name[1..]
			EndIf
		EndIf

		__initMethods
		__initFields

		_exposedTypes.AddLast(Self)

		Return Self
	End Method

	Method PreInitBlock:String()
		Local outs$=""

		If Not (static And noclass) Then
			For Local m:LExposedMethod = EachIn methods
				Local initString$ = m.PreInitBlock()
				If initString Then
					outs :+ "~t" + initString + "~n"
				EndIf
			Next
		EndIf

		If Not noclass Then
			For Local f:LExposedField = EachIn fields
				Local initString$ = f.PreInitBlock()
				If initString Then
					outs :+ "~t" + initString + "~n"
				EndIf
			Next
		EndIf

		Return outs
	End Method

	Method PostInitBlock:String()
		If Not (nonew Or (static And noclass)) Then
			Return 	"~tlua_pushlightuserdata lua_vm, Byte Ptr(TTypeId.ForName(~q" + typeid.Name() + "~q)._class)" + ..
					"~tlua_pushcclosure lua_vm, p_lugi_new_object, 1"
		ElseIf static And Not noclass Then
			Return	"~t"
		EndIf
	End Method

	Method MethodImplementations:String()
		Local m:LExposedMethod
		Local outs$
		Local instMethod% = Not (static And noclass)
		Local block$

		For m = EachIn methods
			block = m.Implementation(instMethod)
			If block Then
				outs :+ "~n~n"+block
			EndIf
		Next

		Return outs
	End Method

	Method _exposedInParent:Int(m:TMethod)
		Local name$ = m.Name().ToLower()

		Local typ:TTypeId = typeid.SuperType()
		While typ

		Wend
	End Method

	Method __initMethods()
		methods = New TList

		If Not exposed Then
			Return
		EndIf

		' Build list of exposed methods
		For Local m:TMethod = EachIn typeid.Methods()

		Next
	End Method

	Method __initFields()
		fields = New TList

		If Not exposed Or hidefields Then
			Return
		EndIf

		For Local f:TField = EachIn typeid.Fields()

		Next
	End Method
End Type

' Builds up the information for all types
Function BuildTypeInfo()
End Function

Private

' Recursively creates the information for types
Function _buildTypeInfo(types:TList)
	' Iterate over types and load data for them
	For Local tid:TTypeId = EachIn types
		New LExposedType.InitWithTypeID(tid)
		_buildTypeInfo(tid.DerivedTypes())
	Next
End Function

SuperStrict

Import "field.bmx"
Import "method.bmx"
Import "metadata.bmx"

Import Brl.LinkedList

Private

Global _exposedTypes:TList = New TList

Public

Type LExposedType
	Field exposed%
	Field static%
	Field noclass%
	Field hidefields%
	Field nonew%

	Field typeid:TTypeId
	Field methods:TList			' TList<LExposedMethod>
	Field fields:TList			' TList<LExposedField>
	Field constructor$
	Field name$

	' Returns an LExposedType for a typeid - recommended to use this instead of creating a new LExposedType and initializing it
	Function ForType:LExposedType(typeid:TTypeId)
		If typeid = ObjectTypeId Or typeid = StringTypeId Or typeid._class = ArrayTypeId._class Then
			Return Null
		EndIf

		Local link:TLink = _exposedTypes.FindLink(typeid)
		If link Then
			Return LExposedType(link.Value())
		EndIf
		Return New LExposedType.InitWithTypeID(typeid)
	End Function

	' Compares LExposedTypes based on the TTypeId associated with each LExposedType
	Method Compare:Int(other:Object)
		Return typeid.Compare(LExposedType(other).typeid)
	End Method

	' Returns a list of exposed types to iterate over
	Function EnumTypes:TList()
		Return _exposedTypes.Copy()
	End Function

	' Initializes an LExposedType with information for the type passed
	' The object returned may not be the one you originally sent the initialize message to
	' If there is already an instance for this type, that instance will be returned instead
	' of proceeding with intialization
	Method InitWithTypeID:LExposedType(tid:TTypeId)
		If tid = ObjectTypeId Or typeid = StringTypeId Or typeid._class = ArrayTypeId._class Then
			Return Null
		EndIf

		Local lnk:TLink = _exposedTypes.FindLink(tid)
		If lnk Then
			Return LExposedType(lnk.Value())
		EndIf

		exposed = typeid.Metadata(LUGI_META_EXPOSE).ToInt()>0
		static = typeid.Metadata(LUGI_META_STATIC).ToInt()>0
		noclass = typeid.Metadata(LUGI_META_NOCLASS).ToInt()>0
		hidefields = typeid.Metadata(LUGI_META_HIDEFIELDS).ToInt()>0
		nonew = typeid.Metadata(LUGI_META_DISABLECONSTRUCTOR).ToInt()>0

		name = typeid.Metadata(LUGI_META_RENAME).Trim()
		If Not name Then
			name = typeid.Name()
		EndIf

		If Not (nonew Or (static and noclass)) Then
			constructor = typeid.Metadata(LUGI_META_CONSTRUCTOR).Trim()
			If Not constructor Then
				' Force New[A-Z_].* as default
				constructor = "New"+name[0..1].ToUpper()+name[1..]
			EndIf
		EndIf

		__initMethods
		__initFields

		_exposedTypes.AddLast(Self)

		Return Self
	End Method

	Method PreInitBlock:String()
		Local outs$=""

		If Not (static And noclass) Then
			For Local m:LExposedMethod = EachIn methods
				Local initString$ = m.PreInitBlock()
				If initString Then
					outs :+ "~t" + initString + "~n"
				EndIf
			Next
		EndIf

		If Not noclass Then
			For Local f:LExposedField = EachIn fields
				Local initString$ = f.PreInitBlock()
				If initString Then
					outs :+ "~t" + initString + "~n"
				EndIf
			Next
		EndIf

		Return outs
	End Method

	Method PostInitBlock:String()
		If Not (nonew Or (static And noclass)) Then
			Return 	"~tlua_pushlightuserdata lua_vm, Byte Ptr(TTypeId.ForName(~q" + typeid.Name() + "~q)._class)" + ..
					"~tlua_pushcclosure lua_vm, p_lugi_new_object, 1"
		ElseIf static And Not noclass Then
			Return	"~t"
		EndIf
	End Method

	Method MethodImplementations:String()
		Local m:LExposedMethod
		Local outs$
		Local instMethod% = Not (static And noclass)
		Local block$

		For m = EachIn methods
			block = m.Implementation(instMethod)
			If block Then
				outs :+ "~n~n"+block
			EndIf
		Next

		Return outs
	End Method

	Method _exposedInParent:Int(m:TMethod)
		Local name$ = m.Name().ToLower()

		Local typ:TTypeId = typeid.SuperType()
		While typ

		Wend
	End Method

	Method __initMethods()
		methods = New TList

		If Not exposed Then
			Return
		EndIf

		' Build list of exposed methods
		For Local m:TMethod = EachIn typeid.Methods()

		Next
	End Method

	Method __initFields()
		fields = New TList

		If Not exposed Or hidefields Then
			Return
		EndIf

		For Local f:TField = EachIn typeid.Fields()

		Next
	End Method
End Type

' Builds up the information for all types
Function BuildTypeInfo()
End Function

Private

' Recursively creates the information for types
Function _buildTypeInfo(types:TList)
	' Iterate over types and load data for them
	For Local tid:TTypeId = EachIn types
		New LExposedType.InitWithTypeID(tid)
		_buildTypeInfo(tid.DerivedTypes())
	Next
End Function


Fixed Win32 build issue.

Fixed Win32 build issue.

Strict

Rem
	bbdoc: Direct3D 9 renderer for MaxB3D
End Rem
Module MaxB3D.D3D9Driver
ModuleInfo "Author: Kevin Primm"
ModuleInfo "License: MIT"

Import MaxB3D.Core
Import Prime.D3D9Max2DEx
Import Prime.DirectXEx

?Win32

Import "d3d9.cpp"

Private

Extern "C"
	Function maxb3dD3D9VertexElements:Byte Ptr()'="_maxb3dD3D9VertexElements@0"
End Extern

Function GetD3D9MaxB3DVertexDecl:IDirect3DVertexDeclaration9(d3ddev:IDirect3DDevice9)
	Global decl:IDirect3DVertexDeclaration9,dev:IDirect3DDevice9
	If decl=Null Or dev<>d3ddev
		Assert d3ddev.CreateVertexDeclaration(maxb3dD3D9VertexElements(),decl)=D3D_OK,"Failed to create vertex declaration."
		dev=d3ddev
	EndIf
	Return decl
End Function

Function Pow2Size( n )
	Local t=1
	While t<n
		t:*2
	Wend
	Return t
End Function

Public

Type TD3D9MaxB3DDriver Extends TMaxB3DDriver
	Field _d3ddev:IDirect3DDevice9
	Field _viewporton

	Method SetGraphics( g:TGraphics )
		Super.SetGraphics g
		_d3ddev=Null
		If g<>Null
			_d3ddev=TD3D9Graphics(TMax2DGraphics(g)._graphics).GetDirect3DDevice()
			EndMax2D
		EndIf
	End Method

	Method MakeBuffer:TBuffer(src:Object,width,height,flags)
		Return _parent.MakeBuffer(src,width,height,flags)
	End Method

	Method GetCaps:TCaps()
		Local caps:TCaps=New TCaps
		Return caps
	End Method

	Method Abbr$()
		Return "d3d9"
	End Method

	Method SetMax2D(enable)
		If enable
			Global identity:TMatrix=TMatrix.Identity()
			Local width=GraphicsWidth(),height=GraphicsHeight()
			Local matrix#[]=[..
			2.0/width,0.0,0.0,0.0,..
			 0.0,-2.0/height,0.0,0.0,..
			 0.0,0.0,1.0,0.0,..
			 -1-(1.0/width),1+(1.0/height),1.0,1.0]

			_d3ddev.SetTransform D3DTS_PROJECTION,matrix
			_d3ddev.SetTransform D3DTS_WORLD,identity.ToPtr()
			_d3ddev.SetTransform D3DTS_VIEW,identity.ToPtr()
			_d3ddev.SetTransform D3DTS_TEXTURE0,identity.ToPtr()

			_d3ddev.SetVertexDeclaration Null
			_d3ddev.SetIndices Null
			_d3ddev.SetFVF D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1

			_d3ddev.SetRenderState D3DRS_LIGHTING,False
			_d3ddev.SetRenderState D3DRS_ZENABLE,False
			_d3ddev.SetRenderState D3DRS_SCISSORTESTENABLE,_viewporton

			_d3ddev.SetRenderState D3DRS_ALPHATESTENABLE,True

			_d3ddev.SetRenderState D3DRS_WRAP0, 0

			_d3ddev.SetRenderState D3DRS_FOGENABLE,False

			For Local i=2 To 7
				_d3ddev.SetTexture i,Null
			Next

			_d3ddev.SetTextureStageState 0,D3DTSS_COLOROP,D3DTOP_MODULATE
			_d3ddev.SetTextureStageState 0,D3DTSS_ALPHAOP,D3DTOP_MODULATE

			_d3ddev.SetRenderState D3DRS_CULLMODE,D3DCULL_NONE
			_d3ddev.SetRenderState D3DRS_FILLMODE,D3DFILL_SOLID
		Else
			_d3ddev.SetRenderState D3DRS_ZENABLE,True
			_d3ddev.SetRenderState D3DRS_ZWRITEENABLE,True

			_d3ddev.GetRenderState D3DRS_SCISSORTESTENABLE,_viewporton
			_d3ddev.SetRenderState D3DRS_SCISSORTESTENABLE,True

			_d3ddev.SetRenderState D3DRS_LIGHTING,True
			_d3ddev.SetRenderState D3DRS_NORMALIZENORMALS,True
			_d3ddev.SetRenderState D3DRS_AMBIENT,D3DCOLOR_RGB(WorldConfig.AmbientRed,WorldConfig.AmbientGreen,WorldConfig.AmbientBlue)
			_d3ddev.SetRenderState D3DRS_COLORVERTEX, True
			_d3ddev.SetRenderState D3DRS_DIFFUSEMATERIALSOURCE,D3DMCS_MATERIAL

			_d3ddev.SetRenderState D3DRS_SHADEMODE,D3DSHADE_GOURAUD

			'_d3ddev.SetRenderState D3DRS_ALPHAREF, 1
			'_d3ddev.SetRenderState D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL

			_d3ddev.SetRenderState D3DRS_CULLMODE,D3DCULL_CW
		EndIf
	End Method

	Method SetCamera(camera:TCamera)
		Local clearflags
		If camera._clsmode&CLSMODE_COLOR clearflags:|D3DCLEAR_TARGET
		If camera._clsmode&CLSMODE_DEPTH clearflags:|D3DCLEAR_ZBUFFER

		Local viewport[]=[camera._viewx,camera._viewy,camera._viewwidth-camera._viewx,camera._viewheight-camera._viewy]
		_d3ddev.SetScissorRect viewport
		_d3ddev.Clear(1,viewport,clearflags,D3DCOLOR_XRGB(camera._brush._r*255,camera._brush._g*255,camera._brush._b*255),1.0,0)

		Select camera._fogmode
		Case FOGMODE_LINEAR
			_d3ddev.SetRenderState D3DRS_FOGENABLE,True
			_d3ddev.SetRenderState D3DRS_FOGSTART,Int(Varptr camera._fognear)
			_d3ddev.SetRenderState D3DRS_FOGEND,Int(Varptr camera._fogfar)
		Default
			_d3ddev.SetRenderState D3DRS_FOGENABLE,False
		End Select

		Local ratio#=(Float(camera._viewwidth)/camera._viewheight)
		Local proj_matrix:TMatrix=TMatrix.PerspectiveFovRH(ATan((1.0/(camera._zoom*ratio)))*2.0,ratio#,Max(0,camera._near-0.396250010),camera._far) ' Ugly hack. Real cause needs investigation.
		proj_matrix=TMatrix.Scale(-1,1,-1).Multiply(proj_matrix)
		_d3ddev.SetTransform D3DTS_PROJECTION,proj_matrix.ToPtr()

		Local matrix:TMatrix=camera._matrix.Inverse()
		_d3ddev.SetTransform D3DTS_VIEW,matrix.ToPtr()

		camera._lastmodelview=matrix
		camera._lastprojection=proj_matrix

		camera._lastviewport[0]=camera._viewx
		camera._lastviewport[1]=camera._viewy
		camera._lastviewport[2]=camera._viewwidth
		camera._lastviewport[3]=camera._viewheight

		camera._lastfrustum=TFrustum.Extract(camera._lastmodelview,camera._lastprojection)
	End Method

	Method SetLight(light:TLight,index)
		If light=Null
			_d3ddev.LightEnable index,False
			Return
		EndIf

		Local brush:TBrush=light._brush

		Global d3dlight:D3DLIGHT9=New D3DLIGHT9
		d3dlight.Type_=D3DLIGHT_DIRECTIONAL
		d3dlight.Diffuse_r=brush._r;d3dlight.Diffuse_g=brush._g;d3dlight.Diffuse_b=brush._b;d3dlight.Diffuse_a=brush._a
		'd3dlight.Ambient_r=WorldConfig.AmbientRed/255.0;d3dlight.Ambient_g=WorldConfig.AmbientGreen/255.0;d3dlight.Ambient_b=WorldConfig.AmbientBlue/255.0;d3dlight.Ambient_a=1.0

		d3dlight.Direction_x=0.0;d3dlight.Direction_y=0.0;d3dlight.Direction_z=1.0

		Local matrix:TMatrix=light._matrix
		matrix.TransformVec3 d3dlight.Direction_x,d3dlight.Direction_y,d3dlight.Direction_z
		matrix.GetPosition d3dlight.Position_x,d3dlight.Position_y,d3dlight.Position_z

		d3dlight.Range=light._range

		_d3ddev.SetLight index,d3dlight
		_d3ddev.LightEnable index,True
	End Method

	Method SetBrush(brush:TBrush,hasalpha)
		_d3ddev.SetRenderState D3DRS_ALPHATESTENABLE,False

		Local alpha_blending = (brush._fx&FX_FORCEALPHA Or hasalpha)>0
		_d3ddev.SetRenderState D3DRS_ALPHABLENDENABLE,alpha_blending
		_d3ddev.SetRenderState D3DRS_ZWRITEENABLE,Not alpha_blending

		If brush._fx&FX_FULLBRIGHT
			_d3ddev.SetRenderState D3DRS_AMBIENT,$ffffffff
		Else
			_d3ddev.SetRenderState D3DRS_AMBIENT,D3DCOLOR_RGB(WorldConfig.AmbientRed,WorldConfig.AmbientGreen,WorldConfig.AmbientBlue)
		EndIf

		If brush._fx&FX_NOCULLING
			_d3ddev.SetRenderState D3DRS_CULLMODE,D3DCULL_NONE
		Else
			_d3ddev.SetRenderState D3DRS_CULLMODE,D3DCULL_CW
		EndIf

		If brush._fx&FX_WIREFRAME Or WorldConfig.Wireframe
			_d3ddev.SetRenderState D3DRS_FILLMODE,D3DFILL_WIREFRAME
		Else
			_d3ddev.SetRenderState D3DRS_FILLMODE,D3DFILL_SOLID
		EndIf

		Local material:D3DMATERIAL9 = New D3DMATERIAL9
		material.Diffuse_r=brush._r;material.Diffuse_g=brush._g;material.Diffuse_b=brush._b;material.Diffuse_a=brush._a
		material.Ambient_r=brush._r;material.Ambient_g=brush._g;material.Ambient_b=brush._b;material.Ambient_a=brush._a

		_d3ddev.SetMaterial material

		For Local i=0 To 7
			Local texture:TTexture=brush._texture[i]
			If texture=Null Or texture._blend=BLEND_NONE
				_d3ddev.SetTexture i,Null
				Continue
			EndIf

			_d3ddev.SetTexture i,UpdateTextureRes(texture._frame[brush._textureframe[i]],texture._flags)._tex

			Local matrix:TMatrix=TMatrix.Identity()
			'matrix._m[0,0]=-texture._sx
			'matrix._m[1,1]=texture._sy
			matrix=TMatrix.YawPitchRoll(0,0,-texture._r).Multiply(matrix)
			matrix=TMatrix.Scale(-texture._sx,texture._sy,1).Multiply(matrix)
			matrix._m[2,0]=-texture._px
			matrix._m[2,1]=-texture._py

			_d3ddev.SetTransform D3DTS_TEXTURE0+i,matrix.ToPtr()

			_d3ddev.SetTextureStageState i,D3DTSS_TEXTURETRANSFORMFLAGS,D3DTTFF_COUNT2

			_d3ddev.SetSamplerState i,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR
			_d3ddev.SetSamplerState i,D3DSAMP_MINFILTER,D3DTEXF_LINEAR

			_d3ddev.SetRenderState D3DRS_ALPHATESTENABLE, texture._flags&TEXTURE_ALPHA

			If texture._flags&TEXTURE_MIPMAP
				_d3ddev.SetSamplerState i,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR
			Else
				_d3ddev.SetSamplerState i,D3DSAMP_MIPFILTER,D3DTEXF_NONE
			EndIf

			If texture._flags&TEXTURE_CLAMPU
				_d3ddev.SetSamplerState i,D3DSAMP_ADDRESSU,D3DTADDRESS_CLAMP
			Else
				_d3ddev.SetSamplerState i,D3DSAMP_ADDRESSU,D3DTADDRESS_WRAP
			EndIf

			If texture._flags&TEXTURE_CLAMPV
				_d3ddev.SetSamplerState i,D3DSAMP_ADDRESSU,D3DTADDRESS_CLAMP
			Else
				_d3ddev.SetSamplerState i,D3DSAMP_ADDRESSV,D3DTADDRESS_WRAP
			EndIf

			If texture._flags&TEXTURE_SPHEREMAP
				_d3ddev.SetTextureStageState i,D3DTSS_TEXCOORDINDEX,D3DTSS_TCI_SPHEREMAP
			Else
				_d3ddev.SetTextureStageState i,D3DTSS_TEXCOORDINDEX,texture._coords
			EndIf

			'_d3ddev.SetTextureStageState i,D3DTSS_COLOROP,D3DTOP_SELECTARG1
			'_d3ddev.SetTextureStageState i,D3DTSS_ALPHAOP,D3DTOP_SELECTARG2

			Select texture._blend
			Case BLEND_ALPHA
				'glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL)
			Case BLEND_MULTIPLY
				_d3ddev.SetTextureStageState i,D3DTSS_COLOROP,D3DTOP_MODULATE
				_d3ddev.SetTextureStageState i,D3DTSS_ALPHAOP,D3DTOP_MODULATE
			Case BLEND_ADD
				_d3ddev.SetTextureStageState i,D3DTSS_COLOROP,D3DTOP_ADD
				_d3ddev.SetTextureStageState i,D3DTSS_ALPHAOP,D3DTOP_ADD
			Case BLEND_DOT3
				_d3ddev.SetTextureStageState i,D3DTSS_COLOROP,D3DTOP_DOTPRODUCT3
				_d3ddev.SetTextureStageState i,D3DTSS_ALPHAOP,D3DTOP_DOTPRODUCT3
			Case BLEND_MULTIPLY2
				_d3ddev.SetTextureStageState i,D3DTSS_COLOROP,D3DTOP_MODULATE2X
				_d3ddev.SetTextureStageState i,D3DTSS_ALPHAOP,D3DTOP_MODULATE2X
			Default
				_d3ddev.SetTextureStageState i,D3DTSS_COLOROP,D3DTOP_MODULATE
				_d3ddev.SetTextureStageState i,D3DTSS_ALPHAOP,D3DTOP_MODULATE
			End Select
		Next
	End Method

	Method RenderSurface(resource:TSurfaceRes,brush:TBrush)
		Local res:TD3D9SurfaceRes=TD3D9SurfaceRes(resource)

		_d3ddev.SetFVF 0
		_d3ddev.SetVertexDeclaration GetD3D9MaxB3DVertexDecl(_d3ddev)
		_d3ddev.SetStreamSource 0,res._pos,0,12
		_d3ddev.SetStreamSource 1,res._nml,0,12
		_d3ddev.SetStreamSource 2,res._clr,0,16
		_d3ddev.SetStreamSource 3,res._tex[0],0,8
		_d3ddev.SetIndices res._tri
		_d3ddev.DrawIndexedPrimitive D3DPT_TRIANGLELIST,0,0,res._vertexcnt,0,res._trianglecnt

		Return res._trianglecnt
	End Method

	Method RenderSprite(sprite:TSprite)
		Global _data#[]=[ -1.0, 1.0,0.0, 0.0,0.0,1.0, 0.0,0.0, ..
		                   1.0, 1.0,0.0, 0.0,0.0,1.0, 1.0,0.0, ..
		                  -1.0,-1.0,0.0, 0.0,0.0,1.0, 0.0,1.0, ..
		                   1.0,-1.0,0.0, 0.0,0.0,1.0, 1.0,1.0     ]

		_d3ddev.SetVertexDeclaration Null
		_d3ddev.SetFVF D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1
		_d3ddev.DrawPrimitiveUP D3DPT_TRIANGLESTRIP,2,_data,8*4
	End Method

	Method BeginEntityRender(entity:TEntity)
		_d3ddev.SetTransform D3DTS_WORLD,entity.GetMatrix(True).ToPtr()
	End Method

	Method EndEntityRender(entity:TEntity)
	End Method

	Method RenderFlat(flat:TFlat)
		Local x#,y#,z#
		flat.GetScale x,y,z,True

		Global _data#[]=[ -1.0,0.0, 1.0, 0.0,1.0,0.0, 0.0,0.0, ..
		                   1.0,0.0, 1.0, 0.0,1.0,0.0,   x,0.0, ..
		                  -1.0,0.0,-1.0, 0.0,1.0,0.0, 0.0,  z, ..
		                   1.0,0.0,-1.0, 0.0,1.0,0.0,   x,  z     ]

		_d3ddev.SetVertexDeclaration Null
		_d3ddev.SetFVF D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1
		_d3ddev.DrawPrimitiveUP D3DPT_TRIANGLESTRIP,2,_data,8*4
	End Method

	Method RenderTerrain(terrain:TTerrain)
	End Method

	Method RenderBSPTree(tree:TBSPTree)
		Local node:TBSPNode=tree.Node
		If node=Null Return
		Local triangles
		triangles:+RenderBSPTree(node.In)

		_d3ddev.SetVertexDeclaration Null
		_d3ddev.SetFVF D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1

		For Local poly:TBSPPolygon=EachIn node.On
			Local ptA:TVector=poly.Point[0],v0
			Local trianglecnt=poly.Count()-2
			Local data#[trianglecnt*24],dataptr:Float Ptr=data
			For Local i=0 To trianglecnt-1
				Local ptB:TVector=poly.Point[i+1],ptC:TVector=poly.Point[i+2]
				data[i*24+00]=ptA.x;data[i*24+01]=ptA.y;data[i*24+02]=ptA.z;data[i*24+03]=poly.Plane.x;data[i*24+04]=poly.Plane.y;	data[i*24+05]=poly.Plane.z;data[i*24+06]=0.0;data[i*24+07]=0.0
				data[i*24+08]=ptB.x;data[i*24+09]=ptB.y;data[i*24+10]=ptB.z;data[i*24+11]=poly.Plane.x;data[i*24+12]=poly.Plane.y;	data[i*24+13]=poly.Plane.z;data[i*24+14]=0.0;data[i*24+15]=0.0
				data[i*24+16]=ptC.x;data[i*24+17]=ptC.y;data[i*24+18]=ptC.z;data[i*24+19]=poly.Plane.x;data[i*24+20]=poly.Plane.y;data[i*24+21]=poly.Plane.z;data[i*24+22]=0.0;data[i*24+23]=0.0
			Next
			_d3ddev.DrawPrimitiveUP D3DPT_TRIANGLELIST,trianglecnt,data,8*4
			triangles:+trianglecnt
		Next

		triangles:+RenderBSPTree(node.Out)
		Return triangles
	End Method

	Method UpdateTextureRes:TD3D9TextureRes(frame:TTextureFrame,flags)
		Local res:TD3D9TextureRes=TD3D9TextureRes(frame._res)
		If res And frame._updateres=False Return res

		If res=Null res=New TD3D9TextureRes
		frame._res=res

		Local pixmap:TPixmap=frame._pixmap
		Local tex_width=Pow2Size(pixmap.width),tex_height=Pow2Size(pixmap.height)
		pixmap=ConvertPixmap(ResizePixmap(pixmap,tex_width,tex_height),PF_BGRA8888)
		If res._tex=Null Assert _d3ddev.CreateTexture(tex_width,tex_height,(flags & TEXTURE_MIPMAP)=0,D3DUSAGE_AUTOGENMIPMAP,D3DFMT_A8R8G8B8,D3DPOOL_MANAGED,res._tex,Null)=D3D_OK

		Local rect:D3DLOCKED_RECT=New D3DLOCKED_RECT
		res._tex.LockRect 0,rect,Null,0
		MemCopy rect.pBits,pixmap.pixels,pixmap.width*pixmap.height*4
		res._tex.UnlockRect 0

		frame._updateres=False

		Return res
	End Method

	Method UpdateSurfaceRes:TD3D9SurfaceRes(surface:TSurface)
		Local res:TD3D9SurfaceRes=TD3D9SurfaceRes(surface._res)
		If res=Null res=New TD3D9SurfaceRes;surface._reset=-1

		If surface._reset=0 Return res
		If surface._reset=-1 surface._reset=1|2|4|8|16|32|64|128|256

		If surface._reset&1 And surface._vertexpos UploadVertexData res._pos,surface._vertexpos
		If surface._reset&2 And surface._vertexnml UploadVertexData res._nml,surface._vertexnml
		If surface._reset&4 And surface._vertexclr UploadVertexData res._clr,surface._vertexclr
		If surface._reset&8 And surface._triangle
			If res._tri=Null _d3ddev.CreateIndexBuffer(surface._triangle.length*4,0,D3DFMT_INDEX32,D3DPOOL_MANAGED,res._tri,Null)
			Local dataptr:Byte Ptr
			Assert res._tri.Lock(0,0,dataptr,0)=D3D_OK,"Failed to lock index buffer."
			MemCopy dataptr,surface._triangle,surface._triangle.length*4
			res._tri.Unlock()
		EndIf

		For Local i=0 To surface._vertextex.length-1
			If surface._reset&Int(2^(4+i)) UploadVertexData res._tex[i],surface._vertextex[i]
		Next

		res._trianglecnt=surface._trianglecnt
		res._vertexcnt=surface._vertexcnt

		surface._reset=0
		surface._res=res

		Return res
	End Method

	Method MergeSurfaceRes:TSurfaceRes(base:TSurface,animation:TSurface,data)
		If animation=Null Return UpdateSurfaceRes(base)
		Local base_res:TD3D9SurfaceRes=UpdateSurfaceRes(base)
		Local anim_res:TD3D9SurfaceRes=UpdateSurfaceRes(animation)
		Local res:TD3D9SurfaceRes=base_res.Copy()
		res._pos=anim_res._pos
		Return res
	End Method

	Method UploadVertexData(buffer:IDirect3DVertexBuffer9 Var,data#[])
		If buffer=Null _d3ddev.CreateVertexBuffer(data.length*4,0,0,D3DPOOL_MANAGED,buffer,Null)
		Local dataptr:Byte Ptr
		Assert buffer.Lock(0,0,dataptr,0)=D3D_OK,"Failed to lock vertex buffer."
		MemCopy dataptr,data,data.length*4
		buffer.Unlock()
	End Method

End Type

Type TD3D9TextureRes Extends TTextureRes
	Field _tex:IDirect3DTexture9
End Type

Type TD3D9SurfaceRes Extends TSurfaceRes
	Field _pos:IDirect3DVertexBuffer9
	Field _nml:IDirect3DVertexBuffer9
	Field _clr:IDirect3DVertexBuffer9
	Field _tri:IDirect3DIndexBuffer9
	Field _tex:IDirect3DVertexBuffer9[8]

	Method Copy:TD3D9SurfaceRes()
		Local res:TD3D9SurfaceRes=New TD3D9SurfaceRes
		res._vertexcnt=_vertexcnt;res._trianglecnt=_trianglecnt
		res._pos=_pos;res._nml=_nml;res._clr=_clr;res._tri=_tri;
		For Local i=0 To 7
			res._tex[i]=_tex[i]
		Next
		Return res
	End Method
End Type

Rem
	bbdoc: Needs documentation. #TODO
End Rem
Function D3D9MaxB3DDriver:TD3D9MaxB3DDriver()
	If D3D9Max2DDriver()
		Global driver:TD3D9MaxB3DDriver=New TD3D9MaxB3DDriver
		driver._parent=D3D9Max2DExDriver()
		Return driver
	End If
End Function

Rem
	bbdoc: Utility function that sets the MaxB3D D3D9 driver and calls Graphics.
End Rem
Function D3D9Graphics3D:TGraphics(width,height,depth=0,hertz=0,flags=0)
	SetGraphicsDriver D3D9MaxB3DDriver(),GRAPHICS_BACKBUFFER|GRAPHICS_DEPTHBUFFER
	Return Graphics(width,height,depth,hertz,flags)
End Function

Local driver:TD3D9MaxB3DDriver=D3D9MaxB3DDriver()
If driver SetGraphicsDriver driver,GRAPHICS_BACKBUFFER|GRAPHICS_DEPTHBUFFER

?

Strict

Rem
	bbdoc: Direct3D 9 renderer for MaxB3D
End Rem
Module MaxB3D.D3D9Driver
ModuleInfo "Author: Kevin Primm"
ModuleInfo "License: MIT"

Import MaxB3D.Core
Import Prime.D3D9Max2DEx
Import Prime.DirectXEx

?Win32

Import "d3d9.cpp"

Private

Extern "C"
	Function maxb3dD3D9VertexElements:Byte Ptr()'="_maxb3dD3D9VertexElements@0"
End Extern

Function GetD3D9MaxB3DVertexDecl:IDirect3DVertexDeclaration9(d3ddev:IDirect3DDevice9)
	Global decl:IDirect3DVertexDeclaration9,dev:IDirect3DDevice9
	If decl=Null Or dev<>d3ddev
		Assert d3ddev.CreateVertexDeclaration(maxb3dD3D9VertexElements(),decl)=D3D_OK,"Failed to create vertex declaration."
		dev=d3ddev
	EndIf
	Return decl
End Function

Function Pow2Size( n )
	Local t=1
	While t<n
		t:*2
	Wend
	Return t
End Function

Public

Type TD3D9MaxB3DDriver Extends TMaxB3DDriver
	Field _d3ddev:IDirect3DDevice9
	Field _viewporton

	Method SetGraphics( g:TGraphics )
		Super.SetGraphics g
		_d3ddev=Null
		If g<>Null
			_d3ddev=TD3D9Graphics(TMax2DGraphics(g)._graphics).GetDirect3DDevice()
			EndMax2D
		EndIf
	End Method

	Method MakeBuffer:TBuffer(src:Object,width,height,flags)
		Return _parent.MakeBuffer(src,width,height,flags)
	End Method

	Method GetCaps:TCaps()
		Local caps:TCaps=New TCaps
		Return caps
	End Method

	Method Abbr$()
		Return "d3d9"
	End Method

	Method SetMax2D(enable)
		If enable
			Global identity:TMatrix=TMatrix.Identity()
			Local width=GraphicsWidth(),height=GraphicsHeight()
			Local matrix#[]=[..
			2.0/width,0.0,0.0,0.0,..
			 0.0,-2.0/height,0.0,0.0,..
			 0.0,0.0,1.0,0.0,..
			 -1-(1.0/width),1+(1.0/height),1.0,1.0]

			_d3ddev.SetTransform D3DTS_PROJECTION,matrix
			_d3ddev.SetTransform D3DTS_WORLD,identity.ToPtr()
			_d3ddev.SetTransform D3DTS_VIEW,identity.ToPtr()
			_d3ddev.SetTransform D3DTS_TEXTURE0,identity.ToPtr()

			_d3ddev.SetVertexDeclaration Null
			_d3ddev.SetIndices Null
			_d3ddev.SetFVF D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1

			_d3ddev.SetRenderState D3DRS_LIGHTING,False
			_d3ddev.SetRenderState D3DRS_ZENABLE,False
			_d3ddev.SetRenderState D3DRS_SCISSORTESTENABLE,_viewporton

			_d3ddev.SetRenderState D3DRS_ALPHATESTENABLE,True

			_d3ddev.SetRenderState D3DRS_WRAP0, 0

			_d3ddev.SetRenderState D3DRS_FOGENABLE,False

			For Local i=2 To 7
				_d3ddev.SetTexture i,Null
			Next

			_d3ddev.SetTextureStageState 0,D3DTSS_COLOROP,D3DTOP_MODULATE
			_d3ddev.SetTextureStageState 0,D3DTSS_ALPHAOP,D3DTOP_MODULATE

			_d3ddev.SetRenderState D3DRS_CULLMODE,D3DCULL_NONE
			_d3ddev.SetRenderState D3DRS_FILLMODE,D3DFILL_SOLID
		Else
			_d3ddev.SetRenderState D3DRS_ZENABLE,True
			_d3ddev.SetRenderState D3DRS_ZWRITEENABLE,True

			_d3ddev.GetRenderState D3DRS_SCISSORTESTENABLE,_viewporton
			_d3ddev.SetRenderState D3DRS_SCISSORTESTENABLE,True

			_d3ddev.SetRenderState D3DRS_LIGHTING,True
			_d3ddev.SetRenderState D3DRS_NORMALIZENORMALS,True
			_d3ddev.SetRenderState D3DRS_AMBIENT,D3DCOLOR_RGB(WorldConfig.AmbientRed,WorldConfig.AmbientGreen,WorldConfig.AmbientBlue)
			_d3ddev.SetRenderState D3DRS_COLORVERTEX, True
			_d3ddev.SetRenderState D3DRS_DIFFUSEMATERIALSOURCE,D3DMCS_MATERIAL

			_d3ddev.SetRenderState D3DRS_SHADEMODE,D3DSHADE_GOURAUD

			'_d3ddev.SetRenderState D3DRS_ALPHAREF, 1
			'_d3ddev.SetRenderState D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL

			_d3ddev.SetRenderState D3DRS_CULLMODE,D3DCULL_CW
		EndIf
	End Method

	Method SetCamera(camera:TCamera)
		Local clearflags
		If camera._clsmode&CLSMODE_COLOR clearflags:|D3DCLEAR_TARGET
		If camera._clsmode&CLSMODE_DEPTH clearflags:|D3DCLEAR_ZBUFFER

		Local viewport[]=[camera._viewx,camera._viewy,camera._viewwidth-camera._viewx,camera._viewheight-camera._viewy]
		_d3ddev.SetScissorRect viewport
		_d3ddev.Clear(1,viewport,clearflags,D3DCOLOR_XRGB(camera._brush._r*255,camera._brush._g*255,camera._brush._b*255),1.0,0)

		Select camera._fogmode
		Case FOGMODE_LINEAR
			_d3ddev.SetRenderState D3DRS_FOGENABLE,True
			_d3ddev.SetRenderState D3DRS_FOGSTART,Int(Varptr camera._fognear)
			_d3ddev.SetRenderState D3DRS_FOGEND,Int(Varptr camera._fogfar)
		Default
			_d3ddev.SetRenderState D3DRS_FOGENABLE,False
		End Select

		Local ratio#=(Float(camera._viewwidth)/camera._viewheight)
		Local proj_matrix:TMatrix=TMatrix.PerspectiveFovRH(ATan((1.0/(camera._zoom*ratio)))*2.0,ratio#,Max(0,camera._near-0.396250010),camera._far) ' Ugly hack. Real cause needs investigation.
		proj_matrix=TMatrix.Scale(-1,1,-1).Multiply(proj_matrix)
		_d3ddev.SetTransform D3DTS_PROJECTION,proj_matrix.ToPtr()

		Local matrix:TMatrix=camera._matrix.Inverse()
		_d3ddev.SetTransform D3DTS_VIEW,matrix.ToPtr()

		camera._lastmodelview=matrix
		camera._lastprojection=proj_matrix

		camera._lastviewport[0]=camera._viewx
		camera._lastviewport[1]=camera._viewy
		camera._lastviewport[2]=camera._viewwidth
		camera._lastviewport[3]=camera._viewheight

		camera._lastfrustum=TFrustum.Extract(camera._lastmodelview,camera._lastprojection)
	End Method

	Method SetLight(light:TLight,index)
		If light=Null
			_d3ddev.LightEnable index,False
			Return
		EndIf

		Local brush:TBrush=light._brush

		Global d3dlight:D3DLIGHT9=New D3DLIGHT9
		d3dlight.Type_=D3DLIGHT_DIRECTIONAL
		d3dlight.Diffuse_r=brush._r;d3dlight.Diffuse_g=brush._g;d3dlight.Diffuse_b=brush._b;d3dlight.Diffuse_a=brush._a
		'd3dlight.Ambient_r=WorldConfig.AmbientRed/255.0;d3dlight.Ambient_g=WorldConfig.AmbientGreen/255.0;d3dlight.Ambient_b=WorldConfig.AmbientBlue/255.0;d3dlight.Ambient_a=1.0

		d3dlight.Direction_x=0.0;d3dlight.Direction_y=0.0;d3dlight.Direction_z=1.0

		Local matrix:TMatrix=light._matrix
		matrix.TransformVec3 d3dlight.Direction_x,d3dlight.Direction_y,d3dlight.Direction_z
		matrix.GetPosition d3dlight.Position_x,d3dlight.Position_y,d3dlight.Position_z

		d3dlight.Range=light._range

		_d3ddev.SetLight index,d3dlight
		_d3ddev.LightEnable index,True
	End Method

	Method SetBrush(brush:TBrush,hasalpha)
		_d3ddev.SetRenderState D3DRS_ALPHATESTENABLE,False

		Local alpha_blending = (brush._fx&FX_FORCEALPHA Or hasalpha)>0
		_d3ddev.SetRenderState D3DRS_ALPHABLENDENABLE,alpha_blending
		_d3ddev.SetRenderState D3DRS_ZWRITEENABLE,Not alpha_blending

		If brush._fx&FX_FULLBRIGHT
			_d3ddev.SetRenderState D3DRS_AMBIENT,$ffffffff
		Else
			_d3ddev.SetRenderState D3DRS_AMBIENT,D3DCOLOR_RGB(WorldConfig.AmbientRed,WorldConfig.AmbientGreen,WorldConfig.AmbientBlue)
		EndIf

		If brush._fx&FX_NOCULLING
			_d3ddev.SetRenderState D3DRS_CULLMODE,D3DCULL_NONE
		Else
			_d3ddev.SetRenderState D3DRS_CULLMODE,D3DCULL_CW
		EndIf

		If brush._fx&FX_WIREFRAME Or WorldConfig.Wireframe
			_d3ddev.SetRenderState D3DRS_FILLMODE,D3DFILL_WIREFRAME
		Else
			_d3ddev.SetRenderState D3DRS_FILLMODE,D3DFILL_SOLID
		EndIf

		Local material:D3DMATERIAL9 = New D3DMATERIAL9
		material.Diffuse_r=brush._r;material.Diffuse_g=brush._g;material.Diffuse_b=brush._b;material.Diffuse_a=brush._a
		material.Ambient_r=brush._r;material.Ambient_g=brush._g;material.Ambient_b=brush._b;material.Ambient_a=brush._a

		_d3ddev.SetMaterial material

		For Local i=0 To 7
			Local texture:TTexture=brush._texture[i]
			If texture=Null Or texture._blend=BLEND_NONE
				_d3ddev.SetTexture i,Null
				Continue
			EndIf

			_d3ddev.SetTexture i,UpdateTextureRes(texture._frame[brush._textureframe[i]],texture._flags)._tex

			Local matrix:TMatrix=TMatrix.Identity()
			'matrix._m[0,0]=-texture._sx
			'matrix._m[1,1]=texture._sy
			matrix=TMatrix.YawPitchRoll(0,0,-texture._r).Multiply(matrix)
			matrix=TMatrix.Scale(-texture._sx,texture._sy,1).Multiply(matrix)
			matrix._m[2,0]=-texture._px
			matrix._m[2,1]=-texture._py

			_d3ddev.SetTransform D3DTS_TEXTURE0+i,matrix.ToPtr()

			_d3ddev.SetTextureStageState i,D3DTSS_TEXTURETRANSFORMFLAGS,D3DTTFF_COUNT2

			_d3ddev.SetSamplerState i,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR
			_d3ddev.SetSamplerState i,D3DSAMP_MINFILTER,D3DTEXF_LINEAR

			_d3ddev.SetRenderState D3DRS_ALPHATESTENABLE, texture._flags&TEXTURE_ALPHA

			If texture._flags&TEXTURE_MIPMAP
				_d3ddev.SetSamplerState i,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR
			Else
				_d3ddev.SetSamplerState i,D3DSAMP_MIPFILTER,D3DTEXF_NONE
			EndIf

			If texture._flags&TEXTURE_CLAMPU
				_d3ddev.SetSamplerState i,D3DSAMP_ADDRESSU,D3DTADDRESS_CLAMP
			Else
				_d3ddev.SetSamplerState i,D3DSAMP_ADDRESSU,D3DTADDRESS_WRAP
			EndIf

			If texture._flags&TEXTURE_CLAMPV
				_d3ddev.SetSamplerState i,D3DSAMP_ADDRESSU,D3DTADDRESS_CLAMP
			Else
				_d3ddev.SetSamplerState i,D3DSAMP_ADDRESSV,D3DTADDRESS_WRAP
			EndIf

			If texture._flags&TEXTURE_SPHMAP
				_d3ddev.SetTextureStageState i,D3DTSS_TEXCOORDINDEX,D3DTSS_TCI_SPHEREMAP
			Else
				_d3ddev.SetTextureStageState i,D3DTSS_TEXCOORDINDEX,texture._coords
			EndIf

			'_d3ddev.SetTextureStageState i,D3DTSS_COLOROP,D3DTOP_SELECTARG1
			'_d3ddev.SetTextureStageState i,D3DTSS_ALPHAOP,D3DTOP_SELECTARG2

			Select texture._blend
			Case BLEND_ALPHA
				'glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL)
			Case BLEND_MULTIPLY
				_d3ddev.SetTextureStageState i,D3DTSS_COLOROP,D3DTOP_MODULATE
				_d3ddev.SetTextureStageState i,D3DTSS_ALPHAOP,D3DTOP_MODULATE
			Case BLEND_ADD
				_d3ddev.SetTextureStageState i,D3DTSS_COLOROP,D3DTOP_ADD
				_d3ddev.SetTextureStageState i,D3DTSS_ALPHAOP,D3DTOP_ADD
			Case BLEND_DOT3
				_d3ddev.SetTextureStageState i,D3DTSS_COLOROP,D3DTOP_DOTPRODUCT3
				_d3ddev.SetTextureStageState i,D3DTSS_ALPHAOP,D3DTOP_DOTPRODUCT3
			Case BLEND_MULTIPLY2
				_d3ddev.SetTextureStageState i,D3DTSS_COLOROP,D3DTOP_MODULATE2X
				_d3ddev.SetTextureStageState i,D3DTSS_ALPHAOP,D3DTOP_MODULATE2X
			Default
				_d3ddev.SetTextureStageState i,D3DTSS_COLOROP,D3DTOP_MODULATE
				_d3ddev.SetTextureStageState i,D3DTSS_ALPHAOP,D3DTOP_MODULATE
			End Select
		Next
	End Method

	Method RenderSurface(resource:TSurfaceRes,brush:TBrush)
		Local res:TD3D9SurfaceRes=TD3D9SurfaceRes(resource)

		_d3ddev.SetFVF 0
		_d3ddev.SetVertexDeclaration GetD3D9MaxB3DVertexDecl(_d3ddev)
		_d3ddev.SetStreamSource 0,res._pos,0,12
		_d3ddev.SetStreamSource 1,res._nml,0,12
		_d3ddev.SetStreamSource 2,res._clr,0,16
		_d3ddev.SetStreamSource 3,res._tex[0],0,8
		_d3ddev.SetIndices res._tri
		_d3ddev.DrawIndexedPrimitive D3DPT_TRIANGLELIST,0,0,res._vertexcnt,0,res._trianglecnt

		Return res._trianglecnt
	End Method

	Method RenderSprite(sprite:TSprite)
		Global _data#[]=[ -1.0, 1.0,0.0, 0.0,0.0,1.0, 0.0,0.0, ..
		                   1.0, 1.0,0.0, 0.0,0.0,1.0, 1.0,0.0, ..
		                  -1.0,-1.0,0.0, 0.0,0.0,1.0, 0.0,1.0, ..
		                   1.0,-1.0,0.0, 0.0,0.0,1.0, 1.0,1.0     ]

		_d3ddev.SetVertexDeclaration Null
		_d3ddev.SetFVF D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1
		_d3ddev.DrawPrimitiveUP D3DPT_TRIANGLESTRIP,2,_data,8*4
	End Method

	Method BeginEntityRender(entity:TEntity)
		_d3ddev.SetTransform D3DTS_WORLD,entity.GetMatrix(True).ToPtr()
	End Method

	Method EndEntityRender(entity:TEntity)
	End Method

	Method RenderFlat(flat:TFlat)
		Local x#,y#,z#
		flat.GetScale x,y,z,True

		Global _data#[]=[ -1.0,0.0, 1.0, 0.0,1.0,0.0, 0.0,0.0, ..
		                   1.0,0.0, 1.0, 0.0,1.0,0.0,   x,0.0, ..
		                  -1.0,0.0,-1.0, 0.0,1.0,0.0, 0.0,  z, ..
		                   1.0,0.0,-1.0, 0.0,1.0,0.0,   x,  z     ]

		_d3ddev.SetVertexDeclaration Null
		_d3ddev.SetFVF D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1
		_d3ddev.DrawPrimitiveUP D3DPT_TRIANGLESTRIP,2,_data,8*4
	End Method

	Method RenderTerrain(terrain:TTerrain)
	End Method

	Method RenderBSPTree(tree:TBSPTree)
		Local node:TBSPNode=tree.Node
		If node=Null Return
		Local triangles
		triangles:+RenderBSPTree(node.In)

		_d3ddev.SetVertexDeclaration Null
		_d3ddev.SetFVF D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1

		For Local poly:TBSPPolygon=EachIn node.On
			Local ptA:TVector=poly.Point[0],v0
			Local trianglecnt=poly.Count()-2
			Local data#[trianglecnt*24],dataptr:Float Ptr=data
			For Local i=0 To trianglecnt-1
				Local ptB:TVector=poly.Point[i+1],ptC:TVector=poly.Point[i+2]
				data[i*24+00]=ptA.x;data[i*24+01]=ptA.y;data[i*24+02]=ptA.z;data[i*24+03]=poly.Plane.x;data[i*24+04]=poly.Plane.y;	data[i*24+05]=poly.Plane.z;data[i*24+06]=0.0;data[i*24+07]=0.0
				data[i*24+08]=ptB.x;data[i*24+09]=ptB.y;data[i*24+10]=ptB.z;data[i*24+11]=poly.Plane.x;data[i*24+12]=poly.Plane.y;	data[i*24+13]=poly.Plane.z;data[i*24+14]=0.0;data[i*24+15]=0.0
				data[i*24+16]=ptC.x;data[i*24+17]=ptC.y;data[i*24+18]=ptC.z;data[i*24+19]=poly.Plane.x;data[i*24+20]=poly.Plane.y;data[i*24+21]=poly.Plane.z;data[i*24+22]=0.0;data[i*24+23]=0.0
			Next
			_d3ddev.DrawPrimitiveUP D3DPT_TRIANGLELIST,trianglecnt,data,8*4
			triangles:+trianglecnt
		Next

		triangles:+RenderBSPTree(node.Out)
		Return triangles
	End Method

	Method UpdateTextureRes:TD3D9TextureRes(frame:TTextureFrame,flags)
		Local res:TD3D9TextureRes=TD3D9TextureRes(frame._res)
		If res And frame._updateres=False Return res

		If res=Null res=New TD3D9TextureRes
		frame._res=res

		Local pixmap:TPixmap=frame._pixmap
		Local tex_width=Pow2Size(pixmap.width),tex_height=Pow2Size(pixmap.height)
		pixmap=ConvertPixmap(ResizePixmap(pixmap,tex_width,tex_height),PF_BGRA8888)
		If res._tex=Null Assert _d3ddev.CreateTexture(tex_width,tex_height,(flags & TEXTURE_MIPMAP)=0,D3DUSAGE_AUTOGENMIPMAP,D3DFMT_A8R8G8B8,D3DPOOL_MANAGED,res._tex,Null)=D3D_OK

		Local rect:D3DLOCKED_RECT=New D3DLOCKED_RECT
		res._tex.LockRect 0,rect,Null,0
		MemCopy rect.pBits,pixmap.pixels,pixmap.width*pixmap.height*4
		res._tex.UnlockRect 0

		frame._updateres=False

		Return res
	End Method

	Method UpdateSurfaceRes:TD3D9SurfaceRes(surface:TSurface)
		Local res:TD3D9SurfaceRes=TD3D9SurfaceRes(surface._res)
		If res=Null res=New TD3D9SurfaceRes;surface._reset=-1

		If surface._reset=0 Return res
		If surface._reset=-1 surface._reset=1|2|4|8|16|32|64|128|256

		If surface._reset&1 And surface._vertexpos UploadVertexData res._pos,surface._vertexpos
		If surface._reset&2 And surface._vertexnml UploadVertexData res._nml,surface._vertexnml
		If surface._reset&4 And surface._vertexclr UploadVertexData res._clr,surface._vertexclr
		If surface._reset&8 And surface._triangle
			If res._tri=Null _d3ddev.CreateIndexBuffer(surface._triangle.length*4,0,D3DFMT_INDEX32,D3DPOOL_MANAGED,res._tri,Null)
			Local dataptr:Byte Ptr
			Assert res._tri.Lock(0,0,dataptr,0)=D3D_OK,"Failed to lock index buffer."
			MemCopy dataptr,surface._triangle,surface._triangle.length*4
			res._tri.Unlock()
		EndIf

		For Local i=0 To surface._vertextex.length-1
			If surface._reset&Int(2^(4+i)) UploadVertexData res._tex[i],surface._vertextex[i]
		Next

		res._trianglecnt=surface._trianglecnt
		res._vertexcnt=surface._vertexcnt

		surface._reset=0
		surface._res=res

		Return res
	End Method

	Method MergeSurfaceRes:TSurfaceRes(base:TSurface,animation:TSurface,data)
		If animation=Null Return UpdateSurfaceRes(base)
		Local base_res:TD3D9SurfaceRes=UpdateSurfaceRes(base)
		Local anim_res:TD3D9SurfaceRes=UpdateSurfaceRes(animation)
		Local res:TD3D9SurfaceRes=base_res.Copy()
		res._pos=anim_res._pos
		Return res
	End Method

	Method UploadVertexData(buffer:IDirect3DVertexBuffer9 Var,data#[])
		If buffer=Null _d3ddev.CreateVertexBuffer(data.length*4,0,0,D3DPOOL_MANAGED,buffer,Null)
		Local dataptr:Byte Ptr
		Assert buffer.Lock(0,0,dataptr,0)=D3D_OK,"Failed to lock vertex buffer."
		MemCopy dataptr,data,data.length*4
		buffer.Unlock()
	End Method

End Type

Type TD3D9TextureRes Extends TTextureRes
	Field _tex:IDirect3DTexture9
End Type

Type TD3D9SurfaceRes Extends TSurfaceRes
	Field _pos:IDirect3DVertexBuffer9
	Field _nml:IDirect3DVertexBuffer9
	Field _clr:IDirect3DVertexBuffer9
	Field _tri:IDirect3DIndexBuffer9
	Field _tex:IDirect3DVertexBuffer9[8]

	Method Copy:TD3D9SurfaceRes()
		Local res:TD3D9SurfaceRes=New TD3D9SurfaceRes
		res._vertexcnt=_vertexcnt;res._trianglecnt=_trianglecnt
		res._pos=_pos;res._nml=_nml;res._clr=_clr;res._tri=_tri;
		For Local i=0 To 7
			res._tex[i]=_tex[i]
		Next
		Return res
	End Method
End Type

Rem
	bbdoc: Needs documentation. #TODO
End Rem
Function D3D9MaxB3DDriver:TD3D9MaxB3DDriver()
	If D3D9Max2DDriver()
		Global driver:TD3D9MaxB3DDriver=New TD3D9MaxB3DDriver
		driver._parent=D3D9Max2DExDriver()
		Return driver
	End If
End Function

Rem
	bbdoc: Utility function that sets the MaxB3D D3D9 driver and calls Graphics.
End Rem
Function D3D9Graphics3D:TGraphics(width,height,depth=0,hertz=0,flags=0)
	SetGraphicsDriver D3D9MaxB3DDriver(),GRAPHICS_BACKBUFFER|GRAPHICS_DEPTHBUFFER
	Return Graphics(width,height,depth,hertz,flags)
End Function

Local driver:TD3D9MaxB3DDriver=D3D9MaxB3DDriver()
If driver SetGraphicsDriver driver,GRAPHICS_BACKBUFFER|GRAPHICS_DEPTHBUFFER

?


prep for cleanup

prep for cleanup

' Blue Moon
' JIT engine/opcodes

' this is the core of the runtime: the machine code generator and opcodes
' it's a halfway blend between JIT and interpreter: a basic JIT generates machine code
' for "unrolled" interpreter loops, theoretically (ha!) providing a speed boost
' it can also "inline" selected operations, coming closer to being a true JIT

' for now this is an Include rather than Import lib due to recursive dependency
' we'll fix that later

Rem
SuperStrict

Import "blueerror.bmx"
Import "bluevm.bmx"
Import etc etc...
End Rem

'Private	'may need to rename and open these (in the shared interface?)
Extern
	Type Stack
		Field retIP:Byte Ptr, prevBase:Stack, varp:Long Ptr, func:Bytecode, _:Long Ptr, argv:Long Ptr, retv:Long Ptr, argc:Short, retc:Short
	End Type
	Type Bytecode
		Field mcode:Byte Ptr, idMod:Int, kcount:Int, pcount:Int, upvars:Int, frameSz:Int, icount:Int, vm:Byte Ptr
	End Type
End Extern
'Public

Type BlueJIT Final
	Const PROLOGUESZ:Int = 25, ISIZE:Int = 5

	Global opTbl:Int(s:Stack, b:Bytecode, r:Byte Ptr)[], opc:BlueOpcode
	Global Prologue:Int[] = [ ..
		$90, $90, ..
		$8b, $44, $24, $04, ..                     'mov 4(%esp), %eax
		$89, $44, $24, $f4, ..                     'mov %eax, -12(%esp)
		$c7, $44, $24, $f8, $FF, $FF, $FF, $FF, .. 'movl ########, -8(%esp)
		$89, $64, $24, $fc, ..                     'mov %esp, -4(%esp)
		$83, $ec, $0c..                            'sub $12, %esp
	]

	Function Compile:Byte Ptr(vm:BlueVM, ins:Byte Ptr, icount:Int, bytecode:Bytecode)
		?Not x86
		RuntimeError "The Blue Moon JIT does not support your platform (x86-32 only at this time)"
		?
		Assert SizeOf(0:Long) = SizeOf(0:Double) And SizeOf(0:Int) = 4 And SizeOf(0:Long) = 8 And SizeOf(Byte Ptr(0)) = 4, ..
			"assumptions about platform datatype sizes are invalid"

		If opTbl = Null Then InitOpTbl()

		' compute executable code size
		Local codesize:Int = PROLOGUESZ, opPos:Int[icount]	'since sizes are irregular, looking up offsets is easiest
		For Local i:Int = 0 Until icount
			opPos[i] = codesize	'finalized below
			Select ins[i * 8]
				Case opc.SETTAB, opc.GETTAB
					codesize :+ ISIZE * 2
				Default
					codesize :+ ISIZE
			End Select
		Next

		' allocate executable space
		Local code:Byte Ptr = vm.mem.AllocCodeBlock(codesize)

		'finalize op offsets se we can look them up
		For Local i:Int = 0 Until icount
			opPos[i] :+ Int(code)	'direct to executable space
		Next

		' emplace prologue (used for calling in from native only)
		For Local p:Int = 0 Until PROLOGUESZ
			code[p] = Prologue[p]
		Next
		Byte Ptr Ptr(code + 14)[0] = Byte Ptr(bytecode)	'replace the ########

		' epilogue (shared location per-vm, probably already set)
		vm.mem.returnToNative[0] = $c30cc483	'add $12, %esp ; ret  - i.e. restore the stack to normal

		Local ktable:Long Ptr = Long Ptr(ins + 8 * bytecode.icount + 8 * bytecode.upvars)	'constant table

		' generate machine code!
		For Local i:Int = 0 Until icount	'emplace opcode calls and supporting bytecode data
			Local codep:Byte Ptr = Byte Ptr(opPos[i]), bytecodep:Byte Ptr = Byte Ptr(Int(codep) + vm.mem.PAGESZ)
			Local bi:Int = i * 8, op:Int = ins[bi], ip:Int Ptr = Int Ptr(ins + bi), func:Byte Ptr = opTbl[op]

			codep[0] = $e8	'call
			Byte Ptr Ptr(codep + 1)[0] = func - Int(codep + ISIZE)

			bytecodep[0] = ins[bi + 1] ; bytecodep[1] = ins[bi + 2]	'used by most, may get overwritten

			Select op
				Case opc.LOADSI, opc.CLOSURE, opc.RET, opc.POSTCALL
					Int Ptr(bytecodep + 1)[0] = ip[1]

				Case opc.LOADK
					Long Ptr Ptr(bytecodep + 1)[0] = ktable + ip[1]

				Case opc.SETTABSI, opc.GETTABSI
					Short Ptr(bytecodep)[1] = ip[1]

				Case opc.SETTAB, opc.GETTAB
					codep[ISIZE] = $90 ; Int Ptr(codep + ISIZE + 1)[0] = $90909090	'nops; the space is needed in the bytecode
					Long Ptr Ptr(bytecodep + 2)[0] = ktable + ip[1]
					Long Ptr Ptr(bytecodep + 2)[1] = Null	'inline cache space

				Case opc.GETUPV
					If Int Ptr(ins + icount * 8)[2 * bytecodep[1]] = -1
						Byte Ptr Ptr(codep + 1)[0] = Byte Ptr(BlueJIT.GETENV) - Int(codep + ISIZE)
						Byte Ptr Ptr(bytecodep + 1)[0] = Varptr(vm._ENV.val)	'note: doesn't allow _ENV to be changed from Max
					EndIf

				Case opc.CALL
					Short Ptr(bytecodep)[1] = ip[1]

				Case opc.JIF, opc.JNOT
					Int Ptr(bytecodep + 1)[0] = opPos[i + ip[1]]

				Case opc.JMP
					codep[0] = $e9	'use a true jump
					Int Ptr(codep + 1)[0] = opPos[i + ip[1]] - (opPos[i] + ISIZE)

				Default	'binary operations A = B op C
					bytecodep[2] = ip[1]
			End Select
		Next

		Return code
	End Function

	Const STACKFRAME_INC:Int = BlueVMMemory.STACKFRAMESZ + 4, BYTECODE_INC:Int = BlueVMMemory.BYTECODESZ + 4
	Const IP_OFFSET:Int = BlueVMMemory.PAGESZ - ISIZE

	'note: extern vars must take into account the vtbl offset, so that converting from a typed pointer can be a simple cast (faster)

	Function MOV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "MOV      //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		varp[rp[0]] = varp[rp[1]]
	End Function
	Function GETLC(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function SETLC(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "SETLC    //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(bc.vm)
		vm.mem.Write(Long Ptr Ptr(varp + rp[0])[0][0], varp[rp[1]])	'unlikely but technically possible to need this
	End Function
	Function LOADK(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "LOADK    //"
		Local rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET, varp:Long Ptr = stk.varp + rp[0], kp:Long Ptr = Long Ptr Ptr(rp + 1)[0]
		varp[0] = kp[0]
	End Function
	Function LOADSI(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "LOADSI   //"
		Local rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local val:Int = Int Ptr(rp + 1)[0]
		Double Ptr(stk.varp)[rp[0]] = Double(val)
	End Function
	Function LOADBOOL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "LOADBOOL //"
		Local rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET, vp:Int Ptr = Int Ptr(stk.varp + rp[0])
		vp[1] = BlueTypeTag.NANBOX | BlueTypeTag.BOOL
		vp[0] = Int Ptr(rp + 1)[0]
	End Function
	Function LOADNIL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "LOADNIL  //"
		Local rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Int Ptr(stk.varp + rp[0])[1] = BlueTypeTag.NANBOX | BlueTypeTag.NIL
	End Function

	Function GETTAB(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "GETTAB   //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET, kp:Long Ptr = Long Ptr Ptr(rp + 2)[0]
		Local tabp:Byte Ptr = Byte Ptr Ptr(varp + rp[1])[0]
		'type check
		Print "  tag: " + Bin(Int Ptr(kp)[1])
		Local keyslot:Long Ptr = Null, slot:Long Ptr = BlueTable.GetSlot(tabp, kp[0], Varptr(keyslot))
		Print "  slot: " + Hex(Int(slot))
		If (slot = Null) Or (keyslot[0] <> kp[0])	'not in table; invoke metamethod
			'if keyslot == 1 then it's invalid
			DebugStop
		Else
			varp[rp[0]] = slot[0]
		EndIf
	End Function
	Function GETTABSI(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function SETTAB(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "SETTAB   //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET, kp:Long Ptr = Long Ptr Ptr(rp + 2)[0]
		Local tabp:Byte Ptr = Byte Ptr Ptr(varp + rp[0])[0]
		Print "  tag: " + Bin(Int Ptr(kp)[1])
		Local keyslot:Long Ptr = Null, slot:Long Ptr = BlueTable.GetSlot(tabp, kp[0], Varptr(keyslot))
		Print "  slot: " + Hex(Int(slot))
		If (slot = Null) Or (keyslot[0] <> kp[0])	'not in table; invoke metamethod or rawset
			'if keyslot == 1 then it's invalid
			Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(bc.vm)
			BlueTable.RawSet(vm.mem, tabp, kp[0], varp[rp[1]])
		Else
			slot[0] = varp[rp[1]]
		EndIf
	End Function
	Function SETTABSI(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "SETTABSI //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(bc.vm)
		Local tabp:Byte Ptr = varp + rp[0]
		Print "  " + Short Ptr(rp)[1]
		Local key:Long ; Double Ptr(Varptr(key))[0] = Short Ptr(rp)[1]
		BlueTable.RawSet(vm.mem, Byte Ptr Ptr(tabp)[0], key, varp[rp[1]])
	End Function
	Function GETTABI(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "GETTABI  //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local tabp:Byte Ptr = varp + rp[1]
		Print "  tag: " + Bin(Int Ptr(varp + rp[2])[1])
		Local val:Long = BlueTable.RawGet(Byte Ptr Ptr(tabp)[0], varp[rp[2]])
		varp[rp[0]] = val
	End Function
	Function SETTABI(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "SETTABI  //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(bc.vm)
		Local tabp:Byte Ptr = varp + rp[0]
		Print "  " + Double Ptr(varp)[rp[2]] + " " + Double Ptr(varp)[rp[1]]
		Print "  tag: " + Bin(Int Ptr(varp + rp[2])[1])
		BlueTable.RawSet(vm.mem, Byte Ptr Ptr(tabp)[0], varp[rp[2]], varp[rp[1]])
	End Function
	Function GETUPV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "GETUPV   //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Print "  " + rp[0] + " " + rp[1] + " " + rp[2]
		DebugStop
		Local upvp:Byte Ptr Ptr = Byte Ptr Ptr(Byte Ptr(stk) + STACKFRAME_INC), valp:Long Ptr = Long Ptr(upvp[rp[1]])
		varp[rp[0]] = valp[0]
	End Function
	Function GETENV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)	'"unlisted" instruction: GETUPV for _ENV is translated into this at load-time
		Print "GETENV   //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Print "  " + rp[0] + " " + Hex(Int Ptr(rp + 1)[0])
		'need a type check
		varp[rp[0]] = Long Ptr Ptr(rp + 1)[0][0]
	End Function
	Function SETUPV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function NEWTAB(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "NEWTAB   //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(bc.vm)
		Local tab:Byte Ptr = vm.mem.AllocTable(Null)
		Local d:Int Ptr = Int Ptr(varp + rp[0])
		d[0] = Int(tab) ; d[1] = BlueTypeTag.NANBOX | BlueTypeTag.TBL
	End Function
	Function CLOSURE(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "CLOSURE  //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(bc.vm)
		Local d:Int Ptr = Int Ptr(varp + rp[0])
		Local cbytecode:Bytecode = vm.funIndex[Int Ptr(rp + 1)[0]], upvp:Int Ptr = Int Ptr(Byte Ptr(cbytecode) + BYTECODE_INC + 8 * cbytecode.icount)
		Local closure:Byte Ptr = vm.mem.AllocClosure(cbytecode, cbytecode.upvars)	'upvars is always even (alignment)

		'get upvalues off the stack
		For Local u:Int = 0 Until cbytecode.upvars
			If upvp[2 * u]
				Byte Ptr Ptr(closure)[2 + u] = Byte Ptr Ptr(Byte Ptr(stk) + STACKFRAME_INC)[upvp[2 * u + 1]]
			Else
				Byte Ptr Ptr(closure)[2 + u] = Byte Ptr Ptr(varp + upvp[2 * u + 1])[0]
			EndIf
		Next

		d[0] = Int(closure) ; d[1] = BlueTypeTag.NANBOX | BlueTypeTag.FUN
	End Function
	Function NEWUPV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "NEWUPV   //"
		Local rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(bc.vm)
		Local upv:Byte Ptr = vm.mem.AllocObject(8, BlueTypeTag.UPV), d:Int Ptr = Int Ptr(stk.varp + rp[0])
		Long Ptr(upv)[0] = Long Ptr(d)[0]	'promote a value if it existed (useful for parameters)
		d[0] = Int(upv) ; d[1] = BlueTypeTag.NANBOX | BlueTypeTag.UPV
	End Function

	Function ADD(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "ADD      //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local d:Double Ptr = Double Ptr(varp + rp[0])
		Local r:Int Ptr = Int Ptr(varp + rp[1]), l:Int Ptr = Int Ptr(varp + rp[2])
		If l[1] & BlueTypeTag.NANBOX_CHK = BlueTypeTag.NANBOX Or r[1] & BlueTypeTag.NANBOX_CHK = BlueTypeTag.NANBOX
			DebugStop
		Else
			Print "  " + Double Ptr(l)[0] + " " + Double Ptr(r)[0]
			d[0] = Double Ptr(l)[0] + Double Ptr(r)[0]
		EndIf
	End Function
	Function SUB(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function MUL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function DIV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function NMOD(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function POW(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function CAT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function IDIV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function BAND(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function BOR(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function BXOR(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function BSHL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function BSHR(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function UNM(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function LNOT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function ALEN(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function BNOT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function UNP(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function EQ(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "EQ       //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = (Byte Ptr Ptr(retptr) - 4)[0] + IP_OFFSET
		Local r:Double = Double Ptr(varp)[rp[1]]
		Local l:Double = Double Ptr(varp)[rp[2]]
		Int Ptr(varp + rp[0])[0] = l = r
	End Function
	Function LT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function LEQ(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function

	Function JMP(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		RuntimeError "wait, why are we here?"
	End Function
	Function JIF(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "JIF      //"
		Local rp:Byte Ptr = (Byte Ptr Ptr(retptr) - 4)[0] + IP_OFFSET
		If Int Ptr(stk.varp + rp[0])[0]
			Local target:Int = Int Ptr(rp + 1)[0]
			Int Ptr(retptr)[-4] = target
		EndIf
	End Function
	Function JNOT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function CALL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "CALL     //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local fp:Int Ptr = Int Ptr(varp + rp[0])
		If Not PrepareCall(fp, stk, rp, varp, retptr)	'PrepareCall sets everything up so there's nothing else to do to make the call happen
			'__call metamethod
		EndIf
	End Function
	Function TCALL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function RET:Byte Ptr(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "RET      //"
		Local varp:Long Ptr = stk.varp
		Local rp0:Byte Ptr = Byte Ptr Ptr(retptr)[-4], rp:Byte Ptr = rp0 + IP_OFFSET

		Local oldStk:Stack = stk.prevBase
		Local retv:Long Ptr = varp + rp[0]
		If oldStk
			oldStk.retv = retv
			oldStk.retc = Int Ptr(rp + 1)[0]
			Byte Ptr Ptr(retptr)[-4] = stk.retIP
			Byte Ptr Ptr(retptr)[-3] = Byte Ptr(oldStk)
			Byte Ptr Ptr(retptr)[-2] = Byte Ptr(oldStk.func) + BYTECODE_INC
		Else	'return to native code
			Local codePage:Byte Ptr = Byte Ptr(Int(rp0) & ((Int(2^12)-1) Shl 20))
			Byte Ptr Ptr(retptr)[-4] = codePage + 4
			Return retv
		EndIf
	End Function
	Function RETVA(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function POSTCALL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "POSTCALL //"
		Local varp:Long Ptr = stk.varp
		Local rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET

		For Local r:Int = 0 Until Min(Int Ptr(rp + 1)[0], stk.retc)
			(varp + rp[0])[r] = stk.retv[r]
		'	Print "  return " + r + ": " + Double Ptr(stk.retv)[r]
		Next
		'nil the rest
	End Function
	Function VARARG(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function VAINIT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function CALLINIT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function

	Function PrepareCall:Int(fp:Int Ptr, stk:Stack, rp:Byte Ptr, varp:Long Ptr, retptr:Byte Ptr)
		If fp[1] = BlueTypeTag.NANBOX | BlueTypeTag.FUN
			Local newStk0:Stack ; Byte Ptr Ptr(Varptr(newStk0))[0] = Byte Ptr(stk) + stk.func.frameSz
			Local newStk:Stack = newStk0	'micro-opt: newStk0 can't be a register because of the unwieldy conversion

			newStk.retIP = Byte Ptr Ptr(retptr)[-4]
			newStk.prevBase = stk
			Local closure:Byte Ptr = Byte Ptr(fp[0])
			Local newBC:Bytecode = Bytecode Ptr(closure)[0]
			Local voff:Int = STACKFRAME_INC + 4 * newBC.upvars
			newStk.varp = Long Ptr(Byte Ptr(newStk) + voff)
			newStk.func = newBC

			Local argc_actual:Int = Short Ptr(rp)[1], argc_required:Int = newBC.pcount
		'	Local argc_min:Int ; If argc_actual < argc_required Then argc_min = argc_actual Else argc_min = argc_required
			Local argc_min:Int = argc_actual - argc_required ; argc_min = (argc_min & (argc_min Shr 31)) + argc_required 'branchless 32-bit Min

			Local argv:Long Ptr = varp + rp[1], destv:Long Ptr = newStk.varp
			For Local a:Int = 0 Until argc_min
				destv[a] = argv[a]
			'	Print "  arg " + a + ": " + argv[a]
			Next
			For Local a:Int = argc_min Until argc_required	'nil any unfilled parameters
				Int Ptr(destv + a)[1] = BlueTypeTag.NANBOX | BlueTypeTag.NIL
			Next
			newStk.argv = argv + argc_min	'argv should be to the varargs (if any)
			newStk.argc = argc_actual - argc_min	'argc is the number of varargs

			Local destup:Byte Ptr Ptr = Byte Ptr Ptr(Byte Ptr(newStk) + STACKFRAME_INC)
			Local upv:Byte Ptr Ptr = Byte Ptr Ptr(closure + 8)
			For Local up:Int = 0 Until newBC.upvars	'emplace upvars
				destup[up] = upv[up]
			'	Print "  upv " + up + ": " + destv[up]
			'	Print "  val: " + Double Ptr Ptr(Byte Ptr Ptr(closure) + 2 + up)[0][0]
			Next

			'note that the following OVERWRITE THE PARAMETERS (in release mode), so no touching stk from here on
			Byte Ptr Ptr(retptr)[-4] = newBC.mcode
			Byte Ptr Ptr(retptr)[-3] = Byte Ptr(newStk)
			Byte Ptr Ptr(retptr)[-2] = Byte Ptr(newBC) + BYTECODE_INC

		ElseIf fp[1] = BlueTypeTag.NANBOX | BlueTypeTag.NATFUN	'native call
			Local fun:Int(vm:BlueVM, ac:Int, av:Long Ptr, rv:Long Ptr) = Byte Ptr(fp[0])
			Local argc:Int = Short Ptr(rp)[1], argv:Long Ptr = varp + rp[1], retv:Long Ptr = Long Ptr(Byte Ptr(stk) + stk.func.frameSz)
			Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(stk.func.vm)
			stk.retc = fun(vm, argc, argv, retv)
			stk.retv = retv
		Else
			Return False	'not a function; take appropriate action
		EndIf
		Return True	'all good and will auto-call when the caller returns
	End Function

	Function InitOpTbl()
		opc = New BlueOpcode
		opTbl = [MOV, GETLC, SETLC, LOADK, LOADSI, LOADBOOL, LOADNIL, ..
			GETTAB, GETTABSI, SETTAB, SETTABSI, GETTABI, SETTABI, GETUPV, SETUPV, ..
			NEWTAB, CLOSURE, NEWUPV, ..
			ADD, SUB, MUL, DIV, NMOD, POW, CAT, ..
			IDIV, BAND, BOR, BXOR, BSHL, BSHR, ..
			UNM, LNOT, ALEN, BNOT, UNP, ..
			EQ, LT, LEQ, ..
			JMP, JIF, JNOT, CALL, TCALL, RETVA, RETVA, ..	'not a mistake: RET doesn't fit but we need the spacer
			POSTCALL, VARARG, VAINIT, CALLINIT]

		opTbl[opc.RET] = Byte Ptr(RET)
	End Function

	Function Identity:Byte Ptr(b:Byte Ptr)
		Return b
	End Function
	Function PointerToExtType:Byte Ptr(p:Byte Ptr)	'horrible pointer abuse (increment down so we can ignore the nonexistent vtbl)
		Return p - SizeOf(Byte Ptr(0))
	End Function
	Global BPtoS:Stack(p:Byte Ptr) = Byte Ptr(BlueJIT.PointerToExtType)
End Type

' Blue Moon
' JIT engine/opcodes

' this is the core of the runtime: the machine code generator and opcodes
' it's a halfway blend between JIT and interpreter: a basic JIT generates machine code
' for "unrolled" interpreter loops, theoretically (ha!) providing a speed boost
' it can also "inline" selected operations, coming closer to being a true JIT

' for now this is an Include rather than Import lib due to recursive dependency
' we'll fix that later

Rem
SuperStrict

Import "blueerror.bmx"
Import "bluevm.bmx"
Import etc etc...
End Rem

'Private	'may need to rename and open these (in the shared interface?)
Extern
	Type Stack
		Field retIP:Byte Ptr, prevBase:Stack, varp:Long Ptr, func:Bytecode, _:Long Ptr, argv:Long Ptr, retv:Long Ptr, argc:Short, retc:Short
	End Type
	Type Bytecode
		Field mcode:Byte Ptr, idMod:Int, kcount:Int, pcount:Int, upvars:Int, frameSz:Int, icount:Int, vm:Byte Ptr
	End Type
End Extern
'Public

Type BlueJIT Final
	Const PROLOGUESZ:Int = 25, ISIZE:Int = 5

	Global opTbl:Int(s:Stack, b:Bytecode, r:Byte Ptr)[], opc:BlueOpcode
	Global Prologue:Int[] = [ ..
		$90, $90, ..
		$8b, $44, $24, $04, ..                     'mov 4(%esp), %eax
		$89, $44, $24, $f4, ..                     'mov %eax, -12(%esp)
		$c7, $44, $24, $f8, $FF, $FF, $FF, $FF, .. 'movl ########, -8(%esp)
		$89, $64, $24, $fc, ..                     'mov %esp, -4(%esp)
		$83, $ec, $0c..                            'sub $12, %esp
	]

	Function Compile:Byte Ptr(vm:BlueVM, ins:Byte Ptr, icount:Int, bytecode:Bytecode)
		?Not x86
		RuntimeError "The Blue Moon JIT does not support your platform (x86-32 only at this time)"
		?
		Assert SizeOf(0:Long) = SizeOf(0:Double) And SizeOf(0:Int) = 4 And SizeOf(0:Long) = 8 And SizeOf(Byte Ptr(0)) = 4, ..
			"assumptions about platform datatype sizes are invalid"

		If opTbl = Null Then InitOpTbl()

		' compute executable code size
		Local codesize:Int = PROLOGUESZ, opPos:Int[icount]	'since sizes are irregular, looking up offsets is easiest
		For Local i:Int = 0 Until icount
			opPos[i] = codesize	'finalized below
			Select ins[i * 8]
				Case opc.SETTAB, opc.GETTAB
					codesize :+ ISIZE * 2
				Default
					codesize :+ ISIZE
			End Select
		Next

		' allocate executable space
		Local code:Byte Ptr = vm.mem.AllocCodeBlock(codesize)

		'finalize op offsets se we can look them up
		For Local i:Int = 0 Until icount
			opPos[i] :+ Int(code)	'direct to executable space
		Next

		' emplace prologue (used for calling in from native only)
		For Local p:Int = 0 Until PROLOGUESZ
			code[p] = Prologue[p]
		Next
		Byte Ptr Ptr(code + 14)[0] = Byte Ptr(bytecode)	'replace the ########

		' epilogue (shared location per-vm, probably already set)
		vm.mem.returnToNative[0] = $c30cc483	'add $12, %esp ; ret  - i.e. restore the stack to normal

		Local ktable:Long Ptr = Long Ptr(ins + 8 * bytecode.icount + 8 * bytecode.upvars)	'constant table

		' generate machine code!
		For Local i:Int = 0 Until icount	'emplace opcode calls and supporting bytecode data
			Local codep:Byte Ptr = Byte Ptr(opPos[i]), bytecodep:Byte Ptr = Byte Ptr(Int(codep) + vm.mem.PAGESZ)
			Local bi:Int = i * 8, op:Int = ins[bi], ip:Int Ptr = Int Ptr(ins + bi), func:Byte Ptr = opTbl[op]

			codep[0] = $e8	'call
			Byte Ptr Ptr(codep + 1)[0] = func - Int(codep + ISIZE)

			bytecodep[0] = ins[bi + 1] ; bytecodep[1] = ins[bi + 2]	'used by most, may get overwritten

			Select op
				Case opc.LOADSI, opc.CLOSURE, opc.RET, opc.POSTCALL
					Int Ptr(bytecodep + 1)[0] = ip[1]

				Case opc.LOADK
					Long Ptr Ptr(bytecodep + 1)[0] = ktable + ip[1]

				Case opc.SETTABSI, opc.GETTABSI
					Short Ptr(bytecodep)[1] = ip[1]

				Case opc.SETTAB, opc.GETTAB
					codep[ISIZE] = $90 ; Int Ptr(codep + ISIZE + 1)[0] = $90909090	'nops; the space is needed in the bytecode
					Long Ptr Ptr(bytecodep + 2)[0] = ktable + ip[1]
					Long Ptr Ptr(bytecodep + 2)[1] = Null	'inline cache space

				Case opc.GETUPV
					If Int Ptr(ins + icount * 8)[2 * bytecodep[1]] = -1
						Byte Ptr Ptr(codep + 1)[0] = Byte Ptr(BlueJIT.GETENV) - Int(codep + ISIZE)
						Byte Ptr Ptr(bytecodep + 1)[0] = Varptr(vm._ENV.val)	'note: doesn't allow _ENV to be changed from Max
					EndIf

				Case opc.CALL
					Short Ptr(bytecodep)[1] = ip[1]

				Case opc.JIF, opc.JNOT
					Int Ptr(bytecodep + 1)[0] = opPos[i + ip[1]]

				Case opc.JMP
					codep[0] = $e9	'use a true jump
					Int Ptr(codep + 1)[0] = opPos[i + ip[1]] - (opPos[i] + ISIZE)

				Default	'binary operations A = B op C
					bytecodep[2] = ip[1]
			End Select
		Next

		Return code
	End Function

	Const STACKFRAME_INC:Int = BlueVMMemory.STACKFRAMESZ + 4, BYTECODE_INC:Int = BlueVMMemory.BYTECODESZ + 4
	Const IP_OFFSET:Int = BlueVMMemory.PAGESZ - ISIZE

	'note: extern vars must take into account the vtbl offset, so that converting from a typed pointer can be a simple cast (faster)

	Function MOV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "MOV      //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		varp[rp[0]] = varp[rp[1]]	'assigning through Double is unsafe as it can corrupt the bit pattern
	End Function
	Function GETLC(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function SETLC(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "SETLC    //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		'add a write barrier here
		Long Ptr Ptr(varp + rp[0])[0][0] = varp[rp[1]]
	End Function
	Function LOADK(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "LOADK    //"
		Local rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET, varp:Long Ptr = stk.varp + rp[0], kp:Long Ptr = Long Ptr Ptr(rp + 1)[0]
		varp[0] = kp[0]
	End Function
	Function LOADSI(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "LOADSI   //"
		Local rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local val:Int = Int Ptr(rp + 1)[0]
		Double Ptr(stk.varp)[rp[0]] = Double(val)
	End Function
	Function LOADBOOL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "LOADBOOL //"
		Local rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET, vp:Int Ptr = Int Ptr(stk.varp + rp[0])
		vp[1] = BlueTypeTag.NANBOX | BlueTypeTag.BOOL
		vp[0] = Int Ptr(rp + 1)[0]
	End Function
	Function LOADNIL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "LOADNIL  //"
		Local rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Int Ptr(stk.varp + rp[0])[1] = BlueTypeTag.NANBOX | BlueTypeTag.NIL
	End Function

	Function GETTAB(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "GETTAB   //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET, kp:Long Ptr = Long Ptr Ptr(rp + 2)[0]
		Local tabp:Byte Ptr = Byte Ptr Ptr(varp + rp[1])[0]
		'type check
		Print "  tag: " + Bin(Int Ptr(kp)[1])
		Local keyslot:Long Ptr = Null, slot:Long Ptr = BlueTable.GetSlot(tabp, kp[0], Varptr(keyslot))
		Print "  slot: " + Hex(Int(slot))
		If (slot = Null) Or (keyslot[0] <> kp[0])	'not in table; invoke metamethod
			'if keyslot == 1 then it's invalid
			DebugStop
		Else
			varp[rp[0]] = slot[0]
		EndIf
	End Function
	Function GETTABSI(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function SETTAB(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "SETTAB   //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET, kp:Long Ptr = Long Ptr Ptr(rp + 2)[0]
		Local tabp:Byte Ptr = Byte Ptr Ptr(varp + rp[0])[0]
		Print "  tag: " + Bin(Int Ptr(kp)[1])
		Local keyslot:Long Ptr = Null, slot:Long Ptr = BlueTable.GetSlot(tabp, kp[0], Varptr(keyslot))
		Print "  slot: " + Hex(Int(slot))
		If (slot = Null) Or (keyslot[0] <> kp[0])	'not in table; invoke metamethod or rawset
			'if keyslot == 1 then it's invalid
			Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(bc.vm)
			BlueTable.RawSet(vm.mem, tabp, kp[0], varp[rp[1]])
		Else
			slot[0] = varp[rp[1]]
		EndIf
	End Function
	Function SETTABSI(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "SETTABSI //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(bc.vm)
		Local tabp:Byte Ptr = varp + rp[0]
		Print "  " + Short Ptr(rp)[1]
		Local key:Long ; Double Ptr(Varptr(key))[0] = Short Ptr(rp)[1]
		BlueTable.RawSet(vm.mem, Byte Ptr Ptr(tabp)[0], key, varp[rp[1]])
	End Function
	Function GETTABI(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "GETTABI  //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local tabp:Byte Ptr = varp + rp[1]
		Print "  tag: " + Bin(Int Ptr(varp + rp[2])[1])
		Local val:Long = BlueTable.RawGet(Byte Ptr Ptr(tabp)[0], varp[rp[2]])
		Local v:Double = Double Ptr(Varptr(val))[0]
		Print "  " + v
		varp[rp[0]] = val
	End Function
	Function SETTABI(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "SETTABI  //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(bc.vm)
		Local tabp:Byte Ptr = varp + rp[0]
		Print "  " + Double Ptr(varp)[rp[2]] + " " + Double Ptr(varp)[rp[1]]
		Print "  tag: " + Bin(Int Ptr(varp + rp[2])[1])
		BlueTable.RawSet(vm.mem, Byte Ptr Ptr(tabp)[0], varp[rp[2]], varp[rp[1]])
	End Function
	Function GETUPV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "GETUPV   //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Print "  " + rp[0] + " " + rp[1] + " " + rp[2]
		DebugStop
		Local upvp:Byte Ptr Ptr = Byte Ptr Ptr(Byte Ptr(stk) + STACKFRAME_INC), valp:Long Ptr = Long Ptr(upvp[rp[1]])
		varp[rp[0]] = valp[0]
	End Function
	Function GETENV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)	'"unlisted" instruction: GETUPV for _ENV is translated into this at load-time
		Print "GETENV   //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Print "  " + rp[0] + " " + Hex(Int Ptr(rp + 1)[0])
		'need a type check
		varp[rp[0]] = Long Ptr Ptr(rp + 1)[0][0]
	End Function
	Function SETUPV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function NEWTAB(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "NEWTAB   //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(bc.vm)
		Local tab:Byte Ptr = vm.mem.AllocTable(Null)
		Local d:Int Ptr = Int Ptr(varp + rp[0])
		d[0] = Int(tab) ; d[1] = BlueTypeTag.NANBOX | BlueTypeTag.TBL
	End Function
	Function CLOSURE(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "CLOSURE  //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(bc.vm)
		Local d:Int Ptr = Int Ptr(varp + rp[0])
		Local cbytecode:Bytecode = vm.funIndex[Int Ptr(rp + 1)[0]], upvp:Int Ptr = Int Ptr(Byte Ptr(cbytecode) + BYTECODE_INC + 8 * cbytecode.icount)
		Local closure:Byte Ptr = vm.mem.AllocClosure(cbytecode, cbytecode.upvars)	'upvars is always even (alignment)

		'get upvalues off the stack
		For Local u:Int = 0 Until cbytecode.upvars
			If upvp[2 * u]
				Byte Ptr Ptr(closure)[2 + u] = Byte Ptr Ptr(Byte Ptr(stk) + STACKFRAME_INC)[upvp[2 * u + 1]]
			Else
				Byte Ptr Ptr(closure)[2 + u] = Byte Ptr Ptr(varp + upvp[2 * u + 1])[0]
			EndIf
		Next

		d[0] = Int(closure) ; d[1] = BlueTypeTag.NANBOX | BlueTypeTag.FUN
	End Function
	Function NEWUPV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "NEWUPV   //"
		Local rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(bc.vm)
		Local upv:Byte Ptr = vm.mem.AllocObject(8, BlueTypeTag.UPV), d:Int Ptr = Int Ptr(stk.varp + rp[0])
		Long Ptr(upv)[0] = Long Ptr(d)[0]	'promote a value if it existed (useful for parameters)
		d[0] = Int(upv) ; d[1] = BlueTypeTag.NANBOX | BlueTypeTag.UPV
	End Function

	Function ADD(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		Print "ADD      //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local d:Double Ptr = Double Ptr(varp + rp[0])
		Local r:Int Ptr = Int Ptr(varp + rp[1]), l:Int Ptr = Int Ptr(varp + rp[2])
		If l[1] & BlueTypeTag.NANBOX_CHK = BlueTypeTag.NANBOX Or r[1] & BlueTypeTag.NANBOX_CHK = BlueTypeTag.NANBOX
			DebugStop
		Else
			Print "  " + Double Ptr(l)[0] + " " + Double Ptr(r)[0]
			d[0] = Double Ptr(l)[0] + Double Ptr(r)[0]
		EndIf
	End Function
	Function SUB(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function MUL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function DIV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function NMOD(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function POW(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function CAT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function IDIV(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function BAND(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function BOR(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function BXOR(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function BSHL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function BSHR(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function UNM(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function LNOT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function ALEN(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function BNOT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function UNP(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function EQ(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "EQ       //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = (Byte Ptr Ptr(retptr) - 4)[0] + IP_OFFSET
		Local r:Double = Double Ptr(varp)[rp[1]]
		Local l:Double = Double Ptr(varp)[rp[2]]
		Int Ptr(varp + rp[0])[0] = l = r
	End Function
	Function LT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function LEQ(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function

	Function JMP(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
		RuntimeError "wait, why are we here?"
	End Function
	Function JIF(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "JIF      //"
		Local rp:Byte Ptr = (Byte Ptr Ptr(retptr) - 4)[0] + IP_OFFSET
		If Int Ptr(stk.varp + rp[0])[0]
			Local target:Int = Int Ptr(rp + 1)[0]
			Int Ptr(retptr)[-4] = target
		EndIf
	End Function
	Function JNOT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function CALL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "CALL     //"
		Local varp:Long Ptr = stk.varp, rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET
		Local fp:Int Ptr = Int Ptr(varp + rp[0])
		If Not PrepareCall(fp, stk, rp, varp, retptr)	'PrepareCall sets everything up so there's nothing else to do to make the call happen
			'__call metamethod
		EndIf
	End Function
	Function TCALL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function RET:Byte Ptr(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "RET      //"
		Local varp:Long Ptr = stk.varp
		Local rp0:Byte Ptr = Byte Ptr Ptr(retptr)[-4], rp:Byte Ptr = rp0 + IP_OFFSET

		Local oldStk:Stack = stk.prevBase
		Local retv:Long Ptr = varp + rp[0]
		If oldStk
			oldStk.retv = retv
			oldStk.retc = Int Ptr(rp + 1)[0]
			Byte Ptr Ptr(retptr)[-4] = stk.retIP
			Byte Ptr Ptr(retptr)[-3] = Byte Ptr(oldStk)
			Byte Ptr Ptr(retptr)[-2] = Byte Ptr(oldStk.func) + BYTECODE_INC
		Else	'return to native code
			Local codePage:Byte Ptr = Byte Ptr(Int(rp0) & ((Int(2^12)-1) Shl 20))
			Byte Ptr Ptr(retptr)[-4] = codePage + 4
			Return retv
		EndIf
	End Function
	Function RETVA(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function POSTCALL(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	'	Print "POSTCALL //"
		Local varp:Long Ptr = stk.varp
		Local rp:Byte Ptr = Byte Ptr Ptr(retptr)[-4] + IP_OFFSET

		For Local r:Int = 0 Until Min(Int Ptr(rp + 1)[0], stk.retc)
			(varp + rp[0])[r] = stk.retv[r]
		'	Print "  return " + r + ": " + Double Ptr(stk.retv)[r]
		Next
		'nil the rest
	End Function
	Function VARARG(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function VAINIT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function
	Function CALLINIT(stk:Stack, bc:Bytecode, retptr:Byte Ptr)
	End Function

	Function PrepareCall:Int(fp:Int Ptr, stk:Stack, rp:Byte Ptr, varp:Long Ptr, retptr:Byte Ptr)
		If fp[1] = BlueTypeTag.NANBOX | BlueTypeTag.FUN
			Local newStk0:Stack ; Byte Ptr Ptr(Varptr(newStk0))[0] = Byte Ptr(stk) + stk.func.frameSz
			Local newStk:Stack = newStk0	'micro-opt: newStk0 can't be a register because of the unwieldy conversion

			newStk.retIP = Byte Ptr Ptr(retptr)[-4]
			newStk.prevBase = stk
			Local closure:Byte Ptr = Byte Ptr(fp[0])
			Local newBC:Bytecode = Bytecode Ptr(closure)[0]
			Local voff:Int = STACKFRAME_INC + 4 * newBC.upvars
			newStk.varp = Long Ptr(Byte Ptr(newStk) + voff)
			newStk.func = newBC

			Local argc_actual:Int = Short Ptr(rp)[1], argc_required:Int = newBC.pcount
		'	Local argc_min:Int ; If argc_actual < argc_required Then argc_min = argc_actual Else argc_min = argc_required
			Local argc_min:Int = argc_actual - argc_required ; argc_min = (argc_min & (argc_min Shr 31)) + argc_required 'branchless 32-bit Min

			Local argv:Long Ptr = varp + rp[1], destv:Long Ptr = newStk.varp
			For Local a:Int = 0 Until argc_min
				destv[a] = argv[a]
			'	Print "  arg " + a + ": " + argv[a]
			Next
			For Local a:Int = argc_min Until argc_required	'nil any unfilled parameters
				Int Ptr(destv + a)[1] = BlueTypeTag.NANBOX | BlueTypeTag.NIL
			Next
			newStk.argv = argv + argc_min	'argv should be to the varargs (if any)
			newStk.argc = argc_actual - argc_min	'argc is the number of varargs

			Local destup:Byte Ptr Ptr = Byte Ptr Ptr(Byte Ptr(newStk) + STACKFRAME_INC)
			Local upv:Byte Ptr Ptr = Byte Ptr Ptr(closure + 8)
			For Local up:Int = 0 Until newBC.upvars	'emplace upvars
				destup[up] = upv[up]
			'	Print "  upv " + up + ": " + destv[up]
			'	Print "  val: " + Double Ptr Ptr(Byte Ptr Ptr(closure) + 2 + up)[0][0]
			Next

			'note that the following OVERWRITE THE PARAMETERS (in release mode), so no touching stk from here on
			Byte Ptr Ptr(retptr)[-4] = newBC.mcode
			Byte Ptr Ptr(retptr)[-3] = Byte Ptr(newStk)
			Byte Ptr Ptr(retptr)[-2] = Byte Ptr(newBC) + BYTECODE_INC

		ElseIf fp[1] = BlueTypeTag.NANBOX | BlueTypeTag.NATFUN	'native call
			Local fun:Int(vm:BlueVM, ac:Int, av:Long Ptr, rv:Long Ptr) = Byte Ptr(fp[0])
			Local argc:Int = Short Ptr(rp)[1], argv:Long Ptr = varp + rp[1], retv:Long Ptr = Long Ptr(Byte Ptr(stk) + stk.func.frameSz)
			Local convert:BlueVM(p:Byte Ptr) = Byte Ptr(Identity), vm:BlueVM = convert(stk.func.vm)
			stk.retc = fun(vm, argc, argv, retv)
			stk.retv = retv
		Else
			Return False	'not a function; take appropriate action
		EndIf
		Return True	'all good and will auto-call when the caller returns
	End Function

	Function InitOpTbl()
		opc = New BlueOpcode
		opTbl = [MOV, GETLC, SETLC, LOADK, LOADSI, LOADBOOL, LOADNIL, ..
			GETTAB, GETTABSI, SETTAB, SETTABSI, GETTABI, SETTABI, GETUPV, SETUPV, ..
			NEWTAB, CLOSURE, NEWUPV, ..
			ADD, SUB, MUL, DIV, NMOD, POW, CAT, ..
			IDIV, BAND, BOR, BXOR, BSHL, BSHR, ..
			UNM, LNOT, ALEN, BNOT, UNP, ..
			EQ, LT, LEQ, ..
			JMP, JIF, JNOT, CALL, TCALL, RETVA, RETVA, ..	'not a mistake: RET doesn't fit but we need the spacer
			POSTCALL, VARARG, VAINIT, CALLINIT]

		opTbl[opc.RET] = Byte Ptr(RET)
	End Function

	Function Identity:Byte Ptr(b:Byte Ptr)
		Return b
	End Function
	Function PointerToExtType:Byte Ptr(p:Byte Ptr)	'horrible pointer abuse (increment down so we can ignore the nonexistent vtbl)
		Return p - SizeOf(Byte Ptr(0))
	End Function
	Global BPtoS:Stack(p:Byte Ptr) = Byte Ptr(BlueJIT.PointerToExtType)
End Type