; 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;y0;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