diff --git a/docs/lang/functions.md b/docs/lang/functions.md index f549b440..ffb3b38e 100644 --- a/docs/lang/functions.md +++ b/docs/lang/functions.md @@ -39,7 +39,7 @@ Examples: You are not allowed to call such functions directly. The function cannot have parameters and the return type should be `void`. - * `kernal_interrupt` – the function is an interrupt handler called from a generic vendor-provider hardware interrupt handler. + * `kernal_interrupt` – the function is an interrupt handler called from a generic vendor-provided hardware interrupt handler. The hardware instruction handler is assumed to have preserved the CPU registers, so this function only has to preserve the zeropage pseudoregisters. An example is the Commodore 64 interrupt handler that calls the function at an address read from $314/$315. @@ -85,9 +85,12 @@ and return `void` or a value of size max 2 bytes, can be accessed via a pointer. void f() {} + void g(byte x) {} function.void.to.void p = f.pointer + function.byte.to.void p = g.pointer call(p) + call(p, 13) The value of the pointer `f.pointer` may not be the same as the value of the function address `f.addr`. diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index d3af40c7..7b03bd62 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -2211,9 +2211,15 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa return None } val function = thing.asInstanceOf[MangledFunction] - if (function.params.length != actualParams.length) { + if (function.name == "call") { + if (actualParams.isEmpty || actualParams.length > 2) { + log.error("Invalid number of parameters for function `call`", actualParams.headOption.flatMap(_._2.position)) + } + } else { + if (function.params.length != actualParams.length && function.name != "call") { log.error(s"Invalid number of parameters for function `$name`", actualParams.headOption.flatMap(_._2.position)) } + } if (name == "call") return Some(function) function.params match { case NormalParamSignature(params) => diff --git a/src/test/scala/millfork/test/FunctionPointerSuite.scala b/src/test/scala/millfork/test/FunctionPointerSuite.scala index 6770d5e5..71ac808b 100644 --- a/src/test/scala/millfork/test/FunctionPointerSuite.scala +++ b/src/test/scala/millfork/test/FunctionPointerSuite.scala @@ -1,7 +1,7 @@ package millfork.test import millfork.Cpu -import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCrossPlatformRun, ShouldNotCompile} +import millfork.test.emu.{EmuCrossPlatformBenchmarkRun, EmuSizeOptimizedCrossPlatformRun, EmuUnoptimizedCrossPlatformRun, ShouldNotCompile} import org.scalatest.{AppendedClues, FunSuite, Matchers} /** @@ -30,7 +30,7 @@ class FunctionPointerSuite extends FunSuite with Matchers with AppendedClues{ } test("Function pointers 2") { - EmuUnoptimizedCrossPlatformRun (Cpu.Mos, Cpu.Cmos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Motorola6809)( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Cmos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Motorola6809)( """ | const byte COUNT = 128 | array output0[COUNT] @$c000 @@ -100,7 +100,7 @@ class FunctionPointerSuite extends FunSuite with Matchers with AppendedClues{ } test("Function pointers 3") { - EmuUnoptimizedCrossPlatformRun (Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)( + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)( """ | const byte COUNT = 128 | array(word) output0[COUNT] @$c000 @@ -122,6 +122,32 @@ class FunctionPointerSuite extends FunSuite with Matchers with AppendedClues{ } } + test("Function pointers 4") { + EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)( + """ + | const byte COUNT = 128 + | array(byte) output0[COUNT] @$c000 + | noinline void fill(function.byte.to.byte f) { + | byte i + | for i,0,until,COUNT { + | call(f, i) + | } + | } + | byte iota_at(byte x) { + | output0[x] = x + | return x + | } + | void main() { + | fill(iota_at.pointer) + | } + | + """.stripMargin) { m => + for (i <- 0 until 0x80) { + m.readByte(0xc000 + i) should equal(i) withClue ("id " + i) + } + } + } + test("Interrupt pointers") { EmuUnoptimizedCrossPlatformRun (Cpu.Mos, Cpu.Z80, Cpu.Motorola6809)( """