[< back to index](../README.md) # 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 (alias: `int16`) * `int24` – 3-byte value of undefined signedness, defaulting to unsigned (alias: `farword`; this alias is deprecated and will be removed in the future) * `long` – 4-byte value of undefined signedness, defaulting to unsigned (alias: `int32`) * `int40`, `int48`,... `int128` – even larger types * `sbyte` – signed 1-byte value * `ubyte` – unsigned 1-byte value * `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. You can create pointer values by suffixing `.addr` to the name of a variable, function or array. 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` 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`) ## Typed pointers For every type `T`, there is a pointer type defined called `pointer.T`. Unlike raw pointers, they are not subject to arithmetic. 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. 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 p->x.y[0]->z[0][6] // you can stack it ## `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. ## Boolean types TODO ## Special types * `void` – a unit type containing no information, can be only used as a return type for a function. ## Enumerations Enumeration is a 1-byte type that represents a set of values: enum { } 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 `.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: enum E { EA, EB } byte b E e e = EA // ok e = b // won't compile b = e // won't compile b = byte(e) // ok e = E(b) // ok array a[E] // E is plain, array has size 2 a[0] // won't compile a[EB] // ok Plain enumerations have their variants equal to `byte(0)` to `byte(.count - 1)`. Tip: You can use an enumeration with no variants as a strongly checked alternative byte type, as there are no checks on values when converting bytes to enumeration values and vice versa. ## Structs Struct is a compound type containing multiple fields of various types: struct { } 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 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. ## Unions union { } 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. Unions currently do not have an equivalent of struct constructors. This may be improved on in the future.