ADVANCED TECHNIQUES
Updated: Feb 14, 2009

ARRAYREF +
ARRAYREF$ +

  Similar to the BYREF statement described below, ARRAYREF() and ARRAYREF$()
  statements assign values to array elements based on a pointer to the array
  object and the subscripts.  E.g.,

  ARRAYREF(Aptr, i, j) = x    'write numeric value
  ARRAYREF$(Bptr, i, j) = s$  'write string value

  where Aptr and Bptr are pointers to numeric and string arrays respectively,
  and i and j are the subscripts of the array element.

  The pointers to a dimensioned array are obtained with the OBJPTR() Numeric
  Function (please see Numeric Functions).  The number of subscripts typically
  agrees with the number used in the DIM or REDIM statements for a paticular
  destination array.

  Please see the ARRAYREF Numeric Function and ARRAYREF$ String Function. 
  These statements and functions providing read/write access to arrays based
  on array object pointers, facilitate writing of procedures using arrays,
  which may work with any array, as described by arguments such as the
  pointer, number and range of subscripts, string vs numeric type, etc.

  The ARRAYREF family of HotBasic keywords support all array variable types
  except VARIANT.

ASM + statement

  Inserts assembler language statement directly into your executable.

  Dim MyVar As Long
  ASM mov MyVar,1: ASM shl MyVar,5
  PRINT MyVar '=32
  ASM mov MyVar,esp
  PRINT "Stack pointer is "; HEX$(MyVar)
  ASM mov ebx, esp    ;get stack pointer
  ASM mov eax,[ebx+4] ;get stack value
  ASM mov MyVar,eax
  PRINT "Stack contains   "; HEX$(MyVar)

  Note: Assembler code can use any CPU registers but must preserve esp.
  Assembler code may reference any variables dimensioned in the main HotBasic
  code.  Assembler code may create new labels, variables and procedures, but
  these will be "unknown" (not in Symbol Table) to the HotBasic source code.
  Assembler code may be inserted in any $APPTYPE.

  ASM is convenient to insert one or more assembler statements.
  BEGIN/END ASM is convenient for long assembler code insertions.
  Assembler code must be compatible with Microsoft assembler language.

  Please see Appendix > ASM inserts for more details.

BEGIN ASM +
  ;pure assembler code only
END ASM  

BEGIN RUNONCE +
  'code which is run only once in an application
EXIT RUNONCE  'an optional exit from the run-once code block
  'optional additional code
END RUNONCE
 
  The BEGIN RUNONCE, END RUNONCE and EXIT RUNONCE Statements define a code
  block which is only run once in an application.  An important use in
  SUB/FUNCTION procedures is to prevent repeated execution of code with
  repeated calls to the procedure; the code is run once, enhancing procedure
  performance.  The following types of statements generate executable code and
  therefore should be in a RUNONCE code block:

  1. DECLARE ... LIB "xyz" where "xyz" is a .dll file.
  2. DEFSTR or DIM t as STRING; these set up the string buffer which need be
     done only once in an app.  An exception might be a procedure that wants
     to ensure that the string is set to NULL upon procedure entry.
     [DIM of other variable types does not generate executable code.]
  3. DIM of any array, e.g., DIM a(9) as LONG
     [Use the INITARRAY statement outside a RUNONCE block if you want the
     array set to all zeros at every entry instance for the procedure.]
  4. DIM/CREATE of any BITMAP, FORM and MENU objects.
  5. Generally, anything you want to run once.

  A RUNONCE code block would most frequently be found in procedures where the
  above kinds of statements are found.  E.g., a procedure in an .obj or .dll
  module that itself declares and calls other .dll modules.

BYREF + statement stores a value at the location specified by its address.

  BYREF(address) = value  

  Please see the related BYREF Numeric Function

BYREF$ + statement stores a string in a fixed-length ("static") buffer.

  BYREF$(address, max_bytes) = string

  where address points to the destination buffer of size max_bytes.
  The source string is truncated if necessary to fit in the destination.

