mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-13 21:37:08 +00:00
Assignments of large objects accessed through pointers
This commit is contained in:
parent
adc93aeb74
commit
8d4e7b9326
@ -321,11 +321,11 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
||||
case Some(NumericConstant(n, _)) if n >= 0 && (targetType.size * n) <= 127 =>
|
||||
x match {
|
||||
case _: PointerType =>
|
||||
DerefExpression(result, n.toInt, targetType)
|
||||
DerefExpression(result, targetType.size * n.toInt, targetType)
|
||||
case _ =>
|
||||
DerefExpression(
|
||||
("pointer." + targetType.name) <| result,
|
||||
n.toInt, targetType)
|
||||
targetType.size * n.toInt, targetType)
|
||||
}
|
||||
case _ =>
|
||||
val scaledIndex = arraySizeInBytes match {
|
||||
@ -485,9 +485,6 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
||||
targetType.size match {
|
||||
case 1 => IndexedExpression(name, optimizeExpr(index, Map())).pos(pos)
|
||||
case _ =>
|
||||
if (targetType.size != 2) {
|
||||
ctx.log.error("Cannot access a large array element directly", expr.position)
|
||||
}
|
||||
val arraySizeInBytes = pointy match {
|
||||
case p:ConstantPointy => p.sizeInBytes
|
||||
case _ => None
|
||||
|
@ -946,8 +946,18 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
AssemblyLine(LDA, am, addr)) ++
|
||||
expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
|
||||
case _ =>
|
||||
ctx.log.error("Cannot read a large object indirectly")
|
||||
Nil
|
||||
exprTypeAndVariable match {
|
||||
case Some((variableType, variable)) =>
|
||||
prepare ++ (0 until variableType.size).flatMap { i =>
|
||||
val load =
|
||||
if (i >= targetType.size) List(AssemblyLine.immediate(LDA, 0))
|
||||
else if (am == AbsoluteY) List(AssemblyLine.absolute(LDA, addr + offset + i))
|
||||
else if (i == 0) List(AssemblyLine.immediate(LDY, offset), AssemblyLine(LDA, am, addr))
|
||||
else List(AssemblyLine.implied(INY), AssemblyLine(LDA, am, addr))
|
||||
load ++ AssemblyLine.variable(ctx, STA, variable, i)
|
||||
}
|
||||
case None => Nil
|
||||
}
|
||||
}
|
||||
case SumExpression(params, decimal) =>
|
||||
assertAllArithmetic(ctx, params.map(_._2))
|
||||
@ -1852,6 +1862,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
def compileAssignment(ctx: CompilationContext, source: Expression, target: LhsExpression): List[AssemblyLine] = {
|
||||
val env = ctx.env
|
||||
val sourceType = AbstractExpressionCompiler.checkAssignmentTypeAndGetSourceType(ctx, source, target)
|
||||
val lhsType = AbstractExpressionCompiler.getExpressionType(ctx, target)
|
||||
val b = env.get[Type]("byte")
|
||||
val w = env.get[Type]("word")
|
||||
target match {
|
||||
@ -1869,33 +1880,16 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
val (prepare, addr, am) = getPhysicalPointerForDeref(ctx, inner)
|
||||
env.eval(source) match {
|
||||
case Some(constant) =>
|
||||
(targetType.size, am) match {
|
||||
case (1, AbsoluteY) =>
|
||||
prepare ++ List(
|
||||
AssemblyLine.immediate(LDA, constant),
|
||||
AssemblyLine.absolute(STA, addr + offset))
|
||||
case (1, _) =>
|
||||
prepare ++ List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.immediate(LDA, constant),
|
||||
AssemblyLine(STA, am, addr))
|
||||
case (2, AbsoluteY) =>
|
||||
prepare ++ List(
|
||||
AssemblyLine.immediate(LDA, constant.loByte),
|
||||
AssemblyLine.absolute(STA, addr + offset),
|
||||
AssemblyLine.immediate(LDA, constant.hiByte),
|
||||
AssemblyLine.absolute(STA, addr + offset + 1))
|
||||
case (2, _) =>
|
||||
prepare ++ List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.immediate(LDA, constant.loByte),
|
||||
AssemblyLine(STA, am, addr),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.immediate(LDA, constant.hiByte),
|
||||
AssemblyLine(STA, am, addr))
|
||||
am match {
|
||||
case AbsoluteY =>
|
||||
prepare ++ (0 until targetType.size).flatMap(i => List(
|
||||
AssemblyLine.immediate(LDA, constant.subbyte(i)),
|
||||
AssemblyLine.absolute(STA, addr + offset + i)))
|
||||
case _ =>
|
||||
ctx.log.error("Cannot assign to a large object indirectly", target.position)
|
||||
Nil
|
||||
prepare ++ (0 until targetType.size).flatMap(i => List(
|
||||
if (i == 0) AssemblyLine.immediate(LDY, offset) else AssemblyLine.implied(INY),
|
||||
AssemblyLine.immediate(LDA, constant.subbyte(i)),
|
||||
AssemblyLine(STA, am, addr)))
|
||||
}
|
||||
case None =>
|
||||
source match {
|
||||
@ -1911,24 +1905,117 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
AssemblyLine.variable(ctx, LDA, variable) ++ List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine(STA, am, addr))
|
||||
case (2, AbsoluteY) =>
|
||||
prepare ++
|
||||
AssemblyLine.variable(ctx, LDA, variable) ++ List(
|
||||
AssemblyLine.absolute(STA, addr + offset)) ++
|
||||
AssemblyLine.variable(ctx, LDA, variable, 1) ++ List(
|
||||
AssemblyLine.absolute(STA, addr + offset + 1))
|
||||
case (2, _) =>
|
||||
prepare ++
|
||||
AssemblyLine.variable(ctx, LDA, variable) ++ List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine(STA, am, addr)) ++
|
||||
AssemblyLine.variable(ctx, LDA, variable, 1) ++ List(
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine(STA, am, addr))
|
||||
case (_, AbsoluteY) =>
|
||||
prepare ++ (0 until targetType.size).flatMap { i =>
|
||||
val load = if (i >= sourceType.size) List(AssemblyLine.immediate(LDA, 0)) else AssemblyLine.variable(ctx, LDA, variable, i)
|
||||
load ++ List(
|
||||
AssemblyLine.absolute(STA, addr + offset + i))
|
||||
}
|
||||
case (_, _) =>
|
||||
prepare ++ (0 until targetType.size).flatMap { i =>
|
||||
val load = if (i >= sourceType.size) List(AssemblyLine.immediate(LDA, 0)) else AssemblyLine.variable(ctx, LDA, variable, i)
|
||||
load ++ List(
|
||||
if (i == 0) AssemblyLine.immediate(LDY, offset) else AssemblyLine.implied(INY),
|
||||
AssemblyLine(STA, am, addr))
|
||||
}
|
||||
case _ =>
|
||||
ctx.log.error("Cannot assign to a large object indirectly", target.position)
|
||||
Nil
|
||||
}
|
||||
case DerefExpression(innerSource, sourceOffset, _) =>
|
||||
val (prepareSource, addrSource, amSource) = getPhysicalPointerForDeref(ctx, innerSource)
|
||||
(am, amSource) match {
|
||||
case (AbsoluteY, AbsoluteY) =>
|
||||
prepare ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
|
||||
if (i >= sourceType.size) List(
|
||||
AssemblyLine.immediate(LDA, 0),
|
||||
AssemblyLine.absolute(STA, addr + offset + i))
|
||||
else List(
|
||||
AssemblyLine.absolute(LDA, addrSource + sourceOffset + i),
|
||||
AssemblyLine.absolute(STA, addr + offset + i))
|
||||
}
|
||||
case (AbsoluteY, _) =>
|
||||
prepare ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
|
||||
if (i >= sourceType.size) List(
|
||||
AssemblyLine.immediate(LDA, 0),
|
||||
AssemblyLine.absolute(STA, addr + offset + i))
|
||||
else List(
|
||||
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
|
||||
AssemblyLine(LDA, amSource, addrSource),
|
||||
AssemblyLine.absolute(STA, addr + offset + i))
|
||||
}
|
||||
case (_, AbsoluteY) =>
|
||||
prepare ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
|
||||
if (i >= sourceType.size) List(
|
||||
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
|
||||
AssemblyLine.immediate(LDA, 0),
|
||||
AssemblyLine(STA, am, addr))
|
||||
else List(
|
||||
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
|
||||
AssemblyLine.absolute(LDA, addrSource + sourceOffset + i),
|
||||
AssemblyLine(STA, am, addr))
|
||||
}
|
||||
case (IndexedY, IndexedY) =>
|
||||
val reg = env.get[ThingInMemory]("__reg.loword")
|
||||
(addr, addrSource) match {
|
||||
case (MemoryAddressConstant(th1: Thing), MemoryAddressConstant(th2: Thing))
|
||||
if (th1.name == "__reg.loword" || th1.name == "__reg") && (th2.name == "__reg.loword" || th2.name == "__reg") =>
|
||||
(MosExpressionCompiler.changesZpreg(prepareSource, 2) || MosExpressionCompiler.changesZpreg(prepareSource, 3),
|
||||
MosExpressionCompiler.changesZpreg(prepareSource, 2) || MosExpressionCompiler.changesZpreg(prepareSource, 3)) match {
|
||||
case (_, false) =>
|
||||
prepare ++ List(
|
||||
AssemblyLine.zeropage(LDA, reg),
|
||||
AssemblyLine.zeropage(STA, reg, 2),
|
||||
AssemblyLine.zeropage(LDA, reg, 1),
|
||||
AssemblyLine.zeropage(STA, reg, 3)) ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
|
||||
if (i >= sourceType.size) List(
|
||||
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
|
||||
AssemblyLine.immediate(LDA, 0),
|
||||
AssemblyLine.indexedY(STA, reg, 2))
|
||||
else List(
|
||||
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
|
||||
AssemblyLine.indexedY(LDA, reg),
|
||||
AssemblyLine.indexedY(STA, reg, 2))
|
||||
}
|
||||
case (false, true) =>
|
||||
prepareSource ++ List(
|
||||
AssemblyLine.zeropage(LDA, reg),
|
||||
AssemblyLine.zeropage(STA, reg, 2),
|
||||
AssemblyLine.zeropage(LDA, reg, 1),
|
||||
AssemblyLine.zeropage(STA, reg, 3)) ++ prepare ++ (0 until targetType.size).flatMap { i =>
|
||||
if (i >= sourceType.size) List(
|
||||
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
|
||||
AssemblyLine.immediate(LDA, 0),
|
||||
AssemblyLine.indexedY(STA, reg))
|
||||
else List(
|
||||
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
|
||||
AssemblyLine.indexedY(LDA, reg, 2),
|
||||
AssemblyLine.indexedY(STA, reg))
|
||||
}
|
||||
case _ =>
|
||||
prepare ++ List(
|
||||
AssemblyLine.zeropage(LDA, reg, 1),
|
||||
AssemblyLine.implied(PHA),
|
||||
AssemblyLine.zeropage(LDA, reg),
|
||||
AssemblyLine.implied(PHA)) ++ fixTsx(fixTsx(prepareSource)) ++ List(
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.zeropage(STA, reg, 2),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.zeropage(STA, reg, 3)) ++ (0 until targetType.size).flatMap { i =>
|
||||
if (i >= sourceType.size) List(
|
||||
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
|
||||
AssemblyLine.immediate(LDA, 0),
|
||||
AssemblyLine.indexedY(STA, reg, 2))
|
||||
else List(
|
||||
if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY),
|
||||
AssemblyLine.indexedY(LDA, reg),
|
||||
AssemblyLine.indexedY(STA, reg, 2))
|
||||
}
|
||||
}
|
||||
case _ => ???
|
||||
}
|
||||
case _ => ???
|
||||
}
|
||||
case _ =>
|
||||
(targetType.size, am) match {
|
||||
case (1, _) =>
|
||||
|
@ -46,11 +46,18 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
def compileToHL(ctx: CompilationContext, expression: Expression): List[ZLine] = compile(ctx, expression, ZExpressionTarget.HL)
|
||||
|
||||
def compileDerefPointer(ctx: CompilationContext, expression: DerefExpression): List[ZLine] = {
|
||||
compileToHL(ctx, expression.inner) ++ (expression.offset match {
|
||||
case 0 => Nil
|
||||
case i if i < 5 => List.fill(i)(ZLine.register(INC_16, ZRegister.HL)) // TODO: a better threshold
|
||||
case _ => List(ZLine.ldImm8(ZRegister.C, expression.offset), ZLine.ldImm8(ZRegister.B, 0), ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
})
|
||||
import ZRegister._
|
||||
val innerPart = compileToHL(ctx, expression.inner)
|
||||
innerPart match {
|
||||
case List(ZLine0(LD_16, TwoRegisters(HL, IMM_16), c)) =>
|
||||
List(ZLine(LD_16, TwoRegisters(HL, IMM_16), c + expression.offset))
|
||||
case _ =>
|
||||
innerPart ++ (expression.offset match {
|
||||
case 0 => Nil
|
||||
case i if i < 5 => List.fill(i)(ZLine.register(INC_16, ZRegister.HL)) // TODO: a better threshold
|
||||
case _ => List(ZLine.ldImm8(ZRegister.C, expression.offset), ZLine.ldImm8(ZRegister.B, 0), ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
def compileToEHL(ctx: CompilationContext, expression: Expression): List[ZLine] = compile(ctx, expression, ZExpressionTarget.EHL)
|
||||
@ -569,6 +576,13 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.E, 0),
|
||||
ZLine.ldImm8(ZRegister.D, 0)) // TODO
|
||||
case ZExpressionTarget.EHL =>
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.E, 0)) // TODO
|
||||
case ZExpressionTarget.HL =>
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
@ -587,6 +601,57 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
ZLine.ld8(ZRegister.D, ZRegister.MEM_HL))
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
}
|
||||
case 3 => target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.EHL | ZExpressionTarget.DEHL =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.D, ZRegister.MEM_HL),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.MEM_HL),
|
||||
ZLine.ldImm8(ZRegister.H, 0),
|
||||
ZLine.implied(EX_DE_HL)) // TODO
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.C, ZRegister.MEM_HL),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.B, ZRegister.MEM_HL),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.C),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.B),
|
||||
ZLine.ldImm8(ZRegister.D, 0)) // TODO
|
||||
}
|
||||
}
|
||||
case 4 => target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.DEHL =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.D, ZRegister.MEM_HL),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.implied(EX_DE_HL)) // TODO
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.C, ZRegister.MEM_HL),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.B, ZRegister.MEM_HL),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.D, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.C),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.B)) // TODO
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
ctx.log.error("Cannot read a large object indirectly")
|
||||
Nil
|
||||
@ -1313,7 +1378,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
|
||||
def signExtend(ctx: CompilationContext, targetAddr: Constant, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
||||
if (bytes == 0) return Nil
|
||||
if (bytes <= 0) return Nil
|
||||
val prepareA = if (signedSource) {
|
||||
signExtendHighestByte(ctx, hiRegister)
|
||||
} else {
|
||||
@ -1323,7 +1388,10 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
prepareA ++ fillUpperBytes
|
||||
}
|
||||
|
||||
private def signExtendHighestByte(ctx: CompilationContext, hiRegister: ZRegister.Value): List[ZLine] = {
|
||||
private def signExtendHighestByte(ctx: CompilationContext, hiRegister: ZRegister.Value, signedSource: Boolean = true): List[ZLine] = {
|
||||
if (!signedSource) {
|
||||
return List(ZLine.ldImm8(ZRegister.A, 0))
|
||||
}
|
||||
val prefix = if (hiRegister == ZRegister.A) Nil else List(ZLine.ld8(ZRegister.A, hiRegister))
|
||||
val label = ctx.nextLabel("sx")
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
@ -1346,7 +1414,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
|
||||
def signExtendViaIX(ctx: CompilationContext, targetOffset: Int, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
||||
if (bytes == 0) return Nil
|
||||
if (bytes <= 0) return Nil
|
||||
val prepareA = if (signedSource) {
|
||||
signExtendHighestByte(ctx, hiRegister)
|
||||
} else {
|
||||
@ -1357,7 +1425,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
|
||||
def signExtendViaIY(ctx: CompilationContext, targetOffset: Int, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
||||
if (bytes == 0) return Nil
|
||||
if (bytes <= 0) return Nil
|
||||
val prepareA = if (signedSource) {
|
||||
signExtendHighestByte(ctx, hiRegister)
|
||||
} else {
|
||||
@ -1368,7 +1436,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
|
||||
def signExtendViaHL(ctx: CompilationContext, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
||||
if (bytes == 0) return Nil
|
||||
if (bytes <= 0) return Nil
|
||||
val prepareA = if (signedSource) {
|
||||
signExtendHighestByte(ctx, hiRegister)
|
||||
} else {
|
||||
@ -1415,16 +1483,6 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ld8(ZRegister.A, ZRegister.L), ZLine.ldAbs8(targetAddr, ZRegister.A))
|
||||
case 2 =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)){
|
||||
List(ZLine.ldAbs16(targetAddr, ZRegister.HL))
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
ZLine.ldAbs8(targetAddr, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.ldAbs8(targetAddr + 1, ZRegister.A))
|
||||
}
|
||||
case n =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)){
|
||||
ZLine.ldAbs16(targetAddr, ZRegister.HL) :: signExtend(ctx, targetAddr + 2, ZRegister.H, n - 2, signedSource)
|
||||
@ -1438,16 +1496,118 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def storeEHL(ctx: CompilationContext, targetAddr: Constant, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ld8(ZRegister.A, ZRegister.L), ZLine.ldAbs8(targetAddr, ZRegister.A))
|
||||
case 2 =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)){
|
||||
List(ZLine.ldAbs16(targetAddr, ZRegister.HL))
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
ZLine.ldAbs8(targetAddr, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.ldAbs8(targetAddr + 1, ZRegister.A))
|
||||
}
|
||||
case n =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)){
|
||||
List(
|
||||
ZLine.ldAbs16(targetAddr, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.E),
|
||||
ZLine.ldAbs8(targetAddr + 2, ZRegister.A)) ++ signExtend(ctx, targetAddr + 3, ZRegister.E, n - 3, signedSource)
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
ZLine.ldAbs8(targetAddr, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.ldAbs8(targetAddr + 1, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.E),
|
||||
ZLine.ldAbs8(targetAddr + 2, ZRegister.A)) ++ signExtend(ctx, targetAddr + 3, ZRegister.E, n - 3, signedSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def storeDEHL(ctx: CompilationContext, targetAddr: Constant, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ld8(ZRegister.A, ZRegister.L), ZLine.ldAbs8(targetAddr, ZRegister.A))
|
||||
case 2 =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)){
|
||||
List(ZLine.ldAbs16(targetAddr, ZRegister.HL))
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
ZLine.ldAbs8(targetAddr, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.ldAbs8(targetAddr + 1, ZRegister.A))
|
||||
}
|
||||
case 3 =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(
|
||||
ZLine.ldAbs16(targetAddr, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.E),
|
||||
ZLine.ldAbs8(targetAddr + 2, ZRegister.A))
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
ZLine.ldAbs8(targetAddr, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.ldAbs8(targetAddr + 1, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.E),
|
||||
ZLine.ldAbs8(targetAddr + 2, ZRegister.A))
|
||||
}
|
||||
case n =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)){
|
||||
List(
|
||||
ZLine.ldAbs16(targetAddr, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.E),
|
||||
ZLine.ldAbs8(targetAddr + 2, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.D),
|
||||
ZLine.ldAbs8(targetAddr + 3, ZRegister.A)) ++ signExtend(ctx, targetAddr + 4, ZRegister.D, n - 4, signedSource)
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
ZLine.ldAbs8(targetAddr, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.ldAbs8(targetAddr + 1, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.E),
|
||||
ZLine.ldAbs8(targetAddr + 2, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.D),
|
||||
ZLine.ldAbs8(targetAddr + 3, ZRegister.A)) ++ signExtend(ctx, targetAddr + 4, ZRegister.D, n - 4, signedSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def storeHLViaIX(ctx: CompilationContext, offset: Int, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
// TODO: LD (nnnn),HL compatibility?
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ldViaIx(offset, ZRegister.L))
|
||||
case 2 => List(ZLine.ldViaIx(offset, ZRegister.L), ZLine.ldViaIx(offset + 1, ZRegister.H))
|
||||
case n => List(ZLine.ldViaIx(offset, ZRegister.L), ZLine.ldViaIx(offset + 1, ZRegister.H)) ++ signExtendViaIX(ctx, offset + 2, ZRegister.H, n - 2, signedSource)
|
||||
}
|
||||
}
|
||||
|
||||
def storeEHLViaIX(ctx: CompilationContext, offset: Int, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
import ZRegister._
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ldViaIx(offset, L))
|
||||
case 2 => List(ZLine.ldViaIx(offset, L), ZLine.ldViaIx(offset + 1, H))
|
||||
case n => List(ZLine.ldViaIx(offset, L), ZLine.ldViaIx(offset + 1, H), ZLine.ldViaIx(offset + 2, E)) ++ signExtendViaIX(ctx, offset + 3, E, n - 3, signedSource)
|
||||
}
|
||||
}
|
||||
|
||||
def storeDEHLViaIX(ctx: CompilationContext, offset: Int, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
import ZRegister._
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ldViaIx(offset, L))
|
||||
case 2 => List(ZLine.ldViaIx(offset, L), ZLine.ldViaIx(offset + 1, H))
|
||||
case 3 => List(ZLine.ldViaIx(offset, L), ZLine.ldViaIx(offset + 1, H), ZLine.ldViaIx(offset + 2, E))
|
||||
case n => List(ZLine.ldViaIx(offset, L), ZLine.ldViaIx(offset + 1, H), ZLine.ldViaIx(offset + 2, E), ZLine.ldViaIx(offset + 3, D)) ++ signExtendViaIX(ctx, offset + 4, D, n - 4, signedSource)
|
||||
}
|
||||
}
|
||||
|
||||
def storeHLViaIY(ctx: CompilationContext, offset: Int, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
// TODO: LD (nnnn),HL compatibility?
|
||||
targetSize match {
|
||||
@ -1458,6 +1618,27 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def storeEHLViaIY(ctx: CompilationContext, offset: Int, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
import ZRegister._
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ldViaIy(offset, L))
|
||||
case 2 => List(ZLine.ldViaIy(offset, L), ZLine.ldViaIy(offset + 1, H))
|
||||
case n => List(ZLine.ldViaIy(offset, L), ZLine.ldViaIy(offset + 1, H), ZLine.ldViaIy(offset + 2, E)) ++ signExtendViaIY(ctx, offset + 3, E, n - 3, signedSource)
|
||||
}
|
||||
}
|
||||
|
||||
def storeDEHLViaIY(ctx: CompilationContext, offset: Int, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
import ZRegister._
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ldViaIy(offset, L))
|
||||
case 2 => List(ZLine.ldViaIy(offset, L), ZLine.ldViaIy(offset + 1, H))
|
||||
case 3 => List(ZLine.ldViaIy(offset, L), ZLine.ldViaIy(offset + 1, H), ZLine.ldViaIy(offset + 2, E))
|
||||
case n => List(ZLine.ldViaIy(offset, L), ZLine.ldViaIy(offset + 1, H), ZLine.ldViaIy(offset + 2, E), ZLine.ldViaIy(offset + 3, D)) ++ signExtendViaIY(ctx, offset + 4, D, n - 4, signedSource)
|
||||
}
|
||||
}
|
||||
|
||||
def storeA(ctx: CompilationContext, target: LhsExpression, signedSource: Boolean): List[ZLine] = {
|
||||
val env = ctx.env
|
||||
target match {
|
||||
@ -1509,43 +1690,52 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
|
||||
def storeHL(ctx: CompilationContext, target: LhsExpression, signedSource: Boolean): List[ZLine] = {
|
||||
import ZRegister._
|
||||
val env = ctx.env
|
||||
target match {
|
||||
case VariableExpression(vname) =>
|
||||
env.get[Variable](vname) match {
|
||||
case v: VariableInMemory => storeHL(ctx, v.toAddress, v.typ.size, signedSource)
|
||||
case v: StackVariable =>
|
||||
import ZRegister._
|
||||
if (ctx.options.flag(CompilationFlag.UseIxForStack)){
|
||||
storeHLViaIX(ctx, v.baseOffset, v.typ.size, signedSource)
|
||||
} else if (ctx.options.flag(CompilationFlag.UseIyForStack)){
|
||||
storeHLViaIY(ctx, v.baseOffset, v.typ.size, signedSource)
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitIntel8085Opcodes) && ctx.options.flag(CompilationFlag.EmitIllegals)) {
|
||||
List(ZLine.register(PUSH, DE),
|
||||
ZLine.imm8(LD_DESP, v.baseOffset + 2),
|
||||
ZLine.implied(SHLX),
|
||||
ZLine.register(POP, DE))
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(
|
||||
ZLine.register(PUSH, DE),
|
||||
ZLine.implied(EX_DE_HL)) ++
|
||||
fixTsx(ctx, calculateStackAddressToHL(ctx, v)) ++
|
||||
List(
|
||||
ZLine.ld8(MEM_HL, E),
|
||||
ZLine.register(INC_16, HL),
|
||||
ZLine.ld8(MEM_HL, D),
|
||||
if (v.typ.size > 2) {
|
||||
List(ZLine.register(PUSH, DE),
|
||||
ZLine.imm8(LD_DESP, v.baseOffset + 2),
|
||||
ZLine.implied(SHLX),
|
||||
ZLine.register(INC_16, DE)
|
||||
) ++ signExtendHighestByte(ctx, H) ++ List.fill(v.typ.size - 2)(List(
|
||||
ZLine.register(INC_16, DE),
|
||||
ZLine.ld8(MEM_DE, A)
|
||||
)).flatten ++ List(
|
||||
ZLine.register(POP, DE))
|
||||
} else {
|
||||
List(ZLine.register(PUSH, DE),
|
||||
ZLine.imm8(LD_DESP, v.baseOffset + 2),
|
||||
ZLine.implied(SHLX),
|
||||
ZLine.register(POP, DE))
|
||||
}
|
||||
} else {
|
||||
List(
|
||||
ZLine.register(PUSH, DE),
|
||||
ZLine.ld8(D, H),
|
||||
ZLine.ld8(E, L)) ++
|
||||
List(ZLine.register(PUSH, DE)) ++
|
||||
(if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes))
|
||||
List(ZLine.implied(EX_DE_HL))
|
||||
else List(ZLine.ld8(D, H), ZLine.ld8(E, L))) ++
|
||||
fixTsx(ctx, calculateStackAddressToHL(ctx, v)) ++
|
||||
List(
|
||||
ZLine.ld8(MEM_HL, E),
|
||||
ZLine.register(INC_16, HL),
|
||||
ZLine.ld8(MEM_HL, D),
|
||||
ZLine.register(POP, DE))
|
||||
ZLine.ld8(MEM_HL, D)) ++
|
||||
(if (v.typ.size > 2)
|
||||
signExtendHighestByte(ctx, D) ++
|
||||
List.fill(v.typ.size - 2)(List(
|
||||
ZLine.register(INC_16, HL),
|
||||
ZLine.ld8(MEM_HL, A)
|
||||
)).flatten
|
||||
else Nil) ++
|
||||
List(ZLine.register(POP, DE))
|
||||
}
|
||||
}
|
||||
case IndexedExpression(pointyName, indexExpr) =>
|
||||
@ -1561,16 +1751,31 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
(ZLine.ld8(ZRegister.A, ZRegister.H) :: storeA(ctx, hi, signedSource))
|
||||
case e:DerefExpression =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8085Opcodes) && ctx.options.flag(CompilationFlag.EmitIllegals)) {
|
||||
List(ZLine.register(PUSH, ZRegister.HL)) ++ compileDerefPointer(ctx, e) ++ List(
|
||||
List(ZLine.register(PUSH, ZRegister.HL)) ++ fixTsx(ctx, compileDerefPointer(ctx, e)) ++ List(
|
||||
ZLine.register(POP, ZRegister.DE),
|
||||
ZLine.implied(EX_DE_HL),
|
||||
ZLine.implied(SHLX))
|
||||
ZLine.implied(SHLX)) ++
|
||||
(if (e.targetType.size > 2)
|
||||
signExtendHighestByte(ctx, H) ++
|
||||
List(ZLine.register(INC_16, DE)) ++
|
||||
List.fill(e.targetType.size - 2)(List(
|
||||
ZLine.register(INC_16, DE),
|
||||
ZLine.ld8(MEM_DE, A)
|
||||
)).flatten
|
||||
else Nil)
|
||||
} else {
|
||||
List(ZLine.register(PUSH, ZRegister.HL)) ++ compileDerefPointer(ctx, e) ++ List(
|
||||
List(ZLine.register(PUSH, ZRegister.HL)) ++ fixTsx(ctx, compileDerefPointer(ctx, e)) ++ List(
|
||||
ZLine.register(POP, ZRegister.BC),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.C),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.B))
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.B)) ++
|
||||
(if (e.targetType.size > 2)
|
||||
signExtendHighestByte(ctx, B) ++
|
||||
List.fill(e.targetType.size - 2)(List(
|
||||
ZLine.register(INC_16, HL),
|
||||
ZLine.ld8(MEM_HL, A)
|
||||
)).flatten
|
||||
else Nil)
|
||||
}
|
||||
case _: SeparateBytesExpression =>
|
||||
ctx.log.error("Invalid `:`", target.position)
|
||||
@ -1578,45 +1783,133 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def storeLarge(ctx: CompilationContext, target: LhsExpression, source: Expression): List[ZLine] = {
|
||||
def storeEHL(ctx: CompilationContext, target: LhsExpression, signedSource: Boolean): List[ZLine] = {
|
||||
import ZRegister._
|
||||
val env = ctx.env
|
||||
target match {
|
||||
val targetType = AbstractExpressionCompiler.getExpressionType(ctx, target)
|
||||
val preparePointer = target match {
|
||||
case VariableExpression(vname) =>
|
||||
env.get[Variable](vname) match {
|
||||
case v: Variable =>
|
||||
import ZRegister._
|
||||
val size = v.typ.size
|
||||
val reads = compileByteReads(ctx, source, size, ZExpressionTarget.HL)
|
||||
val stores = compileByteStores(ctx, target, size, includeStep = true)
|
||||
if (stores.exists(_.exists(_.changesRegister(HL)))) {
|
||||
if (reads.tail.exists(_.exists(_.changesRegister(HL)))) {
|
||||
// most likely stack-to-stack copy
|
||||
// use DE as the secondary pointer
|
||||
val fixedReads = reads.head.init ++ List(ZLine.ld8(E, L), ZLine.ld8(D, H), ZLine.ld8(A, MEM_DE)) :: reads.tail.map(_.map {
|
||||
case l@ZLine0(LD, TwoRegisters(A, MEM_HL), _) => l.copy(registers = TwoRegisters(A, MEM_DE))
|
||||
case l@ZLine0(INC_16, OneRegister(HL), _) => l.copy(registers = OneRegister(DE))
|
||||
case l@ZLine0(DEC_16, OneRegister(HL), _) => l.copy(registers = OneRegister(DE))
|
||||
case l => l
|
||||
})
|
||||
fixedReads.zip(stores).flatMap(t => t._1 ++ t._2)
|
||||
} else {
|
||||
val fixedReads = reads.head ++ List(ZLine.ld8(B, H)) :: reads.tail.map(_.map {
|
||||
case l@ZLine0(LD, TwoRegisters(reg, H), _) => l.copy(registers = TwoRegisters(reg, B))
|
||||
case l@ZLine0(LD, TwoRegisters(reg, L), _) => l.copy(registers = TwoRegisters(reg, C))
|
||||
case l => l
|
||||
})
|
||||
val fixedStores = stores.map(_.map {
|
||||
case l@ZLine0(LD, TwoRegisters(H, reg), _) => l.copy(registers = TwoRegisters(B, reg))
|
||||
case l@ZLine0(LD, TwoRegisters(L, reg), _) => l.copy(registers = TwoRegisters(C, reg))
|
||||
case l => l
|
||||
})
|
||||
fixedReads.zip(fixedStores).flatMap(t => t._1 ++ t._2)
|
||||
}
|
||||
case v: VariableInMemory => return storeEHL(ctx, v.toAddress, v.typ.size, signedSource)
|
||||
case v: StackVariable =>
|
||||
if (ctx.options.flag(CompilationFlag.UseIxForStack)){
|
||||
return storeEHLViaIX(ctx, v.baseOffset, v.typ.size, signedSource)
|
||||
} else if (ctx.options.flag(CompilationFlag.UseIyForStack)){
|
||||
return storeEHLViaIY(ctx, v.baseOffset, v.typ.size, signedSource)
|
||||
} else {
|
||||
reads.zip(stores).flatMap(t => t._1 ++ t._2)
|
||||
calculateStackAddressToHL(ctx, v.baseOffset)
|
||||
}
|
||||
}
|
||||
case _ => ???
|
||||
case e: DerefExpression => compileDerefPointer(ctx, e)
|
||||
}
|
||||
List(ZLine.register(PUSH, HL)) ++
|
||||
stashDEIfChanged(ctx, fixTsx(ctx, preparePointer)) ++
|
||||
List(
|
||||
ZLine.register(POP, BC),
|
||||
ZLine.ld8(MEM_HL, C),
|
||||
ZLine.register(INC_16, HL),
|
||||
ZLine.ld8(MEM_HL, B),
|
||||
ZLine.register(INC_16, HL),
|
||||
ZLine.ld8(MEM_HL, E)) ++ (
|
||||
if (targetType.size > 3) signExtendHighestByte(ctx, E, signedSource) ++ List.fill(targetType.size - 3)(List(ZLine.register(INC_16, HL), ZLine.ld8(MEM_HL, A))).flatten
|
||||
else Nil)
|
||||
}
|
||||
|
||||
def storeDEHL(ctx: CompilationContext, target: LhsExpression, signedSource: Boolean): List[ZLine] = {
|
||||
import ZRegister._
|
||||
val env = ctx.env
|
||||
val targetType = AbstractExpressionCompiler.getExpressionType(ctx, target)
|
||||
val preparePointer = target match {
|
||||
case VariableExpression(vname) =>
|
||||
env.get[Variable](vname) match {
|
||||
case v: VariableInMemory => return storeDEHL(ctx, v.toAddress, v.typ.size, signedSource)
|
||||
case v: StackVariable =>
|
||||
if (ctx.options.flag(CompilationFlag.UseIxForStack)){
|
||||
return storeDEHLViaIX(ctx, v.baseOffset, v.typ.size, signedSource)
|
||||
} else if (ctx.options.flag(CompilationFlag.UseIyForStack)){
|
||||
return storeDEHLViaIY(ctx, v.baseOffset, v.typ.size, signedSource)
|
||||
} else {
|
||||
calculateStackAddressToHL(ctx, v.baseOffset)
|
||||
}
|
||||
}
|
||||
case e: DerefExpression => compileDerefPointer(ctx, e)
|
||||
}
|
||||
List(ZLine.register(PUSH, HL)) ++
|
||||
stashDEIfChanged(ctx, fixTsx(ctx, preparePointer)) ++
|
||||
List(
|
||||
ZLine.register(POP, BC),
|
||||
ZLine.ld8(MEM_HL, C),
|
||||
ZLine.register(INC_16, HL),
|
||||
ZLine.ld8(MEM_HL, B),
|
||||
ZLine.register(INC_16, HL),
|
||||
ZLine.ld8(MEM_HL, E),
|
||||
ZLine.register(INC_16, HL),
|
||||
ZLine.ld8(MEM_HL, D)) ++ (
|
||||
if (targetType.size > 4) signExtendHighestByte(ctx, D, signedSource) ++ List.fill(targetType.size - 4)(List(ZLine.register(INC_16, HL), ZLine.ld8(MEM_HL, A))).flatten
|
||||
else Nil)
|
||||
}
|
||||
|
||||
def storeLarge(ctx: CompilationContext, target: LhsExpression, source: Expression): List[ZLine] = {
|
||||
import ZRegister._
|
||||
def fuse(firstRead: List[ZLine], firstStore: List[ZLine]): List[ZLine] = {
|
||||
// TODO: ?
|
||||
(firstRead.last, firstStore.last) match {
|
||||
case (
|
||||
ZLine0(LD, TwoRegisters(A, _) | TwoRegistersOffset(A, _, _), _),
|
||||
ZLine0(LD, TwoRegisters(_, A) | TwoRegistersOffset(_, A, _), _)) =>
|
||||
firstRead.init ++ firstStore.init ++ List(firstRead.last, firstStore.last)
|
||||
}
|
||||
}
|
||||
val env = ctx.env
|
||||
val targetType = AbstractExpressionCompiler.getExpressionType(ctx, target)
|
||||
env.eval(source) match {
|
||||
case Some(constant) =>
|
||||
val stores = compileByteStores(ctx, target, targetType.size, includeStep = true)
|
||||
stores.zipWithIndex.flatMap {
|
||||
case (store, ix) =>
|
||||
store.init ++ List(ZLine.ldImm8(A, constant.subbyte(ix)), store.last)
|
||||
}
|
||||
case None =>
|
||||
val size = targetType.size
|
||||
val reads = compileByteReads(ctx, source, size, ZExpressionTarget.HL)
|
||||
val stores = compileByteStores(ctx, target, size, includeStep = true)
|
||||
if (stores.exists(_.exists(_.changesRegister(HL)))) {
|
||||
if (reads.tail.exists(_.exists(_.changesRegister(HL)))) {
|
||||
// most likely stack-to-stack copy
|
||||
// use DE as the secondary pointer
|
||||
val fixedReads = reads.head.init ++ List(ZLine.ld8(E, L), ZLine.ld8(D, H), ZLine.ld8(A, MEM_DE)) :: reads.tail.map(_.map {
|
||||
case l@ZLine0(LD, TwoRegisters(A, MEM_HL), _) => l.copy(registers = TwoRegisters(A, MEM_DE))
|
||||
case l@ZLine0(INC_16, OneRegister(HL), _) => l.copy(registers = OneRegister(DE))
|
||||
case l@ZLine0(DEC_16, OneRegister(HL), _) => l.copy(registers = OneRegister(DE))
|
||||
case l => l
|
||||
})
|
||||
val fixedStores = (stashDEIfChanged(ctx, stores.head.init) :+ stores.head.last) :: stores.tail
|
||||
fuse(fixedReads.head, fixedStores.head) ++ fixedReads.tail.zip(fixedStores.tail).flatMap(t => t._1 ++ t._2)
|
||||
} else if (reads.tail.exists(_.exists(_.readsRegister(H)))) { // TODO: when?
|
||||
ctx.log.debug("Weird branch of storeLarge reached!")
|
||||
if (ctx.log.traceEnabled) {
|
||||
ctx.log.trace("reads:")
|
||||
reads.foreach(l => ctx.log.trace(_, None))
|
||||
ctx.log.trace("stores:")
|
||||
stores.foreach(l => ctx.log.trace(_, None))
|
||||
}
|
||||
val fixedReads = (reads.head.init ++ List(ZLine.ld8(B, H), reads.head.last)) :: reads.tail.map(_.map {
|
||||
case l@ZLine0(LD, TwoRegisters(reg, H), _) => l.copy(registers = TwoRegisters(reg, B))
|
||||
case l@ZLine0(LD, TwoRegisters(reg, L), _) => l.copy(registers = TwoRegisters(reg, C))
|
||||
case l => l
|
||||
})
|
||||
val fixedStores = stores.map(_.map {
|
||||
case l@ZLine0(LD, TwoRegisters(H, reg), _) => l.copy(registers = TwoRegisters(B, reg))
|
||||
case l@ZLine0(LD, TwoRegisters(L, reg), _) => l.copy(registers = TwoRegisters(C, reg))
|
||||
case l => l
|
||||
})
|
||||
fuse(fixedReads.head, fixedStores.head) ++ fixedReads.tail.zip(fixedStores.tail).flatMap(t => t._1 ++ t._2)
|
||||
} else {
|
||||
fuse(reads.head, stores.head) ++ reads.tail.zip(stores.tail).flatMap(t => t._1 ++ t._2)
|
||||
}
|
||||
} else {
|
||||
fuse(reads.head, stores.head) ++ reads.tail.zip(stores.tail).flatMap(t => t._1 ++ t._2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1795,6 +2088,20 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
case _ => List(ZLine.ldImm8(ZRegister.A, 0)) // TODO: signed large types?
|
||||
}
|
||||
}
|
||||
case e@DerefExpression(_, _, _) =>
|
||||
import ZRegister._
|
||||
val prepareHL = compileDerefPointer(ctx, e)
|
||||
List.tabulate(size) { i =>
|
||||
if (i == 0) {
|
||||
prepareHL :+ ZLine.ld8(A, MEM_HL)
|
||||
} else if (i < e.targetType.size) {
|
||||
List(ZLine.register(INC_16, HL), ZLine.ld8(A, MEM_HL))
|
||||
} else if (e.targetType.isSigned) {
|
||||
signExtendHighestByte(ctx, ZRegister.MEM_HL)
|
||||
} else {
|
||||
List(ZLine.ldImm8(A, 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1866,6 +2173,17 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
Nil
|
||||
}
|
||||
}
|
||||
case de@DerefExpression(inner, offset, targetType) =>
|
||||
import ZRegister._
|
||||
val prepareHL = compileDerefPointer(ctx, de)
|
||||
List.tabulate(size) { i =>
|
||||
if (i == 0) {
|
||||
prepareHL :+ ZLine.ld8(MEM_HL, A)
|
||||
} else if (i < targetType.size) {
|
||||
if (includeStep) List(ZLine.register(INC_16, HL), ZLine.ld8(MEM_HL, A))
|
||||
else List(ZLine.ld8(MEM_HL, A))
|
||||
} else Nil
|
||||
}
|
||||
case _ =>
|
||||
ctx.log.error("Cannot modify large object accessed via such complex expression", lhs.position)
|
||||
List.fill(size)(Nil)
|
||||
|
@ -110,6 +110,14 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
||||
val store = Z80ExpressionCompiler.storeHL(ctx, destination, sourceType.isSigned)
|
||||
load ++ store
|
||||
}
|
||||
case 3 =>
|
||||
val load = Z80ExpressionCompiler.compile(ctx, source, ZExpressionTarget.EHL, BranchSpec.None)
|
||||
val store = Z80ExpressionCompiler.storeEHL(ctx, destination, sourceType.isSigned)
|
||||
load ++ store
|
||||
case 4 =>
|
||||
val load = Z80ExpressionCompiler.compile(ctx, source, ZExpressionTarget.DEHL, BranchSpec.None)
|
||||
val store = Z80ExpressionCompiler.storeDEHL(ctx, destination, sourceType.isSigned)
|
||||
load ++ store
|
||||
case s => Z80ExpressionCompiler.storeLarge(ctx, destination, source)
|
||||
}) -> Nil
|
||||
case s: IfStatement =>
|
||||
|
@ -469,7 +469,7 @@ object ZBuiltIns {
|
||||
def performLongInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, opcodeFirst: ZOpcode.Value, opcodeLater: ZOpcode.Value, size: Int, decimal: Boolean = false): List[ZLine] = {
|
||||
if (lhs.isInstanceOf[DerefExpression]) {
|
||||
ctx.log.error("Too complex left-hand-side expression", lhs.position)
|
||||
return Z80ExpressionCompiler.compileToHL(ctx, lhs) ++ Z80ExpressionCompiler.compileToHL(ctx, rhs)
|
||||
return Z80ExpressionCompiler.compile(ctx, lhs, ZExpressionTarget.NOTHING) ++ Z80ExpressionCompiler.compile(ctx, rhs, ZExpressionTarget.NOTHING)
|
||||
}
|
||||
if (size == 2 && !decimal) {
|
||||
// n × INC HL
|
||||
@ -532,6 +532,7 @@ object ZBuiltIns {
|
||||
val store = Z80ExpressionCompiler.compileByteStores(ctx, lhs, size, includeStep = false)
|
||||
val loadRight = Z80ExpressionCompiler.compileByteReads(ctx, rhs, size, ZExpressionTarget.BC)
|
||||
val rightIsBig = loadRight.exists(_.exists(l => l.changesRegister(ZRegister.HL) || l.changesRegister(ZRegister.DE)))
|
||||
// TODO: don't evaluate LHS twice
|
||||
val (leftStasher, calcStasher, loadLeft) = if (rightIsBig) {
|
||||
(
|
||||
((e: List[ZLine]) => Z80ExpressionCompiler.stashHLIfChanged(ctx, e)),
|
||||
|
@ -3,12 +3,12 @@ package millfork.test
|
||||
import millfork.{Cpu, CpuFamily, OptimizationPresets}
|
||||
import millfork.assembly.mos.opt.{AlwaysGoodOptimizations, DangerousOptimizations}
|
||||
import millfork.test.emu._
|
||||
import org.scalatest.{FunSuite, Matchers}
|
||||
import org.scalatest.{AppendedClues, FunSuite, Matchers}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
class ArraySuite extends FunSuite with Matchers {
|
||||
class ArraySuite extends FunSuite with Matchers with AppendedClues {
|
||||
|
||||
test("Array assignment") {
|
||||
val src =
|
||||
@ -457,13 +457,6 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
}
|
||||
|
||||
test("Invalid array things that will become valid in the future") {
|
||||
ShouldNotCompile(
|
||||
"""
|
||||
| array(int32) a[7] @$c000
|
||||
| void main () {
|
||||
| a[0] = 2
|
||||
| }
|
||||
""".stripMargin)
|
||||
ShouldNotCompile(
|
||||
"""
|
||||
| array(int32) a[7] @$c000
|
||||
@ -478,12 +471,139 @@ class ArraySuite extends FunSuite with Matchers {
|
||||
| a[0] += 2
|
||||
| }
|
||||
""".stripMargin)
|
||||
ShouldNotCompile(
|
||||
}
|
||||
|
||||
test("Various large assignments involving arrays") {
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80)(
|
||||
"""
|
||||
| array(int32) a[7] @$c000
|
||||
| void main () {
|
||||
| a[0] = 2
|
||||
| }
|
||||
""".stripMargin) { m => }
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80)(
|
||||
"""
|
||||
| array(int32) a[7] @$c000
|
||||
| int32 main () {
|
||||
| return a[4]
|
||||
| }
|
||||
""".stripMargin)
|
||||
""".stripMargin) { m => }
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80)(
|
||||
"""
|
||||
| array(int32) a[7] @$c000
|
||||
| noinline void f(byte i) {
|
||||
| a[i] = a[i+2]
|
||||
| }
|
||||
| void main () {
|
||||
| f(1)
|
||||
| }
|
||||
""".stripMargin) { m => }
|
||||
}
|
||||
|
||||
test("Various large assignments involving arrays and arithmetic conversions") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80)(
|
||||
"""
|
||||
| array(int32) a[7] @$c000
|
||||
| array(int24) b[7] @$c100
|
||||
| noinline void init() {
|
||||
| b[0] = 1
|
||||
| b[1] = 2
|
||||
| a[0].b3 = 4
|
||||
| a[1].b3 = 4
|
||||
| a[2].b3 = 4
|
||||
| a[3].b3 = 4
|
||||
| a[4].b3 = 4
|
||||
| a[5].b3 = 4
|
||||
| }
|
||||
| noinline byte id(byte x) = x
|
||||
| void main () {
|
||||
| int24 tmp1
|
||||
| stack int24 tmp2
|
||||
| int32 tmp3
|
||||
| stack int32 tmp4
|
||||
| init()
|
||||
| a[id(0)] = b[id(0)]
|
||||
| memory_barrier()
|
||||
| tmp1 = b[id(0)]
|
||||
| memory_barrier()
|
||||
| tmp2 = b[id(0)]
|
||||
| memory_barrier()
|
||||
| a[id(1)] = tmp1
|
||||
| memory_barrier()
|
||||
| a[id(2)] = tmp2
|
||||
| memory_barrier()
|
||||
| tmp3 = b[id(0)]
|
||||
| memory_barrier()
|
||||
| tmp4 = b[id(0)]
|
||||
| memory_barrier()
|
||||
| a[id(3)] = tmp3
|
||||
| memory_barrier()
|
||||
| a[id(4)] = tmp4
|
||||
| }
|
||||
""".stripMargin) { m =>
|
||||
m.readMedium(0xc100) should equal(1)
|
||||
m.readMedium(0xc103) should equal(2)
|
||||
m.readLong(0xc000) should equal(1)
|
||||
m.readLong(0xc004) should equal(1)
|
||||
m.readLong(0xc008) should equal(1)
|
||||
m.readLong(0xc00c) should equal(1)
|
||||
m.readLong(0xc010) should equal(1)
|
||||
}
|
||||
}
|
||||
|
||||
test("Various large assignments involving arrays and arithmetic conversions 2") {
|
||||
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Intel8080, Cpu.Z80)(
|
||||
"""
|
||||
| array(int64) a[7] @$c000
|
||||
| array(int40) b[7] @$c100
|
||||
| noinline void init() {
|
||||
| b[0] = 1
|
||||
| b[1] = 2
|
||||
| a[0].b7 = 4
|
||||
| a[1].b7 = 4
|
||||
| a[2].b7 = 4
|
||||
| a[3].b7 = 4
|
||||
| a[4].b7 = 4
|
||||
| a[5].b7 = 4
|
||||
| }
|
||||
| noinline byte id(byte x) = x
|
||||
| void main () {
|
||||
| int64 tmp1
|
||||
| stack int64 tmp2
|
||||
| int40 tmp3
|
||||
| stack int40 tmp4
|
||||
| init()
|
||||
| a[id(0)] = b[id(0)]
|
||||
| memory_barrier()
|
||||
| tmp1 = b[id(0)]
|
||||
| memory_barrier()
|
||||
| tmp2 = b[id(0)]
|
||||
| memory_barrier()
|
||||
| a[id(1)] = tmp1
|
||||
| memory_barrier()
|
||||
| a[id(2)] = tmp2
|
||||
| memory_barrier()
|
||||
| tmp3 = b[id(0)]
|
||||
| memory_barrier()
|
||||
| tmp4 = b[id(0)]
|
||||
| memory_barrier()
|
||||
| a[id(3)] = tmp3
|
||||
| memory_barrier()
|
||||
| a[id(4)] = tmp4
|
||||
| }
|
||||
""".stripMargin) { m =>
|
||||
m.readLong(0xc100) should equal(1) withClue "init b[0]"
|
||||
m.readLong(0xc105) should equal(2) withClue "init b[1]"
|
||||
m.readLong(0xc000) should equal(1) withClue "b0..b3 of a[0] initted from b[0]"
|
||||
m.readLong(0xc004) should equal(0) withClue "b4..b7 of a[0] initted from b[0]"
|
||||
m.readLong(0xc008) should equal(1) withClue "b0..b3 of a[1] initted from static int64"
|
||||
m.readLong(0xc00c) should equal(0) withClue "b4..b7 of a[1] initted from static int64"
|
||||
m.readLong(0xc010) should equal(1) withClue "b0..b3 of a[2] initted from stack int64"
|
||||
m.readLong(0xc014) should equal(0) withClue "b4..b7 of a[2] initted from stack int64"
|
||||
m.readLong(0xc018) should equal(1) withClue "b0..b3 of a[3] initted from static int40"
|
||||
m.readLong(0xc01c) should equal(0) withClue "b4..b7 of a[3] initted from static int40"
|
||||
m.readLong(0xc020) should equal(1) withClue "b0..b3 of a[4] initted from stack int40"
|
||||
m.readLong(0xc024) should equal(0) withClue "b4..b7 of a[4] initted from stack int40"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user