Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Memory that is long-sized (32 bits) can contain a value that is one of 232 possible combinations of bits (i.e., one of 4,294,967,296 combinations). The Spin language performs all mathematic operations using 32-bit signed math, meaning every long value is considered to be in the range -2,147,483,648 to +2,147,483,647. However, the actual numeric value contained within a long is subject to how a computer and user interpret it. In Propeller Assembly a long value can be treated as both signed and unsigned.

Long Variable Declaration (Syntax 1)

In VAR blocks, syntax 1 of LONG is used to declare global, symbolic variables that are either long-sized, or are any array of longs. For example:

Code Block
VAR
  long Temp                      'Temp is a long (2 words, 4 bytes)
  long List[25]                  'List is a long array

The above example declares two variables (symbols), Temp and List. Temp is simply a single, long-sized variable. The line under the Temp declaration uses the optional Count field to create an array of 25 long-sized variable elements called List. Both Temp and List can be accessed from any PUB or PRI method within the same object that this VAR block was declared; they are global to the object. An example of this is below.

Code Block
PUB SomeMethod
  Temp := 25_000_000             'Set Temp to 25,000,000
  List[0] := 500_000             'Set first element of List to 500,000
  List[1] := 9_000               'Set second element of List to 9,000
  List[24] := 60                 'Set last element of List to 60

For more information about using LONG in this way, refer to the VAR section's Variable Declarations (Syntax 1) and keep in mind that LONG is used for the Size field in that description.

Long Data Declaration (Syntax 2)

In DAT blocks, syntax 2 of LONG is used to declare and initialize long-aligned, and/or long-sized data that is compiled as constant values in main memory. DAT blocks allow this declaration to have an optional symbol preceding it, which can be used for later reference (see DAT). For example:

Code Block
DAT
  MyData long 640_000, $BB50                'Long-aligned/sized data
  MyList byte long $FF995544, long 1_000    'Byte-aligned/long-sized

The above example declares two data symbols, MyData and MyList. MyData points to the start of long-aligned and long-sized data in main memory. MyData's values, in main memory, are 640,000 and $0000BB50, respectively. MyList uses a special DAT block syntax of LONG that creates a byte-aligned but long-sized set of data in main memory. MyList's values, in main memory, are $FF995544 and 1,000, respectively. When accessed a byte at a time, MyList contains $44, $55, $99, $FF, 232 and 3, 0 and 0 since the data is stored in little-endian format.

Note: MyList could have been defined as word-aligned, long-sized data if the "byte" reference were replaced with "word".

This data is compiled into the object and resulting application as part of the executable code section and may be accessed using the read/write form, syntax 3, of LONG (see below). For more information about using LONG in this way, refer to the DAT section's Declaring Data (Syntax 1) and keep in mind that LONG is used for the Size field in that description.

Data items may be repeated by using the optional Count field. For example:

Code Block
DAT
  MyData long 640_000, $BB50[3]

The above example declares a long-aligned, long-sized data table, called MyData, consisting of the following four values: 640000, $BB50, $BB50, $BB50. There were three occurrences of $BB50 due to the [3] in the declaration immediately after it.

Reading/Writing Longs of Main Memory (Syntax 3)

In PUB and PRI blocks, syntax 3 of LONG is used to read or write long-sized values of main memory. This is done by writing expressions that refer to main memory using the form: long[BaseAddress][Offset]. Here's an example.

Code Block
PUB MemTest | Temp
  Temp := LONG[@MyData][1]                  'Read long value
  long[@MyList][0] := Temp + $01234567      'Write long value

DAT
  MyData long 640_000, $BB50                'Long-sized/aligned data
  MyList byte long $FF995544, long 1_000    'Byte-sized/aligned long data

In this example, the DAT block (bottom of code) places its data in memory as shown in Figure 2 2. The first data element of MyData is placed at memory address $18. The last data element of MyData is placed at memory address $1C, with the first element of MyList immediately following it at $20. Note that the starting address ($18) is arbitrary and is likely to change as the code is modified or the object itself is included in another application.

$24
(3)
Long Address—
(Long Offset)—
[Long Symbol]—$20
(2)
$1C
(1)
$18
(0)
[MyData]

Data as longs—

640,000

 

 

 

 

$BB50

 

 

 

 

