3DOMS012.INC

Assembly source for MS-DOS 3D engine, 2790 lines. Raw file

;       LKS 3-Dimensional Object Management System (3DOMS) version 0.103
;       3DOMS.INC - <c> LKS   Created: 22/Dec/1994   Now: 12/Jan/1995.


;  PRINTOUT #3 - The code so far. 12/Jan/1995.  Heap functions well tested.
; There were quite a few bugs in the CREATE/DELETE routines.
;  An error in LOGOFF meant that the computer rebooted when the interrupt
; vector 3Dh was reset was sorted out (I never found out why it did it, but
; swapping the memory deallocation and vector resetting bits seemed to stop
; it).

;  Improvements from printout #2:
; - Any obvious mistakes in comments or code fixed.
; - Triangle routine written and debugged (It took some doing!)
; - Basic rendering completed and working - it's far too slow.

;  Aims:
; - Make the blitting routines use the screen width and other variables.
; - Incorporate point structures.
; - Incorporate basic textures.



;----------------------------- Documentation --------------------------------;

comment |


  The logon and logoff routines must be called at the start and end of the
host program respectively.


;-------- Structure.

  (POINT) <- Face <- Shape <- FORM <- Object <- Group <- UNIVERSE

TEXTURES

  When I have perfected the basic rendering, I will work on texturing methods
including bitmapping, RGB colours (using dithering and an orthogonal palette),
and reflectiveness and surface normal smooth shading (for fast computers
only!).  Other graphical effects which I hope to include to add realism to
scenes are light source shading and distance fading.

POINTS

  To make rendering calculations more efficient, I want to introduce a point
structure.  Then, a point shared between many faces will have all it's
calculations done once per shape, not once per face.  Faces will still be
created in the same way, and points will be managed behind the scenes.

FACES

  Faces are the most basic pieces of a 3-dimensional thing. To avoid any
programming difficulties on my part and to make rendering as fast as possible,
all faces are triangular.  If higher polygons are required, they must be made
from more than one face.

SHAPES

  Shapes are the basic building blocks of complicated objects.  A shape must
be totally convex otherwise rendering will be inaccurate.  If you need to
make a convex object, it must consist of more than one shape.  For example,
if you want to make a plane, you may have each wing as one shape, the body as
another and the tail as another.
  Center co-ordinates are first used in shapes.  These are used to tell 3DOMS
which order to put shapes on the screen if they overlap.  It starts by drawing
the one with a center furthest from the viewer and works forward.  The
programmer has to specify the center co-ordinates themselves - they should be
centralised and 'inside' the shape if possible.

FORMS

  Shapes and faces are constants - if you want to have more than one plane
flying around, as long as all the planes look the same, they can all use the
same shapes and faces.  A form is basically a descriptor for a linked list of
shapes.  When you create a new object, you specify it's form, which will be
used when the object is drawn.

OBJECTS

  Collections of shapes of a particular form.  All the planes would be
objects.

GROUPS

  Collections of objects.  These can have moving parts, which will be objects,
because each object can be moved or rotated individually.  A group may be a
plane object and some wheel and flap objects, as the wheels and flaps are not
fixed in position on the plane.
  Groups are the main linked list, so, even if an object exists on it's own,
it has to be a member of the group - the only one.
  I may add a flag which allows you to make groups invisible - the renderer
will just miss them out - so that, instead of destroying a group temporarily
when it is not needed, it can just be made invisible.

UNIVERSE

  I added a universe structure so that groups could be treated as normal
child structures and could use the common create/delete routines.



;-------- Function 00h - Get Version And Run-Time Info

  Inputs: AH - 00h

  Returns: AH - Major version number.
	   AL - Minor version number.
	   BX - Segment of heap.
	   CX - Size of heap in 32-byte cells.
	   DX - Number of 32-byte cells free.

  This function returns information about the status of LKS 3DOMS.



;*------- Function 08h - Move Camera

  Inputs: AH - 08h
	  DS:SI - Pointer to new camera data:
	    3 double word co-ordinates, 3 rotation bytes and one rotation
	    order byte - 16 bytes in all.

  Returns: (null)

  Moves and rotates the viewpoint.



;*------- Function 09h - Change Camera

  Inputs: AH - 08h
	  DS:SI - Pointer to new camera data:
	    Double-words screen-to-eye distance and screen-to-origin distance.

  Returns: (null)

  Changes the distance from the viewer's eye to the screen and from the screen
to the origin.



;*------- Function 0Ah - Change View Area.

  Inputs: AH - 0Ah
	  BX - Virtual width of buffer.
	  CX - New width of viewing area.
	  DX - New height of viewing area.
	  DI - Start of viewing area in virtual buffer.

  Returns: (null)

  This function changes the real and virtual size of the output buffer.  If
outputting to the screen in a smaller window, for example, the virtual width
would still be 320, the virtual offset (in DI) would be the offset of the top
left hand pixel of the viewing area, and CX and DX would be the size of the
area.



;-------- Function 10h - Create Face

  Inputs: AH - 10h
	  DS:SI - Pointer to face data:
		  9 co-ordinate words and a colour byte (19 bytes in all).
	  ES - Segment of parent shape.

  Returns: Carry clear - Success
	     AX - Segment of created face.
	   Carry set - Error
	     AX - Error code.

  Creates a new face constant in the shape specified.



;-------- Function 11h - Delete Face

  Inputs: AH - 11h
	  ES - Segment of parent shape.
	  DS - Segment of face.

  Returns: Carry clear - Success
	     AX - Number of free cells.
	   Carry set - Error
	     AX - Error code.

  Deletes the face constant from the shape constant specified and frees the
cell which contained it.



;-------- Function 14h - Create Shape

  Inputs: AH - 14h
	  DS:SI - Pointer to shape data:
		  3 co-ordinate words for center of shape.
	  ES - Segment of parent form.

  Returns: Carry clear - Success
	     AX - Segment of created shape.
	   Carry set - Error
	     AX - Error code.

  Creates a new shape constant in the form specified.



;-------- Function 15h - Delete Shape

  Inputs: AH - 11h
	  ES - Segment of shape.

  Returns: Carry clear - Success
	     AX - Number of free cells.
	   Carry set - Error
	     AX - Error code.

  Deletes the shape constant from the form specified and frees the cell which
contained it.



;-------- Function 18h - Create Object

  Inputs: AH - 18h
	  DS:SI - Pointer to object data:
	    3 double word co-ordinates, 3 rotation bytes and one rotation
	    order byte - 16 bytes in all.
	  ES - Segment of parent group.

  Returns: Carry clear - Success
	     AX - Segment of created object.
	   Carry set - Error
	     AX - Error code.

  Creates a new object in the group specified.



;-------- Function 19h - Delete Object

  Inputs: AH - 19h
	  ES - Segment of object.

  Returns: Carry clear - Success
	     AX - Number of free cells.
	   Carry set - Error
	     AX - Error code.

  Deletes the object from the group specified  and frees the cell which
contained it.



;-------- Function 1Ah - Move Object

  Inputs: AH - 1Ah
	  DS:SI - Pointer to object data:
	    3 double word co-ordinates, 3 rotation bytes and one rotation
	    order byte - 16 bytes in all.
	  ES - Segment of object.

  Returns: Carry clear - Success
	   Carry set - Error
	     AX - Error code.

  Changes the co-ordinates and rotation of an object.



;-------- Function 1Bh - Change Object Form

  Inputs: AH - 1Ah
	  BX - Segment of new form (or 0 to just get current form).
	  ES - Segment of object.

  Returns: Carry clear - Success
	     AX - Segment of old form.
	   Carry set - Error
	     AX - Error code.

  Changes the form of an object.



;-------- Function 1Ch - Create Group

  Inputs: AH - 1Ch
	  DS:SI - Pointer to group data:
	    3 double word co-ordinates, 3 rotation bytes and one rotation
	    order byte - 16 bytes in all.
	  ES - Segment of parent universe.

  Returns: Carry clear - Success
	     AX - Segment of created group.
	   Carry set - Error
	     AX - Error code.

  Creates a new group.



;-------- Function 1Dh - Delete Group

  Inputs: AH - 1Dh
	  ES - Segment of group.

  Returns: Carry clear - Success
	     AX - Number of free cells.
	   Carry set - Error
	     AX - Error code.

  Deletes the group specified and frees the cell which contained it.



;-------- Function 1Eh - Move Group

  Inputs: AH - 1Eh
	  DS:SI - Pointer to group data:
	    3 double word co-ordinates, 3 rotation bytes and one rotation
	    order byte - 16 bytes in all.
	  ES - Segment of object.

  Returns: Carry clear - Success
	   Carry set - Error
	     AX - Error code.

  Changes the co-ordinates and rotation of a group.



;-------- Function 20h - Create Form

  Inputs: AH - 20h
	  DS:SI - Pointer to form data:
		  3 co-ordinate words for center of form.

  Returns: Carry clear - Success
	     AX - Segment of created form.
	   Carry set - Error
	     AX - Error code.

  Creates a new form.



;-------- Function 21h - Delete Form

  Inputs: AH - 21h
	  DS - Segment of form.

  Returns: Carry clear - Success
	     AX - Number of free cells.
	   Carry set - Error
	     AX - Error code.

  Deletes the form specified and frees the cell which contained it.  It is
unwise to delete a form which is still being used by any objects.  In v0.1,
anything could happen.  Later versions will stop this from happening and
hopefully have automatic heap verification and defragmentation.



;-------- Function 40h - Find Free cell

  Inputs: AH - 20h

  Returns: AX - Segment of first free cell.

  This function finds the first free cell on the heap.  It does not allocate
it or change the number of free cells; it merely searches through the heap
for the first cell with a magic of 00h (free).



;*------- Function 80h - Render.

  Inputs: AH - 80h
	  DS - Segment of universe.
	  ES - Segment of display buffer (this may be 0A000h or any other
	       64k buffer which will be overlayed with a 320x200 bitmap).

  Returns: (none).

  Draws the whole scene in a 320x200x256 bitmap.  It is usually desirable to
