--------------- FIDO MESSAGE AREA==> TOPIC: 125 QUICK BASIC Ref: F5G00173 Date: 04/17/98 From: DAVID AUKERMAN Time: 08:03pm \/To: ALL (Read 3 times) Subj: Code FAQ 04/98 03/12 '>>> Page 3 of CODE0498.FAQ begins here. CALL NewSub(Name$) NewSub Name$ Any number of variables can be passed to a SUB: CALL NewSub(Name$, Text$, File$) NewSub Name$, Text$, File$ However, the SUB statement you made at the very beginning must match *exactly* the TYPEs and number of variables in the calling statement: SUB NewSub(Name$, Text$, File$) PRINT Name$ PRINT Text$ PRINT File$ END SUB Notice that if you had SUB NewSub(Name$, Text$, File%), QB would not be able to run your program, since the TYPE of the third variable wouldn't be the same in both places. The SUB would be expecting an integer, but it would get a string. That isn't good. Those are the basics of SUBroutines. FUNCTIONs are *exactly* the same as SUBs, with one major difference. A FUNCTION returns a value to the calling procedure, and therefore must be used differently. For instance: a! = GetANumber! PRINT GetANumber! * 4 FUNCTION GetANumber! INPUT "Enter any number"; GetANumber! END FUNCTION This will ask for a number, ask for another number, and print the value of four times the second number. Parameters (the variables which are passed to a SUB or FUNCTION inside the parentheses) work the same way here, too: PRINT NewName$("David") FUNCTION NewName$(OldName$) PRINT "The old name was "; OldName$; "." INPUT "Enter the new name: ", NewName$ END FUNCTION Notice that there is only one legal way to call a FUNCTION, and to use parameters when calling a FUNCTION. One more note: once you save a file after creating one of the above examples, QB will put a DELCARE statement at the beginning of the program. For instance: DECLARE SUB NewSub (Name$, Text$, File$) If you change the parameter list after this statement is created, you will have to edit this statement manually to match the new parameter list. 3) HOW CAN I PRODUCE A DELAY IN MY PROGRAM INDEPENDENT OF THE CPU SPEED? Bill White answers this question very well: [quote] In the past there has been extensive discussion in this echo of this subject, which brought out some problems. A summary of this discussion follows: The old BASIC statement we used for years: FOR i=1 to 1000: NEXT i is not independent of the CPU speed. You could use SLEEP 2. This, however, has problems: integer numbers only, user can hit _ANY_ key to jump out of it, and that key will be held in the buffer waiting to bite the next INPUT command unless dumped with DO: LOOP UNTIL INKEY$ = "" '>>> Page 3 of CODE0498.FAQ ends here. Continued on next page. ___ * SLMR 2.0 * DO: LOOP UNTIL Bored --- Maximus/2 3.01 * Origin: The I.O. Board - 4GB -X< Anderson, IN >X- V34+ (1:2255/10) --------------- FIDO MESSAGE AREA==> TOPIC: 125 QUICK BASIC Ref: F5G00174 Date: 04/17/98 From: DAVID AUKERMAN Time: 08:03pm \/To: ALL (Read 3 times) Subj: Code FAQ 04/98 04/12 '>>> Page 4 of CODE0498.FAQ begins here. TIMER can be used - it ticks off 1/18.2 of a second: delay = 2 finish = TIMER + delay DO LOOP UNTIL TIMER => finish This works, but has a fatal midnight flaw: the timer is reset to 0 at midnight and it is therefore possible that "finish" will never be reached. It's easy to test for midnight (there are 86,400 seconds in a day). I leave that as an exercise for the reader! However, it is easy not to depend on TIMER to do the counting - count it yourself. Something like: delay = whatever t! = INT(TIMER) DO IF t! <> INT(TIMER) THEN t! = INT(TIMER) count = count + 1 PRINT count END IF LOOP UNTIL count = delay This routine doesn't care what the reading of TIMER is, only that it has changed. A roll-over at midnight is just as valid a change as an increase of one second. If you want 1/10's of a second, you'll need to alter accordingly. Since TIMER ticks at 18.2 times a second, the smallest interval will be 0.0549450549 sec. [end quote] If you're looking for something else, Chad Beck suggests the following: [quote] Here's a delay routine[...]: it's small, it doesn't use floating point values, it has an 18th of a second accuracy, and it accounts perfectly for midnight. Num18ths is the number of 18ths of a second that you want to delay for. [In other words, Num18ths is the number of seconds you want in the delay, times 18 -- or, more accurately, 18.2.] DEFINT A-Z DEF SEG = 0 FOR Delay = 1 to Num18ths Timr = PEEK(&H46C) 'Read BIOS timer tick count DO LOOP WHILE Timr = PEEK(&H46C) NEXT [end quote] 4) WHAT ARE INTERRUPTS, AND HOW DO I USE THEM? INTERRUPTs are built-in general purpose functions that can be accessed by a programmer of most every language. They can really give you some nice results. Essentially, they add many useful functions to the Basic language. One note here: they MUST be done in QuickBasic, as QBasic doesn't have the CALL INTERRUPT option. Also, you *must* load QB with the /L switch. The first step is to include the QuickBasic include file at the beginning of your program: '$INCLUDE: 'qb.bi' This includes the TYPE definitions required to use the INTERRUPTs. The next statement to enter, when you want to use an INTERRUPT, is: DIM InReg AS RegType, OutReg AS RegType You can call InReg and OutReg anything you want, but it's a good idea to make sure you know which is "in" and which is "out." You can now assign values to the AX, BX, CX, DX, BP, SI, DI, and Flags "registers" now defined in InReg and '>>> Page 4 of CODE0498.FAQ ends here. Continued on next page. ___ * SLMR 2.0 * C:\DOS C:\DOS\RUN RUN\DOS\RUN --- Maximus/2 3.01 * Origin: The I.O. Board - 4GB -X< Anderson, IN >X- V34+ (1:2255/10) --------------- FIDO MESSAGE AREA==> TOPIC: 125 QUICK BASIC Ref: F5G00175 Date: 04/17/98 From: DAVID AUKERMAN Time: 08:03pm \/To: ALL (Read 3 times) Subj: Code FAQ 04/98 05/12 '>>> Page 5 of CODE0498.FAQ begins here. OutReg. InReg has registers that go into the INTERRUPT; from the elements in InRegs, the INTERRUPT reads what it's supposed to do. OutReg has the registers that the INTERRUPT passes any output to. To assign values to the registers, do it just like a normal variable: InReg.ax = &H4F02 'stands for 4F02, base-16 (HEX) InReg.bx = &H101 'stands for 101, base-16 (HEX) Once you have the appropriate values in the registers, you are ready to call the INTERRUPT. CALL INTERRUPT(IntNumber, InReg, OutReg) IntNumber is the number of the INTERRUPT you want to call. For example, 10h (10, base-16 or HEX) is the screen INTERRUPT, so you'd call it like this: CALL INTERRUPT(&H10, InReg, OutReg) Any values that the INTERRUPT is supposed to return will be stored in the OutReg, and can be checked like you would any other variable: IF OutReg.Bx = 10 THEN [...] A note: QB also offers the CALL INTERRUPTX function, which does the exact same thing as CALL INTERRUPT, but it allows you to use two extra registers, DS and ES. Everything is exactly the same as CALL INTERRUPT except that you add an "X" on the end. So, this is all great you say, but how do I figure out which INTERRUPT will do what? Good question. You can't, on your own. It's a severe understatement to say that poking around with INTERRUPTs is pretty dangerous if you don't know what you're tapping into. You're going to need to keep your eyes open for a list of INTERRUPTs, and there are a lot of them -- INTs, that is. The best list out there is Ralf Brown's Interrupt List, which should be on any program-oriented BBS in your area. Check out the No-Code FAQ for more information on where to find it (or do a web search for it). 5) CAN I USE INTERRUPTS WITH QBASIC? Thanks to Hans Lunsing, yes! This routine uses QBasic's CALL ABSOLUTE feature to run the appropriate machine code so that if you're stuck with QBasic, you too can enjoy the power of INTERRUPTs: [begin] ' InterruptX ' Interrupt procedure for QBASIC ' By Hans Lunsing ' Edited to fit FidoNet QB Code FAQ by Dave Shea ' -------------------------------------------------------------- DEFINT A-Z ' Register type for use with InterruptX TYPE RegTypeX Ax AS INTEGER BX AS INTEGER CX AS INTEGER DX AS INTEGER BP AS INTEGER SI AS INTEGER DI AS INTEGER Flags AS INTEGER DS AS INTEGER ES AS INTEGER END TYPE CONST FALSE = 0, TRUE = NOT FALSE DECLARE SUB InterruptX (IntNo%, Inreg AS RegTypeX, OutReg AS_ RegTypeX) '--------------------------------------------------------------- ' The machine code of the interrupt procedure for InterruptX '--------------------------------------------------------------- InterruptXASM: '>>> Page 5 of CODE0498.FAQ ends here. Continued on next page. ___ * SLMR 2.0 * Veni Vedi Velcro: I came, I saw, I stuck around. --- Maximus/2 3.01 * Origin: The I.O. Board - 4GB -X< Anderson, IN >X- V34+ (1:2255/10) --------------- FIDO MESSAGE AREA==> TOPIC: 125 QUICK BASIC Ref: F5G00176 Date: 04/17/98 From: DAVID AUKERMAN Time: 08:04pm \/To: ALL (Read 3 times) Subj: Code FAQ 04/98 06/12 '>>> Page 6 of CODE0498.FAQ begins here. ' Number of bytes DATA 190 ' Hexadecimal representation of machine code DATA 55,8B,EC,8B,5E,0C,8B,17,0A,F6 DATA 74,07,C7,07,FF,FF,E9,A7,00,8B DATA 5E,06,8B,1F,2E,88,97,77,00,32 DATA C0,80,FA,25,74,05,80,FA,26,75 DATA 02,0C,02,50,1E,06,56,57,9C,8B DATA 76,0A,80,FA,20,7C,05,80,FA,30 DATA 7C,0A,81,7C,08,FF,FF,74,03,8B DATA 6C,08,8B,44,0E,25,D5,0F,50,8B DATA 04,8B,5C,02,8B,4C,04,8B,54,06 DATA 8B,7C,0C,FF,74,0A,81,7C,12,FF DATA FF,74,03,8E,44,12,81,7C,10,FF DATA FF,74,03,8E,5C,10,5E,9D,CD,00 DATA 55,8B,EC,9C,83,C5,0E,F6,46,FE DATA 02,74,02,45,45,1E,56,8E,5E,FC DATA 8B,76,08,89,04,89,5C,02,89,4C DATA 04,89,54,06,8F,44,0A,89,7C,0C DATA 8F,44,10,8C,44,12,8F,44,0E,8F DATA 44,08,F6,46,FE,02,74,02,44,44 DATA 9D,5F,5E,07,1F,58,5D,CA,08,00 '--------------------------------------------------------------- ' Example: get current video mode '--------------------------------------------------------------- DIM r AS RegTypeX r.ax = &HF00 InterruptX &H10, r, r VideoMode = (r.ax AND &HFF) PRINT "Video mode is "; VideoMode END SUB InterruptX (IntNo AS INTEGER, Inreg AS RegTypeX, OutReg AS_ RegTypeX) STATIC '--------------------------------------------------------------- ' Interrupt procedure. Works in the same way as its QB 4.5 ' counterpart.