1158 lines
43 KiB
Plaintext
1158 lines
43 KiB
Plaintext
@database beginner.guide
|
|
|
|
@Master beginner
|
|
|
|
@Width 75
|
|
|
|
|
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
|
the input file beginner.
|
|
|
|
|
|
@NODE "main" "More About Statements and Expressions"
|
|
@Next "BuiltIns.guide/main"
|
|
@Prev "Types.guide/main"
|
|
@Toc "Contents.guide/main"
|
|
|
|
More About Statements and Expressions
|
|
*************************************
|
|
|
|
This chapter details various E statements and expressions that were not
|
|
covered in Part One. It also completes some of the partial descriptions
|
|
given in Part One.
|
|
|
|
|
|
@{" Turning an Expression into a Statement " Link "Turning an Expression into a Statement" }
|
|
@{" Initialised Declarations " Link "Initialised Declarations" }
|
|
@{" Assignments " Link "Assignments" }
|
|
@{" More Expressions " Link "More Expressions" }
|
|
@{" More Statements " Link "More Statements" }
|
|
@{" Unification " Link "Unification" }
|
|
@{" Quoted Expressions " Link "Quoted Expressions" }
|
|
@{" Assembly Statements " Link "Assembly Statements" }
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Turning an Expression into a Statement" "Turning an Expression into a Statement"
|
|
@Next "Initialised Declarations"
|
|
@Toc "main"
|
|
|
|
Turning an Expression into a Statement
|
|
======================================
|
|
|
|
The @{b }VOID@{ub } operator converts an expression to a statement. It does this
|
|
by evaluating the expression and then throwing the result away. This may
|
|
not seem very useful, but in fact we've done it a lot already. We didn't
|
|
use @{b }VOID@{ub } explicitly because E does this automatically if it finds an
|
|
expression where it was expecting a statement (normally when it is on a
|
|
line by itself). Some of the expressions we've turned into statements
|
|
were the procedure calls (to @{b }WriteF@{ub } and @{b }fred@{ub }) and the use of @{b }++@{ub }. Remember
|
|
that all procedure calls denote values because they're really functions
|
|
that return zero by default (see @{"Functions" Link "Procedures.guide/Functions" }).
|
|
|
|
For example, the following code fragments are equivalent:
|
|
|
|
VOID WriteF('Hello world\\n')
|
|
VOID x++
|
|
|
|
WriteF('Hello world\\n')
|
|
x++
|
|
|
|
Since E automatically uses @{b }VOID@{ub } it's a bit of a waste of time writing it
|
|
in, although there may be occasions when you would want to use it to make
|
|
this voiding process more explicit (to the reader). The important thing
|
|
is the fact that expressions can validly be used as statements in E.
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Initialised Declarations" "Initialised Declarations"
|
|
@Next "Assignments"
|
|
@Prev "Turning an Expression into a Statement"
|
|
@Toc "main"
|
|
|
|
Initialised Declarations
|
|
========================
|
|
|
|
Some variables can be initialised using constants in their declarations.
|
|
The variables you cannot initialise in this way are array and complex type
|
|
variables (and procedure parameters, obviously). All the other kinds can
|
|
be initialised, whether they are local or global. An @{fg shine }initialised
|
|
declaration@{fg text } looks very much like a constant definition, with the value
|
|
following the variable name and a @{b }=@{ub } character joining them. The following
|
|
example illustrates initialised declarations:
|
|
|
|
SET ENGLISH, FRENCH, GERMAN, JAPANESE, RUSSIAN
|
|
|
|
CONST FREDLANGS=ENGLISH OR FRENCH OR GERMAN
|
|
|
|
DEF fredspeak=FREDLANGS,
|
|
p=NIL:PTR TO LONG, q=0:PTR TO rec
|
|
|
|
PROC fred()
|
|
DEF x=1, y=88
|
|
/* Rest of procedure */
|
|
ENDPROC
|
|
|
|
Notice how you need to use a constant like @{b }FREDLANGS@{ub } in order to
|
|
initialise the declaration of @{b }fredspeak@{ub } to something mildly complicated.
|
|
Also, notice the initialisation of the pointers @{b }p@{ub } and @{b }q@{ub }, and the position
|
|
of the type information.
|
|
|
|
Of course, if you want to initialise variables with anything more than
|
|
a simple constant you can use assignments at the start of the code.
|
|
Generally, you should always initialise your variables (using either
|
|
method) so that they are guaranteed to have a sensible value when you use
|
|
them. Using the value of a variable that you haven't initialised in some
|
|
way will probably get you in to a lot of trouble, because the value will
|
|
just be something random that happened to be in the memory which is now
|
|
being used by the variable. There are rules for how E initialises some
|
|
kinds of variables (see the `Reference Manual', but it's wise to
|
|
explicitly initialise even those, as (strangely enough!) this will make
|
|
your program more readable.
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Assignments" "Assignments"
|
|
@Next "More Expressions"
|
|
@Prev "Initialised Declarations"
|
|
@Toc "main"
|
|
|
|
Assignments
|
|
===========
|
|
|
|
We've already seen some assignments--these were assignment statements.
|
|
Assignment expressions are similar except (as you've guessed) they can be
|
|
used in expressions. This is because they return the value on the
|
|
right-hand side of the assignment as well as performing the assignment.
|
|
This is useful for efficiently checking that the value that's been
|
|
assigned is sensible. For instance, the following code fragments are
|
|
equivalent, but the first uses an assignment expression instead of a
|
|
normal assignment statement.
|
|
|
|
IF (x:=y*z)=0
|
|
WriteF('Error: y*z is zero (and x is zero)\\n')
|
|
ELSE
|
|
WriteF('OK: y*z is not zero (and x is y*z)\\n')
|
|
ENDIF
|
|
|
|
x:=y*z
|
|
IF x=0
|
|
WriteF('Error: y*z is zero (and x is zero)\\n')
|
|
ELSE
|
|
WriteF('OK: y*z is not zero (and x is y*z)\\n')
|
|
ENDIF
|
|
|
|
You can easily tell the assignment expression: it's in parentheses and not
|
|
on a line by itself. Notice the use of parentheses to group the
|
|
assignment expression. Technically, the assignment operator has a very
|
|
low precedence. Less technically, it will take as much as it can of the
|
|
right-hand side to form the value to be assigned, so you need to use
|
|
parentheses to stop @{b }x@{ub } getting the value @{b }((y*z)=0)@{ub } (which will be @{b }TRUE@{ub } or
|
|
@{b }FALSE@{ub }, i.e., -1 or zero).
|
|
|
|
Assignment expressions, however, don't allow as rich a left-hand side
|
|
as assignment statements. The only thing allowed on the left-hand side of
|
|
an assignment expression is a variable name, whereas the statement form
|
|
allows:
|
|
|
|
@{fg shine }var@{fg text }
|
|
@{fg shine }var@{fg text } [ @{fg shine }expression@{fg text } ]
|
|
@{fg shine }var@{fg text } . @{fg shine }obj_element_name@{fg text }
|
|
^ @{fg shine }var@{fg text }
|
|
|
|
(With as many repetitions of object element selection and/or array
|
|
indexing as the elements' types allow.) Each of these may end with @{b }++@{ub } or
|
|
@{b }--@{ub }. Therefore, the following are all valid assignments (the last
|
|
three use assignment expressions):
|
|
|
|
x:=2
|
|
x--:=1
|
|
x[a*b]:=rubble
|
|
x.apple++:=3
|
|
x[22].apple:=y*z
|
|
x[].banana.basket[6]:=3+full(9)
|
|
x[].pear--:=fred(2,4)
|
|
|
|
x.pear:=(y:=2)
|
|
x[y*z].table[1].orange:=(IF (y:=z)=2 THEN 77 ELSE 33)
|
|
WriteF('x is now \\d\\n', x:=1+(y:=(z:=fred(3,5)/2)*8))
|
|
|
|
You may be wondering what the @{b }++@{ub } or @{b }--@{ub } affect. Well, it's very simple:
|
|
they only affect the @{fg shine }var@{fg text }, which is @{b }x@{ub } in all of the assignment statements
|
|
above. Notice that @{b }x[].pear--@{ub } is the same as @{b }x.pear--@{ub }, for the same
|
|
reasons mentioned earlier (see @{"Element selection and element types" Link "Types.guide/Element selection and element types" }).
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "More Expressions" "More Expressions"
|
|
@Next "More Statements"
|
|
@Prev "Assignments"
|
|
@Toc "main"
|
|
|
|
More Expressions
|
|
================
|
|
|
|
This section discusses side-effects, details two new operators (@{b }BUT@{ub } and
|
|
@{b }SIZEOF@{ub }) and completes the description of the @{b }AND@{ub } and @{b }OR@{ub } operators.
|
|
|
|
|
|
@{" Side-effects " Link "Side-effects" }
|
|
@{" BUT expression " Link "BUT expression" }
|
|
@{" Bitwise AND and OR " Link "Bitwise AND and OR" }
|
|
@{" SIZEOF expression " Link "SIZEOF expression" }
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Side-effects" "Side-effects"
|
|
@Next "BUT expression"
|
|
@Toc "More Expressions"
|
|
|
|
Side-effects
|
|
------------
|
|
|
|
If evaluating an expression causes the contents of variables to change
|
|
then that expression is said to have @{fg shine }side-effects@{fg text }. An assignment
|
|
expression is a simple example of an expression with side-effects. Less
|
|
obvious ones involve function calls with pointers to variables, where the
|
|
function alters the data being pointed to.
|
|
|
|
Generally, expressions with side-effects should be avoided unless it is
|
|
really obvious what is happening. This is because it can be difficult to
|
|
find problems with your program's code if subtleties are buried in
|
|
complicated expressions. On the other hand, side-effecting expressions
|
|
are concise and often very elegant. They are also useful for @{i }obfuscating@{ui }
|
|
your code (i.e., making it difficult to understand--a form of copy
|
|
protection!).
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "BUT expression" "BUT expression"
|
|
@Next "Bitwise AND and OR"
|
|
@Prev "Side-effects"
|
|
@Toc "More Expressions"
|
|
|
|
@{b }BUT@{ub } expression
|
|
--------------
|
|
|
|
@{b }BUT@{ub } is used to sequence two expressions. @{b }@{ub }@{fg shine }exp1@{fg text }@{b } BUT @{ub }@{fg shine }exp2@{fg text }@{b }@{ub } evaluates
|
|
@{fg shine }exp1@{fg text }, and then evaluates and returns the value of @{fg shine }exp2@{fg text }. This may not
|
|
seem very useful at first sight, but if the first expression is an
|
|
assignment it allows for a more general assignment expression. For
|
|
example, the following code fragments are equivalent:
|
|
|
|
fred((x:=12*3) BUT x+y)
|
|
|
|
x:=12*3
|
|
fred(x+y)
|
|
|
|
Notice that parentheses need to be used around the assignment expression
|
|
(in the first fragment) for the reasons given earlier (see @{"Assignments" Link "Assignments" }).
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Bitwise AND and OR" "Bitwise AND and OR"
|
|
@Next "SIZEOF expression"
|
|
@Prev "BUT expression"
|
|
@Toc "More Expressions"
|
|
|
|
Bitwise @{b }AND@{ub } and @{b }OR@{ub }
|
|
------------------
|
|
|
|
As hinted in the earlier chapters, the operators @{b }AND@{ub } and @{b }OR@{ub } are not
|
|
simply logical operators. In fact, they are both bit-wise operators,
|
|
where a @{fg shine }bit@{fg text } is a binary digit (i.e., the zeroes or ones in the binary
|
|
form of a number). So, to see how they work we should look at what
|
|
happens to zeroes and ones:
|
|
|
|
x y x OR y x AND y
|
|
------------------------
|
|
1 1 1 1
|
|
1 0 1 0
|
|
0 1 1 0
|
|
0 0 0 0
|
|
|
|
Now, when you @{b }AND@{ub } or @{b }OR@{ub } two numbers the corresponding bits (binary
|
|
digits) of the numbers are compared individually, according to the above
|
|
table. So if @{b }x@{ub } were @{b }%0111010@{ub } and @{b }y@{ub } were @{b }%1010010@{ub } then @{b }x AND y@{ub } would be
|
|
@{b }%0010010@{ub } and @{b }x OR y@{ub } would be @{b }%1111010@{ub }:
|
|
|
|
%0111010 %0111010
|
|
AND OR
|
|
%1010010 %1010010
|
|
------- -------
|
|
%0010010 %1111010
|
|
|
|
The numbers (in binary form) are lined up above each other, just like you
|
|
do additions with normal numbers (i.e., starting with the right-hand
|
|
digits, and maybe padding with zeroes on the left-hand side). The two
|
|
bits in each column are @{b }AND@{ub }-ed or @{b }OR@{ub }-ed to give the result below the line.
|
|
|
|
So, how does this work for @{b }TRUE@{ub } and @{b }FALSE@{ub }, and logic operations? Well,
|
|
@{b }FALSE@{ub } is the number zero, so all the bits of @{b }FALSE@{ub } are zeroes, and @{b }TRUE@{ub } is
|
|
-1, which has all 32 bits as ones (these numbers are @{b }LONG@{ub } so they are
|
|
32-bit quantities). So @{b }AND@{ub }-ing and @{b }OR@{ub }-ing these values always gives
|
|
numbers which have all zero bits (i.e., @{b }FALSE@{ub }) or all one bits (i.e.,
|
|
@{b }TRUE@{ub }), as appropriate. It's only when you start mixing numbers that
|
|
aren't zero or -1 that you can muck up the logic. The non-zero numbers
|
|
one and four are (by themselves) considered to be true, but @{b }4 AND 1@{ub } is
|
|
@{b }%100 AND %001@{ub } which is zero (i.e., false). So when you use @{b }AND@{ub } as the
|
|
logical operator it's not strictly true that all non-zero numbers
|
|
represent true. @{b }OR@{ub } does not give such problems so all non-zero numbers
|
|
are treated as true. Run this example to see why you should be careful:
|
|
|
|
PROC main()
|
|
test(TRUE, 'TRUE\\t\\t')
|
|
test(FALSE, 'FALSE\\t\\t')
|
|
test(1, '1\\t\\t')
|
|
test(4, '4\\t\\t')
|
|
test(TRUE OR TRUE, 'TRUE OR TRUE\\t')
|
|
test(TRUE AND TRUE, 'TRUE AND TRUE\\t')
|
|
test(1 OR 4, '1 OR 4\\t\\t')
|
|
test(1 AND 4, '1 AND 4\\t\\t')
|
|
ENDPROC
|
|
|
|
PROC test(x, title)
|
|
WriteF(title)
|
|
WriteF(IF x THEN ' is TRUE\\n' ELSE ' is FALSE\\n')
|
|
ENDPROC
|
|
|
|
Here's the output that should be generated:
|
|
|
|
TRUE is TRUE
|
|
FALSE is FALSE
|
|
1 is TRUE
|
|
4 is TRUE
|
|
TRUE OR TRUE is TRUE
|
|
TRUE AND TRUE is TRUE
|
|
1 OR 4 is TRUE
|
|
1 AND 4 is FALSE
|
|
|
|
So, @{b }AND@{ub } and @{b }OR@{ub } are primarily bit-wise operators, but they can be used
|
|
as logical operators under most circumstances, with zero representing
|
|
false and all other numbers representing true. Care must be taken when
|
|
using @{b }AND@{ub } with some pairs of non-zero numbers, since the bit-wise @{b }AND@{ub } of
|
|
such numbers does not always give a non-zero (or true) result.
|
|
|
|
You can easily turn any value into a real truth value using the
|
|
expression @{b }x<>FALSE@{ub }, where @{b }x@{ub } represents the value to be converted. For
|
|
example, this expression is true: @{b }(1<>FALSE) AND (4<>FALSE)@{ub }.
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "SIZEOF expression" "SIZEOF expression"
|
|
@Prev "Bitwise AND and OR"
|
|
@Toc "More Expressions"
|
|
|
|
@{b }SIZEOF@{ub } expression
|
|
-----------------
|
|
|
|
@{b }SIZEOF@{ub } returns the size, in bytes (8-bits, like a @{b }CHAR@{ub }), of an OBJECT
|
|
or a built-in type (like @{b }LONG@{ub }). This can be useful for determining
|
|
storage requirements. For instance, the following code fragment prints
|
|
the size of the object @{b }rec@{ub }:
|
|
|
|
OBJECT rec
|
|
tag, check
|
|
table[8]:ARRAY
|
|
data:LONG
|
|
ENDOBJECT
|
|
|
|
PROC main()
|
|
WriteF('Size of rec object is \\d bytes\\n', SIZEOF rec)
|
|
ENDPROC
|
|
|
|
You may think that @{b }SIZEOF@{ub } is unnecessary because you can easily
|
|
calculate the size of an object just by looking at the sizes of the
|
|
elements. Whilst this is generally true (it was for the @{b }rec@{ub } object),
|
|
there is one thing to be careful about: alignment. This means that @{b }ARRAY@{ub },
|
|
@{b }INT@{ub }, @{b }LONG@{ub } and object typed elements must start at an even memory address.
|
|
Normally this isn't a problem, but if you have an odd number of
|
|
consecutive @{b }CHAR@{ub } typed elements or an odd sized @{b }ARRAY OF CHAR@{ub }, an extra,
|
|
@{fg shine }pad@{fg text } byte is introduced into the object so that the following element is
|
|
aligned properly. For an @{b }ARRAY OF CHAR@{ub } this pad byte could be considered
|
|
part of the array, so in effect this means array sizes are rounded up to
|
|
the nearest even number. Otherwise, pad bytes are just an unusable part
|
|
of an object, and their presence means the object size is not quite what
|
|
you'd expect. Try the following program:
|
|
|
|
OBJECT rec2
|
|
tag, check
|
|
table[7]:ARRAY
|
|
data:LONG
|
|
ENDOBJECT
|
|
|
|
PROC main()
|
|
WriteF('Size of rec2 object is \\d bytes\\n', SIZEOF rec2)
|
|
ENDPROC
|
|
|
|
The only difference between the @{b }rec@{ub } and @{b }rec2@{ub } objects is that the array
|
|
size is seven in @{b }rec2@{ub }. If you run the program you'll see that the size of
|
|
the object has not changed. We might just as well have declared the @{b }table@{ub }
|
|
element to be a slightly bigger array (i.e., have eight elements).
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "More Statements" "More Statements"
|
|
@Next "Unification"
|
|
@Prev "More Expressions"
|
|
@Toc "main"
|
|
|
|
More Statements
|
|
===============
|
|
|
|
This section details five new statements (@{b }INC@{ub }, @{b }DEC@{ub }, @{b }JUMP@{ub }, @{b }EXIT@{ub } and
|
|
@{b }LOOP@{ub }) and describes the use of labelling.
|
|
|
|
|
|
@{" INC and DEC statements " Link "INC and DEC statements" }
|
|
@{" Labelling and the JUMP statement " Link "Labelling and the JUMP statement" }
|
|
@{" EXIT statement " Link "EXIT statement" }
|
|
@{" LOOP block " Link "LOOP block" }
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "INC and DEC statements" "INC and DEC statements"
|
|
@Next "Labelling and the JUMP statement"
|
|
@Toc "More Statements"
|
|
|
|
@{b }INC@{ub } and @{b }DEC@{ub } statements
|
|
----------------------
|
|
|
|
@{b }INC x@{ub } is the same as the statement @{b }x:=x+1@{ub }. However, because it doesn't
|
|
do an addition it's a bit more efficient. Similarly, @{b }DEC x@{ub } is the same as
|
|
@{b }x:=x-1@{ub }.
|
|
|
|
The observant reader may think that @{b }INC@{ub } and @{b }DEC@{ub } are the same as @{b }++@{ub } and
|
|
@{b }--@{ub }. But there's one important difference: @{b }INC x@{ub } always increases @{b }x@{ub } by
|
|
one, whereas @{b }x++@{ub } may increase @{b }x@{ub } by more than one depending on the type to
|
|
which @{b }x@{ub } points. For example, if @{b }x@{ub } were a pointer to @{b }INT@{ub } then @{b }x++@{ub } would
|
|
increase @{b }x@{ub } by two (@{b }INT@{ub } is 16-bit, which is two bytes).
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Labelling and the JUMP statement" "Labelling and the JUMP statement"
|
|
@Next "EXIT statement"
|
|
@Prev "INC and DEC statements"
|
|
@Toc "More Statements"
|
|
|
|
Labelling and the @{b }JUMP@{ub } statement
|
|
--------------------------------
|
|
|
|
A @{fg shine }label@{fg text } names a position in a program, and these names are global
|
|
(they can be referred to in any procedure). The most common use of label
|
|
is with the @{b }JUMP@{ub } statement, but you can also use labels to mark the
|
|
position of some data (see @{"Assembly Statements" Link "Assembly Statements" }). To define a label you
|
|
write a name followed by a colon immediately before the position you want
|
|
to mark. This must be just before the beginning of a statement, either on
|
|
the previous line (by itself) or the start of the same line.
|
|
|
|
The @{b }JUMP@{ub } statement makes execution continue from the position marked by
|
|
a label. This position must be in the same procedure as the @{b }JUMP@{ub }
|
|
statement, but it can be, for instance, outside of a loop (and the @{b }JUMP@{ub }
|
|
will then have terminated that loop). For example, the following code
|
|
fragments are equivalent:
|
|
|
|
x:=1
|
|
y:=2
|
|
JUMP rubble
|
|
x:=9999
|
|
y:=1234
|
|
rubble:
|
|
z:=88
|
|
|
|
x:=1
|
|
y:=2
|
|
z:=88
|
|
|
|
As you can see the @{b }JUMP@{ub } statement has caused the second group of
|
|
assignments to @{b }x@{ub } and @{b }y@{ub } to be skipped. A more useful example uses @{b }JUMP@{ub } to
|
|
help terminate a loop:
|
|
|
|
x:=1
|
|
y:=2
|
|
WHILE x<10
|
|
IF y<10
|
|
WriteF('x is \\d, y is \\d\\n', x, y)
|
|
ELSE
|
|
JUMP end
|
|
ENDIF
|
|
x:=x+2
|
|
y:=y+2
|
|
ENDWHILE
|
|
end:
|
|
WriteF('Finished!\\n')
|
|
|
|
This loop terminates if @{b }x@{ub } is not less than ten (the @{b }WHILE@{ub } check), or if @{b }y@{ub }
|
|
is not less than ten (the @{b }JUMP@{ub } in the @{b }IF@{ub } block). This may seem pretty
|
|
familiar, because it's practically the same as an example earlier (see
|
|
@{"WHILE loop" Link "Introduction.guide/WHILE loop" }). In fact, it's equivalent to:
|
|
|
|
x:=1
|
|
y:=2
|
|
WHILE (x<10) AND (y<10)
|
|
WriteF('x is \\d, y is \\d\\n', x, y)
|
|
x:=x+2
|
|
y:=y+2
|
|
ENDWHILE
|
|
WriteF('Finished!\\n')
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "EXIT statement" "EXIT statement"
|
|
@Next "LOOP block"
|
|
@Prev "Labelling and the JUMP statement"
|
|
@Toc "More Statements"
|
|
|
|
@{b }EXIT@{ub } statement
|
|
--------------
|
|
|
|
As noted above, you can use the @{b }JUMP@{ub } statement and labelling to break
|
|
out of a loop prematurely. However, a much nicer mechanism exists for
|
|
@{b }WHILE@{ub } and @{b }FOR@{ub } loops: the @{b }EXIT@{ub } statement. This statement will terminate
|
|
the closest one of these loops (of which it is part) if the supplied
|
|
expression evaluates to true (i.e., a non-zero value). Any loop using
|
|
@{b }EXIT@{ub } can be re-written without it, but sometimes at the expense of
|
|
readability.
|
|
|
|
The following fragments of code are equivalent:
|
|
|
|
FOR x:=1 TO 10
|
|
y:=f(x)
|
|
EXIT y=-1
|
|
WriteF('x=\\d, f(x)=\\d\\n', x, y)
|
|
ENDFOR
|
|
|
|
FOR x:=1 TO 10
|
|
y:=f(x)
|
|
IF y=-1 THEN JUMP end
|
|
WriteF('x=\\d, f(x)=\\d\\n', x, y)
|
|
ENDFOR
|
|
end:
|
|
|
|
This example shows a situation which is arguably more readable using
|
|
something like @{b }EXIT@{ub }. It can be rewritten using a @{b }WHILE@{ub } loop, as below,
|
|
but the code is a bit less clear.
|
|
|
|
going:=TRUE
|
|
x:=1
|
|
WHILE going AND (x<=10)
|
|
y:=f(x)
|
|
IF y=-1
|
|
going:=FALSE
|
|
ELSE
|
|
WriteF('x=\\d, f(x)=\\d\\n', x, y)
|
|
INC x
|
|
ENDIF
|
|
ENDWHILE
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "LOOP block" "LOOP block"
|
|
@Prev "EXIT statement"
|
|
@Toc "More Statements"
|
|
|
|
@{b }LOOP@{ub } block
|
|
----------
|
|
|
|
A @{b }LOOP@{ub } block is a multi-line statement. It's the general form of loops
|
|
like the @{b }WHILE@{ub } loop, and it builds a loop with no check. So, this kind of
|
|
loop would normally never end. However, as we now know, you can terminate
|
|
a @{b }LOOP@{ub } block using the @{b }JUMP@{ub } statement. As an example, the following two
|
|
code fragments are equivalent:
|
|
|
|
x:=0
|
|
LOOP
|
|
IF x<100
|
|
WriteF('x is \\d\\n', x++)
|
|
ELSE
|
|
JUMP end
|
|
ENDIF
|
|
ENDLOOP
|
|
end:
|
|
WriteF('Finished\\n')
|
|
|
|
x:=0
|
|
WHILE x<100
|
|
WriteF('x is \\d\\n', x++)
|
|
ENDWHILE
|
|
WriteF('Finished\\n')
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Unification" "Unification"
|
|
@Next "Quoted Expressions"
|
|
@Prev "More Statements"
|
|
@Toc "main"
|
|
|
|
Unification
|
|
===========
|
|
|
|
In E, @{fg shine }unification@{fg text } is a way of doing complicated, conditional
|
|
assignments. It may also be referred to as @{fg shine }pattern matching@{fg text } because
|
|
that is what it does: it matches patterns and tries to fit values to the
|
|
variables mentioned in those patterns. The result of a unification is
|
|
true or false, depending on whether the pattern was successfully matched.
|
|
|
|
The basic form of a unification expression is:
|
|
|
|
@{fg shine }expression@{fg text } <=> @{fg shine }pattern@{fg text }
|
|
|
|
The only things that can be used in a pattern are constants and variable
|
|
names, and lists of patterns. (Strictly speaking, lisp-cells are also
|
|
allowed, but this variant of unification is beyond the scope of this
|
|
Guide.) The @{fg shine }pattern@{fg text } is matched against the @{fg shine }expression@{fg text } as follows:
|
|
|
|
@{b }*@{ub } If @{fg shine }pattern@{fg text } is a constant then the match succeeds only if
|
|
@{fg shine }expression@{fg text } evaluates to the same value. So, the simple
|
|
unification expression @{b }x<=>1@{ub } is similar to an equality check @{b }x=1@{ub }.
|
|
|
|
@{b }*@{ub } If @{fg shine }pattern@{fg text } is a variable name then the match is always successful
|
|
and the variable is assigned the value of @{fg shine }expression@{fg text }. So, the
|
|
simple unification expression @{b }1<=>x@{ub } is similar to an assignment @{b }x:=1@{ub }.
|
|
|
|
@{b }*@{ub } If @{fg shine }pattern@{fg text } is a list then @{fg shine }expression@{fg text } is assumed to be a list, and
|
|
each element of @{fg shine }pattern@{fg text } is taken to be a pattern to be
|
|
(recursively) matched against the corresponding element (by index) of
|
|
the @{fg shine }expression@{fg text } list. The match succeeds only if the @{fg shine }pattern@{fg text } list
|
|
and the @{fg shine }expression@{fg text } list are the same length and all the elements
|
|
match. (It is a serious programming error if @{fg shine }pattern@{fg text } is a list but
|
|
@{fg shine }expression@{fg text } does not represent a list. In this case, strange things
|
|
may happen and the program may crash.)
|
|
|
|
So, the things in @{fg shine }pattern@{fg text } that control whether a match succeeds are the
|
|
constants and the lists.
|
|
|
|
If a match succeeds then all variables mentioned in the @{fg shine }pattern@{fg text } will
|
|
be assigned the appropriate values. However, if a match fails you should
|
|
consider all variables involved in the pattern to have undefined values
|
|
(so you may need to initialise them to safely use their values again).
|
|
This is because the actual way that unification is implemented may not
|
|
follow the rules above in the obvious way, but will have the same effect
|
|
in the successful case and will affect only the variables mentioned in the
|
|
pattern if the match fails.
|
|
|
|
For example, the following program shows a couple of simple unification
|
|
expressions in use:
|
|
|
|
PROC main()
|
|
DEF x, lt
|
|
x:=0
|
|
WriteF('x is \\d\\n', x)
|
|
lt:=[9,-1,7,4]
|
|
|
|
/* The next line uses unification */
|
|
IF lt <=> [9,-1,x,4]
|
|
WriteF('First match succeeded\\n')
|
|
WriteF('1) x is now \\d\\n', x)
|
|
ELSE
|
|
WriteF('First match failed\\n')
|
|
/* To be safe, reset x */
|
|
x:=0
|
|
ENDIF
|
|
|
|
/* The next line uses unification */
|
|
IF lt <=> [1,x,6,4]
|
|
WriteF('Second match succeeded\\n')
|
|
WriteF('2) x is now \\d\\n', x)
|
|
ELSE
|
|
WriteF('Second match failed\\n')
|
|
/* To be safe, reset x */
|
|
x:=0
|
|
ENDIF
|
|
ENDPROC
|
|
|
|
The first match will succeed in this example, and there will be a
|
|
side-effect of assigning seven to @{b }x@{ub }. The second match will not succeed
|
|
because, for instance, the first element of @{b }lt@{ub } is not one.
|
|
|
|
We can rewrite the above example without using the unification operator
|
|
(to show why unification is so useful). This code follows the rules in
|
|
one particular way, so is not guaranteed to have the same effect as the
|
|
unification version if any of the matches fail.
|
|
|
|
PROC main()
|
|
DEF x, lt, match
|
|
x:=0
|
|
WriteF('x is \\d\\n', x)
|
|
lt:=[9,-1,7,4]
|
|
|
|
/* The next lines mimic: lt <=> [9,-1,x,4] */
|
|
match:=FALSE
|
|
IF ListLen(lt)=4
|
|
IF ListItem(lt, 0)=9
|
|
IF ListItem(lt, 1)=-1
|
|
x:=ListItem(lt,2)
|
|
IF ListItem(lt, 3)=4 THEN match:=TRUE
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
IF match
|
|
WriteF('First match succeeded\\n')
|
|
WriteF('1) x is now \\d\\n', x)
|
|
ELSE
|
|
WriteF('First match failed\\n')
|
|
/* To be safe, reset x */
|
|
x:=0
|
|
ENDIF
|
|
|
|
/* The next lines mimic: lt <=> [1,x,6,4] */
|
|
match:=FALSE
|
|
IF ListLen(lt)=4
|
|
IF ListItem(lt, 0)=1
|
|
x:=ListItem(lt, 1)
|
|
IF ListItem(lt, 2)=6
|
|
IF ListItem(lt, 3)=4 THEN match:=TRUE
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
IF match
|
|
WriteF('Second match succeeded\\n')
|
|
WriteF('2) x is now \\d\\n', x)
|
|
ELSE
|
|
WriteF('Second match failed\\n')
|
|
/* To be safe, reset x */
|
|
x:=0
|
|
ENDIF
|
|
ENDPROC
|
|
|
|
Here's a slightly more complicated example, which shows how you might
|
|
use patterns made up of nested lists. Remember that if the pattern is a
|
|
list then the expression to be matched must be a list. If this is not the
|
|
case (e.g., if the expression represents @{b }NIL@{ub }) then your program could
|
|
behave strangely or even crash your computer. A similar, but less
|
|
disastrous, problem is if the converse happens: the pattern is not a list
|
|
but the expression to be matched is a list. In this case the pointer (to
|
|
the list) is matched against the pattern constant or assigned to the
|
|
pattern variable.
|
|
|
|
PROC main()
|
|
DEF x=10, y=-3, p=NIL:PTR TO LONG, lt, i
|
|
WriteF('x is \\d, y is \\d\\n', x, y)
|
|
lt:=[[23,x],y]
|
|
|
|
/* This basically swaps x and y */
|
|
IF lt <=> [[23,y],x]
|
|
WriteF('First match succeeded\\n')
|
|
WriteF('1) Now x is \\d, y is \\d\\n', x, y)
|
|
ELSE
|
|
WriteF('First match failed\\n')
|
|
/* To be safe, reset x and y */
|
|
x:=10; y:=-3
|
|
ENDIF
|
|
|
|
/* This will make p point to the sub-list of lt */
|
|
IF lt <=> [p,-3]
|
|
WriteF('Second match succeeded\\n')
|
|
WriteF('2) p is now $\\h (a pointer to a list)\\n', p)
|
|
FOR i:=0 TO ListLen(p)-1
|
|
WriteF(' Element \\d of the list p is \\d\\n', i, p[i])
|
|
ENDFOR
|
|
ELSE
|
|
WriteF('First match failed\\n')
|
|
/* To be safe, reset p */
|
|
p:=NIL
|
|
ENDIF
|
|
ENDPROC
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Quoted Expressions" "Quoted Expressions"
|
|
@Next "Assembly Statements"
|
|
@Prev "Unification"
|
|
@Toc "main"
|
|
|
|
Quoted Expressions
|
|
==================
|
|
|
|
@{fg shine }Quoted expressions@{fg text } are a powerful feature of the E language, and they
|
|
require quite a bit of advanced knowledge. Basically, you can @{fg shine }quote@{fg text } any
|
|
expression by starting it with the back-quote character ` (be careful not
|
|
to get it mixed up with the quote character ' which is used for strings).
|
|
This quoting action does not evaluate the expression; instead, the address
|
|
of the code for the expression is returned. This address can be used just
|
|
like any other address, so you can, for instance, store it in a variable
|
|
and pass it to procedures. Of course, at some point you will use the
|
|
address to execute the code and get the value of the expression.
|
|
|
|
The idea of quoted expressions was borrowed from the functional
|
|
programming language Lisp. Also borrowed were some powerful functions
|
|
which combine lists with quoted expressions to give very concise and
|
|
readable statements.
|
|
|
|
|
|
@{" Evaluation " Link "Evaluation" }
|
|
@{" Quotable expressions " Link "Quotable expressions" }
|
|
@{" Lists and quoted expressions " Link "Lists and quoted expressions" }
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Evaluation" "Evaluation"
|
|
@Next "Quotable expressions"
|
|
@Toc "Quoted Expressions"
|
|
|
|
Evaluation
|
|
----------
|
|
|
|
When you've quoted an expression you have the address of the code which
|
|
calculates the value of the expression. To evaluate the expression you
|
|
pass this address to the @{b }Eval@{ub } function. So now we have a round-about way
|
|
of calculating the value of an expression. (If you have a GB keyboard you
|
|
can get the ` character by holding down the ALT key and pressing the '
|
|
key, which is in the corner just below the ESC key. On a US and most
|
|
European keyboards it's on the same key but you don't have to press the
|
|
ALT key at the same time.)
|
|
|
|
PROC main()
|
|
DEF adr, x, y
|
|
x:=0; y:=0
|
|
adr:=`1+(fred(x,1)*8)-y
|
|
x:=2; y:=7
|
|
WriteF('The value is \\d\\n', Eval(adr))
|
|
x:=1; y:=100
|
|
WriteF('The value is now \\d\\n', Eval(adr))
|
|
ENDPROC
|
|
|
|
PROC fred(a,b) RETURN (a+b)*a+20
|
|
|
|
This is the output that should be generated:
|
|
|
|
The value is 202
|
|
The value is now 77
|
|
|
|
This example shows a quite complicated expression being quoted. The
|
|
address of the expression is stored in the variable @{b }adr@{ub }, and the
|
|
expression is evaluated using @{b }Eval@{ub } in the calls to @{b }WriteF@{ub }. The values of
|
|
the variables @{b }x@{ub } and @{b }y@{ub } when the expression is quoted are irrelevant--only
|
|
their values each time @{b }Eval@{ub } is used are significant. Therefore, when @{b }Eval@{ub }
|
|
is used in the second call to @{b }WriteF@{ub } the values of @{b }x@{ub } and @{b }y@{ub } have changed so
|
|
the resulting value is different.
|
|
|
|
Repeatedly evaluating the same expression is the most obvious use of
|
|
quoted expressions. Another common use is when you want to do the same
|
|
thing for a variety of different expressions. For example, if you wanted
|
|
to discover the amount of time it takes to calculate the results of
|
|
various expressions it would be best to use quoted expressions, something
|
|
like this:
|
|
|
|
DEF x,y
|
|
|
|
PROC main()
|
|
x:=999; y:=173
|
|
time(`x+y, 'Addition')
|
|
time(`x*y, 'Multiplication')
|
|
time(`fred(x), 'Procedure call')
|
|
ENDPROC
|
|
|
|
PROC time(exp, message)
|
|
WriteF(message)
|
|
/* Find current time */
|
|
Eval(exp)
|
|
/* Find new time and calculate difference, t */
|
|
WriteF(': time taken \\d\\n', t)
|
|
ENDPROC
|
|
|
|
This is just the outline of a program--it's not complete so don't bother
|
|
running it. The complete version is given as a worked example later (see
|
|
@{"Timing Expressions" Link "Examples.guide/Timing Expressions" }).
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Quotable expressions" "Quotable expressions"
|
|
@Next "Lists and quoted expressions"
|
|
@Prev "Evaluation"
|
|
@Toc "Quoted Expressions"
|
|
|
|
Quotable expressions
|
|
--------------------
|
|
|
|
There is no restriction on the kinds of expression you can quote,
|
|
except that you need to be careful about variable scoping. If you use
|
|
local variables in a quoted expression you can only @{b }Eval@{ub } it within the
|
|
same procedure (so the variables are in scope). However, if you use only
|
|
global variables you can @{b }Eval@{ub } it in any procedure. Therefore, if you are
|
|
going to pass a quoted expression to a procedure and do something with it,
|
|
you should use only global variables.
|
|
|
|
A word of warning: @{b }Eval@{ub } does not check to see if the address it's been
|
|
given is really the address of an expression. You can therefore get in a
|
|
real mess if you pass it, say, the address of a variable using @{b }{@{ub }@{fg shine }var@{fg text } @{b } }@{ub }.
|
|
You need to check all uses of things like @{b }Eval@{ub } yourself, because the E
|
|
compiler lets you write things like @{b }Eval(x+9)@{ub }, where you probably meant to
|
|
write @{b }Eval(`x+9)@{ub }. That's because you might want the address you pass to
|
|
@{b }Eval@{ub } to be the result of complicated expressions. So you may have meant
|
|
to pass @{b }x+9@{ub } as the parameter!
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Lists and quoted expressions" "Lists and quoted expressions"
|
|
@Prev "Quotable expressions"
|
|
@Toc "Quoted Expressions"
|
|
|
|
Lists and quoted expressions
|
|
----------------------------
|
|
|
|
There are several E built-in functions which use lists and quoted
|
|
expressions in powerful ways. These functions are similar to functional
|
|
programming constructs and, basically, they allow for very readable code,
|
|
which otherwise would require iterative algorithms (i.e., loops).
|
|
|
|
@{b }MapList(@{ub }@{fg shine }address@{fg text }@{b },@{ub }@{fg shine }list@{fg text }@{b },@{ub }@{fg shine }e-list@{fg text }@{b },@{ub }@{fg shine }quoted-exp@{fg text }@{b })@{ub }
|
|
The @{fg shine }address@{fg text } is the address of a variable (e.g., @{b }{x}@{ub }), @{fg shine }list@{fg text } is a
|
|
list or E-list (the source), @{fg shine }e-list@{fg text } is an E-list variable (the
|
|
destination), and @{fg shine }quoted-exp@{fg text } is the address of an expression which
|
|
involves the addressed variable (e.g., @{b }`x+2@{ub }). The effect of the
|
|
function is to take, in turn, a value from @{fg shine }list@{fg text }, store it at
|
|
@{fg shine }address@{fg text }, evaluate the quoted expression and store the result in the
|
|
destination list. The resulting list is also returned (for
|
|
convenience).
|
|
|
|
For example:
|
|
|
|
MapList({y}, [1,2,3,a,99,1+c], lt, `y*y)
|
|
|
|
results in @{b }lt@{ub } taking the value:
|
|
|
|
[1,4,9,a*a,9801,(1+c)*(1+c)]
|
|
|
|
Functional programmers would say that @{b }MapList@{ub } @{fg shine }mapped@{fg text } the function
|
|
(the quoted expression) across the (source) list.
|
|
|
|
@{b }ForAll(@{ub }@{fg shine }address@{fg text }@{b },@{ub }@{fg shine }list@{fg text }@{b },@{ub }@{fg shine }quoted-exp@{fg text }@{b })@{ub }
|
|
Works just like @{b }MapList@{ub } except that the resulting list is not stored.
|
|
Instead, @{b }ForAll@{ub } returns @{b }TRUE@{ub } if every element of the resulting list is
|
|
true (i.e., non-zero), and @{b }FALSE@{ub } otherwise. In this way it decides
|
|
whether the quoted expression is true @{fg shine }for all@{fg text } elements of the
|
|
source list. For example, the following are @{b }TRUE@{ub }:
|
|
|
|
ForAll({x}, [1,2,-13,8,0], `x<10)
|
|
ForAll({x}, [1,2,-13,8,0], `x<=8)
|
|
ForAll({x}, [1,2,-13,8,0], `x>-20)
|
|
|
|
and these are @{b }FALSE@{ub }:
|
|
|
|
ForAll({x}, [1,2,-13,8,0], `x OR x)
|
|
ForAll({x}, [1,2,-13,8,0], `x=2)
|
|
ForAll({x}, [1,2,-13,8,0], `x<>2)
|
|
|
|
@{b }Exists(@{ub }@{fg shine }address@{fg text }@{b },@{ub }@{fg shine }list@{fg text }@{b },@{ub }@{fg shine }quoted-exp@{fg text }@{b })@{ub }
|
|
Works just like @{b }ForAll@{ub } except it returns @{b }TRUE@{ub } if the quoted
|
|
expression is true (i.e., non-zero) for at least one of the source
|
|
list elements, and @{b }FALSE@{ub } otherwise. For example, the following are
|
|
@{b }TRUE@{ub }:
|
|
|
|
Exists({x}, [1,2,-13,8,0], `x<10)
|
|
Exists({x}, [1,2,-13,8,0], `x=2)
|
|
Exists({x}, [1,2,-13,8,0], `x>0)
|
|
|
|
and these are @{b }FALSE@{ub }:
|
|
|
|
Exists({x}, [1,2,-13,8,0], `x<-20)
|
|
Exists({x}, [1,2,-13,8,0], `x=4)
|
|
Exists({x}, [1,2,-13,8,0], `x>8)
|
|
|
|
@{b }SelectList(@{ub }@{fg shine }address@{fg text }@{b },@{ub }@{fg shine }list@{fg text }@{b },@{ub }@{fg shine }e-list@{fg text }@{b },@{ub }@{fg shine }quoted-exp@{fg text }@{b })@{ub }
|
|
Works just like @{b }MapList@{ub } except the @{fg shine }quoted-exp@{fg text } is used to decide
|
|
which elements from @{fg shine }list@{fg text } are copied to @{fg shine }e-list@{fg text }. The only elements
|
|
which are copied are those for which @{fg shine }quoted-exp@{fg text } is true (i.e.,
|
|
non-zero). The resulting list is also returned (for convenience).
|
|
|
|
For example:
|
|
|
|
SelectList({y}, [99,6,1,2,7,1,1,6,6], lt, `y>5)
|
|
|
|
results in @{b }lt@{ub } taking the value:
|
|
|
|
[99,6,7,6,6]
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Assembly Statements" "Assembly Statements"
|
|
@Prev "Quoted Expressions"
|
|
@Toc "main"
|
|
|
|
Assembly Statements
|
|
===================
|
|
|
|
The E language incorporates an assembler so you can write Assembly
|
|
mnemonics as E statements. You can even write complete Assembly programs
|
|
and compile them using the E compiler. More powerfully, you can use E
|
|
variables as part of the mnemonics, so you can really mix Assembly
|
|
statements with normal E statements.
|
|
|
|
This is not really the place to discuss Assembly programming, so if you
|
|
plan to use this feature of E you should get yourself a good book,
|
|
preferably on Amiga Assembly. Remember that the Amiga uses the Motorola
|
|
68000 CPU, so you need to learn the Assembly language for that CPU. More
|
|
powerful and newer Amigas use more advanced CPUs (such as the 68020) which
|
|
have extra mnemonics. Programs written using just 68000 CPU mnemonics
|
|
will work on all Amigas.
|
|
|
|
If you don't know 68000 Assembly language you ought to skip this
|
|
section and just bear in mind that E statements you don't recognise are
|
|
probably Assembly mnemonics.
|
|
|
|
|
|
@{" Assembly and the E language " Link "Assembly and the E language" }
|
|
@{" Static memory " Link "Static memory" }
|
|
@{" Things to watch out for " Link "Things to watch out for" }
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Assembly and the E language" "Assembly and the E language"
|
|
@Next "Static memory"
|
|
@Toc "Assembly Statements"
|
|
|
|
Assembly and the E language
|
|
---------------------------
|
|
|
|
You can reference E variables simply by using them in an operand.
|
|
Follow the comments in the next example (the comments are on the same
|
|
lines as the Assembly mnemonics, the other lines are normal E statements):
|
|
|
|
PROC main()
|
|
DEF x
|
|
x:=1
|
|
MOVE.L x, D0 /* Copy the value in x to register D0 */
|
|
ADD.L D0, D0 /* Double the value in D0 */
|
|
MOVE.L D0, x /* Copy the value in D0 back to variable x */
|
|
WriteF('x is now \\d\\n', x)
|
|
ENDPROC
|
|
|
|
Constants can also be referenced but you need to precede the constant with
|
|
a @{b }#@{ub }.
|
|
|
|
CONST APPLE=2
|
|
|
|
PROC main()
|
|
DEF x
|
|
MOVE.L #APPLE, D0 /* Copy the constant APPLE to register D0 */
|
|
ADD.L D0, D0 /* Double the value in D0 */
|
|
MOVE.L D0, x /* Copy the value in D0 to variable x */
|
|
WriteF('x is now \\d\\n', x)
|
|
ENDPROC
|
|
|
|
Labels and procedures can similarly be referenced, but these are
|
|
PC-relative so you can only address them in this way. The following
|
|
example illustrates this, but doesn't do anything useful:
|
|
|
|
PROC main()
|
|
DEF x
|
|
LEA main(PC), A0 /* Copy the address of main to register A0 */
|
|
MOVE.L A0, x /* Copy the value in A0 to variable x */
|
|
WriteF('x is now \\d\\n', x)
|
|
ENDPROC
|
|
|
|
You can call Amiga system functions in the same way as you would normally
|
|
in Assembly. You need to load the A6 register with the appropriate
|
|
library base, load the other registers with appropriate data and then @{b }JSR@{ub }
|
|
to the system routine. This next example uses the E built-in variable
|
|
@{b }intuitionbase@{ub } and the Intuition library routine @{b }DisplayBeep@{ub }. When you run
|
|
it the screen flashes (or, with Workbench 2.1 and above, you might get a
|
|
beep or a sampled sound, depending on your Workbench setup).
|
|
|
|
PROC main()
|
|
MOVE.L #NIL, A0
|
|
MOVE.L intuitionbase, A6
|
|
JSR DisplayBeep(A6)
|
|
ENDPROC
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Static memory" "Static memory"
|
|
@Next "Things to watch out for"
|
|
@Prev "Assembly and the E language"
|
|
@Toc "Assembly Statements"
|
|
|
|
Static memory
|
|
-------------
|
|
|
|
Assembly programs reserve static memory for things like strings using
|
|
@{b }DC@{ub } mnemonics. However, these aren't real mnemonics. They are, in
|
|
fact, compiler directives and they can vary from compiler to compiler.
|
|
The E versions are @{b }LONG@{ub }, @{b }INT@{ub } and @{b }CHAR@{ub } (the type names), which take a list
|
|
of values, reserve the appropriate amount of static memory and fill in
|
|
this memory with the supplied values. The @{b }CHAR@{ub } form also allows a list of
|
|
characters to be supplied more easily as a string. In this case, the
|
|
string will automatically be aligned to an even memory location, although
|
|
you are responsible for null-terminating it. You can also include a whole
|
|
file as static data using @{b }INCBIN@{ub } (and the file named using this statement
|
|
must exist when the program is compiled). To get at the data you mark it
|
|
with a label.
|
|
|
|
This next example is a bit contrived, but illustrates some static data:
|
|
|
|
PROC main()
|
|
DEF x:PTR TO CHAR
|
|
LEA datatable(PC), A0
|
|
MOVE.L A0, x
|
|
WriteF(x)
|
|
ENDPROC
|
|
|
|
datatable:
|
|
CHAR 'Hello world\\n', 0
|
|
moredata:
|
|
LONG 1,5,-999,0; INT -1,222
|
|
INCBIN 'file.data'; CHAR 0,7,-8
|
|
|
|
The Assembly stuff to get the label address is not really necessary, so
|
|
the example could have been just:
|
|
|
|
PROC main()
|
|
WriteF({datatable})
|
|
ENDPROC
|
|
|
|
datatable:
|
|
CHAR 'Hello world\\n', 0
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Things to watch out for" "Things to watch out for"
|
|
@Prev "Static memory"
|
|
@Toc "Assembly Statements"
|
|
|
|
Things to watch out for
|
|
-----------------------
|
|
|
|
There are a few things to be aware of when using Assembly with E:
|
|
|
|
@{b }*@{ub } All mnemonics and registers must be in uppercase, whilst, of course,
|
|
E variables etc., follow the normal E rules.
|
|
|
|
@{b }*@{ub } Most standard Assemblers use @{b };@{ub } to mark the rest of the line as a
|
|
comment. In E you can use @{b }->@{ub } for the same effect, or you can use the
|
|
@{b }/*@{ub } and @{b }*/@{ub } delimiters.
|
|
|
|
@{b }*@{ub } Registers A4 and A5 are used internally by E, so should not be messed
|
|
with if you are mixing E and Assembly code. Other registers might
|
|
also be used, especially if you've used the @{b }REG@{ub } keyword. Refer to
|
|
the `Reference Manual' for more details.
|
|
|
|
@{b }*@{ub } E function calls like @{b }WriteF@{ub } can affect registers. Refer to the
|
|
`Reference Manual' for more details.
|
|
|
|
|
|
@ENDNODE
|
|
|