draw the scren into a buffer or a hidden screen, then display it so that the
user cannot see the polygons being drawn.  This function clears the output
buffer and then calls Function 88h for each group starting with the one
furthest from the viewer.



;*------- Function 88h - Draw Group.

  Inputs: AH - 88h
	  DS - Segment of group.
	  ES - Output segment.

  Renders a group.  Calls Function 8Ah for each shape in the group, starting
from the one the furthest away from the viewer.



;*------- Function 8Ah - Draw Object.

  Inputs: AH - 8Ah
	  DS - Segment of object.
	  ES - Output segment.

  Renders a shape.  Calls Function 8Ch for each shape in the object.



;*------- Function 8Ch - Draw Shape.

  Inputs: AH - 8Ch
	  DS - Segment of shape.
	  ES - Output segment.

  Renders a shape.  Calls Function 8Eh for each face in the shape.



;*------- Function 8Eh - Draw Face.

  Inputs: AH - 8Eh
	  DS - Segment of face.
	  ES - Output segment.

  Renders a face.  Decides whether the face is visible or not then draws the
triangle if it is.



;*------- Function 90h - Line.

  Inputs: AH - 90h
	  AL - Palette index.
	  BX - Offset of start of line.
	  CX - Width of line.
	  DX - Height of line.
	  ES - Segment of output bitmap.

  Draws a line from ES:[BX] to ES:[BX+CX+320*DX].  It does not do any boundary
checking, so the line may wrap around on the screen.



;*------- Function 92h - Horizontal Line.

  Inputs: AH - 92h
	  AL - Palette index.
	  BX - Offset of start of line.
	  CX - Width of line.
	  ES - Segment of output bitmap.

  Draws a horizontal line form ES:[BX] to ES:[BX+CX].



;-------- Function 94h - Triangle.

  Inputs: AH - 94h
	  AL - Palette index.
	  DS:SI - Pointer to 6 co-ordinate words.
	  ES - Segment of output bitmap.

  Draws a filled triangle.  Nearly always.



;*------- Function 96h - Shaded triangle.

  Inputs: AH - 96h
	  AL BH BL - Palette indexes.
	  DS:SI - Pointer to 6 co-ordinate words
	  ES - Segment of output bitmap.

|



;----------------------------- Constants ------------------------------------;



;-------- Errors.

ERROR_HEAPOUT   	equ -1	;  Out of heap space error.
ERROR_BADFACE   	equ -2	;  BADPARENT error used in place of these in
ERROR_BADSHAPE  	equ -3	; most circumstances.
ERROR_BADFORM   	equ -4
ERROR_BADOBJECT 	equ -5
ERROR_BADGROUP  	equ -6
ERROR_BADUNIVERSE	equ -7

ERROR_BADPARENT		equ -8	;  The parent specified is not valid.
ERROR_BADCHILD		equ -9	;  The child structure specified is not valid.

ERROR_TOOMANY   	equ -16	;  Too many child structures in a parent.

ERROR_BADLIST   	equ -32	;  Linked list corrupt.
ERROR_BADHEAP   	equ -33	;  Heap corrupt.

ERROR_GPFAULT   	equ -64



;-------- Offsets for renderer.

INT3D_2D_X1	equ 0000h
INT3D_2D_Y1	equ 0002h
INT3D_2D_X2	equ 0004h
INT3D_2D_Y2	equ 0006h
INT3D_2D_X3	equ 0008h
INT3D_2D_Y3	equ 000Ah

INT3D_3D_X1	equ 0000h
INT3D_3D_Y1	equ 0004h
INT3D_3D_Z1	equ 0008h
INT3D_3D_X2	equ 000Ch
INT3D_3D_Y2	equ 0010h
INT3D_3D_Z2	equ 0014h
INT3D_3D_X3	equ 0018h
INT3D_3D_Y3	equ 001Ch
INT3D_3D_Z3	equ 0020h



;-------- Rotation Order constants.

RORDER_XYZ	equ 00000000b
RORDER_XZY	equ 00000100b
RORDER_YXZ	equ 00000001b
RORDER_YZX	equ 00000101b
RORDER_ZXY	equ 00000010b
RORDER_ZYX	equ 00000110b



;-------- Magics.

MAGIC_FREE      equ 00h
MAGIC_FACE      equ 'F'
MAGIC_SHAPE     equ 'S'
MAGIC_FORM      equ 'M'
MAGIC_OBJECT    equ 'O'
MAGIC_GROUP     equ 'G'
MAGIC_UNIVERSE	equ 'U'



;-------- Common offsets.

;  Offsets which are common to all structures.  These are used by the common
; create/delete routines.

_MAGIC		equ 0000h	;  (B) Magic - indicates type of structure.
_PARENT		equ 0001h	;  (W) Segment of parent structure.
_NUM		equ 0003h	;  (B) Number of child structures.
_PTR		equ 0004h	;  (W) Segment of first child in linked list.
_PREV		equ 0006h	;  (W) Segment of previous structure in list.
_NEXT		equ 0008h	;  (W) Segment of next structure in list.
_SSDATA		equ 000Ah	;  (.) Start of structure-specific data.



;  All structures take up 32 bytes (2 paragraphs) - one cell.

;  Forms, faces and shapes are constant.  They can be common for any objects
; which all have the same appearance.



;-------- Faces.

F_MAGIC         equ 0000h	;  (B) Magic byte.
F_PARENT        equ 0001h       ;  (W) Segment of parent shape.
F_SPARE00       equ 0003h       ;  (B) There are no child structures.
F_SPARE01	equ 0004h	;  (W) There are no child structures.
F_PREV          equ 0006h       ;  (W) Segment of previous face, or 0 if none.
F_NEXT          equ 0008h       ;  (W) Segment of next face, or 0 if none.
F_DATA          equ 000Ah       ;  (.) Start of face description data.
F_X1            equ 000Ah       ;  (W) Co-ordinates of corners of face.
F_Y1            equ 000Ch
F_Z1            equ 000Eh
F_X2            equ 0010h
F_Y2            equ 0012h
F_Z2            equ 0014h
F_X3            equ 0016h
F_Y3            equ 0018h
F_Z3            equ 001Ah
F_COL           equ 001Ch       ;  (B) Colour of face (palette index).



;-------- Shapes.

S_MAGIC         equ 0000h       ;  (B) Magic byte.
S_PARENT        equ 0001h       ;  (W) Segment of parent form.
S_NUM           equ 0003h       ;  (B) Number of faces.
S_PTR           equ 0004h       ;  (W) Segment of first face in linked list.
S_PREV          equ 0006h       ;  (W) Segment of previous shape, or 0 if none.
S_NEXT          equ 0008h       ;  (W) Segment of next shape, or 0 if none.
S_DATA          equ 000Ah       ;  (.) Start of data - S_CX.
S_CX            equ 000Ah       ;  (W) Co-ordinates of center of shape.
S_CY            equ 000Ch
S_CZ            equ 000Eh



;-------- Forms.

M_MAGIC         equ 0000h       ;  (B) Magic byte.
M_NUMOBJ        equ 0001h       ;  (W) Number of objects which use this form.
M_NUM           equ 0003h       ;  (B) Number of shapes in form.
M_PTR           equ 0004h       ;  (W) Segment of first shape in linked list.
M_PREV          equ 0006h       ;  (W) Segment of previous form in list.
M_NEXT          equ 0008h       ;  (W) Segment of next form in linked list.
M_SPARE01       equ 000Ah       ;  (W)
M_SPARE02       equ 000Ch       ;  (W)
M_DATA          equ 000Eh       ;  (.)
M_CX            equ 000Eh       ;  (W) Co-ordinates center of form.
M_CY            equ 0010h
M_CZ            equ 0012h




;  Objects and groups have to be created for every 3D object which exists in
; the 3DOMS arena.



;-------- Objects.

O_MAGIC         equ 0000h       ;  (B) Magic byte.
O_PARENT        equ 0001h       ;  (W) Segment of parent group.
O_SPARE00       equ 0003h       ;  (B)
O_PTR           equ 0004h       ;  (W) Segment of form.
O_PREV          equ 0006h       ;  (W) Segment of previous object, or 0.
O_NEXT          equ 0008h       ;  (W) Segment of next object, or 0 if none.
O_DATA          equ 000Ah       ;  (.)
O_CX            equ 000Ah       ;  (W) Co-ordinates of center of object.
O_CY            equ 000Ch
O_CZ            equ 000Eh
O_X             equ 0010h       ;  (D) Transformations.
O_Y             equ 0014h
O_Z             equ 0018h
O_RX            equ 001Ch       ;  (B) Rotation about the axes.
O_RY            equ 001Dh
O_RZ            equ 001Eh
O_RORDER        equ 001Fh       ;  (B) Order of rotations.



;-------- Groups.

G_MAGIC         equ 0000h	;  (B) Magic byte.
G_PARENT        equ 0001h      	;  (W) Segment of parent universe.
G_NUM           equ 0003h	;  (B) Number of objects.
G_PTR           equ 0004h	;  (W) Segment of first object in linked list.
G_PREV          equ 0006h       ;  (W) Segment of previous group, or 0.
G_NEXT          equ 0008h       ;  (W) Segment of next group, or 0 if none.
G_DATA          equ 000Ah       ;  (.)
G_CX            equ 000Ah       ;  (W) Co-ordinates of center of group.
G_CY            equ 000Ch
G_CZ            equ 000Eh
G_X             equ 0010h       ;  (D) Transformations.
G_Y             equ 0014h
G_Z             equ 0018h
G_RX            equ 001Ch       ;  (B) Rotation about the axes.
G_RY            equ 001Dh
G_RZ            equ 001Eh
G_RORDER        equ 001Fh       ;  (B) Order of rotations.



;-------- Universe.

;  There is one universe - it is basicly a descriptor for the whole scene. I
; may incorporate the camera transformations and rotations into this
; structure.

U_MAGIC		equ 0000h	; (B) Magic byte.
U_SPARE00	equ 0001h	; (W)
U_NUM		equ 0003h	; (B) Number of groups.
U_PTR		equ 0004h	; (W) Segment of first group in linked list.



;-------- Other constants.

