Microsoft KB Archive/58530

From BetaArchive Wiki

Article ID: 58530

Article Last Modified on 8/16/2005


  • Microsoft QuickBasic 4.5 for MS-DOS

This article was previously published under Q58530


The information in this article is consolidated from multiple separate articles in this Knowledge Base. Changes, if any, will need to be made everywhere the information appears, which includes both printed and electronic copies.


Answers to Common Questions About Microsoft QuickBasic Version 4.50

This article is intended for individuals who are using QuickBasic version 4.50 for the IBM PC and compatibles, and provides answers to common questions, along with supplementary information on QuickBasic version 4.50.

The following is an outline of topics covered by this article:


    1. Mixed-language programming version-compatibility list
    2. Video problems

    1. How to use "COM1:" and "COM2:" communications
    2. Example of key trapping
    3. Floating point affects execution speed
    4. Limitations of floating-point mathematics
    5. Common floating-point errors
    6. Reference books for floating-point mathematics
    7. Reference books for QuickBasic

Memory Management and Modular Programming

Modular Programming


What is modular programming? How do I modularize a program?


QuickBasic has several features to help you create programs that are organized in a logical and structured manner. This style of programming is often referred to as "modular" programming. There are many advantages to modular programming; the most important advantage is that the logic of a modular program is clearer.

A modular program is structured in such a way that the various functional blocks of source code are isolated in separate procedures. This makes it easy to reuse particular procedures in other programming projects and greatly simplifies debugging. In a modular program, problems can be quickly isolated to a given procedure. The procedure can then be tested and debugged separately, without you having to consider the remainder of the program.

QuickBasic Modular Programming Vocabulary

There are several important terms used by QuickBasic programmers when talking about program structure. It is important to understand these terms in order to fully utilize the QuickBasic programming environment.

  1. Module: A section of program source code that is stored in a separate file and can be separately compiled. Every program has at least one module (the main module) and may consist of many modules (support modules). The modules are separately compiled and linked together to produce a finished executable program.
  2. Procedure: A general term referring to either SUBs or FUNCTIONs in QuickBasic. Conceptually, a procedure is a functional block of code that performs a specific action and that is logically distinct from the rest of the program.
  3. SUB: Short for SUBProgram. A block of QuickBasic code is delimited by SUB....END SUB. Do not confuse this with a subroutine. Subroutines are blocks of code that are terminated with a RETURN statement and were used in older forms of Basic.
  4. FUNCTION: A block of code that is delimited by FUNCTION....END FUNCTION. Similar to SUBs except that FUNCTIONs are used when a procedure is needed that returns a single value. Do not confuse FUNCTIONs with the older DEF FN functions.
  5. Module level code: The code that is found at the module level, which is outside of any procedure.
  6. Procedure level code: The code that is found inside a SUB or FUNCTION procedure.

QuickBasic Modular Programming Features

By pressing the F2 key or choosing View Subs from the View menu inside QuickBasic, you can see an outline view of the program that is currently loaded. The resulting window will show each module name, and each of the procedures in that module will be indented under it. Each editing window in QuickBasic shows the code of one procedure at a time. Use the View Subs display window to select the procedure that you want to edit. Also, from this same display, you can delete and move procedures at will. Whenever you enter the reserved word SUB or FUNCTION, followed by a procedure name, QuickBasic creates a new editing window for you so that you can add the code for that procedure.

The fastest way to program in QuickBasic is to design your program so that the main module-level code calls procedures. Just determine each action that the program must perform and then write a procedure to accomplish that task. Then create the module-level code to tie all of the procedures together.

Execution starts at the beginning of the main module-level code. If your program requires more than one module, your support modules will contain additional procedures; however, note that since the only way to transfer control across modules is to call a procedure, module- level code in support modules is unreachable. This means that support modules contain only declarative statements and procedures. You will never have any executable statements in the module-level code of a support module. The flow of control, for the overall program, is governed by the module-level code of your main module.

