mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-06 20:37:12 +00:00
nullptr
This commit is contained in:
parent
9ea04db566
commit
0a36d83164
@ -18,6 +18,8 @@
|
||||
|
||||
* Pointers can now be typed.
|
||||
|
||||
* Added `nullptr`.
|
||||
|
||||
* Arrays can now have elements of types other than `byte` (still limited in size to 1 byte though).
|
||||
|
||||
* Added hint for identifiers with typos.
|
||||
@ -36,7 +38,9 @@
|
||||
|
||||
* Fixed `#use` not accessing all preprocessor parameters.
|
||||
|
||||
* Fixed `#pragma` not respecting `#if`
|
||||
* Fixed `#pragma` not respecting `#if`.
|
||||
|
||||
* 8080 and LR35902: fixed large stack variables.
|
||||
|
||||
* Optimization improvements.
|
||||
|
||||
|
@ -90,6 +90,8 @@ The following features are defined based on the chosen CPU and compilation optio
|
||||
|
||||
* `PAL` – 1 if the target is PAL, 0 otherwise
|
||||
|
||||
* `NULLPTR` – physical value of `nullptr`, default 0
|
||||
|
||||
### Built-in preprocessor functions and operators
|
||||
|
||||
The `defined` function returns 1 if the feature is defined, 0 otherwise.
|
||||
|
@ -66,6 +66,13 @@ Examples:
|
||||
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->z // 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
|
||||
|
||||
@ -103,7 +110,7 @@ Assigment between numeric types and enumerations is not possible without an expl
|
||||
Plain enumerations have their variants equal to `byte(0)` to `byte(<name>.count - 1)`.
|
||||
|
||||
Tip: You can use an enumeration with no variants as a strongly checked alternative byte type,
|
||||
as there are no checks no values when converting bytes to enumeration values and vice versa.
|
||||
as there are no checks on values when converting bytes to enumeration values and vice versa.
|
||||
|
||||
## Structs
|
||||
|
||||
|
@ -196,7 +196,7 @@ object Main {
|
||||
}
|
||||
val callGraph = new StandardCallGraph(program, options.log)
|
||||
|
||||
val env = new Environment(None, "", platform.cpuFamily, options.jobContext)
|
||||
val env = new Environment(None, "", platform.cpuFamily, options)
|
||||
env.collectDeclarations(program, options)
|
||||
|
||||
val assemblyOptimizations = optLevel match {
|
||||
@ -248,7 +248,7 @@ object Main {
|
||||
}
|
||||
val callGraph = new StandardCallGraph(program, options.log)
|
||||
|
||||
val env = new Environment(None, "", platform.cpuFamily, options.jobContext)
|
||||
val env = new Environment(None, "", platform.cpuFamily, options)
|
||||
env.collectDeclarations(program, options)
|
||||
|
||||
val assemblyOptimizations = optLevel match {
|
||||
|
@ -28,7 +28,7 @@ class AbstractExpressionCompiler[T <: AbstractCode] {
|
||||
}
|
||||
|
||||
def callingContext(ctx: CompilationContext, callee: String, v: VariableInMemory): CompilationContext = {
|
||||
val result = new Environment(Some(ctx.env), "", ctx.options.platform.cpuFamily, ctx.jobContext)
|
||||
val result = new Environment(Some(ctx.env), "", ctx.options.platform.cpuFamily, ctx.options)
|
||||
val localName = v.name.stripPrefix(callee + '$')
|
||||
result.addVariable(ctx.options, localName, v, None)
|
||||
ctx.copy(env = result)
|
||||
|
@ -16,7 +16,7 @@ case class CompilationContext(env: Environment,
|
||||
breakLabels: Map[String, Label] = Map(),
|
||||
continueLabels: Map[String, Label] = Map()){
|
||||
def withInlinedEnv(environment: Environment, newLabel: String): CompilationContext = {
|
||||
val newEnv = new Environment(Some(env), newLabel, environment.cpuFamily, jobContext)
|
||||
val newEnv = new Environment(Some(env), newLabel, environment.cpuFamily, options)
|
||||
newEnv.things ++= environment.things
|
||||
copy(env = newEnv)
|
||||
}
|
||||
|
25
src/main/scala/millfork/env/Environment.scala
vendored
25
src/main/scala/millfork/env/Environment.scala
vendored
@ -18,8 +18,10 @@ import scala.collection.mutable.ListBuffer
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
//noinspection NotImplementedCode
|
||||
class Environment(val parent: Option[Environment], val prefix: String, val cpuFamily: CpuFamily.Value, val jobContext: JobContext) {
|
||||
class Environment(val parent: Option[Environment], val prefix: String, val cpuFamily: CpuFamily.Value, val options: CompilationOptions) {
|
||||
|
||||
@inline
|
||||
def jobContext: JobContext = options.jobContext
|
||||
@inline
|
||||
def log: Logger = jobContext.log
|
||||
@inline
|
||||
@ -50,7 +52,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
m.environment.getAllPrefixedThings
|
||||
case _ => Map[String, Thing]()
|
||||
}.fold(things.toMap)(_ ++ _)
|
||||
val e = new Environment(None, "", cpuFamily, jobContext)
|
||||
val e = new Environment(None, "", cpuFamily, options)
|
||||
e.things.clear()
|
||||
e.things ++= allThings
|
||||
e
|
||||
@ -397,6 +399,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
|
||||
if (parent.isEmpty) {
|
||||
addThing(VoidType, None)
|
||||
addThing(NullType, None)
|
||||
addThing(BuiltInBooleanType, None)
|
||||
val b = BasicPlainType("byte", 1)
|
||||
val w = BasicPlainType("word", 2)
|
||||
@ -433,8 +436,20 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
addThing(falseType, None)
|
||||
addThing(ConstantThing("true", NumericConstant(1, 0), trueType), None)
|
||||
addThing(ConstantThing("false", NumericConstant(0, 0), falseType), None)
|
||||
addThing(ConstantThing("__zeropage_usage", UnexpandedConstant("__zeropage_usage", 1), b), None)
|
||||
addThing(ConstantThing("__heap_start", UnexpandedConstant("__heap_start", 2), p), None)
|
||||
val nullptrValue = options.features.getOrElse("NULLPTR", 0L)
|
||||
val nullptrConstant = NumericConstant(nullptrValue, 2)
|
||||
addThing(ConstantThing("nullptr", nullptrConstant, NullType), None)
|
||||
addThing(ConstantThing("nullptr.hi", nullptrConstant.hiByte.quickSimplify, b), None)
|
||||
addThing(ConstantThing("nullptr.lo", nullptrConstant.loByte.quickSimplify, b), None)
|
||||
addThing(ConstantThing("nullptr.raw", nullptrConstant, p), None)
|
||||
addThing(ConstantThing("nullptr.raw.hi", nullptrConstant.hiByte.quickSimplify, b), None)
|
||||
addThing(ConstantThing("nullptr.raw.lo", nullptrConstant.loByte.quickSimplify, b), None)
|
||||
val __zeropage_usage = UnexpandedConstant("__zeropage_usage", 1)
|
||||
addThing(ConstantThing("__zeropage_usage", __zeropage_usage, b), None)
|
||||
val __heap_start = UnexpandedConstant("__heap_start", 2)
|
||||
addThing(ConstantThing("__heap_start", __heap_start, p), None)
|
||||
addThing(ConstantThing("__heap_start.hi", __heap_start.hiByte, b), None)
|
||||
addThing(ConstantThing("__heap_start.lo", __heap_start.loByte, b), None)
|
||||
addThing(ConstantThing("$0000", NumericConstant(0, 2), p), None)
|
||||
addThing(FlagBooleanType("set_carry",
|
||||
BranchingOpcodeMapping(Opcode.BCS, IfFlagSet(ZFlag.C)),
|
||||
@ -928,7 +943,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
log.error(s"Non-macro function `$name` cannot have inlinable parameters", stmt.position)
|
||||
}
|
||||
|
||||
val env = new Environment(Some(this), name + "$", cpuFamily, jobContext)
|
||||
val env = new Environment(Some(this), name + "$", cpuFamily, options)
|
||||
stmt.params.foreach(p => env.registerParameter(p, options))
|
||||
def params: ParamSignature = if (stmt.assembly) {
|
||||
AssemblyParamSignature(stmt.params.map {
|
||||
|
14
src/main/scala/millfork/env/Thing.scala
vendored
14
src/main/scala/millfork/env/Thing.scala
vendored
@ -82,6 +82,20 @@ case class PointerType(name: String, targetName: String, var target: Option[Type
|
||||
override def pointerTargetName: String = targetName
|
||||
}
|
||||
|
||||
case object NullType extends VariableType {
|
||||
override def size: Int = 2
|
||||
|
||||
override def isSigned: Boolean = false
|
||||
|
||||
override def name: String = "null$"
|
||||
|
||||
override def isPointy: Boolean = true
|
||||
|
||||
override def isSubtypeOf(other: Type): Boolean = this == other || other.isPointy && other.size == 2
|
||||
|
||||
override def isAssignableTo(targetType: Type): Boolean = this == targetType || targetType.isPointy && targetType.size == 2
|
||||
}
|
||||
|
||||
case class EnumType(name: String, count: Option[Int]) extends VariableType {
|
||||
override def size: Int = 1
|
||||
|
||||
|
@ -102,7 +102,7 @@ class PointerSuite extends FunSuite with Matchers {
|
||||
|
|
||||
| void main() {
|
||||
| heapEnd = heap.addr
|
||||
| this = pointer.pointlist(0)
|
||||
| this = nullptr
|
||||
| point tmp
|
||||
| tmp.x = 3
|
||||
| tmp.y = 3
|
||||
@ -152,4 +152,20 @@ class PointerSuite extends FunSuite with Matchers {
|
||||
m.readByte(0xc000) should equal(44)
|
||||
}
|
||||
}
|
||||
|
||||
test("nullptr") {
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80)(
|
||||
"""
|
||||
| void main() {
|
||||
| pointer.word pw
|
||||
| pointer p
|
||||
| word w
|
||||
| pw = nullptr
|
||||
| p = nullptr
|
||||
| w = word(nullptr)
|
||||
| }
|
||||
""".stripMargin) { m =>
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ class ZLineSizeSuite extends FunSuite with Matchers {
|
||||
private def runCase(line: ZLine): Unit = {
|
||||
val platform = EmuPlatform.get(Cpu.Z80)
|
||||
val jobContext = JobContext(TestErrorReporting.log, new LabelGenerator)
|
||||
val env = new Environment(None, "", CpuFamily.I80, jobContext)
|
||||
val options = CompilationOptions(platform, Map(), None, 0, Map(), jobContext)
|
||||
val env = new Environment(None, "", CpuFamily.I80, options)
|
||||
val correctSize = new Z80Assembler(null, env, platform).emitInstruction("default", options, 0x100, line) - 0x100
|
||||
val guessedSize = line.sizeInBytes
|
||||
guessedSize should equal(correctSize)
|
||||
|
@ -185,7 +185,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
}
|
||||
val program = nodeOptimizations.foldLeft(withLibraries.applyImportantAliases)((p, opt) => p.applyNodeOptimization(opt, options))
|
||||
val callGraph = new StandardCallGraph(program, log)
|
||||
val env = new Environment(None, "", CpuFamily.M6502, options.jobContext)
|
||||
val env = new Environment(None, "", CpuFamily.M6502, options)
|
||||
env.collectDeclarations(program, options)
|
||||
|
||||
val hasOptimizations = assemblyOptimizations.nonEmpty
|
||||
@ -205,7 +205,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
|
||||
|
||||
// compile
|
||||
val env2 = new Environment(None, "", CpuFamily.M6502, options.jobContext)
|
||||
val env2 = new Environment(None, "", CpuFamily.M6502, options)
|
||||
env2.collectDeclarations(program, options)
|
||||
val assembler = new MosAssembler(program, env2, platform)
|
||||
val output = assembler.assemble(callGraph, assemblyOptimizations, options)
|
||||
|
@ -109,7 +109,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
|
||||
}
|
||||
val program = nodeOptimizations.foldLeft(withLibraries.applyImportantAliases)((p, opt) => p.applyNodeOptimization(opt, options))
|
||||
val callGraph = new StandardCallGraph(program, log)
|
||||
val env = new Environment(None, "", CpuFamily.I80, options.jobContext)
|
||||
val env = new Environment(None, "", CpuFamily.I80, options)
|
||||
env.collectDeclarations(program, options)
|
||||
|
||||
val hasOptimizations = assemblyOptimizations.nonEmpty
|
||||
@ -129,7 +129,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
|
||||
|
||||
|
||||
// compile
|
||||
val env2 = new Environment(None, "", CpuFamily.I80, options.jobContext)
|
||||
val env2 = new Environment(None, "", CpuFamily.I80, options)
|
||||
env2.collectDeclarations(program, options)
|
||||
val assembler = new Z80Assembler(program, env2, platform)
|
||||
val output = assembler.assemble(callGraph, assemblyOptimizations, options)
|
||||
|
@ -45,7 +45,7 @@ object ShouldNotCompile extends Matchers {
|
||||
// prepare
|
||||
val callGraph = new StandardCallGraph(program, log)
|
||||
val cpuFamily = CpuFamily.forType(cpu)
|
||||
val env = new Environment(None, "", cpuFamily, options.jobContext)
|
||||
val env = new Environment(None, "", cpuFamily, options)
|
||||
env.collectDeclarations(program, options)
|
||||
|
||||
var unoptimizedSize = 0L
|
||||
|
Loading…
x
Reference in New Issue
Block a user