I/O Pins

I/O pins are defined as variables -- either as a group, or as a single pin -- using the following syntax:

Symbol   VAR   Port{.Bit}

DigCtrl         VAR     RA                      ' 4 IO pins
Segments        VAR     RB                      ' 8 IO pins
Sio             VAR     RC.7                    ' 1 IO pin

As of SX/B version 1.5, three 16-bit pseudo-ports have been added:

On power-up or an external reset (via MCLR), an SX/B program clears all I/O pins (port bits cleared to 0) and initializes them to inputs (TRIS bits set to 1).



Constants

Constant values can be declared in four ways: decimal (default), hex, binary, and ASCII using the following syntax:

Symbol   CON   Value

Hex numbers are preceded with a dollar sign ($), binary numbers are preceded with a percent sign (%), and ASCII characters and strings are enclosed in double quotes ("). If no special punctuation is used then the SX/B compiler will assume the value is decimal. An underscore character may be used in numbers for clarity.

MaxCount        CON     4_000                   ' decimal
LedMask         CON     $F0                     ' hex
SegMap          CON     %0110_0101              ' binary
Letter          CON     "A"                     ' ASCII character
Baud            CON     "T9600"                 ' ASCII string

Constant names can be any combination of letters, numbers, and underscores (_), but the first character must not be a number. Also, constant names cannot use reserved words, such as SX/B instruction names (SERIN, GOTO, etc.) and SX aliases (RA, OPTION, etc.). The maximum number of characters allowed for a constant name is 32.

As of version 1.10 you may define computed constants for use by SASM in assembly routines. For example:

B2400           CON     16                      ' 2400 Baud
B9600           CON     4                       ' 9600 Baud
BitTm           CON     B9600                   ' samples per bit
BitTm15         CON     3*BitTm/2               ' 1.5 bits

In the examples above, the first three constants may be used anywhere in the program. The final definition, for BitTm15, is a computed constant and my be used anywhere a variable is allowed. Note that computed constants may not be used where a constant value is required (e.g., the frequency parameter of FREQOUT).

String constants can be used to create "shortcuts" that ease programming, in effect, allowing the programmer to create new commands. For example:

  GOSUB RX_Byte, @serByte                       ' get command

can be replaced with:

  SERRX @serByte                                ' get command

by defining the following string constant:

SERRX           CON     "GOSUB RX_Byte, "

Using string constants in this manner has generally been superceded by the SUB and FUNC definitions (for subroutine names and parameter requirements). See below for an improved method of declaring subroutines and functions.



Variables

SX/B supports words, bytes, arrays of bytes, and bit variables using the following syntax:

Symbol   VAR   Word{.bitIndex}
Symbol   VAR   Byte{(Size)}{.bitIndex}
Symbol   VAR   Bit

result          VAR     Word                    ' word (16 bites)
count           VAR     Byte                    ' one byte (eight bits)
display         VAR     Byte(4)                 ' array of bytes
timer           VAR     Byte(NumTimers)         ' named constants are allowed
alarm           VAR     Bit                     ' one bit

When defining byte arrays, only constant values or named constants may be used for the Size parameter, and array elements are zero-based, that is the elements are indexed from zero to Size-1. For example, myArray(10) contains the elements myArray(0) through myArray(9). In the SX18/20/28 arrays are limited to 16 elements; in the SX48/52 arrays may have up to 223 elements. When defining arrays for the SX18/20/28 it is best to define them largest to smallest to maximize RAM use efficiency.

Note that word variables may not be used in the index of an array. You can, however use the LSB of a word variable like this:

idx             VAR     Word

Start:
  FOR idx = 1 TO 10
    Leds(idx_LSB) = idx
  NEXT
  END

Variable names may be aliased (renamed) for programming convenience. This also allows a group of bit variables to be included in the same byte for single-line evaluation or modification

clock           VAR     Byte(3)                 ' clock array
secs            VAR     clock(0)                ' seconds
mins            VAR     clock(1)                ' minutes
hrs             VAR     clock(2)                ' hours

flags           VAR     Word                    ' all flags
hiTemp          VAR     flags.15                ' high temp flag
loTemp          VAR     flags.14                ' low temp flag

Internal SX aliases may also be renamed for improved program readability. For example:

Segs            VAR     RB                      ' display segments (anodes)
TRIS_Segs       VAR     TRIS_B                  ' segments TRIS reg
DigCtrl         VAR     RA                      ' digit control (cathode)
TRIS_Dig        VAR     TRIS_A                  ' digits TRIS reg

Variable names can be any combination of letters, numbers, and underscores (_), but the first character must not be a number. Also, variable names cannot use reserved words, such as SX/B instruction names (SERIN, GOTO, etc.) and SX aliases (RA, OPTION, etc.). The maximum number of characters allowed for a variable name is 32.

On power-up or an external reset (via MCLR), an SX/B program initializes all variables to zero unless the NOSTARTUP option is used with the PROGRAM directive.

Variables are usually passed by value using this form:

  var1 = var2                                   ' copy var2 to var1

... actually copies the value of var2 and places it into var1. The address (RAM location) of a variable may be passed by prefacing the variable name with '@':

  var1 = @var2                                  ' put address of var2 into var1

This feature is particularly useful for passing address parameters to subroutines, especially when using PUT, GET, or the __RAM() system array.

Note that when assigning a bit variable the value of a byte or word, as in...

  bitVar = variable

... the bit variable will get cleared to zero if the variable is zero, otherwise the bit variable will be set to one. If you would rather copy a specific bit from a byte or word variable, simply use the bit index you wish to copy:

  bitVar = variable.bitIndex

Word variables have an additional assignment option with two byte values (variables or constants):

  wordVar = lsbValue, msbValue

Variable space (bytes):

 Device General Arrays Max. Array
 SX18/20 20 6x16 + 1x5 + 1x4 16 (each)
 SX28 19 6x16 + 1x5 + 1x4 16 (each)
 SX48/52 17 223 223



Program Labels

An SX/B program uses labels to define entry points into code sections or data tables. When used as a code entry point or data table name, label names must start in column one (not indented), end with a colon(:), and be on its own line. When used elsewhere in the program, labels are named without the colon, as in the example below:

Start:                                          ' colon required
  idx = 0
  PAUSE 500

Main:                                           ' colon required
  READ Msg + idx, char                          ' no colon required
  INC idx
  IF char = 0 THEN Start                        ' no colon required
  SEROUT Sio, Baud, char
  GOTO Main                                     ' no colon required

' -------------------------------------------------------------------------

Msg:                                            ' colon required
  DATA "SX/B makes the SX fun!", 13, 0

Label names can be any combination of letters, numbers, and underscores (_), but the first character must not be a number. Also, label names cannot use reserved words, such as SX/B instruction names (SERIN, GOTO, etc.) and SX aliases (RA, OPTION, etc.). The maximum number of characters allowed for a label name is 32.



Comments

Comments can be used to add additional information to a program. The apostrophe character (') begins a comment section; anything to the right of the comment character will be ignored by the compiler. This allows comments to be added to a line of code. Using the comment character is also a convenient way to disable a line of code without removing it from the program.

' Display a running counter on RB

Main:
  INC RB                                        ' update LEDs
'  INC RB                                       ' this line disabled
  PAUSE 100                                     ' insert delay
  GOTO Main                                     ' run forever

Note: For backward-compatibility with older versions of the BASIC programming language, REM may be used to define a line comment, though this style is outdated and generally discouraged.

REM This is an old-fashioned style comment and typically not used.


Inline Assembly Instructions

SX assembly instructions can be inserted into an SX/B program using the "\" (back-slash) character to preface the assembly code statement. For large blocks of assembly code ASM...ENDASM is recommended.

LedsLo          VAR     RB
LedsHi          VAR     RC

cntr            VAR     Word                    ' 16-bit counter

Start:
  TRIS_B = %00000000                            ' make RB pins outputs
  TRIS_C = %00000000                            ' make RC pins outputs

Main:
  \ MOV LedsLo, cntr_LSB                        ' copy low byte to LEDs
  \ MOV LedsHi, cntr_MSB                        ' copy high byte to LEDs
  PAUSE 100
  \ INC cntr_LSB                                ' update counter
  \ SZ                                          ' skip if cntr_LSB is zero
  \ JMP Main                                    ' jump to Main
  \ INC cntr_LSB                                ' increment high byte
  \ JMP Main                                    ' jump to Main

Note: Array elements hold the address of a variable so they should not be used in inline assembly instructions.



Subroutine Declaration

The programming and use of subroutines is simplified by declaring the subroutine name and the parameter(s) (if any) required. Additionally, the declaration of subroutines allows them to return a byte value (see FUNC, below, for return word values). Declaring subroutines offers significant advantages to the programmer:

SX/B subroutines are defined using the following syntax:

Label   SUB   {Min{, Max}}

Where Min is the minimum number of required parameters (if any) and Max is the maximum number of parameters passed to the subroutine. If Max is not specified then Min is the fixed number of parameters allowed.

The following short segment shows how predefined subroutines simplify SX/B program development:

TX_BYTE         SUB     1, 2                    ' sub with 1 or 2 parameters

Start:
  TXBYTE "*"                                    ' send one asterisk
  TXBYTE "-", 20                                ' send 20 dashes
  TXBYTE                                        ' raises syntax error
  END

TX_BYTE:
  temp1 = __PARAM1                              ' save character
  IF __PARAMCNT = 2 THEN
    temp2 = __PARAM2                            ' save repeats
  ELSE
    temp2 = 1                                   ' set to 1 if not specified
  ENDIF
  DO WHILE temp2 > 0
    SEROUT SOut, Baud, temp1                    ' send the byte
    DEC temp2                                   ' dec repeats
  LOOP
  RETURN

Subroutines can also behave like functions in other languages, returning a byte value directly to a variable after the subroutine call. For example:

  char = RX_BYTE

The value/variable to be returned to the calling code is placed after RETURN at the end of the subroutine code:

RX_BYTE:
  SERIN Sin, Baud, temp1                        ' wait for serial input
  RETURN temp1                                  ' return to caller

When defining subroutines that require no parameters (as RX_BYTE, above), it is best to define the subroutine with a zero parameter count, as this will prevent the compiler from generating an assembly instruction (CLR __PARAMCNT) that is not needed by the program:

RX_BYTE         SUB     0                       ' receive serial byte   

Subroutines do not have to be declared, but by doing so allows the subroutine code to be placed anywhere in the listing (without declaration the code must be in the first half of a code page), GOSUB is no longer needed to call the subroutine, and the compiler is able to check the for the proper number of parameters. The only requirement is that the SUB declaration(s) be placed in the first half of a code page. The advantages of the SUB declaration far outweigh the minor effort required to add the declaration.

When declaring a subroutine for string handling, it must be set to accept at least two parameters (base and offset address values). See READ for details on handling strings with SX/B.



Function Declaration

As of SX/B 1.5, a subroutine can return one or two bytes when defined as a function. As with SUB, FUNC routines are declared in the first half of a code page, but the actual code may reside anywhere.

SX/B functions are defined using the following syntax:

Label   FUNC   ReturnCount{, Min{, Max}}

Where ReturnCount is the number of bytes (0 - 4) returned by the function, Min is the minimum number of required parameters (if any), and Max is the maximum number of parameters passed to the function.

The following short segment shows how to define and use a function that returns a 16-bit value:

FREQ_IN         FUNC    2                       ' function returns two bytes

Start:
  freq1 = FREQ_IN                               ' get frequency
  END

FREQ_IN:
  COUNT Fpin, 1000, tmpW1                       ' count cycles for one second
  RETURN tmpW1                                  ' return two bytes

Note that a function can return more bytes than the target variable. If, for example, a function is designed to return a word and the target output variable for that function is a byte, only the LSB of the return value will be assigned.

The programmer may assign additional return bytes manually, immediately following the function call. In the example below the function is designed to return a 32-bit result. The low word of the result is automatically assigned by the compiler, the high word of the result is manually assigned on the line that follows.

' -------------------------------------------------------------------------
' Variables
' -------------------------------------------------------------------------

result          VAR     Word                    ' 32-bit result
resultHi        VAR     Word
tmpW1           VAR     Word                    ' subroutine work vars
tmpW2           VAR     Word
tmpW3           VAR     Word

WATCH result, 32, UHEX                          ' display 32-bit result

' =========================================================================
  PROGRAM Start
' =========================================================================

' -------------------------------------------------------------------------
' Subroutine Declarations
' -------------------------------------------------------------------------

MULT32		FUNC	4, 2, 4

' -------------------------------------------------------------------------
' Program Code
' -------------------------------------------------------------------------

Start:
  result = MULT32 $FFFF, $0010			' get low word
  resultHi = __PARAM3, __PARAM4			' get high word
  BREAK						' display result in Debug
  END

' -------------------------------------------------------------------------
' Subroutine Code
' -------------------------------------------------------------------------

' Use: MULT32 value1, value2
' -- multiplies two values
' -- when mixing a word and byte, the word must be declared first

MULT32:
  IF __PARAMCNT = 2 THEN			' byte * byte
    tmpW1 = __PARAM1
    tmpW2 = __PARAM2
  ENDIF
  IF __PARAMCNT = 3 THEN			' word * byte
    tmpW1 = __WPARAM12
    tmpW2 = __PARAM3
  ENDIF
  IF __PARAMCNT = 4 THEN			' word * word
    tmpW1 = __WPARAM12
    tmpW2 = __WPARAM34
  ENDIF
  tmpW3 = tmpW1 ** tmpW2			' calculate high word
  tmpW2 = tmpW1 * tmpW2				' calculate low word
  RETURN tmpW2, tmpW3				' return 32 bits, LSW first

The discussion above applies to simple variables only. When using an array element as the target, all bytes are automatically assigned. For example:

bigVal          VAR     Byte(4)

...

  bigVal = MULT32 $1234, $1234

In this case, bigVal(0) .. bigVal(3) are assigned to the return variables __PARAM1 .. __PARAM4 from the function.