Volume 1, Number 4 September 3, 1990 ************************************************** * * * QBNews * * * * International QuickBASIC Electronic * * Newsleter * * * * Dedicated to promoting QuickBASIC around * * the world * * * ************************************************** The QBNews is an electronic newsletter published by Clearware Computing. It can be freely distributed providing NO CHARGE is charged for distribution. The QBNews is copyrighted in full by Clearware Computing. The authors hold the copyright to their individual articles. All program code appearing in QBNews is released into the public domain. You may do what you wish with the code except copyright it. QBNews must be distributed whole and unmodified. You can write The QBNews at: The QBNews P.O. Box 507 Sandy Hook, CT 06482 Copyright (c) 1990 by Clearware Computing. The QBNews Page i Volume 1, Number 4 September 3, 1990 ---------------------------------------------------------------------- T A B L E O F C O N T E N T S 1. From the Editors Desk CONTEST !!!! ................................................. 1 2. Beginners Corner The BASICS of QB'S Serial Communications by Ranjit Aiyagari .. 2 3. Who ya gonna call? CALL INTERRUPT Using a FOSSIL - Take 1 by Hector Plasmic .................... 6 Using a FOSSIL - Take 2 by Chris Wagner ..................... 7 4. Product Announcements Index Manager for PDS 7 and Networks ......................... 10 Don Malin's Cross Reference Program .......................... 11 5. Power Programming Peeking at DOS with P.D.Q. by Ethan Winer .................... 13 A Pop-Up Communications Program with PDQ by Dave Cleary ...... 18 A Pop-Up Calculator with Stay-Res by Larry Stone ............. 20 6. Algorithms Improving your IQUEUE by Jim Mack ............................ 23 7. Input Past End Get the QBNews on Disk ...................................... 29 Contacting the QBNews ....................................... 30 The QBNews Page ii Volume 1, Number 4 September 3, 1990 ---------------------------------------------------------------------- F r o m t h e E d i t o r s D e s k ---------------------------------------------------------------------- Free software to a lucky QBNews reader!!! In order to gauge readership anmd distribution of this newsletter, I am holding a contest. One lucky reader will get his choice of either Crescent Softwares PDQComm or Don Malin's XREF. Both are fine packages. A description of XREF is included in this issue's Product announcement section. PDQComm is a replacement library for QuickBASIC's internal Comm routines. It was also written by yours truely. Originally, it was meant to add communications support to PDQ but it also works fine with QuickBASIC and even has a far string version for BC7. Contact Crescent Software at 203-438-5300 or the Crescent Software Support BBS at 203-426-5958 for more info on these or other Crescent products. Now here are the rules. Send a postcard with your Name, Address, and how you received your copy of the QBNews. I don't care if it is "I downloaded it from XYZ bbs" or "I got it from my friend Bill", I would just like to know. All entries must be postmarked by November 15,1990 and only 1 entry per address will be allowed. This contest is void where prohibited by law. Winner will be notified and announced in the November issue of the QBNews. Even if you aren't interrested in any of the prizes, drop me a postcard anyways. I'd like to know if this newsletter is reaching everybody who would want it. That brings me to another topic regarding mail. It seems that all the letters I get are from beginning to intermiediate programmers. The QBNews tries to please everybody from beginner to "Power Programmer", but I don't receive mail from these advanced users. I would like to hear from you so you can let me know what you think about some of our "advanced" articles. I know there are "Professional" QB programmers out there and I would like to hear your views on the subject matter in the QBNews also. This issue is kind of on the large size due to the special TSR programs you receive with it. I had to leave some sections out to keep the size resonable. If you find that you missed a section not included in this issue, let me know. I really would like to know what my readers find interresting and what they don't. Well, so long for another 3 months. Enjoy. Dave Cleary Send your postcards to: The QBNews Reader Contest P.O. Box 507 Sandy Hook, CT 06482 Remember to say how you received your copy. The QBNews Page 1 Volume 1, Number 4 September 3, 1990 ---------------------------------------------------------------------- B e g i n n e r s C o r n e r ---------------------------------------------------------------------- The BASICS of QB'S Serial Communications by Ranjit Aiyagari QuickBASIC has always been recognized for having a unique feature which most other languages (i.e. Pascal, C, Fortran) lacked: full serial communications support built in to the language. In this article, I'll explain BASIC's communications functions and, more importantly, their uses. If you always wanted to write a BBS or a terminal program in QuickBASIC but never had the faintest idea how, keep reading. In order to use the modem (or any other serial device) from BASIC, you must first OPEN it, treating it as you would treat a file. The flexible syntax of the OPEN COM statement is explained well in Microsoft's manuals, but I'll give a most common syntax of it: OPEN "COM1:2400,N,8,1,OP0,DS0" FOR RANDOM AS #FileNumber% LEN=1024 The above statement, as you can probably gather from its simple syntax, will open communications port number 1 at a most common setting of 2400 bps, 8 data bits, 1 stop bit, and no parity. The OP0 and DS0 parameters signify, respectively, for BASIC not to wait at all before communications lines become active, and for BASIC to ignore (for simplicity's sake) the DSR (Data Set Ready) line. Lastly, the LEN parameter specifies the length of the input buffer. Now that you've opened it, output to the modem is easy. To send an "AT".. command, just do a PRINT #1, ModemCommand$. Getting input from the modem, however, can be done in multiple ways: Character by character: This is a method I prefer, getting characters one at a time and dealing with them. To get one character from the modem, use the INPUT$ function in the following manner: Char$ = INPUT$(1, #FileNumber%) [where FileNumber% is of course the file number under which you opened the communications port. Everything waiting in the modem buffer: With this method, you can store whatever is waiting in the input buffer in a string, and then deal with the string as you wish using BASIC's string manipulation functions. This is done by using the LOC function. Normally returning the position of the last byte read for files, LOC changes meaning when used with communications devices. Instead, it returns the number of characters waiting in the input buffer. To input the modem input buffer to a string, do the following: ModemInput$ = INPUT$(LOC(FileNumber%), #FileNumber%). Now you can use INSTR to figure out whether certain characters were returned, or MID$ to extract part of the input. A commonly asked question is "How do I recover from a carrier drop?" Since the CD (carrier detect) option in the OPEN statement The QBNews Page 2 Volume 1, Number 4 September 3, 1990 defaults to zero, your program seems to have no way of knowing whether the other side is connected or not. The method I recommend for this is using an ON TIMER routine, such as this: ON TIMER(15) GOSUB CheckCarrier ' Remember to compile with /v when using ON TIMER Now, we use a low-level method of checking whether the DCD (data carrier detect) line is up or down. You must know the communications port base address first, so to figure it out, do the following (assuming the communications port is in a variable ComPort%): DEF SEG = &H40 BaseAddress% = PEEK((ComPort-1)*2) + PEEK((ComPort-1)*2+1)*256 ' The base address is usually &H3F8 for COM1 and &H2F8 for COM2 Use the INP function: CheckCarrier: IF (INP(BaseAddress+6) AND 128) = 0 Then Print "Carrier lost" Connected% = 0 ELSE Connected% = -1 END IF RETURN Another common question: "How do I wait for a ring and then answer?" This is far simpler, and uses the EOF function, which returns false if characters are waiting in the input buffer (You should make sure to have the statement CONST False = 0, True = NOT False at the top of your program). To wait for the ring: PRINT #FileNumber%, "ATE0" SLEEP 2 ' Wait for the modem to respond Dummy$ = INPUT$(LOC(FileNumber%), #FileNumber%) ' Discard "OK" ModemInput$ = "" DO IF NOT EOF(FileNumber%) THEN ' Characters are waiting, so get them ModemInput$ = ModemInput$ + Input$(1, #FileNumber%) END IF LOOP UNTIL INKEY$ <> "" OR INSTR(ModemInput$, "RING") Dummy$ = INPUT$(LOC(FileNumber%), #FileNumber%) ' Clear the buffer ' Now, to answer: PRINT #FileNumber%, "ATA" Lastly, probably the most frequently asked question about QB communications: "How do I change the BPS rate while connected?" This requires some low-level UART (Universal Asynchronous Receiver- Transmitter) accessing [the UART is the chip in control of the serial port]. The best use of this is to read the modem's "CONNECT..." message and change the BPS rate accordingly. It requires a series of The QBNews Page 3 Volume 1, Number 4 September 3, 1990 four OUT statements: FUNCTION JustNumbers%(X$) STATIC ' Takes a string and returns the numeric value of the numeric ' characters inside it. Temp$ = "" FOR CheckString% = 1 TO LEN(X$) Char$ = MID$(X$, CheckString%, 1) IF INSTR("1234567890", Char$) THEN Temp$ = Temp$ + Char$ NEXT CheckString% IF Temp$ = "" THEN JustNumbers% = -1 ELSE JustNumbers% = VAL(Temp$) Temp$ = "": X$ = "" ' Empty the strings END FUNCTION ' Assume we've just sent the "ATA" ModemInput$ = "" DO IF NOT EOF(FileNumber%) THEN ModemInput$ = ModemInput$ + INPUT$(1, #FileNumber%) END IF Connect% = INSTR(ModemInput$, "CONNECT") LOOP UNTIL Connect% AND_ INSTR(Connect%, ModemInput$, CHR$(13)+CHR$(10)) NewBpsRate% = JustNumbers%(ModemInput$) IF NewBpsRate% = -1 THEN NewBpsRate% = 300 ' Just a "CONNECT" ' Now, change the BPS rate. Since we already initialized at 2400 ' don't bother changing it if the other side is also at 2400. ' Also, assume the base address is stored in BaseAddress% ' (discussed above) IF NewBpsRate% <> 2400 THEN OUT BaseAddress% + 3, (INP(BaseAddress% + 3) OR &H80)' Enable DLAB OUT BaseAddress%, (115200 \ NewBpsRate%) MOD &H100 ' Send LSB OUT BaseAddress% + 1, (115200 \ NewBpsRate%) \ &H100 ' Send MSB OUT BaseAddress% + 3, (INP(BaseAddress% + 3) AND &H7F)'Disable DLAB END IF Now, for an explanation. The first and the fourth OUT statements, as you can see, modify the DLAB, the divisor latch access bit. The reason for this is that when the DLAB is enabled, the first two registers (BaseAddress% and BaseAddress+1), instead of being the receive buffer/transmit holding and interrupt enable registers, change their meaning to the low and high byte of the BPS divisor (also called the LSB and MSB). The BPS divisor is calculated by dividing 115200, the maximum serial port speed, by the BPS rate desired. Thus, these four statements will modify the BPS rate to any value between 1 and 115200. This can be useful when writing both terminal programs and BBS's, when you want your software to "fall The QBNews Page 4 Volume 1, Number 4 September 3, 1990 back", that is, be able to transmit at any speed lower than the highest. If you need help on any topic dealing with serial communications in QuickBASIC/BASIC PDS, please feel free to send me a message on the echo. I've written BBS's and terminal programs in QuickBASIC, along with small communications functions in Assembler, and probably would be able to help you. The QBNews Page 5 Volume 1, Number 4 September 3, 1990 ---------------------------------------------------------------------- W h o y a g o n n a c a l l ? C A L L I N T E R R U P T ---------------------------------------------------------------------- Using a FOSSIL - Take 1 by Hector Plasmic From FOSSIL.DOC (for version 5 FOSSILs): "FOSSIL is an acronym for "Fido/Opus/SEAdog Standard Interface Layer". To say that the concept has gained wide acceptance in the FidoNet community would be an understatement... There are already FOSSIL implementations for the Tandy 2000, Heath/Zenith 100, Sanyo 555 and other "non-IBM" architectures. With each new 'port' of the spec, the potential of a properly coded FOSSIL application grows!" QuickBASIC has some problems with modem communications. There are ways around most of the problems; patch this, avoid that, use OUT, use a third-party library, etc. But it can be a pain in the neck to worry about ways around all the flaws in OPEN COM (and, in the case of third-party libraries, expensive). FOSSIL will not only handle your communications routines with Interrupt-driven buffered modem i/o, but makes your programs a little more "generic" than they would be otherwise. FOSSIL drivers "steal" interrupt 14h (usually the BIOS's serial communications interrupt) and install themselves instead. From that point onward, calls to interrupt 14h go through the FOSSIL instead of to the BIOS directly. The FOSSIL is basically a "BIOS extender" which provides many more functions than the usual three or so provided. While I won't attempt to cover every aspect of FOSSIL usage, I thought I'd present a "FOSSIL package" for QB 4.x that you can use to build your own programs that use the FOSSIL for communications. The subroutines in the file FOSSIL1.BAS assume that you know the baud rate and commport number you're dealing with, although the FOSSIL will use the commport at whatever baud it's already set at...all you really need to know is if you're operating local-only or online. For more information on the FOSSIL, download one and look for the enclosed FOSSILV5.ARC inside the archive. It will contain FOSSIL.DOC and FOSSIL.CHT. These two documents should contain all the information you need to utilize every aspect of the FOSSIL. A few of the FOSSIL drivers available are: X00.SYS (Ray Gwinn) OPUS!COMM (Bob Hartman) BNU (David Nugent) The QBNews Page 6 Volume 1, Number 4 September 3, 1990 Using a FOSSIL - Take 2 by Chris Wagner So what is this FOSSIL thing? Well, FOSSIL is an acronym for "Fido/Opus/SEAdog Standard Interface Layer". What the FOSSIL does is bypass the BIOS communications functions and replaces them with it's own. By doing this, the programmer can concentrate on the software without worrying about the hardware as much. The FOSSIL itself is installed by adding a line in your config.sys file that includes the FOSSIL device driver as well as a few parameters for the FOSSIL such as transmit buffer size or locked baud rate. For more information about the FOSSIL itself and installation, read FOSSIL.DOC and FOSSIL.CHT. These files come with the FOSSIL device driver which can be found on many public BBS's. To obtain a complete fossil device driver package, look for a file called X00____.___. Underscores are for version and archive type information which will vary. Once the FOSSIL is installed, you need to access it somehow. That's where I come in. The program that I have included in FOSSIL2.BAS is a terminal program that does all it's serial communications via the fossil device driver. To use this program, you must have INTERRUPT in your library, or load QB.LIB / QB.QLB for proper operation. What advantages does a FOSSIL give me? Well, there are many. You have the ability to change baud rate WITHOUT closing the serial channel, Baud rates up to 38400 directly supported, Status functions like character waiting and carrier detect available, and the ability to terminate a program WITHOUT dropping DTR. All of this can be done using REGULAR QuickBASIC programming and no additional libraries. In the included MYFOS.BAS program, I have coded the program so that all of the SUB programs can be put directly into a library if desired. This gives the ability to call FOSSIL functions from any of your programs once they are in your library. You will notice that many of the SUB programs have many parameters that must be given. I did this so that they can be used in any program from a library. The DataType Reg is used extensively in the program, and is also passed to all of the SUB programs. This allows for the INTERRUPT calls that access the FOSSIL. In the following paragraphs, I will describe the SUB programs and their parameters. Many of these SUB programs are very small, but have many parameters that are of great importance. Parser: This SUB is a command line parser (divider) that returns an array of parameters that where given on the command line when the program was run. Using parameters, you can start the program with special options other than defaults. AnsiPrint: All that this sub is for is to print data to the screen via The QBNews Page 7 Volume 1, Number 4 September 3, 1990 the CONS: device allowing for ANSI graphics. FossilInit: This SUB activates the fossil. It must be called before ANY other fossil functions. This SUB will also automatically raise DTR. FossilDeInit: This SUB de-activates the FOSSIL. It should be called when your program is finished using the fossil and before your program terminates. The state of DTR is not affected by this SUB. SetHandShake: This SUB sets up the handshaking between Computer, FOSSIL, and remote computer. There is many ways to use this, but use wisely. I find that FOSSIL RTS/CTS works best for my applications. Remember, in binary file transfers that the characters that represent XON/XOFF will be embedded in data and will cause problems. Therefore, XON/XOFF should be avoided sometimes. SetPortParams. This SUB has many parameters in order to make it generic. It allows you to set the serial port Baud, Bits per word, stop bits, and parity. SetDTR: This sub allows you to raise or lower DTR. This is useful to hang up a modem to terminate communications. Transmit: This SUB is used to send transmit data to the serial port one character at a time. It will return -2 (and clear the output buffer) if the output buffer is filled. If this is undesired (automatic clearing of the buffer) it can easily be removed from the SUB. Receive: This sub is used to get data from the serial port one character at a time. It will return -1 if there is no data waiting in the buffer. If the receive buffer is over-run, it will be automatically cleared and a -2 will be returned. Status: This SUB returns the status of the FOSSIL buffers and Carrier Detect signal. It returns bitmapped data and is documented in the SUB program. BaudSelect: This sub is used to change BAUD rate only, but requires all the parameters such as bits per word, parity, and stop bits. To demonstrate all of these SUB programs, I made yet another SUB program called TERMINAL that you can also put in a library. The terminal can be used to call BBS's or whatever as well as upload and download files as long as you have the Zmodem protocol program DSZ in your path. The commands that the terminal accepts are documented by remarks in the source code. [Editor's Note] The QBNews Page 8 Volume 1, Number 4 September 3, 1990 Fossil1.bas and Fossil2.bas are included in the file Fossil.zip. ********************************************************************** Chris Wagner is system operator of DREAMLINE, Conshococken PA (FidoNet Address 1:273/708) (215) 825-8996. He can be reached there or in care of this newsletter. ********************************************************************** The QBNews Page 9 Volume 1, Number 4 September 3, 1990 ---------------------------------------------------------------------- P r o d u c t A n n o u n c e m e n t s ---------------------------------------------------------------------- Index Manager for PDS 7 and Networks INDEX MANAGER ALLOWS QUICKBASIC/PDS 7 NETWORK OPERATION For years there has been a critical need for a simple, reasonably priced way to run QuickBASIC on a network. This problem has now been solved. Index Manager(tm), the popular $59 QuickBASIC B+ Tree file indexing package, has been enhanced to support Microsoft BASIC Professional Development System 7.0 and network use. The PDS 7.0 version provides support for Microsoft's new "far string" environment which eliminates BASIC's 64K data area limitation. The Index Manager PDS 7.0 version sells for $99 and can be used with either QuickBASIC or PDS 7.0 with no coding changes. The Index Manager network version sells for $250 and allows either QuickBASIC or PDS 7.0 to be used on a network in the file sharing, record locking mode. For further information, contact: CDP Consultants 1700 Circo Del Cielo Drive El Cajon, CA 92020 619-440-6482. The QBNews Page 10 Volume 1, Number 4 September 3, 1990 Don Malin's Cross Reference Program Crescent Software has announced a sophisticated cross-reference program for BASIC programmers. Unlike other cross-reference utilities that merely list each variable, Crescent's XREF provides a wealth of useful reports that show exactly what is going on in a program. Highlights include a Call Tree report which shows the interrelationships between all procedures. This report may be organized either alphabetically, or logically (in order of occurrence). Further, line numbers may be shown relative to the beginning of procedures like the QuickBASIC environment, or relative to the beginning of the source file. XREF also prints a Table of Contents for the entire listing, showing the starting page number for each procedure. All reports may be sent to a printer, disk file, or browsed and scrolled on-screen. According to Crescent, XREF is extremely easy to use, relying on pull-down menus and dialogue boxes for all program operations. XREF reads the main program's .MAK file, and uses that to examine all the files that comprise an entire application automatically. This includes modules that are external to the main BASIC program, and $INCLUDE files. XREF understands SHARED, COMMON, COMMON SHARED, DIM AS, DEFINT, and all of the other DEF-type statements. The program is compatible with all versions of Microsoft BASIC including BASICA and GW-BASIC, so it is ideal when converting a large program from those earlier BASIC dialects. XREF also includes a unique utility to extract all quoted strings and remarks and write them to file, for proofing with any popular spelling checker. A companion utility will then remerge the corrected text back into the source file, with the original spacing intact. This feature also simplifies translating a program into a foreign language. The Object Summary report lists all of the program objects, such as BASIC key words, procedures (SUBs and FUNCTIONs), simple variables, TYPE variables, and both Static and Dynamic arrays. Program objects are grouped by their type, and displayed alphabetically. Each object is listed with information about where it was defined, and the number of times it was used. Other reports show BASIC key word usage, and indicate which variables are passed as parameters, and which are Static, Dynamic, Automatic (local stack variables), and which are Shared. XREF requires any version of Microsoft BASIC; 512K RAM (640K plus 64K EMS recommended but not mandatory); DOS 2.1 or later; and a PC/XT/AT or compatible computer. Full BASIC source is provided, however Crescent's QuickPak Professional library is needed to recompile the XREF program. 5-1/4 inch disks are standard, although 3-1/2 inch disks are available if specified when ordering. XREF costs only $59.00, which includes free technical assistance and a comprehensive manual with tips and tricks for program optimization. The QBNews Page 11 Volume 1, Number 4 September 3, 1990 Crescent Software 32 Seventy Acres West Redding, CT 06896 Phone: 203-438-5300 FAX: 203-431-4626 CompuServe: 72657,3070 The QBNews Page 12 Volume 1, Number 4 September 3, 1990 ---------------------------------------------------------------------- P o w e r P r o g r a m m i n g ---------------------------------------------------------------------- Peeking at DOS with P.D.Q. by Ethan Winer When most programmers consider writing a TSR program, they immediately think of having to learn C or assembly language. DOS was never intended to support multiple applications operating simultaneously, and creating a TSR involves some extremely tricky programming. To answer this need my company, Crescent Software, has developed an add-on library for use with QuickBASIC and BASIC 7 that lets BASIC programmers write several types of TSR programs which take as little as 3K of memory when resident. P.D.Q. can also be used to write non-TSR programs as well, with .EXE file sizes starting at 350 bytes. Our purpose here, however, is to examine an interesting and useful utility that we provide as a TSR programming example with the product. A ready to run version of this program is also provided in the file DOSWATCH.EXE. P.D.Q. supports three different ways to create a TSR program using BASIC. The first uses a "simplified" method, whereby the programmer specifies a hot key, and a location in the program that is to be executed each time that key is pressed. Once the program receives control, it may freely open and access disk files, regardless of what the underlying application is doing at the time. Because DOS is not reentrant, this is normally a very difficult programming feat to accomplish. Indeed, entire books have been written about the various complexities of writing TSR programs. Therefore, much of the appeal of P.D.Q. is that it does all of the hard parts for you through the supplied assembly language routines. An example of this type of TSR is in the accompanying article for TSRTerm. The second type of TSR lets you manually trap system interrupts, and with the ease of BASIC accomplish virtually anything that could be done in assembly language. Of course, the programmer must understand how interrupt services are accessed, and I often recommend Peter Norton's excellent "Programmer's Guide to the IBM PC" published by Microsoft Press. The third type of TSR lets you combine the flexibility of manual interrupt handling with the safety of the simplified hot key methods. In the DOSWATCH program I will describe here we will manually intercept DOS Interrupt 21h (Hex) to provide a "window" into its inner workings. P.D.Q. TSR INTERRUPT HANDLING SERVICES There are many routines provided with P.D.Q. to support interrupt handling, and several of these will be described. Also, for each interrupt that a program will be handling, an 18-element TYPE variable must be defined. This TYPE variable is used to hold the CPU's registers, as well as the address and segment to use if the original interrupt will be accessed. The Registers TYPE variable is very much The QBNews Page 13 Volume 1, Number 4 September 3, 1990 like the TYPE variable BASIC requires when using CALL INTERRUPT, in that the various CPU registers may be assigned and examined. Several additional TYPE elements are also defined, which are specific to P.D.Q. For example, the segment and address of the BASIC interrupt handling code are stored there. We'll begin by briefly examining the P.D.Q. TSR routines used in DOSWATCH. The first is PointIntHere, and it indicates where in the BASIC program to begin executing when the specified interrupt occurs. Because BASIC does not provide a direct way to obtain the address of a particular statement or line, a call to PointIntHere must be immediately followed by a GOTO. The very next program statement will then receive control each time the specified interrupt occurs. The next two routines are IntEntry1 and IntEntry2, and these must be the very first two statements in the body of the BASIC interrupt handler code. These routines copy the current CPU register values into the TYPE variable, so they may be examined and set by your program. (Internally, IntEntry1 saves the current value of the AX register and establishes "DGROUP addressability", so variables within the TSR program will be accessed correctly. IntEntry2 then copies the remaining register values into the TYPE variable, and jumps to the correct location in the TSR program.) IntEntry2 also expects an "Action" parameter, which tells it what to do if another interrupt occurs before you have finished processing the first one. There are two options -- pass control on to the original interrupt handler, or ignore the interrupt entirely and simply return to the caller. The only situation in which a second interrupt could occur like this is when trapping hardware interrupts such as the timer tick or the keyboard or communications interrupts. While a program is processing the interrupt, it may call one of the three P.D.Q. interrupt service routines. The first of these is GotoOldInt, which passes control to the original interrupt handler. GotoOldInt is used when a program is intercepting only certain services, and the current service is not one of those. In that case you would want to pass control on to the original (or subsequent) handler. For example, if you are intercepting DOS interrupt 21h and care only about, say, service 4Eh, then you would call GotoOldInt for all of the other services so DOS could handle them. The second routine is CallOldInt, and this routine lets you call the original interrupt as a subroutine, and then receive control again when it has finished. This would be useful when intercepting the printer interrupt, for instance, perhaps to test the success of the last print attempt. The final interrupt handling routine is ReturnFromInt, which returns control to the underlying application. ReturnFromInt would be used when your program has processed the interrupt entirely by itself, and no further action is needed by the original handler. Neither CallOldInt nor ReturnFromInt are used by DOSWATCH, and these are The QBNews Page 14 Volume 1, Number 4 September 3, 1990 described solely for your interest. INSIDE DOSWATCH The DOSWATCH program shows how to intercept DOS Interrupt 21h, and it also serves as a good example of writing a general purpose interrupt handler using P.D.Q. DOSWATCH is a TSR program that monitors every call made to DOS Interrupt 21h, and then displays information about the current service that is being requested. The service number is printed in the upper left corner of the screen, and in many cases additional information is also shown. Watching DOS as it works can be very enlightening, and DOSWATCH lets you view not only the activity of your application programs, but also DOS itself. The DOSWATCH.BAS source listing is shown in Figure 1. DOSWATCH begins by including PDQDECL.BAS, which declares all of the available P.D.Q. extensions and defines the Registers TYPE variable. Next, a unique ID string is defined, which in this case is also the sign-on message. P.D.Q. uses the ID string internally to let you check for multiple installations. However, that feature is not exploited here for the sake of simplicity. Because DOSWATCH may receive control at any time, it is essential that the regular BASIC PRINT statement is not used. Unlike the simplified TSR method which allows nearly any BASIC statement to be used freely without regard to the current state of DOS or the BIOS, DOSWATCH must use the PDQPrint routine that writes directly to video memory. PDQPrint is a "quick print" routine, and it expects the row and column as passed parameters. Therefore, we must use CSRLIN and POS(0) to know where that is. (P.D.Q. does in fact allow TSR programs to perform nearly any service within a manual interrupt handler, but an additional call is needed and DOSWATCH doesn't truly need that feature.) The next two statements define the strings that will receive the information message, and the file or directory name when appropriate. The statements that follow establish several variables that will be used by the program. Using variables as parameters is always faster than constants, because BC.EXE generates extra code to store constants in memory each time they are used. Likewise, assigning Zero$ once eliminates repeated references to CHR$() each time INSTR is used later on in the program. This is true for both P.D.Q. and regular QuickBASIC. Although it may not be obvious, using INSTR(DOSName$, Zero$) is faster and creates less code than INSTR(DOSName$, CHR$(0)), because CHR$() is actually a called routine. A few microseconds either way is unlikely to matter in most programs, but in a TSR that steals interrupt processing time, speed is at least as important as program size. (Defining and assigning Zero$ adds a dozen or so bytes.) The last preparatory step is to assign Registers.IntNum to the The QBNews Page 15 Volume 1, Number 4 September 3, 1990 value &H21, to specify which interrupt is to be trapped. Finally, PointIntHere is called, followed by a GOTO to the very end of the source listing where the program is installed as a TSR by calling the EndTSR routine. The remaining program statements will now be executed every time an Interrupt 21h occurs. The first two steps are calls to IntEntry1 and IntEntry2, and this is mandatory in all TSR programs that do not use the simplified method. These routines simply save the current processor registers values, so they can be restored when DOSWATCH passes control to DOS later on. The next two steps clear the Ticks variable, and derive the current service number from the AX register. For DOS services that process a file or directory name, DOSWATCH pauses for a half-second allowing time to read the name. Ticks specifies the number of timer ticks in 18ths of a second. For other services only the service number is displayed, and there is no added delay. Fifteen different DOS services are recognized, and these are filtered through a SELECT/CASE block. All of the supported services assign Message$ to the appropriate text, and those services that process a file or directory name also use the GetDOSName subroutine. GetDOSName copies the current name into the DOSName$ variable, and also sets Ticks to 8 to allow time to read it. In some cases, additional information is assigned to Message$, for example when reporting a drive letter or file handle number. This information is taken from the appropriate registers as necessary. If a particular service is not supported, then Message$ is simply cleared to blanks in the CASE ELSE code. Because DOSWATCH is not actually processing any of the DOS services, the final step is to call GotoOldInt, which in turn jumps to the internal DOS function dispatcher. Thus, DOSWATCH displays what is about to happen, before DOS actually receives control. The remaining statements comprise the GetDOSName subroutine, which copies the current file or directory name into the DOSName$ variable. In this case, the P.D.Q. BlockCopy routine copies the first 50 characters pointed to by DS:DX into DOSName$, and then INSTR is used to locate the CHR$(0) which marks the end of the name. Only those characters that precede the zero byte are retained, and the LEFT$ assignment clears any characters that follow the name. Running DOSWATCH once will install it as a TSR, and all subsequent DOS operations will then be displayed on the top line of the screen. Observing DOSWATCH as it works can provide much insight into DOS' internal operation. For example, you will no doubt find it enlightening to start QuickBASIC and load a program such as DOSWATCH itself. This shows DOS at work as it loads and executes QB.EXE, and then you can watch QuickBASIC as it loads the main program and the PDQDECL.BAS Include file. Equally interesting is all the unnecessary DOS activity QuickBASIC performs to obtain a list of file names when you select Alt-F-L from the pull-down menu. The QBNews Page 16 Volume 1, Number 4 September 3, 1990 You could also modify DOSWATCH to pause briefly for all of the DOS services rather than just some of them. Even though the operation of your PC will be slowed dramatically, many varied and interesting facets of DOS will become apparent. For example, each time you use the DIR command, DOS (actually COMMAND.COM) always changes to the current directory, opens the directory "file", and then writes the directory information to Handle 1. The added delay will also let you observe DOS as it closes all available handles each time a program terminates. As you can see, besides providing an excellent way to learn more about TSR programming, DOSWATCH is also a very useful utility program in its own right. One of our primary goals in developing P.D.Q. was to allow BASIC programmers to create programs as small and fast as those written in C. With a minimum .EXE file size less than one-fourth that of QuickC and Turbo C, I believe we have met that goal. [Editor's note: P.D.Q. costs $129 plus $6 for 2nd day shipping, and it is available from Crescent Software at the address shown below.] ********************************************************************** Ethan Winer is President of Crescent Software, and is the author of their QuickPak Professional and P.D.Q. products. Ethan may be reached at 32 Seventy Acres, West Redding, CT 06896, 203-438-5300; on CompuServe at 72657,3070; and on MCI Mail as EWINER. ********************************************************************** The QBNews Page 17 Volume 1, Number 4 September 3, 1990 A Pop-Up Communications Program with PDQ by Dave Cleary One of the most asked questions I have heard is "How do I make a QuickBASIC program a TSR?". That is the reason I chose to devote an issue of The QBNews on how to accomplish this. With the help of Crescent Software's PDQ or Microhelp's Stay-Res Plus, TSR programming is made easy for QuickBASIC programmers. I am going to explain a Pop-Up communications program that is included as a demo with PDQComm. PDQComm adds communications support to PDQ and it also fixes some of the "features" that Microsoft has built into QuickBASIC's internal communications support. TSRTerm is a PDQ simplified pop-up. It is called simplified because all the hard work of making sure your TSR won't crash the system is taken care for you. The first thing you need to do when creating a simplified PDQ TSR is to create an ID$. PDQ uses this string to determine if the program is already installed and also is used when the program is de- installed. It is important that this is your first string assignment because QB dynamically moves strings around as they get added and deleted and ID$ can not move. ID$ also has to be unique. One good practice is to use the program's name and version number as the ID. Next you need to specify the hot key. The hot key is stored in a normal integer and is specified in two portions. They upper byte is the shift mask while the lower byte is the scan code for the key desired. The shift mask lets you select the Alt, Ctrl, Left Shift, and Right Shift to be used with your hot key. You can also OR the values of the shift mask together to get 3 or 4 key combinations such as Ctrl-Alt-C. I have chosen Alt-C for my hot key. Specifying the hot key in hex with &H makes it easier to work with. &H8 is the shift mask for the alternate key while &H2E is the scan code for the C key. Right after I specify the hot key, I check to see if the program is already installed. This is done by a call to the TSRInstalled function, passing it your ID$. If the program is installed, TSRInstalled returns the DGROUP segment where it was found. This is used for deinstalling the TSR later in the code. Otherwise it returns a 0. We then do some port initialization with PDQComm routines. You may notice that the PDQComm routines mimic QuickBASIC's as much as possible. This was done to make PDQComm easy to integrate into your QuickBASIC program. BTW, PDQComm also works with regular QuickBASIC and PDS 7 far strings. Now comes the hard part of using PDQ. You make a call to PopUpHere with your hot key and ID$. You must then put a GOTO immediately after the call to PopUpHere. The GOTO jumps over the code that will be executed when it pops up. All you have to do is make a call to EndTSR and your program is now resident. Well, that isn't too hard. That is what makes PDQ so good. It is very simple to make all kinds of TSR's. The QBNews Page 18 Volume 1, Number 4 September 3, 1990 When you invoke the hot key of your TSR, control will be given to your program right after the GOTO statement. Will you are popped up, you can use almost any QuickBASIC statement you want without worrying about crashing your system. The only restrictions you have is the use of INPUT and INKEY$. PDQ gives you replacements for these routines, BIOSInput and BIOSInkey, so you can easily get around this restriction. Next, you need to know how to return control to the underlying application. This is done with a call to PopDown. PDQ doesn't save and restore screens for you so you need to do it yourself. That is what I am doing in the statements preceding the call to PopDown. I restore the cursor position and screen that I had saved when the program had popped-up. All that leaves now is the PDQ routine used to deinstall your TSR from memory. That code is under the label Deinstall. In this program, I have chosen to deinstall it by executing it again at the DOS prompt. If TSRTerm finds itself already installed, it will deinstall itself from memory. This is done with a call to PopDeinstal function. You pass this function the segment that was determined by the call to TSRInstalled and your ID$. It will then tell you if your program was successfully deinstalled or not. That is all there is to programming pop-up TSRs with PDQ. PDQ and PDQComm is available from Crescent Software for $129 and $49 respectively. You can contact Crescent at 203-438-5300. ********************************************************************** Dave Cleary is an electronics engineer and programmer for Vectron Laboratories in Norwalk, CT. He is also the author of PDQComm and editor of this newsletter. He can be reached in care of this newsletter, on Compuserve as 76510,1725, on Prodigy as HSRW18A, or on the Crescent Software Support BBS 203-426-5958 1:141/777. ********************************************************************** [Editor's Note] Since TSRTerm was meant only for demonstration purposes, the baud and dial prefix is hardcoded in. These are 1200 and ATDT respectively. You will need to recompile and link with PDQ and PDQComm to change these. All files are located in TERM.ZIP. The QBNews Page 19 Volume 1, Number 4 September 3, 1990 Using Stay-Res Plus to make TSR's by Larry Stone Have you ever had the need to create a program that is RAM resident? Needless to say, it is impossible to do it directly in BASIC because of the controlling influence of the BASIC engine. This doesn't mean that it can't be done, it means that it can't be done directly. There are two commercial products available that can allow your QB programs to become RAM resident. One product, PDQ is offered by Crescent Software and the other, Stay-Res Plus by MicroHelp, Inc. I own both products and highly recommend each. I use PDQ for small, memory resident utilities - those programs whose DOS kernels are no larger than 15K bytes. But, most of what I code turns out to be full-fledged application programs. Not little utilities performing a single minded task, rather, my programs tend to be large applications that have multiple menus and perform multiple I/O functions. Stay-Res Plus excels in this arena. Stay-Res Plus, coupled with a little additional effort by you, can turn your application into a TSR (Terminate and Stay Resident). A Stay-Res Plus assisted application can pop-up with the press of up to 23 pre-defined hot keys, on a specific date and time, on the "ring detect" from a communications port, or even from a POKE by your other programs. Your Stay-Res program can even SHELL other applications if popped-up over DOS. And, most important, when your program goes to sleep, it only locks out a 10K - 11K byte DOS kernel, sending the rest of the memory "image" to either EMS, or to temporary disk files (note Stay-Res will allow the entire application to be DOS RAM resident). To write a Stay-Res application demands of you to conform to some basic (no pun intended) rules. Your program needs an initialization section where all of your arrays and TYPEs are DIMed, and CONST's are defined, and where all of the necessary logic required is performed before entering the main program section. Here is where your program displays it's message that it is going to sleep. Your program also needs a memory-resident section which the main section will transfer control to, every time it pops down. Because your program will be TSR, there are a few do's and don'ts you must adhere to. Never, ever ERASE or REDIM an array. This is due to how BASIC will handle these commands. Issuing these commands will allow BASIC to move memory around. Once this happens, Stay-Res cannot protect the memory pool and your program will probably crash. If you need to clear a numeric array, place it into a FOR...NEXT loop and set each element of the array to zero. String arrays would be set to null Dynamic arrays also need special attention due to the manner that BASIC allocates their memory. QB4, BASCOM6, or PDS will store dynamic arrays downward from the top of the heap (high memory) instead of just above the stack (bottom of the heap), leaving a gap in between. In order for Stay-Res to protect this memory, you must instruct QB to put these arrays just above the stack, making all of the program's memory contiguous. You do this by using BASIC's SETMEM instruction followed The QBNews Page 20 Volume 1, Number 4 September 3, 1990 by a call to SrAutoSetBlock. A note for all of those Stay-Res users who may be reading this. Stay-Res v3.3 has an undocumented command, SrAutoSetBlock. This command can be used in lieu of SrSetBlock and is much more efficient. Simply DIM and define every variable possible at the top of your code (pretend your programming in Pascal), then issue the command HeapMem& = FRE(-1). Subtract a fudge factor from HeapMem& to account for anything missed or for what variable length strings may require: HeapMem& = HeapMem& - 2048 then have BASIC set the memory contiguous: MemNeeded& = SETMEM(-HeapMem&) and follow up with the call CALL SrAutoSetBlock(Paragraphs%, Ecode%). Then you should check if it will work with: IF MemNeeded& <= HeapMem& OR Ecode% <> 0 THEN PRINT "Not Enough Memory - Bye Bye". One important question to consider is which BASIC commands cannot be used with Stay-Res. Well, under no circumstance can you ever use BEEP or PRINT CHR$(7), or BASIC's RUN or SHELL statements once your program has become memory resident the first time (use the alternative SHELL's supplied with Stay-Res). You can use SOUND or PLAY so long as you allow sufficient time for the speaker to shut off before going to sleep. However, when Stay-Res pops up with an Ecode% equal to 1 then DOS is, at this time, busy and you cannot use LOCK, UNLOCK, DATE$ TIME$, PEN, LOF(), EOF(), PLAY, LPRINT, RANDOMIZE TIMER, and all other file related commands except LOC(), and all directory commands. So, what does this mean? The manual trys explain it with an example to instruct DOS to copy a *huge* file then pop-up your Stay Res program in the middle of the COPY. I tried that and, on my system, DOS simply suspended the COPY. Stay-Res did not pop-up with Ecode = 1 and, when my program popped down, DOS continued as if it were never interrupted. I even tried this procedure with PKzip and it too simply suspended it's operation until I popped down. I suspect that on my computer, it would have to be networked with local I/O activity before an Ecode of 1 would be returned by Stay-Res. Never-the-less, if DOS is busy, set a flag and if the user needs any of the commands listed, print a message to try later when DOS isn't so busy. Compiling and linking Stay-Res must be done from the DOS command line. Compiled programs must use the /O switch and the linker must be instructed to place the Stay-Res object first and a supporting object last. The link command should look like: LINK STAYQB4+prog+otherMods+MHMISCP,prog,nul,externalLibs; The term, prog stands for the name of your program. Notice that prog is repeated mid-way in the command and separated by commas. This will instruct the linker to name the completed EXE with your program name. To facilitate your coding TSR's, MicroHelp supplies a template program with clear, concise, pre-defined areas and GOSUB instructions required. If you can paint by numbers then you can use this template. Creating a TSR is not a snap but, neither is it very difficult if you adhere to the rules. To illustrate the power of Stay-Res, accompanying this article is The QBNews Page 21 Volume 1, Number 4 September 3, 1990 a full function, scientific and statistical calculator with built-in mouse support. The calculator has 72 buttons supporting 92 functions such as, trigometric computations (including inverse and hyperbolic), log e, log 10, and log n, angular conversions, binary, octal, complex and hexidecimal number support, a 500 element array for statistical computations, permutations, combinations, four types or means, nth roots, standard deviations, a 512 line user configurable look up table and, not-to-mention, factorials and random number table whose sequence far exceeds BASIC's and offers three types of random number - defined range randoms, randoms with an exponential curve defined by your mean, and randoms with a normal curve defined by your mean and your standard deviation. The EXE size is 129,440 bytes and requires some 204,000 bytes to load. The program incorporates ERROR trapping in several subprograms and functions. However, when it pops down, it reduces it- self to a 10 Kbyte kernel!!! I and my beta testers found the calculator to be 100% compatible with QB, QBX, Lotus 123 v2.1, MS WORD, Word Perfect, Qedit, PC-Write, dClip, CLIPPER applications, dBase, LIST, et. al. In fact, the only program that it won't pop up over is Code View or Lotus v1.1 because these are not "well-behaved" programs (Microsoft broke their own compatibility requirements when they constructed Code View). If a Stay-Res program is not compatible with another program, I assure you the fault is that the other program is not well-behaved. ********************************************************************** Larry Stone is President of LSRGroup and is involved in writing software for marine and aquatic research. He can be reached at LSRGroup, P.O. Box 5715, Charleston, OR 97420, or in care of this newsletter. ********************************************************************** [Editor's Note] When I asked Larry to do a pop up calculator, I expected a small 4 function little program. Well, Larry isn't known for a lack of effort and has come up with a truely professional program. Because of the time and effort that has gone into the program, I feel that it is only right for him to offer it as shareware. But, as readers of the QBNews, you also get a special bonus when you register. Just mention that you read the QBNews and Larry will send you full source code when you register. The file CALC.ZIP contains an abridge version of the documentation. To get the full documentation, you can download it from these fine boards: The Crescent Software Support BBS 203-426-5958 2400 Baud The Empire Builder 503-888-4121 2400 Baud The QBNews Page 22 Volume 1, Number 4 September 3, 1990 ---------------------------------------------------------------------- A l g o r i t h m s ---------------------------------------------------------------------- Improving your IQUEUE by Jim Mack With all that's been added to the BASIC language in the past few years, it might be hard to believe that there's a more-or- less elementary data structure that's been ignored. Actually, "ignored" is a bit strong, since you can build this structure out of more primitive elements using the core language, but I'll bet most of you never have. You've probably gathered from the title that what I'm talking about is a data stack. Now, whether a stack is an essential data structure is certainly debatable: after all, aren't high-level languages supposed to insulate you from such machine-like things as registers and stacks? And haven't you gotten along for years without a queue? If you've never programmed in MASM (or Forth), you might be blissfully unaware of the possibilities of just "pushing" data somewhere and later "popping" it back... and of the concept of feeding data to another process faster than it can be absorbed. Well, you're in for a not-too-rude awakening here, as we explore the world of buffered data. We'll discover two useful sets of routines and see how you might apply them to some common programming tasks. "Queue". The word is so weird-looking, let's get it out of the way first. Pronounced "Q" (then why are all those other letters there?), it's just another word for a "line" such as you might encounter at a theater or bank. We used to say as children "it forms at the end"... not strictly true, but close enough. People (or objects) are "handled" in the same order that they join the line: first come, first served. Another common way of saying this is FIFO (arf!) or first-in, first-out. If there's room in a theater lobby for 20 people to wait in line ("on" line for you New Yorkers), then we can think of the lobby as buffering 20 items. When the lobby is full, no one may enter until someone leaves at the head. It's the same for data. If one process generates data items at roughly the same rate that another process uses them, but with bursts of activity followed by periods of calm, a buffer can smooth out the peaks. The originating process can place items into a queue as fast as it likes, while the receiving process can take time to work with an item, and get a new one as it requires. A good example of this activity is QB's RS-232 serial COM. Characters arriving at the port interrupt normal processing and are quickly placed into a queue. As your program becomes aware of them, The QBNews Page 23 Volume 1, Number 4 September 3, 1990 it retrieves characters and, well, does what it does with them. If your program empties the bucket roughly as fast as it's filling up, then the queue provides some leeway for those characters which might need a little more processing. The larger the buffer, the "spongier" the process can be. There are two ways of handling buffered data. One way is to let the buffer fill up, then disallow any more input until it's emptied again. A movie theater functions like that: if you don't get in at 6, you wait until 8... and nobody goes in at 8 until the 6 o'clock crowd has left. Far more useful for data is the "circular" queue, often called a ring buffer (note to jewelers: I know this isn't what *you* call a ring buffer. Don't write). There are analogies for both in the real world. The theater line is a straight queue. A service department might instead use a "take a number" scheme with tickets numbered from 1 to 100. Once number 100 is serviced, number 1 is next, and so on. "Take a number" also introduces the idea of a pointer. In a theater, the entire line moves forward whenever someone leaves from the head. In the service department, folks may move around until their number comes up. In data terms, the "theater" method is terribly inefficient. Instead, we use pointers to indicate where the current head and tail of the queue are. With pointers, the size of the queue doesn't affect the speed of operations, so enqueueing and dequeueing data items can be very fast. We turn an ordinary queue into a circular one by the simple action of "wrapping" the pointers, or in more technical terms, by incrementing them MOD the length of the queue... 99 + 1 = 100, but 100 + 1 = 1. A queue has two pointers. The "write" pointer indicates where in the buffer any new item will be stored. The "read" pointer gives the location from which the oldest item will be retrieved. These are circular if they're wrapped around when about to point off the end. The queue is full if incrementing the write pointer would cause it to equal the read pointer. So now, at least in concept, you know all about the queue. It's a form of buffer, a FIFO stack. It's really that simple... but not as simple as the LIFO (last-in, first-out) stack. To review what a LIFO stack is, we move from the theater to the restaurant (almost like real life, isn't it?). Norton's First Law of Computer Explication states, in part, that "...every treatise on The Stack will begin by discussing dinner plates...", so here we go. The QBNews Page 24 Volume 1, Number 4 September 3, 1990 Ever eaten in a cafeteria? Sure you have. Then you've seen those "plate-o-matic" devices that, when you take the top plate, another one springs up behind it...? Let's perform what nuclear theorists call a "thought experiment". How do plates get there in the first place? Do you suppose the cafeteria, having studied queues, replaces them at the bottom as they're taken off the top? Right. They operate on a last-washed first-dirtied basis, just like the stack we think of in CPU terms. Items are "pushed" onto a LIFO stack, and later "popped" back off. Technically, a FIFO queue is a stack too, but we usually reserve the word "stack" for the LIFO version. A LIFO stack can be managed with only one pointer: where the next pushed item will go or from where the next popped item will come. You could shuffle all the data each time you push or pop a data item, but as we saw with queues, a pointer is a lot more efficient. So, a stack is a temporary holding area for data. It can be used for other things, but that pretty much sums it up. It's tough to come up with any other real-world example of a LIFO stack. Fortunately most of us are already familiar with the idea because nearly every computer operation involves some use of the CPU's system stack. But while we realize how vital a stack is for the CPU, what does it buy us at the program level to have our own stack? After all, in QB if we need somewhere to put an item aside temporarily, why, we just reference a new variable... it might even be created on the system stack, and we'd never need to know. Well, there are times when an algorithm runs more smoothly, or is easier to implement, using a stack. Too, you may not want the overhead of another temporary variable (or ten), or may not want to dream up Yet Another Unique Variable Name. If so, the stack's the ticket. A data stack can also allow you to perform some recursive operations within an otherwise STATIC procedure. One example of a technique well suited to a stack is Help. I've written a "hyper-help" utility, wherein the user can follow links embedded in the text to jump from one topic (page) to another in any sequence that pleases her. Keeping track of the path being followed so we can unwind to any point (and branch again from there), is potentially a nightmare. But with a stack the job is trivial: whenever she negotiates a link, we just push the current location. When she wishes to back up, we pop page locations back off the stack. Simple, direct, and obvious. A queue can find use in passing a variable number of arguments to a procedure, something QB can't do on its own. Put as many items into the queue as you like... the called procedure just pops them out until there are no more. Queues find their best use with asynchronous The QBNews Page 25 Volume 1, Number 4 September 3, 1990 processes, for example when passing data from an interrupt handler to a program, or vice versa. Now for the details. I've written this so there's one stack and one queue, each 255 items long. The code for them is in IQUEUE.ASM and ISTACK.ASM, two small MASM files which define several new QB SUBs and FUNCTIONs, and which reserve the space for the buffers. There's nothing to stop you from using more than one of each, but you'll have to extend the concept on your own. Once you've had a look at the MASM code, you'll see just how simple all this really is. There are three nice things about doing it in MASM. First, it takes up no precious DGROUP data space (well, 6 bytes for pointers, but who's counting?). The reason we don't need any DGROUP space for the buffers is that we put them into the code segments we declare for the procedures. Second, everything about these is automatically global to every procedure in any module which DECLAREs them. This applies to linked- in libraries, run-time libs... anything which "knows" about these can share the procedures and the data without resorting to COMMON or SHARED declarations. Third and most important, it "encapsulates" the queue and the stack, so you can tell everyone you're using "object-oriented techniques" in your programming. Of course I'm joking, right? Well, only a little... Here are the DECLAREs you'll need to include. For IQUEUE: DECLARE SUB IQflush () DECLARE SUB IQput (intvalue%) DECLARE FUNCTION IQget% () DECLARE FUNCTION IQfree% () DECLARE FUNCTION IQavail% () DECLARE SUB ISclear () DECLARE SUB ISpush (intvalue%) DECLARE FUNCTION ISpop% () DECLARE FUNCTION ISfree% () DECLARE FUNCTION ISavail% () You can use these for integers or characters. The listings include versions for long integers as well. Naturally, you can use the long-integer version for smaller objects too, but you'll have to explicitly convert from integer to long, etc, to do that. If you don't like these names, or if they conflict with names you're already using, you can use ALIAS to rename them without going to the bother of editing and reassembling the MASM code. For example: The QBNews Page 26 Volume 1, Number 4 September 3, 1990 DECLARE SUB PushError ALIAS "ISPush" (intval%) You can then use PushError instead of ISPush to refer to the same routine. So let's see how these work in QB. The IQFlush procedure does what its name implies: it empties the queue of all data. For speed, all it really does is reset the pointers, since the contents of the queue don't matter if the pointers say it's empty. So to start from scratch at any time, just say: IQFlush To place an element into the queue is just as easy: IQPut AnyInteger% IQPut ComplexType.IntArray(elno) IQPut SADD(j$) IQPut (((1000 * 2) \ 3) + 7) ^ 2 Anything which evaluates to an integer can be put into the queue, but watch it... if the queue is full when you try to PUT an element, you'll generate an Overflow Error in QB. If this might be a problem, use the IQFree function: IF IQFree THEN 'meaning "if there's any room" IQPut MyElement% END IF Note that the reason you get an Overflow Error is because that's the way I designed it: if you want the code to whistle "Lara's Theme" or format your hard disk, you can replace my very minimal QUERR with your own error handler. Overflow is an easy error to generate, and it does more or less correspond with this condition. To retrieve an element from the queue, use IQGet. You can treat IQGet just as though it were a simple integer: PRINT IQGet k = IQGet MOD 3 CALL MySub(r$, hpos%, IQGet%) But as with PUT, if you try to GET when the queue is empty, you'll get an Overflow. To guard against that, use IQAvail: IF IQAvail THEN 'meaning "if anything's there" k = IQGet + 19 END IF And that's it. The STACK routines are exactly analogous to the QUEUE routines, so there's no need to review them separately. The MASM listings are commented well enough that if you need to modify them, The QBNews Page 27 Volume 1, Number 4 September 3, 1990 you can. You'll need MASM 5.1 to reassemble these if you make changes. Two notes: to make underflow and overflow detection easier in the IQUEUE procedures, the actual number of elements you can store at once is one less than the EQU'd size of the queue. And if you intend to use the queue with interrupts, you'll want to surround any pointer operations with CLI/STI to avoid collisions. If you run into any problems, you can reach me via CompuServe at 76630,2012, or on BIX as "jsmack". Your comments are always welcome. ********************************************************************** Jim Mack is a programmer specializing in real-time systems for the entertainment industry. He can be reached via CIS ID 76630,2012 on the MSSYS forum in the BASIC or MASM sections, or at Editing Services Co., PO Box 599, Plymouth MI 48170, (313) 459-4618 ********************************************************************** [EDITOR'S NOTE] You can find the ASM source and assembled .OBJ's for this article in the file IQUEUE.ZIP. The QBNews Page 28 Volume 1, Number 4 September 3, 1990 ---------------------------------------------------------------------- I n p u t P a s t E n d ---------------------------------------------------------------------- Due to demand, you can now get the QBNews mailed to you on disk for a nominal charge. The rates are as follows: United States $3.00 North America (outside US) $4.00 Outside US $6.00 Please add $1.00 if you want it on 3.5" media. Also, you can receive it in either ZIP, PAK, or LHZ format. If you don't specify, you will receive it in ZIP format. The first time you request the QBNews, you will receive all available issues. That way you won't miss any. And of course, you can always download it free from Treasure Island. First time callers are granted limited download privlidges so you can get it. Treasure Island is 9600 HST, is at FIDO address 1:141/730, and its phone is 203-791-8532. To request your copy on disk, send US funds to: The QBNews P.O. Box 507 Sandy Hook, CT 06482 Remember to specify which archive format you want. The QBNews Page 29 Volume 1, Number 4 September 3, 1990 WE NEED AUTHORS! If you are interested in writing for the QBNews, you can contact me at the address below. I can also be reached on Compuserve as 76510,1725 or on Prodigy as HSRW18A. I am also the Sysop of the Crescent Software Support BBS, 203-426-5958 1:141/770, so I can be reached there also. If you are submitting articles, Iask that they be ASCII text with no more than 70 characters per line. You can write me at: The QBNews P.O. Box 507 Sandy Hook, CT 06482 David Cleary The QBNews Page 30