SpectNet IDE

Visual Studio 2017/2019 integrated ZX Spectrum IDE for the Community

Z80 Assembler » Expressions

The SpectNetIde assembler has a rich syntax for evaluating expressions. You can use the very same syntax with the #if directives, the Z80 instructions, and the compiler statements.

You can use operands and operators just like in most programming languages. Nevertheless, the SpectNetIde implementation has its particular way of evaluating expressions:

  • Expressions can be one of these types:
    • Booleans (true or false)
    • integers (64-bit)
    • floating point numbers (64-bit precision)
    • strings (with 8-bit characters)
  • The assembler applies implicit conversion whenever it’s possible.
    • Floating point numbers are truncated to integer values.
    • The true Boolean literal is represented with the integer value 1; false with 0.
    • When the assembler needs a Boolean value, 0 is taken into account as false, any other values as true.
    • There is no implicit conversion between strings and any numeric values.
  • When the compiler needs a 16-bit value (for example, ld hl,NNNN), it uses the rightmost 16 bits of an expression’s value.
  • When a Z80 operation (for example, ld a,NN) needs an 8-bit value, it utilizes the rightmost 8 bits.

In the future, these compiler features may change by issuing a warning in case of arithmetic overflow.

  • Besides the parentheses — ( and ) — you can use square brackets — [ and ] — to group operations and change operator precedence.
