mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-07 11:39:08 +00:00
Allow for preinitialized global variables
This commit is contained in:
parent
115f040fe2
commit
86ef4fcaf4
@ -102,7 +102,7 @@ object Main {
|
||||
ErrorReporting.debug(f"Unoptimized code size: ${assembler.unoptimizedCodeSize}%5d B")
|
||||
ErrorReporting.debug(f"Optimized code size: ${assembler.optimizedCodeSize}%5d B")
|
||||
ErrorReporting.debug(f"Gain: ${(100L * (assembler.unoptimizedCodeSize - assembler.optimizedCodeSize) / assembler.unoptimizedCodeSize.toDouble).round}%5d%%")
|
||||
ErrorReporting.debug(f"Initialized arrays: ${assembler.initializedArraysSize}%5d B")
|
||||
ErrorReporting.debug(f"Initialized variables: ${assembler.initializedVariablesSize}%5d B")
|
||||
|
||||
if (c.outputAssembly) {
|
||||
val path = Paths.get(assOutput)
|
||||
|
22
src/main/scala/millfork/env/Environment.scala
vendored
22
src/main/scala/millfork/env/Environment.scala
vendored
@ -54,9 +54,10 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
case _ => None
|
||||
}.toList
|
||||
|
||||
def allPreallocatables: List[PrellocableThing] = things.values.flatMap {
|
||||
def allPreallocatables: List[PreallocableThing] = things.values.flatMap {
|
||||
case m: NormalFunction => Some(m)
|
||||
case m: InitializedArray => Some(m)
|
||||
case m: InitializedMemoryVariable => Some(m)
|
||||
case _ => None
|
||||
}.toList
|
||||
|
||||
@ -413,7 +414,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
stmt.assemblyParamPassingConvention match {
|
||||
case ByVariable(name) =>
|
||||
val zp = typ.name == "pointer" // TODO
|
||||
val v = MemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto)
|
||||
val v = UninitializedMemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto)
|
||||
addThing(v, stmt.position)
|
||||
registerAddressConstant(v, stmt.position)
|
||||
if (typ.size == 2) {
|
||||
@ -452,7 +453,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
case Some(aa) => RelativeArray(stmt.name + ".array", aa, length.toInt)
|
||||
}
|
||||
addThing(array, stmt.position)
|
||||
registerAddressConstant(MemoryVariable(stmt.name, p, VariableAllocationMethod.None), stmt.position)
|
||||
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None), stmt.position)
|
||||
val a = address match {
|
||||
case None => array.toAddress
|
||||
case Some(aa) => aa
|
||||
@ -486,7 +487,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
val data = contents.map(x => eval(x).getOrElse(Constant.error(s"Array `${stmt.name}` has non-constant contents", stmt.position)))
|
||||
val array = InitializedArray(stmt.name + ".array", address, data)
|
||||
addThing(array, stmt.position)
|
||||
registerAddressConstant(MemoryVariable(stmt.name, p, VariableAllocationMethod.None), stmt.position)
|
||||
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None), stmt.position)
|
||||
val a = address match {
|
||||
case None => array.toAddress
|
||||
case Some(aa) => aa
|
||||
@ -538,7 +539,7 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
}
|
||||
} else {
|
||||
if (stmt.stack && stmt.global) ErrorReporting.error(s"`$name` is static or global and cannot be on stack", position)
|
||||
if (stmt.initialValue.isDefined) ErrorReporting.error(s"`$name` is not a constant and cannot have a value", position)
|
||||
if (stmt.initialValue.isDefined && parent.isDefined) ErrorReporting.error(s"`$name` is local and not a constant and therefore cannot have a value", position)
|
||||
if (stmt.stack) {
|
||||
val v = StackVariable(prefix + name, typ, this.baseStackOffset)
|
||||
baseStackOffset += typ.size
|
||||
@ -550,7 +551,16 @@ class Environment(val parent: Option[Environment], val prefix: String) {
|
||||
} else {
|
||||
val (v, addr) = stmt.address.fold[(VariableInMemory, Constant)]({
|
||||
val alloc = if (typ.name == "pointer") VariableAllocationMethod.Zeropage else if (stmt.global) VariableAllocationMethod.Static else VariableAllocationMethod.Auto
|
||||
val v = MemoryVariable(prefix + name, typ, alloc)
|
||||
if (alloc != VariableAllocationMethod.Static && stmt.initialValue.isDefined) {
|
||||
ErrorReporting.error(s"`$name` cannot be preinitialized`", position)
|
||||
}
|
||||
val v = stmt.initialValue.fold[MemoryVariable](UninitializedMemoryVariable(prefix + name, typ, alloc)){ive =>
|
||||
if (options.flags(CompilationFlag.ReadOnlyArrays)) {
|
||||
ErrorReporting.warn("Initialized variable in read-only segment", options, position)
|
||||
}
|
||||
val ivc = eval(ive).getOrElse(Constant.error(s"Initial value of `$name` is not a constant", position))
|
||||
InitializedMemoryVariable(name, None, typ, ivc)
|
||||
}
|
||||
registerAddressConstant(v, stmt.position)
|
||||
(v, v.toAddress)
|
||||
})(a => {
|
||||
|
26
src/main/scala/millfork/env/Thing.scala
vendored
26
src/main/scala/millfork/env/Thing.scala
vendored
@ -75,7 +75,7 @@ sealed trait ThingInMemory extends Thing {
|
||||
def toAddress: Constant
|
||||
}
|
||||
|
||||
sealed trait PrellocableThing extends ThingInMemory {
|
||||
sealed trait PreallocableThing extends ThingInMemory {
|
||||
def shouldGenerate: Boolean
|
||||
|
||||
def address: Option[Constant]
|
||||
@ -118,7 +118,15 @@ case class StackVariable(name: String, typ: Type, baseOffset: Int) extends Varia
|
||||
def sizeInBytes: Int = typ.size
|
||||
}
|
||||
|
||||
case class MemoryVariable(name: String, typ: Type, alloc: VariableAllocationMethod.Value) extends VariableInMemory with UninitializedMemory {
|
||||
object MemoryVariable {
|
||||
def unapply(v: MemoryVariable) = Some((v.name, v.typ, v.alloc))
|
||||
}
|
||||
|
||||
abstract class MemoryVariable extends VariableInMemory {
|
||||
def alloc: VariableAllocationMethod.Value
|
||||
}
|
||||
|
||||
case class UninitializedMemoryVariable(name: String, typ: Type, alloc: VariableAllocationMethod.Value) extends MemoryVariable with UninitializedMemory {
|
||||
override def sizeInBytes: Int = typ.size
|
||||
|
||||
override def zeropage: Boolean = alloc == VariableAllocationMethod.Zeropage
|
||||
@ -126,6 +134,16 @@ case class MemoryVariable(name: String, typ: Type, alloc: VariableAllocationMeth
|
||||
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
|
||||
}
|
||||
|
||||
case class InitializedMemoryVariable(name: String, address: Option[Constant], typ: Type, initialValue: Constant) extends MemoryVariable with PreallocableThing {
|
||||
override def zeropage: Boolean = false
|
||||
|
||||
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
|
||||
|
||||
override def shouldGenerate: Boolean = true
|
||||
|
||||
override def alloc: VariableAllocationMethod.Value = VariableAllocationMethod.Static
|
||||
}
|
||||
|
||||
trait MlArray extends ThingInMemory
|
||||
|
||||
case class UninitializedArray(name: String, sizeInBytes: Int) extends MlArray with UninitializedMemory {
|
||||
@ -138,7 +156,7 @@ case class RelativeArray(name: String, address: Constant, sizeInBytes: Int) exte
|
||||
override def toAddress: Constant = address
|
||||
}
|
||||
|
||||
case class InitializedArray(name: String, address: Option[Constant], contents: List[Constant]) extends MlArray with PrellocableThing {
|
||||
case class InitializedArray(name: String, address: Option[Constant], contents: List[Constant]) extends MlArray with PreallocableThing {
|
||||
override def shouldGenerate = true
|
||||
}
|
||||
|
||||
@ -196,7 +214,7 @@ case class NormalFunction(name: String,
|
||||
code: List[ExecutableStatement],
|
||||
interrupt: Boolean,
|
||||
reentrant: Boolean,
|
||||
position: Option[Position]) extends FunctionInMemory with PrellocableThing {
|
||||
position: Option[Position]) extends FunctionInMemory with PreallocableThing {
|
||||
override def shouldGenerate = true
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ class Assembler(private val rootEnv: Environment) {
|
||||
var env = rootEnv.allThings
|
||||
var unoptimizedCodeSize = 0
|
||||
var optimizedCodeSize = 0
|
||||
var initializedArraysSize = 0
|
||||
var initializedVariablesSize = 0
|
||||
|
||||
val mem = new CompiledMemory
|
||||
val labelMap = mutable.Map[String, Int]()
|
||||
@ -154,7 +154,7 @@ class Assembler(private val rootEnv: Environment) {
|
||||
mem.banks(0).writeable(index) = true
|
||||
index += 1
|
||||
}
|
||||
initializedArraysSize += items.length
|
||||
initializedVariablesSize += items.length
|
||||
case InitializedArray(name, Some(_), items) => ???
|
||||
case f: NormalFunction if f.address.isDefined =>
|
||||
var index = f.address.get.asInstanceOf[NumericConstant].value.toInt
|
||||
@ -187,7 +187,23 @@ class Assembler(private val rootEnv: Environment) {
|
||||
mem.banks(0).writeable(index) = true
|
||||
index += 1
|
||||
}
|
||||
initializedArraysSize += items.length
|
||||
initializedVariablesSize += items.length
|
||||
case m@InitializedMemoryVariable(name, None, typ, value) =>
|
||||
if (options.flags(CompilationFlag.PreventJmpIndirectBug) && (index & 0xff) + typ.size > 0x100) {
|
||||
index = (index & 0xffff00) + 0x100
|
||||
}
|
||||
labelMap(name) = index
|
||||
val altName = m.name.stripPrefix(env.prefix) + "`"
|
||||
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
|
||||
assembly.append("* = $" + index.toHexString)
|
||||
assembly.append(name)
|
||||
for(i <- 0 until typ.size) {
|
||||
writeByte(0, index, value.subbyte(i))
|
||||
assembly.append(" !byte " + value.subbyte(i).quickSimplify)
|
||||
mem.banks(0).writeable(index) = true
|
||||
index += 1
|
||||
}
|
||||
initializedVariablesSize += typ.size
|
||||
case _ =>
|
||||
}
|
||||
val allocator = platform.allocator
|
||||
|
@ -25,4 +25,31 @@ class BasicSymonTest extends FunSuite with Matchers {
|
||||
| }
|
||||
""".stripMargin).readByte(0xc000) should equal(1)
|
||||
}
|
||||
|
||||
test("Preallocated variables") {
|
||||
val m = EmuUnoptimizedRun(
|
||||
"""
|
||||
| array output [2] @$c000
|
||||
| byte number = 4
|
||||
| void main () {
|
||||
| output[0] = number
|
||||
| number += 1
|
||||
| output[1] = number
|
||||
| }
|
||||
""".stripMargin)
|
||||
m.readByte(0xc000) should equal(4)
|
||||
m.readByte(0xc001) should equal(5)
|
||||
}
|
||||
|
||||
test("Preallocated variables 2") {
|
||||
val m = EmuUnoptimizedRun(
|
||||
"""
|
||||
| word output @$c000
|
||||
| word number = 344
|
||||
| void main () {
|
||||
| output = number
|
||||
| }
|
||||
""".stripMargin)
|
||||
m.readWord(0xc000) should equal(344)
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import com.loomcom.symon.{Bus, Cpu, CpuState}
|
||||
import fastparse.core.Parsed.{Failure, Success}
|
||||
import millfork.assembly.opt.AssemblyOptimization
|
||||
import millfork.compiler.{CompilationContext, MlCompiler}
|
||||
import millfork.env.{Environment, InitializedArray, NormalFunction}
|
||||
import millfork.env.{Environment, InitializedArray, InitializedMemoryVariable, NormalFunction}
|
||||
import millfork.error.ErrorReporting
|
||||
import millfork.node.StandardCallGraph
|
||||
import millfork.node.opt.NodeOptimization
|
||||
@ -134,6 +134,11 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization],
|
||||
d.contents.foreach(c => println(" !byte " + c))
|
||||
unoptimizedSize += d.contents.length
|
||||
optimizedSize += d.contents.length
|
||||
case d: InitializedMemoryVariable =>
|
||||
println(d.name)
|
||||
0.until(d.typ.size).foreach(c => println(" !byte " + d.initialValue.subbyte(c)))
|
||||
unoptimizedSize += d.typ.size
|
||||
optimizedSize += d.typ.size
|
||||
}
|
||||
|
||||
ErrorReporting.assertNoErrors("Compile failed")
|
||||
|
Loading…
x
Reference in New Issue
Block a user