PAGE 80,132
TITLE - XTASM.ASM - Assembly routines for 3D engine
;;;.286P
;==============================================================================
; COMMAND LINE ASSEMBLY
;	MASM /B63 /Z /D_Mx FILENAME;
;
;  WHERE Mx SPECIFIES MODEL (l OR c) FOR LARGE OR COMPACT MODELS
;==============================================================================
	INCLUDE		ET.EQU
	INCLUDE		ET.MAC

	ANGLE_30    equ	    160
	ANGLE_360   equ	    1920

	extrn	_bMaps:DWORD
	extrn	_oMaps:DWORD
	extrn	_bdfp:DWORD
	extrn	_PageBegin:WORD
	extrn	_InvCosTable:DWORD
	extrn	_InvSinTable:DWORD
	extrn	_LongCosTable:DWORD
	extrn	_DistanceTable:WORD
	extrn	_AdjustTable:DWORD
	extrn	_CenterRow:WORD
	extrn	_MaxDistance:WORD
	extrn	_TopColor:WORD
	extrn	_BottomColor:WORD

	extrn	_lowmask:BYTE
	extrn	_Walls:WORD


	extrn	_xRay:NEAR
	extrn	_yRay:NEAR
	extrn	N_LDIV@:FAR


	PUBLIC		_graphinit
	PUBLIC		_usepage
	PUBLIC		_flippage
	PUBLIC		_SetPalette
	PUBLIC		_DrawWalls
	PUBLIC		_DrawOneObject

SC_INDEX equ	03c4h	;Sequence Controller Index
CRTC_INDEX equ	03d4h	;CRT Controller Index
MISC_OUTPUT equ 03c2h	;Miscellaneous Output register
SCREEN_SEG equ	0a000h	;segment of display memory in mode X


_TEXT	segment byte public 'CODE'
DGROUP	group	_DATA,_BSS
	assume	cs:_TEXT,ds:DGROUP
_ViewPage  dw 0 ; The address of the current view page
_WritePage dw 0 ; The address of the current write page
PageTable dw 4 dup (0)	 ; Address of 4 pages
ScreenWidth dw 320/4	 ; The width of the screen
PageSize    dw 320/4*240 ; The size of each page
CRTParms label	word
		  dw	  00d06h  ;vertical total
		  dw	  03e07h  ;overflow (bit 8 of vertical counts)
		  dw	  04209h  ;cell height (2 to double-scan)
		  dw	  0ea10h  ;v sync start
		  dw	  0ac11h  ;v sync end and protect cr0-cr7
		  dw	  0df12h  ;vertical displayed
		  dw	  00014h  ;turn off dword mode
		  dw	  0e715h  ;v blank start
		  dw	  00616h  ;v blank end
		  dw	  0e317h  ;turn on byte mode
CRT_PARM_LENGTH equ	(($-CRTParms)/2)

_TEXT	ends
_DATA	segment word public 'DATA'
; Index/data pairs for CRT Controller registers that differ between
; mode 13h and mode X.
_d@	label	byte
_DATA	ends
_BSS	segment word public 'BSS'
_b@	label	byte
_BSS	ends


public _graphinit
public _ViewPage,_WritePage
public ScreenWidth,PageSize,PageTable


_TEXT	SEGMENT byte public 'CODE'
	ASSUME cs:_TEXT

;==============================================================================
; void graphinit(int width,int psize,int mask)
;   width - width of the screen in bytes/plane (defaults to 320/4 if 0)
;   psize - size of each page in bytes/plane (defaults to 320*200/4 if 0)
;==============================================================================
PBEGIN	 _graphinit
	 push bp
	 mov bp,sp
	 push si
	 push di
	 push ds

	 mov ax,cs
	 mov ds,ax
	 mov es,ax

	 mov bx,320/4
	 mov cs:[ScreenWidth],bx
	 mov bx,320/4*240
	mov cs:[PageSize],bx

	; Set up the page table

	xor ax,ax
	mov bx,cs:[PageSize]
	lea di,cs:[PageTable]
	mov cx,4