Memory Management, the Big Picture


How do I get more string space?


There are several things to keep in mind when programming in QuickBasic. Because Basic manages memory for you, you need to understand the limitations of the Basic memory-management system. The most important points for the programmer are the following:

  1. Your program receives one 64K code segment for each module that it contains. If the code generated by the compiler for a given module approaches 64K, you must add a new module to your program to continue adding code. The easiest way to determine if you need to add a new module is to look at the .MAP file produced by the linker, LINK.EXE. You will notice at the top of the .MAP file listing that there is a segment with the same name as your module with an _CODE appended, listed under the BC_CODE class. This is the code segment for that module and must be smaller than 64K.
  2. Your entire executable program, no matter how many modules it contains, has only one near data segment. All ordinary variables, all variable-length STRINGs, and your stack are located in this segment. The FRE() function can be used to determine if you are close to filling up this segment. The "Out of STRING Space" error indicates that this segment is full.

    Usually, the best approach to minimizing your usage of the near data segment is to move as many items as possible out into the far heap. However, the Basic memory management engine allows only certain types of data items to be placed in far memory. These items are DYNAMIC arrays of numeric data or fixed-length STRINGs. Variable-length STRINGs never go in the far heap.

    The easiest way to move these items out of the near data segment is to make all arrays DYNAMIC unless there is a specific reason to keep them STATIC. Usually, the program structure shown below is the simplest organization:

          Pseudo Code
          DIM all STATIC arrays first
          COMMON statements next
          REM $DYNAMIC
          DIM all DYNAMIC arrays here,
              including the ones placed in COMMON

    Also, if practical, use only arrays of fixed-length STRINGs. Remember, arrays of variable-length STRINGs cannot go into the far heap. These techniques will minimize the portion of the near data segment that your program's data requires, leaving more room for conventional variables and variable-length STRINGs. If any of your DYNAMIC arrays must be larger than 64K, you will need to start QuickBasic or compile with the /Ah option.

    Note: Microsoft Basic Professional Development System (PDS) 7.00 offers support for far variable-length strings and Expanded Memory Specification (EMS 4.0), while giving your programs more code and data space so that you can develop dramatically larger programs for MS-DOS or OS/2.



Which versions of Microsoft FORTRAN, Macro Assembler, Pascal, C Compiler, and QuickC are required with QuickBasic 4.50 for mixed- language programming?


QuickBasic version 4.50 creates .OBJ modules that can be linked with .OBJ modules from the following languages:

Microsoft Pascal Version 4.00
Microsoft FORTRAN versions 4.10 and 5.00
Microsoft C version 5.10 and QuickC versions 1.01 and 2.00
Microsoft Macro Assembler (MASM) versions 5.00 and later

Programs should be assembled/compiled using the medium, large, or huge memory model to be compatible with compiled Basic, which is effectively in the medium memory model.

Video Problems

QuickBasic version 4.50 is more selective of the video hardware on which it will operate than QuickBasic versions 4.00 and 4.00b. QuickBasic requires a video card that is 100-percent compatible with an IBM CGA, EGA, VGA, or Hercules Monochrome card.

If QB.EXE version 4.50 does not operate with your video system, try invoking QuickBasic with each of the video-specific options, such as:

   Option      Description
   -----       -----------

   /b          (black-and-white) option
   /nohi       (no high-intensity) option
   /g          (update screen as fast as possible) option
   /h          (high-resolution) option


   qb /b

Also, try setting the video mode from MS-DOS using the MODE command before initiating QuickBasic (for example, MODE CO80 or MODE BW80).


   MODE CO80

   MODE BW80

If a ghost image appears after running QuickBasic on your video system, use the MS-DOS MODE command to clear the screen if CLS doesn't clear it.