MAX_FACES       equ 07Fh        ;  Maximum number of faces in a shape.
MAX_SHAPES      equ 07Fh        ;  Maximum number of shapes in a form.
MAX_FORMS       equ 07Fh        ;  Maximum number of forms.
MAX_OBJECTS     equ 07Fh        ;  Maximum number of objects in a group.
MAX_GROUPS      equ 07Fh        ;  Maximum number of groups.



;----------------------------- 3DOMS Data Area ------------------------------;



  ;  Storage for old INT 3Dh vector.
_3doms_int3Dseg         dw 0000h
_3doms_int3Doff         dw 0000h

  ;  Requested min and max size of heap on call to logon routine.
_3doms_minheap          dw 0000h
_3doms_maxheap          dw 0000h

  ;  Heap descriptors.
_3doms_heapseg          dw 0000h        ;  Segment of allocated memory.
_3doms_heapsize         dw 0000h        ;  Size in 32-byte cells of heap.
_3doms_heapfree         dw 0000h        ;  Number of free 32-butes cells.

  ;  Segment of universe.
_3doms_universe		dw 0000h        ;  Segment of null group.

  ;  Global thing counters.
_3doms_faces            dw 0000h        ;  Total number of faces.
_3doms_shapes           dw 0000h        ;  Total number of shapes.
_3doms_forms            dw 0000h        ;  Number of forms.
_3doms_objects          dw 0000h        ;  Total number of objects.
_3doms_groups           dw 0000h        ;  Number of groups.

  ;  Camera descriptors.
_3doms_camera_x         dd 0            ;  Camera location.
_3doms_camera_y         dd 0
_3doms_camera_z         dd 0
_3doms_camera_rx        db 00h          ;  Camera rotations.
_3doms_camera_ry        db 00h
_3doms_camera_rz        db 00h
_3doms_camera_rorder    db 00h          ;  Camera rotation order.
_3doms_camera_scr2eye	dd 00008000h	;  Screen to eye distance.
_3doms_camera_scr2org	dd 00008000h	;  Screen to origin distance.

  ;  View area descriptors.
_3doms_view_xsize	dw 320		;  Size of viewing window in buffer.
_3doms_view_ysize	dw 200
_3doms_view_vwidth	dw 320		;  Width of virtual screen.
_3doms_view_diff	dw 0		;  Virtual width - view width.
_3doms_view_offset	dw 0000h	;  Offset into buffer of view window.



;----------------------------- Lookup Tables --------------------------------;



  ;  Sine and cosine table - 256 double word entries for each, but the tables
  ; overlap quite substantially.
_3doms_sin	dd 000000000h, 000000006h, 00000000Ch, 000000012h  ;  0°
		dd 000000019h, 00000001Fh, 000000025h, 00000002Bh
		dd 000000032h, 000000038h, 00000003Eh, 000000044h
		dd 00000004Ah, 000000050h, 000000056h, 00000005Ch
		dd 000000062h, 000000067h, 00000006Dh, 000000073h
		dd 000000078h, 00000007Eh, 000000083h, 000000089h
		dd 00000008Eh, 000000093h, 000000098h, 00000009Dh
		dd 0000000A2h, 0000000A7h, 0000000ACh, 0000000B0h
		dd 0000000B5h, 0000000B9h, 0000000BDh, 0000000C2h
		dd 0000000C6h, 0000000CAh, 0000000CDh, 0000000D1h
		dd 0000000D5h, 0000000D8h, 0000000DBh, 0000000DFh
		dd 0000000E2h, 0000000E4h, 0000000E7h, 0000000EAh
		dd 0000000ECh, 0000000EFh, 0000000F1h, 0000000F3h
		dd 0000000F5h, 0000000F7h, 0000000F8h, 0000000FAh
		dd 0000000FBh, 0000000FCh, 0000000FDh, 0000000FEh
		dd 0000000FFh, 0000000FFh, 0000000FFh, 000000100h
_3doms_cos	dd 000000100h, 000000100h, 0000000FFh, 0000000FFh  ;  90°
		dd 0000000FFh, 0000000FEh, 0000000FDh, 0000000FCh
		dd 0000000FBh, 0000000FAh, 0000000F8h, 0000000F7h
		dd 0000000F5h, 0000000F3h, 0000000F1h, 0000000EFh
		dd 0000000ECh, 0000000EAh, 0000000E7h, 0000000E4h
		dd 0000000E2h, 0000000DFh, 0000000DBh, 0000000D8h
		dd 0000000D5h, 0000000D1h, 0000000CDh, 0000000CAh
		dd 0000000C6h, 0000000C2h, 0000000BDh, 0000000B9h
		dd 0000000B5h, 0000000B0h, 0000000ACh, 0000000A7h
		dd 0000000A2h, 00000009Dh, 000000098h, 000000093h
		dd 00000008Eh, 000000089h, 000000083h, 00000007Eh
		dd 000000078h, 000000073h, 00000006Dh, 000000067h
		dd 000000062h, 00000005Ch, 000000056h, 000000050h
		dd 00000004Ah, 000000044h, 00000003Eh, 000000038h
		dd 000000032h, 00000002Bh, 000000025h, 00000001Fh
		dd 000000019h, 000000012h, 00000000Ch, 000000006h
		dd 000000000h, 0FFFFFFFAh, 0FFFFFFF4h, 0FFFFFFEEh  ;  180°
		dd 0FFFFFFE7h, 0FFFFFFE1h, 0FFFFFFDBh, 0FFFFFFD5h
		dd 0FFFFFFCEh, 0FFFFFFC8h, 0FFFFFFC2h, 0FFFFFFBCh
		dd 0FFFFFFB6h, 0FFFFFFB0h, 0FFFFFFAAh, 0FFFFFFA4h
		dd 0FFFFFF9Eh, 0FFFFFF99h, 0FFFFFF93h, 0FFFFFF8Dh
		dd 0FFFFFF88h, 0FFFFFF82h, 0FFFFFF7Dh, 0FFFFFF77h
		dd 0FFFFFF72h, 0FFFFFF6Dh, 0FFFFFF68h, 0FFFFFF63h
		dd 0FFFFFF5Eh, 0FFFFFF59h, 0FFFFFF54h, 0FFFFFF50h
		dd 0FFFFFF4Bh, 0FFFFFF47h, 0FFFFFF43h, 0FFFFFF3Eh
		dd 0FFFFFF3Ah, 0FFFFFF36h, 0FFFFFF33h, 0FFFFFF2Fh
		dd 0FFFFFF2Bh, 0FFFFFF28h, 0FFFFFF25h, 0FFFFFF21h
		dd 0FFFFFF1Eh, 0FFFFFF1Ch, 0FFFFFF19h, 0FFFFFF16h
		dd 0FFFFFF14h, 0FFFFFF11h, 0FFFFFF0Fh, 0FFFFFF0Dh
		dd 0FFFFFF0Bh, 0FFFFFF09h, 0FFFFFF08h, 0FFFFFF06h
		dd 0FFFFFF05h, 0FFFFFF04h, 0FFFFFF03h, 0FFFFFF02h
		dd 0FFFFFF01h, 0FFFFFF01h, 0FFFFFF01h, 0FFFFFF00h
		dd 0FFFFFF00h, 0FFFFFF00h, 0FFFFFF01h, 0FFFFFF01h  ;  270°
		dd 0FFFFFF01h, 0FFFFFF02h, 0FFFFFF03h, 0FFFFFF04h
		dd 0FFFFFF05h, 0FFFFFF06h, 0FFFFFF08h, 0FFFFFF09h
		dd 0FFFFFF0Bh, 0FFFFFF0Dh, 0FFFFFF0Fh, 0FFFFFF11h
		dd 0FFFFFF14h, 0FFFFFF16h, 0FFFFFF19h, 0FFFFFF1Ch
		dd 0FFFFFF1Eh, 0FFFFFF21h, 0FFFFFF25h, 0FFFFFF28h
		dd 0FFFFFF2Bh, 0FFFFFF2Fh, 0FFFFFF33h, 0FFFFFF36h
		dd 0FFFFFF3Ah, 0FFFFFF3Eh, 0FFFFFF43h, 0FFFFFF47h
		dd 0FFFFFF4Bh, 0FFFFFF50h, 0FFFFFF54h, 0FFFFFF59h
		dd 0FFFFFF5Eh, 0FFFFFF63h, 0FFFFFF68h, 0FFFFFF6Dh
		dd 0FFFFFF72h, 0FFFFFF77h, 0FFFFFF7Dh, 0FFFFFF82h
		dd 0FFFFFF88h, 0FFFFFF8Dh, 0FFFFFF93h, 0FFFFFF99h
		dd 0FFFFFF9Eh, 0FFFFFFA4h, 0FFFFFFAAh, 0FFFFFFB0h
		dd 0FFFFFFB6h, 0FFFFFFBCh, 0FFFFFFC2h, 0FFFFFFC8h
		dd 0FFFFFFCEh, 0FFFFFFD5h, 0FFFFFFDBh, 0FFFFFFE1h
		dd 0FFFFFFE7h, 0FFFFFFEEh, 0FFFFFFF4h, 0FFFFFFFAh
		dd 000000000h, 000000006h, 00000000Ch, 000000012h  ;  360°
		dd 000000019h, 00000001Fh, 000000025h, 00000002Bh
		dd 000000032h, 000000038h, 00000003Eh, 000000044h
		dd 00000004Ah, 000000050h, 000000056h, 00000005Ch
		dd 000000062h, 000000067h, 00000006Dh, 000000073h
		dd 000000078h, 00000007Eh, 000000083h, 000000089h
		dd 00000008Eh, 000000093h, 000000098h, 00000009Dh
		dd 0000000A2h, 0000000A7h, 0000000ACh, 0000000B0h
		dd 0000000B5h, 0000000B9h, 0000000BDh, 0000000C2h
		dd 0000000C6h, 0000000CAh, 0000000CDh, 0000000D1h
		dd 0000000D5h, 0000000D8h, 0000000DBh, 0000000DFh
		dd 0000000E2h, 0000000E4h, 0000000E7h, 0000000EAh
		dd 0000000ECh, 0000000EFh, 0000000F1h, 0000000F3h
		dd 0000000F5h, 0000000F7h, 0000000F8h, 0000000FAh
		dd 0000000FBh, 0000000FCh, 0000000FDh, 0000000FEh
		dd 0000000FFh, 0000000FFh, 0000000FFh, 000000100h