init4:
	stosw
	add ax,bx
	loop init4


; Now set up the graphics mode

	mov ax,13h
	int 10h ; Set 320x200x256

	mov	dx,3d4h
	mov	al,13h
	out	dx,al
	inc	dx
	mov	al,2dh		;Set bytes per line at 90
	out	dx,al

	mov	dx,3c4h
	mov	al,4
	out	dx,al
	inc	dx
	in	al,dx
	and	al,0f7h		;Turn off chain4 mode
	out	dx,al

	mov	dx,3d4h
	mov	al,17h
	out	dx,al
	inc	dx
	in	al,dx
	or	al,40h		;Turn on byte mode
	out	dx,al

	mov	dx,3d4h
	mov	al,14h
	out	dx,al
	inc	dx
	in	al,dx
	and	al,0bfh		;Turn off double-word mode
	out	dx,al

	mov	dx,3dah
	in	al,dx
	mov	dx,3c0h
	mov	al,30h		;Select Attribute index 10h
	out	dx,al
	mov	al,71h		;Turn on PCS, PPC and G/A
	out	dx,al

	mov ax,SCREEN_SEG ;now clear all display memory, 8 pixels
	mov es,ax	      ; at a time
	sub di,di	;point ES:DI to display memory
	sub ax,ax	;clear to zero-value pixels
	mov cx,8000h ;# of words in display memory
	rep stosw	;clear all of display memory

	pop ds
	pop di	;restore C register vars
	pop si
	pop bp	;restore caller's stack frame
	ret
_graphinit endp

;==============================================================================
; void SetVGAmode(void);
;==============================================================================
PBEGIN	_SetVGAmode
	push	bp
	mov	ax,13h
	int	10h		; Set 320x200x256
	pop	bp
	ret
_SetVGAmode endp

;==============================================================================
; void usepage(int page)
;==============================================================================
UPnum	equ	[bp+ABASE]

PBEGIN	_usepage
	push bp
	mov bp,sp
	mov bx,UPnum
	and bx,3
	shl bx,1
	mov ax,cs:PageTable[bx]
	mov cs:[_WritePage],ax
	pop bp
	ret
_usepage endp

;==============================================================================
; void flippage()
;
; NOTE: Some lines have been commented out for test purposes. If you experience
;	flicker when the page flips, un-comment the lines waiting for vertical
;	retrace.
;==============================================================================
PBEGIN	_flippage
	mov cx,cs:[_ViewPage]
	xchg cx,cs:[_WritePage]
	mov cs:[_ViewPage],cx ; An ISR will set the VGA at the next retrace

	mov  bh,0dH
	mov  bl,cl
	mov dx,3dah

Ret1:
;;;;	in al,dx
;;;;	test al,1
;;;;	jnz Ret1 ; Wait for display enable



;	cli
	mov al,0ch
	mov ah,ch
	mov dx,3d4h
	out dx,ax	; set the displayed offset (high)
;	inc al
;	mov ah,cl
;;;	mov ax,bx
;;;	out dx,ax	; set the displayed offset (low)
;	sti

;;	mov dx,3dah
Ret2:
;;	in al,dx
;;	test al,8
;;	jz Ret2 ; Wait for the video card to use the address just set

	ret
_flippage endp


;==============================================================================
; void SetPalette(unsigned char far *PalBuf);
;==============================================================================
SPbuf	equ	[bp+ABASE]

PBEGIN	_SetPalette
	push	bp
	mov	bp,sp
	push	ds
	push	si

	lds	si,dword ptr SPbuf
	mov	cx,256
	xor	bx,bx
	cld
	mov	dx,3C8H
sp010:
	mov	al,bl
	out	dx,al
	inc	dx
	lodsb
	out	dx,al
	lodsb
	out	dx,al
	lodsb
	out	dx,al
	dec	dx
	inc	bx
	loop	sp010

	pop	si
	pop	ds
	pop	bp
	ret
_SetPalette endp

