+; Catacomb 3-D Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+; ID_RF_A.ASM\r
+\r
+IDEAL\r
+MODEL MEDIUM,C\r
+\r
+INCLUDE "ID_ASM.EQU"\r
+\r
+;============================================================================\r
+\r
+TILESWIDE = 21\r
+TILESHIGH = 14\r
+\r
+UPDATESIZE = (TILESWIDE+1)*TILESHIGH+1\r
+\r
+DATASEG\r
+\r
+EXTRN screenseg:WORD\r
+EXTRN updateptr:WORD\r
+EXTRN updatestart:WORD\r
+EXTRN masterofs:WORD ;start of master tile port\r
+EXTRN bufferofs:WORD ;start of current buffer port\r
+EXTRN screenstart:WORD ;starts of three screens (0/1/master) in EGA mem\r
+EXTRN grsegs:WORD\r
+EXTRN mapsegs:WORD\r
+EXTRN originmap:WORD\r
+EXTRN updatemapofs:WORD\r
+EXTRN tinf:WORD ;seg pointer to map header and tile info\r
+EXTRN blockstarts:WORD ;offsets from bufferofs for each update block\r
+\r
+planemask db ?\r
+planenum db ?\r
+\r
+CODESEG\r
+\r
+screenstartcs dw ? ;in code segment for accesability\r
+\r
+\r
+\r
+\r
+IFE GRMODE-CGAGR\r
+;============================================================================\r
+;\r
+; CGA refresh routines\r
+;\r
+;============================================================================\r
+\r
+TILEWIDTH = 4\r
+\r
+;=================\r
+;\r
+; RFL_NewTile\r
+;\r
+; Draws a composit two plane tile to the master screen and sets the update\r
+; spot to 1 in both update pages, forcing the tile to be copied to the\r
+; view pages the next two refreshes\r
+;\r
+; Called to draw newlly scrolled on strips and animating tiles\r
+;\r
+;=================\r
+\r
+PROC RFL_NewTile updateoffset:WORD\r
+PUBLIC RFL_NewTile\r
+USES SI,DI\r
+\r
+;\r
+; mark both update lists at this spot\r
+;\r
+ mov di,[updateoffset]\r
+\r
+ mov bx,[updateptr] ;start of update matrix\r
+ mov [BYTE bx+di],1\r
+\r
+ mov dx,SCREENWIDTH-TILEWIDTH ;add to get to start of next line\r
+\r
+;\r
+; set di to the location in screenseg to draw the tile\r
+;\r
+ shl di,1\r
+ mov si,[updatemapofs+di] ;offset in map from origin\r
+ add si,[originmap]\r
+ mov di,[blockstarts+di] ;screen location for tile\r
+ add di,[masterofs]\r
+\r
+;\r
+; set BX to the foreground tile number and SI to the background number\r
+; If either BX or SI = 0xFFFF, the tile does not need to be masked together\r
+; as one of the planes totally eclipses the other\r
+;\r
+ mov es,[mapsegs+2] ;foreground plane\r
+ mov bx,[es:si]\r
+ mov es,[mapsegs] ;background plane\r
+ mov si,[es:si]\r
+\r
+ mov es,[screenseg]\r
+\r
+ or bx,bx\r
+ jz @@singletile\r
+ jmp @@maskeddraw ;draw both together\r
+\r
+;=============\r
+;\r
+; Draw single background tile from main memory\r
+;\r
+;=============\r
+\r
+@@singletile:\r
+ shl si,1\r
+ mov ds,[grsegs+STARTTILE16*2+si]\r
+\r
+ xor si,si ;block is segment aligned\r
+\r
+REPT 15\r
+ movsw\r
+ movsw\r
+ add di,dx\r
+ENDM\r
+ movsw\r
+ movsw\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+ ret\r
+\r
+\r
+;=========\r
+;\r
+; Draw a masked tile combo\r
+; Interupts are disabled and the stack segment is reassigned\r
+;\r
+;=========\r
+@@maskeddraw:\r
+ cli ; don't allow ints when SS is set\r
+ shl bx,1\r
+ mov ss,[grsegs+STARTTILE16M*2+bx]\r
+ shl si,1\r
+ mov ds,[grsegs+STARTTILE16*2+si]\r
+\r
+ xor si,si ;first word of tile data\r
+\r
+REPT 16\r
+ mov ax,[si] ;background tile\r
+ and ax,[ss:si] ;mask\r
+ or ax,[ss:si+64] ;masked data\r
+ stosw\r
+ mov ax,[si+2] ;background tile\r
+ and ax,[ss:si+2] ;mask\r
+ or ax,[ss:si+66] ;masked data\r
+ stosw\r
+ add si,4\r
+ add di,dx\r
+ENDM\r
+\r
+ mov ax,@DATA\r
+ mov ss,ax\r
+ sti\r
+ mov ds,ax\r
+ ret\r
+ENDP\r
+\r
+ENDIF\r
+\r
+\r
+\r
+IFE GRMODE-EGAGR\r
+;===========================================================================\r
+;\r
+; EGA refresh routines\r
+;\r
+;===========================================================================\r
+\r
+TILEWIDTH = 2\r
+\r
+;=================\r
+;\r
+; RFL_NewTile\r
+;\r
+; Draws a composit two plane tile to the master screen and sets the update\r
+; spot to 1 in both update pages, forcing the tile to be copied to the\r
+; view pages the next two refreshes\r
+;\r
+; Called to draw newlly scrolled on strips and animating tiles\r
+;\r
+; Assumes write mode 0\r
+;\r
+;=================\r
+\r
+PROC RFL_NewTile updateoffset:WORD\r
+PUBLIC RFL_NewTile\r
+USES SI,DI\r
+\r
+;\r
+; mark both update lists at this spot\r
+;\r
+ mov di,[updateoffset]\r
+\r
+ mov bx,[updatestart] ;page 0 pointer\r
+ mov [BYTE bx+di],1\r
+ mov bx,[updatestart+2] ;page 1 pointer\r
+ mov [BYTE bx+di],1\r
+\r
+;\r
+; set screenstartcs to the location in screenseg to draw the tile\r
+;\r
+ shl di,1\r
+ mov si,[updatemapofs+di] ;offset in map from origin\r
+ add si,[originmap]\r
+ mov di,[blockstarts+di] ;screen location for tile\r
+ add di,[masterofs]\r
+ mov [cs:screenstartcs],di\r
+\r
+;\r
+; set BX to the foreground tile number and SI to the background number\r
+; If either BX or SI = 0xFFFF, the tile does not need to be masked together\r
+; as one of the planes totally eclipses the other\r
+;\r
+ mov es,[mapsegs+2] ;foreground plane\r
+ mov bx,[es:si]\r
+ mov es,[mapsegs] ;background plane\r
+ mov si,[es:si]\r
+\r
+ mov es,[screenseg]\r
+ mov dx,SC_INDEX ;for stepping through map mask planes\r
+\r
+ or bx,bx\r
+ jz @@singletile\r
+ jmp @@maskeddraw ;draw both together\r
+\r
+;=========\r
+;\r
+; No foreground tile, so draw a single background tile.\r
+;\r
+;=========\r
+@@singletile:\r
+\r
+ mov bx,SCREENWIDTH-2 ;add to get to start of next line\r
+ shl si,1\r
+\r
+ mov ax,[cs:screenstartcs]\r
+ mov ds,[grsegs+STARTTILE16*2+si]\r
+\r
+ xor si,si ;block is segment aligned\r
+\r
+ mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
+\r
+ mov cx,4 ;draw four planes\r
+@@planeloop:\r
+ mov dx,SC_INDEX\r
+ WORDOUT\r
+\r
+ mov di,[cs:screenstartcs] ;start at same place in all planes\r
+\r
+REPT 15\r
+ movsw\r
+ add di,bx\r
+ENDM\r
+ movsw\r
+\r
+ shl ah,1 ;shift plane mask over for next plane\r
+ loop @@planeloop\r
+\r
+ mov ax,ss\r
+ mov ds,ax ;restore turbo's data segment\r
+ ret\r
+\r
+\r
+;=========\r
+;\r
+; Draw a masked tile combo\r
+; Interupts are disabled and the stack segment is reassigned\r
+;\r
+;=========\r
+@@maskeddraw:\r
+ cli ; don't allow ints when SS is set\r
+ shl bx,1\r
+ mov ss,[grsegs+STARTTILE16M*2+bx]\r
+ shl si,1\r
+ mov ds,[grsegs+STARTTILE16*2+si]\r
+\r
+ xor si,si ;first word of tile data\r
+\r
+ mov ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
+\r
+ mov di,[cs:screenstartcs]\r
+@@planeloopm:\r
+ WORDOUT\r
+tileofs = 0\r
+lineoffset = 0\r
+REPT 16\r
+ mov bx,[si+tileofs] ;background tile\r
+ and bx,[ss:tileofs] ;mask\r
+ or bx,[ss:si+tileofs+32] ;masked data\r
+ mov [es:di+lineoffset],bx\r
+tileofs = tileofs + 2\r
+lineoffset = lineoffset + SCREENWIDTH\r
+ENDM\r
+ add si,32\r
+ shl ah,1 ;shift plane mask over for next plane\r
+ cmp ah,10000b\r
+ je @@done ;drawn all four planes\r
+ jmp @@planeloopm\r
+\r
+@@done:\r
+ mov ax,@DATA\r
+ mov ss,ax\r
+ sti\r
+ mov ds,ax\r
+ ret\r
+ENDP\r
+\r
+ENDIF\r
+\r
+IFE GRMODE-VGAGR\r
+;============================================================================\r
+;\r
+; VGA refresh routines\r
+;\r
+;============================================================================\r
+\r
+\r
+ENDIF\r
+\r
+\r
+;============================================================================\r
+;\r
+; reasonably common refresh routines\r
+;\r
+;============================================================================\r
+\r
+\r
+;=================\r
+;\r
+; RFL_UpdateTiles\r
+;\r
+; Scans through the update matrix pointed to by updateptr, looking for 1s.\r
+; A 1 represents a tile that needs to be copied from the master screen to the\r
+; current screen (a new row or an animated tiled). If more than one adjacent\r
+; tile in a horizontal row needs to be copied, they will be copied as a group.\r
+;\r
+; Assumes write mode 1\r
+;\r
+;=================\r
+\r
+\r
+; AX 0/1 for scasb, temp for segment register transfers\r
+; BX width for block copies\r
+; CX REP counter\r
+; DX line width deltas\r
+; SI source for copies\r
+; DI scas dest / movsb dest\r
+; BP pointer to UPDATETERMINATE\r
+;\r
+; DS\r
+; ES\r
+; SS\r
+\r
+PROC RFL_UpdateTiles\r
+PUBLIC RFL_UpdateTiles\r
+USES SI,DI,BP\r
+\r
+ jmp SHORT @@realstart\r
+@@done:\r
+;\r
+; all tiles have been scanned\r
+;\r
+ ret\r
+\r
+@@realstart:\r
+ mov di,[updateptr]\r
+ mov bp,(TILESWIDE+1)*TILESHIGH+1\r
+ add bp,di ; when di = bx, all tiles have been scanned\r
+ push di\r
+ mov cx,-1 ; definately scan the entire thing\r
+\r
+;\r
+; scan for a 1 in the update list, meaning a tile needs to be copied\r
+; from the master screen to the current screen\r
+;\r
+@@findtile:\r
+ pop di ; place to continue scaning from\r
+ mov ax,ss\r
+ mov es,ax ; search in the data segment\r
+ mov ds,ax\r
+ mov al,1\r
+ repne scasb\r
+ cmp di,bp\r
+ je @@done\r
+\r
+ cmp [BYTE di],al\r
+ jne @@singletile\r
+ jmp @@tileblock\r
+\r
+;============\r
+;\r
+; copy a single tile\r
+;\r
+;============\r
+EVEN\r
+@@singletile:\r
+ inc di ; we know the next tile is nothing\r
+ push di ; save off the spot being scanned\r
+ sub di,[updateptr]\r
+ shl di,1\r
+ mov di,[blockstarts-4+di] ; start of tile location on screen\r
+ mov si,di\r
+ add di,[bufferofs] ; dest in current screen\r
+ add si,[masterofs] ; source in master screen\r
+\r
+ mov dx,SCREENWIDTH-TILEWIDTH\r
+ mov ax,[screenseg]\r
+ mov ds,ax\r
+ mov es,ax\r
+\r
+;--------------------------\r
+\r
+IFE GRMODE-CGAGR\r
+\r
+REPT 15\r
+ movsw\r
+ movsw\r
+ add si,dx\r
+ add di,dx\r
+ENDM\r
+ movsw\r
+ movsw\r
+\r
+ENDIF\r
+\r
+;--------------------------\r
+\r
+IFE GRMODE-EGAGR\r
+\r
+REPT 15\r
+ movsb\r
+ movsb\r
+ add si,dx\r
+ add di,dx\r
+ENDM\r
+ movsb\r
+ movsb\r
+\r
+ENDIF\r
+\r
+;--------------------------\r
+\r
+ jmp @@findtile\r
+\r
+;============\r
+;\r
+; more than one tile in a row needs to be updated, so do it as a group\r
+;\r
+;============\r
+EVEN\r
+@@tileblock:\r
+ mov dx,di ; hold starting position + 1 in dx\r
+ inc di ; we know the next tile also gets updated\r
+ repe scasb ; see how many more in a row\r
+ push di ; save off the spot being scanned\r
+\r
+ mov bx,di\r
+ sub bx,dx ; number of tiles in a row\r
+ shl bx,1 ; number of bytes / row\r
+\r
+ mov di,dx ; lookup position of start tile\r
+ sub di,[updateptr]\r
+ shl di,1\r
+ mov di,[blockstarts-2+di] ; start of tile location\r
+ mov si,di\r
+ add di,[bufferofs] ; dest in current screen\r
+ add si,[masterofs] ; source in master screen\r
+\r
+ mov dx,SCREENWIDTH\r
+ sub dx,bx ; offset to next line on screen\r
+IFE GRMODE-CGAGR\r
+ sub dx,bx ; bx is words wide in CGA tiles\r
+ENDIF\r
+\r
+ mov ax,[screenseg]\r
+ mov ds,ax\r
+ mov es,ax\r
+\r
+REPT 15\r
+ mov cx,bx\r
+IFE GRMODE-CGAGR\r
+ rep movsw\r
+ENDIF\r
+IFE GRMODE-EGAGR\r
+ rep movsb\r
+ENDIF\r
+ add si,dx\r
+ add di,dx\r
+ENDM\r
+ mov cx,bx\r
+IFE GRMODE-CGAGR\r
+ rep movsw\r
+ENDIF\r
+IFE GRMODE-EGAGR\r
+ rep movsb\r
+ENDIF\r
+\r
+ dec cx ; was 0 from last rep movsb, now $ffff for scasb\r
+ jmp @@findtile\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+\r
+\r
+;=================\r
+;\r
+; RFL_MaskForegroundTiles\r
+;\r
+; Scan through update looking for 3's. If the foreground tile there is a\r
+; masked foreground tile, draw it to the screen\r
+;\r
+;=================\r
+\r
+PROC RFL_MaskForegroundTiles\r
+PUBLIC RFL_MaskForegroundTiles\r
+USES SI,DI,BP\r
+ jmp SHORT @@realstart\r
+@@done:\r
+;\r
+; all tiles have been scanned\r
+;\r
+ ret\r
+\r
+@@realstart:\r
+ mov di,[updateptr]\r
+ mov bp,(TILESWIDE+1)*TILESHIGH+2\r
+ add bp,di ; when di = bx, all tiles have been scanned\r
+ push di\r
+ mov cx,-1 ; definately scan the entire thing\r
+;\r
+; scan for a 3 in the update list\r
+;\r
+@@findtile:\r
+ mov ax,ss\r
+ mov es,ax ; scan in the data segment\r
+ mov al,3\r
+ pop di ; place to continue scaning from\r
+ repne scasb\r
+ cmp di,bp\r
+ je @@done\r
+\r
+;============\r
+;\r
+; found a tile, see if it needs to be masked on\r
+;\r
+;============\r
+\r
+ push di\r
+\r
+ sub di,[updateptr]\r
+ shl di,1\r
+ mov si,[updatemapofs-2+di] ; offset from originmap\r
+ add si,[originmap]\r
+\r
+ mov es,[mapsegs+2] ; foreground map plane segment\r
+ mov si,[es:si] ; foreground tile number\r
+\r
+ or si,si\r
+ jz @@findtile ; 0 = no foreground tile\r
+\r
+ mov bx,si\r
+ add bx,INTILE ;INTILE tile info table\r
+ mov es,[tinf]\r
+ test [BYTE PTR es:bx],80h ;high bit = masked tile\r
+ jz @@findtile\r
+\r
+;-------------------\r
+\r
+IFE GRMODE-CGAGR\r
+;=================\r
+;\r
+; mask the tile CGA\r
+;\r
+;=================\r
+\r
+ mov di,[blockstarts-2+di]\r
+ add di,[bufferofs]\r
+ mov es,[screenseg]\r
+ shl si,1\r
+ mov ds,[grsegs+STARTTILE16M*2+si]\r
+\r
+ mov bx,64 ;data starts 64 bytes after mask\r
+\r
+ xor si,si\r
+\r
+lineoffset = 0\r
+REPT 16\r
+ mov ax,[es:di+lineoffset] ;background\r
+ and ax,[si] ;mask\r
+ or ax,[si+bx] ;masked data\r
+ mov [es:di+lineoffset],ax ;background\r
+ inc si\r
+ inc si\r
+ mov ax,[es:di+lineoffset+2] ;background\r
+ and ax,[si] ;mask\r
+ or ax,[si+bx] ;masked data\r
+ mov [es:di+lineoffset+2],ax ;background\r
+ inc si\r
+ inc si\r
+lineoffset = lineoffset + SCREENWIDTH\r
+ENDM\r
+ENDIF\r
+\r
+;-------------------\r
+\r
+IFE GRMODE-EGAGR\r
+;=================\r
+;\r
+; mask the tile\r
+;\r
+;=================\r
+\r
+ mov [BYTE planemask],1\r
+ mov [BYTE planenum],0\r
+\r
+ mov di,[blockstarts-2+di]\r
+ add di,[bufferofs]\r
+ mov [cs:screenstartcs],di\r
+ mov es,[screenseg]\r
+ shl si,1\r
+ mov ds,[grsegs+STARTTILE16M*2+si]\r
+\r
+ mov bx,32 ;data starts 32 bytes after mask\r
+\r
+@@planeloopm:\r
+ mov dx,SC_INDEX\r
+ mov al,SC_MAPMASK\r
+ mov ah,[ss:planemask]\r
+ WORDOUT\r
+ mov dx,GC_INDEX\r
+ mov al,GC_READMAP\r
+ mov ah,[ss:planenum]\r
+ WORDOUT\r
+\r
+ xor si,si\r
+ mov di,[cs:screenstartcs]\r
+lineoffset = 0\r
+REPT 16\r
+ mov cx,[es:di+lineoffset] ;background\r
+ and cx,[si] ;mask\r
+ or cx,[si+bx] ;masked data\r
+ inc si\r
+ inc si\r
+ mov [es:di+lineoffset],cx\r
+lineoffset = lineoffset + SCREENWIDTH\r
+ENDM\r
+ add bx,32 ;the mask is now further away\r
+ inc [ss:planenum]\r
+ shl [ss:planemask],1 ;shift plane mask over for next plane\r
+ cmp [ss:planemask],10000b ;done all four planes?\r
+ je @@drawn ;drawn all four planes\r
+ jmp @@planeloopm\r
+\r
+@@drawn:\r
+ENDIF\r
+\r
+;-------------------\r
+\r
+ mov ax,ss\r
+ mov ds,ax\r
+ mov cx,-1 ;definately scan the entire thing\r
+\r
+ jmp @@findtile\r
+\r
+ENDP\r
+\r
+\r
+END\r
+\r