1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-31 18:41:30 +00:00
millfork/src/main/scala/millfork/compiler/z80/ZBuiltIns.scala
2019-04-30 00:41:42 +02:00

563 lines
25 KiB
Scala
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package millfork.compiler.z80
import millfork.assembly.z80._
import millfork.compiler.CompilationContext
import millfork.node._
import ZOpcode._
import millfork.CompilationFlag
import millfork.env._
import millfork.error.ConsoleLogger
import scala.collection.mutable
/**
* @author Karol Stasiak
*/
object ZBuiltIns {
def compile8BitOperation(ctx: CompilationContext, op: ZOpcode.Value, params: List[Expression]): List[ZLine] = {
var const = op match {
case AND => NumericConstant(0xff, 1)
case OR | XOR => Constant.Zero
}
var hasConst = false
var result = mutable.ListBuffer[ZLine]()
params.foreach(expr => ctx.env.eval(expr) match {
case None =>
if (result.isEmpty) {
result ++= Z80ExpressionCompiler.compileToA(ctx, expr)
} else {
result += ZLine.ld8(ZRegister.E, ZRegister.A)
result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToA(ctx, expr))
result += ZLine.register(op, ZRegister.E)
}
case Some(c) =>
hasConst = true
const = op match {
case AND => CompoundConstant(MathOperator.And, const, c).quickSimplify
case OR => CompoundConstant(MathOperator.Or, const, c).quickSimplify
case XOR => CompoundConstant(MathOperator.Exor, const, c).quickSimplify
}
})
if (hasConst) {
result += ZLine.imm8(op, const)
}
result.toList
}
def compile16BitOperation(ctx: CompilationContext, op: ZOpcode.Value, params: List[Expression]): List[ZLine] = {
var const = op match {
case AND => NumericConstant(0xffff, 2)
case OR | XOR => Constant.Zero
}
var hasConst = false
var result = mutable.ListBuffer[ZLine]()
params.foreach(expr => ctx.env.eval(expr) match {
case None =>
if (result.isEmpty) {
result ++= Z80ExpressionCompiler.compileToHL(ctx, expr)
} else {
result += ZLine.ld8(ZRegister.E, ZRegister.L)
result += ZLine.ld8(ZRegister.D, ZRegister.H)
result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToHL(ctx, expr))
result ++= List(
ZLine.ld8(ZRegister.A, ZRegister.L),
ZLine.register(op, ZRegister.E),
ZLine.ld8(ZRegister.L, ZRegister.A),
ZLine.ld8(ZRegister.A, ZRegister.H),
ZLine.register(op, ZRegister.D),
ZLine.ld8(ZRegister.H, ZRegister.A)
)
}
case Some(c) =>
hasConst = true
const = op match {
case AND => CompoundConstant(MathOperator.And, const, c).quickSimplify
case OR => CompoundConstant(MathOperator.Or, const, c).quickSimplify
case XOR => CompoundConstant(MathOperator.Exor, const, c).quickSimplify
}
})
if (hasConst) {
result ++= List(
ZLine.ld8(ZRegister.A, ZRegister.L),
ZLine.imm8(op, const.loByte),
ZLine.ld8(ZRegister.L, ZRegister.A),
ZLine.ld8(ZRegister.A, ZRegister.H),
ZLine.imm8(op, const.hiByte),
ZLine.ld8(ZRegister.H, ZRegister.A)
)
}
result.toList
}
def compile8BitSum(ctx: CompilationContext, params: List[(Boolean, Expression)], decimal: Boolean): List[ZLine] = {
var const = Constant.Zero
var hasConst = false
var result = mutable.ListBuffer[ZLine]()
params.foreach {
case (false, expr) =>
ctx.env.eval(expr) match {
case None =>
if (result.isEmpty) {
result ++= Z80ExpressionCompiler.compileToA(ctx, expr)
} else {
result += ZLine.ld8(ZRegister.E, ZRegister.A)
result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToA(ctx, expr))
result += ZLine.register(ADD, ZRegister.E)
if (decimal) {
result += ZLine.implied(DAA)
}
}
case Some(c) =>
hasConst = true
if (decimal) {
const = CompoundConstant(MathOperator.DecimalPlus, const, c).quickSimplify
} else {
const = (const + c).quickSimplify
}
}
case (true, expr) =>
ctx.env.eval(expr) match {
case None =>
if (result.isEmpty) {
if (decimal) ???
result ++= Z80ExpressionCompiler.compileToA(ctx, expr)
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
result += ZLine.implied(NEG)
} else {
result += ZLine.implied(CPL)
result += ZLine.register(INC, ZRegister.A)
}
} else {
if (!decimal || ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
// TODO: optimize
result += ZLine.ld8(ZRegister.D, ZRegister.A)
result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToA(ctx, expr))
result += ZLine.ld8(ZRegister.E, ZRegister.A)
result += ZLine.ld8(ZRegister.A, ZRegister.D)
result += ZLine.register(SUB, ZRegister.E)
if (decimal) {
result += ZLine.implied(DAA)
}
} else {
// -' on Intel 8080
result += ZLine.ld8(ZRegister.D, ZRegister.A)
result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToA(ctx, expr))
result += ZLine.ld8(ZRegister.E, ZRegister.A)
result += ZLine.ldImm8(ZRegister.A, 0x9a)
result += ZLine.register(SUB, ZRegister.E)
result += ZLine.register(ADD, ZRegister.D)
result += ZLine.implied(DAA)
}
}
case Some(c) =>
hasConst = true
if (decimal) {
const = CompoundConstant(MathOperator.DecimalMinus, const, c).quickSimplify
} else {
const = (const - c).quickSimplify
}
}
}
if (hasConst) {
const match {
case CompoundConstant(MathOperator.DecimalMinus | MathOperator.Minus, NumericConstant(0, _), c) =>
if (!decimal || ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
result += ZLine.imm8(SUB, c.loByte)
if (decimal) {
result += ZLine.implied(DAA)
}
} else {
import ZRegister._
result += ZLine.ld8(E, A)
result += ZLine.ldImm8(A, 0x9a)
result += ZLine.imm8(SUB, c.loByte)
result += ZLine.register(ADD, E)
result += ZLine.implied(DAA)
}
case NumericConstant(n, _) if n < 0 && !decimal =>
result += ZLine.imm8(SUB, (-n.toInt) & 0xff)
case _ =>
result += ZLine.imm8(ADD, const.loByte)
if (decimal) {
result += ZLine.implied(DAA)
}
}
}
result.toList
}
def compile16BitSum(ctx: CompilationContext, params: List[(Boolean, Expression)], decimal: Boolean): List[ZLine] = {
var const: Constant = NumericConstant(0, 2)
var hasConst = false
var result = mutable.ListBuffer[ZLine]()
if (decimal) {
params.foreach {
case (false, expr) =>
ctx.env.eval(expr) match {
case None =>
if (result.isEmpty) {
result ++= Z80ExpressionCompiler.compileToHL(ctx, expr)
} else {
result += ZLine.ld8(ZRegister.E, ZRegister.L)
result += ZLine.ld8(ZRegister.D, ZRegister.H)
result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToHL(ctx, expr))
result += ZLine.ld8(ZRegister.A, ZRegister.L)
result += ZLine.register(ADD, ZRegister.E)
result += ZLine.implied(DAA)
result += ZLine.ld8(ZRegister.L, ZRegister.A)
result += ZLine.ld8(ZRegister.A, ZRegister.H)
result += ZLine.register(ADC, ZRegister.D)
result += ZLine.implied(DAA)
result += ZLine.ld8(ZRegister.H, ZRegister.A)
}
case Some(c) =>
hasConst = true
const = CompoundConstant(MathOperator.DecimalPlus, const, c).quickSimplify
}
case (true, expr) =>
ctx.env.eval(expr) match {
case Some(c) if hasConst && const.isProvablyGreaterOrEqualThan(c)=>
const = CompoundConstant(MathOperator.DecimalMinus, const, c).quickSimplify
case _ =>
if (result.isEmpty) {
result += ZLine.ldImm16(ZRegister.HL, const)
const = NumericConstant(0, 2)
hasConst = false
}
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
result += ZLine.ld8(ZRegister.E, ZRegister.L)
result += ZLine.ld8(ZRegister.D, ZRegister.H)
result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToHL(ctx, expr))
result += ZLine.ld8(ZRegister.A, ZRegister.E)
result += ZLine.register(SUB, ZRegister.L)
result += ZLine.implied(DAA)
result += ZLine.ld8(ZRegister.L, ZRegister.A)
result += ZLine.ld8(ZRegister.A, ZRegister.D)
result += ZLine.register(SBC, ZRegister.H)
result += ZLine.implied(DAA)
result += ZLine.ld8(ZRegister.H, ZRegister.A)
} else {
result += ZLine.ld8(ZRegister.E, ZRegister.L)
result += ZLine.ld8(ZRegister.D, ZRegister.H)
result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToHL(ctx, expr))
result += ZLine.ldImm8(ZRegister.A, 0x9A)
result += ZLine.register(SUB, ZRegister.L)
result += ZLine.register(ADD, ZRegister.E)
result += ZLine.implied(DAA)
result += ZLine.ld8(ZRegister.L, ZRegister.A)
result += ZLine.ldImm8(ZRegister.A, 0x99)
result += ZLine.imm8(ADC, 0)
result += ZLine.register(SUB, ZRegister.H)
result += ZLine.register(ADD, ZRegister.D)
result += ZLine.implied(DAA)
result += ZLine.ld8(ZRegister.H, ZRegister.A)
}
}
}
} else {
params.foreach {
case (false, expr) =>
ctx.env.eval(expr) match {
case None =>
if (result.isEmpty) {
result ++= Z80ExpressionCompiler.compileToHL(ctx, expr)
} else {
result += ZLine.ld8(ZRegister.E, ZRegister.L)
result += ZLine.ld8(ZRegister.D, ZRegister.H)
result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToHL(ctx, expr))
result += ZLine.registers(ADD_16, ZRegister.HL, ZRegister.DE)
}
case Some(c) =>
hasConst = true
const = (const + c).quickSimplify
}
case (true, expr) =>
ctx.env.eval(expr) match {
case None =>
if (result.isEmpty) {
???
} else {
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
// TODO: optimize
result += ZLine.ld8(ZRegister.D, ZRegister.H)
result += ZLine.ld8(ZRegister.E, ZRegister.L)
result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToHL(ctx, expr))
result += ZLine.ld8(ZRegister.B, ZRegister.H)
result += ZLine.ld8(ZRegister.C, ZRegister.L)
result += ZLine.ld8(ZRegister.H, ZRegister.D)
result += ZLine.ld8(ZRegister.L, ZRegister.E)
result += ZLine.register(OR, ZRegister.A)
result += ZLine.registers(SBC_16, ZRegister.HL, ZRegister.BC)
} else {
// TODO: optimize
result += ZLine.ld8(ZRegister.D, ZRegister.H)
result += ZLine.ld8(ZRegister.E, ZRegister.L)
result ++= Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToHL(ctx, expr))
result += ZLine.ld8(ZRegister.A, ZRegister.E)
result += ZLine.register(SUB, ZRegister.L)
result += ZLine.ld8(ZRegister.L, ZRegister.A)
result += ZLine.ld8(ZRegister.A, ZRegister.D)
result += ZLine.register(SBC, ZRegister.H)
result += ZLine.ld8(ZRegister.H, ZRegister.A)
}
}
case Some(c) =>
hasConst = true
const = (const - c).quickSimplify
}
}
}
if (hasConst) {
result ++= List(
ZLine.ldImm16(ZRegister.DE, const),
ZLine.registers(ADD_16, ZRegister.HL, ZRegister.DE)
)
}
result.toList
}
def perform8BitInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, opcode: ZOpcode.Value, decimal: Boolean = false): List[ZLine] = {
val (lv, calculateAddress):(LocalVariableAddressOperand, List[ZLine]) = Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, lhs, forWriting = true).getOrElse{
ctx.log.error("Invalid left-hand-side expression", lhs.position)
LocalVariableAddressViaHL -> Nil
}
val constantRight = ctx.env.eval(rhs)
val calculateChange = Z80ExpressionCompiler.compileToA(ctx, rhs)
val setup = (calculateChange.exists(Z80ExpressionCompiler.changesHL), calculateAddress.exists(Z80ExpressionCompiler.changesA)) match {
case (false, false) => calculateChange ++ calculateAddress
case (true, false) => calculateChange ++ calculateAddress
case (false, true) => calculateAddress ++ calculateChange
case (true, true) => calculateAddress ++ Z80ExpressionCompiler.stashHLIfChanged(ctx, calculateChange)
}
opcode match {
case ADD if decimal =>
setup ++ List(
ZLine.register(ADD, lv),
ZLine.implied(DAA),
ZLine.ld8(lv, ZRegister.A))
case ADD if !decimal =>
constantRight match {
// n × INC (HL) = 11n cycles, n bytes
// n × INC (IX,d) = 23n cycles, 2n bytes
// LD A,n ; ADD (HL) ; LD (HL),A = 21 cycles, 4 bytes
// LD A,n ; ADD (IX,d) ; LD (IX,d),A = 45 cycles, 6 bytes
case Some(NumericConstant(1, _)) =>
calculateAddress :+ ZLine.register(INC, lv)
case Some(NumericConstant(2, _)) =>
calculateAddress :+ ZLine.register(INC, lv) :+ ZLine.register(INC, lv)
case Some(NumericConstant(3, _)) if ctx.options.flag(CompilationFlag.OptimizeForSpeed) =>
calculateAddress ++ List(ZLine.register(INC, lv), ZLine.register(INC, lv), ZLine.register(INC, lv))
case Some(NumericConstant(0xff | -1, _)) =>
calculateAddress :+ ZLine.register(DEC, lv)
case Some(NumericConstant(0xfe | -2, _)) =>
calculateAddress :+ ZLine.register(DEC, lv) :+ ZLine.register(DEC, lv)
case Some(NumericConstant(0xfd | -3, _)) if ctx.options.flag(CompilationFlag.OptimizeForSpeed) =>
calculateAddress ++ List(ZLine.register(DEC, lv), ZLine.register(DEC, lv), ZLine.register(DEC, lv))
case _ =>
setup ++ List(
ZLine.register(ADD, lv),
ZLine.ld8(lv, ZRegister.A))
}
case SUB if decimal =>
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
setup ++ List(
ZLine.ld8(ZRegister.E, ZRegister.A),
ZLine.ld8(ZRegister.A, lv),
ZLine.register(SUB, ZRegister.E),
ZLine.implied(DAA),
ZLine.ld8(lv, ZRegister.A))
} else {
setup ++ List(
ZLine.ld8(ZRegister.E, ZRegister.A),
ZLine.ld8(ZRegister.D, lv),
ZLine.ldImm8(ZRegister.A, 0x9a),
ZLine.register(SUB, ZRegister.E),
ZLine.register(ADD, ZRegister.D),
ZLine.implied(DAA),
ZLine.ld8(lv, ZRegister.A))
}
case SUB if !decimal=>
constantRight match {
case Some(NumericConstant(1, _)) =>
calculateAddress :+ ZLine.register(DEC, lv)
case Some(NumericConstant(2, _)) =>
calculateAddress :+ ZLine.register(DEC, lv) :+ ZLine.register(INC, lv)
case Some(NumericConstant(3, _)) if ctx.options.flag(CompilationFlag.OptimizeForSpeed) =>
calculateAddress ++ List(ZLine.register(DEC, lv), ZLine.register(INC, lv), ZLine.register(INC, lv))
case Some(NumericConstant(0xff | -1, _)) =>
calculateAddress :+ ZLine.register(INC, lv)
case Some(NumericConstant(0xfe | -2, _)) =>
calculateAddress :+ ZLine.register(INC, lv) :+ ZLine.register(INC, lv)
case Some(NumericConstant(0xfd | -3, _)) if ctx.options.flag(CompilationFlag.OptimizeForSpeed) =>
calculateAddress ++ List(ZLine.register(INC, lv), ZLine.register(INC, lv), ZLine.register(INC, lv))
case _ =>
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
setup ++ List(
ZLine.implied(NEG),
ZLine.register(ADD, lv),
ZLine.ld8(lv, ZRegister.A))
} else {
setup ++ List(
ZLine.implied(CPL),
ZLine.register(INC, ZRegister.A),
ZLine.register(ADD, lv),
ZLine.ld8(lv, ZRegister.A))
}
}
case XOR =>
constantRight match {
case Some(NumericConstant(0, _)) =>
calculateAddress
case Some(NumericConstant(0xff | -1, _)) =>
calculateAddress ++ List(
ZLine.ld8(ZRegister.A, lv),
ZLine.implied(CPL),
ZLine.ld8(lv, ZRegister.A))
case _ =>
setup ++ List(
ZLine.register(XOR, lv),
ZLine.ld8(lv, ZRegister.A))
}
case OR =>
constantRight match {
case Some(NumericConstant(0, _)) =>
calculateAddress
case Some(NumericConstant(0xff | -1, _)) =>
calculateAddress :+ ZLine.ldImm8(lv, 0xff)
case Some(NumericConstant(n, _)) if n.&(n - 1) == 0 && ctx.options.flag(CompilationFlag.EmitExtended80Opcodes) =>
calculateAddress :+ ZLine.register(ZOpcodeClasses.SET_seq(Integer.numberOfTrailingZeros(n.toInt)), lv)
case _ =>
setup ++ List(
ZLine.register(OR, lv),
ZLine.ld8(lv, ZRegister.A))
}
case AND =>
constantRight match {
case Some(NumericConstant(0, _)) =>
calculateAddress :+ ZLine.ldImm8(lv, 0)
case Some(NumericConstant(0xff | -1, _)) =>
calculateAddress
case Some(NumericConstant(n, _)) if n.^(0xff).&(n.^(0xff) - 1) == 0 && ctx.options.flag(CompilationFlag.EmitExtended80Opcodes) =>
calculateAddress :+ ZLine.register(ZOpcodeClasses.RES_seq(Integer.numberOfTrailingZeros(n.^(0xff).toInt)), lv)
case _ =>
setup ++ List(
ZLine.register(AND, lv),
ZLine.ld8(lv, ZRegister.A))
}
}
}
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")
return Z80ExpressionCompiler.compileToHL(ctx, lhs) ++ Z80ExpressionCompiler.compileToHL(ctx, rhs)
}
if (size == 2 && !decimal) {
// n × INC HL
// 6n cycles, n bytes
// LD A,L ; ADD A,n ; LD L,A ; LD H,A ; ADC A,m ; LD H,A
// 30 cycles, 8 bytes
// LD A,L ; ADD A,n ; LD L,A ; JR NC,... ; INC H
// 27 bytes, 7 bytes
// LD DE,nn ; ADD HL,DE
// 21 cycles, 4 bytes
val maxForInc = 3
if (opcodeFirst == ZOpcode.ADD) {
ctx.env.eval(rhs) match {
case Some(NumericConstant(0, _)) =>
return Z80ExpressionCompiler.compileToHL(ctx, lhs)
case Some(NumericConstant(n, _)) if n > 0 && n <= maxForInc =>
return Z80ExpressionCompiler.compileToHL(ctx, lhs) ++
List.fill(n.toInt)(ZLine.register(INC_16, ZRegister.HL)) ++
Z80ExpressionCompiler.storeHL(ctx, lhs, signedSource = false)
case Some(NumericConstant(n, _)) if n < 0 && n >= -maxForInc =>
return Z80ExpressionCompiler.compileToHL(ctx, lhs) ++
List.fill(-n.toInt)(ZLine.register(DEC_16, ZRegister.HL)) ++
Z80ExpressionCompiler.storeHL(ctx, lhs, signedSource = false)
case _ =>
val loadRight = Z80ExpressionCompiler.compileToDE(ctx, rhs)
val loadLeft = Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToHL(ctx, lhs))
val calculateAndStore = ZLine.registers(ADD_16, ZRegister.HL, ZRegister.DE) :: Z80ExpressionCompiler.storeHL(ctx, lhs, signedSource = false)
return loadRight ++ loadLeft ++ calculateAndStore
}
}
if (opcodeFirst == ZOpcode.SUB) {
ctx.env.eval(rhs) match {
case Some(NumericConstant(0, _)) =>
return Z80ExpressionCompiler.compileToHL(ctx, lhs)
case Some(NumericConstant(n, _)) if n > 0 && n <= maxForInc =>
return Z80ExpressionCompiler.compileToHL(ctx, lhs) ++
List.fill(n.toInt)(ZLine.register(DEC_16, ZRegister.HL)) ++
Z80ExpressionCompiler.storeHL(ctx, lhs, signedSource = false)
case Some(NumericConstant(n, _)) if n < 0 && n >= -maxForInc =>
return Z80ExpressionCompiler.compileToHL(ctx, lhs) ++
List.fill(-n.toInt)(ZLine.register(INC_16, ZRegister.HL)) ++
Z80ExpressionCompiler.storeHL(ctx, lhs, signedSource = false)
case _ =>
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
val loadRight = Z80ExpressionCompiler.compileToDE(ctx, rhs)
val loadLeft = Z80ExpressionCompiler.stashDEIfChanged(ctx, Z80ExpressionCompiler.compileToHL(ctx, lhs))
// OR A clears carry before SBC
val calculateAndStore = List(
ZLine.register(OR, ZRegister.A),
ZLine.registers(SBC_16, ZRegister.HL, ZRegister.DE)) ++
Z80ExpressionCompiler.storeHL(ctx, lhs, signedSource = false)
return loadRight ++ loadLeft ++ calculateAndStore
}
}
}
}
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)))
val (leftStasher, calcStasher, loadLeft) = if (rightIsBig) {
(
((e: List[ZLine]) => Z80ExpressionCompiler.stashHLIfChanged(ctx, e)),
((e: List[ZLine]) => Z80ExpressionCompiler.stashDEIfChanged(ctx, e)),
Z80ExpressionCompiler.compileByteReads(ctx, lhs, size, ZExpressionTarget.BC)
)
} else {
(
((e: List[ZLine]) => Z80ExpressionCompiler.stashBCIfChanged(ctx, e)),
((e: List[ZLine]) => e),
Z80ExpressionCompiler.compileByteReads(ctx, lhs, size, ZExpressionTarget.HL)
)
}
List.tabulate(size) { i =>
import ZRegister._
// TODO: stash things correctly?
if (opcodeFirst == ZOpcode.SUB && decimal && !ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
// TODO: check if carry is ok
if (i == 0) {
loadRight(i) ++ calcStasher(List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++
(leftStasher(loadLeft(i)) ++ List(
ZLine.ld8(D, A),
ZLine.ldImm8(A, 0x9A),
ZLine.register(SUB, E),
ZLine.register(ADD, D),
ZLine.implied(DAA)) ++ store(i)))
} else {
loadRight(i) ++ calcStasher(List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++
(leftStasher(loadLeft(i)) ++ List(
ZLine.ld8(D, A),
ZLine.ldImm8(A, 0x99),
ZLine.imm8(ADC, 0),
ZLine.register(SUB, E),
ZLine.register(ADD, D),
ZLine.implied(DAA)) ++ store(i)))
}
} else {
val firstPhase = loadRight(i) ++ calcStasher(List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++
(leftStasher(loadLeft(i)) :+ ZLine.register(if (i == 0) opcodeFirst else opcodeLater, ZRegister.E)))
val secondPhase = if (decimal) firstPhase :+ ZLine.implied(ZOpcode.DAA) else firstPhase
secondPhase ++ store(i)
}
}.flatten
}
}