;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;
;   Software renderer
;   Copyright (C) 2006  Darragh Coy.
;
;   This program is free software: you can redistribute it and/or modify
;   it under the terms of the GNU General Public License as published by
;   the Free Software Foundation, either version 3 of the License, or
;   (at your option) any later version.
;
;   This program is distributed in the hope that it will be useful,
;   but WITHOUT ANY WARRANTY; without even the implied warranty of
;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;   GNU General Public License for more details.
;
;   You should have received a copy of the GNU General Public License
;   along with this program.  If not, see <http://www.gnu.org/licenses/>.
;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; 
; Purpose: Assembly scanline function. Writes one scanline to the screen. This code is FAST, much faster (almost 2x) than
;	   than the prior c compiler generated code. The main reason for this is its leverage of the SIMD floating point instructions 
;	   offered by pentium III and greater processors- the compiler usually never spots where to use such instructions.
;	   This code was written in consultation using the intel manuals and other sources on SSE instructions.
;	   It still could be made faster by writing four pixels at once to the screenbuffer and by using the fast
;	   non temporal writes offered by the SSE instruction set...
;
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.686		; enable pentium pro instructions, pentium pro, pentium II and later
.xmm		; enable mmx and sse instructions
.model flat	; flat memory model, no segmentation

.data
.code

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; ScanLine function:
;
; Params on stack (on entry to function): 
;
; sp + 4 :	pointer to color buffer at scanline start
; sp + 8 :	pointer to depth buffer at scanline start
; sp + 12:	number of scanline pixels
; sp + 16:	starting z value
; sp + 20:	starting r value
; sp + 24:	starting g value
; sp + 28:	starting b value
; sp + 32:	z increment for each pixel
; sp + 36:	r increment for each pixel
; sp + 40:	g increment for each pixel
; sp + 44:	b increment for each pixel
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

_R_ScanLineASM proc

	;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	; Save all registers
	;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	pushad	; first param on stack now at sp + 36

	;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	; Load parameters
	;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	mov edi , [ esp + 36 ]  	; color buffer pointer at scanline start
	mov esi , [ esp + 40 ]  	; depth buffer pointer at scanline start
	mov ecx , [ esp + 44 ]  	; number of scanline pixels

	movups xmm0 , [esp + 48]  	; load four floats for z,r,g,b values     into sse register 1
	movups xmm1 , [esp + 64]  	; load four floats for z,r,g,b increments (after every pixel) into sse register 2

	;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	; Make sure number of scanlines not negative! : if so then jump to the end
	;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
	cmp  ecx , 0
	jle  scan_end
	
	;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	; Scanline loop
	;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	scan_start:

		;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		; Load current z-value and z-value in depth buffer and check if current z is less
		;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

		movss   xmm2 , dword ptr [esi]	; move current z buffer value into the low double word of the xmm2 register
		comiss  xmm0 , xmm2				; compare z buffer value with current z-value: commis compares two low double words and sets eflags register

		jnc depth_fail					; if current z value greater than z buffer value then depth test fail: do not write the pixel
		
		movss   dword ptr [esi], xmm0 	; otherwise save the depth buffer value and write the pixel

		;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		; Convert the floating point r,g,b values so that they are in integer format and combine into one 32-bit int
		; for writing to the screen.
		; The shufps moves the doublewords from the source xmm register to the desination register.
		; The immediate value in the instruction tells which double words go into which part of the destination.
		; The first two bits of the immediate operand tell which double word goes into the the first double word of the destination,
		; The second two bits tell which dw goes into the second dw of the destination and so on...
		;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

		shufps	  xmm0 , xmm0 , 10010011b	; shuffle double words in xmm0 right by one dw so that first dw is b component
		cvtss2si  eax  , xmm0				; convert the b component into an integer and move to eax

		shufps	  xmm0 , xmm0 , 10010011b	; shuffle double words in xmm0 right by one dw so that first dw is g component
		cvtss2si  ebx  , xmm0				; convert the g component into an integer and move to ebx
		sal	  ebx  , 8						; shift g component left 8 bits so that it is in correct place for color integer
		or	  eax  , ebx					; combine g component with b component

		shufps	  xmm0 , xmm0 , 10010011b	; shuffle double words in xmm0 right by one dw so that first dw is r component
		cvtss2si  ebx  , xmm0				; convert the r component into an integer and move to ebx
		sal	  ebx  , 16						; shift r component left 16 bits so that it is in correct place for color integer
		or	  eax  , ebx					; combine r component with b and g component

		shufps	  xmm0 , xmm0 , 10010011b	; shuffle double words in xmm0 right by one dw so that first dw is z component
	
		mov [edi] , eax 					; write rgb pixel in eax to screen buffer
		
		;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		; Increment z,r,g,b values and move pointers on
		;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

		depth_fail:

		addps 	xmm0 , xmm1 ; increment the z,r,g,b values by amounts in xmm1 register
		add	edi	 , 4		; move on screen buffer pointer
		add	esi	 , 4		; move on depth buffer pointer

	loop scan_start

	;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~	

	scan_end:

	popad	; restore all modified registers back to previous state
	ret	; return from the function

_R_ScanLineASM endp

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


end
