Introduction
This is an Assembly version of the original C++ algorithm which can be found here.
Background
Please refer to the original C++ algorithm here.
Prepare
Object Oriented with Struct
Although the Assembly language has no OO feature, we can use its STRUCT
to simulate the simple class behavior and use invoke mechanism to make a library based architecture.
Debug in Visual Studio
There is a way to develop MASM Assembly in Visual Studio, you can find how to do this in "VisualStudio ASM Setup" folder or reference my article A 2D List Implementation in MASM Assembly, this makes Assembly debug much easier. Here is a screenshot example, the ESI value 0x00392BB8
is the list pointer value, if you check memory address 0x00392bd8
, which is the value at above pointer address, then you will find the list struct data in details with further tracing.
C Functions Used
The C calling convention model for all projects.
.model flat, c
This takes advantage of using all C functions, i.e., fopen
, realloc
, printf
, etc., for File IO, memory allocation, and console output.
To utilize these functions, compile with additional include path and link with msvcrt.lib, check vcDevEnv.bat and ccm.bat for details.
The C Runtime (crt
) Library msvcrt.lib is at "C:\Program Files\Microsoft Visual Studio 10.0\VC\lib\msvcrt.lib", you can dump its modules to check if the C functions you are trying to use are included in this library.
For these functions prototypes, you can find it from online MSDN, or check the files in include path at "C:\Program Files\Microsoft Visual Studio 10.0\VC\include". You even can dig in its source code at "C:\Program Files\Microsoft Visual Studio 10.0\VC\crt\src".
Most of C functions return value in EAX
with different meaning, but it might have other registers that are modified after invoking function, i.e., printf modify ECX
after its inovking, ECX
is sensitive register for LOOP. To find out these details, the easiest way is to debug register values changes before and after calling. There are not too many online resources as expected for these details.
List Implementation
The essential part in main function GeoPolygonProc.asm is using a List
data structure.
The Assembly language has no built-in List
, an array has to be declared with a pre-known fixed size before its referencing, a bigger than actual requirement array is usually declared with an evaluation of its referencing situation, but in some circumstances, we even don't know how big it should be.
For this GeoProc
project, particular in GeoPolygonProc.asm, we need a list to hold all face planes, but we cannot assume how many faces we could find. For each face vertices, we also don't know how many vertice points it could include, so a 2D list is also required to store every face vertices points, the major dimension is face index and minor index is vertice index, this face vertice index is the original order in the polygon vertices.
Once again, here is my article A 2D List Implementation in MASM Assembly to implement basic list and 2D list for DWORD
and REAL8
data type.
For this project, we need three more data types to implement their list data structure, one is GeoPoint
list, another is GeoPlane
list, the third one is GeoFace
list. All push
methods for the three lists, PushPoint
, PushPlane
and PushFace
have been implemented although the LASGeoProc
project does't require GeoFace
list because the method PointInside3DPolygon
only uses GeoPlane
list.
Constants
Here are samples of constants used in the projects:
...
PFILE TYPEDEF PTR FILE
...
PDWORD TYPEDEF PTR DWORD
...
PListPlane TYPEDEF PTR ListPlane
...
Macros
Here is a sample:
CmpReal MACRO r1:REQ, r2:REQ
fld r1
fcomp r2
fnstsw ax
sahf
endm
List Library
Here is ListI struct
and its two methods, for all other List
types, please check the attached zip package.
ListI
ListI STRUCT
p PDWORD ?
n DWORD 0
ListI ENDS
Here is ListI
structure diagram:
PushI
Push a DWORD
data into a ListI
variable.
PushI proc uses ebx esi ecx,
list : PTR ListI,
element : DWORD
local endPos : DWORD
local count : DWORD
mov esi, list
cmp (ListI PTR [esi]).p, 0
JNE NOT_NULL
invoke calloc, 0, TYPE DWORD
mov (ListI PTR [esi]).p, eax
mov (ListI PTR [esi]).n, 0
NOT_NULL:
mov esi, list
mov ebx, (ListI PTR [esi]).n
mov count, ebx
imul ebx, TYPE DWORD
mov endPos, ebx
add ebx, TYPE DWORD
invoke realloc, (ListI PTR [esi]).p, ebx
mov (ListI PTR [esi]).p, eax
mov esi, eax
add esi, endPos
mov ebx, element
mov [esi], ebx
mov esi, list
mov ebx, count
inc ebx
mov (ListI PTR [esi]).n, ebx
ret
PushI endp
Push2DI
Push a ListI
variable to a List2DI
variable.
Push2DI proc uses ebx esi ecx,
list2d: PTR List2DI,
element : PTR ListI
local elNumber, elBytes, elAddr, elEndAddr : DWORD
mov esi, element
mov ebx, (ListI PTR [esi]).n
mov elNumber, ebx
imul ebx, TYPE DWORD
mov elBytes, ebx
mov ebx, [esi]
mov elAddr, ebx
mov ebx, elAddr
add ebx, elBytes
mov elEndAddr, ebx
mov esi, list2d
cmp (List2DI PTR [esi]).dp, 0
JNE NOT_NULL
invoke CopyFirstI, list2d, elAddr, elBytes, elNumber
JMP BLANK
NOT_NULL:
invoke CopyDPI,list2d, elAddr, elBytes
invoke CopyNPI, list2d, elNumber
mov esi, list2d
inc (List2DI PTR [esi]).n
BLANK:
ret
Push2DI endp
ContainsListI
This is the key function to work in SetFacePlanes
function within GeoPolygonProc.asm, which utilizes List2DI
struct
and ListI struct
. This function is to check if a new found face is a new face, in that case, the new found face is faceVerticeIndexInOneFace
, which is a ListI
variable, the faceVerticeIndex
is a List2DI
variable.
ContainsListI proc uses ebx esi,
list2d : PTR List2DI,
list : PTR ListI
local isContains, rows, i : DWORD
mov isContains, FALSE
mov esi, list2d
mov ebx, (List2DI PTR [esi]).n
cmp ebx, 0
JE DONE_FALSE
mov rows, ebx
invoke CopyListI, list, ADDR listCopyI
invoke BubbleSortListI, ADDR listCopyI
mov i, 0
mov ecx,rows
L1:
invoke GetListIElement, list2d, i, ADDR tempListI
invoke BubbleSortListI, ADDR tempListI
invoke CompareListI, ADDR tempListI, ADDR listCopyI
cmp eax, TRUE
JE DONE_TRUE
inc i
loop L1
JMP DONE_FALSE
DONE_TRUE:
mov isContains, TRUE
DONE_FALSE:
mov eax, isContains
ret
ContainsListI endp
List GeoProc Library
Here are GeoPoint
List
and List GeoPlane List struct
s.
ListPoint
ListPoint STRUCT
p PGeoPoint ?
n DWORD 0
ListPoint ENDS
ListPlane
ListPlane STRUCT
p PGeoPlane ?
n DWORD 0
ListPlane ENDS
PushPoint
PushPoint proc uses ebx esi,
list : PTR ListPoint,
element : GeoPoint
local endPos, newSize : DWORD
local count : DWORD
mov esi, list
cmp (ListPoint PTR [esi]).n, 0
JNE NOT_NULL
invoke calloc, 1, TYPE GeoPoint
mov (ListPoint PTR [esi]).p, eax
mov (ListPoint PTR [esi]).n, 0
NOT_NULL:
mov esi, list
mov ebx, (ListPoint PTR [esi]).n
cmp ebx, 0
JNE NOT_BLANK
mov esi, list
mov ebx, (ListPoint PTR [esi]).p
invoke memmove, ebx, ADDR element, TYPE GeoPoint
mov ebx, 1
mov (ListPoint PTR [esi]).n, ebx
JMP DONE
NOT_BLANK:
mov esi, list
mov ebx, (ListPoint PTR [esi]).n
mov count, ebx
imul ebx, TYPE GeoPoint
mov endPos, ebx
add ebx, TYPE GeoPoint
mov newSize, ebx
mov esi, list
mov ebx, (ListPoint PTR [esi]).p
invoke realloc, ebx, newSize
mov (ListPoint PTR [esi]).p, eax
mov ebx, (ListPoint PTR [esi]).p
add ebx, endPos
invoke memmove, ebx, ADDR element, TYPE GeoPoint
mov esi, list
mov ebx, count
inc ebx
mov (ListPoint PTR [esi]).n, ebx
DONE:
ret
PushPoint endp
PushPlane
PushPlane proc uses ebx esi,
list : PTR ListPlane,
element : GeoPlane
local endPos, newSize : DWORD
local count : DWORD
mov esi, list
cmp (ListPlane PTR [esi]).n, 0
JNE NOT_NULL
invoke calloc, 1, TYPE GeoPlane
mov (ListPlane PTR [esi]).p, eax
mov (ListPlane PTR [esi]).n, 0
NOT_NULL:
mov esi, list
mov ebx, (ListPlane PTR [esi]).n
cmp ebx, 0
JNE NOT_BLANK
mov esi, list
mov ebx, (ListPlane PTR [esi]).p
invoke memmove, ebx, ADDR element, TYPE GeoPlane
mov ebx, 1
mov (ListPlane PTR [esi]).n, ebx
JMP DONE
NOT_BLANK:
mov esi, list
mov ebx, (ListPlane PTR [esi]).n
mov count, ebx
imul ebx, TYPE GeoPlane
mov endPos, ebx
add ebx, TYPE GeoPlane
mov newSize, ebx
mov esi, list
mov ebx, (ListPlane PTR [esi]).p
invoke realloc, ebx, newSize
mov (ListPlane PTR [esi]).p, eax
mov ebx, (ListPlane PTR [esi]).p
add ebx, endPos
invoke memmove, ebx, ADDR element, TYPE GeoPlane
mov esi, list
mov ebx, count
inc ebx
mov (ListPlane PTR [esi]).n, ebx
DONE:
ret
PushPlane endp
PushFace
PushFace proc uses ebx edx eax esi,
list : PTR ListFace,
element : PTR GeoFace
local numberOfPoints : DWORD
local ptsAddr, idxAddr : DWORD
local ptsSize, idxSize: DWORD
local newGeoFaceAddr : DWORD
mov esi, list
cmp (ListFace PTR [esi]).n, 0
JNE NOT_NULL
invoke calloc, 1, TYPE ListFace
mov (ListFace PTR [esi]).p, eax
mov (ListFace PTR [esi]).n, 0
invoke calloc, 1, TYPE GeoFace
mov esi, list
mov (ListFace PTR [esi]).p, eax
NOT_NULL:
mov esi, element
mov ebx, (GeoFace PTR [esi]).ptsAddr
mov ptsAddr, ebx
mov esi, element
mov ebx, (GeoFace PTR [esi]).idxAddr
mov idxAddr, ebx
mov esi, element
mov ebx, (GeoFace PTR [esi]).n
mov numberOfPoints, ebx
mov ebx, numberOfPoints
imul ebx, TYPE GeoPoint
mov ptsSize, ebx
mov ebx, numberOfPoints
imul ebx, TYPE DWORD
mov idxSize, ebx
mov esi, list
mov ebx, (ListFace PTR [esi]).n
cmp ebx, 0
JNE NOT_BLANK
invoke calloc, numberOfPoints, TYPE GeoPoint
mov esi, list
mov esi, (ListFace PTR [esi]).p
mov (GeoFace PTR [esi]).ptsAddr, eax
invoke memmove, (GeoFace PTR [esi]).ptsAddr, ptsAddr, ptsSize
invoke calloc, numberOfPoints, TYPE DWORD
mov esi, list
mov esi, (ListFace PTR [esi]).p
mov (GeoFace PTR [esi]).idxAddr, eax
invoke memmove, (GeoFace PTR [esi]).idxAddr, idxAddr, idxSize
mov esi, list
mov (ListFace PTR [esi]).n, 1
mov esi, list
mov esi, (ListFace PTR [esi]).p
mov ebx, numberOfPoints
mov (GeoFace PTR [esi]).n, ebx
JMP DONE
NOT_BLANK:
mov esi, list
inc (ListFace PTR [esi]).n
mov esi, list
mov ebx, (ListFace PTR [esi]).p
mov edx, (ListFace PTR [esi]).n
imul edx, TYPE GeoFace
invoke realloc, ebx, edx
mov esi, list
mov (ListFace PTR [esi]).p, eax
mov esi, list
mov ebx, (ListFace PTR [esi]).n
dec ebx
imul ebx, TYPE GeoFace
add ebx, (ListFace PTR [esi]).p
mov newGeoFaceAddr, ebx
invoke calloc, 1, TYPE DWORD
mov esi, newGeoFaceAddr
mov (GeoFace PTR [esi]).ptsAddr, eax
invoke calloc, 1, TYPE DWORD
mov esi, newGeoFaceAddr
mov (GeoFace PTR [esi]).idxAddr, eax
mov esi, newGeoFaceAddr
mov ebx, numberOfPoints
mov (GeoFace PTR [esi]).n, ebx
mov esi, newGeoFaceAddr
invoke malloc, ptsSize
mov (GeoFace PTR [esi]).ptsAddr, eax
invoke memmove, (GeoFace PTR [esi]).ptsAddr, ptsAddr, ptsSize
mov esi, newGeoFaceAddr
invoke malloc, idxSize
mov (GeoFace PTR [esi]).idxAddr, eax
invoke memmove, (GeoFace PTR [esi]).idxAddr, idxAddr, idxSize
DONE:
ret
PushFace endp
GeoProc Libary
GeoPoint
GeoPoint STRUCT
x REAL8 0.0
y REAL8 0.0
z REAL8 0.0
GeoPoint ENDS
Here is the main part of GeoPoint.asm:
NewGeoPoint proc,
x: REAL8,
y: REAL8,
z: REAL8
fld x
fstp newPoint.x
fld y
fstp newPoint.y
fld z
fstp newPoint.z
mov esi, offset newPoint
ret
NewGeoPoint endp
AddGeoPoint proc,
pt1: GeoPoint,
pt2: GeoPoint
AddReal pt1.x, pt2.x, addPoint.x
AddReal pt1.y, pt2.y, addPoint.y
AddReal pt1.z, pt2.z, addPoint.z
mov esi, offset addPoint
ret
AddGeoPoint endp
GeoVector
GeoVector STRUCT
p0 GeoPoint <?>
p1 GeoPoint <?>
x real8 0.0
y real8 0.0
z real8 0.0
GeoVector ENDS
Here is the main part of GeoVector.asm:
NewGeoVector proc,
p0: GeoPoint,
p1: GeoPoint
MovGeoPointM p0, newVector.p0
MovGeoPointM p1, newVector.p1
SubReal p1.x, p0.x, newVector.x
SubReal p1.y, p0.y, newVector.y
SubReal p1.z, p0.z, newVector.z
mov esi, offset newVector
ret
NewGeoVector endp
MulGeoVector proc,
u: GeoVector,
v: GeoVector
local r1, r2 : real8
local pt : GeoPoint
MulReal u.y, v.z, r1
MulReal u.z, v.y, r2
SubReal r1, r2, mulVector.x
MulReal u.z, v.x, r1
MulReal u.x, v.z, r2
SubReal r1, r2, mulVector.y
MulReal u.x, v.y, r1
MulReal u.y, v.x, r2
SubReal r1, r2, mulVector.z
MovGeoPointM u.p0, mulVector.p0
MovGeoPointM mulVector, pt
invoke AddGeoPoint, u.p0, pt
GetGeoPoint mulVector.p1
mov esi, offset mulVector
ret
MulGeoVector endp
GeoPlane
GeoPlane STRUCT
a REAL8 0.0
b REAL8 0.0
cc REAL8 0.0
d REAL8 0.0
GeoPlane ENDS
Here is the main part of GeoPlane.asm:
NewGeoPlaneB proc,
p0: GeoPoint,
p1: GeoPoint,
p2: GeoPoint
local r1, r2, r3 : REAL8
local u : GeoVector
local v : GeoVector
local n : GeoVector
invoke NewGeoVector, p0, p1
GetGeoVector u
invoke NewGeoVector, p0, p2
GetGeoVector v
invoke MulGeoVector, u, v
GetGeoVector n
fld n.z
fld n.y
fld n.x
fstp newPlaneB.a
fstp newPlaneB.b
fstp newPlaneB.cc
MulReal newPlaneB.a, p0.x, r1
MulReal newPlaneB.b, p0.y, r2
MulReal newPlaneB.cc, p0.z, r3
fld r1
fadd r2
fadd r3
fmul minusOne
fstp newPlaneB.d
mov esi, offset newPlaneB
ret
NewGeoPlaneB endp
NegGeoPlane proc,
pl: GeoPlane
MulReal pl.a, minusOne, negPlane.a
MulReal pl.b, minusOne, negPlane.b
MulReal pl.cc, minusOne, negPlane.cc
MulReal pl.d, minusOne, negPlane.d
mov esi, offset negPlane
ret
NegGeoPlane endp
MulGeoPlane proc,
pl: GeoPlane,
pt: GeoPoint
local r1, r2, r3 : REAL8
MulReal pt.x, pl.a, r1
MulReal pt.y, pl.b, r2
MulReal pt.z, pl.cc, r3
fld r1
fadd r2
fadd r3
fadd pl.d
fstp mulPlaneVal
mov eax, offset mulPlaneVal
ret
MulGeoPlane endp
GeoPolygon
GeoPolygon STRUCT
ptsAddr PGeoPoint ?
n DWORD 0
GeoPolygon ENDS
Here is the main part of GeoPolygon.asm:
NewGeoPolygon proc uses esi ebx,
ptsAddr: PTR GeoPoint,
ptsCount: DWORD,
local oriAddr, destAddr, byteCount : DWORD
mov esi, offset newPolygon
mov ebx, [esi]
mov oriAddr, ebx
invoke calloc, ptsCount, TYPE GeoPoint
mov destAddr, eax
mov esi, offset newPolygon
mov [esi], eax
mov ebx, ptsCount
imul ebx, TYPE GeoPoint
mov byteCount, ebx
invoke memmove, destAddr, ptsAddr, byteCount
mov ebx, ptsCount
mov (GeoPolygon PTR newPolygon).n, ebx
mov ebx, oriAddr
cmp ebx, destAddr
JE SKIP_FREE
mov ebx, oriAddr
cmp ebx, 0
JE SKIP_FREE
invoke free, oriAddr
SKIP_FREE:
mov eax, offset newPolygon
ret
NewGeoPolygon endp
GeoFace
GeoFace STRUCT
ptsAddr PGeoPoint ?
idxAddr PDWORD ?
n DWORD 0
GeoFace ENDS
Here is the main part of GeoFace.asm:
NewGeoFace proc uses edi esi ebx,
ptsAddr: PGeoPoint,
idxAddr: PDWORD,
ptsCount: DWORD
local oriAddr, destPtsAddr, destIdxAddr, ptsByteCount, idxByteCount : DWORD
mov esi, offset newFace
mov ebx, [esi]
mov oriAddr, ebx
invoke calloc, ptsCount, TYPE GeoPoint
mov destPtsAddr, eax
mov esi, offset newFace
mov [esi], eax
invoke calloc, ptsCount, TYPE DWORD
test eax, eax
jz CALLOC_FAIL_IDX
mov destIdxAddr, eax
mov esi, offset newFace
add esi, TYPE DWORD
mov [esi], eax
mov ebx, ptsCount
imul ebx, TYPE GeoPoint
mov ptsByteCount, ebx
mov ebx, ptsCount
imul ebx, TYPE DWORD
mov idxByteCount, ebx
invoke memmove, destPtsAddr, ptsAddr, ptsByteCount
invoke memmove, destIdxAddr, idxAddr, idxByteCount
mov ebx, ptsCount
mov (GeoFace PTR newFace).n, ebx
mov ebx, oriAddr
cmp ebx, destPtsAddr
JE SKIP_FREE
mov ebx, oriAddr
cmp ebx, 0
JE SKIP_FREE
invoke free, oriAddr
SKIP_FREE:
mov eax, offset newFace
JMP DONE
CALLOC_FAIL_IDX:
mov eax, 0
DONE:
ret
NewGeoFace endp
GeoPolygonProc
This is the essential part in the whole project, so the complete code will be shown below. You can find all referencing functions in the attached zip package.
Here is the complete code of GeoPolygonProc.asm:
.686p
.model flat, c
include GeoPolygonProc.inc
.data
newPolygonProc GeoPolygonProc <?>
pPolygon PGeoPolygon ?
tempListR1 ListR <?>
tempListR2 ListR <?>
tempListR3 ListR <?>
MaxUnitMeasureError REAL8 0.001
tempCR1 REAL8 6.0
tempR1 REAL8 0.0
faceVerticeIndex List2DI <?>
pointInSamePlaneIndex ListI <?>
faceVerticeIndexInOneFace ListI <?>
faceIdxListI ListI <?>
facePtsListPoint ListPoint <?>
.code
InitGeoPolygonProc proc uses esi,
invoke calloc, 1, TYPE DWORD
mov esi, offset newPolygonProc
mov (GeoPolygonProc PTR [esi]).facePlanes, eax
invoke calloc, 1, TYPE DWORD
mov esi, offset newPolygonProc
mov esi, (GeoPolygonProc PTR [esi]).facePlanes
mov (ListPlane PTR [esi]).p, eax
mov (ListPlane PTR [esi]).n, 0
invoke calloc, 1, TYPE DWORD
mov esi, offset newPolygonProc
mov (GeoPolygonProc PTR [esi]).faces, eax
invoke calloc, 1, TYPE DWORD
mov esi, offset newPolygonProc
mov esi, (GeoPolygonProc PTR [esi]).faces
mov (ListFace PTR [esi]).p, eax
mov (ListFace PTR [esi]).n, 0
ret
InitGeoPolygonProc endp
NewGeoPolygonProc proc uses esi ebx,
pPolygonIn: PGeoPolygon
call InitGeoPolygonProc
mov ebx, pPolygonIn
mov pPolygon, ebx
call SetBoundary
call SetMinError
call SetFacePlanes
mov eax, offset newPolygonProc
ret
NewGeoPolygonProc endp
SetBoundary proc uses ebx esi,
local n : DWORD
mov esi, pPolygon
mov ebx, (GeoPolygon PTR [esi]).n
mov n, ebx
mov esi, (GeoPolygon PTR [esi]).ptsAddr
mov ecx, n
L1:
INVOKE PushR, ADDR tempListR1, (GeoPoint PTR [esi]).x
INVOKE PushR, ADDR tempListR2, (GeoPoint PTR [esi]).y
INVOKE PushR, ADDR tempListR3, (GeoPoint PTR [esi]).z
add esi, TYPE GeoPoint
LOOP L1
INVOKE BubbleSortListR, ADDR tempListR1
INVOKE BubbleSortListR, ADDR tempListR2
INVOKE BubbleSortListR, ADDR tempListR3
mov esi, offset newPolygonProc
LoadListRFirstLast tempListR1, n
fstp (GeoPolygonProc PTR [esi]).x1
fstp (GeoPolygonProc PTR [esi]).x0
LoadListRFirstLast tempListR2, n
fstp (GeoPolygonProc PTR [esi]).y1
fstp (GeoPolygonProc PTR [esi]).y0
LoadListRFirstLast tempListR3, n
fstp (GeoPolygonProc PTR [esi]).z1
fstp (GeoPolygonProc PTR [esi]).z0
ret
SetBoundary endp
SetMinError proc,
mov esi, offset newPolygonProc
fld (GeoPolygonProc PTR [esi]).x0
fabs
fstp tempR1
AddAbsR (GeoPolygonProc PTR [esi]).x1, tempR1
AddAbsR (GeoPolygonProc PTR [esi]).y0, tempR1
AddAbsR (GeoPolygonProc PTR [esi]).y1, tempR1
AddAbsR (GeoPolygonProc PTR [esi]).z0, tempR1
AddAbsR (GeoPolygonProc PTR [esi]).z1, tempR1
fld tempR1
fmul MaxUnitMeasureError
fdiv tempCR1
mov esi, offset newPolygonProc
fstp (GeoPolygonProc PTR [esi]).minError
ret
SetMinError endp
SetFacePlanes proc uses ebx edx eax esi,
local numberOfVertices : DWORD
local verticesAddr : DWORD
local i, j, k, l, p, m, inLeftCount, inRightCount : DWORD
local numberOfFaces : DWORD
local p0 : GeoPoint
local p1 : GeoPoint
local p2 : GeoPoint
local pt : GeoPoint
local trianglePlane : GeoPlane
local negTrianglePlane : GeoPlane
local dis : REAL8
local absDis : REAL8
local minError : REAL8
local tempi, count, idx : DWORD
local pFace : PGeoFace
mov numberOfFaces, 0
mov esi, offset newPolygonProc
fld (GeoPolygonProc PTR [esi]).minError
fstp minError
mov esi, pPolygon
mov ebx, (GeoPolygon PTR [esi]).n
mov numberOfVertices, ebx
mov ebx, (GeoPolygon PTR [esi]).ptsAddr
mov verticesAddr, ebx
mov i, 0
mov edx, numberOfVertices
L1:
mov edx, numberOfVertices
cmp i, edx
JAE L1_DONE
GetGeoPointAtIndex verticesAddr, i, p0
mov ebx, i
inc ebx
mov j, ebx
L2:
mov edx, numberOfVertices
cmp j, edx
JAE L2_DONE
GetGeoPointAtIndex verticesAddr, j, p1
mov ebx, j
inc ebx
mov k, ebx
L3:
mov edx, numberOfVertices
cmp k, edx
JAE L3_DONE
GetGeoPointAtIndex verticesAddr, k, p2
INVOKE NewGeoPlaneB, p0, p1, p2
GetGeoPlane trianglePlane
INVOKE InitListI, ADDR pointInSamePlaneIndex
mov inLeftCount, 0
mov inRightCount, 0
mov m, 0
mov l, 0
L4:
mov edx, numberOfVertices
cmp l, edx
JAE L4_DONE
mov ebx, i
cmp l, ebx
JE NEXT_L4
mov ebx, j
cmp l, ebx
JE NEXT_L4
mov ebx, k
cmp l, ebx
JE NEXT_L4
GetGeoPointAtIndex verticesAddr, l, pt
INVOKE MulGeoPlane, trianglePlane, pt
mov esi, eax
fld REAL8 PTR[esi]
fstp dis
fld dis
fabs
fstp absDis
fld absDis
fcomp minError
fnstsw ax
sahf
jnb SET_LEFT_RIGHT_COUNT
INVOKE PUSHI, ADDR pointInSamePlaneIndex, l
JMP NEXT_L4
SET_LEFT_RIGHT_COUNT:
fldz
fcomp dis
fnstsw ax
sahf
jb ADD_RIGHT_COUNT
inc inLeftCount
JMP NEXT_L4
ADD_RIGHT_COUNT:
inc inRightCount
NEXT_L4:
inc l
JMP L4
L4_DONE:
mov ebx, inLeftCount
cmp ebx, 0
JE FACE_FOUND
mov ebx, inRightCount
cmp ebx, 0
JNE NEXT_L3
FACE_FOUND:
INVOKE InitListI, ADDR faceVerticeIndexInOneFace
INVOKE PUSHI, ADDR faceVerticeIndexInOneFace, i
INVOKE PUSHI, ADDR faceVerticeIndexInOneFace, j
INVOKE PUSHI, ADDR faceVerticeIndexInOneFace, k
mov ebx, pointInSamePlaneIndex.n
mov m, ebx
mov p, 0
L5:
mov ebx, m
cmp p, ebx
JAE L5_DONE
GetListIVal pointInSamePlaneIndex, p, tempi
INVOKE PUSHI, ADDR faceVerticeIndexInOneFace, tempi
inc p
JMP L5
L5_DONE:
INVOKE ContainsListI, ADDR faceVerticeIndex, ADDR faceVerticeIndexInOneFace
cmp eax, TRUE
JE NEXT_L3
inc numberOfFaces
INVOKE Push2DI, ADDR faceVerticeIndex, ADDR faceVerticeIndexInOneFace
mov ebx, inRightCount
cmp ebx, 0
JNE CHECK_LEFT
mov esi, offset newPolygonProc
INVOKE PushPlane, (GeoPolygonProc PTR [esi]).facePlanes, trianglePlane
JMP NEXT_L3
CHECK_LEFT:
INVOKE NegGeoPlane, trianglePlane
GetGeoPlane negTrianglePlane
mov esi, offset newPolygonProc
INVOKE PushPlane, (GeoPolygonProc PTR [esi]).facePlanes, negTrianglePlane
NEXT_L3:
inc k
JMP L3
L3_DONE:
inc j
JMP L2
L2_DONE:
inc i
JMP L1
L1_DONE:
mov ebx, numberOfFaces
mov esi, offset newPolygonProc
mov (GeoPolygonProc PTR [esi]).numberOfFaces, ebx
mov i, 0
LFACE:
mov edx, numberOfFaces
cmp i, edx
JAE LFACE_DONE
INVOKE GetListIElement, ADDR faceVerticeIndex, i, ADDR faceIdxListI
mov esi, offset faceIdxListI
mov ebx, (ListI PTR [esi]).n
mov count, ebx
invoke InitListPoint, Addr facePtsListPoint
mov j, 0
L6:
mov edx, count
cmp j, edx
JAE L6_DONE
GetListIVal faceIdxListI, j, idx
GetGeoPointAtIndex verticesAddr, idx, pt
INVOKE PushPoint, ADDR facePtsListPoint, pt
inc j
JMP L6
L6_DONE:
INVOKE NewGeoFace, (ListPoint PTR [facePtsListPoint]).p, (ListI PTR [faceIdxListI]).p, count
mov pFace, eax
mov esi, offset newPolygonProc
INVOKE PushFace, (GeoPolygonProc PTR [esi]).faces, pFace
inc i
JMP LFACE
LFACE_DONE:
ret
SetFacePlanes endp
PointInside3DPolygon proc uses ebx edx esi,
x: REAL8,
y: REAL8,
z: REAL8
local i, isInside : DWORD
local pt : GeoPoint
local facePlaneAddr : DWORD
local dis : REAL8
mov ebx, TRUE
mov isInside, ebx
INVOKE NewGeoPoint, x, y, z
GetGeoPoint pt
mov esi, offset newPolygonProc
mov esi, (GeoPolygonProc PTR [esi]).facePlanes
mov ebx, (ListPlane PTR [esi]).p
mov facePlaneAddr, ebx
mov i, 0
mov esi, offset newPolygonProc
mov edx, (GeoPolygonProc PTR [esi]).numberOfFaces
L1:
cmp i, edx
JAE L1_DONE
mov esi, facePlaneAddr
INVOKE MulGeoPlane, (GeoPlane PTR [esi]), pt
mov esi, eax
fld REAL8 PTR[esi]
fstp dis
fldz
fcomp dis
fnstsw ax
sahf
jb RETURN_FALSE
add facePlaneAddr, TYPE GeoPlane
inc i
JMP L1
L1_DONE:
JMP RETURN_TRUE
RETURN_FALSE:
mov isInside, FALSE
RETURN_TRUE:
mov eax, isInside
ret
PointInside3DPolygon endp
end
Test and API Usage
The test program is GeoProcTest.asm, it tests both List
library and GeoProc
library.
Console Output
Here is the convenient macro to wrap C function printf
for console output, since printf
will modify EAX
and ECX
, they are protected in stack in the macro, directly using printf
only could be an issue when the printf
is in a loop with ECX
as a loop count or anywhere an assumption for invariant ECX
is required.
MyPrintf MACRO formatString:REQ, printList:VARARG
push eax
push ecx
INVOKE printf, formatString, printList
pop ecx
pop eax
EndM
Code for List Library Test
Please refer to my article A 2D List Implementation in MASM Assembly, the methods PushPoint
and PushPlane
in ListGeoProc.asm do not have test program related, they are tested with debug in Visual Studio and approved with their references correctly functioning in GeoPolygonProc.asm.
Code for GeoProc Library Test
Here is main test code, which is also a GeoProc
library usage template:
main PROC
call ListTest
call GeoPointTest
call GeoVectorTest
call GeoPlaneTest
call GeoPolygonTest
call GeoFaceTest
call GeoPolygonProcTest
ret
main ENDP
GeoPointTest proc
local pt1 : GeoPoint
local pt2 : GeoPoint
local pt3 : GeoPoint
invoke NewGeoPoint, p1.x, p1.y, p1.z
GetGeoPoint pt1
invoke NewGeoPoint, p2.x, p2.y, p2.z
GetGeoPoint pt2
invoke AddGeoPoint, pt1, pt2
GetGeoPoint pt3
MyPrintf offset FmtStr, ADDR strGeoPointTitle
MyPrintf offset FmtFFF, pt1.x, pt1.y, pt1.z
MyPrintf offset FmtFFF, pt2.x, pt2.y, pt2.z
MyPrintf offset FmtFFF, pt3.x, pt3.y, pt3.z
ret
GeoPointTest endp
GeoVectorTest proc,
local pt0 : GeoPoint
local pt1 : GeoPoint
local pt2 : GeoPoint
local u : GeoVector
local v : GeoVector
local n : GeoVector
mov esi, offset vtsArr
GetGeoPoint pt0
GetGeoPoint pt1
GetGeoPoint pt2
invoke NewGeoVector, pt0, pt2
GetGeoVector u
invoke NewGeoVector, pt0, pt1
GetGeoVector v
invoke MulGeoVector, u, v
GetGeoVector n
MyPrintf offset FmtStr, ADDR strGeoVectorTitle
MyPrintf offset FmtFFF, u.x, u.y, u.z
MyPrintf offset FmtFFF, v.x, v.y, v.z
MyPrintf offset FmtFFF, n.x, n.y, n.z
ret
GeoVectorTest endp
GeoPlaneTest proc,
local plA : GeoPlane
local plB : Geoplane
local negpl : GeoPlane
local dis : REAL8
invoke NewGeoPlaneB, p1, p2, p3
GetGeoPlane plB
invoke NewGeoPlaneA, pl.a, pl.b, pl.cc, pl.d
GetGeoPlane plA
invoke NegGeoPlane, plA
GetGeoPlane negpl
invoke MulGeoPlane, pl, pt
mov esi, eax
fld REAL8 PTR[esi]
fstp dis
MyPrintf offset FmtStr, ADDR strGeoPlaneTitle
MyPrintf offset FmtFFFF, plB.a, plB.b, plB.cc, plB.d
MyPrintf offset FmtFFFF, plA.a, plA.b, plA.cc, plA.d
MyPrintf offset FmtFFFF, negpl.a, negpl.b, negpl.cc, negpl.d
MyPrintf offset FmtF, dis
ret
GeoPlaneTest endp
GeoPolygonTest proc uses edi ecx eax ebx,
local count : DWORD
mov eax, LENGTHOF vtsArr
mov count, eax
MyPrintf offset FmtStr, ADDR strGeoPolygonTitle
invoke NewGeoPolygon, ADDR vtsArr, count
mov pPolygon, eax
mov esi, pPolygon
mov esi, (GeoPolygon PTR [esi]).ptsAddr
mov i, 1
mov edi, 0
mov ecx, count
L3:
MyPrintf offset FmtIFFF, i, (GeoPoint PTR [esi]).x, (GeoPoint PTR [esi]).y, (GeoPoint PTR [esi]).z
add esi, TYPE GeoPoint
inc i
loop L3
mov esi, pPolygon
MyPrintf offset FmtInt, (GeoPolygon PTR [esi]).n
ret
GeoPolygonTest endp
GeoFaceTest proc uses edi ecx eax edx ebx,
local count, ptsAddr, idxAddr, idx : DWORD
mov eax, LENGTHOF faceIdxArr
mov count, eax
MyPrintf offset FmtStr, ADDR strGeoFaceTitle
invoke NewGeoFace, ADDR vtsArr, ADDR faceIdxArr, count
mov pFace, eax
mov esi, pFace
mov ebx, (GeoFace PTR [esi]).ptsAddr
mov ptsAddr, ebx
mov ebx, (GeoFace PTR [esi]).idxAddr
mov idxAddr, ebx
mov ecx, count
L3:
mov esi, idxAddr
mov ebx, [esi]
mov idx, ebx
mov esi, ptsAddr
MyPrintf offset FmtIFFF, idx, (GeoPoint PTR [esi]).x, (GeoPoint PTR [esi]).y, (GeoPoint PTR [esi]).z
add ptsAddr, TYPE GeoPoint
add idxAddr, TYPE DWORD
loop L3
mov esi, pFace
MyPrintf offset FmtInt, (GeoFace PTR [esi]).n
ret
GeoFaceTest endp
GeoPolygonProcTest proc uses edi ecx eax edx ebx,
local isInside : DWORD
local count, faceAddr, ptsAddr, idxAddr, idx : DWORD
invoke NewGeoPolygonProc, pPolygon
mov pPolygonProc, eax
mov esi, pPolygonProc
MyPrintf offset FmtStr, ADDR strGeoPolygonProcTitle
MyPrintf offset FmtFF, (GeoPolygonProc PTR [esi]).x0, (GeoPolygonProc PTR [esi]).x1
MyPrintf offset FmtFF, (GeoPolygonProc PTR [esi]).y0, (GeoPolygonProc PTR [esi]).y1
MyPrintf offset FmtFF, (GeoPolygonProc PTR [esi]).z0, (GeoPolygonProc PTR [esi]).z1
MyPrintf offset FmtF, (GeoPolygonProc PTR [esi]).minError
MyPrintf offset FmtSI, ADDR strGeoPolygonProcFacePlanes, (GeoPolygonProc PTR [esi]).numberOfFaces
mov esi, pPolygonProc
mov ecx, (GeoPolygonProc PTR [esi]).numberOfFaces
mov esi, (GeoPolygonProc PTR [esi]).facePlanes
mov esi, (ListPlane PTR [esi]).p
L1:
MyPrintf offset FmtFFFF, (GeoPlane PTR [esi]).a, (GeoPlane PTR [esi]).b, (GeoPlane PTR [esi]).cc, (GeoPlane PTR [esi]).d
add esi, TYPE GeoPlane
LOOP L1
mov esi, pPolygonProc
mov esi, (GeoPolygonProc PTR [esi]).faces
mov ebx, (ListFace PTR [esi]).p
mov faceAddr, ebx
mov ebx, (ListFace PTR [esi]).n
mov count, ebx
mov i, 0
L2:
mov edx, count
cmp i, edx
JAE L2_DONE
mov esi, faceAddr
mov ebx, (GeoFace PTR [esi]).ptsAddr
mov ptsAddr, ebx
mov ebx, (GeoFace PTR [esi]).idxAddr
mov idxAddr, ebx
mov ecx, (GeoFace PTR [esi]).n
MyPrintf offset FmtSI, ADDR strGeoPolygonProcFace, i
L3:
mov esi, idxAddr
mov ebx, [esi]
mov idx, ebx
mov esi, ptsAddr
MyPrintf offset FmtIFFF, idx, (GeoPoint PTR [esi]).x, (GeoPoint PTR [esi]).y, (GeoPoint PTR [esi]).z
add ptsAddr, TYPE GeoPoint
add idxAddr, TYPE DWORD
loop L3
add faceAddr, TYPE GeoFace
inc i
JMP L2
L2_DONE:
INVOKE PointInside3DPolygon, ptInside.x, ptInside.y, ptInside.z
mov isInside, eax
MyPrintf offset FmtInt, isInside
INVOKE PointInside3DPolygon, ptOutside.x, ptOutside.y, ptOutside.z
mov isInside, eax
MyPrintf offset FmtInt, isInside
ret
GeoPolygonProcTest endp<span id="cke_bm_111E" style="display: none;"> </span>
File IO
This is a preparation for LAS process. File IO is using C functions fopen
, fread
, fwrite
, fprintf
and fclose
, memory buffer is using malloc
and free
.
Here are prototypes for these File IO C functions:
fopen PROTO,
filename : PTR BYTE,
mode : PTR BYTE
fread PROTO,
buffer : PTR,
unitsize : DWORD,
count : DWORD,
stream : PTR FILE
fwrite PROTO,
buffer : PTR,
unitsize : DWORD,
count : DWORD,
stream : PTR FILE
fseek PROTO,
stream : PTR FILE,
pos : DWORD,
origin : DWORD
fclose PROTO,
stream : PTR FILE
fprintf PROTO,
stream : PTR FILE,
format : PTR BYTE, ; "%15.6f " will return 0x00000010 in eax because format "%15.6f " has 16 bytes to write
varlist : VARARG
LAS Process
The process has two steps, first use GeoProc
library to create a GeoPolygonProc global
instance newPolygonProc
, then use its PointInside3DPolygon
method to check point is inside or outside.
Here is the main part of LASGeoProc.asm:
.code
main PROC
MyPrintf offset FmtStr, ADDR strBegin
call SetUpGeoPolygonProc
call LASProcess
MyPrintf offset FmtStr, ADDR strEnd
ret
main ENDP
SetUpGeoPolygonProc Proc,
INVOKE NewGeoPolygon, ADDR vtsArr, vtsCount
mov pPolygon, eax
INVOKE NewGeoPolygonProc, pPolygon
mov pPolygonProc, eax
mov esi, pPolygonProc
fld (GeoPolygonProc PTR [esi]).x0
fstp x0
fld (GeoPolygonProc PTR [esi]).x1
fstp x1
fld (GeoPolygonProc PTR [esi]).y0
fstp y0
fld (GeoPolygonProc PTR [esi]).y1
fstp y1
fld (GeoPolygonProc PTR [esi]).z0
fstp z0
fld (GeoPolygonProc PTR [esi]).z1
fstp z1
ret
SetUpGeoPolygonProc endp
LASProcess proc,
local i, record_loc : DWORD
local x : REAL8
local y : REAL8
local z : REAL8
local isInside : DWORD
local insideCount : DWORD
INVOKE fopen, ADDR strFileInput, ADDR modeRB
mov rbFile, eax
INVOKE fopen, ADDR strFileOutputB, ADDR modeWB
mov wbFile, eax
INVOKE fopen, ADDR strFileOutputT, ADDR modeWT
mov wtFile, eax
INVOKE fseek, rbFile, 96, 0
INVOKE fread, offset offset_to_point_data_value, TYPE DWORD, 1, rbFile
INVOKE fseek, rbFile, 105, 0
INVOKE fread, offset record_len, TYPE WORD, 1, rbFile
INVOKE fread, offset record_num, TYPE DWORD, 1, rbFile
INVOKE fseek, rbFile, 131, 0
INVOKE fread, offset x_scale, TYPE REAL8, 1, rbFile
INVOKE fread, offset y_scale, TYPE REAL8, 1, rbFile
INVOKE fread, offset z_scale, TYPE REAL8, 1, rbFile
INVOKE fread, offset x_offset, TYPE REAL8, 1, rbFile
INVOKE fread, offset y_offset, TYPE REAL8, 1, rbFile
INVOKE fread, offset z_offset, TYPE REAL8, 1, rbFile
invoke malloc, offset_to_point_data_value
mov bufHeader, eax
INVOKE fseek, rbFile, 0, 0
INVOKE fread, bufHeader, 1, offset_to_point_data_value, rbFile
INVOKE fwrite, bufHeader, 1, offset_to_point_data_value, wbFile
invoke malloc, record_len
mov bufRecord, eax
mov i, 0
mov insideCount, 0
L1:
mov edx, record_num
cmp i, edx
JAE DONE
mov ebx, record_len
imul ebx, i
add ebx, offset_to_point_data_value
mov record_loc, ebx
INVOKE fseek, rbFile, record_loc, 0
INVOKE fread, offset xi, TYPE DWORD, 1, rbFile
INVOKE fread, offset yi, TYPE DWORD, 1, rbFile
INVOKE fread, offset zi, TYPE DWORD, 1, rbFile
fild xi
fmul x_scale
fadd x_offset
fstp x
fild yi
fmul y_scale
fadd y_offset
fstp y
fild zi
fmul z_scale
fadd z_offset
fstp z
CmpReal x0, x
JNB NEXT
CmpReal x, x1
JNB NEXT
CmpReal y0, y
JNB NEXT
CmpReal y, y1
JNB NEXT
CmpReal z0, z
JNB NEXT
CmpReal z, z1
JNB NEXT
INVOKE PointInside3DPolygon, x, y, z
mov isInside, eax
cmp isInside, FALSE
JE NEXT
INVOKE fseek, rbFile, record_loc, 0
INVOKE fread, bufRecord, 1, record_len, rbFile
INVOKE fwrite, bufRecord, 1, record_len, wbFile
INVOKE fprintf, wtFile, offset FmtOutputFile, x, y, z
inc insideCount
NEXT:
inc i
JMP L1
DONE:
INVOKE fseek, wbFile, 107, 0
INVOKE fwrite, ADDR insideCount, TYPE DWORD, 1, wbFile
INVOKE fclose, rbFile
INVOKE fclose, wbFile
INVOKE fclose, wtFile
invoke free, bufHeader
invoke free, bufRecord
ret
LASProcess endp
end
Compile
In Visual Studio, all three projects are automatically built with correct configuration.
Here is a command line compile batch file to build the static
library, test and LAS process.
cls
echo off
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
/c .\src\GeoPoint.asm /Fo GeoPoint.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
/c .\src\GeoVector.asm /Fo GeoVector.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
/c .\src\GeoPlane.asm /Fo GeoPlane.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
/c .\src\GeoPolygon.asm /Fo GeoPolygon.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
/c .\src\GeoFace.asm /Fo GeoFace.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
/c .\src\GeoPolygonProc.asm /Fo GeoPolygonProc.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
/c .\src\ListGeoProc.asm /Fo ListGeoProc.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
/c .\src\List.asm /Fo List.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
/c .\src\ListUtility.asm /Fo ListUtility.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\lib.exe" /subsystem:console ^
/out:.\lib\GeoProc.lib ^
GeoPoint.obj GeoVector.obj GeoPlane.obj GeoPolygon.obj GeoFace.obj GeoPolygonProc.obj ^
ListGeoProc.obj List.obj ListUtility.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
/c .\ListTest.asm /Fo ListTest.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
/c .\GeoProcTest.asm /Fo GeoProcTest.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\link.exe" /out:GeoProcTest.exe ^
/libpath:.\lib GeoProc.lib ^
/libpath:"C:\Program Files\Microsoft Visual Studio 10.0\VC\lib" msvcrt.lib ^
GeoProcTest.obj ListTest.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\ml.exe" /I .\include ^
/c .\LASGeoProc.asm /Fo LASGeoProc.obj
"C:\Program Files\Microsoft Visual Studio 10.0\VC\bin\link.exe" /out:LASGeoProc.exe ^
/libpath:.\lib GeoProc.lib ^
/libpath:"C:\Program Files\Microsoft Visual Studio 10.0\VC\lib" msvcrt.lib ^
LASGeoProc.obj
del *.obj
Before running this ccm.bat, either running a complete VC++ vcvars32.bat, or running a minimum version vcDevEnv.bat to set up VC++ development environment in command line like this:
echo off
set path=C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE;%path%
set include=C:\Program Files\Microsoft Visual Studio 10.0\VC\INCLUDE;
C:\Program Files\Microsoft SDKs\Windows\v7.0A\include;%include%
set lib=C:\Program Files\Microsoft Visual Studio 10.0\VC\LIB;
C:\Program Files\Microsoft SDKs\Windows\v7.0A\lib;
set libpath=C:\Program Files\Microsoft Visual Studio 10.0\VC\LIB;%libpath%
History
- 3rd March, 2016
- 4th March, 2016
- Second version
- Completed
PushFace
method