1
0
mirror of https://github.com/KarolS/millfork.git synced 2025-01-01 06:29:53 +00:00

Define and document more magic suffixes and constants (see #87)

This commit is contained in:
Karol Stasiak 2021-01-13 19:35:11 +01:00
parent fe4f0dcfd9
commit 499e650752
10 changed files with 97 additions and 9 deletions

View File

@ -173,7 +173,7 @@ Default: `after_code`.
* `segment_NAME_bank` the bank number the segment belongs to. Default: `0`. * `segment_NAME_bank` the bank number the segment belongs to. Default: `0`.
For better debugging on NES, RAM segments should use bank number `$ff`. For better debugging on NES, RAM segments should use bank number `$ff`.
* `segment_NAME_fill` the byte value used to fill gaps and other unused space in the bank. Default: `0`. * `segment_NAME_fill` the byte value used to fill gaps and other unused space in the segment. Default: `0`.
* `segment_NAME_layout` a comma-separated list of object names that defines in what order the objects are laid out in the segment. * `segment_NAME_layout` a comma-separated list of object names that defines in what order the objects are laid out in the segment.
One item has to be `*`, it means "all the other objects". One item has to be `*`, it means "all the other objects".

View File

@ -26,6 +26,8 @@
* [Predefined constants](lang/predefined_constants.md) * [Predefined constants](lang/predefined_constants.md)
* [List of magic suffixes](lang/suffixes.md)
* [List of text encodings and escape sequences](lang/text.md) * [List of text encodings and escape sequences](lang/text.md)
* [Defining custom encodings](lang/custom-encoding.md) * [Defining custom encodings](lang/custom-encoding.md)

View File

@ -2,7 +2,9 @@
# Predefined constants # Predefined constants
* `byte nullchar` the null terminator for strings in the default encoding, equivalent to `""z[0]` * `byte nullchar` the null terminator for strings in the default encoding, equivalent to `""z[0]`, can be overriden by the `NULLCHAR` feature
* `byte nullchar_scr` the null terminator for strings in the screen encoding, equivalent to `""scrz[0]`, can be overriden by the `NULLCHAR_SCR` feature
* `null$ nullptr` the invalid pointer value; the value of the `NULLPTR` feature * `null$ nullptr` the invalid pointer value; the value of the `NULLPTR` feature

46
docs/lang/suffixes.md Normal file
View File

@ -0,0 +1,46 @@
[< back to index](../doc_index.md)
# Magic suffixes
## Byte-related suffixes
These suffixes can be only applied to arithmetic or pointer variables:
* `.lo` the least significant byte of a two-byte variable (word, pointer) (use `lo(_)` for arbitrary expressions)
* `.hi` the most significant byte of a two-byte variable (word, pointer) (use `hi(_)` for arbitrary expressions)
* `.loword` the least significant byte of a three- or four-byte variable
* `.hiword` the most significant byte of a three- or four-byte variable
* `.b0`, `.b1` etc. the given byte of the multi-byte arithmetic variable, with `.b0` being the least significant byte
## Pointer-related suffixes:
These suffixes can be applied to variables, arrays, functions or pointable expressions (sometimes called _lvalues_):
* `.addr` returns address of the object (type `pointer`) (constant unless on Lunix)
* `.rawaddr` returns the raw address constant of the object (type `pointer`, the same as `.addr` unless on Lunix, guaranteed to be constant)
* `.pointer` returns the typed pointer to the object
This suffix is available only on expressions that have a type of a typed pointer:
* `.raw` a view of the pointer as a raw pointer
## Segment-related suffixes
These suffixes can be applied to variables, arrays, or functions:
* `.segment.bank` (or `.segment` for short) returns the bank number of the segment the object is in
* `.segment.start` returns the start address of the segment the object is in
* `.segment.end` returns the last address of the segment the object is in
* `.segment.fill` returns the byte value used to fill gaps and other unused space in the segment the object is in
See also [the list of predefined constants](./predefined_constants.md).

View File

@ -108,6 +108,12 @@ For every variable `x` larger than a byte, extra subvariables are defined:
* the lowest word: `x.loword` (=`x.b1:x.b0`) * the lowest word: `x.loword` (=`x.b1:x.b0`)
* if `x` is a typed pointer:
* a view of as a raw pointer: `x.raw`
See also [the list of magic suffixes](./suffixes.md).
### Constant declarations ### Constant declarations
`const <type> <name> = <value>` `const <type> <name> = <value>`

View File

@ -158,7 +158,9 @@ Functions that are interrupt pointers have their own pointer types:
Boolean types can be used as conditions. They have two possible values, `true` and `false`. 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.
The value `false` is stored as 0, `true` as 1.
* 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:
@ -174,6 +176,10 @@ Boolean types can be used as conditions. They have two possible values, `true` a
2\. LR35902 does not support these types due to the lack of appropriate flags 2\. LR35902 does not support these types due to the lack of appropriate flags
You can convert from a boolean type to an arithmetic type by simply casting:
byte c = byte(x >= 0x80)
Examples: Examples:
bool f() = true bool f() = true

View File

@ -26,6 +26,7 @@ nav:
- Types: lang/types.md - Types: lang/types.md
- Literals: lang/literals.md - Literals: lang/literals.md
- Predefined constants: lang/predefined_constants.md - Predefined constants: lang/predefined_constants.md
- List of magic suffixes: lang/suffixes.md
- Text encodings: lang/text.md - Text encodings: lang/text.md
- Custom text encodings: lang/custom-encoding.md - Custom text encodings: lang/custom-encoding.md
- Operators: lang/operators.md - Operators: lang/operators.md

View File

@ -512,6 +512,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
addUnexpandedPointerConstant(s"segment.$segment.end") addUnexpandedPointerConstant(s"segment.$segment.end")
addUnexpandedWordConstant(s"segment.$segment.length") addUnexpandedWordConstant(s"segment.$segment.length")
addUnexpandedByteConstant(s"segment.$segment.bank") addUnexpandedByteConstant(s"segment.$segment.bank")
addUnexpandedByteConstant(s"segment.$segment.fill")
} }
addThing(ConstantThing("$0000", Constant.WordZero, p), None) addThing(ConstantThing("$0000", Constant.WordZero, p), None)
addThing(FlagBooleanType("set_carry", addThing(FlagBooleanType("set_carry",
@ -1431,8 +1432,21 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
private def registerAddressConstant(thing: ThingInMemory, position: Option[Position], options: CompilationOptions, targetType: Option[Type]): Unit = { private def registerAddressConstant(thing: ThingInMemory, position: Option[Position], options: CompilationOptions, targetType: Option[Type]): Unit = {
val b = get[Type]("byte") val b = get[Type]("byte")
if (!thing.zeropage && options.flag(CompilationFlag.LUnixRelocatableCode)) {
val w = get[Type]("word") val w = get[Type]("word")
val ptr = get[Type]("pointer")
val segment = thing.bank(options)
for (bankNumber <- options.platform.bankNumbers.get(segment)) {
addThing(ConstantThing(thing.name + ".segment", NumericConstant(bankNumber, 1), b), position)
addThing(ConstantThing(thing.name + ".segment.bank", NumericConstant(bankNumber, 1), b), position)
}
for (bankFill <- options.platform.bankFill.get(segment)) {
addThing(ConstantThing(thing.name + ".segment.fill", NumericConstant(bankFill, 1), b), position)
}
addThing(ConstantThing(thing.name + ".segment.start", UnexpandedConstant(s"segment.$segment.start", 2), ptr), position)
addThing(ConstantThing(thing.name + ".segment.heapstart", UnexpandedConstant(s"segment.$segment.heapstart", 2), ptr), position)
addThing(ConstantThing(thing.name + ".segment.end", UnexpandedConstant(s"segment.$segment.end", 2), ptr), position)
addThing(ConstantThing(thing.name + ".segment.length", UnexpandedConstant(s"segment.$segment.length", 2), w), position)
if (!thing.zeropage && options.flag(CompilationFlag.LUnixRelocatableCode)) {
val relocatable = UninitializedMemoryVariable(thing.name + ".addr", w, VariableAllocationMethod.Static, None, Set.empty, defaultVariableAlignment(options, 2), isVolatile = false) val relocatable = UninitializedMemoryVariable(thing.name + ".addr", w, VariableAllocationMethod.Static, None, Set.empty, defaultVariableAlignment(options, 2), isVolatile = false)
val addr = relocatable.toAddress val addr = relocatable.toAddress
addThing(relocatable, position) addThing(relocatable, position)
@ -1445,9 +1459,9 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
addThing(RelativeVariable(thing.name + ".pointer.lo", addr, b, zeropage = false, None, isVolatile = false), position) addThing(RelativeVariable(thing.name + ".pointer.lo", addr, b, zeropage = false, None, isVolatile = false), position)
} }
val rawaddr = thing.toAddress val rawaddr = thing.toAddress
addThing(ConstantThing(thing.name + ".rawaddr", rawaddr, get[Type]("pointer")), position) addThing(ConstantThing(thing.name + ".rawaddr", rawaddr, ptr), position)
addThing(ConstantThing(thing.name + ".rawaddr.hi", rawaddr.hiByte, get[Type]("byte")), position) addThing(ConstantThing(thing.name + ".rawaddr.hi", rawaddr.hiByte, b), position)
addThing(ConstantThing(thing.name + ".rawaddr.lo", rawaddr.loByte, get[Type]("byte")), position) addThing(ConstantThing(thing.name + ".rawaddr.lo", rawaddr.loByte, b), position)
thing match { thing match {
case f: FunctionInMemory if f.canBePointedTo => case f: FunctionInMemory if f.canBePointedTo =>
val actualAddr = if (f.requiresTrampoline(options)) { val actualAddr = if (f.requiresTrampoline(options)) {
@ -1468,10 +1482,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
} }
} else { } else {
val addr = thing.toAddress val addr = thing.toAddress
addThing(ConstantThing(thing.name + ".addr", addr, get[Type]("pointer")), position) addThing(ConstantThing(thing.name + ".addr", addr, ptr), position)
addThing(ConstantThing(thing.name + ".addr.hi", addr.hiByte, b), position) addThing(ConstantThing(thing.name + ".addr.hi", addr.hiByte, b), position)
addThing(ConstantThing(thing.name + ".addr.lo", addr.loByte, b), position) addThing(ConstantThing(thing.name + ".addr.lo", addr.loByte, b), position)
addThing(ConstantThing(thing.name + ".rawaddr", addr, get[Type]("pointer")), position) addThing(ConstantThing(thing.name + ".rawaddr", addr, ptr), position)
addThing(ConstantThing(thing.name + ".rawaddr.hi", addr.hiByte, b), position) addThing(ConstantThing(thing.name + ".rawaddr.hi", addr.hiByte, b), position)
addThing(ConstantThing(thing.name + ".rawaddr.lo", addr.loByte, b), position) addThing(ConstantThing(thing.name + ".rawaddr.lo", addr.loByte, b), position)
targetType.foreach { tt => targetType.foreach { tt =>
@ -2175,6 +2189,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
Subvariable(".hiword", 0, w), Subvariable(".hiword", 0, w),
Subvariable(".hiword.lo", 1, b), Subvariable(".hiword.lo", 1, b),
Subvariable(".hiword.hi", 0, b), Subvariable(".hiword.hi", 0, b),
Subvariable(".lo", 2, b),
Subvariable(".b0", 2, b), Subvariable(".b0", 2, b),
Subvariable(".b1", 1, b), Subvariable(".b1", 1, b),
Subvariable(".b2", 0, b) Subvariable(".b2", 0, b)
@ -2185,6 +2200,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
Subvariable(".hiword", 1, w), Subvariable(".hiword", 1, w),
Subvariable(".hiword.lo", 1, b), Subvariable(".hiword.lo", 1, b),
Subvariable(".hiword.hi", 2, b), Subvariable(".hiword.hi", 2, b),
Subvariable(".lo", 0, b),
Subvariable(".b0", 0, b), Subvariable(".b0", 0, b),
Subvariable(".b1", 1, b), Subvariable(".b1", 1, b),
Subvariable(".b2", 2, b)) Subvariable(".b2", 2, b))
@ -2195,6 +2211,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
Subvariable(".loword.hi", 2, b), Subvariable(".loword.hi", 2, b),
Subvariable(".hiword.lo", 1, b), Subvariable(".hiword.lo", 1, b),
Subvariable(".hiword.hi", 0, b), Subvariable(".hiword.hi", 0, b),
Subvariable(".lo", 3, b),
Subvariable(".b0", 3, b), Subvariable(".b0", 3, b),
Subvariable(".b1", 2, b), Subvariable(".b1", 2, b),
Subvariable(".b2", 1, b), Subvariable(".b2", 1, b),
@ -2206,6 +2223,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
Subvariable(".loword.hi", 1, b), Subvariable(".loword.hi", 1, b),
Subvariable(".hiword.lo", 2, b), Subvariable(".hiword.lo", 2, b),
Subvariable(".hiword.hi", 3, b), Subvariable(".hiword.hi", 3, b),
Subvariable(".lo", 0, b),
Subvariable(".b0", 0, b), Subvariable(".b0", 0, b),
Subvariable(".b1", 1, b), Subvariable(".b1", 1, b),
Subvariable(".b2", 2, b), Subvariable(".b2", 2, b),

View File

@ -655,6 +655,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
unimportantLabelMap += s"segment.$segment.heapstart" -> (defaultBank -> allocator.heapStart) unimportantLabelMap += s"segment.$segment.heapstart" -> (defaultBank -> allocator.heapStart)
unimportantLabelMap += s"segment.$segment.length" -> (defaultBank -> (allocator.endBefore - allocator.startAt)) unimportantLabelMap += s"segment.$segment.length" -> (defaultBank -> (allocator.endBefore - allocator.startAt))
unimportantLabelMap += s"segment.$segment.bank" -> (defaultBank -> platform.bankNumbers(segment)) unimportantLabelMap += s"segment.$segment.bank" -> (defaultBank -> platform.bankNumbers(segment))
unimportantLabelMap += s"segment.$segment.fill" -> (defaultBank -> platform.bankFill(segment))
} }
env = rootEnv.allThings env = rootEnv.allThings

View File

@ -19,6 +19,8 @@ class SegmentSuite extends FunSuite with Matchers {
| } | }
| byte a5 | byte a5
| byte output @$c000 | byte output @$c000
| volatile byte output2 @$c001
| volatile byte output3 @$c002
| void main() { | void main() {
| output = 0 | output = 0
| if a1.addr.hi & $e0 == $80 { output += 1 } | if a1.addr.hi & $e0 == $80 { output += 1 }
@ -26,10 +28,14 @@ class SegmentSuite extends FunSuite with Matchers {
| if a3.addr.hi & $e0 == $80 { output += 1 } | if a3.addr.hi & $e0 == $80 { output += 1 }
| if a4.addr.hi & $e0 == $a0 { output += 1 } | if a4.addr.hi & $e0 == $a0 { output += 1 }
| if a5.addr.hi & $e0 == $00 { output += 1 } | if a5.addr.hi & $e0 == $00 { output += 1 }
| output2 = a1.segment.bank ^ main.segment.bank
| output2 ^= segment.second.bank ^ segment.default.bank
| output3 = lo(main.segment.start)
| } | }
""".stripMargin """.stripMargin
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)(source) { m => EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)(source) { m =>
m.readByte(0xc000) should equal(source.count(_ == '+')) m.readByte(0xc000) should equal(source.count(_ == '+'))
m.readByte(0xc001) should equal(0)
} }
} }
} }