;----------------------------- Initialisation Code --------------------------;



;-------- Logon_3DOMS

;  Sets up interrupt vector and stack and initialises variables.

;  Inputs:
;       BX - Min cells to allocate.
;       DX - Max cells to allocate.

;  Returns:
;       Carry clear:
;         AX - Segment of heap.
;         CX - Size of heap in cells.
;       Carry set:
;         AX - Error code.

logon_3doms:
  push bx
  push cx
  push dx
  push ds
  push es

    ;  Allocate memory.
    ;  Save parameters.
  mov _3doms_minheap, bx
  mov _3doms_maxheap, dx
    ;  First try the maximum size requested.
  mov ah, 48h
  mov bx, dx
  shl bx, 1
  int 21h
    ;  Was that allocation successful?
  jnc logon_3doms_memok
    ;  If not that much memory free, see if the minimum can be allocated.
  shr bx, 1
  cmp bx, _3doms_minheap
  jl logon_3doms_nomem
    ;  Allocate all that can be allocated.
  mov ah, 48h
  shl bx, 1
  int 21h
  jnc logon_3doms_memok
logon_3doms_nomem:
    ;  There is not enough memory to satisfy the minimum memory requirements.
  stc
  mov ax, ERROR_HEAPOUT
  pop es
  pop ds
  pop dx
  pop cx
  mov cx, bx
  shr cx, 1
  pop bx
  ret
logon_3doms_memok:
  mov _3doms_heapseg, ax
  shr bx, 1
  mov _3doms_heapsize, bx
  mov _3doms_heapfree, bx

    ;  Change interrupt.
    ;  Get old interrupt vector.
  mov ax, 353Dh
  int 21h
  mov _3doms_int3Dseg, es
  mov _3doms_int3Doff, bx

    ;  Set interrupt vector.
  mov ax, 253Dh
  push cs
  pop ds
  mov dx, offset int3D_intvect
  int 21h

    ;  Set all cells on heap to free.
  mov ax, _3doms_heapseg
  mov cx, _3doms_heapsize
logon_3doms_freeheap:
  mov ds, ax
  mov byte ptr ds: [_MAGIC], MAGIC_FREE
  add ax, 2
  loop logon_3doms_freeheap

    ;  Set up universe structure.
  mov ah, 40h
  int 3Dh
  mov ds, ax
  dec _3doms_heapfree
  mov byte ptr ds: [U_MAGIC], MAGIC_UNIVERSE
  mov byte ptr ds: [U_NUM], 0
  mov word ptr ds: [U_PTR], 0000h
  mov _3doms_universe, ax

  pop es
  pop ds
  pop dx
  pop cx
  pop bx
  mov ax, _3doms_heapseg
  mov cx, _3doms_heapsize
  ret



;-------- Logoff_3DOMS

;  Restores interrupt vector.

logoff_3doms:
  push ax
  push dx
  push ds
  push es

    ;  Free allocated memory.
  mov ah, 49h
  mov es, _3doms_heapseg
  int 21h

    ;  Reset interrupt vector.
  mov ax, 253Dh
  mov dx, _3doms_int3Doff
  mov ds, _3doms_int3Dseg
  int 21h

  pop es
  pop ds
  pop dx
  pop ax
  ret



;----------------------------- The 3DOMS Interrupt --------------------------;



;  3DOMS Identifier - look for to see if 3DOMS TSR loaded.

db '3DOMSv', 00h, 10h



;  Bootstrap for the 3DOMS interrupt handler.

int3D_intvect:
    ;  Branch to the required function.
  cmp ah, 00h
  je int3D_info_

  cmp ah, 10h
  je int3D_newface_
  cmp ah, 11h
  je int3D_delface_

  cmp ah, 14h
  je int3D_newshape_
  cmp ah, 15h
  je int3D_delshape_

  cmp ah, 18h
  je int3D_newobject_
  cmp ah, 19h
  je int3D_delobject_
  cmp ah, 1Ah
  je int3D_moveobject_
  cmp ah, 1Bh
  je int3D_formobject_

  cmp ah, 1Ch
  je int3D_newgroup_
  cmp ah, 1Dh
  je int3D_delgroup_
  cmp ah, 1Eh
  je int3D_movegroup_

  cmp ah, 20h
  je int3D_newform_
  cmp ah, 21h
  je int3D_delform_

  cmp ah, 40h
  je int3D_findfree_

  cmp ah, 80h
  je int3D_render_
  cmp ah, 88h
  je int3D_drawgroup_
  cmp ah, 8Ah
  je int3D_drawobject_
  cmp ah, 8Ch
  je int3D_drawshape_
  cmp ah, 8Eh
  je int3D_drawface_

  cmp ah, 94h
  je int3D_triangle_



    ;  Set carry flag on stack and IRET.
int3D_error:
  pop word ptr cs: [_int3D_calloff]
  pop word ptr cs: [_int3D_callseg]
  pop word ptr cs: [_int3D_callflag]
  or word ptr cs: [_int3D_callflag], 0001h
  push word ptr cs: [_int3D_callflag]
  push word ptr cs: [_int3D_callseg]
  push word ptr cs: [_int3D_calloff]
  iret



    ;  Clear carry flag on stack and IRET.
int3D_end:
  pop word ptr cs: [_int3D_calloff]
  pop word ptr cs: [_int3D_callseg]
  pop word ptr cs: [_int3D_callflag]
  and word ptr cs: [_int3D_callflag], 0FFFEh
  push word ptr cs: [_int3D_callflag]
  push word ptr cs: [_int3D_callseg]
  push word ptr cs: [_int3D_calloff]
  iret

_int3D_callseg	dw 0000h
_int3D_calloff	dw 0000h
_int3D_callflag	dw 0000h



;-------- Function branches.

int3D_info_:            jmp int3D_info

int3D_newface_:         jmp int3D_newface
int3D_delface_:         jmp int3D_delface

int3D_newshape_:        jmp int3D_newshape
int3D_delshape_:        jmp int3D_delshape

int3D_newobject_:       jmp int3D_newobject
int3D_delobject_:       jmp int3D_delobject
int3D_moveobject_:      jmp int3D_moveobject
int3D_formobject_:      jmp int3D_formobject

int3D_newgroup_:        jmp int3D_newgroup
int3D_delgroup_:        jmp int3D_delgroup
int3D_movegroup_:       jmp int3D_movegroup

int3D_newform_:         jmp int3D_newform
int3D_delform_:         jmp int3D_delform

int3D_findfree_:        jmp int3D_findfree

int3D_render_:		jmp int3D_render
int3D_drawgroup_:	jmp int3D_drawgroup
int3D_drawobject_:	jmp int3D_drawobject
int3D_drawshape_:	jmp int3D_drawshape
int3D_drawface_:	jmp int3D_drawface

int3D_triangle_:	jmp int3D_triangle



;----------------------------- The 3DOMS Functions --------------------------;



;-------- Function 00h - Info.

;  Inputs:
;       AH - 00h

;  Returns:
;       AH - Major version number.
;       AL - Minor version number.
;       BX - Segment of heap.
;       CX - Size of heap in 32-byte cells.
;       DX - Number of free 32-byte cells in heap.

int3D_info:
  mov ax, 0010h         ;  Version 0.1 (00.10).
  mov bx, _3doms_heapseg
  mov cx, _3doms_heapsize
  mov dx, _3doms_heapfree
  jmp int3D_end



;----------------------------- Structure Management -------------------------;



;-------- Function 10h - Create Face.

;  Inputs:
;       AH - 10h
;       DS:SI - Face data.
;       ES - Parent shape.

;  Returns:
;       Carry clear:
;         AX - Segment of face.
;       Carry set:
;         AX - Error code.

int3D_newface:
  push bx
  push cx

    ;  Create face.
  mov ah, MAGIC_SHAPE
  mov al, MAGIC_FACE
  mov bl, MAX_FACES
  mov cx, 10
  call int3D_create

  pop cx
  pop bx

    ;  Exit if error.
  jnc int3D_newface_ok
  jmp int3D_error
int3D_newface_ok:

  inc _3doms_faces

  jmp int3D_end



;-------- Function 11h - Delete a face.

;  Inputs:
;       AH - 11h
;       ES - Segment of face.

;  Returns:
;       Carry clear:
;         AX - Segment of parent.
;       Carry set:
;         AX - Error code.

int3D_delface:

    ;  Delete face.
  mov ah, MAGIC_SHAPE
  mov al, MAGIC_FACE
  call int3D_delete

    ;  Exit if error.
  jnc int3D_delface_ok
  jmp int3D_error
int3D_delface_ok:

  dec _3doms_faces

  jmp int3D_end



;-------- Function 14h - Create a shape.

;  Inputs:
;       AH - 14h
;       DS:SI - Shape data.
;       ES - Parent form.

;  Returns:
;       Carry clear:
;         AX - Segment of shape.
;       Carry set:
;         AX - Error code.

int3D_newshape:
  push bx
  push cx

    ;  Create shape.
  mov ah, MAGIC_FORM
  mov al, MAGIC_SHAPE
  mov bl, MAX_SHAPES
  mov cx, 3
  call int3D_create

  pop cx
  pop bx

    ;  Exit if error.
  jnc int3D_newshape_ok
  jmp int3D_error
int3D_newshape_ok:

  inc _3doms_shapes

  jmp int3D_end



;-------- Function 15h - Delete a shape.

;  Inputs:
;       AH - 15h
;       ES - Segment of shape.

;  Returns:
;       Carry clear:
;         AX - Segment of parent form.
;       Carry set:
;         AX - Error code.

int3D_delshape:

    ;  Delete shape.
  mov ah, MAGIC_FORM
  mov al, MAGIC_SHAPE
  call int3D_delete

    ;  Exit if error.
  jnc int3D_delshape_ok
  jmp int3D_error
