CnC_Red_Alert/WWFLAT32/MCGAPRIM/DRAWLINE.ASM

431 lines
13 KiB
NASM
Raw Normal View History

;
; Command & Conquer Red Alert(tm)
; Copyright 2025 Electronic Arts Inc.
;
; 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/>.
;
;***************************************************************************
;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S **
;***************************************************************************
;* *
;* Project Name : Westwood 32 bit Library *
;* *
;* File Name : DRAWLINE.ASM *
;* *
;* Programmer : Phil W. Gorrow *
;* *
;* Start Date : June 16, 1994 *
;* *
;* Last Update : August 30, 1994 [IML] *
;* *
;*-------------------------------------------------------------------------*
;* Functions: *
;* VVC::Scale -- Scales a virtual viewport to another virtual viewport *
;* Normal_Draw -- jump loc for drawing scaled line of normal pixel *
;* __DRAW_LINE -- Assembly routine to draw a line *
;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *
IDEAL
P386
MODEL USE32 FLAT
INCLUDE "mcgaprim.inc"
INCLUDE ".\gbuffer.inc"
CODESEG
;***************************************************************************
;* VVC::DRAW_LINE -- Scales a virtual viewport to another virtual viewport *
;* *
;* INPUT: WORD sx_pixel - the starting x pixel position *
;* WORD sy_pixel - the starting y pixel position *
;* WORD dx_pixel - the destination x pixel position *
;* WORD dy_pixel - the destination y pixel position *
;* WORD color - the color of the line to draw *
;* *
;* Bounds Checking: Compares sx_pixel, sy_pixel, dx_pixel and dy_pixel *
;* with the graphic viewport it has been assigned to. *
;* *
;* HISTORY: *
;* 06/16/1994 PWG : Created. *
;* 08/30/1994 IML : Fixed clipping bug. *
;*=========================================================================*
PROC MCGA_Draw_Line C NEAR
USES eax,ebx,ecx,edx,esi,edi
;*==================================================================
;* Define the arguements that the function takes.
;*==================================================================
ARG this:DWORD ; associated graphic view port
ARG x1_pixel:DWORD ; the start x pixel position
ARG y1_pixel:DWORD ; the start y pixel position
ARG x2_pixel:DWORD ; the dest x pixel position
ARG y2_pixel:DWORD ; the dest y pixel position
ARG color:DWORD ; the color we are drawing
;*==================================================================
;* Define the local variables that we will use on the stack
;*==================================================================
LOCAL clip_min_x:DWORD
LOCAL clip_max_x:DWORD
LOCAL clip_min_y:DWORD
LOCAL clip_max_y:DWORD
LOCAL clip_var:DWORD
LOCAL accum:DWORD
LOCAL bpr:DWORD
;*==================================================================
;* Take care of find the clip minimum and maximums
;*==================================================================
mov ebx,[this]
xor eax,eax
mov [clip_min_x],eax
mov [clip_min_y],eax
mov eax,[(GraphicViewPort ebx).GVPWidth]
mov [clip_max_x],eax
add eax,[(GraphicViewPort ebx).GVPXAdd]
mov [bpr],eax
mov eax,[(GraphicViewPort ebx).GVPHeight]
mov [clip_max_y],eax
;*==================================================================
;* Adjust max pixels as they are tested inclusively.
;*==================================================================
dec [clip_max_x]
dec [clip_max_y]
;*==================================================================
;* Set the registers with the data for drawing the line
;*==================================================================
mov eax,[x1_pixel] ; eax = start x pixel position
mov ebx,[y1_pixel] ; ebx = start y pixel position
mov ecx,[x2_pixel] ; ecx = dest x pixel position
mov edx,[y2_pixel] ; edx = dest y pixel position
;*==================================================================
;* This is the section that "pushes" the line into bounds.
;* I have marked the section with PORTABLE start and end to signify
;* how much of this routine is 100% portable between graphics modes.
;* It was just as easy to have variables as it would be for constants
;* so the global vars ClipMaxX,ClipMinY,ClipMaxX,ClipMinY are used
;* to clip the line (default is the screen)
;* PORTABLE start
;*==================================================================
cmp eax,[clip_min_x]
jl short ??clip_it
cmp eax,[clip_max_x]
jg short ??clip_it
cmp ebx,[clip_min_y]
jl short ??clip_it
cmp ebx,[clip_max_y]
jg short ??clip_it
cmp ecx,[clip_min_x]
jl short ??clip_it
cmp ecx,[clip_max_x]
jg short ??clip_it
cmp edx,[clip_min_y]
jl short ??clip_it
cmp edx,[clip_max_y]
jle short ??on_screen
;*==================================================================
;* Takes care off clipping the line.
;*==================================================================
??clip_it:
call NEAR PTR ??set_bits
xchg eax,ecx
xchg ebx,edx
mov edi,esi
call NEAR PTR ??set_bits
mov [clip_var],edi
or [clip_var],esi
jz short ??on_screen
test edi,esi
jne short ??off_screen
shl esi,2
call [DWORD PTR cs:??clip_tbl+esi]
jc ??clip_it
xchg eax,ecx
xchg ebx,edx
shl edi,2
call [DWORD PTR cs:??clip_tbl+edi]
jmp ??clip_it
??on_screen:
jmp ??draw_it
??off_screen:
jmp ??out
;*==================================================================
;* Jump table for clipping conditions
;*==================================================================
??clip_tbl DD ??nada,??a_up,??a_dwn,??nada
DD ??a_lft,??a_lft,??a_dwn,??nada
DD ??a_rgt,??a_up,??a_rgt,??nada
DD ??nada,??nada,??nada,??nada
??nada:
clc
retn
??a_up:
mov esi,[clip_min_y]
call NEAR PTR ??clip_vert
stc
retn
??a_dwn:
mov esi,[clip_max_y]
neg esi
neg ebx
neg edx
call NEAR PTR ??clip_vert
neg ebx
neg edx
stc
retn
;*==================================================================
;* xa'=xa+[(miny-ya)(xb-xa)/(yb-ya)]
;*==================================================================
??clip_vert:
push edx
push eax
mov [clip_var],edx ; clip_var = yb
sub [clip_var],ebx ; clip_var = (yb-ya)
neg eax ; eax=-xa
add eax,ecx ; (ebx-xa)
mov edx,esi ; edx=miny
sub edx,ebx ; edx=(miny-ya)
imul edx
idiv [clip_var]
pop edx
add eax,edx
pop edx
mov ebx,esi
retn
??a_lft:
mov esi,[clip_min_x]
call NEAR PTR ??clip_horiz
stc
retn
??a_rgt:
mov esi,[clip_max_x]
neg eax
neg ecx
neg esi
call NEAR PTR ??clip_horiz
neg eax
neg ecx
stc
retn
;*==================================================================
;* ya'=ya+[(minx-xa)(yb-ya)/(xb-xa)]
;*==================================================================
??clip_horiz:
push edx
mov [clip_var],ecx ; clip_var = xb
sub [clip_var],eax ; clip_var = (xb-xa)
sub edx,ebx ; edx = (yb-ya)
neg eax ; eax = -xa
add eax,esi ; eax = (minx-xa)
imul edx ; eax = (minx-xa)(yb-ya)
idiv [clip_var] ; eax = (minx-xa)(yb-ya)/(xb-xa)
add ebx,eax ; ebx = xa+[(minx-xa)(yb-ya)/(xb-xa)]
pop edx
mov eax,esi
retn
;*==================================================================
;* Sets the condition bits
;*==================================================================
??set_bits:
xor esi,esi
cmp ebx,[clip_min_y] ; if y >= top its not up
jge short ??a_not_up
or esi,1
??a_not_up:
cmp ebx,[clip_max_y] ; if y <= bottom its not down
jle short ??a_not_down
or esi,2
??a_not_down:
cmp eax,[clip_min_x] ; if x >= left its not left
jge short ??a_not_left
or esi,4
??a_not_left:
cmp eax,[clip_max_x] ; if x <= right its not right
jle short ??a_not_right
or esi,8
??a_not_right:
retn
;*==================================================================
;* Draw the line to the screen.
;* PORTABLE end
;*==================================================================
??draw_it:
sub edx,ebx ; see if line is being draw down
jnz short ??not_hline ; if not then its not a hline
jmp short ??hline ; do special case h line
??not_hline:
jg short ??down ; if so there is no need to rev it
neg edx ; negate for actual pixel length
xchg eax,ecx ; swap x's to rev line draw
sub ebx,edx ; get old edx
??down:
push edx
push eax
mov eax,[bpr]
mul ebx
mov ebx,eax
mov eax,[this]
add ebx,[(GraphicViewPort eax).GVPOffset]
pop eax
pop edx
mov esi,1 ; assume a right mover
sub ecx,eax ; see if line is right
jnz short ??not_vline ; see if its a vertical line
jmp ??vline
??not_vline:
jg short ??right ; if so, the difference = length
??left:
neg ecx ; else negate for actual pixel length
neg esi ; negate counter to move left
??right:
cmp ecx,edx ; is it a horiz or vert line
jge short ??horiz ; if ecx > edx then |x|>|y| or horiz
??vert:
xchg ecx,edx ; make ecx greater and edx lesser
mov edi,ecx ; set greater
mov [accum],ecx ; set accumulator to 1/2 greater
shr [accum],1
;*==================================================================
;* at this point ...
;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater;
;* esi=adder; accum=accumulator
;* in a vertical loop the adder is conditional and the inc constant
;*==================================================================
??vert_loop:
add ebx,eax
mov eax,[color]
??v_midloop:
mov [ebx],al
dec ecx
jl short ??out
add ebx,[bpr]
sub [accum],edx ; sub the lesser
jge ??v_midloop ; any line could be new
add [accum],edi ; add greater for new accum
add ebx,esi ; next pixel over
jmp ??v_midloop
??horiz:
mov edi,ecx ; set greater
mov [accum],ecx ; set accumulator to 1/2 greater
shr [accum],1
;*==================================================================
;* at this point ...
;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater;
;* esi=adder; accum=accumulator
;* in a vertical loop the adder is conditional and the inc constant
;*==================================================================
??horiz_loop:
add ebx,eax
mov eax,[color]
??h_midloop:
mov [ebx],al
dec ecx ; dec counter
jl short ??out ; end of line
add ebx,esi
sub [accum],edx ; sub the lesser
jge ??h_midloop
add [accum],edi ; add greater for new accum
add ebx,[bpr] ; goto next line
jmp ??h_midloop
;*==================================================================
;* Special case routine for horizontal line draws
;*==================================================================
??hline:
cmp eax,ecx ; make eax < ecx
jl short ??hl_ac
xchg eax,ecx
??hl_ac:
sub ecx,eax ; get len
inc ecx
push edx
push eax
mov eax,[bpr]
mul ebx
mov ebx,eax
mov eax,[this]
add ebx,[(GraphicViewPort eax).GVPOffset]
pop eax
pop edx
add ebx,eax
mov edi,ebx
mov eax,[color]
mov ah,al ; make it a word of color
shr ecx,1 ; convert to number of words to write
rep stosw ; write as many words as possible
adc ecx,ecx ; add the carry flag back in
rep stosb ; move odd one if any are odd
jmp short ??out ; get outt
;*==================================================================
;* a special case routine for vertical line draws
;*==================================================================
??vline:
mov ecx,edx ; get length of line to draw
inc ecx
add ebx,eax
mov eax,[color]
??vl_loop:
mov [ebx],al ; store bit
add ebx,[bpr]
dec ecx
jnz ??vl_loop
??out:
ret
ENDP MCGA_Draw_Line
END