mirror of
https://github.com/KarolS/millfork.git
synced 2025-02-08 00:30:38 +00:00
Allow putting pointer variables anywhere
This commit is contained in:
parent
194f79f907
commit
4cd1db0e0f
@ -198,13 +198,37 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
AssemblyLine.zeropage(STA, reg, 1),
|
||||
AssemblyLine.immediate(LDA, addr.loByte),
|
||||
AssemblyLine.zeropage(STA, reg))
|
||||
case VariablePointy(addr, _, _) =>
|
||||
case VariablePointy(addr, _, _, true) =>
|
||||
List(
|
||||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.zeropage(ADC, addr + 1),
|
||||
AssemblyLine.zeropage(STA, reg, 1),
|
||||
AssemblyLine.zeropage(LDA, addr),
|
||||
AssemblyLine.zeropage(STA, reg))
|
||||
case VariablePointy(addr, _, _, false) =>
|
||||
List(
|
||||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.absolute(ADC, addr + 1),
|
||||
AssemblyLine.zeropage(STA, reg, 1),
|
||||
AssemblyLine.absolute(LDA, addr),
|
||||
AssemblyLine.zeropage(STA, reg))
|
||||
case StackVariablePointy(offset, _, _) =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitEmulation65816Opcodes)) {
|
||||
List(
|
||||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.stackRelative(ADC, offset + 1 + ctx.extraStackOffset),
|
||||
AssemblyLine.zeropage(STA, reg, 1),
|
||||
AssemblyLine.stackRelative(LDA, offset + ctx.extraStackOffset),
|
||||
AssemblyLine.zeropage(STA, reg))
|
||||
} else {
|
||||
List(
|
||||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.absoluteX(ADC, offset + 1 + ctx.extraStackOffset),
|
||||
AssemblyLine.zeropage(STA, reg, 1),
|
||||
AssemblyLine.absoluteX(LDA, offset + ctx.extraStackOffset),
|
||||
AssemblyLine.zeropage(STA, reg))
|
||||
}
|
||||
}
|
||||
compileIndex ++ prepareRegister
|
||||
}
|
||||
@ -279,21 +303,21 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
val cmos = ctx.options.flag(CompilationFlag.EmitCmosOpcodes)
|
||||
register match {
|
||||
case MosRegister.A =>
|
||||
List(AssemblyLine.implied(PHA)) ++ code ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
List(AssemblyLine.implied(PHA)) ++ fixTsx(code) ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
case MosRegister.X =>
|
||||
if (code.exists(l => OpcodeClasses.ChangesX(l.opcode))) {
|
||||
if (cmos)
|
||||
List(AssemblyLine.implied(PHX)) ++ code ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
List(AssemblyLine.implied(PHX)) ++ fixTsx(code) ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
else
|
||||
List(AssemblyLine.implied(TXA), AssemblyLine.implied(PHA)) ++ code ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
List(AssemblyLine.implied(TXA), AssemblyLine.implied(PHA)) ++ fixTsx(code) ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
} else {
|
||||
code ++ List(AssemblyLine.implied(TXA), AssemblyLine.indexedY(STA, reg))
|
||||
}
|
||||
case MosRegister.Y =>
|
||||
if (cmos)
|
||||
List(AssemblyLine.implied(PHY)) ++ code ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
List(AssemblyLine.implied(PHY)) ++ fixTsx(code) ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
else
|
||||
List(AssemblyLine.implied(TYA), AssemblyLine.implied(PHA)) ++ code ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
List(AssemblyLine.implied(TYA), AssemblyLine.implied(PHA)) ++ fixTsx(code) ++ List(AssemblyLine.implied(PLA), AssemblyLine.indexedY(STA, reg))
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,13 +326,16 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
List(AssemblyLine.absolute(store, env.genRelativeVariable(p.value + constIndex, b, zeropage = false)))
|
||||
case (p: VariablePointy, _, _, 2) =>
|
||||
wrapWordIndexingStorage(prepareWordIndexing(ctx, p, indexExpr))
|
||||
case (p: VariablePointy, _, 0 | 1, _) if !p.zeropage =>
|
||||
// TODO: optimize?
|
||||
wrapWordIndexingStorage(prepareWordIndexing(ctx, p, indexExpr))
|
||||
case (p: ConstantPointy, Some(v), 2, _) =>
|
||||
val w = env.get[VariableType]("word")
|
||||
wrapWordIndexingStorage(prepareWordIndexing(ctx, ConstantPointy(p.value + constIndex, None, if (constIndex.isProvablyZero) p.size else None, w, p.elementType, NoAlignment), v))
|
||||
case (p: ConstantPointy, Some(v), 1, _) =>
|
||||
storeToArrayAtUnknownIndex(v, p.value)
|
||||
//TODO: should there be a type check or a zeropage check?
|
||||
case (pointerVariable:VariablePointy, None, _, 0 | 1) =>
|
||||
case (pointerVariable@VariablePointy(varAddr, _, _, true), None, _, 0 | 1) =>
|
||||
register match {
|
||||
case MosRegister.A =>
|
||||
List(AssemblyLine.immediate(LDY, constIndex), AssemblyLine.indexedY(STA, pointerVariable.addr))
|
||||
@ -320,21 +347,51 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
ctx.log.error("Cannot store a word in an array", target.position)
|
||||
Nil
|
||||
}
|
||||
case (pointerVariable:VariablePointy, Some(_), _, 0 | 1) =>
|
||||
case (p@VariablePointy(varAddr, _, _, true), Some(_), _, 0 | 1) =>
|
||||
val calculatingIndex = compile(ctx, indexExpr, Some(b, RegisterVariable(MosRegister.Y, b)), NoBranching)
|
||||
register match {
|
||||
case MosRegister.A =>
|
||||
preserveRegisterIfNeeded(ctx, MosRegister.A, calculatingIndex) :+ AssemblyLine.indexedY(STA, pointerVariable.addr)
|
||||
preserveRegisterIfNeeded(ctx, MosRegister.A, calculatingIndex) :+ AssemblyLine.indexedY(STA, varAddr)
|
||||
case MosRegister.X =>
|
||||
preserveRegisterIfNeeded(ctx, MosRegister.X, calculatingIndex) ++ List(AssemblyLine.implied(TXA), AssemblyLine.indexedY(STA, pointerVariable.addr))
|
||||
preserveRegisterIfNeeded(ctx, MosRegister.X, calculatingIndex) ++ List(AssemblyLine.implied(TXA), AssemblyLine.indexedY(STA, varAddr))
|
||||
case MosRegister.Y =>
|
||||
AssemblyLine.implied(TYA) :: preserveRegisterIfNeeded(ctx, MosRegister.A, calculatingIndex) ++ List(
|
||||
AssemblyLine.indexedY(STA, pointerVariable.addr), AssemblyLine.implied(TAY)
|
||||
AssemblyLine.indexedY(STA, varAddr), AssemblyLine.implied(TAY)
|
||||
)
|
||||
case _ =>
|
||||
ctx.log.error("Cannot store a word in an array", target.position)
|
||||
Nil
|
||||
}
|
||||
case (StackVariablePointy(offset, _, _), None, _, 0 | 1) if ctx.options.flag(CompilationFlag.EmitEmulation65816Opcodes) =>
|
||||
register match {
|
||||
case MosRegister.A =>
|
||||
List(AssemblyLine.immediate(LDY, constIndex), AssemblyLine.indexedSY(STA, offset))
|
||||
case MosRegister.Y =>
|
||||
List(AssemblyLine.implied(TYA), AssemblyLine.immediate(LDY, constIndex), AssemblyLine.indexedSY(STA, offset), AssemblyLine.implied(TAY))
|
||||
case MosRegister.X =>
|
||||
List(AssemblyLine.immediate(LDY, constIndex), AssemblyLine.implied(TXA), AssemblyLine.indexedSY(STA, offset))
|
||||
case _ =>
|
||||
ctx.log.error("Cannot store a word in an array", target.position)
|
||||
Nil
|
||||
}
|
||||
case (p@StackVariablePointy(offset, _, _), Some(_), _, 0 | 1) if ctx.options.flag(CompilationFlag.EmitEmulation65816Opcodes) =>
|
||||
val calculatingIndex = compile(ctx, indexExpr, Some(b, RegisterVariable(MosRegister.Y, b)), NoBranching)
|
||||
register match {
|
||||
case MosRegister.A =>
|
||||
preserveRegisterIfNeeded(ctx, MosRegister.A, calculatingIndex) :+ AssemblyLine.indexedSY(STA, offset)
|
||||
case MosRegister.X =>
|
||||
preserveRegisterIfNeeded(ctx, MosRegister.X, calculatingIndex) ++ List(AssemblyLine.implied(TXA), AssemblyLine.indexedSY(STA, offset))
|
||||
case MosRegister.Y =>
|
||||
AssemblyLine.implied(TYA) :: preserveRegisterIfNeeded(ctx, MosRegister.A, calculatingIndex) ++ List(
|
||||
AssemblyLine.indexedSY(STA, offset), AssemblyLine.implied(TAY)
|
||||
)
|
||||
case _ =>
|
||||
ctx.log.error("Cannot store a word in an array", target.position)
|
||||
Nil
|
||||
}
|
||||
case (p: StackVariablePointy, _, _, _) =>
|
||||
// TODO: optimize?
|
||||
wrapWordIndexingStorage(prepareWordIndexing(ctx, p, indexExpr))
|
||||
case _ =>
|
||||
ctx.log.error("Invalid index for writing", indexExpr.position)
|
||||
Nil
|
||||
@ -716,6 +773,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
a.elementType, NoAlignment), v) ++ loadFromReg()
|
||||
case (a: VariablePointy, _, 2, _) =>
|
||||
prepareWordIndexing(ctx, a, indexExpr) ++ loadFromReg()
|
||||
case (p: VariablePointy, _, 0 | 1, _) if !p.zeropage =>
|
||||
prepareWordIndexing(ctx, p, indexExpr) ++ loadFromReg()
|
||||
case (p:VariablePointy, None, 0 | 1, _) =>
|
||||
register match {
|
||||
case MosRegister.A =>
|
||||
@ -735,6 +794,17 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
case MosRegister.Y =>
|
||||
calculatingIndex ++ List(AssemblyLine.indexedY(LDA, p.addr), AssemblyLine.implied(TAY))
|
||||
}
|
||||
case (p: StackVariablePointy, _, 0 | 1, _) if ctx.options.flag(CompilationFlag.EmitEmulation65816Opcodes) =>
|
||||
register match {
|
||||
case MosRegister.A =>
|
||||
List(AssemblyLine.immediate(LDY, constantIndex), AssemblyLine.indexedSY(LDA, p.offset))
|
||||
case MosRegister.Y =>
|
||||
List(AssemblyLine.immediate(LDY, constantIndex), AssemblyLine.indexedSY(LDA, p.offset), AssemblyLine.implied(TAY))
|
||||
case MosRegister.X =>
|
||||
List(AssemblyLine.immediate(LDY, constantIndex), AssemblyLine.indexedSY(LDA, p.offset), AssemblyLine.implied(TAX))
|
||||
}
|
||||
case (p: StackVariablePointy, _, _, _) =>
|
||||
prepareWordIndexing(ctx, p, indexExpr) ++ loadFromReg()
|
||||
case _ =>
|
||||
ctx.log.error("Invalid index for reading", indexExpr.position)
|
||||
Nil
|
||||
|
@ -1004,7 +1004,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
List(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
}
|
||||
}
|
||||
case VariablePointy(varAddr, _, _) =>
|
||||
case VariablePointy(varAddr, _, _, _) =>
|
||||
env.eval(i.index) match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
@ -1034,6 +1034,10 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
}
|
||||
}
|
||||
case _: StackVariablePointy =>
|
||||
compileToHL(ctx, VariableExpression(i.name).pos(i.position)) ++
|
||||
stashHLIfChanged(ctx, compileToBC(ctx, i.index)) ++
|
||||
List(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
}
|
||||
}
|
||||
|
||||
|
2
src/main/scala/millfork/env/Constant.scala
vendored
2
src/main/scala/millfork/env/Constant.scala
vendored
@ -215,6 +215,8 @@ case class MemoryAddressConstant(var thing: ThingInMemory) extends Constant {
|
||||
case _ => false
|
||||
}
|
||||
|
||||
override def fitsProvablyIntoByte: Boolean = thing.zeropage
|
||||
|
||||
override def requiredSize = 2
|
||||
|
||||
override def toString: String = thing.name
|
||||
|
25
src/main/scala/millfork/env/Environment.scala
vendored
25
src/main/scala/millfork/env/Environment.scala
vendored
@ -294,7 +294,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
}
|
||||
|
||||
def getArrayOrPointer(arrayName: String): Thing = {
|
||||
maybeGet[ThingInMemory](arrayName).
|
||||
maybeGet[StackVariable](arrayName).
|
||||
orElse(maybeGet[ThingInMemory](arrayName)).
|
||||
orElse(maybeGet[ThingInMemory](arrayName + ".array")).
|
||||
orElse(maybeGet[ConstantThing](arrayName)).
|
||||
getOrElse(log.fatal(s"`$arrayName` is not an array or a pointer"))
|
||||
@ -314,7 +315,11 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
case th:VariableInMemory if th.typ.isPointy=>
|
||||
val b = get[VariableType]("byte")
|
||||
val w = get[VariableType]("word")
|
||||
VariablePointy(th.toAddress, w, b)
|
||||
VariablePointy(th.toAddress, w, b, th.zeropage)
|
||||
case th:StackVariable if th.typ.isPointy =>
|
||||
val b = get[VariableType]("byte")
|
||||
val w = get[VariableType]("word")
|
||||
StackVariablePointy(th.baseOffset, w, b)
|
||||
case _ =>
|
||||
log.error(s"$name is not a valid pointer or array")
|
||||
val b = get[VariableType]("byte")
|
||||
@ -1148,17 +1153,6 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
val b = get[Type]("byte")
|
||||
val w = get[Type]("word")
|
||||
val typ = get[VariableType](stmt.typ)
|
||||
if (stmt.typ == "pointer" || stmt.typ == "farpointer") {
|
||||
// if (stmt.constant) {
|
||||
// log.error(s"Pointer `${stmt.name}` cannot be constant")
|
||||
// }
|
||||
stmt.address.flatMap(eval) match {
|
||||
case Some(NumericConstant(a, _)) =>
|
||||
if ((a & 0xff00) != 0)
|
||||
log.error(s"Pointer `${stmt.name}` cannot be located outside the zero page")
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
val alignment = stmt.alignment.getOrElse(defaultVariableAlignment(options, typ.size))
|
||||
if (stmt.constant) {
|
||||
if (stmt.stack) log.error(s"`$name` is a constant and cannot be on stack", position)
|
||||
@ -1191,7 +1185,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
} else {
|
||||
val (v, addr) = stmt.address.fold[(VariableInMemory, Constant)]({
|
||||
val alloc =
|
||||
if (isPointy || typ.name == "__reg$type") VariableAllocationMethod.Zeropage
|
||||
if (isPointy && stmt.bank.isEmpty) VariableAllocationMethod.Zeropage
|
||||
else if (typ.name == "__reg$type") VariableAllocationMethod.Zeropage
|
||||
else if (stmt.global) VariableAllocationMethod.Static
|
||||
else if (stmt.register) VariableAllocationMethod.Register
|
||||
else VariableAllocationMethod.Auto
|
||||
@ -1257,7 +1252,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
(".b1", 1, b),
|
||||
(".b2", 2, b),
|
||||
(".b3", 3, b))
|
||||
case sz if sz > 4 => List.tabulate(sz){ i => (".b" + i, i, b) }
|
||||
case sz if sz > 4 => (".lo", 0, b) :: (".loword", 0, w) :: List.tabulate(sz){ i => (".b" + i, i, b) }
|
||||
case _ => Nil
|
||||
}
|
||||
case _ => Nil
|
||||
|
6
src/main/scala/millfork/env/Pointy.scala
vendored
6
src/main/scala/millfork/env/Pointy.scala
vendored
@ -8,7 +8,11 @@ trait Pointy {
|
||||
def elementType: VariableType
|
||||
}
|
||||
|
||||
case class VariablePointy(addr: Constant, indexType: VariableType, elementType: VariableType) extends Pointy {
|
||||
case class StackVariablePointy(offset: Int, indexType: VariableType, elementType: VariableType) extends Pointy {
|
||||
override def name: Option[String] = None
|
||||
}
|
||||
|
||||
case class VariablePointy(addr: Constant, indexType: VariableType, elementType: VariableType, zeropage: Boolean) extends Pointy {
|
||||
override def name: Option[String] = None
|
||||
}
|
||||
|
||||
|
2
src/main/scala/millfork/env/Thing.scala
vendored
2
src/main/scala/millfork/env/Thing.scala
vendored
@ -175,7 +175,7 @@ object VariableAllocationMethod extends Enumeration {
|
||||
val Auto, Register, Static, Zeropage, None = Value
|
||||
}
|
||||
|
||||
case class StackVariable(name: String, typ: Type, baseOffset: Int) extends Variable {
|
||||
case class StackVariable(name: String, typ: Type, baseOffset: Int) extends Variable with IndexableThing {
|
||||
def sizeInBytes: Int = typ.size
|
||||
override def isVolatile: Boolean = false
|
||||
}
|
||||
|
42
src/test/scala/millfork/test/PointerSuite.scala
Normal file
42
src/test/scala/millfork/test/PointerSuite.scala
Normal file
@ -0,0 +1,42 @@
|
||||
package millfork.test
|
||||
|
||||
import millfork.Cpu
|
||||
import millfork.test.emu.EmuCrossPlatformBenchmarkRun
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
class PointerSuite extends FunSuite with Matchers {
|
||||
|
||||
test("Pointers outside zeropage") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Sixteen, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||
"""
|
||||
| pointer p @$c004
|
||||
| array output[2] @$c000
|
||||
| void main() {
|
||||
| p = output.addr
|
||||
| output[0] = 45
|
||||
| p[1] = p[0]
|
||||
| }
|
||||
""".stripMargin) { m =>
|
||||
m.readByte(0xc001) should equal(45)
|
||||
}
|
||||
}
|
||||
|
||||
test("Pointers on stack") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Sixteen, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||
"""
|
||||
| array output[2] @$c000
|
||||
| void main() {
|
||||
| stack pointer p
|
||||
| p = output.addr
|
||||
| output[0] = 45
|
||||
| p[1] = p[0]
|
||||
| }
|
||||
""".stripMargin) { m =>
|
||||
m.readByte(0xc001) should equal(45)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user