The following is a list of known compatibility problems:

  1. According to Microsoft's testing, the following cards loaded QB.EXE, but had numerous problems with screen swapping:

    Tecmar VGA
    Quadram VGA
    Vega Video Seven FastWrite VGA
    Vega VGA (a customer suggested QB /H for better Vega VGA behavior)

  2. According to Microsoft's testing, the following cards will not load QB.EXE:

    COMPAQ Laptop (BIOS problem -- no correction)
    COMPAQ SLT/286 (okay with AC power, fails with battery unless you disable the power-conservation utility PWRCON.EXE or PWRCON.COM.)
    Genoa SuperVGA HiRes
    Sigma EGA



How should I set up my COMmunications line in QuickBasic 4.50?


If you have problems using COM1 or COM2, try the following OPEN statement, which makes Basic as tolerant as possible of hardware- related problems:

   OPEN "COM1:300,N,8,1,BIN,CD0,CS0,DS0,OP0,RS,TB2048,RB2048" AS #1

(This OPEN is FOR RANDOM access.) The following is an explanation of each recommended parameter used in this OPEN statement:

  1. The higher the baud rate, the greater the chances for problems; thus, 300 baud is unlikely to give you problems. 2400 baud is the highest speed possible over most telephone lines, due to their limited high-frequency capability. 19,200 baud, which requires a direct wire connection, is most likely to cause problems. (Possible baud rates for QuickBasic are 75, 110, 150, 300, 600, 1200, 1800, 2400, 4800, 9600, and 19,200.)
  2. Parity usually does not help you significantly; because of this, you should try No parity (N).

    For those devices that require parity, you should use the PE option (Parity Enable, required to turn on parity checking) in the OPEN COM statement. When the PE option turns on parity checking, a "Device I/O error" occurs if the two communicating programs have two different parities. (Parity can be even, odd, none, space, or mark.) For example, a "Device I/O error" occurs when two programs try to talk to each other across a serial line using the following two different OPEN COM statements:

          OPEN "COM1:1200,O,7,2,PE" FOR RANDOM AS #1


          OPEN "COM2:1200,E,7,2,PE" FOR RANDOM AS #2

    If the PE option is removed from the OPEN COM statements above, no error message displays.

  3. 8 data bits require No parity (N) because of the size limit for Basic's communications data frame (10 bits).
  4. The BIN (binary mode) is the default. Note: The ASC option does NOT support XON/XOFF protocol, and the XON and XOFF characters are passed without special handling.
  5. Ignoring hardware handshaking often corrects many problems. Thus, if your application does not require handshaking, you should try turning off the following hardware line-checking:

    CD0 = Turns off time-out for Data Carrier Detect (DCD) line.
    CS0 = Turns off time-out for Clear To Send (CTS) line.
    DS0 = Turns off time-out for Data Set Ready (DSR) line.
    OP0 = Turns off time-out for a successful OPEN.

  6. RS suppresses detection of Request To Send (RTS).
  7. For buffer-related problems, try increasing the transmit and receive buffer sizes above the 512-byte default:

    TB2048 = Increases the transmit buffer size to 2048 bytes.
    RB2048 = Increases the receive buffer size to 2048 bytes.

    A larger receive buffer can help you work around Basic delays caused by statements such as PAINT, which use the processor intensively.

The following are additional important hints for troubleshooting communications problems:

  1. You should use the INPUT$(x) function in conjunction with the LOC(n) function to receive all input from the communications device [where x is the number of characters returned by LOC(n), which is the number of characters in the input queue waiting to be read; n is the file number that you OPENed for "COM1:" or "COM2:"]. Avoid using the INPUT#n statement to input from the communications port because INPUT#n waits for a carriage return (ASCII 13) character.

