'=========================================================================== ' Subject: COMPLETE BLAST! (MODE 13H) SUBS Date: 10-04-96 (00:00) ' Author: Andrew L. Ayers Code: QB, QBasic, PDS ' Origin: andrewa@indirect.com Packet: GRAPHICS.ABC '=========================================================================== '**************************************************************************** ' ' Description : Blast! Library - VGA Mode 13 Off-Screen Buffer Manipulation ' routines ' Written by : Copyright (c) 1996 by Andrew L. Ayers ' Date : 10/04/96 ' Comments : Users of QBASIC will find that this program just barely ' fits into their environment. This is because QBASIC only ' allows 160K of memory for code AND data. Since I create ' two off-screen buffers of 64K each, this leaves only 32K ' for code and any other buffers. In most cases, you should ' need only one off-screen buffer, giving you 96K for both ' code and any other buffers. There shouldn't be any problems ' with QuickBASIC, though, since it allows you to use 64K for ' each procedure, and the rest of memory for your data. Notice ' the use of REDIM for the declaration of the buffers. This is ' so I could resize the buffers to zero at the end of the ' program. When I was coding this in QBASIC, I realized I need- ' ed to do this when HELP would no longer load due to low ' memory! ' ' If you want to view the source for the assembler routines, ' they are contained within the ".ASM" files included in this ' archive. ' ' I went to a lot of time and trouble to create this library ' of routines. They are intended to fill a void that has ' existed for a while. I am distributing this library as free- ' ware, though I am retaining the copyright. You may use this ' library in any way you see fit. I only ask that you put my ' name somewhere in your opening credits. Something like ' "Sprite Routines developed by Andrew L. Ayers" or "Developed ' using the Blast! Library by Andrew L. Ayers". Other than ' that, it's free. Thank you, and have phun! ' '**************************************************************************** ' ' Declare our procedures ' DECLARE SUB DrawLine (dsegment%, doffset%, x1%, y1%, x2%, y2%, colr%) DECLARE SUB BlastGet (dsegment%, doffset%, ssegment%, soffset%, x1%, y1%, x2%, y2%) DECLARE SUB BlastPut (dsegment%, doffset%, ssegment%, soffset%, xpos%, ypos%, icol%) DECLARE SUB BlastPset (segment%, offset%, xpos%, ypos%, col%) DECLARE SUB BlastCopy (fsegment%, foffset%, tsegment%, toffset%) ' ' Reserve assembler routine code memory ' DIM SHARED code1%(14), code2%(21), code3%(76), code4%(76) ' ' BlastCopy! (BLSTCOPY.ASM) ' code$ = "1E5589E58B460E8ED88B760C8B460A8EC08B7E08B9007DF3A55D1FCA0800" ' DEF SEG = VARSEG(code1%(0)) ' FOR i% = 0 TO 29 d% = VAL("&h" + MID$(code$, i% * 2 + 1, 2)) POKE VARPTR(code1%(0)) + i%, d% NEXT i% ' DEF SEG ' ' BlastPset! (BLSTPSET.ASM) ' code$ = "1E5589E58B46108ED88B760AB106D3E689F3B102D3E601DE8B5E0C01DE8B5E0E01DE8A460888045D1FCA0A00" ' DEF SEG = VARSEG(code2%(0)) ' FOR i% = 0 TO 43 d% = VAL("&h" + MID$(code$, i% * 2 + 1, 2)) POKE VARPTR(code2%(0)) + i%, d% NEXT i% ' DEF SEG ' ' BlastPut! (BLASTPUT.ASM) ' code$ = "1E5589E58B460C508B460A508B46108ED88B760E8B04B103D3E8508B5EFE" code$ = code$ + "01C3895EFE8B4402508B5EFC01C3895EFC83C60489760E89E58B46188ED8" code$ = code$ + "8B76168A04468976163A461074208B5E1C8EDB8B7612B106D3E689F3B102" code$ = code$ + "D3E601DE8B5E1401DE8B5E1A01DE88048B4614408946148B460639461475" code$ = code$ + "BE8B46142B46028946148B4612408946128B460439461275A6585858585D" code$ = code$ + "1FCA0E00" ' DEF SEG = VARSEG(code3%(0)) ' FOR i% = 0 TO 153 d% = VAL("&h" + MID$(code$, i% * 2 + 1, 2)) POKE VARPTR(code3%(0)) + i%, d% NEXT i% ' DEF SEG ' ' BlastGet! (BLASTGET.ASM) ' code$ = "1E5589E58B460A508B4608508B460A2B460E40508B46082B460C40508B46128ED8" code$ = code$ + "8B76108B46FABB0800F7E3890446468B46F88904464689761089E58B5E" code$ = code$ + "1E8EDB8B7614B106D3E689F3B102D3E601DE8B5E1601DE8B5E1C01DE8A" code$ = code$ + "048B5E1A8EDB8B76188804468976188B4616408946168B460639461676" code$ = code$ + "C38B46162B46028946168B4614408946148B460439461476AB58585858" code$ = code$ + "5D1FCA1000" ' DEF SEG = VARSEG(code4%(0)) ' FOR i% = 0 TO 153 d% = VAL("&h" + MID$(code$, i% * 2 + 1, 2)) POKE VARPTR(code4%(0)) + i%, d% NEXT i% ' DEF SEG ' '**************************************************************************** ' ' Reserve memory for two off-screen buffers, sprites and demo variables ' Use REDIM so that we may de-allocate the memory at the end of the program ' REDIM buffer1%(31999) ' This is an off-screen buffer REDIM buffer2%(31999) ' This is another... REDIM sbuffer%(2000) ' This is our sprite buffer ' DIM bx%(40), by%(40), bt%(40), dx%(40), dy%(40)' Variables for demo ' '**************************************************************************** ' ' Initialize Balls ' FOR t = 0 TO 39 bx%(t) = INT(RND * 200) + 50: by%(t) = INT(RND * 100) + 50 bt%(t) = INT(RND * 2) DO dx%(t) = INT(RND * 11) - 5: dy%(t) = INT(RND * 11) - 5 LOOP UNTIL dx%(t) <> 0 AND dy%(t) <> 0 NEXT t ' SCREEN 13 ' ' Now, show some instructions ' COLOR 2 LOCATE 2, 2: PRINT "Blast! Library Demo by Andrew L. Ayers" ' COLOR 15 LOCATE 4, 1: PRINT "The following demo illustrates a simple" LOCATE 5, 1: PRINT "use of the Blast! Library. Many 32 x 32" LOCATE 6, 1: PRINT "and 16 x 16 sprites (up to 40) bounce on" LOCATE 7, 1: PRINT "the screen in random directions. Use the" LOCATE 8, 1: PRINT "+/- keys to add or subtract sprites from" LOCATE 9, 1: PRINT "the screen. Notice how the sprites can" LOCATE 10, 1: PRINT "overlap each other, without showing a" LOCATE 11, 1: PRINT "black border. This masking effect is" LOCATE 12, 1: PRINT "done by simply specifying black (0) as" LOCATE 13, 1: PRINT "the invisible color. While in this demo" LOCATE 14, 1: PRINT "I am using 32 x 32 and 16 x 16 sprites," LOCATE 15, 1: PRINT "any size sprite could be used. It all" LOCATE 16, 1: PRINT "depends on the level of detail your" LOCATE 17, 1: PRINT "sprites need as well as the number of" LOCATE 18, 1: PRINT "sprites you place on screen at one time." ' ' Draw a simple graphic for the background ' COLOR 4 LOCATE 21, 3: PRINT "Please wait - Preparing Images."; ' x1% = 159: y1% = 99: colr% = 0 ' FOR t% = 0 TO 319 STEP 4 CALL DrawLine(VARSEG(buffer2%(0)), VARPTR(buffer2%(0)), x1%, y1%, t%, 0, colr%) colr% = colr% + 1: IF colr% > 255 THEN colr% = 0 NEXT ' PRINT "."; ' FOR t% = 0 TO 199 STEP 4 CALL DrawLine(VARSEG(buffer2%(0)), VARPTR(buffer2%(0)), x1%, y1%, 319, t%, colr%) colr% = colr% + 1: IF colr% > 255 THEN colr% = 0 NEXT ' PRINT "."; ' FOR t% = 319 TO 0 STEP -4 CALL DrawLine(VARSEG(buffer2%(0)), VARPTR(buffer2%(0)), x1%, y1%, t%, 199, colr%) colr% = colr% + 1: IF colr% > 255 THEN colr% = 0 NEXT ' PRINT "."; ' FOR t% = 199 TO 0 STEP -4 CALL DrawLine(VARSEG(buffer2%(0)), VARPTR(buffer2%(0)), x1%, y1%, 0, t%, colr%) colr% = colr% + 1: IF colr% > 255 THEN colr% = 0 NEXT ' COLOR 2 LOCATE 21, 1: PRINT "Hit any key to begin demo - [ESC] exits" COLOR 15 ' DO: LOOP UNTIL INKEY$ <> "" ' CLS ' ' Create a 32 x 32 sprite for us to use ' o = 0 FOR t% = 14 TO 0 STEP -1 c% = 31 - t% CIRCLE (15 + o, 15 + o), t%, c% PAINT (15 + o, 15 + o), c% o = o - .4 NEXT t% ' ' Get our sprites ' CALL BlastGet(&HA000, 0, VARSEG(sbuffer%(0)), VARPTR(sbuffer%(0)), 0, 0, 31, 31) ' LINE (11, 11)-(18, 18), 0, BF ' Clear out center ' CALL BlastGet(&HA000, 0, VARSEG(sbuffer%(0)), VARPTR(sbuffer%(514)), 7, 7, 22, 22) ' ' Initialize Demo Variables ' numballs% = 10: done% = 0 ' DO ' ' Copy our background graphic to our off-screen buffer (which clears off ' old images of our sprites...) If you are using only a single buffer, just ' "get" the background behind each sprite before moving, move, then replace ' the background, or just erase with the "ERASE arrayname" (see HELP) ' command. I could done the "get, move, replace" on this demo, but I wanted ' to show how to use a background using the library... ' CALL BlastCopy(VARSEG(buffer2%(0)), VARPTR(buffer2%(0)), VARSEG(buffer1%(0)), VARPTR(buffer1%(0))) ' ' Put our sprites ' FOR t% = 0 TO numballs% - 1 bx%(t%) = bx%(t%) + dx%(t%): by%(t%) = by%(t%) + dy%(t%) IF bx%(t%) > 287 THEN dx%(t%) = -dx%(t%): bx%(t%) = 287 IF bx%(t%) < 0 THEN dx%(t%) = -dx%(t%): bx%(t%) = 0 IF by%(t%) > 167 THEN dy%(t%) = -dy%(t%): by%(t%) = 167 IF by%(t%) < 0 THEN dy%(t%) = -dy%(t%): by%(t%) = 0 CALL BlastPut(VARSEG(buffer1%(0)), VARPTR(buffer1%(0)), VARSEG(sbuffer%(0)), VARPTR(sbuffer%(bt%(t%) * 514)), bx%(t%), by%(t%), 0) NEXT ' ' Copy the off-screen buffer to the visible screen ' CALL BlastCopy(VARSEG(buffer1%(0)), VARPTR(buffer1%(0)), &HA000, 0) ' ' Get user input for number of balls, or exit ' a$ = INKEY$ SELECT CASE a$ CASE "-", "_" numballs% = numballs% - 1 IF numballs% < 1 THEN numballs% = 1 CASE "+", "=" numballs% = numballs% + 1 IF numballs% > 40 THEN numballs% = 40 CASE CHR$(27) done% = 1 END SELECT ' LOOP UNTIL done% ' CLS ' ' Deallocate our large buffers, using REDIM to size to zero ' REDIM buffer1%(0) ' This is an off-screen buffer REDIM buffer2%(0) ' This is another... REDIM sbuffer%(0) ' This is our sprite buffer SUB BlastCopy (fsegment%, foffset%, tsegment%, toffset%) ' ' No error checking is done for this routine, so be careful when ' you set the from and to segements and offsets - you could crash ' your machine... ' WAIT &H3DA, 8 ' Wait for vertical retrace ' ' Copy! ' DEF SEG = VARSEG(code1%(0)) ' CALL ABSOLUTE(BYVAL fsegment%, BYVAL foffset%, BYVAL tsegment%, BYVAL toffset%, VARPTR(code1%(0))) ' DEF SEG ' END SUB SUB BlastGet (dsegment%, doffset%, ssegment%, soffset%, x1%, y1%, x2%, y2%) ' ' No error checking is done for X and Y coordinates, nor for any segments ' and offsets into memory. Therefore, use care when setting them so you ' don't crash your machine. ' DEF SEG = VARSEG(code4%(0)) ' CALL ABSOLUTE(BYVAL dsegment%, BYVAL doffset%, BYVAL ssegment%, BYVAL soffset%, BYVAL x1%, BYVAL y1%, BYVAL x2%, BYVAL y2%, VARPTR(code4%(0))) ' DEF SEG ' END SUB SUB BlastPset (segment%, offset%, xpos%, ypos%, col%) ' ' No error checking is done for X and Y coordinates, nor for any segments ' and offsets into memory. Therefore, use care when setting them so you ' don't crash your machine. ' ' Plot the pixel! ' DEF SEG = VARSEG(code2%(0)) ' CALL ABSOLUTE(BYVAL segment%, BYVAL offset%, BYVAL xpos%, BYVAL ypos%, BYVAL col%, VARPTR(code2%(0))) ' DEF SEG ' END SUB SUB BlastPut (dsegment%, doffset%, ssegment%, soffset%, xpos%, ypos%, icol%) ' ' No error checking is done for X and Y coordinates, nor for any segments ' and offsets into memory. Therefore, use care when setting them so you ' don't crash your machine. ' DEF SEG = VARSEG(code3%(0)) ' CALL ABSOLUTE(BYVAL dsegment%, BYVAL doffset%, BYVAL ssegment%, BYVAL soffset%, BYVAL xpos%, BYVAL ypos%, BYVAL icol%, VARPTR(code3%(0))) ' DEF SEG ' END SUB SUB DrawLine (dsegment%, doffset%, x1%, y1%, x2%, y2%, colr%) ' ' This is a simple routine which uses the traditional ' Bresenham Algorithm to draw a line between two points. ' It is pretty fast, but not fast enough for REAL work ' (like 3D and such) but you can use it if you want. I ' plan on making an assembler version which should be ' MUCH faster. ' ' No error checking is performed for endpoints in this routine, ' so be careful not to let the ends fall out of bounds, since ' doing so may cause your machine to crash... ' DEF SEG = dsegment% ' error.term% = 0 ' xdiff% = x2% - x1%: ydiff% = y2% - y1% xstep% = 1: ystep% = 320 ' IF x1% >= x2% THEN xstep% = -1: xdiff% = -xdiff% IF y1% >= y2% THEN ystep% = -320: ydiff% = -ydiff% ' xend% = ABS(xdiff%) - 1: yend% = ABS(ydiff%) - 1 ' tt& = doffset% + (y1% * 320) + x1% ' IF xdiff% > ydiff% THEN ' FOR xx% = 0 TO xend% POKE tt&, colr% tt& = tt& + xstep% error.term% = error.term% + ydiff% IF error.term% >= xdiff% THEN tt& = tt& + ystep% error.term% = error.term% - xdiff% END IF NEXT ' ELSE ' FOR yy% = 0 TO yend% POKE tt&, colr% tt& = tt& + ystep% error.term% = error.term% + xdiff% IF error.term% >= ydiff% THEN tt& = tt& + xstep% error.term% = error.term% - ydiff% END IF NEXT ' END IF ' DEF SEG ' END SUB