TIP: Click on subject to list as thread! ANSI
echo: power_bas
to: ALLE
from: THOMAS GOHEL
date: 1996-10-01 00:00:00
subject: FAQ: PowerBASIC 12/16 (Tip`s to C++)

    ===================================================
    7.  Hints in Connection with Turbo-C or Borland C++
    ===================================================
    Short overview:
    7.1.  The author
    7.2.  Why write/use external routines for PB3 in C
    7.3.  The correct memory model
    7.4.  Limitations by the PowerBASIC 3.x compiler/-linker
    7.5.  Passing Parameters
    7.6.  PowerBasic example
    7.7.  Corresponding C module
    7.8.  The assembler code, corresponding to the C module
    7.9.  Usage of routines from external C libraries
    7.10. Preparations if PB V2.1 is used
    7.1. The author
    ---------------
    The hints for "PowerBASIC in cooperation with C compilers like Turbo-C or
    Borland C++ have been contributed to This FAQ by:
                Andras Hoeffken 
                Andras Hoeffken 
                Andras Hoeffken 
    Very useful hints can also be found in the file CTOPB.FAQ contained in 
he
    original PB Vs. 3.2 distribution.
    7.2. Why write/use external routines for PB3 in C
    -------------------------------------------------
    - C routines produce fast running code
    - for extremely fast code: first write a C source, then let the C 
ompiler
      translate this source to ASM code (is done much faster as if one would
      write ASM code directly, see example), finally optimize the ASM code
    - Routines from external C libraries can be used with PB
    7.3. The correct memory model
    -----------------------------
    For the generation of *.EXE files (e.g. by MASM or C + LINK) different 
    memory models can be selected, e.g. Tiny, Small, Medium, Compact,
    Large, Huge, ...
    For the *.EXE files, generated by PowerBASIC, the following is valid 
nly:
            PowerBASIC 3.x - memory model = LARGE
            (PB uses 32-bit FAR pointers for both code- and datasegments)
    The Borland C compiler in his menu:
            Options / Compiler / Code Generation / Model
    has therefore to be set to the LARGE model.
    7.4. Limitations by the PowerBASIC 3.x compiler/-linker
    -------------------------------------------------------
    a) The PB3 linker can only link .OBJ modules, which have ONE data 
egment,
       with a 2nd data segment or a DGROUP the PB3 linker refuses work and
       produces Errors. - C compilers in their default set-up always use
       several data segment names, which are combined i a DGROUP (this has
       certain advantages). For the cooperation with PB, the IDE of the
       C-Compilers in its menu:
                   Options / Compiler / Names
       must therefore be set up like:
           Code Segment: _TEXT          Bss Segment:      _DATA
           Code Group:                  Bss Group:
           Code Class:   TEXT           Bss Class:        DATA
           Data Segment: _DATA          Far Data Segment:
           Data Group:                  Far Data Group:
           Data Class:   DATA           Far Data Class:
           (the stars, originally present, MUST be erased!)
       Now, the C compiler produces only ONE data segment name and no more a
           DGROUP !
    b) The PB3 linker (< Vs. 3.2) does not accept an "_" with segment names
       (standard with C), therefore: use "$ALIAS" in the $LINK line
       (see example)!
    c) The PB3 compiler (< Vs. 3.2) does not accept "_" in names of FUNCTIONS
       and SUBs (standard with C), therefore: use "$ALIAS" in DECLARE lines
       (see example)!
    d) The PB3 compiler transfers parameters to functions and subs in the
       order "from right to left" (Pascal convention) and assumes, that the
       external routine purges the stack before returning to PB. C compilers
       work in the opposite way. Therefore: use "CDECL" in the DECLARE lines 
       (see example)!
    7.5. Passing Parameters
    -----------------------
    PB3 passes parameters to an external routine by 2 different ways:
        - using 'far pointers' ('by reference' or 'by copy')
        - by putting the values directly on the stack (BYVAL)
    The declarations in the C routines must be selected correspondingly!
    7.6. PowerBasic example
    -----------------------
    In the following .BAS program 2 routines show the mechanisms:
    - A: in the add-function "addab" (integer) "c=a+b" is calculated, then
         "x=c+1" is returned as the result of the function.
    - B: in the string-function "chst" the first character of a string is
         replaced by a "*".
    --- Cut ------------------------------------------------------------
    'PB3_TBC.BAS - linking Turbo C functions into PB3.x
    $ALIAS DATA AS "_DATA"  'assignment of one(!) segment name
    $LINK "pb3_tbc.obj"     'special C set-up without DGROUP!
    DEFINT A-Z
    DECLARE FUNCTION addab CDECL ALIAS "_addab" (a, BYVAL b, c)
    DECLARE SUB chst CDECL ALIAS "_chst" (word, word, integer)
    CLS: a = 7: b = 1: c = 0
    PRINT "c_before                =";c         'c=0
    x = addab (a, b, c)                         'A: c=a+b
    PRINT "c_after = a(7) + b(1)   =";c         'c=8
    PRINT "x = c_after + 1         =";x         'x=9
    a$ = "hallo"
    CALL chst (strseg(a$), strptr(a$), len(a$)) 'B: change string
    PRINT "changed string          = ";a$       'prints "*allo"
    END
    --- Cut End --------------------------------------------------------
    7.7. Corresponding C modul
    --------------------------
    --- Cut ------------------------------------------------------------
    /* Modul PB3_TBC.C  */
    #include           /* C library function (for strings) */
    static int d = 1;         /* d and e are put into the same data segment 
/
    static int e;             /* d and e are NO  PB-variables */
    int addab(int far *a, int b, int far *c)
    {
        *c = *a + b;
         e = *c + d;
         return e;
    }
    void chst(unsigned far *stseg, unsigned far *stofs, int far *stlen)
    {
        char far *stdata;      /* pointer to the first string char */
        if (*stlen)            /* if string length > 0 */
        {
        stdata = (char far *) MK_FP(*stseg, *stofs); /* fetch the pointer */
        if (stdata)            /* if we have a valid string */
            *stdata = '*';     /* replace first character */
        }                      /* (do NOT overwrite the string length !) */
    }
    --- Cut End --------------------------------------------------------
    7.8. The assembler code, corresponding to the C module
    ------------------------------------------------------
    If someone is uncertain about the code, produced by the C compiler: get 
    the corresponding ASM code and look, how compact and fast the C generated
    code is.
    To get the ASM code, shown below, first the above module PB3_TBC.C was
    compiled to PB3_TBC.OBJ (with the set-up of par. 7.4 !!), then with a
    disassembler for .OBJ-Files (OBJ2ASM.EXE) the following file PB3_TBC.ASM
    was gained. (You can instead order the IDE of the C compiler to produce
    equivalent ASM code, too. But with an external .OBJ disassembler, you are 
    quite better sure, what the PB3_TBC.OBJ module really contains)
    --- Cut ------------------------------------------------------------
    ;File PB3_TBC.ASM (disassembled from PB3_TBC.OBJ)
    _TEXT  SEGMENT BYTE PUBLIC 'CODE'
    _TEXT  ENDS
    _DATA  SEGMENT WORD PUBLIC 'DATA'
                                ;attention: no BSS segment, no DGROUP !!
    _DATA  ENDS
    PUBLIC _addab
    PUBLIC _chst
    _TEXT   SEGMENT
    assume cs: _TEXT
    assume ds: _DATA
    _addab:
    push   bp
    mov    bp,sp
    ; c = a + b:
    les    bx,dword ptr [bp+006h] ; a
    mov    ax,es:[bx]
    add    ax,[bp+00Ah]           ;   + b
    les    bx,dword ptr [bp+00Ch]
    mov    es:[bx],ax             ; c
    ;e = c + d:
    mov    ax,es:[bx]             ;c (line can be put out for optimization)
    add    ax,$S1                 ;   + d
    mov    dx,seg _DATA
    mov    es,dx
    mov    es:$S2,ax              ; e
    ;return e:
    mov    ax,seg _DATA           ; (line can be put out for optimization)
    mov    es,ax                  ; (line can be put out for optimization)
    mov    ax,es:$S2              ; ax = e
    pop    bp
    retf
    _chst:
    push   bp
    mov    bp,sp
    sub    sp,+004h
    les    bx,dword ptr [bp+00Eh] ; len(a$)
    cmp    word ptr es:[bx],+000h ; nul?
    jz     $L3                    ; yes
    les    bx,dword ptr [bp+006h] ; strseg(a$) - segment
    mov    ax,es:[bx]
    les    bx,dword ptr [bp+00Ah] ; strptr(a$) - offset
    mov    dx,es:[bx]
    mov    [bp-002h],ax           ; own pointer
    mov    [bp-004h],dx
    mov    ax,[bp-004h]
    or     ax,[bp-002h]           ; pointer = 0?
    jz     $L3                    ; yes
    les    bx,dword ptr [bp-004h] ; points to first string char
    mov    byte ptr es:[bx],2Ah   ; overwrite char with '*'
    $L3:
    mov    sp,bp
    pop    bp
    retf
    _TEXT        ENDS
    _DATA        SEGMENT
    $S1     dw     00001h  ; initialized data
    $S2     dw     00000h  ; uninitialized data (here NOT in the
                           ; BSS segment!)
   _DATA        ENDS
    END
    --- Cut End --------------------------------------------------------
    7.9. Usage of routines from external C libraries
    ------------------------------------------------
    Although PB v3.x is much better adapted to C conventions than PB v2.1,
    it's mostly still NOT possible to immediately link .OBJ modules, 
    corresponding to the normal C standard. Reason: C generates more than
    one data segment together with a DGROUP. To overcome this, one has to use
    C or ASM source code and to re-compile/re-assemble these sources with 
    corresponding to the aspects of par. 7.3 and 7.4a. There are 2 
    possibilities:
    - With Borland C++ for the most runtime libraries the complete source 
ode
      is delivered with the standard distribution including instructions, how
      to change the sources, and including MAKE files to re-generate own
      versions of the library modules.
    - If for an .OBJ module no source code is available, it's mostly possible 
      with small modules to get the corresponding ASM source code by using an
      .OBJ-disassembler. Then, after having adapted the definitions of the 
      data segments correctly to the PB3 conventions, it can be re-assembled
      again.
    7.10. Preparations if PB V2.1 is used
    -------------------------------------
    The difference between PB v3.x and PB v2.1 is like follows:
    PB v2.1 does not know a "$ALIAS" instruction. AS PB v2.1 can not work
    with "_" (underscores), in the IDE of the C compiler additional switches
    have to be changed:
    - with segment names (see par 7.4a) "_" are not allowed
    - in the menu "Options / Compiler / Advanced Code Generation" one has to
      deactivate the switch "Generate underbars".
    PB v2.1 does not know the CDECL instruction. One therefore has to put 
    instructions into the C source code, that the C functions have to be 
    compiled corresponding to the PASCAL-conventions (this corresponds to the 
    PB-convention). For this, e.g in the example of par 7.6 and 7.7, the 
    following (changed) lines have to be used:
        DECLARE FUNCTION addab (a, BYVAL b, c)
        DECLARE SUB chst (word, word, integer)
        int pascal addab(int far *a, int b, int far *c)
        void pascal chst(unsigned far *stseg, unsigned far *stofs,
                                                       int far *stlen)
--- CrossPoint v3.11 R
---------------
* Origin: -= http://www.snafu.de/~pbsound/ =- (2:2410/330.1)

SOURCE: echomail via exec-pc

Email questions or comments to sysop@ipingthereforeiam.com
All parts of this website painstakingly hand-crafted in the U.S.A.!
IPTIA BBS/MUD/Terminal/Game Server List, © 2025 IPTIA Consulting™.