int3D_delshape_ok:

  dec _3doms_shapes

  jmp int3D_end



;-------- Function 18h - Create an object.

;  Inputs:
;       AH - 18h
;       DS:SI - object data.
;       ES - Parent group.

;  Returns:
;       Carry clear:
;         AX - Segment of object.
;       Carry set:
;         AX - Error code.

int3D_newobject:
  push bx
  push cx

    ;  Create object.
  mov ah, MAGIC_GROUP
  mov al, MAGIC_OBJECT
  mov bl, MAX_OBJECTS
  mov cx, 11
  call int3D_create

  pop cx
  pop bx

    ;  Exit if error.
  jnc int3D_newobject_ok
  jmp int3D_error
int3D_newobject_ok:

  inc _3doms_objects

  jmp int3D_end



;-------- Function 19h - Delete an object.

;  Inputs:
;       AH - 19h
;       ES - Segment of object.

;  Returns:
;       Carry clear:
;         AX - Segment of parent group.
;       Carry set:
;         AX - Error code.

int3D_delobject:

    ;  Delete object.
  mov ah, MAGIC_GROUP
  mov al, MAGIC_OBJECT
  call int3D_delete

    ;  Exit if error.
  jnc int3D_delobject_ok
  jmp int3D_error
int3D_delobject_ok:

  dec _3doms_objects

  jmp int3D_end



;-------- Function 1Ah - Move an object.

;  Inputs:
;       AH - 1Ah
;       DS:SI - Object data.
;       ES - Object.

;  Returns:
;       Carry set:
;         AX - Error code.

int3D_moveobject:

    ;  Make sure object is valid.
  cmp byte ptr es: [O_MAGIC], MAGIC_OBJECT
  je int3D_moveobject_objectok
  mov ax, ERROR_BADOBJECT
  jmp int3D_error
int3D_moveobject_objectok:

  push si
  push di
  push cx

    ;  Copy object parameters.
  mov si, bx
  mov di, O_DATA
  mov cx, 11
  rep movsw

  pop cx
  pop di
  pop si
  jmp int3D_end



;-------- Function 1Bh - Change the form of an object.

;  Inputs:
;       AH - 1Bh
;       BX - New form (or 0000h to just get old form).
;       ES - Object.

;  Returns:
;       Carry clear:
;         AX - Old form.
;       Carry set:
;         AX - Error code.

int3D_formobject:

    ;  Make sure object is valid.
  cmp byte ptr es: [O_MAGIC], MAGIC_OBJECT
  je int3D_formobject_objectok
  mov ax, ERROR_BADOBJECT
  jmp int3D_error
int3D_formobject_objectok:

    ;  Get old form.
  mov ax, es: [O_PTR]

    ;  Only change form if BX is not 0.
  cmp bx, 0
  je int3D_formobject_end
    ;  Check new form is valid.
  push ds
  mov ds, bx
  cmp byte ptr ds: [M_MAGIC], MAGIC_FORM
  je int3D_formobject_formok
    ;  Return with error code.
  pop ds
  mov ax, ERROR_BADFORM
  jmp int3D_error

int3D_formobject_formok:
  mov es: [O_PTR], bx
  pop ds

int3D_formobject_end:
  jmp int3D_end



;-------- Function 1Ch - Create a group.

;  Inputs:
;       AH - 1Ch
;       DS:SI - group data.
;	ES - Parent universe.

;  Returns:
;       Carry clear:
;         AX - Segment of group.
;       Carry set:
;         AX - Error code.

int3D_newgroup:
  push bx
  push cx

    ;  Create group.
  mov ah, MAGIC_UNIVERSE
  mov al, MAGIC_GROUP
  mov bl, MAX_GROUPS
  mov cx, 11
  call int3D_create

  pop cx
  pop bx

    ;  Exit if error.
  jnc int3D_newgroup_ok
  jmp int3D_error
int3D_newgroup_ok:

  inc _3doms_groups

  jmp int3D_end



;-------- Function 1Dh - Delete a group.

;  Inputs:
;       AH - 1Dh
;       ES - Segment of group.

;  Returns:
;       Carry clear:
;         AX - Number of free cells.
;       Carry set:
;         AX - Error code.

int3D_delgroup:

    ;  Delete group.
  mov ah, MAGIC_UNIVERSE
  mov al, MAGIC_GROUP
  call int3D_delete

    ;  Exit if error.
  jnc int3D_delgroup_ok
  jmp int3D_error
int3D_delgroup_ok:

  dec _3doms_groups

  jmp int3D_end



;-------- Function 1Eh - Move a group.

;  Inputs:
;       AH - 1Eh
;       DS:SI - Group data.
;       ES - Group.

;  Returns:
;       Carry set:
;         AX - Error code.

int3D_movegroup:

    ;  Make sure object is valid.
  cmp byte ptr es: [G_MAGIC], MAGIC_GROUP
  je int3D_movegroup_groupok
  mov ax, ERROR_BADGROUP
  jmp int3D_error
int3D_movegroup_groupok:

  push si
  push di
  push cx

    ;  Copy group parameters.
  mov si, bx
  mov di, G_DATA
  mov cx, 11
  rep movsw

  pop cx
  pop di
  pop si
  jmp int3D_end



;-------- Function 20h - Create a form.

;  Inputs:
;       AH - 20h
;       DS:SI - form data.

;  Returns:
;       Carry clear:
;         AX - Segment of form.
;       Carry set:
;         AX - Error code.

int3D_newform:

    ;  Find first free cell.
  mov ah, 40h
  int 3Dh
  jnc int3D_newform_memok
  mov ax, ERROR_HEAPOUT
  jmp int3D_error
int3D_newform_memok:

    ;  Create form.
  push es
  mov es, ax
  mov byte ptr es: [M_MAGIC], MAGIC_FORM
  mov byte ptr es: [M_NUM], 0

    ;  Copy form parameters.
int3D_newform_copy:
  push si
  push di
  mov si, bx
  mov di, M_DATA
  push cx
  mov cx, 3
  rep movsw
  pop cx
  pop di
  pop si
  pop es

    ;  Change counters.
  inc _3doms_forms
  dec _3doms_heapfree

  jmp int3D_end



;-------- Function 21h - Delete a form.

;  Inputs:
;       AH - 21h
;       ES - Segment of form.

;  Returns:
;       Carry clear:
;         AX - Number of free cells.
;       Carry set:
;         AX - Error code.

int3D_delform:

    ;  Check if form is valid.
  cmp byte ptr ds: [M_MAGIC], MAGIC_FORM
  je int3D_delform_formok
  mov ax, ERROR_BADFORM
  jmp int3D_error
int3D_delform_formok:

    ;  Free the cell occupied by the form.
  mov byte ptr ds: [M_MAGIC], MAGIC_FREE

    ;  Change counters.
  dec _3doms_forms
  inc _3doms_heapfree

  jmp int3D_end



;----------------------------- Structure Routines ---------------------------;



;-------- Routine: Create child structure.

;  Inputs:
;	AH - Magic of parent.
;	AL - Magic of child.
;	BL - Max number of child structures for parent.
;	CX - Number of words of data pointed to by DS:SI.
;       DS:SI - Structure dependant data.
;	ES - Parent.

;  Returns:
;	Carry clear:
;	  AX - Segment of new structure.
;	Carry set:
;	  AX - Error code.

_int3D_create_magics	dw 0000h
_int3D_create_child	dw 0000h

int3D_create:

  mov _int3D_create_magics, ax

    ;  Make sure parent structure is valid.
  cmp byte ptr es: [_MAGIC], ah
  je int3D_create_parentok
  mov ax, ERROR_BADPARENT
  stc
  ret
int3D_create_parentok:

    ;  Make sure not too many structures in parent.
  cmp es: [_NUM], bl
  jl int3D_create_numok
  mov ax, ERROR_TOOMANY
  stc
  ret
int3D_create_numok:

    ;  Find first free cell.
  mov ah, 40h
  int 3Dh
  jnc int3D_create_memok
  mov ax, ERROR_HEAPOUT
  stc
  ret
int3D_create_memok:
  mov _int3D_create_child, ax

  push dx
  push es

    ;  If there are no child structures in the parent then create a new
    ; linked list.
  cmp byte ptr es: [_NUM], 0
  jne int3D_create_listok

  mov es: [_PTR], ax
  xor dx, dx
  jmp int3D_create_next

    ;  Otherwise insert child into linked list before the first child and
    ; change pointer in parent.
int3D_create_listok:
  mov dx, es: [_PTR]
  mov es: [_PTR], ax
  mov es, dx
  mov es: [_PREV], ax

int3D_create_next:
    ;  Do bits which are common to first and any other child structures.
  mov es, ax
  mov ax, _int3D_create_magics
  mov es: [_MAGIC], al
  mov byte ptr es: [_NUM], 00h
  mov es: [_NEXT], dx
  mov word ptr es: [_PREV], 0000h
  pop ax
  push ax
  mov es: [_PARENT], ax

  push cx
  push si
  push di

    ;  Copy structure-dependant data.
  mov di, _SSDATA
  rep movsw

  pop di
  pop si
  pop cx
  pop es
  pop dx

    ;  Change counters.
  inc byte ptr es: [_NUM]
  inc _3doms_objects
  dec _3doms_heapfree

  mov ax, _int3D_create_child
  clc
  ret



;-------- Routine: Delete structure.

;  Inputs:
;	AH - Magic of parent.
;	AL - Magic of child.
;	ES - Child structure to delete.

;  Returns:
;	Carry clear:
;	  AX - Segment of new structure.
;	Carry set:
;	  AX - Error code.

_int3D_delete_magics	dw 0000h
_int3D_delete_child	dw 0000h

int3D_delete:

  mov _int3D_delete_magics, ax

    ;  Make sure child structure is valid.
  cmp byte ptr es: [_MAGIC], al
  je int3D_delete_childok
  mov ax, ERROR_BADCHILD
  stc
  ret
