From 1454b6951e065e83af5b24e84b68382da63949ee Mon Sep 17 00:00:00 2001 From: sparky4 Date: Thu, 12 Nov 2015 13:58:43 -0600 Subject: [PATCH] in the process of converting xlib into watcom friendly --- 16/xlib/makefile | 99 +- 16/xlib/model.inc | 41 +- 16/xlib/xbmtools.asm | 2 +- 16/xlib/xdetect.asm | 4 +- 16/xlib/xfileio.asm | 2 +- 16/xlib/xlib.inc | 4 +- 16/xlib/xpoint.asm | 10 +- 16/xlib/xpoint.inc | 1 - 16/xlib/xrletool.asm | 4 +- modex105/ASM.BAT | 1 + modex105/DEMOS/BASIC7/CHARDEMO.BAS | 164 ++ modex105/DEMOS/BASIC7/MAKE-LIB.BAT | 5 + modex105/DEMOS/BASIC7/MODEX.BI | 63 + modex105/DEMOS/BASIC7/MODEX.QLB | Bin 0 -> 11141 bytes modex105/DEMOS/BASIC7/TEST6.BAS | 562 ++++ modex105/DEMOS/BASIC7/UASM-BC7.BAT | 1 + modex105/DEMOS/BASIC7/UTILS.ASM | 406 +++ modex105/DEMOS/BASIC7/UTILS.BI | 51 + modex105/DEMOS/C/C_UTILS.ASM | 405 +++ modex105/DEMOS/C/C_UTILS.H | 117 + modex105/DEMOS/C/C_UTILS.LST | 597 ++++ modex105/DEMOS/C/C_UTILS.SBR | Bin 0 -> 1731 bytes modex105/DEMOS/C/MODEX.ASM | 3295 ++++++++++++++++++++++ modex105/DEMOS/C/MODEX.BI | 63 + modex105/DEMOS/C/MODEX.H | 76 + modex105/DEMOS/C/UTLS-ASM.BAT | 1 + modex105/DEMOS/C/X-DEMO.C | 780 ++++++ modex105/DEMOS/C/X-DEMO.EXE | Bin 0 -> 41090 bytes modex105/DEMOS/C/X-DEMO.PRJ | Bin 0 -> 5188 bytes modex105/DEMOS/C/x.exe | Bin 0 -> 41090 bytes modex105/DEMOS/CHARDEMO.EXE | Bin 0 -> 13066 bytes modex105/DEMOS/PASCAL/TEST5.PAS | 488 ++++ modex105/DEMOS/QB45/MAKE-LIB.BAT | 5 + modex105/DEMOS/QB45/MODEX.BI | 63 + modex105/DEMOS/QB45/MODEX.QLB | Bin 0 -> 9739 bytes modex105/DEMOS/QB45/TEST6A.BAS | 561 ++++ modex105/DEMOS/QB45/TEST6A.EXE | Bin 0 -> 40544 bytes modex105/DEMOS/QB45/UASM-QB4.BAT | 1 + modex105/DEMOS/QB45/UTILS.ASM | 406 +++ modex105/DEMOS/QB45/UTILS.BI | 51 + modex105/DEMOS/ROM_8X8.FNT | Bin 0 -> 1024 bytes modex105/DEMOS/SPACEAGE.FNT | Bin 0 -> 1024 bytes modex105/DEMOS/SYSTEM.FNT | Bin 0 -> 1024 bytes modex105/DEMOS/TEST6.EXE | Bin 0 -> 19990 bytes modex105/FONTEDIT/CHARSETS.CS | Bin 0 -> 2144 bytes modex105/FONTEDIT/CSEDIT.DOC | 196 ++ modex105/FONTEDIT/CSEDIT.EXE | Bin 0 -> 68368 bytes modex105/FONTEDIT/INVERSE.FNT | Bin 0 -> 1024 bytes modex105/FONTEDIT/MOUSEIMG.CS | Bin 0 -> 128 bytes modex105/FONTEDIT/PALETTE.CS | Bin 0 -> 768 bytes modex105/FONTEDIT/ROM_8X8.FNT | Bin 0 -> 1024 bytes modex105/FONTEDIT/SPACEAGE.FNT | Bin 0 -> 1024 bytes modex105/FONTEDIT/SYSTEM.FNT | Bin 0 -> 1024 bytes modex105/MODE-X.TXT | 44 + modex105/MODEX.ASM | 3295 ++++++++++++++++++++++ modex105/MODEX.BI | 63 + modex105/MODEX.H | 76 + modex105/MODEX.LST | 4122 ++++++++++++++++++++++++++++ modex105/MODEX.SBR | Bin 0 -> 12235 bytes modex105/PACKING.LST | 87 + modex105/PALEDIT/BAKAPI.PAL | Bin 0 -> 768 bytes modex105/PALEDIT/CHARSETS.CS | Bin 0 -> 2144 bytes modex105/PALEDIT/DIAGONAL.PAL | Bin 0 -> 768 bytes modex105/PALEDIT/GAMECOLR.PAL | Bin 0 -> 768 bytes modex105/PALEDIT/MOUSEIMG.CS | Bin 0 -> 128 bytes modex105/PALEDIT/PALEDIT.DOC | 166 ++ modex105/PALEDIT/PALEDIT.EXE | Bin 0 -> 70230 bytes modex105/PALEDIT/PALVIEW.EXE | Bin 0 -> 10358 bytes modex105/PALEDIT/PRIME.PAL | Bin 0 -> 768 bytes modex105/PALEDIT/RGB.PAL | Bin 0 -> 768 bytes modex105/PALEDIT/SCROLL.PAL | Bin 0 -> 768 bytes modex105/README.DOC | 76 + modex105/modex105.zip | Bin 0 -> 264999 bytes 73 files changed, 16365 insertions(+), 89 deletions(-) create mode 100755 modex105/ASM.BAT create mode 100755 modex105/DEMOS/BASIC7/CHARDEMO.BAS create mode 100755 modex105/DEMOS/BASIC7/MAKE-LIB.BAT create mode 100755 modex105/DEMOS/BASIC7/MODEX.BI create mode 100755 modex105/DEMOS/BASIC7/MODEX.QLB create mode 100755 modex105/DEMOS/BASIC7/TEST6.BAS create mode 100755 modex105/DEMOS/BASIC7/UASM-BC7.BAT create mode 100755 modex105/DEMOS/BASIC7/UTILS.ASM create mode 100755 modex105/DEMOS/BASIC7/UTILS.BI create mode 100755 modex105/DEMOS/C/C_UTILS.ASM create mode 100755 modex105/DEMOS/C/C_UTILS.H create mode 100755 modex105/DEMOS/C/C_UTILS.LST create mode 100755 modex105/DEMOS/C/C_UTILS.SBR create mode 100755 modex105/DEMOS/C/MODEX.ASM create mode 100755 modex105/DEMOS/C/MODEX.BI create mode 100755 modex105/DEMOS/C/MODEX.H create mode 100755 modex105/DEMOS/C/UTLS-ASM.BAT create mode 100755 modex105/DEMOS/C/X-DEMO.C create mode 100755 modex105/DEMOS/C/X-DEMO.EXE create mode 100755 modex105/DEMOS/C/X-DEMO.PRJ create mode 100755 modex105/DEMOS/C/x.exe create mode 100755 modex105/DEMOS/CHARDEMO.EXE create mode 100755 modex105/DEMOS/PASCAL/TEST5.PAS create mode 100755 modex105/DEMOS/QB45/MAKE-LIB.BAT create mode 100755 modex105/DEMOS/QB45/MODEX.BI create mode 100755 modex105/DEMOS/QB45/MODEX.QLB create mode 100755 modex105/DEMOS/QB45/TEST6A.BAS create mode 100755 modex105/DEMOS/QB45/TEST6A.EXE create mode 100755 modex105/DEMOS/QB45/UASM-QB4.BAT create mode 100755 modex105/DEMOS/QB45/UTILS.ASM create mode 100755 modex105/DEMOS/QB45/UTILS.BI create mode 100755 modex105/DEMOS/ROM_8X8.FNT create mode 100755 modex105/DEMOS/SPACEAGE.FNT create mode 100755 modex105/DEMOS/SYSTEM.FNT create mode 100755 modex105/DEMOS/TEST6.EXE create mode 100755 modex105/FONTEDIT/CHARSETS.CS create mode 100755 modex105/FONTEDIT/CSEDIT.DOC create mode 100755 modex105/FONTEDIT/CSEDIT.EXE create mode 100755 modex105/FONTEDIT/INVERSE.FNT create mode 100755 modex105/FONTEDIT/MOUSEIMG.CS create mode 100755 modex105/FONTEDIT/PALETTE.CS create mode 100755 modex105/FONTEDIT/ROM_8X8.FNT create mode 100755 modex105/FONTEDIT/SPACEAGE.FNT create mode 100755 modex105/FONTEDIT/SYSTEM.FNT create mode 100755 modex105/MODE-X.TXT create mode 100755 modex105/MODEX.ASM create mode 100755 modex105/MODEX.BI create mode 100755 modex105/MODEX.H create mode 100755 modex105/MODEX.LST create mode 100755 modex105/MODEX.SBR create mode 100755 modex105/PACKING.LST create mode 100755 modex105/PALEDIT/BAKAPI.PAL create mode 100755 modex105/PALEDIT/CHARSETS.CS create mode 100755 modex105/PALEDIT/DIAGONAL.PAL create mode 100755 modex105/PALEDIT/GAMECOLR.PAL create mode 100755 modex105/PALEDIT/MOUSEIMG.CS create mode 100755 modex105/PALEDIT/PALEDIT.DOC create mode 100755 modex105/PALEDIT/PALEDIT.EXE create mode 100755 modex105/PALEDIT/PALVIEW.EXE create mode 100755 modex105/PALEDIT/PRIME.PAL create mode 100755 modex105/PALEDIT/RGB.PAL create mode 100755 modex105/PALEDIT/SCROLL.PAL create mode 100755 modex105/README.DOC create mode 100755 modex105/modex105.zip diff --git a/16/xlib/makefile b/16/xlib/makefile index bb99010b..cba0dcac 100755 --- a/16/xlib/makefile +++ b/16/xlib/makefile @@ -19,28 +19,23 @@ XLIB_VERSION=06w # Set the compiler: either BCC or TCC # CC=wcl - LIB=wlib AFLAGS = -0 -m$(MODEL) CFLAGS = -0 -mh CPPFLAGS= -0 -m$(MODEL) -LDFLAGS = -m$(MODEL) - +LDFLAGS = -0 -m$(MODEL) #%.$(OBJ): %.c # $(CC) -c $(CFLAGS) $< -#%.$(OBJ): *.asm -# wasm $(AFLAGS) $* \ - -$(OBJ) - +# $*$(OBJ).asm#: $*.asm# +# wasm $(AFLAGS) $* \ +EXEC=demo1.exe demo2.exe demo3.exe demo4.exe demo5.exe demo6.exe demo7.exe demo8.exe# demo9.exe demo10.exe XLIBOBJS= xprintf.$(OBJ) xpoint.$(OBJ) xline.$(OBJ) xmain.$(OBJ) xpal.$(OBJ) xpbitmap.$(OBJ) xrect.$(OBJ) xtext.$(OBJ) xcbitmap.$(OBJ) xdetect.$(OBJ) xbmtools.$(OBJ) xfileio.$(OBJ) xrletool.$(OBJ) xbezier.$(OBJ) xpbmclip.$(OBJ) xvbitmap.$(OBJ) xmakevbm.$(OBJ) xmouse.$(OBJ) xcircle.$(OBJ) xclippbm.$(OBJ) xcomppbm.$(OBJ) xpolygon.$(OBJ) xvsync.$(OBJ) xfill.$(OBJ) xcbitm32.$(OBJ) - -all: demo1.exe demo2.exe demo3.exe demo4.exe demo5.exe demo6.exe demo7.exe demo8.exe# demo9.exe demo10.exe +all: $(XLIBOBJS) $(EXEC) demo1.exe : demo1.$(OBJ) $(XLIBOBJS) $(XLIBOBJS)#xlib$(XLIB_VERSION)$(MODEL).lib $(CC) $(CFLAGS) demo1.$(OBJ) $(XLIBOBJS)#xlib$(XLIB_VERSION)$(MODEL).lib @@ -108,52 +103,52 @@ demo8.$(OBJ) : demo8.c xprintf.$(OBJ) : xprintf.c $(CC) -c $(CFLAGS) xprintf.c -xpoint.$(OBJ) : xpoint.asm xpoint.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xpoint.asm xpoint.inc xlib.inc model.inc -xline.$(OBJ) : xline.asm xline.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xline.asm xline.inc xlib.inc model.inc -xmain.$(OBJ): xmain.asm xmain.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xmain.asm xmain.inc xlib.inc model.inc -xpal.$(OBJ) : xpal.asm xpal.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xpal.asm xpal.inc xlib.inc model.inc -xpbitmap.$(OBJ): xpbitmap.asm xpbitmap.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xpbitmap.asm xpbitmap.inc xlib.inc model.inc -xpbmclip.$(OBJ): xpbmclip.asm xpbmclip.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xpbmclip.asm xpbmclip.inc xlib.inc model.inc -xclippbm.$(OBJ): xclippbm.asm xclippbm.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xclippbm.asm xclippbm.inc xlib.inc model.inc -xrect.$(OBJ): xpal.asm xpal.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xpal.asm xpal.inc xlib.inc model.inc -xtext.$(OBJ): xtext.asm xtext.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xtext.asm xtext.inc xlib.inc model.inc -xcbitmap.$(OBJ): xcbitmap.asm xcbitmap.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xcbitmap.asm xcbitmap.inc xlib.inc model.inc -xcomppbm.$(OBJ): xcomppbm.asm xcomppbm.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xcomppbm.asm xcomppbm.inc xlib.inc model.inc +xpoint.$(OBJ) : xpoint.asm## xpoint.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xpoint.asm## xpoint.inc xlib.inc model.inc +xline.$(OBJ) : xline.asm# xline.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xline.asm# xline.inc xlib.inc model.inc +xmain.$(OBJ): xmain.asm# xmain.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xmain.asm# xmain.inc xlib.inc model.inc +xpal.$(OBJ) : xpal.asm# xpal.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xpal.asm# xpal.inc xlib.inc model.inc +xpbitmap.$(OBJ): xpbitmap.asm# xpbitmap.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xpbitmap.asm# xpbitmap.inc xlib.inc model.inc +xpbmclip.$(OBJ): xpbmclip.asm# xpbmclip.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xpbmclip.asm# xpbmclip.inc xlib.inc model.inc +xclippbm.$(OBJ): xclippbm.asm# xclippbm.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xclippbm.asm# xclippbm.inc xlib.inc model.inc +xrect.$(OBJ): xpal.asm# xpal.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xpal.asm# xpal.inc xlib.inc model.inc +xtext.$(OBJ): xtext.asm# xtext.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xtext.asm# xtext.inc xlib.inc model.inc +xcbitmap.$(OBJ): xcbitmap.asm# xcbitmap.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xcbitmap.asm# xcbitmap.inc xlib.inc model.inc +xcomppbm.$(OBJ): xcomppbm.asm# xcomppbm.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xcomppbm.asm# xcomppbm.inc xlib.inc model.inc xcbitm32.$(OBJ): xcbitm32.c $(CC) -c $(CFLAGS) xcbitm32.c -xdetect.$(OBJ) : xdetect.asm xdetect.inc model.inc - $(CC) -c $(CFLAGS) xdetect.asm xdetect.inc model.inc -xbmtools.$(OBJ): xbmtools.asm xbmtools.inc model.inc - $(CC) -c $(CFLAGS) xbmtools.asm xbmtools.inc model.inc -xfileio.$(OBJ) : xfileio.asm xfileio.inc model.inc - $(CC) -c $(CFLAGS) xfileio.asm xfileio.inc model.inc -xrletool.$(OBJ): xrletool.asm xrletool.inc model.inc - $(CC) -c $(CFLAGS) xrletool.asm xrletool.inc model.inc -xvbitmap.$(OBJ): xvbitmap.asm xvbitmap.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xvbitmap.asm xvbitmap.inc xlib.inc model.inc +xdetect.$(OBJ) : xdetect.asm# xdetect.inc model.inc + $(CC) -c $(CFLAGS) xdetect.asm# xdetect.inc model.inc +xbmtools.$(OBJ): xbmtools.asm# xbmtools.inc model.inc + $(CC) -c $(CFLAGS) xbmtools.asm# xbmtools.inc model.inc +xfileio.$(OBJ) : xfileio.asm# xfileio.inc model.inc + $(CC) -c $(CFLAGS) xfileio.asm# xfileio.inc model.inc +xrletool.$(OBJ): xrletool.asm# xrletool.inc model.inc + $(CC) -c $(CFLAGS) xrletool.asm# xrletool.inc model.inc +xvbitmap.$(OBJ): xvbitmap.asm# xvbitmap.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xvbitmap.asm# xvbitmap.inc xlib.inc model.inc xmakevbm.$(OBJ): xmakevbm.c xvbitmap.h $(CC) -c $(CFLAGS) xmakevbm.c xvbitmap.h -xmouse.$(OBJ) : xmouse.asm xlib.inc model.inc - $(CC) -c $(CFLAGS) xmouse.asm xlib.inc model.inc -xcircle.$(OBJ) : xcircle.asm xcircle.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xcircle.asm xcircle.inc xlib.inc model.inc -xpolygon.$(OBJ): xpolygon.asm xpolygon.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xpolygon.asm xpolygon.inc xlib.inc model.inc -xvsync.$(OBJ) : xvsync.asm xvsync.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xvsync.asm xvsync.inc xlib.inc model.inc -xfill.$(OBJ) : xfill.asm xfill.inc xlib.inc model.inc - $(CC) -c $(CFLAGS) xfill.asm xfill.inc xlib.inc model.inc +xmouse.$(OBJ) : xmouse.asm# xlib.inc model.inc + $(CC) -c $(CFLAGS) xmouse.asm# xlib.inc model.inc +xcircle.$(OBJ) : xcircle.asm# xcircle.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xcircle.asm# xcircle.inc xlib.inc model.inc +xpolygon.$(OBJ): xpolygon.asm# xpolygon.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xpolygon.asm# xpolygon.inc xlib.inc model.inc +xvsync.$(OBJ) : xvsync.asm# xvsync.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xvsync.asm# xvsync.inc xlib.inc model.inc +xfill.$(OBJ) : xfill.asm# xfill.inc xlib.inc model.inc + $(CC) -c $(CFLAGS) xfill.asm# xfill.inc xlib.inc model.inc # #other~ diff --git a/16/xlib/model.inc b/16/xlib/model.inc index afcdced4..0aded8e9 100755 --- a/16/xlib/model.inc +++ b/16/xlib/model.inc @@ -1,21 +1,20 @@ -IFDEF s - DISPLAY "XLIB04 Small Model" - .model small -ELSE - IFDEF c - DISPLAY "XLIB04 Compact Model" - .model compact - ELSE - IFDEF l - DISPLAY "XLIB04 Large Model" - .model large - ELSE - DISPLAY "WARNING: Model was not defined at the command line." - DISPLAY " Using default small model ie /ds " - DISPLAY " Include in TASM commandline either /ds, /dc or /dl" - .model small - ENDIF - ENDIF -ENDIF - - \ No newline at end of file +/*;IFDEF s +; DISPLAY "XLIB04 Small Model" +; .model small +;ELSE +; IFDEF c +; DISPLAY "XLIB04 Compact Model" +; .model compact +; ELSE +; IFDEF l +; DISPLAY "XLIB04 Large Model" +; .model large +; ELSE +; DISPLAY "WARNING: Model was not defined at the command line." +; DISPLAY " Using default small model ie /ds " +; DISPLAY " Include in TASM commandline either /ds, /dc or /dl" +;; .model small +; ENDIF +; ENDIF +;ENDIF +*/ diff --git a/16/xlib/xbmtools.asm b/16/xlib/xbmtools.asm index 4638d42a..f39c9a32 100755 --- a/16/xlib/xbmtools.asm +++ b/16/xlib/xbmtools.asm @@ -43,7 +43,7 @@ COMMENT $ $ LOCALS -.286 +.8086 include model.inc include xbmtools.inc diff --git a/16/xlib/xdetect.asm b/16/xlib/xdetect.asm index 4b921682..48ad5f77 100755 --- a/16/xlib/xdetect.asm +++ b/16/xlib/xdetect.asm @@ -15,7 +15,7 @@ ; teg@bart.dsto.gov.au ;----------------------------------------------------------------------- LOCALS -.286 +.8086 include model.inc include xdetect.inc @@ -266,4 +266,4 @@ _x_mousedriver proc _x_mousedriver endp -end \ No newline at end of file +end diff --git a/16/xlib/xfileio.asm b/16/xlib/xfileio.asm index 08546127..40a07775 100755 --- a/16/xlib/xfileio.asm +++ b/16/xlib/xfileio.asm @@ -22,7 +22,7 @@ COMMENT $ $ LOCALS -.286 +.8086 include model.inc include xfileio.inc diff --git a/16/xlib/xlib.inc b/16/xlib/xlib.inc index 58e256dd..3dbfa758 100755 --- a/16/xlib/xlib.inc +++ b/16/xlib/xlib.inc @@ -19,8 +19,8 @@ ; -LOCALS -.286 +;.LOCALS +;.8086 ; First lets find out what memory model to use diff --git a/16/xlib/xpoint.asm b/16/xlib/xpoint.asm index a2be8bfe..f6bed961 100755 --- a/16/xlib/xpoint.asm +++ b/16/xlib/xpoint.asm @@ -33,7 +33,7 @@ include xpoint.inc ; ; -_x_put_pix proc +_x_put_pix proc ARG X:word,Y:word,PgOfs:word,Color:word push bp ;preserve caller's stack frame mov bp,sp ;point to local stack frame @@ -41,7 +41,8 @@ _x_put_pix proc mov ax,[_ScrnLogicalByteWidth] mul [Y] ;offset of pixel's scan line in page mov bx,[X] - shr bx,2 ;X/4 = offset of pixel in scan line + shr bx,1 ;X/4 = offset of pixel in scan line + shr bx,1 ;X/4 = offset of pixel in scan line add bx,ax ;offset of pixel in page add bx,[PgOfs] ;offset of pixel in display memory mov ax,SCREEN_SEG @@ -100,8 +101,3 @@ _x_get_pix proc ret _x_get_pix endp end - - - end - - \ No newline at end of file diff --git a/16/xlib/xpoint.inc b/16/xlib/xpoint.inc index 58b36dce..7ed175c8 100755 --- a/16/xlib/xpoint.inc +++ b/16/xlib/xpoint.inc @@ -17,4 +17,3 @@ global _x_put_pix :proc global _x_get_pix :proc - \ No newline at end of file diff --git a/16/xlib/xrletool.asm b/16/xlib/xrletool.asm index 5a9143fe..0c70623e 100755 --- a/16/xlib/xrletool.asm +++ b/16/xlib/xrletool.asm @@ -83,7 +83,7 @@ structure in any depth should have a section on RLE compression. $ LOCALS -.286 +.8086 include model.inc include xrletool.inc @@ -652,4 +652,4 @@ _x_file_RLDecode endp end - \ No newline at end of file + diff --git a/modex105/ASM.BAT b/modex105/ASM.BAT new file mode 100755 index 00000000..2e5785f9 --- /dev/null +++ b/modex105/ASM.BAT @@ -0,0 +1 @@ +MASM modex,modex,modex,nul \ No newline at end of file diff --git a/modex105/DEMOS/BASIC7/CHARDEMO.BAS b/modex105/DEMOS/BASIC7/CHARDEMO.BAS new file mode 100755 index 00000000..627e3278 --- /dev/null +++ b/modex105/DEMOS/BASIC7/CHARDEMO.BAS @@ -0,0 +1,164 @@ +DEFINT A-Z +DECLARE SUB PRINT.STRING (Text$, Xpos%, Ypos%, Colour%) +DECLARE FUNCTION MakePal$ (Red%, Green%, Blue%) +DECLARE SUB LOAD.FONT (FontFile$, FontNum%) +DECLARE SUB ERROR.OUT (Text$) + + REM $INCLUDE: 'MODEX.BI' + + REM $INCLUDE: 'UTILS.BI' + +TYPE FONT + SetData AS STRING * 1024 +END TYPE + + +TYPE VGAPalette + PalData AS STRING * 768 +END TYPE + + + ' Alternate form of LOAD_DAC_REGISTERS so we can pass an offset into + ' a String instead of the Address of the String + +DECLARE SUB LOAD.DACS ALIAS "LOAD_DAC_REGISTERS" (BYVAL Addr&, BYVAL StartReg%, BYVAL EndReg%, BYVAL VSync%) + + + ' + 'MODE X DEMO of Multiple Character Sets and Block Color Cycling + ' + 'By Matt Pritchard + ' + +COMMON SHARED CharSet() AS FONT + +DIM Pal AS VGAPalette + + REM $DYNAMIC + +DIM SHARED CharSet(0 TO 3) AS FONT + + + LOAD.FONT "SYSTEM.FNT", 0 + LOAD.FONT "ROM_8x8.FNT", 1 + LOAD.FONT "SPACEAGE.FNT", 2 + + + IF SET.MODEX(Mode320x240) = False THEN + ERROR.OUT "ERROR SETTING MODE X" + END IF + + + A$ = "": B$ = "" + FOR X = 0 TO 31: A$ = A$ + MakePal$(31 - X, X, 0): NEXT X + FOR X = 0 TO 31: A$ = A$ + MakePal$(0, 31 - X, X): NEXT X + FOR X = 0 TO 31: A$ = A$ + MakePal$(X, 0, 31 - X): NEXT X + + FOR X = 0 TO 31: B$ = B$ + MakePal$(31 - X, X, X): NEXT X + FOR X = 0 TO 31: B$ = B$ + MakePal$(X, 31 - X, X): NEXT X + FOR X = 0 TO 31: B$ = B$ + MakePal$(X, X, 31 - X): NEXT X + + Black$ = STRING$(192, 0) + White$ = STRING$(128 * 3, 48) + + Pal1$ = Black$ + A$ + A$ + B$ + B$ + A$ + + LOAD.DACS SSEGADD(Black$), 64, 127, 1 + LOAD.DACS SSEGADD(Black$), 20, 63, 0 + + LOAD.DACS SSEGADD(White$), 128, 255, 0 + + '*** Background *** + + FOR X = 0 TO 319 + FOR Y = 0 TO 239 + IF ((X + Y) AND 1) = 1 THEN SET.POINT X, Y, 64 + X \ 5 ELSE SET.POINT X, Y, 20 + Y \ 6 + NEXT Y + NEXT X + + '*** Draw Font Displays *** + + PRINT.STRING "FONT: SYSTEM.FNT", 11, 7, 15 + PRINT.STRING "FONT: ROM_8x8.FNT", 11, 17, 15 + PRINT.STRING "FONT: SPACEAGE.FNT", 11, 27, 15 + PRINT.STRING "PRESS ANY KEY TO CONTINUE", 8, 29, 14 + + + FOR F = 0 TO 2 + SET.DISPLAY.FONT CharSet(F), 1 + Yp = F * 80 + 10 + FOR Y = 0 TO 96 STEP 32 + FOR X = 0 TO 31 + TGPRINTC 128 + Y + X, X * 10 + 1, Yp, 128 + Y + NEXT X + Yp = Yp + 10 + NEXT Y + NEXT F + + DO + LOOP UNTIL SCAN.KEYBOARD + + Offset = 0 + Restart = 192 + MaxOfs = 192 + 96 * 6 + + Delay = 100 + + Offset2 = 0 + Offset2Dir = 3 + Offset2Min = 192 + Offset2Max = Offset2Min + 192 * 6 + + DO + LOAD.DACS SSEGADD(Pal1$) + Offset, 64, 127, 1 + Offset = Offset + 3 + IF Offset >= MaxOfs THEN Offset = Restart + IF Delay THEN + Delay = Delay - 1 + ELSE + LOAD.DACS SSEGADD(Pal1$) + Offset2, 20, 60, 0 + IF Offset2 = Offset2Max THEN Offset2Dir = -3 + IF Offset2 = Offset2Min THEN Offset2Dir = 3 + Offset2 = Offset2 + Offset2Dir + END IF + + LOOP UNTIL SCAN.KEYBOARD + + ERROR.OUT "DEMO OVER" + +REM $STATIC +SUB ERROR.OUT (Text$) + + SET.VIDEO.MODE 3 + + DOS.PRINT Text$ + + END + +END SUB + +SUB LOAD.FONT (FontFile$, FontNum) STATIC + + IF LEN(DIR$(FontFile$)) = 0 THEN ERROR.OUT "FILE NOT FOUND: " + FontFile$ + + OPEN FontFile$ FOR BINARY AS #1 + + SEEK #1, 1 + GET #1, , CharSet(FontNum) + + CLOSE #1 + +END SUB + +FUNCTION MakePal$ (Red, Green, Blue) STATIC + + MakePal$ = CHR$(Red) + CHR$(Green) + CHR$(Blue) + +END FUNCTION + +SUB PRINT.STRING (Text$, Xpos, Ypos, Colour) + + TPRINT.STR SSEG(Text$), SADD(Text$), LEN(Text$), Xpos * 8, Ypos * 8, Colour + +END SUB + diff --git a/modex105/DEMOS/BASIC7/MAKE-LIB.BAT b/modex105/DEMOS/BASIC7/MAKE-LIB.BAT new file mode 100755 index 00000000..fc0b3b5c --- /dev/null +++ b/modex105/DEMOS/BASIC7/MAKE-LIB.BAT @@ -0,0 +1,5 @@ +ECHO ... Building MODEX.QLB for BASIC PDS 7.1 +LIB MODEX -+MODEX,, +LIB MODEX -+UTILS,, +DEL MODEX.BAK +LINK /Q MODEX+UTILS, MODEX.QLB, NUL, C:\BC7\LIB\QBXQLB.LIB; diff --git a/modex105/DEMOS/BASIC7/MODEX.BI b/modex105/DEMOS/BASIC7/MODEX.BI new file mode 100755 index 00000000..6b1d7afe --- /dev/null +++ b/modex105/DEMOS/BASIC7/MODEX.BI @@ -0,0 +1,63 @@ + + ' ===== SCREEN RESOLUTIONS ===== + +CONST Mode320x200 = 0, Mode320x400 = 1 +CONST Mode360x200 = 2, Mode360x400 = 3 +CONST Mode320x240 = 4, Mode320x480 = 5 +CONST Mode360x240 = 6, Mode360x480 = 7 + + ' ===== MODE X SETUP ROUTINES ===== + +DECLARE FUNCTION SET.VGA.MODEX% ALIAS "SET_VGA_MODEX" (BYVAL ModeType%, BYVAL MaxXpos%, BYVAL MaxYpos%, BYVAL Pages%) +DECLARE FUNCTION SET.MODEX% ALIAS "SET_MODEX" (BYVAL Mode%) + + ' ===== BASIC GRAPHICS PRIMITIVES ===== + +DECLARE SUB CLEAR.VGA.SCREEN ALIAS "CLEAR_VGA_SCREEN" (BYVAL ColorNum%) +DECLARE SUB SET.POINT ALIAS "SET_POINT" (BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorNum%) +DECLARE FUNCTION READ.POINT% ALIAS "READ_POINT" (BYVAL Xpos%, BYVAL Ypos%) +DECLARE SUB FILL.BLOCK ALIAS "FILL_BLOCK" (BYVAL Xpos1%, BYVAL Ypos1%, BYVAL Xpos2%, BYVAL Ypos2%, BYVAL ColorNum%) +DECLARE SUB DRAW.LINE ALIAS "DRAW_LINE" (BYVAL Xpos1%, BYVAL Ypos1%, BYVAL Xpos2%, BYVAL Ypos2%, BYVAL ColorNum%) + + ' ===== DAC COLOR REGISTER ROUTINES ===== + +DECLARE SUB SET.DAC.REGISTER ALIAS "SET_DAC_REGISTER" (BYVAL RegNo%, BYVAL Red%, BYVAL Green%, BYVAL Blue%) +DECLARE SUB GET.DAC.REGISTER ALIAS "GET_DAC_REGISTER" (BYVAL RegNo%, Red%, Green%, Blue%) +DECLARE SUB LOAD.DAC.REGISTERS ALIAS "LOAD_DAC_REGISTERS" (SEG PalData AS ANY, BYVAL StartReg%, BYVAL EndReg%, BYVAL VSync%) +DECLARE SUB READ.DAC.REGISTERS ALIAS "READ_DAC_REGISTERS" (SEG PalData AS ANY, BYVAL StartReg%, BYVAL EndReg%) + + + ' ===== PAGE FLIPPING AND SCROLLING ROUTINES ===== + +DECLARE SUB SET.ACTIVE.PAGE ALIAS "SET_ACTIVE_PAGE" (BYVAL PageNo%) +DECLARE FUNCTION GET.ACTIVE.PAGE% ALIAS "GET_ACTIVE_PAGE" +DECLARE SUB SET.DISPLAY.PAGE ALIAS "SET_DISPLAY_PAGE" (BYVAL PageNo%) +DECLARE FUNCTION GET.DISPLAY.PAGE% ALIAS "GET_DISPLAY_PAGE" +DECLARE SUB SET.WINDOW ALIAS "SET_WINDOW" (BYVAL DisplayPage%, BYVAL XOffset%, BYVAL YOffset%) +DECLARE FUNCTION GET.X.OFFSET% ALIAS "GET_X_OFFSET" () +DECLARE FUNCTION GET.Y.OFFSET% ALIAS "GET_Y_OFFSET" () +DECLARE SUB SYNC.DISPLAY ALIAS "SYNC_DISPLAY" + + ' ===== TEXT DISPLAY ROUTINES ===== + +DECLARE SUB GPRINTC (BYVAL CharacterNum%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%, BYVAL ColorB%) +DECLARE SUB TGPRINTC (BYVAL CharacterNum%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%) +DECLARE SUB PRINT.STR ALIAS "PRINT_STR" (BYVAL StrSeg%, BYVAL StrOfs%, BYVAL MaxLen%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%, BYVAL ColorB%) +DECLARE SUB TPRINT.STR ALIAS "TPRINT_STR" (BYVAL StrSeg%, BYVAL StrOfs%, BYVAL MaxLen%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%) +DECLARE SUB SET.DISPLAY.FONT ALIAS "SET_DISPLAY_FONT" (SEG FontData AS ANY, BYVAL FontNumber%) + + ' ===== BITMAP (SPRITE) DISPLAY ROUTINES ===== + +DECLARE SUB DRAW.BITMAP ALIAS "DRAW_BITMAP" (SEG Image AS ANY, BYVAL Xpos%, BYVAL Ypos%, BYVAL xWidth%, BYVAL Height%) +DECLARE SUB TDRAW.BITMAP ALIAS "TDRAW_BITMAP" (SEG Image AS ANY, BYVAL Xpos%, BYVAL Ypos%, BYVAL xWidth%, BYVAL Height%) + + ' ==== VIDEO MEMORY to VIDEO MEMORY COPY ROUTINES ===== + +DECLARE SUB COPY.PAGE ALIAS "COPY_PAGE" (BYVAL SourcePage%, BYVAL DestPage%) +DECLARE SUB COPY.BITMAP ALIAS "COPY_BITMAP" (BYVAL SourcePage%, BYVAL X1%, BYVAL Y1%, BYVAL X2%, BYVAL Y2%, BYVAL DestPage%, BYVAL DestX1%, BYVAL DestY1%) + + + + + + diff --git a/modex105/DEMOS/BASIC7/MODEX.QLB b/modex105/DEMOS/BASIC7/MODEX.QLB new file mode 100755 index 0000000000000000000000000000000000000000..70d22db30925923e0b60160c34b8aa97949e70d0 GIT binary patch literal 11141 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 literal 0 HcmV?d00001 diff --git a/modex105/DEMOS/BASIC7/TEST6.BAS b/modex105/DEMOS/BASIC7/TEST6.BAS new file mode 100755 index 00000000..220a67ba --- /dev/null +++ b/modex105/DEMOS/BASIC7/TEST6.BAS @@ -0,0 +1,562 @@ +'File: TEST6.BAS +'Descp.: A Mode "X" demonstration +'Author: Matt Pritchard +'Date: 14 April, 1993 +' +DECLARE SUB DEMO.RES (Mode%, Xmax%, Ymax%) +DECLARE SUB ERROR.OUT (Message$) +DECLARE FUNCTION GET.KEY% () +DECLARE SUB LOAD.SHAPES () +DECLARE SUB PAGE.DEMO () +DECLARE SUB PRINT.TEXT (Text$, Xpos%, Ypos%, ColorF%, ColorB%) +DECLARE SUB TPRINT.TEXT (Text$, Xpos%, Ypos%, ColorF%) +DEFINT A-Z + + +TYPE ShapeType + ImgData AS STRING * 512 + xWidth AS INTEGER + yWidth AS INTEGER +END TYPE + +TYPE Sprite + Xpos AS INTEGER + Ypos AS INTEGER + XDir AS INTEGER + YDir AS INTEGER + Shape AS INTEGER +END TYPE + + +CONST MaxShapes = 32 + + REM $INCLUDE: 'UTILS.BI' + REM $INCLUDE: 'MODEX.BI' + +DIM SHARED Img(32) AS ShapeType +COMMON SHARED Img() AS ShapeType + + + CALL INIT.RANDOM + + CALL LOAD.SHAPES + + CALL DEMO.RES(Mode320x200, 320, 200) + CALL DEMO.RES(Mode320x400, 320, 400) + + CALL DEMO.RES(Mode360x200, 360, 200) + CALL DEMO.RES(Mode360x400, 360, 400) + + CALL DEMO.RES(Mode320x240, 320, 240) + CALL DEMO.RES(Mode320x480, 320, 480) + + CALL DEMO.RES(Mode360x240, 360, 240) + CALL DEMO.RES(Mode360x480, 360, 480) + + CALL PAGE.DEMO + + SET.VIDEO.MODE 3 + DOS.PRINT "THIS MODE X DEMO IS FINISHED" + END + +SUB DEMO.RES (Mode, Xmax, Ymax) + + IF SET.MODEX%(Mode) = 0 THEN + ERROR.OUT "Unable to SET_MODEX" + STR$(Mode) + END IF + + XCenter = Xmax \ 2 + + X1 = 10 + Y1 = 10 + X2 = Xmax - 1 + Y2 = Ymax - 1 + + FOR Z = 0 TO 3 + Colr = 31 - Z * 2 + DRAW.LINE X1 + Z, Y1 + Z, X2 - Z, Y1 + Z, Colr + DRAW.LINE X1 + Z, Y1 + Z, X1 + Z, Y2 - Z, Colr + DRAW.LINE X1 + Z, Y2 - Z, X2 - Z, Y2 - Z, Colr + DRAW.LINE X2 - Z, Y1 + Z, X2 - Z, Y2 - Z, Colr + NEXT Z + + XChars = Xmax \ 10 + YChars = Ymax \ 10 + + FOR X = 0 TO XChars - 1 + TGPRINTC 48 + ((X + 1) MOD 10), X * 10 + 1, 1, 9 + ((X \ 8) MOD 7) + DRAW.LINE X * 10 + 9, 0, X * 10 + 9, 3, 15 + NEXT X + + FOR Y = 0 TO YChars - 1 + TGPRINTC 48 + ((Y + 1) MOD 10), 1, Y * 10 + 1, 9 + ((Y \ 10) MOD 7) + DRAW.LINE 0, Y * 10 + 9, 3, Y * 10 + 9, 15 + NEXT Y + + ' Draw Lines + + FOR X = 0 TO 63 + N = 15 + X * .75 + SET.DAC.REGISTER 64 + X, N, N, N + SET.DAC.REGISTER 128 + X, 0, N, N + + DRAW.LINE 103 - X, 60, 40 + X, 123, 64 + X + DRAW.LINE 40, 60 + X, 103, 123 - X, 128 + X + + NEXT X + TPRINT.TEXT "LINE TEST", 37, 130, c.BLUE + + Y = 60: Gap = 0 + FOR X = 0 TO 9 + FILL.BLOCK 120, Y, 120 + X, Y + Gap, 64 + X + FILL.BLOCK 140 - (15 - X), Y, 150 + X, Y + Gap, 230 + X + FILL.BLOCK 170 - (15 - X), Y, 170, Y + Gap, 128 + X + Y = Y + Gap + 2 + Gap = Gap + 1 + NEXT X + TPRINT.TEXT "FILL TEST", 110, 46, c.GREEN + + + FOR X = 190 TO 250 STEP 2 + FOR Y = 60 TO 122 STEP 2 + SET.POINT X, Y, X + Y + X + Y + NEXT Y + NEXT X + + TPRINT.TEXT "PIXEL TEST", 182, 130, c.RED + + FOR X = 190 TO 250 STEP 2 + FOR Y = 60 TO 122 STEP 2 + IF READ.POINT(X, Y) <> ((X + Y + X + Y) AND 255) THEN + ERROR.OUT "READ.PIXEL Failure" + END IF + NEXT Y + NEXT X + + + + Msg$ = " This is a MODE X demo " + PRINT.TEXT Msg$, XCenter - (LEN(Msg$) * 4), 20, c.bRED, c.BLUE + Msg$ = "Screen Resolution is by " + Xp = XCenter - (LEN(Msg$) * 4) + PRINT.TEXT Msg$, Xp, 30, c.bGREEN, c.BLACK + + PRINT.TEXT LTRIM$(STR$(Xmax)), Xp + 8 * 21, 30, c.bPURPLE, c.BLACK + PRINT.TEXT LTRIM$(STR$(Ymax)), Xp + 8 * 28, 30, c.bWHITE, c.BLACK + + FOR X = 0 TO 15 + SET.DAC.REGISTER 230 + X, 63 - X * 4, 0, 15 + X * 3 + DRAW.LINE 30 + X, Ymax - 6 - X, Xmax - 20 - X, Ymax - 6 - X, 230 + X + NEXT X + TPRINT.TEXT "Press to Continue", XCenter - (26 * 4), Ymax - 18, c.YELLOW + + X = GET.KEY% + IF X = KyESC THEN ERROR.OUT "ABORT" + +END SUB + +SUB ERROR.OUT (Message$) + + SET.VIDEO.MODE 3 + DOS.PRINT Message$ + END + +END SUB + +FUNCTION GET.KEY% + + DO + X = SCAN.KEYBOARD + LOOP UNTIL X + + GET.KEY% = X + +END FUNCTION + +SUB LOAD.SHAPES + +DIM Grid(1 TO 32, 1 TO 32) + + FOR Shape = 0 TO MaxShapes - 1 + + FOR Y = 1 TO 32 + FOR X = 1 TO 32 + Grid(X, Y) = 0 + NEXT X + NEXT Y + + Style = RANDOM.INT(6) + Colour = 1 + RANDOM.INT(15) + + SELECT CASE Style + + CASE 0: ' Solid Box + + DO + xWidth = 3 + RANDOM.INT(30) + yWidth = 3 + RANDOM.INT(30) + LOOP UNTIL ((xWidth * yWidth) <= 512) + + FOR Y = 1 TO yWidth + FOR X = 1 TO xWidth + Grid(X, Y) = Colour + NEXT X + NEXT Y + + CASE 1: ' Hollow Box + + DO + xWidth = 5 + RANDOM.INT(28) + yWidth = 5 + RANDOM.INT(28) + LOOP UNTIL ((xWidth * yWidth) <= 512) + + FOR Y = 1 TO yWidth + FOR X = 1 TO xWidth + Grid(X, Y) = Colour + NEXT X + NEXT Y + + HollowX = 1 + RANDOM.INT(xWidth \ 2 - 1) + HollowY = 1 + RANDOM.INT(yWidth \ 2 - 1) + + FOR Y = HollowY + 1 TO yWidth - HollowY + FOR X = HollowX + 1 TO xWidth - HollowX + Grid(X, Y) = nil + NEXT X + NEXT Y + + CASE 2: ' Solid Diamond + + xWidth = 3 + 2 * RANDOM.INT(10) + yWidth = xWidth + Centre = xWidth \ 2 + + FOR Y = 0 TO Centre + FOR X = 0 TO Y + Grid(Centre - X + 1, Y + 1) = Colour + Grid(Centre + X + 1, Y + 1) = Colour + Grid(Centre - X + 1, yWidth - Y) = Colour + Grid(Centre + X + 1, yWidth - Y) = Colour + NEXT X + NEXT Y + + + CASE 3: ' Hollow Diamond + + + xWidth = 3 + 2 * RANDOM.INT(10) + yWidth = xWidth + Centre = xWidth \ 2 + sWidth = RANDOM.INT(Centre) + + FOR Y = 0 TO Centre + FOR X = 0 TO Y + IF X + (Centre - Y) >= sWidth THEN + Grid(Centre - X + 1, Y + 1) = Colour + Grid(Centre + X + 1, Y + 1) = Colour + Grid(Centre - X + 1, yWidth - Y) = Colour + Grid(Centre + X + 1, yWidth - Y) = Colour + END IF + NEXT X + NEXT Y + + CASE 4: ' Ball + + xWidth = 7 + 2 * RANDOM.INT(8) + yWidth = xWidth + Centre = 1 + xWidth \ 2 + + FOR Y = 1 TO yWidth + FOR X = 1 TO xWidth + D = SQR(((Centre - X) * (Centre - X)) + ((Centre - Y) * (Centre - Y))) + IF D < Centre THEN Grid(X, Y) = 150 + Colour * 2 + D * 3 + NEXT X + NEXT Y + + CASE 5: ' Ball + + + xWidth = 7 + 2 * RANDOM.INT(8) + yWidth = xWidth + Centre = 1 + xWidth \ 2 + sWidth = RANDOM.INT(xWidth) + + FOR Y = 1 TO yWidth + FOR X = 1 TO xWidth + D = SQR(((Centre - X) * (Centre - X)) + ((Centre - Y) * (Centre - Y))) + IF D < Centre AND D >= sWidth THEN Grid(X, Y) = 150 + Colour * 2 + D * 3 + NEXT X + NEXT Y + + END SELECT + + Img(Shape).xWidth = xWidth + Img(Shape).yWidth = yWidth + + A$ = STRING$(xWidth * yWidth, nil) + + c = 1 + FOR Y = 1 TO yWidth + FOR X = 1 TO xWidth + MID$(A$, c, 1) = CHR$(Grid(X, Y)) + c = c + 1 + NEXT X + NEXT Y + + Img(Shape).ImgData = A$ + + + NEXT Shape + +END SUB + +SUB PAGE.DEMO + +CONST MaxSprites = 64 + +DIM Obj(MaxSprites) AS Sprite +DIM LastX(MaxSprites, 1), LastY(MaxSprites, 1) +DIM LastObjects(1) + + ScreenX = 360: ScreenY = 240 + + IF SET.VGA.MODEX%(Mode320x200, ScreenX, ScreenY, 3) = 0 THEN + ERROR.OUT "Unable to SET_VGA_MODEX" + STR$(Mode) + END IF + + SET.ACTIVE.PAGE 0 + + CLEAR.VGA.SCREEN c.BLACK + + PRINT.TEXT "This is a Test of the Following Functions:", 10, 9, c.bWHITE, c.BLACK + + DRAW.LINE 10, 18, 350, 18, c.YELLOW + PRINT.TEXT "SET_ACTIVE_PAGE", 10, 20, c.bBLUE, c.BLACK + PRINT.TEXT "SET_DISPLAY_PAGE", 10, 30, c.GREEN, c.BLACK + PRINT.TEXT "SET_DAC_REGISTER", 10, 40, c.RED, c.BLACK + PRINT.TEXT "CLEAR_VGA_SCREEN", 10, 50, c.CYAN, c.BLACK + + PRINT.TEXT "TDRAW_BITMAP", 10, 60, c.PURPLE, c.BLACK + PRINT.TEXT "COPY_PAGE", 10, 70, c.GREEN, c.BLACK + PRINT.TEXT "COPY_BITMAP", 10, 80, c.CYAN, c.BLACK + + PRINT.TEXT "GPRINTC", 10, 90, c.BLUE, c.BLACK + PRINT.TEXT "TGPRINTC", 10, 100, c.GREEN, c.BLACK + PRINT.TEXT "SET_WINDOW", 10, 110, c.RED, c.BLACK + + PRINT.TEXT "VIRTUAL SCREEN SIZES", 190, 20, c.bBLUE, c.BLACK + PRINT.TEXT " SMOOTH SCROLLING", 190, 30, c.GREEN, c.BLACK + PRINT.TEXT " SPRITE ANIMATION", 190, 40, c.CYAN, c.BLACK + PRINT.TEXT " PAGE FLIPPING", 190, 50, c.RED, c.BLACK + PRINT.TEXT " COLOR CYCLING", 190, 60, c.PURPLE, c.BLACK + + + FOR X = 0 TO 60 + SET.DAC.REGISTER 50 + X, 3 + X, 0, 60 - X + SET.DAC.REGISTER 150 + X, 3 + X, 0, 60 - X + NEXT X + + c = 0: DC = 1 + FOR X = 0 TO ScreenX \ 2 + DRAW.LINE ScreenX \ 2 - 1, ScreenY \ 4, X, ScreenY - 1, c + 50 + DRAW.LINE ScreenX \ 2, ScreenY \ 4, ScreenX - X - 1, ScreenY - 1, c + 50 + c = c + DC + IF c = 0 OR c = 60 THEN DC = -DC + NEXT X + + TPRINT.TEXT "Press to Continue", 72, 190, c.bWHITE + TPRINT.TEXT "< > = Faster < > = Slower", 72, 204, c.bGREEN + TPRINT.TEXT "< > = Fewer Shapes < > = More Shapes", 32, 218, c.bCYAN + + TGPRINTC 43, 80, 204, c.YELLOW + TGPRINTC 45, 200, 204, c.YELLOW + + TGPRINTC 25, 40, 218, c.YELLOW + TGPRINTC 24, 200, 218, c.YELLOW + + COPY.PAGE 0, 1 + COPY.PAGE 0, 2 + + FOR X = 1 TO MaxSprites + DO + Obj(X).XDir = RANDOM.INT(7) - 3 + Obj(X).YDir = RANDOM.INT(7) - 3 + LOOP WHILE (Obj(X).XDir = 0 AND Obj(X).YDir = 0) + + Obj(X).Shape = X MOD MaxShapes + + SpriteX = Img(Obj(X).Shape).xWidth + SpriteY = Img(Obj(X).Shape).yWidth + + Obj(X).Xpos = 1 + RANDOM.INT(ScreenX - SpriteX - 2) + Obj(X).Ypos = 1 + RANDOM.INT(ScreenY - SpriteY - 2) + + LastX(X, 0) = Obj(X).Xpos + LastX(X, 1) = Obj(X).Xpos + LastY(X, 0) = Obj(X).Ypos + LastY(X, 1) = Obj(X).Ypos + NEXT X + + CurrentPage = 0 + + 'View Shift... + + ViewX = 0 + ViewY = 0 + ViewMax = 3 + ViewCnt = 0 + ViewXD = 1 + ViewYD = 1 + + SetColor = 3: SDir = 1 + PrevColor = 0: PDir = 1 + + VisObjects = MaxSprites \ 2 + LastObjects(0) = 0 + LastObjects(1) = 0 + +DRAW.LOOP: + + + SET.ACTIVE.PAGE CurrentPage + + ' Erase Old Images + + FOR X = 1 TO LastObjects(CurrentPage) + + X1 = LastX(X, CurrentPage) AND &HFFFC + Y1 = LastY(X, CurrentPage) + X2 = ((LastX(X, CurrentPage) + Img(Obj(X).Shape).xWidth)) OR 3 + Y2 = Y1 + Img(Obj(X).Shape).yWidth - 1 + + COPY.BITMAP 2, X1, Y1, X2, Y2, CurrentPage, X1, Y1 + + NEXT X + + ' Draw new images + + FOR X = 1 TO VisObjects + + SpriteX = Img(Obj(X).Shape).xWidth + SpriteY = Img(Obj(X).Shape).yWidth + + ' Move Sprite + +REDOX: + NewX = Obj(X).Xpos + Obj(X).XDir + IF NewX < 0 OR NewX + SpriteX > ScreenX THEN + Obj(X).XDir = -Obj(X).XDir + IF RANDOM.INT(20) = 1 THEN + DO + Obj(X).XDir = RANDOM.INT(7) - 3 + Obj(X).YDir = RANDOM.INT(7) - 3 + LOOP WHILE (Obj(X).XDir = 0 AND Obj(X).YDir = 0) + GOTO REDOX + END IF + END IF + Obj(X).Xpos = Obj(X).Xpos + Obj(X).XDir + +REDOY: + NewY = Obj(X).Ypos + Obj(X).YDir + IF NewY < 0 OR NewY + SpriteY > ScreenY THEN + Obj(X).YDir = -Obj(X).YDir + IF RANDOM.INT(20) = 1 THEN + DO + Obj(X).XDir = RANDOM.INT(7) - 3 + Obj(X).YDir = RANDOM.INT(7) - 3 + LOOP WHILE (Obj(X).XDir = 0 AND Obj(X).YDir = 0) + GOTO REDOY + END IF + END IF + Obj(X).Ypos = Obj(X).Ypos + Obj(X).YDir + + 'Draw Sprite + + TDRAW.BITMAP Img(Obj(X).Shape), Obj(X).Xpos, Obj(X).Ypos, SpriteX, SpriteY + + LastX(X, CurrentPage) = Obj(X).Xpos + LastY(X, CurrentPage) = Obj(X).Ypos + + NEXT X + + LastObjects(CurrentPage) = VisObjects + + ' Pan Screen Back & Forth + + ViewCnt = ViewCnt + 1 + IF ViewCnt >= ViewMax THEN + ViewX = ViewX + ViewXD + IF ViewX = 0 OR ViewX = 39 THEN ViewXD = -ViewXD + IF ViewXD < 0 THEN + ViewY = ViewY + ViewYD + IF ViewY = 0 OR ViewY = 39 THEN ViewYD = -ViewYD + END IF + + SET.WINDOW CurrentPage, ViewX, ViewY + + ViewCnt = 0 + ELSE + SET.DISPLAY.PAGE CurrentPage + END IF + + ' Cycle Colors + + SET.DAC.REGISTER 50 + PrevColor, 3 + PrevColor, 0, 60 - PrevColor + SET.DAC.REGISTER 50 + SetColor, SetColor, 10, 63 - SetColor + + SET.DAC.REGISTER 150 + PrevColor, 3 + PrevColor, 0, 60 - PrevColor + SET.DAC.REGISTER 150 + SetColor, 63, 63, SetColor + + SetColor = SetColor + SDir + IF SetColor = 60 OR SetColor = 0 THEN SDir = -SDir + + PrevColor = PrevColor + PDir + IF PrevColor = 60 OR PrevColor = 0 THEN PDir = -PDir + + CurrentPage = 1 - CurrentPage + + Code = SCAN.KEYBOARD + + IF Code = False THEN GOTO DRAW.LOOP + + IF Code = KyPlus THEN + IF ViewMax < 12 THEN ViewMax = ViewMax + 1 + GOTO DRAW.LOOP + END IF + + IF Code = KyMinus THEN + IF ViewMax > 1 THEN ViewMax = ViewMax - 1 + IF ViewCnt >= ViewMax THEN ViewCnt = 0 + GOTO DRAW.LOOP + END IF + + IF Code = KyUp THEN + IF VisObjects < MaxSprites THEN VisObjects = VisObjects + 1 + GOTO DRAW.LOOP + END IF + + IF Code = KyDown THEN + IF VisObjects > 1 THEN VisObjects = VisObjects - 1 + GOTO DRAW.LOOP + END IF + + +END SUB + +SUB PRINT.TEXT (Text$, Xpos, Ypos, ColorF, ColorB) + + IF LEN(Text$) = 0 THEN EXIT SUB + + PRINT.STR SSEG(Text$), SADD(Text$), LEN(Text$), Xpos, Ypos, ColorF, ColorB + + +END SUB + +SUB TPRINT.TEXT (Text$, Xpos, Ypos, ColorF) + + IF LEN(Text$) = 0 THEN EXIT SUB + + TPRINT.STR SSEG(Text$), SADD(Text$), LEN(Text$), Xpos, Ypos, ColorF + +END SUB + diff --git a/modex105/DEMOS/BASIC7/UASM-BC7.BAT b/modex105/DEMOS/BASIC7/UASM-BC7.BAT new file mode 100755 index 00000000..5ad67fb5 --- /dev/null +++ b/modex105/DEMOS/BASIC7/UASM-BC7.BAT @@ -0,0 +1 @@ +MASM /DFARSTRINGS utils, utils, utils, nul; \ No newline at end of file diff --git a/modex105/DEMOS/BASIC7/UTILS.ASM b/modex105/DEMOS/BASIC7/UTILS.ASM new file mode 100755 index 00000000..811b8f8e --- /dev/null +++ b/modex105/DEMOS/BASIC7/UTILS.ASM @@ -0,0 +1,406 @@ +;======================================================= +;=== UTILS.ASM - Asm Utilities for QuickBasic/BC7 === +;======================================================= + + PAGE 255, 132 + + .MODEL Medium + .286 + + ; ==== MACROS ==== + + ; macros to PUSH and POP multiple registers + +PUSHx MACRO R1, R2, R3, R4, R5, R6, R7, R8 + IFNB + push R1 ; Save R1 + PUSHx R2, R3, R4, R5, R6, R7, R8 + ENDIF +ENDM + +POPx MACRO R1, R2, R3, R4, R5, R6, R7, R8 + IFNB + pop R1 ; Restore R1 + POPx R2, R3, R4, R5, R6, R7, R8 + ENDIF +ENDM + + ; Macro to Clear a Register to 0 + +CLR MACRO Register + xor Register, Register ; Set Register = 0 +ENDM + + ; Macros to Decrement Counter & Jump on Condition + +LOOPx MACRO Register, Destination + dec Register ; Counter-- + jnz Destination ; Jump if not 0 +ENDM + +LOOPjz MACRO Register, Destination + dec Register ; Counter-- + jz Destination ; Jump if 0 +ENDM + + + ; ==== General Constants ==== + + False EQU 0 + True EQU -1 + nil EQU 0 + + b EQU BYTE PTR + w EQU WORD PTR + d EQU DWORD PTR + o EQU OFFSET + f EQU FAR PTR + s EQU SHORT + ?x4 EQU + ?x3 EQU + + +IFDEF FARSTRINGS + + EXTRN stringaddress:far + EXTRN stringlength:far + +ENDIF + + + .Data + + EVEN + +RND_Seed DW 7397, 29447, 802 +RND_Mult DW 179, 183, 182 +RND_ModV DW 32771, 32779, 32783 + +CR_LF DB 13, 10 ; the CRLF data + + .Code + +;================= +;DOS_PRINT (Text$) +;================= +; +; Prints Text Directly to DOS console w/ CR/LF +; + + PUBLIC DOS_PRINT + +DP_Stack STRUC + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + DP_Text DW ? ; Address of Text$ Descriptor +DP_Stack ENDS + + +DOS_PRINT PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + mov BP, SP ; Set up Stack Frame + + mov SI, [BP].DP_Text ; Get Addr of Text$ descriptor + +IFDEF FARSTRINGS + push SI ; Push Addr of BC7 Decriptor Ptr + call stringaddress ; Get Address + Len of string!!! + ; DX:AX = Addr CX = Len + mov DS, DX ; DS = DX = Segment of string + mov DX, AX ; DX = AX = Offset of String +ELSE + mov CX, [SI] ; put its length into CX + mov DX, [SI+02] ; now DS:DX points to the String +ENDIF + + jcxz @No_Print ; Don't Print if empty + + mov BX, 1 ; 1= DOS Handle for Display + mov AH, 40h ; Write Text Function + int 21h ; Call DOS to do it + +@No_Print: + mov AX, SEG DGROUP ; Restore DGroup + mov DS, AX + + mov DX, o CR_LF ; Get Addr of CR/LF pair + mov CX, 2 ; 2 Characters to Write + mov BX, 1 ; 1= DOS Handle for Display + + mov AH, 40h ; Write Text Function + int 21h ; Call DOS to do it + + cld ; Reset Direction Flag + POPx DI, SI, DS, BP ; Restore Saved Registers + ret 2 ; Exit & Clean Up Stack + +DOS_PRINT ENDP + + +;================== +;DOS_PRINTS (Text$) +;================== +; +; Print Text$ Directly to DOS console +; without a trailing CR/LF +; + + PUBLIC DOS_PRINTS + +DOS_PRINTS PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + mov BP, SP ; Set up Stack Frame + + mov SI, [BP].DP_Text ; Get Addr of Text$ descriptor + +IFDEF FARSTRINGS + push SI ; Push Addr of BC7 Decriptor Ptr + call stringaddress ; Get Address + Len of string!!! + ; DX:AX = Addr CX = Len + mov DS, DX ; DS = DX = Segment of string + mov DX, AX ; DX = AX = Offset of String +ELSE + mov CX, [SI] ; put its length into CX + mov DX, [SI+02] ; now DS:DX points to the String +ENDIF + + jcxz @DPS_Exit ; Don't Print if empty + + mov BX, 1 ; 1= DOS Handle for Display + mov AH, 40h ; Write Text Function + int 21h ; Call DOS to do it + +@DPS_Exit: + cld ; Reset Direction Flag + POPx DI, SI, DS, BP ; Restore Saved Registers + ret 2 ; Exit & Clean Up Stack + +DOS_PRINTS ENDP + + +;====================== +;SET_VIDEO_MODE (Mode%) +;====================== +; +; Sets the Video Mode through the BIOS +; + + PUBLIC SET_VIDEO_MODE + +SVM_Stack STRUC + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + SVM_Mode DB ?,? ; Desired Video Mode +SVM_Stack ENDS + + +SET_VIDEO_MODE PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + mov BP, SP ; Set up Stack Frame + + CLR AH ; Function 0 + mov AL, [BP].SVM_Mode ; Get Mode # + + int 10H ; Change Video Modes + +@SVM_Exit: + POPx DI, SI, DS, BP ; Restore Saved Registers + ret 2 ; Exit & Clean Up Stack + +SET_VIDEO_MODE ENDP + + +;============== +;SCAN_KEYBOARD% +;============== +; +; Function to scan keyboard for a pressed key +; + + PUBLIC SCAN_KEYBOARD + +SCAN_KEYBOARD PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + + mov AH, 01H ; Function #1 + int 16H ; Call Keyboard Driver + jz @SK_NO_KEY ; Exit if Zero flag set + + mov AH, 00H ; Remove Key from Buffer + int 16H ; Get Keycode in AX + + or AL, AL ; Low Byte Set (Ascii?) + jz @SK_Exit ; if not, it's a F-Key + + CLR AH ; Clear ScanCode if Ascii + jmp s @SK_Exit ; Return Key in AX + +@SK_NO_KEY: + CLR AX ; Return Nil (no Keypress) + +@SK_Exit: + cld ; Reset Direction Flag + POPx DI, SI, DS, BP ; Restore Saved Registers + ret ; Exit & Clean Up Stack + +SCAN_KEYBOARD ENDP + + +;==================== +;RANDOM_INT (MaxInt%) +;==================== +; +; Returns a pseudo-random number in the range of (0.. MaxInt-1) +; + + + PUBLIC RANDOM_INT + +RI_Stack STRUC + DW ? ; BP + DD ? ; Caller + RI_MaxVal DW ? ; Maximum Value to Return + 1 +RI_Stack ENDS + + +RANDOM_INT PROC FAR + + push BP ; Preserve Important Registers + mov BP, SP ; Set up Stack Frame + + CLR BX ; BX is the data index + CLR CX ; CX is the accumulator + +REPT 3 + mov AX, RND_Seed[BX] ; load the initial seed + mul RND_Mult[BX] ; multiply it + div RND_ModV[BX] ; and obtain the Mod value + mov RND_Seed[BX], DX ; save that for the next time + + add CX, DX ; add it into the accumulator + inc BX + inc BX ; point to the next set of values +ENDM + + mov AX, CX ; AX = Random # + CLR DX ; DX = 0 + div [BP].RI_MaxVal ; DX = DX:AX / MAxVal Remainder + + mov AX, DX + + pop BP ; Restore BP + ret 2 ; back to BASIC with AX holding the result + +RANDOM_INT ENDP + + +;=========== +;INIT_RANDOM +;=========== +; +; Scrambles the psuedo-random number sequence +; (XOR's the seed value with the timer) +; + + PUBLIC INIT_RANDOM + +INIT_RANDOM PROC FAR + + clr AX ; Segment = 0000 + mov ES, AX + mov AX, ES:[046Ch] ; Get Timer Lo Word + + xor RND_Seed, AX ; Scramble 1st Seed + + ret ; Exit & Clean Up Stack + +INIT_RANDOM ENDP + + +;==================== +;INT_SQR (X%, Round%) +;==================== +; +; Returns the Integer Square Root of (X) +; Round allows the return value to be rounded to the +; nearest integer value by passing 0x80. Passing 0 +; return the Integer Portion only. The rounding amound is +; a number from 0 to 1 multiplied by 256, thus +; 0.5 * 0x100 = 0x80! +; + +ISQ_Stack STRUC + DW ?,? ; BP, DI + DD ? ; Caller + ISQ_Round DW ? ; Amount to Round Result * 256 + ISQ_X DW ? ; "X" +ISQ_Stack ENDS + + PUBLIC INT_SQR + +INT_SQR PROC FAR + + PUSHx BP, DI ; Save BP + mov BP, SP ; Set up Stack Frame + + xor AX, AX ; {xor eax,eax} + xor DX, DX ; {xor edx,edx} + mov DI, [BP].ISQ_X ; {mov edi,x} + + mov CX, 16 ; {mov cx, 32} + +@ISQ_L: + + shl DI, 1 ; {shl edi,1} + rcl DX, 1 ; {rcl edx,1} + shl DI, 1 ; {shl edi,1} + rcl DX, 1 ; {rcl edx,1} + shl AX, 1 ; {shl eax,1} + mov BX, AX ; {mov ebx,eax} + shl BX, 1 ; {shl ebx,1} + inc BX ; {inc ebx} + cmp DX, BX ; {cmp edx,ebx} + jl @ISQ_S + + sub DX, BX ; {sub edx,ebx} + inc AX ; {inc eax} + +@ISQ_S: + loop @ISQ_L + + add ax, [BP].ISQ_Round ; {add eax,$00008000} + ; {*round* result in hi word: ie. +0.5} + shr ax, 8 ; {shr eax,16} {to ax (result)} + + POPx DI, BP ; Restore Registers + ret 4 ; Exit + +INT_SQR ENDP + + +;============ +;TIMER_COUNT& +;============ +; +; Returns the current timer value as an integer/long integer +; + + + PUBLIC TIMER_COUNT + +TIMER_COUNT PROC FAR + + clr AX ; Segment = 0000 + mov ES, AX ; use ES to get at data + mov AX, ES:[046Ch] ; Get Timer Lo Word + mov DX, ES:[046Eh] ; Get Timer Hi Word + ret ; Exit & Return value in DX:AX + +TIMER_COUNT ENDP + + + END diff --git a/modex105/DEMOS/BASIC7/UTILS.BI b/modex105/DEMOS/BASIC7/UTILS.BI new file mode 100755 index 00000000..aeafeef4 --- /dev/null +++ b/modex105/DEMOS/BASIC7/UTILS.BI @@ -0,0 +1,51 @@ + + ' Misc Constants + +CONST True = -1, False = 0, nil = 0 + + ' Keyboard Codes: Extended + +CONST KyF1 = &H3B00, KyF2 = &H3C00, KyF3 = &H3D00, KyF4 = &H3E00, KyF5 = &H3F00 +CONST KyF6 = &H4000, KyF7 = &H4100, KyF8 = &H4200, KyF9 = &H4300, KyF10 = &H4400 + +CONST KyUp = &H4800, KyLeft = &H4B00, KyRight = &H4D00, KyDown = &H5000 +CONST KySLeft = &HCB00, KySRight = &HCD00, KySUp = &HC800, KySDown = &HD000 + +CONST KyHome = &H4700, KyPgUp = &H4900, KyEnd = &H4F00, KyPgDn = &H5100 +CONST KySHome = &HC700, KySPgUp = &HC900, KySEnd = &HCF00, KySPgDn = &HD100 + +CONST KyIns = &H5200, KyDel = &H5300, KyRvsTab = &H8F00 +CONST KySIns = &HC200, KySDel = &HC300 + +CONST KyAltA = &H1E00, KyAltB = &H3000, KyAltC = &H2E00, KyAltD = &H2000 +CONST KyAltE = &H1200, KyAltF = &H2100, KyAltG = &H2200, KyAltH = &H2300 +CONST KyAltI = &H1700, KyAltJ = &H2400, KyAltK = &H2500, KyAltL = &H2600 +CONST KyAltM = &H3200, KyAltN = &H3100, KyAltO = &H1800, KyAltP = &H1900 +CONST KyAltQ = &H1000, KyAltR = &H1300, KyAltS = &H1F00, KyAltT = &H1400 +CONST KyAltU = &H1600, KyAltV = &H2F00, KyAltW = &H1100, KyAltX = &H2D00 +CONST KyAltY = &H1500, KyAltZ = &H2C00 + + ' Keyboard Codes: Ascii + +CONST KyBS = 8, KyTab = 9, KyCR = 13, KyESC = &H1B, KyClr = &H7F +CONST KyPlus = 45, KyMinus = 43 + + ' Color Constants + +CONST c.BLACK = 0, c.BLUE = 1, c.GREEN = 2, c.CYAN = 3 +CONST c.RED = 4, c.PURPLE = 5, c.BROWN = 6, c.WHITE = 7 +CONST c.GREY = 8, c.bBLUE = 9, c.bGREEN = 10, c.bCYAN = 11 +CONST c.bRED = 12, c.bPURPLE = 13, c.YELLOW = 14, c.bWHITE = 15 +CONST c.BRIGHT = 8 + + ' From UTILS.ASM + +DECLARE SUB DOS.PRINT ALIAS "DOS_PRINT" (Text$) +DECLARE SUB DOS.PRINTS ALIAS "DOS_PRINTS" (Text$) +DECLARE SUB SET.VIDEO.MODE ALIAS "SET_VIDEO_MODE" (BYVAL Mode%) +DECLARE FUNCTION SCAN.KEYBOARD% ALIAS "SCAN_KEYBOARD" +DECLARE FUNCTION RANDOM.INT ALIAS "RANDOM_INT" (BYVAL MaxInt%) +DECLARE SUB INIT.RANDOM ALIAS "INIT_RANDOM" +DECLARE FUNCTION TIMER.COUNT& ALIAS "TIMER_COUNT" +DECLARE FUNCTION INT.SQR ALIAS "INT_SQR" (BYVAL X%, BYVAL Round%) + diff --git a/modex105/DEMOS/C/C_UTILS.ASM b/modex105/DEMOS/C/C_UTILS.ASM new file mode 100755 index 00000000..8302561a --- /dev/null +++ b/modex105/DEMOS/C/C_UTILS.ASM @@ -0,0 +1,405 @@ +;======================================================= +;=== C_UTILS.ASM - Asm Utilities for C/C++ === +;======================================================= + + PAGE 255, 132 + + .MODEL Medium + .286 + + ; ==== MACROS ==== + + ; macros to PUSH and POP multiple registers + +PUSHx MACRO R1, R2, R3, R4, R5, R6, R7, R8 + IFNB + push R1 ; Save R1 + PUSHx R2, R3, R4, R5, R6, R7, R8 + ENDIF +ENDM + +POPx MACRO R1, R2, R3, R4, R5, R6, R7, R8 + IFNB + pop R1 ; Restore R1 + POPx R2, R3, R4, R5, R6, R7, R8 + ENDIF +ENDM + + ; Macro to Clear a Register to 0 + +CLR MACRO Register + xor Register, Register ; Set Register = 0 +ENDM + + ; Macros to Decrement Counter & Jump on Condition + +LOOPx MACRO Register, Destination + dec Register ; Counter-- + jnz Destination ; Jump if not 0 +ENDM + +LOOPjz MACRO Register, Destination + dec Register ; Counter-- + jz Destination ; Jump if 0 +ENDM + + + ; ==== General Constants ==== + + False EQU 0 + True EQU -1 + nil EQU 0 + + b EQU BYTE PTR + w EQU WORD PTR + d EQU DWORD PTR + o EQU OFFSET + f EQU FAR PTR + s EQU SHORT + ?x4 EQU + ?x3 EQU + + + .Data + + EVEN + +RND_Seed DW 7397, 29447, 802 +RND_Mult DW 179, 183, 182 +RND_ModV DW 32771, 32779, 32783 + +CR_LF DB 13, 10 ; the CRLF data + + .Code + +;=========================================== +;void far pascal dos_print (far char *Text) +;=========================================== +; +; - Print Text Directly to DOS console w/ CR/LF +; + + PUBLIC DOS_PRINT + +DP_Stack STRUC + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + DP_Text DD ? ; Far Address of Text to print +DP_Stack ENDS + + +DOS_PRINT PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + mov BP, SP ; Set up Stack Frame + + lds DX, [BP].DP_Text ; Get Addr of Text$ descriptor + + ; Compute Length of string + + CLR CX ; Length = 0 + mov SI, DX ; DS:SI = String data + +@@DP_Scan_it: + + cmp b [SI], 0 ; Null Byte found? + je @@DP_Got_Len ; exit loop if so + + inc CX ; Len++ + inc SI ; Point to next char + jmp s @@DP_Scan_it ; check again... + +@@DP_Got_len: + + jcxz @No_Print ; Don't Print if empty + + mov BX, 1 ; 1= DOS Handle for Display + mov AH, 40h ; Write Text Function + int 21h ; Call DOS to do it + +@No_Print: + mov AX, SEG DGROUP ; Restore DGroup + mov DS, AX + + mov DX, o CR_LF ; Get Addr of CR/LF pair + mov CX, 2 ; 2 Characters to Write + mov BX, 1 ; 1= DOS Handle for Display + + mov AH, 40h ; Write Text Function + int 21h ; Call DOS to do it + + cld ; Reset Direction Flag + POPx DI, SI, DS, BP ; Restore Saved Registers + ret 4 ; Exit & Clean Up Stack + +DOS_PRINT ENDP + + +;=========================================== +;void far pascal dos_prints (char far *Text) +;=========================================== +; +; Print Text Directly to DOS console +; without a trailing CR/LF +; + + PUBLIC DOS_PRINTS + +DOS_PRINTS PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + mov BP, SP ; Set up Stack Frame + + lds DX, [BP].DP_Text ; Get Addr of Text$ descriptor + + ; Compute Length of string + + CLR CX ; Length = 0 + mov SI, DX ; DS:SI = String data + +@@DPS_Scan_it: + + cmp b [SI], 0 ; Null Byte found? + je @@DPS_Got_Len ; exit loop if so + + inc CX ; Len++ + inc SI ; Point to next char + jmp s @@DPS_Scan_it ; check again... + +@@DPS_Got_len: + + jcxz @DPS_Exit ; Don't Print if empty + + mov BX, 1 ; 1= DOS Handle for Display + mov AH, 40h ; Write Text Function + int 21h ; Call DOS to do it + +@DPS_Exit: + cld ; Reset Direction Flag + POPx DI, SI, DS, BP ; Restore Saved Registers + ret 2 ; Exit & Clean Up Stack + +DOS_PRINTS ENDP + + +;========================================= +;void far pascal set_video_mode (int Mode) +;========================================= +; +; Sets the Video Mode through the BIOS +; + + PUBLIC SET_VIDEO_MODE + +SVM_Stack STRUC + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + SVM_Mode DB ?,? ; Desired Video Mode +SVM_Stack ENDS + + +SET_VIDEO_MODE PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + mov BP, SP ; Set up Stack Frame + + CLR AH ; Function 0 + mov AL, [BP].SVM_Mode ; Get Mode # + + int 10H ; Change Video Modes + +@SVM_Exit: + POPx DI, SI, DS, BP ; Restore Saved Registers + ret 2 ; Exit & Clean Up Stack + +SET_VIDEO_MODE ENDP + + +;=================================== +;int far pascal scan_keyboard (void) +;=================================== +; +; Function to scan keyboard for a pressed key +; + + PUBLIC SCAN_KEYBOARD + +SCAN_KEYBOARD PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + + mov AH, 01H ; Function #1 + INT 16H ; Call Keyboard Driver + JZ @SK_NO_KEY ; Exit if Zero flag set + + mov AH, 00H ; Remove Key from Buffer + INT 16H ; Get Keycode in AX + + OR AL, AL ; Low Byte Set (Ascii?) + JZ @SK_Exit ; if not, it's a F-Key + + CLR AH ; Clear ScanCode if Ascii + JMP s @SK_Exit ; Return Key in AX + +@SK_NO_KEY: + CLR AX ; Return Nil (no Keypress) + +@SK_Exit: + cld ; Reset Direction Flag + POPx DI, SI, DS, BP ; Restore Saved Registers + ret ; Exit & Clean Up Stack + +SCAN_KEYBOARD ENDP + + +;======================================== +;int far pascal random_int (int MaxValue) +;======================================== +; +; Returns a pseudo-random number in the range of (0.. MaxInt-1) +; + + + PUBLIC RANDOM_INT + +RI_Stack STRUC + DW ? ; BP + DD ? ; Caller + RI_MaxVal DW ? ; Maximum Value to Return + 1 +RI_Stack ENDS + + +RANDOM_INT PROC FAR + + push BP ; Preserve Important Registers + mov BP, SP ; Set up Stack Frame + + CLR BX ; BX is the data index + CLR CX ; CX is the accumulator + +REPT 3 + mov AX, RND_Seed[BX] ; load the initial seed + mul RND_Mult[BX] ; multiply it + div RND_ModV[BX] ; and obtain the Mod value + mov RND_Seed[BX], DX ; save that for the next time + + add CX, DX ; add it into the accumulator + inc BX + inc BX ; point to the next set of values +ENDM + + mov AX, CX ; AX = Random # + CLR DX ; DX = 0 + div [BP].RI_MaxVal ; DX = DX:AX / MAxVal Remainder + + mov AX, DX + + pop BP ; Restore BP + ret 2 ; back to BASIC with AX holding the result + +RANDOM_INT ENDP + + +;================================== +;void far pascal init_random (void) +;================================== +; +; Scrambles the psuedo-random number sequence +; (XOR's the seed value with the timer) +; + + PUBLIC INIT_RANDOM + +INIT_RANDOM PROC FAR + + CLR AX ; Segment = 0000 + mov ES, AX + mov AX, ES:[046Ch] ; Get Timer Lo Word + + xor RND_Seed, AX ; Scramble 1st Seed + + ret ; Exit & Clean Up Stack + +INIT_RANDOM ENDP + +;========================================= +;int far pascal int_sqr (int X, int Round) +;========================================= +; +; Returns the Integer Square Root of (X) +; Round allows the return value to be rounded to the +; nearest integer value by passing 0x80. Passing 0 +; return the Integer Portion only. The rounding amound is +; a number from 0 to 1 multiplied by 256, thus +; 0.5 * 0x100 = 0x80! +; + +ISQ_Stack STRUC + DW ?,? ; BP, DI + DD ? ; Caller + ISQ_Round DW ? ; Amount to Round Result * 256 + ISQ_X DW ? ; "X" +ISQ_Stack ENDS + + PUBLIC INT_SQR + +INT_SQR PROC FAR + + PUSHx BP, DI ; Save BP + mov BP, SP ; Set up Stack Frame + + xor AX, AX ; {xor eax,eax} + xor DX, DX ; {xor edx,edx} + mov DI, [BP].ISQ_X ; {mov edi,x} + + mov CX, 16 ; {mov cx, 32} + +@ISQ_L: + + shl DI, 1 ; {shl edi,1} + rcl DX, 1 ; {rcl edx,1} + shl DI, 1 ; {shl edi,1} + rcl DX, 1 ; {rcl edx,1} + shl AX, 1 ; {shl eax,1} + mov BX, AX ; {mov ebx,eax} + shl BX, 1 ; {shl ebx,1} + inc BX ; {inc ebx} + cmp DX, BX ; {cmp edx,ebx} + jl @ISQ_S + + sub DX, BX ; {sub edx,ebx} + inc AX ; {inc eax} + +@ISQ_S: + loop @ISQ_L + + add ax, [BP].ISQ_Round ; {add eax,$00008000} + ; {*round* result in hi word: ie. +0.5} + shr ax, 8 ; {shr eax,16} {to ax (result)} + + POPx DI, BP ; Restore Registers + ret 4 ; Exit + +INT_SQR ENDP + +;================================= +;int far pascal timer_count (void) +;================================= +; +; Returns the current timer value as an integer/long integer +; + + PUBLIC TIMER_COUNT + +TIMER_COUNT PROC FAR + + CLR AX ; Segment = 0000 + mov ES, AX + mov AX, ES:[046Ch] ; Get Timer Lo Word + mov DX, ES:[046Eh] ; Get Timer Hi Word + ret ; Exit & Clean Up Stack + +TIMER_COUNT ENDP + + + END diff --git a/modex105/DEMOS/C/C_UTILS.H b/modex105/DEMOS/C/C_UTILS.H new file mode 100755 index 00000000..ed0e188f --- /dev/null +++ b/modex105/DEMOS/C/C_UTILS.H @@ -0,0 +1,117 @@ + +#ifndef __C_UTILS_H +#define __C_UTILS_H + + + /* Misc Constants */ + +#define True -1 +#define False 0 +#define nil 0 + + /* Color Constants */ + +#define c_BLACK 0 +#define c_BLUE 1 +#define c_GREEN 2 +#define c_CYAN 3 +#define c_RED 4 +#define c_PURPLE 5 +#define c_BROWN 6 +#define c_WHITE 7 +#define c_GREY 8 +#define c_bBLUE 9 +#define c_bGREEN 10 +#define c_bCYAN 11 +#define c_bRED 12 +#define c_bPURPLE 13 +#define c_YELLOW 14 +#define c_bWHITE 15 +#define c_BRIGHT 16 + + +#define Ky_F1 0x3B00 +#define Ky_F2 0x3C00 +#define Ky_F3 0x3D00 +#define Ky_F4 0x3E00 +#define Ky_F5 0x3F00 +#define Ky_F6 0x4000 +#define Ky_F7 0x4100 +#define Ky_F8 0x4200 +#define Ky_F9 0x4300 +#define Ky_F10 0x4400 + +#define Ky_Up 0x4800 +#define Ky_Left 0x4B00 +#define Ky_Right 0x4D00 +#define Ky_Down 0x5000 +#define Ky_SUp 0xC800 +#define Ky_SLeft 0xCB00 +#define Ky_SRight 0xCD00 +#define Ky_SDown 0xD000 + +#define Ky_Home 0x4700 +#define Ky_End 0x4F00 +#define Ky_PgUp 0x4900 +#define Ky_PgDn 0x5100 +#define Ky_SHome 0xC700 +#define Ky_SEnd 0xCF00 +#define Ky_SPgUp 0xC900 +#define Ky_SPgDn 0xD100 + +#define Ky_Ins 0x5200 +#define Ky_Del 0x5300 +#define Ky_SIns 0xC200 +#define Ky_SDel 0xC300 + +#define Ky_Tab 0x0009 +#define Ky_RvsTab 0x8F00 +#define Ky_STab 0x8F00 + +#define Ky_BS 0x0008 +#define Ky_CR 0x000D +#define Ky_ESC 0x001B +#define Ky_Clr 0x007F + +#define Ky_Plus 0x002D +#define Ky_Minus 0x002B + +#define Ky_AltA 0x1E00 +#define Ky_AltB 0x3000 +#define Ky_AltC 0x2E00 +#define Ky_AltD 0x2000 +#define Ky_AltE 0x1200 +#define Ky_AltF 0x2100 +#define Ky_AltG 0x2200 +#define Ky_AltH 0x2300 +#define Ky_AltI 0x1700 +#define Ky_AltJ 0x2400 +#define Ky_AltK 0x2500 +#define Ky_AltL 0x2600 +#define Ky_AltM 0x3200 +#define Ky_AltN 0x3100 +#define Ky_AltO 0x1800 +#define Ky_AltP 0x1900 +#define Ky_AltQ 0x1000 +#define Ky_AltR 0x1300 +#define Ky_AltS 0x1F00 +#define Ky_AltT 0x1400 +#define Ky_AltU 0x1600 +#define Ky_AltV 0x2F00 +#define Ky_AltW 0x1100 +#define Ky_AltX 0x2D00 +#define Ky_AltY 0x1500 +#define Ky_AltZ 0x2C00 + + /* .ASM Functions From C_UTILS.ASM */ + +void far pascal dos_print (char far *Text); +void far pascal dos_prints (char far *Text); +void far pascal set_video_mode (int Mode); +int far pascal scan_keyboard (void); +int far pascal random_int (int MaxValue); +void far pascal init_random (void); +int far pascal int_sqr (int X, int Round); +int far pascal timer_count (void); + +#endif \ No newline at end of file diff --git a/modex105/DEMOS/C/C_UTILS.LST b/modex105/DEMOS/C/C_UTILS.LST new file mode 100755 index 00000000..f9664ba0 --- /dev/null +++ b/modex105/DEMOS/C/C_UTILS.LST @@ -0,0 +1,597 @@ +Microsoft (R) Macro Assembler Version 6.11 07/03/14 12:38:18 +c_utils.asm Page 1 - 1 + + + ;======================================================= + ;=== C_UTILS.ASM - Asm Utilities for C/C++ === + ;======================================================= + + PAGE 255, 132 + + .MODEL Medium + .286 + + ; ==== MACROS ==== + + ; macros to PUSH and POP multiple registers + + PUSHx MACRO R1, R2, R3, R4, R5, R6, R7, R8 + IFNB + push R1 ; Save R1 + PUSHx R2, R3, R4, R5, R6, R7, R8 + ENDIF + ENDM + + POPx MACRO R1, R2, R3, R4, R5, R6, R7, R8 + IFNB + pop R1 ; Restore R1 + POPx R2, R3, R4, R5, R6, R7, R8 + ENDIF + ENDM + + ; Macro to Clear a Register to 0 + + CLR MACRO Register + xor Register, Register ; Set Register = 0 + ENDM + + ; Macros to Decrement Counter & Jump on Condition + + LOOPx MACRO Register, Destination + dec Register ; Counter-- + jnz Destination ; Jump if not 0 + ENDM + + LOOPjz MACRO Register, Destination + dec Register ; Counter-- + jz Destination ; Jump if 0 + ENDM + + + ; ==== General Constants ==== + + = 0000 False EQU 0 + =-0001 True EQU -1 + = 0000 nil EQU 0 + + = BYTE PTR b EQU BYTE PTR + = WORD PTR w EQU WORD PTR + = DWORD PTR d EQU DWORD PTR + = OFFSET o EQU OFFSET + = FAR PTR f EQU FAR PTR + = SHORT s EQU SHORT + = ?,?,?,? ?x4 EQU + = ?,?,? ?x3 EQU + + + 0000 .Data + + EVEN + + 0000 1CE5 7307 0322 RND_Seed DW 7397, 29447, 802 + 0006 00B3 00B7 00B6 RND_Mult DW 179, 183, 182 + 000C 8003 800B 800F RND_ModV DW 32771, 32779, 32783 + + 0012 0D 0A CR_LF DB 13, 10 ; the CRLF data + + 0000 .Code + + ;=========================================== + ;void far pascal dos_print (far char *Text) + ;=========================================== + ; + ; - Print Text Directly to DOS console w/ CR/LF + ; + + PUBLIC DOS_PRINT + + 0010 DP_Stack STRUC + 0000 0000 0000 0000 DW ?x4 ; DI, SI, DS, BP + 0000 + 0008 00000000 DD ? ; Caller + 000C 00000000 DP_Text DD ? ; Far Address of Text to print + DP_Stack ENDS + + + 0000 DOS_PRINT PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 0000 55 1 push BP ; Save R1 + 0001 1E 2 push DS ; Save R1 + 0002 56 3 push SI ; Save R1 + 0003 57 4 push DI ; Save R1 + 0004 8B EC mov BP, SP ; Set up Stack Frame + + 0006 C5 56 0C lds DX, [BP].DP_Text ; Get Addr of Text$ descriptor + + ; Compute Length of string + + CLR CX ; Length = 0 + 0009 33 C9 1 xor CX, CX ; Set Register = 0 + 000B 8B F2 mov SI, DX ; DS:SI = String data + + 000D @@DP_Scan_it: + + 000D 80 3C 00 cmp b [SI], 0 ; Null Byte found? + 0010 74 04 je @@DP_Got_Len ; exit loop if so + + 0012 41 inc CX ; Len++ + 0013 46 inc SI ; Point to next char + 0014 EB F7 jmp s @@DP_Scan_it ; check again... + + 0016 @@DP_Got_len: + + 0016 E3 07 jcxz @No_Print ; Don't Print if empty + + 0018 BB 0001 mov BX, 1 ; 1= DOS Handle for Display + 001B B4 40 mov AH, 40h ; Write Text Function + 001D CD 21 int 21h ; Call DOS to do it + + 001F @No_Print: + 001F B8 ---- R mov AX, SEG DGROUP ; Restore DGroup + 0022 8E D8 mov DS, AX + + 0024 BA 0012 R mov DX, o CR_LF ; Get Addr of CR/LF pair + 0027 B9 0002 mov CX, 2 ; 2 Characters to Write + 002A BB 0001 mov BX, 1 ; 1= DOS Handle for Display + + 002D B4 40 mov AH, 40h ; Write Text Function + 002F CD 21 int 21h ; Call DOS to do it + + 0031 FC cld ; Reset Direction Flag + POPx DI, SI, DS, BP ; Restore Saved Registers + 0032 5F 1 pop DI ; Restore R1 + 0033 5E 2 pop SI ; Restore R1 + 0034 1F 3 pop DS ; Restore R1 + 0035 5D 4 pop BP ; Restore R1 + 0036 CA 0004 ret 4 ; Exit & Clean Up Stack + + 0039 DOS_PRINT ENDP + + + ;=========================================== + ;void far pascal dos_prints (char far *Text) + ;=========================================== + ; + ; Print Text Directly to DOS console + ; without a trailing CR/LF + ; + + PUBLIC DOS_PRINTS + + 0039 DOS_PRINTS PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 0039 55 1 push BP ; Save R1 + 003A 1E 2 push DS ; Save R1 + 003B 56 3 push SI ; Save R1 + 003C 57 4 push DI ; Save R1 + 003D 8B EC mov BP, SP ; Set up Stack Frame + + 003F C5 56 0C lds DX, [BP].DP_Text ; Get Addr of Text$ descriptor + + ; Compute Length of string + + CLR CX ; Length = 0 + 0042 33 C9 1 xor CX, CX ; Set Register = 0 + 0044 8B F2 mov SI, DX ; DS:SI = String data + + 0046 @@DPS_Scan_it: + + 0046 80 3C 00 cmp b [SI], 0 ; Null Byte found? + 0049 74 04 je @@DPS_Got_Len ; exit loop if so + + 004B 41 inc CX ; Len++ + 004C 46 inc SI ; Point to next char + 004D EB F7 jmp s @@DPS_Scan_it ; check again... + + 004F @@DPS_Got_len: + + 004F E3 07 jcxz @DPS_Exit ; Don't Print if empty + + 0051 BB 0001 mov BX, 1 ; 1= DOS Handle for Display + 0054 B4 40 mov AH, 40h ; Write Text Function + 0056 CD 21 int 21h ; Call DOS to do it + + 0058 @DPS_Exit: + 0058 FC cld ; Reset Direction Flag + POPx DI, SI, DS, BP ; Restore Saved Registers + 0059 5F 1 pop DI ; Restore R1 + 005A 5E 2 pop SI ; Restore R1 + 005B 1F 3 pop DS ; Restore R1 + 005C 5D 4 pop BP ; Restore R1 + 005D CA 0002 ret 2 ; Exit & Clean Up Stack + + 0060 DOS_PRINTS ENDP + + + ;========================================= + ;void far pascal set_video_mode (int Mode) + ;========================================= + ; + ; Sets the Video Mode through the BIOS + ; + + PUBLIC SET_VIDEO_MODE + + 000E SVM_Stack STRUC + 0000 0000 0000 0000 DW ?x4 ; DI, SI, DS, BP + 0000 + 0008 00000000 DD ? ; Caller + 000C 00 00 SVM_Mode DB ?,? ; Desired Video Mode + SVM_Stack ENDS + + + 0060 SET_VIDEO_MODE PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 0060 55 1 push BP ; Save R1 + 0061 1E 2 push DS ; Save R1 + 0062 56 3 push SI ; Save R1 + 0063 57 4 push DI ; Save R1 + 0064 8B EC mov BP, SP ; Set up Stack Frame + + CLR AH ; Function 0 + 0066 32 E4 1 xor AH, AH ; Set Register = 0 + 0068 8A 46 0C mov AL, [BP].SVM_Mode ; Get Mode # + + 006B CD 10 int 10H ; Change Video Modes + + 006D @SVM_Exit: + POPx DI, SI, DS, BP ; Restore Saved Registers + 006D 5F 1 pop DI ; Restore R1 + 006E 5E 2 pop SI ; Restore R1 + 006F 1F 3 pop DS ; Restore R1 + 0070 5D 4 pop BP ; Restore R1 + 0071 CA 0002 ret 2 ; Exit & Clean Up Stack + + 0074 SET_VIDEO_MODE ENDP + + + ;=================================== + ;int far pascal scan_keyboard (void) + ;=================================== + ; + ; Function to scan keyboard for a pressed key + ; + + PUBLIC SCAN_KEYBOARD + + 0074 SCAN_KEYBOARD PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 0074 55 1 push BP ; Save R1 + Microsoft (R) Macro Assembler Version 6.11 07/03/14 12:38:18 +c_utils.asm Page 2 - 1 + + + 0075 1E 2 push DS ; Save R1 + 0076 56 3 push SI ; Save R1 + 0077 57 4 push DI ; Save R1 + + 0078 B4 01 mov AH, 01H ; Function #1 + 007A CD 16 INT 16H ; Call Keyboard Driver + 007C 74 0C JZ @SK_NO_KEY ; Exit if Zero flag set + + 007E B4 00 mov AH, 00H ; Remove Key from Buffer + 0080 CD 16 INT 16H ; Get Keycode in AX + + 0082 0A C0 OR AL, AL ; Low Byte Set (Ascii?) + 0084 74 06 JZ @SK_Exit ; if not, it's a F-Key + + CLR AH ; Clear ScanCode if Ascii + 0086 32 E4 1 xor AH, AH ; Set Register = 0 + 0088 EB 02 JMP s @SK_Exit ; Return Key in AX + + 008A @SK_NO_KEY: + CLR AX ; Return Nil (no Keypress) + 008A 33 C0 1 xor AX, AX ; Set Register = 0 + + 008C @SK_Exit: + 008C FC cld ; Reset Direction Flag + POPx DI, SI, DS, BP ; Restore Saved Registers + 008D 5F 1 pop DI ; Restore R1 + 008E 5E 2 pop SI ; Restore R1 + 008F 1F 3 pop DS ; Restore R1 + 0090 5D 4 pop BP ; Restore R1 + 0091 CB ret ; Exit & Clean Up Stack + + 0092 SCAN_KEYBOARD ENDP + + + ;======================================== + ;int far pascal random_int (int MaxValue) + ;======================================== + ; + ; Returns a pseudo-random number in the range of (0.. MaxInt-1) + ; + + + PUBLIC RANDOM_INT + + 0008 RI_Stack STRUC + 0000 0000 DW ? ; BP + 0002 00000000 DD ? ; Caller + 0006 0000 RI_MaxVal DW ? ; Maximum Value to Return + 1 + RI_Stack ENDS + + + 0092 RANDOM_INT PROC FAR + + 0092 55 push BP ; Preserve Important Registers + 0093 8B EC mov BP, SP ; Set up Stack Frame + + CLR BX ; BX is the data index + 0095 33 DB 1 xor BX, BX ; Set Register = 0 + CLR CX ; CX is the accumulator + 0097 33 C9 1 xor CX, CX ; Set Register = 0 + + REPT 3 + mov AX, RND_Seed[BX] ; load the initial seed + mul RND_Mult[BX] ; multiply it + div RND_ModV[BX] ; and obtain the Mod value + mov RND_Seed[BX], DX ; save that for the next time + + add CX, DX ; add it into the accumulator + inc BX + inc BX ; point to the next set of values + ENDM + 0099 8B 87 0000 R 1 mov AX, RND_Seed[BX] ; load the initial seed + 009D F7 A7 0006 R 1 mul RND_Mult[BX] ; multiply it + 00A1 F7 B7 000C R 1 div RND_ModV[BX] ; and obtain the Mod value + 00A5 89 97 0000 R 1 mov RND_Seed[BX], DX ; save that for the next time + 00A9 03 CA 1 add CX, DX ; add it into the accumulator + 00AB 43 1 inc BX + 00AC 43 1 inc BX ; point to the next set of values + 00AD 8B 87 0000 R 1 mov AX, RND_Seed[BX] ; load the initial seed + 00B1 F7 A7 0006 R 1 mul RND_Mult[BX] ; multiply it + 00B5 F7 B7 000C R 1 div RND_ModV[BX] ; and obtain the Mod value + 00B9 89 97 0000 R 1 mov RND_Seed[BX], DX ; save that for the next time + 00BD 03 CA 1 add CX, DX ; add it into the accumulator + 00BF 43 1 inc BX + 00C0 43 1 inc BX ; point to the next set of values + 00C1 8B 87 0000 R 1 mov AX, RND_Seed[BX] ; load the initial seed + 00C5 F7 A7 0006 R 1 mul RND_Mult[BX] ; multiply it + 00C9 F7 B7 000C R 1 div RND_ModV[BX] ; and obtain the Mod value + 00CD 89 97 0000 R 1 mov RND_Seed[BX], DX ; save that for the next time + 00D1 03 CA 1 add CX, DX ; add it into the accumulator + 00D3 43 1 inc BX + 00D4 43 1 inc BX ; point to the next set of values + + 00D5 8B C1 mov AX, CX ; AX = Random # + CLR DX ; DX = 0 + 00D7 33 D2 1 xor DX, DX ; Set Register = 0 + 00D9 F7 76 06 div [BP].RI_MaxVal ; DX = DX:AX / MAxVal Remainder + + 00DC 8B C2 mov AX, DX + + 00DE 5D pop BP ; Restore BP + 00DF CA 0002 ret 2 ; back to BASIC with AX holding the result + + 00E2 RANDOM_INT ENDP + + + ;================================== + ;void far pascal init_random (void) + ;================================== + ; + ; Scrambles the psuedo-random number sequence + ; (XOR's the seed value with the timer) + ; + + PUBLIC INIT_RANDOM + + 00E2 INIT_RANDOM PROC FAR + + CLR AX ; Segment = 0000 + 00E2 33 C0 1 xor AX, AX ; Set Register = 0 + 00E4 8E C0 mov ES, AX + 00E6 26: A1 046C mov AX, ES:[046Ch] ; Get Timer Lo Word + + 00EA 31 06 0000 R xor RND_Seed, AX ; Scramble 1st Seed + + 00EE CB ret ; Exit & Clean Up Stack + + 00EF INIT_RANDOM ENDP + + ;========================================= + ;int far pascal int_sqr (int X, int Round) + ;========================================= + ; + ; Returns the Integer Square Root of (X) + ; Round allows the return value to be rounded to the + ; nearest integer value by passing 0x80. Passing 0 + ; return the Integer Portion only. The rounding amound is + ; a number from 0 to 1 multiplied by 256, thus + ; 0.5 * 0x100 = 0x80! + ; + + 000C ISQ_Stack STRUC + 0000 0000 0000 DW ?,? ; BP, DI + 0004 00000000 DD ? ; Caller + 0008 0000 ISQ_Round DW ? ; Amount to Round Result * 256 + 000A 0000 ISQ_X DW ? ; "X" + ISQ_Stack ENDS + + PUBLIC INT_SQR + + 00EF INT_SQR PROC FAR + + PUSHx BP, DI ; Save BP + 00EF 55 1 push BP ; Save R1 + 00F0 57 2 push DI ; Save R1 + 00F1 8B EC mov BP, SP ; Set up Stack Frame + + 00F3 33 C0 xor AX, AX ; {xor eax,eax} + 00F5 33 D2 xor DX, DX ; {xor edx,edx} + 00F7 8B 7E 0A mov DI, [BP].ISQ_X ; {mov edi,x} + + 00FA B9 0010 mov CX, 16 ; {mov cx, 32} + + 00FD @ISQ_L: + + 00FD D1 E7 shl DI, 1 ; {shl edi,1} + 00FF D1 D2 rcl DX, 1 ; {rcl edx,1} + 0101 D1 E7 shl DI, 1 ; {shl edi,1} + 0103 D1 D2 rcl DX, 1 ; {rcl edx,1} + 0105 D1 E0 shl AX, 1 ; {shl eax,1} + 0107 8B D8 mov BX, AX ; {mov ebx,eax} + 0109 D1 E3 shl BX, 1 ; {shl ebx,1} + 010B 43 inc BX ; {inc ebx} + 010C 3B D3 cmp DX, BX ; {cmp edx,ebx} + 010E 7C 03 jl @ISQ_S + + 0110 2B D3 sub DX, BX ; {sub edx,ebx} + 0112 40 inc AX ; {inc eax} + + 0113 @ISQ_S: + 0113 E2 E8 loop @ISQ_L + + 0115 03 46 08 add ax, [BP].ISQ_Round ; {add eax,$00008000} + ; {*round* result in hi word: ie. +0 + .5} + 0118 C1 E8 08 shr ax, 8 ; {shr eax,16} {to ax (result)} + + POPx DI, BP ; Restore Registers + 011B 5F 1 pop DI ; Restore R1 + 011C 5D 2 pop BP ; Restore R1 + 011D CA 0004 ret 4 ; Exit + + 0120 INT_SQR ENDP + + ;================================= + ;int far pascal timer_count (void) + ;================================= + ; + ; Returns the current timer value as an integer/long integer + ; + + PUBLIC TIMER_COUNT + + 0120 TIMER_COUNT PROC FAR + + CLR AX ; Segment = 0000 + 0120 33 C0 1 xor AX, AX ; Set Register = 0 + 0122 8E C0 mov ES, AX + 0124 26: A1 046C mov AX, ES:[046Ch] ; Get Timer Lo Word + 0128 26: 8B 16 046E mov DX, ES:[046Eh] ; Get Timer Hi Word + 012D CB ret ; Exit & Clean Up Stack + + 012E TIMER_COUNT ENDP + + + END + Microsoft (R) Macro Assembler Version 6.11 07/03/14 12:38:18 +c_utils.asm Symbols 3 - 1 + + + + +Macros: + + N a m e Type + +CLR . . . . . . . . . . . . . . Proc +LOOPjz . . . . . . . . . . . . . Proc +LOOPx . . . . . . . . . . . . . Proc +POPx . . . . . . . . . . . . . . Proc +PUSHx . . . . . . . . . . . . . Proc + Microsoft (R) Macro Assembler Version 6.11 07/03/14 12:38:18 +c_utils.asm Symbols 4 - 1 + + + + +Structures and Unions: + + N a m e Size + Offset Type + +DP_Stack . . . . . . . . . . . . 0010 + DP_Text . . . . . . . . . . . 000C DWord +ISQ_Stack . . . . . . . . . . . 000C + ISQ_Round . . . . . . . . . . 0008 Word + ISQ_X . . . . . . . . . . . . 000A Word +RI_Stack . . . . . . . . . . . . 0008 + RI_MaxVal . . . . . . . . . . 0006 Word +SVM_Stack . . . . . . . . . . . 000E + SVM_Mode . . . . . . . . . . . 000C Byte + Microsoft (R) Macro Assembler Version 6.11 07/03/14 12:38:18 +c_utils.asm Symbols 5 - 1 + + + + +Segments and Groups: + + N a m e Size Length Align Combine Class + +C_UTILS_TEXT . . . . . . . . . . 16 Bit 012E Word Public 'CODE' +DGROUP . . . . . . . . . . . . . GROUP +_DATA . . . . . . . . . . . . . 16 Bit 0014 Word Public 'DATA' + Microsoft (R) Macro Assembler Version 6.11 07/03/14 12:38:18 +c_utils.asm Symbols 6 - 1 + + + + +Procedures, parameters and locals: + + N a m e Type Value Attr + +DOS_PRINTS . . . . . . . . . . . P Far 0039 C_UTILS_TEXT Length= 0027 Public +DOS_PRINT . . . . . . . . . . . P Far 0000 C_UTILS_TEXT Length= 0039 Public +INIT_RANDOM . . . . . . . . . . P Far 00E2 C_UTILS_TEXT Length= 000D Public +INT_SQR . . . . . . . . . . . . P Far 00EF C_UTILS_TEXT Length= 0031 Public +RANDOM_INT . . . . . . . . . . . P Far 0092 C_UTILS_TEXT Length= 0050 Public +SCAN_KEYBOARD . . . . . . . . . P Far 0074 C_UTILS_TEXT Length= 001E Public +SET_VIDEO_MODE . . . . . . . . . P Far 0060 C_UTILS_TEXT Length= 0014 Public +TIMER_COUNT . . . . . . . . . . P Far 0120 C_UTILS_TEXT Length= 000E Public + Microsoft (R) Macro Assembler Version 6.11 07/03/14 12:38:18 +c_utils.asm Symbols 7 - 1 + + + + +Symbols: + + N a m e Type Value Attr + +?x3 . . . . . . . . . . . . . . Text ?,?,? +?x4 . . . . . . . . . . . . . . Text ?,?,?,? +@@DPS_Got_len . . . . . . . . . L Near 004F C_UTILS_TEXT +@@DPS_Scan_it . . . . . . . . . L Near 0046 C_UTILS_TEXT +@@DP_Got_len . . . . . . . . . . L Near 0016 C_UTILS_TEXT +@@DP_Scan_it . . . . . . . . . . L Near 000D C_UTILS_TEXT +@CodeSize . . . . . . . . . . . Number 0001h +@DPS_Exit . . . . . . . . . . . L Near 0058 C_UTILS_TEXT +@DataSize . . . . . . . . . . . Number 0000h +@ISQ_L . . . . . . . . . . . . . L Near 00FD C_UTILS_TEXT +@ISQ_S . . . . . . . . . . . . . L Near 0113 C_UTILS_TEXT +@Interface . . . . . . . . . . . Number 0000h +@Model . . . . . . . . . . . . . Number 0004h +@No_Print . . . . . . . . . . . L Near 001F C_UTILS_TEXT +@SK_Exit . . . . . . . . . . . . L Near 008C C_UTILS_TEXT +@SK_NO_KEY . . . . . . . . . . . L Near 008A C_UTILS_TEXT +@SVM_Exit . . . . . . . . . . . L Near 006D C_UTILS_TEXT +@code . . . . . . . . . . . . . Text C_UTILS_TEXT +@data . . . . . . . . . . . . . Text DGROUP +@fardata? . . . . . . . . . . . Text FAR_BSS +@fardata . . . . . . . . . . . . Text FAR_DATA +@stack . . . . . . . . . . . . . Text DGROUP +CR_LF . . . . . . . . . . . . . Byte 0012 _DATA +False . . . . . . . . . . . . . Number 0000h +RND_ModV . . . . . . . . . . . . Word 000C _DATA +RND_Mult . . . . . . . . . . . . Word 0006 _DATA +RND_Seed . . . . . . . . . . . . Word 0000 _DATA +True . . . . . . . . . . . . . . Number -0001h +b . . . . . . . . . . . . . . . Text BYTE PTR +d . . . . . . . . . . . . . . . Text DWORD PTR +f . . . . . . . . . . . . . . . Text FAR PTR +nil . . . . . . . . . . . . . . Number 0000h +o . . . . . . . . . . . . . . . Text OFFSET +s . . . . . . . . . . . . . . . Text SHORT +w . . . . . . . . . . . . . . . Text WORD PTR + + 0 Warnings + 0 Errors diff --git a/modex105/DEMOS/C/C_UTILS.SBR b/modex105/DEMOS/C/C_UTILS.SBR new file mode 100755 index 0000000000000000000000000000000000000000..0301171bdd90f778ed3f244b37130f9388df7f0a GIT binary patch literal 1731 zcmZ8hU1(fY5dQAuK&p#UAC#&fKJ+1i$i~{%wn9$2yN#Fb?%mvb6JlMtU9-E%CfnU~ zf101hwzk#OG>vU+Qxyc!2N9n{@L3cD5qwebS;PmQL{UWW!I`<6G!~ZSyEEr}b7tn7 zf#VpY4i`@p2M@?E>mDuUymV$_FuA{&&g8tHn1VAIbvw0s`>9g<6zE0t(XKMmUJw<0 zHy?s-iluVL5`exDQx!objKacF3Q)yL8lNHdY z(+7JBZZH}JozmniXxuTJN{Wv@-hiJ^M?s}hMq&mu*DsS$~{0n-MBnhI>?z>CnI#*j%$*se|?lw^L;Am0jr`8vA;!PtUtas20*?Hi0*+i(!2Hh%{}P54 z_G#P|n2N?_722Je1Nq{j!*c#C+FFi3V9shrA1PormzFJYr&ErZQ7#snRblNA+d*;C z6T-HvU7lI$73~$+6c;V1EivB_IdG1j_Y7zZ<{aoN{XZ!7^WX&1Qo0MOv0S&_QE5Hw zm^9=@G|V+G0ZTGHjWd zH094>s_NnCv^9gq`OFt0J}$u|(Chr?lQ$-gku%FnC7^3dP43Vsqdz2BReRUM4r z&SY>OS3z^Vxw#t8%}BEo)hj%!GcZ>rq*MyiYe!nkJm^fWUJyos>YWht>1?8)m$MPs3x4WckJ5rSNp>pVkJ&l@Pnu910PcFYZ} z=06a?@BBdlx8ySgOm|rU=eI`yb$hznh;_vxm~+eqXi%#~18?D2sosvCf#gltN!GXq zJMWlTO@sHZ<$b%oe<#F2_sDL&1>fD0z, ; If DX not setup + MOV DX, Register ; then Select Register + ENDIF + IFDIFI , ; If AX not setup + MOV AX, Value ; then Get Data Value + ENDIF + OUT DX, AX ; Set I/O Register(s) +ENDM + + ; Macro to OUT a 8 bit value to an I/O Port + +OUT_8 MACRO Register, Value + IFDIFI , ; If DX not setup + MOV DX, Register ; then Select Register + ENDIF + IFDIFI , ; If AL not Setup + MOV AL, Value ; then Get Data Value + ENDIF + OUT DX, AL ; Set I/O Register +ENDM + + ; macros to PUSH and POP multiple registers + +PUSHx MACRO R1, R2, R3, R4, R5, R6, R7, R8 + IFNB + PUSH R1 ; Save R1 + PUSHx R2, R3, R4, R5, R6, R7, R8 + ENDIF +ENDM + +POPx MACRO R1, R2, R3, R4, R5, R6, R7, R8 + IFNB + POP R1 ; Restore R1 + POPx R2, R3, R4, R5, R6, R7, R8 + ENDIF +ENDM + + ; Macro to Clear Registers to 0 + +CLR MACRO Register, R2, R3, R4, R5, R6 + IFNB + XOR Register, Register ; Set Register = 0 + CLR R2, R3, R4, R5, R6 + ENDIF +ENDM + + ; Macros to Decrement Counter & Jump on Condition + +LOOPx MACRO Register, Destination + DEC Register ; Counter-- + JNZ Destination ; Jump if not 0 +ENDM + +LOOPjz MACRO Register, Destination + DEC Register ; Counter-- + JZ Destination ; Jump if 0 +ENDM + + + ; ===== General Constants ===== + + False EQU 0 + True EQU -1 + nil EQU 0 + + b EQU BYTE PTR + w EQU WORD PTR + d EQU DWORD PTR + o EQU OFFSET + f EQU FAR PTR + s EQU SHORT + ?x4 EQU + ?x3 EQU + + ; ===== VGA Register Values ===== + + VGA_Segment EQU 0A000h ; Vga Memory Segment + + ATTRIB_Ctrl EQU 03C0h ; VGA Attribute Controller + GC_Index EQU 03CEh ; VGA Graphics Controller + SC_Index EQU 03C4h ; VGA Sequencer Controller + SC_Data EQU 03C5h ; VGA Sequencer Data Port + CRTC_Index EQU 03D4h ; VGA CRT Controller + CRTC_Data EQU 03D5h ; VGA CRT Controller Data + MISC_OUTPUT EQU 03C2h ; VGA Misc Register + INPUT_1 EQU 03DAh ; Input Status #1 Register + + DAC_WRITE_ADDR EQU 03C8h ; VGA DAC Write Addr Register + DAC_READ_ADDR EQU 03C7h ; VGA DAC Read Addr Register + PEL_DATA_REG EQU 03C9h ; VGA DAC/PEL data Register R/W + + PIXEL_PAN_REG EQU 033h ; Attrib Index: Pixel Pan Reg + MAP_MASK EQU 002h ; Sequ Index: Write Map Mask reg + READ_MAP EQU 004h ; GC Index: Read Map Register + START_DISP_HI EQU 00Ch ; CRTC Index: Display Start Hi + START_DISP_LO EQU 00Dh ; CRTC Index: Display Start Lo + + MAP_MASK_PLANE1 EQU 00102h ; Map Register + Plane 1 + MAP_MASK_PLANE2 EQU 01102h ; Map Register + Plane 1 + ALL_PLANES_ON EQU 00F02h ; Map Register + All Bit Planes + + CHAIN4_OFF EQU 00604h ; Chain 4 mode Off + ASYNC_RESET EQU 00100h ; (A)synchronous Reset + SEQU_RESTART EQU 00300h ; Sequencer Restart + + LATCHES_ON EQU 00008h ; Bit Mask + Data from Latches + LATCHES_OFF EQU 0FF08h ; Bit Mask + Data from CPU + + VERT_RETRACE EQU 08h ; INPUT_1: Vertical Retrace Bit + PLANE_BITS EQU 03h ; Bits 0-1 of Xpos = Plane # + ALL_PLANES EQU 0Fh ; All Bit Planes Selected + CHAR_BITS EQU 0Fh ; Bits 0-3 of Character Data + + GET_CHAR_PTR EQU 01130h ; VGA BIOS Func: Get Char Set + ROM_8x8_Lo EQU 03h ; ROM 8x8 Char Set Lo Pointer + ROM_8x8_Hi EQU 04h ; ROM 8x8 Char Set Hi Pointer + + ; Constants Specific for these routines + + NUM_MODES EQU 8 ; # of Mode X Variations + + ; Specific Mode Data Table format... + +Mode_Data_Table STRUC + M_MiscR DB ? ; Value of MISC_OUTPUT register + M_Pages DB ? ; Maximum Possible # of pages + M_XSize DW ? ; X Size Displayed on screen + M_YSize DW ? ; Y Size Displayed on screen + M_XMax DW ? ; Maximum Possible X Size + M_YMax DW ? ; Maximum Possible Y Size + M_CRTC DW ? ; Table of CRTC register values +Mode_Data_Table ENDS + + ; ===== DGROUP STORAGE NEEDED (42 BYTES) ===== + + .DATA? + +SCREEN_WIDTH DW 0 ; Width of a line in Bytes +SCREEN_HEIGHT DW 0 ; Vertical Height in Pixels + +LAST_PAGE DW 0 ; # of Display Pages +PAGE_ADDR DW 4 DUP (0) ; Offsets to start of each page + +PAGE_SIZE DW 0 ; Size of Page in Addr Bytes + +DISPLAY_PAGE DW 0 ; Page # currently displayed +ACTIVE_PAGE DW 0 ; Page # currently active + +CURRENT_PAGE DW 0 ; Offset of current Page +CURRENT_SEGMENT DW 0 ; Segment of VGA memory + +CURRENT_XOFFSET DW 0 ; Current Display X Offset +CURRENT_YOFFSET DW 0 ; Current Display Y Offset + +CURRENT_MOFFSET DW 0 ; Current Start Offset + +MAX_XOFFSET DW 0 ; Current Display X Offset +MAX_YOFFSET DW 0 ; Current Display Y Offset + +CHARSET_LOW DW 0, 0 ; Far Ptr to Char Set: 0-127 +CHARSET_HI DW 0, 0 ; Far Ptr to Char Set: 128-255 + + .CODE + + ; ===== DATA TABLES ===== + + ; Data Tables, Put in Code Segment for Easy Access + ; (Like when all the other Segment Registers are in + ; use!!) and reduced DGROUP requirements... + + ; Bit Mask Tables for Left/Right/Character Masks + +Left_Clip_Mask DB 0FH, 0EH, 0CH, 08H + +Right_Clip_Mask DB 01H, 03H, 07H, 0FH + + ; Bit Patterns for converting character fonts + +Char_Plane_Data DB 00H,08H,04H,0CH,02H,0AH,06H,0EH + DB 01H,09H,05H,0DH,03H,0BH,07H,0FH + + ; CRTC Register Values for Various Configurations + +MODE_Single_Line: ; CRTC Setup Data for 400/480 Line modes + DW 04009H ; Cell Height (1 Scan Line) + DW 00014H ; Dword Mode off + DW 0E317H ; turn on Byte Mode + DW nil ; End of CRTC Data for 400/480 Line Mode + +MODE_Double_Line: ; CRTC Setup Data for 200/240 Line modes + DW 04109H ; Cell Height (2 Scan Lines) + DW 00014H ; Dword Mode off + DW 0E317H ; turn on Byte Mode + DW nil ; End of CRTC Data for 200/240 Line Mode + +MODE_320_Wide: ; CRTC Setup Data for 320 Horz Pixels + DW 05F00H ; Horz total + DW 04F01H ; Horz Displayed + DW 05002H ; Start Horz Blanking + DW 08203H ; End Horz Blanking + DW 05404H ; Start H Sync + DW 08005H ; End H Sync + DW nil ; End of CRTC Data for 320 Horz pixels + +MODE_360_Wide: ; CRTC Setup Data for 360 Horz Pixels + DW 06B00H ; Horz total + DW 05901H ; Horz Displayed + DW 05A02H ; Start Horz Blanking + DW 08E03H ; End Horz Blanking + DW 05E04H ; Start H Sync + DW 08A05H ; End H Sync + DW nil ; End of CRTC Data for 360 Horz pixels + +MODE_200_Tall: +MODE_400_Tall: ; CRTC Setup Data for 200/400 Line modes + DW 0BF06H ; Vertical Total + DW 01F07H ; Overflow + DW 09C10H ; V Sync Start + DW 08E11H ; V Sync End/Prot Cr0 Cr7 + DW 08F12H ; Vertical Displayed + DW 09615H ; V Blank Start + DW 0B916H ; V Blank End + DW nil ; End of CRTC Data for 200/400 Lines + +MODE_240_Tall: +MODE_480_Tall: ; CRTC Setup Data for 240/480 Line modes + DW 00D06H ; Vertical Total + DW 03E07H ; Overflow + DW 0EA10H ; V Sync Start + DW 08C11H ; V Sync End/Prot Cr0 Cr7 + DW 0DF12H ; Vertical Displayed + DW 0E715H ; V Blank Start + DW 00616H ; V Blank End + DW nil ; End of CRTC Data for 240/480 Lines + + ; Table of Display Mode Tables + +MODE_TABLE: + DW o MODE_320x200, o MODE_320x400 + DW o MODE_360x200, o MODE_360x400 + DW o MODE_320x240, o MODE_320x480 + DW o MODE_360x240, o MODE_360x480 + + ; Table of Display Mode Components + +MODE_320x200: ; Data for 320 by 200 Pixels + + DB 063h ; 400 scan Lines & 25 Mhz Clock + DB 4 ; Maximum of 4 Pages + DW 320, 200 ; Displayed Pixels (X,Y) + DW 1302, 816 ; Max Possible X and Y Sizes + + DW o MODE_320_Wide, o MODE_200_Tall + DW o MODE_Double_Line, nil + +MODE_320x400: ; Data for 320 by 400 Pixels + + DB 063h ; 400 scan Lines & 25 Mhz Clock + DB 2 ; Maximum of 2 Pages + DW 320, 400 ; Displayed Pixels X,Y + DW 648, 816 ; Max Possible X and Y Sizes + + DW o MODE_320_Wide, o MODE_400_Tall + DW o MODE_Single_Line, nil + +MODE_360x240: ; Data for 360 by 240 Pixels + + DB 0E7h ; 480 scan Lines & 28 Mhz Clock + DB 3 ; Maximum of 3 Pages + DW 360, 240 ; Displayed Pixels X,Y + DW 1092, 728 ; Max Possible X and Y Sizes + + DW o MODE_360_Wide, o MODE_240_Tall + DW o MODE_Double_Line , nil + +MODE_360x480: ; Data for 360 by 480 Pixels + + DB 0E7h ; 480 scan Lines & 28 Mhz Clock + DB 1 ; Only 1 Page Possible + DW 360, 480 ; Displayed Pixels X,Y + DW 544, 728 ; Max Possible X and Y Sizes + + DW o MODE_360_Wide, o MODE_480_Tall + DW o MODE_Single_Line , nil + +MODE_320x240: ; Data for 320 by 240 Pixels + + DB 0E3h ; 480 scan Lines & 25 Mhz Clock + DB 3 ; Maximum of 3 Pages + DW 320, 240 ; Displayed Pixels X,Y + DW 1088, 818 ; Max Possible X and Y Sizes + + DW o MODE_320_Wide, o MODE_240_Tall + DW o MODE_Double_Line, nil + +MODE_320x480: ; Data for 320 by 480 Pixels + + DB 0E3h ; 480 scan Lines & 25 Mhz Clock + DB 1 ; Only 1 Page Possible + DW 320, 480 ; Displayed Pixels X,Y + DW 540, 818 ; Max Possible X and Y Sizes + + DW o MODE_320_WIDE, o MODE_480_Tall + DW o MODE_Single_Line, nil + +MODE_360x200: ; Data for 360 by 200 Pixels + + DB 067h ; 400 scan Lines & 28 Mhz Clock + DB 3 ; Maximum of 3 Pages + DW 360, 200 ; Displayed Pixels (X,Y) + DW 1302, 728 ; Max Possible X and Y Sizes + + DW o MODE_360_Wide, MODE_200_Tall + DW o MODE_Double_Line, nil + +MODE_360x400: ; Data for 360 by 400 Pixels + + DB 067h ; 400 scan Lines & 28 Mhz Clock + DB 1 ; Maximum of 1 Pages + DW 360, 400 ; Displayed Pixels X,Y + DW 648, 816 ; Max Possible X and Y Sizes + + DW o MODE_360_Wide, MODE_400_Tall + DW o MODE_Single_Line, nil + + + ; ===== MODE X SETUP ROUTINES ===== + +;====================================================== +;SET_VGA_MODEX% (ModeType%, MaxXPos%, MaxYpos%, Pages%) +;====================================================== +; +; Sets Up the specified version of Mode X. Allows for +; the setup of multiple video pages, and a virtual +; screen which can be larger than the displayed screen +; (which can then be scrolled a pixel at a time) +; +; ENTRY: ModeType = Desired Screen Resolution (0-7) +; +; 0 = 320 x 200, 4 Pages max, 1.2:1 Aspect Ratio +; 1 = 320 x 400, 2 Pages max, 2.4:1 Aspect Ratio +; 2 = 360 x 200, 3 Pages max, 1.35:1 Aspect Ratio +; 3 = 360 x 400, 1 Page max, 2.7:1 Aspect Ratio +; 4 = 320 x 240, 3 Pages max, 1:1 Aspect Ratio +; 5 = 320 x 480, 1 Page max, 2:1 Aspect Ratio +; 6 = 360 x 240, 3 Pages max, 1.125:1 Aspect Ratio +; 7 = 360 x 480, 1 Page max, 2.25:1 Aspect Ratio +; +; MaxXpos = The Desired Virtual Screen Width +; MaxYpos = The Desired Virtual Screen Height +; Pages = The Desired # of Video Pages +; +; EXIT: AX = Success Flag: 0 = Failure / -1= Success +; + +SVM_STACK STRUC + SVM_Table DW ? ; Offset of Mode Info Table + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + SVM_Pages DW ? ; # of Screen Pages desired + SVM_Ysize DW ? ; Vertical Screen Size Desired + SVM_Xsize DW ? ; Horizontal Screen Size Desired + SVM_Mode DW ? ; Display Resolution Desired +SVM_STACK ENDS + + PUBLIC SET_VGA_MODEX + +SET_VGA_MODEX PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + SUB SP, 2 ; Allocate workspace + MOV BP, SP ; Set up Stack Frame + + ; Check Legality of Mode Request.... + + MOV BX, [BP].SVM_Mode ; Get Requested Mode # + CMP BX, NUM_MODES ; Is it 0..7? + JAE @SVM_BadModeSetup ; If Not, Error out + + SHL BX, 1 ; Scale BX + MOV SI, w MODE_TABLE[BX] ; CS:SI -> Mode Info + MOV [BP].SVM_Table, SI ; Save ptr for later use + + ; Check # of Requested Display Pages + + MOV CX, [BP].SVM_Pages ; Get # of Requested Pages + CLR CH ; Set Hi Word = 0! + CMP CL, CS:[SI].M_Pages ; Check # Pages for mode + JA @SVM_BadModeSetup ; Report Error if too Many Pages + JCXZ @SVM_BadModeSetup ; Report Error if 0 Pages + + ; Check Validity of X Size + + AND [BP].SVM_XSize, 0FFF8h ; X size Mod 8 Must = 0 + + MOV AX, [BP].SVM_XSize ; Get Logical Screen Width + CMP AX, CS:[SI].M_XSize ; Check against Displayed X + JB @SVM_BadModeSetup ; Report Error if too small + CMP AX, CS:[SI].M_XMax ; Check against Max X + JA @SVM_BadModeSetup ; Report Error if too big + + ; Check Validity of Y Size + + MOV BX, [BP].SVM_YSize ; Get Logical Screen Height + CMP BX, CS:[SI].M_YSize ; Check against Displayed Y + JB @SVM_BadModeSetup ; Report Error if too small + CMP BX, CS:[SI].M_YMax ; Check against Max Y + JA @SVM_BadModeSetup ; Report Error if too big + + ; Enough memory to Fit it all? + + SHR AX, 2 ; # of Bytes:Line = XSize/4 + MUL CX ; AX = Bytes/Line * Pages + MUL BX ; DX:AX = Total VGA mem needed + JNO @SVM_Continue ; Exit if Total Size > 256K + + DEC DX ; Was it Exactly 256K??? + OR DX, AX ; (DX = 1, AX = 0000) + JZ @SVM_Continue ; if so, it's valid... + +@SVM_BadModeSetup: + + CLR AX ; Return Value = False + JMP @SVM_Exit ; Normal Exit + +@SVM_Continue: + + MOV AX, 13H ; Start with Mode 13H + INT 10H ; Let BIOS Set Mode + + OUT_16 SC_INDEX, CHAIN4_OFF ; Disable Chain 4 Mode + OUT_16 SC_INDEX, ASYNC_RESET ; (A)synchronous Reset + OUT_8 MISC_OUTPUT, CS:[SI].M_MiscR ; Set New Timing/Size + OUT_16 SC_INDEX, SEQU_RESTART ; Restart Sequencer ... + + OUT_8 CRTC_INDEX, 11H ; Select Vert Retrace End Register + INC DX ; Point to Data + IN AL, DX ; Get Value, Bit 7 = Protect + AND AL, 7FH ; Mask out Write Protect + OUT DX, AL ; And send it back + + MOV DX, CRTC_INDEX ; Vga Crtc Registers + ADD SI, M_CRTC ; SI -> CRTC Parameter Data + + ; Load Tables of CRTC Parameters from List of Tables + +@SVM_Setup_Table: + + MOV DI, CS:[SI] ; Get Pointer to CRTC Data Tbl + ADD SI, 2 ; Point to next Ptr Entry + OR DI, DI ; A nil Ptr means that we have + JZ @SVM_Set_Data ; finished CRTC programming + +@SVM_Setup_CRTC: + MOV AX, CS:[DI] ; Get CRTC Data from Table + ADD DI, 2 ; Advance Pointer + OR AX, AX ; At End of Data Table? + JZ @SVM_Setup_Table ; If so, Exit & get next Table + + OUT DX, AX ; Reprogram VGA CRTC reg + JMP s @SVM_Setup_CRTC ; Process Next Table Entry + + ; Initialize Page & Scroll info, DI = 0 + +@SVM_Set_Data: + MOV DISPLAY_PAGE, DI ; Display Page = 0 + MOV ACTIVE_PAGE, DI ; Active Page = 0 + MOV CURRENT_PAGE, DI ; Current Page (Offset) = 0 + MOV CURRENT_XOFFSET, DI ; Horz Scroll Index = 0 + MOV CURRENT_YOFFSET, DI ; Vert Scroll Index = 0 + MOV CURRENT_MOFFSET, DI ; Memory Scroll Index = 0 + + MOV AX, VGA_SEGMENT ; Segment for VGA memory + MOV CURRENT_SEGMENT, AX ; Save for Future LES's + + ; Set Logical Screen Width, X Scroll and Our Data + + MOV SI, [BP].SVM_Table ; Get Saved Ptr to Mode Info + MOV AX, [BP].SVM_Xsize ; Get Display Width + + MOV CX, AX ; CX = Logical Width + SUB CX, CS:[SI].M_XSize ; CX = Max X Scroll Value + MOV MAX_XOFFSET, CX ; Set Maximum X Scroll + + SHR AX, 2 ; Bytes = Pixels / 4 + MOV SCREEN_WIDTH, AX ; Save Width in Pixels + + SHR AX, 1 ; Offset Value = Bytes / 2 + MOV AH, 13h ; CRTC Offset Register Index + XCHG AL, AH ; Switch format for OUT + OUT DX, AX ; Set VGA CRTC Offset Reg + + ; Setup Data table, Y Scroll, Misc for Other Routines + + MOV AX, [BP].SVM_Ysize ; Get Logical Screen Height + + MOV CX, AX ; CX = Logical Height + SUB BX, CS:[SI].M_YSize ; CX = Max Y Scroll Value + MOV MAX_YOFFSET, CX ; Set Maximum Y Scroll + + MOV SCREEN_HEIGHT, AX ; Save Height in Pixels + MUL SCREEN_WIDTH ; AX = Page Size in Bytes, + MOV PAGE_SIZE, AX ; Save Page Size + + MOV CX, [BP].SVM_Pages ; Get # of Pages + MOV LAST_PAGE, CX ; Save # of Pages + + CLR BX ; Page # = 0 + MOV DX, BX ; Page 0 Offset = 0 + +@SVM_Set_Pages: + + MOV PAGE_ADDR[BX], DX ; Set Page #(BX) Offset + ADD BX, 2 ; Page#++ + ADD DX, AX ; Compute Addr of Next Page + LOOPx CX, @SVM_Set_Pages ; Loop until all Pages Set + + ; Clear VGA Memory + + OUT_16 SC_INDEX, ALL_PLANES_ON ; Select All Planes + LES DI, d CURRENT_PAGE ; -> Start of VGA memory + + CLR AX ; AX = 0 + CLD ; Block Xfer Forwards + MOV CX, 8000H ; 32K * 4 * 2 = 256K + REP STOSW ; Clear dat memory! + + ; Setup Font Pointers + + MOV BH, ROM_8x8_Lo ; Ask for 8x8 Font, 0-127 + MOV AX, GET_CHAR_PTR ; Service to Get Pointer + INT 10h ; Call VGA BIOS + + MOV CHARSET_LOW, BP ; Save Char Set Offset + MOV CHARSET_LOW+2, ES ; Save Char Set Segment + + MOV BH, ROM_8x8_Hi ; Ask for 8x8 Font, 128-255 + MOV AX, GET_CHAR_PTR ; Service to Get Pointer + INT 10h ; Call VGA BIOS + + MOV CHARSET_HI, BP ; Save Char Set Offset + MOV CHARSET_HI+2, ES ; Save Char Set Segment + + MOV AX, True ; Return Success Code + +@SVM_EXIT: + ADD SP, 2 ; Deallocate workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 8 ; Exit & Clean Up Stack + +SET_VGA_MODEX ENDP + + +;================== +;SET_MODEX% (Mode%) +;================== +; +; Quickie Mode Set - Sets Up Mode X to Default Configuration +; +; ENTRY: ModeType = Desired Screen Resolution (0-7) +; (See SET_VGA_MODEX for list) +; +; EXIT: AX = Success Flag: 0 = Failure / -1= Success +; + +SM_STACK STRUC + DW ?,? ; BP, SI + DD ? ; Caller + SM_Mode DW ? ; Desired Screen Resolution +SM_STACK ENDS + + PUBLIC SET_MODEX + +SET_MODEX PROC FAR + + PUSHx BP, SI ; Preserve Important registers + MOV BP, SP ; Set up Stack Frame + + CLR AX ; Assume Failure + MOV BX, [BP].SM_Mode ; Get Desired Mode # + CMP BX, NUM_MODES ; Is it a Valid Mode #? + JAE @SMX_Exit ; If Not, don't Bother + + PUSH BX ; Push Mode Parameter + + SHL BX, 1 ; Scale BX to word Index + MOV SI, w MODE_TABLE[BX] ; CS:SI -> Mode Info + + PUSH CS:[SI].M_XSize ; Push Default X Size + PUSH CS:[SI].M_Ysize ; Push Default Y size + MOV AL, CS:[SI].M_Pages ; Get Default # of Pages + CLR AH ; Hi Byte = 0 + PUSH AX ; Push # Pages + + CALL f SET_VGA_MODEX ; Set up Mode X! + +@SMX_Exit: + POPx SI, BP ; Restore Registers + RET 2 ; Exit & Clean Up Stack + +SET_MODEX ENDP + + + ; ===== BASIC GRAPHICS PRIMITIVES ===== + +;============================ +;CLEAR_VGA_SCREEN (ColorNum%) +;============================ +; +; Clears the active display page +; +; ENTRY: ColorNum = Color Value to fill the page with +; +; EXIT: No meaningful values returned +; + +CVS_STACK STRUC + DW ?,? ; DI, BP + DD ? ; Caller + CVS_COLOR DB ?,? ; Color to Set Screen to +CVS_STACK ENDS + + PUBLIC CLEAR_VGA_SCREEN + +CLEAR_VGA_SCREEN PROC FAR + + PUSHx BP, DI ; Preserve Important Registers + MOV BP, SP ; Set up Stack Frame + + OUT_16 SC_INDEX, ALL_PLANES_ON ; Select All Planes + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + MOV AL, [BP].CVS_COLOR ; Get Color + MOV AH, AL ; Copy for Word Write + CLD ; Block fill Forwards + + MOV CX, PAGE_SIZE ; Get Size of Page + SHR CX, 1 ; Divide by 2 for Words + REP STOSW ; Block Fill VGA memory + + POPx DI, BP ; Restore Saved Registers + RET 2 ; Exit & Clean Up Stack + +CLEAR_VGA_SCREEN ENDP + + +;=================================== +;SET_POINT (Xpos%, Ypos%, ColorNum%) +;=================================== +; +; Plots a single Pixel on the active display page +; +; ENTRY: Xpos = X position to plot pixel at +; Ypos = Y position to plot pixel at +; ColorNum = Color to plot pixel with +; +; EXIT: No meaningful values returned +; + +SP_STACK STRUC + DW ?,? ; BP, DI + DD ? ; Caller + SETP_Color DB ?,? ; Color of Point to Plot + SETP_Ypos DW ? ; Y pos of Point to Plot + SETP_Xpos DW ? ; X pos of Point to Plot +SP_STACK ENDS + + PUBLIC SET_POINT + +SET_POINT PROC FAR + + PUSHx BP, DI ; Preserve Registers + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + MOV AX, [BP].SETP_Ypos ; Get Line # of Pixel + MUL SCREEN_WIDTH ; Get Offset to Start of Line + + MOV BX, [BP].SETP_Xpos ; Get Xpos + MOV CX, BX ; Copy to extract Plane # from + SHR BX, 2 ; X offset (Bytes) = Xpos/4 + ADD BX, AX ; Offset = Width*Ypos + Xpos/4 + + MOV AX, MAP_MASK_PLANE1 ; Map Mask & Plane Select Register + AND CL, PLANE_BITS ; Get Plane Bits + SHL AH, CL ; Get Plane Select Value + OUT_16 SC_Index, AX ; Select Plane + + MOV AL,[BP].SETP_Color ; Get Pixel Color + MOV ES:[DI+BX], AL ; Draw Pixel + + POPx DI, BP ; Restore Saved Registers + RET 6 ; Exit and Clean up Stack + +SET_POINT ENDP + + +;========================== +;READ_POINT% (Xpos%, Ypos%) +;========================== +; +; Read the color of a pixel from the Active Display Page +; +; ENTRY: Xpos = X position of pixel to read +; Ypos = Y position of pixel to read +; +; EXIT: AX = Color of Pixel at (Xpos, Ypos) +; + +RP_STACK STRUC + DW ?,? ; BP, DI + DD ? ; Caller + RP_Ypos DW ? ; Y pos of Point to Read + RP_Xpos DW ? ; X pos of Point to Read +RP_STACK ENDS + + PUBLIC READ_POINT + +READ_POINT PROC FAR + + PUSHx BP, DI ; Preserve Registers + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + MOV AX, [BP].RP_Ypos ; Get Line # of Pixel + MUL SCREEN_WIDTH ; Get Offset to Start of Line + + MOV BX, [BP].RP_Xpos ; Get Xpos + MOV CX, BX + SHR BX, 2 ; X offset (Bytes) = Xpos/4 + ADD BX, AX ; Offset = Width*Ypos + Xpos/4 + + MOV AL, READ_MAP ; GC Read Mask Register + MOV AH, CL ; Get Xpos + AND AH, PLANE_BITS ; & mask out Plane # + OUT_16 GC_INDEX, AX ; Select Plane to read in + + CLR AH ; Clear Return Value Hi byte + MOV AL, ES:[DI+BX] ; Get Color of Pixel + + POPx DI, BP ; Restore Saved Registers + RET 4 ; Exit and Clean up Stack + +READ_POINT ENDP + + +;====================================================== +;FILL_BLOCK (Xpos1%, Ypos1%, Xpos2%, Ypos2%, ColorNum%) +;====================================================== +; +; Fills a rectangular block on the active display Page +; +; ENTRY: Xpos1 = Left X position of area to fill +; Ypos1 = Top Y position of area to fill +; Xpos2 = Right X position of area to fill +; Ypos2 = Bottom Y position of area to fill +; ColorNum = Color to fill area with +; +; EXIT: No meaningful values returned +; + +FB_STACK STRUC + DW ?x4 ; DS, DI, SI, BP + DD ? ; Caller + FB_Color DB ?,? ; Fill Color + FB_Ypos2 DW ? ; Y pos of Lower Right Pixel + FB_Xpos2 DW ? ; X pos of Lower Right Pixel + FB_Ypos1 DW ? ; Y pos of Upper Left Pixel + FB_Xpos1 DW ? ; X pos of Upper Left Pixel +FB_STACK ENDS + + PUBLIC FILL_BLOCK + +FILL_BLOCK PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + CLD ; Direction Flag = Forward + + OUT_8 SC_INDEX, MAP_MASK ; Set up for Plane Select + + ; Validate Pixel Coordinates + ; If necessary, Swap so X1 <= X2, Y1 <= Y2 + + MOV AX, [BP].FB_Ypos1 ; AX = Y1 is Y1< Y2? + MOV BX, [BP].FB_Ypos2 ; BX = Y2 + CMP AX, BX + JLE @FB_NOSWAP1 + + MOV [BP].FB_Ypos1, BX ; Swap Y1 and Y2 and save Y1 + XCHG AX, BX ; on stack for future use + +@FB_NOSWAP1: + SUB BX, AX ; Get Y width + INC BX ; Add 1 to avoid 0 value + MOV [BP].FB_Ypos2, BX ; Save in Ypos2 + + MUL SCREEN_WIDTH ; Mul Y1 by Bytes per Line + ADD DI, AX ; DI = Start of Line Y1 + + MOV AX, [BP].FB_Xpos1 ; Check X1 <= X2 + MOV BX, [BP].FB_Xpos2 ; + CMP AX, BX + JLE @FB_NOSWAP2 ; Skip Ahead if Ok + + MOV [BP].FB_Xpos2, AX ; Swap X1 AND X2 and save X2 + XCHG AX, BX ; on stack for future use + + ; All our Input Values are in order, Now determine + ; How many full "bands" 4 pixels wide (aligned) there + ; are, and if there are partial bands (<4 pixels) on + ; the left and right edges. + +@FB_NOSWAP2: + MOV DX, AX ; DX = X1 (Pixel Position) + SHR DX, 2 ; DX/4 = Bytes into Line + ADD DI, DX ; DI = Addr of Upper-Left Corner + + MOV CX, BX ; CX = X2 (Pixel Position) + SHR CX, 2 ; CX/4 = Bytes into Line + + CMP DX, CX ; Start and end in same band? + JNE @FB_NORMAL ; if not, check for l & r edges + JMP @FB_ONE_BAND_ONLY ; if so, then special processing + +@FB_NORMAL: + SUB CX, DX ; CX = # bands -1 + MOV SI, AX ; SI = PLANE#(X1) + AND SI, PLANE_BITS ; if Left edge is aligned then + JZ @FB_L_PLANE_FLUSH ; no special processing.. + + ; Draw "Left Edge" vertical strip of 1-3 pixels... + + OUT_8 SC_Data, Left_Clip_Mask[SI] ; Set Left Edge Plane Mask + + MOV SI, DI ; SI = Copy of Start Addr (UL) + + MOV DX, [BP].FB_Ypos2 ; Get # of Lines to draw + MOV AL, [BP].FB_Color ; Get Fill Color + MOV BX, SCREEN_WIDTH ; Get Vertical increment Value + +@FB_LEFT_LOOP: + MOV ES:[SI], AL ; Fill in Left Edge Pixels + ADD SI, BX ; Point to Next Line (Below) + LOOPjz DX, @FB_LEFT_CONT ; Exit loop if all Lines Drawn + + MOV ES:[SI], AL ; Fill in Left Edge Pixels + ADD SI, BX ; Point to Next Line (Below) + LOOPx DX, @FB_LEFT_LOOP ; loop until left strip is drawn + +@FB_LEFT_CONT: + + INC DI ; Point to Middle (or Right) Block + DEC CX ; Reset CX instead of JMP @FB_RIGHT + +@FB_L_PLANE_FLUSH: + INC CX ; Add in Left band to middle block + + ; DI = Addr of 1st middle Pixel (band) to fill + ; CX = # of Bands to fill -1 + +@FB_RIGHT: + MOV SI, [BP].FB_Xpos2 ; Get Xpos2 + AND SI, PLANE_BITS ; Get Plane values + CMP SI, 0003 ; Plane = 3? + JE @FB_R_EDGE_FLUSH ; Hey, add to middle + + ; Draw "Right Edge" vertical strip of 1-3 pixels... + + OUT_8 SC_Data, Right_Clip_Mask[SI] ; Right Edge Plane Mask + + MOV SI, DI ; Get Addr of Left Edge + ADD SI, CX ; Add Width-1 (Bands) + DEC SI ; To point to top of Right Edge + + MOV DX, [BP].FB_Ypos2 ; Get # of Lines to draw + MOV AL, [BP].FB_Color ; Get Fill Color + MOV BX, SCREEN_WIDTH ; Get Vertical increment Value + +@FB_RIGHT_LOOP: + MOV ES:[SI], AL ; Fill in Right Edge Pixels + ADD SI, BX ; Point to Next Line (Below) + LOOPjz DX, @FB_RIGHT_CONT ; Exit loop if all Lines Drawn + + MOV ES:[SI], AL ; Fill in Right Edge Pixels + ADD SI, BX ; Point to Next Line (Below) + LOOPx DX, @FB_RIGHT_LOOP ; loop until left strip is drawn + +@FB_RIGHT_CONT: + + DEC CX ; Minus 1 for Middle bands + JZ @FB_EXIT ; Uh.. no Middle bands... + +@FB_R_EDGE_FLUSH: + + ; DI = Addr of Upper Left block to fill + ; CX = # of Bands to fill in (width) + + OUT_8 SC_Data, ALL_PLANES ; Write to All Planes + + MOV DX, SCREEN_WIDTH ; DX = DI Increment + SUB DX, CX ; = Screen_Width-# Planes Filled + + MOV BX, CX ; BX = Quick Refill for CX + MOV SI, [BP].FB_Ypos2 ; SI = # of Line to Fill + MOV AL, [BP].FB_Color ; Get Fill Color + +@FB_MIDDLE_LOOP: + REP STOSB ; Fill in entire line + + MOV CX, BX ; Recharge CX (Line Width) + ADD DI, DX ; Point to start of Next Line + LOOPx SI, @FB_MIDDLE_LOOP ; Loop until all lines drawn + + JMP s @FB_EXIT ; Outa here + +@FB_ONE_BAND_ONLY: + MOV SI, AX ; Get Left Clip Mask, Save X1 + AND SI, PLANE_BITS ; Mask out Row # + MOV AL, Left_Clip_Mask[SI] ; Get Left Edge Mask + MOV SI, BX ; Get Right Clip Mask, Save X2 + AND SI, PLANE_BITS ; Mask out Row # + AND AL, Right_Clip_Mask[SI] ; Get Right Edge Mask byte + + OUT_8 SC_Data, AL ; Clip For Left & Right Masks + + MOV CX, [BP].FB_Ypos2 ; Get # of Lines to draw + MOV AL, [BP].FB_Color ; Get Fill Color + MOV BX, SCREEN_WIDTH ; Get Vertical increment Value + +@FB_ONE_LOOP: + MOV ES:[DI], AL ; Fill in Pixels + ADD DI, BX ; Point to Next Line (Below) + LOOPjz CX, @FB_EXIT ; Exit loop if all Lines Drawn + + MOV ES:[DI], AL ; Fill in Pixels + ADD DI, BX ; Point to Next Line (Below) + LOOPx CX, @FB_ONE_LOOP ; loop until left strip is drawn + +@FB_EXIT: + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 10 ; Exit and Clean up Stack + +FILL_BLOCK ENDP + + +;===================================================== +;DRAW_LINE (Xpos1%, Ypos1%, Xpos2%, Ypos2%, ColorNum%) +;===================================================== +; +; Draws a Line on the active display page +; +; ENTRY: Xpos1 = X position of first point on line +; Ypos1 = Y position of first point on line +; Xpos2 = X position of last point on line +; Ypos2 = Y position of last point on line +; ColorNum = Color to draw line with +; +; EXIT: No meaningful values returned +; + +DL_STACK STRUC + DW ?x3 ; DI, SI, BP + DD ? ; Caller + DL_ColorF DB ?,? ; Line Draw Color + DL_Ypos2 DW ? ; Y pos of last point + DL_Xpos2 DW ? ; X pos of last point + DL_Ypos1 DW ? ; Y pos of first point + DL_Xpos1 DW ? ; X pos of first point +DL_STACK ENDS + + PUBLIC DRAW_LINE + +DRAW_LINE PROC FAR + + PUSHx BP, SI, DI ; Preserve Important Registers + MOV BP, SP ; Set up Stack Frame + CLD ; Direction Flag = Forward + + OUT_8 SC_INDEX, MAP_MASK ; Set up for Plane Select + MOV CH, [BP].DL_ColorF ; Save Line Color in CH + + ; Check Line Type + + MOV SI, [BP].DL_Xpos1 ; AX = X1 is X1< X2? + MOV DI, [BP].DL_Xpos2 ; DX = X2 + CMP SI, DI ; Is X1 < X2 + JE @DL_VLINE ; If X1=X2, Draw Vertical Line + JL @DL_NOSWAP1 ; If X1 < X2, don't swap + + XCHG SI, DI ; X2 IS > X1, SO SWAP THEM + +@DL_NOSWAP1: + + ; SI = X1, DI = X2 + + MOV AX, [BP].DL_Ypos1 ; AX = Y1 is Y1 <> Y2? + CMP AX, [BP].DL_Ypos2 ; Y1 = Y2? + JE @DL_HORZ ; If so, Draw a Horizontal Line + + JMP @DL_BREZHAM ; Diagonal line... go do it... + + ; This Code draws a Horizontal Line in Mode X where: + ; SI = X1, DI = X2, and AX = Y1/Y2 + +@DL_HORZ: + + MUL SCREEN_WIDTH ; Offset = Ypos * Screen_Width + MOV DX, AX ; CX = Line offset into Page + + MOV AX, SI ; Get Left edge, Save X1 + AND SI, PLANE_BITS ; Mask out Row # + MOV BL, Left_Clip_Mask[SI] ; Get Left Edge Mask + MOV CX, DI ; Get Right edge, Save X2 + AND DI, PLANE_BITS ; Mask out Row # + MOV BH, Right_Clip_Mask[DI] ; Get Right Edge Mask byte + + SHR AX, 2 ; Get X1 Byte # (=X1/4) + SHR CX, 2 ; Get X2 Byte # (=X2/4) + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + ADD DI, DX ; Point to Start of Line + ADD DI, AX ; Point to Pixel X1 + + SUB CX, AX ; CX = # Of Bands (-1) to set + JNZ @DL_LONGLN ; jump if longer than one segment + + AND BL, BH ; otherwise, merge clip masks + +@DL_LONGLN: + + OUT_8 SC_Data, BL ; Set the Left Clip Mask + + MOV AL, [BP].DL_ColorF ; Get Line Color + MOV BL, AL ; BL = Copy of Line Color + STOSB ; Set Left (1-4) Pixels + + JCXZ @DL_EXIT ; Done if only one Line Segment + + DEC CX ; CX = # of Middle Segments + JZ @DL_XRSEG ; If no middle segments.... + + ; Draw Middle Segments + + OUT_8 DX, ALL_PLANES ; Write to ALL Planes + + MOV AL, BL ; Get Color from BL + REP STOSB ; Draw Middle (4 Pixel) Segments + +@DL_XRSEG: + OUT_8 DX, BH ; Select Planes for Right Clip Mask + MOV AL, BL ; Get Color Value + STOSB ; Draw Right (1-4) Pixels + + JMP s @DL_EXIT ; We Are Done... + + + ; This Code Draws A Vertical Line. On entry: + ; CH = Line Color, SI & DI = X1 + +@DL_VLINE: + + MOV AX, [BP].DL_Ypos1 ; AX = Y1 + MOV SI, [BP].DL_Ypos2 ; SI = Y2 + CMP AX, SI ; Is Y1 < Y2? + JLE @DL_NOSWAP2 ; if so, Don't Swap them + + XCHG AX, SI ; Ok, NOW Y1 < Y2 + +@DL_NOSWAP2: + + SUB SI, AX ; SI = Line Height (Y2-Y1+1) + INC SI + + ; AX = Y1, DI = X1, Get offset into Page into AX + + MUL SCREEN_WIDTH ; Offset = Y1 (AX) * Screen Width + MOV DX, DI ; Copy Xpos into DX + SHR DI, 2 ; DI = Xpos/4 + ADD AX, DI ; DI = Xpos/4 + ScreenWidth * Y1 + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + ADD DI, AX ; Point to Pixel X1, Y1 + + ;Select Plane + + MOV CL, DL ; CL = Save X1 + AND CL, PLANE_BITS ; Get X1 MOD 4 (Plane #) + MOV AX, MAP_MASK_PLANE1 ; Code to set Plane #1 + SHL AH, CL ; Change to Correct Plane # + OUT_16 SC_Index, AX ; Select Plane + + MOV AL, CH ; Get Saved Color + MOV BX, SCREEN_WIDTH ; Get Offset to Advance Line By + +@DL_VLoop: + MOV ES:[DI], AL ; Draw Single Pixel + ADD DI, BX ; Point to Next Line + LOOPjz SI, @DL_EXIT ; Lines--, Exit if done + + MOV ES:[DI], AL ; Draw Single Pixel + ADD DI, BX ; Point to Next Line + LOOPx SI, @DL_VLoop ; Lines--, Loop until Done + +@DL_EXIT: + + JMP @DL_EXIT2 ; Done! + + ; This code Draws a diagonal line in Mode X + +@DL_BREZHAM: + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + MOV AX, [BP].DL_Ypos1 ; get Y1 value + MOV BX, [BP].DL_Ypos2 ; get Y2 value + MOV CX, [BP].DL_Xpos1 ; Get Starting Xpos + + CMP BX, AX ; Y2-Y1 is? + JNC @DL_DeltaYOK ; if Y2>=Y1 then goto... + + XCHG BX, AX ; Swap em... + MOV CX, [BP].DL_Xpos2 ; Get New Starting Xpos + +@DL_DeltaYOK: + MUL SCREEN_WIDTH ; Offset = SCREEN_WIDTH * Y1 + + ADD DI, AX ; DI -> Start of Line Y1 on Page + MOV AX, CX ; AX = Xpos (X1) + SHR AX, 2 ; /4 = Byte Offset into Line + ADD DI, AX ; DI = Starting pos (X1,Y1) + + MOV AL, 11h ; Staring Mask + AND CL, PLANE_BITS ; Get Plane # + SHL AL, CL ; and shift into place + MOV AH, [BP].DL_ColorF ; Color in Hi Bytes + + PUSH AX ; Save Mask,Color... + + MOV AH, AL ; Plane # in AH + MOV AL, MAP_MASK ; Select Plane Register + OUT_16 SC_Index, AX ; Select initial plane + + MOV AX, [BP].DL_Xpos1 ; get X1 value + MOV BX, [BP].DL_Ypos1 ; get Y1 value + MOV CX, [BP].DL_Xpos2 ; get X2 value + MOV DX, [BP].DL_Ypos2 ; get Y2 value + + MOV BP, SCREEN_WIDTH ; Use BP for Line width to + ; to avoid extra memory access + + SUB DX, BX ; figure Delta_Y + JNC @DL_DeltaYOK2 ; jump if Y2 >= Y1 + + ADD BX, DX ; put Y2 into Y1 + NEG DX ; abs(Delta_Y) + XCHG AX, CX ; and exchange X1 and X2 + +@DL_DeltaYOK2: + MOV BX, 08000H ; seed for fraction accumulator + + SUB CX, AX ; figure Delta_X + JC @DL_DrawLeft ; if negative, go left + + JMP @DL_DrawRight ; Draw Line that slopes right + +@DL_DrawLeft: + + NEG CX ; abs(Delta_X) + + CMP CX, DX ; is Delta_X < Delta_Y? + JB @DL_SteepLeft ; yes, so go do steep line + ; (Delta_Y iterations) + + ; Draw a Shallow line to the left in Mode X + +@DL_ShallowLeft: + CLR AX ; zero low word of Delta_Y * 10000h + SUB AX, DX ; DX:AX <- DX * 0FFFFh + SBB DX, 0 ; include carry + DIV CX ; divide by Delta_X + + MOV SI, BX ; SI = Accumulator + MOV BX, AX ; BX = Add fraction + POP AX ; Get Color, Bit mask + MOV DX, SC_Data ; Sequence controller data register + INC CX ; Inc Delta_X so we can unroll loop + + ; Loop (x2) to Draw Pixels, Move Left, and Maybe Down... + +@DL_SLLLoop: + MOV ES:[DI], AH ; set first pixel, plane data set up + LOOPjz CX, @DL_SLLExit ; Delta_X--, Exit if done + + ADD SI, BX ; add numerator to accumulator + JNC @DL_SLLL2nc ; move down on carry + + ADD DI, BP ; Move Down one line... + +@DL_SLLL2nc: + DEC DI ; Left one addr + ROR AL, 1 ; Move Left one plane, back on 0 1 2 + CMP AL, 87h ; wrap?, if AL <88 then Carry set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + + MOV ES:[DI], AH ; set pixel + LOOPjz CX, @DL_SLLExit ; Delta_X--, Exit if done + + ADD SI, BX ; add numerator to accumulator, + JNC @DL_SLLL3nc ; move down on carry + + ADD DI, BP ; Move Down one line... + +@DL_SLLL3nc: ; Now move left a pixel... + DEC DI ; Left one addr + ROR AL, 1 ; Move Left one plane, back on 0 1 2 + CMP AL, 87h ; Wrap?, if AL <88 then Carry set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + JMP s @DL_SLLLoop ; loop until done + +@DL_SLLExit: + JMP @DL_EXIT2 ; and exit + + ; Draw a steep line to the left in Mode X + +@DL_SteepLeft: + CLR AX ; zero low word of Delta_Y * 10000h + XCHG DX, CX ; Delta_Y switched with Delta_X + DIV CX ; divide by Delta_Y + + MOV SI, BX ; SI = Accumulator + MOV BX, AX ; BX = Add Fraction + POP AX ; Get Color, Bit mask + MOV DX, SC_Data ; Sequence controller data register + INC CX ; Inc Delta_Y so we can unroll loop + + ; Loop (x2) to Draw Pixels, Move Down, and Maybe left + +@DL_STLLoop: + + MOV ES:[DI], AH ; set first pixel + LOOPjz CX, @DL_STLExit ; Delta_Y--, Exit if done + + ADD SI, BX ; add numerator to accumulator + JNC @DL_STLnc2 ; No carry, just move down! + + DEC DI ; Move Left one addr + ROR AL, 1 ; Move Left one plane, back on 0 1 2 + CMP AL, 87h ; Wrap?, if AL <88 then Carry set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + +@DL_STLnc2: + ADD DI, BP ; advance to next line. + + MOV ES:[DI], AH ; set pixel + LOOPjz CX, @DL_STLExit ; Delta_Y--, Exit if done + + ADD SI, BX ; add numerator to accumulator + JNC @DL_STLnc3 ; No carry, just move down! + + DEC DI ; Move Left one addr + ROR AL, 1 ; Move Left one plane, back on 0 1 2 + CMP AL, 87h ; Wrap?, if AL <88 then Carry set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + +@DL_STLnc3: + ADD DI, BP ; advance to next line. + JMP s @DL_STLLoop ; Loop until done + +@DL_STLExit: + JMP @DL_EXIT2 ; and exit + + ; Draw a line that goes to the Right... + +@DL_DrawRight: + CMP CX, DX ; is Delta_X < Delta_Y? + JB @DL_SteepRight ; yes, so go do steep line + ; (Delta_Y iterations) + + ; Draw a Shallow line to the Right in Mode X + +@DL_ShallowRight: + CLR AX ; zero low word of Delta_Y * 10000h + SUB AX, DX ; DX:AX <- DX * 0FFFFh + SBB DX, 0 ; include carry + DIV CX ; divide by Delta_X + + MOV SI, BX ; SI = Accumulator + MOV BX, AX ; BX = Add Fraction + POP AX ; Get Color, Bit mask + MOV DX, SC_Data ; Sequence controller data register + INC CX ; Inc Delta_X so we can unroll loop + + ; Loop (x2) to Draw Pixels, Move Right, and Maybe Down... + +@DL_SLRLoop: + MOV ES:[DI], AH ; set first pixel, mask is set up + LOOPjz CX, @DL_SLRExit ; Delta_X--, Exit if done.. + + ADD SI, BX ; add numerator to accumulator + JNC @DL_SLR2nc ; don't move down if carry not set + + ADD DI, BP ; Move Down one line... + +@DL_SLR2nc: ; Now move right a pixel... + ROL AL, 1 ; Move Right one addr if Plane = 0 + CMP AL, 12h ; Wrap? if AL >12 then Carry not set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + + MOV ES:[DI], AH ; set pixel + LOOPjz CX, @DL_SLRExit ; Delta_X--, Exit if done.. + + ADD SI, BX ; add numerator to accumulator + JNC @DL_SLR3nc ; don't move down if carry not set + + ADD DI, BP ; Move Down one line... + +@DL_SLR3nc: + ROL AL, 1 ; Move Right one addr if Plane = 0 + CMP AL, 12h ; Wrap? if AL >12 then Carry not set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + JMP s @DL_SLRLoop ; loop till done + +@DL_SLRExit: + JMP @DL_EXIT2 ; and exit + + ; Draw a Steep line to the Right in Mode X + +@DL_SteepRight: + CLR AX ; zero low word of Delta_Y * 10000h + XCHG DX, CX ; Delta_Y switched with Delta_X + DIV CX ; divide by Delta_Y + + MOV SI, BX ; SI = Accumulator + MOV BX, AX ; BX = Add Fraction + POP AX ; Get Color, Bit mask + MOV DX, SC_Data ; Sequence controller data register + INC CX ; Inc Delta_Y so we can unroll loop + + ; Loop (x2) to Draw Pixels, Move Down, and Maybe Right + +@STRLoop: + MOV ES:[DI], AH ; set first pixel, mask is set up + LOOPjz CX, @DL_EXIT2 ; Delta_Y--, Exit if Done + + ADD SI, BX ; add numerator to accumulator + JNC @STRnc2 ; if no carry then just go down... + + ROL AL, 1 ; Move Right one addr if Plane = 0 + CMP AL, 12h ; Wrap? if AL >12 then Carry not set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + +@STRnc2: + ADD DI, BP ; advance to next line. + + MOV ES:[DI], AH ; set pixel + LOOPjz CX, @DL_EXIT2 ; Delta_Y--, Exit if Done + + ADD SI, BX ; add numerator to accumulator + JNC @STRnc3 ; if no carry then just go down... + + ROL AL, 1 ; Move Right one addr if Plane = 0 + CMP AL, 12h ; Wrap? if AL >12 then Carry not set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + +@STRnc3: + ADD DI, BP ; advance to next line. + JMP s @STRLoop ; loop till done + +@DL_EXIT2: + POPx DI, SI, BP ; Restore Saved Registers + RET 10 ; Exit and Clean up Stack + +DRAW_LINE ENDP + + + ; ===== DAC COLOR REGISTER ROUTINES ===== + +;================================================= +;SET_DAC_REGISTER (Register%, Red%, Green%, Blue%) +;================================================= +; +; Sets a single (RGB) Vga Palette Register +; +; ENTRY: Register = The DAC # to modify (0-255) +; Red = The new Red Intensity (0-63) +; Green = The new Green Intensity (0-63) +; Blue = The new Blue Intensity (0-63) +; +; EXIT: No meaningful values returned +; + +SDR_STACK STRUC + DW ? ; BP + DD ? ; Caller + SDR_Blue DB ?,? ; Blue Data Value + SDR_Green DB ?,? ; Green Data Value + SDR_Red DB ?,? ; Red Data Value + SDR_Register DB ?,? ; Palette Register # +SDR_STACK ENDS + + PUBLIC SET_DAC_REGISTER + +SET_DAC_REGISTER PROC FAR + + PUSH BP ; Save BP + MOV BP, SP ; Set up Stack Frame + + ; Select which DAC Register to modify + + OUT_8 DAC_WRITE_ADDR, [BP].SDR_Register + + MOV DX, PEL_DATA_REG ; Dac Data Register + OUT_8 DX, [BP].SDR_Red ; Set Red Intensity + OUT_8 DX, [BP].SDR_Green ; Set Green Intensity + OUT_8 DX, [BP].SDR_Blue ; Set Blue Intensity + + POP BP ; Restore Registers + RET 8 ; Exit & Clean Up Stack + +SET_DAC_REGISTER ENDP + +;==================================================== +;GET_DAC_REGISTER (Register%, &Red%, &Green%, &Blue%) +;==================================================== +; +; Reads the RGB Values of a single Vga Palette Register +; +; ENTRY: Register = The DAC # to read (0-255) +; Red = Offset to Red Variable in DS +; Green = Offset to Green Variable in DS +; Blue = Offset to Blue Variable in DS +; +; EXIT: The values of the integer variables Red, +; Green, and Blue are set to the values +; taken from the specified DAC register. +; + +GDR_STACK STRUC + DW ? ; BP + DD ? ; Caller + GDR_Blue DW ? ; Addr of Blue Data Value in DS + GDR_Green DW ? ; Addr of Green Data Value in DS + GDR_Red DW ? ; Addr of Red Data Value in DS + GDR_Register DB ?,? ; Palette Register # +GDR_STACK ENDS + + PUBLIC GET_DAC_REGISTER + +GET_DAC_REGISTER PROC FAR + + PUSH BP ; Save BP + MOV BP, SP ; Set up Stack Frame + + ; Select which DAC Register to read in + + OUT_8 DAC_READ_ADDR, [BP].GDR_Register + + MOV DX, PEL_DATA_REG ; Dac Data Register + CLR AX ; Clear AX + + IN AL, DX ; Read Red Value + MOV BX, [BP].GDR_Red ; Get Address of Red% + MOV [BX], AX ; *Red% = AX + + IN AL, DX ; Read Green Value + MOV BX, [BP].GDR_Green ; Get Address of Green% + MOV [BX], AX ; *Green% = AX + + IN AL, DX ; Read Blue Value + MOV BX, [BP].GDR_Blue ; Get Address of Blue% + MOV [BX], AX ; *Blue% = AX + + POP BP ; Restore Registers + RET 8 ; Exit & Clean Up Stack + +GET_DAC_REGISTER ENDP + + +;=========================================================== +;LOAD_DAC_REGISTERS (SEG PalData, StartReg%, EndReg%, Sync%) +;=========================================================== +; +; Sets a Block of Vga Palette Registers +; +; ENTRY: PalData = Far Pointer to Block of palette data +; StartReg = First Register # in range to set (0-255) +; EndReg = Last Register # in Range to set (0-255) +; Sync = Wait for Vertical Retrace Flag (Boolean) +; +; EXIT: No meaningful values returned +; +; NOTES: PalData is a linear array of 3 byte Palette values +; in the order: Red (0-63), Green (0-63), Blue (0-63) +; + +LDR_STACK STRUC + DW ?x3 ; BP, DS, SI + DD ? ; Caller + LDR_Sync DW ? ; Vertical Sync Flag + LDR_EndReg DB ?,? ; Last Register # + LDR_StartReg DB ?,? ; First Register # + LDR_PalData DD ? ; Far Ptr to Palette Data +LDR_STACK ENDS + + PUBLIC LOAD_DAC_REGISTERS + +LOAD_DAC_REGISTERS PROC FAR + + PUSHx BP, DS, SI ; Save Registers + mov BP, SP ; Set up Stack Frame + + mov AX, [BP].LDR_Sync ; Get Vertical Sync Flag + or AX, AX ; is Sync Flag = 0? + jz @LDR_Load ; if so, skip call + + call f SYNC_DISPLAY ; wait for vsync + + ; Determine register #'s, size to copy, etc + +@LDR_Load: + + lds SI, [BP].LDR_PalData ; DS:SI -> Palette Data + mov DX, DAC_WRITE_ADDR ; DAC register # selector + + CLR AX, BX ; Clear for byte loads + mov AL, [BP].LDR_StartReg ; Get Start Register + mov BL, [BP].LDR_EndReg ; Get End Register + + sub BX, AX ; BX = # of DAC registers -1 + inc BX ; BX = # of DAC registers + mov CX, BX ; CX = # of DAC registers + add CX, BX ; CX = " " * 2 + add CX, BX ; CX = " " * 3 + cld ; Block OUTs forward + out DX, AL ; set up correct register # + + ; Load a block of DAC Registers + + mov DX, PEL_DATA_REG ; Dac Data Register + + rep outsb ; block set DAC registers + + POPx SI, DS, BP ; Restore Registers + ret 10 ; Exit & Clean Up Stack + +LOAD_DAC_REGISTERS ENDP + + +;==================================================== +;READ_DAC_REGISTERS (SEG PalData, StartReg%, EndReg%) +;==================================================== +; +; Reads a Block of Vga Palette Registers +; +; ENTRY: PalData = Far Pointer to block to store palette data +; StartReg = First Register # in range to read (0-255) +; EndReg = Last Register # in Range to read (0-255) +; +; EXIT: No meaningful values returned +; +; NOTES: PalData is a linear array of 3 byte Palette values +; in the order: Red (0-63), Green (0-63), Blue (0-63) +; + +RDR_STACK STRUC + DW ?x3 ; BP, ES, DI + DD ? ; Caller + RDR_EndReg DB ?,? ; Last Register # + RDR_StartReg DB ?,? ; First Register # + RDR_PalData DD ? ; Far Ptr to Palette Data +RDR_STACK ENDS + + PUBLIC READ_DAC_REGISTERS + +READ_DAC_REGISTERS PROC FAR + + PUSHx BP, ES, DI ; Save Registers + mov BP, SP ; Set up Stack Frame + + ; Determine register #'s, size to copy, etc + + les DI, [BP].RDR_PalData ; ES:DI -> Palette Buffer + mov DX, DAC_READ_ADDR ; DAC register # selector + + CLR AX, BX ; Clear for byte loads + mov AL, [BP].RDR_StartReg ; Get Start Register + mov BL, [BP].RDR_EndReg ; Get End Register + + sub BX, AX ; BX = # of DAC registers -1 + inc BX ; BX = # of DAC registers + mov CX, BX ; CX = # of DAC registers + add CX, BX ; CX = " " * 2 + add CX, BX ; CX = " " * 3 + cld ; Block INs forward + + ; Read a block of DAC Registers + + out DX, AL ; set up correct register # + mov DX, PEL_DATA_REG ; Dac Data Register + + rep insb ; block read DAC registers + + POPx DI, ES, BP ; Restore Registers + ret 8 ; Exit & Clean Up Stack + +READ_DAC_REGISTERS ENDP + + + ; ===== PAGE FLIPPING AND SCROLLING ROUTINES ===== + +;========================= +;SET_ACTIVE_PAGE (PageNo%) +;========================= +; +; Sets the active display Page to be used for future drawing +; +; ENTRY: PageNo = Display Page to make active +; (values: 0 to Number of Pages - 1) +; +; EXIT: No meaningful values returned +; + +SAP_STACK STRUC + DW ? ; BP + DD ? ; Caller + SAP_Page DW ? ; Page # for Drawing +SAP_STACK ENDS + + PUBLIC SET_ACTIVE_PAGE + +SET_ACTIVE_PAGE PROC FAR + + PUSH BP ; Preserve Registers + MOV BP, SP ; Set up Stack Frame + + MOV BX, [BP].SAP_Page ; Get Desired Page # + CMP BX, LAST_PAGE ; Is Page # Valid? + JAE @SAP_Exit ; IF Not, Do Nothing + + MOV ACTIVE_PAGE, BX ; Set Active Page # + + SHL BX, 1 ; Scale Page # to Word + MOV AX, PAGE_ADDR[BX] ; Get offset to Page + + MOV CURRENT_PAGE, AX ; And set for future LES's + +@SAP_Exit: + POP BP ; Restore Registers + RET 2 ; Exit and Clean up Stack + +SET_ACTIVE_PAGE ENDP + + +;================ +;GET_ACTIVE_PAGE% +;================ +; +; Returns the Video Page # currently used for Drawing +; +; ENTRY: No Parameters are passed +; +; EXIT: AX = Current Video Page used for Drawing +; + + PUBLIC GET_ACTIVE_PAGE + +GET_ACTIVE_PAGE PROC FAR + + MOV AX, ACTIVE_PAGE ; Get Active Page # + RET ; Exit and Clean up Stack + +GET_ACTIVE_PAGE ENDP + + +;=============================== +;SET_DISPLAY_PAGE (DisplayPage%) +;=============================== +; +; Sets the currently visible display page. +; When called this routine syncronizes the display +; to the vertical blank. +; +; ENTRY: PageNo = Display Page to show on the screen +; (values: 0 to Number of Pages - 1) +; +; EXIT: No meaningful values returned +; + +SDP_STACK STRUC + DW ? ; BP + DD ? ; Caller + SDP_Page DW ? ; Page # to Display... +SDP_STACK ENDS + + PUBLIC SET_DISPLAY_PAGE + +SET_DISPLAY_PAGE PROC FAR + + PUSH BP ; Preserve Registers + MOV BP, SP ; Set up Stack Frame + + MOV BX, [BP].SDP_Page ; Get Desired Page # + CMP BX, LAST_PAGE ; Is Page # Valid? + JAE @SDP_Exit ; IF Not, Do Nothing + + MOV DISPLAY_PAGE, BX ; Set Display Page # + + SHL BX, 1 ; Scale Page # to Word + MOV CX, PAGE_ADDR[BX] ; Get offset in memory to Page + ADD CX, CURRENT_MOFFSET ; Adjust for any scrolling + + ; Wait if we are currently in a Vertical Retrace + + MOV DX, INPUT_1 ; Input Status #1 Register + +@DP_WAIT0: + IN AL, DX ; Get VGA status + AND AL, VERT_RETRACE ; In Display mode yet? + JNZ @DP_WAIT0 ; If Not, wait for it + + ; Set the Start Display Address to the new page + + MOV DX, CRTC_Index ; We Change the VGA Sequencer + + MOV AL, START_DISP_LO ; Display Start Low Register + MOV AH, CL ; Low 8 Bits of Start Addr + OUT DX, AX ; Set Display Addr Low + + MOV AL, START_DISP_HI ; Display Start High Register + MOV AH, CH ; High 8 Bits of Start Addr + OUT DX, AX ; Set Display Addr High + + ; Wait for a Vertical Retrace to smooth out things + + MOV DX, INPUT_1 ; Input Status #1 Register + +@DP_WAIT1: + IN AL, DX ; Get VGA status + AND AL, VERT_RETRACE ; Vertical Retrace Start? + JZ @DP_WAIT1 ; If Not, wait for it + + ; Now Set Display Starting Address + + +@SDP_Exit: + POP BP ; Restore Registers + RET 2 ; Exit and Clean up Stack + +SET_DISPLAY_PAGE ENDP + + +;================= +;GET_DISPLAY_PAGE% +;================= +; +; Returns the Video Page # currently displayed +; +; ENTRY: No Parameters are passed +; +; EXIT: AX = Current Video Page being displayed +; + + PUBLIC GET_DISPLAY_PAGE + +GET_DISPLAY_PAGE PROC FAR + + MOV AX, DISPLAY_PAGE ; Get Display Page # + RET ; Exit & Clean Up Stack + +GET_DISPLAY_PAGE ENDP + + +;======================================= +;SET_WINDOW (DisplayPage%, Xpos%, Ypos%) +;======================================= +; +; Since a Logical Screen can be larger than the Physical +; Screen, Scrolling is possible. This routine sets the +; Upper Left Corner of the Screen to the specified Pixel. +; Also Sets the Display page to simplify combined page +; flipping and scrolling. When called this routine +; syncronizes the display to the vertical blank. +; +; ENTRY: DisplayPage = Display Page to show on the screen +; Xpos = # of pixels to shift screen right +; Ypos = # of lines to shift screen down +; +; EXIT: No meaningful values returned +; + +SW_STACK STRUC + DW ? ; BP + DD ? ; Caller + SW_Ypos DW ? ; Y pos of UL Screen Corner + SW_Xpos DW ? ; X pos of UL Screen Corner + SW_Page DW ? ; (new) Display Page +SW_STACK ENDS + + PUBLIC SET_WINDOW + +SET_WINDOW PROC FAR + + PUSH BP ; Preserve Registers + MOV BP, SP ; Set up Stack Frame + + ; Check if our Scroll Offsets are Valid + + MOV BX, [BP].SW_Page ; Get Desired Page # + CMP BX, LAST_PAGE ; Is Page # Valid? + JAE @SW_Exit ; IF Not, Do Nothing + + MOV AX, [BP].SW_Ypos ; Get Desired Y Offset + CMP AX, MAX_YOFFSET ; Is it Within Limits? + JA @SW_Exit ; if not, exit + + MOV CX, [BP].SW_Xpos ; Get Desired X Offset + CMP CX, MAX_XOFFSET ; Is it Within Limits? + JA @SW_Exit ; if not, exit + + ; Compute proper Display start address to use + + MUL SCREEN_WIDTH ; AX = YOffset * Line Width + SHR CX, 2 ; CX / 4 = Bytes into Line + ADD AX, CX ; AX = Offset of Upper Left Pixel + + MOV CURRENT_MOFFSET, AX ; Save Offset Info + + MOV DISPLAY_PAGE, BX ; Set Current Page # + SHL BX, 1 ; Scale Page # to Word + ADD AX, PAGE_ADDR[BX] ; Get offset in VGA to Page + MOV BX, AX ; BX = Desired Display Start + + MOV DX, INPUT_1 ; Input Status #1 Register + + ; Wait if we are currently in a Vertical Retrace + +@SW_WAIT0: + IN AL, DX ; Get VGA status + AND AL, VERT_RETRACE ; In Display mode yet? + JNZ @SW_WAIT0 ; If Not, wait for it + + ; Set the Start Display Address to the new window + + MOV DX, CRTC_Index ; We Change the VGA Sequencer + MOV AL, START_DISP_LO ; Display Start Low Register + MOV AH, BL ; Low 8 Bits of Start Addr + OUT DX, AX ; Set Display Addr Low + + MOV AL, START_DISP_HI ; Display Start High Register + MOV AH, BH ; High 8 Bits of Start Addr + OUT DX, AX ; Set Display Addr High + + ; Wait for a Vertical Retrace to smooth out things + + MOV DX, INPUT_1 ; Input Status #1 Register + +@SW_WAIT1: + IN AL, DX ; Get VGA status + AND AL, VERT_RETRACE ; Vertical Retrace Start? + JZ @SW_WAIT1 ; If Not, wait for it + + ; Now Set the Horizontal Pixel Pan values + + OUT_8 ATTRIB_Ctrl, PIXEL_PAN_REG ; Select Pixel Pan Register + + MOV AX, [BP].SW_Xpos ; Get Desired X Offset + AND AL, 03 ; Get # of Pixels to Pan (0-3) + SHL AL, 1 ; Shift for 256 Color Mode + OUT DX, AL ; Fine tune the display! + +@SW_Exit: + POP BP ; Restore Saved Registers + RET 6 ; Exit and Clean up Stack + +SET_WINDOW ENDP + + +;============= +;GET_X_OFFSET% +;============= +; +; Returns the X coordinate of the Pixel currently display +; in the upper left corner of the display +; +; ENTRY: No Parameters are passed +; +; EXIT: AX = Current Horizontal Scroll Offset +; + + PUBLIC GET_X_OFFSET + +GET_X_OFFSET PROC FAR + + MOV AX, CURRENT_XOFFSET ; Get current horz offset + RET ; Exit & Clean Up Stack + +GET_X_OFFSET ENDP + + +;============= +;GET_Y_OFFSET% +;============= +; +; Returns the Y coordinate of the Pixel currently display +; in the upper left corner of the display +; +; ENTRY: No Parameters are passed +; +; EXIT: AX = Current Vertical Scroll Offset +; + + PUBLIC GET_Y_OFFSET + +GET_Y_OFFSET PROC FAR + + MOV AX, CURRENT_YOFFSET ; Get current vertical offset + RET ; Exit & Clean Up Stack + +GET_Y_OFFSET ENDP + + +;============ +;SYNC_DISPLAY +;============ +; +; Pauses the computer until the next Vertical Retrace starts +; +; ENTRY: No Parameters are passed +; +; EXIT: No meaningful values returned +; + + PUBLIC SYNC_DISPLAY + +SYNC_DISPLAY PROC FAR + + MOV DX, INPUT_1 ; Input Status #1 Register + + ; Wait for any current retrace to end + +@SD_WAIT0: + IN AL, DX ; Get VGA status + AND AL, VERT_RETRACE ; In Display mode yet? + JNZ @SD_WAIT0 ; If Not, wait for it + + ; Wait for the start of the next vertical retrace + +@SD_WAIT1: + IN AL, DX ; Get VGA status + AND AL, VERT_RETRACE ; Vertical Retrace Start? + JZ @SD_WAIT1 ; If Not, wait for it + + RET ; Exit & Clean Up Stack + +SYNC_DISPLAY ENDP + + + ; ===== TEXT DISPLAY ROUTINES ===== + +;================================================== +;GPRINTC (CharNum%, Xpos%, Ypos%, ColorF%, ColorB%) +;================================================== +; +; Draws an ASCII Text Character using the currently selected +; 8x8 font on the active display page. It would be a simple +; exercise to make this routine process variable height fonts. +; +; ENTRY: CharNum = ASCII character # to draw +; Xpos = X position to draw Character at +; Ypos = Y position of to draw Character at +; ColorF = Color to draw text character in +; ColorB = Color to set background to +; +; EXIT: No meaningful values returned +; + +GPC_STACK STRUC + GPC_Width DW ? ; Screen Width-1 + GPC_Lines DB ?,? ; Scan lines to Decode + GPC_T_SETS DW ? ; Saved Charset Segment + GPC_T_SETO DW ? ; Saved Charset Offset + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + GPC_ColorB DB ?,? ; Background Color + GPC_ColorF DB ?,? ; Text Color + GPC_Ypos DW ? ; Y Position to Print at + GPC_Xpos DW ? ; X position to Print at + GPC_Char DB ?,? ; Character to Print +GPC_STACK ENDS + + PUBLIC GPRINTC + +GPRINTC PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + SUB SP, 8 ; Allocate WorkSpace on Stack + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + MOV AX, SCREEN_WIDTH ; Get Logical Line Width + MOV BX, AX ; BX = Screen Width + DEC BX ; = Screen Width-1 + MOV [BP].GPC_Width, BX ; Save for later use + + MUL [BP].GPC_Ypos ; Start of Line = Ypos * Width + ADD DI, AX ; DI -> Start of Line Ypos + + MOV AX, [BP].GPC_Xpos ; Get Xpos of Character + MOV CX, AX ; Save Copy of Xpos + SHR AX, 2 ; Bytes into Line = Xpos/4 + ADD DI, AX ; DI -> (Xpos, Ypos) + + ;Get Source ADDR of Character Bit Map & Save + + MOV AL, [BP].GPC_Char ; Get Character # + TEST AL, 080h ; Is Hi Bit Set? + JZ @GPC_LowChar ; Nope, use low char set ptr + + AND AL, 07Fh ; Mask Out Hi Bit + MOV BX, CHARSET_HI ; BX = Char Set Ptr:Offset + MOV DX, CHARSET_HI+2 ; DX = Char Set Ptr:Segment + JMP s @GPC_Set_Char ; Go Setup Character Ptr + +@GPC_LowChar: + + MOV BX, CHARSET_LOW ; BX = Char Set Ptr:Offset + MOV DX, CHARSET_LOW+2 ; DX = Char Set Ptr:Segment + +@GPC_Set_Char: + MOV [BP].GPC_T_SETS, DX ; Save Segment on Stack + + MOV AH, 0 ; Valid #'s are 0..127 + SHL AX, 3 ; * 8 Bytes Per Bitmap + ADD BX, AX ; BX = Offset of Selected char + MOV [BP].GPC_T_SETO, BX ; Save Offset on Stack + + AND CX, PLANE_BITS ; Get Plane # + MOV CH, ALL_PLANES ; Get Initial Plane mask + SHL CH, CL ; And shift into position + AND CH, ALL_PLANES ; And mask to lower nibble + + MOV AL, 04 ; 4-Plane # = # of initial + SUB AL, CL ; shifts to align bit mask + MOV CL, AL ; Shift Count for SHL + + ;Get segment of character map + + OUT_8 SC_Index, MAP_MASK ; Setup Plane selections + INC DX ; DX -> SC_Data + + MOV AL, 08 ; 8 Lines to Process + MOV [BP].GPC_Lines, AL ; Save on Stack + + MOV DS, [BP].GPC_T_SETS ; Point to character set + +@GPC_DECODE_CHAR_BYTE: + + MOV SI, [BP].GPC_T_SETO ; Get DS:SI = String + + MOV BH, [SI] ; Get Bit Map + INC SI ; Point to Next Line + MOV [BP].GPC_T_SETO, SI ; And save new Pointer... + + CLR AX ; Clear AX + + CLR BL ; Clear BL + ROL BX, CL ; BL holds left edge bits + MOV SI, BX ; Use as Table Index + AND SI, CHAR_BITS ; Get Low Bits + MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + JZ @GPC_NO_LEFT1BITS ; Skip if No Pixels to set + + MOV AH, [BP].GPC_ColorF ; Get Foreground Color + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@GPC_NO_LEFT1BITS: + XOR AL, CH ; Invert mask for Background + JZ @GPC_NO_LEFT0BITS ; Hey, no need for this + + MOV AH, [BP].GPC_ColorB ; Get background Color + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + + ;Now Do Middle/Last Band + +@GPC_NO_LEFT0BITS: + INC DI ; Point to next Byte + ROL BX, 4 ; Shift 4 bits + + MOV SI, BX ; Make Lookup Pointer + AND SI, CHAR_BITS ; Get Low Bits + MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + JZ @GPC_NO_MIDDLE1BITS ; Skip if no pixels to set + + MOV AH, [BP].GPC_ColorF ; Get Foreground Color + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@GPC_NO_MIDDLE1BITS: + XOR AL, ALL_PLANES ; Invert mask for Background + JZ @GPC_NO_MIDDLE0BITS ; Hey, no need for this + + MOV AH, [BP].GPC_ColorB ; Get background Color + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@GPC_NO_MIDDLE0BITS: + XOR CH, ALL_PLANES ; Invert Clip Mask + CMP CL, 4 ; Aligned by 4? + JZ @GPC_NEXT_LINE ; If so, Exit now.. + + INC DI ; Point to next Byte + ROL BX, 4 ; Shift 4 bits + + MOV SI, BX ; Make Lookup Pointer + AND SI, CHAR_BITS ; Get Low Bits + MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + JZ @GPC_NO_RIGHT1BITS ; Skip if No Pixels to set + + MOV AH, [BP].GPC_ColorF ; Get Foreground Color + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@GPC_NO_RIGHT1BITS: + + XOR AL, CH ; Invert mask for Background + JZ @GPC_NO_RIGHT0BITS ; Hey, no need for this + + MOV AH, [BP].GPC_ColorB ; Get background Color + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@GPC_NO_RIGHT0BITS: + DEC DI ; Adjust for Next Line Advance + +@GPC_NEXT_LINE: + ADD DI, [BP].GPC_Width ; Point to Next Line + XOR CH, CHAR_BITS ; Flip the Clip mask back + + DEC [BP].GPC_Lines ; Count Down Lines + JZ @GPC_EXIT ; Ok... Done! + + JMP @GPC_DECODE_CHAR_BYTE ; Again! Hey! + +@GPC_EXIT: + ADD SP, 08 ; Deallocate stack workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 10 ; Exit and Clean up Stack + +GPRINTC ENDP + + +;========================================== +;TGPRINTC (CharNum%, Xpos%, Ypos%, ColorF%) +;========================================== +; +; Transparently draws an ASCII Text Character using the +; currently selected 8x8 font on the active display page. +; +; ENTRY: CharNum = ASCII character # to draw +; Xpos = X position to draw Character at +; Ypos = Y position of to draw Character at +; ColorF = Color to draw text character in +; +; EXIT: No meaningful values returned +; + +TGP_STACK STRUC + TGP_Width DW ? ; Screen Width-1 + TGP_Lines DB ?,? ; Scan lines to Decode + TGP_T_SETS DW ? ; Saved Charset Segment + TGP_T_SETO DW ? ; Saved Charset Offset + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + TGP_ColorF DB ?,? ; Text Color + TGP_Ypos DW ? ; Y Position to Print at + TGP_Xpos DW ? ; X position to Print at + TGP_Char DB ?,? ; Character to Print +TGP_STACK ENDS + + PUBLIC TGPRINTC + +TGPRINTC PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + SUB SP, 8 ; Allocate WorkSpace on Stack + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + MOV AX, SCREEN_WIDTH ; Get Logical Line Width + MOV BX, AX ; BX = Screen Width + DEC BX ; = Screen Width-1 + MOV [BP].TGP_Width, BX ; Save for later use + + MUL [BP].TGP_Ypos ; Start of Line = Ypos * Width + ADD DI, AX ; DI -> Start of Line Ypos + + MOV AX, [BP].TGP_Xpos ; Get Xpos of Character + MOV CX, AX ; Save Copy of Xpos + SHR AX, 2 ; Bytes into Line = Xpos/4 + ADD DI, AX ; DI -> (Xpos, Ypos) + + ;Get Source ADDR of Character Bit Map & Save + + MOV AL, [BP].TGP_Char ; Get Character # + TEST AL, 080h ; Is Hi Bit Set? + JZ @TGP_LowChar ; Nope, use low char set ptr + + AND AL, 07Fh ; Mask Out Hi Bit + MOV BX, CHARSET_HI ; BX = Char Set Ptr:Offset + MOV DX, CHARSET_HI+2 ; DX = Char Set Ptr:Segment + JMP s @TGP_Set_Char ; Go Setup Character Ptr + +@TGP_LowChar: + + MOV BX, CHARSET_LOW ; BX = Char Set Ptr:Offset + MOV DX, CHARSET_LOW+2 ; DX = Char Set Ptr:Segment + +@TGP_Set_Char: + MOV [BP].TGP_T_SETS, DX ; Save Segment on Stack + + MOV AH, 0 ; Valid #'s are 0..127 + SHL AX, 3 ; * 8 Bytes Per Bitmap + ADD BX, AX ; BX = Offset of Selected char + MOV [BP].TGP_T_SETO, BX ; Save Offset on Stack + + AND CX, PLANE_BITS ; Get Plane # + MOV CH, ALL_PLANES ; Get Initial Plane mask + SHL CH, CL ; And shift into position + AND CH, ALL_PLANES ; And mask to lower nibble + + MOV AL, 04 ; 4-Plane # = # of initial + SUB AL, CL ; shifts to align bit mask + MOV CL, AL ; Shift Count for SHL + + ;Get segment of character map + + OUT_8 SC_Index, MAP_MASK ; Setup Plane selections + INC DX ; DX -> SC_Data + + MOV AL, 08 ; 8 Lines to Process + MOV [BP].TGP_Lines, AL ; Save on Stack + + MOV DS, [BP].TGP_T_SETS ; Point to character set + +@TGP_DECODE_CHAR_BYTE: + + MOV SI, [BP].TGP_T_SETO ; Get DS:SI = String + + MOV BH, [SI] ; Get Bit Map + INC SI ; Point to Next Line + MOV [BP].TGP_T_SETO, SI ; And save new Pointer... + + MOV AH, [BP].TGP_ColorF ; Get Foreground Color + + CLR BL ; Clear BL + ROL BX, CL ; BL holds left edge bits + MOV SI, BX ; Use as Table Index + AND SI, CHAR_BITS ; Get Low Bits + MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + JZ @TGP_NO_LEFT1BITS ; Skip if No Pixels to set + + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + + ;Now Do Middle/Last Band + +@TGP_NO_LEFT1BITS: + + INC DI ; Point to next Byte + ROL BX, 4 ; Shift 4 bits + + MOV SI, BX ; Make Lookup Pointer + AND SI, CHAR_BITS ; Get Low Bits + MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + JZ @TGP_NO_MIDDLE1BITS ; Skip if no pixels to set + + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@TGP_NO_MIDDLE1BITS: + XOR CH, ALL_PLANES ; Invert Clip Mask + CMP CL, 4 ; Aligned by 4? + JZ @TGP_NEXT_LINE ; If so, Exit now.. + + INC DI ; Point to next Byte + ROL BX, 4 ; Shift 4 bits + + MOV SI, BX ; Make Lookup Pointer + AND SI, CHAR_BITS ; Get Low Bits + MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + JZ @TGP_NO_RIGHT1BITS ; Skip if No Pixels to set + + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@TGP_NO_RIGHT1BITS: + + DEC DI ; Adjust for Next Line Advance + +@TGP_NEXT_LINE: + ADD DI, [BP].TGP_Width ; Point to Next Line + XOR CH, CHAR_BITS ; Flip the Clip mask back + + DEC [BP].TGP_Lines ; Count Down Lines + JZ @TGP_EXIT ; Ok... Done! + + JMP @TGP_DECODE_CHAR_BYTE ; Again! Hey! + +@TGP_EXIT: + ADD SP, 08 ; Deallocate stack workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 8 ; Exit and Clean up Stack + +TGPRINTC ENDP + + +;=============================================================== +;PRINT_STR (SEG String, MaxLen%, Xpos%, Ypos%, ColorF%, ColorB%) +;=============================================================== +; +; Routine to quickly Print a null terminated ASCII string on the +; active display page up to a maximum length. +; +; ENTRY: String = Far Pointer to ASCII string to print +; MaxLen = # of characters to print if no null found +; Xpos = X position to draw Text at +; Ypos = Y position of to draw Text at +; ColorF = Color to draw text in +; ColorB = Color to set background to +; +; EXIT: No meaningful values returned +; + +PS_STACK STRUC + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + PS_ColorB DW ? ; Background Color + PS_ColorF DW ? ; Text Color + PS_Ypos DW ? ; Y Position to Print at + PS_Xpos DW ? ; X position to Print at + PS_Len DW ? ; Maximum Length of string to print + PS_Text DW ?,? ; Far Ptr to Text String +PS_STACK ENDS + + PUBLIC PRINT_STR + +PRINT_STR PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + MOV BP, SP ; Set up Stack Frame + +@PS_Print_It: + + MOV CX, [BP].PS_Len ; Get Remaining text Length + JCXZ @PS_Exit ; Exit when out of text + + LES DI, d [BP].PS_Text ; ES:DI -> Current Char in Text + MOV AL, ES:[DI] ; AL = Text Character + AND AX, 00FFh ; Clear High Word + JZ @PS_Exit ; Exit if null character + + DEC [BP].PS_Len ; Remaining Text length-- + INC [BP].PS_Text ; Point to Next text char + + ; Set up Call to GPRINTC + + PUSH AX ; Set Character Parameter + MOV BX, [BP].PS_Xpos ; Get Xpos + PUSH BX ; Set Xpos Parameter + ADD BX, 8 ; Advance 1 Char to Right + MOV [BP].PS_Xpos, BX ; Save for next time through + + MOV BX, [BP].PS_Ypos ; Get Ypos + PUSH BX ; Set Ypos Parameter + + MOV BX, [BP].PS_ColorF ; Get Text Color + PUSH BX ; Set ColorF Parameter + + MOV BX, [BP].PS_ColorB ; Get Background Color + PUSH BX ; Set ColorB Parameter + + CALL f GPRINTC ; Print Character! + JMP s @PS_Print_It ; Process next character + +@PS_Exit: + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 14 ; Exit and Clean up Stack + +PRINT_STR ENDP + + +;================================================================ +;TPRINT_STR (SEG String, MaxLen%, Xpos%, Ypos%, ColorF%, ColorB%) +;================================================================ +; +; Routine to quickly transparently Print a null terminated ASCII +; string on the active display page up to a maximum length. +; +; ENTRY: String = Far Pointer to ASCII string to print +; MaxLen = # of characters to print if no null found +; Xpos = X position to draw Text at +; Ypos = Y position of to draw Text at +; ColorF = Color to draw text in +; +; EXIT: No meaningful values returned +; + +TPS_STACK STRUC + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + TPS_ColorF DW ? ; Text Color + TPS_Ypos DW ? ; Y Position to Print at + TPS_Xpos DW ? ; X position to Print at + TPS_Len DW ? ; Maximum Length of string to print + TPS_Text DW ?,? ; Far Ptr to Text String +TPS_STACK ENDS + + PUBLIC TPRINT_STR + +TPRINT_STR PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + MOV BP, SP ; Set up Stack Frame + +@TPS_Print_It: + + MOV CX, [BP].TPS_Len ; Get Remaining text Length + JCXZ @TPS_Exit ; Exit when out of text + + LES DI, d [BP].TPS_Text ; ES:DI -> Current Char in Text + MOV AL, ES:[DI] ; AL = Text Character + AND AX, 00FFh ; Clear High Word + JZ @TPS_Exit ; Exit if null character + + DEC [BP].TPS_Len ; Remaining Text length-- + INC [BP].TPS_Text ; Point to Next text char + + ; Set up Call to TGPRINTC + + PUSH AX ; Set Character Parameter + MOV BX, [BP].TPS_Xpos ; Get Xpos + PUSH BX ; Set Xpos Parameter + ADD BX, 8 ; Advance 1 Char to Right + MOV [BP].TPS_Xpos, BX ; Save for next time through + + MOV BX, [BP].TPS_Ypos ; Get Ypos + PUSH BX ; Set Ypos Parameter + + MOV BX, [BP].TPS_ColorF ; Get Text Color + PUSH BX ; Set ColorF Parameter + + CALL f TGPRINTC ; Print Character! + JMP s @TPS_Print_It ; Process next character + +@TPS_Exit: + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 12 ; Exit and Clean up Stack + +TPRINT_STR ENDP + + +;=========================================== +;SET_DISPLAY_FONT(SEG FontData, FontNumber%) +;=========================================== +; +; Allows the user to specify their own font data for +; wither the lower or upper 128 characters. +; +; ENTRY: FontData = Far Pointer to Font Bitmaps +; FontNumber = Which half of set this is +; = 0, Lower 128 characters +; = 1, Upper 128 characters +; +; EXIT: No meaningful values returned +; + +SDF_STACK STRUC + DW ? ; BP + DD ? ; Caller + SDF_Which DW ? ; Hi Table/Low Table Flag + SDF_Font DD ? ; Far Ptr to Font Table +SDF_STACK ENDS + + PUBLIC SET_DISPLAY_FONT + +SET_DISPLAY_FONT PROC FAR + + PUSH BP ; Preserve Registers + MOV BP, SP ; Set up Stack Frame + + LES DI, [BP].SDF_Font ; Get Far Ptr to Font + + MOV SI, o CHARSET_LOW ; Assume Lower 128 chars + TEST [BP].SDF_Which, 1 ; Font #1 selected? + JZ @SDF_Set_Font ; If not, skip ahead + + MOV SI, o CHARSET_HI ; Ah, really it's 128-255 + +@SDF_Set_Font: + MOV [SI], DI ; Set Font Pointer Offset + MOV [SI+2], ES ; Set Font Pointer Segment + + POP BP ; Restore Registers + RET 6 ; We are Done.. Outa here + +SET_DISPLAY_FONT ENDP + + + ; ===== BITMAP (SPRITE) DISPLAY ROUTINES ===== + +;====================================================== +;DRAW_BITMAP (SEG Image, Xpos%, Ypos%, Width%, Height%) +;====================================================== +; +; Draws a variable sized Graphics Bitmap such as a +; picture or an Icon on the current Display Page in +; Mode X. The Bitmap is stored in a linear byte array +; corresponding to (0,0) (1,0), (2,0) .. (Width, Height) +; This is the same linear manner as mode 13h graphics. +; +; ENTRY: Image = Far Pointer to Bitmap Data +; Xpos = X position to Place Upper Left pixel at +; Ypos = Y position to Place Upper Left pixel at +; Width = Width of the Bitmap in Pixels +; Height = Height of the Bitmap in Pixels +; +; EXIT: No meaningful values returned +; + +DB_STACK STRUC + DB_LineO DW ? ; Offset to Next Line + DB_PixCount DW ? ; (Minimum) # of Pixels/Line + DB_Start DW ? ; Addr of Upper Left Pixel + DB_PixSkew DW ? ; # of bytes to Adjust EOL + DB_SkewFlag DW ? ; Extra Pix on Plane Flag + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + DB_Height DW ? ; Height of Bitmap in Pixels + DB_Width DW ? ; Width of Bitmap in Pixels + DB_Ypos DW ? ; Y position to Draw Bitmap at + DB_Xpos DW ? ; X position to Draw Bitmap at + DB_Image DD ? ; Far Pointer to Graphics Bitmap +DB_STACK ENDS + + PUBLIC DRAW_BITMAP + +DRAW_BITMAP PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + SUB SP, 10 ; Allocate workspace + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + CLD ; Direction Flag = Forward + + MOV AX, [BP].DB_Ypos ; Get UL Corner Ypos + MUL SCREEN_WIDTH ; AX = Offset to Line Ypos + + MOV BX, [BP].DB_Xpos ; Get UL Corner Xpos + MOV CL, BL ; Save Plane # in CL + SHR BX, 2 ; Xpos/4 = Offset Into Line + + ADD DI, AX ; ES:DI -> Start of Line + ADD DI, BX ; ES:DI -> Upper Left Pixel + MOV [BP].DB_Start, DI ; Save Starting Addr + + ; Compute line to line offset + + MOV BX, [BP].DB_Width ; Get Width of Image + MOV DX, BX ; Save Copy in DX + SHR BX, 2 ; /4 = width in bands + MOV AX, SCREEN_WIDTH ; Get Screen Width + SUB AX, BX ; - (Bitmap Width/4) + + MOV [BP].DB_LineO, AX ; Save Line Width offset + MOV [BP].DB_PixCount, BX ; Minimum # pix to copy + + AND DX, PLANE_BITS ; Get "partial band" size (0-3) + MOV [BP].DB_PixSkew, DX ; Also End of Line Skew + MOV [BP].DB_SkewFlag, DX ; Save as Flag/Count + + AND CX, PLANE_BITS ; CL = Starting Plane # + MOV AX, MAP_MASK_PLANE2 ; Plane Mask & Plane Select + SHL AH, CL ; Select correct Plane + OUT_16 SC_Index, AX ; Select Plane... + MOV BH, AH ; BH = Saved Plane Mask + MOV BL, 4 ; BL = Planes to Copy + +@DB_COPY_PLANE: + + LDS SI, [BP].DB_Image ; DS:SI-> Source Image + MOV DX, [BP].DB_Height ; # of Lines to Copy + MOV DI, [BP].DB_Start ; ES:DI-> Dest pos + +@DB_COPY_LINE: + MOV CX, [BP].DB_PixCount ; Min # to copy + + TEST CL, 0FCh ; 16+PixWide? + JZ @DB_COPY_REMAINDER ; Nope... + + ; Pixel Copy loop has been unrolled to x4 + +@DB_COPY_LOOP: + MOVSB ; Copy Bitmap Pixel + ADD SI, 3 ; Skip to Next Byte in same plane + MOVSB ; Copy Bitmap Pixel + ADD SI, 3 ; Skip to Next Byte in same plane + MOVSB ; Copy Bitmap Pixel + ADD SI, 3 ; Skip to Next Byte in same plane + MOVSB ; Copy Bitmap Pixel + ADD SI, 3 ; Skip to Next Byte in same plane + + SUB CL, 4 ; Pixels to Copy=-4 + TEST CL, 0FCh ; 4+ Pixels Left? + JNZ @DB_COPY_LOOP ; if so, do another block + +@DB_COPY_REMAINDER: + JCXZ @DB_NEXT_LINE ; Any Pixels left on line + +@DB_COPY2: + MOVSB ; Copy Bitmap Pixel + ADD SI,3 ; Skip to Next Byte in same plane + LOOPx CX, @DB_COPY2 ; Pixels to Copy--, Loop until done + +@DB_NEXT_LINE: + + ; any Partial Pixels? (some planes only) + + OR CX, [BP].DB_SkewFlag ; Get Skew Count + JZ @DB_NEXT2 ; if no partial pixels + + MOVSB ; Copy Bitmap Pixel + DEC DI ; Back up to align + DEC SI ; Back up to align + +@DB_NEXT2: + ADD SI, [BP].DB_PixSkew ; Adjust Skew + ADD DI, [BP].DB_LineO ; Set to Next Display Line + LOOPx DX, @DB_COPY_LINE ; Lines to Copy--, Loop if more + + ; Copy Next Plane.... + + DEC BL ; Planes to Go-- + JZ @DB_Exit ; Hey! We are done + + ROL BH, 1 ; Next Plane in line... + OUT_8 SC_Data, BH ; Select Plane + + CMP AL, 12h ; Carry Set if AL=11h + ADC [BP].DB_Start, 0 ; Screen Addr =+Carry + INC w [BP].DB_Image ; Start @ Next Byte + + SUB [BP].DB_SkewFlag, 1 ; Reduce Planes to Skew + ADC [BP].DB_SkewFlag, 0 ; Back to 0 if it was -1 + + JMP s @DB_COPY_PLANE ; Go Copy the Next Plane + +@DB_Exit: + ADD SP, 10 ; Deallocate workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 12 ; Exit and Clean up Stack + +DRAW_BITMAP ENDP + + +;======================================================= +;TDRAW_BITMAP (SEG Image, Xpos%, Ypos%, Width%, Height%) +;======================================================= +; +; Transparently Draws a variable sized Graphics Bitmap +; such as a picture or an Icon on the current Display Page +; in Mode X. Pixels with a value of 0 are not drawn, +; leaving the previous "background" contents intact. +; +; The Bitmap format is the same as for the DRAW_BITMAP function. +; +; ENTRY: Image = Far Pointer to Bitmap Data +; Xpos = X position to Place Upper Left pixel at +; Ypos = Y position to Place Upper Left pixel at +; Width = Width of the Bitmap in Pixels +; Height = Height of the Bitmap in Pixels +; +; EXIT: No meaningful values returned +; + +TB_STACK STRUC + TB_LineO DW ? ; Offset to Next Line + TB_PixCount DW ? ; (Minimum) # of Pixels/Line + TB_Start DW ? ; Addr of Upper Left Pixel + TB_PixSkew DW ? ; # of bytes to Adjust EOL + TB_SkewFlag DW ? ; Extra Pix on Plane Flag + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + TB_Height DW ? ; Height of Bitmap in Pixels + TB_Width DW ? ; Width of Bitmap in Pixels + TB_Ypos DW ? ; Y position to Draw Bitmap at + TB_Xpos DW ? ; X position to Draw Bitmap at + TB_Image DD ? ; Far Pointer to Graphics Bitmap +TB_STACK ENDS + + PUBLIC TDRAW_BITMAP + +TDRAW_BITMAP PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + SUB SP, 10 ; Allocate workspace + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + CLD ; Direction Flag = Forward + + MOV AX, [BP].TB_Ypos ; Get UL Corner Ypos + MUL SCREEN_WIDTH ; AX = Offset to Line Ypos + + MOV BX, [BP].TB_Xpos ; Get UL Corner Xpos + MOV CL, BL ; Save Plane # in CL + SHR BX, 2 ; Xpos/4 = Offset Into Line + + ADD DI, AX ; ES:DI -> Start of Line + ADD DI, BX ; ES:DI -> Upper Left Pixel + MOV [BP].TB_Start, DI ; Save Starting Addr + + ; Compute line to line offset + + MOV BX, [BP].TB_Width ; Get Width of Image + MOV DX, BX ; Save Copy in DX + SHR BX, 2 ; /4 = width in bands + MOV AX, SCREEN_WIDTH ; Get Screen Width + SUB AX, BX ; - (Bitmap Width/4) + + MOV [BP].TB_LineO, AX ; Save Line Width offset + MOV [BP].TB_PixCount, BX ; Minimum # pix to copy + + AND DX, PLANE_BITS ; Get "partial band" size (0-3) + MOV [BP].TB_PixSkew, DX ; Also End of Line Skew + MOV [BP].TB_SkewFlag, DX ; Save as Flag/Count + + AND CX, PLANE_BITS ; CL = Starting Plane # + MOV AX, MAP_MASK_PLANE2 ; Plane Mask & Plane Select + SHL AH, CL ; Select correct Plane + OUT_16 SC_Index, AX ; Select Plane... + MOV BH, AH ; BH = Saved Plane Mask + MOV BL, 4 ; BL = Planes to Copy + +@TB_COPY_PLANE: + + LDS SI, [BP].TB_Image ; DS:SI-> Source Image + MOV DX, [BP].TB_Height ; # of Lines to Copy + MOV DI, [BP].TB_Start ; ES:DI-> Dest pos + + ; Here AH is set with the value to be considered + ; "Transparent". It can be changed! + + MOV AH, 0 ; Value to Detect 0 + +@TB_COPY_LINE: + MOV CX, [BP].TB_PixCount ; Min # to copy + + TEST CL, 0FCh ; 16+PixWide? + JZ @TB_COPY_REMAINDER ; Nope... + + ; Pixel Copy loop has been unrolled to x4 + +@TB_COPY_LOOP: + LODSB ; Get Pixel Value in AL + ADD SI, 3 ; Skip to Next Byte in same plane + CMP AL, AH ; It is "Transparent"? + JE @TB_SKIP_01 ; Skip ahead if so + MOV ES:[DI], AL ; Copy Pixel to VGA screen + +@TB_SKIP_01: + LODSB ; Get Pixel Value in AL + ADD SI, 3 ; Skip to Next Byte in same plane + CMP AL, AH ; It is "Transparent"? + JE @TB_SKIP_02 ; Skip ahead if so + MOV ES:[DI+1], AL ; Copy Pixel to VGA screen + +@TB_SKIP_02: + LODSB ; Get Pixel Value in AL + ADD SI, 3 ; Skip to Next Byte in same plane + CMP AL, AH ; It is "Transparent"? + JE @TB_SKIP_03 ; Skip ahead if so + MOV ES:[DI+2], AL ; Copy Pixel to VGA screen + +@TB_SKIP_03: + LODSB ; Get Pixel Value in AL + ADD SI, 3 ; Skip to Next Byte in same plane + CMP AL, AH ; It is "Transparent"? + JE @TB_SKIP_04 ; Skip ahead if so + MOV ES:[DI+3], AL ; Copy Pixel to VGA screen + +@TB_SKIP_04: + ADD DI, 4 ; Adjust Pixel Write Location + SUB CL, 4 ; Pixels to Copy=-4 + TEST CL, 0FCh ; 4+ Pixels Left? + JNZ @TB_COPY_LOOP ; if so, do another block + +@TB_COPY_REMAINDER: + JCXZ @TB_NEXT_LINE ; Any Pixels left on line + +@TB_COPY2: + LODSB ; Get Pixel Value in AL + ADD SI, 3 ; Skip to Next Byte in same plane + CMP AL, AH ; It is "Transparent"? + JE @TB_SKIP_05 ; Skip ahead if so + MOV ES:[DI], AL ; Copy Pixel to VGA screen + +@TB_SKIP_05: + INC DI ; Advance Dest Addr + LOOPx CX, @TB_COPY2 ; Pixels to Copy--, Loop until done + +@TB_NEXT_LINE: + + ; any Partial Pixels? (some planes only) + + OR CX, [BP].TB_SkewFlag ; Get Skew Count + JZ @TB_NEXT2 ; if no partial pixels + + LODSB ; Get Pixel Value in AL + DEC SI ; Backup to Align + CMP AL, AH ; It is "Transparent"? + JE @TB_NEXT2 ; Skip ahead if so + MOV ES:[DI], AL ; Copy Pixel to VGA screen + +@TB_NEXT2: + ADD SI, [BP].TB_PixSkew ; Adjust Skew + ADD DI, [BP].TB_LineO ; Set to Next Display Line + LOOPx DX, @TB_COPY_LINE ; Lines to Copy--, Loop if More + + ;Copy Next Plane.... + + DEC BL ; Planes to Go-- + JZ @TB_Exit ; Hey! We are done + + ROL BH, 1 ; Next Plane in line... + OUT_8 SC_Data, BH ; Select Plane + + CMP AL, 12h ; Carry Set if AL=11h + ADC [BP].TB_Start, 0 ; Screen Addr =+Carry + INC w [BP].TB_Image ; Start @ Next Byte + + SUB [BP].TB_SkewFlag, 1 ; Reduce Planes to Skew + ADC [BP].TB_SkewFlag, 0 ; Back to 0 if it was -1 + + JMP @TB_COPY_PLANE ; Go Copy the next Plane + +@TB_Exit: + ADD SP, 10 ; Deallocate workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 12 ; Exit and Clean up Stack + +TDRAW_BITMAP ENDP + + + ; ==== VIDEO MEMORY to VIDEO MEMORY COPY ROUTINES ===== + +;================================== +;COPY_PAGE (SourcePage%, DestPage%) +;================================== +; +; Duplicate on display page onto another +; +; ENTRY: SourcePage = Display Page # to Duplicate +; DestPage = Display Page # to hold copy +; +; EXIT: No meaningful values returned +; + +CP_STACK STRUC + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + CP_DestP DW ? ; Page to hold copied image + CP_SourceP DW ? ; Page to Make copy from +CP_STACK ENDS + + PUBLIC COPY_PAGE + +COPY_PAGE PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + MOV BP, SP ; Set up Stack Frame + CLD ; Block Xfer Forwards + + ; Make sure Page #'s are valid + + MOV AX, [BP].CP_SourceP ; Get Source Page # + CMP AX, LAST_PAGE ; is it > Max Page #? + JAE @CP_Exit ; if so, abort + + MOV BX, [BP].CP_DestP ; Get Destination Page # + CMP BX, LAST_PAGE ; is it > Max Page #? + JAE @CP_Exit ; if so, abort + + CMP AX, BX ; Pages #'s the same? + JE @CP_Exit ; if so, abort + + ; Setup DS:SI and ES:DI to Video Pages + + SHL BX, 1 ; Scale index to Word + MOV DI, PAGE_ADDR[BX] ; Offset to Dest Page + + MOV BX, AX ; Index to Source page + SHL BX, 1 ; Scale index to Word + MOV SI, PAGE_ADDR[BX] ; Offset to Source Page + + MOV CX, PAGE_SIZE ; Get size of Page + MOV AX, CURRENT_SEGMENT ; Get Video Mem Segment + MOV ES, AX ; ES:DI -> Dest Page + MOV DS, AX ; DS:SI -> Source Page + + ; Setup VGA registers for Mem to Mem copy + + OUT_16 GC_Index, LATCHES_ON ; Data from Latches = on + OUT_16 SC_Index, ALL_PLANES_ON ; Copy all Planes + + ; Note.. Do *NOT* use MOVSW or MOVSD - they will + ; Screw with the latches which are 8 bits x 4 + + REP MOVSB ; Copy entire Page! + + ; Reset VGA for normal memory access + + OUT_16 GC_Index, LATCHES_OFF ; Data from Latches = off + +@CP_Exit: + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 4 ; Exit and Clean up Stack + +COPY_PAGE ENDP + + +;========================================================================== +;COPY_BITMAP (SourcePage%, X1%, Y1%, X2%, Y2%, DestPage%, DestX1%, DestY1%) +;========================================================================== +; +; Copies a Bitmap Image from one Display Page to Another +; This Routine is Limited to copying Images with the same +; Plane Alignment. To Work: (X1 MOD 4) must = (DestX1 MOD 4) +; Copying an Image to the Same Page is supported, but results +; may be defined when the when the rectangular areas +; (X1, Y1) - (X2, Y2) and (DestX1, DestY1) - +; (DestX1+(X2-X1), DestY1+(Y2-Y1)) overlap... +; No Paramter checking to done to insure that +; X2 >= X1 and Y2 >= Y1. Be Careful... +; +; ENTRY: SourcePage = Display Page # with Source Image +; X1 = Upper Left Xpos of Source Image +; Y1 = Upper Left Ypos of Source Image +; X2 = Lower Right Xpos of Source Image +; Y2 = Lower Right Ypos of Source Image +; DestPage = Display Page # to copy Image to +; DestX1 = Xpos to Copy UL Corner of Image to +; DestY1 = Ypos to Copy UL Corner of Image to +; +; EXIT: AX = Success Flag: 0 = Failure / -1= Success +; + +CB_STACK STRUC + CB_Height DW ? ; Height of Image in Lines + CB_Width DW ? ; Width of Image in "bands" + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + CB_DestY1 DW ? ; Destination Ypos + CB_DestX1 DW ? ; Destination Xpos + CB_DestP DW ? ; Page to Copy Bitmap To + CB_Y2 DW ? ; LR Ypos of Image + CB_X2 DW ? ; LR Xpos of Image + CB_Y1 DW ? ; UL Ypos of Image + CB_X1 DW ? ; UL Xpos of Image + CB_SourceP DW ? ; Page containing Source Bitmap +CB_STACK ENDS + + PUBLIC COPY_BITMAP + +COPY_BITMAP PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + SUB SP, 4 ; Allocate WorkSpace on Stack + MOV BP, SP ; Set up Stack Frame + + ; Prep Registers (and keep jumps short!) + + MOV ES, CURRENT_SEGMENT ; ES -> VGA Ram + CLD ; Block Xfer Forwards + + ; Make sure Parameters are valid + + MOV BX, [BP].CB_SourceP ; Get Source Page # + CMP BX, LAST_PAGE ; is it > Max Page #? + JAE @CB_Abort ; if so, abort + + MOV CX, [BP].CB_DestP ; Get Destination Page # + CMP CX, LAST_PAGE ; is it > Max Page #? + JAE @CB_Abort ; if so, abort + + MOV AX, [BP].CB_X1 ; Get Source X1 + XOR AX, [BP].CB_DestX1 ; Compare Bits 0-1 + AND AX, PLANE_BITS ; Check Plane Bits + JNZ @CB_Abort ; They should cancel out + + ; Setup for Copy processing + + OUT_8 SC_INDEX, MAP_MASK ; Set up for Plane Select + OUT_16 GC_Index, LATCHES_ON ; Data from Latches = on + + ; Compute Info About Images, Setup ES:SI & ES:DI + + MOV AX, [BP].CB_Y2 ; Height of Bitmap in lines + SUB AX, [BP].CB_Y1 ; is Y2 - Y1 + 1 + INC AX ; (add 1 since were not 0 based) + MOV [BP].CB_Height, AX ; Save on Stack for later use + + MOV AX, [BP].CB_X2 ; Get # of "Bands" of 4 Pixels + MOV DX, [BP].CB_X1 ; the Bitmap Occupies as X2-X1 + SHR AX, 2 ; Get X2 Band (X2 / 4) + SHR DX, 2 ; Get X1 Band (X1 / 4) + SUB AX, DX ; AX = # of Bands - 1 + INC AX ; AX = # of Bands + MOV [BP].CB_Width, AX ; Save on Stack for later use + + SHL BX, 1 ; Scale Source Page to Word + MOV SI, PAGE_ADDR[BX] ; SI = Offset of Source Page + MOV AX, [BP].CB_Y1 ; Get Source Y1 Line + MUL SCREEN_WIDTH ; AX = Offset to Line Y1 + ADD SI, AX ; SI = Offset to Line Y1 + MOV AX, [BP].CB_X1 ; Get Source X1 + SHR AX, 2 ; X1 / 4 = Byte offset + ADD SI, AX ; SI = Byte Offset to (X1,Y1) + + MOV BX, CX ; Dest Page Index to BX + SHL BX, 1 ; Scale Source Page to Word + MOV DI, PAGE_ADDR[BX] ; DI = Offset of Dest Page + MOV AX, [BP].CB_DestY1 ; Get Dest Y1 Line + MUL SCREEN_WIDTH ; AX = Offset to Line Y1 + ADD DI, AX ; DI = Offset to Line Y1 + MOV AX, [BP].CB_DestX1 ; Get Dest X1 + SHR AX, 2 ; X1 / 4 = Byte offset + ADD DI, AX ; DI = Byte Offset to (D-X1,D-Y1) + + MOV CX, [BP].CB_Width ; CX = Width of Image (Bands) + DEC CX ; CX = 1? + JE @CB_Only_One_Band ; 0 Means Image Width of 1 Band + + MOV BX, [BP].CB_X1 ; Get Source X1 + AND BX, PLANE_BITS ; Aligned? (bits 0-1 = 00?) + JZ @CB_Check_Right ; if so, check right alignment + JNZ @CB_Left_Band ; not aligned? well.. + +@CB_Abort: + CLR AX ; Return False (Failure) + JMP @CB_Exit ; and Finish Up + + ; Copy when Left & Right Clip Masks overlap... + +@CB_Only_One_Band: + MOV BX, [BP].CB_X1 ; Get Left Clip Mask + AND BX, PLANE_BITS ; Mask out Row # + MOV AL, Left_Clip_Mask[BX] ; Get Left Edge Mask + MOV BX, [BP].CB_X2 ; Get Right Clip Mask + AND BX, PLANE_BITS ; Mask out Row # + AND AL, Right_Clip_Mask[BX] ; Get Right Edge Mask byte + + OUT_8 SC_Data, AL ; Clip For Left & Right Masks + + MOV CX, [BP].CB_Height ; CX = # of Lines to Copy + MOV DX, SCREEN_WIDTH ; DX = Width of Screen + CLR BX ; BX = Offset into Image + +@CB_One_Loop: + MOV AL, ES:[SI+BX] ; Load Latches + MOV ES:[DI+BX], AL ; Unload Latches + ADD BX, DX ; Advance Offset to Next Line + LOOPjz CX, @CB_One_Done ; Exit Loop if Finished + + MOV AL, ES:[SI+BX] ; Load Latches + MOV ES:[DI+BX], AL ; Unload Latches + ADD BX, DX ; Advance Offset to Next Line + LOOPx CX, @CB_One_Loop ; Loop until Finished + +@CB_One_Done: + JMP @CB_Finish ; Outa Here! + + ; Copy Left Edge of Bitmap + +@CB_Left_Band: + + OUT_8 SC_Data, Left_Clip_Mask[BX] ; Set Left Edge Plane Mask + + MOV CX, [BP].CB_Height ; CX = # of Lines to Copy + MOV DX, SCREEN_WIDTH ; DX = Width of Screen + CLR BX ; BX = Offset into Image + +@CB_Left_Loop: + MOV AL, ES:[SI+BX] ; Load Latches + MOV ES:[DI+BX], AL ; Unload Latches + ADD BX, DX ; Advance Offset to Next Line + LOOPjz CX, @CB_Left_Done ; Exit Loop if Finished + + MOV AL, ES:[SI+BX] ; Load Latches + MOV ES:[DI+BX], AL ; Unload Latches + ADD BX, DX ; Advance Offset to Next Line + LOOPx CX, @CB_Left_Loop ; Loop until Finished + +@CB_Left_Done: + INC DI ; Move Dest Over 1 band + INC SI ; Move Source Over 1 band + DEC [BP].CB_Width ; Band Width-- + + ; Determine if Right Edge of Bitmap needs special copy + +@CB_Check_Right: + MOV BX, [BP].CB_X2 ; Get Source X2 + AND BX, PLANE_BITS ; Aligned? (bits 0-1 = 11?) + CMP BL, 03h ; Plane = 3? + JE @CB_Copy_Middle ; Copy the Middle then! + + ; Copy Right Edge of Bitmap + +@CB_Right_Band: + + OUT_8 SC_Data, Right_Clip_Mask[BX] ; Set Right Edge Plane Mask + + DEC [BP].CB_Width ; Band Width-- + MOV CX, [BP].CB_Height ; CX = # of Lines to Copy + MOV DX, SCREEN_WIDTH ; DX = Width of Screen + MOV BX, [BP].CB_Width ; BX = Offset to Right Edge + +@CB_Right_Loop: + MOV AL, ES:[SI+BX] ; Load Latches + MOV ES:[DI+BX], AL ; Unload Latches + ADD BX, DX ; Advance Offset to Next Line + LOOPjz CX, @CB_Right_Done ; Exit Loop if Finished + + MOV AL, ES:[SI+BX] ; Load Latches + MOV ES:[DI+BX], AL ; Unload Latches + ADD BX, DX ; Advance Offset to Next Line + LOOPx CX, @CB_Right_Loop ; Loop until Finished + +@CB_Right_Done: + + ; Copy the Main Block of the Bitmap + +@CB_Copy_Middle: + + MOV CX, [BP].CB_Width ; Get Width Remaining + JCXZ @CB_Finish ; Exit if Done + + OUT_8 SC_Data, ALL_PLANES ; Copy all Planes + + MOV DX, SCREEN_WIDTH ; Get Width of Screen minus + SUB DX, CX ; Image width (for Adjustment) + MOV AX, [BP].CB_Height ; AX = # of Lines to Copy + MOV BX, CX ; BX = Quick REP reload count + MOV CX, ES ; Move VGA Segment + MOV DS, CX ; Into DS + + ; Actual Copy Loop. REP MOVSB does the work + +@CB_Middle_Copy: + MOV CX, BX ; Recharge Rep Count + REP MOVSB ; Move Bands + LOOPjz AX, @CB_Finish ; Exit Loop if Finished + + ADD SI, DX ; Adjust DS:SI to Next Line + ADD DI, DX ; Adjust ES:DI to Next Line + + MOV CX, BX ; Recharge Rep Count + REP MOVSB ; Move Bands + + ADD SI, DX ; Adjust DS:SI to Next Line + ADD DI, DX ; Adjust ES:DI to Next Line + LOOPx AX, @CB_Middle_Copy ; Copy Lines until Done + +@CB_Finish: + OUT_16 GC_Index, LATCHES_OFF ; Data from Latches = on + +@CB_Exit: + ADD SP, 04 ; Deallocate stack workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 16 ; Exit and Clean up Stack + +COPY_BITMAP ENDP + + END ; End of Code Segment diff --git a/modex105/DEMOS/C/MODEX.BI b/modex105/DEMOS/C/MODEX.BI new file mode 100755 index 00000000..6b1d7afe --- /dev/null +++ b/modex105/DEMOS/C/MODEX.BI @@ -0,0 +1,63 @@ + + ' ===== SCREEN RESOLUTIONS ===== + +CONST Mode320x200 = 0, Mode320x400 = 1 +CONST Mode360x200 = 2, Mode360x400 = 3 +CONST Mode320x240 = 4, Mode320x480 = 5 +CONST Mode360x240 = 6, Mode360x480 = 7 + + ' ===== MODE X SETUP ROUTINES ===== + +DECLARE FUNCTION SET.VGA.MODEX% ALIAS "SET_VGA_MODEX" (BYVAL ModeType%, BYVAL MaxXpos%, BYVAL MaxYpos%, BYVAL Pages%) +DECLARE FUNCTION SET.MODEX% ALIAS "SET_MODEX" (BYVAL Mode%) + + ' ===== BASIC GRAPHICS PRIMITIVES ===== + +DECLARE SUB CLEAR.VGA.SCREEN ALIAS "CLEAR_VGA_SCREEN" (BYVAL ColorNum%) +DECLARE SUB SET.POINT ALIAS "SET_POINT" (BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorNum%) +DECLARE FUNCTION READ.POINT% ALIAS "READ_POINT" (BYVAL Xpos%, BYVAL Ypos%) +DECLARE SUB FILL.BLOCK ALIAS "FILL_BLOCK" (BYVAL Xpos1%, BYVAL Ypos1%, BYVAL Xpos2%, BYVAL Ypos2%, BYVAL ColorNum%) +DECLARE SUB DRAW.LINE ALIAS "DRAW_LINE" (BYVAL Xpos1%, BYVAL Ypos1%, BYVAL Xpos2%, BYVAL Ypos2%, BYVAL ColorNum%) + + ' ===== DAC COLOR REGISTER ROUTINES ===== + +DECLARE SUB SET.DAC.REGISTER ALIAS "SET_DAC_REGISTER" (BYVAL RegNo%, BYVAL Red%, BYVAL Green%, BYVAL Blue%) +DECLARE SUB GET.DAC.REGISTER ALIAS "GET_DAC_REGISTER" (BYVAL RegNo%, Red%, Green%, Blue%) +DECLARE SUB LOAD.DAC.REGISTERS ALIAS "LOAD_DAC_REGISTERS" (SEG PalData AS ANY, BYVAL StartReg%, BYVAL EndReg%, BYVAL VSync%) +DECLARE SUB READ.DAC.REGISTERS ALIAS "READ_DAC_REGISTERS" (SEG PalData AS ANY, BYVAL StartReg%, BYVAL EndReg%) + + + ' ===== PAGE FLIPPING AND SCROLLING ROUTINES ===== + +DECLARE SUB SET.ACTIVE.PAGE ALIAS "SET_ACTIVE_PAGE" (BYVAL PageNo%) +DECLARE FUNCTION GET.ACTIVE.PAGE% ALIAS "GET_ACTIVE_PAGE" +DECLARE SUB SET.DISPLAY.PAGE ALIAS "SET_DISPLAY_PAGE" (BYVAL PageNo%) +DECLARE FUNCTION GET.DISPLAY.PAGE% ALIAS "GET_DISPLAY_PAGE" +DECLARE SUB SET.WINDOW ALIAS "SET_WINDOW" (BYVAL DisplayPage%, BYVAL XOffset%, BYVAL YOffset%) +DECLARE FUNCTION GET.X.OFFSET% ALIAS "GET_X_OFFSET" () +DECLARE FUNCTION GET.Y.OFFSET% ALIAS "GET_Y_OFFSET" () +DECLARE SUB SYNC.DISPLAY ALIAS "SYNC_DISPLAY" + + ' ===== TEXT DISPLAY ROUTINES ===== + +DECLARE SUB GPRINTC (BYVAL CharacterNum%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%, BYVAL ColorB%) +DECLARE SUB TGPRINTC (BYVAL CharacterNum%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%) +DECLARE SUB PRINT.STR ALIAS "PRINT_STR" (BYVAL StrSeg%, BYVAL StrOfs%, BYVAL MaxLen%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%, BYVAL ColorB%) +DECLARE SUB TPRINT.STR ALIAS "TPRINT_STR" (BYVAL StrSeg%, BYVAL StrOfs%, BYVAL MaxLen%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%) +DECLARE SUB SET.DISPLAY.FONT ALIAS "SET_DISPLAY_FONT" (SEG FontData AS ANY, BYVAL FontNumber%) + + ' ===== BITMAP (SPRITE) DISPLAY ROUTINES ===== + +DECLARE SUB DRAW.BITMAP ALIAS "DRAW_BITMAP" (SEG Image AS ANY, BYVAL Xpos%, BYVAL Ypos%, BYVAL xWidth%, BYVAL Height%) +DECLARE SUB TDRAW.BITMAP ALIAS "TDRAW_BITMAP" (SEG Image AS ANY, BYVAL Xpos%, BYVAL Ypos%, BYVAL xWidth%, BYVAL Height%) + + ' ==== VIDEO MEMORY to VIDEO MEMORY COPY ROUTINES ===== + +DECLARE SUB COPY.PAGE ALIAS "COPY_PAGE" (BYVAL SourcePage%, BYVAL DestPage%) +DECLARE SUB COPY.BITMAP ALIAS "COPY_BITMAP" (BYVAL SourcePage%, BYVAL X1%, BYVAL Y1%, BYVAL X2%, BYVAL Y2%, BYVAL DestPage%, BYVAL DestX1%, BYVAL DestY1%) + + + + + + diff --git a/modex105/DEMOS/C/MODEX.H b/modex105/DEMOS/C/MODEX.H new file mode 100755 index 00000000..7de25a63 --- /dev/null +++ b/modex105/DEMOS/C/MODEX.H @@ -0,0 +1,76 @@ + +#ifndef __MODEX_H +#define __MODEX_H + + /* ===== SCREEN RESOLUTIONS ===== */ + +#define Mode_320x200 0 +#define Mode_320x400 1 +#define Mode_360x200 2 +#define Mode_360x400 3 +#define Mode_320x240 4 +#define Mode_320x480 5 +#define Mode_360x240 6 +#define Mode_360x480 7 + + /* ===== MODE X SETUP ROUTINES ===== */ + +int far pascal set_vga_modex (int Mode, int MaxXpos, int MaxYpos, int Pages); +int far pascal set_modex (int Mode); + + /* ===== BASIC GRAPHICS PRIMITIVES ===== */ + +void far pascal clear_vga_screen (int Color); +void far pascal set_point (int Xpos, int Ypos, int Color); +int far pascal read_point (int Xpos, int Ypos); +void far pascal fill_block (int Xpos1, int Ypos1, int Xpos2, int Ypos2, + int Color); +void far pascal draw_line (int Xpos1, int Ypos1, int Xpos2, int Ypos2, + int Color); + + /* ===== DAC COLOR REGISTER ROUTINES ===== */ + +void far pascal set_dac_register (int RegNo, int Red, int Green, int Blue); +void far pascal get_dac_register (int RegNo, int* Red, int* Green, int* Blue); +void far pascal load_dac_registers (char far *PalData, int StartReg, + int EndReg, int VSync); +void far pascal readd_dac_registers (char far *PalData, int StartReg, + int EndReg); + + /* ===== PAGE FLIPPING AND SCROLLING ROUTINES ===== */ + +void far pascal set_active_page (int PageNo); +int far pascal get_active_page (void); +void far pascal set_display_page (int PageNo); +int far pascal get_display_page (void); +void far pascal set_window (int DisplayPage, int XOffset, int YOffset); +int far pascal get_x_offset (void); +int far pascal get_y_offset (void); +void far pascal sync_display (void); + + /* ===== TEXT DISPLAY ROUTINES ===== */ + +void far pascal gprintc (int CharNum, int Xpos, int Ypos, int ColorF, + int ColorB); +void far pascal tgprintc (int CharNum, int Xpos, int Ypos, int ColorF); +void far pascal print_str (char far *Text, int MaxLen, int Xpos, int Ypos, + int ColorF, int ColorB); +void far pascal tprint_str (char far *Text, int MaxLen, int Xpos, int Ypos, + int ColorF); +void far pascal set_display_font (char far *FontData, int FontNumber); + + /* ===== BITMAP (SPRITE) DISPLAY ROUTINES ===== */ + +void far pascal draw_bitmap (char far *Image, int Xpos, int Ypos, + int Width, int Height); +void far pascal tdraw_bitmap (char far *Image, int Xpos, int Ypos, + int Width, int Height); + + /* ==== VIDEO MEMORY to VIDEO MEMORY COPY ROUTINES ===== */ + +void far pascal copy_page (int SourcePage, int DestPage); +void far pascal copy_bitmap (int SourcePage, int X1, int Y1, int X2, int Y2, + int DestPage, int DestX1, int DestY1); + + +#endif diff --git a/modex105/DEMOS/C/UTLS-ASM.BAT b/modex105/DEMOS/C/UTLS-ASM.BAT new file mode 100755 index 00000000..d996978f --- /dev/null +++ b/modex105/DEMOS/C/UTLS-ASM.BAT @@ -0,0 +1 @@ +MASM c_utils, c_utils, c_utils, nul; \ No newline at end of file diff --git a/modex105/DEMOS/C/X-DEMO.C b/modex105/DEMOS/C/X-DEMO.C new file mode 100755 index 00000000..2d0621ee --- /dev/null +++ b/modex105/DEMOS/C/X-DEMO.C @@ -0,0 +1,780 @@ +/* X-DEMO.C - a Mode "X" Demo */ +/* By Matt Pritchard, 14 Apr, 1993 */ + +#include +#include + +#include "modex.h" +#include "c_utils.h" + +#define MAX_SHAPES 32 +#define MAX_SPRITES 64 + + /* routines in this file */ + +void demo_res (int, int, int); +int get_key (void); +void error_out (char*); +void load_shapes (void); +int int_sqrt (int, int); +void page_demo (void); + + /* Structures for Sprites */ + +struct Shape +{ + unsigned char Image[512]; + int X_Width; + int Y_Width; +} Img [MAX_SHAPES]; + +struct Sprite +{ + int X_pos; + int Y_pos; + int X_Dir; + int Y_Dir; + int Shape; + int Last_X [2]; + int Last_Y [2]; +} Obj [MAX_SPRITES]; + + + /* MAIN */ + + +int main(int argc, char *argv[]) +{ + + /* if (argc > 0) + { + while (argc > 0) + { + dos_print ("Unknown Argument: "); + dos_print (makefp argv[argc]); + argc--; + } + return (0); + + } + */ + + init_random (); + + load_shapes (); + + demo_res ( Mode_320x200, 320, 200 ); + demo_res ( Mode_320x400, 320, 400 ); + + demo_res ( Mode_360x200, 360, 200 ); + demo_res ( Mode_360x400, 360, 400 ); + + demo_res ( Mode_320x240, 320, 240 ); + demo_res ( Mode_320x480, 320, 480 ); + + demo_res ( Mode_360x240, 360, 240 ); + demo_res ( Mode_360x480, 360, 480 ); + + page_demo (); + + set_video_mode (3); + dos_print ("This Mode X Demo is Finished"); + return (0); + +} + + + /* Demonstrate a given resolution */ + + +void demo_res (int Screen_Mode, int X_max, int Y_max) +{ + +char *Error1 = "Failure while calling SET_MODEX"; +char *Error2 = "Failure during READ_PIXEL test"; + +char *Abort_Msg = "Demo aborted by User"; + +char *Demo_Msg = " This is a MODE X demo "; +char *Scrn_Msg = "Screen Resolution is by "; +char *Cont_Msg = "Press to Continue"; + +char *Line_Msg = "LINE TEST"; +char *Fill_Msg = "FILL TEST"; +char *Pixel_Msg = "PIXEL TEST"; + +char Text[10]; + +int x1, y1, x2, y2 = 0; +int x, y, z = 0; +int X_Center, gap = 0; + + + if (set_modex (Screen_Mode) == 0) + { + error_out (Error1); + } + + X_Center = X_max / 2; + + x1 = 10; + y1 = 10; + x2 = X_max - 1; + y2 = Y_max - 1; + + for (z = 0; z <= 3; z++) + { + y = 31 - z -z; + draw_line (x1+z, y1+z, x2-z, y1+z, y); + draw_line (x1+z, y1+z, x1+z, y2-z, y); + draw_line (x1+z, y2-z, x2-z, y2-z, y); + draw_line (x2-z, y1+z, x2-z, y2-z, y); + } + + for (x = 0; x < (X_max / 10); x++) + { + tgprintc (48 + ((x+1) % 10), x*10+1, 1, 9 + ((x/8) % 7) ); + draw_line (x*10+9, 0, x*10+9, 3, c_bWHITE); + } + + for (y = 0; y < (Y_max / 10); y++) + { + tgprintc (48 + ((y+1) % 10), 1, y*10+1, 9 + ((y/10) % 7) ); + draw_line (0, y*10+9, 3, y*10+9, c_bWHITE); + } + + for (x = 0; x <= 63; x++) + { + z = 15 + (x * 3 / 4); + set_dac_register (64+x, z, z, z); + set_dac_register (128+x, 0, z, z); + + draw_line (103-x, 60, 40+x, 123, 64+x); + draw_line (40, 60+x, 103, 123-x, 128+x); + + } + + tprint_str (Line_Msg, 9, 37, 130, c_BLUE); + + y = 60; + gap = 0; + for (x = 0; x <= 9; x++) + { + fill_block (120, y, 120+x, y+gap, 64+x); + fill_block (140 - (15-x), y, 150+x, y+gap, 230+x); + fill_block (170 - (15-x), y, 170, y+gap, 128+x); + y = y + gap + 2; + gap++; + } + + tprint_str (Fill_Msg, 9, 110, 46, c_GREEN); + + for (x = 190; x <= 250; x+=2) + { + for (y = 60; y <= 122; y+=2) + { + z = (x+x+y+y) & 0xff; + set_point (x, y, z); + } + } + + tprint_str (Pixel_Msg, 10, 182, 130, c_RED); + + for (x = 190; x <= 250; x+=2) + { + for (y = 60; y <= 122; y+=2) + { + z = (x+x+y+y) & 0xff; + if (read_point(x, y) != z) + { + error_out (Error2); + } + } + } + + print_str (Demo_Msg, 23, X_Center - 92, 20, c_bRED, c_BLUE); + + x = X_Center - 124; + print_str (Scrn_Msg, 28, x, 30, c_bGREEN, c_BLACK); + + sprintf (Text, "%3d", X_max); + print_str (Text, 3, x+168, 30, c_bPURPLE, c_BLACK); + + sprintf (Text, "%3d", Y_max); + print_str (Text, 3, x + 224, 30, c_bWHITE, c_BLACK); + + for (x = 0; x <= 15; x++) + { + set_dac_register (230+x, 63-x*4, 0, 15+x*3); + draw_line (30+x, Y_max-6-x, X_max-20-x, Y_max-6-x, 230+x); + } + + tprint_str (Cont_Msg, 27, X_Center - 103, Y_max-18, c_YELLOW); + + if (get_key () == Ky_ESC) + { + error_out (Abort_Msg); + } + + return ; + +} + + + /* Wait for a Keystroke */ + + +int get_key(void) +{ + +int c = 0; + + while (c == 0) + { + c = scan_keyboard (); + } + + return (c); + +} + + + /* Error Handling Routine */ + + +void error_out (char * text) +{ + + set_video_mode (3); + dos_print (text); + exit (EXIT_SUCCESS); + +} + + + /* Routine to generate random sprites */ + + +void load_shapes () +{ + +unsigned char Grid[33][33]; + +char *Error1 = "Bad Shape Selected Error"; + +int Shape; +int x, y, z; +int Style, Color; +int X_Width, Y_Width, Center, S_Width; +int Hollow_X, Hollow_Y; + + for (Shape = 0; Shape < MAX_SHAPES; Shape++) + { + for (y = 0; y <= 32; y++) + { + for (x = 0; x <= 32; x++) + { + Grid[x][y] = c_BLACK; + } + } + + Style = random_int (6); + Color = 1 + random_int (15); + + switch (Style) + + { + /* SOLID BOXES */ + + case 0: + + { + do + { + X_Width = 3 + random_int(30); + Y_Width = 3 + random_int(30); + + } while ( (X_Width * Y_Width) >= 512); + + for (x = 1; x <= X_Width; x++) + { + for (y = 1; y <= Y_Width; y++) + { + Grid[x][y] = Color; + } + } + + break; + + } + /* HOLLOW BOXES */ + + case 1: + + { + do { + X_Width = 6 + random_int(27); + Y_Width = 6 + random_int(27); + } while ( (X_Width * Y_Width) >= 512); + + for (y = 1; y <= Y_Width; y++) + { + for (x = 1; x <= X_Width; x++) + { + Grid[x][y] = Color; + } + } + + Hollow_X = 1 + random_int ((X_Width / 2) -1); + Hollow_Y = 1 + random_int ((Y_Width / 2) -1); + + for (y = Hollow_Y+1; y <= Y_Width-Hollow_Y; y++) + { + for (x = Hollow_X+1; x <= X_Width-Hollow_X; x++) + { + Grid[x][y] = c_BLACK; + } + } + + break; + + } + + /* SOLID DIAMOND */ + + case 2: + + { + + X_Width = 3 + 2 * random_int(10); + Y_Width = X_Width; + Center = X_Width / 2; + + for (y = 0; y <= Center; y++) + { + for (x = 0; x <= y; x++) + { + Grid [Center-x+1][y+1] = Color; + Grid [Center+x+1][y+1] = Color; + Grid [Center-x+1][Y_Width-y] = Color; + Grid [Center+x+1][Y_Width-y] = Color; + } + } + + break; + + } + + /* HOLLOW DIAMOND */ + + case 3: + + { + + X_Width = 3 + 2 * random_int(10); + Y_Width = X_Width; + Center = X_Width / 2; + S_Width = random_int (Center); + + for (y = 0; y <= Center; y++) + { + for (x = 0; x <= y; x++) + { + if ( x+(Center-y) >= S_Width ) + { + Grid [Center-x+1][y+1] = Color; + Grid [Center+x+1][y+1] = Color; + Grid [Center-x+1][Y_Width-y] = Color; + Grid [Center+x+1][Y_Width-y] = Color; + } + } + } + + break; + + } + + /* BALL */ + + case 4: + + { + + X_Width = 7 + 2 * random_int (8); + Y_Width = X_Width; + Center = 1 + X_Width / 2; + + for (y = 1; y <= Y_Width; y++) + { + for (x = 1; x <= X_Width; x++) + { + z = int_sqrt(Center-x, Center-y); + if (z < Center) + { + Grid[x][y] = 150 + Color * 2 + z * 3; + } + } + } + + break; + } + + /* HOLLOW BALLS */ + + case 5: + + { + X_Width = 7 + 2 * random_int (8); + Y_Width = X_Width; + Center = 1 + X_Width / 2; + S_Width = random_int (X_Width); + + for (y = 1; y <= Y_Width; y++) + { + for (x = 1; x <= X_Width; x++) + { + z = int_sqrt(Center-x, Center-y); + if ( (z < Center) && (z >= S_Width) ) + { + Grid[x][y] = 150 + Color * 2 + z * 3; + } + } + } + + + break; + } + + default: + + { + error_out (Error1); + break; + + } + + } + + z = 0; + for (y = 1; y <= Y_Width; y++) + { + for (x = 1; x <= X_Width; x++) + { + Img[Shape].Image[z] = Grid[x][y]; + z++; + } + } + + Img[Shape].X_Width = X_Width; + Img[Shape].Y_Width = Y_Width; + + } + + return; +} + + + /* Quickie Psuedo Integer Square Root Routine */ + + +int int_sqrt ( int x, int y ) +{ + +int Sqr_Table[12] = {1, 4, 9, 6, 25, 36, 49, 64, 81, 100, 121, 144}; + +int r, d; + + d = (x * x) + (y * y); + r = 0; + + while ( d >= Sqr_Table[r] ) + { + r++; + } + + return (r); + +} + + + /* The Bit Sprite Demo */ + + +void page_demo () +{ + +char *Error1 = "Failure during SET_VGA_MODEX (0, 360, 240, 3) call"; + +int Last_Objects[2], Visible_Objects; + +int Screen_X = 384; +int Screen_Y = 672; + +int x, y, z; +int c, dc; +int x1, y1, x2, y2; + +int Sprite_X, Sprite_Y; +int Current_Page; +int New_X, New_Y; + +int View_X, View_Y, View_Max, View_Cnt, View_XD, View_YD; +int Set_Color, Prev_Color, S_Dir, P_Dir; + +int Demo_Running = True; +int redo, code; + + if (set_vga_modex(Mode_320x200, Screen_X, Screen_Y, 3) == 0) + { + error_out (Error1); + } + + set_active_page (0); + clear_vga_screen (c_BLACK); + + print_str ("This is a Test of the Following Functions:", 99, 10, 9, c_bWHITE, c_BLACK); + + draw_line (10, 18, 350, 18, c_YELLOW); + print_str ("SET_ACTIVE_PAGE", 99, 10, 20, c_bBLUE, c_BLACK); + print_str ("SET_DISPLAY_PAGE", 99, 10, 30, c_GREEN, c_BLACK); + print_str ("SET_DAC_REGISTER", 99, 10, 40, c_RED, c_BLACK); + print_str ("CLEAR_VGA_SCREEN", 99, 10, 50, c_CYAN, c_BLACK); + + print_str ("TDRAW_BITMAP", 99, 10, 60, c_PURPLE, c_BLACK); + print_str ("COPY_PAGE", 99, 10, 70, c_GREEN, c_BLACK); + print_str ("COPY_BITMAP", 99, 10, 80, c_CYAN, c_BLACK); + + print_str ("GPRINTC", 99, 10, 90, c_BLUE, c_BLACK); + print_str ("TGPRINTC", 99, 10, 100, c_GREEN, c_BLACK); + print_str ("SET_WINDOW", 99, 10, 110, c_RED, c_BLACK); + + print_str ("VIRTUAL SCREEN SIZES", 20, 190, 20, c_bBLUE, c_BLACK); + print_str (" SMOOTH SCROLLING", 20, 190, 30, c_GREEN, c_BLACK); + print_str (" SPRITE ANIMATION", 20, 190, 40, c_CYAN, c_BLACK); + print_str (" PAGE FLIPPING", 20, 190, 50, c_RED, c_BLACK); + print_str (" COLOR CYCLING", 20, 190, 60, c_PURPLE, c_BLACK); + + for (x = 0; x <=60; x++) + { + set_dac_register (50 + x, 3 + x, 0, 60 - x); + set_dac_register (150 + x, 3 + x, 0, 60 - x); + } + + c = 0; + dc = 1; + for (x = 0; x <= (Screen_X / 2); x++) + { + draw_line (Screen_X / 2 - 1, Screen_Y / 4, x, Screen_Y - 1, c + 50); + draw_line (Screen_X / 2, Screen_Y / 4, Screen_X - x - 1, Screen_Y - 1, c + 50); + c+= dc; + if ((c == 0) || (c == 60) ) { dc = -dc;} + } + + tprint_str ("Press to Continue", 99, 72, 190, c_bWHITE); + tprint_str ("< > = Faster < > = Slower", 99, 72, 204, c_bGREEN); + tprint_str ("< > = Fewer Shapes < > = More Shapes", 99, 32, 218, c_bCYAN); + + tgprintc (43, 80, 204, c_YELLOW); + tgprintc (45, 200, 204, c_YELLOW); + + tgprintc (25, 40, 218, c_YELLOW); + tgprintc (24, 200, 218, c_YELLOW); + + copy_page (0, 1); + copy_page (0, 2); + + for (x = 0; x < MAX_SPRITES; x++) + { + do { + Obj[x].X_Dir = random_int(7) - 3; + Obj[x].Y_Dir = random_int(7) - 3; + } while ( (Obj[x].X_Dir == 0) && (Obj[x].Y_Dir == 0) ); + + Obj[x].Shape = x % MAX_SHAPES; + + Sprite_X = Img[Obj[x].Shape].X_Width; + Sprite_Y = Img[Obj[x].Shape].Y_Width; + + Obj[x].X_pos = 1 + random_int(Screen_X - Sprite_X - 2); + Obj[x].Y_pos = 1 + random_int(Screen_Y - Sprite_Y - 2); + + Obj[x].Last_X[0] = Obj[x].X_pos; + Obj[x].Last_X[1] = Obj[x].X_pos; + Obj[x].Last_Y[0] = Obj[x].Y_pos; + Obj[x].Last_Y[1] = Obj[x].Y_pos; + + } + + Current_Page = 0; + + View_X = 0; + View_Y = 0; + View_Max = 3; + View_Cnt = 0; + View_XD = 1; + View_YD = 1; + + Set_Color = 3; + S_Dir = 1; + Prev_Color = 0; + P_Dir = 1; + + Visible_Objects = MAX_SPRITES / 2; + Last_Objects[0] = 0; + Last_Objects[1] = 0; + + while (Demo_Running) + { + + set_active_page (Current_Page); + + /* Erase Old Images */ + + for (x = 0; x <= Last_Objects[Current_Page]; x++) + { + z = 2; + y = Obj[x].Shape; + x1 = Obj[x].Last_X[Current_Page]; + y1 = Obj[x].Last_Y[Current_Page]; + x2 = x1 + Img[y].X_Width -1; + y2 = y1 + Img[y].Y_Width -1; + + x1 = x1 & 0xfffc; + x2 = x2 | 0x0003; + + copy_bitmap (z, x1, y1, x2, y2, Current_Page, x1, y1); + } + + /* Draw new images */ + + for (x = 0; x <= Visible_Objects; x++) + { + Sprite_X = Img[Obj[x].Shape].X_Width; + Sprite_Y = Img[Obj[x].Shape].Y_Width; + + /* Move Sprite */ + + do + { + redo = False; + New_X = Obj[x].X_pos + Obj[x].X_Dir; + + if (( New_X < 0 ) || (New_X + Sprite_X > Screen_X) ) + { + Obj[x].X_Dir = -Obj[x].X_Dir; + if (random_int(20) == 1) + { + do + { + Obj[x].X_Dir = random_int(7) - 3; + Obj[x].Y_Dir = random_int(7) - 3; + } while ( (Obj[x].X_Dir == 0) && (Obj[x].Y_Dir == 0) ); + redo = True; + } + } + } while (redo); + Obj[x].X_pos = Obj[x].X_pos + Obj[x].X_Dir; + + + do + { + redo = False; + New_Y = Obj[x].Y_pos + Obj[x].Y_Dir; + + if ( (New_Y < 0) || (New_Y + Sprite_Y > Screen_Y) ) + { + Obj[x].Y_Dir = -Obj[x].Y_Dir; + if (random_int(20) == 1) + { + do + { + Obj[x].X_Dir = random_int(7) - 3; + Obj[x].Y_Dir = random_int(7) - 3; + } while ( (Obj[x].X_Dir == 0) && (Obj[x].Y_Dir == 0) ); + redo = True; + } + } + } while (redo); + + Obj[x].Y_pos = Obj[x].Y_pos + Obj[x].Y_Dir; + + /* Draw Sprite */ + + tdraw_bitmap ((char far*) &Img[Obj[x].Shape], Obj[x].X_pos, Obj[x].Y_pos, Sprite_X, Sprite_Y); + + Obj[x].Last_X[Current_Page] = Obj[x].X_pos; + Obj[x].Last_Y[Current_Page] = Obj[x].Y_pos; + + } + + Last_Objects[Current_Page] = Visible_Objects; + + + /* Pan Screen Back & Forth */ + + View_Cnt++; + if (View_Cnt >= View_Max) + { + View_X+= View_XD; + if ( (View_X == 0) || (View_X == 39) ) {View_XD = -View_XD;} + if (View_XD < 0) + { + View_Y+= View_YD; + if ( (View_Y == 0) || (View_Y == 39) ) {View_YD = -View_YD;} + } + + set_window (Current_Page, View_X, View_Y); + + View_Cnt = 0; + } + else + { + set_display_page (Current_Page); + } + + /* Cycle Colors */ + + set_dac_register (50 + Prev_Color, 3 + Prev_Color, 0, 60 - Prev_Color); + set_dac_register (50 + Set_Color, Set_Color, 10, 63 - Set_Color); + + set_dac_register (150 + Prev_Color, 3 + Prev_Color, 0, 60 - Prev_Color); + set_dac_register (150 + Set_Color, 63, 63, Set_Color); + + Set_Color+= S_Dir; + if ( (Set_Color == 60) || (Set_Color == 0) ) {S_Dir = -S_Dir;} + + Prev_Color+= P_Dir; + if ( (Prev_Color == 60) || (Prev_Color == 0) ) {P_Dir = -P_Dir;} + + /* Check for Keystroke */ + + Current_Page = Current_Page ^ 0x01; + + code = scan_keyboard (); + + if (code == Ky_ESC) {Demo_Running = False;} + + if (code == Ky_Plus) + { + if (View_Max < 12) {View_Max++;} + } + + if (code == Ky_Minus) + { + if (View_Max > 1) {View_Max--;} + if (View_Cnt >= View_Max) {View_Cnt = 0;} + } + + if (code == Ky_Up) + { + if (Visible_Objects < MAX_SPRITES-1) {Visible_Objects++;} + } + + if (code == Ky_Down) + { + if (Visible_Objects > 0) {Visible_Objects--;} + } + + } + +} diff --git a/modex105/DEMOS/C/X-DEMO.EXE b/modex105/DEMOS/C/X-DEMO.EXE new file mode 100755 index 0000000000000000000000000000000000000000..7742d145c591d471d2e9b79e0fe99083a3d669f2 GIT binary patch literal 41090 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 JvY09JKLIz`z5M_H literal 0 HcmV?d00001 diff --git a/modex105/DEMOS/C/X-DEMO.PRJ b/modex105/DEMOS/C/X-DEMO.PRJ new file mode 100755 index 0000000000000000000000000000000000000000..7bc4a069233a535f9a2bbb6b21436c8e018409e4 GIT binary patch literal 5188 zcmeI0*;89}6vxjm1PDtlt<^%>!cUD#+aW-ZrMO%|5-?6SlVBN3MV5fnLNGwx0b7@j zOQ+RGU)-%bT6KJIdGW=zRL7;I_0fO8U9`2=^SuFVm3SeEWybNB$tUNWdzN3$-GAYc zSZ7o^WFQvZ7U>$6O?}%V(oAC6%GH8@vTYPJeL@BjbdX7ONRtn1oYX8wG`^pQtsiLo zP~%5TL7!`$UugW2nL$UH4RnlYpegLg6-ciFuK}+EZvgKA?*c1P@_b+wLtFq{2z9lH_plI3kwCxA5Z3@} z8DbrzVn#*@R0;KB;1a+BTnb#q!RpxMEN#@%+-hKP3qx!LZUQ=iE}$ET06jo2a0`R_ zfdL>2+ij4BAdNuU0qJ%~cR;!mxC^)&xQ9V|fP0~i0%O29a38P}*u|jxAw2**2=yUg zH`IrLM}WtGy}&-;ao`ESSir~xvVd&B1e6IUcQEx~731WrZ)@}iJ$9ed(dPC!JS|RF z>WLSTq5Lm%@VFZn5uF~_3O+omY($kJlWf(T)5bz&MFmln7)wuyUz}adTi45sMqzlQrF`uU5~4EJ+9I9P;@;S_`qH2VPE>g(8Ix4v)Jkg1gKfNRj4YT&y>aY+Ee@Xm~-hX6OA?pU4aGc#NJqBZ~<-$ zb}1X!C@o%>x3tn$w_aL&{?KN7Kw1Jpzc&z;76&WdP;)6}XJcXgj%Z)E++gjJ3$Ac( z91p2tG^{5#9a(J&Tc`fnkC%{Q=L$f@t^Xw(A_^6jSWjrUuUc%*4HtR zAeGx2Nb6&+CEW=V%=_Oqf2QId{r^a*1JyLa@0jty{nQ3DM|(@y?Fm`^jn|Ra?{u}c zooSf~Sj0~*<&X($BQxX7T3UXslA|qN>rcz9|6+bK`LQjVmvDHkg--Iro4ShV<7U>_ z)LQ0rdHq%g@e6Fc)|_QACFXpwRAj`0&bTC3YXR)CM3z{s#GLs|DqED(^;?z6BtB9J zC^03fY*Qjimolu>D^jVUT#?U(%)B!64KH;apeJc3jZhD%s+sNiJg4{ge;cWx=wM-{ z#w?B58ciB=$kXvCKiHP;O# zklbpWT?S}t_cggaF6-Yngu+g@e;#|guf^k;&+c?Jh32ty9=;A@1OD9HZmXq>C=YF? X5-O&3w3gOTAr*0V3fVd{aHaSIrzdwi literal 0 HcmV?d00001 diff --git a/modex105/DEMOS/C/x.exe b/modex105/DEMOS/C/x.exe new file mode 100755 index 0000000000000000000000000000000000000000..7742d145c591d471d2e9b79e0fe99083a3d669f2 GIT binary patch literal 41090 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 JvY09JKLIz`z5M_H literal 0 HcmV?d00001 diff --git a/modex105/DEMOS/CHARDEMO.EXE b/modex105/DEMOS/CHARDEMO.EXE new file mode 100755 index 0000000000000000000000000000000000000000..4015bdf14e1d4067c4c2d9321f00accdf71fe452 GIT binary patch literal 13066 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 RBhN(+NB$^IcBSvV{T~&EV%Y!y literal 0 HcmV?d00001 diff --git a/modex105/DEMOS/PASCAL/TEST5.PAS b/modex105/DEMOS/PASCAL/TEST5.PAS new file mode 100755 index 00000000..7cc56bbb --- /dev/null +++ b/modex105/DEMOS/PASCAL/TEST5.PAS @@ -0,0 +1,488 @@ +{ ModeX Turbo Pascal Demo Program } +{ Converted to Turbo Pascal by Scott Wyatt } +{ Original program written in QuickBasic by Matt Prichard } +{ Released to the Public Domain } +{ } +{ Thanks to Matt Prichard for his *EXCELLENT* ModeX Library } +{ Additional Comments by Matt Pritchard } + +Uses Crt; + +{$L modex2.obj} { This file is the external ModeX Library .OBJ } +{$F+} + + { Mode Setting Routines } + +Function SET_VGA_MODEX (Mode,MaxXpos,MaxYpos,Pages : integer) : integer; external; +Function SET_MODEX (Mode:integer) : Integer; external; + + { Graphics Primitives } + +Procedure CLEAR_VGA_SCREEN (Color:integer); external; +Procedure SET_POINT (Xpos,Ypos,Color : integer); external; +Function READ_POINT (Xpos,Ypos:integer) : integer; external; +Procedure FILL_BLOCK (Xpos1,Ypos1,Xpos2,Ypos2,Color:integer); external; +Procedure DRAW_LINE (Xpos1,Ypos1,Xpos2,Ypos2,Color:integer); external; + + { VGA DAC Routines } + +Procedure SET_DAC_REGISTER (RegNo,Red,Green,Blue:integer); external; +Procedure GET_DAC_REGISTER (RegNo,Red,Green,Blue:integer); external; + + { Page and Window Control Routines } + +Procedure SET_ACTIVE_PAGE (PageNo:integer); external; +Function GET_ACTIVE_PAGE : integer; external; +Procedure SET_DISPLAY_PAGE (PageNo:integer); external; +Function GET_DISPLAY_PAGE : integer; external; +Procedure SET_WINDOW (DisplayPage,XOffset,YOffset : integer); external; +Function GET_X_OFFSET : integer; external; +Function GET_Y_OFFSET : integer; external; +Procedure SYNC_DISPLAY; external; + + { Text Display Routines } + +Procedure GPRINTC (CharNum,Xpos,Ypos,ColorF,ColorB:integer); external; +Procedure TGPRINTC ( CharNum,Xpos,Ypos,ColorF : integer); external; +Procedure PRINT_STR (Var Text;MaxLen,Xpos,Ypos,ColorF,ColorB:integer); external; +Procedure TPRINT_STR (Var Text;MaxLen,Xpos,Ypos,ColorF:integer); external; +Procedure SET_DISPLAY_FONT (Var FontData;FontNumber:integer); external; + + { Sprite and VGA memory -> Vga memory Copy Routines } + +Procedure DRAW_BITMAP (Var Image;Xpos,Ypos,Width,Height:integer); external; +Procedure TDRAW_BITMAP (Var Image;Xpos,Ypos,Width,Height:integer); external; +Procedure COPY_PAGE (SourcePage,DestPage:integer); external; +Procedure COPY_BITMAP (SourcePage,X1,Y1,X2,Y2,DestPage,DestX1,DestY1:integer); external; + +{$F-} + + +TYPE Sprite = Record + Xpos : INTEGER; + Ypos : INTEGER; + XDir : INTEGER; + YDir : INTEGER; + Shape : INTEGER; + LastX : INTEGER; + LastY : INTEGER; + END; + + +CONST MaxShapes = 32; + Circle_16 : Array[1..16,1..16] of byte = + (( 0, 0, 0, 0, 0, 0, 20, 20, 20, 20, 0, 0, 0, 0, 0, 0), + ( 0, 0, 0, 0, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0), + ( 0, 0, 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0), + ( 0, 0, 20, 20, 20, 20, 0, 0, 0, 0, 20, 20, 20, 20, 0, 0), + ( 0, 20, 20, 20, 20, 0, 0, 0, 0, 0, 0, 20, 20, 20, 20, 0), + ( 0, 20, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 20, 0), + ( 20, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 20), + ( 20, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 20), + ( 20, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 20), + ( 20, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 20), + ( 0, 20, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 20, 0), + ( 0, 20, 20, 20, 20, 0, 0, 0, 0, 0, 0, 20, 20, 20, 20, 0), + ( 0, 0, 20, 20, 20, 20, 0, 0, 0, 0, 20, 20, 20, 20, 0, 0), + ( 0, 0, 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0), + ( 0, 0, 0, 0, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0), + ( 0, 0, 0, 0, 0, 0, 20, 20, 20, 20, 0, 0, 0, 0, 0, 0)); + Square_16 : Array[1..16,1..16] of byte = + (( 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21), + ( 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21), + ( 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21), + ( 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21), + ( 21, 21, 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 21, 21, 21, 21), + ( 21, 21, 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 21, 21, 21, 21), + ( 21, 21, 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 21, 21, 21, 21), + ( 21, 21, 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 21, 21, 21, 21), + ( 21, 21, 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 21, 21, 21, 21), + ( 21, 21, 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 21, 21, 21, 21), + ( 21, 21, 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 21, 21, 21, 21), + ( 21, 21, 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 21, 21, 21, 21), + ( 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21), + ( 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21), + ( 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21), + ( 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21)); + Diamond : Array[1..8,1..8] of byte = + (( 0, 0, 0, 22, 22, 0, 0, 0), + ( 0, 0, 22, 22, 22, 22, 0, 0), + ( 0, 22, 22, 0, 0, 22, 22, 0), + ( 22, 22, 0, 0, 0, 0, 22, 22), + ( 22, 22, 0, 0, 0, 0, 22, 22), + ( 0, 22, 22, 0, 0, 22, 22, 0), + ( 0, 0, 22, 22, 22, 22, 0, 0), + ( 0, 0, 0, 22, 22, 0, 0, 0)); + Rectangle : Array[1..8,1..3] of byte = + (( 23, 23, 23), + ( 23, 23, 23), + ( 23, 23, 23), + ( 23, 23, 23), + ( 23, 23, 23), + ( 23, 23, 23), + ( 23, 23, 23), + ( 23, 23, 23)); + + { Global Variables ? } + +Var + XCenter,X1,Y1,X2,Y2,Z,Colr,XChars,YChars,X,Y,N,Gap : Integer; + s : string; + s1 : Array[1..35] of Char; + ch : Char; + obj : Array[1..64] of Sprite; + ScreenX,ScreenY : Integer; + c, dc, SpriteX, SpriteY, CurrentPage, LastPage : Integer; + SetColor, SDir, PrevColor, PDir : Byte; + XView, YView : Integer; + XView_Change, YView_Change : Integer; + Right : Boolean; + Number_Of_Shapes : Byte; + + + { Error Handler - Returns to Text Mode & Displays Error } + +Procedure ERROR_OUT(s : string); + Begin + asm + mov ah,0 + mov al,3 + int 10h + end; + WriteLn(s); + Halt(0); +END; + + { Routine to Print a PASCAL string using Print_Str } + +Procedure Print_Text(s : string; X,Y,BColor,FColor : integer); +Var + s1 : Array[1..135] of Char; + i : byte; +Begin + For i := 1 to Length(s) DO + s1[i] := s[i]; + Print_Str(s1,Length(s),X,Y,BColor,FColor); +End; + + { Routine to Transparently Print a PASCAL string using TPrint_Str } + +Procedure TPrint_Text(s : string; X,Y,Color : integer); +Var + s1 : Array[1..135] of Char; + i : byte; +Begin + For i := 1 to Length(s) DO + s1[i] := s[i]; + TPrint_Str(s1,Length(s),X,Y,Color); +End; + + { Routines to show test patterns for a given mode } + +Procedure Demo_Res(Mode, Xmax, Ymax : integer); +Begin + + Str(mode,s); + If Set_ModeX(Mode) = 0 Then + Error_Out('Unable to SET_MODEX '+s); + Clear_VGA_Screen(0); + + XCenter := Xmax div 2; + X1 := 10; + Y1 := 10; + X2 := Xmax - 1; + Y2 := Ymax - 1; + + FOR Z := 0 TO 3 DO + Begin + Colr := 31 - Z * 2; + Draw_Line(X1 + Z, Y1 + Z, X2 - Z, Y1 + Z, Colr); + Draw_Line(X1 + Z, Y1 + Z, X1 + Z, Y2 - Z, Colr); + Draw_Line(X1 + Z, Y2 - Z, X2 - Z, Y2 - Z, Colr); + Draw_Line(X2 - Z, Y1 + Z, X2 - Z, Y2 - Z, Colr); + End; + + XChars := Xmax div 10; + YChars := Ymax div 10; + + FOR X := 0 TO XChars - 1 DO + Begin + TGPRINTC(48 + ((X + 1) MOD 10), X * 10 + 1, 1, 9 + ((X div 8) MOD 7)); + DRAW_LINE(X * 10 + 9, 0, X * 10 + 9, 3, 15); + End; + FOR Y := 0 TO YChars - 1 DO + Begin + TGPRINTC(48 + ((Y + 1) MOD 10), 1, Y * 10 + 1, 9 + ((Y div 10) MOD 7)); + DRAW_LINE(0, Y * 10 + 9, 3, Y * 10 + 9, 15); + End; + + { Test Line Drawing } + + FOR X := 0 TO 63 DO + Begin + N := 15 + ((X * 3) div 4); + SET_DAC_REGISTER(64 + X, N, N, N); + SET_DAC_REGISTER(128 + X, 0, N, N); + DRAW_LINE(103 - X, 60, 40 + X, 123, 64 + X); + DRAW_LINE(40, 60 + X, 103, 123 - X, 128 + X); + End; + s := 'Line Test'; + PRINT_Text(s,37,130,1,0); + + { Test Block Fills } + + Y := 60; + Gap := 0; + FOR X := 0 TO 9 DO + Begin + FILL_BLOCK(120, Y, 120 + X, Y + Gap, 64 + X); + FILL_BLOCK(140 - (15 - X), Y, 150 + X, Y + Gap, 230 + X); + FILL_BLOCK(170 - (15 - X), Y, 170, Y + Gap, 128 + X); + Y := Y + Gap + 2; + Gap := Gap + 1; + End; + s := 'Fill Test'; + Print_Text(s,110, 46, 2,0); + + { Test Pixel Write and Read } + + FOR X := 190 TO 250 DO + FOR Y := 60 TO 122 DO + SET_POINT( X, Y, X + Y + X + Y); + + s := 'Pixel Test'; + Print_Text(s,182, 130, 3,0); + + FOR X := 190 TO 250 DO + FOR Y := 60 TO 122 DO + IF READ_POINT(X, Y) <> ((X + Y + X + Y) AND 255) THEN + WriteLn('READ_PIXEL Failure'); + + { Display rest of screen } + + s := ' This is a MODE X demo '; + Print_Text(s,XCenter - (Length(s) * 4), 20, 3, 1); + s := 'Screen Resolution is by '; + X := XCenter - (Length(s) * 4); + Print_Text(s,X,30,4,0); + Str(XMax,s); + Print_Text(s, X + 8 * 21, 30, 8, 0); + Str(YMax,s); + Print_Text(s, X + 8 * 28, 30, 15, 0); + + FOR X := 0 TO 15 DO + Begin + SET_DAC_REGISTER( 230 + X, 63 - X * 4, 0, 15 + X * 3); + DRAW_LINE(30 + X, Ymax - 6 - X, Xmax - 20 - X, Ymax - 6 - X, 230 + X); + End; + s := 'Press to Continue'; + For x := 1 to length(s) DO + s1[x] := s[x]; + TPrint_Str(s1, length(s), XCenter - (26 * 4), Ymax - 18, 5); + + Ch := ReadKey; + IF Ch = #27 Then + Error_Out('Abort'); + +End; + + + { Initialize Sprites for Sprite Demo } + +Procedure Init_Sprites; +Var i : byte; +Begin + For i := 1 to 64 DO + Begin + Obj[i].XPos := Random(300)+10; + Obj[i].YPos := Random(200)+20; + Obj[i].XDir := Random(10)-5; + Obj[i].YDir := Random(10)-5; + If (Obj[i].XDir = 0) AND (Obj[i].YDir = 0) Then + Begin + Obj[i].XDir := Random(5) + 1; + Obj[i].YDir := Random(5) + 1; + End; + Obj[i].Shape := Random(4)+1; + Obj[i].LastX := obj[i].XPos; + Obj[i].LastY := obj[i].YPos; + End; +End; + +Procedure Set_Sprites(number : byte); +Var i : Byte; +Begin + For i := 1 to number DO + Begin + obj[i].LastX := obj[i].XPos; + obj[i].LastY := obj[i].YPos; + obj[i].XPos := obj[i].XPos + obj[i].XDir; + obj[i].YPos := obj[i].YPos + obj[i].YDir; + If (obj[i].XPos > 335) OR (obj[i].XPos < 5 ) Then + obj[i].XDir := -(obj[i].XDir); + If (obj[i].YPos > 220) OR (obj[i].YPos < 5) Then + obj[i].YDir := -(obj[i].YDir); + End; + For i := 1 to number DO + Case obj[i].Shape of + 1 : TDraw_Bitmap(Circle_16,obj[i].XPos,obj[i].YPos,16,16); + 2 : TDraw_Bitmap(Square_16,obj[i].XPos,obj[i].YPos,16,16); + 3 : TDraw_Bitmap(Diamond,obj[i].XPos,obj[i].YPos,8,8); + 4 : TDraw_Bitmap(Rectangle,obj[i].XPos,obj[i].YPos,3,8); + End; +End; + +Procedure Remove_Sprites(p,number : byte); +Var i : byte; +Begin + For i := 1 to number DO + Copy_Bitmap(2,obj[i].LastX,obj[i].LastY,obj[i].LastX+16,obj[i].LastY+16,p,Obj[i].LastX,Obj[i].LastY); +End; + +Procedure Page_Demo; +Begin + Number_Of_Shapes := 64; + XView_Change := 1; + YView_Change := 1; + XView := 1; + YView := 1; + Right := TRUE; + ScreenX := 360; + ScreenY := 240; + PrevColor := 0; + SetColor := 3; + SDir := 1; + PDir := 1; + Str(0,s); + + IF SET_VGA_MODEX(0, ScreenX, ScreenY, 3) = 0 THEN + ERROR_OUT('Unable to SET_VGA_MODEX' + S); + + SET_ACTIVE_PAGE(0); + CLEAR_VGA_SCREEN(0); + PRINT_TEXT('This is a Test of the Following Functions:', 10, 9, 15, 0); + DRAW_LINE( 10, 18, 350, 18, 4); + Print_Text('SET_ACTIVE_PAGE', 10, 20, 1, 0); + Print_Text('SET_DISPLAY_PAGE', 10, 30, 3,0); + Print_Text('SET_DAC_REGISTER', 10, 40, 3, 0); + Print_Text('CLEAR_VGA_SCREEN', 10, 50, 13, 0); + Print_Text('TDRAW_BITMAP', 10, 60, 14, 0); + Print_Text('COPY_PAGE', 10, 70, 3, 0); + Print_Text('COPY_BITMAP', 10, 80, 13, 0); + Print_Text('GPRINTC', 10, 90, 1, 0); + Print_Text('TGPRINTC', 10, 100, 3, 0); + Print_Text('SYNC_DISPLAY', 10, 110, 3, 0); + Print_Text('SET_WINDOW', 10, 120, 14, 0); + Print_Text('VIRTUAL SCREEN SIZES', 190, 20, 1, 0); + Print_Text(' SMOOTH SCROLLING', 190, 30, 3, 0); + Print_Text(' SPRITE ANIMATION', 190, 40, 13, 0); + Print_Text(' PAGE FLIPPING', 190, 50, 3, 0); + Print_Text(' COLOR CYCLING', 190, 60, 14, 0); + + FOR X := 0 TO 60 DO + Begin + SET_DAC_REGISTER( 50 + X, 3 + X, 0, 60 - X); + SET_DAC_REGISTER( 150 + X, 3 + X, 0, 60 - X); + End; + + c := 0; + DC := 1; + FOR X := 0 TO ScreenX div 2 DO + Begin + DRAW_LINE( ScreenX div 2 - 1, ScreenY div 4, X, ScreenY - 1, c + 50); + DRAW_LINE( ScreenX div 2, ScreenY div 4, ScreenX - X - 1, ScreenY - 1, c + 50); + c := c + DC; + IF (c = 0) OR (c = 60) THEN DC := -DC; + End; + + TPrint_Text('Press to Continue', 82, 190, 15); + TPrint_Text('<+> = Fewer Shapes <-> = More Shapes', 32, 204, 12); + COPY_PAGE( 0, 1); + COPY_PAGE( 0, 2); + + Ch := #0; + CurrentPage := 1; + LastPage := 0; + Set_Sprites(Number_Of_Shapes); + For c := 1 to 4 DO + Set_Dac_Register(19+c,63-(c*10),0,0); + + While Ch <> #27 DO + Begin + Set_Active_Page(currentpage); + Set_Sprites(Number_Of_Shapes); + If Right Then + Begin + XView := XView + XView_Change; + If (XView > 38) OR (XView < 2) Then + Begin + XView_Change := -(XView_Change); + Right := FALSE; + End; + End + Else + Begin + YView := YView + YView_Change; + If (YView > 38) OR (YView < 2) Then + Begin + YView_Change := -(YView_Change); + Right := TRUE; + End; + End; + + Set_Window(currentpage,XView,YView); + Set_Display_Page(currentpage); + Set_Dac_Register(50 + PrevColor, 3 + PrevColor, 0, 60 - PrevColor); + Set_Dac_Register(50 + SetColor, SetColor, 10, 63 - SetColor); + Set_Dac_Register(150 + PrevColor, 3 + PrevColor, 0, 60 - PrevColor); + Set_Dac_Register(150 + SetColor, 63, 63, SetColor); + SetColor := SetColor + SDir; + IF (SetColor = 60) OR (SetColor = 0) THEN SDir := -SDir; + PrevColor := PrevColor + PDir; + IF (PrevColor = 60) OR (PrevColor = 0) THEN PDir := -PDir; + Remove_Sprites(lastpage,Number_Of_Shapes); + + If Keypressed Then + Begin + Ch := ReadKey; + Case Ch of + '-' : If Number_Of_Shapes > 1 Then + Begin + c := Number_Of_Shapes; + Copy_Bitmap(2,obj[c].XPos,obj[c].YPos,obj[c].XPos+16,obj[c].YPos+16, + currentpage,obj[c].XPos,obj[c].YPos); + Dec(Number_Of_Shapes); + End; + '+' : If Number_Of_Shapes < 64 Then Inc(Number_Of_Shapes); + End; + End; + lastpage := (lastpage+1) MOD 2; + currentpage := (currentpage+1) MOD 2; + End; +END; + + { MAIN ROUTINE - Run Through Demos and Exit } + +Begin + + Randomize; + Init_Sprites; + + Demo_Res(0, 320, 200); + Demo_Res(1, 320, 400); + Demo_Res(2, 360, 200); + Demo_Res(3, 360, 400); + Demo_Res(4, 320, 240); + Demo_Res(5, 320, 480); + Demo_Res(6, 360, 240); + Demo_Res(7, 360, 480); + Page_Demo; + + asm + mov ah,0 + mov al,3 + int 10h + end; + WriteLn('THIS MODE X DEMO IS FINISHED'); + +END. \ No newline at end of file diff --git a/modex105/DEMOS/QB45/MAKE-LIB.BAT b/modex105/DEMOS/QB45/MAKE-LIB.BAT new file mode 100755 index 00000000..b04876ad --- /dev/null +++ b/modex105/DEMOS/QB45/MAKE-LIB.BAT @@ -0,0 +1,5 @@ +ECHO ... Building MODEX.QLB for QUICKBASIC 4.5 +LIB MODEX -+MODEX,, +LIB MODEX -+UTILS,, +DEL MODEX.BAK +LINK /Q MODEX+UTILS, MODEX.QLB, NUL, C:\QB45\BQLB45.LIB; diff --git a/modex105/DEMOS/QB45/MODEX.BI b/modex105/DEMOS/QB45/MODEX.BI new file mode 100755 index 00000000..6b1d7afe --- /dev/null +++ b/modex105/DEMOS/QB45/MODEX.BI @@ -0,0 +1,63 @@ + + ' ===== SCREEN RESOLUTIONS ===== + +CONST Mode320x200 = 0, Mode320x400 = 1 +CONST Mode360x200 = 2, Mode360x400 = 3 +CONST Mode320x240 = 4, Mode320x480 = 5 +CONST Mode360x240 = 6, Mode360x480 = 7 + + ' ===== MODE X SETUP ROUTINES ===== + +DECLARE FUNCTION SET.VGA.MODEX% ALIAS "SET_VGA_MODEX" (BYVAL ModeType%, BYVAL MaxXpos%, BYVAL MaxYpos%, BYVAL Pages%) +DECLARE FUNCTION SET.MODEX% ALIAS "SET_MODEX" (BYVAL Mode%) + + ' ===== BASIC GRAPHICS PRIMITIVES ===== + +DECLARE SUB CLEAR.VGA.SCREEN ALIAS "CLEAR_VGA_SCREEN" (BYVAL ColorNum%) +DECLARE SUB SET.POINT ALIAS "SET_POINT" (BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorNum%) +DECLARE FUNCTION READ.POINT% ALIAS "READ_POINT" (BYVAL Xpos%, BYVAL Ypos%) +DECLARE SUB FILL.BLOCK ALIAS "FILL_BLOCK" (BYVAL Xpos1%, BYVAL Ypos1%, BYVAL Xpos2%, BYVAL Ypos2%, BYVAL ColorNum%) +DECLARE SUB DRAW.LINE ALIAS "DRAW_LINE" (BYVAL Xpos1%, BYVAL Ypos1%, BYVAL Xpos2%, BYVAL Ypos2%, BYVAL ColorNum%) + + ' ===== DAC COLOR REGISTER ROUTINES ===== + +DECLARE SUB SET.DAC.REGISTER ALIAS "SET_DAC_REGISTER" (BYVAL RegNo%, BYVAL Red%, BYVAL Green%, BYVAL Blue%) +DECLARE SUB GET.DAC.REGISTER ALIAS "GET_DAC_REGISTER" (BYVAL RegNo%, Red%, Green%, Blue%) +DECLARE SUB LOAD.DAC.REGISTERS ALIAS "LOAD_DAC_REGISTERS" (SEG PalData AS ANY, BYVAL StartReg%, BYVAL EndReg%, BYVAL VSync%) +DECLARE SUB READ.DAC.REGISTERS ALIAS "READ_DAC_REGISTERS" (SEG PalData AS ANY, BYVAL StartReg%, BYVAL EndReg%) + + + ' ===== PAGE FLIPPING AND SCROLLING ROUTINES ===== + +DECLARE SUB SET.ACTIVE.PAGE ALIAS "SET_ACTIVE_PAGE" (BYVAL PageNo%) +DECLARE FUNCTION GET.ACTIVE.PAGE% ALIAS "GET_ACTIVE_PAGE" +DECLARE SUB SET.DISPLAY.PAGE ALIAS "SET_DISPLAY_PAGE" (BYVAL PageNo%) +DECLARE FUNCTION GET.DISPLAY.PAGE% ALIAS "GET_DISPLAY_PAGE" +DECLARE SUB SET.WINDOW ALIAS "SET_WINDOW" (BYVAL DisplayPage%, BYVAL XOffset%, BYVAL YOffset%) +DECLARE FUNCTION GET.X.OFFSET% ALIAS "GET_X_OFFSET" () +DECLARE FUNCTION GET.Y.OFFSET% ALIAS "GET_Y_OFFSET" () +DECLARE SUB SYNC.DISPLAY ALIAS "SYNC_DISPLAY" + + ' ===== TEXT DISPLAY ROUTINES ===== + +DECLARE SUB GPRINTC (BYVAL CharacterNum%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%, BYVAL ColorB%) +DECLARE SUB TGPRINTC (BYVAL CharacterNum%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%) +DECLARE SUB PRINT.STR ALIAS "PRINT_STR" (BYVAL StrSeg%, BYVAL StrOfs%, BYVAL MaxLen%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%, BYVAL ColorB%) +DECLARE SUB TPRINT.STR ALIAS "TPRINT_STR" (BYVAL StrSeg%, BYVAL StrOfs%, BYVAL MaxLen%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%) +DECLARE SUB SET.DISPLAY.FONT ALIAS "SET_DISPLAY_FONT" (SEG FontData AS ANY, BYVAL FontNumber%) + + ' ===== BITMAP (SPRITE) DISPLAY ROUTINES ===== + +DECLARE SUB DRAW.BITMAP ALIAS "DRAW_BITMAP" (SEG Image AS ANY, BYVAL Xpos%, BYVAL Ypos%, BYVAL xWidth%, BYVAL Height%) +DECLARE SUB TDRAW.BITMAP ALIAS "TDRAW_BITMAP" (SEG Image AS ANY, BYVAL Xpos%, BYVAL Ypos%, BYVAL xWidth%, BYVAL Height%) + + ' ==== VIDEO MEMORY to VIDEO MEMORY COPY ROUTINES ===== + +DECLARE SUB COPY.PAGE ALIAS "COPY_PAGE" (BYVAL SourcePage%, BYVAL DestPage%) +DECLARE SUB COPY.BITMAP ALIAS "COPY_BITMAP" (BYVAL SourcePage%, BYVAL X1%, BYVAL Y1%, BYVAL X2%, BYVAL Y2%, BYVAL DestPage%, BYVAL DestX1%, BYVAL DestY1%) + + + + + + diff --git a/modex105/DEMOS/QB45/MODEX.QLB b/modex105/DEMOS/QB45/MODEX.QLB new file mode 100755 index 0000000000000000000000000000000000000000..90122c134b3116f960cfd35a3f2d6144bfa2a819 GIT binary patch literal 9739 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 ((X + Y + X + Y) AND 255) THEN + ERROR.OUT "READ.PIXEL Failure" + END IF + NEXT Y + NEXT X + + + + Msg$ = " This is a MODE X demo " + PRINT.TEXT Msg$, XCenter - (LEN(Msg$) * 4), 20, c.bRED, c.BLUE + Msg$ = "Screen Resolution is by " + Xp = XCenter - (LEN(Msg$) * 4) + PRINT.TEXT Msg$, Xp, 30, c.bGREEN, c.BLACK + + PRINT.TEXT LTRIM$(STR$(Xmax)), Xp + 8 * 21, 30, c.bPURPLE, c.BLACK + PRINT.TEXT LTRIM$(STR$(Ymax)), Xp + 8 * 28, 30, c.bWHITE, c.BLACK + + FOR X = 0 TO 15 + SET.DAC.REGISTER 230 + X, 63 - X * 4, 0, 15 + X * 3 + DRAW.LINE 30 + X, Ymax - 6 - X, Xmax - 20 - X, Ymax - 6 - X, 230 + X + NEXT X + TPRINT.TEXT "Press to Continue", XCenter - (26 * 4), Ymax - 18, c.YELLOW + + X = GET.KEY% + IF X = KyESC THEN ERROR.OUT "ABORT" + +END SUB + +SUB ERROR.OUT (Message$) + + SET.VIDEO.MODE 3 + DOS.PRINT Message$ + END + +END SUB + +FUNCTION GET.KEY% + + DO + X = SCAN.KEYBOARD + LOOP UNTIL X + + GET.KEY% = X + +END FUNCTION + +SUB LOAD.SHAPES + +DIM Grid(1 TO 32, 1 TO 32) + + FOR Shape = 0 TO MaxShapes - 1 + + FOR Y = 1 TO 32 + FOR X = 1 TO 32 + Grid(X, Y) = 0 + NEXT X + NEXT Y + + Style = RANDOM.INT(6) + Colour = 1 + RANDOM.INT(15) + + SELECT CASE Style + + CASE 0: ' Solid Box + + DO + xWidth = 3 + RANDOM.INT(30) + yWidth = 3 + RANDOM.INT(30) + LOOP UNTIL ((xWidth * yWidth) <= 512) + + FOR Y = 1 TO yWidth + FOR X = 1 TO xWidth + Grid(X, Y) = Colour + NEXT X + NEXT Y + + CASE 1: ' Hollow Box + + DO + xWidth = 5 + RANDOM.INT(28) + yWidth = 5 + RANDOM.INT(28) + LOOP UNTIL ((xWidth * yWidth) <= 512) + + FOR Y = 1 TO yWidth + FOR X = 1 TO xWidth + Grid(X, Y) = Colour + NEXT X + NEXT Y + + HollowX = 1 + RANDOM.INT(xWidth \ 2 - 1) + HollowY = 1 + RANDOM.INT(yWidth \ 2 - 1) + + FOR Y = HollowY + 1 TO yWidth - HollowY + FOR X = HollowX + 1 TO xWidth - HollowX + Grid(X, Y) = nil + NEXT X + NEXT Y + + CASE 2: ' Solid Diamond + + xWidth = 3 + 2 * RANDOM.INT(10) + yWidth = xWidth + Centre = xWidth \ 2 + + FOR Y = 0 TO Centre + FOR X = 0 TO Y + Grid(Centre - X + 1, Y + 1) = Colour + Grid(Centre + X + 1, Y + 1) = Colour + Grid(Centre - X + 1, yWidth - Y) = Colour + Grid(Centre + X + 1, yWidth - Y) = Colour + NEXT X + NEXT Y + + + CASE 3: ' Hollow Diamond + + + xWidth = 3 + 2 * RANDOM.INT(10) + yWidth = xWidth + Centre = xWidth \ 2 + sWidth = RANDOM.INT(Centre) + + FOR Y = 0 TO Centre + FOR X = 0 TO Y + IF X + (Centre - Y) >= sWidth THEN + Grid(Centre - X + 1, Y + 1) = Colour + Grid(Centre + X + 1, Y + 1) = Colour + Grid(Centre - X + 1, yWidth - Y) = Colour + Grid(Centre + X + 1, yWidth - Y) = Colour + END IF + NEXT X + NEXT Y + + CASE 4: ' Ball + + xWidth = 7 + 2 * RANDOM.INT(8) + yWidth = xWidth + Centre = 1 + xWidth \ 2 + + FOR Y = 1 TO yWidth + FOR X = 1 TO xWidth + D = SQR(((Centre - X) * (Centre - X)) + ((Centre - Y) * (Centre - Y))) + IF D < Centre THEN Grid(X, Y) = 150 + Colour * 2 + D * 3 + NEXT X + NEXT Y + + CASE 5: ' Ball + + + xWidth = 7 + 2 * RANDOM.INT(8) + yWidth = xWidth + Centre = 1 + xWidth \ 2 + sWidth = RANDOM.INT(xWidth) + + FOR Y = 1 TO yWidth + FOR X = 1 TO xWidth + D = SQR(((Centre - X) * (Centre - X)) + ((Centre - Y) * (Centre - Y))) + IF D < Centre AND D >= sWidth THEN Grid(X, Y) = 150 + Colour * 2 + D * 3 + NEXT X + NEXT Y + + END SELECT + + Img(Shape).xWidth = xWidth + Img(Shape).yWidth = yWidth + + A$ = STRING$(xWidth * yWidth, nil) + + c = 1 + FOR Y = 1 TO yWidth + FOR X = 1 TO xWidth + MID$(A$, c, 1) = CHR$(Grid(X, Y)) + c = c + 1 + NEXT X + NEXT Y + + Img(Shape).ImgData = A$ + + + NEXT Shape + +END SUB + +SUB PAGE.DEMO + +CONST MaxSprites = 64 + +DIM Obj(MaxSprites) AS Sprite +DIM LastX(MaxSprites, 1), LastY(MaxSprites, 1) +DIM LastObjects(1) + + ScreenX = 360: ScreenY = 240 + + IF SET.VGA.MODEX%(Mode320x200, ScreenX, ScreenY, 3) = 0 THEN + ERROR.OUT "Unable to SET_VGA_MODEX" + STR$(Mode) + END IF + + SET.ACTIVE.PAGE 0 + + CLEAR.VGA.SCREEN c.BLACK + + PRINT.TEXT "This is a Test of the Following Functions:", 10, 9, c.bWHITE, c.BLACK + + DRAW.LINE 10, 18, 350, 18, c.YELLOW + PRINT.TEXT "SET_ACTIVE_PAGE", 10, 20, c.bBLUE, c.BLACK + PRINT.TEXT "SET_DISPLAY_PAGE", 10, 30, c.GREEN, c.BLACK + PRINT.TEXT "SET_DAC_REGISTER", 10, 40, c.RED, c.BLACK + PRINT.TEXT "CLEAR_VGA_SCREEN", 10, 50, c.CYAN, c.BLACK + + PRINT.TEXT "TDRAW_BITMAP", 10, 60, c.PURPLE, c.BLACK + PRINT.TEXT "COPY_PAGE", 10, 70, c.GREEN, c.BLACK + PRINT.TEXT "COPY_BITMAP", 10, 80, c.CYAN, c.BLACK + + PRINT.TEXT "GPRINTC", 10, 90, c.BLUE, c.BLACK + PRINT.TEXT "TGPRINTC", 10, 100, c.GREEN, c.BLACK + PRINT.TEXT "SET_WINDOW", 10, 110, c.RED, c.BLACK + + PRINT.TEXT "VIRTUAL SCREEN SIZES", 190, 20, c.bBLUE, c.BLACK + PRINT.TEXT " SMOOTH SCROLLING", 190, 30, c.GREEN, c.BLACK + PRINT.TEXT " SPRITE ANIMATION", 190, 40, c.CYAN, c.BLACK + PRINT.TEXT " PAGE FLIPPING", 190, 50, c.RED, c.BLACK + PRINT.TEXT " COLOR CYCLING", 190, 60, c.PURPLE, c.BLACK + + + FOR X = 0 TO 60 + SET.DAC.REGISTER 50 + X, 3 + X, 0, 60 - X + SET.DAC.REGISTER 150 + X, 3 + X, 0, 60 - X + NEXT X + + c = 0: DC = 1 + FOR X = 0 TO ScreenX \ 2 + DRAW.LINE ScreenX \ 2 - 1, ScreenY \ 4, X, ScreenY - 1, c + 50 + DRAW.LINE ScreenX \ 2, ScreenY \ 4, ScreenX - X - 1, ScreenY - 1, c + 50 + c = c + DC + IF c = 0 OR c = 60 THEN DC = -DC + NEXT X + + TPRINT.TEXT "Press to Continue", 72, 190, c.bWHITE + TPRINT.TEXT "< > = Faster < > = Slower", 72, 204, c.bGREEN + TPRINT.TEXT "< > = Fewer Shapes < > = More Shapes", 32, 218, c.bCYAN + + TGPRINTC 43, 80, 204, c.YELLOW + TGPRINTC 45, 200, 204, c.YELLOW + + TGPRINTC 25, 40, 218, c.YELLOW + TGPRINTC 24, 200, 218, c.YELLOW + + COPY.PAGE 0, 1 + COPY.PAGE 0, 2 + + FOR X = 1 TO MaxSprites + DO + Obj(X).XDir = RANDOM.INT(7) - 3 + Obj(X).YDir = RANDOM.INT(7) - 3 + LOOP WHILE (Obj(X).XDir = 0 AND Obj(X).YDir = 0) + + Obj(X).Shape = X MOD MaxShapes + + SpriteX = Img(Obj(X).Shape).xWidth + SpriteY = Img(Obj(X).Shape).yWidth + + Obj(X).Xpos = 1 + RANDOM.INT(ScreenX - SpriteX - 2) + Obj(X).Ypos = 1 + RANDOM.INT(ScreenY - SpriteY - 2) + + LastX(X, 0) = Obj(X).Xpos + LastX(X, 1) = Obj(X).Xpos + LastY(X, 0) = Obj(X).Ypos + LastY(X, 1) = Obj(X).Ypos + NEXT X + + CurrentPage = 0 + + 'View Shift... + + ViewX = 0 + ViewY = 0 + ViewMax = 3 + ViewCnt = 0 + ViewXD = 1 + ViewYD = 1 + + SetColor = 3: SDir = 1 + PrevColor = 0: PDir = 1 + + VisObjects = MaxSprites \ 2 + LastObjects(0) = 0 + LastObjects(1) = 0 + +DRAW.LOOP: + + + SET.ACTIVE.PAGE CurrentPage + + ' Erase Old Images + + FOR X = 1 TO LastObjects(CurrentPage) + + X1 = LastX(X, CurrentPage) AND &HFFFC + Y1 = LastY(X, CurrentPage) + X2 = ((LastX(X, CurrentPage) + Img(Obj(X).Shape).xWidth)) OR 3 + Y2 = Y1 + Img(Obj(X).Shape).yWidth - 1 + + COPY.BITMAP 2, X1, Y1, X2, Y2, CurrentPage, X1, Y1 + + NEXT X + + ' Draw new images + + FOR X = 1 TO VisObjects + + SpriteX = Img(Obj(X).Shape).xWidth + SpriteY = Img(Obj(X).Shape).yWidth + + ' Move Sprite + +REDOX: + NewX = Obj(X).Xpos + Obj(X).XDir + IF NewX < 0 OR NewX + SpriteX > ScreenX THEN + Obj(X).XDir = -Obj(X).XDir + IF RANDOM.INT(20) = 1 THEN + DO + Obj(X).XDir = RANDOM.INT(7) - 3 + Obj(X).YDir = RANDOM.INT(7) - 3 + LOOP WHILE (Obj(X).XDir = 0 AND Obj(X).YDir = 0) + GOTO REDOX + END IF + END IF + Obj(X).Xpos = Obj(X).Xpos + Obj(X).XDir + +REDOY: + NewY = Obj(X).Ypos + Obj(X).YDir + IF NewY < 0 OR NewY + SpriteY > ScreenY THEN + Obj(X).YDir = -Obj(X).YDir + IF RANDOM.INT(20) = 1 THEN + DO + Obj(X).XDir = RANDOM.INT(7) - 3 + Obj(X).YDir = RANDOM.INT(7) - 3 + LOOP WHILE (Obj(X).XDir = 0 AND Obj(X).YDir = 0) + GOTO REDOY + END IF + END IF + Obj(X).Ypos = Obj(X).Ypos + Obj(X).YDir + + 'Draw Sprite + + TDRAW.BITMAP Img(Obj(X).Shape), Obj(X).Xpos, Obj(X).Ypos, SpriteX, SpriteY + + LastX(X, CurrentPage) = Obj(X).Xpos + LastY(X, CurrentPage) = Obj(X).Ypos + + NEXT X + + LastObjects(CurrentPage) = VisObjects + + ' Pan Screen Back & Forth + + ViewCnt = ViewCnt + 1 + IF ViewCnt >= ViewMax THEN + ViewX = ViewX + ViewXD + IF ViewX = 0 OR ViewX = 39 THEN ViewXD = -ViewXD + IF ViewXD < 0 THEN + ViewY = ViewY + ViewYD + IF ViewY = 0 OR ViewY = 39 THEN ViewYD = -ViewYD + END IF + + SET.WINDOW CurrentPage, ViewX, ViewY + + ViewCnt = 0 + ELSE + SET.DISPLAY.PAGE CurrentPage + END IF + + ' Cycle Colors + + SET.DAC.REGISTER 50 + PrevColor, 3 + PrevColor, 0, 60 - PrevColor + SET.DAC.REGISTER 50 + SetColor, SetColor, 10, 63 - SetColor + + SET.DAC.REGISTER 150 + PrevColor, 3 + PrevColor, 0, 60 - PrevColor + SET.DAC.REGISTER 150 + SetColor, 63, 63, SetColor + + SetColor = SetColor + SDir + IF SetColor = 60 OR SetColor = 0 THEN SDir = -SDir + + PrevColor = PrevColor + PDir + IF PrevColor = 60 OR PrevColor = 0 THEN PDir = -PDir + + CurrentPage = 1 - CurrentPage + + Code = SCAN.KEYBOARD + + IF Code = False THEN GOTO DRAW.LOOP + + IF Code = KyPlus THEN + IF ViewMax < 12 THEN ViewMax = ViewMax + 1 + GOTO DRAW.LOOP + END IF + + IF Code = KyMinus THEN + IF ViewMax > 1 THEN ViewMax = ViewMax - 1 + IF ViewCnt >= ViewMax THEN ViewCnt = 0 + GOTO DRAW.LOOP + END IF + + IF Code = KyUp THEN + IF VisObjects < MaxSprites THEN VisObjects = VisObjects + 1 + GOTO DRAW.LOOP + END IF + + IF Code = KyDown THEN + IF VisObjects > 1 THEN VisObjects = VisObjects - 1 + GOTO DRAW.LOOP + END IF + + +END SUB + +SUB PRINT.TEXT (Text$, Xpos, Ypos, ColorF, ColorB) + + IF LEN(Text$) = 0 THEN EXIT SUB + PRINT.STR VARSEG(Text$), SADD(Text$), LEN(Text$), Xpos, Ypos, ColorF, ColorB + + +END SUB + +SUB TPRINT.TEXT (Text$, Xpos, Ypos, ColorF) + + IF LEN(Text$) = 0 THEN EXIT SUB + + TPRINT.STR VARSEG(Text$), SADD(Text$), LEN(Text$), Xpos, Ypos, ColorF + +END SUB + diff --git a/modex105/DEMOS/QB45/TEST6A.EXE b/modex105/DEMOS/QB45/TEST6A.EXE new file mode 100755 index 0000000000000000000000000000000000000000..e61d038796159e3fdb7d12c95525a9dafb87995f GIT binary patch literal 40544 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 + push R1 ; Save R1 + PUSHx R2, R3, R4, R5, R6, R7, R8 + ENDIF +ENDM + +POPx MACRO R1, R2, R3, R4, R5, R6, R7, R8 + IFNB + pop R1 ; Restore R1 + POPx R2, R3, R4, R5, R6, R7, R8 + ENDIF +ENDM + + ; Macro to Clear a Register to 0 + +CLR MACRO Register + xor Register, Register ; Set Register = 0 +ENDM + + ; Macros to Decrement Counter & Jump on Condition + +LOOPx MACRO Register, Destination + dec Register ; Counter-- + jnz Destination ; Jump if not 0 +ENDM + +LOOPjz MACRO Register, Destination + dec Register ; Counter-- + jz Destination ; Jump if 0 +ENDM + + + ; ==== General Constants ==== + + False EQU 0 + True EQU -1 + nil EQU 0 + + b EQU BYTE PTR + w EQU WORD PTR + d EQU DWORD PTR + o EQU OFFSET + f EQU FAR PTR + s EQU SHORT + ?x4 EQU + ?x3 EQU + + +IFDEF FARSTRINGS + + EXTRN stringaddress:far + EXTRN stringlength:far + +ENDIF + + + .Data + + EVEN + +RND_Seed DW 7397, 29447, 802 +RND_Mult DW 179, 183, 182 +RND_ModV DW 32771, 32779, 32783 + +CR_LF DB 13, 10 ; the CRLF data + + .Code + +;================= +;DOS_PRINT (Text$) +;================= +; +; Prints Text Directly to DOS console w/ CR/LF +; + + PUBLIC DOS_PRINT + +DP_Stack STRUC + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + DP_Text DW ? ; Address of Text$ Descriptor +DP_Stack ENDS + + +DOS_PRINT PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + mov BP, SP ; Set up Stack Frame + + mov SI, [BP].DP_Text ; Get Addr of Text$ descriptor + +IFDEF FARSTRINGS + push SI ; Push Addr of BC7 Decriptor Ptr + call stringaddress ; Get Address + Len of string!!! + ; DX:AX = Addr CX = Len + mov DS, DX ; DS = DX = Segment of string + mov DX, AX ; DX = AX = Offset of String +ELSE + mov CX, [SI] ; put its length into CX + mov DX, [SI+02] ; now DS:DX points to the String +ENDIF + + jcxz @No_Print ; Don't Print if empty + + mov BX, 1 ; 1= DOS Handle for Display + mov AH, 40h ; Write Text Function + int 21h ; Call DOS to do it + +@No_Print: + mov AX, SEG DGROUP ; Restore DGroup + mov DS, AX + + mov DX, o CR_LF ; Get Addr of CR/LF pair + mov CX, 2 ; 2 Characters to Write + mov BX, 1 ; 1= DOS Handle for Display + + mov AH, 40h ; Write Text Function + int 21h ; Call DOS to do it + + cld ; Reset Direction Flag + POPx DI, SI, DS, BP ; Restore Saved Registers + ret 2 ; Exit & Clean Up Stack + +DOS_PRINT ENDP + + +;================== +;DOS_PRINTS (Text$) +;================== +; +; Print Text$ Directly to DOS console +; without a trailing CR/LF +; + + PUBLIC DOS_PRINTS + +DOS_PRINTS PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + mov BP, SP ; Set up Stack Frame + + mov SI, [BP].DP_Text ; Get Addr of Text$ descriptor + +IFDEF FARSTRINGS + push SI ; Push Addr of BC7 Decriptor Ptr + call stringaddress ; Get Address + Len of string!!! + ; DX:AX = Addr CX = Len + mov DS, DX ; DS = DX = Segment of string + mov DX, AX ; DX = AX = Offset of String +ELSE + mov CX, [SI] ; put its length into CX + mov DX, [SI+02] ; now DS:DX points to the String +ENDIF + + jcxz @DPS_Exit ; Don't Print if empty + + mov BX, 1 ; 1= DOS Handle for Display + mov AH, 40h ; Write Text Function + int 21h ; Call DOS to do it + +@DPS_Exit: + cld ; Reset Direction Flag + POPx DI, SI, DS, BP ; Restore Saved Registers + ret 2 ; Exit & Clean Up Stack + +DOS_PRINTS ENDP + + +;====================== +;SET_VIDEO_MODE (Mode%) +;====================== +; +; Sets the Video Mode through the BIOS +; + + PUBLIC SET_VIDEO_MODE + +SVM_Stack STRUC + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + SVM_Mode DB ?,? ; Desired Video Mode +SVM_Stack ENDS + + +SET_VIDEO_MODE PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + mov BP, SP ; Set up Stack Frame + + CLR AH ; Function 0 + mov AL, [BP].SVM_Mode ; Get Mode # + + int 10H ; Change Video Modes + +@SVM_Exit: + POPx DI, SI, DS, BP ; Restore Saved Registers + ret 2 ; Exit & Clean Up Stack + +SET_VIDEO_MODE ENDP + + +;============== +;SCAN_KEYBOARD% +;============== +; +; Function to scan keyboard for a pressed key +; + + PUBLIC SCAN_KEYBOARD + +SCAN_KEYBOARD PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + + mov AH, 01H ; Function #1 + int 16H ; Call Keyboard Driver + jz @SK_NO_KEY ; Exit if Zero flag set + + mov AH, 00H ; Remove Key from Buffer + int 16H ; Get Keycode in AX + + or AL, AL ; Low Byte Set (Ascii?) + jz @SK_Exit ; if not, it's a F-Key + + CLR AH ; Clear ScanCode if Ascii + jmp s @SK_Exit ; Return Key in AX + +@SK_NO_KEY: + CLR AX ; Return Nil (no Keypress) + +@SK_Exit: + cld ; Reset Direction Flag + POPx DI, SI, DS, BP ; Restore Saved Registers + ret ; Exit & Clean Up Stack + +SCAN_KEYBOARD ENDP + + +;==================== +;RANDOM_INT (MaxInt%) +;==================== +; +; Returns a pseudo-random number in the range of (0.. MaxInt-1) +; + + + PUBLIC RANDOM_INT + +RI_Stack STRUC + DW ? ; BP + DD ? ; Caller + RI_MaxVal DW ? ; Maximum Value to Return + 1 +RI_Stack ENDS + + +RANDOM_INT PROC FAR + + push BP ; Preserve Important Registers + mov BP, SP ; Set up Stack Frame + + CLR BX ; BX is the data index + CLR CX ; CX is the accumulator + +REPT 3 + mov AX, RND_Seed[BX] ; load the initial seed + mul RND_Mult[BX] ; multiply it + div RND_ModV[BX] ; and obtain the Mod value + mov RND_Seed[BX], DX ; save that for the next time + + add CX, DX ; add it into the accumulator + inc BX + inc BX ; point to the next set of values +ENDM + + mov AX, CX ; AX = Random # + CLR DX ; DX = 0 + div [BP].RI_MaxVal ; DX = DX:AX / MAxVal Remainder + + mov AX, DX + + pop BP ; Restore BP + ret 2 ; back to BASIC with AX holding the result + +RANDOM_INT ENDP + + +;=========== +;INIT_RANDOM +;=========== +; +; Scrambles the psuedo-random number sequence +; (XOR's the seed value with the timer) +; + + PUBLIC INIT_RANDOM + +INIT_RANDOM PROC FAR + + clr AX ; Segment = 0000 + mov ES, AX + mov AX, ES:[046Ch] ; Get Timer Lo Word + + xor RND_Seed, AX ; Scramble 1st Seed + + ret ; Exit & Clean Up Stack + +INIT_RANDOM ENDP + + +;==================== +;INT_SQR (X%, Round%) +;==================== +; +; Returns the Integer Square Root of (X) +; Round allows the return value to be rounded to the +; nearest integer value by passing 0x80. Passing 0 +; return the Integer Portion only. The rounding amound is +; a number from 0 to 1 multiplied by 256, thus +; 0.5 * 0x100 = 0x80! +; + +ISQ_Stack STRUC + DW ?,? ; BP, DI + DD ? ; Caller + ISQ_Round DW ? ; Amount to Round Result * 256 + ISQ_X DW ? ; "X" +ISQ_Stack ENDS + + PUBLIC INT_SQR + +INT_SQR PROC FAR + + PUSHx BP, DI ; Save BP + mov BP, SP ; Set up Stack Frame + + xor AX, AX ; {xor eax,eax} + xor DX, DX ; {xor edx,edx} + mov DI, [BP].ISQ_X ; {mov edi,x} + + mov CX, 16 ; {mov cx, 32} + +@ISQ_L: + + shl DI, 1 ; {shl edi,1} + rcl DX, 1 ; {rcl edx,1} + shl DI, 1 ; {shl edi,1} + rcl DX, 1 ; {rcl edx,1} + shl AX, 1 ; {shl eax,1} + mov BX, AX ; {mov ebx,eax} + shl BX, 1 ; {shl ebx,1} + inc BX ; {inc ebx} + cmp DX, BX ; {cmp edx,ebx} + jl @ISQ_S + + sub DX, BX ; {sub edx,ebx} + inc AX ; {inc eax} + +@ISQ_S: + loop @ISQ_L + + add ax, [BP].ISQ_Round ; {add eax,$00008000} + ; {*round* result in hi word: ie. +0.5} + shr ax, 8 ; {shr eax,16} {to ax (result)} + + POPx DI, BP ; Restore Registers + ret 4 ; Exit + +INT_SQR ENDP + + +;============ +;TIMER_COUNT& +;============ +; +; Returns the current timer value as an integer/long integer +; + + + PUBLIC TIMER_COUNT + +TIMER_COUNT PROC FAR + + clr AX ; Segment = 0000 + mov ES, AX ; use ES to get at data + mov AX, ES:[046Ch] ; Get Timer Lo Word + mov DX, ES:[046Eh] ; Get Timer Hi Word + ret ; Exit & Return value in DX:AX + +TIMER_COUNT ENDP + + + END diff --git a/modex105/DEMOS/QB45/UTILS.BI b/modex105/DEMOS/QB45/UTILS.BI new file mode 100755 index 00000000..aeafeef4 --- /dev/null +++ b/modex105/DEMOS/QB45/UTILS.BI @@ -0,0 +1,51 @@ + + ' Misc Constants + +CONST True = -1, False = 0, nil = 0 + + ' Keyboard Codes: Extended + +CONST KyF1 = &H3B00, KyF2 = &H3C00, KyF3 = &H3D00, KyF4 = &H3E00, KyF5 = &H3F00 +CONST KyF6 = &H4000, KyF7 = &H4100, KyF8 = &H4200, KyF9 = &H4300, KyF10 = &H4400 + +CONST KyUp = &H4800, KyLeft = &H4B00, KyRight = &H4D00, KyDown = &H5000 +CONST KySLeft = &HCB00, KySRight = &HCD00, KySUp = &HC800, KySDown = &HD000 + +CONST KyHome = &H4700, KyPgUp = &H4900, KyEnd = &H4F00, KyPgDn = &H5100 +CONST KySHome = &HC700, KySPgUp = &HC900, KySEnd = &HCF00, KySPgDn = &HD100 + +CONST KyIns = &H5200, KyDel = &H5300, KyRvsTab = &H8F00 +CONST KySIns = &HC200, KySDel = &HC300 + +CONST KyAltA = &H1E00, KyAltB = &H3000, KyAltC = &H2E00, KyAltD = &H2000 +CONST KyAltE = &H1200, KyAltF = &H2100, KyAltG = &H2200, KyAltH = &H2300 +CONST KyAltI = &H1700, KyAltJ = &H2400, KyAltK = &H2500, KyAltL = &H2600 +CONST KyAltM = &H3200, KyAltN = &H3100, KyAltO = &H1800, KyAltP = &H1900 +CONST KyAltQ = &H1000, KyAltR = &H1300, KyAltS = &H1F00, KyAltT = &H1400 +CONST KyAltU = &H1600, KyAltV = &H2F00, KyAltW = &H1100, KyAltX = &H2D00 +CONST KyAltY = &H1500, KyAltZ = &H2C00 + + ' Keyboard Codes: Ascii + +CONST KyBS = 8, KyTab = 9, KyCR = 13, KyESC = &H1B, KyClr = &H7F +CONST KyPlus = 45, KyMinus = 43 + + ' Color Constants + +CONST c.BLACK = 0, c.BLUE = 1, c.GREEN = 2, c.CYAN = 3 +CONST c.RED = 4, c.PURPLE = 5, c.BROWN = 6, c.WHITE = 7 +CONST c.GREY = 8, c.bBLUE = 9, c.bGREEN = 10, c.bCYAN = 11 +CONST c.bRED = 12, c.bPURPLE = 13, c.YELLOW = 14, c.bWHITE = 15 +CONST c.BRIGHT = 8 + + ' From UTILS.ASM + +DECLARE SUB DOS.PRINT ALIAS "DOS_PRINT" (Text$) +DECLARE SUB DOS.PRINTS ALIAS "DOS_PRINTS" (Text$) +DECLARE SUB SET.VIDEO.MODE ALIAS "SET_VIDEO_MODE" (BYVAL Mode%) +DECLARE FUNCTION SCAN.KEYBOARD% ALIAS "SCAN_KEYBOARD" +DECLARE FUNCTION RANDOM.INT ALIAS "RANDOM_INT" (BYVAL MaxInt%) +DECLARE SUB INIT.RANDOM ALIAS "INIT_RANDOM" +DECLARE FUNCTION TIMER.COUNT& ALIAS "TIMER_COUNT" +DECLARE FUNCTION INT.SQR ALIAS "INT_SQR" (BYVAL X%, BYVAL Round%) + diff --git a/modex105/DEMOS/ROM_8X8.FNT b/modex105/DEMOS/ROM_8X8.FNT new file mode 100755 index 0000000000000000000000000000000000000000..708a4f924815cedc3c5e179b42a7cc9d9c492625 GIT binary patch literal 1024 zcmYLIv1%hR5FJwF8j-jZ*rdt_40l(kSRBY_q!RI=OQFJ*5!~J;?(YN6A8>zgZD7H$ zl}MQ;#%yJGAywF7(cT+v;KsRcaNHt(pu$~WyMl)8=J9c}9nL?U|BNw)!Z}yyN?(1sVqU(r_lx_* z;`^cA+!V)SakF`7yVSK0baFON^RpA6R$5nu#x>{2(f!NIkLh$u^M&!=#30pHRh8oq zHspA%Nkd?xER_@#u}zjGx~}U4b?F9o35^S=RtnGI*k$1mn2E`IpKb3GexA6jgX#d( zM9r=tTkt=Yl`K+UPLB$(m&}q! zOTT5a-|y={41hjH>YXc{>oHF7hIw`#bMPId+q-agn?`Nl^7)gS6K0HSk-#1#(1k*o zsOR8(@a`b&LwagnQ+jS1Y7X!FCurlFrtv1{v%o|8xC^sv%e@fWVQK3c#zL2$y)^hxh42f59a)vzx6a@ZkcEDUF0PO NT(pwbM1+VY(Lc=|finOA literal 0 HcmV?d00001 diff --git a/modex105/DEMOS/SPACEAGE.FNT b/modex105/DEMOS/SPACEAGE.FNT new file mode 100755 index 0000000000000000000000000000000000000000..029bae4effb181a29f61b17c588cd848bc04e82a GIT binary patch literal 1024 zcmYLHJ!|7Y5SbOEUxqr5pFqLs>t~iV;RY!1j zs1T!jZ)J0X<+nTE^JYaBxj=YYL5MSiNTH5);!SAhnReQ4LLJYv6KTJliL?`M0<-sY zlBTX3z-|rM$^0|Re0Tq_dU}1IKT>}gC!1%R#!H_*=J(g9)x-T=89OV>Qc7Q0<$+~s zT?B^K+E5~)uWJ_Y^?HqizX~+}2c+5i7$wSfTjM6OHRDgBC36-M>lO%e$Zv$loio%!zj=P}rObnQjD6ug80Nsfe_-MHKA77r{sZ_H5+LHR z+TvT2QYL`;Fy2UiFu>AN=7IUiA5jg2X2e2eWB~Ob0QN8~c`@PQf5`LWp@9I#X99p= h7cUiYAi$&vVG}~bcO~qr4Z(-d%J<)JrH}|U`3JE0#Z>?R literal 0 HcmV?d00001 diff --git a/modex105/DEMOS/SYSTEM.FNT b/modex105/DEMOS/SYSTEM.FNT new file mode 100755 index 0000000000000000000000000000000000000000..9a1965ebfe5bd67bea71c313ef7ef2f78aa6fbb7 GIT binary patch literal 1024 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 Ix?}Ob0HE06asU7T literal 0 HcmV?d00001 diff --git a/modex105/FONTEDIT/CHARSETS.CS b/modex105/FONTEDIT/CHARSETS.CS new file mode 100755 index 0000000000000000000000000000000000000000..97fe608ff81e4b78105a52984687b21a8a166ff7 GIT binary patch literal 2144 zcmZuxJ!l+96n;6MS4UXnt-=-}41|Eng%sK1kQZ45l?FE{#|SwoHp$@#voXtha@dX{ zQYfmTCagt@!<7qF0>fazkYW)cg-{{Pq!F&fRpE+dlJCu4ehhhcyPv){@Bht-sFQSU z?`Ur=rF3mP9s`G~&wl*=$?EXx>WcWGqImeQw?g*Kn|B$j<8L9yRdu}o^5y<<<={3R zJ91BVemh_^&-b1i&-V^qIlB4L%_JF3?W2uH8ylZp>f!4Cv;EZ}DLZpBtEjiQ;#L-W z6c4L7u75%mXAG#CsbTFza*OjM2!4p3-Wj(2t#=j~I%LJ8fw*LYP9bC>57Rj1{9ZA7Mt z6KS4Onx_vgng8 z5ChN7E_A{E#u-DcG5iJBS|ct9q$P*ET4SFcHJ_hw-p*IT;K*e|=JU&VGnr1~~8H#+(P@Jm%Ot$eFuBJd>w6;$lt( zZ_xR{Vt$gPB+U&6Ju11bu*7}I%|Ck}`V-5W(=w<`MO-ZROI!i+W5yLm>$%e<4W%p) z7`$kjGQ>J~AGk9ZCWI)&)d=%cYd-+8T!t)QpYs5Kexkqot2Z4Fs z`R41b%`e0!UwyeH{!fS&ZY*{lZ@m|a$Iauzf)uuYju0V`tBS9351aLIx zKoYE`puPCvqHV#G^>wrnw9VmR)6&9HxV5`0DPT3nNkt;8{TC0{5AmfyPur=6UpQ=s zxYo!wnILk%u4}5ni%GGJ-#Ow4ghL)og!7>;E{stKtVwWvI2Xw*@hE4KQLFRa)0hVH z{}+J}BY08(?-3mP)Jab%MT$7DR|L0b_`tuYN6S;-uczYEpKvZ!rX1M!Z2JyuC%E7IvA=GDa(`>QD+soN|ACXgvqk-8t6F z`g+b`2YrteKAa~bA0JsVwj+xV5RCQLr_l%|@VE1Ph*N`Wpk_V#vYyv-u06L(E9KAY zn$Pj}eir>RM^&{1s#<$J!mwX3n@lF#&_nC>86X2k^*$bR_5;^75OWrqIpVS%j_3Kj zU$BjM*bnsZ?9kg;v}e&js#Nd~5GEQhM;P#a24hIyzo+2B|8*@NyU3c zKhoDl(t4iF`zI7CEZbYo-&2HyY)=?K#(E3@obyW5K^Oq{Pa2IdqU?;31F1O*Ao42S zpH^MH76I4}Zg!5~0c~&l{;AHgn)NV{g3yP^a9{fpdzW07LSf8BB*6Q(VGO|>EaR=u c@7}+=v9)<$+}Yd`AAF4Uy(KWY!FGSkU-|#d0ssI2 literal 0 HcmV?d00001 diff --git a/modex105/FONTEDIT/CSEDIT.DOC b/modex105/FONTEDIT/CSEDIT.DOC new file mode 100755 index 00000000..97ff07e0 --- /dev/null +++ b/modex105/FONTEDIT/CSEDIT.DOC @@ -0,0 +1,196 @@ + +CSEDIT - A Simple Font Editor by Matt Pritchard + + +CSEDIT is distributed with MODEXnnn.ZIP, the general purpose MODE X +Library for VGA Graphics. + +WHAT YOU NEED TO RUN CSEDIT: + + * A Vga Monitor + * A Microsoft Compatible Mouse + + A Mouse is most definitely required, as the keyboard is used for + nothing except entering file names. + +FILES NEEDED IN THE CURRENT DIRECTORY: + + CSEDIT.EXE - The Font Editor Program + CHARSETS.CS - The Font Editor's Internal Fonts + PALETTE.CS - The Font Editor's Palette + MOUSEIMG.CS - The Font Editor's Mouse Pointer + +SAMPLE FONT FILE THAT SHOULD BE INCLUDED: + + SYSTEM.FNT - The Font used by CSEDIT.EXE + INVERSE.FNT - An Inverted version of SYSTEM.FNT + SPACEAGE.FNT - A Futuristic, Tech style font + ROM_8X8.FNT - The Lower 128 characters from the VGA BIOS Rom + +WHAT IT EDITS: + + 8 by 8 character fonts, 128 characters at a time. 2 fonts at a time. + +HOW IT WORKS/FEATURES: + + CSEDIT allows the user to edit 2 different font groups at a time, + which may be loaded and saved separately. + + A enlarged character grid allows the user to edit individual pixels + on a selected character. + + The Following operations can be performed on a single character or + simultaneously on a selected block of characters. + + * Shift the selected character(s) in any direction + with or without clipping at the edges. + * Vertically Flip the selected character(s) + * Horizontally Flip the selected character(s) + * Rotate the selected character(s) 90 Degrees Clockwise + * Rotate the selected character(s) 90 Degrees Counterclockwise + * Clear the selected character(s) + * Invert the selected character(s) + * XOR the selected character(s) with other character(s) + * AND the selected character(s) with other character(s) + * OR the selected character(s) with other character(s) + * Copy the selected character(s) to another position or font. + + An UNDO feature allows the reversal of the most recent operation. + +DESCRIPTION OF OBJECTS/FEATURES FROM THE TOP DOWN: + + Character Grid: (RED) Box in Upper Left corner of screen. This is + where you edit an individual character. The Left Button sets the + pixel the mouse pointer is on, while the Right Button clears that + pixel. + + Scroll Buttons: The Four Scroll Buttons are labeled with directional + arrows, and arranged in a diamond pattern. Left Clicking on a + directional button will scroll the currently selected character + in that direction, with the pixels on the edge rolling off and + appearing on the other size. Right Clicking will prevent the + pixels from rolling to the other side. + + Vertical Flip Button: + Horizontal Flip Button: Clicking these buttons will flip the pattern + of the currently selected character(s) around the indicated axis. + i.e. the top row will be swapped with the bottom row, etc. or the + left row column will be swapped with right column, etc. + depending upon which button you push. + + Invert Button: Clicking this button causes all pixels in the selected + character(s) to flip flop between on and off. + + Clear Button: Clicking this button erases the selected characters + + Rotate Buttons: Clicking these buttons will rotate the pattern in the + selected character(s) 90 degrees in the indicated direction. + + XOR Button: Clicking this button will let you XOR the currently + selected character(s) with other character(s) in either font. + The Button will turn RED, indicating that it is waiting for + you to click on the desired character (or upper left corner + of the desired block of characters) in either the Red or Green + Character Set Displays. Clicking anywhere else will abort this + process without doing anything. If you click on (any of) the + selected character(s) the operation is aborted. If a block is + selected and the character you click on is in a position where + it can't represent the upper left corner of a block of the same + size, then the operation is not performed. + + AND Button & OR Button: These buttons work just like the XOR Button + except that the Binary operation performed is either an AND or OR + depending upon which button you have selected. + + COPY Button: This button lets you copy a character or selected block + of characters to another area in the current font or the other + font. After clicking, the button turns RED and works much like + the XOR Button. Clicking on a valid position in either font + window will copy the selected character(s) to that location. + + MODE Button: Clicking this button toggles the editor between BLOCK + mode and CHARACTER mode. The current mode is displayed on a plate + at the top of the screen, just to the right of the enlarged + character grid. In character mode the plate will read "CHAR" and + the currently selected character is displayed just to the right + of the plate. In Block mode the plate will read "BLOCK" and the + enlarged character grid is disabled. + + UNDO Button: Clicking this Button will UNDO or reverse the effects of + the most recent operation. + + QUIT Button: Clicking this button will return you to DOS. Any loaded + fonts are not saved, and no confirmation is given. + + + GREEN FONT AREA: This area displays one of the current fonts which + can be edited. The characters are display in order from #0 to #127 + from the upper left, going right, then down. The Font Box is 32 + characters wide and 4 characters Tall. When the editor is in + character mode, just point at and Left Click on the character you + wish to edit and a Cyan box will appear around that character. + + * If you Right Click on a character, the last current character, + which will still appear in the enlarged character grid, will be + copied onto the character you pointed at, replacing it. This is + a shortcut for copying characters: You can hold the right button + down an fill in a large area with a single character pattern. + When the editor is in Block Mode, you select an area by clicking + on any corner of the desired block. Then drag the mouse to the + opposite corner while holding down the left button. A Cyan Box + will stretch to surround the selected block of characters. + + GREEN FONT FILE NAME BOX: This Text Box is used to enter the name + of a font file to load or the name to save the current Green font + as. Just click on the Box, and it will change color and a + flashing cursor will appear. Now you type in a filename or edit + the existing filename. Press or click outside the text + box to end editing. + + GREEN FONT LOAD BUTTON: Clicking this button will load the font file + that is named in the Green File name box. If no name is given or + no such file exists, then nothing will be loaded. + + GREEN FONT SAVE BUTTON: Clicking this button will save the current + font in the Green Font Area under the name given in the File Name + Box. If a Valid name is not provided, nothing will be saved. + + RED FONT AREA: This is just the same as the GREEN FONT AREA; providing + you with the ability to copy and edit between multiple fonts. + + RED FONT FILE NAME BOX: This works just like the GREEN FONT FILE + NAME BOX. + + RED FONT LOAD BUTTON: This works just like the GREEN FONT LOAD BUTTON. + + RED FONT SAVE BUTTON: This works just like the GREEN FONT SAVE BUTTON. + + Message Bar: At the very bottom of the screen, this Bar will display + information and messages for various functions. + + +FONT FILE FORMAT: + + BINARY Image, in order of character. The format is identical to that + used by the VGA ROM. The Files will be exactly 1024 (128 * 8) bytes + long. + + CHARACTER: 8 Bytes + + FONT: Array (0 to 127) of CHARACTER + + +COMMENTS, QUESTIONS, BUG REPORTS, etc: + + Send the to the Author: Matt Pritchard + + Through the 80xxx Fidonet Echo or + + Matt Pritchard + P.O. Box 140264 + Irving, TX 75014 + +CREDITS: + + This Font Editor was written in QuickBASIC 4.5 + diff --git a/modex105/FONTEDIT/CSEDIT.EXE b/modex105/FONTEDIT/CSEDIT.EXE new file mode 100755 index 0000000000000000000000000000000000000000..58b073253ada69b3f089a2f3b359049195178db6 GIT binary patch literal 68368 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 literal 0 HcmV?d00001 diff --git a/modex105/FONTEDIT/PALETTE.CS b/modex105/FONTEDIT/PALETTE.CS new file mode 100755 index 0000000000000000000000000000000000000000..09c5549e4967dcb520ae894bd5116c0b530b4304 GIT binary patch literal 768 zcmZva!HUBm5J2Y=2%RO`noI*WHGwcK1qscTHe1-8|NpDio)pxF%6pnq0K&M13AbBZ z!q&xO7l(w|iNxizg=4r~RaITrh}bmEe!p+qw(tAX=`@bxJkQtb)qB6L>$Yv5`=UV{ z$O7SUU=eTvI(#S%i7f}`l?a`0a6UY=gwUL^7s{}g|hJS4Up z0C)#%fCpfX3=D>0$9YI>IT*u7!-L_;Fh?df1Dhk~A+hCPEuSq8G#Fm3nq!k?$b&4p`kvfpll!wHY YgKmDmA2t3h8~nUSZ1J&%-%)-20~L%`_y7O^ literal 0 HcmV?d00001 diff --git a/modex105/FONTEDIT/ROM_8X8.FNT b/modex105/FONTEDIT/ROM_8X8.FNT new file mode 100755 index 0000000000000000000000000000000000000000..708a4f924815cedc3c5e179b42a7cc9d9c492625 GIT binary patch literal 1024 zcmYLIv1%hR5FJwF8j-jZ*rdt_40l(kSRBY_q!RI=OQFJ*5!~J;?(YN6A8>zgZD7H$ zl}MQ;#%yJGAywF7(cT+v;KsRcaNHt(pu$~WyMl)8=J9c}9nL?U|BNw)!Z}yyN?(1sVqU(r_lx_* z;`^cA+!V)SakF`7yVSK0baFON^RpA6R$5nu#x>{2(f!NIkLh$u^M&!=#30pHRh8oq zHspA%Nkd?xER_@#u}zjGx~}U4b?F9o35^S=RtnGI*k$1mn2E`IpKb3GexA6jgX#d( zM9r=tTkt=Yl`K+UPLB$(m&}q! zOTT5a-|y={41hjH>YXc{>oHF7hIw`#bMPId+q-agn?`Nl^7)gS6K0HSk-#1#(1k*o zsOR8(@a`b&LwagnQ+jS1Y7X!FCurlFrtv1{v%o|8xC^sv%e@fWVQK3c#zL2$y)^hxh42f59a)vzx6a@ZkcEDUF0PO NT(pwbM1+VY(Lc=|finOA literal 0 HcmV?d00001 diff --git a/modex105/FONTEDIT/SPACEAGE.FNT b/modex105/FONTEDIT/SPACEAGE.FNT new file mode 100755 index 0000000000000000000000000000000000000000..029bae4effb181a29f61b17c588cd848bc04e82a GIT binary patch literal 1024 zcmYLHJ!|7Y5SbOEUxqr5pFqLs>t~iV;RY!1j zs1T!jZ)J0X<+nTE^JYaBxj=YYL5MSiNTH5);!SAhnReQ4LLJYv6KTJliL?`M0<-sY zlBTX3z-|rM$^0|Re0Tq_dU}1IKT>}gC!1%R#!H_*=J(g9)x-T=89OV>Qc7Q0<$+~s zT?B^K+E5~)uWJ_Y^?HqizX~+}2c+5i7$wSfTjM6OHRDgBC36-M>lO%e$Zv$loio%!zj=P}rObnQjD6ug80Nsfe_-MHKA77r{sZ_H5+LHR z+TvT2QYL`;Fy2UiFu>AN=7IUiA5jg2X2e2eWB~Ob0QN8~c`@PQf5`LWp@9I#X99p= h7cUiYAi$&vVG}~bcO~qr4Z(-d%J<)JrH}|U`3JE0#Z>?R literal 0 HcmV?d00001 diff --git a/modex105/FONTEDIT/SYSTEM.FNT b/modex105/FONTEDIT/SYSTEM.FNT new file mode 100755 index 0000000000000000000000000000000000000000..9a1965ebfe5bd67bea71c313ef7ef2f78aa6fbb7 GIT binary patch literal 1024 zcmYjPv1%JZ5PcEm5aBRag)K}-6L5KuW{X1(ia+GQkTh4gGKX2#%0V3BF6z_-EK>Os z1ct?etqu`V2p7gEjd3Mh1sBIAZ&oH`(CIO|J8x#*46rf1Kc0>EHS~VdG{ji+(@pq+oD&8?kJE-xo@8yMC!0paxQ=Ty*Nj4yoj$J48Ty*GP# ze)zCDIXF2u_;BZn;pBWWEHGL zCiv7oaxvt1|Gte!6`zNyLS}!M7hMM5s=V~4(3L*AX^)VmD!xk<<5UWMJj#Je2VL=s zU8v0yki6tchyn7)OzbN!-3Z3}rS}F(E1IP*7lD}UBs_21Hjo*ebB}3Tqb9b*;1e;4 zm!F_b61GF`|GrY#q`sJ*6^O$l;b#YmxLn@2TH<~19-a5{qwky}FCAKkz`V|Jo)Mkg zFC_0K%!AFw`d_kGsnfUD57(^+S!f;nI4wE_}=<@sf-WqJO)v(U&;#n}qip zUL|>GA!5b3(^-Z!`Fy1-@=~W-CK#fy)UOmXaMEzngOR#Y!+ojzzc~PZIJ_sU8jcVcNPyf^0GVeOHxtq!b&Xha#_W6, ; If DX not setup + MOV DX, Register ; then Select Register + ENDIF + IFDIFI , ; If AX not setup + MOV AX, Value ; then Get Data Value + ENDIF + OUT DX, AX ; Set I/O Register(s) +ENDM + + ; Macro to OUT a 8 bit value to an I/O Port + +OUT_8 MACRO Register, Value + IFDIFI , ; If DX not setup + MOV DX, Register ; then Select Register + ENDIF + IFDIFI , ; If AL not Setup + MOV AL, Value ; then Get Data Value + ENDIF + OUT DX, AL ; Set I/O Register +ENDM + + ; macros to PUSH and POP multiple registers + +PUSHx MACRO R1, R2, R3, R4, R5, R6, R7, R8 + IFNB + PUSH R1 ; Save R1 + PUSHx R2, R3, R4, R5, R6, R7, R8 + ENDIF +ENDM + +POPx MACRO R1, R2, R3, R4, R5, R6, R7, R8 + IFNB + POP R1 ; Restore R1 + POPx R2, R3, R4, R5, R6, R7, R8 + ENDIF +ENDM + + ; Macro to Clear Registers to 0 + +CLR MACRO Register, R2, R3, R4, R5, R6 + IFNB + XOR Register, Register ; Set Register = 0 + CLR R2, R3, R4, R5, R6 + ENDIF +ENDM + + ; Macros to Decrement Counter & Jump on Condition + +LOOPx MACRO Register, Destination + DEC Register ; Counter-- + JNZ Destination ; Jump if not 0 +ENDM + +LOOPjz MACRO Register, Destination + DEC Register ; Counter-- + JZ Destination ; Jump if 0 +ENDM + + + ; ===== General Constants ===== + + False EQU 0 + True EQU -1 + nil EQU 0 + + b EQU BYTE PTR + w EQU WORD PTR + d EQU DWORD PTR + o EQU OFFSET + f EQU FAR PTR + s EQU SHORT + ?x4 EQU + ?x3 EQU + + ; ===== VGA Register Values ===== + + VGA_Segment EQU 0A000h ; Vga Memory Segment + + ATTRIB_Ctrl EQU 03C0h ; VGA Attribute Controller + GC_Index EQU 03CEh ; VGA Graphics Controller + SC_Index EQU 03C4h ; VGA Sequencer Controller + SC_Data EQU 03C5h ; VGA Sequencer Data Port + CRTC_Index EQU 03D4h ; VGA CRT Controller + CRTC_Data EQU 03D5h ; VGA CRT Controller Data + MISC_OUTPUT EQU 03C2h ; VGA Misc Register + INPUT_1 EQU 03DAh ; Input Status #1 Register + + DAC_WRITE_ADDR EQU 03C8h ; VGA DAC Write Addr Register + DAC_READ_ADDR EQU 03C7h ; VGA DAC Read Addr Register + PEL_DATA_REG EQU 03C9h ; VGA DAC/PEL data Register R/W + + PIXEL_PAN_REG EQU 033h ; Attrib Index: Pixel Pan Reg + MAP_MASK EQU 002h ; Sequ Index: Write Map Mask reg + READ_MAP EQU 004h ; GC Index: Read Map Register + START_DISP_HI EQU 00Ch ; CRTC Index: Display Start Hi + START_DISP_LO EQU 00Dh ; CRTC Index: Display Start Lo + + MAP_MASK_PLANE1 EQU 00102h ; Map Register + Plane 1 + MAP_MASK_PLANE2 EQU 01102h ; Map Register + Plane 1 + ALL_PLANES_ON EQU 00F02h ; Map Register + All Bit Planes + + CHAIN4_OFF EQU 00604h ; Chain 4 mode Off + ASYNC_RESET EQU 00100h ; (A)synchronous Reset + SEQU_RESTART EQU 00300h ; Sequencer Restart + + LATCHES_ON EQU 00008h ; Bit Mask + Data from Latches + LATCHES_OFF EQU 0FF08h ; Bit Mask + Data from CPU + + VERT_RETRACE EQU 08h ; INPUT_1: Vertical Retrace Bit + PLANE_BITS EQU 03h ; Bits 0-1 of Xpos = Plane # + ALL_PLANES EQU 0Fh ; All Bit Planes Selected + CHAR_BITS EQU 0Fh ; Bits 0-3 of Character Data + + GET_CHAR_PTR EQU 01130h ; VGA BIOS Func: Get Char Set + ROM_8x8_Lo EQU 03h ; ROM 8x8 Char Set Lo Pointer + ROM_8x8_Hi EQU 04h ; ROM 8x8 Char Set Hi Pointer + + ; Constants Specific for these routines + + NUM_MODES EQU 8 ; # of Mode X Variations + + ; Specific Mode Data Table format... + +Mode_Data_Table STRUC + M_MiscR DB ? ; Value of MISC_OUTPUT register + M_Pages DB ? ; Maximum Possible # of pages + M_XSize DW ? ; X Size Displayed on screen + M_YSize DW ? ; Y Size Displayed on screen + M_XMax DW ? ; Maximum Possible X Size + M_YMax DW ? ; Maximum Possible Y Size + M_CRTC DW ? ; Table of CRTC register values +Mode_Data_Table ENDS + + ; ===== DGROUP STORAGE NEEDED (42 BYTES) ===== + + .DATA? + +SCREEN_WIDTH DW 0 ; Width of a line in Bytes +SCREEN_HEIGHT DW 0 ; Vertical Height in Pixels + +LAST_PAGE DW 0 ; # of Display Pages +PAGE_ADDR DW 4 DUP (0) ; Offsets to start of each page + +PAGE_SIZE DW 0 ; Size of Page in Addr Bytes + +DISPLAY_PAGE DW 0 ; Page # currently displayed +ACTIVE_PAGE DW 0 ; Page # currently active + +CURRENT_PAGE DW 0 ; Offset of current Page +CURRENT_SEGMENT DW 0 ; Segment of VGA memory + +CURRENT_XOFFSET DW 0 ; Current Display X Offset +CURRENT_YOFFSET DW 0 ; Current Display Y Offset + +CURRENT_MOFFSET DW 0 ; Current Start Offset + +MAX_XOFFSET DW 0 ; Current Display X Offset +MAX_YOFFSET DW 0 ; Current Display Y Offset + +CHARSET_LOW DW 0, 0 ; Far Ptr to Char Set: 0-127 +CHARSET_HI DW 0, 0 ; Far Ptr to Char Set: 128-255 + + .CODE + + ; ===== DATA TABLES ===== + + ; Data Tables, Put in Code Segment for Easy Access + ; (Like when all the other Segment Registers are in + ; use!!) and reduced DGROUP requirements... + + ; Bit Mask Tables for Left/Right/Character Masks + +Left_Clip_Mask DB 0FH, 0EH, 0CH, 08H + +Right_Clip_Mask DB 01H, 03H, 07H, 0FH + + ; Bit Patterns for converting character fonts + +Char_Plane_Data DB 00H,08H,04H,0CH,02H,0AH,06H,0EH + DB 01H,09H,05H,0DH,03H,0BH,07H,0FH + + ; CRTC Register Values for Various Configurations + +MODE_Single_Line: ; CRTC Setup Data for 400/480 Line modes + DW 04009H ; Cell Height (1 Scan Line) + DW 00014H ; Dword Mode off + DW 0E317H ; turn on Byte Mode + DW nil ; End of CRTC Data for 400/480 Line Mode + +MODE_Double_Line: ; CRTC Setup Data for 200/240 Line modes + DW 04109H ; Cell Height (2 Scan Lines) + DW 00014H ; Dword Mode off + DW 0E317H ; turn on Byte Mode + DW nil ; End of CRTC Data for 200/240 Line Mode + +MODE_320_Wide: ; CRTC Setup Data for 320 Horz Pixels + DW 05F00H ; Horz total + DW 04F01H ; Horz Displayed + DW 05002H ; Start Horz Blanking + DW 08203H ; End Horz Blanking + DW 05404H ; Start H Sync + DW 08005H ; End H Sync + DW nil ; End of CRTC Data for 320 Horz pixels + +MODE_360_Wide: ; CRTC Setup Data for 360 Horz Pixels + DW 06B00H ; Horz total + DW 05901H ; Horz Displayed + DW 05A02H ; Start Horz Blanking + DW 08E03H ; End Horz Blanking + DW 05E04H ; Start H Sync + DW 08A05H ; End H Sync + DW nil ; End of CRTC Data for 360 Horz pixels + +MODE_200_Tall: +MODE_400_Tall: ; CRTC Setup Data for 200/400 Line modes + DW 0BF06H ; Vertical Total + DW 01F07H ; Overflow + DW 09C10H ; V Sync Start + DW 08E11H ; V Sync End/Prot Cr0 Cr7 + DW 08F12H ; Vertical Displayed + DW 09615H ; V Blank Start + DW 0B916H ; V Blank End + DW nil ; End of CRTC Data for 200/400 Lines + +MODE_240_Tall: +MODE_480_Tall: ; CRTC Setup Data for 240/480 Line modes + DW 00D06H ; Vertical Total + DW 03E07H ; Overflow + DW 0EA10H ; V Sync Start + DW 08C11H ; V Sync End/Prot Cr0 Cr7 + DW 0DF12H ; Vertical Displayed + DW 0E715H ; V Blank Start + DW 00616H ; V Blank End + DW nil ; End of CRTC Data for 240/480 Lines + + ; Table of Display Mode Tables + +MODE_TABLE: + DW o MODE_320x200, o MODE_320x400 + DW o MODE_360x200, o MODE_360x400 + DW o MODE_320x240, o MODE_320x480 + DW o MODE_360x240, o MODE_360x480 + + ; Table of Display Mode Components + +MODE_320x200: ; Data for 320 by 200 Pixels + + DB 063h ; 400 scan Lines & 25 Mhz Clock + DB 4 ; Maximum of 4 Pages + DW 320, 200 ; Displayed Pixels (X,Y) + DW 1302, 816 ; Max Possible X and Y Sizes + + DW o MODE_320_Wide, o MODE_200_Tall + DW o MODE_Double_Line, nil + +MODE_320x400: ; Data for 320 by 400 Pixels + + DB 063h ; 400 scan Lines & 25 Mhz Clock + DB 2 ; Maximum of 2 Pages + DW 320, 400 ; Displayed Pixels X,Y + DW 648, 816 ; Max Possible X and Y Sizes + + DW o MODE_320_Wide, o MODE_400_Tall + DW o MODE_Single_Line, nil + +MODE_360x240: ; Data for 360 by 240 Pixels + + DB 0E7h ; 480 scan Lines & 28 Mhz Clock + DB 3 ; Maximum of 3 Pages + DW 360, 240 ; Displayed Pixels X,Y + DW 1092, 728 ; Max Possible X and Y Sizes + + DW o MODE_360_Wide, o MODE_240_Tall + DW o MODE_Double_Line , nil + +MODE_360x480: ; Data for 360 by 480 Pixels + + DB 0E7h ; 480 scan Lines & 28 Mhz Clock + DB 1 ; Only 1 Page Possible + DW 360, 480 ; Displayed Pixels X,Y + DW 544, 728 ; Max Possible X and Y Sizes + + DW o MODE_360_Wide, o MODE_480_Tall + DW o MODE_Single_Line , nil + +MODE_320x240: ; Data for 320 by 240 Pixels + + DB 0E3h ; 480 scan Lines & 25 Mhz Clock + DB 3 ; Maximum of 3 Pages + DW 320, 240 ; Displayed Pixels X,Y + DW 1088, 818 ; Max Possible X and Y Sizes + + DW o MODE_320_Wide, o MODE_240_Tall + DW o MODE_Double_Line, nil + +MODE_320x480: ; Data for 320 by 480 Pixels + + DB 0E3h ; 480 scan Lines & 25 Mhz Clock + DB 1 ; Only 1 Page Possible + DW 320, 480 ; Displayed Pixels X,Y + DW 540, 818 ; Max Possible X and Y Sizes + + DW o MODE_320_WIDE, o MODE_480_Tall + DW o MODE_Single_Line, nil + +MODE_360x200: ; Data for 360 by 200 Pixels + + DB 067h ; 400 scan Lines & 28 Mhz Clock + DB 3 ; Maximum of 3 Pages + DW 360, 200 ; Displayed Pixels (X,Y) + DW 1302, 728 ; Max Possible X and Y Sizes + + DW o MODE_360_Wide, MODE_200_Tall + DW o MODE_Double_Line, nil + +MODE_360x400: ; Data for 360 by 400 Pixels + + DB 067h ; 400 scan Lines & 28 Mhz Clock + DB 1 ; Maximum of 1 Pages + DW 360, 400 ; Displayed Pixels X,Y + DW 648, 816 ; Max Possible X and Y Sizes + + DW o MODE_360_Wide, MODE_400_Tall + DW o MODE_Single_Line, nil + + + ; ===== MODE X SETUP ROUTINES ===== + +;====================================================== +;SET_VGA_MODEX% (ModeType%, MaxXPos%, MaxYpos%, Pages%) +;====================================================== +; +; Sets Up the specified version of Mode X. Allows for +; the setup of multiple video pages, and a virtual +; screen which can be larger than the displayed screen +; (which can then be scrolled a pixel at a time) +; +; ENTRY: ModeType = Desired Screen Resolution (0-7) +; +; 0 = 320 x 200, 4 Pages max, 1.2:1 Aspect Ratio +; 1 = 320 x 400, 2 Pages max, 2.4:1 Aspect Ratio +; 2 = 360 x 200, 3 Pages max, 1.35:1 Aspect Ratio +; 3 = 360 x 400, 1 Page max, 2.7:1 Aspect Ratio +; 4 = 320 x 240, 3 Pages max, 1:1 Aspect Ratio +; 5 = 320 x 480, 1 Page max, 2:1 Aspect Ratio +; 6 = 360 x 240, 3 Pages max, 1.125:1 Aspect Ratio +; 7 = 360 x 480, 1 Page max, 2.25:1 Aspect Ratio +; +; MaxXpos = The Desired Virtual Screen Width +; MaxYpos = The Desired Virtual Screen Height +; Pages = The Desired # of Video Pages +; +; EXIT: AX = Success Flag: 0 = Failure / -1= Success +; + +SVM_STACK STRUC + SVM_Table DW ? ; Offset of Mode Info Table + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + SVM_Pages DW ? ; # of Screen Pages desired + SVM_Ysize DW ? ; Vertical Screen Size Desired + SVM_Xsize DW ? ; Horizontal Screen Size Desired + SVM_Mode DW ? ; Display Resolution Desired +SVM_STACK ENDS + + PUBLIC SET_VGA_MODEX + +SET_VGA_MODEX PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + SUB SP, 2 ; Allocate workspace + MOV BP, SP ; Set up Stack Frame + + ; Check Legality of Mode Request.... + + MOV BX, [BP].SVM_Mode ; Get Requested Mode # + CMP BX, NUM_MODES ; Is it 0..7? + JAE @SVM_BadModeSetup ; If Not, Error out + + SHL BX, 1 ; Scale BX + MOV SI, w MODE_TABLE[BX] ; CS:SI -> Mode Info + MOV [BP].SVM_Table, SI ; Save ptr for later use + + ; Check # of Requested Display Pages + + MOV CX, [BP].SVM_Pages ; Get # of Requested Pages + CLR CH ; Set Hi Word = 0! + CMP CL, CS:[SI].M_Pages ; Check # Pages for mode + JA @SVM_BadModeSetup ; Report Error if too Many Pages + JCXZ @SVM_BadModeSetup ; Report Error if 0 Pages + + ; Check Validity of X Size + + AND [BP].SVM_XSize, 0FFF8h ; X size Mod 8 Must = 0 + + MOV AX, [BP].SVM_XSize ; Get Logical Screen Width + CMP AX, CS:[SI].M_XSize ; Check against Displayed X + JB @SVM_BadModeSetup ; Report Error if too small + CMP AX, CS:[SI].M_XMax ; Check against Max X + JA @SVM_BadModeSetup ; Report Error if too big + + ; Check Validity of Y Size + + MOV BX, [BP].SVM_YSize ; Get Logical Screen Height + CMP BX, CS:[SI].M_YSize ; Check against Displayed Y + JB @SVM_BadModeSetup ; Report Error if too small + CMP BX, CS:[SI].M_YMax ; Check against Max Y + JA @SVM_BadModeSetup ; Report Error if too big + + ; Enough memory to Fit it all? + + SHR AX, 2 ; # of Bytes:Line = XSize/4 + MUL CX ; AX = Bytes/Line * Pages + MUL BX ; DX:AX = Total VGA mem needed + JNO @SVM_Continue ; Exit if Total Size > 256K + + DEC DX ; Was it Exactly 256K??? + OR DX, AX ; (DX = 1, AX = 0000) + JZ @SVM_Continue ; if so, it's valid... + +@SVM_BadModeSetup: + + CLR AX ; Return Value = False + JMP @SVM_Exit ; Normal Exit + +@SVM_Continue: + + MOV AX, 13H ; Start with Mode 13H + INT 10H ; Let BIOS Set Mode + + OUT_16 SC_INDEX, CHAIN4_OFF ; Disable Chain 4 Mode + OUT_16 SC_INDEX, ASYNC_RESET ; (A)synchronous Reset + OUT_8 MISC_OUTPUT, CS:[SI].M_MiscR ; Set New Timing/Size + OUT_16 SC_INDEX, SEQU_RESTART ; Restart Sequencer ... + + OUT_8 CRTC_INDEX, 11H ; Select Vert Retrace End Register + INC DX ; Point to Data + IN AL, DX ; Get Value, Bit 7 = Protect + AND AL, 7FH ; Mask out Write Protect + OUT DX, AL ; And send it back + + MOV DX, CRTC_INDEX ; Vga Crtc Registers + ADD SI, M_CRTC ; SI -> CRTC Parameter Data + + ; Load Tables of CRTC Parameters from List of Tables + +@SVM_Setup_Table: + + MOV DI, CS:[SI] ; Get Pointer to CRTC Data Tbl + ADD SI, 2 ; Point to next Ptr Entry + OR DI, DI ; A nil Ptr means that we have + JZ @SVM_Set_Data ; finished CRTC programming + +@SVM_Setup_CRTC: + MOV AX, CS:[DI] ; Get CRTC Data from Table + ADD DI, 2 ; Advance Pointer + OR AX, AX ; At End of Data Table? + JZ @SVM_Setup_Table ; If so, Exit & get next Table + + OUT DX, AX ; Reprogram VGA CRTC reg + JMP s @SVM_Setup_CRTC ; Process Next Table Entry + + ; Initialize Page & Scroll info, DI = 0 + +@SVM_Set_Data: + MOV DISPLAY_PAGE, DI ; Display Page = 0 + MOV ACTIVE_PAGE, DI ; Active Page = 0 + MOV CURRENT_PAGE, DI ; Current Page (Offset) = 0 + MOV CURRENT_XOFFSET, DI ; Horz Scroll Index = 0 + MOV CURRENT_YOFFSET, DI ; Vert Scroll Index = 0 + MOV CURRENT_MOFFSET, DI ; Memory Scroll Index = 0 + + MOV AX, VGA_SEGMENT ; Segment for VGA memory + MOV CURRENT_SEGMENT, AX ; Save for Future LES's + + ; Set Logical Screen Width, X Scroll and Our Data + + MOV SI, [BP].SVM_Table ; Get Saved Ptr to Mode Info + MOV AX, [BP].SVM_Xsize ; Get Display Width + + MOV CX, AX ; CX = Logical Width + SUB CX, CS:[SI].M_XSize ; CX = Max X Scroll Value + MOV MAX_XOFFSET, CX ; Set Maximum X Scroll + + SHR AX, 2 ; Bytes = Pixels / 4 + MOV SCREEN_WIDTH, AX ; Save Width in Pixels + + SHR AX, 1 ; Offset Value = Bytes / 2 + MOV AH, 13h ; CRTC Offset Register Index + XCHG AL, AH ; Switch format for OUT + OUT DX, AX ; Set VGA CRTC Offset Reg + + ; Setup Data table, Y Scroll, Misc for Other Routines + + MOV AX, [BP].SVM_Ysize ; Get Logical Screen Height + + MOV CX, AX ; CX = Logical Height + SUB BX, CS:[SI].M_YSize ; CX = Max Y Scroll Value + MOV MAX_YOFFSET, CX ; Set Maximum Y Scroll + + MOV SCREEN_HEIGHT, AX ; Save Height in Pixels + MUL SCREEN_WIDTH ; AX = Page Size in Bytes, + MOV PAGE_SIZE, AX ; Save Page Size + + MOV CX, [BP].SVM_Pages ; Get # of Pages + MOV LAST_PAGE, CX ; Save # of Pages + + CLR BX ; Page # = 0 + MOV DX, BX ; Page 0 Offset = 0 + +@SVM_Set_Pages: + + MOV PAGE_ADDR[BX], DX ; Set Page #(BX) Offset + ADD BX, 2 ; Page#++ + ADD DX, AX ; Compute Addr of Next Page + LOOPx CX, @SVM_Set_Pages ; Loop until all Pages Set + + ; Clear VGA Memory + + OUT_16 SC_INDEX, ALL_PLANES_ON ; Select All Planes + LES DI, d CURRENT_PAGE ; -> Start of VGA memory + + CLR AX ; AX = 0 + CLD ; Block Xfer Forwards + MOV CX, 8000H ; 32K * 4 * 2 = 256K + REP STOSW ; Clear dat memory! + + ; Setup Font Pointers + + MOV BH, ROM_8x8_Lo ; Ask for 8x8 Font, 0-127 + MOV AX, GET_CHAR_PTR ; Service to Get Pointer + INT 10h ; Call VGA BIOS + + MOV CHARSET_LOW, BP ; Save Char Set Offset + MOV CHARSET_LOW+2, ES ; Save Char Set Segment + + MOV BH, ROM_8x8_Hi ; Ask for 8x8 Font, 128-255 + MOV AX, GET_CHAR_PTR ; Service to Get Pointer + INT 10h ; Call VGA BIOS + + MOV CHARSET_HI, BP ; Save Char Set Offset + MOV CHARSET_HI+2, ES ; Save Char Set Segment + + MOV AX, True ; Return Success Code + +@SVM_EXIT: + ADD SP, 2 ; Deallocate workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 8 ; Exit & Clean Up Stack + +SET_VGA_MODEX ENDP + + +;================== +;SET_MODEX% (Mode%) +;================== +; +; Quickie Mode Set - Sets Up Mode X to Default Configuration +; +; ENTRY: ModeType = Desired Screen Resolution (0-7) +; (See SET_VGA_MODEX for list) +; +; EXIT: AX = Success Flag: 0 = Failure / -1= Success +; + +SM_STACK STRUC + DW ?,? ; BP, SI + DD ? ; Caller + SM_Mode DW ? ; Desired Screen Resolution +SM_STACK ENDS + + PUBLIC SET_MODEX + +SET_MODEX PROC FAR + + PUSHx BP, SI ; Preserve Important registers + MOV BP, SP ; Set up Stack Frame + + CLR AX ; Assume Failure + MOV BX, [BP].SM_Mode ; Get Desired Mode # + CMP BX, NUM_MODES ; Is it a Valid Mode #? + JAE @SMX_Exit ; If Not, don't Bother + + PUSH BX ; Push Mode Parameter + + SHL BX, 1 ; Scale BX to word Index + MOV SI, w MODE_TABLE[BX] ; CS:SI -> Mode Info + + PUSH CS:[SI].M_XSize ; Push Default X Size + PUSH CS:[SI].M_Ysize ; Push Default Y size + MOV AL, CS:[SI].M_Pages ; Get Default # of Pages + CLR AH ; Hi Byte = 0 + PUSH AX ; Push # Pages + + CALL f SET_VGA_MODEX ; Set up Mode X! + +@SMX_Exit: + POPx SI, BP ; Restore Registers + RET 2 ; Exit & Clean Up Stack + +SET_MODEX ENDP + + + ; ===== BASIC GRAPHICS PRIMITIVES ===== + +;============================ +;CLEAR_VGA_SCREEN (ColorNum%) +;============================ +; +; Clears the active display page +; +; ENTRY: ColorNum = Color Value to fill the page with +; +; EXIT: No meaningful values returned +; + +CVS_STACK STRUC + DW ?,? ; DI, BP + DD ? ; Caller + CVS_COLOR DB ?,? ; Color to Set Screen to +CVS_STACK ENDS + + PUBLIC CLEAR_VGA_SCREEN + +CLEAR_VGA_SCREEN PROC FAR + + PUSHx BP, DI ; Preserve Important Registers + MOV BP, SP ; Set up Stack Frame + + OUT_16 SC_INDEX, ALL_PLANES_ON ; Select All Planes + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + MOV AL, [BP].CVS_COLOR ; Get Color + MOV AH, AL ; Copy for Word Write + CLD ; Block fill Forwards + + MOV CX, PAGE_SIZE ; Get Size of Page + SHR CX, 1 ; Divide by 2 for Words + REP STOSW ; Block Fill VGA memory + + POPx DI, BP ; Restore Saved Registers + RET 2 ; Exit & Clean Up Stack + +CLEAR_VGA_SCREEN ENDP + + +;=================================== +;SET_POINT (Xpos%, Ypos%, ColorNum%) +;=================================== +; +; Plots a single Pixel on the active display page +; +; ENTRY: Xpos = X position to plot pixel at +; Ypos = Y position to plot pixel at +; ColorNum = Color to plot pixel with +; +; EXIT: No meaningful values returned +; + +SP_STACK STRUC + DW ?,? ; BP, DI + DD ? ; Caller + SETP_Color DB ?,? ; Color of Point to Plot + SETP_Ypos DW ? ; Y pos of Point to Plot + SETP_Xpos DW ? ; X pos of Point to Plot +SP_STACK ENDS + + PUBLIC SET_POINT + +SET_POINT PROC FAR + + PUSHx BP, DI ; Preserve Registers + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + MOV AX, [BP].SETP_Ypos ; Get Line # of Pixel + MUL SCREEN_WIDTH ; Get Offset to Start of Line + + MOV BX, [BP].SETP_Xpos ; Get Xpos + MOV CX, BX ; Copy to extract Plane # from + SHR BX, 2 ; X offset (Bytes) = Xpos/4 + ADD BX, AX ; Offset = Width*Ypos + Xpos/4 + + MOV AX, MAP_MASK_PLANE1 ; Map Mask & Plane Select Register + AND CL, PLANE_BITS ; Get Plane Bits + SHL AH, CL ; Get Plane Select Value + OUT_16 SC_Index, AX ; Select Plane + + MOV AL,[BP].SETP_Color ; Get Pixel Color + MOV ES:[DI+BX], AL ; Draw Pixel + + POPx DI, BP ; Restore Saved Registers + RET 6 ; Exit and Clean up Stack + +SET_POINT ENDP + + +;========================== +;READ_POINT% (Xpos%, Ypos%) +;========================== +; +; Read the color of a pixel from the Active Display Page +; +; ENTRY: Xpos = X position of pixel to read +; Ypos = Y position of pixel to read +; +; EXIT: AX = Color of Pixel at (Xpos, Ypos) +; + +RP_STACK STRUC + DW ?,? ; BP, DI + DD ? ; Caller + RP_Ypos DW ? ; Y pos of Point to Read + RP_Xpos DW ? ; X pos of Point to Read +RP_STACK ENDS + + PUBLIC READ_POINT + +READ_POINT PROC FAR + + PUSHx BP, DI ; Preserve Registers + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + MOV AX, [BP].RP_Ypos ; Get Line # of Pixel + MUL SCREEN_WIDTH ; Get Offset to Start of Line + + MOV BX, [BP].RP_Xpos ; Get Xpos + MOV CX, BX + SHR BX, 2 ; X offset (Bytes) = Xpos/4 + ADD BX, AX ; Offset = Width*Ypos + Xpos/4 + + MOV AL, READ_MAP ; GC Read Mask Register + MOV AH, CL ; Get Xpos + AND AH, PLANE_BITS ; & mask out Plane # + OUT_16 GC_INDEX, AX ; Select Plane to read in + + CLR AH ; Clear Return Value Hi byte + MOV AL, ES:[DI+BX] ; Get Color of Pixel + + POPx DI, BP ; Restore Saved Registers + RET 4 ; Exit and Clean up Stack + +READ_POINT ENDP + + +;====================================================== +;FILL_BLOCK (Xpos1%, Ypos1%, Xpos2%, Ypos2%, ColorNum%) +;====================================================== +; +; Fills a rectangular block on the active display Page +; +; ENTRY: Xpos1 = Left X position of area to fill +; Ypos1 = Top Y position of area to fill +; Xpos2 = Right X position of area to fill +; Ypos2 = Bottom Y position of area to fill +; ColorNum = Color to fill area with +; +; EXIT: No meaningful values returned +; + +FB_STACK STRUC + DW ?x4 ; DS, DI, SI, BP + DD ? ; Caller + FB_Color DB ?,? ; Fill Color + FB_Ypos2 DW ? ; Y pos of Lower Right Pixel + FB_Xpos2 DW ? ; X pos of Lower Right Pixel + FB_Ypos1 DW ? ; Y pos of Upper Left Pixel + FB_Xpos1 DW ? ; X pos of Upper Left Pixel +FB_STACK ENDS + + PUBLIC FILL_BLOCK + +FILL_BLOCK PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + CLD ; Direction Flag = Forward + + OUT_8 SC_INDEX, MAP_MASK ; Set up for Plane Select + + ; Validate Pixel Coordinates + ; If necessary, Swap so X1 <= X2, Y1 <= Y2 + + MOV AX, [BP].FB_Ypos1 ; AX = Y1 is Y1< Y2? + MOV BX, [BP].FB_Ypos2 ; BX = Y2 + CMP AX, BX + JLE @FB_NOSWAP1 + + MOV [BP].FB_Ypos1, BX ; Swap Y1 and Y2 and save Y1 + XCHG AX, BX ; on stack for future use + +@FB_NOSWAP1: + SUB BX, AX ; Get Y width + INC BX ; Add 1 to avoid 0 value + MOV [BP].FB_Ypos2, BX ; Save in Ypos2 + + MUL SCREEN_WIDTH ; Mul Y1 by Bytes per Line + ADD DI, AX ; DI = Start of Line Y1 + + MOV AX, [BP].FB_Xpos1 ; Check X1 <= X2 + MOV BX, [BP].FB_Xpos2 ; + CMP AX, BX + JLE @FB_NOSWAP2 ; Skip Ahead if Ok + + MOV [BP].FB_Xpos2, AX ; Swap X1 AND X2 and save X2 + XCHG AX, BX ; on stack for future use + + ; All our Input Values are in order, Now determine + ; How many full "bands" 4 pixels wide (aligned) there + ; are, and if there are partial bands (<4 pixels) on + ; the left and right edges. + +@FB_NOSWAP2: + MOV DX, AX ; DX = X1 (Pixel Position) + SHR DX, 2 ; DX/4 = Bytes into Line + ADD DI, DX ; DI = Addr of Upper-Left Corner + + MOV CX, BX ; CX = X2 (Pixel Position) + SHR CX, 2 ; CX/4 = Bytes into Line + + CMP DX, CX ; Start and end in same band? + JNE @FB_NORMAL ; if not, check for l & r edges + JMP @FB_ONE_BAND_ONLY ; if so, then special processing + +@FB_NORMAL: + SUB CX, DX ; CX = # bands -1 + MOV SI, AX ; SI = PLANE#(X1) + AND SI, PLANE_BITS ; if Left edge is aligned then + JZ @FB_L_PLANE_FLUSH ; no special processing.. + + ; Draw "Left Edge" vertical strip of 1-3 pixels... + + OUT_8 SC_Data, Left_Clip_Mask[SI] ; Set Left Edge Plane Mask + + MOV SI, DI ; SI = Copy of Start Addr (UL) + + MOV DX, [BP].FB_Ypos2 ; Get # of Lines to draw + MOV AL, [BP].FB_Color ; Get Fill Color + MOV BX, SCREEN_WIDTH ; Get Vertical increment Value + +@FB_LEFT_LOOP: + MOV ES:[SI], AL ; Fill in Left Edge Pixels + ADD SI, BX ; Point to Next Line (Below) + LOOPjz DX, @FB_LEFT_CONT ; Exit loop if all Lines Drawn + + MOV ES:[SI], AL ; Fill in Left Edge Pixels + ADD SI, BX ; Point to Next Line (Below) + LOOPx DX, @FB_LEFT_LOOP ; loop until left strip is drawn + +@FB_LEFT_CONT: + + INC DI ; Point to Middle (or Right) Block + DEC CX ; Reset CX instead of JMP @FB_RIGHT + +@FB_L_PLANE_FLUSH: + INC CX ; Add in Left band to middle block + + ; DI = Addr of 1st middle Pixel (band) to fill + ; CX = # of Bands to fill -1 + +@FB_RIGHT: + MOV SI, [BP].FB_Xpos2 ; Get Xpos2 + AND SI, PLANE_BITS ; Get Plane values + CMP SI, 0003 ; Plane = 3? + JE @FB_R_EDGE_FLUSH ; Hey, add to middle + + ; Draw "Right Edge" vertical strip of 1-3 pixels... + + OUT_8 SC_Data, Right_Clip_Mask[SI] ; Right Edge Plane Mask + + MOV SI, DI ; Get Addr of Left Edge + ADD SI, CX ; Add Width-1 (Bands) + DEC SI ; To point to top of Right Edge + + MOV DX, [BP].FB_Ypos2 ; Get # of Lines to draw + MOV AL, [BP].FB_Color ; Get Fill Color + MOV BX, SCREEN_WIDTH ; Get Vertical increment Value + +@FB_RIGHT_LOOP: + MOV ES:[SI], AL ; Fill in Right Edge Pixels + ADD SI, BX ; Point to Next Line (Below) + LOOPjz DX, @FB_RIGHT_CONT ; Exit loop if all Lines Drawn + + MOV ES:[SI], AL ; Fill in Right Edge Pixels + ADD SI, BX ; Point to Next Line (Below) + LOOPx DX, @FB_RIGHT_LOOP ; loop until left strip is drawn + +@FB_RIGHT_CONT: + + DEC CX ; Minus 1 for Middle bands + JZ @FB_EXIT ; Uh.. no Middle bands... + +@FB_R_EDGE_FLUSH: + + ; DI = Addr of Upper Left block to fill + ; CX = # of Bands to fill in (width) + + OUT_8 SC_Data, ALL_PLANES ; Write to All Planes + + MOV DX, SCREEN_WIDTH ; DX = DI Increment + SUB DX, CX ; = Screen_Width-# Planes Filled + + MOV BX, CX ; BX = Quick Refill for CX + MOV SI, [BP].FB_Ypos2 ; SI = # of Line to Fill + MOV AL, [BP].FB_Color ; Get Fill Color + +@FB_MIDDLE_LOOP: + REP STOSB ; Fill in entire line + + MOV CX, BX ; Recharge CX (Line Width) + ADD DI, DX ; Point to start of Next Line + LOOPx SI, @FB_MIDDLE_LOOP ; Loop until all lines drawn + + JMP s @FB_EXIT ; Outa here + +@FB_ONE_BAND_ONLY: + MOV SI, AX ; Get Left Clip Mask, Save X1 + AND SI, PLANE_BITS ; Mask out Row # + MOV AL, Left_Clip_Mask[SI] ; Get Left Edge Mask + MOV SI, BX ; Get Right Clip Mask, Save X2 + AND SI, PLANE_BITS ; Mask out Row # + AND AL, Right_Clip_Mask[SI] ; Get Right Edge Mask byte + + OUT_8 SC_Data, AL ; Clip For Left & Right Masks + + MOV CX, [BP].FB_Ypos2 ; Get # of Lines to draw + MOV AL, [BP].FB_Color ; Get Fill Color + MOV BX, SCREEN_WIDTH ; Get Vertical increment Value + +@FB_ONE_LOOP: + MOV ES:[DI], AL ; Fill in Pixels + ADD DI, BX ; Point to Next Line (Below) + LOOPjz CX, @FB_EXIT ; Exit loop if all Lines Drawn + + MOV ES:[DI], AL ; Fill in Pixels + ADD DI, BX ; Point to Next Line (Below) + LOOPx CX, @FB_ONE_LOOP ; loop until left strip is drawn + +@FB_EXIT: + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 10 ; Exit and Clean up Stack + +FILL_BLOCK ENDP + + +;===================================================== +;DRAW_LINE (Xpos1%, Ypos1%, Xpos2%, Ypos2%, ColorNum%) +;===================================================== +; +; Draws a Line on the active display page +; +; ENTRY: Xpos1 = X position of first point on line +; Ypos1 = Y position of first point on line +; Xpos2 = X position of last point on line +; Ypos2 = Y position of last point on line +; ColorNum = Color to draw line with +; +; EXIT: No meaningful values returned +; + +DL_STACK STRUC + DW ?x3 ; DI, SI, BP + DD ? ; Caller + DL_ColorF DB ?,? ; Line Draw Color + DL_Ypos2 DW ? ; Y pos of last point + DL_Xpos2 DW ? ; X pos of last point + DL_Ypos1 DW ? ; Y pos of first point + DL_Xpos1 DW ? ; X pos of first point +DL_STACK ENDS + + PUBLIC DRAW_LINE + +DRAW_LINE PROC FAR + + PUSHx BP, SI, DI ; Preserve Important Registers + MOV BP, SP ; Set up Stack Frame + CLD ; Direction Flag = Forward + + OUT_8 SC_INDEX, MAP_MASK ; Set up for Plane Select + MOV CH, [BP].DL_ColorF ; Save Line Color in CH + + ; Check Line Type + + MOV SI, [BP].DL_Xpos1 ; AX = X1 is X1< X2? + MOV DI, [BP].DL_Xpos2 ; DX = X2 + CMP SI, DI ; Is X1 < X2 + JE @DL_VLINE ; If X1=X2, Draw Vertical Line + JL @DL_NOSWAP1 ; If X1 < X2, don't swap + + XCHG SI, DI ; X2 IS > X1, SO SWAP THEM + +@DL_NOSWAP1: + + ; SI = X1, DI = X2 + + MOV AX, [BP].DL_Ypos1 ; AX = Y1 is Y1 <> Y2? + CMP AX, [BP].DL_Ypos2 ; Y1 = Y2? + JE @DL_HORZ ; If so, Draw a Horizontal Line + + JMP @DL_BREZHAM ; Diagonal line... go do it... + + ; This Code draws a Horizontal Line in Mode X where: + ; SI = X1, DI = X2, and AX = Y1/Y2 + +@DL_HORZ: + + MUL SCREEN_WIDTH ; Offset = Ypos * Screen_Width + MOV DX, AX ; CX = Line offset into Page + + MOV AX, SI ; Get Left edge, Save X1 + AND SI, PLANE_BITS ; Mask out Row # + MOV BL, Left_Clip_Mask[SI] ; Get Left Edge Mask + MOV CX, DI ; Get Right edge, Save X2 + AND DI, PLANE_BITS ; Mask out Row # + MOV BH, Right_Clip_Mask[DI] ; Get Right Edge Mask byte + + SHR AX, 2 ; Get X1 Byte # (=X1/4) + SHR CX, 2 ; Get X2 Byte # (=X2/4) + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + ADD DI, DX ; Point to Start of Line + ADD DI, AX ; Point to Pixel X1 + + SUB CX, AX ; CX = # Of Bands (-1) to set + JNZ @DL_LONGLN ; jump if longer than one segment + + AND BL, BH ; otherwise, merge clip masks + +@DL_LONGLN: + + OUT_8 SC_Data, BL ; Set the Left Clip Mask + + MOV AL, [BP].DL_ColorF ; Get Line Color + MOV BL, AL ; BL = Copy of Line Color + STOSB ; Set Left (1-4) Pixels + + JCXZ @DL_EXIT ; Done if only one Line Segment + + DEC CX ; CX = # of Middle Segments + JZ @DL_XRSEG ; If no middle segments.... + + ; Draw Middle Segments + + OUT_8 DX, ALL_PLANES ; Write to ALL Planes + + MOV AL, BL ; Get Color from BL + REP STOSB ; Draw Middle (4 Pixel) Segments + +@DL_XRSEG: + OUT_8 DX, BH ; Select Planes for Right Clip Mask + MOV AL, BL ; Get Color Value + STOSB ; Draw Right (1-4) Pixels + + JMP s @DL_EXIT ; We Are Done... + + + ; This Code Draws A Vertical Line. On entry: + ; CH = Line Color, SI & DI = X1 + +@DL_VLINE: + + MOV AX, [BP].DL_Ypos1 ; AX = Y1 + MOV SI, [BP].DL_Ypos2 ; SI = Y2 + CMP AX, SI ; Is Y1 < Y2? + JLE @DL_NOSWAP2 ; if so, Don't Swap them + + XCHG AX, SI ; Ok, NOW Y1 < Y2 + +@DL_NOSWAP2: + + SUB SI, AX ; SI = Line Height (Y2-Y1+1) + INC SI + + ; AX = Y1, DI = X1, Get offset into Page into AX + + MUL SCREEN_WIDTH ; Offset = Y1 (AX) * Screen Width + MOV DX, DI ; Copy Xpos into DX + SHR DI, 2 ; DI = Xpos/4 + ADD AX, DI ; DI = Xpos/4 + ScreenWidth * Y1 + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + ADD DI, AX ; Point to Pixel X1, Y1 + + ;Select Plane + + MOV CL, DL ; CL = Save X1 + AND CL, PLANE_BITS ; Get X1 MOD 4 (Plane #) + MOV AX, MAP_MASK_PLANE1 ; Code to set Plane #1 + SHL AH, CL ; Change to Correct Plane # + OUT_16 SC_Index, AX ; Select Plane + + MOV AL, CH ; Get Saved Color + MOV BX, SCREEN_WIDTH ; Get Offset to Advance Line By + +@DL_VLoop: + MOV ES:[DI], AL ; Draw Single Pixel + ADD DI, BX ; Point to Next Line + LOOPjz SI, @DL_EXIT ; Lines--, Exit if done + + MOV ES:[DI], AL ; Draw Single Pixel + ADD DI, BX ; Point to Next Line + LOOPx SI, @DL_VLoop ; Lines--, Loop until Done + +@DL_EXIT: + + JMP @DL_EXIT2 ; Done! + + ; This code Draws a diagonal line in Mode X + +@DL_BREZHAM: + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + MOV AX, [BP].DL_Ypos1 ; get Y1 value + MOV BX, [BP].DL_Ypos2 ; get Y2 value + MOV CX, [BP].DL_Xpos1 ; Get Starting Xpos + + CMP BX, AX ; Y2-Y1 is? + JNC @DL_DeltaYOK ; if Y2>=Y1 then goto... + + XCHG BX, AX ; Swap em... + MOV CX, [BP].DL_Xpos2 ; Get New Starting Xpos + +@DL_DeltaYOK: + MUL SCREEN_WIDTH ; Offset = SCREEN_WIDTH * Y1 + + ADD DI, AX ; DI -> Start of Line Y1 on Page + MOV AX, CX ; AX = Xpos (X1) + SHR AX, 2 ; /4 = Byte Offset into Line + ADD DI, AX ; DI = Starting pos (X1,Y1) + + MOV AL, 11h ; Staring Mask + AND CL, PLANE_BITS ; Get Plane # + SHL AL, CL ; and shift into place + MOV AH, [BP].DL_ColorF ; Color in Hi Bytes + + PUSH AX ; Save Mask,Color... + + MOV AH, AL ; Plane # in AH + MOV AL, MAP_MASK ; Select Plane Register + OUT_16 SC_Index, AX ; Select initial plane + + MOV AX, [BP].DL_Xpos1 ; get X1 value + MOV BX, [BP].DL_Ypos1 ; get Y1 value + MOV CX, [BP].DL_Xpos2 ; get X2 value + MOV DX, [BP].DL_Ypos2 ; get Y2 value + + MOV BP, SCREEN_WIDTH ; Use BP for Line width to + ; to avoid extra memory access + + SUB DX, BX ; figure Delta_Y + JNC @DL_DeltaYOK2 ; jump if Y2 >= Y1 + + ADD BX, DX ; put Y2 into Y1 + NEG DX ; abs(Delta_Y) + XCHG AX, CX ; and exchange X1 and X2 + +@DL_DeltaYOK2: + MOV BX, 08000H ; seed for fraction accumulator + + SUB CX, AX ; figure Delta_X + JC @DL_DrawLeft ; if negative, go left + + JMP @DL_DrawRight ; Draw Line that slopes right + +@DL_DrawLeft: + + NEG CX ; abs(Delta_X) + + CMP CX, DX ; is Delta_X < Delta_Y? + JB @DL_SteepLeft ; yes, so go do steep line + ; (Delta_Y iterations) + + ; Draw a Shallow line to the left in Mode X + +@DL_ShallowLeft: + CLR AX ; zero low word of Delta_Y * 10000h + SUB AX, DX ; DX:AX <- DX * 0FFFFh + SBB DX, 0 ; include carry + DIV CX ; divide by Delta_X + + MOV SI, BX ; SI = Accumulator + MOV BX, AX ; BX = Add fraction + POP AX ; Get Color, Bit mask + MOV DX, SC_Data ; Sequence controller data register + INC CX ; Inc Delta_X so we can unroll loop + + ; Loop (x2) to Draw Pixels, Move Left, and Maybe Down... + +@DL_SLLLoop: + MOV ES:[DI], AH ; set first pixel, plane data set up + LOOPjz CX, @DL_SLLExit ; Delta_X--, Exit if done + + ADD SI, BX ; add numerator to accumulator + JNC @DL_SLLL2nc ; move down on carry + + ADD DI, BP ; Move Down one line... + +@DL_SLLL2nc: + DEC DI ; Left one addr + ROR AL, 1 ; Move Left one plane, back on 0 1 2 + CMP AL, 87h ; wrap?, if AL <88 then Carry set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + + MOV ES:[DI], AH ; set pixel + LOOPjz CX, @DL_SLLExit ; Delta_X--, Exit if done + + ADD SI, BX ; add numerator to accumulator, + JNC @DL_SLLL3nc ; move down on carry + + ADD DI, BP ; Move Down one line... + +@DL_SLLL3nc: ; Now move left a pixel... + DEC DI ; Left one addr + ROR AL, 1 ; Move Left one plane, back on 0 1 2 + CMP AL, 87h ; Wrap?, if AL <88 then Carry set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + JMP s @DL_SLLLoop ; loop until done + +@DL_SLLExit: + JMP @DL_EXIT2 ; and exit + + ; Draw a steep line to the left in Mode X + +@DL_SteepLeft: + CLR AX ; zero low word of Delta_Y * 10000h + XCHG DX, CX ; Delta_Y switched with Delta_X + DIV CX ; divide by Delta_Y + + MOV SI, BX ; SI = Accumulator + MOV BX, AX ; BX = Add Fraction + POP AX ; Get Color, Bit mask + MOV DX, SC_Data ; Sequence controller data register + INC CX ; Inc Delta_Y so we can unroll loop + + ; Loop (x2) to Draw Pixels, Move Down, and Maybe left + +@DL_STLLoop: + + MOV ES:[DI], AH ; set first pixel + LOOPjz CX, @DL_STLExit ; Delta_Y--, Exit if done + + ADD SI, BX ; add numerator to accumulator + JNC @DL_STLnc2 ; No carry, just move down! + + DEC DI ; Move Left one addr + ROR AL, 1 ; Move Left one plane, back on 0 1 2 + CMP AL, 87h ; Wrap?, if AL <88 then Carry set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + +@DL_STLnc2: + ADD DI, BP ; advance to next line. + + MOV ES:[DI], AH ; set pixel + LOOPjz CX, @DL_STLExit ; Delta_Y--, Exit if done + + ADD SI, BX ; add numerator to accumulator + JNC @DL_STLnc3 ; No carry, just move down! + + DEC DI ; Move Left one addr + ROR AL, 1 ; Move Left one plane, back on 0 1 2 + CMP AL, 87h ; Wrap?, if AL <88 then Carry set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + +@DL_STLnc3: + ADD DI, BP ; advance to next line. + JMP s @DL_STLLoop ; Loop until done + +@DL_STLExit: + JMP @DL_EXIT2 ; and exit + + ; Draw a line that goes to the Right... + +@DL_DrawRight: + CMP CX, DX ; is Delta_X < Delta_Y? + JB @DL_SteepRight ; yes, so go do steep line + ; (Delta_Y iterations) + + ; Draw a Shallow line to the Right in Mode X + +@DL_ShallowRight: + CLR AX ; zero low word of Delta_Y * 10000h + SUB AX, DX ; DX:AX <- DX * 0FFFFh + SBB DX, 0 ; include carry + DIV CX ; divide by Delta_X + + MOV SI, BX ; SI = Accumulator + MOV BX, AX ; BX = Add Fraction + POP AX ; Get Color, Bit mask + MOV DX, SC_Data ; Sequence controller data register + INC CX ; Inc Delta_X so we can unroll loop + + ; Loop (x2) to Draw Pixels, Move Right, and Maybe Down... + +@DL_SLRLoop: + MOV ES:[DI], AH ; set first pixel, mask is set up + LOOPjz CX, @DL_SLRExit ; Delta_X--, Exit if done.. + + ADD SI, BX ; add numerator to accumulator + JNC @DL_SLR2nc ; don't move down if carry not set + + ADD DI, BP ; Move Down one line... + +@DL_SLR2nc: ; Now move right a pixel... + ROL AL, 1 ; Move Right one addr if Plane = 0 + CMP AL, 12h ; Wrap? if AL >12 then Carry not set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + + MOV ES:[DI], AH ; set pixel + LOOPjz CX, @DL_SLRExit ; Delta_X--, Exit if done.. + + ADD SI, BX ; add numerator to accumulator + JNC @DL_SLR3nc ; don't move down if carry not set + + ADD DI, BP ; Move Down one line... + +@DL_SLR3nc: + ROL AL, 1 ; Move Right one addr if Plane = 0 + CMP AL, 12h ; Wrap? if AL >12 then Carry not set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + JMP s @DL_SLRLoop ; loop till done + +@DL_SLRExit: + JMP @DL_EXIT2 ; and exit + + ; Draw a Steep line to the Right in Mode X + +@DL_SteepRight: + CLR AX ; zero low word of Delta_Y * 10000h + XCHG DX, CX ; Delta_Y switched with Delta_X + DIV CX ; divide by Delta_Y + + MOV SI, BX ; SI = Accumulator + MOV BX, AX ; BX = Add Fraction + POP AX ; Get Color, Bit mask + MOV DX, SC_Data ; Sequence controller data register + INC CX ; Inc Delta_Y so we can unroll loop + + ; Loop (x2) to Draw Pixels, Move Down, and Maybe Right + +@STRLoop: + MOV ES:[DI], AH ; set first pixel, mask is set up + LOOPjz CX, @DL_EXIT2 ; Delta_Y--, Exit if Done + + ADD SI, BX ; add numerator to accumulator + JNC @STRnc2 ; if no carry then just go down... + + ROL AL, 1 ; Move Right one addr if Plane = 0 + CMP AL, 12h ; Wrap? if AL >12 then Carry not set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + +@STRnc2: + ADD DI, BP ; advance to next line. + + MOV ES:[DI], AH ; set pixel + LOOPjz CX, @DL_EXIT2 ; Delta_Y--, Exit if Done + + ADD SI, BX ; add numerator to accumulator + JNC @STRnc3 ; if no carry then just go down... + + ROL AL, 1 ; Move Right one addr if Plane = 0 + CMP AL, 12h ; Wrap? if AL >12 then Carry not set + ADC DI, 0 ; Adjust Address: DI = DI + Carry + OUT DX, AL ; Set up New Bit Plane mask + +@STRnc3: + ADD DI, BP ; advance to next line. + JMP s @STRLoop ; loop till done + +@DL_EXIT2: + POPx DI, SI, BP ; Restore Saved Registers + RET 10 ; Exit and Clean up Stack + +DRAW_LINE ENDP + + + ; ===== DAC COLOR REGISTER ROUTINES ===== + +;================================================= +;SET_DAC_REGISTER (Register%, Red%, Green%, Blue%) +;================================================= +; +; Sets a single (RGB) Vga Palette Register +; +; ENTRY: Register = The DAC # to modify (0-255) +; Red = The new Red Intensity (0-63) +; Green = The new Green Intensity (0-63) +; Blue = The new Blue Intensity (0-63) +; +; EXIT: No meaningful values returned +; + +SDR_STACK STRUC + DW ? ; BP + DD ? ; Caller + SDR_Blue DB ?,? ; Blue Data Value + SDR_Green DB ?,? ; Green Data Value + SDR_Red DB ?,? ; Red Data Value + SDR_Register DB ?,? ; Palette Register # +SDR_STACK ENDS + + PUBLIC SET_DAC_REGISTER + +SET_DAC_REGISTER PROC FAR + + PUSH BP ; Save BP + MOV BP, SP ; Set up Stack Frame + + ; Select which DAC Register to modify + + OUT_8 DAC_WRITE_ADDR, [BP].SDR_Register + + MOV DX, PEL_DATA_REG ; Dac Data Register + OUT_8 DX, [BP].SDR_Red ; Set Red Intensity + OUT_8 DX, [BP].SDR_Green ; Set Green Intensity + OUT_8 DX, [BP].SDR_Blue ; Set Blue Intensity + + POP BP ; Restore Registers + RET 8 ; Exit & Clean Up Stack + +SET_DAC_REGISTER ENDP + +;==================================================== +;GET_DAC_REGISTER (Register%, &Red%, &Green%, &Blue%) +;==================================================== +; +; Reads the RGB Values of a single Vga Palette Register +; +; ENTRY: Register = The DAC # to read (0-255) +; Red = Offset to Red Variable in DS +; Green = Offset to Green Variable in DS +; Blue = Offset to Blue Variable in DS +; +; EXIT: The values of the integer variables Red, +; Green, and Blue are set to the values +; taken from the specified DAC register. +; + +GDR_STACK STRUC + DW ? ; BP + DD ? ; Caller + GDR_Blue DW ? ; Addr of Blue Data Value in DS + GDR_Green DW ? ; Addr of Green Data Value in DS + GDR_Red DW ? ; Addr of Red Data Value in DS + GDR_Register DB ?,? ; Palette Register # +GDR_STACK ENDS + + PUBLIC GET_DAC_REGISTER + +GET_DAC_REGISTER PROC FAR + + PUSH BP ; Save BP + MOV BP, SP ; Set up Stack Frame + + ; Select which DAC Register to read in + + OUT_8 DAC_READ_ADDR, [BP].GDR_Register + + MOV DX, PEL_DATA_REG ; Dac Data Register + CLR AX ; Clear AX + + IN AL, DX ; Read Red Value + MOV BX, [BP].GDR_Red ; Get Address of Red% + MOV [BX], AX ; *Red% = AX + + IN AL, DX ; Read Green Value + MOV BX, [BP].GDR_Green ; Get Address of Green% + MOV [BX], AX ; *Green% = AX + + IN AL, DX ; Read Blue Value + MOV BX, [BP].GDR_Blue ; Get Address of Blue% + MOV [BX], AX ; *Blue% = AX + + POP BP ; Restore Registers + RET 8 ; Exit & Clean Up Stack + +GET_DAC_REGISTER ENDP + + +;=========================================================== +;LOAD_DAC_REGISTERS (SEG PalData, StartReg%, EndReg%, Sync%) +;=========================================================== +; +; Sets a Block of Vga Palette Registers +; +; ENTRY: PalData = Far Pointer to Block of palette data +; StartReg = First Register # in range to set (0-255) +; EndReg = Last Register # in Range to set (0-255) +; Sync = Wait for Vertical Retrace Flag (Boolean) +; +; EXIT: No meaningful values returned +; +; NOTES: PalData is a linear array of 3 byte Palette values +; in the order: Red (0-63), Green (0-63), Blue (0-63) +; + +LDR_STACK STRUC + DW ?x3 ; BP, DS, SI + DD ? ; Caller + LDR_Sync DW ? ; Vertical Sync Flag + LDR_EndReg DB ?,? ; Last Register # + LDR_StartReg DB ?,? ; First Register # + LDR_PalData DD ? ; Far Ptr to Palette Data +LDR_STACK ENDS + + PUBLIC LOAD_DAC_REGISTERS + +LOAD_DAC_REGISTERS PROC FAR + + PUSHx BP, DS, SI ; Save Registers + mov BP, SP ; Set up Stack Frame + + mov AX, [BP].LDR_Sync ; Get Vertical Sync Flag + or AX, AX ; is Sync Flag = 0? + jz @LDR_Load ; if so, skip call + + call f SYNC_DISPLAY ; wait for vsync + + ; Determine register #'s, size to copy, etc + +@LDR_Load: + + lds SI, [BP].LDR_PalData ; DS:SI -> Palette Data + mov DX, DAC_WRITE_ADDR ; DAC register # selector + + CLR AX, BX ; Clear for byte loads + mov AL, [BP].LDR_StartReg ; Get Start Register + mov BL, [BP].LDR_EndReg ; Get End Register + + sub BX, AX ; BX = # of DAC registers -1 + inc BX ; BX = # of DAC registers + mov CX, BX ; CX = # of DAC registers + add CX, BX ; CX = " " * 2 + add CX, BX ; CX = " " * 3 + cld ; Block OUTs forward + out DX, AL ; set up correct register # + + ; Load a block of DAC Registers + + mov DX, PEL_DATA_REG ; Dac Data Register + + rep outsb ; block set DAC registers + + POPx SI, DS, BP ; Restore Registers + ret 10 ; Exit & Clean Up Stack + +LOAD_DAC_REGISTERS ENDP + + +;==================================================== +;READ_DAC_REGISTERS (SEG PalData, StartReg%, EndReg%) +;==================================================== +; +; Reads a Block of Vga Palette Registers +; +; ENTRY: PalData = Far Pointer to block to store palette data +; StartReg = First Register # in range to read (0-255) +; EndReg = Last Register # in Range to read (0-255) +; +; EXIT: No meaningful values returned +; +; NOTES: PalData is a linear array of 3 byte Palette values +; in the order: Red (0-63), Green (0-63), Blue (0-63) +; + +RDR_STACK STRUC + DW ?x3 ; BP, ES, DI + DD ? ; Caller + RDR_EndReg DB ?,? ; Last Register # + RDR_StartReg DB ?,? ; First Register # + RDR_PalData DD ? ; Far Ptr to Palette Data +RDR_STACK ENDS + + PUBLIC READ_DAC_REGISTERS + +READ_DAC_REGISTERS PROC FAR + + PUSHx BP, ES, DI ; Save Registers + mov BP, SP ; Set up Stack Frame + + ; Determine register #'s, size to copy, etc + + les DI, [BP].RDR_PalData ; ES:DI -> Palette Buffer + mov DX, DAC_READ_ADDR ; DAC register # selector + + CLR AX, BX ; Clear for byte loads + mov AL, [BP].RDR_StartReg ; Get Start Register + mov BL, [BP].RDR_EndReg ; Get End Register + + sub BX, AX ; BX = # of DAC registers -1 + inc BX ; BX = # of DAC registers + mov CX, BX ; CX = # of DAC registers + add CX, BX ; CX = " " * 2 + add CX, BX ; CX = " " * 3 + cld ; Block INs forward + + ; Read a block of DAC Registers + + out DX, AL ; set up correct register # + mov DX, PEL_DATA_REG ; Dac Data Register + + rep insb ; block read DAC registers + + POPx DI, ES, BP ; Restore Registers + ret 8 ; Exit & Clean Up Stack + +READ_DAC_REGISTERS ENDP + + + ; ===== PAGE FLIPPING AND SCROLLING ROUTINES ===== + +;========================= +;SET_ACTIVE_PAGE (PageNo%) +;========================= +; +; Sets the active display Page to be used for future drawing +; +; ENTRY: PageNo = Display Page to make active +; (values: 0 to Number of Pages - 1) +; +; EXIT: No meaningful values returned +; + +SAP_STACK STRUC + DW ? ; BP + DD ? ; Caller + SAP_Page DW ? ; Page # for Drawing +SAP_STACK ENDS + + PUBLIC SET_ACTIVE_PAGE + +SET_ACTIVE_PAGE PROC FAR + + PUSH BP ; Preserve Registers + MOV BP, SP ; Set up Stack Frame + + MOV BX, [BP].SAP_Page ; Get Desired Page # + CMP BX, LAST_PAGE ; Is Page # Valid? + JAE @SAP_Exit ; IF Not, Do Nothing + + MOV ACTIVE_PAGE, BX ; Set Active Page # + + SHL BX, 1 ; Scale Page # to Word + MOV AX, PAGE_ADDR[BX] ; Get offset to Page + + MOV CURRENT_PAGE, AX ; And set for future LES's + +@SAP_Exit: + POP BP ; Restore Registers + RET 2 ; Exit and Clean up Stack + +SET_ACTIVE_PAGE ENDP + + +;================ +;GET_ACTIVE_PAGE% +;================ +; +; Returns the Video Page # currently used for Drawing +; +; ENTRY: No Parameters are passed +; +; EXIT: AX = Current Video Page used for Drawing +; + + PUBLIC GET_ACTIVE_PAGE + +GET_ACTIVE_PAGE PROC FAR + + MOV AX, ACTIVE_PAGE ; Get Active Page # + RET ; Exit and Clean up Stack + +GET_ACTIVE_PAGE ENDP + + +;=============================== +;SET_DISPLAY_PAGE (DisplayPage%) +;=============================== +; +; Sets the currently visible display page. +; When called this routine syncronizes the display +; to the vertical blank. +; +; ENTRY: PageNo = Display Page to show on the screen +; (values: 0 to Number of Pages - 1) +; +; EXIT: No meaningful values returned +; + +SDP_STACK STRUC + DW ? ; BP + DD ? ; Caller + SDP_Page DW ? ; Page # to Display... +SDP_STACK ENDS + + PUBLIC SET_DISPLAY_PAGE + +SET_DISPLAY_PAGE PROC FAR + + PUSH BP ; Preserve Registers + MOV BP, SP ; Set up Stack Frame + + MOV BX, [BP].SDP_Page ; Get Desired Page # + CMP BX, LAST_PAGE ; Is Page # Valid? + JAE @SDP_Exit ; IF Not, Do Nothing + + MOV DISPLAY_PAGE, BX ; Set Display Page # + + SHL BX, 1 ; Scale Page # to Word + MOV CX, PAGE_ADDR[BX] ; Get offset in memory to Page + ADD CX, CURRENT_MOFFSET ; Adjust for any scrolling + + ; Wait if we are currently in a Vertical Retrace + + MOV DX, INPUT_1 ; Input Status #1 Register + +@DP_WAIT0: + IN AL, DX ; Get VGA status + AND AL, VERT_RETRACE ; In Display mode yet? + JNZ @DP_WAIT0 ; If Not, wait for it + + ; Set the Start Display Address to the new page + + MOV DX, CRTC_Index ; We Change the VGA Sequencer + + MOV AL, START_DISP_LO ; Display Start Low Register + MOV AH, CL ; Low 8 Bits of Start Addr + OUT DX, AX ; Set Display Addr Low + + MOV AL, START_DISP_HI ; Display Start High Register + MOV AH, CH ; High 8 Bits of Start Addr + OUT DX, AX ; Set Display Addr High + + ; Wait for a Vertical Retrace to smooth out things + + MOV DX, INPUT_1 ; Input Status #1 Register + +@DP_WAIT1: + IN AL, DX ; Get VGA status + AND AL, VERT_RETRACE ; Vertical Retrace Start? + JZ @DP_WAIT1 ; If Not, wait for it + + ; Now Set Display Starting Address + + +@SDP_Exit: + POP BP ; Restore Registers + RET 2 ; Exit and Clean up Stack + +SET_DISPLAY_PAGE ENDP + + +;================= +;GET_DISPLAY_PAGE% +;================= +; +; Returns the Video Page # currently displayed +; +; ENTRY: No Parameters are passed +; +; EXIT: AX = Current Video Page being displayed +; + + PUBLIC GET_DISPLAY_PAGE + +GET_DISPLAY_PAGE PROC FAR + + MOV AX, DISPLAY_PAGE ; Get Display Page # + RET ; Exit & Clean Up Stack + +GET_DISPLAY_PAGE ENDP + + +;======================================= +;SET_WINDOW (DisplayPage%, Xpos%, Ypos%) +;======================================= +; +; Since a Logical Screen can be larger than the Physical +; Screen, Scrolling is possible. This routine sets the +; Upper Left Corner of the Screen to the specified Pixel. +; Also Sets the Display page to simplify combined page +; flipping and scrolling. When called this routine +; syncronizes the display to the vertical blank. +; +; ENTRY: DisplayPage = Display Page to show on the screen +; Xpos = # of pixels to shift screen right +; Ypos = # of lines to shift screen down +; +; EXIT: No meaningful values returned +; + +SW_STACK STRUC + DW ? ; BP + DD ? ; Caller + SW_Ypos DW ? ; Y pos of UL Screen Corner + SW_Xpos DW ? ; X pos of UL Screen Corner + SW_Page DW ? ; (new) Display Page +SW_STACK ENDS + + PUBLIC SET_WINDOW + +SET_WINDOW PROC FAR + + PUSH BP ; Preserve Registers + MOV BP, SP ; Set up Stack Frame + + ; Check if our Scroll Offsets are Valid + + MOV BX, [BP].SW_Page ; Get Desired Page # + CMP BX, LAST_PAGE ; Is Page # Valid? + JAE @SW_Exit ; IF Not, Do Nothing + + MOV AX, [BP].SW_Ypos ; Get Desired Y Offset + CMP AX, MAX_YOFFSET ; Is it Within Limits? + JA @SW_Exit ; if not, exit + + MOV CX, [BP].SW_Xpos ; Get Desired X Offset + CMP CX, MAX_XOFFSET ; Is it Within Limits? + JA @SW_Exit ; if not, exit + + ; Compute proper Display start address to use + + MUL SCREEN_WIDTH ; AX = YOffset * Line Width + SHR CX, 2 ; CX / 4 = Bytes into Line + ADD AX, CX ; AX = Offset of Upper Left Pixel + + MOV CURRENT_MOFFSET, AX ; Save Offset Info + + MOV DISPLAY_PAGE, BX ; Set Current Page # + SHL BX, 1 ; Scale Page # to Word + ADD AX, PAGE_ADDR[BX] ; Get offset in VGA to Page + MOV BX, AX ; BX = Desired Display Start + + MOV DX, INPUT_1 ; Input Status #1 Register + + ; Wait if we are currently in a Vertical Retrace + +@SW_WAIT0: + IN AL, DX ; Get VGA status + AND AL, VERT_RETRACE ; In Display mode yet? + JNZ @SW_WAIT0 ; If Not, wait for it + + ; Set the Start Display Address to the new window + + MOV DX, CRTC_Index ; We Change the VGA Sequencer + MOV AL, START_DISP_LO ; Display Start Low Register + MOV AH, BL ; Low 8 Bits of Start Addr + OUT DX, AX ; Set Display Addr Low + + MOV AL, START_DISP_HI ; Display Start High Register + MOV AH, BH ; High 8 Bits of Start Addr + OUT DX, AX ; Set Display Addr High + + ; Wait for a Vertical Retrace to smooth out things + + MOV DX, INPUT_1 ; Input Status #1 Register + +@SW_WAIT1: + IN AL, DX ; Get VGA status + AND AL, VERT_RETRACE ; Vertical Retrace Start? + JZ @SW_WAIT1 ; If Not, wait for it + + ; Now Set the Horizontal Pixel Pan values + + OUT_8 ATTRIB_Ctrl, PIXEL_PAN_REG ; Select Pixel Pan Register + + MOV AX, [BP].SW_Xpos ; Get Desired X Offset + AND AL, 03 ; Get # of Pixels to Pan (0-3) + SHL AL, 1 ; Shift for 256 Color Mode + OUT DX, AL ; Fine tune the display! + +@SW_Exit: + POP BP ; Restore Saved Registers + RET 6 ; Exit and Clean up Stack + +SET_WINDOW ENDP + + +;============= +;GET_X_OFFSET% +;============= +; +; Returns the X coordinate of the Pixel currently display +; in the upper left corner of the display +; +; ENTRY: No Parameters are passed +; +; EXIT: AX = Current Horizontal Scroll Offset +; + + PUBLIC GET_X_OFFSET + +GET_X_OFFSET PROC FAR + + MOV AX, CURRENT_XOFFSET ; Get current horz offset + RET ; Exit & Clean Up Stack + +GET_X_OFFSET ENDP + + +;============= +;GET_Y_OFFSET% +;============= +; +; Returns the Y coordinate of the Pixel currently display +; in the upper left corner of the display +; +; ENTRY: No Parameters are passed +; +; EXIT: AX = Current Vertical Scroll Offset +; + + PUBLIC GET_Y_OFFSET + +GET_Y_OFFSET PROC FAR + + MOV AX, CURRENT_YOFFSET ; Get current vertical offset + RET ; Exit & Clean Up Stack + +GET_Y_OFFSET ENDP + + +;============ +;SYNC_DISPLAY +;============ +; +; Pauses the computer until the next Vertical Retrace starts +; +; ENTRY: No Parameters are passed +; +; EXIT: No meaningful values returned +; + + PUBLIC SYNC_DISPLAY + +SYNC_DISPLAY PROC FAR + + MOV DX, INPUT_1 ; Input Status #1 Register + + ; Wait for any current retrace to end + +@SD_WAIT0: + IN AL, DX ; Get VGA status + AND AL, VERT_RETRACE ; In Display mode yet? + JNZ @SD_WAIT0 ; If Not, wait for it + + ; Wait for the start of the next vertical retrace + +@SD_WAIT1: + IN AL, DX ; Get VGA status + AND AL, VERT_RETRACE ; Vertical Retrace Start? + JZ @SD_WAIT1 ; If Not, wait for it + + RET ; Exit & Clean Up Stack + +SYNC_DISPLAY ENDP + + + ; ===== TEXT DISPLAY ROUTINES ===== + +;================================================== +;GPRINTC (CharNum%, Xpos%, Ypos%, ColorF%, ColorB%) +;================================================== +; +; Draws an ASCII Text Character using the currently selected +; 8x8 font on the active display page. It would be a simple +; exercise to make this routine process variable height fonts. +; +; ENTRY: CharNum = ASCII character # to draw +; Xpos = X position to draw Character at +; Ypos = Y position of to draw Character at +; ColorF = Color to draw text character in +; ColorB = Color to set background to +; +; EXIT: No meaningful values returned +; + +GPC_STACK STRUC + GPC_Width DW ? ; Screen Width-1 + GPC_Lines DB ?,? ; Scan lines to Decode + GPC_T_SETS DW ? ; Saved Charset Segment + GPC_T_SETO DW ? ; Saved Charset Offset + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + GPC_ColorB DB ?,? ; Background Color + GPC_ColorF DB ?,? ; Text Color + GPC_Ypos DW ? ; Y Position to Print at + GPC_Xpos DW ? ; X position to Print at + GPC_Char DB ?,? ; Character to Print +GPC_STACK ENDS + + PUBLIC GPRINTC + +GPRINTC PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + SUB SP, 8 ; Allocate WorkSpace on Stack + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + MOV AX, SCREEN_WIDTH ; Get Logical Line Width + MOV BX, AX ; BX = Screen Width + DEC BX ; = Screen Width-1 + MOV [BP].GPC_Width, BX ; Save for later use + + MUL [BP].GPC_Ypos ; Start of Line = Ypos * Width + ADD DI, AX ; DI -> Start of Line Ypos + + MOV AX, [BP].GPC_Xpos ; Get Xpos of Character + MOV CX, AX ; Save Copy of Xpos + SHR AX, 2 ; Bytes into Line = Xpos/4 + ADD DI, AX ; DI -> (Xpos, Ypos) + + ;Get Source ADDR of Character Bit Map & Save + + MOV AL, [BP].GPC_Char ; Get Character # + TEST AL, 080h ; Is Hi Bit Set? + JZ @GPC_LowChar ; Nope, use low char set ptr + + AND AL, 07Fh ; Mask Out Hi Bit + MOV BX, CHARSET_HI ; BX = Char Set Ptr:Offset + MOV DX, CHARSET_HI+2 ; DX = Char Set Ptr:Segment + JMP s @GPC_Set_Char ; Go Setup Character Ptr + +@GPC_LowChar: + + MOV BX, CHARSET_LOW ; BX = Char Set Ptr:Offset + MOV DX, CHARSET_LOW+2 ; DX = Char Set Ptr:Segment + +@GPC_Set_Char: + MOV [BP].GPC_T_SETS, DX ; Save Segment on Stack + + MOV AH, 0 ; Valid #'s are 0..127 + SHL AX, 3 ; * 8 Bytes Per Bitmap + ADD BX, AX ; BX = Offset of Selected char + MOV [BP].GPC_T_SETO, BX ; Save Offset on Stack + + AND CX, PLANE_BITS ; Get Plane # + MOV CH, ALL_PLANES ; Get Initial Plane mask + SHL CH, CL ; And shift into position + AND CH, ALL_PLANES ; And mask to lower nibble + + MOV AL, 04 ; 4-Plane # = # of initial + SUB AL, CL ; shifts to align bit mask + MOV CL, AL ; Shift Count for SHL + + ;Get segment of character map + + OUT_8 SC_Index, MAP_MASK ; Setup Plane selections + INC DX ; DX -> SC_Data + + MOV AL, 08 ; 8 Lines to Process + MOV [BP].GPC_Lines, AL ; Save on Stack + + MOV DS, [BP].GPC_T_SETS ; Point to character set + +@GPC_DECODE_CHAR_BYTE: + + MOV SI, [BP].GPC_T_SETO ; Get DS:SI = String + + MOV BH, [SI] ; Get Bit Map + INC SI ; Point to Next Line + MOV [BP].GPC_T_SETO, SI ; And save new Pointer... + + CLR AX ; Clear AX + + CLR BL ; Clear BL + ROL BX, CL ; BL holds left edge bits + MOV SI, BX ; Use as Table Index + AND SI, CHAR_BITS ; Get Low Bits + MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + JZ @GPC_NO_LEFT1BITS ; Skip if No Pixels to set + + MOV AH, [BP].GPC_ColorF ; Get Foreground Color + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@GPC_NO_LEFT1BITS: + XOR AL, CH ; Invert mask for Background + JZ @GPC_NO_LEFT0BITS ; Hey, no need for this + + MOV AH, [BP].GPC_ColorB ; Get background Color + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + + ;Now Do Middle/Last Band + +@GPC_NO_LEFT0BITS: + INC DI ; Point to next Byte + ROL BX, 4 ; Shift 4 bits + + MOV SI, BX ; Make Lookup Pointer + AND SI, CHAR_BITS ; Get Low Bits + MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + JZ @GPC_NO_MIDDLE1BITS ; Skip if no pixels to set + + MOV AH, [BP].GPC_ColorF ; Get Foreground Color + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@GPC_NO_MIDDLE1BITS: + XOR AL, ALL_PLANES ; Invert mask for Background + JZ @GPC_NO_MIDDLE0BITS ; Hey, no need for this + + MOV AH, [BP].GPC_ColorB ; Get background Color + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@GPC_NO_MIDDLE0BITS: + XOR CH, ALL_PLANES ; Invert Clip Mask + CMP CL, 4 ; Aligned by 4? + JZ @GPC_NEXT_LINE ; If so, Exit now.. + + INC DI ; Point to next Byte + ROL BX, 4 ; Shift 4 bits + + MOV SI, BX ; Make Lookup Pointer + AND SI, CHAR_BITS ; Get Low Bits + MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + JZ @GPC_NO_RIGHT1BITS ; Skip if No Pixels to set + + MOV AH, [BP].GPC_ColorF ; Get Foreground Color + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@GPC_NO_RIGHT1BITS: + + XOR AL, CH ; Invert mask for Background + JZ @GPC_NO_RIGHT0BITS ; Hey, no need for this + + MOV AH, [BP].GPC_ColorB ; Get background Color + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@GPC_NO_RIGHT0BITS: + DEC DI ; Adjust for Next Line Advance + +@GPC_NEXT_LINE: + ADD DI, [BP].GPC_Width ; Point to Next Line + XOR CH, CHAR_BITS ; Flip the Clip mask back + + DEC [BP].GPC_Lines ; Count Down Lines + JZ @GPC_EXIT ; Ok... Done! + + JMP @GPC_DECODE_CHAR_BYTE ; Again! Hey! + +@GPC_EXIT: + ADD SP, 08 ; Deallocate stack workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 10 ; Exit and Clean up Stack + +GPRINTC ENDP + + +;========================================== +;TGPRINTC (CharNum%, Xpos%, Ypos%, ColorF%) +;========================================== +; +; Transparently draws an ASCII Text Character using the +; currently selected 8x8 font on the active display page. +; +; ENTRY: CharNum = ASCII character # to draw +; Xpos = X position to draw Character at +; Ypos = Y position of to draw Character at +; ColorF = Color to draw text character in +; +; EXIT: No meaningful values returned +; + +TGP_STACK STRUC + TGP_Width DW ? ; Screen Width-1 + TGP_Lines DB ?,? ; Scan lines to Decode + TGP_T_SETS DW ? ; Saved Charset Segment + TGP_T_SETO DW ? ; Saved Charset Offset + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + TGP_ColorF DB ?,? ; Text Color + TGP_Ypos DW ? ; Y Position to Print at + TGP_Xpos DW ? ; X position to Print at + TGP_Char DB ?,? ; Character to Print +TGP_STACK ENDS + + PUBLIC TGPRINTC + +TGPRINTC PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + SUB SP, 8 ; Allocate WorkSpace on Stack + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + MOV AX, SCREEN_WIDTH ; Get Logical Line Width + MOV BX, AX ; BX = Screen Width + DEC BX ; = Screen Width-1 + MOV [BP].TGP_Width, BX ; Save for later use + + MUL [BP].TGP_Ypos ; Start of Line = Ypos * Width + ADD DI, AX ; DI -> Start of Line Ypos + + MOV AX, [BP].TGP_Xpos ; Get Xpos of Character + MOV CX, AX ; Save Copy of Xpos + SHR AX, 2 ; Bytes into Line = Xpos/4 + ADD DI, AX ; DI -> (Xpos, Ypos) + + ;Get Source ADDR of Character Bit Map & Save + + MOV AL, [BP].TGP_Char ; Get Character # + TEST AL, 080h ; Is Hi Bit Set? + JZ @TGP_LowChar ; Nope, use low char set ptr + + AND AL, 07Fh ; Mask Out Hi Bit + MOV BX, CHARSET_HI ; BX = Char Set Ptr:Offset + MOV DX, CHARSET_HI+2 ; DX = Char Set Ptr:Segment + JMP s @TGP_Set_Char ; Go Setup Character Ptr + +@TGP_LowChar: + + MOV BX, CHARSET_LOW ; BX = Char Set Ptr:Offset + MOV DX, CHARSET_LOW+2 ; DX = Char Set Ptr:Segment + +@TGP_Set_Char: + MOV [BP].TGP_T_SETS, DX ; Save Segment on Stack + + MOV AH, 0 ; Valid #'s are 0..127 + SHL AX, 3 ; * 8 Bytes Per Bitmap + ADD BX, AX ; BX = Offset of Selected char + MOV [BP].TGP_T_SETO, BX ; Save Offset on Stack + + AND CX, PLANE_BITS ; Get Plane # + MOV CH, ALL_PLANES ; Get Initial Plane mask + SHL CH, CL ; And shift into position + AND CH, ALL_PLANES ; And mask to lower nibble + + MOV AL, 04 ; 4-Plane # = # of initial + SUB AL, CL ; shifts to align bit mask + MOV CL, AL ; Shift Count for SHL + + ;Get segment of character map + + OUT_8 SC_Index, MAP_MASK ; Setup Plane selections + INC DX ; DX -> SC_Data + + MOV AL, 08 ; 8 Lines to Process + MOV [BP].TGP_Lines, AL ; Save on Stack + + MOV DS, [BP].TGP_T_SETS ; Point to character set + +@TGP_DECODE_CHAR_BYTE: + + MOV SI, [BP].TGP_T_SETO ; Get DS:SI = String + + MOV BH, [SI] ; Get Bit Map + INC SI ; Point to Next Line + MOV [BP].TGP_T_SETO, SI ; And save new Pointer... + + MOV AH, [BP].TGP_ColorF ; Get Foreground Color + + CLR BL ; Clear BL + ROL BX, CL ; BL holds left edge bits + MOV SI, BX ; Use as Table Index + AND SI, CHAR_BITS ; Get Low Bits + MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + JZ @TGP_NO_LEFT1BITS ; Skip if No Pixels to set + + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + + ;Now Do Middle/Last Band + +@TGP_NO_LEFT1BITS: + + INC DI ; Point to next Byte + ROL BX, 4 ; Shift 4 bits + + MOV SI, BX ; Make Lookup Pointer + AND SI, CHAR_BITS ; Get Low Bits + MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + JZ @TGP_NO_MIDDLE1BITS ; Skip if no pixels to set + + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@TGP_NO_MIDDLE1BITS: + XOR CH, ALL_PLANES ; Invert Clip Mask + CMP CL, 4 ; Aligned by 4? + JZ @TGP_NEXT_LINE ; If so, Exit now.. + + INC DI ; Point to next Byte + ROL BX, 4 ; Shift 4 bits + + MOV SI, BX ; Make Lookup Pointer + AND SI, CHAR_BITS ; Get Low Bits + MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + JZ @TGP_NO_RIGHT1BITS ; Skip if No Pixels to set + + OUT DX, AL ; Set up Screen Mask + MOV ES:[DI], AH ; Write Foreground color + +@TGP_NO_RIGHT1BITS: + + DEC DI ; Adjust for Next Line Advance + +@TGP_NEXT_LINE: + ADD DI, [BP].TGP_Width ; Point to Next Line + XOR CH, CHAR_BITS ; Flip the Clip mask back + + DEC [BP].TGP_Lines ; Count Down Lines + JZ @TGP_EXIT ; Ok... Done! + + JMP @TGP_DECODE_CHAR_BYTE ; Again! Hey! + +@TGP_EXIT: + ADD SP, 08 ; Deallocate stack workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 8 ; Exit and Clean up Stack + +TGPRINTC ENDP + + +;=============================================================== +;PRINT_STR (SEG String, MaxLen%, Xpos%, Ypos%, ColorF%, ColorB%) +;=============================================================== +; +; Routine to quickly Print a null terminated ASCII string on the +; active display page up to a maximum length. +; +; ENTRY: String = Far Pointer to ASCII string to print +; MaxLen = # of characters to print if no null found +; Xpos = X position to draw Text at +; Ypos = Y position of to draw Text at +; ColorF = Color to draw text in +; ColorB = Color to set background to +; +; EXIT: No meaningful values returned +; + +PS_STACK STRUC + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + PS_ColorB DW ? ; Background Color + PS_ColorF DW ? ; Text Color + PS_Ypos DW ? ; Y Position to Print at + PS_Xpos DW ? ; X position to Print at + PS_Len DW ? ; Maximum Length of string to print + PS_Text DW ?,? ; Far Ptr to Text String +PS_STACK ENDS + + PUBLIC PRINT_STR + +PRINT_STR PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + MOV BP, SP ; Set up Stack Frame + +@PS_Print_It: + + MOV CX, [BP].PS_Len ; Get Remaining text Length + JCXZ @PS_Exit ; Exit when out of text + + LES DI, d [BP].PS_Text ; ES:DI -> Current Char in Text + MOV AL, ES:[DI] ; AL = Text Character + AND AX, 00FFh ; Clear High Word + JZ @PS_Exit ; Exit if null character + + DEC [BP].PS_Len ; Remaining Text length-- + INC [BP].PS_Text ; Point to Next text char + + ; Set up Call to GPRINTC + + PUSH AX ; Set Character Parameter + MOV BX, [BP].PS_Xpos ; Get Xpos + PUSH BX ; Set Xpos Parameter + ADD BX, 8 ; Advance 1 Char to Right + MOV [BP].PS_Xpos, BX ; Save for next time through + + MOV BX, [BP].PS_Ypos ; Get Ypos + PUSH BX ; Set Ypos Parameter + + MOV BX, [BP].PS_ColorF ; Get Text Color + PUSH BX ; Set ColorF Parameter + + MOV BX, [BP].PS_ColorB ; Get Background Color + PUSH BX ; Set ColorB Parameter + + CALL f GPRINTC ; Print Character! + JMP s @PS_Print_It ; Process next character + +@PS_Exit: + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 14 ; Exit and Clean up Stack + +PRINT_STR ENDP + + +;================================================================ +;TPRINT_STR (SEG String, MaxLen%, Xpos%, Ypos%, ColorF%, ColorB%) +;================================================================ +; +; Routine to quickly transparently Print a null terminated ASCII +; string on the active display page up to a maximum length. +; +; ENTRY: String = Far Pointer to ASCII string to print +; MaxLen = # of characters to print if no null found +; Xpos = X position to draw Text at +; Ypos = Y position of to draw Text at +; ColorF = Color to draw text in +; +; EXIT: No meaningful values returned +; + +TPS_STACK STRUC + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + TPS_ColorF DW ? ; Text Color + TPS_Ypos DW ? ; Y Position to Print at + TPS_Xpos DW ? ; X position to Print at + TPS_Len DW ? ; Maximum Length of string to print + TPS_Text DW ?,? ; Far Ptr to Text String +TPS_STACK ENDS + + PUBLIC TPRINT_STR + +TPRINT_STR PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + MOV BP, SP ; Set up Stack Frame + +@TPS_Print_It: + + MOV CX, [BP].TPS_Len ; Get Remaining text Length + JCXZ @TPS_Exit ; Exit when out of text + + LES DI, d [BP].TPS_Text ; ES:DI -> Current Char in Text + MOV AL, ES:[DI] ; AL = Text Character + AND AX, 00FFh ; Clear High Word + JZ @TPS_Exit ; Exit if null character + + DEC [BP].TPS_Len ; Remaining Text length-- + INC [BP].TPS_Text ; Point to Next text char + + ; Set up Call to TGPRINTC + + PUSH AX ; Set Character Parameter + MOV BX, [BP].TPS_Xpos ; Get Xpos + PUSH BX ; Set Xpos Parameter + ADD BX, 8 ; Advance 1 Char to Right + MOV [BP].TPS_Xpos, BX ; Save for next time through + + MOV BX, [BP].TPS_Ypos ; Get Ypos + PUSH BX ; Set Ypos Parameter + + MOV BX, [BP].TPS_ColorF ; Get Text Color + PUSH BX ; Set ColorF Parameter + + CALL f TGPRINTC ; Print Character! + JMP s @TPS_Print_It ; Process next character + +@TPS_Exit: + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 12 ; Exit and Clean up Stack + +TPRINT_STR ENDP + + +;=========================================== +;SET_DISPLAY_FONT(SEG FontData, FontNumber%) +;=========================================== +; +; Allows the user to specify their own font data for +; wither the lower or upper 128 characters. +; +; ENTRY: FontData = Far Pointer to Font Bitmaps +; FontNumber = Which half of set this is +; = 0, Lower 128 characters +; = 1, Upper 128 characters +; +; EXIT: No meaningful values returned +; + +SDF_STACK STRUC + DW ? ; BP + DD ? ; Caller + SDF_Which DW ? ; Hi Table/Low Table Flag + SDF_Font DD ? ; Far Ptr to Font Table +SDF_STACK ENDS + + PUBLIC SET_DISPLAY_FONT + +SET_DISPLAY_FONT PROC FAR + + PUSH BP ; Preserve Registers + MOV BP, SP ; Set up Stack Frame + + LES DI, [BP].SDF_Font ; Get Far Ptr to Font + + MOV SI, o CHARSET_LOW ; Assume Lower 128 chars + TEST [BP].SDF_Which, 1 ; Font #1 selected? + JZ @SDF_Set_Font ; If not, skip ahead + + MOV SI, o CHARSET_HI ; Ah, really it's 128-255 + +@SDF_Set_Font: + MOV [SI], DI ; Set Font Pointer Offset + MOV [SI+2], ES ; Set Font Pointer Segment + + POP BP ; Restore Registers + RET 6 ; We are Done.. Outa here + +SET_DISPLAY_FONT ENDP + + + ; ===== BITMAP (SPRITE) DISPLAY ROUTINES ===== + +;====================================================== +;DRAW_BITMAP (SEG Image, Xpos%, Ypos%, Width%, Height%) +;====================================================== +; +; Draws a variable sized Graphics Bitmap such as a +; picture or an Icon on the current Display Page in +; Mode X. The Bitmap is stored in a linear byte array +; corresponding to (0,0) (1,0), (2,0) .. (Width, Height) +; This is the same linear manner as mode 13h graphics. +; +; ENTRY: Image = Far Pointer to Bitmap Data +; Xpos = X position to Place Upper Left pixel at +; Ypos = Y position to Place Upper Left pixel at +; Width = Width of the Bitmap in Pixels +; Height = Height of the Bitmap in Pixels +; +; EXIT: No meaningful values returned +; + +DB_STACK STRUC + DB_LineO DW ? ; Offset to Next Line + DB_PixCount DW ? ; (Minimum) # of Pixels/Line + DB_Start DW ? ; Addr of Upper Left Pixel + DB_PixSkew DW ? ; # of bytes to Adjust EOL + DB_SkewFlag DW ? ; Extra Pix on Plane Flag + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + DB_Height DW ? ; Height of Bitmap in Pixels + DB_Width DW ? ; Width of Bitmap in Pixels + DB_Ypos DW ? ; Y position to Draw Bitmap at + DB_Xpos DW ? ; X position to Draw Bitmap at + DB_Image DD ? ; Far Pointer to Graphics Bitmap +DB_STACK ENDS + + PUBLIC DRAW_BITMAP + +DRAW_BITMAP PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + SUB SP, 10 ; Allocate workspace + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + CLD ; Direction Flag = Forward + + MOV AX, [BP].DB_Ypos ; Get UL Corner Ypos + MUL SCREEN_WIDTH ; AX = Offset to Line Ypos + + MOV BX, [BP].DB_Xpos ; Get UL Corner Xpos + MOV CL, BL ; Save Plane # in CL + SHR BX, 2 ; Xpos/4 = Offset Into Line + + ADD DI, AX ; ES:DI -> Start of Line + ADD DI, BX ; ES:DI -> Upper Left Pixel + MOV [BP].DB_Start, DI ; Save Starting Addr + + ; Compute line to line offset + + MOV BX, [BP].DB_Width ; Get Width of Image + MOV DX, BX ; Save Copy in DX + SHR BX, 2 ; /4 = width in bands + MOV AX, SCREEN_WIDTH ; Get Screen Width + SUB AX, BX ; - (Bitmap Width/4) + + MOV [BP].DB_LineO, AX ; Save Line Width offset + MOV [BP].DB_PixCount, BX ; Minimum # pix to copy + + AND DX, PLANE_BITS ; Get "partial band" size (0-3) + MOV [BP].DB_PixSkew, DX ; Also End of Line Skew + MOV [BP].DB_SkewFlag, DX ; Save as Flag/Count + + AND CX, PLANE_BITS ; CL = Starting Plane # + MOV AX, MAP_MASK_PLANE2 ; Plane Mask & Plane Select + SHL AH, CL ; Select correct Plane + OUT_16 SC_Index, AX ; Select Plane... + MOV BH, AH ; BH = Saved Plane Mask + MOV BL, 4 ; BL = Planes to Copy + +@DB_COPY_PLANE: + + LDS SI, [BP].DB_Image ; DS:SI-> Source Image + MOV DX, [BP].DB_Height ; # of Lines to Copy + MOV DI, [BP].DB_Start ; ES:DI-> Dest pos + +@DB_COPY_LINE: + MOV CX, [BP].DB_PixCount ; Min # to copy + + TEST CL, 0FCh ; 16+PixWide? + JZ @DB_COPY_REMAINDER ; Nope... + + ; Pixel Copy loop has been unrolled to x4 + +@DB_COPY_LOOP: + MOVSB ; Copy Bitmap Pixel + ADD SI, 3 ; Skip to Next Byte in same plane + MOVSB ; Copy Bitmap Pixel + ADD SI, 3 ; Skip to Next Byte in same plane + MOVSB ; Copy Bitmap Pixel + ADD SI, 3 ; Skip to Next Byte in same plane + MOVSB ; Copy Bitmap Pixel + ADD SI, 3 ; Skip to Next Byte in same plane + + SUB CL, 4 ; Pixels to Copy=-4 + TEST CL, 0FCh ; 4+ Pixels Left? + JNZ @DB_COPY_LOOP ; if so, do another block + +@DB_COPY_REMAINDER: + JCXZ @DB_NEXT_LINE ; Any Pixels left on line + +@DB_COPY2: + MOVSB ; Copy Bitmap Pixel + ADD SI,3 ; Skip to Next Byte in same plane + LOOPx CX, @DB_COPY2 ; Pixels to Copy--, Loop until done + +@DB_NEXT_LINE: + + ; any Partial Pixels? (some planes only) + + OR CX, [BP].DB_SkewFlag ; Get Skew Count + JZ @DB_NEXT2 ; if no partial pixels + + MOVSB ; Copy Bitmap Pixel + DEC DI ; Back up to align + DEC SI ; Back up to align + +@DB_NEXT2: + ADD SI, [BP].DB_PixSkew ; Adjust Skew + ADD DI, [BP].DB_LineO ; Set to Next Display Line + LOOPx DX, @DB_COPY_LINE ; Lines to Copy--, Loop if more + + ; Copy Next Plane.... + + DEC BL ; Planes to Go-- + JZ @DB_Exit ; Hey! We are done + + ROL BH, 1 ; Next Plane in line... + OUT_8 SC_Data, BH ; Select Plane + + CMP AL, 12h ; Carry Set if AL=11h + ADC [BP].DB_Start, 0 ; Screen Addr =+Carry + INC w [BP].DB_Image ; Start @ Next Byte + + SUB [BP].DB_SkewFlag, 1 ; Reduce Planes to Skew + ADC [BP].DB_SkewFlag, 0 ; Back to 0 if it was -1 + + JMP s @DB_COPY_PLANE ; Go Copy the Next Plane + +@DB_Exit: + ADD SP, 10 ; Deallocate workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 12 ; Exit and Clean up Stack + +DRAW_BITMAP ENDP + + +;======================================================= +;TDRAW_BITMAP (SEG Image, Xpos%, Ypos%, Width%, Height%) +;======================================================= +; +; Transparently Draws a variable sized Graphics Bitmap +; such as a picture or an Icon on the current Display Page +; in Mode X. Pixels with a value of 0 are not drawn, +; leaving the previous "background" contents intact. +; +; The Bitmap format is the same as for the DRAW_BITMAP function. +; +; ENTRY: Image = Far Pointer to Bitmap Data +; Xpos = X position to Place Upper Left pixel at +; Ypos = Y position to Place Upper Left pixel at +; Width = Width of the Bitmap in Pixels +; Height = Height of the Bitmap in Pixels +; +; EXIT: No meaningful values returned +; + +TB_STACK STRUC + TB_LineO DW ? ; Offset to Next Line + TB_PixCount DW ? ; (Minimum) # of Pixels/Line + TB_Start DW ? ; Addr of Upper Left Pixel + TB_PixSkew DW ? ; # of bytes to Adjust EOL + TB_SkewFlag DW ? ; Extra Pix on Plane Flag + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + TB_Height DW ? ; Height of Bitmap in Pixels + TB_Width DW ? ; Width of Bitmap in Pixels + TB_Ypos DW ? ; Y position to Draw Bitmap at + TB_Xpos DW ? ; X position to Draw Bitmap at + TB_Image DD ? ; Far Pointer to Graphics Bitmap +TB_STACK ENDS + + PUBLIC TDRAW_BITMAP + +TDRAW_BITMAP PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + SUB SP, 10 ; Allocate workspace + MOV BP, SP ; Set up Stack Frame + + LES DI, d CURRENT_PAGE ; Point to Active VGA Page + CLD ; Direction Flag = Forward + + MOV AX, [BP].TB_Ypos ; Get UL Corner Ypos + MUL SCREEN_WIDTH ; AX = Offset to Line Ypos + + MOV BX, [BP].TB_Xpos ; Get UL Corner Xpos + MOV CL, BL ; Save Plane # in CL + SHR BX, 2 ; Xpos/4 = Offset Into Line + + ADD DI, AX ; ES:DI -> Start of Line + ADD DI, BX ; ES:DI -> Upper Left Pixel + MOV [BP].TB_Start, DI ; Save Starting Addr + + ; Compute line to line offset + + MOV BX, [BP].TB_Width ; Get Width of Image + MOV DX, BX ; Save Copy in DX + SHR BX, 2 ; /4 = width in bands + MOV AX, SCREEN_WIDTH ; Get Screen Width + SUB AX, BX ; - (Bitmap Width/4) + + MOV [BP].TB_LineO, AX ; Save Line Width offset + MOV [BP].TB_PixCount, BX ; Minimum # pix to copy + + AND DX, PLANE_BITS ; Get "partial band" size (0-3) + MOV [BP].TB_PixSkew, DX ; Also End of Line Skew + MOV [BP].TB_SkewFlag, DX ; Save as Flag/Count + + AND CX, PLANE_BITS ; CL = Starting Plane # + MOV AX, MAP_MASK_PLANE2 ; Plane Mask & Plane Select + SHL AH, CL ; Select correct Plane + OUT_16 SC_Index, AX ; Select Plane... + MOV BH, AH ; BH = Saved Plane Mask + MOV BL, 4 ; BL = Planes to Copy + +@TB_COPY_PLANE: + + LDS SI, [BP].TB_Image ; DS:SI-> Source Image + MOV DX, [BP].TB_Height ; # of Lines to Copy + MOV DI, [BP].TB_Start ; ES:DI-> Dest pos + + ; Here AH is set with the value to be considered + ; "Transparent". It can be changed! + + MOV AH, 0 ; Value to Detect 0 + +@TB_COPY_LINE: + MOV CX, [BP].TB_PixCount ; Min # to copy + + TEST CL, 0FCh ; 16+PixWide? + JZ @TB_COPY_REMAINDER ; Nope... + + ; Pixel Copy loop has been unrolled to x4 + +@TB_COPY_LOOP: + LODSB ; Get Pixel Value in AL + ADD SI, 3 ; Skip to Next Byte in same plane + CMP AL, AH ; It is "Transparent"? + JE @TB_SKIP_01 ; Skip ahead if so + MOV ES:[DI], AL ; Copy Pixel to VGA screen + +@TB_SKIP_01: + LODSB ; Get Pixel Value in AL + ADD SI, 3 ; Skip to Next Byte in same plane + CMP AL, AH ; It is "Transparent"? + JE @TB_SKIP_02 ; Skip ahead if so + MOV ES:[DI+1], AL ; Copy Pixel to VGA screen + +@TB_SKIP_02: + LODSB ; Get Pixel Value in AL + ADD SI, 3 ; Skip to Next Byte in same plane + CMP AL, AH ; It is "Transparent"? + JE @TB_SKIP_03 ; Skip ahead if so + MOV ES:[DI+2], AL ; Copy Pixel to VGA screen + +@TB_SKIP_03: + LODSB ; Get Pixel Value in AL + ADD SI, 3 ; Skip to Next Byte in same plane + CMP AL, AH ; It is "Transparent"? + JE @TB_SKIP_04 ; Skip ahead if so + MOV ES:[DI+3], AL ; Copy Pixel to VGA screen + +@TB_SKIP_04: + ADD DI, 4 ; Adjust Pixel Write Location + SUB CL, 4 ; Pixels to Copy=-4 + TEST CL, 0FCh ; 4+ Pixels Left? + JNZ @TB_COPY_LOOP ; if so, do another block + +@TB_COPY_REMAINDER: + JCXZ @TB_NEXT_LINE ; Any Pixels left on line + +@TB_COPY2: + LODSB ; Get Pixel Value in AL + ADD SI, 3 ; Skip to Next Byte in same plane + CMP AL, AH ; It is "Transparent"? + JE @TB_SKIP_05 ; Skip ahead if so + MOV ES:[DI], AL ; Copy Pixel to VGA screen + +@TB_SKIP_05: + INC DI ; Advance Dest Addr + LOOPx CX, @TB_COPY2 ; Pixels to Copy--, Loop until done + +@TB_NEXT_LINE: + + ; any Partial Pixels? (some planes only) + + OR CX, [BP].TB_SkewFlag ; Get Skew Count + JZ @TB_NEXT2 ; if no partial pixels + + LODSB ; Get Pixel Value in AL + DEC SI ; Backup to Align + CMP AL, AH ; It is "Transparent"? + JE @TB_NEXT2 ; Skip ahead if so + MOV ES:[DI], AL ; Copy Pixel to VGA screen + +@TB_NEXT2: + ADD SI, [BP].TB_PixSkew ; Adjust Skew + ADD DI, [BP].TB_LineO ; Set to Next Display Line + LOOPx DX, @TB_COPY_LINE ; Lines to Copy--, Loop if More + + ;Copy Next Plane.... + + DEC BL ; Planes to Go-- + JZ @TB_Exit ; Hey! We are done + + ROL BH, 1 ; Next Plane in line... + OUT_8 SC_Data, BH ; Select Plane + + CMP AL, 12h ; Carry Set if AL=11h + ADC [BP].TB_Start, 0 ; Screen Addr =+Carry + INC w [BP].TB_Image ; Start @ Next Byte + + SUB [BP].TB_SkewFlag, 1 ; Reduce Planes to Skew + ADC [BP].TB_SkewFlag, 0 ; Back to 0 if it was -1 + + JMP @TB_COPY_PLANE ; Go Copy the next Plane + +@TB_Exit: + ADD SP, 10 ; Deallocate workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 12 ; Exit and Clean up Stack + +TDRAW_BITMAP ENDP + + + ; ==== VIDEO MEMORY to VIDEO MEMORY COPY ROUTINES ===== + +;================================== +;COPY_PAGE (SourcePage%, DestPage%) +;================================== +; +; Duplicate on display page onto another +; +; ENTRY: SourcePage = Display Page # to Duplicate +; DestPage = Display Page # to hold copy +; +; EXIT: No meaningful values returned +; + +CP_STACK STRUC + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + CP_DestP DW ? ; Page to hold copied image + CP_SourceP DW ? ; Page to Make copy from +CP_STACK ENDS + + PUBLIC COPY_PAGE + +COPY_PAGE PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + MOV BP, SP ; Set up Stack Frame + CLD ; Block Xfer Forwards + + ; Make sure Page #'s are valid + + MOV AX, [BP].CP_SourceP ; Get Source Page # + CMP AX, LAST_PAGE ; is it > Max Page #? + JAE @CP_Exit ; if so, abort + + MOV BX, [BP].CP_DestP ; Get Destination Page # + CMP BX, LAST_PAGE ; is it > Max Page #? + JAE @CP_Exit ; if so, abort + + CMP AX, BX ; Pages #'s the same? + JE @CP_Exit ; if so, abort + + ; Setup DS:SI and ES:DI to Video Pages + + SHL BX, 1 ; Scale index to Word + MOV DI, PAGE_ADDR[BX] ; Offset to Dest Page + + MOV BX, AX ; Index to Source page + SHL BX, 1 ; Scale index to Word + MOV SI, PAGE_ADDR[BX] ; Offset to Source Page + + MOV CX, PAGE_SIZE ; Get size of Page + MOV AX, CURRENT_SEGMENT ; Get Video Mem Segment + MOV ES, AX ; ES:DI -> Dest Page + MOV DS, AX ; DS:SI -> Source Page + + ; Setup VGA registers for Mem to Mem copy + + OUT_16 GC_Index, LATCHES_ON ; Data from Latches = on + OUT_16 SC_Index, ALL_PLANES_ON ; Copy all Planes + + ; Note.. Do *NOT* use MOVSW or MOVSD - they will + ; Screw with the latches which are 8 bits x 4 + + REP MOVSB ; Copy entire Page! + + ; Reset VGA for normal memory access + + OUT_16 GC_Index, LATCHES_OFF ; Data from Latches = off + +@CP_Exit: + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 4 ; Exit and Clean up Stack + +COPY_PAGE ENDP + + +;========================================================================== +;COPY_BITMAP (SourcePage%, X1%, Y1%, X2%, Y2%, DestPage%, DestX1%, DestY1%) +;========================================================================== +; +; Copies a Bitmap Image from one Display Page to Another +; This Routine is Limited to copying Images with the same +; Plane Alignment. To Work: (X1 MOD 4) must = (DestX1 MOD 4) +; Copying an Image to the Same Page is supported, but results +; may be defined when the when the rectangular areas +; (X1, Y1) - (X2, Y2) and (DestX1, DestY1) - +; (DestX1+(X2-X1), DestY1+(Y2-Y1)) overlap... +; No Paramter checking to done to insure that +; X2 >= X1 and Y2 >= Y1. Be Careful... +; +; ENTRY: SourcePage = Display Page # with Source Image +; X1 = Upper Left Xpos of Source Image +; Y1 = Upper Left Ypos of Source Image +; X2 = Lower Right Xpos of Source Image +; Y2 = Lower Right Ypos of Source Image +; DestPage = Display Page # to copy Image to +; DestX1 = Xpos to Copy UL Corner of Image to +; DestY1 = Ypos to Copy UL Corner of Image to +; +; EXIT: AX = Success Flag: 0 = Failure / -1= Success +; + +CB_STACK STRUC + CB_Height DW ? ; Height of Image in Lines + CB_Width DW ? ; Width of Image in "bands" + DW ?x4 ; DI, SI, DS, BP + DD ? ; Caller + CB_DestY1 DW ? ; Destination Ypos + CB_DestX1 DW ? ; Destination Xpos + CB_DestP DW ? ; Page to Copy Bitmap To + CB_Y2 DW ? ; LR Ypos of Image + CB_X2 DW ? ; LR Xpos of Image + CB_Y1 DW ? ; UL Ypos of Image + CB_X1 DW ? ; UL Xpos of Image + CB_SourceP DW ? ; Page containing Source Bitmap +CB_STACK ENDS + + PUBLIC COPY_BITMAP + +COPY_BITMAP PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + SUB SP, 4 ; Allocate WorkSpace on Stack + MOV BP, SP ; Set up Stack Frame + + ; Prep Registers (and keep jumps short!) + + MOV ES, CURRENT_SEGMENT ; ES -> VGA Ram + CLD ; Block Xfer Forwards + + ; Make sure Parameters are valid + + MOV BX, [BP].CB_SourceP ; Get Source Page # + CMP BX, LAST_PAGE ; is it > Max Page #? + JAE @CB_Abort ; if so, abort + + MOV CX, [BP].CB_DestP ; Get Destination Page # + CMP CX, LAST_PAGE ; is it > Max Page #? + JAE @CB_Abort ; if so, abort + + MOV AX, [BP].CB_X1 ; Get Source X1 + XOR AX, [BP].CB_DestX1 ; Compare Bits 0-1 + AND AX, PLANE_BITS ; Check Plane Bits + JNZ @CB_Abort ; They should cancel out + + ; Setup for Copy processing + + OUT_8 SC_INDEX, MAP_MASK ; Set up for Plane Select + OUT_16 GC_Index, LATCHES_ON ; Data from Latches = on + + ; Compute Info About Images, Setup ES:SI & ES:DI + + MOV AX, [BP].CB_Y2 ; Height of Bitmap in lines + SUB AX, [BP].CB_Y1 ; is Y2 - Y1 + 1 + INC AX ; (add 1 since were not 0 based) + MOV [BP].CB_Height, AX ; Save on Stack for later use + + MOV AX, [BP].CB_X2 ; Get # of "Bands" of 4 Pixels + MOV DX, [BP].CB_X1 ; the Bitmap Occupies as X2-X1 + SHR AX, 2 ; Get X2 Band (X2 / 4) + SHR DX, 2 ; Get X1 Band (X1 / 4) + SUB AX, DX ; AX = # of Bands - 1 + INC AX ; AX = # of Bands + MOV [BP].CB_Width, AX ; Save on Stack for later use + + SHL BX, 1 ; Scale Source Page to Word + MOV SI, PAGE_ADDR[BX] ; SI = Offset of Source Page + MOV AX, [BP].CB_Y1 ; Get Source Y1 Line + MUL SCREEN_WIDTH ; AX = Offset to Line Y1 + ADD SI, AX ; SI = Offset to Line Y1 + MOV AX, [BP].CB_X1 ; Get Source X1 + SHR AX, 2 ; X1 / 4 = Byte offset + ADD SI, AX ; SI = Byte Offset to (X1,Y1) + + MOV BX, CX ; Dest Page Index to BX + SHL BX, 1 ; Scale Source Page to Word + MOV DI, PAGE_ADDR[BX] ; DI = Offset of Dest Page + MOV AX, [BP].CB_DestY1 ; Get Dest Y1 Line + MUL SCREEN_WIDTH ; AX = Offset to Line Y1 + ADD DI, AX ; DI = Offset to Line Y1 + MOV AX, [BP].CB_DestX1 ; Get Dest X1 + SHR AX, 2 ; X1 / 4 = Byte offset + ADD DI, AX ; DI = Byte Offset to (D-X1,D-Y1) + + MOV CX, [BP].CB_Width ; CX = Width of Image (Bands) + DEC CX ; CX = 1? + JE @CB_Only_One_Band ; 0 Means Image Width of 1 Band + + MOV BX, [BP].CB_X1 ; Get Source X1 + AND BX, PLANE_BITS ; Aligned? (bits 0-1 = 00?) + JZ @CB_Check_Right ; if so, check right alignment + JNZ @CB_Left_Band ; not aligned? well.. + +@CB_Abort: + CLR AX ; Return False (Failure) + JMP @CB_Exit ; and Finish Up + + ; Copy when Left & Right Clip Masks overlap... + +@CB_Only_One_Band: + MOV BX, [BP].CB_X1 ; Get Left Clip Mask + AND BX, PLANE_BITS ; Mask out Row # + MOV AL, Left_Clip_Mask[BX] ; Get Left Edge Mask + MOV BX, [BP].CB_X2 ; Get Right Clip Mask + AND BX, PLANE_BITS ; Mask out Row # + AND AL, Right_Clip_Mask[BX] ; Get Right Edge Mask byte + + OUT_8 SC_Data, AL ; Clip For Left & Right Masks + + MOV CX, [BP].CB_Height ; CX = # of Lines to Copy + MOV DX, SCREEN_WIDTH ; DX = Width of Screen + CLR BX ; BX = Offset into Image + +@CB_One_Loop: + MOV AL, ES:[SI+BX] ; Load Latches + MOV ES:[DI+BX], AL ; Unload Latches + ADD BX, DX ; Advance Offset to Next Line + LOOPjz CX, @CB_One_Done ; Exit Loop if Finished + + MOV AL, ES:[SI+BX] ; Load Latches + MOV ES:[DI+BX], AL ; Unload Latches + ADD BX, DX ; Advance Offset to Next Line + LOOPx CX, @CB_One_Loop ; Loop until Finished + +@CB_One_Done: + JMP @CB_Finish ; Outa Here! + + ; Copy Left Edge of Bitmap + +@CB_Left_Band: + + OUT_8 SC_Data, Left_Clip_Mask[BX] ; Set Left Edge Plane Mask + + MOV CX, [BP].CB_Height ; CX = # of Lines to Copy + MOV DX, SCREEN_WIDTH ; DX = Width of Screen + CLR BX ; BX = Offset into Image + +@CB_Left_Loop: + MOV AL, ES:[SI+BX] ; Load Latches + MOV ES:[DI+BX], AL ; Unload Latches + ADD BX, DX ; Advance Offset to Next Line + LOOPjz CX, @CB_Left_Done ; Exit Loop if Finished + + MOV AL, ES:[SI+BX] ; Load Latches + MOV ES:[DI+BX], AL ; Unload Latches + ADD BX, DX ; Advance Offset to Next Line + LOOPx CX, @CB_Left_Loop ; Loop until Finished + +@CB_Left_Done: + INC DI ; Move Dest Over 1 band + INC SI ; Move Source Over 1 band + DEC [BP].CB_Width ; Band Width-- + + ; Determine if Right Edge of Bitmap needs special copy + +@CB_Check_Right: + MOV BX, [BP].CB_X2 ; Get Source X2 + AND BX, PLANE_BITS ; Aligned? (bits 0-1 = 11?) + CMP BL, 03h ; Plane = 3? + JE @CB_Copy_Middle ; Copy the Middle then! + + ; Copy Right Edge of Bitmap + +@CB_Right_Band: + + OUT_8 SC_Data, Right_Clip_Mask[BX] ; Set Right Edge Plane Mask + + DEC [BP].CB_Width ; Band Width-- + MOV CX, [BP].CB_Height ; CX = # of Lines to Copy + MOV DX, SCREEN_WIDTH ; DX = Width of Screen + MOV BX, [BP].CB_Width ; BX = Offset to Right Edge + +@CB_Right_Loop: + MOV AL, ES:[SI+BX] ; Load Latches + MOV ES:[DI+BX], AL ; Unload Latches + ADD BX, DX ; Advance Offset to Next Line + LOOPjz CX, @CB_Right_Done ; Exit Loop if Finished + + MOV AL, ES:[SI+BX] ; Load Latches + MOV ES:[DI+BX], AL ; Unload Latches + ADD BX, DX ; Advance Offset to Next Line + LOOPx CX, @CB_Right_Loop ; Loop until Finished + +@CB_Right_Done: + + ; Copy the Main Block of the Bitmap + +@CB_Copy_Middle: + + MOV CX, [BP].CB_Width ; Get Width Remaining + JCXZ @CB_Finish ; Exit if Done + + OUT_8 SC_Data, ALL_PLANES ; Copy all Planes + + MOV DX, SCREEN_WIDTH ; Get Width of Screen minus + SUB DX, CX ; Image width (for Adjustment) + MOV AX, [BP].CB_Height ; AX = # of Lines to Copy + MOV BX, CX ; BX = Quick REP reload count + MOV CX, ES ; Move VGA Segment + MOV DS, CX ; Into DS + + ; Actual Copy Loop. REP MOVSB does the work + +@CB_Middle_Copy: + MOV CX, BX ; Recharge Rep Count + REP MOVSB ; Move Bands + LOOPjz AX, @CB_Finish ; Exit Loop if Finished + + ADD SI, DX ; Adjust DS:SI to Next Line + ADD DI, DX ; Adjust ES:DI to Next Line + + MOV CX, BX ; Recharge Rep Count + REP MOVSB ; Move Bands + + ADD SI, DX ; Adjust DS:SI to Next Line + ADD DI, DX ; Adjust ES:DI to Next Line + LOOPx AX, @CB_Middle_Copy ; Copy Lines until Done + +@CB_Finish: + OUT_16 GC_Index, LATCHES_OFF ; Data from Latches = on + +@CB_Exit: + ADD SP, 04 ; Deallocate stack workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + RET 16 ; Exit and Clean up Stack + +COPY_BITMAP ENDP + + END ; End of Code Segment diff --git a/modex105/MODEX.BI b/modex105/MODEX.BI new file mode 100755 index 00000000..6b1d7afe --- /dev/null +++ b/modex105/MODEX.BI @@ -0,0 +1,63 @@ + + ' ===== SCREEN RESOLUTIONS ===== + +CONST Mode320x200 = 0, Mode320x400 = 1 +CONST Mode360x200 = 2, Mode360x400 = 3 +CONST Mode320x240 = 4, Mode320x480 = 5 +CONST Mode360x240 = 6, Mode360x480 = 7 + + ' ===== MODE X SETUP ROUTINES ===== + +DECLARE FUNCTION SET.VGA.MODEX% ALIAS "SET_VGA_MODEX" (BYVAL ModeType%, BYVAL MaxXpos%, BYVAL MaxYpos%, BYVAL Pages%) +DECLARE FUNCTION SET.MODEX% ALIAS "SET_MODEX" (BYVAL Mode%) + + ' ===== BASIC GRAPHICS PRIMITIVES ===== + +DECLARE SUB CLEAR.VGA.SCREEN ALIAS "CLEAR_VGA_SCREEN" (BYVAL ColorNum%) +DECLARE SUB SET.POINT ALIAS "SET_POINT" (BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorNum%) +DECLARE FUNCTION READ.POINT% ALIAS "READ_POINT" (BYVAL Xpos%, BYVAL Ypos%) +DECLARE SUB FILL.BLOCK ALIAS "FILL_BLOCK" (BYVAL Xpos1%, BYVAL Ypos1%, BYVAL Xpos2%, BYVAL Ypos2%, BYVAL ColorNum%) +DECLARE SUB DRAW.LINE ALIAS "DRAW_LINE" (BYVAL Xpos1%, BYVAL Ypos1%, BYVAL Xpos2%, BYVAL Ypos2%, BYVAL ColorNum%) + + ' ===== DAC COLOR REGISTER ROUTINES ===== + +DECLARE SUB SET.DAC.REGISTER ALIAS "SET_DAC_REGISTER" (BYVAL RegNo%, BYVAL Red%, BYVAL Green%, BYVAL Blue%) +DECLARE SUB GET.DAC.REGISTER ALIAS "GET_DAC_REGISTER" (BYVAL RegNo%, Red%, Green%, Blue%) +DECLARE SUB LOAD.DAC.REGISTERS ALIAS "LOAD_DAC_REGISTERS" (SEG PalData AS ANY, BYVAL StartReg%, BYVAL EndReg%, BYVAL VSync%) +DECLARE SUB READ.DAC.REGISTERS ALIAS "READ_DAC_REGISTERS" (SEG PalData AS ANY, BYVAL StartReg%, BYVAL EndReg%) + + + ' ===== PAGE FLIPPING AND SCROLLING ROUTINES ===== + +DECLARE SUB SET.ACTIVE.PAGE ALIAS "SET_ACTIVE_PAGE" (BYVAL PageNo%) +DECLARE FUNCTION GET.ACTIVE.PAGE% ALIAS "GET_ACTIVE_PAGE" +DECLARE SUB SET.DISPLAY.PAGE ALIAS "SET_DISPLAY_PAGE" (BYVAL PageNo%) +DECLARE FUNCTION GET.DISPLAY.PAGE% ALIAS "GET_DISPLAY_PAGE" +DECLARE SUB SET.WINDOW ALIAS "SET_WINDOW" (BYVAL DisplayPage%, BYVAL XOffset%, BYVAL YOffset%) +DECLARE FUNCTION GET.X.OFFSET% ALIAS "GET_X_OFFSET" () +DECLARE FUNCTION GET.Y.OFFSET% ALIAS "GET_Y_OFFSET" () +DECLARE SUB SYNC.DISPLAY ALIAS "SYNC_DISPLAY" + + ' ===== TEXT DISPLAY ROUTINES ===== + +DECLARE SUB GPRINTC (BYVAL CharacterNum%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%, BYVAL ColorB%) +DECLARE SUB TGPRINTC (BYVAL CharacterNum%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%) +DECLARE SUB PRINT.STR ALIAS "PRINT_STR" (BYVAL StrSeg%, BYVAL StrOfs%, BYVAL MaxLen%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%, BYVAL ColorB%) +DECLARE SUB TPRINT.STR ALIAS "TPRINT_STR" (BYVAL StrSeg%, BYVAL StrOfs%, BYVAL MaxLen%, BYVAL Xpos%, BYVAL Ypos%, BYVAL ColorF%) +DECLARE SUB SET.DISPLAY.FONT ALIAS "SET_DISPLAY_FONT" (SEG FontData AS ANY, BYVAL FontNumber%) + + ' ===== BITMAP (SPRITE) DISPLAY ROUTINES ===== + +DECLARE SUB DRAW.BITMAP ALIAS "DRAW_BITMAP" (SEG Image AS ANY, BYVAL Xpos%, BYVAL Ypos%, BYVAL xWidth%, BYVAL Height%) +DECLARE SUB TDRAW.BITMAP ALIAS "TDRAW_BITMAP" (SEG Image AS ANY, BYVAL Xpos%, BYVAL Ypos%, BYVAL xWidth%, BYVAL Height%) + + ' ==== VIDEO MEMORY to VIDEO MEMORY COPY ROUTINES ===== + +DECLARE SUB COPY.PAGE ALIAS "COPY_PAGE" (BYVAL SourcePage%, BYVAL DestPage%) +DECLARE SUB COPY.BITMAP ALIAS "COPY_BITMAP" (BYVAL SourcePage%, BYVAL X1%, BYVAL Y1%, BYVAL X2%, BYVAL Y2%, BYVAL DestPage%, BYVAL DestX1%, BYVAL DestY1%) + + + + + + diff --git a/modex105/MODEX.H b/modex105/MODEX.H new file mode 100755 index 00000000..7de25a63 --- /dev/null +++ b/modex105/MODEX.H @@ -0,0 +1,76 @@ + +#ifndef __MODEX_H +#define __MODEX_H + + /* ===== SCREEN RESOLUTIONS ===== */ + +#define Mode_320x200 0 +#define Mode_320x400 1 +#define Mode_360x200 2 +#define Mode_360x400 3 +#define Mode_320x240 4 +#define Mode_320x480 5 +#define Mode_360x240 6 +#define Mode_360x480 7 + + /* ===== MODE X SETUP ROUTINES ===== */ + +int far pascal set_vga_modex (int Mode, int MaxXpos, int MaxYpos, int Pages); +int far pascal set_modex (int Mode); + + /* ===== BASIC GRAPHICS PRIMITIVES ===== */ + +void far pascal clear_vga_screen (int Color); +void far pascal set_point (int Xpos, int Ypos, int Color); +int far pascal read_point (int Xpos, int Ypos); +void far pascal fill_block (int Xpos1, int Ypos1, int Xpos2, int Ypos2, + int Color); +void far pascal draw_line (int Xpos1, int Ypos1, int Xpos2, int Ypos2, + int Color); + + /* ===== DAC COLOR REGISTER ROUTINES ===== */ + +void far pascal set_dac_register (int RegNo, int Red, int Green, int Blue); +void far pascal get_dac_register (int RegNo, int* Red, int* Green, int* Blue); +void far pascal load_dac_registers (char far *PalData, int StartReg, + int EndReg, int VSync); +void far pascal readd_dac_registers (char far *PalData, int StartReg, + int EndReg); + + /* ===== PAGE FLIPPING AND SCROLLING ROUTINES ===== */ + +void far pascal set_active_page (int PageNo); +int far pascal get_active_page (void); +void far pascal set_display_page (int PageNo); +int far pascal get_display_page (void); +void far pascal set_window (int DisplayPage, int XOffset, int YOffset); +int far pascal get_x_offset (void); +int far pascal get_y_offset (void); +void far pascal sync_display (void); + + /* ===== TEXT DISPLAY ROUTINES ===== */ + +void far pascal gprintc (int CharNum, int Xpos, int Ypos, int ColorF, + int ColorB); +void far pascal tgprintc (int CharNum, int Xpos, int Ypos, int ColorF); +void far pascal print_str (char far *Text, int MaxLen, int Xpos, int Ypos, + int ColorF, int ColorB); +void far pascal tprint_str (char far *Text, int MaxLen, int Xpos, int Ypos, + int ColorF); +void far pascal set_display_font (char far *FontData, int FontNumber); + + /* ===== BITMAP (SPRITE) DISPLAY ROUTINES ===== */ + +void far pascal draw_bitmap (char far *Image, int Xpos, int Ypos, + int Width, int Height); +void far pascal tdraw_bitmap (char far *Image, int Xpos, int Ypos, + int Width, int Height); + + /* ==== VIDEO MEMORY to VIDEO MEMORY COPY ROUTINES ===== */ + +void far pascal copy_page (int SourcePage, int DestPage); +void far pascal copy_bitmap (int SourcePage, int X1, int Y1, int X2, int Y2, + int DestPage, int DestX1, int DestY1); + + +#endif diff --git a/modex105/MODEX.LST b/modex105/MODEX.LST new file mode 100755 index 00000000..ce6d955c --- /dev/null +++ b/modex105/MODEX.LST @@ -0,0 +1,4122 @@ +Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 1 - 1 + + + ;======================================================== + ; MODEX.ASM - A Complete Mode X Library + ; + ; Version 1.04 Release, 3 May 1993, By Matt Pritchard + ; With considerable input from Michael Abrash + ; + ; The following information is donated to the public domain in + ; the hopes that save other programmers much frustration. + ; + ; If you do use this code in a product, it would be nice if + ; you include a line like "Mode X routines by Matt Pritchard" + ; in the credits. + ; + ; ========================================================= + ; + ; All of this code is designed to be assembled with MASM 5.10a + ; but TASM 3.0 could be used as well. + ; + ; The routines contained are designed for use in a MEDIUM model + ; program. All Routines are FAR, and is assumed that a DGROUP + ; data segment exists and that DS will point to it on entry. + ; + ; For all routines, the AX, BX, CX, DX, ES and FLAGS registers + ; will not be preserved, while the DS, BP, SI and DI registers + ; will be preserved. + ; + ; Unless specifically noted, All Parameters are assumed to be + ; "PASSED BY VALUE". That is, the actual value is placed on + ; the stack. When a reference is passed it is assumed to be + ; a near pointer to a variable in the DGROUP segment. + ; + ; Routines that return a single 16-Bit integer value will + ; return that value in the AX register. + ; + ; This code will *NOT* run on an 8086/8088 because 80286+ + ; specific instructions are used. If you have an 8088/86 + ; and VGA, you can buy an 80386-40 motherboard for about + ; $160 and move into the 90's. + ; + ; This code is reasonably optimized: Most drawing loops have + ; been unrolled once and memory references are minimized by + ; keeping stuff in registers when possible. + ; + ; Error Trapping varies by Routine. No Clipping is performed + ; so the caller should verify that all coordinates are valid. + ; + ; Several Macros are used to simplify common 2 or 3 instruction + ; sequences. Several Single letter Text Constants also + ; simplify common assembler expressions like "WORD PTR". + ; + ; ------------------ Mode X Variations ------------------ + ; + ; Mode # Screen Size Max Pages Aspect Ratio (X:Y) + ; + ; 0 320 x 200 4 Pages 1.2:1 + ; 1 320 x 400 2 Pages 2.4:1 + ; 2 360 x 200 3 Pages 1.35:1 + ; 3 360 x 400 1 Page 2.7:1 + ; 4 320 x 240 3 Pages 1:1 + ; 5 320 x 480 1 Page 2:1 + ; 6 360 x 240 3 Pages 1.125:1 + ; 7 360 x 480 1 Page 2.25:1 + ; + ; -------------------- The Legal Stuff ------------------ + ; + ; No warranty, either written or implied, is made as to + ; the accuracy and usability of this code product. Use + ; at your own risk. Batteries not included. Pepperoni + ; and extra cheese available for an additional charge. + ; + ; ----------------------- The Author -------------------- + ; + ; Matt Pritchard is a paid programmer who'd rather be + ; writing games. He can be reached at: P.O. Box 140264, + ; Irving, TX 75014 USA. Michael Abrash is a living + ; god, who now works for Bill Gates (Microsoft). + ; + ; -------------------- Revision History ----------------- + ; 4-12-93: v1.02 - SET_POINT & READ_POINT now saves DI + ; SET_MODEX now saves SI + ; 5-3-93: v1.04 - added LOAD_DAC_REGISTERS and + ; READ_DAC_REGISTERS. Expanded CLR Macro + ; to handle multiple registers + ; + + PAGE 255, 132 + + .MODEL Medium + .286 + + ; ===== MACROS ===== + + ; Macro to OUT a 16 bit value to an I/O port + + OUT_16 MACRO Register, Value + IFDIFI , ; If DX not setup + MOV DX, Register ; then Select Register + ENDIF + IFDIFI , ; If AX not setup + MOV AX, Value ; then Get Data Value + ENDIF + OUT DX, AX ; Set I/O Register(s) + ENDM + + ; Macro to OUT a 8 bit value to an I/O Port + + OUT_8 MACRO Register, Value + IFDIFI , ; If DX not setup + MOV DX, Register ; then Select Register + ENDIF + IFDIFI , ; If AL not Setup + MOV AL, Value ; then Get Data Value + ENDIF + OUT DX, AL ; Set I/O Register + ENDM + + ; macros to PUSH and POP multiple registers + + PUSHx MACRO R1, R2, R3, R4, R5, R6, R7, R8 + IFNB + PUSH R1 ; Save R1 + PUSHx R2, R3, R4, R5, R6, R7, R8 + ENDIF + ENDM + + POPx MACRO R1, R2, R3, R4, R5, R6, R7, R8 + IFNB + POP R1 ; Restore R1 + POPx R2, R3, R4, R5, R6, R7, R8 + ENDIF + ENDM + + ; Macro to Clear Registers to 0 + + CLR MACRO Register, R2, R3, R4, R5, R6 + IFNB + XOR Register, Register ; Set Register = 0 + CLR R2, R3, R4, R5, R6 + ENDIF + ENDM + + ; Macros to Decrement Counter & Jump on Condition + + LOOPx MACRO Register, Destination + DEC Register ; Counter-- + JNZ Destination ; Jump if not 0 + ENDM + + LOOPjz MACRO Register, Destination + DEC Register ; Counter-- + JZ Destination ; Jump if 0 + ENDM + + + ; ===== General Constants ===== + + = 0000 False EQU 0 + =-0001 True EQU -1 + = 0000 nil EQU 0 + + = BYTE PTR b EQU BYTE PTR + = WORD PTR w EQU WORD PTR + = DWORD PTR d EQU DWORD PTR + = OFFSET o EQU OFFSET + = FAR PTR f EQU FAR PTR + = SHORT s EQU SHORT + = ?,?,?,? ?x4 EQU + = ?,?,? ?x3 EQU + + ; ===== VGA Register Values ===== + + = A000 VGA_Segment EQU 0A000h ; Vga Memory Segment + + = 03C0 ATTRIB_Ctrl EQU 03C0h ; VGA Attribute Controller + = 03CE GC_Index EQU 03CEh ; VGA Graphics Controller + = 03C4 SC_Index EQU 03C4h ; VGA Sequencer Controller + = 03C5 SC_Data EQU 03C5h ; VGA Sequencer Data Port + = 03D4 CRTC_Index EQU 03D4h ; VGA CRT Controller + = 03D5 CRTC_Data EQU 03D5h ; VGA CRT Controller Data + = 03C2 MISC_OUTPUT EQU 03C2h ; VGA Misc Register + = 03DA INPUT_1 EQU 03DAh ; Input Status #1 Register + + = 03C8 DAC_WRITE_ADDR EQU 03C8h ; VGA DAC Write Addr Register + = 03C7 DAC_READ_ADDR EQU 03C7h ; VGA DAC Read Addr Register + = 03C9 PEL_DATA_REG EQU 03C9h ; VGA DAC/PEL data Register R/W + + = 0033 PIXEL_PAN_REG EQU 033h ; Attrib Index: Pixel Pan Reg + = 0002 MAP_MASK EQU 002h ; Sequ Index: Write Map Mask reg + = 0004 READ_MAP EQU 004h ; GC Index: Read Map Register + = 000C START_DISP_HI EQU 00Ch ; CRTC Index: Display Start Hi + = 000D START_DISP_LO EQU 00Dh ; CRTC Index: Display Start Lo + + = 0102 MAP_MASK_PLANE1 EQU 00102h ; Map Register + Plane 1 + = 1102 MAP_MASK_PLANE2 EQU 01102h ; Map Register + Plane 1 + = 0F02 ALL_PLANES_ON EQU 00F02h ; Map Register + All Bit Planes + + = 0604 CHAIN4_OFF EQU 00604h ; Chain 4 mode Off + = 0100 ASYNC_RESET EQU 00100h ; (A)synchronous Reset + = 0300 SEQU_RESTART EQU 00300h ; Sequencer Restart + + = 0008 LATCHES_ON EQU 00008h ; Bit Mask + Data from Latches + = FF08 LATCHES_OFF EQU 0FF08h ; Bit Mask + Data from CPU + + = 0008 VERT_RETRACE EQU 08h ; INPUT_1: Vertical Retrace Bit + = 0003 PLANE_BITS EQU 03h ; Bits 0-1 of Xpos = Plane # + = 000F ALL_PLANES EQU 0Fh ; All Bit Planes Selected + = 000F CHAR_BITS EQU 0Fh ; Bits 0-3 of Character Data + + = 1130 GET_CHAR_PTR EQU 01130h ; VGA BIOS Func: Get Char Set + = 0003 ROM_8x8_Lo EQU 03h ; ROM 8x8 Char Set Lo Pointer + = 0004 ROM_8x8_Hi EQU 04h ; ROM 8x8 Char Set Hi Pointer + + ; Constants Specific for these routines + + = 0008 NUM_MODES EQU 8 ; # of Mode X Variations + + ; Specific Mode Data Table format... + + 000C Mode_Data_Table STRUC + 0000 00 M_MiscR DB ? ; Value of MISC_OUTPUT register + 0001 00 M_Pages DB ? ; Maximum Possible # of pages + 0002 0000 M_XSize DW ? ; X Size Displayed on screen + 0004 0000 M_YSize DW ? ; Y Size Displayed on screen + 0006 0000 M_XMax DW ? ; Maximum Possible X Size + 0008 0000 M_YMax DW ? ; Maximum Possible Y Size + 000A 0000 M_CRTC DW ? ; Table of CRTC register values + Mode_Data_Table ENDS + + ; ===== DGROUP STORAGE NEEDED (42 BYTES) ===== + + 0000 .DATA? + + 0000 0000 SCREEN_WIDTH DW 0 ; Width of a line in Bytes + 0002 0000 SCREEN_HEIGHT DW 0 ; Vertical Height in Pixels + + 0004 0000 LAST_PAGE DW 0 ; # of Display Pages + 0006 0004 [ PAGE_ADDR DW 4 DUP (0) ; Offsets to start of each page + 0000 + ] + + 000E 0000 PAGE_SIZE DW 0 ; Size of Page in Addr Bytes + + 0010 0000 DISPLAY_PAGE DW 0 ; Page # currently displayed + 0012 0000 ACTIVE_PAGE DW 0 ; Page # currently active + + 0014 0000 CURRENT_PAGE DW 0 ; Offset of current Page + 0016 0000 CURRENT_SEGMENT DW 0 ; Segment of VGA memory + + 0018 0000 CURRENT_XOFFSET DW 0 ; Current Display X Offset + 001A 0000 CURRENT_YOFFSET DW 0 ; Current Display Y Offset + + 001C 0000 CURRENT_MOFFSET DW 0 ; Current Start Offset + + 001E 0000 MAX_XOFFSET DW 0 ; Current Display X Offset + 0020 0000 MAX_YOFFSET DW 0 ; Current Display Y Offset + + 0022 0000 0000 CHARSET_LOW DW 0, 0 ; Far Ptr to Char Set: 0-127 + 0026 0000 0000 CHARSET_HI DW 0, 0 ; Far Ptr to Char Set: 128-255 + + 0000 .CODE + + ; ===== DATA TABLES ===== + + ; Data Tables, Put in Code Segment for Easy Access + ; (Like when all the other Segment Registers are in + ; use!!) and reduced DGROUP requirements... + + ; Bit Mask Tables for Left/Right/Character Masks + + 0000 0F 0E 0C 08 Left_Clip_Mask DB 0FH, 0EH, 0CH, 08H + + 0004 01 03 07 0F Right_Clip_Mask DB 01H, 03H, 07H, 0FH + + ; Bit Patterns for converting character fonts + + 0008 00 08 04 0C 02 0A Char_Plane_Data DB 00H,08H,04H,0CH,02H,0AH,06H,0EH + 06 0E + 0010 01 09 05 0D 03 0B DB 01H,09H,05H,0DH,03H,0BH,07H,0FH + 07 0F + + ; CRTC Register Values for Various Configurations + + 0018 MODE_Single_Line: ; CRTC Setup Data for 400/480 Line modes + 0018 4009 DW 04009H ; Cell Height (1 Scan Line) + 001A 0014 DW 00014H ; Dword Mode off + 001C E317 DW 0E317H ; turn on Byte Mode + 001E 0000 DW nil ; End of CRTC Data for 400/480 Line Mode + + 0020 MODE_Double_Line: ; CRTC Setup Data for 200/240 Line modes + 0020 4109 DW 04109H ; Cell Height (2 Scan Lines) + 0022 0014 DW 00014H ; Dword Mode off + 0024 E317 DW 0E317H ; turn on Byte Mode + 0026 0000 DW nil ; End of CRTC Data for 200/240 Line Mode + + 0028 MODE_320_Wide: ; CRTC Setup Data for 320 Horz Pixels + 0028 5F00 DW 05F00H ; Horz total + 002A 4F01 DW 04F01H ; Horz Displayed + 002C 5002 DW 05002H ; Start Horz Blanking + 002E 8203 DW 08203H ; End Horz Blanking + 0030 5404 DW 05404H ; Start H Sync + 0032 8005 DW 08005H ; End H Sync + 0034 0000 DW nil ; End of CRTC Data for 320 Horz pixels + + 0036 MODE_360_Wide: ; CRTC Setup Data for 360 Horz Pixels + 0036 6B00 DW 06B00H ; Horz total + 0038 5901 DW 05901H ; Horz Displayed + 003A 5A02 DW 05A02H ; Start Horz Blanking + 003C 8E03 DW 08E03H ; End Horz Blanking + 003E 5E04 DW 05E04H ; Start H Sync + 0040 8A05 DW 08A05H ; End H Sync + 0042 0000 DW nil ; End of CRTC Data for 360 Horz pixels + + 0044 MODE_200_Tall: + 0044 MODE_400_Tall: ; CRTC Setup Data for 200/400 Line modes + 0044 BF06 DW 0BF06H ; Vertical Total + 0046 1F07 DW 01F07H ; Overflow + 0048 9C10 DW 09C10H ; V Sync Start + 004A 8E11 DW 08E11H ; V Sync End/Prot Cr0 Cr7 + 004C 8F12 DW 08F12H ; Vertical Displayed + 004E 9615 DW 09615H ; V Blank Start + 0050 B916 DW 0B916H ; V Blank End + 0052 0000 DW nil ; End of CRTC Data for 200/400 Lines + + 0054 MODE_240_Tall: + 0054 MODE_480_Tall: ; CRTC Setup Data for 240/480 Line modes + 0054 0D06 DW 00D06H ; Vertical Total + 0056 3E07 DW 03E07H ; Overflow + 0058 EA10 DW 0EA10H ; V Sync Start + 005A 8C11 DW 08C11H ; V Sync End/Prot Cr0 Cr7 + 005C DF12 DW 0DF12H ; Vertical Displayed + 005E E715 DW 0E715H ; V Blank Start + 0060 0616 DW 00616H ; V Blank End + 0062 0000 DW nil ; End of CRTC Data for 240/480 Lines + + ; Table of Display Mode Tables + + 0064 MODE_TABLE: + 0064 0074 R 0086 R DW o MODE_320x200, o MODE_320x400 + 0068 00E0 R 00F2 R DW o MODE_360x200, o MODE_360x400 + 006C 00BC R 00CE R DW o MODE_320x240, o MODE_320x480 + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 2 - 1 + + + 0070 0098 R 00AA R DW o MODE_360x240, o MODE_360x480 + + ; Table of Display Mode Components + + 0074 MODE_320x200: ; Data for 320 by 200 Pixels + + 0074 63 DB 063h ; 400 scan Lines & 25 Mhz Clock + 0075 04 DB 4 ; Maximum of 4 Pages + 0076 0140 00C8 DW 320, 200 ; Displayed Pixels (X,Y) + 007A 0516 0330 DW 1302, 816 ; Max Possible X and Y Sizes + + 007E 0028 R 0044 R DW o MODE_320_Wide, o MODE_200_Tall + 0082 0020 R 0000 DW o MODE_Double_Line, nil + + 0086 MODE_320x400: ; Data for 320 by 400 Pixels + + 0086 63 DB 063h ; 400 scan Lines & 25 Mhz Clock + 0087 02 DB 2 ; Maximum of 2 Pages + 0088 0140 0190 DW 320, 400 ; Displayed Pixels X,Y + 008C 0288 0330 DW 648, 816 ; Max Possible X and Y Sizes + + 0090 0028 R 0044 R DW o MODE_320_Wide, o MODE_400_Tall + 0094 0018 R 0000 DW o MODE_Single_Line, nil + + 0098 MODE_360x240: ; Data for 360 by 240 Pixels + + 0098 E7 DB 0E7h ; 480 scan Lines & 28 Mhz Clock + 0099 03 DB 3 ; Maximum of 3 Pages + 009A 0168 00F0 DW 360, 240 ; Displayed Pixels X,Y + 009E 0444 02D8 DW 1092, 728 ; Max Possible X and Y Sizes + + 00A2 0036 R 0054 R DW o MODE_360_Wide, o MODE_240_Tall + 00A6 0020 R 0000 DW o MODE_Double_Line , nil + + 00AA MODE_360x480: ; Data for 360 by 480 Pixels + + 00AA E7 DB 0E7h ; 480 scan Lines & 28 Mhz Clock + 00AB 01 DB 1 ; Only 1 Page Possible + 00AC 0168 01E0 DW 360, 480 ; Displayed Pixels X,Y + 00B0 0220 02D8 DW 544, 728 ; Max Possible X and Y Sizes + + 00B4 0036 R 0054 R DW o MODE_360_Wide, o MODE_480_Tall + 00B8 0018 R 0000 DW o MODE_Single_Line , nil + + 00BC MODE_320x240: ; Data for 320 by 240 Pixels + + 00BC E3 DB 0E3h ; 480 scan Lines & 25 Mhz Clock + 00BD 03 DB 3 ; Maximum of 3 Pages + 00BE 0140 00F0 DW 320, 240 ; Displayed Pixels X,Y + 00C2 0440 0332 DW 1088, 818 ; Max Possible X and Y Sizes + + 00C6 0028 R 0054 R DW o MODE_320_Wide, o MODE_240_Tall + 00CA 0020 R 0000 DW o MODE_Double_Line, nil + + 00CE MODE_320x480: ; Data for 320 by 480 Pixels + + 00CE E3 DB 0E3h ; 480 scan Lines & 25 Mhz Clock + 00CF 01 DB 1 ; Only 1 Page Possible + 00D0 0140 01E0 DW 320, 480 ; Displayed Pixels X,Y + 00D4 021C 0332 DW 540, 818 ; Max Possible X and Y Sizes + + 00D8 0028 R 0054 R DW o MODE_320_WIDE, o MODE_480_Tall + 00DC 0018 R 0000 DW o MODE_Single_Line, nil + + 00E0 MODE_360x200: ; Data for 360 by 200 Pixels + + 00E0 67 DB 067h ; 400 scan Lines & 28 Mhz Clock + 00E1 03 DB 3 ; Maximum of 3 Pages + 00E2 0168 00C8 DW 360, 200 ; Displayed Pixels (X,Y) + 00E6 0516 02D8 DW 1302, 728 ; Max Possible X and Y Sizes + + 00EA 0036 R 0044 R DW o MODE_360_Wide, MODE_200_Tall + 00EE 0020 R 0000 DW o MODE_Double_Line, nil + + 00F2 MODE_360x400: ; Data for 360 by 400 Pixels + + 00F2 67 DB 067h ; 400 scan Lines & 28 Mhz Clock + 00F3 01 DB 1 ; Maximum of 1 Pages + 00F4 0168 0190 DW 360, 400 ; Displayed Pixels X,Y + 00F8 0288 0330 DW 648, 816 ; Max Possible X and Y Sizes + + 00FC 0036 R 0044 R DW o MODE_360_Wide, MODE_400_Tall + 0100 0018 R 0000 DW o MODE_Single_Line, nil + + + ; ===== MODE X SETUP ROUTINES ===== + + ;====================================================== + ;SET_VGA_MODEX% (ModeType%, MaxXPos%, MaxYpos%, Pages%) + ;====================================================== + ; + ; Sets Up the specified version of Mode X. Allows for + ; the setup of multiple video pages, and a virtual + ; screen which can be larger than the displayed screen + ; (which can then be scrolled a pixel at a time) + ; + ; ENTRY: ModeType = Desired Screen Resolution (0-7) + ; + ; 0 = 320 x 200, 4 Pages max, 1.2:1 Aspect Ratio + ; 1 = 320 x 400, 2 Pages max, 2.4:1 Aspect Ratio + ; 2 = 360 x 200, 3 Pages max, 1.35:1 Aspect Ratio + ; 3 = 360 x 400, 1 Page max, 2.7:1 Aspect Ratio + ; 4 = 320 x 240, 3 Pages max, 1:1 Aspect Ratio + ; 5 = 320 x 480, 1 Page max, 2:1 Aspect Ratio + ; 6 = 360 x 240, 3 Pages max, 1.125:1 Aspect Ratio + ; 7 = 360 x 480, 1 Page max, 2.25:1 Aspect Ratio + ; + ; MaxXpos = The Desired Virtual Screen Width + ; MaxYpos = The Desired Virtual Screen Height + ; Pages = The Desired # of Video Pages + ; + ; EXIT: AX = Success Flag: 0 = Failure / -1= Success + ; + + 0016 SVM_STACK STRUC + 0000 0000 SVM_Table DW ? ; Offset of Mode Info Table + 0002 0000 0000 0000 DW ?x4 ; DI, SI, DS, BP + 0000 + 000A 00000000 DD ? ; Caller + 000E 0000 SVM_Pages DW ? ; # of Screen Pages desired + 0010 0000 SVM_Ysize DW ? ; Vertical Screen Size Desired + 0012 0000 SVM_Xsize DW ? ; Horizontal Screen Size Desired + 0014 0000 SVM_Mode DW ? ; Display Resolution Desired + SVM_STACK ENDS + + PUBLIC SET_VGA_MODEX + + 0104 SET_VGA_MODEX PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 0104 55 1 PUSH BP ; Save R1 + 0105 1E 2 PUSH DS ; Save R1 + 0106 56 3 PUSH SI ; Save R1 + 0107 57 4 PUSH DI ; Save R1 + 0108 83 EC 02 SUB SP, 2 ; Allocate workspace + 010B 8B EC MOV BP, SP ; Set up Stack Frame + + ; Check Legality of Mode Request.... + + 010D 8B 5E 14 MOV BX, [BP].SVM_Mode ; Get Requested Mode # + 0110 83 FB 08 CMP BX, NUM_MODES ; Is it 0..7? + 0113 73 47 JAE @SVM_BadModeSetup ; If Not, Error out + + 0115 D1 E3 SHL BX, 1 ; Scale BX + 0117 2E: 8B B7 0064 R MOV SI, w MODE_TABLE[BX] ; CS:SI -> Mode Info + 011C 89 76 00 MOV [BP].SVM_Table, SI ; Save ptr for later use + + ; Check # of Requested Display Pages + + 011F 8B 4E 0E MOV CX, [BP].SVM_Pages ; Get # of Requested Pages + CLR CH ; Set Hi Word = 0! + 0122 32 ED 1 XOR CH, CH ; Set Register = 0 + 0124 2E: 3A 4C 01 CMP CL, CS:[SI].M_Pages ; Check # Pages for mode + 0128 77 32 JA @SVM_BadModeSetup ; Report Error if too Many Pages + 012A E3 30 JCXZ @SVM_BadModeSetup ; Report Error if 0 Pages + + ; Check Validity of X Size + + 012C 83 66 12 F8 AND [BP].SVM_XSize, 0FFF8h ; X size Mod 8 Must = 0 + + 0130 8B 46 12 MOV AX, [BP].SVM_XSize ; Get Logical Screen Width + 0133 2E: 3B 44 02 CMP AX, CS:[SI].M_XSize ; Check against Displayed X + 0137 72 23 JB @SVM_BadModeSetup ; Report Error if too small + 0139 2E: 3B 44 06 CMP AX, CS:[SI].M_XMax ; Check against Max X + 013D 77 1D JA @SVM_BadModeSetup ; Report Error if too big + + ; Check Validity of Y Size + + 013F 8B 5E 10 MOV BX, [BP].SVM_YSize ; Get Logical Screen Height + 0142 2E: 3B 5C 04 CMP BX, CS:[SI].M_YSize ; Check against Displayed Y + 0146 72 14 JB @SVM_BadModeSetup ; Report Error if too small + 0148 2E: 3B 5C 08 CMP BX, CS:[SI].M_YMax ; Check against Max Y + 014C 77 0E JA @SVM_BadModeSetup ; Report Error if too big + + ; Enough memory to Fit it all? + + 014E C1 E8 02 SHR AX, 2 ; # of Bytes:Line = XSize/4 + 0151 F7 E1 MUL CX ; AX = Bytes/Line * Pages + 0153 F7 E3 MUL BX ; DX:AX = Total VGA mem needed + 0155 71 0A JNO @SVM_Continue ; Exit if Total Size > 256K + + 0157 4A DEC DX ; Was it Exactly 256K??? + 0158 0B D0 OR DX, AX ; (DX = 1, AX = 0000) + 015A 74 05 JZ @SVM_Continue ; if so, it's valid... + + 015C @SVM_BadModeSetup: + + CLR AX ; Return Value = False + 015C 33 C0 1 XOR AX, AX ; Set Register = 0 + 015E E9 00E6 JMP @SVM_Exit ; Normal Exit + + 0161 @SVM_Continue: + + 0161 B8 0013 MOV AX, 13H ; Start with Mode 13H + 0164 CD 10 INT 10H ; Let BIOS Set Mode + + OUT_16 SC_INDEX, CHAIN4_OFF ; Disable Chain 4 Mode + 0166 BA 03C4 1 MOV DX, SC_INDEX ; then Select Register + 0169 B8 0604 1 MOV AX, CHAIN4_OFF ; then Get Data Value + 016C EF 1 OUT DX, AX ; Set I/O Register(s) + OUT_16 SC_INDEX, ASYNC_RESET ; (A)synchronous Reset + 016D BA 03C4 1 MOV DX, SC_INDEX ; then Select Register + 0170 B8 0100 1 MOV AX, ASYNC_RESET ; then Get Data Value + 0173 EF 1 OUT DX, AX ; Set I/O Register(s) + OUT_8 MISC_OUTPUT, CS:[SI].M_MiscR ; Set New Timing/Size + 0174 BA 03C2 1 MOV DX, MISC_OUTPUT ; then Select Register + 0177 2E: 8A 04 1 MOV AL, CS:[SI].M_MiscR ; then Get Data Value + 017A EE 1 OUT DX, AL ; Set I/O Register + OUT_16 SC_INDEX, SEQU_RESTART ; Restart Sequencer ... + 017B BA 03C4 1 MOV DX, SC_INDEX ; then Select Register + 017E B8 0300 1 MOV AX, SEQU_RESTART ; then Get Data Value + 0181 EF 1 OUT DX, AX ; Set I/O Register(s) + + OUT_8 CRTC_INDEX, 11H ; Select Vert Retrace End Register + 0182 BA 03D4 1 MOV DX, CRTC_INDEX ; then Select Register + 0185 B0 11 1 MOV AL, 11H ; then Get Data Value + 0187 EE 1 OUT DX, AL ; Set I/O Register + 0188 42 INC DX ; Point to Data + 0189 EC IN AL, DX ; Get Value, Bit 7 = Protect + 018A 24 7F AND AL, 7FH ; Mask out Write Protect + 018C EE OUT DX, AL ; And send it back + + 018D BA 03D4 MOV DX, CRTC_INDEX ; Vga Crtc Registers + 0190 83 C6 0A ADD SI, M_CRTC ; SI -> CRTC Parameter Data + + ; Load Tables of CRTC Parameters from List of Tables + + 0193 @SVM_Setup_Table: + + 0193 2E: 8B 3C MOV DI, CS:[SI] ; Get Pointer to CRTC Data Tbl + 0196 83 C6 02 ADD SI, 2 ; Point to next Ptr Entry + 0199 0B FF OR DI, DI ; A nil Ptr means that we have + 019B 74 0D JZ @SVM_Set_Data ; finished CRTC programming + + 019D @SVM_Setup_CRTC: + 019D 2E: 8B 05 MOV AX, CS:[DI] ; Get CRTC Data from Table + 01A0 83 C7 02 ADD DI, 2 ; Advance Pointer + 01A3 0B C0 OR AX, AX ; At End of Data Table? + 01A5 74 EC JZ @SVM_Setup_Table ; If so, Exit & get next Table + + 01A7 EF OUT DX, AX ; Reprogram VGA CRTC reg + 01A8 EB F3 JMP s @SVM_Setup_CRTC ; Process Next Table Entry + + ; Initialize Page & Scroll info, DI = 0 + + 01AA @SVM_Set_Data: + 01AA 89 3E 0010 R MOV DISPLAY_PAGE, DI ; Display Page = 0 + 01AE 89 3E 0012 R MOV ACTIVE_PAGE, DI ; Active Page = 0 + 01B2 89 3E 0014 R MOV CURRENT_PAGE, DI ; Current Page (Offset) = 0 + 01B6 89 3E 0018 R MOV CURRENT_XOFFSET, DI ; Horz Scroll Index = 0 + 01BA 89 3E 001A R MOV CURRENT_YOFFSET, DI ; Vert Scroll Index = 0 + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 3 - 1 + + + 01BE 89 3E 001C R MOV CURRENT_MOFFSET, DI ; Memory Scroll Index = 0 + + 01C2 B8 A000 MOV AX, VGA_SEGMENT ; Segment for VGA memory + 01C5 A3 0016 R MOV CURRENT_SEGMENT, AX ; Save for Future LES's + + ; Set Logical Screen Width, X Scroll and Our Data + + 01C8 8B 76 00 MOV SI, [BP].SVM_Table ; Get Saved Ptr to Mode Info + 01CB 8B 46 12 MOV AX, [BP].SVM_Xsize ; Get Display Width + + 01CE 8B C8 MOV CX, AX ; CX = Logical Width + 01D0 2E: 2B 4C 02 SUB CX, CS:[SI].M_XSize ; CX = Max X Scroll Value + 01D4 89 0E 001E R MOV MAX_XOFFSET, CX ; Set Maximum X Scroll + + 01D8 C1 E8 02 SHR AX, 2 ; Bytes = Pixels / 4 + 01DB A3 0000 R MOV SCREEN_WIDTH, AX ; Save Width in Pixels + + 01DE D1 E8 SHR AX, 1 ; Offset Value = Bytes / 2 + 01E0 B4 13 MOV AH, 13h ; CRTC Offset Register Index + 01E2 86 C4 XCHG AL, AH ; Switch format for OUT + 01E4 EF OUT DX, AX ; Set VGA CRTC Offset Reg + + ; Setup Data table, Y Scroll, Misc for Other Routines + + 01E5 8B 46 10 MOV AX, [BP].SVM_Ysize ; Get Logical Screen Height + + 01E8 8B C8 MOV CX, AX ; CX = Logical Height + 01EA 2E: 2B 5C 04 SUB BX, CS:[SI].M_YSize ; CX = Max Y Scroll Value + 01EE 89 0E 0020 R MOV MAX_YOFFSET, CX ; Set Maximum Y Scroll + + 01F2 A3 0002 R MOV SCREEN_HEIGHT, AX ; Save Height in Pixels + 01F5 F7 26 0000 R MUL SCREEN_WIDTH ; AX = Page Size in Bytes, + 01F9 A3 000E R MOV PAGE_SIZE, AX ; Save Page Size + + 01FC 8B 4E 0E MOV CX, [BP].SVM_Pages ; Get # of Pages + 01FF 89 0E 0004 R MOV LAST_PAGE, CX ; Save # of Pages + + CLR BX ; Page # = 0 + 0203 33 DB 1 XOR BX, BX ; Set Register = 0 + 0205 8B D3 MOV DX, BX ; Page 0 Offset = 0 + + 0207 @SVM_Set_Pages: + + 0207 89 97 0006 R MOV PAGE_ADDR[BX], DX ; Set Page #(BX) Offset + 020B 83 C3 02 ADD BX, 2 ; Page#++ + 020E 03 D0 ADD DX, AX ; Compute Addr of Next Page + LOOPx CX, @SVM_Set_Pages ; Loop until all Pages Set + 0210 49 1 DEC CX ; Counter-- + 0211 75 F4 1 JNZ @SVM_Set_Pages ; Jump if not 0 + + ; Clear VGA Memory + + OUT_16 SC_INDEX, ALL_PLANES_ON ; Select All Planes + 0213 BA 03C4 1 MOV DX, SC_INDEX ; then Select Register + 0216 B8 0F02 1 MOV AX, ALL_PLANES_ON ; then Get Data Value + 0219 EF 1 OUT DX, AX ; Set I/O Register(s) + 021A C4 3E 0014 R LES DI, d CURRENT_PAGE ; -> Start of VGA memory + + CLR AX ; AX = 0 + 021E 33 C0 1 XOR AX, AX ; Set Register = 0 + 0220 FC CLD ; Block Xfer Forwards + 0221 B9 8000 MOV CX, 8000H ; 32K * 4 * 2 = 256K + 0224 F3/ AB REP STOSW ; Clear dat memory! + + ; Setup Font Pointers + + 0226 B7 03 MOV BH, ROM_8x8_Lo ; Ask for 8x8 Font, 0-127 + 0228 B8 1130 MOV AX, GET_CHAR_PTR ; Service to Get Pointer + 022B CD 10 INT 10h ; Call VGA BIOS + + 022D 89 2E 0022 R MOV CHARSET_LOW, BP ; Save Char Set Offset + 0231 8C 06 0024 R MOV CHARSET_LOW+2, ES ; Save Char Set Segment + + 0235 B7 04 MOV BH, ROM_8x8_Hi ; Ask for 8x8 Font, 128-255 + 0237 B8 1130 MOV AX, GET_CHAR_PTR ; Service to Get Pointer + 023A CD 10 INT 10h ; Call VGA BIOS + + 023C 89 2E 0026 R MOV CHARSET_HI, BP ; Save Char Set Offset + 0240 8C 06 0028 R MOV CHARSET_HI+2, ES ; Save Char Set Segment + + 0244 B8 FFFF MOV AX, True ; Return Success Code + + 0247 @SVM_EXIT: + 0247 83 C4 02 ADD SP, 2 ; Deallocate workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + 024A 5F 1 POP DI ; Restore R1 + 024B 5E 2 POP SI ; Restore R1 + 024C 1F 3 POP DS ; Restore R1 + 024D 5D 4 POP BP ; Restore R1 + 024E CA 0008 RET 8 ; Exit & Clean Up Stack + + 0251 SET_VGA_MODEX ENDP + + + ;================== + ;SET_MODEX% (Mode%) + ;================== + ; + ; Quickie Mode Set - Sets Up Mode X to Default Configuration + ; + ; ENTRY: ModeType = Desired Screen Resolution (0-7) + ; (See SET_VGA_MODEX for list) + ; + ; EXIT: AX = Success Flag: 0 = Failure / -1= Success + ; + + 000A SM_STACK STRUC + 0000 0000 0000 DW ?,? ; BP, SI + 0004 00000000 DD ? ; Caller + 0008 0000 SM_Mode DW ? ; Desired Screen Resolution + SM_STACK ENDS + + PUBLIC SET_MODEX + + 0251 SET_MODEX PROC FAR + + PUSHx BP, SI ; Preserve Important registers + 0251 55 1 PUSH BP ; Save R1 + 0252 56 2 PUSH SI ; Save R1 + 0253 8B EC MOV BP, SP ; Set up Stack Frame + + CLR AX ; Assume Failure + 0255 33 C0 1 XOR AX, AX ; Set Register = 0 + 0257 8B 5E 08 MOV BX, [BP].SM_Mode ; Get Desired Mode # + 025A 83 FB 08 CMP BX, NUM_MODES ; Is it a Valid Mode #? + 025D 73 1C JAE @SMX_Exit ; If Not, don't Bother + + 025F 53 PUSH BX ; Push Mode Parameter + + 0260 D1 E3 SHL BX, 1 ; Scale BX to word Index + 0262 2E: 8B B7 0064 R MOV SI, w MODE_TABLE[BX] ; CS:SI -> Mode Info + + 0267 2E: FF 74 02 PUSH CS:[SI].M_XSize ; Push Default X Size + 026B 2E: FF 74 04 PUSH CS:[SI].M_Ysize ; Push Default Y size + 026F 2E: 8A 44 01 MOV AL, CS:[SI].M_Pages ; Get Default # of Pages + CLR AH ; Hi Byte = 0 + 0273 32 E4 1 XOR AH, AH ; Set Register = 0 + 0275 50 PUSH AX ; Push # Pages + + 0276 9A ---- 0104 R CALL f SET_VGA_MODEX ; Set up Mode X! + + 027B @SMX_Exit: + POPx SI, BP ; Restore Registers + 027B 5E 1 POP SI ; Restore R1 + 027C 5D 2 POP BP ; Restore R1 + 027D CA 0002 RET 2 ; Exit & Clean Up Stack + + 0280 SET_MODEX ENDP + + + ; ===== BASIC GRAPHICS PRIMITIVES ===== + + ;============================ + ;CLEAR_VGA_SCREEN (ColorNum%) + ;============================ + ; + ; Clears the active display page + ; + ; ENTRY: ColorNum = Color Value to fill the page with + ; + ; EXIT: No meaningful values returned + ; + + 000A CVS_STACK STRUC + 0000 0000 0000 DW ?,? ; DI, BP + 0004 00000000 DD ? ; Caller + 0008 00 00 CVS_COLOR DB ?,? ; Color to Set Screen to + CVS_STACK ENDS + + PUBLIC CLEAR_VGA_SCREEN + + 0280 CLEAR_VGA_SCREEN PROC FAR + + PUSHx BP, DI ; Preserve Important Registers + 0280 55 1 PUSH BP ; Save R1 + 0281 57 2 PUSH DI ; Save R1 + 0282 8B EC MOV BP, SP ; Set up Stack Frame + + OUT_16 SC_INDEX, ALL_PLANES_ON ; Select All Planes + 0284 BA 03C4 1 MOV DX, SC_INDEX ; then Select Register + 0287 B8 0F02 1 MOV AX, ALL_PLANES_ON ; then Get Data Value + 028A EF 1 OUT DX, AX ; Set I/O Register(s) + 028B C4 3E 0014 R LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + 028F 8A 46 08 MOV AL, [BP].CVS_COLOR ; Get Color + 0292 8A E0 MOV AH, AL ; Copy for Word Write + 0294 FC CLD ; Block fill Forwards + + 0295 8B 0E 000E R MOV CX, PAGE_SIZE ; Get Size of Page + 0299 D1 E9 SHR CX, 1 ; Divide by 2 for Words + 029B F3/ AB REP STOSW ; Block Fill VGA memory + + POPx DI, BP ; Restore Saved Registers + 029D 5F 1 POP DI ; Restore R1 + 029E 5D 2 POP BP ; Restore R1 + 029F CA 0002 RET 2 ; Exit & Clean Up Stack + + 02A2 CLEAR_VGA_SCREEN ENDP + + + ;=================================== + ;SET_POINT (Xpos%, Ypos%, ColorNum%) + ;=================================== + ; + ; Plots a single Pixel on the active display page + ; + ; ENTRY: Xpos = X position to plot pixel at + ; Ypos = Y position to plot pixel at + ; ColorNum = Color to plot pixel with + ; + ; EXIT: No meaningful values returned + ; + + 000E SP_STACK STRUC + 0000 0000 0000 DW ?,? ; BP, DI + 0004 00000000 DD ? ; Caller + 0008 00 00 SETP_Color DB ?,? ; Color of Point to Plot + 000A 0000 SETP_Ypos DW ? ; Y pos of Point to Plot + 000C 0000 SETP_Xpos DW ? ; X pos of Point to Plot + SP_STACK ENDS + + PUBLIC SET_POINT + + 02A2 SET_POINT PROC FAR + + PUSHx BP, DI ; Preserve Registers + 02A2 55 1 PUSH BP ; Save R1 + 02A3 57 2 PUSH DI ; Save R1 + 02A4 8B EC MOV BP, SP ; Set up Stack Frame + + 02A6 C4 3E 0014 R LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + 02AA 8B 46 0A MOV AX, [BP].SETP_Ypos ; Get Line # of Pixel + 02AD F7 26 0000 R MUL SCREEN_WIDTH ; Get Offset to Start of Line + + 02B1 8B 5E 0C MOV BX, [BP].SETP_Xpos ; Get Xpos + 02B4 8B CB MOV CX, BX ; Copy to extract Plane # from + 02B6 C1 EB 02 SHR BX, 2 ; X offset (Bytes) = Xpos/4 + 02B9 03 D8 ADD BX, AX ; Offset = Width*Ypos + Xpos/4 + + 02BB B8 0102 MOV AX, MAP_MASK_PLANE1 ; Map Mask & Plane Select Register + 02BE 80 E1 03 AND CL, PLANE_BITS ; Get Plane Bits + 02C1 D2 E4 SHL AH, CL ; Get Plane Select Value + OUT_16 SC_Index, AX ; Select Plane + 02C3 BA 03C4 1 MOV DX, SC_Index ; then Select Register + 02C6 EF 1 OUT DX, AX ; Set I/O Register(s) + + 02C7 8A 46 08 MOV AL,[BP].SETP_Color ; Get Pixel Color + 02CA 26: 88 01 MOV ES:[DI+BX], AL ; Draw Pixel + + POPx DI, BP ; Restore Saved Registers + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 4 - 1 + + + 02CD 5F 1 POP DI ; Restore R1 + 02CE 5D 2 POP BP ; Restore R1 + 02CF CA 0006 RET 6 ; Exit and Clean up Stack + + 02D2 SET_POINT ENDP + + + ;========================== + ;READ_POINT% (Xpos%, Ypos%) + ;========================== + ; + ; Read the color of a pixel from the Active Display Page + ; + ; ENTRY: Xpos = X position of pixel to read + ; Ypos = Y position of pixel to read + ; + ; EXIT: AX = Color of Pixel at (Xpos, Ypos) + ; + + 000C RP_STACK STRUC + 0000 0000 0000 DW ?,? ; BP, DI + 0004 00000000 DD ? ; Caller + 0008 0000 RP_Ypos DW ? ; Y pos of Point to Read + 000A 0000 RP_Xpos DW ? ; X pos of Point to Read + RP_STACK ENDS + + PUBLIC READ_POINT + + 02D2 READ_POINT PROC FAR + + PUSHx BP, DI ; Preserve Registers + 02D2 55 1 PUSH BP ; Save R1 + 02D3 57 2 PUSH DI ; Save R1 + 02D4 8B EC MOV BP, SP ; Set up Stack Frame + + 02D6 C4 3E 0014 R LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + 02DA 8B 46 08 MOV AX, [BP].RP_Ypos ; Get Line # of Pixel + 02DD F7 26 0000 R MUL SCREEN_WIDTH ; Get Offset to Start of Line + + 02E1 8B 5E 0A MOV BX, [BP].RP_Xpos ; Get Xpos + 02E4 8B CB MOV CX, BX + 02E6 C1 EB 02 SHR BX, 2 ; X offset (Bytes) = Xpos/4 + 02E9 03 D8 ADD BX, AX ; Offset = Width*Ypos + Xpos/4 + + 02EB B0 04 MOV AL, READ_MAP ; GC Read Mask Register + 02ED 8A E1 MOV AH, CL ; Get Xpos + 02EF 80 E4 03 AND AH, PLANE_BITS ; & mask out Plane # + OUT_16 GC_INDEX, AX ; Select Plane to read in + 02F2 BA 03CE 1 MOV DX, GC_INDEX ; then Select Register + 02F5 EF 1 OUT DX, AX ; Set I/O Register(s) + + CLR AH ; Clear Return Value Hi byte + 02F6 32 E4 1 XOR AH, AH ; Set Register = 0 + 02F8 26: 8A 01 MOV AL, ES:[DI+BX] ; Get Color of Pixel + + POPx DI, BP ; Restore Saved Registers + 02FB 5F 1 POP DI ; Restore R1 + 02FC 5D 2 POP BP ; Restore R1 + 02FD CA 0004 RET 4 ; Exit and Clean up Stack + + 0300 READ_POINT ENDP + + + ;====================================================== + ;FILL_BLOCK (Xpos1%, Ypos1%, Xpos2%, Ypos2%, ColorNum%) + ;====================================================== + ; + ; Fills a rectangular block on the active display Page + ; + ; ENTRY: Xpos1 = Left X position of area to fill + ; Ypos1 = Top Y position of area to fill + ; Xpos2 = Right X position of area to fill + ; Ypos2 = Bottom Y position of area to fill + ; ColorNum = Color to fill area with + ; + ; EXIT: No meaningful values returned + ; + + 0016 FB_STACK STRUC + 0000 0000 0000 0000 DW ?x4 ; DS, DI, SI, BP + 0000 + 0008 00000000 DD ? ; Caller + 000C 00 00 FB_Color DB ?,? ; Fill Color + 000E 0000 FB_Ypos2 DW ? ; Y pos of Lower Right Pixel + 0010 0000 FB_Xpos2 DW ? ; X pos of Lower Right Pixel + 0012 0000 FB_Ypos1 DW ? ; Y pos of Upper Left Pixel + 0014 0000 FB_Xpos1 DW ? ; X pos of Upper Left Pixel + FB_STACK ENDS + + PUBLIC FILL_BLOCK + + 0300 FILL_BLOCK PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 0300 55 1 PUSH BP ; Save R1 + 0301 1E 2 PUSH DS ; Save R1 + 0302 56 3 PUSH SI ; Save R1 + 0303 57 4 PUSH DI ; Save R1 + 0304 8B EC MOV BP, SP ; Set up Stack Frame + + 0306 C4 3E 0014 R LES DI, d CURRENT_PAGE ; Point to Active VGA Page + 030A FC CLD ; Direction Flag = Forward + + OUT_8 SC_INDEX, MAP_MASK ; Set up for Plane Select + 030B BA 03C4 1 MOV DX, SC_INDEX ; then Select Register + 030E B0 02 1 MOV AL, MAP_MASK ; then Get Data Value + 0310 EE 1 OUT DX, AL ; Set I/O Register + + ; Validate Pixel Coordinates + ; If necessary, Swap so X1 <= X2, Y1 <= Y2 + + 0311 8B 46 12 MOV AX, [BP].FB_Ypos1 ; AX = Y1 is Y1< Y2? + 0314 8B 5E 0E MOV BX, [BP].FB_Ypos2 ; BX = Y2 + 0317 3B C3 CMP AX, BX + 0319 7E 04 JLE @FB_NOSWAP1 + + 031B 89 5E 12 MOV [BP].FB_Ypos1, BX ; Swap Y1 and Y2 and save Y1 + 031E 93 XCHG AX, BX ; on stack for future use + + 031F @FB_NOSWAP1: + 031F 2B D8 SUB BX, AX ; Get Y width + 0321 43 INC BX ; Add 1 to avoid 0 value + 0322 89 5E 0E MOV [BP].FB_Ypos2, BX ; Save in Ypos2 + + 0325 F7 26 0000 R MUL SCREEN_WIDTH ; Mul Y1 by Bytes per Line + 0329 03 F8 ADD DI, AX ; DI = Start of Line Y1 + + 032B 8B 46 14 MOV AX, [BP].FB_Xpos1 ; Check X1 <= X2 + 032E 8B 5E 10 MOV BX, [BP].FB_Xpos2 ; + 0331 3B C3 CMP AX, BX + 0333 7E 04 JLE @FB_NOSWAP2 ; Skip Ahead if Ok + + 0335 89 46 10 MOV [BP].FB_Xpos2, AX ; Swap X1 AND X2 and save X2 + 0338 93 XCHG AX, BX ; on stack for future use + + ; All our Input Values are in order, Now determine + ; How many full "bands" 4 pixels wide (aligned) there + ; are, and if there are partial bands (<4 pixels) on + ; the left and right edges. + + 0339 @FB_NOSWAP2: + 0339 8B D0 MOV DX, AX ; DX = X1 (Pixel Position) + 033B C1 EA 02 SHR DX, 2 ; DX/4 = Bytes into Line + 033E 03 FA ADD DI, DX ; DI = Addr of Upper-Left Corner + + 0340 8B CB MOV CX, BX ; CX = X2 (Pixel Position) + 0342 C1 E9 02 SHR CX, 2 ; CX/4 = Bytes into Line + + 0345 3B D1 CMP DX, CX ; Start and end in same band? + 0347 75 03 JNE @FB_NORMAL ; if not, check for l & r edges + 0349 E9 0086 JMP @FB_ONE_BAND_ONLY ; if so, then special processing + + 034C @FB_NORMAL: + 034C 2B CA SUB CX, DX ; CX = # bands -1 + 034E 8B F0 MOV SI, AX ; SI = PLANE#(X1) + 0350 83 E6 03 AND SI, PLANE_BITS ; if Left edge is aligned then + 0353 74 27 JZ @FB_L_PLANE_FLUSH ; no special processing.. + + ; Draw "Left Edge" vertical strip of 1-3 pixels... + + OUT_8 SC_Data, Left_Clip_Mask[SI] ; Set Left Edge Plane Mask + 0355 BA 03C5 1 MOV DX, SC_Data ; then Select Register + 0358 2E: 8A 84 0000 R 1 MOV AL, Left_Clip_Mask[SI] ; then Get Data Value + 035D EE 1 OUT DX, AL ; Set I/O Register + + 035E 8B F7 MOV SI, DI ; SI = Copy of Start Addr (UL) + + 0360 8B 56 0E MOV DX, [BP].FB_Ypos2 ; Get # of Lines to draw + 0363 8A 46 0C MOV AL, [BP].FB_Color ; Get Fill Color + 0366 8B 1E 0000 R MOV BX, SCREEN_WIDTH ; Get Vertical increment Value + + 036A @FB_LEFT_LOOP: + 036A 26: 88 04 MOV ES:[SI], AL ; Fill in Left Edge Pixels + 036D 03 F3 ADD SI, BX ; Point to Next Line (Below) + LOOPjz DX, @FB_LEFT_CONT ; Exit loop if all Lines Drawn + 036F 4A 1 DEC DX ; Counter-- + 0370 74 08 1 JZ @FB_LEFT_CONT ; Jump if 0 + + 0372 26: 88 04 MOV ES:[SI], AL ; Fill in Left Edge Pixels + 0375 03 F3 ADD SI, BX ; Point to Next Line (Below) + LOOPx DX, @FB_LEFT_LOOP ; loop until left strip is drawn + 0377 4A 1 DEC DX ; Counter-- + 0378 75 F0 1 JNZ @FB_LEFT_LOOP ; Jump if not 0 + + 037A @FB_LEFT_CONT: + + 037A 47 INC DI ; Point to Middle (or Right) Block + 037B 49 DEC CX ; Reset CX instead of JMP @FB_RIGHT + + 037C @FB_L_PLANE_FLUSH: + 037C 41 INC CX ; Add in Left band to middle block + + ; DI = Addr of 1st middle Pixel (band) to fill + ; CX = # of Bands to fill -1 + + 037D @FB_RIGHT: + 037D 8B 76 10 MOV SI, [BP].FB_Xpos2 ; Get Xpos2 + 0380 83 E6 03 AND SI, PLANE_BITS ; Get Plane values + 0383 83 FE 03 CMP SI, 0003 ; Plane = 3? + 0386 74 2B JE @FB_R_EDGE_FLUSH ; Hey, add to middle + + ; Draw "Right Edge" vertical strip of 1-3 pixels... + + OUT_8 SC_Data, Right_Clip_Mask[SI] ; Right Edge Plane Mask + 0388 BA 03C5 1 MOV DX, SC_Data ; then Select Register + 038B 2E: 8A 84 0004 R 1 MOV AL, Right_Clip_Mask[SI] ; then Get Data Value + 0390 EE 1 OUT DX, AL ; Set I/O Register + + 0391 8B F7 MOV SI, DI ; Get Addr of Left Edge + 0393 03 F1 ADD SI, CX ; Add Width-1 (Bands) + 0395 4E DEC SI ; To point to top of Right Edge + + 0396 8B 56 0E MOV DX, [BP].FB_Ypos2 ; Get # of Lines to draw + 0399 8A 46 0C MOV AL, [BP].FB_Color ; Get Fill Color + 039C 8B 1E 0000 R MOV BX, SCREEN_WIDTH ; Get Vertical increment Value + + 03A0 @FB_RIGHT_LOOP: + 03A0 26: 88 04 MOV ES:[SI], AL ; Fill in Right Edge Pixels + 03A3 03 F3 ADD SI, BX ; Point to Next Line (Below) + LOOPjz DX, @FB_RIGHT_CONT ; Exit loop if all Lines Drawn + 03A5 4A 1 DEC DX ; Counter-- + 03A6 74 08 1 JZ @FB_RIGHT_CONT ; Jump if 0 + + 03A8 26: 88 04 MOV ES:[SI], AL ; Fill in Right Edge Pixels + 03AB 03 F3 ADD SI, BX ; Point to Next Line (Below) + LOOPx DX, @FB_RIGHT_LOOP ; loop until left strip is drawn + 03AD 4A 1 DEC DX ; Counter-- + 03AE 75 F0 1 JNZ @FB_RIGHT_LOOP ; Jump if not 0 + + 03B0 @FB_RIGHT_CONT: + + 03B0 49 DEC CX ; Minus 1 for Middle bands + 03B1 74 51 JZ @FB_EXIT ; Uh.. no Middle bands... + + 03B3 @FB_R_EDGE_FLUSH: + + ; DI = Addr of Upper Left block to fill + ; CX = # of Bands to fill in (width) + + OUT_8 SC_Data, ALL_PLANES ; Write to All Planes + 03B3 BA 03C5 1 MOV DX, SC_Data ; then Select Register + 03B6 B0 0F 1 MOV AL, ALL_PLANES ; then Get Data Value + 03B8 EE 1 OUT DX, AL ; Set I/O Register + + 03B9 8B 16 0000 R MOV DX, SCREEN_WIDTH ; DX = DI Increment + 03BD 2B D1 SUB DX, CX ; = Screen_Width-# Planes Filled + + 03BF 8B D9 MOV BX, CX ; BX = Quick Refill for CX + 03C1 8B 76 0E MOV SI, [BP].FB_Ypos2 ; SI = # of Line to Fill + 03C4 8A 46 0C MOV AL, [BP].FB_Color ; Get Fill Color + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 5 - 1 + + + + 03C7 @FB_MIDDLE_LOOP: + 03C7 F3/ AA REP STOSB ; Fill in entire line + + 03C9 8B CB MOV CX, BX ; Recharge CX (Line Width) + 03CB 03 FA ADD DI, DX ; Point to start of Next Line + LOOPx SI, @FB_MIDDLE_LOOP ; Loop until all lines drawn + 03CD 4E 1 DEC SI ; Counter-- + 03CE 75 F7 1 JNZ @FB_MIDDLE_LOOP ; Jump if not 0 + + 03D0 EB 32 JMP s @FB_EXIT ; Outa here + + 03D2 @FB_ONE_BAND_ONLY: + 03D2 8B F0 MOV SI, AX ; Get Left Clip Mask, Save X1 + 03D4 83 E6 03 AND SI, PLANE_BITS ; Mask out Row # + 03D7 2E: 8A 84 0000 R MOV AL, Left_Clip_Mask[SI] ; Get Left Edge Mask + 03DC 8B F3 MOV SI, BX ; Get Right Clip Mask, Save X2 + 03DE 83 E6 03 AND SI, PLANE_BITS ; Mask out Row # + 03E1 2E: 22 84 0004 R AND AL, Right_Clip_Mask[SI] ; Get Right Edge Mask byte + + OUT_8 SC_Data, AL ; Clip For Left & Right Masks + 03E6 BA 03C5 1 MOV DX, SC_Data ; then Select Register + 03E9 EE 1 OUT DX, AL ; Set I/O Register + + 03EA 8B 4E 0E MOV CX, [BP].FB_Ypos2 ; Get # of Lines to draw + 03ED 8A 46 0C MOV AL, [BP].FB_Color ; Get Fill Color + 03F0 8B 1E 0000 R MOV BX, SCREEN_WIDTH ; Get Vertical increment Value + + 03F4 @FB_ONE_LOOP: + 03F4 26: 88 05 MOV ES:[DI], AL ; Fill in Pixels + 03F7 03 FB ADD DI, BX ; Point to Next Line (Below) + LOOPjz CX, @FB_EXIT ; Exit loop if all Lines Drawn + 03F9 49 1 DEC CX ; Counter-- + 03FA 74 08 1 JZ @FB_EXIT ; Jump if 0 + + 03FC 26: 88 05 MOV ES:[DI], AL ; Fill in Pixels + 03FF 03 FB ADD DI, BX ; Point to Next Line (Below) + LOOPx CX, @FB_ONE_LOOP ; loop until left strip is drawn + 0401 49 1 DEC CX ; Counter-- + 0402 75 F0 1 JNZ @FB_ONE_LOOP ; Jump if not 0 + + 0404 @FB_EXIT: + POPx DI, SI, DS, BP ; Restore Saved Registers + 0404 5F 1 POP DI ; Restore R1 + 0405 5E 2 POP SI ; Restore R1 + 0406 1F 3 POP DS ; Restore R1 + 0407 5D 4 POP BP ; Restore R1 + 0408 CA 000A RET 10 ; Exit and Clean up Stack + + 040B FILL_BLOCK ENDP + + + ;===================================================== + ;DRAW_LINE (Xpos1%, Ypos1%, Xpos2%, Ypos2%, ColorNum%) + ;===================================================== + ; + ; Draws a Line on the active display page + ; + ; ENTRY: Xpos1 = X position of first point on line + ; Ypos1 = Y position of first point on line + ; Xpos2 = X position of last point on line + ; Ypos2 = Y position of last point on line + ; ColorNum = Color to draw line with + ; + ; EXIT: No meaningful values returned + ; + + 0014 DL_STACK STRUC + 0000 0000 0000 0000 DW ?x3 ; DI, SI, BP + 0006 00000000 DD ? ; Caller + 000A 00 00 DL_ColorF DB ?,? ; Line Draw Color + 000C 0000 DL_Ypos2 DW ? ; Y pos of last point + 000E 0000 DL_Xpos2 DW ? ; X pos of last point + 0010 0000 DL_Ypos1 DW ? ; Y pos of first point + 0012 0000 DL_Xpos1 DW ? ; X pos of first point + DL_STACK ENDS + + PUBLIC DRAW_LINE + + 040B DRAW_LINE PROC FAR + + PUSHx BP, SI, DI ; Preserve Important Registers + 040B 55 1 PUSH BP ; Save R1 + 040C 56 2 PUSH SI ; Save R1 + 040D 57 3 PUSH DI ; Save R1 + 040E 8B EC MOV BP, SP ; Set up Stack Frame + 0410 FC CLD ; Direction Flag = Forward + + OUT_8 SC_INDEX, MAP_MASK ; Set up for Plane Select + 0411 BA 03C4 1 MOV DX, SC_INDEX ; then Select Register + 0414 B0 02 1 MOV AL, MAP_MASK ; then Get Data Value + 0416 EE 1 OUT DX, AL ; Set I/O Register + 0417 8A 6E 0A MOV CH, [BP].DL_ColorF ; Save Line Color in CH + + ; Check Line Type + + 041A 8B 76 12 MOV SI, [BP].DL_Xpos1 ; AX = X1 is X1< X2? + 041D 8B 7E 0E MOV DI, [BP].DL_Xpos2 ; DX = X2 + 0420 3B F7 CMP SI, DI ; Is X1 < X2 + 0422 74 5D JE @DL_VLINE ; If X1=X2, Draw Vertical Line + 0424 7C 02 JL @DL_NOSWAP1 ; If X1 < X2, don't swap + + 0426 87 F7 XCHG SI, DI ; X2 IS > X1, SO SWAP THEM + + 0428 @DL_NOSWAP1: + + ; SI = X1, DI = X2 + + 0428 8B 46 10 MOV AX, [BP].DL_Ypos1 ; AX = Y1 is Y1 <> Y2? + 042B 3B 46 0C CMP AX, [BP].DL_Ypos2 ; Y1 = Y2? + 042E 74 03 JE @DL_HORZ ; If so, Draw a Horizontal Line + + 0430 E9 0094 JMP @DL_BREZHAM ; Diagonal line... go do it... + + ; This Code draws a Horizontal Line in Mode X where: + ; SI = X1, DI = X2, and AX = Y1/Y2 + + 0433 @DL_HORZ: + + 0433 F7 26 0000 R MUL SCREEN_WIDTH ; Offset = Ypos * Screen_Width + 0437 8B D0 MOV DX, AX ; CX = Line offset into Page + + 0439 8B C6 MOV AX, SI ; Get Left edge, Save X1 + 043B 83 E6 03 AND SI, PLANE_BITS ; Mask out Row # + 043E 2E: 8A 9C 0000 R MOV BL, Left_Clip_Mask[SI] ; Get Left Edge Mask + 0443 8B CF MOV CX, DI ; Get Right edge, Save X2 + 0445 83 E7 03 AND DI, PLANE_BITS ; Mask out Row # + 0448 2E: 8A BD 0004 R MOV BH, Right_Clip_Mask[DI] ; Get Right Edge Mask byte + + 044D C1 E8 02 SHR AX, 2 ; Get X1 Byte # (=X1/4) + 0450 C1 E9 02 SHR CX, 2 ; Get X2 Byte # (=X2/4) + + 0453 C4 3E 0014 R LES DI, d CURRENT_PAGE ; Point to Active VGA Page + 0457 03 FA ADD DI, DX ; Point to Start of Line + 0459 03 F8 ADD DI, AX ; Point to Pixel X1 + + 045B 2B C8 SUB CX, AX ; CX = # Of Bands (-1) to set + 045D 75 02 JNZ @DL_LONGLN ; jump if longer than one segment + + 045F 22 DF AND BL, BH ; otherwise, merge clip masks + + 0461 @DL_LONGLN: + + OUT_8 SC_Data, BL ; Set the Left Clip Mask + 0461 BA 03C5 1 MOV DX, SC_Data ; then Select Register + 0464 8A C3 1 MOV AL, BL ; then Get Data Value + 0466 EE 1 OUT DX, AL ; Set I/O Register + + 0467 8A 46 0A MOV AL, [BP].DL_ColorF ; Get Line Color + 046A 8A D8 MOV BL, AL ; BL = Copy of Line Color + 046C AA STOSB ; Set Left (1-4) Pixels + + 046D E3 55 JCXZ @DL_EXIT ; Done if only one Line Segment + + 046F 49 DEC CX ; CX = # of Middle Segments + 0470 74 07 JZ @DL_XRSEG ; If no middle segments.... + + ; Draw Middle Segments + + OUT_8 DX, ALL_PLANES ; Write to ALL Planes + 0472 B0 0F 1 MOV AL, ALL_PLANES ; then Get Data Value + 0474 EE 1 OUT DX, AL ; Set I/O Register + + 0475 8A C3 MOV AL, BL ; Get Color from BL + 0477 F3/ AA REP STOSB ; Draw Middle (4 Pixel) Segments + + 0479 @DL_XRSEG: + OUT_8 DX, BH ; Select Planes for Right Clip Mask + 0479 8A C7 1 MOV AL, BH ; then Get Data Value + 047B EE 1 OUT DX, AL ; Set I/O Register + 047C 8A C3 MOV AL, BL ; Get Color Value + 047E AA STOSB ; Draw Right (1-4) Pixels + + 047F EB 43 JMP s @DL_EXIT ; We Are Done... + + + ; This Code Draws A Vertical Line. On entry: + ; CH = Line Color, SI & DI = X1 + + 0481 @DL_VLINE: + + 0481 8B 46 10 MOV AX, [BP].DL_Ypos1 ; AX = Y1 + 0484 8B 76 0C MOV SI, [BP].DL_Ypos2 ; SI = Y2 + 0487 3B C6 CMP AX, SI ; Is Y1 < Y2? + 0489 7E 01 JLE @DL_NOSWAP2 ; if so, Don't Swap them + + 048B 96 XCHG AX, SI ; Ok, NOW Y1 < Y2 + + 048C @DL_NOSWAP2: + + 048C 2B F0 SUB SI, AX ; SI = Line Height (Y2-Y1+1) + 048E 46 INC SI + + ; AX = Y1, DI = X1, Get offset into Page into AX + + 048F F7 26 0000 R MUL SCREEN_WIDTH ; Offset = Y1 (AX) * Screen Width + 0493 8B D7 MOV DX, DI ; Copy Xpos into DX + 0495 C1 EF 02 SHR DI, 2 ; DI = Xpos/4 + 0498 03 C7 ADD AX, DI ; DI = Xpos/4 + ScreenWidth * Y1 + + 049A C4 3E 0014 R LES DI, d CURRENT_PAGE ; Point to Active VGA Page + 049E 03 F8 ADD DI, AX ; Point to Pixel X1, Y1 + + ;Select Plane + + 04A0 8A CA MOV CL, DL ; CL = Save X1 + 04A2 80 E1 03 AND CL, PLANE_BITS ; Get X1 MOD 4 (Plane #) + 04A5 B8 0102 MOV AX, MAP_MASK_PLANE1 ; Code to set Plane #1 + 04A8 D2 E4 SHL AH, CL ; Change to Correct Plane # + OUT_16 SC_Index, AX ; Select Plane + 04AA BA 03C4 1 MOV DX, SC_Index ; then Select Register + 04AD EF 1 OUT DX, AX ; Set I/O Register(s) + + 04AE 8A C5 MOV AL, CH ; Get Saved Color + 04B0 8B 1E 0000 R MOV BX, SCREEN_WIDTH ; Get Offset to Advance Line By + + 04B4 @DL_VLoop: + 04B4 26: 88 05 MOV ES:[DI], AL ; Draw Single Pixel + 04B7 03 FB ADD DI, BX ; Point to Next Line + LOOPjz SI, @DL_EXIT ; Lines--, Exit if done + 04B9 4E 1 DEC SI ; Counter-- + 04BA 74 08 1 JZ @DL_EXIT ; Jump if 0 + + 04BC 26: 88 05 MOV ES:[DI], AL ; Draw Single Pixel + 04BF 03 FB ADD DI, BX ; Point to Next Line + LOOPx SI, @DL_VLoop ; Lines--, Loop until Done + 04C1 4E 1 DEC SI ; Counter-- + 04C2 75 F0 1 JNZ @DL_VLoop ; Jump if not 0 + + 04C4 @DL_EXIT: + + 04C4 E9 0157 JMP @DL_EXIT2 ; Done! + + ; This code Draws a diagonal line in Mode X + + 04C7 @DL_BREZHAM: + 04C7 C4 3E 0014 R LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + 04CB 8B 46 10 MOV AX, [BP].DL_Ypos1 ; get Y1 value + 04CE 8B 5E 0C MOV BX, [BP].DL_Ypos2 ; get Y2 value + 04D1 8B 4E 12 MOV CX, [BP].DL_Xpos1 ; Get Starting Xpos + + 04D4 3B D8 CMP BX, AX ; Y2-Y1 is? + 04D6 73 04 JNC @DL_DeltaYOK ; if Y2>=Y1 then goto... + + 04D8 93 XCHG BX, AX ; Swap em... + 04D9 8B 4E 0E MOV CX, [BP].DL_Xpos2 ; Get New Starting Xpos + + 04DC @DL_DeltaYOK: + 04DC F7 26 0000 R MUL SCREEN_WIDTH ; Offset = SCREEN_WIDTH * Y1 + + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 6 - 1 + + + 04E0 03 F8 ADD DI, AX ; DI -> Start of Line Y1 on Page + 04E2 8B C1 MOV AX, CX ; AX = Xpos (X1) + 04E4 C1 E8 02 SHR AX, 2 ; /4 = Byte Offset into Line + 04E7 03 F8 ADD DI, AX ; DI = Starting pos (X1,Y1) + + 04E9 B0 11 MOV AL, 11h ; Staring Mask + 04EB 80 E1 03 AND CL, PLANE_BITS ; Get Plane # + 04EE D2 E0 SHL AL, CL ; and shift into place + 04F0 8A 66 0A MOV AH, [BP].DL_ColorF ; Color in Hi Bytes + + 04F3 50 PUSH AX ; Save Mask,Color... + + 04F4 8A E0 MOV AH, AL ; Plane # in AH + 04F6 B0 02 MOV AL, MAP_MASK ; Select Plane Register + OUT_16 SC_Index, AX ; Select initial plane + 04F8 BA 03C4 1 MOV DX, SC_Index ; then Select Register + 04FB EF 1 OUT DX, AX ; Set I/O Register(s) + + 04FC 8B 46 12 MOV AX, [BP].DL_Xpos1 ; get X1 value + 04FF 8B 5E 10 MOV BX, [BP].DL_Ypos1 ; get Y1 value + 0502 8B 4E 0E MOV CX, [BP].DL_Xpos2 ; get X2 value + 0505 8B 56 0C MOV DX, [BP].DL_Ypos2 ; get Y2 value + + 0508 8B 2E 0000 R MOV BP, SCREEN_WIDTH ; Use BP for Line width to + ; to avoid extra memory access + + 050C 2B D3 SUB DX, BX ; figure Delta_Y + 050E 73 05 JNC @DL_DeltaYOK2 ; jump if Y2 >= Y1 + + 0510 03 DA ADD BX, DX ; put Y2 into Y1 + 0512 F7 DA NEG DX ; abs(Delta_Y) + 0514 91 XCHG AX, CX ; and exchange X1 and X2 + + 0515 @DL_DeltaYOK2: + 0515 BB 8000 MOV BX, 08000H ; seed for fraction accumulator + + 0518 2B C8 SUB CX, AX ; figure Delta_X + 051A 72 03 JC @DL_DrawLeft ; if negative, go left + + 051C E9 0084 JMP @DL_DrawRight ; Draw Line that slopes right + + 051F @DL_DrawLeft: + + 051F F7 D9 NEG CX ; abs(Delta_X) + + 0521 3B CA CMP CX, DX ; is Delta_X < Delta_Y? + 0523 72 41 JB @DL_SteepLeft ; yes, so go do steep line + ; (Delta_Y iterations) + + ; Draw a Shallow line to the left in Mode X + + 0525 @DL_ShallowLeft: + CLR AX ; zero low word of Delta_Y * 10000h + 0525 33 C0 1 XOR AX, AX ; Set Register = 0 + 0527 2B C2 SUB AX, DX ; DX:AX <- DX * 0FFFFh + 0529 83 DA 00 SBB DX, 0 ; include carry + 052C F7 F1 DIV CX ; divide by Delta_X + + 052E 8B F3 MOV SI, BX ; SI = Accumulator + 0530 8B D8 MOV BX, AX ; BX = Add fraction + 0532 58 POP AX ; Get Color, Bit mask + 0533 BA 03C5 MOV DX, SC_Data ; Sequence controller data register + 0536 41 INC CX ; Inc Delta_X so we can unroll loop + + ; Loop (x2) to Draw Pixels, Move Left, and Maybe Down... + + 0537 @DL_SLLLoop: + 0537 26: 88 25 MOV ES:[DI], AH ; set first pixel, plane data set up + LOOPjz CX, @DL_SLLExit ; Delta_X--, Exit if done + 053A 49 1 DEC CX ; Counter-- + 053B 74 26 1 JZ @DL_SLLExit ; Jump if 0 + + 053D 03 F3 ADD SI, BX ; add numerator to accumulator + 053F 73 02 JNC @DL_SLLL2nc ; move down on carry + + 0541 03 FD ADD DI, BP ; Move Down one line... + + 0543 @DL_SLLL2nc: + 0543 4F DEC DI ; Left one addr + 0544 D0 C8 ROR AL, 1 ; Move Left one plane, back on 0 1 2 + 0546 3C 87 CMP AL, 87h ; wrap?, if AL <88 then Carry set + 0548 83 D7 00 ADC DI, 0 ; Adjust Address: DI = DI + Carry + 054B EE OUT DX, AL ; Set up New Bit Plane mask + + 054C 26: 88 25 MOV ES:[DI], AH ; set pixel + LOOPjz CX, @DL_SLLExit ; Delta_X--, Exit if done + 054F 49 1 DEC CX ; Counter-- + 0550 74 11 1 JZ @DL_SLLExit ; Jump if 0 + + 0552 03 F3 ADD SI, BX ; add numerator to accumulator, + 0554 73 02 JNC @DL_SLLL3nc ; move down on carry + + 0556 03 FD ADD DI, BP ; Move Down one line... + + 0558 @DL_SLLL3nc: ; Now move left a pixel... + 0558 4F DEC DI ; Left one addr + 0559 D0 C8 ROR AL, 1 ; Move Left one plane, back on 0 1 2 + 055B 3C 87 CMP AL, 87h ; Wrap?, if AL <88 then Carry set + 055D 83 D7 00 ADC DI, 0 ; Adjust Address: DI = DI + Carry + 0560 EE OUT DX, AL ; Set up New Bit Plane mask + 0561 EB D4 JMP s @DL_SLLLoop ; loop until done + + 0563 @DL_SLLExit: + 0563 E9 00B8 JMP @DL_EXIT2 ; and exit + + ; Draw a steep line to the left in Mode X + + 0566 @DL_SteepLeft: + CLR AX ; zero low word of Delta_Y * 10000h + 0566 33 C0 1 XOR AX, AX ; Set Register = 0 + 0568 87 D1 XCHG DX, CX ; Delta_Y switched with Delta_X + 056A F7 F1 DIV CX ; divide by Delta_Y + + 056C 8B F3 MOV SI, BX ; SI = Accumulator + 056E 8B D8 MOV BX, AX ; BX = Add Fraction + 0570 58 POP AX ; Get Color, Bit mask + 0571 BA 03C5 MOV DX, SC_Data ; Sequence controller data register + 0574 41 INC CX ; Inc Delta_Y so we can unroll loop + + ; Loop (x2) to Draw Pixels, Move Down, and Maybe left + + 0575 @DL_STLLoop: + + 0575 26: 88 25 MOV ES:[DI], AH ; set first pixel + LOOPjz CX, @DL_STLExit ; Delta_Y--, Exit if done + 0578 49 1 DEC CX ; Counter-- + 0579 74 26 1 JZ @DL_STLExit ; Jump if 0 + + 057B 03 F3 ADD SI, BX ; add numerator to accumulator + 057D 73 09 JNC @DL_STLnc2 ; No carry, just move down! + + 057F 4F DEC DI ; Move Left one addr + 0580 D0 C8 ROR AL, 1 ; Move Left one plane, back on 0 1 2 + 0582 3C 87 CMP AL, 87h ; Wrap?, if AL <88 then Carry set + 0584 83 D7 00 ADC DI, 0 ; Adjust Address: DI = DI + Carry + 0587 EE OUT DX, AL ; Set up New Bit Plane mask + + 0588 @DL_STLnc2: + 0588 03 FD ADD DI, BP ; advance to next line. + + 058A 26: 88 25 MOV ES:[DI], AH ; set pixel + LOOPjz CX, @DL_STLExit ; Delta_Y--, Exit if done + 058D 49 1 DEC CX ; Counter-- + 058E 74 11 1 JZ @DL_STLExit ; Jump if 0 + + 0590 03 F3 ADD SI, BX ; add numerator to accumulator + 0592 73 09 JNC @DL_STLnc3 ; No carry, just move down! + + 0594 4F DEC DI ; Move Left one addr + 0595 D0 C8 ROR AL, 1 ; Move Left one plane, back on 0 1 2 + 0597 3C 87 CMP AL, 87h ; Wrap?, if AL <88 then Carry set + 0599 83 D7 00 ADC DI, 0 ; Adjust Address: DI = DI + Carry + 059C EE OUT DX, AL ; Set up New Bit Plane mask + + 059D @DL_STLnc3: + 059D 03 FD ADD DI, BP ; advance to next line. + 059F EB D4 JMP s @DL_STLLoop ; Loop until done + + 05A1 @DL_STLExit: + 05A1 EB 7B JMP @DL_EXIT2 ; and exit + + ; Draw a line that goes to the Right... + + 05A3 @DL_DrawRight: + 05A3 3B CA CMP CX, DX ; is Delta_X < Delta_Y? + 05A5 72 3E JB @DL_SteepRight ; yes, so go do steep line + ; (Delta_Y iterations) + + ; Draw a Shallow line to the Right in Mode X + + 05A7 @DL_ShallowRight: + CLR AX ; zero low word of Delta_Y * 10000h + 05A7 33 C0 1 XOR AX, AX ; Set Register = 0 + 05A9 2B C2 SUB AX, DX ; DX:AX <- DX * 0FFFFh + 05AB 83 DA 00 SBB DX, 0 ; include carry + 05AE F7 F1 DIV CX ; divide by Delta_X + + 05B0 8B F3 MOV SI, BX ; SI = Accumulator + 05B2 8B D8 MOV BX, AX ; BX = Add Fraction + 05B4 58 POP AX ; Get Color, Bit mask + 05B5 BA 03C5 MOV DX, SC_Data ; Sequence controller data register + 05B8 41 INC CX ; Inc Delta_X so we can unroll loop + + ; Loop (x2) to Draw Pixels, Move Right, and Maybe Down... + + 05B9 @DL_SLRLoop: + 05B9 26: 88 25 MOV ES:[DI], AH ; set first pixel, mask is set up + LOOPjz CX, @DL_SLRExit ; Delta_X--, Exit if done.. + 05BC 49 1 DEC CX ; Counter-- + 05BD 74 24 1 JZ @DL_SLRExit ; Jump if 0 + + 05BF 03 F3 ADD SI, BX ; add numerator to accumulator + 05C1 73 02 JNC @DL_SLR2nc ; don't move down if carry not set + + 05C3 03 FD ADD DI, BP ; Move Down one line... + + 05C5 @DL_SLR2nc: ; Now move right a pixel... + 05C5 D0 C0 ROL AL, 1 ; Move Right one addr if Plane = 0 + 05C7 3C 12 CMP AL, 12h ; Wrap? if AL >12 then Carry not set + 05C9 83 D7 00 ADC DI, 0 ; Adjust Address: DI = DI + Carry + 05CC EE OUT DX, AL ; Set up New Bit Plane mask + + 05CD 26: 88 25 MOV ES:[DI], AH ; set pixel + LOOPjz CX, @DL_SLRExit ; Delta_X--, Exit if done.. + 05D0 49 1 DEC CX ; Counter-- + 05D1 74 10 1 JZ @DL_SLRExit ; Jump if 0 + + 05D3 03 F3 ADD SI, BX ; add numerator to accumulator + 05D5 73 02 JNC @DL_SLR3nc ; don't move down if carry not set + + 05D7 03 FD ADD DI, BP ; Move Down one line... + + 05D9 @DL_SLR3nc: + 05D9 D0 C0 ROL AL, 1 ; Move Right one addr if Plane = 0 + 05DB 3C 12 CMP AL, 12h ; Wrap? if AL >12 then Carry not set + 05DD 83 D7 00 ADC DI, 0 ; Adjust Address: DI = DI + Carry + 05E0 EE OUT DX, AL ; Set up New Bit Plane mask + 05E1 EB D6 JMP s @DL_SLRLoop ; loop till done + + 05E3 @DL_SLRExit: + 05E3 EB 39 JMP @DL_EXIT2 ; and exit + + ; Draw a Steep line to the Right in Mode X + + 05E5 @DL_SteepRight: + CLR AX ; zero low word of Delta_Y * 10000h + 05E5 33 C0 1 XOR AX, AX ; Set Register = 0 + 05E7 87 D1 XCHG DX, CX ; Delta_Y switched with Delta_X + 05E9 F7 F1 DIV CX ; divide by Delta_Y + + 05EB 8B F3 MOV SI, BX ; SI = Accumulator + 05ED 8B D8 MOV BX, AX ; BX = Add Fraction + 05EF 58 POP AX ; Get Color, Bit mask + 05F0 BA 03C5 MOV DX, SC_Data ; Sequence controller data register + 05F3 41 INC CX ; Inc Delta_Y so we can unroll loop + + ; Loop (x2) to Draw Pixels, Move Down, and Maybe Right + + 05F4 @STRLoop: + 05F4 26: 88 25 MOV ES:[DI], AH ; set first pixel, mask is set up + LOOPjz CX, @DL_EXIT2 ; Delta_Y--, Exit if Done + 05F7 49 1 DEC CX ; Counter-- + 05F8 74 24 1 JZ @DL_EXIT2 ; Jump if 0 + + 05FA 03 F3 ADD SI, BX ; add numerator to accumulator + 05FC 73 08 JNC @STRnc2 ; if no carry then just go down... + + 05FE D0 C0 ROL AL, 1 ; Move Right one addr if Plane = 0 + 0600 3C 12 CMP AL, 12h ; Wrap? if AL >12 then Carry not set + 0602 83 D7 00 ADC DI, 0 ; Adjust Address: DI = DI + Carry + 0605 EE OUT DX, AL ; Set up New Bit Plane mask + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 7 - 1 + + + + 0606 @STRnc2: + 0606 03 FD ADD DI, BP ; advance to next line. + + 0608 26: 88 25 MOV ES:[DI], AH ; set pixel + LOOPjz CX, @DL_EXIT2 ; Delta_Y--, Exit if Done + 060B 49 1 DEC CX ; Counter-- + 060C 74 10 1 JZ @DL_EXIT2 ; Jump if 0 + + 060E 03 F3 ADD SI, BX ; add numerator to accumulator + 0610 73 08 JNC @STRnc3 ; if no carry then just go down... + + 0612 D0 C0 ROL AL, 1 ; Move Right one addr if Plane = 0 + 0614 3C 12 CMP AL, 12h ; Wrap? if AL >12 then Carry not set + 0616 83 D7 00 ADC DI, 0 ; Adjust Address: DI = DI + Carry + 0619 EE OUT DX, AL ; Set up New Bit Plane mask + + 061A @STRnc3: + 061A 03 FD ADD DI, BP ; advance to next line. + 061C EB D6 JMP s @STRLoop ; loop till done + + 061E @DL_EXIT2: + POPx DI, SI, BP ; Restore Saved Registers + 061E 5F 1 POP DI ; Restore R1 + 061F 5E 2 POP SI ; Restore R1 + 0620 5D 3 POP BP ; Restore R1 + 0621 CA 000A RET 10 ; Exit and Clean up Stack + + 0624 DRAW_LINE ENDP + + + ; ===== DAC COLOR REGISTER ROUTINES ===== + + ;================================================= + ;SET_DAC_REGISTER (Register%, Red%, Green%, Blue%) + ;================================================= + ; + ; Sets a single (RGB) Vga Palette Register + ; + ; ENTRY: Register = The DAC # to modify (0-255) + ; Red = The new Red Intensity (0-63) + ; Green = The new Green Intensity (0-63) + ; Blue = The new Blue Intensity (0-63) + ; + ; EXIT: No meaningful values returned + ; + + 000E SDR_STACK STRUC + 0000 0000 DW ? ; BP + 0002 00000000 DD ? ; Caller + 0006 00 00 SDR_Blue DB ?,? ; Blue Data Value + 0008 00 00 SDR_Green DB ?,? ; Green Data Value + 000A 00 00 SDR_Red DB ?,? ; Red Data Value + 000C 00 00 SDR_Register DB ?,? ; Palette Register # + SDR_STACK ENDS + + PUBLIC SET_DAC_REGISTER + + 0624 SET_DAC_REGISTER PROC FAR + + 0624 55 PUSH BP ; Save BP + 0625 8B EC MOV BP, SP ; Set up Stack Frame + + ; Select which DAC Register to modify + + OUT_8 DAC_WRITE_ADDR, [BP].SDR_Register + 0627 BA 03C8 1 MOV DX, DAC_WRITE_ADDR ; then Select Register + 062A 8A 46 0C 1 MOV AL, [BP].SDR_Register ; then Get Data Value + 062D EE 1 OUT DX, AL ; Set I/O Register + + 062E BA 03C9 MOV DX, PEL_DATA_REG ; Dac Data Register + OUT_8 DX, [BP].SDR_Red ; Set Red Intensity + 0631 8A 46 0A 1 MOV AL, [BP].SDR_Red ; then Get Data Value + 0634 EE 1 OUT DX, AL ; Set I/O Register + OUT_8 DX, [BP].SDR_Green ; Set Green Intensity + 0635 8A 46 08 1 MOV AL, [BP].SDR_Green ; then Get Data Value + 0638 EE 1 OUT DX, AL ; Set I/O Register + OUT_8 DX, [BP].SDR_Blue ; Set Blue Intensity + 0639 8A 46 06 1 MOV AL, [BP].SDR_Blue ; then Get Data Value + 063C EE 1 OUT DX, AL ; Set I/O Register + + 063D 5D POP BP ; Restore Registers + 063E CA 0008 RET 8 ; Exit & Clean Up Stack + + 0641 SET_DAC_REGISTER ENDP + + ;==================================================== + ;GET_DAC_REGISTER (Register%, &Red%, &Green%, &Blue%) + ;==================================================== + ; + ; Reads the RGB Values of a single Vga Palette Register + ; + ; ENTRY: Register = The DAC # to read (0-255) + ; Red = Offset to Red Variable in DS + ; Green = Offset to Green Variable in DS + ; Blue = Offset to Blue Variable in DS + ; + ; EXIT: The values of the integer variables Red, + ; Green, and Blue are set to the values + ; taken from the specified DAC register. + ; + + 000E GDR_STACK STRUC + 0000 0000 DW ? ; BP + 0002 00000000 DD ? ; Caller + 0006 0000 GDR_Blue DW ? ; Addr of Blue Data Value in DS + 0008 0000 GDR_Green DW ? ; Addr of Green Data Value in DS + 000A 0000 GDR_Red DW ? ; Addr of Red Data Value in DS + 000C 00 00 GDR_Register DB ?,? ; Palette Register # + GDR_STACK ENDS + + PUBLIC GET_DAC_REGISTER + + 0641 GET_DAC_REGISTER PROC FAR + + 0641 55 PUSH BP ; Save BP + 0642 8B EC MOV BP, SP ; Set up Stack Frame + + ; Select which DAC Register to read in + + OUT_8 DAC_READ_ADDR, [BP].GDR_Register + 0644 BA 03C7 1 MOV DX, DAC_READ_ADDR ; then Select Register + 0647 8A 46 0C 1 MOV AL, [BP].GDR_Register ; then Get Data Value + 064A EE 1 OUT DX, AL ; Set I/O Register + + 064B BA 03C9 MOV DX, PEL_DATA_REG ; Dac Data Register + CLR AX ; Clear AX + 064E 33 C0 1 XOR AX, AX ; Set Register = 0 + + 0650 EC IN AL, DX ; Read Red Value + 0651 8B 5E 0A MOV BX, [BP].GDR_Red ; Get Address of Red% + 0654 89 07 MOV [BX], AX ; *Red% = AX + + 0656 EC IN AL, DX ; Read Green Value + 0657 8B 5E 08 MOV BX, [BP].GDR_Green ; Get Address of Green% + 065A 89 07 MOV [BX], AX ; *Green% = AX + + 065C EC IN AL, DX ; Read Blue Value + 065D 8B 5E 06 MOV BX, [BP].GDR_Blue ; Get Address of Blue% + 0660 89 07 MOV [BX], AX ; *Blue% = AX + + 0662 5D POP BP ; Restore Registers + 0663 CA 0008 RET 8 ; Exit & Clean Up Stack + + 0666 GET_DAC_REGISTER ENDP + + + ;=========================================================== + ;LOAD_DAC_REGISTERS (SEG PalData, StartReg%, EndReg%, Sync%) + ;=========================================================== + ; + ; Sets a Block of Vga Palette Registers + ; + ; ENTRY: PalData = Far Pointer to Block of palette data + ; StartReg = First Register # in range to set (0-255) + ; EndReg = Last Register # in Range to set (0-255) + ; Sync = Wait for Vertical Retrace Flag (Boolean) + ; + ; EXIT: No meaningful values returned + ; + ; NOTES: PalData is a linear array of 3 byte Palette values + ; in the order: Red (0-63), Green (0-63), Blue (0-63) + ; + + 0014 LDR_STACK STRUC + 0000 0000 0000 0000 DW ?x3 ; BP, DS, SI + 0006 00000000 DD ? ; Caller + 000A 0000 LDR_Sync DW ? ; Vertical Sync Flag + 000C 00 00 LDR_EndReg DB ?,? ; Last Register # + 000E 00 00 LDR_StartReg DB ?,? ; First Register # + 0010 00000000 LDR_PalData DD ? ; Far Ptr to Palette Data + LDR_STACK ENDS + + PUBLIC LOAD_DAC_REGISTERS + + 0666 LOAD_DAC_REGISTERS PROC FAR + + PUSHx BP, DS, SI ; Save Registers + 0666 55 1 PUSH BP ; Save R1 + 0667 1E 2 PUSH DS ; Save R1 + 0668 56 3 PUSH SI ; Save R1 + 0669 8B EC mov BP, SP ; Set up Stack Frame + + 066B 8B 46 0A mov AX, [BP].LDR_Sync ; Get Vertical Sync Flag + 066E 0B C0 or AX, AX ; is Sync Flag = 0? + 0670 74 05 jz @LDR_Load ; if so, skip call + + 0672 9A ---- 0795 R call f SYNC_DISPLAY ; wait for vsync + + ; Determine register #'s, size to copy, etc + + 0677 @LDR_Load: + + 0677 C5 76 10 lds SI, [BP].LDR_PalData ; DS:SI -> Palette Data + 067A BA 03C8 mov DX, DAC_WRITE_ADDR ; DAC register # selector + + CLR AX, BX ; Clear for byte loads + 067D 33 C0 1 XOR AX, AX ; Set Register = 0 + 067F 33 DB 2 XOR BX, BX ; Set Register = 0 + 0681 8A 46 0E mov AL, [BP].LDR_StartReg ; Get Start Register + 0684 8A 5E 0C mov BL, [BP].LDR_EndReg ; Get End Register + + 0687 2B D8 sub BX, AX ; BX = # of DAC registers -1 + 0689 43 inc BX ; BX = # of DAC registers + 068A 8B CB mov CX, BX ; CX = # of DAC registers + 068C 03 CB add CX, BX ; CX = " " * 2 + 068E 03 CB add CX, BX ; CX = " " * 3 + 0690 FC cld ; Block OUTs forward + 0691 EE out DX, AL ; set up correct register # + + ; Load a block of DAC Registers + + 0692 BA 03C9 mov DX, PEL_DATA_REG ; Dac Data Register + + 0695 F3/ 6E rep outsb ; block set DAC registers + + POPx SI, DS, BP ; Restore Registers + 0697 5E 1 POP SI ; Restore R1 + 0698 1F 2 POP DS ; Restore R1 + 0699 5D 3 POP BP ; Restore R1 + 069A CA 000A ret 10 ; Exit & Clean Up Stack + + 069D LOAD_DAC_REGISTERS ENDP + + + ;==================================================== + ;READ_DAC_REGISTERS (SEG PalData, StartReg%, EndReg%) + ;==================================================== + ; + ; Reads a Block of Vga Palette Registers + ; + ; ENTRY: PalData = Far Pointer to block to store palette data + ; StartReg = First Register # in range to read (0-255) + ; EndReg = Last Register # in Range to read (0-255) + ; + ; EXIT: No meaningful values returned + ; + ; NOTES: PalData is a linear array of 3 byte Palette values + ; in the order: Red (0-63), Green (0-63), Blue (0-63) + ; + + 0012 RDR_STACK STRUC + 0000 0000 0000 0000 DW ?x3 ; BP, ES, DI + 0006 00000000 DD ? ; Caller + 000A 00 00 RDR_EndReg DB ?,? ; Last Register # + 000C 00 00 RDR_StartReg DB ?,? ; First Register # + 000E 00000000 RDR_PalData DD ? ; Far Ptr to Palette Data + RDR_STACK ENDS + + PUBLIC READ_DAC_REGISTERS + + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 8 - 1 + + + 069D READ_DAC_REGISTERS PROC FAR + + PUSHx BP, ES, DI ; Save Registers + 069D 55 1 PUSH BP ; Save R1 + 069E 06 2 PUSH ES ; Save R1 + 069F 57 3 PUSH DI ; Save R1 + 06A0 8B EC mov BP, SP ; Set up Stack Frame + + ; Determine register #'s, size to copy, etc + + 06A2 C4 7E 0E les DI, [BP].RDR_PalData ; ES:DI -> Palette Buffer + 06A5 BA 03C7 mov DX, DAC_READ_ADDR ; DAC register # selector + + CLR AX, BX ; Clear for byte loads + 06A8 33 C0 1 XOR AX, AX ; Set Register = 0 + 06AA 33 DB 2 XOR BX, BX ; Set Register = 0 + 06AC 8A 46 0C mov AL, [BP].RDR_StartReg ; Get Start Register + 06AF 8A 5E 0A mov BL, [BP].RDR_EndReg ; Get End Register + + 06B2 2B D8 sub BX, AX ; BX = # of DAC registers -1 + 06B4 43 inc BX ; BX = # of DAC registers + 06B5 8B CB mov CX, BX ; CX = # of DAC registers + 06B7 03 CB add CX, BX ; CX = " " * 2 + 06B9 03 CB add CX, BX ; CX = " " * 3 + 06BB FC cld ; Block INs forward + + ; Read a block of DAC Registers + + 06BC EE out DX, AL ; set up correct register # + 06BD BA 03C9 mov DX, PEL_DATA_REG ; Dac Data Register + + 06C0 F3/ 6C rep insb ; block read DAC registers + + POPx DI, ES, BP ; Restore Registers + + 06C2 5F 1 POP DI ; Restore R1 + 06C3 07 2 POP ES ; Restore R1 + 06C4 5D 3 POP BP ; Restore R1 + 06C5 CA 0008 ret 8 ; Exit & Clean Up Stack + + 06C8 READ_DAC_REGISTERS ENDP + + + ; ===== PAGE FLIPPING AND SCROLLING ROUTINES ===== + + ;========================= + ;SET_ACTIVE_PAGE (PageNo%) + ;========================= + ; + ; Sets the active display Page to be used for future drawing + ; + ; ENTRY: PageNo = Display Page to make active + ; (values: 0 to Number of Pages - 1) + ; + ; EXIT: No meaningful values returned + ; + + 0008 SAP_STACK STRUC + 0000 0000 DW ? ; BP + 0002 00000000 DD ? ; Caller + 0006 0000 SAP_Page DW ? ; Page # for Drawing + SAP_STACK ENDS + + PUBLIC SET_ACTIVE_PAGE + + 06C8 SET_ACTIVE_PAGE PROC FAR + + 06C8 55 PUSH BP ; Preserve Registers + 06C9 8B EC MOV BP, SP ; Set up Stack Frame + + 06CB 8B 5E 06 MOV BX, [BP].SAP_Page ; Get Desired Page # + 06CE 3B 1E 0004 R CMP BX, LAST_PAGE ; Is Page # Valid? + 06D2 73 0D JAE @SAP_Exit ; IF Not, Do Nothing + + 06D4 89 1E 0012 R MOV ACTIVE_PAGE, BX ; Set Active Page # + + 06D8 D1 E3 SHL BX, 1 ; Scale Page # to Word + 06DA 8B 87 0006 R MOV AX, PAGE_ADDR[BX] ; Get offset to Page + + 06DE A3 0014 R MOV CURRENT_PAGE, AX ; And set for future LES's + + 06E1 @SAP_Exit: + 06E1 5D POP BP ; Restore Registers + 06E2 CA 0002 RET 2 ; Exit and Clean up Stack + + 06E5 SET_ACTIVE_PAGE ENDP + + + ;================ + ;GET_ACTIVE_PAGE% + ;================ + ; + ; Returns the Video Page # currently used for Drawing + ; + ; ENTRY: No Parameters are passed + ; + ; EXIT: AX = Current Video Page used for Drawing + ; + + PUBLIC GET_ACTIVE_PAGE + + 06E5 GET_ACTIVE_PAGE PROC FAR + + 06E5 A1 0012 R MOV AX, ACTIVE_PAGE ; Get Active Page # + 06E8 CB RET ; Exit and Clean up Stack + + 06E9 GET_ACTIVE_PAGE ENDP + + + ;=============================== + ;SET_DISPLAY_PAGE (DisplayPage%) + ;=============================== + ; + ; Sets the currently visible display page. + ; When called this routine syncronizes the display + ; to the vertical blank. + ; + ; ENTRY: PageNo = Display Page to show on the screen + ; (values: 0 to Number of Pages - 1) + ; + ; EXIT: No meaningful values returned + ; + + 0008 SDP_STACK STRUC + 0000 0000 DW ? ; BP + 0002 00000000 DD ? ; Caller + 0006 0000 SDP_Page DW ? ; Page # to Display... + SDP_STACK ENDS + + PUBLIC SET_DISPLAY_PAGE + + 06E9 SET_DISPLAY_PAGE PROC FAR + + 06E9 55 PUSH BP ; Preserve Registers + 06EA 8B EC MOV BP, SP ; Set up Stack Frame + + 06EC 8B 5E 06 MOV BX, [BP].SDP_Page ; Get Desired Page # + 06EF 3B 1E 0004 R CMP BX, LAST_PAGE ; Is Page # Valid? + 06F3 73 2B JAE @SDP_Exit ; IF Not, Do Nothing + + 06F5 89 1E 0010 R MOV DISPLAY_PAGE, BX ; Set Display Page # + + 06F9 D1 E3 SHL BX, 1 ; Scale Page # to Word + 06FB 8B 8F 0006 R MOV CX, PAGE_ADDR[BX] ; Get offset in memory to Page + 06FF 03 0E 001C R ADD CX, CURRENT_MOFFSET ; Adjust for any scrolling + + ; Wait if we are currently in a Vertical Retrace + + 0703 BA 03DA MOV DX, INPUT_1 ; Input Status #1 Register + + 0706 @DP_WAIT0: + 0706 EC IN AL, DX ; Get VGA status + 0707 24 08 AND AL, VERT_RETRACE ; In Display mode yet? + 0709 75 FB JNZ @DP_WAIT0 ; If Not, wait for it + + ; Set the Start Display Address to the new page + + 070B BA 03D4 MOV DX, CRTC_Index ; We Change the VGA Sequencer + + 070E B0 0D MOV AL, START_DISP_LO ; Display Start Low Register + 0710 8A E1 MOV AH, CL ; Low 8 Bits of Start Addr + 0712 EF OUT DX, AX ; Set Display Addr Low + + 0713 B0 0C MOV AL, START_DISP_HI ; Display Start High Register + 0715 8A E5 MOV AH, CH ; High 8 Bits of Start Addr + 0717 EF OUT DX, AX ; Set Display Addr High + + ; Wait for a Vertical Retrace to smooth out things + + 0718 BA 03DA MOV DX, INPUT_1 ; Input Status #1 Register + + 071B @DP_WAIT1: + 071B EC IN AL, DX ; Get VGA status + 071C 24 08 AND AL, VERT_RETRACE ; Vertical Retrace Start? + 071E 74 FB JZ @DP_WAIT1 ; If Not, wait for it + + ; Now Set Display Starting Address + + + 0720 @SDP_Exit: + 0720 5D POP BP ; Restore Registers + 0721 CA 0002 RET 2 ; Exit and Clean up Stack + + 0724 SET_DISPLAY_PAGE ENDP + + + ;================= + ;GET_DISPLAY_PAGE% + ;================= + ; + ; Returns the Video Page # currently displayed + ; + ; ENTRY: No Parameters are passed + ; + ; EXIT: AX = Current Video Page being displayed + ; + + PUBLIC GET_DISPLAY_PAGE + + 0724 GET_DISPLAY_PAGE PROC FAR + + 0724 A1 0010 R MOV AX, DISPLAY_PAGE ; Get Display Page # + 0727 CB RET ; Exit & Clean Up Stack + + 0728 GET_DISPLAY_PAGE ENDP + + + ;======================================= + ;SET_WINDOW (DisplayPage%, Xpos%, Ypos%) + ;======================================= + ; + ; Since a Logical Screen can be larger than the Physical + ; Screen, Scrolling is possible. This routine sets the + ; Upper Left Corner of the Screen to the specified Pixel. + ; Also Sets the Display page to simplify combined page + ; flipping and scrolling. When called this routine + ; syncronizes the display to the vertical blank. + ; + ; ENTRY: DisplayPage = Display Page to show on the screen + ; Xpos = # of pixels to shift screen right + ; Ypos = # of lines to shift screen down + ; + ; EXIT: No meaningful values returned + ; + + 000C SW_STACK STRUC + 0000 0000 DW ? ; BP + 0002 00000000 DD ? ; Caller + 0006 0000 SW_Ypos DW ? ; Y pos of UL Screen Corner + 0008 0000 SW_Xpos DW ? ; X pos of UL Screen Corner + 000A 0000 SW_Page DW ? ; (new) Display Page + SW_STACK ENDS + + PUBLIC SET_WINDOW + + 0728 SET_WINDOW PROC FAR + + 0728 55 PUSH BP ; Preserve Registers + 0729 8B EC MOV BP, SP ; Set up Stack Frame + + ; Check if our Scroll Offsets are Valid + + 072B 8B 5E 0A MOV BX, [BP].SW_Page ; Get Desired Page # + 072E 3B 1E 0004 R CMP BX, LAST_PAGE ; Is Page # Valid? + 0732 73 55 JAE @SW_Exit ; IF Not, Do Nothing + + 0734 8B 46 06 MOV AX, [BP].SW_Ypos ; Get Desired Y Offset + 0737 3B 06 0020 R CMP AX, MAX_YOFFSET ; Is it Within Limits? + 073B 77 4C JA @SW_Exit ; if not, exit + + 073D 8B 4E 08 MOV CX, [BP].SW_Xpos ; Get Desired X Offset + 0740 3B 0E 001E R CMP CX, MAX_XOFFSET ; Is it Within Limits? + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 9 - 1 + + + 0744 77 43 JA @SW_Exit ; if not, exit + + ; Compute proper Display start address to use + + 0746 F7 26 0000 R MUL SCREEN_WIDTH ; AX = YOffset * Line Width + 074A C1 E9 02 SHR CX, 2 ; CX / 4 = Bytes into Line + 074D 03 C1 ADD AX, CX ; AX = Offset of Upper Left Pixel + + 074F A3 001C R MOV CURRENT_MOFFSET, AX ; Save Offset Info + + 0752 89 1E 0010 R MOV DISPLAY_PAGE, BX ; Set Current Page # + 0756 D1 E3 SHL BX, 1 ; Scale Page # to Word + 0758 03 87 0006 R ADD AX, PAGE_ADDR[BX] ; Get offset in VGA to Page + 075C 8B D8 MOV BX, AX ; BX = Desired Display Start + + 075E BA 03DA MOV DX, INPUT_1 ; Input Status #1 Register + + ; Wait if we are currently in a Vertical Retrace + + 0761 @SW_WAIT0: + 0761 EC IN AL, DX ; Get VGA status + 0762 24 08 AND AL, VERT_RETRACE ; In Display mode yet? + 0764 75 FB JNZ @SW_WAIT0 ; If Not, wait for it + + ; Set the Start Display Address to the new window + + 0766 BA 03D4 MOV DX, CRTC_Index ; We Change the VGA Sequencer + 0769 B0 0D MOV AL, START_DISP_LO ; Display Start Low Register + 076B 8A E3 MOV AH, BL ; Low 8 Bits of Start Addr + 076D EF OUT DX, AX ; Set Display Addr Low + + 076E B0 0C MOV AL, START_DISP_HI ; Display Start High Register + 0770 8A E7 MOV AH, BH ; High 8 Bits of Start Addr + 0772 EF OUT DX, AX ; Set Display Addr High + + ; Wait for a Vertical Retrace to smooth out things + + 0773 BA 03DA MOV DX, INPUT_1 ; Input Status #1 Register + + 0776 @SW_WAIT1: + 0776 EC IN AL, DX ; Get VGA status + 0777 24 08 AND AL, VERT_RETRACE ; Vertical Retrace Start? + 0779 74 FB JZ @SW_WAIT1 ; If Not, wait for it + + ; Now Set the Horizontal Pixel Pan values + + OUT_8 ATTRIB_Ctrl, PIXEL_PAN_REG ; Select Pixel Pan Register + 077B BA 03C0 1 MOV DX, ATTRIB_Ctrl ; then Select Register + 077E B0 33 1 MOV AL, PIXEL_PAN_REG ; then Get Data Value + 0780 EE 1 OUT DX, AL ; Set I/O Register + + 0781 8B 46 08 MOV AX, [BP].SW_Xpos ; Get Desired X Offset + 0784 24 03 AND AL, 03 ; Get # of Pixels to Pan (0-3) + 0786 D0 E0 SHL AL, 1 ; Shift for 256 Color Mode + 0788 EE OUT DX, AL ; Fine tune the display! + + 0789 @SW_Exit: + 0789 5D POP BP ; Restore Saved Registers + 078A CA 0006 RET 6 ; Exit and Clean up Stack + + 078D SET_WINDOW ENDP + + + ;============= + ;GET_X_OFFSET% + ;============= + ; + ; Returns the X coordinate of the Pixel currently display + ; in the upper left corner of the display + ; + ; ENTRY: No Parameters are passed + ; + ; EXIT: AX = Current Horizontal Scroll Offset + ; + + PUBLIC GET_X_OFFSET + + 078D GET_X_OFFSET PROC FAR + + 078D A1 0018 R MOV AX, CURRENT_XOFFSET ; Get current horz offset + 0790 CB RET ; Exit & Clean Up Stack + + 0791 GET_X_OFFSET ENDP + + + ;============= + ;GET_Y_OFFSET% + ;============= + ; + ; Returns the Y coordinate of the Pixel currently display + ; in the upper left corner of the display + ; + ; ENTRY: No Parameters are passed + ; + ; EXIT: AX = Current Vertical Scroll Offset + ; + + PUBLIC GET_Y_OFFSET + + 0791 GET_Y_OFFSET PROC FAR + + 0791 A1 001A R MOV AX, CURRENT_YOFFSET ; Get current vertical offset + 0794 CB RET ; Exit & Clean Up Stack + + 0795 GET_Y_OFFSET ENDP + + + ;============ + ;SYNC_DISPLAY + ;============ + ; + ; Pauses the computer until the next Vertical Retrace starts + ; + ; ENTRY: No Parameters are passed + ; + ; EXIT: No meaningful values returned + ; + + PUBLIC SYNC_DISPLAY + + 0795 SYNC_DISPLAY PROC FAR + + 0795 BA 03DA MOV DX, INPUT_1 ; Input Status #1 Register + + ; Wait for any current retrace to end + + 0798 @SD_WAIT0: + 0798 EC IN AL, DX ; Get VGA status + 0799 24 08 AND AL, VERT_RETRACE ; In Display mode yet? + 079B 75 FB JNZ @SD_WAIT0 ; If Not, wait for it + + ; Wait for the start of the next vertical retrace + + 079D @SD_WAIT1: + 079D EC IN AL, DX ; Get VGA status + 079E 24 08 AND AL, VERT_RETRACE ; Vertical Retrace Start? + 07A0 74 FB JZ @SD_WAIT1 ; If Not, wait for it + + 07A2 CB RET ; Exit & Clean Up Stack + + 07A3 SYNC_DISPLAY ENDP + + + ; ===== TEXT DISPLAY ROUTINES ===== + + ;================================================== + ;GPRINTC (CharNum%, Xpos%, Ypos%, ColorF%, ColorB%) + ;================================================== + ; + ; Draws an ASCII Text Character using the currently selected + ; 8x8 font on the active display page. It would be a simple + ; exercise to make this routine process variable height fonts. + ; + ; ENTRY: CharNum = ASCII character # to draw + ; Xpos = X position to draw Character at + ; Ypos = Y position of to draw Character at + ; ColorF = Color to draw text character in + ; ColorB = Color to set background to + ; + ; EXIT: No meaningful values returned + ; + + 001E GPC_STACK STRUC + 0000 0000 GPC_Width DW ? ; Screen Width-1 + 0002 00 00 GPC_Lines DB ?,? ; Scan lines to Decode + 0004 0000 GPC_T_SETS DW ? ; Saved Charset Segment + 0006 0000 GPC_T_SETO DW ? ; Saved Charset Offset + 0008 0000 0000 0000 DW ?x4 ; DI, SI, DS, BP + 0000 + 0010 00000000 DD ? ; Caller + 0014 00 00 GPC_ColorB DB ?,? ; Background Color + 0016 00 00 GPC_ColorF DB ?,? ; Text Color + 0018 0000 GPC_Ypos DW ? ; Y Position to Print at + 001A 0000 GPC_Xpos DW ? ; X position to Print at + 001C 00 00 GPC_Char DB ?,? ; Character to Print + GPC_STACK ENDS + + PUBLIC GPRINTC + + 07A3 GPRINTC PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 07A3 55 1 PUSH BP ; Save R1 + 07A4 1E 2 PUSH DS ; Save R1 + 07A5 56 3 PUSH SI ; Save R1 + 07A6 57 4 PUSH DI ; Save R1 + 07A7 83 EC 08 SUB SP, 8 ; Allocate WorkSpace on Stack + 07AA 8B EC MOV BP, SP ; Set up Stack Frame + + 07AC C4 3E 0014 R LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + 07B0 A1 0000 R MOV AX, SCREEN_WIDTH ; Get Logical Line Width + 07B3 8B D8 MOV BX, AX ; BX = Screen Width + 07B5 4B DEC BX ; = Screen Width-1 + 07B6 89 5E 00 MOV [BP].GPC_Width, BX ; Save for later use + + 07B9 F7 66 18 MUL [BP].GPC_Ypos ; Start of Line = Ypos * Width + 07BC 03 F8 ADD DI, AX ; DI -> Start of Line Ypos + + 07BE 8B 46 1A MOV AX, [BP].GPC_Xpos ; Get Xpos of Character + 07C1 8B C8 MOV CX, AX ; Save Copy of Xpos + 07C3 C1 E8 02 SHR AX, 2 ; Bytes into Line = Xpos/4 + 07C6 03 F8 ADD DI, AX ; DI -> (Xpos, Ypos) + + ;Get Source ADDR of Character Bit Map & Save + + 07C8 8A 46 1C MOV AL, [BP].GPC_Char ; Get Character # + 07CB A8 80 TEST AL, 080h ; Is Hi Bit Set? + 07CD 74 0C JZ @GPC_LowChar ; Nope, use low char set ptr + + 07CF 24 7F AND AL, 07Fh ; Mask Out Hi Bit + 07D1 8B 1E 0026 R MOV BX, CHARSET_HI ; BX = Char Set Ptr:Offset + 07D5 8B 16 0028 R MOV DX, CHARSET_HI+2 ; DX = Char Set Ptr:Segment + 07D9 EB 08 JMP s @GPC_Set_Char ; Go Setup Character Ptr + + 07DB @GPC_LowChar: + + 07DB 8B 1E 0022 R MOV BX, CHARSET_LOW ; BX = Char Set Ptr:Offset + 07DF 8B 16 0024 R MOV DX, CHARSET_LOW+2 ; DX = Char Set Ptr:Segment + + 07E3 @GPC_Set_Char: + 07E3 89 56 04 MOV [BP].GPC_T_SETS, DX ; Save Segment on Stack + + 07E6 B4 00 MOV AH, 0 ; Valid #'s are 0..127 + 07E8 C1 E0 03 SHL AX, 3 ; * 8 Bytes Per Bitmap + 07EB 03 D8 ADD BX, AX ; BX = Offset of Selected char + 07ED 89 5E 06 MOV [BP].GPC_T_SETO, BX ; Save Offset on Stack + + 07F0 83 E1 03 AND CX, PLANE_BITS ; Get Plane # + 07F3 B5 0F MOV CH, ALL_PLANES ; Get Initial Plane mask + 07F5 D2 E5 SHL CH, CL ; And shift into position + 07F7 80 E5 0F AND CH, ALL_PLANES ; And mask to lower nibble + + 07FA B0 04 MOV AL, 04 ; 4-Plane # = # of initial + 07FC 2A C1 SUB AL, CL ; shifts to align bit mask + 07FE 8A C8 MOV CL, AL ; Shift Count for SHL + + ;Get segment of character map + + OUT_8 SC_Index, MAP_MASK ; Setup Plane selections + 0800 BA 03C4 1 MOV DX, SC_Index ; then Select Register + 0803 B0 02 1 MOV AL, MAP_MASK ; then Get Data Value + 0805 EE 1 OUT DX, AL ; Set I/O Register + 0806 42 INC DX ; DX -> SC_Data + + 0807 B0 08 MOV AL, 08 ; 8 Lines to Process + 0809 88 46 02 MOV [BP].GPC_Lines, AL ; Save on Stack + + 080C 8E 5E 04 MOV DS, [BP].GPC_T_SETS ; Point to character set + + 080F @GPC_DECODE_CHAR_BYTE: + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 10 - 1 + + + + 080F 8B 76 06 MOV SI, [BP].GPC_T_SETO ; Get DS:SI = String + + 0812 8A 3C MOV BH, [SI] ; Get Bit Map + 0814 46 INC SI ; Point to Next Line + 0815 89 76 06 MOV [BP].GPC_T_SETO, SI ; And save new Pointer... + + CLR AX ; Clear AX + 0818 33 C0 1 XOR AX, AX ; Set Register = 0 + + CLR BL ; Clear BL + 081A 32 DB 1 XOR BL, BL ; Set Register = 0 + 081C D3 C3 ROL BX, CL ; BL holds left edge bits + 081E 8B F3 MOV SI, BX ; Use as Table Index + 0820 83 E6 0F AND SI, CHAR_BITS ; Get Low Bits + 0823 2E: 8A 84 0008 R MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + 0828 74 07 JZ @GPC_NO_LEFT1BITS ; Skip if No Pixels to set + + 082A 8A 66 16 MOV AH, [BP].GPC_ColorF ; Get Foreground Color + 082D EE OUT DX, AL ; Set up Screen Mask + 082E 26: 88 25 MOV ES:[DI], AH ; Write Foreground color + + 0831 @GPC_NO_LEFT1BITS: + 0831 32 C5 XOR AL, CH ; Invert mask for Background + 0833 74 07 JZ @GPC_NO_LEFT0BITS ; Hey, no need for this + + 0835 8A 66 14 MOV AH, [BP].GPC_ColorB ; Get background Color + 0838 EE OUT DX, AL ; Set up Screen Mask + 0839 26: 88 25 MOV ES:[DI], AH ; Write Foreground color + + ;Now Do Middle/Last Band + + 083C @GPC_NO_LEFT0BITS: + 083C 47 INC DI ; Point to next Byte + 083D C1 C3 04 ROL BX, 4 ; Shift 4 bits + + 0840 8B F3 MOV SI, BX ; Make Lookup Pointer + 0842 83 E6 0F AND SI, CHAR_BITS ; Get Low Bits + 0845 2E: 8A 84 0008 R MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + 084A 74 07 JZ @GPC_NO_MIDDLE1BITS ; Skip if no pixels to set + + 084C 8A 66 16 MOV AH, [BP].GPC_ColorF ; Get Foreground Color + 084F EE OUT DX, AL ; Set up Screen Mask + 0850 26: 88 25 MOV ES:[DI], AH ; Write Foreground color + + 0853 @GPC_NO_MIDDLE1BITS: + 0853 34 0F XOR AL, ALL_PLANES ; Invert mask for Background + 0855 74 07 JZ @GPC_NO_MIDDLE0BITS ; Hey, no need for this + + 0857 8A 66 14 MOV AH, [BP].GPC_ColorB ; Get background Color + 085A EE OUT DX, AL ; Set up Screen Mask + 085B 26: 88 25 MOV ES:[DI], AH ; Write Foreground color + + 085E @GPC_NO_MIDDLE0BITS: + 085E 80 F5 0F XOR CH, ALL_PLANES ; Invert Clip Mask + 0861 80 F9 04 CMP CL, 4 ; Aligned by 4? + 0864 74 23 JZ @GPC_NEXT_LINE ; If so, Exit now.. + + 0866 47 INC DI ; Point to next Byte + 0867 C1 C3 04 ROL BX, 4 ; Shift 4 bits + + 086A 8B F3 MOV SI, BX ; Make Lookup Pointer + 086C 83 E6 0F AND SI, CHAR_BITS ; Get Low Bits + 086F 2E: 8A 84 0008 R MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + 0874 74 07 JZ @GPC_NO_RIGHT1BITS ; Skip if No Pixels to set + + 0876 8A 66 16 MOV AH, [BP].GPC_ColorF ; Get Foreground Color + 0879 EE OUT DX, AL ; Set up Screen Mask + 087A 26: 88 25 MOV ES:[DI], AH ; Write Foreground color + + 087D @GPC_NO_RIGHT1BITS: + + 087D 32 C5 XOR AL, CH ; Invert mask for Background + 087F 74 07 JZ @GPC_NO_RIGHT0BITS ; Hey, no need for this + + 0881 8A 66 14 MOV AH, [BP].GPC_ColorB ; Get background Color + 0884 EE OUT DX, AL ; Set up Screen Mask + 0885 26: 88 25 MOV ES:[DI], AH ; Write Foreground color + + 0888 @GPC_NO_RIGHT0BITS: + 0888 4F DEC DI ; Adjust for Next Line Advance + + 0889 @GPC_NEXT_LINE: + 0889 03 7E 00 ADD DI, [BP].GPC_Width ; Point to Next Line + 088C 80 F5 0F XOR CH, CHAR_BITS ; Flip the Clip mask back + + 088F FE 4E 02 DEC [BP].GPC_Lines ; Count Down Lines + 0892 74 03 JZ @GPC_EXIT ; Ok... Done! + + 0894 E9 FF78 JMP @GPC_DECODE_CHAR_BYTE ; Again! Hey! + + 0897 @GPC_EXIT: + 0897 83 C4 08 ADD SP, 08 ; Deallocate stack workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + 089A 5F 1 POP DI ; Restore R1 + 089B 5E 2 POP SI ; Restore R1 + 089C 1F 3 POP DS ; Restore R1 + 089D 5D 4 POP BP ; Restore R1 + 089E CA 000A RET 10 ; Exit and Clean up Stack + + 08A1 GPRINTC ENDP + + + ;========================================== + ;TGPRINTC (CharNum%, Xpos%, Ypos%, ColorF%) + ;========================================== + ; + ; Transparently draws an ASCII Text Character using the + ; currently selected 8x8 font on the active display page. + ; + ; ENTRY: CharNum = ASCII character # to draw + ; Xpos = X position to draw Character at + ; Ypos = Y position of to draw Character at + ; ColorF = Color to draw text character in + ; + ; EXIT: No meaningful values returned + ; + + 001C TGP_STACK STRUC + 0000 0000 TGP_Width DW ? ; Screen Width-1 + 0002 00 00 TGP_Lines DB ?,? ; Scan lines to Decode + 0004 0000 TGP_T_SETS DW ? ; Saved Charset Segment + 0006 0000 TGP_T_SETO DW ? ; Saved Charset Offset + 0008 0000 0000 0000 DW ?x4 ; DI, SI, DS, BP + 0000 + 0010 00000000 DD ? ; Caller + 0014 00 00 TGP_ColorF DB ?,? ; Text Color + 0016 0000 TGP_Ypos DW ? ; Y Position to Print at + 0018 0000 TGP_Xpos DW ? ; X position to Print at + 001A 00 00 TGP_Char DB ?,? ; Character to Print + TGP_STACK ENDS + + PUBLIC TGPRINTC + + 08A1 TGPRINTC PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 08A1 55 1 PUSH BP ; Save R1 + 08A2 1E 2 PUSH DS ; Save R1 + 08A3 56 3 PUSH SI ; Save R1 + 08A4 57 4 PUSH DI ; Save R1 + 08A5 83 EC 08 SUB SP, 8 ; Allocate WorkSpace on Stack + 08A8 8B EC MOV BP, SP ; Set up Stack Frame + + 08AA C4 3E 0014 R LES DI, d CURRENT_PAGE ; Point to Active VGA Page + + 08AE A1 0000 R MOV AX, SCREEN_WIDTH ; Get Logical Line Width + 08B1 8B D8 MOV BX, AX ; BX = Screen Width + 08B3 4B DEC BX ; = Screen Width-1 + 08B4 89 5E 00 MOV [BP].TGP_Width, BX ; Save for later use + + 08B7 F7 66 16 MUL [BP].TGP_Ypos ; Start of Line = Ypos * Width + 08BA 03 F8 ADD DI, AX ; DI -> Start of Line Ypos + + 08BC 8B 46 18 MOV AX, [BP].TGP_Xpos ; Get Xpos of Character + 08BF 8B C8 MOV CX, AX ; Save Copy of Xpos + 08C1 C1 E8 02 SHR AX, 2 ; Bytes into Line = Xpos/4 + 08C4 03 F8 ADD DI, AX ; DI -> (Xpos, Ypos) + + ;Get Source ADDR of Character Bit Map & Save + + 08C6 8A 46 1A MOV AL, [BP].TGP_Char ; Get Character # + 08C9 A8 80 TEST AL, 080h ; Is Hi Bit Set? + 08CB 74 0C JZ @TGP_LowChar ; Nope, use low char set ptr + + 08CD 24 7F AND AL, 07Fh ; Mask Out Hi Bit + 08CF 8B 1E 0026 R MOV BX, CHARSET_HI ; BX = Char Set Ptr:Offset + 08D3 8B 16 0028 R MOV DX, CHARSET_HI+2 ; DX = Char Set Ptr:Segment + 08D7 EB 08 JMP s @TGP_Set_Char ; Go Setup Character Ptr + + 08D9 @TGP_LowChar: + + 08D9 8B 1E 0022 R MOV BX, CHARSET_LOW ; BX = Char Set Ptr:Offset + 08DD 8B 16 0024 R MOV DX, CHARSET_LOW+2 ; DX = Char Set Ptr:Segment + + 08E1 @TGP_Set_Char: + 08E1 89 56 04 MOV [BP].TGP_T_SETS, DX ; Save Segment on Stack + + 08E4 B4 00 MOV AH, 0 ; Valid #'s are 0..127 + 08E6 C1 E0 03 SHL AX, 3 ; * 8 Bytes Per Bitmap + 08E9 03 D8 ADD BX, AX ; BX = Offset of Selected char + 08EB 89 5E 06 MOV [BP].TGP_T_SETO, BX ; Save Offset on Stack + + 08EE 83 E1 03 AND CX, PLANE_BITS ; Get Plane # + 08F1 B5 0F MOV CH, ALL_PLANES ; Get Initial Plane mask + 08F3 D2 E5 SHL CH, CL ; And shift into position + 08F5 80 E5 0F AND CH, ALL_PLANES ; And mask to lower nibble + + 08F8 B0 04 MOV AL, 04 ; 4-Plane # = # of initial + 08FA 2A C1 SUB AL, CL ; shifts to align bit mask + 08FC 8A C8 MOV CL, AL ; Shift Count for SHL + + ;Get segment of character map + + OUT_8 SC_Index, MAP_MASK ; Setup Plane selections + 08FE BA 03C4 1 MOV DX, SC_Index ; then Select Register + 0901 B0 02 1 MOV AL, MAP_MASK ; then Get Data Value + 0903 EE 1 OUT DX, AL ; Set I/O Register + 0904 42 INC DX ; DX -> SC_Data + + 0905 B0 08 MOV AL, 08 ; 8 Lines to Process + 0907 88 46 02 MOV [BP].TGP_Lines, AL ; Save on Stack + + 090A 8E 5E 04 MOV DS, [BP].TGP_T_SETS ; Point to character set + + 090D @TGP_DECODE_CHAR_BYTE: + + 090D 8B 76 06 MOV SI, [BP].TGP_T_SETO ; Get DS:SI = String + + 0910 8A 3C MOV BH, [SI] ; Get Bit Map + 0912 46 INC SI ; Point to Next Line + 0913 89 76 06 MOV [BP].TGP_T_SETO, SI ; And save new Pointer... + + 0916 8A 66 14 MOV AH, [BP].TGP_ColorF ; Get Foreground Color + + CLR BL ; Clear BL + 0919 32 DB 1 XOR BL, BL ; Set Register = 0 + 091B D3 C3 ROL BX, CL ; BL holds left edge bits + 091D 8B F3 MOV SI, BX ; Use as Table Index + 091F 83 E6 0F AND SI, CHAR_BITS ; Get Low Bits + 0922 2E: 8A 84 0008 R MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + 0927 74 04 JZ @TGP_NO_LEFT1BITS ; Skip if No Pixels to set + + 0929 EE OUT DX, AL ; Set up Screen Mask + 092A 26: 88 25 MOV ES:[DI], AH ; Write Foreground color + + ;Now Do Middle/Last Band + + 092D @TGP_NO_LEFT1BITS: + + 092D 47 INC DI ; Point to next Byte + 092E C1 C3 04 ROL BX, 4 ; Shift 4 bits + + 0931 8B F3 MOV SI, BX ; Make Lookup Pointer + 0933 83 E6 0F AND SI, CHAR_BITS ; Get Low Bits + 0936 2E: 8A 84 0008 R MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + 093B 74 04 JZ @TGP_NO_MIDDLE1BITS ; Skip if no pixels to set + + 093D EE OUT DX, AL ; Set up Screen Mask + 093E 26: 88 25 MOV ES:[DI], AH ; Write Foreground color + + 0941 @TGP_NO_MIDDLE1BITS: + 0941 80 F5 0F XOR CH, ALL_PLANES ; Invert Clip Mask + 0944 80 F9 04 CMP CL, 4 ; Aligned by 4? + 0947 74 15 JZ @TGP_NEXT_LINE ; If so, Exit now.. + + 0949 47 INC DI ; Point to next Byte + 094A C1 C3 04 ROL BX, 4 ; Shift 4 bits + + 094D 8B F3 MOV SI, BX ; Make Lookup Pointer + 094F 83 E6 0F AND SI, CHAR_BITS ; Get Low Bits + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 11 - 1 + + + 0952 2E: 8A 84 0008 R MOV AL, Char_Plane_Data[SI] ; Get Mask in AL + 0957 74 04 JZ @TGP_NO_RIGHT1BITS ; Skip if No Pixels to set + + 0959 EE OUT DX, AL ; Set up Screen Mask + 095A 26: 88 25 MOV ES:[DI], AH ; Write Foreground color + + 095D @TGP_NO_RIGHT1BITS: + + 095D 4F DEC DI ; Adjust for Next Line Advance + + 095E @TGP_NEXT_LINE: + 095E 03 7E 00 ADD DI, [BP].TGP_Width ; Point to Next Line + 0961 80 F5 0F XOR CH, CHAR_BITS ; Flip the Clip mask back + + 0964 FE 4E 02 DEC [BP].TGP_Lines ; Count Down Lines + 0967 74 02 JZ @TGP_EXIT ; Ok... Done! + + 0969 EB A2 JMP @TGP_DECODE_CHAR_BYTE ; Again! Hey! + + 096B @TGP_EXIT: + 096B 83 C4 08 ADD SP, 08 ; Deallocate stack workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + 096E 5F 1 POP DI ; Restore R1 + 096F 5E 2 POP SI ; Restore R1 + 0970 1F 3 POP DS ; Restore R1 + 0971 5D 4 POP BP ; Restore R1 + 0972 CA 0008 RET 8 ; Exit and Clean up Stack + + 0975 TGPRINTC ENDP + + + ;=============================================================== + ;PRINT_STR (SEG String, MaxLen%, Xpos%, Ypos%, ColorF%, ColorB%) + ;=============================================================== + ; + ; Routine to quickly Print a null terminated ASCII string on the + ; active display page up to a maximum length. + ; + ; ENTRY: String = Far Pointer to ASCII string to print + ; MaxLen = # of characters to print if no null found + ; Xpos = X position to draw Text at + ; Ypos = Y position of to draw Text at + ; ColorF = Color to draw text in + ; ColorB = Color to set background to + ; + ; EXIT: No meaningful values returned + ; + + 001A PS_STACK STRUC + 0000 0000 0000 0000 DW ?x4 ; DI, SI, DS, BP + 0000 + 0008 00000000 DD ? ; Caller + 000C 0000 PS_ColorB DW ? ; Background Color + 000E 0000 PS_ColorF DW ? ; Text Color + 0010 0000 PS_Ypos DW ? ; Y Position to Print at + 0012 0000 PS_Xpos DW ? ; X position to Print at + 0014 0000 PS_Len DW ? ; Maximum Length of string to print + 0016 0000 0000 PS_Text DW ?,? ; Far Ptr to Text String + PS_STACK ENDS + + PUBLIC PRINT_STR + + 0975 PRINT_STR PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 0975 55 1 PUSH BP ; Save R1 + 0976 1E 2 PUSH DS ; Save R1 + 0977 56 3 PUSH SI ; Save R1 + 0978 57 4 PUSH DI ; Save R1 + 0979 8B EC MOV BP, SP ; Set up Stack Frame + + 097B @PS_Print_It: + + 097B 8B 4E 14 MOV CX, [BP].PS_Len ; Get Remaining text Length + 097E E3 2F JCXZ @PS_Exit ; Exit when out of text + + 0980 C4 7E 16 LES DI, d [BP].PS_Text ; ES:DI -> Current Char in Text + 0983 26: 8A 05 MOV AL, ES:[DI] ; AL = Text Character + 0986 25 00FF AND AX, 00FFh ; Clear High Word + 0989 74 24 JZ @PS_Exit ; Exit if null character + + 098B FF 4E 14 DEC [BP].PS_Len ; Remaining Text length-- + 098E FF 46 16 INC [BP].PS_Text ; Point to Next text char + + ; Set up Call to GPRINTC + + 0991 50 PUSH AX ; Set Character Parameter + 0992 8B 5E 12 MOV BX, [BP].PS_Xpos ; Get Xpos + 0995 53 PUSH BX ; Set Xpos Parameter + 0996 83 C3 08 ADD BX, 8 ; Advance 1 Char to Right + 0999 89 5E 12 MOV [BP].PS_Xpos, BX ; Save for next time through + + 099C 8B 5E 10 MOV BX, [BP].PS_Ypos ; Get Ypos + 099F 53 PUSH BX ; Set Ypos Parameter + + 09A0 8B 5E 0E MOV BX, [BP].PS_ColorF ; Get Text Color + 09A3 53 PUSH BX ; Set ColorF Parameter + + 09A4 8B 5E 0C MOV BX, [BP].PS_ColorB ; Get Background Color + 09A7 53 PUSH BX ; Set ColorB Parameter + + 09A8 9A ---- 07A3 R CALL f GPRINTC ; Print Character! + 09AD EB CC JMP s @PS_Print_It ; Process next character + + 09AF @PS_Exit: + POPx DI, SI, DS, BP ; Restore Saved Registers + 09AF 5F 1 POP DI ; Restore R1 + 09B0 5E 2 POP SI ; Restore R1 + 09B1 1F 3 POP DS ; Restore R1 + 09B2 5D 4 POP BP ; Restore R1 + 09B3 CA 000E RET 14 ; Exit and Clean up Stack + + 09B6 PRINT_STR ENDP + + + ;================================================================ + ;TPRINT_STR (SEG String, MaxLen%, Xpos%, Ypos%, ColorF%, ColorB%) + ;================================================================ + ; + ; Routine to quickly transparently Print a null terminated ASCII + ; string on the active display page up to a maximum length. + ; + ; ENTRY: String = Far Pointer to ASCII string to print + ; MaxLen = # of characters to print if no null found + ; Xpos = X position to draw Text at + ; Ypos = Y position of to draw Text at + ; ColorF = Color to draw text in + ; + ; EXIT: No meaningful values returned + ; + + 0018 TPS_STACK STRUC + 0000 0000 0000 0000 DW ?x4 ; DI, SI, DS, BP + 0000 + 0008 00000000 DD ? ; Caller + 000C 0000 TPS_ColorF DW ? ; Text Color + 000E 0000 TPS_Ypos DW ? ; Y Position to Print at + 0010 0000 TPS_Xpos DW ? ; X position to Print at + 0012 0000 TPS_Len DW ? ; Maximum Length of string to print + 0014 0000 0000 TPS_Text DW ?,? ; Far Ptr to Text String + TPS_STACK ENDS + + PUBLIC TPRINT_STR + + 09B6 TPRINT_STR PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 09B6 55 1 PUSH BP ; Save R1 + 09B7 1E 2 PUSH DS ; Save R1 + 09B8 56 3 PUSH SI ; Save R1 + 09B9 57 4 PUSH DI ; Save R1 + 09BA 8B EC MOV BP, SP ; Set up Stack Frame + + 09BC @TPS_Print_It: + + 09BC 8B 4E 12 MOV CX, [BP].TPS_Len ; Get Remaining text Length + 09BF E3 2B JCXZ @TPS_Exit ; Exit when out of text + + 09C1 C4 7E 14 LES DI, d [BP].TPS_Text ; ES:DI -> Current Char in Text + 09C4 26: 8A 05 MOV AL, ES:[DI] ; AL = Text Character + 09C7 25 00FF AND AX, 00FFh ; Clear High Word + 09CA 74 20 JZ @TPS_Exit ; Exit if null character + + 09CC FF 4E 12 DEC [BP].TPS_Len ; Remaining Text length-- + 09CF FF 46 14 INC [BP].TPS_Text ; Point to Next text char + + ; Set up Call to TGPRINTC + + 09D2 50 PUSH AX ; Set Character Parameter + 09D3 8B 5E 10 MOV BX, [BP].TPS_Xpos ; Get Xpos + 09D6 53 PUSH BX ; Set Xpos Parameter + 09D7 83 C3 08 ADD BX, 8 ; Advance 1 Char to Right + 09DA 89 5E 10 MOV [BP].TPS_Xpos, BX ; Save for next time through + + 09DD 8B 5E 0E MOV BX, [BP].TPS_Ypos ; Get Ypos + 09E0 53 PUSH BX ; Set Ypos Parameter + + 09E1 8B 5E 0C MOV BX, [BP].TPS_ColorF ; Get Text Color + 09E4 53 PUSH BX ; Set ColorF Parameter + + 09E5 9A ---- 08A1 R CALL f TGPRINTC ; Print Character! + 09EA EB D0 JMP s @TPS_Print_It ; Process next character + + 09EC @TPS_Exit: + POPx DI, SI, DS, BP ; Restore Saved Registers + 09EC 5F 1 POP DI ; Restore R1 + 09ED 5E 2 POP SI ; Restore R1 + 09EE 1F 3 POP DS ; Restore R1 + 09EF 5D 4 POP BP ; Restore R1 + 09F0 CA 000C RET 12 ; Exit and Clean up Stack + + 09F3 TPRINT_STR ENDP + + + ;=========================================== + ;SET_DISPLAY_FONT(SEG FontData, FontNumber%) + ;=========================================== + ; + ; Allows the user to specify their own font data for + ; wither the lower or upper 128 characters. + ; + ; ENTRY: FontData = Far Pointer to Font Bitmaps + ; FontNumber = Which half of set this is + ; = 0, Lower 128 characters + ; = 1, Upper 128 characters + ; + ; EXIT: No meaningful values returned + ; + + 000C SDF_STACK STRUC + 0000 0000 DW ? ; BP + 0002 00000000 DD ? ; Caller + 0006 0000 SDF_Which DW ? ; Hi Table/Low Table Flag + 0008 00000000 SDF_Font DD ? ; Far Ptr to Font Table + SDF_STACK ENDS + + PUBLIC SET_DISPLAY_FONT + + 09F3 SET_DISPLAY_FONT PROC FAR + + 09F3 55 PUSH BP ; Preserve Registers + 09F4 8B EC MOV BP, SP ; Set up Stack Frame + + 09F6 C4 7E 08 LES DI, [BP].SDF_Font ; Get Far Ptr to Font + + 09F9 BE 0022 R MOV SI, o CHARSET_LOW ; Assume Lower 128 chars + 09FC F7 46 06 0001 TEST [BP].SDF_Which, 1 ; Font #1 selected? + 0A01 74 03 JZ @SDF_Set_Font ; If not, skip ahead + + 0A03 BE 0026 R MOV SI, o CHARSET_HI ; Ah, really it's 128-255 + + 0A06 @SDF_Set_Font: + 0A06 89 3C MOV [SI], DI ; Set Font Pointer Offset + 0A08 8C 44 02 MOV [SI+2], ES ; Set Font Pointer Segment + + 0A0B 5D POP BP ; Restore Registers + 0A0C CA 0006 RET 6 ; We are Done.. Outa here + + 0A0F SET_DISPLAY_FONT ENDP + + + ; ===== BITMAP (SPRITE) DISPLAY ROUTINES ===== + + ;====================================================== + ;DRAW_BITMAP (SEG Image, Xpos%, Ypos%, Width%, Height%) + ;====================================================== + ; + ; Draws a variable sized Graphics Bitmap such as a + ; picture or an Icon on the current Display Page in + ; Mode X. The Bitmap is stored in a linear byte array + ; corresponding to (0,0) (1,0), (2,0) .. (Width, Height) + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 12 - 1 + + + ; This is the same linear manner as mode 13h graphics. + ; + ; ENTRY: Image = Far Pointer to Bitmap Data + ; Xpos = X position to Place Upper Left pixel at + ; Ypos = Y position to Place Upper Left pixel at + ; Width = Width of the Bitmap in Pixels + ; Height = Height of the Bitmap in Pixels + ; + ; EXIT: No meaningful values returned + ; + + 0022 DB_STACK STRUC + 0000 0000 DB_LineO DW ? ; Offset to Next Line + 0002 0000 DB_PixCount DW ? ; (Minimum) # of Pixels/Line + 0004 0000 DB_Start DW ? ; Addr of Upper Left Pixel + 0006 0000 DB_PixSkew DW ? ; # of bytes to Adjust EOL + 0008 0000 DB_SkewFlag DW ? ; Extra Pix on Plane Flag + 000A 0000 0000 0000 DW ?x4 ; DI, SI, DS, BP + 0000 + 0012 00000000 DD ? ; Caller + 0016 0000 DB_Height DW ? ; Height of Bitmap in Pixels + 0018 0000 DB_Width DW ? ; Width of Bitmap in Pixels + 001A 0000 DB_Ypos DW ? ; Y position to Draw Bitmap at + 001C 0000 DB_Xpos DW ? ; X position to Draw Bitmap at + 001E 00000000 DB_Image DD ? ; Far Pointer to Graphics Bitmap + DB_STACK ENDS + + PUBLIC DRAW_BITMAP + + 0A0F DRAW_BITMAP PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 0A0F 55 1 PUSH BP ; Save R1 + 0A10 1E 2 PUSH DS ; Save R1 + 0A11 56 3 PUSH SI ; Save R1 + 0A12 57 4 PUSH DI ; Save R1 + 0A13 83 EC 0A SUB SP, 10 ; Allocate workspace + 0A16 8B EC MOV BP, SP ; Set up Stack Frame + + 0A18 C4 3E 0014 R LES DI, d CURRENT_PAGE ; Point to Active VGA Page + 0A1C FC CLD ; Direction Flag = Forward + + 0A1D 8B 46 1A MOV AX, [BP].DB_Ypos ; Get UL Corner Ypos + 0A20 F7 26 0000 R MUL SCREEN_WIDTH ; AX = Offset to Line Ypos + + 0A24 8B 5E 1C MOV BX, [BP].DB_Xpos ; Get UL Corner Xpos + 0A27 8A CB MOV CL, BL ; Save Plane # in CL + 0A29 C1 EB 02 SHR BX, 2 ; Xpos/4 = Offset Into Line + + 0A2C 03 F8 ADD DI, AX ; ES:DI -> Start of Line + 0A2E 03 FB ADD DI, BX ; ES:DI -> Upper Left Pixel + 0A30 89 7E 04 MOV [BP].DB_Start, DI ; Save Starting Addr + + ; Compute line to line offset + + 0A33 8B 5E 18 MOV BX, [BP].DB_Width ; Get Width of Image + 0A36 8B D3 MOV DX, BX ; Save Copy in DX + 0A38 C1 EB 02 SHR BX, 2 ; /4 = width in bands + 0A3B A1 0000 R MOV AX, SCREEN_WIDTH ; Get Screen Width + 0A3E 2B C3 SUB AX, BX ; - (Bitmap Width/4) + + 0A40 89 46 00 MOV [BP].DB_LineO, AX ; Save Line Width offset + 0A43 89 5E 02 MOV [BP].DB_PixCount, BX ; Minimum # pix to copy + + 0A46 83 E2 03 AND DX, PLANE_BITS ; Get "partial band" size (0-3) + 0A49 89 56 06 MOV [BP].DB_PixSkew, DX ; Also End of Line Skew + 0A4C 89 56 08 MOV [BP].DB_SkewFlag, DX ; Save as Flag/Count + + 0A4F 83 E1 03 AND CX, PLANE_BITS ; CL = Starting Plane # + 0A52 B8 1102 MOV AX, MAP_MASK_PLANE2 ; Plane Mask & Plane Select + 0A55 D2 E4 SHL AH, CL ; Select correct Plane + OUT_16 SC_Index, AX ; Select Plane... + 0A57 BA 03C4 1 MOV DX, SC_Index ; then Select Register + 0A5A EF 1 OUT DX, AX ; Set I/O Register(s) + 0A5B 8A FC MOV BH, AH ; BH = Saved Plane Mask + 0A5D B3 04 MOV BL, 4 ; BL = Planes to Copy + + 0A5F @DB_COPY_PLANE: + + 0A5F C5 76 1E LDS SI, [BP].DB_Image ; DS:SI-> Source Image + 0A62 8B 56 16 MOV DX, [BP].DB_Height ; # of Lines to Copy + 0A65 8B 7E 04 MOV DI, [BP].DB_Start ; ES:DI-> Dest pos + + 0A68 @DB_COPY_LINE: + 0A68 8B 4E 02 MOV CX, [BP].DB_PixCount ; Min # to copy + + 0A6B F6 C1 FC TEST CL, 0FCh ; 16+PixWide? + 0A6E 74 18 JZ @DB_COPY_REMAINDER ; Nope... + + ; Pixel Copy loop has been unrolled to x4 + + 0A70 @DB_COPY_LOOP: + 0A70 A4 MOVSB ; Copy Bitmap Pixel + 0A71 83 C6 03 ADD SI, 3 ; Skip to Next Byte in same plane + 0A74 A4 MOVSB ; Copy Bitmap Pixel + 0A75 83 C6 03 ADD SI, 3 ; Skip to Next Byte in same plane + 0A78 A4 MOVSB ; Copy Bitmap Pixel + 0A79 83 C6 03 ADD SI, 3 ; Skip to Next Byte in same plane + 0A7C A4 MOVSB ; Copy Bitmap Pixel + 0A7D 83 C6 03 ADD SI, 3 ; Skip to Next Byte in same plane + + 0A80 80 E9 04 SUB CL, 4 ; Pixels to Copy=-4 + 0A83 F6 C1 FC TEST CL, 0FCh ; 4+ Pixels Left? + 0A86 75 E8 JNZ @DB_COPY_LOOP ; if so, do another block + + 0A88 @DB_COPY_REMAINDER: + 0A88 E3 07 JCXZ @DB_NEXT_LINE ; Any Pixels left on line + + 0A8A @DB_COPY2: + 0A8A A4 MOVSB ; Copy Bitmap Pixel + 0A8B 83 C6 03 ADD SI,3 ; Skip to Next Byte in same plane + LOOPx CX, @DB_COPY2 ; Pixels to Copy--, Loop until done + 0A8E 49 1 DEC CX ; Counter-- + 0A8F 75 F9 1 JNZ @DB_COPY2 ; Jump if not 0 + + 0A91 @DB_NEXT_LINE: + + ; any Partial Pixels? (some planes only) + + 0A91 0B 4E 08 OR CX, [BP].DB_SkewFlag ; Get Skew Count + 0A94 74 03 JZ @DB_NEXT2 ; if no partial pixels + + 0A96 A4 MOVSB ; Copy Bitmap Pixel + 0A97 4F DEC DI ; Back up to align + 0A98 4E DEC SI ; Back up to align + + 0A99 @DB_NEXT2: + 0A99 03 76 06 ADD SI, [BP].DB_PixSkew ; Adjust Skew + 0A9C 03 7E 00 ADD DI, [BP].DB_LineO ; Set to Next Display Line + LOOPx DX, @DB_COPY_LINE ; Lines to Copy--, Loop if more + 0A9F 4A 1 DEC DX ; Counter-- + 0AA0 75 C6 1 JNZ @DB_COPY_LINE ; Jump if not 0 + + ; Copy Next Plane.... + + 0AA2 FE CB DEC BL ; Planes to Go-- + 0AA4 74 1B JZ @DB_Exit ; Hey! We are done + + 0AA6 D0 C7 ROL BH, 1 ; Next Plane in line... + OUT_8 SC_Data, BH ; Select Plane + 0AA8 BA 03C5 1 MOV DX, SC_Data ; then Select Register + 0AAB 8A C7 1 MOV AL, BH ; then Get Data Value + 0AAD EE 1 OUT DX, AL ; Set I/O Register + + 0AAE 3C 12 CMP AL, 12h ; Carry Set if AL=11h + 0AB0 83 56 04 00 ADC [BP].DB_Start, 0 ; Screen Addr =+Carry + 0AB4 FF 46 1E INC w [BP].DB_Image ; Start @ Next Byte + + 0AB7 83 6E 08 01 SUB [BP].DB_SkewFlag, 1 ; Reduce Planes to Skew + 0ABB 83 56 08 00 ADC [BP].DB_SkewFlag, 0 ; Back to 0 if it was -1 + + 0ABF EB 9E JMP s @DB_COPY_PLANE ; Go Copy the Next Plane + + 0AC1 @DB_Exit: + 0AC1 83 C4 0A ADD SP, 10 ; Deallocate workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + 0AC4 5F 1 POP DI ; Restore R1 + 0AC5 5E 2 POP SI ; Restore R1 + 0AC6 1F 3 POP DS ; Restore R1 + 0AC7 5D 4 POP BP ; Restore R1 + 0AC8 CA 000C RET 12 ; Exit and Clean up Stack + + 0ACB DRAW_BITMAP ENDP + + + ;======================================================= + ;TDRAW_BITMAP (SEG Image, Xpos%, Ypos%, Width%, Height%) + ;======================================================= + ; + ; Transparently Draws a variable sized Graphics Bitmap + ; such as a picture or an Icon on the current Display Page + ; in Mode X. Pixels with a value of 0 are not drawn, + ; leaving the previous "background" contents intact. + ; + ; The Bitmap format is the same as for the DRAW_BITMAP function. + ; + ; ENTRY: Image = Far Pointer to Bitmap Data + ; Xpos = X position to Place Upper Left pixel at + ; Ypos = Y position to Place Upper Left pixel at + ; Width = Width of the Bitmap in Pixels + ; Height = Height of the Bitmap in Pixels + ; + ; EXIT: No meaningful values returned + ; + + 0022 TB_STACK STRUC + 0000 0000 TB_LineO DW ? ; Offset to Next Line + 0002 0000 TB_PixCount DW ? ; (Minimum) # of Pixels/Line + 0004 0000 TB_Start DW ? ; Addr of Upper Left Pixel + 0006 0000 TB_PixSkew DW ? ; # of bytes to Adjust EOL + 0008 0000 TB_SkewFlag DW ? ; Extra Pix on Plane Flag + 000A 0000 0000 0000 DW ?x4 ; DI, SI, DS, BP + 0000 + 0012 00000000 DD ? ; Caller + 0016 0000 TB_Height DW ? ; Height of Bitmap in Pixels + 0018 0000 TB_Width DW ? ; Width of Bitmap in Pixels + 001A 0000 TB_Ypos DW ? ; Y position to Draw Bitmap at + 001C 0000 TB_Xpos DW ? ; X position to Draw Bitmap at + 001E 00000000 TB_Image DD ? ; Far Pointer to Graphics Bitmap + TB_STACK ENDS + + PUBLIC TDRAW_BITMAP + + 0ACB TDRAW_BITMAP PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 0ACB 55 1 PUSH BP ; Save R1 + 0ACC 1E 2 PUSH DS ; Save R1 + 0ACD 56 3 PUSH SI ; Save R1 + 0ACE 57 4 PUSH DI ; Save R1 + 0ACF 83 EC 0A SUB SP, 10 ; Allocate workspace + 0AD2 8B EC MOV BP, SP ; Set up Stack Frame + + 0AD4 C4 3E 0014 R LES DI, d CURRENT_PAGE ; Point to Active VGA Page + 0AD8 FC CLD ; Direction Flag = Forward + + 0AD9 8B 46 1A MOV AX, [BP].TB_Ypos ; Get UL Corner Ypos + 0ADC F7 26 0000 R MUL SCREEN_WIDTH ; AX = Offset to Line Ypos + + 0AE0 8B 5E 1C MOV BX, [BP].TB_Xpos ; Get UL Corner Xpos + 0AE3 8A CB MOV CL, BL ; Save Plane # in CL + 0AE5 C1 EB 02 SHR BX, 2 ; Xpos/4 = Offset Into Line + + 0AE8 03 F8 ADD DI, AX ; ES:DI -> Start of Line + 0AEA 03 FB ADD DI, BX ; ES:DI -> Upper Left Pixel + 0AEC 89 7E 04 MOV [BP].TB_Start, DI ; Save Starting Addr + + ; Compute line to line offset + + 0AEF 8B 5E 18 MOV BX, [BP].TB_Width ; Get Width of Image + 0AF2 8B D3 MOV DX, BX ; Save Copy in DX + 0AF4 C1 EB 02 SHR BX, 2 ; /4 = width in bands + 0AF7 A1 0000 R MOV AX, SCREEN_WIDTH ; Get Screen Width + 0AFA 2B C3 SUB AX, BX ; - (Bitmap Width/4) + + 0AFC 89 46 00 MOV [BP].TB_LineO, AX ; Save Line Width offset + 0AFF 89 5E 02 MOV [BP].TB_PixCount, BX ; Minimum # pix to copy + + 0B02 83 E2 03 AND DX, PLANE_BITS ; Get "partial band" size (0-3) + 0B05 89 56 06 MOV [BP].TB_PixSkew, DX ; Also End of Line Skew + 0B08 89 56 08 MOV [BP].TB_SkewFlag, DX ; Save as Flag/Count + + 0B0B 83 E1 03 AND CX, PLANE_BITS ; CL = Starting Plane # + 0B0E B8 1102 MOV AX, MAP_MASK_PLANE2 ; Plane Mask & Plane Select + 0B11 D2 E4 SHL AH, CL ; Select correct Plane + OUT_16 SC_Index, AX ; Select Plane... + 0B13 BA 03C4 1 MOV DX, SC_Index ; then Select Register + 0B16 EF 1 OUT DX, AX ; Set I/O Register(s) + 0B17 8A FC MOV BH, AH ; BH = Saved Plane Mask + 0B19 B3 04 MOV BL, 4 ; BL = Planes to Copy + + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 13 - 1 + + + 0B1B @TB_COPY_PLANE: + + 0B1B C5 76 1E LDS SI, [BP].TB_Image ; DS:SI-> Source Image + 0B1E 8B 56 16 MOV DX, [BP].TB_Height ; # of Lines to Copy + 0B21 8B 7E 04 MOV DI, [BP].TB_Start ; ES:DI-> Dest pos + + ; Here AH is set with the value to be considered + ; "Transparent". It can be changed! + + 0B24 B4 00 MOV AH, 0 ; Value to Detect 0 + + 0B26 @TB_COPY_LINE: + 0B26 8B 4E 02 MOV CX, [BP].TB_PixCount ; Min # to copy + + 0B29 F6 C1 FC TEST CL, 0FCh ; 16+PixWide? + 0B2C 74 3A JZ @TB_COPY_REMAINDER ; Nope... + + ; Pixel Copy loop has been unrolled to x4 + + 0B2E @TB_COPY_LOOP: + 0B2E AC LODSB ; Get Pixel Value in AL + 0B2F 83 C6 03 ADD SI, 3 ; Skip to Next Byte in same plane + 0B32 38 E0 CMP AL, AH ; It is "Transparent"? + 0B34 74 03 JE @TB_SKIP_01 ; Skip ahead if so + 0B36 26: 88 05 MOV ES:[DI], AL ; Copy Pixel to VGA screen + + 0B39 @TB_SKIP_01: + 0B39 AC LODSB ; Get Pixel Value in AL + 0B3A 83 C6 03 ADD SI, 3 ; Skip to Next Byte in same plane + 0B3D 38 E0 CMP AL, AH ; It is "Transparent"? + 0B3F 74 04 JE @TB_SKIP_02 ; Skip ahead if so + 0B41 26: 88 45 01 MOV ES:[DI+1], AL ; Copy Pixel to VGA screen + + 0B45 @TB_SKIP_02: + 0B45 AC LODSB ; Get Pixel Value in AL + 0B46 83 C6 03 ADD SI, 3 ; Skip to Next Byte in same plane + 0B49 38 E0 CMP AL, AH ; It is "Transparent"? + 0B4B 74 04 JE @TB_SKIP_03 ; Skip ahead if so + 0B4D 26: 88 45 02 MOV ES:[DI+2], AL ; Copy Pixel to VGA screen + + 0B51 @TB_SKIP_03: + 0B51 AC LODSB ; Get Pixel Value in AL + 0B52 83 C6 03 ADD SI, 3 ; Skip to Next Byte in same plane + 0B55 38 E0 CMP AL, AH ; It is "Transparent"? + 0B57 74 04 JE @TB_SKIP_04 ; Skip ahead if so + 0B59 26: 88 45 03 MOV ES:[DI+3], AL ; Copy Pixel to VGA screen + + 0B5D @TB_SKIP_04: + 0B5D 83 C7 04 ADD DI, 4 ; Adjust Pixel Write Location + 0B60 80 E9 04 SUB CL, 4 ; Pixels to Copy=-4 + 0B63 F6 C1 FC TEST CL, 0FCh ; 4+ Pixels Left? + 0B66 75 C6 JNZ @TB_COPY_LOOP ; if so, do another block + + 0B68 @TB_COPY_REMAINDER: + 0B68 E3 0F JCXZ @TB_NEXT_LINE ; Any Pixels left on line + + 0B6A @TB_COPY2: + 0B6A AC LODSB ; Get Pixel Value in AL + 0B6B 83 C6 03 ADD SI, 3 ; Skip to Next Byte in same plane + 0B6E 38 E0 CMP AL, AH ; It is "Transparent"? + 0B70 74 03 JE @TB_SKIP_05 ; Skip ahead if so + 0B72 26: 88 05 MOV ES:[DI], AL ; Copy Pixel to VGA screen + + 0B75 @TB_SKIP_05: + 0B75 47 INC DI ; Advance Dest Addr + LOOPx CX, @TB_COPY2 ; Pixels to Copy--, Loop until done + 0B76 49 1 DEC CX ; Counter-- + 0B77 75 F1 1 JNZ @TB_COPY2 ; Jump if not 0 + + 0B79 @TB_NEXT_LINE: + + ; any Partial Pixels? (some planes only) + + 0B79 0B 4E 08 OR CX, [BP].TB_SkewFlag ; Get Skew Count + 0B7C 74 09 JZ @TB_NEXT2 ; if no partial pixels + + 0B7E AC LODSB ; Get Pixel Value in AL + 0B7F 4E DEC SI ; Backup to Align + 0B80 38 E0 CMP AL, AH ; It is "Transparent"? + 0B82 74 03 JE @TB_NEXT2 ; Skip ahead if so + 0B84 26: 88 05 MOV ES:[DI], AL ; Copy Pixel to VGA screen + + 0B87 @TB_NEXT2: + 0B87 03 76 06 ADD SI, [BP].TB_PixSkew ; Adjust Skew + 0B8A 03 7E 00 ADD DI, [BP].TB_LineO ; Set to Next Display Line + LOOPx DX, @TB_COPY_LINE ; Lines to Copy--, Loop if More + 0B8D 4A 1 DEC DX ; Counter-- + 0B8E 75 96 1 JNZ @TB_COPY_LINE ; Jump if not 0 + + ;Copy Next Plane.... + + 0B90 FE CB DEC BL ; Planes to Go-- + 0B92 74 1C JZ @TB_Exit ; Hey! We are done + + 0B94 D0 C7 ROL BH, 1 ; Next Plane in line... + OUT_8 SC_Data, BH ; Select Plane + 0B96 BA 03C5 1 MOV DX, SC_Data ; then Select Register + 0B99 8A C7 1 MOV AL, BH ; then Get Data Value + 0B9B EE 1 OUT DX, AL ; Set I/O Register + + 0B9C 3C 12 CMP AL, 12h ; Carry Set if AL=11h + 0B9E 83 56 04 00 ADC [BP].TB_Start, 0 ; Screen Addr =+Carry + 0BA2 FF 46 1E INC w [BP].TB_Image ; Start @ Next Byte + + 0BA5 83 6E 08 01 SUB [BP].TB_SkewFlag, 1 ; Reduce Planes to Skew + 0BA9 83 56 08 00 ADC [BP].TB_SkewFlag, 0 ; Back to 0 if it was -1 + + 0BAD E9 FF6B JMP @TB_COPY_PLANE ; Go Copy the next Plane + + 0BB0 @TB_Exit: + 0BB0 83 C4 0A ADD SP, 10 ; Deallocate workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + 0BB3 5F 1 POP DI ; Restore R1 + 0BB4 5E 2 POP SI ; Restore R1 + 0BB5 1F 3 POP DS ; Restore R1 + 0BB6 5D 4 POP BP ; Restore R1 + 0BB7 CA 000C RET 12 ; Exit and Clean up Stack + + 0BBA TDRAW_BITMAP ENDP + + + ; ==== VIDEO MEMORY to VIDEO MEMORY COPY ROUTINES ===== + + ;================================== + ;COPY_PAGE (SourcePage%, DestPage%) + ;================================== + ; + ; Duplicate on display page onto another + ; + ; ENTRY: SourcePage = Display Page # to Duplicate + ; DestPage = Display Page # to hold copy + ; + ; EXIT: No meaningful values returned + ; + + 0010 CP_STACK STRUC + 0000 0000 0000 0000 DW ?x4 ; DI, SI, DS, BP + 0000 + 0008 00000000 DD ? ; Caller + 000C 0000 CP_DestP DW ? ; Page to hold copied image + 000E 0000 CP_SourceP DW ? ; Page to Make copy from + CP_STACK ENDS + + PUBLIC COPY_PAGE + + 0BBA COPY_PAGE PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 0BBA 55 1 PUSH BP ; Save R1 + 0BBB 1E 2 PUSH DS ; Save R1 + 0BBC 56 3 PUSH SI ; Save R1 + 0BBD 57 4 PUSH DI ; Save R1 + 0BBE 8B EC MOV BP, SP ; Set up Stack Frame + 0BC0 FC CLD ; Block Xfer Forwards + + ; Make sure Page #'s are valid + + 0BC1 8B 46 0E MOV AX, [BP].CP_SourceP ; Get Source Page # + 0BC4 3B 06 0004 R CMP AX, LAST_PAGE ; is it > Max Page #? + 0BC8 73 3D JAE @CP_Exit ; if so, abort + + 0BCA 8B 5E 0C MOV BX, [BP].CP_DestP ; Get Destination Page # + 0BCD 3B 1E 0004 R CMP BX, LAST_PAGE ; is it > Max Page #? + 0BD1 73 34 JAE @CP_Exit ; if so, abort + + 0BD3 3B C3 CMP AX, BX ; Pages #'s the same? + 0BD5 74 30 JE @CP_Exit ; if so, abort + + ; Setup DS:SI and ES:DI to Video Pages + + 0BD7 D1 E3 SHL BX, 1 ; Scale index to Word + 0BD9 8B BF 0006 R MOV DI, PAGE_ADDR[BX] ; Offset to Dest Page + + 0BDD 8B D8 MOV BX, AX ; Index to Source page + 0BDF D1 E3 SHL BX, 1 ; Scale index to Word + 0BE1 8B B7 0006 R MOV SI, PAGE_ADDR[BX] ; Offset to Source Page + + 0BE5 8B 0E 000E R MOV CX, PAGE_SIZE ; Get size of Page + 0BE9 A1 0016 R MOV AX, CURRENT_SEGMENT ; Get Video Mem Segment + 0BEC 8E C0 MOV ES, AX ; ES:DI -> Dest Page + 0BEE 8E D8 MOV DS, AX ; DS:SI -> Source Page + + ; Setup VGA registers for Mem to Mem copy + + OUT_16 GC_Index, LATCHES_ON ; Data from Latches = on + 0BF0 BA 03CE 1 MOV DX, GC_Index ; then Select Register + 0BF3 B8 0008 1 MOV AX, LATCHES_ON ; then Get Data Value + 0BF6 EF 1 OUT DX, AX ; Set I/O Register(s) + OUT_16 SC_Index, ALL_PLANES_ON ; Copy all Planes + 0BF7 BA 03C4 1 MOV DX, SC_Index ; then Select Register + 0BFA B8 0F02 1 MOV AX, ALL_PLANES_ON ; then Get Data Value + 0BFD EF 1 OUT DX, AX ; Set I/O Register(s) + + ; Note.. Do *NOT* use MOVSW or MOVSD - they will + ; Screw with the latches which are 8 bits x 4 + + 0BFE F3/ A4 REP MOVSB ; Copy entire Page! + + ; Reset VGA for normal memory access + + OUT_16 GC_Index, LATCHES_OFF ; Data from Latches = off + 0C00 BA 03CE 1 MOV DX, GC_Index ; then Select Register + 0C03 B8 FF08 1 MOV AX, LATCHES_OFF ; then Get Data Value + 0C06 EF 1 OUT DX, AX ; Set I/O Register(s) + + 0C07 @CP_Exit: + POPx DI, SI, DS, BP ; Restore Saved Registers + 0C07 5F 1 POP DI ; Restore R1 + 0C08 5E 2 POP SI ; Restore R1 + 0C09 1F 3 POP DS ; Restore R1 + 0C0A 5D 4 POP BP ; Restore R1 + 0C0B CA 0004 RET 4 ; Exit and Clean up Stack + + 0C0E COPY_PAGE ENDP + + + ;========================================================================== + ;COPY_BITMAP (SourcePage%, X1%, Y1%, X2%, Y2%, DestPage%, DestX1%, DestY1%) + ;========================================================================== + ; + ; Copies a Bitmap Image from one Display Page to Another + ; This Routine is Limited to copying Images with the same + ; Plane Alignment. To Work: (X1 MOD 4) must = (DestX1 MOD 4) + ; Copying an Image to the Same Page is supported, but results + ; may be defined when the when the rectangular areas + ; (X1, Y1) - (X2, Y2) and (DestX1, DestY1) - + ; (DestX1+(X2-X1), DestY1+(Y2-Y1)) overlap... + ; No Paramter checking to done to insure that + ; X2 >= X1 and Y2 >= Y1. Be Careful... + ; + ; ENTRY: SourcePage = Display Page # with Source Image + ; X1 = Upper Left Xpos of Source Image + ; Y1 = Upper Left Ypos of Source Image + ; X2 = Lower Right Xpos of Source Image + ; Y2 = Lower Right Ypos of Source Image + ; DestPage = Display Page # to copy Image to + ; DestX1 = Xpos to Copy UL Corner of Image to + ; DestY1 = Ypos to Copy UL Corner of Image to + ; + ; EXIT: AX = Success Flag: 0 = Failure / -1= Success + ; + + 0020 CB_STACK STRUC + 0000 0000 CB_Height DW ? ; Height of Image in Lines + 0002 0000 CB_Width DW ? ; Width of Image in "bands" + 0004 0000 0000 0000 DW ?x4 ; DI, SI, DS, BP + 0000 + 000C 00000000 DD ? ; Caller + 0010 0000 CB_DestY1 DW ? ; Destination Ypos + 0012 0000 CB_DestX1 DW ? ; Destination Xpos + 0014 0000 CB_DestP DW ? ; Page to Copy Bitmap To + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 14 - 1 + + + 0016 0000 CB_Y2 DW ? ; LR Ypos of Image + 0018 0000 CB_X2 DW ? ; LR Xpos of Image + 001A 0000 CB_Y1 DW ? ; UL Ypos of Image + 001C 0000 CB_X1 DW ? ; UL Xpos of Image + 001E 0000 CB_SourceP DW ? ; Page containing Source Bitmap + CB_STACK ENDS + + PUBLIC COPY_BITMAP + + 0C0E COPY_BITMAP PROC FAR + + PUSHx BP, DS, SI, DI ; Preserve Important Registers + 0C0E 55 1 PUSH BP ; Save R1 + 0C0F 1E 2 PUSH DS ; Save R1 + 0C10 56 3 PUSH SI ; Save R1 + 0C11 57 4 PUSH DI ; Save R1 + 0C12 83 EC 04 SUB SP, 4 ; Allocate WorkSpace on Stack + 0C15 8B EC MOV BP, SP ; Set up Stack Frame + + ; Prep Registers (and keep jumps short!) + + 0C17 8E 06 0016 R MOV ES, CURRENT_SEGMENT ; ES -> VGA Ram + 0C1B FC CLD ; Block Xfer Forwards + + ; Make sure Parameters are valid + + 0C1C 8B 5E 1E MOV BX, [BP].CB_SourceP ; Get Source Page # + 0C1F 3B 1E 0004 R CMP BX, LAST_PAGE ; is it > Max Page #? + 0C23 73 7D JAE @CB_Abort ; if so, abort + + 0C25 8B 4E 14 MOV CX, [BP].CB_DestP ; Get Destination Page # + 0C28 3B 0E 0004 R CMP CX, LAST_PAGE ; is it > Max Page #? + 0C2C 73 74 JAE @CB_Abort ; if so, abort + + 0C2E 8B 46 1C MOV AX, [BP].CB_X1 ; Get Source X1 + 0C31 33 46 12 XOR AX, [BP].CB_DestX1 ; Compare Bits 0-1 + 0C34 83 E0 03 AND AX, PLANE_BITS ; Check Plane Bits + 0C37 75 69 JNZ @CB_Abort ; They should cancel out + + ; Setup for Copy processing + + OUT_8 SC_INDEX, MAP_MASK ; Set up for Plane Select + 0C39 BA 03C4 1 MOV DX, SC_INDEX ; then Select Register + 0C3C B0 02 1 MOV AL, MAP_MASK ; then Get Data Value + 0C3E EE 1 OUT DX, AL ; Set I/O Register + OUT_16 GC_Index, LATCHES_ON ; Data from Latches = on + 0C3F BA 03CE 1 MOV DX, GC_Index ; then Select Register + 0C42 B8 0008 1 MOV AX, LATCHES_ON ; then Get Data Value + 0C45 EF 1 OUT DX, AX ; Set I/O Register(s) + + ; Compute Info About Images, Setup ES:SI & ES:DI + + 0C46 8B 46 16 MOV AX, [BP].CB_Y2 ; Height of Bitmap in lines + 0C49 2B 46 1A SUB AX, [BP].CB_Y1 ; is Y2 - Y1 + 1 + 0C4C 40 INC AX ; (add 1 since were not 0 based) + 0C4D 89 46 00 MOV [BP].CB_Height, AX ; Save on Stack for later use + + 0C50 8B 46 18 MOV AX, [BP].CB_X2 ; Get # of "Bands" of 4 Pixels + 0C53 8B 56 1C MOV DX, [BP].CB_X1 ; the Bitmap Occupies as X2-X1 + 0C56 C1 E8 02 SHR AX, 2 ; Get X2 Band (X2 / 4) + 0C59 C1 EA 02 SHR DX, 2 ; Get X1 Band (X1 / 4) + 0C5C 2B C2 SUB AX, DX ; AX = # of Bands - 1 + 0C5E 40 INC AX ; AX = # of Bands + 0C5F 89 46 02 MOV [BP].CB_Width, AX ; Save on Stack for later use + + 0C62 D1 E3 SHL BX, 1 ; Scale Source Page to Word + 0C64 8B B7 0006 R MOV SI, PAGE_ADDR[BX] ; SI = Offset of Source Page + 0C68 8B 46 1A MOV AX, [BP].CB_Y1 ; Get Source Y1 Line + 0C6B F7 26 0000 R MUL SCREEN_WIDTH ; AX = Offset to Line Y1 + 0C6F 03 F0 ADD SI, AX ; SI = Offset to Line Y1 + 0C71 8B 46 1C MOV AX, [BP].CB_X1 ; Get Source X1 + 0C74 C1 E8 02 SHR AX, 2 ; X1 / 4 = Byte offset + 0C77 03 F0 ADD SI, AX ; SI = Byte Offset to (X1,Y1) + + 0C79 8B D9 MOV BX, CX ; Dest Page Index to BX + 0C7B D1 E3 SHL BX, 1 ; Scale Source Page to Word + 0C7D 8B BF 0006 R MOV DI, PAGE_ADDR[BX] ; DI = Offset of Dest Page + 0C81 8B 46 10 MOV AX, [BP].CB_DestY1 ; Get Dest Y1 Line + 0C84 F7 26 0000 R MUL SCREEN_WIDTH ; AX = Offset to Line Y1 + 0C88 03 F8 ADD DI, AX ; DI = Offset to Line Y1 + 0C8A 8B 46 12 MOV AX, [BP].CB_DestX1 ; Get Dest X1 + 0C8D C1 E8 02 SHR AX, 2 ; X1 / 4 = Byte offset + 0C90 03 F8 ADD DI, AX ; DI = Byte Offset to (D-X1,D-Y1) + + 0C92 8B 4E 02 MOV CX, [BP].CB_Width ; CX = Width of Image (Bands) + 0C95 49 DEC CX ; CX = 1? + 0C96 74 0F JE @CB_Only_One_Band ; 0 Means Image Width of 1 Band + + 0C98 8B 5E 1C MOV BX, [BP].CB_X1 ; Get Source X1 + 0C9B 83 E3 03 AND BX, PLANE_BITS ; Aligned? (bits 0-1 = 00?) + 0C9E 74 70 JZ @CB_Check_Right ; if so, check right alignment + 0CA0 75 41 JNZ @CB_Left_Band ; not aligned? well.. + + 0CA2 @CB_Abort: + CLR AX ; Return False (Failure) + 0CA2 33 C0 1 XOR AX, AX ; Set Register = 0 + 0CA4 E9 00D7 JMP @CB_Exit ; and Finish Up + + ; Copy when Left & Right Clip Masks overlap... + + 0CA7 @CB_Only_One_Band: + 0CA7 8B 5E 1C MOV BX, [BP].CB_X1 ; Get Left Clip Mask + 0CAA 83 E3 03 AND BX, PLANE_BITS ; Mask out Row # + 0CAD 2E: 8A 87 0000 R MOV AL, Left_Clip_Mask[BX] ; Get Left Edge Mask + 0CB2 8B 5E 18 MOV BX, [BP].CB_X2 ; Get Right Clip Mask + 0CB5 83 E3 03 AND BX, PLANE_BITS ; Mask out Row # + 0CB8 2E: 22 87 0004 R AND AL, Right_Clip_Mask[BX] ; Get Right Edge Mask byte + + OUT_8 SC_Data, AL ; Clip For Left & Right Masks + 0CBD BA 03C5 1 MOV DX, SC_Data ; then Select Register + 0CC0 EE 1 OUT DX, AL ; Set I/O Register + + 0CC1 8B 4E 00 MOV CX, [BP].CB_Height ; CX = # of Lines to Copy + 0CC4 8B 16 0000 R MOV DX, SCREEN_WIDTH ; DX = Width of Screen + CLR BX ; BX = Offset into Image + 0CC8 33 DB 1 XOR BX, BX ; Set Register = 0 + + 0CCA @CB_One_Loop: + 0CCA 26: 8A 00 MOV AL, ES:[SI+BX] ; Load Latches + 0CCD 26: 88 01 MOV ES:[DI+BX], AL ; Unload Latches + 0CD0 03 DA ADD BX, DX ; Advance Offset to Next Line + LOOPjz CX, @CB_One_Done ; Exit Loop if Finished + 0CD2 49 1 DEC CX ; Counter-- + 0CD3 74 0B 1 JZ @CB_One_Done ; Jump if 0 + + 0CD5 26: 8A 00 MOV AL, ES:[SI+BX] ; Load Latches + 0CD8 26: 88 01 MOV ES:[DI+BX], AL ; Unload Latches + 0CDB 03 DA ADD BX, DX ; Advance Offset to Next Line + LOOPx CX, @CB_One_Loop ; Loop until Finished + 0CDD 49 1 DEC CX ; Counter-- + 0CDE 75 EA 1 JNZ @CB_One_Loop ; Jump if not 0 + + 0CE0 @CB_One_Done: + 0CE0 E9 0094 JMP @CB_Finish ; Outa Here! + + ; Copy Left Edge of Bitmap + + 0CE3 @CB_Left_Band: + + OUT_8 SC_Data, Left_Clip_Mask[BX] ; Set Left Edge Plane Mask + 0CE3 BA 03C5 1 MOV DX, SC_Data ; then Select Register + 0CE6 2E: 8A 87 0000 R 1 MOV AL, Left_Clip_Mask[BX] ; then Get Data Value + 0CEB EE 1 OUT DX, AL ; Set I/O Register + + 0CEC 8B 4E 00 MOV CX, [BP].CB_Height ; CX = # of Lines to Copy + 0CEF 8B 16 0000 R MOV DX, SCREEN_WIDTH ; DX = Width of Screen + CLR BX ; BX = Offset into Image + 0CF3 33 DB 1 XOR BX, BX ; Set Register = 0 + + 0CF5 @CB_Left_Loop: + 0CF5 26: 8A 00 MOV AL, ES:[SI+BX] ; Load Latches + 0CF8 26: 88 01 MOV ES:[DI+BX], AL ; Unload Latches + 0CFB 03 DA ADD BX, DX ; Advance Offset to Next Line + LOOPjz CX, @CB_Left_Done ; Exit Loop if Finished + 0CFD 49 1 DEC CX ; Counter-- + 0CFE 74 0B 1 JZ @CB_Left_Done ; Jump if 0 + + 0D00 26: 8A 00 MOV AL, ES:[SI+BX] ; Load Latches + 0D03 26: 88 01 MOV ES:[DI+BX], AL ; Unload Latches + 0D06 03 DA ADD BX, DX ; Advance Offset to Next Line + LOOPx CX, @CB_Left_Loop ; Loop until Finished + 0D08 49 1 DEC CX ; Counter-- + 0D09 75 EA 1 JNZ @CB_Left_Loop ; Jump if not 0 + + 0D0B @CB_Left_Done: + 0D0B 47 INC DI ; Move Dest Over 1 band + 0D0C 46 INC SI ; Move Source Over 1 band + 0D0D FF 4E 02 DEC [BP].CB_Width ; Band Width-- + + ; Determine if Right Edge of Bitmap needs special copy + + 0D10 @CB_Check_Right: + 0D10 8B 5E 18 MOV BX, [BP].CB_X2 ; Get Source X2 + 0D13 83 E3 03 AND BX, PLANE_BITS ; Aligned? (bits 0-1 = 11?) + 0D16 80 FB 03 CMP BL, 03h ; Plane = 3? + 0D19 74 2C JE @CB_Copy_Middle ; Copy the Middle then! + + ; Copy Right Edge of Bitmap + + 0D1B @CB_Right_Band: + + OUT_8 SC_Data, Right_Clip_Mask[BX] ; Set Right Edge Plane Mask + 0D1B BA 03C5 1 MOV DX, SC_Data ; then Select Register + 0D1E 2E: 8A 87 0004 R 1 MOV AL, Right_Clip_Mask[BX] ; then Get Data Value + 0D23 EE 1 OUT DX, AL ; Set I/O Register + + 0D24 FF 4E 02 DEC [BP].CB_Width ; Band Width-- + 0D27 8B 4E 00 MOV CX, [BP].CB_Height ; CX = # of Lines to Copy + 0D2A 8B 16 0000 R MOV DX, SCREEN_WIDTH ; DX = Width of Screen + 0D2E 8B 5E 02 MOV BX, [BP].CB_Width ; BX = Offset to Right Edge + + 0D31 @CB_Right_Loop: + 0D31 26: 8A 00 MOV AL, ES:[SI+BX] ; Load Latches + 0D34 26: 88 01 MOV ES:[DI+BX], AL ; Unload Latches + 0D37 03 DA ADD BX, DX ; Advance Offset to Next Line + LOOPjz CX, @CB_Right_Done ; Exit Loop if Finished + 0D39 49 1 DEC CX ; Counter-- + 0D3A 74 0B 1 JZ @CB_Right_Done ; Jump if 0 + + 0D3C 26: 8A 00 MOV AL, ES:[SI+BX] ; Load Latches + 0D3F 26: 88 01 MOV ES:[DI+BX], AL ; Unload Latches + 0D42 03 DA ADD BX, DX ; Advance Offset to Next Line + LOOPx CX, @CB_Right_Loop ; Loop until Finished + 0D44 49 1 DEC CX ; Counter-- + 0D45 75 EA 1 JNZ @CB_Right_Loop ; Jump if not 0 + + 0D47 @CB_Right_Done: + + ; Copy the Main Block of the Bitmap + + 0D47 @CB_Copy_Middle: + + 0D47 8B 4E 02 MOV CX, [BP].CB_Width ; Get Width Remaining + 0D4A E3 2B JCXZ @CB_Finish ; Exit if Done + + OUT_8 SC_Data, ALL_PLANES ; Copy all Planes + 0D4C BA 03C5 1 MOV DX, SC_Data ; then Select Register + 0D4F B0 0F 1 MOV AL, ALL_PLANES ; then Get Data Value + 0D51 EE 1 OUT DX, AL ; Set I/O Register + + 0D52 8B 16 0000 R MOV DX, SCREEN_WIDTH ; Get Width of Screen minus + 0D56 2B D1 SUB DX, CX ; Image width (for Adjustment) + 0D58 8B 46 00 MOV AX, [BP].CB_Height ; AX = # of Lines to Copy + 0D5B 8B D9 MOV BX, CX ; BX = Quick REP reload count + 0D5D 8C C1 MOV CX, ES ; Move VGA Segment + 0D5F 8E D9 MOV DS, CX ; Into DS + + ; Actual Copy Loop. REP MOVSB does the work + + 0D61 @CB_Middle_Copy: + 0D61 8B CB MOV CX, BX ; Recharge Rep Count + 0D63 F3/ A4 REP MOVSB ; Move Bands + LOOPjz AX, @CB_Finish ; Exit Loop if Finished + 0D65 48 1 DEC AX ; Counter-- + 0D66 74 0F 1 JZ @CB_Finish ; Jump if 0 + + 0D68 03 F2 ADD SI, DX ; Adjust DS:SI to Next Line + 0D6A 03 FA ADD DI, DX ; Adjust ES:DI to Next Line + + 0D6C 8B CB MOV CX, BX ; Recharge Rep Count + 0D6E F3/ A4 REP MOVSB ; Move Bands + + 0D70 03 F2 ADD SI, DX ; Adjust DS:SI to Next Line + 0D72 03 FA ADD DI, DX ; Adjust ES:DI to Next Line + LOOPx AX, @CB_Middle_Copy ; Copy Lines until Done + 0D74 48 1 DEC AX ; Counter-- + 0D75 75 EA 1 JNZ @CB_Middle_Copy ; Jump if not 0 + + 0D77 @CB_Finish: + OUT_16 GC_Index, LATCHES_OFF ; Data from Latches = on + 0D77 BA 03CE 1 MOV DX, GC_Index ; then Select Register + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Page 15 - 1 + + + 0D7A B8 FF08 1 MOV AX, LATCHES_OFF ; then Get Data Value + 0D7D EF 1 OUT DX, AX ; Set I/O Register(s) + + 0D7E @CB_Exit: + 0D7E 83 C4 04 ADD SP, 04 ; Deallocate stack workspace + POPx DI, SI, DS, BP ; Restore Saved Registers + 0D81 5F 1 POP DI ; Restore R1 + 0D82 5E 2 POP SI ; Restore R1 + 0D83 1F 3 POP DS ; Restore R1 + 0D84 5D 4 POP BP ; Restore R1 + 0D85 CA 0010 RET 16 ; Exit and Clean up Stack + + 0D88 COPY_BITMAP ENDP + + END ; End of Code Segment + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Symbols 16 - 1 + + + + +Macros: + + N a m e Type + +CLR . . . . . . . . . . . . . . Proc +LOOPjz . . . . . . . . . . . . . Proc +LOOPx . . . . . . . . . . . . . Proc +OUT_16 . . . . . . . . . . . . . Proc +OUT_8 . . . . . . . . . . . . . Proc +POPx . . . . . . . . . . . . . . Proc +PUSHx . . . . . . . . . . . . . Proc + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Symbols 17 - 1 + + + + +Structures and Unions: + + N a m e Size + Offset Type + +CB_STACK . . . . . . . . . . . . 0020 + CB_Height . . . . . . . . . . 0000 Word + CB_Width . . . . . . . . . . . 0002 Word + CB_DestY1 . . . . . . . . . . 0010 Word + CB_DestX1 . . . . . . . . . . 0012 Word + CB_DestP . . . . . . . . . . . 0014 Word + CB_Y2 . . . . . . . . . . . . 0016 Word + CB_X2 . . . . . . . . . . . . 0018 Word + CB_Y1 . . . . . . . . . . . . 001A Word + CB_X1 . . . . . . . . . . . . 001C Word + CB_SourceP . . . . . . . . . . 001E Word +CP_STACK . . . . . . . . . . . . 0010 + CP_DestP . . . . . . . . . . . 000C Word + CP_SourceP . . . . . . . . . . 000E Word +CVS_STACK . . . . . . . . . . . 000A + CVS_COLOR . . . . . . . . . . 0008 Byte +DB_STACK . . . . . . . . . . . . 0022 + DB_LineO . . . . . . . . . . . 0000 Word + DB_PixCount . . . . . . . . . 0002 Word + DB_Start . . . . . . . . . . . 0004 Word + DB_PixSkew . . . . . . . . . . 0006 Word + DB_SkewFlag . . . . . . . . . 0008 Word + DB_Height . . . . . . . . . . 0016 Word + DB_Width . . . . . . . . . . . 0018 Word + DB_Ypos . . . . . . . . . . . 001A Word + DB_Xpos . . . . . . . . . . . 001C Word + DB_Image . . . . . . . . . . . 001E DWord +DL_STACK . . . . . . . . . . . . 0014 + DL_ColorF . . . . . . . . . . 000A Byte + DL_Ypos2 . . . . . . . . . . . 000C Word + DL_Xpos2 . . . . . . . . . . . 000E Word + DL_Ypos1 . . . . . . . . . . . 0010 Word + DL_Xpos1 . . . . . . . . . . . 0012 Word +FB_STACK . . . . . . . . . . . . 0016 + FB_Color . . . . . . . . . . . 000C Byte + FB_Ypos2 . . . . . . . . . . . 000E Word + FB_Xpos2 . . . . . . . . . . . 0010 Word + FB_Ypos1 . . . . . . . . . . . 0012 Word + FB_Xpos1 . . . . . . . . . . . 0014 Word +GDR_STACK . . . . . . . . . . . 000E + GDR_Blue . . . . . . . . . . . 0006 Word + GDR_Green . . . . . . . . . . 0008 Word + GDR_Red . . . . . . . . . . . 000A Word + GDR_Register . . . . . . . . . 000C Byte +GPC_STACK . . . . . . . . . . . 001E + GPC_Width . . . . . . . . . . 0000 Word + GPC_Lines . . . . . . . . . . 0002 Byte + GPC_T_SETS . . . . . . . . . . 0004 Word + GPC_T_SETO . . . . . . . . . . 0006 Word + GPC_ColorB . . . . . . . . . . 0014 Byte + GPC_ColorF . . . . . . . . . . 0016 Byte + GPC_Ypos . . . . . . . . . . . 0018 Word + GPC_Xpos . . . . . . . . . . . 001A Word + GPC_Char . . . . . . . . . . . 001C Byte +LDR_STACK . . . . . . . . . . . 0014 + LDR_Sync . . . . . . . . . . . 000A Word + LDR_EndReg . . . . . . . . . . 000C Byte + LDR_StartReg . . . . . . . . . 000E Byte + LDR_PalData . . . . . . . . . 0010 DWord +Mode_Data_Table . . . . . . . . 000C + M_MiscR . . . . . . . . . . . 0000 Byte + M_Pages . . . . . . . . . . . 0001 Byte + M_XSize . . . . . . . . . . . 0002 Word + M_YSize . . . . . . . . . . . 0004 Word + M_XMax . . . . . . . . . . . . 0006 Word + M_YMax . . . . . . . . . . . . 0008 Word + M_CRTC . . . . . . . . . . . . 000A Word +PS_STACK . . . . . . . . . . . . 001A + PS_ColorB . . . . . . . . . . 000C Word + PS_ColorF . . . . . . . . . . 000E Word + PS_Ypos . . . . . . . . . . . 0010 Word + PS_Xpos . . . . . . . . . . . 0012 Word + PS_Len . . . . . . . . . . . . 0014 Word + PS_Text . . . . . . . . . . . 0016 Word +RDR_STACK . . . . . . . . . . . 0012 + RDR_EndReg . . . . . . . . . . 000A Byte + RDR_StartReg . . . . . . . . . 000C Byte + RDR_PalData . . . . . . . . . 000E DWord +RP_STACK . . . . . . . . . . . . 000C + RP_Ypos . . . . . . . . . . . 0008 Word + RP_Xpos . . . . . . . . . . . 000A Word +SAP_STACK . . . . . . . . . . . 0008 + SAP_Page . . . . . . . . . . . 0006 Word +SDF_STACK . . . . . . . . . . . 000C + SDF_Which . . . . . . . . . . 0006 Word + SDF_Font . . . . . . . . . . . 0008 DWord +SDP_STACK . . . . . . . . . . . 0008 + SDP_Page . . . . . . . . . . . 0006 Word +SDR_STACK . . . . . . . . . . . 000E + SDR_Blue . . . . . . . . . . . 0006 Byte + SDR_Green . . . . . . . . . . 0008 Byte + SDR_Red . . . . . . . . . . . 000A Byte + SDR_Register . . . . . . . . . 000C Byte +SM_STACK . . . . . . . . . . . . 000A + SM_Mode . . . . . . . . . . . 0008 Word +SP_STACK . . . . . . . . . . . . 000E + SETP_Color . . . . . . . . . . 0008 Byte + SETP_Ypos . . . . . . . . . . 000A Word + SETP_Xpos . . . . . . . . . . 000C Word +SVM_STACK . . . . . . . . . . . 0016 + SVM_Table . . . . . . . . . . 0000 Word + SVM_Pages . . . . . . . . . . 000E Word + SVM_Ysize . . . . . . . . . . 0010 Word + SVM_Xsize . . . . . . . . . . 0012 Word + SVM_Mode . . . . . . . . . . . 0014 Word +SW_STACK . . . . . . . . . . . . 000C + SW_Ypos . . . . . . . . . . . 0006 Word + SW_Xpos . . . . . . . . . . . 0008 Word + SW_Page . . . . . . . . . . . 000A Word +TB_STACK . . . . . . . . . . . . 0022 + TB_LineO . . . . . . . . . . . 0000 Word + TB_PixCount . . . . . . . . . 0002 Word + TB_Start . . . . . . . . . . . 0004 Word + TB_PixSkew . . . . . . . . . . 0006 Word + TB_SkewFlag . . . . . . . . . 0008 Word + TB_Height . . . . . . . . . . 0016 Word + TB_Width . . . . . . . . . . . 0018 Word + TB_Ypos . . . . . . . . . . . 001A Word + TB_Xpos . . . . . . . . . . . 001C Word + TB_Image . . . . . . . . . . . 001E DWord +TGP_STACK . . . . . . . . . . . 001C + TGP_Width . . . . . . . . . . 0000 Word + TGP_Lines . . . . . . . . . . 0002 Byte + TGP_T_SETS . . . . . . . . . . 0004 Word + TGP_T_SETO . . . . . . . . . . 0006 Word + TGP_ColorF . . . . . . . . . . 0014 Byte + TGP_Ypos . . . . . . . . . . . 0016 Word + TGP_Xpos . . . . . . . . . . . 0018 Word + TGP_Char . . . . . . . . . . . 001A Byte +TPS_STACK . . . . . . . . . . . 0018 + TPS_ColorF . . . . . . . . . . 000C Word + TPS_Ypos . . . . . . . . . . . 000E Word + TPS_Xpos . . . . . . . . . . . 0010 Word + TPS_Len . . . . . . . . . . . 0012 Word + TPS_Text . . . . . . . . . . . 0014 Word + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Symbols 18 - 1 + + + + +Segments and Groups: + + N a m e Size Length Align Combine Class + +DGROUP . . . . . . . . . . . . . GROUP +_DATA . . . . . . . . . . . . . 16 Bit 0000 Word Public 'DATA' +_BSS . . . . . . . . . . . . . . 16 Bit 002A Word Public 'BSS' +MODEX_TEXT . . . . . . . . . . . 16 Bit 0D88 Word Public 'CODE' + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Symbols 19 - 1 + + + + +Procedures, parameters and locals: + + N a m e Type Value Attr + +CLEAR_VGA_SCREEN . . . . . . . . P Far 0280 MODEX_TEXT Length= 0022 Public +COPY_BITMAP . . . . . . . . . . P Far 0C0E MODEX_TEXT Length= 017E Public +COPY_PAGE . . . . . . . . . . . P Far 0BBA MODEX_TEXT Length= 0054 Public +DRAW_BITMAP . . . . . . . . . . P Far 0A0F MODEX_TEXT Length= 00BC Public +DRAW_LINE . . . . . . . . . . . P Far 040B MODEX_TEXT Length= 02A7 Public +FILL_BLOCK . . . . . . . . . . . P Far 0300 MODEX_TEXT Length= 010B Public +GET_ACTIVE_PAGE . . . . . . . . P Far 06E5 MODEX_TEXT Length= 0004 Public +GET_DAC_REGISTER . . . . . . . . P Far 0641 MODEX_TEXT Length= 0025 Public +GET_DISPLAY_PAGE . . . . . . . . P Far 0724 MODEX_TEXT Length= 0004 Public +GET_X_OFFSET . . . . . . . . . . P Far 078D MODEX_TEXT Length= 0004 Public +GET_Y_OFFSET . . . . . . . . . . P Far 0791 MODEX_TEXT Length= 0004 Public +GPRINTC . . . . . . . . . . . . P Far 07A3 MODEX_TEXT Length= 00FE Public +LOAD_DAC_REGISTERS . . . . . . . P Far 0666 MODEX_TEXT Length= 0037 Public +PRINT_STR . . . . . . . . . . . P Far 0975 MODEX_TEXT Length= 0041 Public +READ_DAC_REGISTERS . . . . . . . P Far 069D MODEX_TEXT Length= 002B Public +READ_POINT . . . . . . . . . . . P Far 02D2 MODEX_TEXT Length= 002E Public +SET_ACTIVE_PAGE . . . . . . . . P Far 06C8 MODEX_TEXT Length= 001D Public +SET_DAC_REGISTER . . . . . . . . P Far 0624 MODEX_TEXT Length= 001D Public +SET_DISPLAY_FONT . . . . . . . . P Far 09F3 MODEX_TEXT Length= 001C Public +SET_DISPLAY_PAGE . . . . . . . . P Far 06E9 MODEX_TEXT Length= 003B Public +SET_MODEX . . . . . . . . . . . P Far 0251 MODEX_TEXT Length= 002F Public +SET_POINT . . . . . . . . . . . P Far 02A2 MODEX_TEXT Length= 0030 Public +SET_VGA_MODEX . . . . . . . . . P Far 0104 MODEX_TEXT Length= 014D Public +SET_WINDOW . . . . . . . . . . . P Far 0728 MODEX_TEXT Length= 0065 Public +SYNC_DISPLAY . . . . . . . . . . P Far 0795 MODEX_TEXT Length= 000E Public +TDRAW_BITMAP . . . . . . . . . . P Far 0ACB MODEX_TEXT Length= 00EF Public +TGPRINTC . . . . . . . . . . . . P Far 08A1 MODEX_TEXT Length= 00D4 Public +TPRINT_STR . . . . . . . . . . . P Far 09B6 MODEX_TEXT Length= 003D Public + Microsoft (R) Macro Assembler Version 6.11 07/02/14 15:18:33 +modex.asm Symbols 20 - 1 + + + + +Symbols: + + N a m e Type Value Attr + +?x3 . . . . . . . . . . . . . . Text ?,?,? +?x4 . . . . . . . . . . . . . . Text ?,?,?,? +@CB_Abort . . . . . . . . . . . L Near 0CA2 MODEX_TEXT +@CB_Check_Right . . . . . . . . L Near 0D10 MODEX_TEXT +@CB_Copy_Middle . . . . . . . . L Near 0D47 MODEX_TEXT +@CB_Exit . . . . . . . . . . . . L Near 0D7E MODEX_TEXT +@CB_Finish . . . . . . . . . . . L Near 0D77 MODEX_TEXT +@CB_Left_Band . . . . . . . . . L Near 0CE3 MODEX_TEXT +@CB_Left_Done . . . . . . . . . L Near 0D0B MODEX_TEXT +@CB_Left_Loop . . . . . . . . . L Near 0CF5 MODEX_TEXT +@CB_Middle_Copy . . . . . . . . L Near 0D61 MODEX_TEXT +@CB_One_Done . . . . . . . . . . L Near 0CE0 MODEX_TEXT +@CB_One_Loop . . . . . . . . . . L Near 0CCA MODEX_TEXT +@CB_Only_One_Band . . . . . . . L Near 0CA7 MODEX_TEXT +@CB_Right_Band . . . . . . . . . L Near 0D1B MODEX_TEXT +@CB_Right_Done . . . . . . . . . L Near 0D47 MODEX_TEXT +@CB_Right_Loop . . . . . . . . . L Near 0D31 MODEX_TEXT +@CP_Exit . . . . . . . . . . . . L Near 0C07 MODEX_TEXT +@CodeSize . . . . . . . . . . . Number 0001h +@DB_COPY2 . . . . . . . . . . . L Near 0A8A MODEX_TEXT +@DB_COPY_LINE . . . . . . . . . L Near 0A68 MODEX_TEXT +@DB_COPY_LOOP . . . . . . . . . L Near 0A70 MODEX_TEXT +@DB_COPY_PLANE . . . . . . . . . L Near 0A5F MODEX_TEXT +@DB_COPY_REMAINDER . . . . . . . L Near 0A88 MODEX_TEXT +@DB_Exit . . . . . . . . . . . . L Near 0AC1 MODEX_TEXT +@DB_NEXT2 . . . . . . . . . . . L Near 0A99 MODEX_TEXT +@DB_NEXT_LINE . . . . . . . . . L Near 0A91 MODEX_TEXT +@DL_BREZHAM . . . . . . . . . . L Near 04C7 MODEX_TEXT +@DL_DeltaYOK2 . . . . . . . . . L Near 0515 MODEX_TEXT +@DL_DeltaYOK . . . . . . . . . . L Near 04DC MODEX_TEXT +@DL_DrawLeft . . . . . . . . . . L Near 051F MODEX_TEXT +@DL_DrawRight . . . . . . . . . L Near 05A3 MODEX_TEXT +@DL_EXIT2 . . . . . . . . . . . L Near 061E MODEX_TEXT +@DL_EXIT . . . . . . . . . . . . L Near 04C4 MODEX_TEXT +@DL_HORZ . . . . . . . . . . . . L Near 0433 MODEX_TEXT +@DL_LONGLN . . . . . . . . . . . L Near 0461 MODEX_TEXT +@DL_NOSWAP1 . . . . . . . . . . L Near 0428 MODEX_TEXT +@DL_NOSWAP2 . . . . . . . . . . L Near 048C MODEX_TEXT +@DL_SLLExit . . . . . . . . . . L Near 0563 MODEX_TEXT +@DL_SLLL2nc . . . . . . . . . . L Near 0543 MODEX_TEXT +@DL_SLLL3nc . . . . . . . . . . L Near 0558 MODEX_TEXT +@DL_SLLLoop . . . . . . . . . . L Near 0537 MODEX_TEXT +@DL_SLR2nc . . . . . . . . . . . L Near 05C5 MODEX_TEXT +@DL_SLR3nc . . . . . . . . . . . L Near 05D9 MODEX_TEXT +@DL_SLRExit . . . . . . . . . . L Near 05E3 MODEX_TEXT +@DL_SLRLoop . . . . . . . . . . L Near 05B9 MODEX_TEXT +@DL_STLExit . . . . . . . . . . L Near 05A1 MODEX_TEXT +@DL_STLLoop . . . . . . . . . . L Near 0575 MODEX_TEXT +@DL_STLnc2 . . . . . . . . . . . L Near 0588 MODEX_TEXT +@DL_STLnc3 . . . . . . . . . . . L Near 059D MODEX_TEXT +@DL_ShallowLeft . . . . . . . . L Near 0525 MODEX_TEXT +@DL_ShallowRight . . . . . . . . L Near 05A7 MODEX_TEXT +@DL_SteepLeft . . . . . . . . . L Near 0566 MODEX_TEXT +@DL_SteepRight . . . . . . . . . L Near 05E5 MODEX_TEXT +@DL_VLINE . . . . . . . . . . . L Near 0481 MODEX_TEXT +@DL_VLoop . . . . . . . . . . . L Near 04B4 MODEX_TEXT +@DL_XRSEG . . . . . . . . . . . L Near 0479 MODEX_TEXT +@DP_WAIT0 . . . . . . . . . . . L Near 0706 MODEX_TEXT +@DP_WAIT1 . . . . . . . . . . . L Near 071B MODEX_TEXT +@DataSize . . . . . . . . . . . Number 0000h +@FB_EXIT . . . . . . . . . . . . L Near 0404 MODEX_TEXT +@FB_LEFT_CONT . . . . . . . . . L Near 037A MODEX_TEXT +@FB_LEFT_LOOP . . . . . . . . . L Near 036A MODEX_TEXT +@FB_L_PLANE_FLUSH . . . . . . . L Near 037C MODEX_TEXT +@FB_MIDDLE_LOOP . . . . . . . . L Near 03C7 MODEX_TEXT +@FB_NORMAL . . . . . . . . . . . L Near 034C MODEX_TEXT +@FB_NOSWAP1 . . . . . . . . . . L Near 031F MODEX_TEXT +@FB_NOSWAP2 . . . . . . . . . . L Near 0339 MODEX_TEXT +@FB_ONE_BAND_ONLY . . . . . . . L Near 03D2 MODEX_TEXT +@FB_ONE_LOOP . . . . . . . . . . L Near 03F4 MODEX_TEXT +@FB_RIGHT_CONT . . . . . . . . . L Near 03B0 MODEX_TEXT +@FB_RIGHT_LOOP . . . . . . . . . L Near 03A0 MODEX_TEXT +@FB_RIGHT . . . . . . . . . . . L Near 037D MODEX_TEXT +@FB_R_EDGE_FLUSH . . . . . . . . L Near 03B3 MODEX_TEXT +@GPC_DECODE_CHAR_BYTE . . . . . L Near 080F MODEX_TEXT +@GPC_EXIT . . . . . . . . . . . L Near 0897 MODEX_TEXT +@GPC_LowChar . . . . . . . . . . L Near 07DB MODEX_TEXT +@GPC_NEXT_LINE . . . . . . . . . L Near 0889 MODEX_TEXT +@GPC_NO_LEFT0BITS . . . . . . . L Near 083C MODEX_TEXT +@GPC_NO_LEFT1BITS . . . . . . . L Near 0831 MODEX_TEXT +@GPC_NO_MIDDLE0BITS . . . . . . L Near 085E MODEX_TEXT +@GPC_NO_MIDDLE1BITS . . . . . . L Near 0853 MODEX_TEXT +@GPC_NO_RIGHT0BITS . . . . . . . L Near 0888 MODEX_TEXT +@GPC_NO_RIGHT1BITS . . . . . . . L Near 087D MODEX_TEXT +@GPC_Set_Char . . . . . . . . . L Near 07E3 MODEX_TEXT +@Interface . . . . . . . . . . . Number 0000h +@LDR_Load . . . . . . . . . . . L Near 0677 MODEX_TEXT +@Model . . . . . . . . . . . . . Number 0004h +@PS_Exit . . . . . . . . . . . . L Near 09AF MODEX_TEXT +@PS_Print_It . . . . . . . . . . L Near 097B MODEX_TEXT +@SAP_Exit . . . . . . . . . . . L Near 06E1 MODEX_TEXT +@SDF_Set_Font . . . . . . . . . L Near 0A06 MODEX_TEXT +@SDP_Exit . . . . . . . . . . . L Near 0720 MODEX_TEXT +@SD_WAIT0 . . . . . . . . . . . L Near 0798 MODEX_TEXT +@SD_WAIT1 . . . . . . . . . . . L Near 079D MODEX_TEXT +@SMX_Exit . . . . . . . . . . . L Near 027B MODEX_TEXT +@STRLoop . . . . . . . . . . . . L Near 05F4 MODEX_TEXT +@STRnc2 . . . . . . . . . . . . L Near 0606 MODEX_TEXT +@STRnc3 . . . . . . . . . . . . L Near 061A MODEX_TEXT +@SVM_BadModeSetup . . . . . . . L Near 015C MODEX_TEXT +@SVM_Continue . . . . . . . . . L Near 0161 MODEX_TEXT +@SVM_EXIT . . . . . . . . . . . L Near 0247 MODEX_TEXT +@SVM_Set_Data . . . . . . . . . L Near 01AA MODEX_TEXT +@SVM_Set_Pages . . . . . . . . . L Near 0207 MODEX_TEXT +@SVM_Setup_CRTC . . . . . . . . L Near 019D MODEX_TEXT +@SVM_Setup_Table . . . . . . . . L Near 0193 MODEX_TEXT +@SW_Exit . . . . . . . . . . . . L Near 0789 MODEX_TEXT +@SW_WAIT0 . . . . . . . . . . . L Near 0761 MODEX_TEXT +@SW_WAIT1 . . . . . . . . . . . L Near 0776 MODEX_TEXT +@TB_COPY2 . . . . . . . . . . . L Near 0B6A MODEX_TEXT +@TB_COPY_LINE . . . . . . . . . L Near 0B26 MODEX_TEXT +@TB_COPY_LOOP . . . . . . . . . L Near 0B2E MODEX_TEXT +@TB_COPY_PLANE . . . . . . . . . L Near 0B1B MODEX_TEXT +@TB_COPY_REMAINDER . . . . . . . L Near 0B68 MODEX_TEXT +@TB_Exit . . . . . . . . . . . . L Near 0BB0 MODEX_TEXT +@TB_NEXT2 . . . . . . . . . . . L Near 0B87 MODEX_TEXT +@TB_NEXT_LINE . . . . . . . . . L Near 0B79 MODEX_TEXT +@TB_SKIP_01 . . . . . . . . . . L Near 0B39 MODEX_TEXT +@TB_SKIP_02 . . . . . . . . . . L Near 0B45 MODEX_TEXT +@TB_SKIP_03 . . . . . . . . . . L Near 0B51 MODEX_TEXT +@TB_SKIP_04 . . . . . . . . . . L Near 0B5D MODEX_TEXT +@TB_SKIP_05 . . . . . . . . . . L Near 0B75 MODEX_TEXT +@TGP_DECODE_CHAR_BYTE . . . . . L Near 090D MODEX_TEXT +@TGP_EXIT . . . . . . . . . . . L Near 096B MODEX_TEXT +@TGP_LowChar . . . . . . . . . . L Near 08D9 MODEX_TEXT +@TGP_NEXT_LINE . . . . . . . . . L Near 095E MODEX_TEXT +@TGP_NO_LEFT1BITS . . . . . . . L Near 092D MODEX_TEXT +@TGP_NO_MIDDLE1BITS . . . . . . L Near 0941 MODEX_TEXT +@TGP_NO_RIGHT1BITS . . . . . . . L Near 095D MODEX_TEXT +@TGP_Set_Char . . . . . . . . . L Near 08E1 MODEX_TEXT +@TPS_Exit . . . . . . . . . . . L Near 09EC MODEX_TEXT +@TPS_Print_It . . . . . . . . . L Near 09BC MODEX_TEXT +@code . . . . . . . . . . . . . Text MODEX_TEXT +@data . . . . . . . . . . . . . Text DGROUP +@fardata? . . . . . . . . . . . Text FAR_BSS +@fardata . . . . . . . . . . . . Text FAR_DATA +@stack . . . . . . . . . . . . . Text DGROUP +ACTIVE_PAGE . . . . . . . . . . Word 0012 _BSS +ALL_PLANES_ON . . . . . . . . . Number 0F02h +ALL_PLANES . . . . . . . . . . . Number 000Fh +ASYNC_RESET . . . . . . . . . . Number 0100h +ATTRIB_Ctrl . . . . . . . . . . Number 03C0h +CHAIN4_OFF . . . . . . . . . . . Number 0604h +CHARSET_HI . . . . . . . . . . . Word 0026 _BSS +CHARSET_LOW . . . . . . . . . . Word 0022 _BSS +CHAR_BITS . . . . . . . . . . . Number 000Fh +CRTC_Data . . . . . . . . . . . Number 03D5h +CRTC_Index . . . . . . . . . . . Number 03D4h +CURRENT_MOFFSET . . . . . . . . Word 001C _BSS +CURRENT_PAGE . . . . . . . . . . Word 0014 _BSS +CURRENT_SEGMENT . . . . . . . . Word 0016 _BSS +CURRENT_XOFFSET . . . . . . . . Word 0018 _BSS +CURRENT_YOFFSET . . . . . . . . Word 001A _BSS +Char_Plane_Data . . . . . . . . Byte 0008 MODEX_TEXT +DAC_READ_ADDR . . . . . . . . . Number 03C7h +DAC_WRITE_ADDR . . . . . . . . . Number 03C8h +DISPLAY_PAGE . . . . . . . . . . Word 0010 _BSS +False . . . . . . . . . . . . . Number 0000h +GC_Index . . . . . . . . . . . . Number 03CEh +GET_CHAR_PTR . . . . . . . . . . Number 1130h +INPUT_1 . . . . . . . . . . . . Number 03DAh +LAST_PAGE . . . . . . . . . . . Word 0004 _BSS +LATCHES_OFF . . . . . . . . . . Number FF08h +LATCHES_ON . . . . . . . . . . . Number 0008h +Left_Clip_Mask . . . . . . . . . Byte 0000 MODEX_TEXT +MAP_MASK_PLANE1 . . . . . . . . Number 0102h +MAP_MASK_PLANE2 . . . . . . . . Number 1102h +MAP_MASK . . . . . . . . . . . . Number 0002h +MAX_XOFFSET . . . . . . . . . . Word 001E _BSS +MAX_YOFFSET . . . . . . . . . . Word 0020 _BSS +MISC_OUTPUT . . . . . . . . . . Number 03C2h +MODE_200_Tall . . . . . . . . . L Near 0044 MODEX_TEXT +MODE_240_Tall . . . . . . . . . L Near 0054 MODEX_TEXT +MODE_320_Wide . . . . . . . . . L Near 0028 MODEX_TEXT +MODE_320x200 . . . . . . . . . . L Near 0074 MODEX_TEXT +MODE_320x240 . . . . . . . . . . L Near 00BC MODEX_TEXT +MODE_320x400 . . . . . . . . . . L Near 0086 MODEX_TEXT +MODE_320x480 . . . . . . . . . . L Near 00CE MODEX_TEXT +MODE_360_Wide . . . . . . . . . L Near 0036 MODEX_TEXT +MODE_360x200 . . . . . . . . . . L Near 00E0 MODEX_TEXT +MODE_360x240 . . . . . . . . . . L Near 0098 MODEX_TEXT +MODE_360x400 . . . . . . . . . . L Near 00F2 MODEX_TEXT +MODE_360x480 . . . . . . . . . . L Near 00AA MODEX_TEXT +MODE_400_Tall . . . . . . . . . L Near 0044 MODEX_TEXT +MODE_480_Tall . . . . . . . . . L Near 0054 MODEX_TEXT +MODE_Double_Line . . . . . . . . L Near 0020 MODEX_TEXT +MODE_Single_Line . . . . . . . . L Near 0018 MODEX_TEXT +MODE_TABLE . . . . . . . . . . . L Near 0064 MODEX_TEXT +NUM_MODES . . . . . . . . . . . Number 0008h +PAGE_ADDR . . . . . . . . . . . Word 0006 _BSS +PAGE_SIZE . . . . . . . . . . . Word 000E _BSS +PEL_DATA_REG . . . . . . . . . . Number 03C9h +PIXEL_PAN_REG . . . . . . . . . Number 0033h +PLANE_BITS . . . . . . . . . . . Number 0003h +READ_MAP . . . . . . . . . . . . Number 0004h +ROM_8x8_Hi . . . . . . . . . . . Number 0004h +ROM_8x8_Lo . . . . . . . . . . . Number 0003h +Right_Clip_Mask . . . . . . . . Byte 0004 MODEX_TEXT +SCREEN_HEIGHT . . . . . . . . . Word 0002 _BSS +SCREEN_WIDTH . . . . . . . . . . Word 0000 _BSS +SC_Data . . . . . . . . . . . . Number 03C5h +SC_Index . . . . . . . . . . . . Number 03C4h +SEQU_RESTART . . . . . . . . . . Number 0300h +START_DISP_HI . . . . . . . . . Number 000Ch +START_DISP_LO . . . . . . . . . Number 000Dh +True . . . . . . . . . . . . . . Number -0001h +VERT_RETRACE . . . . . . . . . . Number 0008h +VGA_Segment . . . . . . . . . . Number A000h +b . . . . . . . . . . . . . . . Text BYTE PTR +d . . . . . . . . . . . . . . . Text DWORD PTR +f . . . . . . . . . . . . . . . Text FAR PTR +nil . . . . . . . . . . . . . . Number 0000h +o . . . . . . . . . . . . . . . Text OFFSET +s . . . . . . . . . . . . . . . Text SHORT +w . . . . . . . . . . . . . . . Text WORD PTR + + 0 Warnings + 0 Errors diff --git a/modex105/MODEX.SBR b/modex105/MODEX.SBR new file mode 100755 index 0000000000000000000000000000000000000000..4da653b0d38749807dfb8156d36fc9787e1da174 GIT binary patch literal 12235 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 literal 0 HcmV?d00001 diff --git a/modex105/PACKING.LST b/modex105/PACKING.LST new file mode 100755 index 00000000..88f817ca --- /dev/null +++ b/modex105/PACKING.LST @@ -0,0 +1,87 @@ + +PACKING LIST FOR MODEX104 + +DIRECTORY: \ - The Mode X Library versoon 1.04 + +ASM BAT 26 05-14-93 6:00p - Batch File to Assemble MODEX.ASM +MODE-X TXT 2135 05-14-93 6:00p - File Describing MODE X Routines +MODEX ASM 117039 05-14-93 6:00p - Assembly source to Mode X Library +MODEX BI 3238 05-14-93 6:00p - Include File for BASIC/PDS +MODEX H 2943 05-14-93 6:00p - Include File for C/C++ +MODEX OBJ 5208 05-14-93 6:00p - The Mode X Library +README DOC 3259 05-14-93 6:00p - Information on this Product +PACKING LST 4767 05-14-93 6:00p - This File + +DIRECTORY: \DEMOS - Mode X Demos + +CHARDEMO EXE 13066 05-14-93 6:00p - Demo of Multiple Fonts & Color Cycling +TEST6 EXE 19990 05-14-93 6:00p - Main Mode X Demo +ROM_8X8 FNT 1024 05-14-93 6:00p - Font for CHARDEMO.EXE +SPACEAGE FNT 1024 05-14-93 6:00p - Font for CHARDEMO.EXE +SYSTEM FNT 1024 05-14-93 6:00p - Font for CHARDEMO.EXE + +DIRECTORY: \DEMOS\BASIC7 - Demo Sources for Microsoft BASIC 7.1 (PDS) + +MAKE-LIB BAT 166 05-14-93 6:00p - Batch File to make MODEX.LIB/.QLB +MODEX BI 3238 05-14-93 6:00p - Include File for MODE X Library +MODEX LIB 7189 05-14-93 6:00p - Mode X & Utility Libraries for QBX +MODEX OBJ 5208 05-14-93 6:00p - Mode X Library - Object File +MODEX QLB 11141 05-14-93 6:00p - Mode X & Utility Quick Library +TEST6 BAS 12733 05-14-93 6:00p - Main Demo Source Code (TEST6.EXE) +UASM-BC7 BAT 43 05-14-93 6:00p - Batch file to Make UTILS.OBJ for QBX +UTILS ASM 8506 05-14-93 6:00p - Basic Utilities - Assembler source +UTILS BI 2028 05-14-93 6:00p - Basic Utilities - Basic Includes +UTILS OBJ 681 05-14-93 6:00p - Basic Utilities - Object File +CHARDEMO BAS 3431 05-14-93 6:00p - Source to CHARDEMO.EXE + +DIRECTORY: \DEMOS\C - Demo Sources for Borland C/C++ + +C_UTILS ASM 8782 05-14-93 6:00p - C Utilities - Assembler source +C_UTILS H 2623 05-14-93 6:00p - C Utilities - C Includes +C_UTILS OBJ 648 05-14-93 6:00p - C Utilities - Object File +MODEX H 2943 05-14-93 6:00p - Mode X Library C Incldues +MODEX OBJ 5208 05-14-93 6:00p - Mode X Library +UTLS-ASM BAT 36 05-14-93 6:00p - Batch File to Make C_UTILS.OBJ +X-DEMO C 15085 05-14-93 6:00p - Source to Main Demo (TEST6) in C +X-DEMO EXE 41090 05-14-93 6:00p - C Version of Main Demo +X-DEMO PRJ 5188 05-14-93 6:00p - Borland C Project file + +DIRECTORY: \DEMOS\PASCAL - Demo Sources for Turbo Pascal + +TEST5 PAS 15873 05-14-93 6:00p - Source for a TP Version of TEST6.EXE + +DIRECTORY: \DEMOS\QB45 - Demo Sources for Microsoft QuickBASIC 4.5 + +MAKE-LIB BAT 164 05-14-93 6:00p - Batch File to make MODEX.LIB/.QLB +MODEX BI 3238 05-14-93 6:00p - Include File for MODE X Library +MODEX LIB 7189 05-14-93 6:00p - Mode X & Utility Libraries for QB45 +MODEX OBJ 5208 05-14-93 6:00p - Mode X Library - Object File +MODEX QLB 9739 05-14-93 6:00p - Mode X & Utility Quick Library/QB45 +TEST6A BAS 12743 05-14-93 6:00p - Main Demo Source Code (TEST6.EXE) +TEST6A EXE 40544 05-14-93 6:00p - QB45 Version of Main Demo +UASM-QB4 BAT 30 05-14-93 6:00p - Batch file to Make UTILS.OBJ for QB45 +UTILS ASM 8506 05-14-93 6:00p - Basic Utilities - Assembler source +UTILS BI 2028 05-14-93 6:00p - Basic Utilities - Basic Includes +UTILS OBJ 628 05-14-93 6:00p - Basic Utilities - Object File + +DIRECTORY: \FONTEDIT - Font Editor + +CSEDIT EXE 39242 05-14-93 6:00p - Font Editor Program +CSEDIT DOC 8629 05-14-93 6:00p - Font Editor Documentation +CHARSETS CS 2144 05-14-93 6:00p - Internal Fonts for Editor +MOUSEIMG CS 128 05-14-93 6:00p - Mouse Pointers for Editor +PALETTE CS 768 05-14-93 6:00p - Palette for Editor +INVERSE FNT 1024 05-14-93 6:00p - Sample Font +ROM_8X8 FNT 1024 05-14-93 6:00p - Sample Font +SPACEAGE FNT 1024 05-14-93 6:00p - Sample Font +SYSTEM FNT 1024 05-14-93 6:00p - Sample Font + +DIRECTORY: \PALEDIT - Palette Editor + +PALEDIT EXE 31954 05-14-93 6:00p - Palette Editor Program +PALEDIT DOC 6688 05-14-93 6:00p - Palette Editor Documentation +CHARSETS CS 2144 05-14-93 6:00p - Internal Fonts for Editor +MOUSEIMG CS 128 05-14-93 6:00p - Mouse Pointers for Editor +GAMECOLR PAL 768 05-14-93 6:00p - Sample Palette +PRIME PAL 768 05-14-93 6:00p - Sample Palette +RGB PAL 768 05-14-93 6:00p - Sample Palette diff --git a/modex105/PALEDIT/BAKAPI.PAL b/modex105/PALEDIT/BAKAPI.PAL new file mode 100755 index 0000000000000000000000000000000000000000..d4d4a6a097bb4a8dd05fbaf5509f700be81b1b52 GIT binary patch literal 768 zcmZvaK??#Q5Jta4k>xhchKAID9Tti}mO5g7U!dKIx>8Wj~Y(D_9fe^$) zq~HZ4*hm{H+YdxCl6aCpB84F2g+^&ZW&6Q7IXMYV&Q7Fo78k^wqz#qr2Y^>#10KL! z7#N1J;x<&aAB^Fh;lVH&<^r)9*zC9smF)*>d9b{fazkYW)cg-{{Pq!F&fRpE+dlJCu4ehhhcyPv){@Bht-sFQSU z?`Ur=rF3mP9s`G~&wl*=$?EXx>WcWGqImeQw?g*Kn|B$j<8L9yRdu}o^5y<<<={3R zJ91BVemh_^&-b1i&-V^qIlB4L%_JF3?W2uH8ylZp>f!4Cv;EZ}DLZpBtEjiQ;#L-W z6c4L7u75%mXAG#CsbTFza*OjM2!4p3-Wj(2t#=j~I%LJ8fw*LYP9bC>57Rj1{9ZA7Mt z6KS4Onx_vgng8 z5ChN7E_A{E#u-DcG5iJBS|ct9q$P*ET4SFcHJ_hw-p*IT;K*e|=JU&VGnr1~~8H#+(P@Jm%Ot$eFuBJd>w6;$lt( zZ_xR{Vt$gPB+U&6Ju11bu*7}I%|Ck}`V-5W(=w<`MO-ZROI!i+W5yLm>$%e<4W%p) z7`$kjGQ>J~AGk9ZCWI)&)d=%cYd-+8T!t)QpYs5Kexkqot2Z4Fs z`R41b%`e0!UwyeH{!fS&ZY*{lZ@m|a$Iauzf)uuYju0V`tBS9351aLIx zKoYE`puPCvqHV#G^>wrnw9VmR)6&9HxV5`0DPT3nNkt;8{TC0{5AmfyPur=6UpQ=s zxYo!wnILk%u4}5ni%GGJ-#Ow4ghL)og!7>;E{stKtVwWvI2Xw*@hE4KQLFRa)0hVH z{}+J}BY08(?-3mP)Jab%MT$7DR|L0b_`tuYN6S;-uczYEpKvZ!rX1M!Z2JyuC%E7IvA=GDa(`>QD+soN|ACXgvqk-8t6F z`g+b`2YrteKAa~bA0JsVwj+xV5RCQLr_l%|@VE1Ph*N`Wpk_V#vYyv-u06L(E9KAY zn$Pj}eir>RM^&{1s#<$J!mwX3n@lF#&_nC>86X2k^*$bR_5;^75OWrqIpVS%j_3Kj zU$BjM*bnsZ?9kg;v}e&js#Nd~5GEQhM;P#a24hIyzo+2B|8*@NyU3c zKhoDl(t4iF`zI7CEZbYo-&2HyY)=?K#(E3@obyW5K^Oq{Pa2IdqU?;31F1O*Ao42S zpH^MH76I4}Zg!5~0c~&l{;AHgn)NV{g3yP^a9{fpdzW07LSf8BB*6Q(VGO|>EaR=u c@7}+=v9)<$+}Yd`AAF4Uy(KWY!FGSkU-|#d0ssI2 literal 0 HcmV?d00001 diff --git a/modex105/PALEDIT/DIAGONAL.PAL b/modex105/PALEDIT/DIAGONAL.PAL new file mode 100755 index 0000000000000000000000000000000000000000..5dee1969fc3e9dfe5bee855460c512ba3a850e69 GIT binary patch literal 768 zcmWO4fugA}007W2rBcR}O4&6{OBqvI%Gm$^)qCd~4gg>dt%dE`E^_?PL#dC+Ix03v zg)s=;JrU8Lq&#;GQ)M~QO6RqSZwB9WAV;TF@YMk&t+()|8fhN>SZXNw zlH!w9V#CCj>_H=5QV=V{ZZuhLc}5w0$MS6i*`%&nsp;R@Bdai@dcK(N!vFYzrI<7GXD-dFIX2(s-WZI;c#O)}jL4N_^8 zTcu8u)7UF|4Pgnx;^Wefi<_1%Ep1+!vLNT!Y}jtu?JDGc&+$l%HZrbP=9$}#$AjmS z*Na+Q6g63GQa8zs>ZAOi5HxtjUZd00DYeRtim4rL!)D8N$Ihm-XMf}viC>HtuD_TQ zw{IT5c@|#g$5NW2HtQqFP3lMas4(adbn^P@b#;~O(dtj5s{bLtd&Hga3vnS?g@jJX YqmSBdQG+6i^ro%x_>On~3fi#v3oGhTWdHyG literal 0 HcmV?d00001 diff --git a/modex105/PALEDIT/MOUSEIMG.CS b/modex105/PALEDIT/MOUSEIMG.CS new file mode 100755 index 0000000000000000000000000000000000000000..101e2084b44a2da8647ea004e20ffc599897dc29 GIT binary patch literal 128 zcmXwwyA6Xt5JVqBG8vc(jsUrhb3`33!65-+x3nvTG6*aUE5LfeXnx8{>eQ%KC5%j> xL+EL0E7_Ch82ppy6n|(>dLPTn2m0He)d4^WFp&TN literal 0 HcmV?d00001 diff --git a/modex105/PALEDIT/PALEDIT.DOC b/modex105/PALEDIT/PALEDIT.DOC new file mode 100755 index 00000000..61d0b2ee --- /dev/null +++ b/modex105/PALEDIT/PALEDIT.DOC @@ -0,0 +1,166 @@ + +PALEDIT - A Simple VGA 256 Color Palette Editor + + +PALEDIT is distributed with MODEXnnn.ZIP, the general purpose MODE X +Library for VGA Graphics. + +WHAT YOU NEED TO RUN PALEDIT: + + * A Vga Monitor + * A Microsoft Compatible Mouse + + A Mouse is most definitely required, as the keyboard is used for + nothing except entering file names. + +FILES NEEDED IN THE CURRENT DIRECTORY: + + PALEDIT.EXE - The Palette Editor Program + CHARSETS.CS - The Palette Editor's Internal Fonts + MOUSEIMG.CS - The Palette Editor's Mouse Pointer + +SAMPLE PALETTE FILE THAT SHOULD BE INCLUDED: + + RGB.PAL - A Simple Palette with Reds, Greens, and Blues + PRIME.PAL - A Simple Palette + GAMECOLR.PAL - A Bright Palette from a Game of mine. + +WHAT IT EDITS: + + The VGA DAC Registers, all 256 of them. + +HOW IT WORKS/FEATURES: + + PALEDIT allows the user to see the entire VGA Palette of 256 colors + and select and modify the RED, GREEN, and BLUE values of any individual + color (DAC) register. The entire group of 256 colors can be saved to + a disk file for later retrieval. + + Individual "SLIDERS" show the current RED, GREEN, and BLUE color + components of the current color and allow them to be changed. + + The Following operations can be performed. + + * Raise, Lower, and set the RED, GREEN, or BLUE components. + * Copy the current RGB values to another Color (DAC) Register + * Brighten the selected color + * Darken and selected color + * Reset the selected color to its original state + * Blend an entire range of colors, creating a smooth + Transition from one color to another + * Optionally Lock out the first 16 colors to prevent + Accidental Modification + +DESCRIPTION OF OBJECTS/FEATURES FROM THE TOP DOWN: + + COLOR SLIDERS: In the upper left of the screen there are + Three Rectangular Boxes: One for each primary color: + RED, GREEN, and BLUE. Each Box has an arrow at each + end, and a scale bar in the middle, connecting the two + arrows. The scale bar is much like a thermometer, + indicating how much of that color is in the selected + color. To the right of each Box, the name of the color + is indicated, along with the content color in the form + of a number from 0 to 63; where 0 means none of that + color goes into making the selected color, and 63 means + that the selected color is saturated with that color. + + Clicking the mouse on the slider's left arrow box will + decrease the amount of that primary color in the selected + color. Holding the mouse button down will reduce the + color value all the way to 0. + + Clicking the mouse on the slider's right arrow box will + increase the amount of that primary color in the selected + color. Holding the mouse button down will increase the + color value all the way to 63. + + Clicking the mouse on the scale bar will set the amount + of that primary color to the value the represents that + position on the slider. + + LOCK Button: Clicking the button toggles the lockout of the + first 16 colors. When they are locked out, they can not + be modified, and when selected the word "LOCKED" will + appear below the color # on the Color Information Display. + + LOAD Button: Clicking this button will load the Palette file + that is named in the Palette File name box. If no name is + given or no such file exists, then nothing will be loaded. + + SAVE Button: Clicking this button will save the current Palette + in a file using the name given in the Palette File Name Box. + If a Valid name is not provided, nothing will be saved. + + QUIT Button: Clicking this button will return you to DOS. + Nothing is saved, and no confirmation is given. + + + Color Information Display: This Box is on the left side of the + Screen, below the Color Sliders. It shows the number of the + currently selected color (from 0 to 255) and indicates if + that color is locked. To the right of this box is a big + square showing the current color. + + LIGHTER Button: Clicking this button will make the selected + color brighter. + + DARKER Button: Clicking this button will make the selected + color darker. + + RESET Button: Clicking this button will restore the selected + color to the value it had when it was first selected. + + BLEND Button: Clicking this button will let you Blend a range + of colors together. One end of the range of colors is the + currently selected color. After Clicking the BLEND button. + You must click on the color at the other end of the range + in the Palette Display Box. All of the colors in between + those two colors will be changed to represent a gradual + transition from the color at one end to the color at the + other end. + + PALETTE FILE NAME BOX: This Text Box is used to enter the name + of a Palette file to load or the name to save the current + Palette as. Just click on the Box, and it will change color + and a flashing cursor will appear. Now you type in a filename + or edit the existing filename. Press or click + outside the text box to end editing. + + PALETTE DISPLAY BOX: This Box shows all 256 colors in an array + of 32 by 8 color blocks. The Currently Selected Color will + have a Box around it. Clicking on a color with the Left + Mouse button will make that color the new currently selected + color. Clicking on a color with the Right Mouse Button will + copy the color value from the old selected color to it, before + it is made the new selected color. + + Message Bar: At the very bottom of the screen, this Bar will display + information and messages for various functions. + +PALETTE FILE FORMAT: + + BINARY image, in order of VGA DAC (Color) Number. 3 Bytes Per + Color, 256 Colors. 768 Bytes total. The Files will be exactly + 768 bytes in size. + + COLOR: + RED: 1 BYTE + GREEN: 1 BYTE + BLUE: 1 BYTE + + PALETTE: Array (0 to 255) of COLOR + +COMMENTS, QUESTIONS, BUG REPORTS, etc: + + Send the to the Author: Matt Pritchard + + Through the 80xxx Fidonet Echo or + + Matt Pritchard + P.O. Box 140264 + Irving, TX 75014 + +CREDITS: + + This Palette Editor was written in QuickBASIC 4.5 diff --git a/modex105/PALEDIT/PALEDIT.EXE b/modex105/PALEDIT/PALEDIT.EXE new file mode 100755 index 0000000000000000000000000000000000000000..f69e172d05b32a8aa903c3857db0b199c84a91de GIT binary patch literal 70230 zcmeFadw5huwl`eed-v`p-RX27go`nW2oVT36AX|$kP9N0Ccz{S2#(`8j>GXdNq2A@ zgkT(!&PG)5I8Kh^Aw)eML?`29oJQtoNMpjqO9CDzTn;h>o!vAl0_Fx}f4^0`yOSU| z-#pLz$NPNGN2uMkt5&UAwQAL>RjaDH7q2W}HH@)Xrqija8eW08jaAyB@u@@j|Ns4e zn*;Z+wa;CYoptx}SjO_qj|t6^*4`Oxfk3e~|2t3BSJ)Dp>~15oyJv`Yw@znwM+x@Y zuR<1!l&(X1CIY1|^ryE<%TcuUM?h0M&&F6~`c=K^5WLZ>RoW~F%)J?aV`nkERturb zUG0B$w@G$)4PIUdTP*U(M*!lH_eLxhy<>HIL{@uS=weYWA6}Wt+>AY;TTq!AyN9t2 zy1UH>Za?T0yarag5Fl!~1yHgmceI~wKfS4`TyJ-kT;{0d@(@aQhp_HPF3UZx-%ZiU zRTF^-(7Y(H)2QR_>PV3Ngwg!>rE)H-d8{b}{3fUv1EfmNMZSHcYas=e+$ zw!mIj$^@x|(Tf4xuU#3s(^|h(s7&7~Ffh~K%-V+|7K?|h%zHNj7yQf#A#^5sNb1bW z7CmA)l0jth1uZ7=n1=CLq3&6sew$FYP0(;j%$q6%aJ5R$3r6(Q3qly4FA5@_wStAY zpBL=z7Z6@Vs705lY3Qj7=)afz{nsk(5Oj=Q-8+zU(SUr)qn@n&0;;Bdr%<<3sDDLB zk;V$^1@E2AonW-PUkjGsh1%%Vy(>_j@_6|k>rSipNBX)xef@4BUV26NZDMnLbDEBA z5WFT<%d^MmTcx)+@eQ2#w?R&;)F=#N?ze!n0pV?gM&Kmk>vn^XSA>m%*UV%@s-)dS zoSL*-aO(jlB1nLBgzdD}>4jPwJ8u5yuIP7!kJ9R-;Oo2eDh6NQ!>gL~$`4{_9*Uu5 zD27Ay`U=_zYCaY|x=RB1%(!>_H6-_fgf@U61CTT0-V^FR9-Q~7mItIWvNeHLX%d9+Fj2_IhF3X-_+Q65(#lbE{{f_5$l`!xv~Ufo{@ zU@4DaTcrzJf-VXw>b(r@*eZ45>AnEyiwKtyx&TZPqzxa&yiX4riZ*$JMK-uZc|(Y7 zz)(c2+zQ==5qz7b8ssIm%6+2zE#*kiOY|zyt2Ye*fYkwZDxwx-ZZHXk1%peFCfgtd z618$eRD*?i*9PgSjers%wcQMrodK2IiceB5mvuX>TRQ5WgmN;~J_zMh{}hjso~qxB z9zjmhQvfz0pfJ}gyySlyc3NxWfL3e1$h@b91++)4a~jz4)*Zw)J532xeXG=`Hz8rH zU}HqeK483_6t^CUG_`~yJdH3NwC)Wk!5%2Vp214cZ3RdTLIS|v!zVQ<(zMg+4#SI# za3?a}$0x|=8Hr8aV$|+k$e!IZjCFerHU?GgmRR?+a%R}V>hEK+wS>)GmT4!QjAEpQ z4frT$nl7iOAUXWJ^9yNO4BPN~fMnK}vos^y@Vh;+3`**^-Mw=2pn8y`>5rL7HGv`X z(M;=BBL>wrqc(UjPb5M66BU*FhAlHlpdPK^)Psdw6PL3QVB}R2NFDP!bk9=z)TDCe z{x41`T7}m>LF2_3!d8wpQ6mh*8Ddjr2-x)EO|!tGmEe({p$+v9vpu6(yw$;$2@)D< zUj{_+(!(4XuZNI<6^7puzK59*tS$gm*C!A&d94ASg|Q9);TAyfRSaqOFitrlqqQbb zoB7n&k6!N|((76o1IY^7MvW2?{f!p21j;e7GM$NorcA0!0M{^i1?qbJy1MizqaR$R z67V6FX!K~X0*xSU^a*vMm{~81X##_0da~dB0@R8m22@apI10UE5oyk}ikLHz1@%*wdm_SQgeif-1RpFsEm$~# z7oN$ruyBsJ z9b_*MN!KhCq5P5M&Oum!un=G;iQ5Fv3%8;0qF`Y=FP6uj%SAJzEVmsY4`F$rFu_yd zR_T65tbKr`;`uOJh38uKI0o$f2oEDXfUuSYHEZ4dWY0Z}hirX-1|T2Fe6UasmB9lA zNZ5nPb!#!i0tJGD<^d!Q6bK}{zeIZqg6&x;+CbAPu>{W&Fr!tvkN61y_ez9S2qg&j zp}w~Y7u1H8Gga;7-=3a~N2*RT% z@-9?9FRP(06%8om;Hb+HcV7kS$AYNWi?PVwz)@F-5+-S&aUhf<0L;A}VFN-%05z4> zP(KOyR_Q5@dNY+Zne6WBAnGlg>a8N#u+LJXo&=hw5H=$K%)JF+E5fq@)KpeOy^YKA z^BnaH#KZ`oeldvpB~JCr9Q6)r)Ha}b9^nN9fVp2nco|_w05z4>Q129J9(;wPehqPV zEKt85M7>)ahwQg_hcpn?JAvjEgx3%N=H89)7D7V+HI>y+zs-?0^5;8vx+ep*JBa!( z9QAvgYfYft{Wj1vBD{kDF!x^&-a}{#pr*1K>Sj?7vi5V-Er`2k0`-9)>JK^Uk9pJD z2z4{i>_=!p0GRtjgpU#00;s91hWdyo0QILF^=F8?(}B7pi25W)eTwUc)6}RVK=UcW zX9xgupF}u?a5{jR$`We#0w6jQMD!)6P7%XV+d1k}WVydYP!P@mjB93s*XTMAp>+4_ z?e6nPx-XGomian}>>?Mg%N$u37nUAUxd3oqM7WI5h0ueLhvEIj9MVvIyp31I*AVX) zH{!_*596sfpy&onO??2riqMb140gBPV6VL&Yp7O9#4Oh;Nd~lEM23Wb0=0jI=ce9d zs7yB*2G4aX(eMzI)vRJGCSQ7WTam=AV#?DRX#;k*4X^)}MAg`ts&c!IHu4l}?0hn7fItPln*k%rUjz!E7EukHolXfXop2KTSH&7$WfsT&52`yqrG2>*%@ zuvMu}zxDq(n93an6bY=Wr^d4G4p?-p(s08=ct*h9X_X?ekah#i4a3Sk9Bha{h(wb~ z3D%9p0u046HQdCx6ujoVm!YU`etO@Va}Odxyh zyl`3q%?Q>t(?AZFBu?*aw4buvGZB&yW&?~%Il)s6Y7+Y72k<5b@uqXUnVkB0gcn)v zbc9TVc>%lxui?e&_y_Q21@SK6co!OIYF$Klk>y^1un=KU058E4UKnWH?23kUgWenk z3r;V<=K@R69fLsjAFXN#E(7j`Xr|@+fyqqda5RHza8Nktd_TVFz$XUq%>r!5AUHoh zo=^Ay%<&Q206u9jzFgpw0{E5!4tB1;UMWBy&nJ8U=J*IMfKOXv(Q=(+Uro|ui7X<3 zyL^>wg7oUiZ{yJ2T^E8EQNaA`&@D8#*IDo?`SZ+&xR;FPOIk)5Y-2)gn-2n_4L+Iuh9_JV=H>O+k(>-){^XBj}BmK2%YJupMh;B@W ze!6L5tV~Dt5V|SVD{>5@f*1mH4`ASQ2QUn!n`3D1CEi8em~P2W_kbUhm)Y9u{T$u2 zgpmj8ZC%|WR=>(X;!|R9pM=!i7nHgO$Qr?``+XOvPtUpX*Ox&h{1i|`1-qX?x44iqI5%DoCNC3snim-=#Ota1bGG2976 zk8z}rbENB0J607mosa?d7)m{kupVIpO8+qisE|>+m^_KKcK!_GUPVgfb_JEAsn&$~ z_4X!6<`iCJAaQ}cHk!}9k3**iOc9P%Gao7f9H>Bz8V8<4Ov~KNiLK_u)^HANAsY|? z?#&3*2sH>>5CZ0-J8Bq~D?tp~i1~PRZ$lCv@{~u+_r~^Vqusp{DdKE!>UnVLc>_Z$ zG?}8FPl0|}X`rzf2VT8E=w1Lk*@3lZVeP{4PqyKU{xq+ti{MEp2)>r!YXk60XAt~L z1piV1zT-QBHv#@cFe}tw1@-qjK8L!MPryR|0uIXLTk4 zY6uf*y(nNFt(SyB&VO#%4jt^*8M^KgP}~r#xsl`$ukOY`&6LL}zKt{0fJ*apeqBqt zIIZ^QaHXS@5=jFiVW#zwVSeR|gi^!@z)JmUmQuO;4j`i8JOmLE{D>$kfG7wy1d$(t zv_EOtp@@*+M?_fxL_x42i2M-L2Z#fsM2l0rer2#IkyMD4lCQ)GJMf}|0MJUeMI;4mnf?A^$ z@wb~7q4@WU_#tl0nY0YDOUYn^B}-C3n6GDt2=6EiNk1TB1V~h04=4dp3<4s?+*`oM zzXbW%L^6t3cT<3ml*jq_#3kaR5q!iJ!|i&Q6YqaVnu_O=y5^DKI`J{GiUM1bvbeyG z{SW$(b3!lzf1_o_p!P> zR)5IQDt&0MYWqv|9~;ziD_`>7$Ls~}j}3OUTyh^m4}XXri;4wz_$G1)^FCvQEyjDS zRcZs?c-&Bz$?8vn>P&bYp7nQD4!YxVt8@xs3uW5trqM_~-n1|`C{<5L=4u*~XE<;_HVS>oaO%MoT@ns@436SRv@u*Yl za-47wL~O?K^8-V6{naF3`bfaGoVLKzB)@~zeG1wWNS?61sQZk+V7c-HUnh3pb*4Yh ze5md?e+}gF#mq2J(gSnG`!Qq#rz;2-nnuZehI^QIy10t4T)ti{Y5A?z_x zA(;B{B3b%Z4Pcv2x(bWrDjcVbt=BD}9aK!_^9KL;IFuq?psaz_ zl|~(YOL=X zb`%Y?F0e4=uuyl>P@l=Xzl2@XV=w{NGdhZq1s{uD*pdXU<+SOA~rrmQwP zFBkx$kr#wn;2*c&EBz0e^bZ9)M5=v?(lO&6Uy5U^G;qsBg{YGG@k{^ z5}!M?;3NlY)FiFfmV6gf_A`|SCAu{vB4E9dw^ZsWa zXwpU~$==t7dc6c4J}{T@S6Pgg^isVj5peMEgTLHn?bHIDfPlIZBpiZ(^9@*r)A_0) zinn&FNWL`esz4Rp58Z(tC}7&2hd2Nh1H5BVXvTgju%$TsG^Q-`kd z&&bu-PlM-QO2n=)PFhuy)0eSrs1iBga13+rfGKB^{L>*t_z60S@a0YQbi!bfMliPp zY5p^KjJBb+aO7Lr2Z&p#3hUr1a8i$vw>kjzD;%z}5tMBx)C|nu+APjp_6cxc&O}H; zn2nH(kcyCwK&J-hA!H%sAS^&wh_DD@2^P}NSol$u+9ZBvWVnP?=5Wxg?M^IUuv6yq zoZv=9&|G@fuxAFV{WlzpYt6Y##)HH~z}y5){xbvYVp__Soaf6G@^qCbJjzqg4n$86 zL{APxJ8+`QkI;VxtmFdE{nX^$E5P z0Mh<~Wl2t(j9LQerMd-X5BED*15P+1*qbQ=yorMk07DZXb^rH}Al>Xi&Vifee0bBG zw(sSDB;fi~OCQ>V+naek9p5Vxpy1?9b57ql=LSvBCdb)X4(@L{q`#85qfp|f{zP>5 zP?m)XWcgyo^UqD^huLc_m|nsCGTj2?;ZP`YT_qp;0WJ;zl!JpgAK!o&x87fGi6$^T z*U@!rU^$-y*$Q+juz}FCoLF-I4eoGo>h>FT_)@Bfgfs`@7+6CNZQvAup!qW&JdK4P z7wtnNYdNP80wRUADlc>ube6P0kj%t57NWfYumu6dZd}75wB~wrcMt**2zb@k*TaVu z3G6de^ix#B#~6=k95YeTL1Lr{F)f#ejsz}3yaz^7jl6w)6mwkK7_|(>1*zx;BxQqU zoM5C)3-YHR1BcqQp~}NbN&5#lg^n+*oecx#$y9CUbpbwCa}AdrY@Bui67}Ik0+HbvKy1h1S7#(oOG`@F+*zJ=$fJ1|n z+qN{}prm}<|LUQmlKj-_lc3;J?F1a1SfvAi%`mfTyXlA>Ci&X2*lP>UQu~98diNr< zH#*Qtig1>U153sYW(nnJEEyjFrU+*VML0_U^p9AAW=&f|7jbV7nxcX|K`<#8l^I!-n3y-ZM@$$Rf7 zeyTqPn(L{&IsF|$`sW3!&LJZRukM^c)s)Ap-l%gkf!2(*hlg%OpoV`W50qa!5+kC1 zf#ix@D5*&l7D&KIqiE^K#k3c^nYsVD7AV0MEDE;3PRzrr+a72EzzLagsfG{ z!yOWL9ze+K^VTviELTvw_i+|&l=7*$fsw3<<(3ZY@1Qkg#S=H&s-_)ke~XQCw`l%n zdLYsqkf7y3`imquNAc<|3eZn^oPO>@1Cg78<7$;3 zFF*L5BCVt?2Ma;Czn6m*#jTVR4=8)>i`2({@eY8Zj5a871p6)WW%5-6AC&j`>v2=$ zhJ*3}KSZna5DiMax*tLkA0w276U7~=i#O$H^^@{b1P+F*r`l1Sjy{b-;< z%HtJ&uA>TXJ}66rNXiKbUftyZB$UUI)N&-6N_`AeKPJ&W;UBPH32?~L_a+Z*C_Ij# zu%6G2nq;X->oF)Eziv=G%-rK{AXGQ!z`&r`5G4LdA|9{qCj-P&9v3RQ$)asa9fgPN zMg#mt5?|X!(tfk{q0xgU$3bIhyMI>HT71Lda`TpD28YX2!Io9iaKWp)I?yu8qn6Rh zhk)Z5gXYQLput!ebhV4*c)jM>L8mRp3iL%r9>rR|&U^_^8aq(~Ggx~k<}mqaaBhA% zI8kHP-i4Aiv}&yZNjUs%uMNfWIcTIb1c{*KE(^nQSF&Mygy;{fwd< zJH=#iLJX^~2JWo{zI8BoWje0H`TZxp z#Uts8MDTtDKh(mV1YFs`uHxQWKF~=QlFia8J&Pj*?q|VgTHeFHtBr*;)GmfoJlN6& zgKp9dW_K*e8@NeFjNS$X?6u;49Gdw)F3f#{t(vE%=<1)BP@s}|Ap2y)_zr*SUiTFL>wlgzV!DUy;MSgv+ZUL)* zS;B#CCTKh+R|>zy=YAdxet|j=O_VbsM8P9WenxlH)k-PSBEpVM74zY`m+-oT!NbLm zLj@h#yltWI0L}&(d#J6NIPv|I@7G1Q`|4hn>KCxOZGKgY9&iMOhDhGwC;59c(fz^{ z>!JV=ZJeP*m7hrMR2Ff3CviML)t~7ddn-G5wL1qid{nU7K@NVuy^Zq2gA;)NR*!AK zv1QXHrOn_gobBy@qrb|1J=mzB%ZiqvNpufpGt+ zIVwRw{kUH_lS@9i*M{7XETLod+V#lb(4b;Sqoek?KnY@(!C!~7VEkCU|4JpH9a4}o zwNv|adjCO$he_%%Q4i!i>d$#}NDiI&UyDGWj||Cq$e%->Lvys-0a`tj6WH|(+#Aqp zemh80ptP1k)UEZ`G>|eFW)M>cE@t5#m_8^JICU__rwI^JXmu6Zx9o77w%e0+zWeiF#!N+&>M;g@@_6B{>-6bLS6q_0AoNFsDB?B zoDoQ%t|KveJy1gHOEpQ8kwF=RibN5DtMxG@P|;ul6&V6S346M0^IJ&gAAm;REy??|%ZoXe@%k3*+>pvtvae$K zx^k1JT%YgW1!cV(;VsGTD$s68d;2h@U=x|Ny4*p!mJ1+ZikirW=Wo6wZ4VF0Q zD(mhcDgQUfU|T&iVBT^?^Eqq5 zO=m3Q4Q4D-G*|eIeZy51GtGm1Zo5WN3{~n5KLjaligGB@4*u-G)!Ly`9@k|Jnl1}m zD?@3zD@L40PC$uiS|FeekfF_3lyDzHp!0$mgC`%-UT+6V1Tq3563igU2uzDacw?Xh z^)e+8z@Utq_4a*1ljR2{n*ckoBR2S83vG}MI_H8{&LQxSi~`NBg>rj!kU+h|1fIj}C~cdLA6tPK$;akGN?-us3m?dm!DNdsf*2+@!*J2gtB?u6ze2)*6C3w)KmmXc zFd-5K5`YB&lOZH>Lue2~zHj7cZ1=b49j(VX_Du9OzZXcXMTXX|fsqy9eK3RY1bT@+ zgBgK=9q1*02I`;ww0$1~mR4QLqrH0eUsvG|=xMtTqXYn|tN+je5C6QfBB@AJQ{=6@A~}LoPAN z1yV?tGc@FgV_2g}2?GF}B+XgKOlYDa)wz|0(F- z^yzkkRPf=e`2~tK!_loI8B08%%v;6qEKvH=iyddW#PL8?&t4au!@I;-%-A;A4E=u!5r!j%Df&MO z4~lOHzZb4#?Gpb}R2SJi`OETLN%xuMRXX6QaGJZs5Qn7X3yN8%JTBm_teLTuD~i1= z89C0ea1a4bbl8X&A$Db+uBcQ*`!YRV_dI1HE1R&#$P!PqA8+qyKXIm~B30)cfhL%6 zF1qpv^BK*@%^gl%riY48fb%8s2(NaUQpdVPokNPxvQ!>tLY58Cy}umo{nb1<%OWqg z5|qRAoSY>oU+JFf@sMbsM5Q^z{)%-1lL8kZ?!bSn~1RCDTl#(d@9)#}XQ#i^lAsU>ST`ad=HyNcz* znWr%`tuZ4%T6O5Q9+!_6gOU1_eKExz`?5lzC$XvBd+Cib-L5q&iahzxDVDpRE1g~J zd0p=qV?OfOG^f6Ea-~-XIe`>8#<&()T+6NUA}%HvDJDBfNvm4anuYIM@_1)wE-S2R zM@JC7&g|E1PJ>;U)L&%ZkKqL6v9dIW2D3qzW$_K`YBo7TDzhxQ%H>w}n1_U-t6Ara zk`Kop=)8qktQ4!h;jil)O}bU4%H<)<_hestyQI{7e!L;kB%Xv|1! z%#4;|KDsT2`XcclD9Cgef#rS0z^WG6I!D^`^PXM~{h@2j1P_eLrv8=1MHtKd_QrH1 zbQO76Ap~IVnb18ItV=4lz|*#e=_v;Nvq#64<=fwHwYMmvxCJD$<=6onur* zdO1F9^XXOZFwv{tp_?yXQ6A8hhSRIbSyZX&ocT(&&i5$3xxo>}%Q($xIM-(PcBXry zGH{>E8Tl@%IM|p9iWY5GM(gCymD_Y>`G9gxCG5<3JJmnlVK%Fd(emf__-cK3X%&Q2 zs(Pnkua5buycO4g%<;NL$Z+p}rxSwnO8vZ2Ls}kle(l|!UbJ#W)zQ3Vf;BJnZM9KV z`q&EBA_+r6=#`OH@Ls#+(kms!u2nkfng-SgeP7f3dWB>1_)1fjp+Q%9DE1wmTDq2# zUWe|szojgCWw>>M4723XRWO=6e{s^%I>6ZC ztK!Ptj$1M-t}JzoY!EB1X_*J$_pS~d$ zR)Af-$Jc|x3siv4SA7P=Q&p;fl|W0$VPJS?q@M_fmDgY@*{zIL&-8gpyuY7cP?6po z&YV$@`FV+u#m}$#F28pw9AnSM)!;YEN7qU`dS=z<7swbpFrmycCK}9I(a?!@`2=af zqQtH$Em2T@d8anGN{T%RO=%4(?=cLVQ7{H-)|f(0I1I@pCg%on^*?37m*bU4l^daS z1n>Dd7yz!NCS0CyPx^ivtZCI$B@fL6E{;9>4 z8={QNDNS(YO07o5x^joj(qrS{dQ$!qI_&iq24u1w=Tpt z(Yt<*`G_Oho(C&)UiUhSYm}}q#C&9Bkt0MfLU#bkx)aKycYvaVuQZplW}ait&l}g`UdK*qP(2ivjXeiy!8v7U6=BG2&Y}R}VWPzQ+2o<>;3}%ho3Ob7+pt z_S5xLJ#5d_&qzlt&q)7c5#rh{>a?R4@9$?P9!!%z1WmL8C^02A&B@g}l*xsrb)8+u z+t0M05su5bwoXyVwJsnlns*8~FE7=1=7UR_&hd#|u60t?Iz8ShFi>aNm0M=4=uA>} zPxsxa)ZL+!O!Q4sUeAIfErWv$Z}+x)^D)nSHibH*M?O$Ix80lAv^7_M=?z*|=vjgM zznE!bQldW%cXr9gV3(~CUwk2Y>xnmGN<7lm<~Z3pjv3|s@%u`rO$}kClTxI2!%J^} zMp|orMta1Y*aR}wq))>O3YXdA|K^(ZX}BtV7OqZPYet55)@(eLIdk?(&w6*x-jseJ zoIPJQ!)>Zi)Y%vfo7Cqz$GJl)ly7H4At+0k)8uB)E1k1_mObtqlyeWOQ2uAO+gzc1 zGCQ$JS*E^+d(kEa@^;54;d9P(xh)k+<7}uB#l#wtm3~C^H7&~DX4f=(PtGogwxgC? z)U798NIW4QUGXM5Eiz?aSy+mv)V%#or5-t>JDV?MDB+C!3){t~`_rYq@D ztPx7h%0sNe`boIWSaSH>3)zdsbI;&WAk|fdZ$6zCqA&eMj{ZwHgrUCFEYG4sc5_RS zBLd9c+7u^eO&cb)h0C)5;QLY;HAnez5+wee+UC0#i>1HE(nz*+8sA)|Myxo~dss2E zinve0eYN)s$IYHXX@B&i<~Vud=s0<)HU5)mBfcjhj4|rHOK-T+$AxW){vcdQz*>*2 zco;WwjtRn&;wc-eMsBreZ}Nv~04{y2CW%8-`>piaw$}#kKn5<`(I4xcc7L*4m$17mJ<(E*USDnUycq_u90? zlEZFh`RF!xXhr>naHTEDw~z4CQnYMR*@UX&Xsn`6!eoEu;fxY)eKbzFG_k5LUkPRMMbK5mSJ}8icIwUN-h2T} zc(qLTinnBv_nVoC1H~~I!R3v5Icz(Wc1l<|Vku3RUUN!FNeSEjk{Q?Ym5GzKE8{2q z&X19Z&XHicg`9Q%!LB?pvFi9U#%%$9(fAJQG^^|MJ`-=Aq|Q?QKCx(E^(9qiFozA* z$upIYC%%wymJeHR;Y_VjupP`NHeA5h_`zZD<~Dhz9NL+^@Rw4`v5o0ITKwK4T!}xl zT|O3nV8hhi(ZYwQ0PIQR$*`6r^TG8OVA?FKYG1E7 z4KPT2qh(|KzKte@O>`%P`9e~fpU}%~O31|Rv~DyXS-;R}RLd`X^Kv)-#T0crYT+>{0Mv1v&ZdG5qH`;jV4Y_Z-(c_)L2`P|# zqc}mFnditpD^h1|S2oF6nw~WaWPSGg3JwOov&W=HN5qt#^WMuRMd&D34&Dez0#%$urWY;j0OX&$*w{ zocjr%a}NeUZ!+h~S0P{8{9vT=%E!q{b5yC}Ts8X0d&dm(Nc!7_zpD{iJil=7k%;$) zv;4woYWSMsrAwXPtts}jNTaZF<8-lE5H@i;XY*KI8+IVNW~EXR}_ z=|3j0x$R2Z*^tvOqZ_z)(+|F9H#`$mr+bLW`%M;;vQ9P_6! zd1H&mP8@S3>q?frP-zfbvaM=MHa4EKhcmXO$Wzm4U#9$4tbAfP*{lyuuQt5*nMHY+ zDRcD%CV$>9tv4%kGg|Ngz3d2AeyQ)@@5n-!UvXZqIK&mS)D@j@8Axy$q$M6Ks=3!% zb233~j8S%f+bLi?(?8W)jtM$rCUD<_}Y@Jd07hP#+WuKw+Ol6;G z-TzkKt4lmm`Gok$I@7$0Cv*mfZk{q-S316?t>WCB6)=GJww-Z?D^YMo)SOmE>e8m& z<$S%r8GgsIy6Ss{#HJQd=E^p;W``(3YqmuZT-lMX?6Jx@R%USadYc)FonmSEdCpN4 zi=*^tNUYNUXvUOQR@&j6KP}C^6g7Ot%7k}qcew7A0A}*NpO&BHG$~EYb+5(uc3Rea zy!;U_Cf}=R`SYC?J`;0vgmb1u72s3@p zrQJQx8KLB})FsXuP+@aEg>;+uF*PlJuG6L@v()(j{Yg~6?{D6R)U<57vj+g8w@?MN z$vIZeHl<`+)(LX9Wsj=9i-SdHXtcT(OP<}Sv5qUL!yMhIQyihGKPuIWr?J=(mxMI+ zda!y?PN}7LEKxet@5*AQ%t#%!Zq$qIXG(|5Fv?3U2qxiZsVKCS{jWD^YOyE!0~ls< z?vxnVlC$M|BcUfk-(~tVRdrw*LmTC0pgcOSbG&PDgq%GV79URd>A`a(|7BjM(Y1KA z$9r*#oE@QjHqU$aR6E5!oF`|GR$As2ksmbH8J2iL8OPugPG|G)kv$&xLOXQmsRfz% zl#ds!#H{ys_(1?H!n2K)pi@%bW2GS;xW&Gy$ujluNUx_l%LF;KdA!z4gLJKzEGutQZHoSQ{I<&#G~9#xv|a=IGr7ahOx?rtY*S8%yeI6tWfe*KEjkaw|kfA z$oX2RB>+#qjbVVdO4vFWC$g5c{);Po9^RIS>t@J>6Hr7l~exHu7 zr0#D?jV+B-d}{K=Qfoz@>KKMqc{C#sPyffM!<-{HqPB)u<*3@#Vs!R24D-MNJ4*SB zn$U$jcp$N~_`TW?3Ms7cq=vw4ksVdxc$rY}vd??q50m>pF01Nq@m1QDVXUa;Ct=)I z4O{>HWD5jHsBFE}Be&w>eY@g;BDz8<=6(C{iYtb4JvIP3)!XdwhQeC29R4%llZ&+za!?exW z>Mq~jr3=seJMzoLTx9}#ZkPPYn)pv@T8t$ti#^`ClOQU+h0ESwHGGemU3|;&)rvK~ zTA+4{OPBg$K#p^4oGc#vT?z>N7!)b%rle)^GPo3^5d|h(d z?iPCIZtC5{*46Lc1x0jeji+%8HrxzzlReAO<^s99F_%1@CGbifoCsT80xv5w45jfE z>&0Z}g!gT?SdDQNY0NRU0KZ?bTZy~#Er&tqE%BW@bFl=*R!EaipU?!mWQE5V$|jD< z)E!`KM_5=a?l`hArkB<(7Mo(O#!QcmTU)Sp!`k|_U&e;%#EGmzXJYtz^8aCpx<`t4 z7Qa=zr?|QJqvFqszbsB#b^oegtZMrCuUECM`gYaW)tRfEtIZ{4CEt}K-+$qL6TUBd zOod*nCKF$d9H{2Z_p7l7s`)v4Q2*Hft7@L~R}&J(t0~d39fBznw@h)CQ$I6iv3S&9 z&EnW+W2rR;ojzF2UbaRzxSCI5$F3UMno+CMS3io@P&I$N?LWrdsG8ybd^HoUt0rRg zmfO2;AE;&+zKaq}qt=9FR8VWsH<9R@<3GT(>sxd4z6mgGP&Gn^pJ~Ihz_fvC-sMaS z`$4Atqt+1pqetVYF+Lf6;&5wP#@<6wK4I^jw{}VUEs>6&_#!A0;aIiz!RX5L4=kDM zguNd{cS#_zwjK|G4|7c2`{Jzua22=+d<=o75qP^o z1*YgC7!~p9dOZ#5rYjT2IHon2HeIogabPfbDy~d)>i=k`9DBSYf`5*2SQ?BR(geS@ zSe^bPYTU8Wc5gvpBm2k(4_O70;+QMyL%NRL`q6xH-7$^r-cyeD4#ts40sw&GGtHHuZESWhRIo%im?LWA+^ zc@4X|TXf8+Z#(dHT#aSi;e^A^J3>^)ZP?ASEzuIY+CvUHhc9(r@V}4yB;gY%=YuPM z2+8e)Am=&+{`Ua{l5$^K+XrRBlE)V_Tfbho z+k?in{XMBVz0!=WD_Ddl)S8~7O_g7n%0uI-C*;=r9h;xu^Rn>N$sj!}v+a ztND@;V>y$%vSxe^YI`X6=mA_62RYM~D)k-J9t0`PU0DQgMX7qUY4honkIGD1fmtX3 z@O59>$6X9y%grrP7OI|Bso1tKrd3*PTZ-ozTQQzLvem>KHse<-qAT>LF;nQ&Oy~#t zRjtn^EEeyWbkC$;O?qR}SCfWMPMCc1XFB|9Qeb)o%#e9+$B*Cmam$bY`eWtC=YQPs z;M|{9{#5rs(gUj=c<+Iql$d@p=O+h$Vi_K+CQMbS8caw-tBFQ6zn%nR;ilF6;|VEr zM9?NtwbuBnnZsLCkJj9-Rb$d+;`hY(FNl#%l05mpf;LJ1_$VYAt6wOYf&!y_UhMvopH6%`d58#{jd_=yuIPMtb6At7P*?AaVX z!^SdVSjKdQ!uUTugT-Vp8xRf77?gVBjF^~=J7O}X$7IZk$;hDpvod0)XT;o*F*IXP z>Wwp|PR+P;YDUu3jO3{q8T3CnV`@^y)H^eVW(-QbaRy^iMiymdQ6pJY6#X9=#jH`x z7&SCwQ0k2{tk$S-Yt%?<)M#r|6#X9^WgQu14UZa{F(~!M84(dtkr7d&BcjGeL`Bj6 zu~8ADqaq@shGq;(y>SM9qfTTt6SG;E%|`z%HfFLh{L;aYj6tb4&M=v5Atsx}WV4!V zHu`V1nJhL_h;3-bpwt^@n9Vke*%oTHg_&(O`X6R9huX{*+t7?bsW;Bxa(;uy-e<>? zIc0NXcS(&n#gdL6oUqz)UE@J(0}cf|WW{bx_}*?mP%Ht7nwj<4+#b)~k_lRABW0MJ zf7i?`d$c1GCRM7#1{2EWH@w6jFsD*sPNfbprv_}P{&cdX>NLAaDa|DF#9<=iWnE}Z z54Sqf{Z@x$HSS?L-*kLdr2omn9OD{nd-3}}M-mSXSXS?TFrh&|@VW>2C*J*RLc{QZ zG-J>{yG4zxvz3ml>EC=|^ZCt(8n_v#S!i&lT+yu?({-YvGt6hGxDvK5w7a=UbsGPx zsmZA)Gpn*!ggfnL^W=TxH^OG_(LYE>&j`)+rB;}UR@Fl`7dekgtu@9NPxOvoSSenO zIHEG5@QZll==Ik2)-o}!`ax#y`lTdX%0BarM@ZUW%@)p``QrL~qi_kyUk3rsdgP>v zAOd7v_Qb{C79St~Z$FE>@yMP`UH7-z59bLUaOSk!R=3#-_H3~Vk=0gL zZA8zN9Jl;?0l(70wL z_Dk=Tk5jz^rFkJ|_&tu0+!WX`<=-r&Jz(EdSNdaC!T@`g($|G)*oSfKxjx@gKk6i) zUP*vtoUBGeTBRS`KAYGo{ls=UFu`n^s98NL8rzt6J}g(+`TvD(3GQ7 zPW~+Lhk1ErU|xQeJ+1q8ip_bebEY%Lx!QU2dHG+R+Pq8)p4eVT+}?>(XwkEP4_p>Y z1NXHxG;m+rh-FY)Q(IH4wAIE}KiaB?uV&ybnw;_v)+jhEcn*h6DJTH}Za<^NNSK8- zd0)Fie?prp5-zj98UIvqSIIF+~;l16Gz<6Z;j zUc<&7oP(j2qEp|0pvTjY36^!w@hIP>I3ge=$4afUvFIu_)pRtPl~Z$iJov$<+nW>H z+7I9_$S>~>eYBfh-P*AUR}hyjau#h$&trENIPHxWwszEAgR@(8WTmlwM~0g<5fpaJ zcR8nQO2_wC&2^05ly0L^GdBD(2b+ecIJ zJ9c5Sr?pDIvhAF9nCZlTCFT}jAEC$a%U|!H2h1}PXDpi$x3+i&d51>bDd=HWHsL42 z@ZIjz16yWTg7Vul<7RecKz{Z3sWHtvgpTIb_&YkrUybI38fw0hcc^*BKhx1H1z&8N zL4zgQaSP2=k&cL({i3*6nTC$HE4GxP`i(ZSfI7PAD^9`qCZL0@zF$k~wnfq>}?4ujLP?aIm7+m(*lPQ!K_Xt>qR+vzkXo~Vg_%8a%) zB^Cc!H|}%9-bpGBQJs@8V|ID)P2ClT;W2p1tntNK;~5|J;%{p{Cw~IY_nDRIq!#HJ z8{-azR%xm2FVkD4Wwz7P4>M)^n8c>~&F1K>9Xo7dOtSQ6i$mJ^XN%G}rgN#XWVRf= z5tysZHPIVvuISgSa%7p!VQG;zn{`;9Vqb5&GHJGB7*FWxX!~o$-*w99*$!jBRAt^) zb5XI(c1RSjxq!V3nQfng^3Je1(rZ@7zgDDI!HcU;JYvryBSm>6X=PE3b(5`T+LJcr z&$CKbR~^gGtBKxZQzL(4Te%`Ra;t4!T5=Mev+{NT%nGny+)|Og*~}cH;8;qQs?83o z@*vLW{MM$-g~jnZ8%}NhyG_}9m-5A3J-*Po*Q`p(tg2&#u-9|$cQ{9b3*c{u-F_E- zgv|ElU0bEM!uP6b<3i;gvFtQHvc%G3b!%;ze-Mufhh5e!IG$Di ztQp@TVBTh~f6-k3g4yWF=O=#L=Mt1e!C34mz+Hs6ly%lp2~S&TXk7fd(L6r3RF_%e zdCnc4sDua*!jx}I4JnuKm2%i;G2=^QorZIN0&zH6PWuy*au<$nI48bWV^*?R**MuD zZtCA=c3#BYfwBl3@7Sx4Z|?3;n0^~Jw#xL0&@a*M8~h>nF+o;Q)7`~a>nq~uEFp~|Za zg>kAPrLEL}0>1UnB_7Ftx}-;0qRN+wvG+1f&XwpS2*8#ppRyIj${HqT0l8kz5?#5X zYbml%DXSUINsGR;c)af2W>yx%m4w?9#W+TWZ*%fZ$X|x@M?$V7uhz$Vh>yP6!X*_cwr3K(~R_WK>!kK)ka=pZyVU_Dec)a8bu!m>|$os#xg6Ndv zWrk})htRf%*|g&e@%xY_#vhAs-+E$eGj{4k;h4~_-Gfc+B6#`RCGB6z{VB(t2G`8o z+`BKmir#+zD$WCL>8RLmOOc*8uhVH8Oz-xq@sVq7kiB!tJt{VdQ<|~q+N=WzOuPev zb*-)LVVkmkm25kEJ1 zw4>r|4CH!W{qyD(kX|`@i@EzK4)Eb39X7ln!kPf0>ksxT%bD!Z0~<~lf&1H3`4n|Y zsgx24qD2``Nb+&Vn7S?Ic#m?bUv9?7C$uV1RV4?%@Z=Xuh`8KFME=2Y^qdD`5sxN$ z3VdP#`lRqT{@!wgsegqeeM@pOuD{r);G|rE&w#VMTg_;O9%rnvb=MC!*m{wim^e6h zDW$LM8(o%Ow@CE;9pDgMoV~#TDcs30DN#&`QhlM_9T*ec$8k7KxlL87`;{<$&PQp# ziepCtMyzKU7)2wV7$ZlbF>P{lfLw5iv;;9KKKiY2p#xeXnw1&xF^ZMi1fwV-~{|QE;`M3Wdx;n&qUI%(94k9g}E3e0)mBx>&~uSJeD?Pt_^ml1p?UA6Fnfz2Oo7 zPw}W$DDq{qatkXPEhp8Qt0G@AW1y6oQxI7vy727vaJ1tx9aH6gp=W^{+O<#T7&g~2 zPS_{(`YyOcoP3PJ;;UQk5$>vN5h}mzPq|orNdBzXLwTh}s@nStRsM`5mkuf!sVX@N z-c4r&hVWE9OKEZD3bVArzAxnd`Hnc@k_K@(2r&WMwR%QcBCK+sK&PsFC*!*v!V=eA zuJo78kRLwyria=K>T1m?7aWl|Mk=eyhdN~>^V#BC77!b1&B{7eaqD`(wESg6%?t4@ z%1K>HN9jDR#!wy9N`RR2oUtFrTWihj-pa3W+)U`XcInltItfjPe#tC!xbT#%FPq~J z-|Y%Bu7PG%B2}eOr&Q}yv>Z2Oo&n1}Slh%`rtiRX6km|9+}1BA?J&y`uE-tcXGGV! zeiu|tNPJx_>r>`H z)qAR>9cGuYHCI&CBEn>+BZ}Y39{CWI`~nrH`Gj?PTnO7Pw+GM`@hb71T#A76n;tqR zdFQeMY?a2PfWM`;OSN5u|bo5C7QdjC48XVL`G2~4*R9?ZC&*5a{J)V3ys_Ir(6m)zQ z?l6M=tMn<+pnh0N^vmWl0eymrWk|}#{$h^{bhLt1_2~W{61(_r$X_c?UNg2Q0*4F2 zTV=7_BEdc3aE6l++W#h z6ndz2)Jm=~T8u3s2F+{c76T45U2|pWh|Qd*NjuGSjV_rVSBZCA^L;C4U6cFcdn(dI zM@SI&SFWsU@fT1~xBx{lkur_(Ph69`efwNVuaJ7dlkQm>Q5+CbuSe+cjdhHU_oPXz z^iuq#7Ew2VL%77pEb6&Jl;kEh$%ml|-oU`Cj&QBI7JmtMtAtAxI00g)UZnbPUhGQz zC1@6p@-s%#O)5zC)4mMX&4T2A4V;c^)TBFeH))_*K`?r{9G;4RJbL2&!D)%^c(8 zRlT}g8ZRcmD|yVfSYFiK?k$jKVa}S5b2=ryl!{cnbCSI33ZRBT54y6h02b8Ar{ZzH zN~rXj$giY4a#g!?uYeLa+TZS#Gh7B&wjLA0 znafaK+WD2jZQEf-UBay>M;2yST!dfL-T4iG*Y|>l7k_Vbp3&Xg53@72a&5OBhorlg zE=9(rS26r>RW2k^tyi=i^ISL2oIzi zJ&9dVDqrh`nM>@2exaI^4GE!hJ2+$r9kS3UqxVV3%;<&eYbg@+!Pua5TjW+DTlX8} zJ9NJxInkyb`P&>9>L$74GA-hy{OjhKDV1Ntli3(XJAfruv2ir!sy3G}<-=`WXJc zkd$u$g5Om6xaaN?kDa)uOuLGcGQL~omUwBWnTAN1HbAIle4r_#t`UceJj)96WDH$u zZVD0{BLoM@wezAJxznsza6D!wt}E>{uf*j~^bO>H{k7bbi;n4lQ=aOlk@4-*i*)f2 zu-5o}O4>C{3l^GpyE~R(L{hbe?hfBaOXWlHpU4+oi+Ivvozat*PbzJv856;4=5k9- z1cX*?r#}9w1RB0j$?T^bScm{4ki07(2DQgZ>Bd!?JR^uC+n-fJWhoI5-kc*B6`*d%GLX8wFmVVBn`&{xfg?Obu; z!|X|kz4pXztAe=M6MLP+3b!xu*Dt2v_sti{`S&lzCC*AD*Mpy5i|((#_`!=7@odoD zW8AOv7#;W?(3vU<=v`_|*@vZ?8u`^U-yE^TaxX^#!DFjTZ9UHIyVNjF-mRwMJk|-O zHeuZM-4L%*k1&Qc?p8w^(E^?zK~SZb+6aO$)XK;CSuUr3B_UTJ!sM4q3JrVIWFp^1 z?@>#wlMrWqT*!(U0!-q;Th=<(jho#{({^t_@8!AgUHr3Aq zlZ7^q;X9phjp>)%=chj{3?uhYT06Wq_7$D)K5aS3^usJv_`9hsF3$>+e`Ih^+_;=5 z?+quXOjg9vEA;qPSX7hgIOVDlaq7IFw1u*YjgEJxr}ThV;lt_t4-?QLy3C&WZ+tpq z-;AR(KA!Ox{5H;Lo*~TeOAJXQlf@M7RGFLtl$k)FGcY7yDUI_hR#~o#vV>vZSk5_B z1uaAmpM?!_;(X0YOBl*0aK#8WU9NM2GO&k#QH4!Og$wxU;h$4scB|N0u9GXndlI%I zh%FwlQ41XtU9HQJ$3&mjIqgIza^15+>OSt8F(Dpo6wHgC9Vocv*5 z>nYdi30;O0)?|g>L`&pO5~0bA4du8C{Z{{d>th! zM~T`|;^!#QI!g48l0ZjEkfUUTqhzF`WVEB?2}g;^Q4-}SdDc<#oTDV#QS!W_WQwEY z1xHDYqa@Z*GSyKM?I!clpCCQGGnU0cKj*^!gC9@qRa~vge9VH$| z2?$))3(w%-vtD=#2gQ0}JaPPsFoqm93Bz!ZY!b9M$TkTI9ORn>>Kh7pSLxcb!VK3- z1jy=|>RC$4QTV%f01yPiFNYnHtjvcJg7>Kf)jm2FI7N$epHIe;k6~K*K6>wRFoh||3^yP?BBt$c(|1h3Tq^Gk`Ky(}jF zj{hbLI^lItKLQ8GmrQb|IXs*B04f9X*j*At-V>s#zz`sc67Hh2O@&?Z6BJC1$5WyPvKJ(>l{z#?DfPVf!%L`)aGiYp z#Gr(Usa$<2T{mutvrL7RP}uZzcQTccxWPTH($kQElU^suP`>9Fh3UsWse=OazpIk5 z_;gjuta7DRs^2G>W8tB@aAHlQG^%b~p<%Pe)L1Mb=S~(E1E<(9hd}){&zd<;?nNm3 zky?;|hE_eEf5(S!^oZ3$W*x&n?{O#?UhVnzFgE#-&9Xjh>2WB4?&E_!$DAJlY(zhB zP|wwk!z@;_j{ZdBkc$^vaj{+F2p2Ct#l=4~igkyJnhrE4Ij9MB;|^R*a>&WCXqF=! z$B>WFgjDQUtJ$$jvty4Yfv=os?aOd_3~gxfuj!Olv>L{1x^CBw+EiQoS`B3#wr!UN zU5-}W#(y}`qCh;mVUK1BR6`!C7UVwN{2`BUm#>HN#v5TaB5(*IDA0fY*TfuurXPGS zun?BVO*X4>ale+{1zHqmddlR`a*86hd9cpw0GHibSbS$#CO|=DkG5+B3DiV(4GVj% zIJZ@Cz)g`mduY4Hqg|`X7D_$FHV{eJ{AJF*;55EKUIa#pe`1~6C#oVV46XBzL86A$MZ?ml~#xv1o#eE_5ZznGn%H?a+>$nw@x*8v36aNvU7mIC28Af?{B& z{y18Wsh3iIGv#fvd)#FrZCXF?E?wAH8pyfxY}8p)IP zcYYeObVt6%V`xDZk(PBy9A!zzNNp})7D;g`OFT(R6(_YMqHBv0gqdhq`qTw_vhzM?1!daRYqTVZV4qNU4tBI+i`dc5lC@V}FQCu*!?&f**Ui?9G5P zOq(Q5vH}B(l+=a%S*kcP>Ri;310yK9)Sv(Fv5M)L?2RQEfHnOwBolqc8ogB$gM|q6 z0$7Rw&5pKLC)v72&l(y1j=Q>TIr<&YFytpFqT?YpFXL^|SRWBw7E7xo&-mgp-Tmm@7KQ1=Ihpn#3CToQxi&|Iv};6Oot zTzZ?Y98It!u$*y%GbWJS6pQq*a;DIUs~RGzH3tsLiY4vK6l97EkR?#!SltB>)S9|e zX_Y=JHL^y%ywLa!s&XXLA8COT#z^7h)Gy!omB!;H<)RnH*T4pcA}*AKxF`kxs0x%0 z%Bfh2qD7(hDxA)?aVQw-+PDXzI93}1d>muE5=$ifkrexi5?gI>XuJX)Ty6Lca!*{_ z#)Z2{3VqIqb-@)cIU_joi+UV&4uj1DKWIv))r})F;;lbuNackOIu>|s|3Q-@e{shT z8WIL$QsT*JZTgou!=LrDon)Dma&B9%k2+Uy-nq~`lGU0arSI#hT)IGeLnp`!w1Tb> zj+y9m!~x%a$Y&K9U|~E)#$Ld}Ir$b35mYiP^ca2szjg#^d$)_DsX~mfYUmoBWTE>= zp|)0+=>+zRj{9S-H2jitZFBdPl+=ygSC*J%vdRr$2tq%4ECs!-l*+(bIbld~m3DeN z>6FcFKFw;pY2sQ`xshN)1vURcC*Vsn(0$y8<&Lg(n%5r+NFw$|>yulZx=piWhmb~a zK?i^EUheRP>3cBW(6;nuXNtYKBE)szUUudles(9wK2rEK9*m&FWzd$WBasG}MU*%| zb*T6o6Mx=c3XK*HLDmFrG2lTP_JxkGSgqXmba!*0v%4vMVYxzEI41pD6-9dfJ6RjM z?`)7q&MyRKpt{*AWK62boL{KnE@EBJW5i}#WU{?U){B*_aqmw$B&(m)Qyf{~6Zf1s zpj86^i@PE&#!OFdx%Xo6GrJbBSBfJEGWo@)u1HTYr>TQi{S;)JMb`eFq*6ABR8yG% z#6Ecg?f3wulXz;V_W>j({%yO2E8u8N-^)EqP{ITo4veiIFt(Ep=g4J;qK-7U4upng z;avp4W+lu#E?cn_Tt*F7WLLlslZCs{>4*kXl*{j~pk*DNR@Z|2;0(aZomIBAQkTM~ zlM^RS=207ZxS*nPWn1f2?xq4pv&=OHP5J+08;6EH=4if?3DIPL*;Z8K-8*h)^W3Eku2tnBvm#iaC!pU!9N+g(Y6K)l@bx?@A{yVC_&i;AaCLF zz`aFyRF%^TJAK_ggITm=yYx9^vWD$7fejxuIs|^ei+k-0UE!h z-ExUb)bCJ%S5IY4vlBX@BP)-9BT#X@H98HNEb!NbT0-mwFaQQGS950_ausaI_Gf0r z9<~hI02RwS*9AZB0iW}YWw<$DS4&scMsKASMH<8g5uL6n-!s}Cy=3#s957c<&nA)2 zlDLJb&nC^n_t~UZ-&l~Eno6#pO)8as<#GM->rzm)d7M<*!jw{){eaT*zxq0zAd+9E zEWvTpt?~SHD()rg^L#8pzee&?DeI(cz&1k#pKO2|FJ+{7nV@?3W(n^rCA~1e-z5;;b~EmsKy{5YG#d zowx^9d7+`jU*ywUlr$zOEQ#rt0XFgMUAAh|tcJ@Q%ijsez$sxh#ZWf>C|Oka3~E;T zunwFVYyT2}!60-MeW@uRv6Jw%60~{Wp^Hn+cVG0k0v;i%=O6~#w9wtw}B07Xa1njBDP+dbhX}J3#`3xr< zWKa|!zHC25Fq=}m13Vim3H5Cxym zfp(&Kxm{?`=t*f3Ptr0LTqc&6J{mm-O9iYOfx79ShM>+B@Z!(AGx#PqS;_!UkIi|n zK|nd+9Hsa-DocDA8Gq!uGo8_1)+vcFUi2?758%}y*~e1TnSN<*3c-vvv~rzH)iZ_0 z(fR`;K$J+B(w@iObA!{ni4I93b2QY{g*n$3oo48H$h@zHv5pAFwoFA$-HA$%C&a2E zOi`sEzXN`-EqjSm)GG}rm6n(>RS~46k|mi=kyj6WCLl~6b~te85SC7={8=b@%7gWV zL8dP5LBn;v)w|j7S&Cb1;1-PHr%BiaV`>s5R868(8ah{6ljtvpOXysGZb6WJZU81U z_PIe(M_yO}gCTAKZJ(pGiC0>D?wVPm2(RS{It~5I+`C(L^Ft6 zpu~N-1seNYA8vulK367;BpS9T3?jf9FiJ*-u}nYa3;bty&>#j!H5N6m{eY5(*grT- zXd`5$Zf9D0xg)bosl}>4UH*AynM(Vu=63$~hc!Ji5Du!lAJ{787zhfK?S^btJB(~Q z*+Zpdt}td@BKS(RA*C36(IM>BwsB!qWc(FX(FeA!^t6<+C$y;P&}Kwyqbgvg!ao5^ z6)B8W$}|HWqXs;5a$+m9n9j(`cBZA4VG*Zc#*7F~H)BStJhs8wiBsTP=Rjlh1~pv% zA#DF9OBhLpw1O&@ze*_;3Dm2;kyJ&%2#T42QPmvmB7r{+4+%QrVy4}8L&{bQRQqi9 z5PsR${r1Tkud<0+RWJz;DK65URb-Bl^|XtRrF*;LBKvO`u5q3 zTV|1#Dj&RKJcJ=i+c=dd-`=_+Lq#dph#`vA!PUb_E$9n@oGY-<7jyb+dnr z>bA@oyf`(Y2=funa8z^fS-qfp>~L6fFjx;Fzv)D-pn%qz+PIF?2+vlT@XQ?S>C| z;Q(LDP;OkVGB%gC7@5^N%8KM}_A#sVltl%40vc2!9wA6oMHeLn@Eb5LR1*K1BwqUj2ePiLA-@hv^zlyLOD@~*-wUeZ3>8(jpQ?n5zJk-)lN`C{4RtRaI_&NwL4i6I;2dDxo zq8_bTOArRJ^xN7T7%y$xrGJt(18E_;y`wdI-6W#hW9wD|#>fEMTNSX1WikmMeGVUr zKcpjwK^8K=YTwiX6*@W0)peo%)Kj%NchOaUp(AY8-Z)aJaMXLsUHnGMv`VCxgl}mA z4fJW>xFrg)fOZs2EcUb)`^+YO;qX;Bfun?Xy9oR!nCL8`E!v;N zCP9D&k*aEe1Q{iZUZt%+VV1Yt6vnr+RmOs3%2bima#0vY&>@dp>Q4klFxr;dc4NV7 z;7%1&P_(oC#)35jP@*Qu$ViCmB50)kuo%3VghXCM>c_u@ARd^21(y?p?G$@_^o zl|c~Z^PZDKFqrKHhz1m<)N}G=1%a+iF1YMPU$%n=GPcgF#A3FL|ITmzV=$%|XLg3? zq?2FgC!AP3^sUZD#-*|d`U}0(;=90mDFh8T|I4B9yIESqng7<2EHwGvJqfsa zaTH(4)ytr08H&G2SQK;BQJrJ9P7jpXq!Qt^KU?9OE?Jm{ZT^bdZNAs49j~!7N}P zmBj~N{T=wPXYx+ZG9Z_~_aNiQoG6dZhO6Ww`;lw*0@qOne?S>5aXOS9@8dezhwt#% zAK>9T{Z`F%0=}-V?y3*#aZYyvb}gGJ3`2l^s{w)3gMq3MsBR#T=?F?7%xQ93M`H}> zmyD~fdZ?QG7FavUMOdw=)*@vOJ5v{V@KOkBKg}MW0>gUD-hFtb#2SOG5>PPXVQk{X zpfORmJceq0Ou;Ozb!<{SUSrN}k2XbXy#Pb~G>ek0P3E^_=&{e^K9ohE|L>vIT)M>h zI4dDoMjK1A14g&U*&!GIDLbr_WJt`;9LgH&%})6I!R$!BS5S}hGq2wGC_m>1^D`fB z{@?TSHS@ias;{0`e>kU?0FY8hiX^2ec(M$uAMv2Y7~hYCe?lD) zh)n2aTQo#^NfCfG;1bDgRHN_*Xc!;!1q6us8Oj$B3Mm-_zJMeW4`K_NfP0T=0{AQa zgKy#|ECG=Zk<;D%yr*BJJ`h;~cJ=W^{Y&_#V0}5j5HBtVnh|Fz?HT4&d3)wQnE#!&Q$Vb_dG9MD6WCg400 z=l{8{OH&N#>yi~D6uO*e6oWlhqR2k5--B-KcUoaM!B$QXbVyuKA@RZqX!wx0xBChm z?7q$p?YFBBaNl-YVxDMZ$BJGPNMuOU*v9G{P@)E%;j zFZDumV_i;!AZH`?=w}KO$i3Kzy?TD5$8rsc1rS1y-1EY1oMY(=$nS0%cuwC zp7*%HX?z@rO(-x3F7yWIw!JsAj+tjMApQF@#aw_T<9bX4r_H$%b<6cV4AFjz#^H4l z97-CAMNzl9su`?a5BFGpMWCWsazkiKR<1nimaquvT%F0p??f*=8p*vtFs9G5&aQe> zrS%8XMRg}$(M;iL>7qf;QC?5Br~Y~;NmvG0yrgnk*kv%r(#6|yByY+PuwW@C949m4E zfqi&Q>*PB;g!g`@UL06fV?;QCj0kZnpE*`Rh-ZH=n&wkshmDc*F}x%Jljc}Y3Pv)< z{CB^`I@^!bq#ID(NBmkl$GY2SdW{OZY>dRn6I2+FZlqyv8clCdVZ1T&4KfJTlUqKH zc0nA8HXi<$v<^G@Bo7z;YXWB0VEAfULxuGiBiE4INoDOVyc4i>tA4Kj7#6dSYp|4f z0w8`)tMT8a@(*{%9x^NK+TZGTysmeaNjKf-0h(dkX@7o2gA>3B0zBb!PNRK3c7d$I z?SRiK4Z#YJ;SD|3GzLh1oY7k?dO;q&HAkNs4UC0;a8hDw3r^{7DQey^U!Jl37xIuL zu-qev%TgI%>6u<|cY2I#g`Cs2d-;K&1x?xZrXH3*GM=L{gsEh`ii!UtW{Km(MKJ5A zZS6W?#I~C{Ay9O;^7-*^>J<~A&2=ayLTQ}JuW#`N{d#u6-Pz<`DWy_=ktf6J8W{^B zB1_o&pn!YP(^Va69ZnV?vAqmoFB)m_J#b3)K9ry>(j^%?-qb^jfi*6nbE;yZB^O3T zd3vTV(DFvqk)lIPUjkV55SO;l9>bsXLb?%~6jS(FLm6x7Rn+DHT4IYrXazJxIK z6TKW8QKNI$5%VZEzE(HKeS&|<&7EZ9-_u7`#I#!^38ggugqx3W6U`m4`Xm+?Y}HRu znAL2(sY})@YxXRLUK75hl85Re7X99r)D@)?i*heCg4j8st>4-2YHQ}JJUMEZu1Zap zO%6gxTqfxg9Uuj&X9$8vcPYV^nS$8!<~lSD7f|G91S1ZBBBWU zTA&}7w2}pOaZT~)!)^DmAPP+3uO3DnDbVI>T49nTgD_yl6+^>a=wi2Z==jeb68$X= zvkF`9lUBcl2fxvS_1kg&H=VmP53bQ`L%2@ZdC(zS*JEkbsjs2eYbj!);ap4xoN2JK zM9<6hq;d^WN64E)8>N+Lcj@igx3x9fH$_hxa32_ar=$)J_jT=L zt|B3UB^KD#I>=Gq;LZ!!GXUdReVq|03GDWaHky**tQq ze1mcC24Xr$=*Co0=N6JXWSS?oToOZaXXnpoe>YcVmxA3I`7i&Pxi>}qp z#_A|49n8jP-tl0`epn_Df;9g=zT^Qcl$XRwa|w8w>czT3dJ1oh6wbaSdv>?)7&#z+Yn67ETlX#qj0WUz#K=*jKl=ESzpt0PZIl1GU~Qr3Uj+XRfq(hH zFdY6L5dYBFadBZ&$H#|Hn~-39apLqRW;~hr)So96OkVqRVTAo>Y$W&0x)+0|N6v^y z3{4719yc>=*7%o(&kmn6VXkqWY5p?{Mx=~fs80<{3wb$cksep|H-||z1qFT zo#$rU`R=#fHupOeRkkYtC5<%$e*5kKr+Ii>NN!-9-wHZzj9E2VyJ=$t`*+J58+)Ls z{)ki$iORT7U1xx4fmQLbe3Z=L*Z`F|F3FQYPOW_ItCo)RXT{ z_`R9l2-Ca`pJYKN4!d)I)`(28z=e-?XkKoT~cWMccT}cfB*%yMz zM@wo7YcT6Q(d+g6TRrd5AQ8l6A)G<&qeVya5$S0@&UB{ppTHLPO+Ejxi&Qc4qLm_5 zB3>}e5@0jb`o37hE_a!3y^mdjevkg~Gd-cug~>(kGu!{DhpHl+=?lr7P#An$2Yr!_ z=?%#pQK-EMqauvAu(cD;g4^NcCGerVMm|o$jnA(=wGEq2WS!#PkPmE4A>$La@Nb@S z#e7V5u`@cP5@DPJTHrB`AW9hS2$Bc^jv%Svhpt6XI%E>;7a${~|LHr<`-7=JPG*$? z`v+TLsdY}A%rcLkbe`)J_Y1QAOxBc8qX^}zvC)E|OP__`4t0;9HY%46826`D9uGLfB!4i<+YayyCKflm8FO>vkQoH^m_G_6F8(j@sXdw24i-8q&gobwR z&d&50?&+PtkM!uVw{D+)N3TA^n=fL`Ll)QN{JK+1kL{Nbt9vp=7aM6Ty93wZi3ZN0 z&9gO^*a`>m-MvfrDfft7X}1^C&ZpnwU!zipm7W3%1xXZs5~%dO8~1+5YFBEopOTFK z0(R_VU1sLq`Qjge+K5i2$}DWq*kbrR%vAMGj4jN1`poDFK^n&|GJ6vOJt>G z=xU2d=!E4KrtMX$dg#)NSvv{DIypWvFsJl@_sSdaogo1S)?-M(*M1U^y~$jC6-a-m z!=rcUyN$c_s;e6YDWbL&5~?UrM3D*wQw)9%#eaF!GJ{z7cnyBATbj5p%3y$}DE{4} zfHR8LKmj}eeu}+GBNRp!duwtHk#{PvPgs1)LRJ(@rB&a; z$b^6P9^tkj!=dq3V=1DN380^sGnXcY#q~D}F)JZSN{JCK#*;`4lf5d<7z->LH|huQiMDjI=CtA0`QFdM)T%kx;KSk?~9-TsxTJ z^je;0Jf>djGvBHtOy3h0{kL*pf4;qrE%2VOD8DVoXs}2x^X5oiA5nyf!aB73pxpgC zwh}q`B{XyE2&X}7R0Y}MNwHhzZ0TF3$oRy$?+I6q$5i36c*u=LSYUP+;3ejjIV1rk zk?0=8&f(vgdtn%j8|gI zYWR=tSX2md0hebBYQ2Wpf!_C}VQZk|uMAhfcE5|O+_7F?aF@BJt8Zai^eR}0EB477 z+e9SQ4~*&>i#Lj%(`Xjo>KJPP=|tIPlZSMdYOzFmh)n;7zrUfLqyaN_zV3O2txBFn z8c{geZc6K7v)y%5Gafm}>z>c5I#NG^aG1!>I+>B<){a<^SqRGV)wo-3x1w7xp#JCl+A7&kI+o~az_6l1C$rG7%-2!Jyiv82sn+PCLs1@)CVn#PKFfdM z-}w)V`dd$4R0Xzx8Yx^wry0U^A7uUaYyTT%wwX3Du5CRw>^EvB(P_!L0t3+@R z^B(J;Fz2o7<;?*Hn_c2j1}7dwPI}iX0svW#_Z&Ip!7LLWiH{)Piiu&V}mJwvk%vb0nj$7e}War8`mjM z>OxP1)E`fY7|Wqo#CXtE?h&)&57T#&9TcI%6iCiUt%5}?HU?EUVdEg+#FOdnRtOXJ zyutA>xqM9{6OXO32CoO=V7)iWBko7EI-vUuHdN|v>TVuOQFd%fg{`)d0VVQ!s2+J} z?CB~1ybRoYVuMSTl>xFx@^?Ub7{4Cpa2IO{Wb!N_Tmo+H9(+3h6vLiK8qW&VX0mNh zY`QX6VOC=MofQDbWBVDd28%h0?q;ykG*ehe_GXgQz_i8xkD1iNB>1OU95p11Vg|8e z3M`Um3UmMGZ2regs!1m4Ny)0w>6%H&^k}f?fRl3|r^`sIhGM3D)HDqqUwkL$m-Wjr zSu=##s<4yqpZz7+SoVB=o^*i*So$ig72Mn?>WW3si!?`C7kV&M&qYhAN!|HwN^h2x zQ~C!*XY#AchxO$ETp3+UYyD41h1Iq;ceLH8@#ks?3(u_R9V&lWw;d7JIVWB0K1Z5> z=yk#Q{@W%~(L0L$asH)0BJc6frwodI)TwVPhD|zwz5R9>z&xk*e_gt*+<%AOe{+xD zJo#o;(yTUw7!*;rHjkv@G~O-rFw+ok|GM->C|=t@ix}+Kt}AMSb#yvi9zgAlt+eP9 zKD225icXd6tO3Fc;lsb7mPyw&6J;JLA`ns4(v{k=TJVQxH@s9!U)1K{+^x;m9xJc# z?#5T%RNc4)``C5h@BmPt^FWBY{F3Tm zCGaXF9p3||>GE~-!Efncbau~9zyJ@yTl%=uIB;i>cW*iqkWO&*FkB~)WbQc`?Qlx! zO+&`~AQi9W&Xh&An`5|}XID_jPhVQF1LfQlRlz0$EZ$Ws0j?Z7>oF%J7=!^mC3CqY zUA1zn2BtvV{(L=BunGz$esSkjU z%g``bVM|s{m?E1>NiqP`lHkT$&b~qU(n=~M%T_gJ=@*`ZRw>c!ap$8!@fK~LNoSEU zk*i5cY$+FW*>Y~awEHAm$6T3G@2Yp5Jl0iPVM#@soPYPSq^q*R8roeMDzPSXRqCxu zvW2=qcy>MWAOL#;KM1n;3KQ3NSAtqHl({~Qi`K)kAOK(&7?9g)G$F|#fbfpmaxQ*0MaDJ!TPXeroVPZ#BEInSU|h?1=9q>gC#Hye4SFEEQwI}$ zoI9&peD0Ctxn)A~diE$VdIzdfPPV*thur$6twm8!7Kl!sJW5c?;eu{`!?A{ANWR+% zyT?u^^Mkx5S&9;PO=Nn>cKym7?&ehzLcx*ky7`dNDkPJ{Zv=?$wl$+DizeR{pCFl!*YHLFwl}xW53tV< zvd=fz=Z~<@A0_ZaZYNYon0u0`6%>ddU{me7(ce{-U|oRQ2QN-vn1(zM@XT4}s)DI& zVp=Ph&I(x1<7tk_t5>KBT0o zH~^NcQ%YFim#oi}6z*iKlS=Fa&lp~k4@G=%i46wMBTC+ZVm_ec&oEHcloY^dn=VxU^>!OYsc9)V=QfgI-A173NsECAUJwOR!|g#-L=BDog9W)Kn4&^5PVAtm330Fu zC!v%(M&YeTjG}g^04Agfv6a|PslB<}YM{cWsZm8IctS{6O96#B`pNgC_gsjMysc6I_C1LSN+6vyoN>n;?zrFdK-A$3692 z3)ngmBvUV4w#aJa>$9>$mS&vzoBHSKKdCRM|Em5k^=cg7Q)jAkaC~3ASp5ea)6~CF z+i+Z{{*^i(N4t8F`u8|4Q2$b$hvPr|-t&tc8$Fiw`?KGNet-A-Oq1vLPtCXZZNT@O z=Bj3?pVjX*zdM>2{F3|@`rQkBIO5c>StI@#lr~a1ym&;@m<^-z{JtJ}Wz2>#{21LR zQ}D>(;NYO(@xf07KOLOsXA9dMc4XZBae02;Pfzf|@!HTlzrT*UJ%+fvI(FCCkH!{{ z{buZ1@%Ryb$!7;XAB`=CFLB>7HeGwf?}A?)KHlRwzjJ;wwC8X|uA{XDx;7kp{Mzuj z;3tKT*S`nn#NEf=>)+}3(C?IACw#sBgW=_L!gPtu2WlhlpQM72WDoo$5V~gZuYyWo zq>o6p$<7rNljrf|<$1k3a8CYi4_u3`gXu8jirx3yu6x@f?QRc6eUkRe%IRlHE)e(e zlehf$q4!k|mS60R>Xd&?+z0*`lE`zFzaF=}S$Bk-yV=oob5?FXMb5wL82-wW zvAY~3?BBXi^u4n=bTdhR&s!%Nx`&(hku+u0sXr}giht!d@A*&+_58dulY8n5 zI(dHhYwcwR-VT8KvG+5wxdQq=ngqnSS`aFc`n>g#FMR&%wG)MJe)Yl;?2S!*_#nB~ zt@)-c@Fy|klqpMj;>f2d|9FTy9&b4McNwL(O-WZR_(caP?CYh$zgxdBrJeZ1hW{=rbbZR6pT>|!&SuNhsysD?u+$-EtGVD^UMjw!=BQspBDe4k`8f+&@j>^q)7YaJ7{G)0VgQ$+ltx zQ)-RP_j*vz)87zAbr~hk{aah(@L%L-v-i*(Z?kOTA`s(edFX}_&ACXWWmf2pvd~aC=DYt=IPLo(!9PWX z*5C4e$o;g0G*pQJxmn@bL#6TOsVtH^(g=fg@V3St{}&3|3#syK)t<)xz99R`ZM zP%n5t0|_XQz}pJpb$%9Qj~ zmWuBUWXU-gV*8`8yA<7r0M?bcO8s5`?T>RMDC+OXq}_*PwADYqSDtTC)FKzak%;}n ze{TboTXKN2`w#{CZT!j+_oUu+KtFK)H=XNU9Vtu{BXaeX;_-Rqkn=|GeaE;)`7?Lq zVjh5Jq+BoicO9Y$`H5?`KPj~Y=K+clf)6m1FfyvK>?x%%EUIyBciC*Epusf`J~*h9 zY?7KVu&5H%fUnA$OjecYWfeD1$t6M%CUV{EX@E)#7pa^iQMn-Hy1nmYYPnfyg7!5J zyq1_Mc)iQlD1{qhz;ae8hU4(sBR+flffS)kox~Z+N6lyA-+*Hmtwz~Yn@+DhBoshq zFTtd#^K;M=cL*HUDXg7zXq9qpcfnoRVQ5U6rp}<&{A^JwXH|YLp-iXWl6GIfe$?}% z+)_EWnpWRhDRaFfx80D~&Qi8>li2So_i{~pqq^3%W7Php z&8=NE3byky+ffO3gKOZ9ahNgL+V#0tEotT_ZP*bx>I2wnW0oahtJszg*wgGCwvH9p zOW3gv%Z0XfWv;*F3D~_iHZ(MxY^YDRb_yfc9`CLO@2bpJ7mCdx&NWzH*`04U$`H5I`DYl#XvWLeTqt+!Q5j~T$`nfShwkJs_algO4tJ+1_eBm z9I6csHw-k%baxBKb4M46PI_*U6mGuVEwaOb&9F3tQWzn1V#vp?8R|nRp!Xw*AO>{A zVvMH18@7gl7()^obS9x6C**a_0M#RMM7&hpzocb+;8EMj$4LyPJm@@d!@(>LI@6D` zH{kMP{#DPk40Ux%)U8zS6Nsq~KGIL}J?KpT^x*;81I77Z-Ugoo!3Oj7$cf~=IDfkpYF2_(ro`KbQ25K4iMP2o}G#8roJ%Lzo|Seh$t`QT{~XgA{InhWSH^)Pg9_ z$7Z!i^iE#=rKCmn_)!!;n_3L@3|bNB=NE&Saxp)J`d-3-j~pV_&}a@T|B#A(nYL?=~MG**+NQCF$~An}yPL9Y6FH~5ugf|DZ*1F4E|j?7!MVMGGmN6LfD1jIHTuHR(fkJ+ z-rtxLxIm`;kO~amL48Kaw@F8_yI8qF{Z)*r@ zm-{fpv5z~8+3Sxm8g`*kAx%TZQrD5b?pn%Y9gTYRSN1Yn%l5GsSQmSoz5CY>*i$SF z3_m~s#`@a)M^)|&aj@JL($qhykE&2NVX2r%TA|Q--aHy#9j2R1?pX6gC_yY?LQqCZ zeVcidpn;e7Tqy3N+024^&mENkHlX5 zX<{V9le;P<|8rutO3Bc~w*7_rt9_V#9+)=e_9Sc)&$Kaxzf?g%o3MIBzFShUMrj9s zE#Qw%6gU6s19*zfw^DpaGBq-5@eM<3&QBM8aitRTMQE$|@(-2#Q;%YW!p(9@xMAGO z^%(cE^$iu_lKR61Ro;G7)WKxj8qgLnM;TKWY-q*-p&eDYTs1jwlRZ&cXi*owtgx>_ z=T(TU1+X3^cf!*_CG3|n4;brt2ul;TD;7?|l{jeNpmazoh}%JuFDVjAnALE-zERcq zR=2Pl0}Lw&$haogCX7BL)&TAmA2w0JE>|^NF7-4tx$-HTyZ%ABiYQkT1~=jWBT>c9 zRbvCrL+o4?y1%ImwffT3e9xK-@;r7q3>VN(UTJ9Jg48cdRTzOFP4&w%%kyHvuv!QR zv{v;l^JIKY`41?@JSNwN&{+>->upZSFp!&{-p9^16fHuJX zvdsRvL|vyo%HCtHF@+YDsjKi=rY+CAg;E1Q)uXWJrb2-+_GypIDaXOS7NFoT z%j>V0e;69)r_V8XJ@`CawU0;UML{-PR*_o3TwzpKb~|miX|G(0&o@LmgCK=t%XcXU z#&>&Ft-FN=xXH0&4`2S3Qn&`&o=ZTs5JAkZ_Q)_stFs-Fvmk1^#;`9dP=~nJQQVUH z8z|`u4NWawySeIt@)Zh5n}Wr)XV4H|;7&Sp1NtDbQNgC7UKOP2Na0*HcY?XfU<`Pv z@O70dQ#z#aImZu#1593QTu2i`*Cr|@6=+UMC56keaRmwAl?cw;DfpFuGGMOK)~m~V zihEtot)e+FBTMGu!=O6naRAM)-X+!hB?)twMy9_E_G22!hZmLFyego`$jZcGm_BiD-yUDgLf8DT5 ze64u7D6vORut0o*=fo$NmiOY&CpbXbXq2@KT%S?3D@PB^6GvYkoRme8EqHRNTR_`C`{FE#>k ziV`wJV!)>&5nSNlYQfO!$A&XzP_bj2eu1|nN++FzI3SD}0>Y_4hDXJ1XYgpEmDnyJA-xM+u*ZtMK;{_rjR48uWz|KcvXK2gDxX^7I5XM zF;#q$9F*2)RF$xBze$!>NvGcw$Opn;Tw-eTrpKKTYP|t(bS0`7>sg^iJf0U${7dV7 z`0At9dtJr4=Az1?CNcsYJU8yO-Nr;~ux&=WZ0ec{eY@BqPk7ohZD@;}kG*vm>n#+g z`gN&#vbB}8%on-WY4xDXKs1A7c z>Z8|88fXW_?Ebef`bS~9YNgf^Z%aNjk=vM-iOUqu!7g6unS{8CTF=u3yO=Q15^Vo3e)A4$*PLCe zMiYxjc}jl&ZCkBIxGn0az?%q_${5RNqPyaqV(5}v-{OtQxv{(LW0I3wpflPnx^KBP zlBDp1+uBcnJ^j6H; zkjHinm!zDOsNC@h^+-RFM6afyxDckVk4>b_6LJG$Uy+-GbJekPrRL$e^4K7=KVL@K z;y=L;EZ)wZks>WHlrYxJX-wmvK_ z`7mt{1+pp(CLLeQEXFZy2g!8;mYm3SDANYk0O&Ic5d{d(-`w5mhxK3*&l`?h!m5V^ zJC9((88Y4kttZKc57y0u+fFowqO!xI{e)_`3c?Zc*q~#1q^n4zlf>%5Po|vp=W{~q z>&*ce;9#X|wBSot#ctG}H|se!^hNw>ywZHMdHIl`MAa9J%z82X;B2Zk2?}6H`78|Q z$e`|JDb}AA38I-!b3>0kNonhIo3YE$3#krkad+$YO^anWa%A6oJlx5sithU|z5#;= zY{PTEH}KIZv?Sg4(Rm!U9g+lIB-$4HQrZrajU;030lhTWy^Op_{>aVp*%W{BrUxpq zZs+2Ih5sUr<%QHTXUr`jech3wvz$0!GIEPlObZj!U`?Xa7o!1C2Gb#mwcTrvGFK(c zHA&2U>l3P5xku3v!#RMQ5D{S5bT; zv3ffACdrbRvnOt5N9=un8UJ_ja$$Noyn=3KLx$7&bPpeTGdr`zgO?aMFqK&OY+pc`)vwQykRZb+-K{L)}05$|gjimCEOC12ytiG~UUMcgT*mtR!! zAGt;UIYN1jl7G+5PZhGUMHumU+v_t+$cAIFB!%lP`_h+h^SXu!85l_WmfukFOWb_8 z;LSNCu`F1{SGX6i?G}^0ePi&w-nrpPB5(A6&-IrV2Xmc{TxL-GJb`m(FFv@O;%5ll zQe|xt77oh0l)U?4t{fsVBaLLc2_yp$owsqbtYvA~(k2Vzi*uG2xcax`UoqY10Dya` zp*5#(al<*Jgx-zJCk~N;5pGewTx>!3mmXy`9XqW3uVt%zl*`4WB)_x^(=3fN+NtHiC}vqT_xz zI>sM(Fy1kq7!EpvQ&Qm_QEm8YJOOTnfCDL*cX{Bje;~+`j0jJ}{_*Q0F2om)UkI22 z_^=|n=;HB!Iq-`voPh6FQKyZ@v2lHJ@2<-2RH zaq}Pa8vt}z#H~~&+yiN$|b;U3gjK_>|xct3i;_24ma`y5Bt)!kO zsywgTw%6q(AEH0b+4;2l$Vq;DvXbm>01q}@TN+(AE)6!7b_HxIGsv&ft^+Q>4$@Q= zS=w6s5qI-lgJW3fsJe0eq_=z5Fl88Ym@BlL2K?)tpBDOozjz{5Iv=LFk~y67<7HU? zf+gJl>$rMvTrEj>AfcT9kPx>bz(H-r5>v94?I8PJORr℞6Tu?4W+slD)hB_fLLp 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 LZQpnAec3!FlnS+^wcg+JrM`4b~t%&As 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

RuOR|xlwa%V{n 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 F{{R{PXgUA@ literal 0 HcmV?d00001 -- 2.39.2