1 ; Reconstructed Commander Keen 4-6 Source Code
\r
2 ; Copyright (C) 2021 K1n9_Duk3
\r
4 ; This file is primarily based on:
\r
5 ; Catacomb 3-D Source Code
\r
6 ; Copyright (C) 1993-2014 Flat Rock Software
\r
8 ; This program is free software; you can redistribute it and/or modify
\r
9 ; it under the terms of the GNU General Public License as published by
\r
10 ; the Free Software Foundation; either version 2 of the License, or
\r
11 ; (at your option) any later version.
\r
13 ; This program is distributed in the hope that it will be useful,
\r
14 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
15 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
16 ; GNU General Public License for more details.
\r
18 ; You should have received a copy of the GNU General Public License along
\r
19 ; with this program; if not, write to the Free Software Foundation, Inc.,
\r
20 ; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
22 ;=================================
\r
24 ; CGA view manager routines
\r
26 ;=================================
\r
28 ;============================================================================
\r
30 ; All of these routines draw into a floating virtual screen segment in main
\r
31 ; memory. bufferofs points to the origin of the drawing page in screenseg.
\r
32 ; The routines that write out words must take into account buffer wrapping
\r
33 ; and not write a word at 0xffff (which causes an exception on 386s).
\r
35 ; The direction flag should be clear
\r
37 ;============================================================================
\r
41 plotpixels db 0c0h,030h,0ch,03h
\r
42 colorbyte db 000000b,01010101b,10101010b,11111111b
\r
43 colorword dw 0,5555h,0aaaah,0ffffh
\r
47 ;============================================================================
\r
49 ; VW_Plot (int x,y,color)
\r
51 ;============================================================================
\r
54 PROC VW_Plot x:WORD, y:WORD, color:WORD
\r
68 add di,ax ; di = byte on screen
\r
71 mov ah,[plotpixels+bx]
\r
73 mov cl,[colorbyte+bx]
\r
78 and al,ah ; mask off other pixels
\r
87 ;============================================================================
\r
89 ; VW_Vlin (int yl,yh,x,color)
\r
91 ;============================================================================
\r
93 PROC VW_Vlin yl:WORD, yh:WORD, x:WORD, color:WORD
\r
102 add di,[ylookup+bx]
\r
107 add di,ax ; di = byte on screen
\r
110 mov ah,[plotpixels+bx]
\r
112 mov bl,[colorbyte+bx]
\r
118 inc cx ;number of pixels to plot
\r
124 and al,ah ; mask off other pixels
\r
137 ;============================================================================
\r
140 ;===================
\r
144 ; xcoord in bytes (8 pixels), ycoord in pixels
\r
145 ; All Tile8s are in one grseg, so an offset is calculated inside it
\r
149 ;===================
\r
151 PROC VW_DrawTile8 xcoord:WORD, ycoord:WORD, tile:WORD
\r
152 PUBLIC VW_DrawTile8
\r
161 add di,[ylookup+bx]
\r
172 mov ds,[grsegs+STARTTILE8*2] ; segment for all tile8s
\r
179 movsb ;no word moves because of segment wrapping
\r
187 mov ds,ax ;restore turbo's data segment
\r
194 ;============================================================================
\r
198 ; Draws a masked block shape to the screen. bufferofs is NOT accounted for.
\r
199 ; The mask comes first, then the data. Seperate unwound routines are used
\r
200 ; to speed drawing.
\r
202 ; Mask blocks will allways be an even width because of the way IGRAB works
\r
206 ;============================================================================
\r
213 maskroutines dw mask0,mask0,mask2E,mask2O,mask4E,mask4O
\r
214 dw mask6E,mask6O,mask8E,mask8O,mask10E,mask10O
\r
215 dw mask12E,mask12O,mask14E,mask14O,mask16E,mask16O
\r
223 PROC VW_MaskBlock segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD, planesize:WORD
\r
224 PUBLIC VW_MaskBlock
\r
231 sub dx,di ;dx = delta to start of next line
\r
233 mov bx,[planesize] ; si+bx = data location
\r
235 cmp di,UNWOUNDMASKS
\r
236 jbe @@unwoundroutine
\r
240 ; General purpose masked block drawing. This could be optimised into
\r
241 ; four routines to use words, but few play loop sprites should be this big!
\r
245 mov [ss:linedelta],dx
\r
249 mov dx,[height] ;scan lines to draw
\r
261 add di,[ss:linedelta]
\r
268 ret ;width of 0 = no drawing
\r
273 ; use the unwound routines
\r
278 shr di,1 ;we only have even width unwound routines
\r
281 rcl di,1 ;shift a 1 in if destination is odd
\r
283 mov ax,[maskroutines+di] ;call the right routine
\r
288 mov cx,[height] ;scan lines to draw
\r
294 ; Horizontally unwound routines to draw certain masked blocks faster
\r
315 MACRO SPRITELOOP addr
\r
499 ;============================================================================
\r
503 ; Draws a masked block shape to the screen. bufferofs is NOT accounted for.
\r
504 ; The mask comes first, then the data.
\r
506 ; Mask blocks will allways be an even width because of the way IGRAB works
\r
508 ;============================================================================
\r
510 PROC VW_InverseMask segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD
\r
511 PUBLIC VW_InverseMask
\r
514 mov es, [screenseg]
\r
516 mov dx, [linewidth]
\r
537 mov ds,ax ;restore turbo's data segment
\r
543 ;============================================================================
\r
545 ; VW_ScreenToScreen
\r
547 ; Basic block copy routine. Copies one block of screen memory to another,
\r
548 ; bufferofs is NOT accounted for.
\r
552 ;============================================================================
\r
554 PROC VW_ScreenToScreen source:WORD, dest:WORD, wide:WORD, height:WORD
\r
555 PUBLIC VW_ScreenToScreen
\r
566 mov di,[dest] ;start at same place in all planes
\r
567 mov dx,[height] ;scan lines to draw
\r
570 ; if the width, source, and dest are all even, use word moves
\r
571 ; This is allways the case in the CGA refresh
\r
591 mov ds,ax ;restore turbo's data segment
\r
605 mov ds,ax ;restore turbo's data segment
\r
612 ;============================================================================
\r
616 ; Basic block drawing routine. Takes a block shape at segment pointer source
\r
617 ; of width by height data, and draws it to dest in the virtual screen,
\r
618 ; based on linewidth. bufferofs is NOT accounted for.
\r
619 ; There are four drawing routines to provide the best optimized code while
\r
620 ; accounting for odd segment wrappings due to the floating screens.
\r
624 ;============================================================================
\r
628 memtoscreentable dw eventoeven,eventoodd,oddtoeven,oddtoodd
\r
633 PROC VW_MemToScreen source:WORD, dest:WORD, wide:WORD, height:WORD
\r
634 PUBLIC VW_MemToScreen
\r
644 xor si,si ;block is segment aligned
\r
647 shr [wide],1 ;change wide to words, and see if carry is set
\r
648 rcl di,1 ;1 if wide is odd
\r
651 rcl di,1 ;shift a 1 in if destination is odd
\r
652 shl di,1 ;to index into a word width table
\r
653 mov dx,[height] ;scan lines to draw
\r
655 jmp [ss:memtoscreentable+di] ;call the right routine
\r
659 ; Copy an even width block to an even destination address
\r
664 mov di,[dest] ;start at same place in all planes
\r
674 mov ds,ax ;restore turbo's data segment
\r
680 ; Copy an odd width block to an even video address
\r
685 mov di,[dest] ;start at same place in all planes
\r
690 movsb ;copy the last byte
\r
696 mov ds,ax ;restore turbo's data segment
\r
702 ; Copy an even width block to an odd video address
\r
707 mov di,[dest] ;start at same place in all planes
\r
708 dec ax ;one word has to be handled seperately
\r
720 mov ds,ax ;restore turbo's data segment
\r
726 ; Copy an odd width block to an odd video address
\r
731 mov di,[dest] ;start at same place in all planes
\r
742 mov ds,ax ;restore turbo's data segment
\r
748 ;===========================================================================
\r
752 ; Copies a block of video memory to main memory, in order from planes 0-3.
\r
753 ; This could be optimized along the lines of VW_MemToScreen to take advantage
\r
754 ; of word copies, but this is an infrequently called routine.
\r
758 ;===========================================================================
\r
760 PROC VW_ScreenToMem source:WORD, dest:WORD, wide:WORD, height:WORD
\r
761 PUBLIC VW_ScreenToMem
\r
774 mov dx,[height] ;scan lines to draw
\r
786 mov ds,ax ;restore turbo's data segment
\r
793 ;===========================================================================
\r
795 ; MISC CGA ROUTINES
\r
797 ;===========================================================================
\r
807 PROC VW_SetScreen crtc:WORD
\r
808 PUBLIC VW_SetScreen
\r
811 ; for some reason, my XT's EGA card doesn't like word outs to the CRTC
\r
818 mov al,0ch ;start address high register
\r
824 mov al,0dh ;start address low register
\r
837 if NUMFONT+NUMFONTM
\r
839 ;===========================================================================
\r
841 ; GENERAL FONT DRAWING ROUTINES
\r
843 ;===========================================================================
\r
847 px dw ? ; proportional character drawing coordinates
\r
849 pdrawmode db 11000b ; 8 = OR, 24 = XOR, put in GC_DATAROTATE
\r
850 fontcolor db 15 ;0-15 mapmask value
\r
852 PUBLIC px,py,pdrawmode,fontcolor
\r
855 ; offsets in font structure
\r
857 pcharheight = 0 ;lines high
\r
858 charloc = 2 ;pointers to every character
\r
859 charwidth = 514 ;every character's width in pixels
\r
862 propchar dw ? ; the character number to shift
\r
865 fontcolormask dw ? ; font color expands into this
\r
868 BUFFHEIGHT = 32 ; must be twice as high as font for masked fonts
\r
870 databuffer db BUFFWIDTH*BUFFHEIGHT dup (?)
\r
872 bufferwidth dw ? ; bytes with valid info / line
\r
873 bufferheight dw ? ; number of lines currently used
\r
877 PUBLIC bufferwidth,bufferheight,bufferbyte,bufferbit
\r
879 screenspot dw ? ; where the buffer is going
\r
881 bufferextra dw ? ; add at end of a line copy
\r
886 ;======================
\r
888 ; Macros to table shift a byte of font
\r
890 ;======================
\r
893 mov al,[es:bx] ; source
\r
897 mov ax,[bp+si] ; table shift into two bytes
\r
898 or [di],al ; or with first byte
\r
900 mov [di],ah ; replace next byte
\r
901 inc bx ; next source byte
\r
905 mov al,[es:bx] ; source
\r
909 mov ax,[bp+si] ; table shift into two bytes
\r
911 and [di],al ; and with first byte
\r
913 mov [di],ah ; replace next byte
\r
914 inc bx ; next source byte
\r
918 ;=======================
\r
922 ; Pass buffer start in SI (somewhere in databuffer)
\r
923 ; Draws the buffer to the screen buffer
\r
925 ;========================
\r
927 PROC VWL_XORBuffer NEAR
\r
932 mov ax,[colorword+bx]
\r
933 mov [fontcolormask],ax
\r
936 mov di,[screenspot]
\r
938 mov bx,[bufferwidth] ;calculate offsets for end of each line
\r
939 mov [bufferwidth],bx
\r
943 ret ;nothing to draw
\r
950 ; clear the last byte so word draws can be used
\r
956 mov [BYTE databuffer+BUFFWIDTH*line+bx],al
\r
964 mov [screenextra],ax
\r
967 mov [bufferextra],ax
\r
970 mov bx,[bufferheight] ;lines to copy
\r
971 mov bp,[fontcolormask]
\r
975 lodsb ;get a word from the buffer
\r
977 xor [es:di],al ;draw it
\r
981 add si,[bufferextra]
\r
982 add di,[screenextra]
\r
993 ;============================================================================
\r
995 ; NON MASKED FONT DRAWING ROUTINES
\r
997 ;============================================================================
\r
1003 shiftdrawtable dw 0,shift1wide,shift2wide,shift3wide,shift4wide
\r
1004 dw shift5wide,shift6wide
\r
1008 ;==================
\r
1012 ; Call with BX = character number (0-255)
\r
1013 ; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts
\r
1014 ; them to the new position
\r
1016 ;==================
\r
1018 PROC ShiftPropChar NEAR
\r
1020 mov si,[fontnumber]
\r
1022 mov es,[grsegs+STARTFONT*2+si] ;segment of font to use
\r
1025 ; find character location, width, and height
\r
1027 mov si,[es:charwidth+bx]
\r
1028 and si,0ffh ;SI hold width in pixels
\r
1030 mov bx,[es:charloc+bx] ;BX holds pointer to character data
\r
1033 ; look up which shift table to use, based on bufferbit
\r
1035 mov di,[bufferbit]
\r
1037 mov bp,[shifttabletable+di] ;BP holds pointer to shift table
\r
1039 mov di,OFFSET databuffer
\r
1040 add di,[bufferbyte] ;DI holds pointer to buffer
\r
1042 mov cx,[bufferbit]
\r
1043 add cx,si ;add twice because pixel == two bits
\r
1044 add cx,si ;new bit position
\r
1047 mov [bufferbit],ax ;new bit position
\r
1052 add [bufferbyte],ax ;new byte position
\r
1056 shr si,1 ;bytes the character is wide
\r
1057 shl si,1 ;*2 to look up in shiftdrawtable
\r
1059 mov cx,[es:pcharheight]
\r
1061 jmp [ss:shiftdrawtable+si] ;procedure to draw this width
\r
1064 ; one byte character
\r
1071 add di,dx ; next line in buffer
\r
1078 ; two byte character
\r
1087 add di,dx ; next line in buffer
\r
1094 ; three byte character
\r
1103 add di,dx ; next line in buffer
\r
1111 ; four byte character
\r
1121 add di,dx ; next line in buffer
\r
1129 ; five byte character
\r
1140 add di,dx ; next line in buffer
\r
1147 ; six byte character
\r
1159 add di,dx ; next line in buffer
\r
1169 ;============================================================================
\r
1171 ;==================
\r
1173 ; VW_DrawPropString
\r
1175 ; Draws a C string of characters at px/py and advances px
\r
1177 ;==================
\r
1181 PROC VW_DrawPropString string:DWORD
\r
1182 PUBLIC VW_DrawPropString
\r
1186 ; proportional spaceing, which clears the buffer ahead of it, so only
\r
1187 ; clear the first collumn
\r
1192 mov [BYTE databuffer+BUFFWIDTH*line],al
\r
1197 ; shift the characters into the buffer
\r
1202 shl ax,1 ;one pixel == two bits
\r
1203 mov [bufferbit],ax
\r
1204 mov [bufferbyte],0
\r
1206 mov ax,[WORD string]
\r
1207 mov [stringptr],ax
\r
1208 mov ax,[WORD string+2]
\r
1209 mov [stringptr+2],ax
\r
1212 mov es,[stringptr+2]
\r
1213 mov bx,[stringptr]
\r
1219 call ShiftPropChar
\r
1224 ; calculate position to draw buffer on screen
\r
1228 mov di,[ylookup+bx]
\r
1229 add di,[bufferofs]
\r
1230 add di,[panadjust]
\r
1234 shr ax,1 ;x location in bytes
\r
1236 mov [screenspot],di
\r
1241 mov ax,[bufferbyte]
\r
1244 mov bx,[bufferbit]
\r
1245 shr bx,1 ;two bits == one pixel
\r
1252 mov ax,[bufferbyte]
\r
1253 test [bufferbit],7
\r
1255 inc ax ;so the partial byte also gets drawn
\r
1257 mov [bufferwidth],ax
\r
1258 mov si,[fontnumber]
\r
1260 mov es,[grsegs+STARTFONT*2+si]
\r
1261 mov ax,[es:pcharheight]
\r
1262 mov [bufferheight],ax
\r
1264 mov si,OFFSET databuffer
\r
1265 call VWL_XORBuffer
\r
1273 ;============================================================================
\r
1275 ; MASKED FONT DRAWING ROUTINES
\r
1277 ;============================================================================
\r
1283 mshiftdrawtable dw 0,mshift1wide,mshift2wide,mshift3wide
\r
1288 ;==================
\r
1292 ; Call with BX = character number (0-255)
\r
1293 ; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts
\r
1294 ; them to the new position
\r
1296 ;==================
\r
1298 PROC ShiftMPropChar NEAR
\r
1300 mov es,[grsegs+STARTFONTM*2] ;segment of font to use
\r
1303 ; find character location, width, and height
\r
1305 mov si,[es:charwidth+bx]
\r
1306 and si,0ffh ;SI hold width in pixels
\r
1308 mov bx,[es:charloc+bx] ;BX holds pointer to character data
\r
1311 ; look up which shift table to use, based on bufferbit
\r
1313 mov di,[bufferbit]
\r
1315 mov bp,[shifttabletable+di] ;BP holds pointer to shift table
\r
1317 mov di,OFFSET databuffer
\r
1318 add di,[bufferbyte] ;DI holds pointer to buffer
\r
1321 ; advance position by character width
\r
1323 mov cx,[bufferbit]
\r
1324 add cx,si ;new bit position
\r
1327 mov [bufferbit],ax ;new bit position
\r
1332 add [bufferbyte],ax ;new byte position
\r
1337 shr si,1 ;bytes the character is wide
\r
1338 shl si,1 ;*2 to look up in shiftdrawtable
\r
1340 mov cx,[es:pcharheight]
\r
1342 jmp [ss:mshiftdrawtable+si] ;procedure to draw this width
\r
1345 ; one byte character
\r
1353 add di,dx ; next line in buffer
\r
1357 mov cx,[es:pcharheight]
\r
1362 add di,dx ; next line in buffer
\r
1368 ; two byte character
\r
1377 add di,dx ; next line in buffer
\r
1381 mov cx,[es:pcharheight]
\r
1387 add di,dx ; next line in buffer
\r
1393 ; three byte character
\r
1402 add di,dx ; next line in buffer
\r
1406 mov cx,[es:pcharheight]
\r
1413 add di,dx ; next line in buffer
\r
1421 ;============================================================================
\r
1423 ;==================
\r
1425 ; VW_DrawMPropString
\r
1427 ; Draws a C string of characters at px/py and advances px
\r
1429 ;==================
\r
1433 PROC VW_DrawMPropString string:DWORD
\r
1434 PUBLIC VW_DrawMPropString
\r
1438 ; clear out the first byte of the buffer, the rest will automatically be
\r
1439 ; cleared as characters are drawn into it
\r
1441 mov es,[grsegs+STARTFONTM*2]
\r
1442 mov dx,[es:pcharheight]
\r
1443 mov di,OFFSET databuffer
\r
1446 mov bx,BUFFWIDTH-1
\r
1451 stosb ; fill the mask part with $ff
\r
1458 stosb ; fill the data part with $0
\r
1463 ; shift the characters into the buffer
\r
1467 mov [bufferbit],ax
\r
1468 mov [bufferbyte],0
\r
1470 mov ax,[WORD string]
\r
1471 mov [stringptr],ax
\r
1472 mov ax,[WORD string+2]
\r
1473 mov [stringptr+2],ax
\r
1476 mov es,[stringptr+2]
\r
1477 mov bx,[stringptr]
\r
1483 call ShiftMPropChar
\r
1488 ; calculate position to draw buffer on screen
\r
1492 mov di,[ylookup+bx]
\r
1493 add di,[bufferofs]
\r
1498 shr ax,1 ;x location in bytes
\r
1500 mov [screenspot],di
\r
1505 mov ax,[bufferbyte]
\r
1515 mov ax,[bufferbyte]
\r
1516 test [bufferbit],7
\r
1518 inc ax ;so the partial byte also gets drawn
\r
1520 mov [bufferwidth],ax
\r
1521 mov es,[grsegs+STARTFONTM*2]
\r
1522 mov ax,[es:pcharheight]
\r
1523 mov [bufferheight],ax
\r
1525 mov si,OFFSET databuffer
\r
1526 call BufferToScreen ; cut out mask
\r
1528 call BufferToScreen ; SI is still in the right position in buffer
\r
1534 endif ; if numfontm
\r