Microsoft KB Archive/68186

= Run-Time Fixup Facts; Pointer to STRINGASSIGN Hangs If No /O =

Article ID: 68186

Article Last Modified on 10/20/2003

-

APPLIES TO


 * Microsoft BASIC Professional Development System 7.1
 * Microsoft BASIC Professional Development System 7.0

-



This article was previously published under Q68186



SUMMARY
The C and assembly languages support &quot;pointers to functions&quot; (or &quot;function pointers&quot; for short). However, function pointers to routines that are in the Basic run-time module (such as STRINGLENGTH, STRINGADDRESS, and STRINGASSIGN) do not work unless you compile with BC /O. Function pointers to routines in the Basic run-time don't work properly without /O because the &quot;back patcher&quot; used in Basic's run-time system can't back patch indirect far calls.

(Function pointers to routines in the Basic run-time module do work with Basic's /O because /O uses no back-patched fixups.)

Your assembler or C program can only make direct calls (by name) to run-time module routines, such as STRINGLENGTH, STRINGADDRESS, and STRINGASSIGN, when you compile the invoking Basic program without /O. This limitation is by design.



MORE INFORMATION
Note that the STRINGLENGTH, STRINGADDRESS, STRINGASSIGN, and STRINGRELEASE routines allow you to use Basic PDS's far variable-length strings in mixed-language (C, assembler, and Pascal) programming. For more information, see pages 368-375 of the &quot;Microsoft Basic 7.0: Language Reference&quot; (for 7.00 and 7.10).

How &quot;Back Patching&quot; Operates in Basic's Run-Time Module
If you compile without the BC /O option, you cannot use a function pointer to a Basic run-time routine, such as STRINGLENGTH, STRINGADDRESS, or STRINGASSIGN, because of the back-patched fixups used by the run-time module.

Under MS-DOS, Basic run-time modules are analogous to (but not exactly the same as) an OS/2 or Windows DLL (dynamically linked library). Under OS/2, a Basic run-time module really is a DLL. Below is a quick description of how Basic's run-time module system works under MS-DOS:


 * 1) At LINK time, a small Basic .LIB file satisfies (provides &quot;fixups&quot; for) all of the calls for run-time support. For example, you might link to BRT71ENR.LIB (for 7.10) or BRT70ENR.LIB (for 7.00).
 * 2) During run time, a call to STRINGADDRESS jumps to the fixup location determined at LINK time. At this fixup location is information that describes the offset in the run-time module (BRT7nxxx.EXE) where this call should really jump. (This information is available only at run time, and not at LINK time.)
 * 3) Now that the call to STRINGADDRESS really knows where to jump, the call is physically &quot;back patched&quot; with the correct far address for the actual call to STRINGADDRESS. (This is called a back-patched fixup.)
 * 4) The first call to STRINGADDRESS can now execute the correct routine located in the BRT7nxxx.EXE run-time module.
 * 5) Now that the first STRINGADDRESS call has been &quot;back patched,&quot; all subsequent calls to STRINGADDRESS will jump straight to the STRINGADDRESS routine in BRT7nxxx.EXE (the Basic run-time module), instead of sidetracking through the small fixup routine provided in BRT7nxxx.LIB.

The problem with attempting to use a function pointer to the STRINGADDRESS routine when not compiled with /O is that the run-time back patcher doesn't know how to back patch indirect far calls. The indirect far call uses 4 bytes of stack space, whereas the back-patch pointer expects to use 5 bytes of stack space, so the back patch is off by 1 byte on indirect far calls, which causes the hanging problem. Correct back patching can only be done for direct calls to STRINGADDRESS and STRINGLENGTH.

When compiled with /O, Basic directly calls support routines instead of back patching the calls. This explains why the code example below works with BC /O, but fails when compiled without BC /O.

The following three methods work around this design limitation:


 * 1) Compile all programs with /O. -or-


 * 1) Change the function pointer calls (calls to stradr and strlen in the assembler example below) to direct calls to STRINGADDRESS and STRINGLENGTH. -or-


 * 1) Write two assembly PROCedures, with names such as stradr and strlen. The PROC called stradr would directly call STRINGADDRESS. The PROC strlen would directly call STRINGLENGTH.

Code Example
The Basic routine below works correctly when compiled with BC /O, but when compiled without /O, the Basic routine hangs when calling the assembly routine (at run time).

TEST.BAS
' Basic code to call the assembly procedure below. DECLARE SUB TestText (a$, length%, SegAddress&) DIM length%, SegAddress& a$ = &quot;blah&quot; PRINT &quot;A$'s segmented address is: &quot;, HEX$(SSEGADD(a$)) PRINT &quot;Calling the assembly routine.&quot; CALL TestText(a$, length%, SegAddress&) PRINT length% PRINT &quot;A$'s segmented address is: &quot;, HEX$(SegAddress&) ' This should be the same as the value printed above. PRINT &quot;Back from the assembly call.&quot; END

TESTTEXT.ASM
extrn  STRINGADDRESS:FAR extrn  STRINGLENGTH:FAR .MODEL MEDIUM, Basic .DATA stradr dd  STRINGADDRESS  ; Attempt to make a pointer to                                   ; StringAddress strlen dd  STRINGLENGTH   ; A pointer to STRINGLENGTH .CODE ; Pointer to a$'s adescriptor is at [BP + 10] ; Pointer to length%   is at [BP + 8] ; Pointer to SegAddress is at [BP + 6] PUBLIC TestText TestText PROC push  bp         mov    bp,sp mov   ax, [BP+10] ; Address of A$'s descriptor off the stack push  ax          ; Pass it to STRINGLENGTH.

; call  STRINGLENGTH  ; This direct call always works. call  DWORD PTR strlen ; This fails without /O.

mov   BX,[BP+8]   ; BX Now points to length% mov   [BX],AX     ; Put the value return by STRINGLENGTH in                            ; length% mov   ax, [BP + 10] ; Get the address of the descriptor. push  ax            ; Pass it to StringAddress.

; call  STRINGADDRESS ; ** This direct call always works. call  DWORD PTR stradr ; ** This fails without /O.

mov   bx,[BP+6]     ; Get the address of SegAddress& mov   [bx],ax       ; The segment was stored in ax, mov   [bx+2],dx     ;  the offset was stored in dx. pop   bp            ; Restore the BP register's value. ret   2 TestText ENDP END

