ATALAN Programming Language Reference

Syntax elements

Literals

Comments

Identifiers

Blocks

Variables, types, constants and assignment

Type declaration

Const declaration

Input & output

Variable location

Variable assignment

Multiple assignments

Writing to specified address [TODO]

Labels

Integers

Associated constants

Limit constants

Enumerators

Booleans

Structures [TODO]

Element access

Arrays

Initialization

Element access

2D arrays

Range access [TODO]

File references

Addresses

Scopes

Indexed scopes

Operators

Conditions

Commands

Printing

Labels & Jumps

Conditional execution

Loops

For

Loop scope

Loop over existing variable

Step

Where

While & Until

Combinations

Procedures

Default arguments

Multiple results

Return

Nested procedures

Procedures with identical signatures

Forward declaration of procedures

Procedures at specified address

Definition of trashed variables

Modules

Assembler includes

Processor modules

Platform modules

System modules

Application modules

Syntax elements

Atalan source code is ASCII text file. It does not support reading of UTF-8 files, it can however safely skip UTF-8 header, so if you mistakenly safe your source code using UTF-8 (hello PSPad), there should be no problem.

Atalan is case insensitive.

Literals

Numeric and text literals may be defined.

65535 dec $494949 hex %0101010 bin "C" character string

It is possible to separate parts on a numeric constant by apostrophe.

65'535 $ff'ff %0101'0101'0101'1111

Text literals are enclosed in double quotes. Special characters may be enclosed in square brackets.

