Article ID: 45850 - View products that this article applies to.
This article was previously published under Q45850
Programmers may want to understand how Microsoft QuickBasic and Microsoft Basic Compiler arrange memory in order to write programs that make efficient use of system resources. The QuickBasic language provides a variety of data types and modular code constructs that allow you to manage the data and code of your programs.
This article covers the following topics:
Appendix B explains the .MAP file created when the Appendix A program example is linked. You can use the LINK map to determine how close each module is to approaching the 64K code limit and how close the .EXE program's static variables are getting to the 64K limit in the DGROUP.
Appendixes C and D describe in detail the memory mapping for running programs in the following three different environments: the QB.EXE editor, a compiled .EXE program using the run-time module, and an .EXE compiled with the stand-alone option.
DefinitionsA "module" is defined as an individual source file containing Basic procedures.
"Module-level code" is defined as the statements within a module that are outside a SUB...END SUB, FUNCTION...END FUNCTION, or DEF FN..END DEF definition block.
A "support module" is a source file, separate from the main module, that contains additional SUB or FUNCTION procedures.
Modular ProgrammingModular programming deals with splitting programs into separate modules (i.e., separate source files containing SUB or FUNCTION procedures). The reasons for doing this are the following:
Consider a program Main.BAS, which is broken into three modules:
Main.EXE is the finished executable program. When you compile in QuickBasic's environment (QB.EXE), the environment automatically compiles each module and LINKs them together to produce the same .OBJ files and finished executable program.
To make an .EXE program from the QB.EXE environment, do the following:
What Goes at the Module Level in a Support ModuleA support module, when compiled, produces an object (.OBJ) file that is LINKed with a main module to create an executable file. The module-level code of the main module is the only code directly executed by QuickBasic. The module-level code of the support modules cannot be CALLed, RUN, or CHAINed. This module-level code of the support modules is used only for:
The compiler metacommands (REM $INCLUDE, REM $STATIC, and REM $DYNAMIC) can also be used at the module level. REM $INCLUDE pastes extra source into the module at compile time. REM $DYNAMIC and REM $STATIC declare subsequent arrays as $DYNAMIC (allocated at run time) or $STATIC (allocated at compile time).
You can use TYPE...END TYPE, DIM, and COMMON SHARED statements in the module-level code. Variables and arrays can be shared between modules using the COMMON SHARED statement at the module level.
COMMON, COMMON SHARED, SHARED, and DIM SHAREDThe SHARED statement gives a SUB or FUNCTION procedure access to variables declared at the main module level of that module (without passing them as parameters). It does NOT give access to variables declared in support modules.
The COMMON statement makes variables available at the module level between modules. The SHARED attribute for the COMMON statement (i.e., COMMON SHARED) is required to share the variables with SUBprograms or FUNCTIONs. The list of variables in the COMMON and COMMON SHARED statements must match in type in the COMMON and COMMON SHARED statements in each module.
When using the COMMON (or COMMON SHARED) statement, static and dynamic arrays are dimensioned differently. Static arrays in COMMON must be DIMensioned BEFORE the COMMON statement in all the modules with that COMMON statement. Dynamic arrays in COMMON must be DIMensioned AFTER the COMMON statement in just the main module and should not be DIMensioned in any support modules.
There are two differences between using the SHARED statement and the DIM SHARED statement:
$DYNAMIC and $STATIC ArraysThe default setting for storing arrays is REM $STATIC, which stores all arrays in DGROUP (the default data segment). For any Basic .EXE program, DGROUP is limited to 64K. If you are using static arrays, you can get more memory in DGROUP by making the arrays dynamic, which moves them into the far heap. To make an array dynamic, DIMension the array after the metacommand REM $DYNAMIC, or DIMension the array with a variable in its subscript.
All dynamic arrays are stored on the far heap except arrays of variable- length strings. Strings, variable-length string arrays, and simple variables are always stored in DGROUP. To store strings in the far heap, you must DIMension a dynamic array of fixed-length strings. DIMensioning strings as fixed length in dynamic arrays helps to free up storage space that they otherwise could have taken in DGROUP, which is limited to 64K.
Huge ArraysHuge arrays are arrays that are larger than 64K. When using huge arrays, you must invoke the QB.EXE editor and BC.EXE compiler with the /AH option. The huge array must be DIMensioned as a dynamic array, either with a variable in the array subscript or with the preceding metacommand REM $DYNAMIC. The /AH option allows dynamic arrays of user-defined types, fixed- length strings, and numeric data to occupy all of available memory.
The number of bytes in a single element of a huge array should preferably be a power of 2, in order to avoid wasted memory and to enable arrays to be larger than 128K, as explained below.
Space is allocated for a huge array contiguously in the far heap, with the restriction that no single array element (or record) is allowed to be split across a 64K boundary. If a record size is not a power of 2, the array is allocated at an offset high enough, relative to the array's base segment address (returned by the VARSEG function), such that no array element is split across the boundary at exactly 64K above the base segment. The value returned by the VARPTR function for the first element of the array then indicates both the offset of the array, and also the size of a gap created in far heap. This gap fragments the far heap, and is wasted, unused memory. The size of the gap is equal to (65,536) MOD (array record size). [In the worst case, the gap can be up to (array record size) minus 1 in size.]
A "Subscript out of range" error occurs when allocating a huge array larger than 128K if the array elements have a size that is not an even power of 2. Arrays larger than 128K must have an element size that is a power of 2 (2, 4, 8, 16, 32, 64, etc.), since arrays are stored contiguously and no single array element is allowed to be split across a 64K boundary.
The final major aspect of QuickBasic memory management is that QuickBasic attempts to make efficient use of memory, which may cause variable-length strings, variable-length string arrays, and dynamic arrays to move around in memory from statement to statement at run time. (Other variables have a fixed location determined at EXE load time.) In addition, if you perform a CHAIN statement, then any data passed in a COMMON block may move. This means that the results of the VARPTR, VARSEG, VARPTR$, or SADD function should always be used immediately after that function is invoked.
APPENDIX A: SAMPLE PROGRAMThis sample program demonstrates the topics covered in this article. This program gives an example of ways to DIMension arrays, and to pass variables between modules and SUBprograms. The program consists of two modules, each which defines one SUBprogram.
Main Module (EXAMPL1.BAS)
Support Module (EXAMPL2.BAS)
APPENDIX B: LINK .MAP FILE EXPLANATIONBelow is a .MAP file created when the program example in Appendix A is linked. The .MAP file from a successful link shows how close a module's code segment is approaching the 64K code limit, and how close the static variables for the entire .EXE program are to approaching the 64K limit in the DGROUP. To generate a listing map of a program, include a map filename in the third argument of the LINK command, as in the following example:
Notes for the link .MAP shown further below are as follows:
Start Stop Length Name Class 00000H 00159H 0015AH EXAMPL1_CODE BC_CODE 00160H 001C1H 00062H EXAMPL2_CODE BC_CODE 001C2H 03931H 03770H CODE CODE 03932H 03A0CH 000DBH INIT_CODE CODE 03A10H 041B2H 007A3H _TEXT CODE 041C0H 065EFH 02430H EMULATOR_TEXT CODE 065F0H 065F0H 00000H C_ETEXT ENDCODE 065F0H 065F7H 00008H FAR_HDR FAR_MSG 065F8H 06C44H 0064DH FAR_MSG FAR_MSG 06C45H 06C46H 00002H FAR_PAD FAR_MSG 06C47H 06C47H 00001H FAR_EPAD FAR_MSG 06C50H 06DBFH 00170H EMULATOR_DATA FAR_DATA 06DC0H 06DC0H 00000H BR_DATA BLANK 06DC0H 06DEFH 00030H BR_SKYS BLANK 06DF0H 06EFBH 0010CH COMMON BLANK 06EFCH 070E3H 001E8H BC_DATA BC_VARS 070E4H 070E9H 00006H NMALLOC BC_VARS 070EAH 070EAH 00000H ENMALLOC BC_VARS 070EAH 070EAH 00000H BC_FT BC_SEGS 070F0H 0710FH 00020H BC_CN BC_SEGS 07110H 07122H 00013H BC_DS BC_SEGS 07124H 07124H 00000H BC_SAB BC_SEGS 07124H 0712BH 00008H BC_SA BC_SEGS 0712CH 0712FH 00004H BC_SAE BC_SEGS 07130H 07345H 00216H _DATA DATA 07346H 0761FH 002DAH _BSS DATA 07620H 0771CH 000FDH BR_DATA DATA 0771EH 0771EH 00000H XIB DATA 0771EH 07741H 00024H XI DATA 07742H 07742H 00000H XIE DATA 07742H 0774DH 0000CH DBDATA DATA 0774EH 0775BH 0000EH CDATA DATA 0775CH 0775CH 00000H XIFB DATA 0775CH 0775CH 00000H XIF DATA 0775CH 0775CH 00000H XIFE DATA 0775CH 0775CH 00000H XPB DATA 0775CH 0775CH 00000H XP DATA 0775CH 0775CH 00000H XPE DATA 0775CH 0775CH 00000H XCB DATA 0775CH 0775FH 00004H XC DATA 07760H 07760H 00000H XCE DATA 07760H 07760H 00000H XCFB DATA 07760H 07760H 00000H XCF DATA 07760H 07760H 00000H XCFE DATA 07760H 0779FH 00040H CONST DATA 077A0H 077A0H 00000H BC_DATA BC_DATA 077A0H 077A0H 00000H XOB BSS 077A0H 077A0H 00000H XO BSS 077A0H 077A0H 00000H XOE BSS 077A0H 07F9FH 00800H STACK STACK Origin Group 06DC:0 DGROUP 065F:0 FMGROUP Program entry point at 03A1:00C8
APPENDIX C: WHERE QuickBasic 4.00, 4.00b, AND 4.50 STORE THEIR ARRAYSThere is one difference in array storage between programs run as compiled .EXE files and programs run within the QuickBasic versions 4.00, 4.00b, and 4.50 environment (QB.EXE). In the QB.EXE environment, an array that is static, not in COMMON, and not composed of variable-length strings, is stored in the far heap (instead of DGROUP, as in .EXE programs).
This changes memory management in your program, depending on where you run your program.
Thus, in programs run within the QuickBasic 4.00, 4.00b, or 4.50 environment (QB.EXE), arrays are stored as follows:
APPENDIX D: MEMORY MAPSThis appendix contains one general memory map and three detailed run-time memory maps.
General Memory DiagramThe following diagram summarizes how QuickBasic compiled programs are loaded into memory at run time:
The following Figures 1, 2, and 3 describe in more detail the arrangement of code and data in memory at run time.
High Memory (640K maximum) +-----------+ | | The far heap stores dynamic arrays. The far | (Far) | heap consists of the rest of high memory | Heap | available after MS-DOS and the Basic | | program's DGROUP segment and code --- +-----------+ segment(s) are allocated. | | | The stack is used to store temporary data, | | Stack | such as variables that are passed to a DGROUP | | subprogram procedure. The default stack Up to |- - - - - -| size is 2K. 64K | | The DGROUP (default data segment) is used | | (Near) | to store all static arrays, strings, | | Data | simple variables, and the stack. DGROUP --- +-----------+ can be up to 64K in size. : +-----------+ | Code | The code segments store the executable code. | Segments | Each module can have up to 64K for its +-----------+ code segment. Low Memory
Figure 1The first figure (below) shows the run-time memory map when the program is executed within the QB.EXE version 4.x environment:
Figure 1 (above): The Memory Map for QB.EXE 4.x Environment
+-------------+ | Quick | | Library | +-------------+ |Communication| User-specified size | buffers | +-------------+ | | This area contains items, such as | FAR heap | large/huge arrays and user code, | | dynamic arrays. +-------------+ User Data ------->Run-time heap| Files buffers, etc. End DS:xxxx +-------------+ | String heap | +-------------+ | User Program| DGROUP | Data | +-------------+ UP to | User Stack | 64K +-------------+ | Quick Lib | | Data | +-------------+ | QuickBasic | User Data | Static | Start DS:0 ------->| Data | +-------------+ | QuickBasic | QB.EXE Low Memory ------->| Code | +-------------+ | MS-DOS | MS-DOS Operating System +-------------+ 0000:0000 ------->
Figure 2This second figure (below) shows the run-time memory map as it appears when the run-time module BRUN4x.EXE is used with the separate compilation (Make EXE File...) method:
Figure 2 (above): The BRUN4x.EXE Run-Time Module Memory for an .EXE
+-------------+ | BRUN4x.EXE | Separately loaded run-time code +-------------+ |Communication| User-specified size | buffers | +-------------+ | | This area holds less-frequently- | | used items, such as dynamic | FAR heap | arrays, the user environment | | table. | | | | +-------------+ User Data ------->Run-time heap| End DS:xxxx +-------------+ | String heap | +-------------+ | User Stack | Preset to 2K +-------------+ | Named | Named COMMON areas | COMMON | DGROUP +-------------+ | BC_DATA | User program variables Up to +-------------+ 64K | BC_CONST | User program constants +-------------+ | Blank COMMON| +-------------+ | _DATA | QuickBasic run-time data areas, User Data | CONST | used during user code execution Start DS:0 ------->| _BSS | +-------------+ | User Code | User program separately linked Low Memory ------->| | with BRUN4x.LIB +-------------+ | MS-DOS | MS-DOS Operating System +-------------+ 0000:0000 ------->
Figure 3This third figure (below) shows the run-time memory map when the stand- alone library (BCOM4x.LIB) option is used with the separate compilation (Make EXE File...) method:
Figure 3 (above): The Stand-Alone (BCOM4x.LIB Library) Memory for an .EXE
+-------------+ |Communication| User specified size | buffers | +-------------+ | | This area contains less frequently | | used items, such as large | FAR heap | numeric arrays, the user | | environment table, dynamic | | arrays. | | +-------------+ User Data ------->|Run-time heap| Files, buffers, etc. End DS:xxxx +-------------+ | String heap | +-------------+ | User Stack | Preset to 2K bytes +-------------+ | Named | Named COMMON areas | COMMON | DGROUP +-------------+ | BC_DATA | User program variables Up to +-------------+ 64K | BC_CONST | User program constants +-------------+ | Blank COMMON| Library and user definitions +-------------+ | _DATA | QuickBasic run-time data areas, User Data | CONST | used during user code execution Start DS:0 ------->| _BSS | +-------------+ |Run-time code| Run-time code linked into file +-------------+ | User Code | User program separately linked Low Memory ------->| | with BCOM4x.LIB +-------------+ | MS-DOS | MS-DOS Operating System +-------------+ 0000:0000 ------->
Article ID: 45850 - Last Review: August 16, 2005 - Revision: 2.1