mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-24 15:29:23 +00:00
Fix and extend Z80 word/long operations
This commit is contained in:
parent
57bde60ced
commit
0da4637036
@ -102,6 +102,10 @@ object ZLine {
|
||||
def ldAbs8(target: Constant, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegisters(MEM_ABS_8, source), target)
|
||||
|
||||
def ldAbs16(target: Constant, source: ZRegister.Value): ZLine = ZLine(LD_16, TwoRegisters(MEM_ABS_16, source), target)
|
||||
|
||||
def ldViaIx(target: ZRegister.Value, sourceOffset: Int): ZLine = ZLine(LD, TwoRegisters(target, ZRegister.MEM_IX_D), NumericConstant(sourceOffset, 1))
|
||||
|
||||
def ldViaIx(targetOffset: Int, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegisters(ZRegister.MEM_IX_D, source), NumericConstant(targetOffset, 1))
|
||||
}
|
||||
|
||||
case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Constant, elidable: Boolean = true) extends AbstractCode {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package millfork.compiler.z80
|
||||
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.z80._
|
||||
import millfork.compiler._
|
||||
import millfork.env._
|
||||
@ -88,12 +89,22 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
def stashHLIfChanged(lines: List[ZLine]): List[ZLine] = if (lines.exists(changesHL))
|
||||
ZLine.register(PUSH, ZRegister.HL) :: (lines :+ ZLine.register(POP, ZRegister.HL)) else lines
|
||||
|
||||
def targetifyA(target: ZExpressionTarget.Value, lines: List[ZLine]): List[ZLine] = target match {
|
||||
def targetifyA(target: ZExpressionTarget.Value, lines: List[ZLine], isSigned: Boolean): List[ZLine] = target match {
|
||||
case ZExpressionTarget.NOTHING | ZExpressionTarget.A => lines
|
||||
case ZExpressionTarget.HL => lines ++ List(
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.H, 0)
|
||||
)
|
||||
case ZExpressionTarget.HL => lines ++ (if (isSigned) {
|
||||
val label = Z80Compiler.nextLabel("sx")
|
||||
List(
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.H, 0xff),
|
||||
ZLine.imm8(OR, 0x7f),
|
||||
ZLine.jump(label, IfFlagSet(ZFlag.S)), // TODO: gameboy has no S flag
|
||||
ZLine.ldImm8(ZRegister.H, 0),
|
||||
ZLine.label(label))
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.H, 0))
|
||||
})
|
||||
}
|
||||
|
||||
def targetifyHL(target: ZExpressionTarget.Value, lines: List[ZLine]): List[ZLine] = target match {
|
||||
@ -139,7 +150,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
case SumExpression(params, decimal) =>
|
||||
getParamMaxSize(ctx, params.map(_._2)) match {
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitSum(ctx, params, decimal))
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitSum(ctx, params, decimal), isSigned = false)
|
||||
case 2 => targetifyHL(target, ZBuiltIns.compile16BitSum(ctx, params, decimal))
|
||||
}
|
||||
case f@FunctionCallExpression(name, params) =>
|
||||
@ -147,8 +158,47 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
case "not" =>
|
||||
assertBool(ctx, "not", params, 1)
|
||||
compile(ctx, params.head, target, branches.flip)
|
||||
case "hi" | "lo" => ??? // TODO
|
||||
case "nonet" => ??? // TODO
|
||||
case "hi" =>
|
||||
if (params.length != 1) {
|
||||
ErrorReporting.error("Too many parameters for hi/lo", f.position)
|
||||
Nil
|
||||
} else {
|
||||
compileToHL(ctx, params.head) ++ (target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.A=> List(ZLine.ld8(ZRegister.A, ZRegister.H))
|
||||
case ZExpressionTarget.HL=> List(ZLine.ld8(ZRegister.L, ZRegister.H), ZLine.ldImm8(ZRegister.H, 0))
|
||||
})
|
||||
}
|
||||
case "lo" =>
|
||||
if (params.length != 1) {
|
||||
ErrorReporting.error("Too many parameters for hi/lo", f.position)
|
||||
Nil
|
||||
} else {
|
||||
compileToHL(ctx, params.head) ++ (target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.A => List(ZLine.ld8(ZRegister.A, ZRegister.L))
|
||||
case ZExpressionTarget.HL => List(ZLine.ldImm8(ZRegister.H, 0))
|
||||
})
|
||||
}
|
||||
case "nonet" =>
|
||||
if (params.length != 1) {
|
||||
ErrorReporting.error("Invalid number of parameters", f.position)
|
||||
Nil
|
||||
} else {
|
||||
compileToA(ctx, params.head) ++ (target match {
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
case ZExpressionTarget.A => Nil
|
||||
case ZExpressionTarget.HL =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.H, 0),
|
||||
ZLine.register(RL, ZRegister.H))
|
||||
} else {
|
||||
???
|
||||
}
|
||||
})
|
||||
}
|
||||
case "&&" =>
|
||||
assertBool(ctx, "&&", params)
|
||||
branches match {
|
||||
@ -175,18 +225,18 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
|
||||
case "&" =>
|
||||
getParamMaxSize(ctx, params) match {
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, AND, params))
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, AND, params), isSigned = false)
|
||||
case 2 => targetifyHL(target, ZBuiltIns.compile16BitOperation(ctx, AND, params))
|
||||
}
|
||||
case "*" => ???
|
||||
case "|" =>
|
||||
getParamMaxSize(ctx, params) match {
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, OR, params))
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, OR, params), isSigned = false)
|
||||
case 2 => targetifyHL(target, ZBuiltIns.compile16BitOperation(ctx, OR, params))
|
||||
}
|
||||
case "^" =>
|
||||
getParamMaxSize(ctx, params) match {
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, XOR, params))
|
||||
case 1 => targetifyA(target, ZBuiltIns.compile8BitOperation(ctx, XOR, params), isSigned = false)
|
||||
case 2 => targetifyHL(target, ZBuiltIns.compile16BitOperation(ctx, XOR, params))
|
||||
}
|
||||
case ">>>>" =>
|
||||
@ -195,14 +245,14 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
case "<<" =>
|
||||
val (l, r, size) = assertBinary(ctx, params)
|
||||
size match {
|
||||
case 1 => targetifyA(target, Z80Shifting.compile8BitShift(ctx, l, r, left = true))
|
||||
case 1 => targetifyA(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) = assertBinary(ctx, params)
|
||||
size match {
|
||||
case 1 => targetifyA(target, Z80Shifting.compile8BitShift(ctx, l, r, left = false))
|
||||
case 1 => targetifyA(target, Z80Shifting.compile8BitShift(ctx, l, r, left = false), isSigned = false)
|
||||
case 2 => Z80Shifting.compile16BitShift(ctx, l, r, left = false)
|
||||
case _ => ???
|
||||
}
|
||||
@ -266,25 +316,25 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, ADD)
|
||||
case _ => ???
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, ADD, ADC, size, decimal = false)
|
||||
}
|
||||
case "-=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, SUB)
|
||||
case _ => ???
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, SUB, SBC, size, decimal = false)
|
||||
}
|
||||
case "+'=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, ADD, decimal = true)
|
||||
case _ => ???
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, ADD, ADC, size, decimal = true)
|
||||
}
|
||||
case "-'=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, SUB, decimal = true)
|
||||
case _ => ???
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, SUB, SBC, size, decimal = true)
|
||||
}
|
||||
Nil
|
||||
case "<<=" =>
|
||||
@ -319,19 +369,19 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, AND)
|
||||
case _ => ???
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, AND, AND, size, decimal = false)
|
||||
}
|
||||
case "^=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, XOR)
|
||||
case _ => ???
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, XOR, XOR, size, decimal = false)
|
||||
}
|
||||
case "|=" =>
|
||||
val (l, r, size) = assertAssignmentLike(ctx, params)
|
||||
size match {
|
||||
case 1 => ZBuiltIns.perform8BitInPlace(ctx, l, r, OR)
|
||||
case _ => ???
|
||||
case _ => ZBuiltIns.performLongInPlace(ctx, l, r, OR, OR, size, decimal = false)
|
||||
}
|
||||
case _ =>
|
||||
env.maybeGet[Type](f.functionName) match {
|
||||
@ -351,7 +401,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
failed = true
|
||||
}
|
||||
return sourceType.size match {
|
||||
case 1 => targetifyA(target, compileToA(ctx, params.head))
|
||||
case 1 => targetifyA(target, compileToA(ctx, params.head), isSigned = sourceType.isSigned)
|
||||
case 2 => targetifyHL(target, compileToHL(ctx, params.head))
|
||||
case _ => ???
|
||||
}
|
||||
@ -390,7 +440,14 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
}
|
||||
} ++ List(ZLine(CALL, NoRegisters, function.toAddress))
|
||||
}
|
||||
result
|
||||
function.returnType.size match {
|
||||
case 1 =>
|
||||
targetifyA(target, result, isSigned = function.returnType.isSigned)
|
||||
case 2 =>
|
||||
targetifyHL(target, result)
|
||||
case _ =>
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -560,4 +617,107 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
compile(ctx, conjunction, target, branches)
|
||||
}
|
||||
}
|
||||
|
||||
def compileByteReads(ctx: CompilationContext, rhs: Expression, size: Int): List[List[ZLine]] = {
|
||||
if (size == 1) throw new IllegalArgumentException
|
||||
val env = ctx.env
|
||||
env.eval(rhs) match {
|
||||
case Some(constant) =>
|
||||
List.tabulate(size)(i => List(ZLine.ldImm8(ZRegister.A, constant.subbyte(i))))
|
||||
case None =>
|
||||
rhs match {
|
||||
case VariableExpression(vname) =>
|
||||
env.get[Variable](vname) match {
|
||||
case v: VariableInMemory =>
|
||||
List.tabulate(size) { i =>
|
||||
if (i < size) {
|
||||
List(ZLine.ldAbs8(ZRegister.A, v.toAddress + i))
|
||||
} else if (v.typ.isSigned) {
|
||||
???
|
||||
} else {
|
||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||
}
|
||||
}
|
||||
case v: StackVariable =>
|
||||
List.tabulate(size) { i =>
|
||||
if (i < size) {
|
||||
List(ZLine.ldViaIx(ZRegister.A, v.baseOffset + i))
|
||||
} else if (v.typ.isSigned) {
|
||||
???
|
||||
} else {
|
||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
case SeparateBytesExpression(hi, lo) =>
|
||||
List.tabulate(size) { i =>
|
||||
if (i == 0) {
|
||||
compileToA(ctx, lo)
|
||||
} else if (i == 1) {
|
||||
compileToA(ctx, hi)
|
||||
} else {
|
||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
List.tabulate(size) { i =>
|
||||
if (i == 0) {
|
||||
compileToHL(ctx, rhs) :+ ZLine.ld8(ZRegister.A, ZRegister.L)
|
||||
} else if (i == 1) {
|
||||
List(ZLine.ld8(ZRegister.A, ZRegister.H))
|
||||
} else {
|
||||
// TODO: signed words?
|
||||
List(ZLine.ldImm8(ZRegister.A, 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def compileByteStores(ctx: CompilationContext, lhs: LhsExpression, size: Int): List[List[ZLine]] = {
|
||||
if (size == 1) throw new IllegalArgumentException
|
||||
val env = ctx.env
|
||||
lhs match {
|
||||
case VariableExpression(vname) =>
|
||||
env.get[Variable](vname) match {
|
||||
case v: VariableInMemory =>
|
||||
if (v.typ.size < size) {
|
||||
ErrorReporting.error(s"Variable `$vname` is too small", lhs.position)
|
||||
}
|
||||
List.tabulate(size) { i =>
|
||||
if (i < size) {
|
||||
List(ZLine.ldAbs8(v.toAddress + i, ZRegister.A))
|
||||
} else {
|
||||
Nil
|
||||
}
|
||||
}
|
||||
case v: StackVariable =>
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
case SeparateBytesExpression(hi: LhsExpression, lo: LhsExpression) =>
|
||||
if (size > 2) {
|
||||
ErrorReporting.error(s"Left hand side is too small", lhs.position)
|
||||
}
|
||||
List.tabulate(size) { i =>
|
||||
if (i == 0) {
|
||||
storeA(ctx, lo, signedSource = false)
|
||||
} else if (i == 1) {
|
||||
storeA(ctx, hi, signedSource = false)
|
||||
} else {
|
||||
Nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package millfork.compiler.z80
|
||||
|
||||
import millfork.assembly.z80.{ZLine, ZOpcode}
|
||||
import millfork.assembly.z80.{TwoRegisters, ZLine, ZOpcode}
|
||||
import millfork.compiler.CompilationContext
|
||||
import millfork.node._
|
||||
import ZOpcode._
|
||||
@ -47,7 +47,7 @@ object ZBuiltIns {
|
||||
|
||||
def compile16BitOperation(ctx: CompilationContext, op: ZOpcode.Value, params: List[Expression]): List[ZLine] = {
|
||||
var const = op match {
|
||||
case AND => NumericConstant(0xffff, 1)
|
||||
case AND => NumericConstant(0xffff, 2)
|
||||
case OR | XOR => Constant.Zero
|
||||
}
|
||||
var hasConst = false
|
||||
@ -157,7 +157,7 @@ object ZBuiltIns {
|
||||
}
|
||||
|
||||
def compile16BitSum(ctx: CompilationContext, params: List[(Boolean, Expression)], decimal: Boolean): List[ZLine] = {
|
||||
var const = Constant.Zero
|
||||
var const: Constant = NumericConstant(0, 2)
|
||||
var hasConst = false
|
||||
var result = mutable.ListBuffer[ZLine]()
|
||||
if (decimal) {
|
||||
@ -239,8 +239,7 @@ object ZBuiltIns {
|
||||
}
|
||||
if (hasConst) {
|
||||
result ++= List(
|
||||
ZLine.ldImm8(ZRegister.D, const.hiByte),
|
||||
ZLine.ldImm8(ZRegister.E, const.loByte),
|
||||
ZLine.ldImm16(ZRegister.DE, const),
|
||||
ZLine.registers(ADD_16, ZRegister.HL, ZRegister.DE)
|
||||
)
|
||||
}
|
||||
@ -322,7 +321,7 @@ object ZBuiltIns {
|
||||
ZLine.register(XOR, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
}
|
||||
case XOR =>
|
||||
case OR =>
|
||||
constantRight match {
|
||||
case Some(NumericConstant(0, _)) =>
|
||||
calculateAddress
|
||||
@ -330,7 +329,7 @@ object ZBuiltIns {
|
||||
calculateAddress :+ ZLine.ldImm8(ZRegister.MEM_HL, 0xff)
|
||||
case _ =>
|
||||
setup ++ List(
|
||||
ZLine.register(XOR, ZRegister.MEM_HL),
|
||||
ZLine.register(OR, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
}
|
||||
case AND =>
|
||||
@ -347,4 +346,34 @@ 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) {
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
val store = Z80ExpressionCompiler.compileByteStores(ctx, lhs, size)
|
||||
val loadLeft = Z80ExpressionCompiler.compileByteReads(ctx, lhs, size)
|
||||
val loadRight = Z80ExpressionCompiler.compileByteReads(ctx, rhs, size)
|
||||
List.tabulate(size) {i =>
|
||||
// TODO: stash things correctly?
|
||||
val firstPhase = loadRight(i) ++ List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++ (loadLeft(i) :+ ZLine.register(if (i==0) opcodeFirst else opcodeLater, ZRegister.E))
|
||||
val secondPhase = if (decimal) firstPhase :+ ZLine.implied(ZOpcode.DAA) else firstPhase
|
||||
secondPhase ++ store(i)
|
||||
}.flatten
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user