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

Z80: >>' operator

This commit is contained in:
Karol Stasiak 2018-08-01 18:49:37 +02:00
parent 107474978e
commit d4beba11a1
5 changed files with 181 additions and 20 deletions

View File

@ -12,9 +12,6 @@ Certain expressions require the commandline flag `-fzp-register` (`.ini` equival
They will be marked with (zpreg) next to them.
The flag is enabled by default, but you can disable it if you need to.
**Work in progress**:
Certain expressions may not work on non-6502 targets yet. This should improve in the future.
## Precedence
Millfork has different operator precedence compared to most other languages. From highest to lowest it goes:
@ -105,7 +102,10 @@ There are no division, remainder or modulo operators.
## Decimal arithmetic operators
These operators work using the decimal arithmetic and will not work on Ricoh CPU's.
These operators work using the decimal arithmetic (packed BCD).
**Work in progress**:
These operations don't work on Ricoh-based targets (i.e. Famicom) yet.
The compiler issues a warning if these operators appear in the code.
* `+'`, `-'`: decimal addition/subtraction

View File

@ -1,12 +1,14 @@
package millfork.compiler.z80
import millfork.CompilationFlag
import millfork.assembly.z80.ZLine
import millfork.assembly.z80._
import millfork.compiler.CompilationContext
import millfork.env.NumericConstant
import millfork.error.ConsoleLogger
import millfork.node.{Expression, LhsExpression, ZRegister}
import scala.collection.mutable.ListBuffer
/**
* @author Karol Stasiak
*/
@ -70,6 +72,137 @@ object Z80DecimalBuiltIns {
}
}
def compileShiftARight(ctx: CompilationContext, clearCarry: Boolean, preserveCarry: Boolean, output: List[ZLine]): List[ZLine] = {
import millfork.assembly.z80.ZOpcode._
import ZRegister._
val skipHiDigit = ctx.nextLabel("ds")
val skipLoDigit = ctx.nextLabel("ds")
val result = ListBuffer[ZLine]()
if (clearCarry) {
result += ZLine.register(OR, A)
}
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
// Z80 and Sharp
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
result += ZLine.register(RR, A)
} else {
result += ZLine.implied(RRA)
}
if (preserveCarry) {
result += ZLine.register(PUSH, AF)
}
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
result += ZLine.jump(skipHiDigit, IfFlagClear(ZFlag.S))
} else {
result += ZLine.register(BIT7, A)
result += ZLine.jumpR(ctx, skipHiDigit, IfFlagSet(ZFlag.Z))
}
result += ZLine.imm8(SUB, 0x30)
result += ZLine.label(skipHiDigit)
result += ZLine.register(BIT3, A)
result += ZLine.jumpR(ctx, skipLoDigit, IfFlagSet(ZFlag.Z))
result += ZLine.imm8(SUB, 3)
result += ZLine.label(skipLoDigit)
} else {
// Intel 8080
result += ZLine.implied(RRA)
if (preserveCarry) {
result += ZLine.register(PUSH, AF)
}
result += ZLine.register(OR, A)
result += ZLine.jump(skipHiDigit, IfFlagClear(ZFlag.S))
result += ZLine.imm8(SUB, 0x30)
result += ZLine.label(skipHiDigit)
result += ZLine.ld8(E, A)
result += ZLine.imm8(AND, 8)
result += ZLine.ld8(A, E)
result += ZLine.jump(skipLoDigit, IfFlagSet(ZFlag.Z))
result += ZLine.imm8(SUB, 3)
result += ZLine.label(skipLoDigit)
}
if (output == Nil && preserveCarry) throw new IllegalArgumentException
result ++= output
if (preserveCarry) {
result += ZLine.register(POP, AF)
}
result.toList
}
def compileByteShiftRight(ctx: CompilationContext, l: Option[Expression], r: Expression): List[ZLine] = {
val left = l match {
case Some(e) => Z80ExpressionCompiler.compileToA(ctx, e)
case _ => Nil
}
ctx.env.eval(r) match {
case Some(NumericConstant(0, _)) => left
case Some(NumericConstant(n, _)) =>
left ++ List.fill(n.toInt)(compileShiftARight(ctx, clearCarry = true, preserveCarry = false, Nil)).flatten
case _ =>
val right = Z80ExpressionCompiler.compile8BitTo(ctx, r, ZRegister.B)
val load = if (!left.exists(_.changesRegister(ZRegister.B))) {
right ++ left
} else if (!right.exists(_.changesRegister(ZRegister.A))) {
left ++ right
} else {
right ++ Z80ExpressionCompiler.stashBCIfChanged(ctx, left)
}
val label = ctx.nextLabel("ds")
load ++ List(ZLine.label(label)) ++
compileShiftARight(ctx, clearCarry = true, preserveCarry = false, Nil) ++ ZLine.djnz(ctx, label)
}
}
def compileWordShiftRight(ctx: CompilationContext, l: Expression, r: Expression): List[ZLine] = {
val left = Z80ExpressionCompiler.compileToHL(ctx, l)
ctx.env.eval(r) match {
case Some(NumericConstant(0, _)) => left
case Some(NumericConstant(n, _)) =>
left ++ List.fill(n.toInt) {
List(ZLine.ld8(ZRegister.A, ZRegister.H)) ++
compileShiftARight(ctx, clearCarry = true, preserveCarry = true, List(ZLine.ld8(ZRegister.H, ZRegister.A))) ++
List(ZLine.ld8(ZRegister.A, ZRegister.L)) ++
compileShiftARight(ctx, clearCarry = false, preserveCarry = false, List(ZLine.ld8(ZRegister.L, ZRegister.A)))
}.flatten
case _ =>
val right = Z80ExpressionCompiler.compile8BitTo(ctx, r, ZRegister.B)
val load = if (!left.exists(_.changesRegister(ZRegister.B))) {
right ++ left
} else if (!right.exists(_.changesRegister(ZRegister.HL))) {
left ++ right
} else {
left ++ Z80ExpressionCompiler.stashHLIfChanged(ctx, right)
}
val label = ctx.nextLabel("ds")
load ++
List(ZLine.label(label), ZLine.ld8(ZRegister.A, ZRegister.H)) ++
compileShiftARight(ctx, clearCarry = true, preserveCarry = true, Nil) ++
List(ZLine.ld8(ZRegister.H, ZRegister.A), ZLine.ld8(ZRegister.A, ZRegister.L)) ++
compileShiftARight(ctx, clearCarry = false, preserveCarry = false, Nil) ++
List(ZLine.ld8(ZRegister.L, ZRegister.A)) ++ ZLine.djnz(ctx, label)
}
}
def compileInPlaceShiftRight(ctx: CompilationContext, l: LhsExpression, r: Expression, size: Int): List[ZLine] = {
val loads = Z80ExpressionCompiler.compileByteReads(ctx, l, size, ZExpressionTarget.HL)
val stores = Z80ExpressionCompiler.compileByteStores(ctx, l, size, includeStep = false)
ctx.env.eval(r) match {
case Some(NumericConstant(0, _)) => loads.flatten
case Some(NumericConstant(n, _)) =>
List.fill(n.toInt)(loads.zip(stores).zipWithIndex.reverse.flatMap {
case ((ld, st), i) =>
ld ++ compileShiftARight(ctx, i == size - 1, i != 0, st)
}).flatten
case _ =>
val right = Z80ExpressionCompiler.compile8BitTo(ctx, r, ZRegister.B)
val label = ctx.nextLabel("ds")
right ++ List(ZLine.label(label)) ++
loads.zip(stores).zipWithIndex.reverse.flatMap {
case ((ld, st), i) =>
Z80ExpressionCompiler.stashBCIfChanged(ctx, ld) ++ compileShiftARight(ctx, i == size - 1, i != 0, st)
} ++ ZLine.djnz(ctx, label)
}
}
def compileInPlaceByteMultiplication(ctx: CompilationContext, r: Expression): List[ZLine] = {
val multiplier = ctx.env.eval(r) match {

View File

@ -617,7 +617,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
case ">>'" =>
assertAllArithmeticBytes("Long shift ops not supported", ctx, params)
val (l, r, 1) = assertArithmeticBinary(ctx, params)
???
targetifyA(ctx, target, Z80DecimalBuiltIns.compileByteShiftRight(ctx, Some(l), r), isSigned = false)
case "<" =>
val (size, signed) = assertArithmeticComparison(ctx, params)
compileTransitiveRelation(ctx, "<", params, target, branches) { (l, r) =>
@ -729,7 +729,21 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
}
case ">>'=" =>
val (l, r, size) = assertArithmeticAssignmentLike(ctx, params)
???
size match {
case 1 =>
calculateAddressToAppropriatePointer(ctx, l) match {
case Some((lvo, code)) =>
code ++
(ZLine.ld8(ZRegister.A, lvo) ::
(Z80DecimalBuiltIns.compileByteShiftRight(ctx, None, r) :+ ZLine.ld8(lvo, ZRegister.A)))
case None =>
ctx.log.error("Invalid left-hand side", l.position)
Nil
}
case 2 =>
Z80DecimalBuiltIns.compileWordShiftRight(ctx, l, r) ++ storeHL(ctx, l, signedSource = false)
case _ => Z80DecimalBuiltIns.compileInPlaceShiftRight(ctx, l, r, size)
}
case "*=" =>
assertAllArithmeticBytes("Long multiplication not supported", ctx, params)
val (l, r, 1) = assertArithmeticAssignmentLike(ctx, params)
@ -1441,7 +1455,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
ctx.log.error(s"Variable `$vname` is too small", lhs.position)
}
import ZRegister._
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
if (ctx.options.flag(CompilationFlag.UseIxForStack)) {
List.tabulate(size) { i =>
if (i < size) {
List(ZLine.ldViaIx(v.baseOffset + i, ZRegister.A))
@ -1449,6 +1463,15 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
Nil
}
}
} else
if (ctx.options.flag(CompilationFlag.UseIyForStack)) {
List.tabulate(size) { i =>
if (i < size) {
List(ZLine.ldViaIy(v.baseOffset + i, ZRegister.A))
} else {
Nil
}
}
} else {
val prepareHL = calculateStackAddressToHL(ctx, v)
List.tabulate(size) { i =>

View File

@ -167,6 +167,7 @@ case class Z80Parser(filename: String, input: String, currentDirectory: String,
case "IM" => asmExpression.map((IM, NoRegisters, None, _))
case "EI" => imm(EI)
case "DI" => imm(DI)
case "HLT" => imm(HALT)
case "HALT" => imm(HALT)
case "STOP" => imm(STOP)

View File

@ -211,7 +211,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
}
test("Decimal right shift test") {
val m = EmuUnoptimizedRun(
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
"""
| byte output @$c000
| void main () {
@ -220,12 +220,13 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
| output = n >>' 2
| }
| byte thirty_six() { return $36 }
""".stripMargin)
m.readByte(0xc000) should equal(9)
""".stripMargin) { m=>
m.readByte(0xc000) should equal(9)
}
}
test("Decimal right shift test 2") {
val m = EmuUnoptimizedRun(
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
"""
| byte output @$c000
| void main () {
@ -233,12 +234,13 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
| output >>'= 2
| }
| byte thirty_six() { return $36 }
""".stripMargin)
m.readByte(0xc000) should equal(9)
""".stripMargin) { m =>
m.readByte(0xc000) should equal(9)
}
}
test("Decimal right shift test 3") {
val m = EmuUnoptimizedRun(
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
"""
| word output @$c000
| void main () {
@ -246,8 +248,9 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
| output >>'= 2
| }
| word thirty_six() { return $364 }
""".stripMargin)
m.readWord(0xc000) should equal(0x91)
""".stripMargin) { m =>
m.readWord(0xc000) should equal(0x91)
}
}
private def toDecimal(v: Int): Int = {
@ -272,15 +275,16 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
test("Decimal word right-shift comprehensive suite") {
for (i <- List(0, 1, 10, 100, 1000, 2000, 500, 200, 280, 300, 5234, 7723, 7344, 9, 16, 605, 1111, 2222, 3333, 9999, 8888, 8100)) {
val m = EmuUnoptimizedRun(
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
"""
| word output @$c000
| void main () {
| output = $#
| output >>'= 1
| }
""".stripMargin.replace("#", i.toString))
toDecimal(m.readWord(0xc000)) should equal(i/2)
""".stripMargin.replace("#", i.toString)) {m =>
toDecimal(m.readWord(0xc000)) should equal(i/2)
}
}
}