ASM Graphics & Mouse
A Tutorial by Charles Johnson
Hello everyone, as promised here is another tutorial written by me, about stuff you might want to know to help you write cool games.
Before I go on... allow me to explicate for a little while.
First of all, I am no writer, I'm a systems engineer, if you're looking for good writing and clear instructions, don't look here.
Also, I don't see any reason to hide source code from others. Few of us has actually completed a game, so there's no reason to hide your secrets from anybody else. These are DOS games we're writing here - nobody writes DOS code anymore. When ID released the source for Wolfenstein, it spoke to me, it was like "Hey! there aren't any secrets anymore!". You see, everything we write here has been documented in some book on how to write games at Barnes&Noble, and is probably also on my bookshelf. So I see no need to hide source like some others of us do.
And for the code I've written here, if any of you saw my last tutorial (highly doubtful) you'd see that I write with VB-DOS, I've changed over recently to QB45, partially for the executable size, but mainly because I like to write stuff for other people to see, and most of you guys use Quick BASIC, and I happen to own it, so I decided to switch over.
A word on assemblers - I do a lot of work in assembly language, my assembler of choice is A86, by Eric Isaacson - search for it on your search engine of choice. I like to work in assembly, and I think all programmers, especially BASIC programmers should learn assembly language, it's the best place to start.
Don't let my prodigous use of assembly language scare you. In this package I've included object libraries for those of you who would rather not mess with the assembly code and just link in the routines you want.
Here is a list of files included in this package:
I'll go over the files one by one in order. I won't go too much into detail on the assembly programs, because the main focus of this article is the program pcxget.bas, so I'll spend the most time on it.
mouse.8 - Contained herein are my mouse routines, I'll run 'em down one by one.
Mouseinit - if any of you have used any mouse code in the past, you'll recognise this one. It simply initializes the mouse driver. I don't check driver status, so watch out if you don't hav a mouse driver loaded.
MouseCross - I don't use this routine in this program, but this routine turns the mouse cursor into a little cross-hair for accurate clicking.
ShowMouse - turns the mouse cursor on.
HideMouse - this may be the most valuable mouse routine available. In Windows programming, the mouse driver is notified when you draw on the screen and turns itself off so it doesn't ruin your drawings. However, in DOS, the driver doesn't know when your drawing so you have to turn it off or it will ruin your drawings.
lButton, rButton - these functions tell you the status of the mouse buttons.
MouseX, MouseY - these functions tell you the X and Y location of the mouse cursor - in pixels, so if you're in text mode, divide by 8 (or 16).
XorLine - this routine gets some heavy use here, it draws a line from the top of the screen to the bottom and across the screen, showing the cursor location. This makes selecting graphics very easy. The lines are drawn using xor so they can be erased without saving any background by xoring a second time.
XorBox - this routine draws a box from one location to another using xor, so the lines can be erased without saving the background.
myget.8 - This file contains a single routine.
MyGet - this routine grabs a region of the screen into an array similar to a QB get, the differences are: A segment can be passed for use with an offscreen buffer. You pass in the Width and height instead of x2 and y2. and so the code doesn't have to deal with array descriptors, you have to pass the first element of the array instead of the array itself:
MyGet &hA000, 10, 10, 30, 30, Array(0) 'pass the first element, not the arrayThe resulting array is exactly the same as created by the QB Get statement.
myput.8 - Here's how you put the graphics back onto the screen.
MyPut - My put uses an array as created with the QB get or myget, and puts it on the screen, with one major difference...all pixels that are color #0 are not put to the screen, they become transparent.
Every good game needs a transparent put routine, be it using a screen mask or not putting a particular color, whatever it is, every game needs one.
SolidPut - This routine is the same as the QB put using the PSET option. The reason I use my own routines for getting and solid putting is that I often need to change the video segment for use with an off screen buffer for flicker free graphics.
mylib.bi - declarations for all my assembler routines.
Declare Sub MouseInit() Declare Sub MouseCross() Declare Sub ShowMouse() Declare Sub HideMouse() Declare Function lButton%() Declare Function rButton%() Declare Function MouseX%() Declare Function MouseY%() Declare Sub XorLine(BYval X%, BYval Y%) Declare Sub XorBox(Byval X1%, Byval Y1%, Byval X2%, Byval Y2%) Declare Sub MyGet(Byval Segment%, Byval X%, Byval Y%, Byval W%, Byval H%, SEG Array) Declare Sub MyPut(Byval Segment%, Byval X%, Byval Y%, SEG Array) Declare Sub SolidPut(Byval Segment%, Byval X%, Byval Y%, SEG Array)mylib.lib - The object code library containing all the assembly routines.
Contained herein are all the object files for all the .8 files included in this packet. Using these you can create quick libraries, and link them to your programs after compilation.
pcx.bas - a very good pcx loader written by Jonathan Leger.
I got this one from an ABC packet. For those of you who don't know what ABC is, search the web for "All BASIC Code", you'll be glad you did. My own PCX loader was written in C and I didn't want to throw a third language at you at this point.
pcxget.mak - MAK files allow QB to load multiple files
For those of you who use QB45 and have never used the load file and create file choices on you menu bar...A MAK file is simply a text file that has a list of other files within it. If you load a MAK file into QB instead of a BAS file, it will load all the files that are listed within. When you choose to create an EXE file within the IDE, QB will compile and link all the files together. That way you can segregate your code into self contained modules.
Test.pcx - It's just a PCX file I included so you could test the program.
pcxget.bas - This file is the main focus of this article, I'll run down the code to give you some idea of the crazy things I do.
'Always include this: DEFINT A-Z 'Include my external routines: '$INCLUDE: 'mylib.bi' DECLARE SUB ShowPCX (file$) DIM OldX, OldY DIM NewX, NewY '$DYNAMIC DIM img(0) AS INTEGER DIM Bak(0) AS INTEGER CLS COLOR 15 'Just printing some instructions for the hapless user PRINT PRINT PRINT "PCXGet allows you to load a Screen 13 PCX file and select a " PRINT "region to be saved in a QuickBASIC get format." PRINT PRINT "L = Load pcx file" PRINT "S = Save Gotten image as QuickBASIC get" PRINT "Q = Quit" PRINT SLEEP DO: LOOP WHILE LEN(INKEY$) SCREEN 13 'Initialize the mouse MouseInit 'Display the PCX file on the command line IF LEN(COMMAND$) THEN ShowPCX COMMAND$ 'Since the first thing the main loop does is to clear 'the mouse lines before calculating the new postion, 'we need to give it something to clear. XorLine OldX, OldY DO 'Retrieve the new mouse coordinates NewX = MouseX% NewY = MouseY% 'If it has moved IF OldX <> NewX OR OldY <> NewY THEN 'Clear the old lines 'And draw the new XorLine OldX, OldY XorLine NewX, NewY 'Then store the new as old OldX = NewX OldY = NewY END IF 'If the left button goes down IF lButton% THEN 'Clear away the mouse lines XorLine OldX, OldY 'Save the starting position StartX = NewX StartY = NewY 'Draw the first box so we have something to clear XorBox StartX, StartY, NewX, NewY 'Draw boxes while the mouse button is down DO WHILE lButton% 'Get the new coords NewX = MouseX% NewY = MouseY% 'If it has moved... IF OldX <> NewX OR OldY <> NewY THEN 'Clear the old box 'And Draw the new XorBox StartX, StartY, OldX, OldY XorBox StartX, StartY, NewX, NewY 'Save those coords OldX = NewX OldY = NewY END IF LOOP 'Clear the last box drawn XorBox StartX, StartY, OldX, OldY 'Recalc the coords for boxes drawn 'from the top up or from right to left 'xorbox takes this into account, but get doesn't IF StartX > OldX THEN SWAP StartX, OldX IF StartY > OldY THEN SWAP StartY, OldY 'Calculate the array size W = OldX - StartX + 1: H = OldY - StartY + 1 max = (W * H + 5) / 2 'One for the image REDIM img(0 TO max) 'One for the saved background REDIM Bak(0 TO max) 'Get the image MyGet &HA000, StartX, StartY, W, H, img(0) 'Save the coords OldX = NewX OldY = NewY 'Get the background MyGet &HA000, NewX, NewY, W, H, Bak(0) 'Display the gotten image with transparency MyPut &HA000, NewX, NewY, img(0) 'Loop until a button press DO UNTIL lButton OR rButton 'Get the new coords NewX = MouseX% NewY = MouseY% 'If they've changed IF OldX <> NewX OR OldY <> NewY THEN 'Here's a trick: 'Wait until the screen is not being drawn before you 'display your graphics, that'll minimize the flicker DO: LOOP UNTIL INP(&H3DA) AND 8 'Put the background SolidPut &HA000, OldX, OldY, Bak(0) 'Get the background MyGet &HA000, NewX, NewY, W, H, Bak(0) 'Draw the gotten image at the new coords MyPut &HA000, NewX, NewY, img(0) 'save the coords OldX = NewX OldY = NewY END IF LOOP 'Loop until the buttons have been released DO WHILE lButton OR rButton: LOOP 'Replace the background SolidPut &HA000, OldX, OldY, Bak(0) 'Draw the lines so the can be erased again. XorLine NewX, NewY END IF 'Now deal with any button presses. a$ = INKEY$ IF LEN(a$) THEN a$ = UCASE$(a$) SELECT CASE ASC(a$) CASE 27, 81: EXIT DO CASE 76 REDIM Bak(0 TO 2561) 'Load a PCX file. MyGet &HA000, 0, 0, 320, 16, Bak(0) LOCATE 1, 1, 1 INPUT "Filename"; a$ IF LEN(a$) THEN ShowPCX a$ XorLine NewX, NewY ELSE SolidPut &HA000, 0, 0, Bak(0) END IF CASE 83 'Save the gotten array. REDIM Bak(0 TO 2561) MyGet &HA000, 0, 0, 320, 16, Bak(0) LOCATE 1, 1, 1 INPUT "Filename"; a$ IF LEN(a$) THEN OPEN a$ FOR BINARY AS 1 FOR t = 0 TO UBOUND(img) PUT #1, , img(t) NEXT t CLOSE END IF SolidPut &HA000, 0, 0, Bak(0) END SELECT END IF LOOP SCREEN 0
Charles Johnson can be contacted at firstname.lastname@example.org.