1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-07-05 09:28:54 +00:00

Many things:

– better support for LR35902
– stack variables for 8080 and LR35902
– bugfixes
– minor optimizations
This commit is contained in:
Karol Stasiak 2018-07-27 19:11:59 +02:00
parent 600e49cf49
commit 5c25e653bf
8 changed files with 772 additions and 281 deletions

View File

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

View File

@ -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] = {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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