"This is text." "I said: ["]Hello!["]"

Following escape sequences are supported: ["] " [[ [ ]] ]

Comments

Anything after ; to the end of line is comment.

Identifiers

Identifiers must start with letter and may contain numbers, underlines and apostrophes. Identifier may be enclosed in apostrophes. In such case, it may contain any character except apostrophe or newline.

Example: name x1 x2 x'pos 'RH-' 'else' ; this is identifier, even if else is keyword x x' x'' ; three different identifiers '*'

Blocks

Commands are organized in blocks.

Block may be defined using several methods:

In following examples, block starts after 'then' and everything before "Hello" is part of the block.

Line block start somewhere on the line and continue until the end of line.

if x = 10 then a=1 b=2 "Hello"

Parentheses ignore line ends and whitespaces completely.

if x = 10 then ( a=1 b=2 ) "Hello"

or

if x = 10 then ( a=1 b=2 ) "Hello"

Indented blocks must have first character of block on next line indented more than the line that starts the block. Block ends, when there is some indented less than lines in the block.

if x = 10 then a=1 b=2 "Hello"

Both TABS and spaces can be used to define indent, but they must not be mixed. If both is used on same line, there must be first TABS, then spaces. This prevents some common errors when using indent.

Variables, types, constants and assignment

Variables do not have to be defined, they are declared using assignment action.

[const|type|in|out] name ["@" adr] ["," name]* [":" [min ".." max]|[var] [ "(" dim ["," dim2] ")" ] ["=" value ["," value]

name Name of variable, multiple variables may be declared/assigned (separated by comma) adr Place variable at specified address or register.

Type is declared using one of following methods:

Type declaration

Type is defined using 'type' keyword.

type short:-128..127 ; signed byte type type byte:0..255 type word:0..65535 type int:-32768..32767 type long:0..$ffffff type char:byte

Const declaration

Constant is variable, that is initialized during declaration and never assigned again. Array may be used as const to define static data.

const TAB = 3 * 3 ; it is possible to use expressions to evaluate constants const SPC = 32 const DIGITS:char(16) = "0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"

Const keyword introduces a block, so it is possible to declare multiple constants at once.

const TOP = 1 BOTTOM = 2

Input & output

In and out keywords may be used to define, that variable represents input or output register on system, where registers are mapped into memory.

'in' defines input register. This means, that value of variable may change between reads and it is therefore necessary to always read the value from register (optimizer will not cache values from this register).

'in sequence' defines input register, that works as sequence. Reading this register has some kind of side effect (for example increments some internal pointer etc). Optimizer will not remove reading from the register even if the red value is never used.

'out' specifies the register is used for output. Writing to that register has some side effect. Optimizer may not remove any write to such regiser.

Variable location

It is possible to place variable to specified address. This is usually used to define system and hardware registers. PCOLR @704:array(4) ; player & missile graphics color (4 missiles) COLOR @708:array(5) ; playfield color SDLSTL @560:adr ; address of beginning of display list STICK @632:array(4) Value of some hardware registers changes automatically. It is necessary to mark such variables as 'in' or 'out'.

This syntax may be laso used to define variable as an alias for some other variable (place the two variables to same location). This feature is most commonly used when definining headers of external procedures.

Slices of existing arrays may be defined using address.

memory:array (0..7) of array (0..255) of byte bitmap@memory(4..7)

Variable bitmap will have type 'array(0..3) of array(0..255) of byte' which will be automatically deduced by compiler. Acessing it will have the same effect as accessing the memory array.

Variable assignment

x = y

Multiple assignments

x,y:int x,y = 3,4 x,y = y,x [TODO] x,y = cursor

Writing to specified address [TODO]

Anonymous variable may be assigned (like POKE in BASIC). It is possible to specify type too.

@712 = 0 ; set background color to black @$230:adr = my_dlist @buf:array(100) = 0 ; set 100 bytes at address stored in variable buf to 0 (memset)

Labels

Label is specified as variable located at address where address is not specified. Note, that there must be at least one space after label definition.

name@

Integers

Integer type is declared using numeric range. Compiler automatically decides, how many bytes to use.

byte:0..255 word:0..$ffff int:-32768..32767 flag:0..1

Associated constants

It is possible to associate constant with integer type. Associated constant works like enum, but the type remains integer (i.e. you can still assign numbers to them).

color:0..255 const gray:color = 0 const pink:color = 4

When using associated constant, it must be preceded by type name and dot. It is not necessary, when the type is clear (assigning or comparing with variable of the )

c1:0..255 c2:color c1 = color.gray c2 = pink ; c2 is of type color, color. is not necessary

Limit constants

All integer variables have built-in associated constants min and max defining minimal and maximal possible value (limit) of the variable.

x:13..100 min = x.min max = x.max "x:[min]..[max]"

Will print x:13..100.

It is possible to define custom associated variable with name of some built-in constant. It does not change the type of the variable. Built-in constant will be unavailable.

Enumerators

Enumerations are integer types, that define list of named values, that may be assign to them.

Enums are declared using enum keyword optionally followed by numeric range. If numeric range is specified, all constants associated with this enum must be in the range. If not specified, range is computed automatically based on specified values.

button_state:enum (pressed = 0, not'pressed = 1) color: enum gray pink purple

Booleans

Any integer type may be defined as bool by defining associated constant 'true' and/or 'false'. Variable of bool type may be tested just using variable name, no relational operator is required.

Both true and false constant may have arbitrary value (it must not be 0 or 1).

button_state:enum (pressed = 0, not'pressed = 1, true = 0) left_button:button_state if left_button then "Left pressed."

Structures [TODO]

Structure is defined as list of variable declarations. Either "," or new line may be used as separator.

xcoord:0..319 ycoord:0..239 point: x:xcoord ; x screen coordinate y:ycoord ; y screen coordinate

Using @ inside structure places the variable at specified offset from the beginning of a structure. Structures with 'holes' can be defined this way, even if it is not usually very useful.

audch: f:byte ; frequency c:byte ; control aud@$D200:audch(4)

Element access

Structure elements are accesses using dot operator.

p:point p.x = 10 p.y = 20

Arrays

Array is defined using keyword array.

name:array [[min..]max[,[min..]max]] [of type]

If the array size is defined using single value, the value defines maximal index. Minimal index is then 0. So x:array(31) defines array of 32 elements (index 0..31). Index minimal value can be defined too. It must be positive integer (>= 0). x:array(1..10) defines array of 10 elements from 1 to 10.

Array can be defined as one or two dimensional.

If the type of the array is ommitted, it is byte.

Initialization

It is possible to initialize arrays using literals. Array constants are defined as comma separated list of values. It is not necessary to define dimension for initialized array.

If there is reference to array variable as part of array initializers, pointer to that array is stored in the array. This is possible for byte arrays too, in such case the element will occupy multiple bytes (usually 2 bytes for 8-bit processors).

When some item is to be repeated several times in initialization, it is posible to use <n> TIMES <item> construct. <n> must be integer number. If it is lower or equal 0, no item will be generated.

disp:array(0..39,0..23) of byte const a:array of byte = 3 times 112, disp, 0 ;Array has dimension 0..6

Element access

Array element is accessed using parentheses.

arr:array(10) of byte arr(1) = arr(2) scr:array(39,23) of byte arr(0,0) = 65

2D arrays

2D arrays are organized so, that first index represents X coordinate and second index Y coordinate on 2D grid. This is to provide comfortable means of working with display data.

Following example defines 'screen' of 24 lines with 40 columns and sets the character in the middle of the screen to 'A'.

screen:array(0..39,0..23) of byte x = 19 y = 11 screen(x,y) = 65

Range access [TODO]

When assigning single variable to array, all items in array are set.

screen:array(39,239) of byte screen = 0 ; clear the screen (fill with 0)

File references

Wherever reference to array is expected, it is possible to specify reference to file, that contains contents of that array using file keyword.

For example: set'font file "baloon.fnt"

Reference to file is relative to location of the source file.

Addresses

Adress represents address of some memory location. On 8-bit architectures, address is usually 16 bits (2 bytes).

Adress may define, what type of variable it references (including procedure or array).

const a1:array = 10,11,12,13,14 a:adr a = a1 b = a(0) ; b = 10 b = a(2) ; b = 12 a = a1(2) ; a represents array 12,13,14 now b = a1(1) ; b = 13

Adresses may be passed to procedures. This can be used to pass arrays to procedured.

print2:proc x:adr = b1 = x(0) b2 = x(1) "[b1],[b2]" print2 a1 print2 a1(2)

Scopes

It is possible to define explicitly named scopes. When scope is defined, variables may be defined in this scope using dot syntax like scope.name.

sprite:scope sprite.x: array (0..3) of byte sprite.color: array(0..3) of byte sprite.x(0) = 100 sprite.color(0) = red

It is possible to have code parsed inside the defined scope using initialization. If there is dot before the a name, it will be searched or defined only in current scope. It may be used to force creating of name conflicting with outed definition.

sprite:scope = x: array (0..3) of byte .color: array(0..3) of byte ;see use of .

Indexed scopes

When referencing scope variable of array type, it is posible to specify index after the name of the scope instead of the name of variable. So assignments from previous example may be written as sprite(0).x = 100 sprite(0).color = red

Operators

* / mod Multiplication, division, modulus + - Addition, substraction sqrt Square root lo hi Low/high byte of a word (lo $abcd = $cd, hi $abcd = $ab) bitnot Binary negation bitand Binary and bitor bitxor Binary or and exlusive or ( ) Parentheses

Conditions

Expression used in conditions have slightly different rules than normal expressions. They (at least in theory) evaluate to true/false. If simple value is used, it's 0 value means false, any other value true.

not Logical negation and or Logical operators = <> < > <= >= Relational operators is isn't Same as '=' '<>' (lower priority). [TODO]

Relational operator may be chained, so it is possible to write for example 10<x<100 etc.

Logical operators are evaluated using short circuit evaluation. Binary operators (except xor) can not be used in conditions, because their keywords are used for logical operators.

Commands

Printing

String constant used as command will be printed to screen.

"Hello, World!" "" "I'm here!"

Square braces may be used to insert expressions into the printed string.

x = 4 y = 6 "Sum of [x] and [y] is [x + y]."

Expression type is automatically recognized, there is no need to specify it. New line will be printed after string, unless it is followed by comma.

"Sum of [x] and [y] is", "[x + y]"

Labels & Jumps

Label is be defined as

label@

It is possible to jump on specified label unconditionally using goto.

goto label

It is also possible to jump to address specified in variable.

x:word x = 1000 goto x ; jump to address 1000

Conditional execution

Full conditional statement is supported. Note, that the blocks may be defined using indent. It is possible to optionally use THEN keyword after condition. Arbitrary number of else if sections is supported.

if <cond> [then] <code> else if <cond2> <code> else <code>

Short one-line version is supported. if <cond> then <code>

Again, it is not necessary to use then: if <cond> goto <label>

Loops

Loops are written like:

["for" var [":" range] ["step" step]["where" filter]]["while" cond | "until" cond] code_block

For

"For" part of loop enables iteration over specified loop variable. Loop variable must be integer. All possible values will be iterated, depending on variable type.

Range may be defined as:

Loop scope

Loop provides it's own local scope, so all variables (including loop variable) declared in the loop will be only accessible in the loop.

Loop over existing variable

It is sometimes usefull to know the state of the loop variable after the loop has exited. In such case, it is possible to loop over existing variable. No range is defined in this case.

x:1..60000 for x until KEY = Q "You hit [x]."

Step

When iterationg over variable, it is possible to specify value that will be added to the variable in every step. If the step specification is ommited, 1 is used.

Where

"Where" may be used after for to restrict the iterated values by condition. It is same, as the first command in the loop was "if ". Usually where containg some reference to loop variable, but it is not strictly required.

Print random sequence in ascending order:

for x:1..1000 where RANDOM mod 1 = 1 "[x]"

While & Until

It is possible to specify condition for loop using while or until keyword. It is also usable without for part.

While will repeat commands in the block as long as the specified condition is true. while <cond> <block>

Until will repeat commands in the block as long as the specified condition is not true. until <cond> <block>

While or until may be combined with for.

for <var> where <filter> <body> Loop body is executed only if the condition after where is true.

Combinations

Following loop will print odd numbers until Q is pressed.

for k:1..10000 where k mod 2 = 0 until KEY = Q "[k]"

Procedures

Procedures can be defined using proc type. After the proc keyword follows block defining procedure arguments. Arguments marked using "<" are output arguments ( results).

Arguments marked ">" are input arguments. Input arguments are default.

name ":" "proc" args = code addw:proc i:word j:word >k:word = k = i + j add3: word proc i:word j:word k:word >result:word = result = i + j + k

Default arguments

It is possible to specify default argument value. Argument with defined default value does not have to be specified when procedure is called.

Default value must be constant expression specified as argument assignment in procedure header (after equal sign). When specifying default value for some argument, indented or parenthesized block must be used.

addw:proc(i:word j:word = 1 >k:word) = k = i + j x = addw 14 20 ;x is now 34 x = addw 14 ;x is now 15

Multiple results

Procedure may define more than one output arguments (results).

sumdiv:proc a,b:byte >sum:byte >div:byte = sum = a + b div = a - b a:byte b:byte a,b = sumdiv 10 3 "Sum is [a], div is [b]"

Return

Procedure may return to it's caller from using return statement. It is possible to specify result values as return arguments.

sumdiv:proc a,b:byte >sum:byte >div:byte = return a + b a - b

Nested procedures

It is possible to define local procedure inside other procedure.

set'line'color:proc = wait'line:proc = WSYNC = 0 COL'BK = VCOUNT * 2 + RTCLOCK wait'line

Procedures with identical signatures

Procedure may be declared using type of another procedure.

subw:addw = k = i - j

Forward declaration of procedures

It is possible to declare the procedure header in advance and later define it's body. Although recursive functions are not supported, it is usefull in some special situations like when assigning address of procedure to variable.

sum:proc e,f:byte >s:byte sum = s = e + f

Procedures at specified address

It is possible to define routines in ROM using @ syntax.

This is especially usefull with procedure arguments with defined location (either in register or at some adress).

reset@$E034:proc out_char@$E75f:proc c@_a

Definition of trashed variables

When defining header of procedure that is external (either at specified address or defined in associated assembler file), it is possible to list variables used (trashed) by the procedure.

Such variables are listed between procedure arguments with @ prefix. It is important to mark registers the procedure uses too.

_std_print_adr:proc a@_arr:adr @_arr @_a @_x @_y

Modules

Atalan provides system of modules. Use of module may be declared with 'use' keyword followed by list of module names (not filenames!). Module name is either identifier or string.

use rmt, simple_sprites

Modules may use other modules too. Module can be used only once (any other use is ignored). Cyclic dependency of modules is detected and reported as an error.

Paths used in module 'file' command are relative to the location of the module.

Assembler includes

For each module (name.atl) there may be associated assembler source code (name.asm). If such file exists, it is automatically included at the end of source code.

Processor modules

Processor modules are stored in %SYSTEM%/processor/%module%/%module%.atl directory.

They define processor for which the code may be compiled. Application may use only one processor module. Processor module is usually not used directly by application, platform module uses the specific processor.

Platform modules

Platform modules are stored in %SYSTEM%/platform/%module%/%module%.atl directory.

They define computer platform, for which the code may be compiled. Application may use only one platform module.

System modules

System modules are platform independent modules defined by language. Platform modules are stored in %SYSTEM%/module/%module%.atl directory.

Application modules

Application modules are defined by application and are stored in the application directory.