;==============================================================================
; void DrawWalls(void);
;==============================================================================
Y2	equ	[bp-2]
AVLOW	equ	[bp-4]
AVHI	equ	[bp-6]
VCOL	equ	[bp-8]
BOFFLOW equ	[bp-10]
BOFFHI	equ	[bp-12]
sColor	equ	[bp-14]
fColor	equ	[bp-16]

PBEGIN	_DrawWalls
	push	bp
	mov	bp,sp
	sub	sp,18
	push	ds
	push	si
	push	di

	mov	dx,3c4H
	mov	ax,0F02H
	out	dx,ax			;Select map mask and all planes

	mov	bx,word ptr DGROUP:_MaxDistance ; Get max distance to wall
	shl	bx,1			; Make a word index
	mov	ax,word ptr DGROUP:_DistanceTable[bx] ;Look up actual height
	cmp	ax,200			; Full screen height?
	jae	drw000			; Yes, nothing to fill in

	and	ax,0FFFEH		; Strip any odd values
	mov	bx,ax			; Save height
	mov	si,ax			; Again save height
	shr	bx,1			; Get Height / 2
	mov	dx,100			; Pick up our center row
	sub	dx,bx			; and sub ht/2
	mov	bx,dx			; Save new value

	mov	ax,90
	imul	dx
	mov	bx,ax
	mov	ax,90
	imul	si
	mov	si,ax

;	mov	cl,6			; Get ready to mult by 80
;	shl	bx,cl			; first mult by 64
;	shl	si,cl			; also mult the original ht
;	mov	cl,4			; No do a mult by 16
;	shl	dx,cl			; to top layer
;	shl	ax,cl			; and initial height
;	add	bx,dx			; Add x64 and x16 for x80
;	add	si,ax			; do same with height

	mov	ax,0A000H
	mov	es,ax
	mov	di,cs:_WritePage	;get start of buffer

	mov	ax,ds:_TopColor
	mov	ah,al
	shr	bx,1			;only fill words
	mov	cx,bx			;get amount to fill in
  rep	stosw
	add	di,si			;then skip amount of wall
	mov	ax,ds:_BottomColor
	mov	ah,al
	mov	cx,bx
  rep	stosw


drw000:
	xor	bx,bx			;Initial loop/plane counter

drw010:
	push	bx			;save loop/plane counter
	mov	VCOL,bx			;Set beginning video column
	mov	dx,3c5H
	mov	al,byte ptr DGROUP:_lowmask[bx]
	out	dx,al			;select mask to write to
	mov	si,offset _Walls	; Point to wall array
	shl	bx,1
	shl	bx,1
	shl	bx,1			;x 8 for correct wall structure
	add	si,bx

	mov	cx,80			;Number of walls to display
drw020:
	push	ds			;save data segment
	push	si			;save wall pointer
	push	cx			;save wall count
	lodsw				;Get bitmap number
	xchg	ax,bx			;use bx as the index
	shl	bx,1
	shl	bx,1			;dword index into bitmap table
	mov	di,word ptr DGROUP:_bMaps[bx]	    ;offset to bitmap
	mov	es,word ptr DGROUP:_bMaps[bx+2]	    ;segment of bitmap
	lodsw				;get bitmap column to display
	mov	cl,6
	shl	ax,cl			;x64 to get correct row
	add	di,ax			;and add to bitmap offset
	lodsw				;get bitmap distance
	shl	ax,1			; Make word index for table lookup
	mov	si,ax			;save distance
	mov	bx,word ptr DGROUP:_DistanceTable[si]  ;get height of bitmap

	mov	cx,100
	mov	ax,bx			; Pick up the height
	shr	ax,1			; Divide by two
	sub	cx,ax			; And subtract from center for Y1

	mov	ax,cx			; Start with Y1
	add	ax,bx			; and add height to it for Y2

	cmp	ax,199			; Don't let Y2 go beyond screen height
	jle	short drw030
	mov	ax,199			; Else force it to screen height