Avoid using the GET#n statement for communications because GET#n waits for the buffer to fill (and buffer overrun could then occur).

  1. For an example of data communications, please refer to the TERMINAL.BAS sample program that comes on the release disk for QuickBasic version 4.50. Many communications problems may actually be due to inappropriate source code design and flow of control.
  2. Many communications problems can be shown only on certain hardware configurations and are difficult to resolve or duplicate on other computers. We recommend experimenting with a direct connection (with a short null-modem cable) instead of with a phone/modem link between sender and receiver to isolate problems on a given configuration.
  3. The wiring schemes for cables vary widely. Check the pin wiring on your cable. For direct cable connections, a long or high-resistance cable is more likely to cause problems than a short, low-resistance cable.
  4. If both "COM1:" and "COM2:" are open, "COM2:" will be serviced first. At high baud rates, "COM1:" can lose characters when competing for processor time with "COM2:".
  5. Using the ON COM GOSUB statement instead of polling the LOC(n) function to detect communications input can sometimes help you work around timing or buffering problems caused by delays in Basic. Delays in Basic can be caused by string-space garbage collection, PAINT statements, or other operations that heavily use the processor.

Many commercial communications programs use sophisticated techniques not found in Microsoft Basic, and may give better performance.

If you need better communications performance than you are getting from Basic, you may want to try Microsoft C. (You can call Microsoft C versions 5.x routines from QuickBasic version 4.50.)


Why doesn't QuickBasic support COM3 and COM4 serial ports?


Support for these two additional communications ports requires a larger code size for QuickBasic in both the compiler and the run-time module. Therefore, the decision was made NOT to support COM3 and COM4.

The QuickBasic compiler supports the use of serial communications ports COM1 and COM2 through the use of the OPEN "COM" statement.

To access COM3 and COM4, it is possible for compiled Basic to call third-party library routines, which are listed in catalogs such as the "Provantage Buyer's Guide" [in the United States: (800) 336-1166, in Canada: (800) 225-1166, Customer Service: (216) 494-8899]. For example, you may want to contact the Software Interphase Company to determine if its QuickComm product supports COM3 and COM4, which are called from Microsoft compiled Basic.


Can you give an example of key trapping?


Pressing any key in combination with CTRL, SHIFT, ALT, CAPS LOCK, or NUM LOCK changes the keyboard scan code. To trap combinations of keys, the KEY statement requires adding together the values of the keyboard flags as shown in the code example below.

The following is a code example:

   CONST alt = &H8
   CONST noflag = &H0
   CONST leftshift = &H1
   CONST rightshift = &H2
   CONST ctrl = &H4
   CONST numlock = &H20
   CONST capslock = &H40
   CONST extendedkeyboard = &H80
   CONST left = &H4B
   CONST right = &H4D
   CONST up = &H48
   CONST down = &H50
   CONST C = &H2E
   CONST scrolllock = &H46

   KEY 15, CHR$(extendedkeyboard + numlock) + CHR$(left)
   KEY 16, CHR$(extendedkeyboard + numlock) + CHR$(right)
   KEY 17, CHR$(extendedkeyboard + numlock) + CHR$(up)
   KEY 18, CHR$(extendedkeyboard + numlock) + CHR$(down)
   KEY 19, CHR$(ctrl + capslock) + CHR$(C)
   KEY 20, CHR$(extendedkeyboard + ctrl + numlock) + CHR$(scrolllock)

   ON KEY(15) GOSUB Keyleft
   ON KEY(16) GOSUB Keyright
   ON KEY(17) GOSUB Keyup
   ON KEY(18) GOSUB Keydown
   ON KEY(19) GOSUB Keybreak
   ON KEY(20) GOSUB Keybreak

   KEY(15) ON
   KEY(16) ON
   KEY(17) ON
   KEY(18) ON
   KEY(19) ON
   KEY(20) ON


    PRINT "left"

    PRINT "right"

    PRINT "up"

    PRINT "down"

    PRINT "break"

Execution Speed


Why are the newer versions of QuickBasic slower than the previous ones?