$FF995544

 

 

 

1,000

 

 

 

Data as bytes—

0

196

9

0

$50

 

$BB

$00

$00

$44

 

$55

$99

$FF

232

3

0

0

 

...

Code Block
long[@MyData][1] → long[$18][1] → long[$18 + (1*4)] → long[$1C]

The next line, long[@MyList][0] := Temp + $01234567, writes a long-sized value to main memory. It sets the value at main memory address $20 to $012400B7. The address $20 was calculated from the address of the symbol MyList ($20) plus long offset 0 (0 bytes).

Code Block
long[@MyList][0] → long[$20][0] → long[$20 + (0*4)] → long[$20]

The value $012400B7 was derived from the current value of Temp plus $01234567; $BB50 + $01234567 equals $012400B7.

Addressing Main Memory

As Figure 2 2 suggests, main memory is really just a set of contiguous bytes (see "data as bytes" row) that can also be read as longs (4-byte sets) when done properly. In fact, the above example shows that even the addresses are calculated in terms of bytes. This concept is a consistent theme for any commands that use addresses.

Main memory is ultimately addressed in terms of bytes regardless of the size of value you are accessing; byte, word, or long. This is advantageous when thinking about how bytes, words, and longs relate to each other, but it may prove problematic when thinking of multiple items of a single size, like longs.

For this reason, the LONG designator has a very handy feature to facilitate addressing from a long-centric perspective. Its BaseAddress field when combined with the optional Offset field operates in a base-aware fashion.

Imagine accessing longs of memory from a known starting point (the BaseAddress). You may naturally think of the next long or longs as being a certain distance from that point (the Offset). While those longs are indeed a certain number of "bytes" beyond a given point, it's easier to think of them as a number of "longs" beyond a point (i.e., the 4th long, rather than the long that starts beyond the 12th byte). The LONG designator treats it properly by taking the Offset value (units of longs), multiplies it by 4 (number of bytes per long), and adds that result to the BaseAddress to determine the correct long of memory to read. It also clears the lowest two bits of BaseAddress to ensure the address referenced is a long-aligned one.

So, when reading values from the MyData list, long[@MyData][0] reads the first long value and long[@MyData][1] reads the second.

If the Offset field were not used, the above statements would have to be something like long[@MyData], and long[@MyData+4], respectively. The result is the same, but the way it's written may not be as clear.
For more explanation of how data is arranged in memory, see the DAT section's Declaring Data (Syntax 1).

An Alternative Memory Reference

There is yet another way to access the data from the code example above; you could reference the data symbols directly. For example, these statements read the first two longs of the MyData list:

Code Block
Temp := MyData[0]
Temp := MyData[1]

So why wouldn't you just use direct symbol references all the time? Consider the following case:

Code Block
Temp := MyList[0]
Temp := MyList[1]

Referring back to the example code above Figure 2 2 you might expect these two statements to read the first and second longs of MyList; $FF995544 and 1000, respectively. Instead, it reads the first and second "bytes" of MyList, $44 and $55, respectively.

What happened? Unlike MyData, the MyList entry is defined in the code as byte-sized and byte-aligned data. The data does indeed consist of long-sized values, because each element is preceded by LONG, but since the symbol for the list is declared as byte-sized, all direct references to it will return individual bytes.

However, the LONG designator can be used instead, since the list also happens to be long-aligned because of its position following MyData.

Code Block
Temp := long[@MyList][0]
Temp := long[@MyList][1]

The above reads the first long, $FF995544, followed by the second long, 1000, of MyList. This feature is very handy should a list of data need to be accessed as both bytes and longs at various times in an application.

Other Addressing Phenomena

Both the LONG and direct symbol reference techniques demonstrated above can be used to access any location in main memory, regardless of how it relates to defined data. Here are some examples:

Code Block
Temp := long[@MyList][-1]        'Read last long of MyData (before MyList)
Temp := long[@MyData][2]         'Read first long of MyList (after MyData)
Temp := MyList[-8]               'Read first byte of MyData
Temp := MyData[-2]               'Read long that is two longs before MyData

These examples read beyond the logical borders (start point or end point) of the lists of data they reference. This may be a useful trick, but more often it's done by mistake; be careful when addressing memory, especially if you're writing to that memory.