drw030:
	mov	Y2,ax			;save y2
	shl	si,1			; Turn object distance into dword index

	mov	ax,word ptr DGROUP:_AdjustTable[si+2]
	mov	dx,word ptr DGROUP:_AdjustTable[si]

	mov	AVHI,ax			; Get (64 * 65536) / height
	mov	AVLOW,dx
	xchg	si,di			;si now points to bitmap
	mov	di,VCOL			; Video column to display at
	sar	di,1			; Divide by 4 to get correct planar col
	sar	di,1			; Divide by 4 to get correct planar col
	add	di,word ptr cs:_WritePage ; Add in page offset
	mov	ax,es			; Pick up segment to bitmap
	mov	ds,ax			; now ds:si points to bitmap
	mov	ax,0A000H		; Video segment
	mov	es,ax			; now es:di points to screen
	mov	word ptr BOFFLOW,0	; Initialize the current bitmap offset
	mov	word ptr BOFFHI,0

	cmp	cx,word ptr Y2		; is Y1 > Y2?
	jge	short drw090		; Yes, get on out
	or	cx,cx			; Y1 <= 0?
	jle	drw050			; Yes, no sky color needed

;	mov	ax,cx			; Get copy of Y1
;	mov	bx,cx			; and another copy
;	mov	cl,6
;	shl	ax,cl			; do a mult 64
;	add	di,ax			; and add to video offset
;	mov	cl,4
;	mov	ax,bx			; get original Y1
;	shl	ax,cl			; do a mult 16
;	add	di,ax			; and add to video for a mult 80
;	xchg	cx,bx			; restore original Y1

	mov	ax,90
	imul	cx
	add	di,ax

drw050:
	mov	bx,89			; Offset to next video row
	mov	dx,AVLOW		; Hold onto lsb of adjustment
	mov	ax,AVHI			; Pick up msb of bitmap adjustment

;------------------------------------------------------------------------------
; Here is where the actual bitmap is transferred to the screen. Better
; optimization could be done by precalculating the adjustment factor instead
; of looping until Y1 >= 0. This has not been tried so I don't know if the
; speed would make it worth it....
;------------------------------------------------------------------------------
drw060:
	or	cx,cx			; Is Y1 still < 0 ?
	jl	short drw070		; Yes, don't start drawing yet

	movsb				;Move bitmap to video
	dec	si
	add	di,bx			;next row of video

drw070:
	add	word ptr BOFFLOW,dx	; Add lsb to current offset
	adc	si,ax			; Use msb to get next bitmap location
	inc	cx			; Next y1
	cmp	cx,word ptr Y2		; Beyond Y2 yet?
	jl	short drw060		; Nope, keep looping

drw090:
	pop	cx			;get wall count
	pop	si			;recover structure pointer
	pop	ds
	add	si,32			;Wall structure size * 4
	add	word ptr VCOL,4		;next video column to display at
	dec	cx
	jz	drw100
	jmp	drw020			;next wall to display

drw100:
	pop	bx			;recover loop/plane counter
	inc	bx
	cmp	bx,4			;all 4 planes?
	je	drwDone
	jmp	drw010

drwDone:
	mov	dx,3c5H
	mov	al,0FFH			;Enable all planes again
	out	dx,al
	pop	di
	pop	si
	pop	ds
	mov	sp,bp
	pop	bp
	ret
_DrawWalls endp

;==============================================================================
;			  +4	     +6		+8	    +10	       +12
;  void DrawOneObject(int ObjNum,int ObjCol,int ObjDist,int VidCol,int PageNum);
;==============================================================================
PBEGIN	_DrawOneObject
	push	bp
	mov	bp,sp
	sub	sp,20
	push	si
	push	di
	push	ds

	mov	cx,word ptr [bp+10]	; Video column
	and	cx,3			; Only need lower three bits
	mov	ah,11h
	rol	ah,cl
	mov	al,2
	mov	dx,3c4h
	out	dx,ax

	mov	bx,word ptr [bp+8]	; Get the distance to the bitmap
	shl	bx,1			; Make word index for table lookup
	mov	si,bx			; Save index for now
	mov	bx,word ptr DGROUP:_DistanceTable[bx]

