mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-03 19:31:02 +00:00
Z80: >>' operator
This commit is contained in:
parent
107474978e
commit
d4beba11a1
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 =>
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user