How to port a program from BAPI to BAPI/CC
February 6, 2009

First, read the QBASIC -> PBCC porting notes and the BAPI/CC changelog. Then:

  1. make a directory, 'old' and move the existing sourcefile and executable to it. Also move, if desired, any .BAK or .LOG files with the same names as the sourcefile (they will be overwritten by PBCC at compile time). Delete BAPISDK.INI.
  2. use the stub in the BAPI/CC documentation to create a template for your BAPI/CC program, or copy a pre-existing BAPI/CC program (such as STUB.BAS) to your dev directory and rename it to the name of the program you're porting. Try and select a template that works the same way (eg. requires INI file, requires commandline parameter, loads extra settings from INI modules, etc).
  3. open the new source in PBCC and the old source in an editor or viewer.
  4. get the program header information from the old source and type it into the new source eg. p.name$, p.copy$ and the initial p. settings (p.needini% etc).
  5. unless they will be useful, delete any existing GLOBAL declarations in the header, and any existing code in readuserini, from the template. Also remove any program- specific routines at the end (left behind from the previous program used as a template).
  6. get the program-specific variablenames from readuserini (in the old source) and enter them as GLOBAL variables at the top of the new source (as per point 8 of the QBASIC -> PBCC porting notes). These variables are those used by the findsetting function (which loads settings from the main section of the INI file, not from within modules).

    Note: global variables come from:

    1. DIMs and constants in readuserINI
    2. findsetting calls in readuserINI
    3. load module code
    4. code in the main body of the program

  7. get the program-specific arraynames from readuserini (in the old source) and enter them as GLOBAL arrays at the top of the new source (as per point 7 of the QBASIC -> PBCC porting notes). These arrays are those used by DIM statements.

    This QBASIC code:

       DIM rulesetlists$(d.max%), filters$(inielemax%)
    

    becomes:

       GLOBAL rulesetlists$(), filters$()
    
  8. copy the program-specific variable settings from readuserini (in the old source) and paste them into readuserini in the new source. eg. mysetting%=10 or readim_e$="setting"
  9. ensure the logic to read INI files and commandlines, and load and check rulesets, if any, is identical (this usually involves reading each line of the main program loop and the loadrule and checkrule subroutines, making changes where necessary). Copy- and-paste any program-specific code from the main loop in the old source to the main loop in the new source.
  10. copy-and-paste the program-specific portions of "loadrule" (if any) to the new source. These usually look like this: indextrue$ = inimod$(ourrulenum%, 20). If extra data is loaded, copy-and-paste the program-specific portions of the extradata loader into the new source. Get the variable and array names used and declare them GLOBAL at the top of the new source (as per points 7 and 8 of the QBASIC -> PBCC porting notes).

    Note: take care to allow for the fact that BAPI/CC, unlike previous versions, does NOT strip the leading settingname= from extra settings as they are loaded. The settingname is provided to the application as well as the setting value, in the same format as the INI file (eg settingname=settingvalue). This is done so that more than one type of extradata can be loaded (eg. so that the application can differentiate between them).

  11. check whether loadrule has used any arrays. if so, ensure DIM statements are placed at the top of the loadrule (or equivalent) routine. copy-and-paste the DIM statement(s) from readuserini (in the old source) into loadrule in the new source. loadrule usually uses arrays if "extra" data is being loaded from the INI file. Otherwise it usually uses only the rulesetlists$() and inimod$() arrays (both of which are pre-declared global by BAPI/CC).
  12. copy-and-paste the data validation routine (usually "checkrule") from the old source into the new source. Ensure the variable it uses to inform the rest of the program that the data is good is GLOBAL. This is usually called ruleok%. The variable ruleok% is declared global by BAPI/CC for this purpose.
  13. copy-and-paste the main work routine (sometimes known as "munchrule") into the munchrule section of the new source. copy-and-paste any program-specific subroutines at the bottom of the new source. This is where the bulk of the program-specific code is transferred.
  14. copy the program-specific DIM statements from readuserini (in the old source) and paste them into each subroutine in the new source that uses them (as per point 7 of the QBASIC -> PBCC porting notes). Keyword-search the source for the name of the each array to find each call. PB/CC sometimes gives an 'array not dimensioned' error - sometimes it doesn't, leaving the problem to be discovered as a GPF at runtime.
  15. close the old source file, ensuring not to save any changes you made. Then, attempt to build and execute the new source. Check the QBASIC -> PBCC porting notes to deal with errors. You'll need to SUB the names of each subroutine, change each RETURN to an END SUB, change each GOSUB to a CALL, type any untyped variables, remove any fullstops from variablenames (replace with underscores), rename any variablenames that are now reserved words, rewrite your errorhandlers, and possibly replace your SHELL commands, and probably some other stuff as well. PBCC will point out each syntax error when you try to compile. Search-and-replace can be very handy here. A 'duplicate name definition' error is sometimes because of an incorrect type specifier on a variable - for example mystring$ as a global string, but accidentally referred to it in your code as mystring%. 'SUB expected' usually means a missing END IF, WEND or NEXT.

    You'll also need to recode any calls to routines that have been removed or changed, such as the findfiles subroutine (which no longer exists) - see the BAPI/CC changelog.

    Once you get the program to compile, try BAPI/CC's new p_debug% toggle if you have logic issues.

  16. Debug. GPFs at runtime are usually due to an attempt to access an undimensioned array. A routine that returns no data is possibly suffering from a local variable that should be declared GLOBAL. Two classes of variables pose what seem to be the trickiest challenges when porting, as they are buried in the code, and cause all sorts of knock-on effects when set wrongly.

    The first, which might be called the "global variable scoped locally" class, are usually control variables which are set by a subroutine for use elsewhere in the program.

    The second, which might be called the "static variable scoped locally" class, as it contains variables that are set by a subroutine, for later use by the SAME subroutine. In QBASIC these variables remain set; in PBCC, they are destroyed when the subroutine exits. There may be little indication in the code that the variable is intended to be static.

Watch out for the few changes in the library, including the change to the abort routine (all detailed in the BAPI/CC changelog).