mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-23 23:30:22 +00:00
Improve documentation
This commit is contained in:
parent
bb419d961f
commit
6fddf1cf0d
@ -131,8 +131,8 @@ Only used for 6502-based targets. Cannot be used together with `zp_pointers`.
|
|||||||
A segment named `default` is always required.
|
A segment named `default` is always required.
|
||||||
Default: `default`. In all options below, `NAME` refers to a segment name.
|
Default: `default`. In all options below, `NAME` refers to a segment name.
|
||||||
|
|
||||||
* `default_code_segment` – the default segment for code and initialized arrays.
|
* `default_code_segment` – the default segment for code and const arrays.
|
||||||
Note that the default segment for uninitialized arrays and variables is always `default`.
|
Note that the default segment for writable arrays and variables is always `default`.
|
||||||
Default: `default`
|
Default: `default`
|
||||||
|
|
||||||
* `ram_init_segment` – the segment storing a copy of initial values for preinitialized writable arrays and variables.
|
* `ram_init_segment` – the segment storing a copy of initial values for preinitialized writable arrays and variables.
|
||||||
@ -141,11 +141,13 @@ Default: none.
|
|||||||
|
|
||||||
* `segment_NAME_start` – the first address used for automatic allocation in the segment.
|
* `segment_NAME_start` – the first address used for automatic allocation in the segment.
|
||||||
Note that on 6502-like targets, the `default` segment shouldn't start before $200, as the $0-$1FF range is reserved for the zeropage and the stack.
|
Note that on 6502-like targets, the `default` segment shouldn't start before $200, as the $0-$1FF range is reserved for the zeropage and the stack.
|
||||||
The `main` function will be placed as close to the beginning of its segment as possible, but not necessarily at `segment_NAME_start`
|
The first object defined in `segment_NAME_layout` (usually the `main` function)
|
||||||
|
will be placed as close to the beginning of its segment as possible,
|
||||||
|
but not necessarily at `segment_NAME_start`
|
||||||
|
|
||||||
* `segment_NAME_end` – the last address in the segment
|
* `segment_NAME_end` – the last address in the segment
|
||||||
|
|
||||||
* `segment_NAME_codeend` – the last address in the segment for code and initialized arrays.
|
* `segment_NAME_codeend` – the last address in the segment for code and const arrays.
|
||||||
Only uninitialized variables are allowed between `segment_NAME_codeend` and `segment_NAME_end`.
|
Only uninitialized variables are allowed between `segment_NAME_codeend` and `segment_NAME_end`.
|
||||||
Default: the same as `segment_NAME_end`.
|
Default: the same as `segment_NAME_end`.
|
||||||
|
|
||||||
|
@ -145,11 +145,15 @@ Note you cannot mix those operators, so `a <= b < c` is not valid.
|
|||||||
**WARNING:** Currently in cases like `a < f() < b`, `f()` may be evaluated an undefined number of times
|
**WARNING:** Currently in cases like `a < f() < b`, `f()` may be evaluated an undefined number of times
|
||||||
(the current implementation calls it twice, but do not rely on this behaviour).
|
(the current implementation calls it twice, but do not rely on this behaviour).
|
||||||
|
|
||||||
|
The `==` and `!=` operators also work for non-arithmetic types.
|
||||||
|
|
||||||
* `==`: equality
|
* `==`: equality
|
||||||
`enum == enum`
|
`enum == enum`
|
||||||
`byte == byte`
|
`byte == byte`
|
||||||
`simple word == simple word`
|
`simple word == simple word`
|
||||||
`word == constant`
|
`word == constant`
|
||||||
|
`simple word == word` (zpreg)
|
||||||
|
`word == simple word` (zpreg)
|
||||||
`simple long == simple long`
|
`simple long == simple long`
|
||||||
|
|
||||||
* `!=`: inequality
|
* `!=`: inequality
|
||||||
@ -157,12 +161,15 @@ Note you cannot mix those operators, so `a <= b < c` is not valid.
|
|||||||
`byte != byte`
|
`byte != byte`
|
||||||
`simple word != simple word`
|
`simple word != simple word`
|
||||||
`word != constant`
|
`word != constant`
|
||||||
|
`simple word != word` (zpreg)
|
||||||
|
`word != simple word` (zpreg)
|
||||||
`simple long != simple long`
|
`simple long != simple long`
|
||||||
|
|
||||||
* `>`, `<`, `<=`, `>=`: inequality
|
* `>`, `<`, `<=`, `>=`: inequality
|
||||||
`byte > byte`
|
`byte > byte`
|
||||||
`simple word > word`
|
`simple word > simple word`
|
||||||
`word > simple word`
|
`simple word > word` (zpreg)
|
||||||
|
`word > simple word` (zpreg)
|
||||||
`simple long > simple long`
|
`simple long > simple long`
|
||||||
|
|
||||||
Currently, `>`, `<`, `<=`, `>=` operators perform signed comparison
|
Currently, `>`, `<`, `<=`, `>=` operators perform signed comparison
|
||||||
|
@ -2,10 +2,13 @@
|
|||||||
|
|
||||||
# Types
|
# Types
|
||||||
|
|
||||||
Millfork puts extra limitations on which types can be used in which contexts.
|
|
||||||
|
|
||||||
## Numeric types
|
## Numeric types
|
||||||
|
|
||||||
|
Millfork puts extra limitations on which types can be used in which contexts.
|
||||||
|
1-byte arithmetic types work in every context.
|
||||||
|
2-byte arithmetic types work in context that are not overly complicated.
|
||||||
|
3-byte and larger types have limited capabilities.
|
||||||
|
|
||||||
* `byte` – 1-byte value of undefined signedness, defaulting to unsigned
|
* `byte` – 1-byte value of undefined signedness, defaulting to unsigned
|
||||||
|
|
||||||
* `word` – 2-byte value of undefined signedness, defaulting to unsigned
|
* `word` – 2-byte value of undefined signedness, defaulting to unsigned
|
||||||
@ -17,7 +20,7 @@ Millfork puts extra limitations on which types can be used in which contexts.
|
|||||||
* `long` – 4-byte value of undefined signedness, defaulting to unsigned
|
* `long` – 4-byte value of undefined signedness, defaulting to unsigned
|
||||||
(alias: `int32`)
|
(alias: `int32`)
|
||||||
|
|
||||||
* `int40`, `int48`,... `int128` – even larger types
|
* `int40`, `int48`,... `int128` – even larger unsigned types
|
||||||
|
|
||||||
* `sbyte` – signed 1-byte value
|
* `sbyte` – signed 1-byte value
|
||||||
|
|
||||||
@ -47,26 +50,43 @@ Numeric types can be converted automatically:
|
|||||||
|
|
||||||
* from a type of defined signedness to a type of undefined signedness (`sbyte`→`byte`)
|
* from a type of defined signedness to a type of undefined signedness (`sbyte`→`byte`)
|
||||||
|
|
||||||
|
Numeric types can be also converted explicitly from a smaller to an equal or bigger size.
|
||||||
|
This is useful in situations like preventing overflow or underflow,
|
||||||
|
or forcing zero extension or sign extension:
|
||||||
|
|
||||||
|
byte a
|
||||||
|
a = 30
|
||||||
|
a * a // expression of type byte, equals 132
|
||||||
|
word(a) * a // expression of type word, equals 900
|
||||||
|
|
||||||
|
word x
|
||||||
|
byte y
|
||||||
|
y = $80
|
||||||
|
x = y // does zero extension and assigns value $0080
|
||||||
|
x = sbyte(y) // does sign extension and assigns value $FF80
|
||||||
|
|
||||||
## Typed pointers
|
## Typed pointers
|
||||||
|
|
||||||
For every type `T`, there is a pointer type defined called `pointer.T`.
|
For every type `T`, there is a pointer type defined called `pointer.T`.
|
||||||
|
|
||||||
Unlike raw pointers, they are not subject to arithmetic.
|
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.
|
You can index the pointer like a raw pointer or an array.
|
||||||
|
An expression like `p[n]` accesses the `n`th object in an array of consecutive objects pointed to by `p`.
|
||||||
If the type `T` is of size 2, you can index the pointer only with the constant 0.
|
|
||||||
|
|
||||||
You can create pointer values by suffixing `.pointer` to the name of a variable, function or array.
|
You can create pointer values by suffixing `.pointer` to the name of a variable, function or array.
|
||||||
|
|
||||||
|
You can replace C-style pointer arithmetic by combining indexing and `.pointer`: C `p+5`, Millfork `p[5].pointer`.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
pointer.t p
|
pointer.t p
|
||||||
p.pointer // expression of type pointer, pointing to the same location in memory as 'p'
|
p = t1.pointer // assigning a pointer
|
||||||
|
p.raw // expression of type pointer, pointing to the same location in memory as 'p'
|
||||||
p.lo // equivalent to 'p.raw.lo'
|
p.lo // equivalent to 'p.raw.lo'
|
||||||
p.hi // 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[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[i] // accessing the ith element; if 'sizeof(t) == 1', then 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 // 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
|
p->x.y[0]->z[0][6] // you can stack it
|
||||||
|
|
||||||
@ -85,7 +105,7 @@ there is a pointer type defined called `function.A.to.B`, which represents funct
|
|||||||
B function_name(A parameter)
|
B function_name(A parameter)
|
||||||
B function_name() // if A is void
|
B function_name() // if A is void
|
||||||
|
|
||||||
Examples:
|
To call a pointed-to function, use `call`. Examples:
|
||||||
|
|
||||||
word i
|
word i
|
||||||
function.void.to.word p1 = f1.pointer
|
function.void.to.word p1 = f1.pointer
|
||||||
@ -101,18 +121,18 @@ The value of the pointer `f.pointer` may not be the same as the value of the fun
|
|||||||
|
|
||||||
## Boolean types
|
## Boolean types
|
||||||
|
|
||||||
Boolean types can be used as conditions. They have two possible values, `true` and `false`, although
|
Boolean types can be used as conditions. They have two possible values, `true` and `false`.
|
||||||
|
|
||||||
* `bool` – a 1-byte boolean value. An uninitialized variable of type `bool` may contain an invalid value.
|
* `bool` – a 1-byte boolean value. An uninitialized variable of type `bool` may contain an invalid value.
|
||||||
|
|
||||||
* several boolean types based on the CPU flags that may be used only as a return type for a function written in assembly:
|
* several boolean types based on the CPU flags that may be used only as a return type for a function written in assembly:
|
||||||
|
|
||||||
true if flag set | true if flag clear | 6502 flag | 8080 flag | Z80 flag | LR35902 flag
|
true if flag set | true if flag clear | 6502 flag | 6809 flag | 8080 flag | Z80 flag | LR35902 flag
|
||||||
-----------------|--------------------|-----------|-----------|----------|-------------
|
-----------------|--------------------|-----------|-----------|-----------|----------|-------------
|
||||||
`set_carry` | `clear_carry` | C | C | C | C
|
`set_carry` | `clear_carry` | C | C | C | C | C
|
||||||
`set_zero` | `clear_zero` | Z | Z | Z | Z
|
`set_zero` | `clear_zero` | Z | Z | Z | Z | Z
|
||||||
`set_overflow` | `clear_overflow` | V | P¹ | P/V | _n/a_²
|
`set_overflow` | `clear_overflow` | V | V | P¹ | P/V | *n/a*²
|
||||||
`set_negative` | `clear_negative` | N | S | S | _n/a_²
|
`set_negative` | `clear_negative` | N | N | S | S | *n/a*²
|
||||||
|
|
||||||
1\. 8080 does not have a dedicated overflow flag, so since Z80 reuses the P flag for overflow,
|
1\. 8080 does not have a dedicated overflow flag, so since Z80 reuses the P flag for overflow,
|
||||||
8080 uses the same type names for compatibility.
|
8080 uses the same type names for compatibility.
|
||||||
@ -123,6 +143,8 @@ Examples:
|
|||||||
|
|
||||||
bool f() = true
|
bool f() = true
|
||||||
|
|
||||||
|
bool g(byte x) = x == 7 || x > 100
|
||||||
|
|
||||||
void do_thing(bool b) {
|
void do_thing(bool b) {
|
||||||
if b { do_one_thing() }
|
if b { do_one_thing() }
|
||||||
else { do_another_thing() }
|
else { do_another_thing() }
|
||||||
@ -135,6 +157,9 @@ Examples:
|
|||||||
#elseif ARCH_I80
|
#elseif ARCH_I80
|
||||||
SCF
|
SCF
|
||||||
? RET
|
? RET
|
||||||
|
#elseif ARCH_6809
|
||||||
|
ORCC #1
|
||||||
|
? RTS
|
||||||
#else
|
#else
|
||||||
#error
|
#error
|
||||||
#endif
|
#endif
|
||||||
@ -174,6 +199,14 @@ Assignment between numeric types and enumerations is not possible without an exp
|
|||||||
a[0] // won't compile
|
a[0] // won't compile
|
||||||
a[EB] // ok
|
a[EB] // ok
|
||||||
|
|
||||||
|
enum X {} // enum with no variants
|
||||||
|
enum Y { // enum with renumberedvariants
|
||||||
|
YA = 5
|
||||||
|
YB // YB is internally represented as 6
|
||||||
|
}
|
||||||
|
array a2[X] // won't compile
|
||||||
|
array a2[Y] // won't compile
|
||||||
|
|
||||||
|
|
||||||
Plain enumerations have their variants equal to `byte(0)` to `byte(<name>.count - 1)`.
|
Plain enumerations have their variants equal to `byte(0)` to `byte(<name>.count - 1)`.
|
||||||
|
|
||||||
@ -190,7 +223,7 @@ A struct is represented in memory as a contiguous area of variables laid out one
|
|||||||
|
|
||||||
Struct can have a maximum size of 255 bytes. Larger structs are not supported.
|
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:
|
You can access a field of a struct with a dot:
|
||||||
|
|
||||||
struct point { word x, word y }
|
struct point { word x, word y }
|
||||||
|
|
||||||
|
@ -39,6 +39,26 @@ class EnumSuite extends FunSuite with Matchers {
|
|||||||
""".stripMargin){_=>}
|
""".stripMargin){_=>}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("Enum renumber test") {
|
||||||
|
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)(
|
||||||
|
"""
|
||||||
|
| enum ugly {
|
||||||
|
| u0, u1
|
||||||
|
| u7 = 7, u8,
|
||||||
|
| u9
|
||||||
|
| }
|
||||||
|
| ugly output0 @$c000
|
||||||
|
| ugly output1 @$c001
|
||||||
|
| void main () {
|
||||||
|
| output0 = u1
|
||||||
|
| output1 = u9
|
||||||
|
| }
|
||||||
|
""".stripMargin){ m =>
|
||||||
|
m.readByte(0xc000) should equal(1)
|
||||||
|
m.readByte(0xc001) should equal(9)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test("Enum arrays") {
|
test("Enum arrays") {
|
||||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)(
|
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)(
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user