CALLFUNC pointer, param_n, ..., param_2, param_1

  The *last* parameter is listed *first*.  pointer must be dimensioned variable
  = CODEPTR(label) or CODEPTR(sub)

  DEFDWORD MyProcAddr, FirstParam, LastParam, RetAddr, FirstArg, LastArg
  MyProcAddr = CODEPTR(MyProc)
  'FirstParam is last pushed on to the stack below
  CALLFUNC MyProcAddr, LastParam, FirstParam
  'code
  END

  MyProc:
  POP RetAddr: POP FirstArg: POP LastArg: PUSH RetAddr 'now stack is OK
  'code
  RETURN

  MyProc above is a LABEL/RETURN procedure without DECLARE.  Its code manages
  argument retrieval from the stack and stack cleanup.

  Another example:
  CALLFUNC WinProcAddr, lParam, wParam, uMsg, hWnd 'hWnd is *first* argument.

EXTERNAL symbol_1[, symbol_2, ... symbol_n]

  Used with multiple .obj modules each containing an EXTERNAL statement, where
  the 4-byte value or address is defined with DIM in only one module.  Thus,
  more than one module can access the same user symbol.

  EXTERNAL i, j, k  'placed in each linked .obj module
  DEFINT i, j, k    'defined in only one .obj module

FUNCCALL pointer, param_1, param_2,..., param_n

  Same as CALLFUNC, except for order of parameters.

MEMCPY addr1, addr2, nbytes

  where addr1 is destination, addr2 is source and nbytes is number of bytes to
  copy; all arguments are non-float values.

  MEMCPY(@M1, @M2, 8)  'or with () omitted MEMCOPY @M1, @M2, 8 

MEMSET addr, byte, count; where byte and count are integer values.

  MEMSET(@M,46,80)  'fills M with 80 dot (ASCII 46) characters.

  Caution: Improper use of MEMCPY or MEMSET will likely cause fatal error.

POP MyVar; pops application stack to MyVar; e.g., POP i: POP j

PUSH MyVar; pushes value of MyVar on to application stack.

  Caution: Improper use of POP and PUSH will likely cause fatal error.

REDIMEX lpFormArray, first_new, last_new

  where lpFormArray points to a FORM Object array (e.g., @MyButtons), and
  first_new and last_new is the range of new objects after REDIM.

  Used after a REDIM which increases the number FORM Objects in array.
  REDIMEX (+) initializes the newly created FORM Objects.

  DIM MyButtons(1 to 5) as BUTTON
  REDIM MyButtons(1 to 10)
  REDIMEX @MyButtons,6,10  

RETVAL integer; sets SUB/FUNCTION return value; e.g., RETVAL MyVar

  Used to set return value in CallBack procedures, .obj and .dll modules.
  Use RESULT = in user-defined FUNCTIONs called from the user program.

SETGTK handle, string property descriptor, numeric value

  Linux GTK-mode only; uses g_object_set to assign the "numeric value" to the
  "property" of the object identified by its "handle".  Examples:

  SETGTK(myEdit,"text",@"HotBasic rocks")
  SETGTK(myEdit,"editable",false)

SYSCALL integer, variable number of arguments

  Linux only; does kernel syscall where integer is the syscall identifier,
  followed by the number of arguments required by the prototype for the
  syscall.  Example:

  SYSCALL 4, 1, @"hello", 5  'print hello to StdOut

  where 4 is write, 1 is StdOut, with the string pointer and length.

WINDOW + statement implements FORM object property assignments or methods
  by object handle.

  WINDOW(handle).Left = 10  'set property
  WINDOW(handle).Repaint    'do method

  Syntax:  WINDOW(handle[,qualified_type]).member

  The optional qualified_type argument is needed in special cases where the
  compiler needs this information to generate special code in your application.
  If handle alone does not work, add the qualified type:

  WINDOW(handle, TRACKBAR).color = 255 'red

  Similar WINDOW syntax applies to WINDOW in String and Numeric Funtions.


+ Penthouse (registered) version

Copyright 2003-2009 James J Keene PhD
Original Publication: Oct 8, 2003