The execution speed differences among different versions of QuickBasic are due to different internal floating-point math representations. QuickBasic versions 1.00 and 2.00 used Microsoft Binary Format (MBF) representation. QuickBasic 3.00 was actually shipped with two different versions: QB.EXE used MBF representation, and QB87.EXE, for machines with a math coprocessor, used IEEE format floating-point representation. All Microsoft Basic products (for MS-DOS or OS/2) since QuickBasic version 4.00 have used IEEE format exclusively.

There are several reasons why Microsoft chose to support the IEEE floating-point standard rather than continuing to use MBF. The main reason is that IEEE is the industry standard and is required for the Intel 80x87 math coprocessors. Also, all other Microsoft language products (except COBOL) use IEEE. Therefore, supporting IEEE in QuickBasic, as well, makes interlanguage programming using any combination of these products much easier.

IEEE support, however, requires more overhead and did cause execution speed to slow, although execution speed on machines equipped with a math coprocessor is slightly faster than older (MBF) versions of Basic.

Since INTEGER math is much faster than floating-point math, the best way to improve the speed of your program is to use INTEGER variables whenever possible. One easy way to accomplish this is to use the following code fragment in the beginning of your programs:


The default, in Basic, is to create new variables as SINGLE-precision floating point. Using the DEFINT A-Z forces all new variables that are not declared as some other type (usually this is done with a type specifier such as x! for SINGLE precision) to be INTEGERs. On most programs this makes a dramatic improvement. The variables that have the greatest effect upon execution speed are those that are frequently accessed, such as loop and array indexes. Make these types of items INTEGERs whenever possible.

Limitations of Floating-Point Mathematics


What are some mathematical rounding issues?


Floating-point mathematics is a complex topic that confuses many programmers. The tutorial below should help you recognize programming situations where floating-point errors are likely to occur and show you how to avoid them. It should also allow you to recognize cases that are caused by inherent floating-point math limitations as opposed to compiler errors.

Decimal and Binary Number Systems

Normally, we count things in base 10. The base is completely arbitrary. (The only reason that people have traditionally used base 10 is that they have 10 fingers, which make handy counting tools.)

The number 532.25 in decimal (base 10) means the following:

   (5 * 10^2) + (3 * 10^1) + (2 * 10^0) + (2 * 10^-1) + (5 * 10^-2)
       500    +     30     +      2     +     2/10    +    5/100
   =  532.25

In the binary number system (base 2), each column represents a power of 2, instead of 10. For example, the number 101.01 means the following:

   (1 * 2^2) + (0 * 2^1) + (1 * 2^0) + (0 * 2^-1) + (1 * 2^-2)
       4     +      0    +     1     +      0     +    1/4
   =  5.25  Decimal

How Integers Are Represented in PCs

Since there is no fractional part to an integer, its machine representation is much simpler than it is for floating-point values. Normal integers on PCs are 2 bytes (16 bits) long with the most significant bit indicating the sign. Long integers are 4 bytes long. Positive values are straightforward binary numbers. For example:

1 Decimal = 1 Binary
2 Decimal = 10 Binary
22 Decimal = 10110 Binary etc.

However, negative integers are represented using the "two's complement" scheme. To get the two's complement representation for a negative number, you need to take the binary representation for the number's absolute value and then flip all the bits and add 1. For example:

   4 Decimal = 0000 0000 0000 0100
               1111 1111 1111 1011     flip the bits
     -4      = 1111 1111 1111 1100     add 1

Note that -1 Decimal = 1111 1111 1111 1111 in binary, which explains why Basic treats -1 as logical true (all bits = 1). This is a consequence of not having separate operators for bitwise and logical comparisons. Often in Basic, it is convenient to use the code fragment below when your program will be making many logical comparisons. This greatly aids readability.

   CONST TRUE = -1

Note that adding any combination of two's complement numbers together using ordinary binary arithmetic produces the correct result.

Floating-Point Complications