dob010:
	mov	cx,100
	mov	ax,bx		    ; Pick up the height
	shr	ax,1		    ; Divide by two
	sub	cx,ax		    ; And subtract from center for Y1

	mov	di,cx		    ; Start with Y1
	add	di,bx		    ; and add height to it for Y2

	cmp	di,199		    ; Don't let Y2 go beyond screen height
	jle	short dob020
	mov	di,199		    ; Else force it to screen height

dob020:
	mov	word ptr [bp-2],di  ;save y2
	shl	si,1		    ; Turn object distance into dword index

	mov	ax,word ptr DGROUP:_AdjustTable[si+2]
	mov	dx,word ptr DGROUP:_AdjustTable[si]

	mov	word ptr [bp-14],ax	; Get (64 * 65536) / height
	mov	word ptr [bp-16],dx

	mov	bx,word ptr [bp+4]	; Bitmap number to display
	shl	bx,1			; dword index
	shl	bx,1			; dword index
	mov	ax,word ptr DGROUP:_oMaps[bx+2]
	mov	dx,word ptr DGROUP:_oMaps[bx]

	mov	bx,word ptr [bp+6]	; Bitmap column to display
	shl	bx,1
	shl	bx,1
	shl	bx,1
	shl	bx,1
	shl	bx,1
	shl	bx,1			; x 64 to get correct bitmap row
	add	dx,bx			; then add to start of bitmap
	mov	word ptr [bp-6],ax
	mov	word ptr [bp-8],dx

	mov	di,word ptr [bp+10]	; Video column to display at
	sar	di,1			; Divide by 4 to get correct planar col
	sar	di,1			; Divide by 4 to get correct planar col
	mov	bx,word ptr [bp+12]	; Page number to display at
	shl	bx,1			; Make a word index
	add	di,word ptr DGROUP:_PageBegin[bx]   ;Pick up actual offset
	mov	ax,0A000H		; Video segment
	mov	es,ax
	mov	word ptr [bp-18],0	; Initialize the current bitmap offset
	mov	word ptr [bp-20],0

	or	cx,cx			; Is Y1 > 0 ?
	jle	short dob030		; Nope, start above video

;	xchg	si,cx			; hold onto original value
;	mov	ax,si			; get Y1 coordinate
;	mov	cl,6			; mult by 64
;	shl	ax,cl
;	mov	dx,si			; get back Y1
;	mov	cl,4
;	shl	dx,cl			; mult by 16
;	add	ax,dx			; now add to Y1 * 64 to get Y1 * 80
;	add	di,ax			; and add to start of video
;	xchg	cx,si			; get back original Y1

	mov	ax,90
	imul	cx
	add	di,ax

dob030:
	cmp	cx,word ptr [bp-2]	; is Y1 > Y2?
	jge	short dob090		; Yes, get on out

	mov	bx,90			;Amount to next row of video
	lds	si,dword ptr [bp-8]	;Get image buffer
	mov	dx,word ptr [bp-16]	; Hold onto lsb of adjustment

dob040:
	or	cx,cx			; Is Y1 still < 0 ?
	jl	short dob080		; Yes, don't start drawing yet

	lodsb
	dec	si
	or	al,al			;Transparent color?
	jz	dob050			;Yes, don't write it to video
	mov	byte ptr es:[di],al	;place character in video

dob050:
	add	di,bx			;next row of video

dob080:
	mov	ax,word ptr [bp-14]	; Pick up msb of bitmap adjustment
	add	word ptr [bp-20],dx	;   and add to current offset
	adc	si,ax			; Keep the msb correct
	inc	cx			; Next y1
	cmp	cx,word ptr [bp-2]	; Beyond Y2 yet?
	jl	short dob040		; Nope, keep looping

dob090:
	pop	ds
	pop	di
	pop	si
	mov	sp,bp
	pop	bp
	ret
_DrawOneObject	  endp


_TEXT	ENDS
	END