; This is valid
ld hl,(Offset+#20)*2+BaseAddr

; Just like this
ld hl,[Offset+#20]*2+BaseAddr

Instant and Late Expression Evaluation

Depending on the context in which an expression is used, the compiler evaluates it instantly or decides to postpone the evaluation. For example, when you use the .org pragma, the compiler applies immediate evaluation. Let’s assume, this is your code:

Start: .org #8000 + Later
; code body (omitted)
Later: .db #ff

The value of Later depends on the address in .org, and the .org address depends on Later, so this declaration could not be resolved properly, it’s like a deadlock. To avoid such situations, the .org pragma would raise an error, as the moment of its evaluation the Later symbol’s value is unknown.

For most Z80 instructions the compiler uses late evaluation:

Start: .org #6000
    ld hl,(MyVar)
    ; code body omitted
    ret
MyVar: .defs 2

When the compiler reaches the ld hl,(MyVar) instruction, it does not know the value of MyVar. Nonetheless, it does not stop with an error, but generates the machine code for ld hl,(0), namely #21, #00, and #00; takes a note (it is called a fixup) when MyVal gets a value, the two #00 bytes generated at address #6001 should be updated accordingly.

Operands

You can use the following operands in epressions:

  • Boolean, Decimal and hexadecimal literals
  • Character literals
  • Identifiers
  • The current assembly address

String literals cannot be used as operands.

Operators

You can use about a dozen operators, including unary, binary and ternary ones. In this section you will learn about them. I will introduce them in descending order of their precendence.

Conditional Operator

The assembler supports using only one ternary operator, the conditional operator:

conditional-expression ? true-value : false-value

This operation results in -1:

2 > 3 ? 2 : -1

When the conditional-expression evaluates to true, the operation results in true-value; otherwise in false-value.

Conditional expressions are evaluated from right to left, in contrast to binary operators, which use left-to-right evaluation.

Binary Bitwise Operators

Operator token Precedence Description
| 1 Bitwise OR
^ 2 Bitwise XOR
& 3 Bitwise AND — string concatenation with new line

The & operator can be applied on two strings. If you do so, the compiler concatenates the two strings and puts a \r\n (next line) character pair between them.

Relational Operators

Operator token Precedence Description
== 4 Equality
!= 4 Non-equality
< 5 Less than
<= 5 Less than or equal
> 5 Greater than
>= 5 Greater than or equal

Shift Operators

The bits of the left operand are shifted by the number of bits given by the right operand.

Operator token Precedence Description
<< 6 Shift left
>> 6 Shift right

Basic Arithmetic Operators

Operator token Precedence Description
+ 7 Addition — string concatenation
- 7 Subtraction
* 8 Multiplication
/ 8 Division
% 8 Modulo calculation

Min-Max operators

Operator token Precedence Description
<? 9 Minimum of the left and right operand
>? 9 Maximum of the left and right operand

Unary operators

Operator token Precedence Description
+ 10 Unary plus
- 10 Unary minus
~ 10 Unary bitwise NOT
! 10 Unary logical NOT

Do not forget, you can change the defult precendence with ( and ), or with [ and ].

Functions

The Z80 assembler provides a number of functions that can have zero, one, or more arguments. Several functions (for example as rnd()) have overloads with different signatures. Each function has a name and a parameter list wrapped into parentheses, the parameters are separated by a comma. Of course, parameters can be expressions, and they may invoke other functions, too. Here are a few samples:

lenght("Hello" + " world")
max(value1, value2)
sin(pi()/2)
sqrt(pear + 3.0)

The SpectNetIde support these function signatures:

Signature Value Description
abs(integer) integer The absolute value of an integer number.
abs(float) float The absolute value of a float number.
acos(float) float The angle whose cosine is the specified number.
asin(float) float The angle whose sine is the specified number.
atan(float) float The angle whose tangent is the specified number.
atan2(float, float) float The angle whose tangent is the quotient of two specified numbers.
attr(integer, integer, boolean, boolean) integer Retrieves the color attribute byte value defined by ink (first argument, 0 to 7), paper (second argument, 0 to 7), bright (third argument, 0 - non-zero), and flash (fourth argument, 0 - non-zero). The bright and flash values are optional.
attraddr(integer, integer) integer Returns the memory address of the byte specified screen attribute in the given line (first argument, from top to bottom, 0-192) and column (second argumment, from left to right, 0-255).ceiling(float)
bright(boolean) integer Retrieves the bright flag defined by the attribute (0 - non-zero). Can be ORed to create color attribute value.
ceiling(float) float The smallest integral value that is greater than or equal to the specified number.
cos(float) float The cosine of the specified angle.
cosh(float) float The hyperbolic cosine of the specified angle.
exp(float) float e raised to the specified power.
fill(string, integer) string Creates a new string by concatenating the specified one with the given times.
flash(boolean) integer Retrieves the flash flag defined by the argument (0 - non-zero). Can be ORed to create color attribute value.
floor(float) float The largest integer less than or equal to the specified number.
frac(float) float The fractional part of the specified number.
high(integer) integer The leftmost 8 bits (MSB) of a 16-bit integer number.
ink(integer) integer Retrieves the three ink bits defined by the color argument (0 to 7). Can be ORed to create color attribute value.
int(float) integer The integer part of the specified number.
lcase(string) string The lowercase version of the input string.
left(string, integer) string Takes the leftmost characters of the string with the length specified.
len(string) integer The length of the specified string.
length(string) integer The length of the specified string.
log(float) float The natural (base e) logarithm of a specified number.
log(float, float) float The logarithm of a specified number in a specified base.
log10(float) float The base 10 logarithm of a specified number.
low(integer) integer The rightmost 8 bits (LSB) of an integer number.
lowercase(string) string The lowercase version of the input string.
max(integer, integer) integer The larger of two integer numbers.
max(float, float) float The larger of two float numbers.
min(integer, integer) integer The smaller of two integer numbers.
min(float, float) float The smaller of two float numbers.
nat() float Represents the natural logarithmic base, specified by the constant, e.
paper(integer) integer retrieves the three paper bits defined by the argument (0 to 7). Can be ORed to create color attribute value.
pi() float Represents the ratio of the circumference of a circle to its diameter, specified by the constant, π.
pow(float, float) float The specified number raised to the specified power.
right(string, integer) string Takes the rightmost characters of the string with the length specified.
round(float) float Rounds a float value to the nearest integral value.
round(float, int) float Rounds a float value to a specified number of fractional digits.
rnd() integer Returns a random 32-bit number.
rnd(integer, integer) integer Returns a random 32-bit integer between the first and second number.
scraddr(integer, integer) integer Retrieves the memory address of the screen pixel byte in the specified line (first argument, from top to bottom, 0-192) and in the specified column (second argument, from left to right, 0-255).
sign(integer) integer Returns an integer that indicates the sign of an integer number.
sign(float) integer Returns an integer that indicates the sign of a float number.
sin(float) float The sine of the specified angle.
sinh(float) float The hyperbolic sine of the specified angle.
sqrt(float) float The square root of a specified number.
str(bool) string Convert the input value to a string.
str(integer) string Convert the input value to a string.
str(float) string Convert the input value to a string.
substr(string, integer, integer) string Takes a substring of the specified string from the given position (zero-based) and length.
tan(float) float The tangent of the specified angle.
tanh(float) float The hyperbolic tangent of the specified angle.
truncate(float) integer Calculates the integral part of a specified number.
ucase(string) string The uppercase version of the input string.
uppercase(string) string The uppercase version of the input string.
word(integer) integer The rightmost 16 bits of an integer number.

Functions have the same precedence as the unary operators (such as the unary + and -).

Parse Time Functions

The compiler provides a construct, parse time functions. These functions can receive a Z80 assembly language token and transform them into other language constructs. As the name suggests, these function run in the parsing phase, before the compiler emits code.

The lreg() and hreg() Parse Time Functions

These functions accept a 16-bit register pair token (BC, DE, HL, IX, or IY), and retrieve the lower or higher 8-bit register half of their input. Here is a sample code snippet:

ld a,lreg(bc)
ld c,hreg(hl)
ld a,lreg(ix)
ld l,hreg(de)

The compiler sees as if you wrote this:

ld a,c
ld c,h
ld a,ixl
ld l,d

The textof() Parse Time Function

You can use textof(), which accepts these kinds of tokens: mnemonic, register, register indirection, C port, or condition. This function translates these tokens into uppercase string constants that represent them. Here is a sample:

.dm textof(ldir)
.dm textof(bc)
.dm textof((de))
.dm textof((c))
.dm textof(nz)

The compiler sees as if you wrote this code:

.dm "LDIR"
.dm "BC"
.dm "(DE)"
.dm "(C)"
.dm "NZ"