Every decimal integer can be exactly represented by a binary integer; however, this is not true for fractional numbers. In fact, every number that is irrational in base 10 also is irrational in any system with a base smaller than 10.

For binary, in particular, only fractional numbers that can be represented in the form p/q, where q is an integer power of 2, can be expressed exactly with a finite number of bits. Even common decimal fractions, such as decimal 0.0001, cannot be represented exactly in binary (0.0001 is a repeating binary fraction with a period of 104 bits).

This explains why a simple example, such as the following, will print 1.000054 as output:

   SUM = 0
   FOR I% = 1 TO 10000
      SUM = SUM + 0.0001
   NEXT I%
   PRINT SUM                   ' theoretically = 1.0

The small error in representing 0.0001 in binary propagates to the sum.

For the same reason, you should always be very cautious when making comparisons on real numbers. The following example illustrates a common programming error:

   item1# = 69.82#
   item2# = 69.20# + 0.62#
   IF item1# = item2# then print "Equality!"

This will NOT print "Equality." Why? Because 69.82 cannot be represented exactly in binary, which causes the value that results from the assignment to be SLIGHTLY different (in binary) than the value that is generated from the expression. In practice, you should always code such comparisons in such a way as to allow for some tolerance. For example:

   IF (item1# < 69.83#) AND (item1# > 69.81#) then print "Equal"

This WILL print "Equal."

Other Common Floating-Point Errors

The following are common floating-point errors:

  1. Round-off error

    This error results when all of the bits in a binary number cannot be used in a calculation.

    Example: Adding 0.0001 to 0.9900 (SINGLE precision)

    Decimal 0.0001 will be represented as follows:

    (1.)10100011011011100010111 * 2^(-14+Bias) (13 leading zeros in binary!)

    0.9900 will be represented as follows:

    (1.)11111010111000010100011 * 2^(-1+Bias)

    Now to actually add these numbers, the decimal (binary) points must be aligned. For this they must be unnormalized. Here is the resulting addition:

           .000000000000011010001101 * 2^0  (Only 11 of 23 bits retained)
          +.111111010111000010100011 * 2^0
           .111111010111011100110000 * 2^0

    This is called a round-off error because some computers round when shifting for addition. Others simply truncate. Round-off errors are important to consider whenever you are adding or multiplying two very different values.

  2. Subtracting two almost equal values


    This will be normalized. Note that although the original numbers each had four significant digits, the result has only one significant digit.

  3. Overflow and underflow

    This occurs when the result is too large or too small to be represented by the data type.
  4. Quantizing error

    This occurs with those numbers that cannot be represented in exact form by the floating-point standard.
  5. Division by a very small number

    This can trigger a "divide by zero" error or can produce bad results, as in the following example:

          A = 112000000
          B = 100000
          C = 0.0009
          X = A - B / C

    In QuickBasic, X now has the value 888887, instead of the correct answer, 900000.

  6. Output error

    This type of error occurs when the output functions alter the values they are working with.


For more information on floating-point mathematics, consult the following references:

Stark, Peter A. "Introduction to Numerical Methods." McMillan Publishing, 1970.

Forsythe, George E; Malcolm, Moler. "Computer Methods for Mathematical Computations." Prentice-Hall, 1977.

For further information on the topics covered in this article, consult the following reference books:

Hergert, Douglas. "Microsoft Quickbasic Developing Structured Programs in the Microsoft QuickBasic Environment." Microsoft Press, 1989.

Wilton, Richard. "Programmer's Guide to PC & PS/2 Video Systems." Microsoft Press, 1987.

Norton, Peter. "The New Peter Norton Programmer's Guide to the IBM PC & PS/2." Microsoft Press, 1985.

Duncan, Ray. "Advanced MS-DOS Programming." Microsoft Press, 1988.

Clark Craig, John. "Microsoft QuickBasic Programmer's Toolbox." Microsoft Press, 1988.

Additional query words: QuickBas 4.50

Keywords: KB58530