2019-07-15 12:21:50 +00:00
|
|
|
|
[< back to index](../doc_index.md)
|
2018-04-02 22:21:26 +00:00
|
|
|
|
|
2018-01-18 21:35:25 +00:00
|
|
|
|
# Types
|
|
|
|
|
|
|
|
|
|
Millfork puts extra limitations on which types can be used in which contexts.
|
|
|
|
|
|
|
|
|
|
## Numeric types
|
|
|
|
|
|
|
|
|
|
* `byte` – 1-byte value of undefined signedness, defaulting to unsigned
|
|
|
|
|
|
|
|
|
|
* `word` – 2-byte value of undefined signedness, defaulting to unsigned
|
2018-07-30 12:33:16 +00:00
|
|
|
|
(alias: `int16`)
|
2018-01-18 21:35:25 +00:00
|
|
|
|
|
2018-07-30 12:33:16 +00:00
|
|
|
|
* `int24` – 3-byte value of undefined signedness, defaulting to unsigned
|
2019-07-09 20:03:53 +00:00
|
|
|
|
(alias: `farword`; this alias is deprecated and will be removed in the future)
|
2018-05-14 00:16:46 +00:00
|
|
|
|
|
2018-01-18 21:35:25 +00:00
|
|
|
|
* `long` – 4-byte value of undefined signedness, defaulting to unsigned
|
2018-07-30 12:33:16 +00:00
|
|
|
|
(alias: `int32`)
|
|
|
|
|
|
|
|
|
|
* `int40`, `int48`,... `int128` – even larger types
|
2018-01-18 21:35:25 +00:00
|
|
|
|
|
|
|
|
|
* `sbyte` – signed 1-byte value
|
|
|
|
|
|
|
|
|
|
* `ubyte` – unsigned 1-byte value
|
|
|
|
|
|
2019-04-18 15:20:35 +00:00
|
|
|
|
* `pointer` – raw pointers; the same as `word`, but variables of this type default to be zero-page-allocated
|
|
|
|
|
and you can index `pointer`-typed expressions.
|
2018-08-03 11:00:52 +00:00
|
|
|
|
You can create pointer values by suffixing `.addr` to the name of a variable, function or array.
|
2018-01-18 21:35:25 +00:00
|
|
|
|
|
2018-08-03 11:00:52 +00:00
|
|
|
|
You can access single bytes of variables by using the following notations:
|
|
|
|
|
|
|
|
|
|
* for 2-byte-sized variables: `.lo` for the least significant byte and `.hi` for the most significant byte
|
|
|
|
|
|
|
|
|
|
* for larger variables: `.b0` for the least significant byte and then `.b1`, `.b2` and so on
|
|
|
|
|
|
|
|
|
|
You can also access words that are parts of variables:
|
|
|
|
|
|
|
|
|
|
* for 3-byte-sized variables: `.loword` is the word formed from `.b1` and `.b0` and `.hiword` is the word formed from `.b2` and `.b1`
|
|
|
|
|
|
|
|
|
|
* for 4-byte-sized variables: `.loword` is the word formed from `.b1` and `.b0` and `.hiword` is the word formed from `.b3` and `.b2`
|
|
|
|
|
|
2018-01-18 21:35:25 +00:00
|
|
|
|
Numeric types can be converted automatically:
|
|
|
|
|
|
|
|
|
|
* from a smaller type to a bigger type (`byte`→`word`)
|
|
|
|
|
|
|
|
|
|
* from a type of undefined signedness to a type of defined signedness (`byte`→`sbyte`)
|
|
|
|
|
|
|
|
|
|
* from a type of defined signedness to a type of undefined signedness (`sbyte`→`byte`)
|
|
|
|
|
|
2019-04-15 17:45:26 +00:00
|
|
|
|
## Typed pointers
|
|
|
|
|
|
|
|
|
|
For every type `T`, there is a pointer type defined called `pointer.T`.
|
|
|
|
|
|
|
|
|
|
Unlike raw pointers, they are not subject to arithmetic.
|
|
|
|
|
|
2019-04-18 15:20:35 +00:00
|
|
|
|
If the type `T` is of size 1, you can index the pointer like a raw pointer.
|
|
|
|
|
|
|
|
|
|
If the type `T` is of size 2, you can index the pointer only with the constant 0.
|
|
|
|
|
|
2019-04-15 17:45:26 +00:00
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
|
|
pointer.t p
|
|
|
|
|
p.raw // expression of type pointer, pointing to the same location in memory as 'p'
|
|
|
|
|
p.lo // equivalent to 'p.raw.lo'
|
|
|
|
|
p.hi // equivalent to 'p.raw.lo'
|
|
|
|
|
p[0] // valid only if the type 't' is of size 1 or 2, accesses the pointed element
|
|
|
|
|
p[i] // valid only if the type 't' is of size 1, equivalent to 't(p.raw[i])'
|
|
|
|
|
p->x // valid only if the type 't' has a field called 'x', accesses the field 'x' of the pointed element
|
2019-04-18 14:24:46 +00:00
|
|
|
|
p->x.y[0]->z[0][6] // you can stack it
|
2019-04-16 14:56:23 +00:00
|
|
|
|
|
|
|
|
|
## `nullptr`
|
|
|
|
|
|
|
|
|
|
There is a 2-byte constant `nullptr` that can be assigned to any 2-byte pointer type.
|
|
|
|
|
Its actual value is defined using the feature `NULLPTR`, by default it's 0.
|
|
|
|
|
|
|
|
|
|
`nullptr` isn't directly assignable to non-pointer types.
|
2019-04-15 17:45:26 +00:00
|
|
|
|
|
2019-07-26 22:58:10 +00:00
|
|
|
|
## Function pointers
|
|
|
|
|
|
2019-07-30 20:49:32 +00:00
|
|
|
|
For every type `A` of size 1 or 2 (or `void`) and every type `B` of size 1 or 2 (or `void`),
|
2019-07-26 22:58:10 +00:00
|
|
|
|
there is a pointer type defined called `function.A.to.B`, which represents functions with a signature like this:
|
|
|
|
|
|
|
|
|
|
B function_name(A parameter)
|
|
|
|
|
B function_name() // if A is void
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
|
|
word i
|
|
|
|
|
function.void.to.word p1 = f1.pointer
|
|
|
|
|
i = call(p1)
|
|
|
|
|
function.byte.to.byte p2 = f2.pointer
|
|
|
|
|
i += call(p2, 7)
|
2019-07-30 20:49:32 +00:00
|
|
|
|
function.word.to.byte p3 = f3.pointer
|
|
|
|
|
i += call(p2, 7)
|
2019-07-26 22:58:10 +00:00
|
|
|
|
|
|
|
|
|
Using `call` on 6502 requires at least 4 bytes of zeropage pseudoregister.
|
|
|
|
|
|
2019-07-30 20:49:32 +00:00
|
|
|
|
The value of the pointer `f.pointer` may not be the same as the value of the function address `f.addr`.
|
2019-07-26 22:58:10 +00:00
|
|
|
|
|
2018-01-18 21:35:25 +00:00
|
|
|
|
## Boolean types
|
|
|
|
|
|
|
|
|
|
TODO
|
|
|
|
|
|
2019-07-26 17:02:32 +00:00
|
|
|
|
* `bool` – a 1-byte boolean value
|
|
|
|
|
|
2018-01-18 21:35:25 +00:00
|
|
|
|
## Special types
|
|
|
|
|
|
|
|
|
|
* `void` – a unit type containing no information, can be only used as a return type for a function.
|
2018-07-20 20:46:53 +00:00
|
|
|
|
|
|
|
|
|
## Enumerations
|
|
|
|
|
|
|
|
|
|
Enumeration is a 1-byte type that represents a set of values:
|
|
|
|
|
|
|
|
|
|
enum <name> { <variants, separated by commas or newlines> }
|
|
|
|
|
|
|
|
|
|
The first variant has value 0. Every next variant has a value increased by 1 compared to a previous one.
|
|
|
|
|
|
|
|
|
|
Alternatively, a variant can be given a custom constant value, which will change the sequence.
|
|
|
|
|
|
|
|
|
|
If there is at least one variant and no variant is given a custom constant value,
|
|
|
|
|
then the enumeration is considered _plain_. Plain enumeration types can be used as array keys.
|
|
|
|
|
For plain enumerations, a constant `<name>.count` is defined,
|
|
|
|
|
equal to the number of variants in the enumeration.
|
|
|
|
|
|
|
|
|
|
Assigment between numeric types and enumerations is not possible without an explicit type cast:
|
|
|
|
|
|
2019-06-05 16:34:32 +00:00
|
|
|
|
enum E { EA, EB }
|
2018-07-20 20:46:53 +00:00
|
|
|
|
byte b
|
|
|
|
|
E e
|
2019-06-05 16:34:32 +00:00
|
|
|
|
e = EA // ok
|
2018-07-20 20:46:53 +00:00
|
|
|
|
e = b // won't compile
|
|
|
|
|
b = e // won't compile
|
|
|
|
|
b = byte(e) // ok
|
|
|
|
|
e = E(b) // ok
|
|
|
|
|
|
2019-06-05 16:34:32 +00:00
|
|
|
|
array a[E] // E is plain, array has size 2
|
|
|
|
|
a[0] // won't compile
|
|
|
|
|
a[EB] // ok
|
|
|
|
|
|
|
|
|
|
|
2018-07-20 20:46:53 +00:00
|
|
|
|
Plain enumerations have their variants equal to `byte(0)` to `byte(<name>.count - 1)`.
|
|
|
|
|
|
|
|
|
|
Tip: You can use an enumeration with no variants as a strongly checked alternative byte type,
|
2019-04-16 14:56:23 +00:00
|
|
|
|
as there are no checks on values when converting bytes to enumeration values and vice versa.
|
2019-04-14 23:58:51 +00:00
|
|
|
|
|
|
|
|
|
## Structs
|
|
|
|
|
|
|
|
|
|
Struct is a compound type containing multiple fields of various types:
|
|
|
|
|
|
|
|
|
|
struct <name> { <field definitions (type and name), separated by commas or newlines>}
|
|
|
|
|
|
|
|
|
|
A struct is represented in memory as a contiguous area of variables laid out one after another.
|
|
|
|
|
|
|
|
|
|
Struct can have a maximum size of 255 bytes. Larger structs are not supported.
|
|
|
|
|
|
|
|
|
|
You can access a field of a struct with the dot:
|
|
|
|
|
|
|
|
|
|
struct point { word x, word y }
|
|
|
|
|
|
|
|
|
|
point p
|
|
|
|
|
p.x = 3
|
|
|
|
|
p.y.lo = 4
|
|
|
|
|
|
|
|
|
|
Offsets are available as `structname.fieldname.offset`:
|
|
|
|
|
|
|
|
|
|
pointer ptr
|
|
|
|
|
ptr = p.addr
|
|
|
|
|
ptr += point.y.offset
|
|
|
|
|
// ptr points now at p.y
|
|
|
|
|
|
|
|
|
|
// alternatively:
|
|
|
|
|
ptr = p.y.addr
|
2019-04-15 17:45:26 +00:00
|
|
|
|
|
2019-06-24 22:45:49 +00:00
|
|
|
|
You can create constant expressions of struct types using so-called struct constructors, e.g.:
|
|
|
|
|
|
|
|
|
|
point(5,6)
|
|
|
|
|
|
|
|
|
|
All arguments to the constructor must be constant.
|
|
|
|
|
|
2019-04-15 17:45:26 +00:00
|
|
|
|
## Unions
|
|
|
|
|
|
|
|
|
|
union <name> { <field definitions (type and name), separated by commas or newlines>}
|
|
|
|
|
|
|
|
|
|
Unions are pretty similar to structs, with the difference that all fields of the union
|
|
|
|
|
start at the same point in memory and therefore overlap each other.
|
|
|
|
|
|
|
|
|
|
struct point { byte x, byte y }
|
|
|
|
|
union point_or_word { point p, word w }
|
|
|
|
|
|
|
|
|
|
point_or_word u
|
|
|
|
|
u.p.x = 0
|
|
|
|
|
u.p.y = 0
|
|
|
|
|
if u.w == 0 { ok() }
|
|
|
|
|
|
|
|
|
|
Offset constants are also available, but they're obviously all zero.
|
2019-06-24 22:45:49 +00:00
|
|
|
|
|
|
|
|
|
Unions currently do not have an equivalent of struct constructors. This may be improved on in the future.
|