ADVANCED TECHNIQUES
Updated: Apr 3, 2006

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 INT64, SINGLE and 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 eax,[esp+4]: 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 +
  $INCLUDE MyCode.asm  'file contains pure assembler code
END ASM  

  BEGIN/END ASM must be used for assembler code $INCLUDE files which must
  contain only assembler code.

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.

BEGIN THREAD +
  'code which is run completely before another thread is processed
EXIT THREAD  'an optional exit from the thread code block
  'optional additional code
END THREAD

  When multiple threads enter a THREAD code block, only one thread is
  processed at a time -- the first to arrive.  Others are queued to wait until
  its predecessor thread has completed the code block.

  A THREAD code block may be used to prevent unwanted re-entrance or multiple
  threads running the same code in the same time interval and as a tool to
  achieve "thread-safe" processing.

  The hbThread variable minus one is the number of times other threads (one or
  more of them) have tried to enter the THREAD code block (or "tunnel").
  Thus, the thread currently running in the code block can detect if another
  thread is waiting to enter and take appropriate corrective action (e.g.,
  draw less or do less or even set a flag within the block to "ignore" the
  next thread that "enters" the code block so you can "catch up" with the
  events).

  IF hbThread > one THEN  'true if threads are waiting.

  A THREAD code block is like a tunnel that allows another thread to enter
  only when the present thread in the tunnel leaves!  Therefore, any
  thread-specific data used or processed in the code block cannot be corrupted
  by actions of another thread. 

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.

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-2006 James J Keene PhD
Original Publication: Oct 8, 2003
