1
0
mirror of https://github.com/KarolS/millfork.git synced 2025-09-27 14:16:37 +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. They will be marked with (zpreg) next to them.
The flag is enabled by default, but you can disable it if you need to. 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 ## Precedence
Millfork has different operator precedence compared to most other languages. From highest to lowest it goes: 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 ## 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. The compiler issues a warning if these operators appear in the code.
* `+'`, `-'`: decimal addition/subtraction * `+'`, `-'`: decimal addition/subtraction

View File

@@ -1,12 +1,14 @@
package millfork.compiler.z80 package millfork.compiler.z80
import millfork.CompilationFlag import millfork.CompilationFlag
import millfork.assembly.z80.ZLine import millfork.assembly.z80._
import millfork.compiler.CompilationContext import millfork.compiler.CompilationContext
import millfork.env.NumericConstant import millfork.env.NumericConstant
import millfork.error.ConsoleLogger import millfork.error.ConsoleLogger
import millfork.node.{Expression, LhsExpression, ZRegister} import millfork.node.{Expression, LhsExpression, ZRegister}
import scala.collection.mutable.ListBuffer
/** /**
* @author Karol Stasiak * @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] = { def compileInPlaceByteMultiplication(ctx: CompilationContext, r: Expression): List[ZLine] = {
val multiplier = ctx.env.eval(r) match { val multiplier = ctx.env.eval(r) match {

View File

@@ -617,7 +617,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
case ">>'" => case ">>'" =>
assertAllArithmeticBytes("Long shift ops not supported", ctx, params) assertAllArithmeticBytes("Long shift ops not supported", ctx, params)
val (l, r, 1) = assertArithmeticBinary(ctx, params) val (l, r, 1) = assertArithmeticBinary(ctx, params)
??? targetifyA(ctx, target, Z80DecimalBuiltIns.compileByteShiftRight(ctx, Some(l), r), isSigned = false)
case "<" => case "<" =>
val (size, signed) = assertArithmeticComparison(ctx, params) val (size, signed) = assertArithmeticComparison(ctx, params)
compileTransitiveRelation(ctx, "<", params, target, branches) { (l, r) => compileTransitiveRelation(ctx, "<", params, target, branches) { (l, r) =>
@@ -729,7 +729,21 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
} }
case ">>'=" => case ">>'=" =>
val (l, r, size) = assertArithmeticAssignmentLike(ctx, params) 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 "*=" => case "*=" =>
assertAllArithmeticBytes("Long multiplication not supported", ctx, params) assertAllArithmeticBytes("Long multiplication not supported", ctx, params)
val (l, r, 1) = assertArithmeticAssignmentLike(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) ctx.log.error(s"Variable `$vname` is too small", lhs.position)
} }
import ZRegister._ import ZRegister._
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) { if (ctx.options.flag(CompilationFlag.UseIxForStack)) {
List.tabulate(size) { i => List.tabulate(size) { i =>
if (i < size) { if (i < size) {
List(ZLine.ldViaIx(v.baseOffset + i, ZRegister.A)) List(ZLine.ldViaIx(v.baseOffset + i, ZRegister.A))
@@ -1449,6 +1463,15 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
Nil 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 { } else {
val prepareHL = calculateStackAddressToHL(ctx, v) val prepareHL = calculateStackAddressToHL(ctx, v)
List.tabulate(size) { i => 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 "IM" => asmExpression.map((IM, NoRegisters, None, _))
case "EI" => imm(EI) case "EI" => imm(EI)
case "DI" => imm(DI) case "DI" => imm(DI)
case "HLT" => imm(HALT)
case "HALT" => imm(HALT) case "HALT" => imm(HALT)
case "STOP" => imm(STOP) case "STOP" => imm(STOP)

View File

@@ -211,7 +211,7 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
} }
test("Decimal right shift test") { test("Decimal right shift test") {
val m = EmuUnoptimizedRun( EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
""" """
| byte output @$c000 | byte output @$c000
| void main () { | void main () {
@@ -220,12 +220,13 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
| output = n >>' 2 | output = n >>' 2
| } | }
| byte thirty_six() { return $36 } | byte thirty_six() { return $36 }
""".stripMargin) """.stripMargin) { m=>
m.readByte(0xc000) should equal(9) m.readByte(0xc000) should equal(9)
}
} }
test("Decimal right shift test 2") { test("Decimal right shift test 2") {
val m = EmuUnoptimizedRun( EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
""" """
| byte output @$c000 | byte output @$c000
| void main () { | void main () {
@@ -233,12 +234,13 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
| output >>'= 2 | output >>'= 2
| } | }
| byte thirty_six() { return $36 } | byte thirty_six() { return $36 }
""".stripMargin) """.stripMargin) { m =>
m.readByte(0xc000) should equal(9) m.readByte(0xc000) should equal(9)
}
} }
test("Decimal right shift test 3") { test("Decimal right shift test 3") {
val m = EmuUnoptimizedRun( EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
""" """
| word output @$c000 | word output @$c000
| void main () { | void main () {
@@ -246,8 +248,9 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
| output >>'= 2 | output >>'= 2
| } | }
| word thirty_six() { return $364 } | word thirty_six() { return $364 }
""".stripMargin) """.stripMargin) { m =>
m.readWord(0xc000) should equal(0x91) m.readWord(0xc000) should equal(0x91)
}
} }
private def toDecimal(v: Int): Int = { private def toDecimal(v: Int): Int = {
@@ -272,15 +275,16 @@ class ByteDecimalMathSuite extends FunSuite with Matchers {
test("Decimal word right-shift comprehensive suite") { 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)) { 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 | word output @$c000
| void main () { | void main () {
| output = $# | output = $#
| output >>'= 1 | output >>'= 1
| } | }
""".stripMargin.replace("#", i.toString)) """.stripMargin.replace("#", i.toString)) {m =>
toDecimal(m.readWord(0xc000)) should equal(i/2) toDecimal(m.readWord(0xc000)) should equal(i/2)
}
} }
} }