1
0
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:
Karol Stasiak 2018-07-03 01:47:31 +02:00
parent 57bde60ced
commit 0da4637036
3 changed files with 222 additions and 29 deletions

View File

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

View File

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

View File

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