int3D_delete_childok:

  push ds

    ;  Check if parent is valid.  Note that this only checks that the parent
    ; pointer points to a parent, not whether it is the correct one - it
    ; doesn't stop you removing a child from the wrong parent!
  mov ds, es: [_PARENT]

  cmp ds: [_MAGIC], ah
  je int3D_delete_parentok
  mov ax, ERROR_BADPARENT
  pop ds
  clc
  ret
int3D_delete_parentok:

  push bx
  push dx

    ;  Get segments of previous and next child structures.
  mov bx, es: [_PREV]
  mov dx, es: [_NEXT]

    ;  Check both point to child structures unless they are null.

    ;  Check previous child.
  cmp bx, 0000h
  je int3D_delete_prevok
  mov ds, bx
  cmp ds: [_MAGIC], al
  je int3D_delete_prevok
    ;  This is an illegal child, so exit with error code.
  mov ax, ERROR_BADLIST
  pop dx
  pop bx
  pop ds
  stc
  ret
int3D_delete_prevok:

    ;  Check next child.
  cmp dx, 0000h
  je int3D_delete_nextok
  mov ds, dx
  cmp ds: [_MAGIC], al
  je int3D_delete_nextok
    ;  This is an illegal shape, so exit with error code.
  mov ax, ERROR_BADLIST
  pop dx
  pop bx
  pop ds
  stc
  ret
int3D_delete_nextok:

    ;  Remove child from linked list.
    ;  Put segment of next child in previous unless this is the first child.
  cmp bx, 0000h
  je int3D_delete_first
  mov ds, bx
  mov ds: [_NEXT], dx
  jmp int3D_delete_notfirst
int3D_delete_first:

    ;  This was the first child, so change pointer in parent structure.
  mov ds, es: [_PARENT]
  mov ds: [_PTR], dx
int3D_delete_notfirst:

    ;  Put segment of previous child in next unless this is the last child.
  cmp dx, 0000h
  je int3D_delete_last
  mov ds, dx
  mov ds: [_PREV], bx
int3D_delete_last:

  mov ax, es: [_PARENT]

    ;  Free the cell occupied by the child.
  mov byte ptr es: [_MAGIC], MAGIC_FREE

    ;  Change counters.
  mov ds, ax
  dec byte ptr ds: [_NUM]
  inc _3doms_heapfree

  pop dx
  pop bx
  pop ds
  clc
  ret



;----------------------------- Heap Management ------------------------------;



;-------- Function 40h - Find first free cell.

;  Inputs:
;       AH - 40h

;  Returns:
;       Carry clear:
;         AX - Segment of first free cell.
;       Carry set:
;         AX - Undefined (first segment after heap).

int3D_findfree:
  push ds
  push cx
  mov ds, _3doms_heapseg
  mov cx, _3doms_heapsize

int3D_findfree_loop:
    ;  Is this cell free?
  cmp byte ptr ds: [0], 00h
  je int3D_findfirst_found
    ;  If not, move on to next cell
  mov ax, ds
  add ax, 2
  mov ds, ax
  loop int3D_findfree_loop
    ;  Return an error if the end of the heap is reached before a free cell
    ; is found.
  pop cx
  pop ds
  jmp int3D_error

    ;  If a free cell has been found, put it's segment in AX and return.
int3D_findfirst_found:
  mov ax, ds
  pop cx
  pop ds

    ;  TEMPORARY - Fill cell with 0FFh's (apart from magic).
  push es
  push di
  push cx
  push ax
  cld
  mov es, ax
  mov di, 1
  mov al, 0FFh
  mov cx, 31
  rep stosb
  pop ax
  pop cx
  pop di
  pop es

  jmp int3D_end



;----------------------------- Rendering Functions --------------------------;



;-------- Function 80h - Render.

;  Inputs:
;	AH - 80h
;	DS - Segment of universe.
;	ES - Output Segment.

;  Returns:
;	Carry clear:
;	  AX - Undefined.
;	Carry set:
;	  AX - Error.
;	  BX - Segment of erroneous structure.

int3D_render:

    ;  Check universe magic.
  cmp byte ptr ds: [U_MAGIC], MAGIC_UNIVERSE
  je int3D_render_universeok
  mov ax, ERROR_BADUNIVERSE
  jmp int3D_error
int3D_render_universeok:

  push cx
  push ds

    ;  Go through groups, calling Function 88h for each - don't worry about
    ; order yet.
  xor ch, ch
  mov cl, ds: [U_NUM]
  mov ds, ds: [U_PTR]
  jcxz int3D_render_end

int3D_render_loop:
  mov ah, 88h
  int 3Dh
  jnc int3D_render_groupok
  pop bx
  pop cx
  jmp int3D_error

int3D_render_groupok:
  mov ds, ds: [G_NEXT]
  loop int3D_render_loop

int3D_render_end:
  pop ds
  pop cx
  jmp int3D_end



;-------- Function 88h - Draw group.

;  Inputs:
;	AH - 88h
;	DS - Segment of group.
;	ES - Output Segment.

;  Returns:
;	Carry clear:
;	  AX - Undefined.
;	Carry set:
;	  AX - Error.
;	  BX - Segment of erroneous structure.

int3D_drawgroup:

    ;  Check group magic.
  cmp byte ptr ds: [G_MAGIC], MAGIC_GROUP
  je int3D_drawgroup_groupok
  mov ax, ERROR_BADGROUP
  jmp int3D_error
int3D_drawgroup_groupok:

  push cx
  push ds

    ;  Go through objects, calling Function 8Ah for each - don't worry about
    ; order yet.
  xor ch, ch
  mov cl, ds: [G_NUM]
  mov ds, ds: [G_PTR]
  jcxz int3D_drawgroup_end

int3D_drawgroup_loop:

  mov ah, 8Ah
  int 3Dh
  jnc int3D_drawgroup_objectok
  pop bx
  pop cx
  jmp int3D_error

int3D_drawgroup_objectok:
  mov ds, ds: [O_NEXT]
  loop int3D_drawgroup_loop

int3D_drawgroup_end:
  pop ds
  pop cx
  jmp int3D_end



;-------- Function 8Ah - Draw object.

;  Inputs:
;	AH - 8Ah
;	DS - Segment of object.
;	ES - Output Segment.

;  Returns:
;	Carry clear:
;	  AX - Undefined.
;	Carry set:
;	  AX - Error.
;	  BX - Segment of erroneous structure.

int3D_drawobject:

    ;  Check object magic.
  cmp byte ptr ds: [O_MAGIC], MAGIC_OBJECT
  je int3D_drawobject_objectok
  mov ax, ERROR_BADOBJECT
  jmp int3D_error
int3D_drawobject_objectok:

  push bx
  push cx
  push ds

  mov bx, ds
  mov ds, ds: [O_PTR]
    ;  Check form magic.
  cmp byte ptr ds: [F_MAGIC], MAGIC_FORM
  je int3D_drawobject_formok
  pop ds
  pop cx
  pop bx
  mov ax, ERROR_BADFORM
  jmp int3D_error
int3D_drawobject_formok:

    ;  Go through shapes, calling Function 8Ch for each - don't worry about
    ; order yet.
  xor ch, ch
  mov cl, ds: [M_NUM]
  mov ds, ds: [M_PTR]
  jcxz int3D_drawobject_end

int3D_drawobject_loop:
  mov ah, 8Ch
  int 3Dh
  jnc int3D_drawobject_shapeok
  pop bx
  pop cx
  add sp, 2
  jmp int3D_error

int3D_drawobject_shapeok:
  mov ds, ds: [S_NEXT]
  loop int3D_drawobject_loop

int3D_drawobject_end:
  pop ds
  pop cx
  pop bx
  jmp int3D_end



;-------- Function 8Ch - Draw shape.

;  Inputs:
;	AH - 8Ch
;	BX - Sement of parent object.
;	DS - Segment of shape.
;	ES - Output Segment.

;  Returns:
;	Carry clear:
;	  AX - Undefined.
;	Carry set:
;	  AX - Error.
;	  BX - Segment of erroneous structure.

int3D_drawshape:

    ;  Check shape magic.
  cmp byte ptr ds: [S_MAGIC], MAGIC_SHAPE
  je int3D_drawshape_shapeok
  mov ax, ERROR_BADSHAPE
  jmp int3D_error
int3D_drawshape_shapeok:

  push cx
  push ds

    ;  Go through faces, calling Function 8Eh for each.
  xor ch, ch
  mov cl, ds: [S_NUM]
  mov ds, ds: [S_PTR]
  jcxz int3D_drawshape_end

int3D_drawshape_loop:
  mov ah, 8Eh
  int 3Dh
  jnc int3D_drawshape_faceok
  pop bx
  pop cx
  jmp int3D_error

int3D_drawshape_faceok:
  mov ds, ds: [F_NEXT]
  loop int3D_drawshape_loop

int3D_drawshape_end:
  pop ds
  pop cx
  jmp int3D_end



;-------- Function 8Eh - Draw face.

;  Inputs:
;	AH - 8Eh
;	BX - Sement of parent object.
;	DS - Segment of face.
;	ES - Output Segment.

;  Returns:
;	Carry clear:
;	  AX - Undefined.
;	Carry set:
;	  AX - Error.

_int3D_drawface_xyz	dd 9 dup (0000h)
_int3D_drawface_scrxy	dw 6 dup (0000h)

