mirror of
https://github.com/KarolS/millfork.git
synced 2025-04-10 16:39:59 +00:00
Many things:
– better support for LR35902 – stack variables for 8080 and LR35902 – bugfixes – minor optimizations
This commit is contained in:
parent
600e49cf49
commit
5c25e653bf
@ -15,6 +15,9 @@ import scala.collection.mutable
|
||||
object Z80BulkMemoryOperations {
|
||||
import Z80StatementCompiler.compileForStatement
|
||||
|
||||
/**
|
||||
* Compiles loops like <code>for i,a,until,b { p[i] = q[i] }</code>
|
||||
*/
|
||||
def compileMemcpy(ctx: CompilationContext, target: IndexedExpression, source: IndexedExpression, f: ForStatement): List[ZLine] = {
|
||||
val sourceOffset = removeVariableOnce(f.variable, source.index).getOrElse(return compileForStatement(ctx, f))
|
||||
if (!sourceOffset.isPure) return compileForStatement(ctx, f)
|
||||
@ -26,13 +29,18 @@ object Z80BulkMemoryOperations {
|
||||
_ => calculateSource -> Nil,
|
||||
next => List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.register(next, ZRegister.HL)
|
||||
ZLine.register(next, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.MEM_DE, ZRegister.A)
|
||||
),
|
||||
decreasing => Some(if (decreasing) LDDR else LDIR)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compiles loops like <code>for i,a,until,b { p[i] = a }</code>,
|
||||
* where <code>a</code> is an arbitrary expression independent of <code>i</code>
|
||||
*/
|
||||
def compileMemset(ctx: CompilationContext, target: IndexedExpression, source: Expression, f: ForStatement): List[ZLine] = {
|
||||
val loadA = Z80ExpressionCompiler.stashHLIfChanged(Z80ExpressionCompiler.compileToA(ctx, source)) :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
|
||||
compileMemoryBulk(ctx, target, f,
|
||||
@ -44,6 +52,10 @@ object Z80BulkMemoryOperations {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles loops like <code>for i,a,until,b { target[i] = z }</code>,
|
||||
* where <code>z</code> is an expression depending on <code>source[i]</code>
|
||||
*/
|
||||
def compileMemtransform(ctx: CompilationContext, target: IndexedExpression, operator: String, source: Expression, f: ForStatement): List[ZLine] = {
|
||||
val c = determineExtraLoopRegister(ctx, f, source.containsVariable(f.variable))
|
||||
val load = buildMemtransformLoader(ctx, ZRegister.MEM_HL, f.variable, operator, source, c.loopRegister).getOrElse(return compileForStatement(ctx, f))
|
||||
@ -60,6 +72,11 @@ object Z80BulkMemoryOperations {
|
||||
compileForStatement(ctx, f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles loops like <code>for i,a,until,b { target1[i] = x1 ; target2[i] = x2 }</code>,
|
||||
* where <code>x1</code> is an expression depending on <code>source1[i]</code>,
|
||||
* and <code>x2</code> is an expression depending on <code>source2[i]</code>
|
||||
*/
|
||||
def compileMemtransform2(ctx: CompilationContext,
|
||||
target1: IndexedExpression, operator1: String, source1: Expression,
|
||||
target2: IndexedExpression, operator2: String, source2: Expression,
|
||||
@ -216,84 +233,111 @@ object Z80BulkMemoryOperations {
|
||||
}
|
||||
} else {
|
||||
val (operation, daa, shift) = operator match {
|
||||
case "+=" => (ZOpcode.ADD, false, None)
|
||||
case "+'=" => (ZOpcode.ADD, true, None)
|
||||
case "-=" => (ZOpcode.SUB, false, None)
|
||||
case "-'=" => (ZOpcode.SUB, true, None)
|
||||
case "|=" => (ZOpcode.OR, false, None)
|
||||
case "&=" => (ZOpcode.AND, false, None)
|
||||
case "^=" => (ZOpcode.XOR, false, None)
|
||||
case "<<=" => (ZOpcode.SLA, false, Some(RL, 0xfe))
|
||||
case ">>=" => (ZOpcode.SRL, false, Some(RR, 0x7f))
|
||||
case "+=" => (ZOpcode.ADD, false, false)
|
||||
case "+'=" => (ZOpcode.ADD, true, false)
|
||||
case "-=" => (ZOpcode.SUB, false, false)
|
||||
case "-'=" => (ZOpcode.SUB, true, false)
|
||||
case "|=" => (ZOpcode.OR, false, false)
|
||||
case "&=" => (ZOpcode.AND, false, false)
|
||||
case "^=" => (ZOpcode.XOR, false, false)
|
||||
case ">>=" => (ZOpcode.SRL, false, true)
|
||||
case "<<=" => (ZOpcode.SLA, false, true)
|
||||
case "<<'=" => (ZOpcode.SLA, true, true)
|
||||
case _ => return None
|
||||
}
|
||||
shift match {
|
||||
case Some((nonZ80, mask)) =>
|
||||
Some(env.eval(source) match {
|
||||
case Some(NumericConstant(n, _)) =>
|
||||
if (n <= 0) Nil else {
|
||||
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
if (element == ZRegister.MEM_HL && n <= 2) {
|
||||
List.fill(n.toInt)(ZLine.register(operation, ZRegister.MEM_HL))
|
||||
} else {
|
||||
val builder = mutable.ListBuffer[ZLine]()
|
||||
builder += ZLine.ld8(ZRegister.A, element)
|
||||
for (_ <- 0 until n.toInt) {
|
||||
builder += ZLine.register(operation, ZRegister.A)
|
||||
}
|
||||
builder += ZLine.ld8(element, ZRegister.A)
|
||||
builder.toList
|
||||
}
|
||||
} else {
|
||||
if (shift) {
|
||||
env.eval(source) match {
|
||||
case Some(NumericConstant(n, _)) =>
|
||||
if (n <= 0) Some(Nil) else {
|
||||
operation match {
|
||||
case SLA =>
|
||||
val builder = mutable.ListBuffer[ZLine]()
|
||||
builder += ZLine.ld8(ZRegister.A, element)
|
||||
for (_ <- 0 until n.toInt) {
|
||||
builder += ZLine.register(nonZ80, ZRegister.A)
|
||||
builder += ZLine.imm8(AND, mask)
|
||||
builder += ZLine.register(ADD, ZRegister.A)
|
||||
if (daa) builder += ZLine.implied(DAA)
|
||||
}
|
||||
builder += ZLine.ld8(element, ZRegister.A)
|
||||
builder.toList
|
||||
}
|
||||
Some(builder.toList)
|
||||
case SRL =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
if (element == ZRegister.MEM_HL && n <= 2) {
|
||||
Some(List.fill(n.toInt)(ZLine.register(SRL, ZRegister.MEM_HL)))
|
||||
} else {
|
||||
val builder = mutable.ListBuffer[ZLine]()
|
||||
builder += ZLine.ld8(ZRegister.A, element)
|
||||
for (_ <- 0 until n.toInt) {
|
||||
builder += ZLine.register(SRL, ZRegister.A)
|
||||
}
|
||||
builder += ZLine.ld8(element, ZRegister.A)
|
||||
Some(builder.toList)
|
||||
}
|
||||
} else {
|
||||
val builder = mutable.ListBuffer[ZLine]()
|
||||
builder += ZLine.ld8(ZRegister.A, element)
|
||||
// TODO: tricks with AND?
|
||||
for (_ <- 0 until n.toInt) {
|
||||
builder += ZLine.register(OR, ZRegister.A)
|
||||
builder += ZLine.register(RR, ZRegister.A)
|
||||
}
|
||||
builder += ZLine.ld8(element, ZRegister.A)
|
||||
Some(builder.toList)
|
||||
}
|
||||
case _ => throw new IllegalStateException()
|
||||
}
|
||||
}
|
||||
case _ => return None
|
||||
}
|
||||
} else {
|
||||
val mod = source match {
|
||||
case VariableExpression(n) if n == loopVariable =>
|
||||
List(ZLine.register(operation, loopRegister))
|
||||
case _ => env.eval(source) match {
|
||||
case Some(NumericConstant(1, _)) if operator == "+=" && element == ZRegister.MEM_HL =>
|
||||
return Some(List(ZLine.register(INC, ZRegister.MEM_HL)))
|
||||
case Some(NumericConstant(1, _)) if operator == "-=" && element == ZRegister.MEM_HL =>
|
||||
return Some(List(ZLine.register(DEC, ZRegister.MEM_HL)))
|
||||
case Some(NumericConstant(2, _)) if operator == "+=" && element == ZRegister.MEM_HL && ctx.options.flag(CompilationFlag.OptimizeForSize) =>
|
||||
return Some(List(ZLine.register(INC, ZRegister.MEM_HL), ZLine.register(INC, ZRegister.MEM_HL)))
|
||||
case Some(NumericConstant(2, _)) if operator == "-=" && element == ZRegister.MEM_HL && ctx.options.flag(CompilationFlag.OptimizeForSize) =>
|
||||
return Some(List(ZLine.register(DEC, ZRegister.MEM_HL), ZLine.register(DEC, ZRegister.MEM_HL)))
|
||||
case Some(c) =>
|
||||
if (daa) {
|
||||
List(ZLine.imm8(operation, c), ZLine.implied(DAA))
|
||||
} else {
|
||||
List(ZLine.imm8(operation, c))
|
||||
}
|
||||
case _ => return None
|
||||
})
|
||||
case None =>
|
||||
val mod = source match {
|
||||
case VariableExpression(n) if n == loopVariable =>
|
||||
List(ZLine.register(operation, loopRegister))
|
||||
case _ => env.eval(source) match {
|
||||
case Some(NumericConstant(1, _)) if operator == "+=" && element == ZRegister.MEM_HL =>
|
||||
return Some(List(ZLine.register(INC, ZRegister.MEM_HL)))
|
||||
case Some(NumericConstant(1, _)) if operator == "-=" && element == ZRegister.MEM_HL =>
|
||||
return Some(List(ZLine.register(DEC, ZRegister.MEM_HL)))
|
||||
case Some(NumericConstant(2, _)) if operator == "+=" && element == ZRegister.MEM_HL && ctx.options.flag(CompilationFlag.OptimizeForSize) =>
|
||||
return Some(List(ZLine.register(INC, ZRegister.MEM_HL), ZLine.register(INC, ZRegister.MEM_HL)))
|
||||
case Some(NumericConstant(2, _)) if operator == "-=" && element == ZRegister.MEM_HL && ctx.options.flag(CompilationFlag.OptimizeForSize) =>
|
||||
return Some(List(ZLine.register(DEC, ZRegister.MEM_HL), ZLine.register(DEC, ZRegister.MEM_HL)))
|
||||
case Some(c) =>
|
||||
if (daa) {
|
||||
List(ZLine.imm8(operation, c), ZLine.implied(DAA))
|
||||
} else {
|
||||
List(ZLine.imm8(operation, c))
|
||||
}
|
||||
case _ => return None
|
||||
}
|
||||
}
|
||||
Some(
|
||||
ZLine.ld8(ZRegister.A, element) :: (mod :+ ZLine.ld8(element, ZRegister.A))
|
||||
)
|
||||
}
|
||||
Some(
|
||||
ZLine.ld8(ZRegister.A, element) :: (mod :+ ZLine.ld8(element, ZRegister.A))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a tight memcpy-like loop.
|
||||
* The result looks like this (assuming Intel 8080 and a small iteration count:
|
||||
* <br> calculate byte count to BC
|
||||
* <br> calculate target address to HL/DE
|
||||
* <br> perform extraAddressCalculations._1 (protected from clobbering BC and DE, not HL)
|
||||
* <br> perform extraAddressCalculations._2 (protected from clobbering HL and DE, not BC)
|
||||
* <br> (here B or BC contains iteration count)
|
||||
* <br> .me_label
|
||||
* <br> loadA(INC/DEC) // do stuff, including modifying the target array!
|
||||
* <br> INC/DEC HL/DE
|
||||
* <br> DJNZ .me_label
|
||||
*
|
||||
* <p>The entire loop at the end may be replaced with a single instruction on Z80
|
||||
*
|
||||
* @param ctx compilation context
|
||||
* @param target target indexed expression
|
||||
* @param f original for statement
|
||||
* @param useDEForTarget use DE instead of HL for target
|
||||
* @param extraAddressCalculations extra calculations to perform before the loop, before and after POP BC (parameter: is count small)
|
||||
* @param loadA byte value calculation (parameter: INC_16 or DEC_16)
|
||||
* @param loadA byte value calculation (parameter: INC_16 or DEC_16); if you need to store to the target array, do it here
|
||||
* @param z80Bulk Z80 opcode for faster operation (parameter: is decreasing)
|
||||
* @return
|
||||
*/
|
||||
@ -357,7 +401,7 @@ object Z80BulkMemoryOperations {
|
||||
} else {
|
||||
List(
|
||||
ZLine.register(next, if (useDEForTarget) ZRegister.DE else ZRegister.HL),
|
||||
ZLine.register(DEC_16, ZRegister.BC),
|
||||
ZLine.register(DEC_16, ZRegister.BC), // <-- Z flag is set here?
|
||||
ZLine.ld8(ZRegister.A, ZRegister.C),
|
||||
ZLine.register(OR, ZRegister.B),
|
||||
ZLine.jump(label, IfFlagSet(ZFlag.Z))
|
||||
|
@ -1,5 +1,6 @@
|
||||
package millfork.compiler.z80
|
||||
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.z80._
|
||||
import millfork.compiler.{ComparisonType, _}
|
||||
import millfork.env.NumericConstant
|
||||
@ -71,29 +72,67 @@ object Z80Comparisons {
|
||||
val calculateRight = Z80ExpressionCompiler.compileToHL(ctx, r)
|
||||
val (calculated, useBC) = if (calculateLeft.exists(Z80ExpressionCompiler.changesBC)) {
|
||||
if (calculateLeft.exists(Z80ExpressionCompiler.changesDE)) {
|
||||
calculateRight ++ List(ZLine.register(ZOpcode.PUSH, ZRegister.HL)) ++ calculateLeft ++ List(ZLine.register(ZOpcode.POP, ZRegister.BC)) -> false
|
||||
calculateRight ++ List(ZLine.register(ZOpcode.PUSH, ZRegister.HL)) ++ Z80ExpressionCompiler.fixTsx(calculateLeft) ++ List(ZLine.register(ZOpcode.POP, ZRegister.BC)) -> false
|
||||
} else {
|
||||
calculateRight ++ List(ZLine.ld8(ZRegister.D, ZRegister.H), ZLine.ld8(ZRegister.E, ZRegister.L)) ++ calculateLeft -> false
|
||||
}
|
||||
} else {
|
||||
calculateRight ++ List(ZLine.ld8(ZRegister.B, ZRegister.H), ZLine.ld8(ZRegister.C, ZRegister.L)) ++ calculateLeft -> true
|
||||
}
|
||||
val calculateFlags = calculated ++ List(
|
||||
ZLine.register(ZOpcode.OR, ZRegister.A),
|
||||
ZLine.registers(ZOpcode.SBC_16, ZRegister.HL, if (useBC) ZRegister.BC else ZRegister.DE))
|
||||
if (branches == NoBranching) return calculateFlags
|
||||
val jump = (compType, branches) match {
|
||||
case (Equal, BranchIfTrue(label)) => ZLine.jump(label, IfFlagSet(ZFlag.Z))
|
||||
case (Equal, BranchIfFalse(label)) => ZLine.jump(label, IfFlagClear(ZFlag.Z))
|
||||
case (NotEqual, BranchIfTrue(label)) => ZLine.jump(label, IfFlagClear(ZFlag.Z))
|
||||
case (NotEqual, BranchIfFalse(label)) => ZLine.jump(label, IfFlagSet(ZFlag.Z))
|
||||
case (LessUnsigned, BranchIfTrue(label)) => ZLine.jump(label, IfFlagSet(ZFlag.C))
|
||||
case (LessUnsigned, BranchIfFalse(label)) => ZLine.jump(label, IfFlagClear(ZFlag.C))
|
||||
case (GreaterOrEqualUnsigned, BranchIfTrue(label)) => ZLine.jump(label, IfFlagClear(ZFlag.C))
|
||||
case (GreaterOrEqualUnsigned, BranchIfFalse(label)) => ZLine.jump(label, IfFlagSet(ZFlag.C))
|
||||
case _ => ???
|
||||
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
|
||||
val calculateFlags = calculated ++ List(
|
||||
ZLine.register(ZOpcode.OR, ZRegister.A),
|
||||
ZLine.registers(ZOpcode.SBC_16, ZRegister.HL, if (useBC) ZRegister.BC else ZRegister.DE))
|
||||
if (branches == NoBranching) return calculateFlags
|
||||
val jump = (compType, branches) match {
|
||||
case (Equal, BranchIfTrue(label)) => ZLine.jump(label, IfFlagSet(ZFlag.Z))
|
||||
case (Equal, BranchIfFalse(label)) => ZLine.jump(label, IfFlagClear(ZFlag.Z))
|
||||
case (NotEqual, BranchIfTrue(label)) => ZLine.jump(label, IfFlagClear(ZFlag.Z))
|
||||
case (NotEqual, BranchIfFalse(label)) => ZLine.jump(label, IfFlagSet(ZFlag.Z))
|
||||
case (LessUnsigned, BranchIfTrue(label)) => ZLine.jump(label, IfFlagSet(ZFlag.C))
|
||||
case (LessUnsigned, BranchIfFalse(label)) => ZLine.jump(label, IfFlagClear(ZFlag.C))
|
||||
case (GreaterOrEqualUnsigned, BranchIfTrue(label)) => ZLine.jump(label, IfFlagClear(ZFlag.C))
|
||||
case (GreaterOrEqualUnsigned, BranchIfFalse(label)) => ZLine.jump(label, IfFlagSet(ZFlag.C))
|
||||
case _ => ???
|
||||
}
|
||||
calculateFlags :+ jump
|
||||
} else if (compType == Equal || compType == NotEqual) {
|
||||
import ZRegister._
|
||||
import ZOpcode._
|
||||
val calculateFlags = calculated ++ List(
|
||||
ZLine.ld8(A, L),
|
||||
ZLine.register(XOR, if (useBC) C else E),
|
||||
ZLine.ld8(L, A),
|
||||
ZLine.ld8(A, H),
|
||||
ZLine.register(XOR, if (useBC) B else D),
|
||||
ZLine.register(OR, L))
|
||||
if (branches == NoBranching) return calculateFlags
|
||||
val jump = (compType, branches) match {
|
||||
case (Equal, BranchIfTrue(label)) => ZLine.jump(label, IfFlagSet(ZFlag.Z))
|
||||
case (Equal, BranchIfFalse(label)) => ZLine.jump(label, IfFlagClear(ZFlag.Z))
|
||||
case (NotEqual, BranchIfTrue(label)) => ZLine.jump(label, IfFlagClear(ZFlag.Z))
|
||||
case (NotEqual, BranchIfFalse(label)) => ZLine.jump(label, IfFlagSet(ZFlag.Z))
|
||||
case _ => throw new IllegalStateException()
|
||||
}
|
||||
calculateFlags :+ jump
|
||||
} else {
|
||||
import ZRegister._
|
||||
import ZOpcode._
|
||||
val calculateFlags = calculated ++ List(
|
||||
ZLine.ld8(A, L),
|
||||
ZLine.register(SUB, if (useBC) C else E),
|
||||
ZLine.ld8(A, H),
|
||||
ZLine.register(SBC, if (useBC) B else D))
|
||||
if (branches == NoBranching) return calculateFlags
|
||||
val jump = (compType, branches) match {
|
||||
case (LessUnsigned, BranchIfTrue(label)) => ZLine.jump(label, IfFlagSet(ZFlag.C))
|
||||
case (LessUnsigned, BranchIfFalse(label)) => ZLine.jump(label, IfFlagClear(ZFlag.C))
|
||||
case (GreaterOrEqualUnsigned, BranchIfTrue(label)) => ZLine.jump(label, IfFlagClear(ZFlag.C))
|
||||
case (GreaterOrEqualUnsigned, BranchIfFalse(label)) => ZLine.jump(label, IfFlagSet(ZFlag.C))
|
||||
case _ => ???
|
||||
}
|
||||
calculateFlags :+ jump
|
||||
}
|
||||
calculateFlags :+ jump
|
||||
}
|
||||
|
||||
def compileLongRelativeComparison(ctx: CompilationContext, compType: ComparisonType.Value, l: Expression, r: Expression, branches: BranchSpec): List[ZLine] = {
|
||||
|
@ -26,7 +26,15 @@ object Z80Compiler extends AbstractCompiler[ZLine] {
|
||||
case NormalParamSignature(List(param)) if param.typ.size == 1 =>
|
||||
List(ZLine.ldAbs8(param.toAddress, ZRegister.A))
|
||||
case NormalParamSignature(List(param)) if param.typ.size == 2 =>
|
||||
List(ZLine.ldAbs16(param.toAddress, ZRegister.HL))
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(ZLine.ldAbs16(param.toAddress, ZRegister.HL))
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
ZLine.ldAbs8(param.toAddress, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.ldAbs8(param.toAddress + 1, ZRegister.A))
|
||||
}
|
||||
case _ => Nil
|
||||
}
|
||||
label :: (stackPointerFixAtBeginning(ctx) ++ storeParamsFromRegisters ++ chunk)
|
||||
@ -35,20 +43,67 @@ object Z80Compiler extends AbstractCompiler[ZLine] {
|
||||
def stackPointerFixAtBeginning(ctx: CompilationContext): List[ZLine] = {
|
||||
val m = ctx.function
|
||||
if (m.stackVariablesSize == 0) return Nil
|
||||
if (!ctx.options.flags(CompilationFlag.EmitZ80Opcodes)) {
|
||||
ErrorReporting.error(s"Target CPU does not support stack variables", m.position)
|
||||
return Nil
|
||||
}
|
||||
// if (!ctx.options.flags(CompilationFlag.EmitZ80Opcodes)) {
|
||||
// ErrorReporting.error(s"Target CPU does not support stack variables", m.position)
|
||||
// return Nil
|
||||
// }
|
||||
if (m.stackVariablesSize > 127) {
|
||||
ErrorReporting.error(s"Function ${m.name} has too many local stack variables", m.position)
|
||||
return Nil
|
||||
}
|
||||
import millfork.assembly.z80.ZOpcode._
|
||||
import ZRegister._
|
||||
List(
|
||||
ZLine.register(PUSH, IX),
|
||||
ZLine.ldImm16(IX, 0x10000 - ctx.function.stackVariablesSize.&(1).+(ctx.function.stackVariablesSize)),
|
||||
ZLine.registers(ADD_16, IX, SP),
|
||||
ZLine.ld16(SP, IX))
|
||||
val localVariableArea = ctx.function.stackVariablesSize.&(1).+(ctx.function.stackVariablesSize)
|
||||
if (ctx.options.flag(CompilationFlag.UseIxForStack)) {
|
||||
List(
|
||||
ZLine.register(PUSH, IX),
|
||||
ZLine.ldImm16(IX, 0x10000 - localVariableArea),
|
||||
ZLine.registers(ADD_16, IX, SP),
|
||||
ZLine.ld16(SP, IX))
|
||||
} else if (localVariableArea == 2) {
|
||||
// cycles: 11
|
||||
// bytes: 1
|
||||
List(ZLine.register(PUSH, HL))
|
||||
} else if (localVariableArea == 4) {
|
||||
// cycles: 22
|
||||
// bytes: 2
|
||||
List(ZLine.register(PUSH, HL), ZLine.register(PUSH, HL))
|
||||
} else {
|
||||
val preserveHL = ctx.function.params match {
|
||||
case NormalParamSignature(List(param)) => param.typ.size == 2
|
||||
case _ => false
|
||||
}
|
||||
val threshold = if (ctx.options.flag(CompilationFlag.OptimizeForSpeed)) {
|
||||
// at LVA=6, PUSH is 33 cycles
|
||||
if (preserveHL) 7 else 5
|
||||
} else if (ctx.options.flag(CompilationFlag.OptimizeForSize)) {
|
||||
// at LVA=10, PUSH is 5 bytes but more cycles
|
||||
// at LVA=14, PUSH is 7 bytes but more cycles
|
||||
if (preserveHL) 13 else 9
|
||||
} else {
|
||||
// 44ø 4B is better than 35ø 7B
|
||||
// 33ø 3B is better than 27ø 5B
|
||||
if (preserveHL) 9 else 7
|
||||
}
|
||||
if (localVariableArea < threshold) {
|
||||
List.fill(localVariableArea >> 1)(ZLine.register(PUSH, HL))
|
||||
} else if (preserveHL) {
|
||||
// cycles: 4 + 10 + 11 + 6 + 4 = 35
|
||||
// bytes: 7
|
||||
List(
|
||||
ZLine.implied(EX_DE_HL),
|
||||
ZLine.ldImm16(HL, 0x10000 - localVariableArea),
|
||||
ZLine.registers(ADD_16, HL, SP),
|
||||
ZLine.ld16(SP, HL),
|
||||
ZLine.implied(EX_DE_HL))
|
||||
} else {
|
||||
// cycles: 10 + 11 + 6 = 27
|
||||
// bytes: 5
|
||||
List(
|
||||
ZLine.ldImm16(HL, 0x10000 - localVariableArea),
|
||||
ZLine.registers(ADD_16, HL, SP),
|
||||
ZLine.ld16(SP, HL))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,39 +100,69 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
false
|
||||
}
|
||||
|
||||
def fixTsx(code: List[ZLine]): List[ZLine] = code match {
|
||||
case (ldhlsp@ZLine(LD_HLSP, _, param, _)) :: xs => ldhlsp.copy(parameter = param + 2) :: fixTsx(xs)
|
||||
case (ldhl@ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), param, _)) ::
|
||||
(addhlsp@ZLine(ADD_16, TwoRegisters(ZRegister.HL, ZRegister.SP), _, _)) ::
|
||||
(ldsphl@ZLine(LD_16, TwoRegisters(ZRegister.SP, ZRegister.HL), _, _)) ::
|
||||
xs => ??? // TODO: ldhl :: addhlsp :: ldsphl :: fixTsx(xs)
|
||||
case (ldhl@ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), param, _)) ::
|
||||
(addhlsp@ZLine(ADD_16, TwoRegisters(ZRegister.HL, ZRegister.SP), _, _)) ::
|
||||
xs => ldhl.copy(parameter = param + 2) :: addhlsp :: fixTsx(xs)
|
||||
case (x@ZLine(EX_SP, _, _, _)) :: xs =>
|
||||
// EX_SP should be only generated by the optimizer
|
||||
ErrorReporting.info("Stray EX (SP) encountered, generated code might be invalid")
|
||||
x :: fixTsx(xs)
|
||||
case x :: xs => x :: fixTsx(xs)
|
||||
case Nil => Nil
|
||||
}
|
||||
|
||||
def stashAFIfChanged(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesAF))
|
||||
ZLine.register(PUSH, ZRegister.AF) :: (lines :+ ZLine.register(POP, ZRegister.AF)) else lines
|
||||
ZLine.register(PUSH, ZRegister.AF) :: (fixTsx(lines) :+ ZLine.register(POP, ZRegister.AF)) else lines
|
||||
|
||||
def stashAFIfChangedF(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesF))
|
||||
ZLine.register(PUSH, ZRegister.AF) :: (lines :+ ZLine.register(POP, ZRegister.AF)) else lines
|
||||
ZLine.register(PUSH, ZRegister.AF) :: (fixTsx(lines) :+ ZLine.register(POP, ZRegister.AF)) else lines
|
||||
|
||||
def stashBCIfChanged(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesBC))
|
||||
ZLine.register(PUSH, ZRegister.BC) :: (lines :+ ZLine.register(POP, ZRegister.BC)) else lines
|
||||
ZLine.register(PUSH, ZRegister.BC) :: (fixTsx(lines) :+ ZLine.register(POP, ZRegister.BC)) else lines
|
||||
|
||||
def stashDEIfChanged(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesDE))
|
||||
ZLine.register(PUSH, ZRegister.DE) :: (lines :+ ZLine.register(POP, ZRegister.DE)) else lines
|
||||
ZLine.register(PUSH, ZRegister.DE) :: (fixTsx(lines) :+ ZLine.register(POP, ZRegister.DE)) else lines
|
||||
|
||||
def stashHLIfChanged(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesHL))
|
||||
ZLine.register(PUSH, ZRegister.HL) :: (lines :+ ZLine.register(POP, ZRegister.HL)) else lines
|
||||
ZLine.register(PUSH, ZRegister.HL) :: (fixTsx(lines) :+ ZLine.register(POP, ZRegister.HL)) else lines
|
||||
|
||||
def targetifyA(target: ZExpressionTarget.Value, lines: List[ZLine], isSigned: Boolean): List[ZLine] = {
|
||||
def toWord(h:ZRegister.Value, l: ZRegister.Value) ={
|
||||
def targetifyA(ctx: CompilationContext, target: ZExpressionTarget.Value, lines: List[ZLine], isSigned: Boolean): List[ZLine] = {
|
||||
def toWord(h: ZRegister.Value, l: ZRegister.Value) = {
|
||||
lines ++ (if (isSigned) {
|
||||
val label = Z80Compiler.nextLabel("sx")
|
||||
List(
|
||||
ZLine.ld8(l, ZRegister.A),
|
||||
ZLine.ldImm8(h, 0xff),
|
||||
ZLine.imm8(OR, 0x7f),
|
||||
ZLine.jump(label, IfFlagSet(ZFlag.S)), // TODO: gameboy has no S flag
|
||||
ZLine.ldImm8(h, 0),
|
||||
ZLine.label(label))
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(l, ZRegister.A),
|
||||
ZLine.ldImm8(h, 0))
|
||||
})
|
||||
val label = Z80Compiler.nextLabel("sx")
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(
|
||||
ZLine.ld8(l, ZRegister.A),
|
||||
ZLine.ldImm8(h, 0xff),
|
||||
ZLine.imm8(OR, 0x7f),
|
||||
ZLine.jump(label, IfFlagSet(ZFlag.S)),
|
||||
ZLine.ldImm8(h, 0),
|
||||
ZLine.label(label))
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
// TODO: is this optimal?
|
||||
List(
|
||||
ZLine.ld8(l, ZRegister.A),
|
||||
ZLine.ldImm8(h, 0xff),
|
||||
ZLine.register(BIT7, ZRegister.A),
|
||||
ZLine.jump(label, IfFlagClear(ZFlag.Z)),
|
||||
ZLine.ldImm8(h, 0),
|
||||
ZLine.label(label))
|
||||
} else {
|
||||
throw new IllegalStateException()
|
||||
}
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(l, ZRegister.A),
|
||||
ZLine.ldImm8(h, 0))
|
||||
})
|
||||
}
|
||||
|
||||
target match {
|
||||
case ZExpressionTarget.NOTHING | ZExpressionTarget.A => lines
|
||||
case ZExpressionTarget.HL => toWord(ZRegister.H, ZRegister.L)
|
||||
@ -141,7 +171,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def targetifyHL(target: ZExpressionTarget.Value, lines: List[ZLine]): List[ZLine] = target match {
|
||||
def targetifyHL(ctx: CompilationContext, target: ZExpressionTarget.Value, lines: List[ZLine]): List[ZLine] = target match {
|
||||
case ZExpressionTarget.NOTHING | ZExpressionTarget.HL => lines
|
||||
case ZExpressionTarget.A => lines :+ ZLine.ld8(ZRegister.A, ZRegister.L)
|
||||
case ZExpressionTarget.BC => lines ++ List(ZLine.ld8(ZRegister.C, ZRegister.L), ZLine.ld8(ZRegister.B, ZRegister.H))
|
||||
@ -173,31 +203,73 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
case VariableExpression(name) =>
|
||||
env.get[Variable](name) match {
|
||||
case v: VariableInMemory =>
|
||||
import ZRegister._
|
||||
v.typ.size match {
|
||||
case 0 => ???
|
||||
case 1 => loadByte(v.toAddress, target)
|
||||
case 2 => target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.HL => List(ZLine.ldAbs16(ZRegister.HL, v))
|
||||
case ZExpressionTarget.BC => List(ZLine.ldAbs16(ZRegister.BC, v))
|
||||
case ZExpressionTarget.DE => List(ZLine.ldAbs16(ZRegister.DE, v))
|
||||
case ZExpressionTarget.HL =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(ZLine.ldAbs16(HL, v))
|
||||
} else {
|
||||
// TODO: is it optimal?
|
||||
List(ZLine.ldAbs8(A, v), ZLine.ld8(L, A), ZLine.ldAbs8(A, v.toAddress + 1), ZLine.ld8(H, A))
|
||||
}
|
||||
case ZExpressionTarget.BC =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
|
||||
List(ZLine.ldAbs16(BC, v))
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(ZLine.ldAbs16(HL, v), ZLine.ld8(B, H), ZLine.ld8(C, L))
|
||||
} else {
|
||||
// TODO: is it optimal?
|
||||
List(ZLine.ldAbs8(A, v), ZLine.ld8(C, A), ZLine.ldAbs8(A, v.toAddress + 1), ZLine.ld8(B, A))
|
||||
}
|
||||
case ZExpressionTarget.DE =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
|
||||
List(ZLine.ldAbs16(DE, v))
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(ZLine.ldAbs16(HL, v), ZLine.ld8(D, H), ZLine.ld8(E, L))
|
||||
} else {
|
||||
// TODO: is it optimal?
|
||||
List(ZLine.ldAbs8(A, v), ZLine.ld8(E, A), ZLine.ldAbs8(A, v.toAddress + 1), ZLine.ld8(D, A))
|
||||
}
|
||||
}
|
||||
case _ => ???
|
||||
}
|
||||
case v: StackVariable =>
|
||||
v.typ.size match {
|
||||
case 0 => ???
|
||||
case 1 => loadByteViaIX(v.baseOffset, target)
|
||||
case 2 => target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.HL =>
|
||||
List(ZLine.ldViaIx(ZRegister.L, v.baseOffset), ZLine.ldViaIx(ZRegister.H, v.baseOffset + 1))
|
||||
case ZExpressionTarget.BC =>
|
||||
List(ZLine.ldViaIx(ZRegister.C, v.baseOffset), ZLine.ldViaIx(ZRegister.B, v.baseOffset + 1))
|
||||
case ZExpressionTarget.DE =>
|
||||
List(ZLine.ldViaIx(ZRegister.E, v.baseOffset), ZLine.ldViaIx(ZRegister.D, v.baseOffset + 1))
|
||||
if (ctx.options.flag(CompilationFlag.UseIxForStack)) {
|
||||
v.typ.size match {
|
||||
case 0 => ???
|
||||
case 1 => loadByteViaIX(v.baseOffset, target)
|
||||
case 2 => target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.HL =>
|
||||
List(ZLine.ldViaIx(ZRegister.L, v.baseOffset), ZLine.ldViaIx(ZRegister.H, v.baseOffset + 1))
|
||||
case ZExpressionTarget.BC =>
|
||||
List(ZLine.ldViaIx(ZRegister.C, v.baseOffset), ZLine.ldViaIx(ZRegister.B, v.baseOffset + 1))
|
||||
case ZExpressionTarget.DE =>
|
||||
List(ZLine.ldViaIx(ZRegister.E, v.baseOffset), ZLine.ldViaIx(ZRegister.D, v.baseOffset + 1))
|
||||
}
|
||||
case _ => ???
|
||||
}
|
||||
} else {
|
||||
val loadHL = calculateStackAddressToHL(ctx, v)
|
||||
import ZRegister._
|
||||
v.typ.size match {
|
||||
case 0 => ???
|
||||
case 1 => loadHL ++ loadByteViaHL(target)
|
||||
case 2 => target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.HL =>
|
||||
loadHL ++ List(ZLine.ld8(A,MEM_HL), ZLine.register(INC_16, HL), ZLine.ld8(H, MEM_HL), ZLine.ld8(L, A))
|
||||
case ZExpressionTarget.BC =>
|
||||
loadHL ++ List(ZLine.ld8(C,MEM_HL), ZLine.register(INC_16, HL), ZLine.ld8(B, MEM_HL))
|
||||
case ZExpressionTarget.DE =>
|
||||
loadHL ++ List(ZLine.ld8(E,MEM_HL), ZLine.register(INC_16, HL), ZLine.ld8(D, MEM_HL))
|
||||
}
|
||||
case _ => ???
|
||||
}
|
||||
case _ => ???
|
||||
}
|
||||
}
|
||||
case i: IndexedExpression =>
|
||||
@ -207,19 +279,29 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
case SumExpression(params, decimal) =>
|
||||
getArithmeticParamMaxSize(ctx, params.map(_._2)) match {
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitSum(ctx, params, decimal), isSigned = false)
|
||||
case 2 => targetifyHL(target, ZBuiltIns.compile16BitSum(ctx, params, decimal))
|
||||
case 1 => targetifyA(ctx, target, ZBuiltIns.compile8BitSum(ctx, params, decimal), isSigned = false)
|
||||
case 2 => targetifyHL(ctx, target, ZBuiltIns.compile16BitSum(ctx, params, decimal))
|
||||
}
|
||||
case SeparateBytesExpression(h, l) =>
|
||||
val hi = compileToA(ctx, h)
|
||||
val lo = compileToA(ctx, l)
|
||||
if (!lo.exists(x => x.changesRegister(ZRegister.H) || x.readsRegister(ZRegister.H))) {
|
||||
hi ++ List(ZLine.ld8(ZRegister.H, ZRegister.A)) ++
|
||||
lo ++ List(ZLine.ld8(ZRegister.L, ZRegister.A))
|
||||
} else if (!hi.exists(x => x.changesRegister(ZRegister.L) || x.readsRegister(ZRegister.L))) {
|
||||
lo ++ List(ZLine.ld8(ZRegister.L, ZRegister.A)) ++
|
||||
hi ++ List(ZLine.ld8(ZRegister.H, ZRegister.A))
|
||||
} else ???
|
||||
|
||||
def xxx(hr: ZRegister.Value, lr: ZRegister.Value): List[ZLine] = {
|
||||
if (!lo.exists(x => x.changesRegister(hr) || x.readsRegister(hr))) {
|
||||
hi ++ List(ZLine.ld8(hr, ZRegister.A)) ++
|
||||
lo ++ List(ZLine.ld8(lr, ZRegister.A))
|
||||
} else if (!hi.exists(x => x.changesRegister(lr) || x.readsRegister(lr))) {
|
||||
lo ++ List(ZLine.ld8(lr, ZRegister.A)) ++
|
||||
hi ++ List(ZLine.ld8(hr, ZRegister.A))
|
||||
} else ???
|
||||
}
|
||||
|
||||
target match {
|
||||
case ZExpressionTarget.NOTHING => hi ++ lo
|
||||
case ZExpressionTarget.HL => xxx(ZRegister.H, ZRegister.L)
|
||||
case ZExpressionTarget.DE => xxx(ZRegister.D, ZRegister.E)
|
||||
case ZExpressionTarget.BC => xxx(ZRegister.B, ZRegister.C)
|
||||
}
|
||||
case f@FunctionCallExpression(name, params) =>
|
||||
name match {
|
||||
case "not" =>
|
||||
@ -266,7 +348,11 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
ZLine.ldImm8(ZRegister.H, 0),
|
||||
ZLine.register(RL, ZRegister.H))
|
||||
} else {
|
||||
???
|
||||
List(
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.A, 0),
|
||||
ZLine.register(RL, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.A))
|
||||
}
|
||||
case ZExpressionTarget.BC =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
@ -275,16 +361,24 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
ZLine.ldImm8(ZRegister.B, 0),
|
||||
ZLine.register(RL, ZRegister.B))
|
||||
} else {
|
||||
???
|
||||
List(
|
||||
ZLine.ld8(ZRegister.C, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.A, 0),
|
||||
ZLine.register(RL, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.B, ZRegister.A))
|
||||
}
|
||||
case ZExpressionTarget.DE =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.C, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.E, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.B, 0),
|
||||
ZLine.register(RL, ZRegister.B))
|
||||
ZLine.register(RL, ZRegister.D))
|
||||
} else {
|
||||
???
|
||||
List(
|
||||
ZLine.ld8(ZRegister.E, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.A, 0),
|
||||
ZLine.register(RL, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.D, ZRegister.A))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -314,43 +408,43 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
|
||||
case "&" =>
|
||||
getArithmeticParamMaxSize(ctx, params) match {
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, AND, params), isSigned = false)
|
||||
case 2 => targetifyHL(target, ZBuiltIns.compile16BitOperation(ctx, AND, params))
|
||||
case 1 => targetifyA(ctx, target, ZBuiltIns.compile8BitOperation(ctx, AND, params), isSigned = false)
|
||||
case 2 => targetifyHL(ctx, target, ZBuiltIns.compile16BitOperation(ctx, AND, params))
|
||||
}
|
||||
case "*" =>
|
||||
assertAllArithmeticBytes("Long multiplication not supported", ctx, params)
|
||||
targetifyA(target, Z80Multiply.compile8BitMultiply(ctx, params), isSigned = false)
|
||||
targetifyA(ctx, target, Z80Multiply.compile8BitMultiply(ctx, params), isSigned = false)
|
||||
case "|" =>
|
||||
getArithmeticParamMaxSize(ctx, params) match {
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, OR, params), isSigned = false)
|
||||
case 2 => targetifyHL(target, ZBuiltIns.compile16BitOperation(ctx, OR, params))
|
||||
case 1 => targetifyA(ctx, target, ZBuiltIns.compile8BitOperation(ctx, OR, params), isSigned = false)
|
||||
case 2 => targetifyHL(ctx, target, ZBuiltIns.compile16BitOperation(ctx, OR, params))
|
||||
}
|
||||
case "^" =>
|
||||
getArithmeticParamMaxSize(ctx, params) match {
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, XOR, params), isSigned = false)
|
||||
case 2 => targetifyHL(target, ZBuiltIns.compile16BitOperation(ctx, XOR, params))
|
||||
case 1 => targetifyA(ctx, target, ZBuiltIns.compile8BitOperation(ctx, XOR, params), isSigned = false)
|
||||
case 2 => targetifyHL(ctx, target, ZBuiltIns.compile16BitOperation(ctx, XOR, params))
|
||||
}
|
||||
case ">>>>" =>
|
||||
val (l, r, 2) = assertArithmeticBinary(ctx, params)
|
||||
targetifyA(target, compileToHL(ctx, l) ++ Z80Shifting.compileNonetShiftRight(ctx, r), isSigned = false)
|
||||
targetifyA(ctx, target, compileToHL(ctx, l) ++ Z80Shifting.compileNonetShiftRight(ctx, r), isSigned = false)
|
||||
case "<<" =>
|
||||
val (l, r, size) = assertArithmeticBinary(ctx, params)
|
||||
size match {
|
||||
case 1 => targetifyA(target, Z80Shifting.compile8BitShift(ctx, l, r, left = true), isSigned = false)
|
||||
case 1 => targetifyA(ctx, target, Z80Shifting.compile8BitShift(ctx, l, r, left = true), isSigned = false)
|
||||
case 2 => Z80Shifting.compile16BitShift(ctx, l, r, left = true)
|
||||
case _ => ???
|
||||
}
|
||||
case ">>" =>
|
||||
val (l, r, size) = assertArithmeticBinary(ctx, params)
|
||||
size match {
|
||||
case 1 => targetifyA(target, Z80Shifting.compile8BitShift(ctx, l, r, left = false), isSigned = false)
|
||||
case 1 => targetifyA(ctx, target, Z80Shifting.compile8BitShift(ctx, l, r, left = false), isSigned = false)
|
||||
case 2 => Z80Shifting.compile16BitShift(ctx, l, r, left = false)
|
||||
case _ => ???
|
||||
}
|
||||
case "<<'" =>
|
||||
assertAllArithmeticBytes("Long shift ops not supported", ctx, params)
|
||||
val (l, r, 1) = assertArithmeticBinary(ctx, params)
|
||||
targetifyA(target, compileToA(ctx, l) ++ Z80DecimalBuiltIns.compileByteShiftLeft(ctx, r), isSigned = false)
|
||||
targetifyA(ctx, target, compileToA(ctx, l) ++ Z80DecimalBuiltIns.compileByteShiftLeft(ctx, r), isSigned = false)
|
||||
case ">>'" =>
|
||||
assertAllArithmeticBytes("Long shift ops not supported", ctx, params)
|
||||
val (l, r, 1) = assertArithmeticBinary(ctx, params)
|
||||
@ -519,8 +613,8 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
failed = true
|
||||
}
|
||||
return sourceType.size match {
|
||||
case 1 => targetifyA(target, compileToA(ctx, params.head), isSigned = sourceType.isSigned)
|
||||
case 2 => targetifyHL(target, compileToHL(ctx, params.head))
|
||||
case 1 => targetifyA(ctx, target, compileToA(ctx, params.head), isSigned = sourceType.isSigned)
|
||||
case 2 => targetifyHL(ctx, target, compileToHL(ctx, params.head))
|
||||
case _ => ???
|
||||
}
|
||||
case None =>
|
||||
@ -573,9 +667,9 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
function.returnType.size match {
|
||||
case 1 =>
|
||||
targetifyA(target, result, isSigned = function.returnType.isSigned)
|
||||
targetifyA(ctx, target, result, isSigned = function.returnType.isSigned)
|
||||
case 2 =>
|
||||
targetifyHL(target, result)
|
||||
targetifyHL(ctx, target, result)
|
||||
case _ =>
|
||||
result
|
||||
}
|
||||
@ -610,7 +704,12 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
case VariableExpression(name) =>
|
||||
env.get[Variable](name) match {
|
||||
case v:VariableInMemory => Some(LocalVariableAddressViaHL -> List(ZLine.ldImm16(ZRegister.HL, v.toAddress)))
|
||||
case v:StackVariable => Some(LocalVariableAddressViaIX(v.baseOffset) -> Nil)
|
||||
case v:StackVariable =>
|
||||
if (ctx.options.flag(CompilationFlag.UseIxForStack)){
|
||||
Some(LocalVariableAddressViaIX(v.baseOffset) -> Nil)
|
||||
} else {
|
||||
Some(LocalVariableAddressViaHL -> calculateStackAddressToHL(ctx, v))
|
||||
}
|
||||
}
|
||||
case i:IndexedExpression => Some(LocalVariableAddressViaHL -> calculateAddressToHL(ctx, i))
|
||||
case _:SeparateBytesExpression => None
|
||||
@ -618,6 +717,16 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def calculateStackAddressToHL(ctx: CompilationContext, v: StackVariable): List[ZLine] = {
|
||||
if (ctx.options.flag(CompilationFlag.UseIxForStack)) {
|
||||
???
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitSharpOpcodes)) {
|
||||
List(ZLine.imm8(ZOpcode.LD_HLSP, v.baseOffset + ctx.extraStackOffset))
|
||||
} else {
|
||||
List(ZLine.ldImm16(ZRegister.HL, v.baseOffset + ctx.extraStackOffset), ZLine.registers(ZOpcode.ADD_16, ZRegister.HL, ZRegister.SP))
|
||||
}
|
||||
}
|
||||
|
||||
def calculateAddressToHL(ctx: CompilationContext, i: IndexedExpression): List[ZLine] = {
|
||||
val env = ctx.env
|
||||
val pointy = env.getPointy(i.name)
|
||||
@ -632,19 +741,38 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
List(ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
}
|
||||
case VariablePointy(varAddr, _, _) =>
|
||||
compileToHL(ctx, i.index) ++
|
||||
loadBCFromHL ++
|
||||
List(
|
||||
ZLine.ldAbs16(ZRegister.HL, varAddr),
|
||||
ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
env.eval(i.index) match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(ZLine.ldAbs16(ZRegister.HL, varAddr))
|
||||
} else {
|
||||
// TODO: is this reasonable?
|
||||
List(
|
||||
ZLine.ldAbs8(ZRegister.A, varAddr),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldAbs8(ZRegister.A, varAddr + 1),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.A))
|
||||
}
|
||||
case _ =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
compileToBC(ctx, i.index) ++
|
||||
List(
|
||||
ZLine.ldAbs16(ZRegister.HL, varAddr),
|
||||
ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
} else {
|
||||
// TODO: is this reasonable?
|
||||
compileToBC(ctx, i.index) ++
|
||||
List(
|
||||
ZLine.ldAbs8(ZRegister.A, varAddr),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldAbs8(ZRegister.A, varAddr + 1),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.A),
|
||||
ZLine.registers(ADD_16, ZRegister.HL, ZRegister.BC))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val loadBCFromHL = List(
|
||||
ZLine.ld8(ZRegister.B, ZRegister.H),
|
||||
ZLine.ld8(ZRegister.C, ZRegister.L)
|
||||
)
|
||||
|
||||
def loadByte(sourceAddr: Constant, target: ZExpressionTarget.Value): List[ZLine] = {
|
||||
target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
@ -675,10 +803,10 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
}
|
||||
|
||||
def signExtend(targetAddr: Constant, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
||||
def signExtend(ctx: CompilationContext, targetAddr: Constant, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
||||
if (bytes == 0) return Nil
|
||||
val prepareA = if (signedSource) {
|
||||
signExtendHighestByte(hiRegister)
|
||||
signExtendHighestByte(ctx, hiRegister)
|
||||
} else {
|
||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||
}
|
||||
@ -686,20 +814,32 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
prepareA ++ fillUpperBytes
|
||||
}
|
||||
|
||||
private def signExtendHighestByte(hiRegister: ZRegister.Value) = {
|
||||
private def signExtendHighestByte(ctx: CompilationContext, hiRegister: ZRegister.Value): List[ZLine] = {
|
||||
val prefix = if (hiRegister == ZRegister.A) Nil else List(ZLine.ld8(ZRegister.A, hiRegister))
|
||||
val label = Z80Compiler.nextLabel("sx")
|
||||
prefix ++ List(
|
||||
ZLine.imm8(OR, 0x7f),
|
||||
ZLine.jump(label, IfFlagSet(ZFlag.S)),
|
||||
ZLine.ldImm8(ZRegister.A, 0),
|
||||
ZLine.label(label))
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
prefix ++ List(
|
||||
ZLine.imm8(OR, 0x7f),
|
||||
ZLine.jump(label, IfFlagSet(ZFlag.S)),
|
||||
ZLine.ldImm8(ZRegister.A, 0),
|
||||
ZLine.label(label))
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
// TODO: is this optimal?
|
||||
prefix ++ List(
|
||||
ZLine.imm8(OR, 0x7f),
|
||||
ZLine.register(BIT7, ZRegister.A),
|
||||
ZLine.jump(label, IfFlagClear(ZFlag.Z)),
|
||||
ZLine.ldImm8(ZRegister.A, 0),
|
||||
ZLine.label(label))
|
||||
} else {
|
||||
throw new IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
def signExtendViaIX(targetOffset: Int, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
||||
def signExtendViaIX(ctx: CompilationContext, targetOffset: Int, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
||||
if (bytes == 0) return Nil
|
||||
val prepareA = if (signedSource) {
|
||||
signExtendHighestByte(hiRegister)
|
||||
signExtendHighestByte(ctx, hiRegister)
|
||||
} else {
|
||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||
}
|
||||
@ -707,39 +847,76 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
prepareA ++ fillUpperBytes
|
||||
}
|
||||
|
||||
def storeA(targetAddr: Constant, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
def signExtendViaHL(ctx: CompilationContext, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
||||
if (bytes == 0) return Nil
|
||||
val prepareA = if (signedSource) {
|
||||
signExtendHighestByte(ctx, hiRegister)
|
||||
} else {
|
||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||
}
|
||||
val fillUpperBytes = List.fill(bytes)(List(ZLine.register(INC_16, ZRegister.HL), ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))).flatten
|
||||
prepareA ++ fillUpperBytes
|
||||
}
|
||||
|
||||
def storeA(ctx: CompilationContext, targetAddr: Constant, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ldAbs8(targetAddr, ZRegister.A))
|
||||
case n => ZLine.ldAbs8(targetAddr, ZRegister.A) :: signExtend(targetAddr + 1, ZRegister.A, n - 1, signedSource)
|
||||
case n => ZLine.ldAbs8(targetAddr, ZRegister.A) :: signExtend(ctx, targetAddr + 1, ZRegister.A, n - 1, signedSource)
|
||||
}
|
||||
}
|
||||
|
||||
def storeAViaIX(targetOffset: Int, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
def storeAViaHL(ctx: CompilationContext, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
case n => ZLine.ld8(ZRegister.MEM_HL, ZRegister.A) :: signExtendViaHL(ctx, ZRegister.A, n - 1, signedSource)
|
||||
}
|
||||
}
|
||||
|
||||
def storeAViaIX(ctx: CompilationContext, targetOffset: Int, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ldViaIx(targetOffset, ZRegister.A))
|
||||
case n => ZLine.ldViaIx(targetOffset, ZRegister.A) :: signExtendViaIX(targetOffset + 1, ZRegister.A, n - 1, signedSource)
|
||||
case n => ZLine.ldViaIx(targetOffset, ZRegister.A) :: signExtendViaIX(ctx, targetOffset + 1, ZRegister.A, n - 1, signedSource)
|
||||
}
|
||||
}
|
||||
|
||||
def storeHL(targetAddr: Constant, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
def storeHL(ctx: CompilationContext, targetAddr: Constant, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
// TODO: LD (nnnn),HL compatibility?
|
||||
targetSize match {
|
||||
case 0 => Nil
|
||||
case 1 => List(ZLine.ld8(ZRegister.A, ZRegister.L), ZLine.ldAbs8(targetAddr, ZRegister.A))
|
||||
case 2 => List(ZLine.ldAbs16(targetAddr, ZRegister.HL))
|
||||
case n => ZLine.ldAbs16(targetAddr, ZRegister.HL) :: signExtend(targetAddr + 2, ZRegister.H, n - 2, signedSource)
|
||||
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)
|
||||
} 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)) ++ signExtend(ctx, targetAddr + 2, ZRegister.H, n - 2, signedSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def storeHLViaIX(offset: Int, targetSize: Int, signedSource: Boolean): List[ZLine] = {
|
||||
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(offset + 2, ZRegister.H, n - 2, signedSource)
|
||||
case n => List(ZLine.ldViaIx(offset, ZRegister.L), ZLine.ldViaIx(offset + 1, ZRegister.H)) ++ signExtendViaIX(ctx, offset + 2, ZRegister.H, n - 2, signedSource)
|
||||
}
|
||||
}
|
||||
|
||||
@ -748,12 +925,17 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
target match {
|
||||
case VariableExpression(vname) =>
|
||||
env.get[Variable](vname) match {
|
||||
case v: VariableInMemory => storeA(v.toAddress, v.typ.size, signedSource)
|
||||
case v: StackVariable => storeAViaIX(v.baseOffset, v.typ.size, signedSource)
|
||||
case v: VariableInMemory => storeA(ctx, v.toAddress, v.typ.size, signedSource)
|
||||
case v: StackVariable =>
|
||||
if (ctx.options.flag(CompilationFlag.UseIxForStack)){
|
||||
storeAViaIX(ctx, v.baseOffset, v.typ.size, signedSource)
|
||||
} else {
|
||||
calculateStackAddressToHL(ctx, v) ++ storeAViaHL(ctx, v.typ.size, signedSource)
|
||||
}
|
||||
}
|
||||
case i:IndexedExpression =>
|
||||
calculateAddressToHL(ctx, i) match {
|
||||
case List(ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr, _)) => storeA(addr, 1, signedSource)
|
||||
case List(ZLine(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr, _)) => storeA(ctx, addr, 1, signedSource)
|
||||
case code => if (code.exists(changesA)) {
|
||||
List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++ stashDEIfChanged(code) :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.E)
|
||||
} else code :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
|
||||
@ -768,14 +950,39 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
target match {
|
||||
case VariableExpression(vname) =>
|
||||
env.get[Variable](vname) match {
|
||||
case v: VariableInMemory => storeHL(v.toAddress, v.typ.size, signedSource)
|
||||
case v: StackVariable => storeHLViaIX(v.baseOffset, v.typ.size, signedSource)
|
||||
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.EmitIntel8080Opcodes)) {
|
||||
List(
|
||||
ZLine.register(PUSH, DE),
|
||||
ZLine.implied(EX_DE_HL)) ++
|
||||
fixTsx(calculateStackAddressToHL(ctx, v)) ++
|
||||
List(
|
||||
ZLine.ld8(MEM_HL, E),
|
||||
ZLine.register(INC_16, HL),
|
||||
ZLine.ld8(MEM_HL, D),
|
||||
ZLine.register(POP, DE))
|
||||
} else {
|
||||
List(
|
||||
ZLine.register(PUSH, DE),
|
||||
ZLine.ld8(D, H),
|
||||
ZLine.ld8(E, L)) ++
|
||||
fixTsx(calculateStackAddressToHL(ctx, v)) ++
|
||||
List(
|
||||
ZLine.ld8(MEM_HL, E),
|
||||
ZLine.register(INC_16, HL),
|
||||
ZLine.ld8(MEM_HL, D),
|
||||
ZLine.register(POP, DE))
|
||||
}
|
||||
}
|
||||
case IndexedExpression(pointyName, indexExpr) =>
|
||||
env.getPointy(pointyName) match {
|
||||
case p: ConstantPointy =>
|
||||
env.evalVariableAndConstantSubParts(indexExpr) match {
|
||||
case (None, offset) => ZLine.ld8(ZRegister.A, ZRegister.L) :: storeA((p.value + offset).quickSimplify, 1, signedSource)
|
||||
case (None, offset) => ZLine.ld8(ZRegister.A, ZRegister.L) :: storeA(ctx, (p.value + offset).quickSimplify, 1, signedSource)
|
||||
}
|
||||
}
|
||||
case SeparateBytesExpression(hi: LhsExpression, lo: LhsExpression) =>
|
||||
@ -794,7 +1001,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
env.get[Variable](vname) match {
|
||||
case v: Variable =>
|
||||
val size = v.typ.size
|
||||
compileByteReads(ctx, source, size, ZExpressionTarget.HL).zip(compileByteStores(ctx, target, size)).flatMap(t => t._1 ++ t._2)
|
||||
compileByteReads(ctx, source, size, ZExpressionTarget.HL).zip(compileByteStores(ctx, target, size, includeStep = false)).flatMap(t => t._1 ++ t._2)
|
||||
}
|
||||
case _ => ???
|
||||
}
|
||||
@ -818,7 +1025,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
case IndexedExpression(_, VariableExpression(_)) =>
|
||||
case IndexedExpression(_, LiteralExpression(_, _)) =>
|
||||
case IndexedExpression(_, GeneratedConstantExpression(_, _)) =>
|
||||
case IndexedExpression(_, SumExpression(params, false)) if isUpToOneVar(params) =>
|
||||
case IndexedExpression(_, SumExpression(sumParams, false)) if isUpToOneVar(sumParams) =>
|
||||
case _ =>
|
||||
ErrorReporting.warn("A complex expression may be evaluated multiple times", ctx.options, e.position)
|
||||
}
|
||||
@ -845,19 +1052,35 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
if (i < v.typ.size) {
|
||||
List(ZLine.ldAbs8(ZRegister.A, v.toAddress + i))
|
||||
} else if (v.typ.isSigned) {
|
||||
ZLine.ldAbs8(ZRegister.A, v.toAddress + v.typ.size - 1) :: signExtendHighestByte(ZRegister.A)
|
||||
ZLine.ldAbs8(ZRegister.A, v.toAddress + v.typ.size - 1) :: signExtendHighestByte(ctx, ZRegister.A)
|
||||
} else {
|
||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||
}
|
||||
}
|
||||
case v: StackVariable =>
|
||||
List.tabulate(size) { i =>
|
||||
if (i < v.typ.size) {
|
||||
List(ZLine.ldViaIx(ZRegister.A, v.baseOffset + i))
|
||||
} else if (v.typ.isSigned) {
|
||||
ZLine.ldViaIx(ZRegister.A, v.baseOffset + v.typ.size - 1) :: signExtendHighestByte(ZRegister.A)
|
||||
} else {
|
||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||
import ZRegister._
|
||||
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
|
||||
List.tabulate(size) { i =>
|
||||
if (i < v.typ.size) {
|
||||
List(ZLine.ldViaIx(A, v.baseOffset + i))
|
||||
} else if (v.typ.isSigned) {
|
||||
ZLine.ldViaIx(A, v.baseOffset + v.typ.size - 1) :: signExtendHighestByte(ctx, A)
|
||||
} else {
|
||||
List(ZLine.ldImm8(A, 0))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val prepareHL = calculateStackAddressToHL(ctx, v)
|
||||
List.tabulate(size) { i =>
|
||||
if (i == 0) {
|
||||
prepareHL :+ ZLine.ld8(A, MEM_HL)
|
||||
} else if (i < v.typ.size) {
|
||||
List(ZLine.register(INC_16, HL), ZLine.ld8(A, MEM_HL))
|
||||
} else if (v.typ.isSigned) {
|
||||
ZLine.ld8(A, MEM_HL) :: signExtendHighestByte(ctx, ZRegister.A)
|
||||
} else {
|
||||
List(ZLine.ldImm8(A, 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -885,7 +1108,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
if (i == 0) {
|
||||
if (typ.isSigned) {
|
||||
(compileToA(ctx, rhs) :+ ZLine.ld8(l, ZRegister.A)) ++
|
||||
signExtendHighestByte(ZRegister.A) ++ List(ZLine.ld8(h, ZRegister.A), ZLine.ld8(ZRegister.A, l))
|
||||
signExtendHighestByte(ctx, ZRegister.A) ++ List(ZLine.ld8(h, ZRegister.A), ZLine.ld8(ZRegister.A, l))
|
||||
} else {
|
||||
compileToA(ctx, rhs)
|
||||
}
|
||||
@ -914,7 +1137,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
|
||||
|
||||
def compileByteStores(ctx: CompilationContext, lhs: LhsExpression, size: Int): List[List[ZLine]] = {
|
||||
def compileByteStores(ctx: CompilationContext, lhs: LhsExpression, size: Int, includeStep: Boolean): List[List[ZLine]] = {
|
||||
if (size == 1) throw new IllegalArgumentException
|
||||
val env = ctx.env
|
||||
lhs match {
|
||||
@ -935,11 +1158,24 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
if (v.typ.size < size) {
|
||||
ErrorReporting.error(s"Variable `$vname` is too small", lhs.position)
|
||||
}
|
||||
List.tabulate(size) { i =>
|
||||
if (i < size) {
|
||||
List(ZLine.ldViaIx(v.baseOffset + i, ZRegister.A))
|
||||
} else {
|
||||
Nil
|
||||
import ZRegister._
|
||||
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
|
||||
List.tabulate(size) { i =>
|
||||
if (i < size) {
|
||||
List(ZLine.ldViaIx(v.baseOffset + i, ZRegister.A))
|
||||
} else {
|
||||
Nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val prepareHL = calculateStackAddressToHL(ctx, v)
|
||||
List.tabulate(size) { i =>
|
||||
if (i == 0) {
|
||||
prepareHL :+ ZLine.ld8(MEM_HL, A)
|
||||
} else if (i < v.typ.size) {
|
||||
if (includeStep) List(ZLine.register(INC_16, HL), ZLine.ld8(MEM_HL, A))
|
||||
else List(ZLine.ld8(MEM_HL, A))
|
||||
} else Nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package millfork.compiler.z80
|
||||
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.z80._
|
||||
import millfork.compiler.{BranchSpec, CompilationContext}
|
||||
import millfork.env.{CompoundConstant, Constant, MathOperator, NumericConstant}
|
||||
@ -17,21 +18,48 @@ object Z80Multiply {
|
||||
import millfork.assembly.z80.ZOpcode._
|
||||
import ZRegister._
|
||||
import ZLine._
|
||||
val lblAdd = Z80Compiler.nextLabel("mu")
|
||||
val lblLoop = Z80Compiler.nextLabel("mu")
|
||||
val lblStart = Z80Compiler.nextLabel("mu")
|
||||
List(
|
||||
ld8(E, A),
|
||||
ldImm8(A, 0),
|
||||
jumpR(ctx, lblStart),
|
||||
label(lblAdd),
|
||||
register(ADD, E),
|
||||
label(lblLoop),
|
||||
register(SLA, E),
|
||||
label(lblStart),
|
||||
register(SRL, D),
|
||||
jumpR(ctx, lblAdd, IfFlagSet(ZFlag.C)),
|
||||
jumpR(ctx, lblLoop, IfFlagClear(ZFlag.Z)))
|
||||
if(ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
val lblAdd = Z80Compiler.nextLabel("mu")
|
||||
val lblLoop = Z80Compiler.nextLabel("mu")
|
||||
val lblStart = Z80Compiler.nextLabel("mu")
|
||||
List(
|
||||
ld8(E, A),
|
||||
ldImm8(A, 0),
|
||||
jumpR(ctx, lblStart),
|
||||
label(lblAdd),
|
||||
register(ADD, E),
|
||||
label(lblLoop),
|
||||
register(SLA, E),
|
||||
label(lblStart),
|
||||
register(SRL, D),
|
||||
jumpR(ctx, lblAdd, IfFlagSet(ZFlag.C)),
|
||||
jumpR(ctx, lblLoop, IfFlagClear(ZFlag.Z)))
|
||||
} else {
|
||||
// TODO: optimize
|
||||
val lblAdd = Z80Compiler.nextLabel("mu")
|
||||
val lblLoop = Z80Compiler.nextLabel("mu")
|
||||
val lblStart = Z80Compiler.nextLabel("mu")
|
||||
List(
|
||||
ld8(E, A),
|
||||
ldImm8(C, 0),
|
||||
jumpR(ctx, lblStart),
|
||||
label(lblAdd),
|
||||
ld8(A, C),
|
||||
register(ADD, E),
|
||||
ld8(C, A),
|
||||
label(lblLoop),
|
||||
ld8(A, E),
|
||||
register(ADD, A),
|
||||
ld8(E, A),
|
||||
label(lblStart),
|
||||
register(OR, A),
|
||||
ld8(A, D),
|
||||
register(RR, A),
|
||||
ld8(D, A),
|
||||
jumpR(ctx, lblAdd, IfFlagSet(ZFlag.C)),
|
||||
jumpR(ctx, lblLoop, IfFlagClear(ZFlag.Z)),
|
||||
ld8(A, C))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,11 +145,11 @@ object Z80Multiply {
|
||||
count match {
|
||||
case 0 => List(ZLine.ldImm8(A, 0))
|
||||
case 1 => Nil
|
||||
case n if n > 0 && n.-(1).&(n).==(0) => List.fill(Integer.numberOfTrailingZeros(n))(ZLine.register(SLA, A))
|
||||
case n if n > 0 && n.-(1).&(n).==(0) => List.fill(Integer.numberOfTrailingZeros(n))(ZLine.register(ADD, A))
|
||||
case _ =>
|
||||
ZLine.ld8(E,A) :: Integer.toString(count & 0xff, 2).tail.flatMap{
|
||||
case '0' => List(ZLine.register(SLA, A))
|
||||
case '1' => List(ZLine.register(SLA, A), ZLine.register(ADD, E))
|
||||
case '0' => List(ZLine.register(ADD, A))
|
||||
case '1' => List(ZLine.register(ADD, A), ZLine.register(ADD, E))
|
||||
}.toList
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,8 @@ import scala.collection.GenTraversableOnce
|
||||
object Z80Shifting {
|
||||
|
||||
private def fixAfterShiftIfNeeded(extendedOps: Boolean, left: Boolean, i: Long): List[ZLine] =
|
||||
if (extendedOps) {
|
||||
if (extendedOps || left) {
|
||||
Nil
|
||||
} else if (left) {
|
||||
List(ZLine.imm8(ZOpcode.AND, 0xff & (0xff << i)))
|
||||
} else {
|
||||
List(ZLine.imm8(ZOpcode.AND, 0xff & (0xff >> i)))
|
||||
}
|
||||
@ -28,9 +26,9 @@ object Z80Shifting {
|
||||
val extendedOps = ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)
|
||||
val op =
|
||||
if (extendedOps) {
|
||||
if (left) ZOpcode.SLA else ZOpcode.SRL
|
||||
if (left) ZOpcode.ADD else ZOpcode.SRL
|
||||
} else {
|
||||
if (left) ZOpcode.RLC else ZOpcode.RRC
|
||||
if (left) ZOpcode.ADD else ZOpcode.RRC
|
||||
}
|
||||
val l = Z80ExpressionCompiler.compileToA(ctx, lhs)
|
||||
env.eval(rhs) match {
|
||||
@ -56,9 +54,9 @@ object Z80Shifting {
|
||||
val extendedOps = ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)
|
||||
val op =
|
||||
if (extendedOps) {
|
||||
if (left) ZOpcode.SLA else ZOpcode.SRL
|
||||
if (left) ZOpcode.ADD else ZOpcode.SRL
|
||||
} else {
|
||||
if (left) ZOpcode.RLC else ZOpcode.RRC
|
||||
if (left) ZOpcode.ADD else ZOpcode.RRC
|
||||
}
|
||||
env.eval(rhs) match {
|
||||
case Some(NumericConstant(i, _)) =>
|
||||
@ -69,10 +67,30 @@ object Z80Shifting {
|
||||
} else {
|
||||
Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, lhs) match {
|
||||
case Some((register, l)) =>
|
||||
// SLA A = 8 cycles
|
||||
// SLA (HL) = 15 cycles
|
||||
// for shifting left:
|
||||
// ADD A = 4 cycles
|
||||
// SRL (HL) = 15 cycles
|
||||
// LD A,(HL) or LD (HL),A = 7 cycles
|
||||
// SLA (IX+d) = 23 cycles
|
||||
// SRL (IX+d) = 23 cycles
|
||||
// LD A,(IX+d) or LD (IX+d),A = 19 cycles
|
||||
// when using A is profitable or equal?
|
||||
// for HL:
|
||||
// 15x >= 7 + 4x + 7
|
||||
// 11x >= 14
|
||||
// x >= 14/11
|
||||
// for IX+d
|
||||
// 23x >= 19 + 4x + 19
|
||||
// 19x >= 38
|
||||
// x >= 2
|
||||
// so:
|
||||
// - for x == 1, don't use A
|
||||
// - for x >= 2, use A
|
||||
//
|
||||
// for shifting right:
|
||||
// SRL A = 8 cycles
|
||||
// SRL (HL) = 15 cycles
|
||||
// LD A,(HL) or LD (HL),A = 7 cycles
|
||||
// SRL (IX+d) = 23 cycles
|
||||
// LD A,(IX+d) or LD (IX+d),A = 19 cycles
|
||||
// when using A is profitable?
|
||||
// for HL:
|
||||
@ -86,8 +104,10 @@ object Z80Shifting {
|
||||
// so:
|
||||
// - for x <= 2, don't use A
|
||||
// - for x >= 3, use A
|
||||
if (extendedOps && i <= 2) {
|
||||
if (extendedOps && i <= 2 && op != ZOpcode.ADD) {
|
||||
l ++ List.tabulate(i.toInt)(_ => ZLine.register(op, register))
|
||||
} else if (extendedOps && i == 1 && op == ZOpcode.ADD) {
|
||||
l ++ List.tabulate(i.toInt)(_ => ZLine.register(ZOpcode.SLA, register))
|
||||
} else {
|
||||
l ++ List(ZLine.ld8(ZRegister.A, register)) ++
|
||||
List.tabulate(i.toInt)(_ => ZLine.register(op, ZRegister.A)) ++
|
||||
@ -129,6 +149,7 @@ object Z80Shifting {
|
||||
} else {
|
||||
l ++ (1L until i).flatMap(_ => List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.register(ZOpcode.OR, ZRegister.A),
|
||||
ZLine.register(ZOpcode.RR, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
@ -136,12 +157,11 @@ object Z80Shifting {
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A)
|
||||
)) ++ List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.register(ZOpcode.OR, ZRegister.A),
|
||||
ZLine.register(ZOpcode.RR, ZRegister.A),
|
||||
ZLine.imm8(ZOpcode.AND, (0xff >> i) & 0xff),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
ZLine.register(ZOpcode.RR, ZRegister.A),
|
||||
// ZLine.imm8(ZOpcode.AND, (0xff << (i - 8)) & 0xff), // TODO: properly mask the low byte!!!
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A)
|
||||
)
|
||||
}
|
||||
@ -164,8 +184,7 @@ object Z80Shifting {
|
||||
if (left) {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
ZLine.register(ZOpcode.RL, ZRegister.A),
|
||||
ZLine.imm8(ZOpcode.AND, 0xfe),
|
||||
ZLine.register(ZOpcode.ADD, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.register(ZOpcode.RL, ZRegister.A),
|
||||
@ -173,8 +192,8 @@ object Z80Shifting {
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.register(ZOpcode.OR, ZRegister.A),
|
||||
ZLine.register(ZOpcode.RR, ZRegister.A),
|
||||
ZLine.imm8(ZOpcode.AND, 0x7f),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
ZLine.register(ZOpcode.RR, ZRegister.A),
|
||||
@ -219,17 +238,16 @@ object Z80Shifting {
|
||||
|
||||
def compileLongShiftInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, size: Int, left: Boolean): List[ZLine] = {
|
||||
val extended = ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)
|
||||
val store = Z80ExpressionCompiler.compileByteStores(ctx, lhs, size)
|
||||
val store = Z80ExpressionCompiler.compileByteStores(ctx, lhs, size, includeStep = false)
|
||||
val loadLeft = Z80ExpressionCompiler.compileByteReads(ctx, lhs, size, ZExpressionTarget.HL)
|
||||
val shiftOne = if (left) {
|
||||
loadLeft.zip(store).zipWithIndex.flatMap {
|
||||
case ((ld, st), ix) =>
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
val shiftByte = if (ix == 0) {
|
||||
if (extended) List(ZLine.register(SLA, A))
|
||||
else List(ZLine.register(RL, A), ZLine.imm8(AND, 0xfe))
|
||||
} else List(ZLine.register(RL, A))
|
||||
val shiftByte =
|
||||
if (ix == 0) List(ZLine.register(ADD, A))
|
||||
else List(ZLine.register(RL, A))
|
||||
ld ++ shiftByte ++ st
|
||||
}
|
||||
} else {
|
||||
@ -239,7 +257,7 @@ object Z80Shifting {
|
||||
import ZRegister._
|
||||
val shiftByte = if (ix == 0) {
|
||||
if (extended) List(ZLine.register(SRL, A))
|
||||
else List(ZLine.register(RR, A), ZLine.imm8(AND, 0x7f))
|
||||
else List(ZLine.register(OR, A), ZLine.register(RR, A))
|
||||
} else List(ZLine.register(RR, A))
|
||||
ld ++ shiftByte ++ st
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package millfork.compiler.z80
|
||||
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.BranchingOpcodeMapping
|
||||
import millfork.assembly.z80._
|
||||
import millfork.compiler._
|
||||
@ -187,18 +188,44 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
||||
if (ctx.function.stackVariablesSize > 0) {
|
||||
import ZRegister._
|
||||
val localVariableArea = ctx.function.stackVariablesSize.&(1).+(ctx.function.stackVariablesSize)
|
||||
if (ctx.function.returnType.size == 2) {
|
||||
List(
|
||||
ZLine.ldImm16(IX, localVariableArea),
|
||||
ZLine.registers(ADD_16, IX, SP),
|
||||
ZLine.ld16(SP, IX),
|
||||
ZLine.register(POP, IX))
|
||||
if (ctx.options.flags(CompilationFlag.UseIxForStack)) {
|
||||
if (ctx.function.returnType.size == 2) {
|
||||
List(
|
||||
ZLine.ldImm16(IX, localVariableArea),
|
||||
ZLine.registers(ADD_16, IX, SP),
|
||||
ZLine.ld16(SP, IX),
|
||||
ZLine.register(POP, IX))
|
||||
} else {
|
||||
List(
|
||||
ZLine.ldImm16(HL, localVariableArea),
|
||||
ZLine.registers(ADD_16, HL, SP),
|
||||
ZLine.ld16(SP, HL),
|
||||
ZLine.register(POP, IX))
|
||||
}
|
||||
} else {
|
||||
List(
|
||||
ZLine.ldImm16(HL, localVariableArea),
|
||||
ZLine.registers(ADD_16, HL, SP),
|
||||
ZLine.ld16(SP, HL),
|
||||
ZLine.register(POP, IX))
|
||||
if (ctx.function.returnType.size == 2) {
|
||||
if (ctx.options.flags(CompilationFlag.EmitSharpOpcodes)) {
|
||||
List(ZLine.imm8(ADD_SP, localVariableArea))
|
||||
} else if (localVariableArea == 2) {
|
||||
List(ZLine.register(INC_16, SP), ZLine.register(INC_16, SP))
|
||||
} else if (ctx.options.flags(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(
|
||||
ZLine.implied(EX_DE_HL),
|
||||
ZLine.ldImm16(HL, localVariableArea),
|
||||
ZLine.registers(ADD_16, HL, SP),
|
||||
ZLine.ld16(SP, HL),
|
||||
ZLine.implied(EX_DE_HL))
|
||||
} else {
|
||||
???
|
||||
}
|
||||
} else if (localVariableArea == 2) {
|
||||
List(ZLine.register(POP, HL))
|
||||
} else {
|
||||
List(
|
||||
ZLine.ldImm16(HL, localVariableArea),
|
||||
ZLine.registers(ADD_16, HL, SP),
|
||||
ZLine.ld16(SP, HL))
|
||||
}
|
||||
}
|
||||
} else Nil
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ object ZBuiltIns {
|
||||
if (result.isEmpty) {
|
||||
if (decimal) ???
|
||||
result ++= Z80ExpressionCompiler.compileToA(ctx, expr)
|
||||
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
|
||||
result += ZLine.implied(NEG)
|
||||
} else {
|
||||
result += ZLine.implied(CPL)
|
||||
@ -227,7 +227,7 @@ object ZBuiltIns {
|
||||
if (result.isEmpty) {
|
||||
???
|
||||
} else {
|
||||
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
|
||||
// TODO: optimize
|
||||
result += ZLine.ld8(ZRegister.D, ZRegister.H)
|
||||
result += ZLine.ld8(ZRegister.E, ZRegister.L)
|
||||
@ -236,10 +236,19 @@ object ZBuiltIns {
|
||||
result += ZLine.ld8(ZRegister.C, ZRegister.L)
|
||||
result += ZLine.ld8(ZRegister.H, ZRegister.D)
|
||||
result += ZLine.ld8(ZRegister.L, ZRegister.E)
|
||||
/// TODO: carry?
|
||||
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(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) =>
|
||||
@ -258,14 +267,9 @@ object ZBuiltIns {
|
||||
}
|
||||
|
||||
def perform8BitInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, opcode: ZOpcode.Value, decimal: Boolean = false): List[ZLine] = {
|
||||
val (calculateAddress, lv):(List[ZLine], LocalVariableAddressOperand) = lhs match {
|
||||
case VariableExpression(name) =>
|
||||
ctx.env.get[Variable](name) match {
|
||||
case v: VariableInMemory => List(ZLine.ldImm16(ZRegister.HL, v.toAddress)) -> LocalVariableAddressViaHL
|
||||
case v: StackVariable => Nil -> LocalVariableAddressViaIX(v.baseOffset)
|
||||
case _ => ???
|
||||
}
|
||||
case i: IndexedExpression => Z80ExpressionCompiler.calculateAddressToHL(ctx, i) -> LocalVariableAddressViaHL
|
||||
val (lv, calculateAddress):(LocalVariableAddressOperand, List[ZLine]) = Z80ExpressionCompiler.calculateAddressToAppropriatePointer(ctx, lhs).getOrElse{
|
||||
ErrorReporting.error("Invalid left-hand-side expression", lhs.position)
|
||||
LocalVariableAddressViaHL -> Nil
|
||||
}
|
||||
val constantRight = ctx.env.eval(rhs)
|
||||
val calculateChange = Z80ExpressionCompiler.compileToA(ctx, rhs)
|
||||
@ -273,7 +277,7 @@ object ZBuiltIns {
|
||||
case (false, false) => calculateChange ++ calculateAddress
|
||||
case (true, false) => calculateChange ++ calculateAddress
|
||||
case (false, true) => calculateAddress ++ calculateChange
|
||||
case (true, true) => calculateAddress ++ List(ZLine.register(PUSH, ZRegister.HL)) ++ calculateChange ++ List(ZLine.register(POP, ZRegister.HL))
|
||||
case (true, true) => calculateAddress ++ Z80ExpressionCompiler.stashHLIfChanged(calculateChange)
|
||||
}
|
||||
opcode match {
|
||||
case ADD if decimal =>
|
||||
@ -385,24 +389,64 @@ object ZBuiltIns {
|
||||
|
||||
def performLongInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, opcodeFirst: ZOpcode.Value, opcodeLater: ZOpcode.Value, size: Int, decimal: Boolean = false): List[ZLine] = {
|
||||
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) {
|
||||
val loadRight = Z80ExpressionCompiler.compileToHL(ctx, rhs) ++ List(ZLine.ld8(ZRegister.D, ZRegister.H), ZLine.ld8(ZRegister.E, ZRegister.L))
|
||||
val loadLeft = Z80ExpressionCompiler.stashDEIfChanged(Z80ExpressionCompiler.compileToHL(ctx, lhs))
|
||||
val calculateAndStore = ZLine.registers(ADD_16, ZRegister.HL, ZRegister.DE) :: Z80ExpressionCompiler.storeHL(ctx, lhs, signedSource = false)
|
||||
return loadRight ++ loadLeft ++ calculateAndStore
|
||||
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(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.options.flag(CompilationFlag.EmitZ80Opcodes)) {
|
||||
val loadRight = Z80ExpressionCompiler.compileToHL(ctx, rhs) ++ List(ZLine.ld8(ZRegister.D, ZRegister.H), ZLine.ld8(ZRegister.E, ZRegister.L))
|
||||
val loadLeft = Z80ExpressionCompiler.stashDEIfChanged(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
|
||||
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(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)
|
||||
val store = Z80ExpressionCompiler.compileByteStores(ctx, lhs, size, includeStep = false)
|
||||
val loadLeft = Z80ExpressionCompiler.compileByteReads(ctx, lhs, size, ZExpressionTarget.HL)
|
||||
val loadRight = Z80ExpressionCompiler.compileByteReads(ctx, rhs, size, ZExpressionTarget.BC)
|
||||
List.tabulate(size) {i =>
|
||||
|
Loading…
x
Reference in New Issue
Block a user