Files
2026-07-14 19:31:07 +02:00

661 lines
13 KiB
NASM

; Hovertank 3-D Source Code
; Copyright (C) 1993-2014 Flat Rock Software
;
; 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 2 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, write to the Free Software Foundation, Inc.,
; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
;============================================================================
;
; DELTA ENGINE CODE
;
; Call DE_ClearOld to initialize tables
;
; Repeat:
;
; Call DE_NewFrame to ready the next frame
; Call DE_AddLine to add line segments to the new frame
; Call DE_StraightDraw or DE_DeltaDraw to draw the marked lines
;
;============================================================================
IDEAL
MODEL SMALL,C
;============================================================================
;
; EGA Graphic routines
;
;============================================================================
SC_INDEX = 03C4h
SC_RESET = 0
SC_CLOCK = 1
SC_MAPMASK = 2
SC_CHARMAP = 3
SC_MEMMODE = 4
CRTC_INDEX = 03D4h
CRTC_H_TOTAL = 0
CRTC_H_DISPEND = 1
CRTC_H_BLANK = 2
CRTC_H_ENDBLANK = 3
CRTC_H_RETRACE = 4
CRTC_H_ENDRETRACE = 5
CRTC_V_TOTAL = 6
CRTC_OVERFLOW = 7
CRTC_ROWSCAN = 8
CRTC_MAXSCANLINE = 9
CRTC_CURSORSTART = 10
CRTC_CURSOREND = 11
CRTC_STARTHIGH = 12
CRTC_STARTLOW = 13
CRTC_CURSORHIGH = 14
CRTC_CURSORLOW = 15
CRTC_V_RETRACE = 16
CRTC_V_ENDRETRACE = 17
CRTC_V_DISPEND = 18
CRTC_OFFSET = 19
CRTC_UNDERLINE = 20
CRTC_V_BLANK = 21
CRTC_V_ENDBLANK = 22
CRTC_MODE = 23
CRTC_LINECOMPARE = 24
GC_INDEX = 03CEh
GC_SETRESET = 0
GC_ENABLESETRESET = 1
GC_COLORCOMPARE = 2
GC_DATAROTATE = 3
GC_READMAP = 4
GC_MODE = 5
GC_MISCELLANEOUS = 6
GC_COLORDONTCARE = 7
GC_BITMASK = 8
ATR_INDEX = 03c0h
ATR_MODE = 16
ATR_OVERSCAN = 17
ATR_COLORPLANEENABLE = 18
ATR_PELPAN = 19
ATR_COLORSELECT = 20
;==================
;
; View area equates
;
;==================
VIEWX = (4*8)
VIEWY = (4*8)
VIEWWIDTH = (32*8)
VIEWHEIGHT = (12*8)
VIEWXH = (VIEWX+VIEWWIDTH-1)
VIEWYH = (VIEWY+VIEWHEIGHT-1)
;=======================
;
; The delta tables hold linked line segments to be drawn
;
;=======================
FARDATA
MAXSEGS = 2000
SG_START = 0
SG_END = 2
SG_COLOR = 4
SG_NEXT = 6
SEGSIZE = 8
deltatable1 db SEGSIZE*MAXSEGS dup (?)
deltatable2 db SEGSIZE*MAXSEGS dup (?)
deltatable3 db SEGSIZE*MAXSEGS dup (?)
startline dw 0,VIEWX-1,99,redrawline
endline dw VIEWXH+1,9999,99,0
blankline dw VIEWX,VIEWXH,0,endline
redrawline dw VIEWX,VIEWXH,-1,endline
;==========================
;
; dataseg
;
;==========================
DATASEG
freesegment dw ? ; pointer to next free spot in deltatable?
freestarttable dw deltatable1,deltatable2,deltatable3
linestart1 dw VIEWHEIGHT dup(?)
linestart2 dw VIEWHEIGHT dup(?)
linestart3 dw VIEWHEIGHT dup(?)
linestartptr dw ? ; pointer to linestart[tableon]
linestarttable dw linestart1,linestart2,linestart3
tableon dw ?
PUBLIC tableon,linestart1,linestart2,linestart3,linestartptr,freesegment
;
; parameters for AddLine
;
al_xl dw ?
al_xh dw ?
al_y dw ?
al_color dw ?
PUBLIC al_xl,al_xh,al_y,al_color
CODESEG
;====================
;
; DE_AddLine
;
; Adds a new segment to the current line list
; Takes globall variables al_??? as parameters
;
; AX = scratch
; BX = previous segment ptr
; CX = al_xl
; DX = al_xh
; SI = start segment ptr
; DI = new segment ptr
; BP = end segment ptr
;
;====================
PROC DE_AddLine
PUBLIC DE_AddLine
USES si,di,bp
mov ax,@FARDATA
mov es,ax ; es points to tables
mov cx,[al_xl]
mov dx,[al_xh]
;
; find the first segment on line al_y that contains the new line
;
mov bx,[al_y]
sub bx,VIEWY
shl bx,1
add bx,[linestartptr]
mov bx,[bx] ; bx = prev segment
mov si,[es:bx+SG_NEXT] ; si = start pointer
@@findfirst:
cmp cx,[es:si+SG_END]
jle @@gotfirst
mov bx,si ; pre = start
mov si,[es:si+SG_NEXT] ; start = start->next
jmp @@findfirst
@@gotfirst:
mov di,[freesegment] ; new = freesegment++
add [freesegment],SEGSIZE
mov [es:di+SG_START],cx ; new->start = xl
mov [es:di+SG_END],dx ; new->end = xh
mov ax,[al_color]
mov [es:di+SG_COLOR],ax ; new->color = color
cmp [es:si+SG_END],dx
jge @@isge
jmp @@notge
;
; the entire new segment fits inside start seg
; start->end >= xh
;
@@isge:
cmp [es:si+SG_COLOR],ax ; ax still holds [al_color]
jne @@different
ret ; same color as old line, do nothing
@@different:
cmp [es:si+SG_START],cx ; if (start->start == xl)
jne @@notstarteq ; cover first part of old segment
mov [es:bx+SG_NEXT],di ; pre->next = new
cmp [es:si+SG_END],dx
jne @@notexact ; cover it exactly?
mov ax,[es:si+SG_NEXT]
mov [es:di+SG_NEXT],ax ; new->next = start->next
ret
@@notexact:
mov [es:di+SG_NEXT],si ; new->next = start
mov ax,dx
inc ax
mov [es:si+SG_START],ax ; start->start = xh+1
ret
@@notstarteq:
cmp [es:si+SG_END],dx ; if (start->end == xh)
jne @@notendeq ; cover last part of old segment
mov ax,[es:si+SG_NEXT]
mov [es:di+SG_NEXT],ax ; new->next = start->next
mov [es:si+SG_NEXT],di ; start->next = new
mov ax,cx
dec ax
mov [es:si+SG_END],ax ; start->end = xl-1
ret
@@notendeq:
mov bp,[freesegment] ; cover the middle of old segment
add [freesegment],SEGSIZE ; end = freesegment++
mov ax,dx
inc ax
mov [es:bp+SG_START],ax ; end->start = xh+1
mov ax,[es:si+SG_END]
mov [es:bp+SG_END],ax ; end->end = start->end
mov ax,[es:si+SG_NEXT]
mov [es:bp+SG_NEXT],ax ; end->next = start->next
mov ax,[es:si+SG_COLOR]
mov [es:bp+SG_COLOR],ax ; end->color = start->color
mov ax,cx
dec ax
mov [es:si+SG_END],ax ; start->end = xl-1
mov [es:si+SG_NEXT],di ; start->next = new
mov [es:di+SG_NEXT],bp ; new->next = end
ret
;
;
; partially cover start seg, and scan ahead to cover more
;
@@notge:
mov bp,[es:si+SG_NEXT] ; end = start->next
@@findend:
cmp [es:bp+SG_END],dx ; while (end->end <= xh_
jg @@endfound
mov bp,[es:bp+SG_NEXT] ; end = end->next
jmp @@findend
@@endfound:
cmp [es:bp+SG_END],dx
je @@mergeend
mov ax,[es:al_color]
cmp [es:bp+SG_COLOR],ax
jne @@splitend
@@mergeend: ; if (end->end == xh || end->color == color)
mov ax,[es:bp+SG_NEXT]
mov [es:di+SG_NEXT],ax ; new->next = end->next
jmp @@enddone
@@splitend: ; else
mov ax,dx
inc ax
mov [es:bp+SG_START],ax ; end->start = xh+1
mov [es:di+SG_NEXT],bp ; new->next = end
@@enddone:
cmp [es:si+SG_START],cx
je @@mergestart
mov ax,[al_color]
cmp [es:si+SG_COLOR],ax
jne @@splitstart
@@mergestart:
mov [es:bx+SG_NEXT],si ; pre->next = new
ret
@@splitstart:
mov ax,cx
dec ax
mov [es:si+SG_END],ax ; start->end = xl-1
mov [es:si+SG_NEXT],di ; start->next = new
ret
ENDP
;=====================
;
; DE_StraightDraw
;
; Draws ALL line segments in current frame, with no delta work
;
;=====================
DATASEG
yline dw ?
startptr dw ?
oldstartptr dw ?
newseg dw ?
CODESEG
EXTRN DrawLine:PROC
PROC DE_StraightDraw
PUBLIC DE_StraightDraw
USES SI,DI
mov dx,GC_INDEX
mov ax,GC_MODE + 2*256
out dx,ax ; set EGA write mode 2
mov ax,@FARDATA
mov es,ax ; es points to tables
mov ax,[linestartptr]
mov [startptr],ax
mov [yline],VIEWY ; for (y=VIEWY;y<=VIEWYH;y++)
@@lineloop:
mov bx,[startptr] ; pointer to first seg on line
add [startptr],2 ; point to next line
mov bx,[bx] ; point to first pointer
mov bx,[es:bx+SG_NEXT] ; first pointer is just junk
@@segmentloop:
push es
push bx ; save it off
push [es:bx+SG_COLOR]
push [yline]
push [es:bx+SG_END]
push [es:bx+SG_START]
call DrawLine ; DrawLine(start,end,y,color)
add sp,8
pop bx ; restore segment pointer
pop es
mov bx,[es:bx+SG_NEXT] ; next pointer
cmp [WORD es:bx+SG_START],VIEWXH
jle @@segmentloop ; while (seg->start <= VIEWXH)
inc [yline]
cmp [yline],VIEWYH
jle @@lineloop
mov dx,GC_INDEX
mov ax,GC_BITMASK+255*256
out dx,ax ; no pixel mask
mov dx,GC_INDEX
mov ax,GC_MODE + 2*256
out dx,ax ; set EGA write mode 0
ret
ENDP
;=====================
;
; DE_DeltaDraw
;
; Draws only the pieces of the line segments needed to change old frame
;
; AX : scratch
; BX : scratch / line color
; CX : start
; DX : end
; SI : current table
; DI : old table
; BP : x
;
;=====================
PROC DE_DeltaDraw
PUBLIC DE_DeltaDraw
USES SI,DI,BP
mov dx,GC_INDEX
mov ax,GC_MODE + 2*256
out dx,ax ; set EGA write mode 2
mov ax,@FARDATA
mov es,ax ; es points to tables
mov si,[tableon] ; figure out old table
inc si
cmp si,3
jl @@gottable
xor si,si
@@gottable:
shl si,1
mov ax,[linestarttable+si]
mov [oldstartptr],ax
mov ax,[linestartptr]
mov [startptr],ax
mov [yline],VIEWY ; for (y=VIEWY;y<=VIEWYH;y++)
;
; delta draw one line
;
@@lineloop:
mov bx,[startptr]
mov si,[bx] ; pointer to first pointer now
add bx,2
mov [startptr],bx
mov si,[es:si+SG_NEXT] ; first pointer is just junk
mov bx,[oldstartptr]
mov di,[bx] ; pointer to first pointer then
add bx,2
mov [oldstartptr],bx
mov di,[es:di+SG_NEXT] ; first pointer is just junk
mov bp,VIEWX ; x = VIEWX
@@segmentloop:
mov ax,[es:si+SG_COLOR]
cmp ax,[es:di+SG_COLOR]
je @@skipsome ; common line color?
;
; draw over some old segments
;
mov bx,[es:si+SG_COLOR]
mov cx,bp ; start = x
@@checkseg:
mov ax,[es:si+SG_END]
cmp ax,[es:di+SG_END] ; both end at same place?
jne @@notsameend
mov dx,ax ; end = newseg->end
mov si,[es:si+SG_NEXT]
mov di,[es:di+SG_NEXT] ; bump both new and old
mov bp,[es:si+SG_START] ; x = newseg->start
jmp @@drawseg
@@notsameend:
jg @@newgreater ; new end before old?
mov dx,ax ; end = newseg->end
mov si,[es:si+SG_NEXT] ; bump si
mov bp,[es:si+SG_START] ; x = newseg->start
jmp @@drawseg
@@newgreater:
mov dx,[es:di+SG_END] ; end = oldseg->end
mov di,[es:di+SG_NEXT] ; bump old
mov bp,[es:di+SG_START] ; x = oldseg->start
cmp bp,VIEWXH
jg @@drawseg ; if at end of line, draw, else
mov ax,[es:si+SG_COLOR] ; if next old is still different
cmp ax,[es:di+SG_COLOR] ; color, keep extending segment
jne @@checkseg
;
; Draw a line segment
;
@@drawseg:
push es ; drawline will save and restore SI/DI
push bx
push [yline]
push dx
push cx
call DrawLine ; DrawLine(start,end,y,color)
add sp,8
pop es
jmp @@while
;
; new line is same color as old,
; so skip some drawing
;
@@skipsome:
mov ax,[es:si+SG_END]
cmp ax,[es:di+SG_END] ; both end at same place?
jne @@notsameendsk
mov si,[es:si+SG_NEXT]
mov di,[es:di+SG_NEXT] ; bump both new and old
mov bp,[es:si+SG_START] ; x = newseg->start
jmp @@while
@@notsameendsk:
jg @@newgreatersk ; new end before old?
mov si,[es:si+SG_NEXT] ; bump si
mov bp,[es:si+SG_START] ; x = newseg->start
jmp @@while
@@newgreatersk:
mov di,[es:di+SG_NEXT] ; bump old
mov bp,[es:di+SG_START] ; x = oldseg->start
;
; is line done ?
;
@@while:
cmp bp,VIEWXH
jg @@segdone
jmp @@segmentloop
@@segdone:
inc [yline]
cmp [yline],VIEWYH
jg @@done
jmp @@lineloop
@@done:
;
; all lines are done
;
mov dx,GC_INDEX
mov ax,GC_BITMASK+255*256
out dx,ax ; no pixel mask
mov dx,GC_INDEX
mov ax,GC_MODE + 2*256
out dx,ax ; set EGA write mode 0
ret
ENDP
;=====================
;
; DE_ClearOld
;
; Resets all frame segments so next two frames are completely drawn
;
; SI = table number clearing * 2
; DI = linestart pointer
;
;=====================
PROC DE_ClearOld
PUBLIC DE_ClearOld
USES SI,DI
mov ax,ds
mov es,ax
xor si,si
@@tableloop:
mov di,[linestarttable + si] ; di points to start of pointer table
mov ax,OFFSET startline
mov cx,VIEWHEIGHT ; for (y=0;y<VIEWHEIGHT;y++)
rep stosw ; linestart[tableon][y] = redraw
inc si
inc si
cmp si,6
jl @@tableloop
mov [tableon],0 ; next frame will start on table 1
ret
ENDP
;=====================
;
; DE_NewFrame
;
; Bumps tableon and set up default values for new frame
;
; di = freesegment pointer
; bx = linestart pointer
;
;=====================
DATASEG
stubline dw 0,VIEWX-1,99,0, VIEWX,VIEWXH,0,endline
CODESEG
PROC DE_NewFrame
PUBLIC DE_NewFrame
USES si,di
mov ax,@FARDATA
mov es,ax ; es points to tables
mov si,[tableon]
inc si
cmp si,3
jl @@gottable
xor si,si
@@gottable:
mov [tableon],si
shl si,1
mov di,[freestarttable+si] ; di = pointer to free segments
mov bx,[linestarttable+si] ; bx = pointer to line start list
mov [linestartptr],bx
mov dx,VIEWHEIGHT ; for (y=viewheight;y>0;y--)
@@yloop:
;
; copy two segments to each line
;
mov [bx],di
inc bx
inc bx ; linestartptr++ = new
mov si,OFFSET stubline
mov ax,di
add ax,SEGSIZE
mov [si+SG_NEXT],ax ; patch pointer
mov cx,(SEGSIZE*2)/2
rep movsw ; copy two new segments
dec dx
jnz @@yloop
mov [freesegment],di
ret
ENDP
END