1
0
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:
Karol Stasiak 2019-04-15 00:27:34 +02:00
parent 194f79f907
commit 4cd1db0e0f
7 changed files with 146 additions and 29 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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