int3D_drawface:

  push bx
  push ecx
  push edx
  push si
  push ds

    ;  Calculate spacial co-ordinates of face after rotations.
    ;  Copy co-ordinates into work area.
  mov ax, ds: [F_X1]
  cwde
  mov _int3D_drawface_xyz[INT3D_3D_X1], eax
  mov ax, ds: [F_Y1]
  cwde
  mov _int3D_drawface_xyz[INT3D_3D_Y1], eax
  mov ax, ds: [F_Z1]
  cwde
  mov _int3D_drawface_xyz[INT3D_3D_Z1], eax
  mov ax, ds: [F_X2]
  cwde
  mov _int3D_drawface_xyz[INT3D_3D_X2], eax
  mov ax, ds: [F_Y2]
  cwde
  mov _int3D_drawface_xyz[INT3D_3D_Y2], eax
  mov ax, ds: [F_Z2]
  cwde
  mov _int3D_drawface_xyz[INT3D_3D_Z2], eax
  mov ax, ds: [F_X3]
  cwde
  mov _int3D_drawface_xyz[INT3D_3D_X3], eax
  mov ax, ds: [F_Y3]
  cwde
  mov _int3D_drawface_xyz[INT3D_3D_Y3], eax
  mov ax, ds: [F_Z3]
  cwde
  mov _int3D_drawface_xyz[INT3D_3D_Z3], eax

    ;  Do object rotation.
  mov ds, bx
  mov bx, O_RX
  mov si, offset _int3D_drawface_xyz
  call int3D_rotate
  add si, 12
  call int3D_rotate
  add si, 12
  call int3D_rotate

    ;  Do object transformations.
  mov eax, ds: [O_X]
  add _int3D_drawface_xyz[INT3D_3D_X1], eax
  add _int3D_drawface_xyz[INT3D_3D_X2], eax
  add _int3D_drawface_xyz[INT3D_3D_X3], eax
  mov eax, ds: [O_Y]
  add _int3D_drawface_xyz[INT3D_3D_Y1], eax
  add _int3D_drawface_xyz[INT3D_3D_Y2], eax
  add _int3D_drawface_xyz[INT3D_3D_Y3], eax
  mov eax, ds: [O_Z]
  add _int3D_drawface_xyz[INT3D_3D_Z1], eax
  add _int3D_drawface_xyz[INT3D_3D_Z2], eax
  add _int3D_drawface_xyz[INT3D_3D_Z3], eax

    ;  Do group rotation.
  mov ds, ds: [O_PARENT]
  mov bx, G_RX
  mov si, offset _int3D_drawface_xyz
  call int3D_rotate
  add si, 12
  call int3D_rotate
  add si, 12
  call int3D_rotate

    ;  Do group transformations.
  mov eax, ds: [G_X]
  add _int3D_drawface_xyz[INT3D_3D_X1], eax
  add _int3D_drawface_xyz[INT3D_3D_X2], eax
  add _int3D_drawface_xyz[INT3D_3D_X3], eax
  mov eax, ds: [G_Y]
  add _int3D_drawface_xyz[INT3D_3D_Y1], eax
  add _int3D_drawface_xyz[INT3D_3D_Y2], eax
  add _int3D_drawface_xyz[INT3D_3D_Y3], eax
  mov eax, ds: [G_Z]
  add _int3D_drawface_xyz[INT3D_3D_Z1], eax
  add _int3D_drawface_xyz[INT3D_3D_Z2], eax
  add _int3D_drawface_xyz[INT3D_3D_Z3], eax

    ;  Calculate screen co-ordinates.
    ;  SX1 = (scr2eye*X1) / (scr2eye+scr2org+Z1).
  mov eax, dword ptr _int3D_drawface_xyz[INT3D_3D_X1]
  imul dword ptr _3doms_camera_scr2eye
  mov ecx, _3doms_camera_scr2eye
  add ecx, _3doms_camera_scr2org
  add ecx, _int3D_drawface_xyz[INT3D_3D_Z1]
  idiv ecx
  shr eax, 8
  add ax, 159
  mov _int3D_drawface_scrxy[INT3D_2D_X1], ax
    ;  SY1 = (scr2eye*Y1) / (scr2eye+scr2org+Z1).
  mov eax, dword ptr _int3D_drawface_xyz[INT3D_3D_Y1]
  imul dword ptr _3doms_camera_scr2eye
  mov ecx, _3doms_camera_scr2eye
  add ecx, _3doms_camera_scr2org
  add ecx, _int3D_drawface_xyz[INT3D_3D_Z1]
  idiv ecx
  shr eax, 8
  add ax, 99
  mov _int3D_drawface_scrxy[INT3D_2D_Y1], ax
    ;  SX2 = (scr2eye*X2) / (scr2eye+scr2org+Z2).
  mov eax, dword ptr _int3D_drawface_xyz[INT3D_3D_X2]
  imul dword ptr _3doms_camera_scr2eye
  mov ecx, _3doms_camera_scr2eye
  add ecx, _3doms_camera_scr2org
  add ecx, _int3D_drawface_xyz[INT3D_3D_Z2]
  idiv ecx
  shr eax, 8
  add ax, 159
  mov _int3D_drawface_scrxy[INT3D_2D_X2], ax
    ;  SY2 = (scr2eye*Y2) / (scr2eye+scr2org+Z2).
  mov eax, dword ptr _int3D_drawface_xyz[INT3D_3D_Y2]
  imul dword ptr _3doms_camera_scr2eye
  mov ecx, _3doms_camera_scr2eye
  add ecx, _3doms_camera_scr2org
  add ecx, _int3D_drawface_xyz[INT3D_3D_Z2]
  idiv ecx
  shr eax, 8
  add ax, 99
  mov _int3D_drawface_scrxy[INT3D_2D_Y2], ax
    ;  SX3 = (scr2eye*X3) / (scr2eye+scr2org+Z3).
  mov eax, dword ptr _int3D_drawface_xyz[INT3D_3D_X3]
  imul dword ptr _3doms_camera_scr2eye
  mov ecx, _3doms_camera_scr2eye
  add ecx, _3doms_camera_scr2org
  add ecx, _int3D_drawface_xyz[INT3D_3D_Z3]
  idiv ecx
  shr eax, 8
  add ax, 159
  mov _int3D_drawface_scrxy[INT3D_2D_X3], ax
    ;  SY3 = (scr2eye*Y3) / (scr2eye+scr2org+Z3).
  mov eax, dword ptr _int3D_drawface_xyz[INT3D_3D_Y3]
  imul dword ptr _3doms_camera_scr2eye
  mov ecx, _3doms_camera_scr2eye
  add ecx, _3doms_camera_scr2org
  add ecx, _int3D_drawface_xyz[INT3D_3D_Z3]
  idiv ecx
  shr eax, 8
  add ax, 99
  mov _int3D_drawface_scrxy[INT3D_2D_Y3], ax

    ;  Calculate cross vector product - If this is -ive then the shape is
    ; facing away - don't draw it.
  mov cx, _int3D_drawface_scrxy[INT3D_2D_X3]
  sub cx, _int3D_drawface_scrxy[INT3D_2D_X2]
  mov ax, _int3D_drawface_scrxy[INT3D_2D_Y2]
  sub ax, _int3D_drawface_scrxy[INT3D_2D_Y1]
  imul cx
  mov bx, ax
  mov cx, _int3D_drawface_scrxy[INT3D_2D_X2]
  sub cx, _int3D_drawface_scrxy[INT3D_2D_X1]
  mov ax, _int3D_drawface_scrxy[INT3D_2D_Y3]
  sub ax, _int3D_drawface_scrxy[INT3D_2D_Y2]
  imul cx
  sub ax, bx
  jle int3D_drawface_nodraw

    ;  Draw face.
  pop ds
  push ds
  mov ah, 94h
  mov al, ds: [F_COL]
  push cs
  pop ds
  mov si, offset _int3D_drawface_scrxy
  int 3Dh
int3D_drawface_nodraw:

  pop ds
  pop si
  pop edx
  pop ecx
  pop bx
  jmp int3D_end



;-------- Rotate

;  Inputs:
;	DS:BX - Angles and rotation order (4 bytes).
;	CS:SI - 3 Co-ordinate dwords to be rotated.

int3D_rotate:

  test byte ptr ds: [bx+3], 00000001b
  jnz int3D_rotate_y__
  test byte ptr ds: [bx+3], 00000010b
  jnz int3D_rotate_z__

int3D_rotate_x__:
  mov al, ds: [bx]
  call int3D_rotate_x
  test byte ptr ds: [bx+3], 00000100b
  jz int3D_rotate__yz
int3D_rotate__zy:
  mov al, ds: [bx+2]
  call int3D_rotate_z
  mov al, ds: [bx+1]
  call int3D_rotate_y
  ret
int3D_rotate__yz:
  mov al, ds: [bx+1]
  call int3D_rotate_y
  mov al, ds: [bx+2]
  call int3D_rotate_z
  ret

int3D_rotate_y__:
  mov al, ds: [bx+1]
  call int3D_rotate_y
  test byte ptr ds: [bx+3], 00000100b
  jz int3D_rotate__xz
int3D_rotate__zx:
  mov al, ds: [bx+2]
  call int3D_rotate_z
  mov al, ds: [bx]
  call int3D_rotate_x
  ret
int3D_rotate__xz:
  mov al, ds: [bx]
  call int3D_rotate_x
  mov al, ds: [bx+2]
  call int3D_rotate_z
  ret

int3D_rotate_z__:
  mov al, ds: [bx+2]
  call int3D_rotate_z

  test byte ptr ds: [bx+3], 00000100b
  jz int3D_rotate__xy
int3D_rotate__yx:
  mov al, ds: [bx+1]
  call int3D_rotate_y
  mov al, ds: [bx]
  call int3D_rotate_x
  ret
int3D_rotate__xy:
  mov al, ds: [bx]
  call int3D_rotate_x
  mov al, ds: [bx+1]
  call int3D_rotate_y
  ret



;-------- Rotate_x

;  Inputs:
;	AL - Angle.
;	CS:SI - 3 Co-ordinate dwords to be rotated about the X-axis.

int3D_rotate_x:
  push bx
    ;  Calculate offset in sine table.
  xor bx, bx
  mov bl, al
  shl bx, 2

    ;  NY = Y*cos(AL)-Z*sin(AL)
  mov ecx, cs: INT3D_3D_Y1[si]
  mov eax, _3doms_cos[bx]
  imul ecx
  mov al, dl
  ror eax, 8
  mov cs: INT3D_3D_Y1[si], eax
  mov eax, _3doms_sin[bx]
  imul dword ptr cs: INT3D_3D_Z1[si]
  mov al, dl
  ror eax, 8
  sub cs: INT3D_3D_Y1[si], eax
    ;  NZ = Y*sin(AL)+Z*cos(AL)
  mov eax, _3doms_cos[bx]
  imul dword ptr cs: INT3D_3D_Z1[si]
  mov al, dl
  ror eax, 8
  mov cs: INT3D_3D_Z1[si], eax
  mov eax, _3doms_sin[bx]
  imul ecx
  mov al, dl
  ror eax, 8
  add cs: INT3D_3D_Z1[si], eax

  pop bx
  ret



