-=-=-=-=-=-=-=-=-=-=-=-=-=- = Qbasic = - Developers - = Forum = -=-=-=-=-=-=-=-=-=-=-=-=-=- Issue I Qbasic Developers Forum is a FREE newsletter made for Advanced Basic Programming. BASICA/GW-BASIC, Qbasic, and QuickBasic coding methods are discussed. It is written by Acidus Software. To obtain membership Email me at: LordAcidus@aol.com ************************************ *The FAT (Feature Allocation Table)* ************************************ (editorials) -From the Acid Pool... Acidus Software ideals and current projects (features) -"Ground Control to Major Tom..." Modem Communications in Qbasic -Morphing the Mouse How to create and use your own mouse icons -Player too Supporting multi-player games in Qbasic (every issue) -Face Lift Give speed Computability to your programs -Quick Tips Fast solutions to little annoying programming problems -ABORT Final Thoughts ----------------- From the Acid pool ----------------- Acidus Software is a small computer company that holds the ideal that any action that can be done in a another computer language can be done through BASIC. We are currently designing an OS through Qbasic, as well as a real time war simulation game called Voices of Liberty(tm). A modem turn based game similar to Masters of Orion is also being created. We also have created an FULLY-FUNCTIONABLE BBS through GW-BASIC, Which supports Xmodem Zmodem Uploading and Downloading. -------------------------------- "Ground Control to Major Tom..." -------------------------------- THE MODEM. ohhhh, its scary. To most, its a little magic boxes that opens our computer to the world. In reality, it is very simple to use. The modem takes bytes of info, turns it into a noise, and sends it over a phone line. Theoretically, if you could whistle fast enough and at the right pitches, you could send a file with the sounds you make. See, no voodoo magic stuff. As for sending files, that's easy to. The modem sends a ASCII character to tell the other computer it is sending a file. It then breaks up that file into chunks and sends it in hunks of bytes (128, 1k, it depends). The modem then waits for a ASCII character from the other computer telling it that all is well, and to send the next hunk. Ok, I've had my history lesson now, you say, but how do I do it in Qbasic? SIMPLE. 4 commands are all you need for simple communications. They are: OPEN COM.........Open up the modem to get stuff from BASIC INPUT$...........Gets stuff from the modem LOC..............Tells you if the modem has any stuff from an outside source PRINT #..........Send stuff to the modem To start work with the modem, you have to open a "path" to it. This is just like opening a file, except you use the "OPEN COM" statement. Here is a sample: OPEN "COM2:2400,N,8,1,RB2048,TB2048" FOR RANDOM AS #1 What the #&*! is that?!?, Just like opening a file he says? BULL! Well, here is a secret, Opened Devices (Your modem) act just like opened files. Don't worry, the only thing you need to worry about is the number between "COM" and ":" and the number between ":" and ",N". All the other stuff deals with transmission settings and the RB TB things deal with Uploading and Downloading. The first Number is COM port number your modem is on. Because BASIC was created so long ago, you can only access ports 1 and 2. If you modem is not on COM port 1 or 2, there is a way around that, but it can cause so big time errors with other programs because you manually switch the memory addresses of the COM ports. Don't worry about that now, I will go into that in another issue. The 2nd number is the Baud. BAUD is the speed of the Modem. Qbasic can't access COM ports at any higher speed than 9600, so if you have a 14.4 Modem, Qbasic can still use it, but it won't go any faster than 9600. Note this restriction is for GW-BASIC, BASICA, and QBASIC. I don't know if compiled QuickBasic programs have this restriction but I don't think they do. To send stuff to your modem, you use the PRINT #n command, where n is the file number, in this case 1. But there is no point sending stuff like "Hello Chris" to your modem right now, because you are not connected to anything. All you have done with the "OPEN COM" statement, is made a little path from you modem to your program so they can talk to each other. But you want to talk to an outside source, like a friend or a BBS, you have to tell the modem to dial a number. To do this you must know that ALL modems have a set of commands echoed on to their memory chips that make them do stuff. You can't just say "Hey modem, dial up 770-555-9806", you have to talk in the modem's language. This isn't as bad as it sounds. All commands begin with "AT". Here are some common ones: MODEM SPEECH TRANSLATION ------------------------------------------- "ATDT###-###-####" | "Hey Modem, dial ###-###-####" "ATZ" | "Hey Modem, This sucks, hang up the phone" "ATS0=#" | "Wait until you someone calls and the phone rings | # number of times, then try to connect modems" "ATM2H1L#" | "Set your speaker Volume at # (1-3)" So, if you wanted to call someone first you would use an OPEN COM statement to ready the modem then you would use an INPUT statement to get the phone number to dial as a string, than use PRINT #n to talk to the modem. Here is an example of a simple phone dialer: (replace COM# with the COM port your modem is on) CLS PRINT "Opening a Path to your Modem..." OPEN "COM2:2400,N,8,1,RB7048,TB7048" FOR RANDOM AS #1 PRINT "Please Enter the Number you wish to Call" INPUT PhoneNumber$ PRINT "Talking to your modem..." PRINT #1, "ATDT" ;PhoneNumber$ PRINT "There you go, pick up the phone and talk" PRINT "Press [ESC] to hang up DO LOOP UNTIL INKEY$ = CHR$(27) PRINT #1, "ATZ" But here comes the biggest problem of Modem control with Qbasic, HOW DO I READ WHAT COMES FROM THE MODEM? Well there is a little Function called LOC that does this. The syntax is: LOC(n) n is the file number which if you used my sample, would be 1 LOC tells you where in a file you are. File? I am not accessing a file! you exclaim. As I said before, files and devices work the same way. But with a modem, LOC tells if it has received anything. Fine, now you know if the modem is getting stuff, put how do you know what it is getting? For that you use the INPUT$(x,y) function. INPUT$(x ,y) x is the number of bytes to get from a file/device y is the number of the opened file/device "x" Should ALWAYS be 1. I know this means that only 1 character can be read on each pass, but this way EVERY character is read, and none are skipped. If you were getting an 11 byte transmission, and "x" was 2, only the first 10 characters would be read (because it is a multiple of 2) the last part would be skipped. This is way for NORMAL communications, keep "x" as 1. I will going into Downloading in a later newsletter, in which "x" is not 1. One last thing I will talk about is the "ATS0=#" command. You can use this to wait for a call Alright, I will put this all together give you a fully commented communications program. You can use this to call up any BBS and interact with it. Note I have not included any Downloading Abilities. I will Address this as well as uploading through Qbasic using the Xmodem and Zmodem protocols later: '------------------------- 'Conn-X ver 1.0 CLS PRINT "Conn-X Ver 1.0 Acidus Software(tm)" PRINT "What COM port is your Modem on?" INPUT ">", port$ baud$ = "9600" '9600 should work with most computers. If you have 'an older one use "2400" 'Open up that com port OPEN "COM" + port$ + ":" + baud$+ ",N,8,1,RB2048,TB2048" FOR RANDOM AS #1 PRINT "You can:" PRINT "1-Call someone" PRINT "2-Wait for a call" PRINT "3-Quit" DO a = VAL(INKEY$) LOOP UNTIL a >= 1 and a <= 3 'Get choice IF a = 3 THEN CLOSE : SYSTEM IF a = 2 THEN GOTO wait PRINT "Number to call?" INPUT ">", number$ PRINT #1, "ATDT" + number 'tell the modem to dial the number GOTO chat wait: PRINT #1, "ATS0=1" 'tell modem to connect after 1 ring 'When a modem connect it returns "CONNECT ####" 'The next hunk of code waits until the modem connects before moving on a$ = "" DO IF LOC(1) THEN a$ = a$ + INPUT$(1, 1) 'if anything in modem add it to a$ LOOP UNTIL INSTR(a$, "CONNECT") 'Wait until modem have Connected chat: 'If you where waiting for a call, a lot of ASCII character will be printed 'on the screen. Don't worry, that just the computers getting in sync and 'talking 'You also will not see what you type CLS PRINT "Conn-X CHAT, press [ESC] to quit DO t$ = INKEY$ IF LEN(t$) THEN PRINT #1, t$ 'if you typed something send it to the modem 'this will be send by the modem to the other 'computer IF LOC(1) THEN r$ = INPUT$(1, 1)'if the is something to get, get it and save 'it as r$ IF LEN(r$) THEN PRINT r$; 'if r$ <> "" then print it. the ";" means a 'line is not started LOOP UNTIL t$ = CHR$(27) 'keep doing this until [esc] is pressed PRINT #1, "ATZ" 'tell the modem to hang up CLOSE 'close the open com statement '------------------------- There you go, a simple communications program through Qbasic that lets you talk to another computer. As I said Uploading/Downloading will be discussed in a later newsletter ------------------ MORPHING THE MOUSE ------------------ Qbasic doesn't normal support mouse programming, but through "CALL ABSOLUTE" the mouse can be accessed. But when you use it, you get an Arrow looking thing, like in Windows. But in other games, you don't have just one type of cursor. In "Command & Conquer", You have a wrench cursor to repair stuff, a bull's eye to attack things, and so on. Even in windows you have that hour glass. When I began programming "Voices Of Liberty" for Acidus Software, (Which is a real-time war simulator, similar to "Command & Conquer"), I wanted to make it look as professional as possible. So I need a way to make different cursors other then an arrow. After thinking about it for a very long time, I realized that answer was so simple. I then spent about 6 hours programming and perfecting, turning my prototype into a series of "SUBs" that can be used in any program. To change the cursor, I had to use "GET" and "PUT" using the mouse's x and y coordinates. Now, before I go on, you have to know how the mouse works in Qbasic. First off, the x and y coordinates are for ONE pixel above the tip of the arrow. Also, the "CALL ABSOLUTE" that is used to access the mouse returns that the screen is always 640x200. This means modifications are necessary. "Voices of Liberty" uses Screen 1 because I am writing it for CGA. Screen 1 is 320x200. when I use CALL ABSOLUTE to get the current x coordinate, it returns say, 360. To find out where on a 320x200 screen it is you must divide that by 2 (640/2=320) This converts the x coordinate into Screen 1 dimensions. Even in Text mode, you have to divide the x coordinate by 8 to get the column number (640/8=80 columns), and you must divide the y coordinate by 8 as well to get the correct row (200/8=25 rows). Here is a slice of code from Acidus Software's "Voices of Liberty"(tm) which uses custom made pointers '------------------------- 'POINTER1.BAS 'Prototype ver 3.7 of Voices Of Liberties 'The following are SUB programs that WEREN'T written by Acidus Software 'They simply load the mouse and check it x/y coordinates DECLARE SUB MouseDriver (ax%, bx%, cx%, dx%) DECLARE FUNCTION Initialize% () DECLARE SUB CursorOff () DECLARE SUB cursoron () DECLARE SUB GetMouse (lb%, rb%, xMouse%, yMouse%) DECLARE SUB LocateCursor (x%, y%) 'The following are SUB used to make your own cursor DECLARE SUB LoadPointer (load$) DECLARE SUB MovePointer (xM%, yM%) DIM SHARED PointerX DIM SHARED PointerY DIM SHARED DimX 'how many pixels wide the cursor is DIM SHARED DimY 'how many pixels tall the cusur is DIM SHARED pointer(100) 'The cursors image DIM SHARED UnderPointer(100) 'image of what is under the cursor 'The following allows for mouse use in Qbasic. It is NOT written by Acidus 'Software DIM SHARED Mouse$ Mouse$ = SPACE$(57) FOR i% = 1 TO 57 READ a$ H$ = CHR$(VAL("&H" + a$)) MID$(Mouse$, i%, 1) = H$ NEXT i% DATA 55,89,E5,8B,5E,0C,8B,07,50,8B,5E,0A,8B,07,50,8B DATA 5E,08,8B,0F,8B,5E,06,8B,17,5B,58,1E,07,CD,33,53 DATA 8B,5E,0C,89,07,58,8B,5E,0A,89,07,8B,5E,08,89,0F DATA 8B,5E,06,89,17,5D,CA,08,00 CLS ms% = Initialize% IF NOT ms% THEN PRINT "Mouse not found" END END IF 'GOTO bypass 'UN comment this line after you run the program once 'the following lines of code make a cursor 'I make all my pointers 10x10 SCREEN 1 CLS FOR y = 1 TO 10 FOR x = 1 TO 10 READ r PSET (x, y), r NEXT NEXT GET (1, 1)-(10, 10), pointer DEF SEG = VARSEG(pointer(0)) BSAVE "cursor1.cpf", 0, 100 DATA 3,0,0,0,0,0,0,0,0,3 DATA 0,3,0,0,0,0,0,0,3,0 DATA 0,0,3,0,0,0,0,3,0,0 DATA 0,0,0,3,0,0,3,0,0,0 DATA 0,0,0,0,0,0,0,0,0,0 DATA 0,0,0,0,0,0,0,0,0,0 DATA 0,0,0,3,0,0,3,0,0,0 DATA 0,0,3,0,0,0,0,3,0,0 DATA 0,3,0,0,0,0,0,0,3,0 DATA 3,0,0,0,0,0,0,0,0,3 ByPass: SCREEN 1 'This creates a random background for you to move your cursor over: CLS RANDOMIZE TIMER FOR a = 1 TO 100 xx = INT(RND * 300) + 1 yy = INT(RND * 180) + 1 cc = INT(RND * 2) + 1 LINE (xx, yy)-(xx + 20, yy + 20), cc, BF NEXT 'To call a custom cusur (which I call pointers) you must first load 'it. Be sure to put the correct wide and tall DIMs, because your cursor is 'centered on the tip of the default arrow head. 'You must also always turn off the cursor before loading a custom pointer DimX = 10 '10 pixels wide DimY = 10 '10 pixels tall LoadPointer "Cursor1.cpf" pointerloop: DO x = xMouse% y = yMouse% CALL GetMouse(lb%, rb%, xMouse%, yMouse%) IF x <> xMouse% OR y <> yMouse% THEN MovePointer xMouse%, yMouse% LOOP UNTIL INKEY$=CHR$(27) STOP SUB CursorOff 'Turns off the mouse cursor ax% = 2 MouseDriver ax%, 0, 0, 0 END SUB SUB cursoron 'Turn on the mouse cursor ax% = 1 MouseDriver ax%, 0, 0, 0 END SUB SUB GetMouse (lb%, rb%, xMouse%, yMouse%) ax% = 3 MouseDriver ax%, bx%, cx%, dx% lb% = ((bx% AND 1) <> 0) rb% = ((bx% AND 2) <> 0) xMouse% = cx% yMouse% = dx% END SUB FUNCTION Initialize% ax% = 0 MouseDriver ax%, 0, 0, 0 Initialize% = ax% END FUNCTION SUB LoadPointer (load$) 'Load the image of your custom pointer DEF SEG = VARSEG(pointer(0)) BLOAD load$, 0 DEF SEG 'Find out the current x,y coordinates of the mouse CALL GetMouse(lb%, rb%, xMouse%, yMouse%) 'The next statements make it so the center of your pointer is the x,y 'coordinates of the mouse by dividing the width and height by 2 'the xMouse%/2 is used to get the x coordinate into Screen 1 dimensions 'PointerX and PointerY are the top left coordinates of the pointers image. 'They are the top left corner because that is where the image must be PUT 'so that the center of the pointer is the returned x,y Mouse coordinates PointerX = (xMouse% / 2) - INT(DimX / 2) PointerY = yMouse% - INT(DimY / 2) LOCATE 1: PRINT PointerX, PointerY 'The following make sure you stay inside the bounds of the screen 'in screen mode 1, the dimensions are 320x200, so change the following 'numbers if you have different dimensions (like 640x200) IF PointerX < 1 THEN PointerX = 1 IF PointerX > 319 - (DimX) THEN PointerX = 319 - (DimX) IF PointerY < 1 THEN PointerY = 1 IF PointerY > 199 - DimY THEN PointerY = 199 - DimY 'The next statement saves the image of what will be under your pointer GET (PointerX, PointerY)-(PointerX + DimX, PointerY + DimY), UnderPointer 'The next statement places your pointer at that location PUT (PointerX, PointerY), pointer, OR END SUB SUB LocateCursor (x%, y%) 'This moves your ax% = 4 cx% = x% dx% = y% MouseDriver ax%, 0, cx%, dx% END SUB SUB MouseDriver (ax%, bx%, cx%, dx%) DEF SEG = VARSEG(Mouse$) Mouse% = SADD(Mouse$) CALL Absolute(ax%, bx%, cx%, dx%, Mouse%) END SUB SUB MovePointer (xM%, yM%) 'This sub is called when the mouse is moved to a new position '1st replace the background PUT (PointerX, PointerY), UnderPointer, PSET 'calculate the new location of the top right corner of your pointer 'the xM%/2 is to get the right Dimensions for screen 1 PointerX = (xM% / 2) - INT(DimX / 2) PointerY = yM% - INT(DimY / 2) 'Make sure PointerX and PointerY are within Screen Dimensions IF PointerX < 1 THEN PointerX = 1 IF PointerX > 319 - (DimX) THEN PointerX = 319 - (DimX) IF PointerY < 1 THEN PointerY = 1 IF PointerY > 199 - DimY THEN PointerY = 199 - DimY 'Save the image under the pointer GET (PointerX, PointerY)-(PointerX + DimX, PointerY + DimY), UnderPointer 'place the pointer PUT (PointerX, PointerY), pointer, OR END SUB '------------------------- There you go. This method is very useful, and with a little tinkering you could make it support many different custom cursors. ---------- PLAYER TOO ---------- Games. They pass the time. But think video games. What kind are a lot of fun? The kind where you play with/against another human player. Human players add an element of surprise to each game. They learn from they past mistakes, and adopt new styles. Most Computers have a RANDOM type feature in them somewhere, no matter how complex the Artificial Intelligence is. So, to build multi-player, and thus, more enjoy programs, Game coders MUST learn how to program for to players. There are only 2 ways to have multi-player games in Qbasic, as on any computer. One way is to play over a Modem/Network. The other way is to have both characters play on the same computer. Many people overlook how hard it is to support 2 players evenly in Qbasic. A method I always see is both players use the same keyboard. This method looks good, but doesn't work at all. Look at this example from a never released Acidus Software game "Fighter's DANCE": StartOfGameLoop: ...blah... a$ = UCASE$(INKEY$) if a$ = "4" THEN 'move player 1 left if a$ = "6" THEN 'move player 1 right if a$ = "0" THEN 'player 1 punches if a$ = "A" THEN 'move player 2 left if a$ = "D" THEN 'move player 2 right if a$ = "\" THEN 'player 2 punches ...blah... ...blah... GOTO StartOfGameLoop There are BIG TIME flaws to this method. First, only one player can move only once with ever GameLoop pass. Second, if say, Player 1, holds down [6], then his character will move and Player 2 doesn't get the chance to do anything. The ONLY way to have 2 players play the keyboard is to use a lot of "ON KEY(n) GOSUB label" statements. Another way to have 2 players play on the same computer is if you have 2 joysticks. Use the STICK(n) and STRIG(n) functions. The only way to Avoid the problem of one player hogging the input such as in "FIGHTERS DANCE", you have to use these 2 methods. A final way to have multi-player games is to use a Modem/Network. Now, Networks are hard as #%&* to deal with. Since I don't have one at home I can fool around with, I will not go into any form of Network programming right now. Modems, however, I know quite a bit about. If you don't, read the article entitled "Ground Control to Major Tom..." in this issue. There are really 2 forms of Multi-player games using a Modem. 1-Real time This means the same program is running on 2 different machines. Both players go at the same time, and the Computer constantly talk to each other updating each players status. This is how almost all the newer games are. "Warcraft", "Command & Conquer", "Doom", and "Quake" are all examples of Real-time modem games. Real-time modem games are fun to play, because you must react instantly to the other players moves. Real time also makes your game run slower, because it must often (Normal once through each game loop), send and receive game information. In complex games, transporting large strings of info becomes a problem. I talk later on how to compress data, but I am currently working on saving the game data as a binary file and sending the file across the modem. I will go more into sending game data in file for in a later issue. 2-Turn based This means a player does all his moves, then another player does all his moves. This is method is a older way of multi-player programming, but is still quite good in the right situations. It is also faster because the modem doesn't have to transmit ever second. It is only suited for some games because you don't see what your opponent did until your next turn. There are 2 ways to do this method. 2a-Turn based dual action In this Both player are playing their game at once at there on separate machines. The players do their stuff then select the end of their turn. The computers then transmit all the new data. The next turn begins with both players see what the other did last turn, and react. If one player ends their turn before another player, they must wait until the other is done before the new data arrives. 2b-Turn based single action Each player moves one at a time, and then end their turn. At the end of the turn, The computers transmit new data, and the next person can go. This was the first kind of multi-player games, and are very slow because you have to wait until the other person is done before you can move. This method is good for games like chess, and other board-game type games. Simple war games are also programmed using this method. To transmit game information over a modem, you must have a way to tell where a number ends, and where it begins. Else "8210" could be a 8 and 210, 82 and 10, or 821 and 0. I don't recommend sending strings of actual text across a modem, because of the extra time it takes. Now for an example. Lets say I am playing a racing game and I need to tell send only my current place, speed, and distance traveled to the other computer. I would use my system of make all these number 3 digits, so that way since all the numbers are 3 digits long, the other computer can extract them corrected. lets say I'm in 3rd place, going 127mph, and I've gone 41 miles so far. I would make 3 be 003, the speed can stay as is because it is already 3 digits, and distance would become "041" This FUNCTION returns a string representation of any number pumped into it as a 3 digit string. FUNCTION MakeString$(x) if x>=1000 then print "Number more than 3 digits" MakeString$="" exit function end if if x>=100 then '3 digit MakeString$=mid$(str$(x),2,3) 'the mid$ function is used because when ever 'you convert a number to a string, a space is 'automatically added to the beginning by BASIC 'The mid$ is used to bypass it so your string 'contains only numbers exit function end if if x>=10 then '2 digits MakeString$="0"+mid$(str$(x),2,2) exit function end if 'Must be 1 digit MakeString$="00"+mid$(str$(x),2,1) end function I then would piece these 3 integers into a single string of numbers to transmit. I would use a code tidbit thing this: SendMe$=MakeString$(place)+MakeString$(speed)+MakeString$(distance) Using the numbers I gave before, you would send a string that looked like this: "003127041" You than send it over the modem using PRINT #n,SendMe$. The receiving computer gets the values you sent by a code hunk similar to the following. receive$="" Do if loc(1) then receive$=input$(loc(1),1) loop until len(receive$)=9 'wait until all numbers are received 'Extract the values from ,the received string OtherPlayersPlace=val(mid$(receive$,1,3)) OtherPlayersSpeed=val(mid$(receive$,4,3)) OtherPlayersDistance=val(mid$(receive$,7,3)) There you go, a simple way to transmit game information over the modem thus created multi-player games. ---------- Face Lift ---------- Don't you hate it when you are playing some game for Basic and you get a "Enter Speed (1-9000)"-type message. It is SO unprofessional. It means some where in the program (probably at the end of the main loop) is a little bit of code such as: FOR delay = 1 to NumberTheyEnteredForSpeed NEXT This is so the program won't go to fast on some computers. Thetas all well and good, but it is pointless to ask the user when you can do it yourself. It is like asking the user to seed the RANDOMIZE statement. But, if you don't ask, then how do you know how long to delay at the end of the main game loop you may wonder. Well that is easy too, here's what you do: 1-edit you code to this: ... Opening crap like DECLAREs and ON...GOTOs ... TIMER ON 'add this MainGameLoop: start = TIMER 'add this ... Your game logic stuff ... FOR delay = 1 to NumberTheyEnteredForSpeed 'The delay loop NEXT LOCATE 1 : PRINT TIMER - start 'add this GOTO MainGameLoop 2-Run the program many times, and enter a new number for speed each time until you get a speed that is not to fast and not to slow for the game. Once you get it, look at top right corner of the screen and write down the number that keeps appearing there. (For the example's sake, we are going to say it was .34) 3-Re-edit your code to look like this ... Opening crap like DECLAREs and ON...GOTOs ... TIMER ON MainGameLoop: start = TIMER ... Your game logic stuff ... DO : LOOP UNTIL TIMER - start > .34 GOTO MainGameLoop Of course, your number probably won't be .34, so replace it with your number The DO : LOOP waits for .34 seconds from the start of the MainGameLoop until it makes another pass. 4-How does this work? Here is an Example: Bob is playing PACMAN(tm) on an INTEL Pentium II 300mhz It takes the Pentium .09 seconds to go through the whole game loop. The program then sits in the DO : LOOP thingy for .25 seconds (.34 - .09=.25) The program then goes back to the start of the Main Game loop and repeats. Bill is Playing PACMAN(tm) on a 386sx 33mhz It takes the 386sx .20 seconds to go through the whole game loop. The program then sits in the DO : LOOP thingy for .14 seconds (.34 - .20=.14) The program then goes back to the start of the Main Game loop and repeats. No matter what Computer is used, the Main Game Loop is only run once every .34 seconds. What happens if you have a really slow computer? Dave is poor and has to run a copy of PACMAN(tm) that he stole on a IBM XT (c. 1984) It takes the piece-of-s...er...XT .8 seconds to go through the main game loop Then program hits the DO : LOOP thingy and says "Oh, .8 seconds have passed, so I don't have to wait at all in the DO : LOOP thingy, because it took me longer than .34 seconds because I suck. I'll just exit the DO : LOOP thingy and go back to the start of the main game loop!" This makes the program look better, and run the same on all computers. Now, I know you will NEVER come across an old XT, 386s are all but dead, and 486s are slowly dying; but this is still helpful because a Pentium 75mhz is much slower then a Pentium MMX 166mhz which is slower than a Pentium II 333Mhz. ---------- Quick Tips ---------- How do I turn off the Keyboard? This is a wicked trick to do to someone; can be used as a trick by Compiling this to an EXE and adding it to a AUTOEXEC.BAT; and makes a great addition to a Security Program if the wrong Password is entered. To disable all input from the Keyboard: DEF SEG = 64 OUT 97, 204 To Re-able the Keyboard: DEF SEG = 64 OUT 97, 76 ******BE CAREFUL******** You can EASILY lock yourself out and have to Reboot to use your keyboard again How Do I Reboot a Computer? There are many way to do this, But I know of 3 that ALWAYS work, and have been tested on XT's, 386's, 486's, and Pentiums. Here you go: For a COLD BOOT: SHELL "ECHO G = FFFF:0000 | DEBUG" 'This only works if DEBUG.EXE is in a 'pathed directory -or- The Shell one is slower than the next method. It also leave 2 files behind with gibberish names that Debug makes. DEF SEG = &HFFFF 'This is the better one CALL Absolute(0) For a WARM BOOT: DEF SEG = 0 POKE &H473, &H12 'FLAG FOR WARM BOOT POKE &H472, &H34 'FLAG FOR WARM BOOT DEF SEG = &HFFFF CALL Absolute(0) How do I Center Text? This is another SUB I use all the time that gives your programs a very professional look. There are 2 versions of this. Cen40 is mainly used in SCREEN mode 1, 7, and 13, but it just depends on the WIDTH setting. Use Cen on 80 WIDTH 'SUB Cen(txt$, row) LOCATE row, 39 - LEN(txt$) / 2: PRINT txt$ END SUB 'SUB Cen40(txt$, row) LOCATE row, 19 - LEN(txt$) / 2: PRINT txt$ END SUB ----- ABORT ----- Well, that's the first issue of Qbasic Developers Forum. I hope I was able to help you expand your ability to program, and to create better programs. If you like what you learned, let me know. My Email address is LordAcidus@aol.com or snail mail Billy Hoffman c/o Acidus Software 1349 Garrick Way Marietta GA, 30068 Oh, I have a BBS that runs from 12am until 12pm EST, open to the public Sept 1, 1998 Hack the planet, Acidus