mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-28 11:31:58 +00:00
Define and document more magic suffixes and constants (see #87)
This commit is contained in:
parent
fe4f0dcfd9
commit
499e650752
@ -173,7 +173,7 @@ Default: `after_code`.
|
||||
* `segment_NAME_bank` – the bank number the segment belongs to. Default: `0`.
|
||||
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.
|
||||
One item has to be `*`, it means "all the other objects".
|
||||
|
@ -26,6 +26,8 @@
|
||||
|
||||
* [Predefined constants](lang/predefined_constants.md)
|
||||
|
||||
* [List of magic suffixes](lang/suffixes.md)
|
||||
|
||||
* [List of text encodings and escape sequences](lang/text.md)
|
||||
|
||||
* [Defining custom encodings](lang/custom-encoding.md)
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
# 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
|
||||
|
||||
|
46
docs/lang/suffixes.md
Normal file
46
docs/lang/suffixes.md
Normal 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).
|
||||
|
@ -107,6 +107,12 @@ For every variable `x` larger than a byte, extra subvariables are defined:
|
||||
* constituent bytes, from low to high: `x.b0`, `x.b1`, `x.b2`, etc.
|
||||
|
||||
* 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
|
||||
|
||||
|
@ -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`.
|
||||
|
||||
* `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:
|
||||
|
||||
@ -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
|
||||
|
||||
You can convert from a boolean type to an arithmetic type by simply casting:
|
||||
|
||||
byte c = byte(x >= 0x80)
|
||||
|
||||
Examples:
|
||||
|
||||
bool f() = true
|
||||
|
@ -26,6 +26,7 @@ nav:
|
||||
- Types: lang/types.md
|
||||
- Literals: lang/literals.md
|
||||
- Predefined constants: lang/predefined_constants.md
|
||||
- List of magic suffixes: lang/suffixes.md
|
||||
- Text encodings: lang/text.md
|
||||
- Custom text encodings: lang/custom-encoding.md
|
||||
- Operators: lang/operators.md
|
||||
|
30
src/main/scala/millfork/env/Environment.scala
vendored
30
src/main/scala/millfork/env/Environment.scala
vendored
@ -512,6 +512,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
addUnexpandedPointerConstant(s"segment.$segment.end")
|
||||
addUnexpandedWordConstant(s"segment.$segment.length")
|
||||
addUnexpandedByteConstant(s"segment.$segment.bank")
|
||||
addUnexpandedByteConstant(s"segment.$segment.fill")
|
||||
}
|
||||
addThing(ConstantThing("$0000", Constant.WordZero, p), None)
|
||||
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 = {
|
||||
val b = get[Type]("byte")
|
||||
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 w = get[Type]("word")
|
||||
val relocatable = UninitializedMemoryVariable(thing.name + ".addr", w, VariableAllocationMethod.Static, None, Set.empty, defaultVariableAlignment(options, 2), isVolatile = false)
|
||||
val addr = relocatable.toAddress
|
||||
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)
|
||||
}
|
||||
val rawaddr = thing.toAddress
|
||||
addThing(ConstantThing(thing.name + ".rawaddr", rawaddr, get[Type]("pointer")), position)
|
||||
addThing(ConstantThing(thing.name + ".rawaddr.hi", rawaddr.hiByte, get[Type]("byte")), position)
|
||||
addThing(ConstantThing(thing.name + ".rawaddr.lo", rawaddr.loByte, get[Type]("byte")), position)
|
||||
addThing(ConstantThing(thing.name + ".rawaddr", rawaddr, ptr), position)
|
||||
addThing(ConstantThing(thing.name + ".rawaddr.hi", rawaddr.hiByte, b), position)
|
||||
addThing(ConstantThing(thing.name + ".rawaddr.lo", rawaddr.loByte, b), position)
|
||||
thing match {
|
||||
case f: FunctionInMemory if f.canBePointedTo =>
|
||||
val actualAddr = if (f.requiresTrampoline(options)) {
|
||||
@ -1468,10 +1482,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
}
|
||||
} else {
|
||||
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.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.lo", addr.loByte, b), position)
|
||||
targetType.foreach { tt =>
|
||||
@ -2175,6 +2189,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
Subvariable(".hiword", 0, w),
|
||||
Subvariable(".hiword.lo", 1, b),
|
||||
Subvariable(".hiword.hi", 0, b),
|
||||
Subvariable(".lo", 2, b),
|
||||
Subvariable(".b0", 2, b),
|
||||
Subvariable(".b1", 1, 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.lo", 1, b),
|
||||
Subvariable(".hiword.hi", 2, b),
|
||||
Subvariable(".lo", 0, b),
|
||||
Subvariable(".b0", 0, b),
|
||||
Subvariable(".b1", 1, 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(".hiword.lo", 1, b),
|
||||
Subvariable(".hiword.hi", 0, b),
|
||||
Subvariable(".lo", 3, b),
|
||||
Subvariable(".b0", 3, b),
|
||||
Subvariable(".b1", 2, 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(".hiword.lo", 2, b),
|
||||
Subvariable(".hiword.hi", 3, b),
|
||||
Subvariable(".lo", 0, b),
|
||||
Subvariable(".b0", 0, b),
|
||||
Subvariable(".b1", 1, b),
|
||||
Subvariable(".b2", 2, b),
|
||||
|
@ -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.length" -> (defaultBank -> (allocator.endBefore - allocator.startAt))
|
||||
unimportantLabelMap += s"segment.$segment.bank" -> (defaultBank -> platform.bankNumbers(segment))
|
||||
unimportantLabelMap += s"segment.$segment.fill" -> (defaultBank -> platform.bankFill(segment))
|
||||
}
|
||||
|
||||
env = rootEnv.allThings
|
||||
|
@ -19,6 +19,8 @@ class SegmentSuite extends FunSuite with Matchers {
|
||||
| }
|
||||
| byte a5
|
||||
| byte output @$c000
|
||||
| volatile byte output2 @$c001
|
||||
| volatile byte output3 @$c002
|
||||
| void main() {
|
||||
| output = 0
|
||||
| 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 a4.addr.hi & $e0 == $a0 { 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
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)(source) { m =>
|
||||
m.readByte(0xc000) should equal(source.count(_ == '+'))
|
||||
m.readByte(0xc001) should equal(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user