1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-31 18:41:30 +00:00
This commit is contained in:
Karol Stasiak 2019-04-16 16:56:23 +02:00
parent 9ea04db566
commit 0a36d83164
13 changed files with 76 additions and 18 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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 {

View File

@ -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)

View File

@ -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)
}

View File

@ -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 {

View File

@ -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

View File

@ -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 =>
}
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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