;-------- Rotate_y

;  Inputs:
;	AL - Angle.
;	CS:SI - 3 Co-ordinate dwords to be rotated about the Y-axis.

int3D_rotate_y:
  push bx
    ;  Calculate offset in sine table.
  xor bx, bx
  mov bl, al
  shl bx, 2

    ;  NY = Y*cos(AL)-Z*sin(AL)
  mov ecx, cs: INT3D_3D_X1[si]
  mov eax, _3doms_cos[bx]
  imul ecx
  mov al, dl
  ror eax, 8
  mov cs: INT3D_3D_X1[si], eax
  mov eax, _3doms_sin[bx]
  imul dword ptr cs: INT3D_3D_Z1[si]
  mov al, dl
  ror eax, 8
  sub cs: INT3D_3D_X1[si], eax
    ;  NZ = Y*sin(AL)+Z*cos(AL)
  mov eax, _3doms_cos[bx]
  imul dword ptr cs: INT3D_3D_Z1[si]
  mov al, dl
  ror eax, 8
  mov cs: INT3D_3D_Z1[si], eax
  mov eax, _3doms_sin[bx]
  imul ecx
  mov al, dl
  ror eax, 8
  add cs: INT3D_3D_Z1[si], eax

  pop bx
  ret



;-------- Rotate_z

;  Inputs:
;	AL - Angle.
;	CS:SI - 3 Co-ordinate dwords to be rotated about the Z-axis.

int3D_rotate_z:
  push bx
    ;  Calculate offset in sine table.
  xor bx, bx
  mov bl, al
  shl bx, 2

    ;  NY = Y*cos(AL)-Z*sin(AL)
  mov ecx, cs: INT3D_3D_X1[si]
  mov eax, _3doms_cos[bx]
  imul ecx
  mov al, dl
  ror eax, 8
  mov cs: INT3D_3D_X1[si], eax
  mov eax, _3doms_sin[bx]
  imul dword ptr cs: INT3D_3D_Y1[si]
  mov al, dl
  ror eax, 8
  sub cs: INT3D_3D_X1[si], eax
    ;  NZ = Y*sin(AL)+Z*cos(AL)
  mov eax, _3doms_cos[bx]
  imul dword ptr cs: INT3D_3D_Y1[si]
  mov al, dl
  ror eax, 8
  mov cs: INT3D_3D_Y1[si], eax
  mov eax, _3doms_sin[bx]
  imul ecx
  mov al, dl
  ror eax, 8
  add cs: INT3D_3D_Y1[si], eax

  pop bx
  ret



;----------------------------- Graphics Functions ---------------------------;



;-------- Function 94h - Triangle.

;  Inputs:
;	AH - 94h
;	AL - Colour.
;	DS:SI - 6 co-ordinate words.
;	ES - Output segment.

_int3D_triangle_colour	db 00h
_int3D_triangle_x1x2	dw 0000h
_int3D_triangle_y2y1	dw 0000h
_int3D_triangle_x1x3	dw 0000h
_int3D_triangle_y3y1	dw 0000h
_int3D_triangle_x2x3	dw 0000h
_int3D_triangle_y3y2	dw 0000h

int3D_triangle:
  mov _int3D_triangle_colour, al

  push ax
  push bx
  push ecx
  push dx
  push esi
  push edi

  and esi, 0FFFFh
  and edi, 0FFFFh

    ;  Sort the co-ordinates into order - lowest y-value first.
    ;  Swap (X1,Y1) and (X2,Y2) if necessary.
  mov ax, INT3D_2D_Y1[si]
  cmp ax, INT3D_2D_Y2[si]
  jl int3D_triangle_y1ok
  xchg ax, INT3D_2D_Y2[si]
  xchg ax, INT3D_2D_Y1[si]
  mov ax, INT3D_2D_X1[si]
  xchg ax, INT3D_2D_X2[si]
  xchg ax, INT3D_2D_X1[si]
int3D_triangle_y1ok:
    ;  Swap (X2,Y2) and (X3,Y3) if necessary.
  mov ax, INT3D_2D_Y2[si]
  cmp ax, INT3D_2D_Y3[si]
  jl int3D_triangle_y2ok
  xchg ax, INT3D_2D_Y3[si]
  xchg ax, INT3D_2D_Y2[si]
  mov ax, INT3D_2D_X2[si]
  xchg ax, INT3D_2D_X3[si]
  xchg ax, INT3D_2D_X2[si]
int3D_triangle_y2ok:
    ;  Swap (X1,Y1) and (X2,Y2) again if necessary.
  mov ax, INT3D_2D_Y1[si]
  cmp ax, INT3D_2D_Y2[si]
  jl int3D_triangle_y1ok2
  xchg ax, INT3D_2D_Y2[si]
  xchg ax, INT3D_2D_Y1[si]
  mov ax, INT3D_2D_X1[si]
  xchg ax, INT3D_2D_X2[si]
  xchg ax, INT3D_2D_X1[si]
int3D_triangle_y1ok2:

  mov ax, INT3D_2D_X1[si]
  sub ax, INT3D_2D_X2[si]
  mov _int3D_triangle_x1x2, ax

  mov ax, INT3D_2D_Y2[si]
  sub ax, INT3D_2D_Y1[si]
  mov _int3D_triangle_y2y1, ax

  mov ax, INT3D_2D_X1[si]
  sub ax, INT3D_2D_X3[si]
  mov _int3D_triangle_x1x3, ax

  mov ax, INT3D_2D_Y3[si]
  sub ax, INT3D_2D_Y1[si]
  mov _int3D_triangle_y3y1, ax

  mov ax, INT3D_2D_X2[si]
  sub ax, INT3D_2D_X3[si]
  mov _int3D_triangle_x2x3, ax

  mov ax, INT3D_2D_Y3[si]
  sub ax, INT3D_2D_Y2[si]
  mov _int3D_triangle_y3y2, ax

  mov ax, 320
  mul word ptr INT3D_2D_Y1[si]
  mov bx, ax
  add bx, INT3D_2D_X2[si]
  add ax, INT3D_2D_X3[si]
  mov di, ax

  mov cx, _int3D_triangle_y3y1
  rol ecx, 16
  mov cx, _int3D_triangle_y2y1
  jcxz int3D_triangle_notop

int3D_triangle_y1loop:
  push di
  push bx

  mov ax, _int3D_triangle_x1x2
  imul cx
  idiv _int3D_triangle_y2y1
  add bx, ax

  mov ax, _int3D_triangle_x1x3
  ror ecx, 16
  imul cx
  idiv _int3D_triangle_y3y1
  add di, ax

    ;  Horiz line.
  cmp di, bx
  jb int3D_triangle_y1noswap
  xchg bx, di
int3D_triangle_y1noswap:
  push ecx

  xor ecx, ecx
  mov cx, bx
  sub cx, di
  inc cx
  mov al, _int3D_triangle_colour
  call int3D_line

  pop ecx
  dec cx
  rol ecx, 16
  pop bx
  pop di
  add di, 320
  add bx, 320
  loop int3D_triangle_y1loop
  jmp int3D_triangle_aftertop

int3D_triangle_notop:
  mov bx, INT3D_2D_X1[si]
  mov di, INT3D_2D_X2[si]
  cmp bx, di
  jb int3D_triangle_notop_noswap
  xchg bx, di
int3D_triangle_notop_noswap:
  mov cx, di
  sub cx, bx
  inc cx
  mov ax, 320
  mul word ptr INT3D_2D_Y1[si]
  mov di, bx
  add di, ax
  mov al, _int3D_triangle_colour
  call int3D_line

int3D_triangle_aftertop:

  mov ax, 320
  mul word ptr INT3D_2D_Y2[si]
  add ax, INT3D_2D_X3[si]
  mov bx, ax
  mov di, ax
  mov cx, _int3D_triangle_y3y2
  jcxz int3D_triangle_nobottom

int3D_triangle_y2loop:
  push di
  push bx

  mov ax, _int3D_triangle_x2x3
  imul cx
  idiv _int3D_triangle_y3y2
  add bx, ax

  mov ax, _int3D_triangle_x1x3
  imul cx
  idiv _int3D_triangle_y3y1
  add di, ax

  cmp bx, di
  jb int3D_triangle_y2noswap
  xchg bx, di
int3D_triangle_y2noswap:
  push ecx

  xor ecx, ecx
  mov cx, di
  sub cx, bx
  mov di, bx
  inc cx
  mov al, _int3D_triangle_colour
  call int3D_line

  pop ecx
  pop bx
  pop di
  add bx, 320
  add di, 320
  loop int3D_triangle_y2loop
  jmp int3D_triangle_afterbottom

int3D_triangle_nobottom:
  mov bx, INT3D_2D_X2[si]
  mov di, INT3D_2D_X3[si]
  cmp bx, di
  jb int3D_triangle_nobottom_noswap
  xchg bx, di
int3D_triangle_nobottom_noswap:
  mov cx, di
  sub cx, bx
  inc cx
  mov ax, 320
  mul word ptr INT3D_2D_Y2[si]
  mov di, bx
  add di, ax
  mov al, _int3D_triangle_colour
  call int3D_line

int3D_triangle_afterbottom:

  pop edi
  pop esi
  pop dx
  pop ecx
  pop bx
  pop ax
  jmp int3D_end



int3D_line:
  jcxz int3D_line_noline

;  rep stosb		;  Slow but safe while testing rendering functions.
;  ret

  shr cx, 1
  jnc int3D_line_nobyte
  stosb
int3D_line_nobyte:
  jcxz int3D_line_noline
  mov ah, al
  shr cx, 1
  jnc int3D_line_noword
  stosw
int3D_line_noword:
  jcxz int3D_line_noline
  push ax
  rol eax, 16
  pop ax
  rep stosd
int3D_line_noline:
  ret



;--------  End of code (line 2783).