diff --git a/CHANGELOG.md b/CHANGELOG.md index b4e499e1..78e7fa62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ * Removed command line flag `--detailed-flow`. Detailed flow analysis was slow, broken, hard to maintain, and didn't even help that much. -* Added `*'=` and `<<<<` operators. +* Added `*'=` and `nonet` operators. (Also, the `<<<<` operator, but it will be phased out before 0.2 and replaced by `nonet(a << b)`.) * Added support for zeropage pseudoregisters, allowing for some operators work with more types of operands. diff --git a/doc/abi/generated-labels.md b/doc/abi/generated-labels.md index 51045ac9..85287bf4 100644 --- a/doc/abi/generated-labels.md +++ b/doc/abi/generated-labels.md @@ -36,6 +36,8 @@ where `11111` is a sequential number and `xx` is the type: * `is` – optimized addition of carry using undocumented instructions +* `no` – nonet to word extension caused by the `nonet` operator + * `od` – end of a `do-while` statement * `or` – logical alternative short-circuiting diff --git a/src/main/scala/millfork/compiler/ExpressionCompiler.scala b/src/main/scala/millfork/compiler/ExpressionCompiler.scala index 33fe541c..fa9a0c95 100644 --- a/src/main/scala/millfork/compiler/ExpressionCompiler.scala +++ b/src/main/scala/millfork/compiler/ExpressionCompiler.scala @@ -39,6 +39,7 @@ object ExpressionCompiler { if (getExpressionType(ctx, lo).size > 1) ErrorReporting.error("Lo byte too large", lo.position) w case SumExpression(params, _) => b + case FunctionCallExpression("nonet", params) => w case FunctionCallExpression("not", params) => bool case FunctionCallExpression("hi", params) => bool case FunctionCallExpression("lo", params) => bool @@ -805,6 +806,32 @@ object ExpressionCompiler { } else compilation } } + case "nonet" => + if (params.length != 1) { + ErrorReporting.error("Invalid number of parameters", f.position) + Nil + } else { + assertAllBytes("Nonet argument has to be a byte", ctx, params) + params.head match { + case SumExpression(addends, _) => + if (addends.exists(a => !a._1)) { + ErrorReporting.warn("Nonet subtraction may not work as expected", ctx.options, expr.position) + } + if (addends.size > 2) { + ErrorReporting.warn("Nonet addition works correctly only for two operands", ctx.options, expr.position) + } + case FunctionCallExpression("+" | "+'" | "<<" | "<<<<" | "<<'" | "nonet", _) => // ok + case _ => + ErrorReporting.warn("Unspecified nonet operation, results might be unpredictable", ctx.options, expr.position) + } + val label = MfCompiler.nextLabel("no") + compile(ctx, params.head, Some(b -> RegisterVariable(Register.A, b)), BranchSpec.None) ++ List( + AssemblyLine.immediate(LDX, 0), + AssemblyLine.relative(BCC, label), + AssemblyLine.implied(INX), + AssemblyLine.label(label) + ) ++ expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position) + } case "&&" => assertBool(ctx, params, 2) val a = params.head @@ -851,6 +878,7 @@ object ExpressionCompiler { BuiltIns.compileNonetOps(ctx, v, r) } case "<<<<" => + ErrorReporting.warn("`x <<<< y` is obsolete, use `nonet(x << y)`", ctx.options, expr.position) assertAllBytes("Long shift ops not supported", ctx, params) val (l, r, 1) = assertBinary(ctx, params) BuiltIns.compileNonetLeftShift(ctx, l, r) diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index 555a5c13..fe712bd5 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -841,5 +841,5 @@ class Environment(val parent: Option[Environment], val prefix: String) { } object Environment { - val predefinedFunctions = Set("not", "hi", "lo") + val predefinedFunctions = Set("not", "hi", "lo", "nonet") } diff --git a/src/test/scala/millfork/test/NonetSuite.scala b/src/test/scala/millfork/test/NonetSuite.scala index db71b6f4..19d62d1b 100644 --- a/src/test/scala/millfork/test/NonetSuite.scala +++ b/src/test/scala/millfork/test/NonetSuite.scala @@ -1,6 +1,6 @@ package millfork.test -import millfork.test.emu.EmuUnoptimizedRun +import millfork.test.emu.{EmuBenchmarkRun, EmuUnoptimizedRun} import org.scalatest.{FunSuite, Matchers} /** @@ -28,7 +28,7 @@ class NonetSuite extends FunSuite with Matchers { } test("Nonet left shift") { - val m = EmuUnoptimizedRun( + EmuBenchmarkRun( """ | word output0 @$c000 | word output1 @$c002 @@ -42,10 +42,34 @@ class NonetSuite extends FunSuite with Matchers { | output2 = a <<<< 6 | output3 = a <<<< 7 | } - """.stripMargin) + """.stripMargin) { m => m.readWord(0xc000) should equal(0x06) m.readWord(0xc002) should equal(0x0C) m.readWord(0xc004) should equal(0xC0) m.readWord(0xc006) should equal(0x180) + } + } + + test("Nonet left shift 2") { + EmuBenchmarkRun( + """ + | word output0 @$c000 + | word output1 @$c002 + | word output2 @$c004 + | word output3 @$c006 + | void main () { + | byte a + | a = 3 + | output0 = nonet(a << 1) + | output1 = nonet(a << 2) + | output2 = nonet(a << 6) + | output3 = nonet(a << 7) + | } + """.stripMargin) { m => + m.readWord(0xc000) should equal(0x06) + m.readWord(0xc002) should equal(0x0C) + m.readWord(0xc004) should equal(0xC0) + m.readWord(0xc006) should equal(0x180) + } } }