1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-12-23 08:29:35 +00:00

Improve documentation

This commit is contained in:
Karol Stasiak 2019-10-24 15:09:11 +02:00
parent bb419d961f
commit 6fddf1cf0d
4 changed files with 85 additions and 23 deletions

View File

@ -131,8 +131,8 @@ Only used for 6502-based targets. Cannot be used together with `zp_pointers`.
A segment named `default` is always required.
Default: `default`. In all options below, `NAME` refers to a segment name.
* `default_code_segment` the default segment for code and initialized arrays.
Note that the default segment for uninitialized arrays and variables is always `default`.
* `default_code_segment` the default segment for code and const arrays.
Note that the default segment for writable arrays and variables is always `default`.
Default: `default`
* `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.
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_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`.
Default: the same as `segment_NAME_end`.

View File

@ -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
(the current implementation calls it twice, but do not rely on this behaviour).
The `==` and `!=` operators also work for non-arithmetic types.
* `==`: equality
`enum == enum`
`byte == byte`
`simple word == simple word`
`word == constant`
`simple word == word` (zpreg)
`word == simple word` (zpreg)
`simple long == simple long`
* `!=`: inequality
@ -157,12 +161,15 @@ Note you cannot mix those operators, so `a <= b < c` is not valid.
`byte != byte`
`simple word != simple word`
`word != constant`
`simple word != word` (zpreg)
`word != simple word` (zpreg)
`simple long != simple long`
* `>`, `<`, `<=`, `>=`: inequality
`byte > byte`
`simple word > word`
`word > simple word`
`simple word > simple word`
`simple word > word` (zpreg)
`word > simple word` (zpreg)
`simple long > simple long`
Currently, `>`, `<`, `<=`, `>=` operators perform signed comparison

View File

@ -2,10 +2,13 @@
# Types
Millfork puts extra limitations on which types can be used in which contexts.
## 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
* `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
(alias: `int32`)
* `int40`, `int48`,... `int128` even larger types
* `int40`, `int48`,... `int128` even larger unsigned types
* `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`)
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
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.
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`.
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:
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.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[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.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() // if A is void
Examples:
To call a pointed-to function, use `call`. Examples:
word i
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 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.
* 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
-----------------|--------------------|-----------|-----------|----------|-------------
`set_carry` | `clear_carry` | C | C | C | C
`set_zero` | `clear_zero` | Z | Z | Z | Z
`set_overflow` | `clear_overflow` | V | P¹ | P/V | _n/a_²
`set_negative` | `clear_negative` | N | S | S | _n/a_²
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 | C
`set_zero` | `clear_zero` | Z | Z | Z | Z | Z
`set_overflow` | `clear_overflow` | V | V | P¹ | P/V | *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,
8080 uses the same type names for compatibility.
@ -123,6 +143,8 @@ Examples:
bool f() = true
bool g(byte x) = x == 7 || x > 100
void do_thing(bool b) {
if b { do_one_thing() }
else { do_another_thing() }
@ -135,6 +157,9 @@ Examples:
#elseif ARCH_I80
SCF
? RET
#elseif ARCH_6809
ORCC #1
? RTS
#else
#error
#endif
@ -174,6 +199,14 @@ Assignment between numeric types and enumerations is not possible without an exp
a[0] // won't compile
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)`.
@ -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.
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 }

View File

@ -39,6 +39,26 @@ class EnumSuite extends FunSuite with Matchers {
""".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") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)(
"""