From a064ade1e0a2db4c2271c66dd2c8b39b6893495e Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 29 Sep 2024 23:18:51 +0200 Subject: [PATCH] better codegen for call() function --- .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 35 +++-- .../compiler/astprocessing/AstChecker.kt | 2 +- compiler/test/ast/TestSubroutines.kt | 3 + docs/source/todo.rst | 4 +- examples/test.p8 | 124 ++++-------------- 5 files changed, 57 insertions(+), 111 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index f7f34999e..070260beb 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -266,23 +266,40 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } private fun funcCall(fcall: PtBuiltinFunctionCall) { + // note: the routine can return a word value (in AY) val constAddr = fcall.args[0].asConstInteger() if(constAddr!=null) { asmgen.out(" jsr ${constAddr.toHex()}") - } else { - asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY) // jump address - asmgen.out(""" - sta (+)+1 - sty (+)+2 -+ jsr 0 ; modified""") - // TODO: avoid using modifying code here by pushing return address on the stack manually and use JMP (INDIRECT) ? And if it's just a variable, simply JMP (variable) ! - // TODO: also do this for CallFar below! + return } - // note: the routine can return a word value (in AY) + val identifier = fcall.args[0] as? PtIdentifier + if(identifier!=null) { + asmgen.out(""" + ; push a return address so the jmp becomes indirect jsr + lda #>((+)-1) + pha + lda #<((+)-1) + pha + jmp (${asmgen.asmSymbolName(identifier)}) ++""") + return + } + + asmgen.assignExpressionToVariable(fcall.args[0], asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD) // jump address + asmgen.out(""" + ; push a return address so the jmp becomes indirect jsr + lda #>((+)-1) + pha + lda #<((+)-1) + pha + jmp (P8ZP_SCRATCH_W2) ++""") } private fun funcCallFar(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) { + // TODO apply same optimizations here as used on call() codegen above + if(asmgen.options.compTarget.name != "cx16") throw AssemblyError("callfar only works on cx16 target at this time") diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index acd232596..b34600567 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1346,7 +1346,7 @@ internal class AstChecker(private val program: Program, if(target.name=="call") { if(args[0] is AddressOf) errors.err("can't call this indirectly, just use normal function call syntax", args[0].position) - if(args[0] is IdentifierReference) { + else if(args[0] is IdentifierReference) { val callTarget = (args[0] as IdentifierReference).targetStatement(program) if(callTarget !is VarDecl) errors.err("can't call this indirectly, just use normal function call syntax", args[0].position) diff --git a/compiler/test/ast/TestSubroutines.kt b/compiler/test/ast/TestSubroutines.kt index fb18068bf..a80971c83 100644 --- a/compiler/test/ast/TestSubroutines.kt +++ b/compiler/test/ast/TestSubroutines.kt @@ -92,8 +92,11 @@ main { main { sub start() { uword func = 12345 + uword[] pointers = [1234,6789] void call(func) ; ok void call(12345) ; ok + void call(pointers[1]) ; ok + void call(cx16.r0+10) ; ok cx16.r0 = call(func) ; ok void call(&start) ; error void call(start) ; error diff --git a/docs/source/todo.rst b/docs/source/todo.rst index bf020fc13..66e952dce 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -5,9 +5,8 @@ Regenerate skeleton doc files. "invalid number of arguments" -> print the list of missing arguments -call() asm gen in funcCall() could be improved by not using modifying code , see the TODO. +callfar() should allow setting an argument in the X register as well? + similar optimizations that call() got. -callfar() should allow setting an argument in the X register as well? Add a new SublimeText syntax file for prog8, and also install this for bat: https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions @@ -21,6 +20,7 @@ Future Things and Ideas ^^^^^^^^^^^^^^^^^^^^^^^ Compiler: +- AST weirdness: why is call(...) a normal FunctionCallStatement and not a BuiltinFunctionCall? What does ror() produce for instance? - Can we support signed % (remainder) somehow? - Don't add "random" rts to %asm blocks but instead give a warning about it? (but this breaks existing behavior that others already depend on... command line switch? block directive?) - IR: implement missing operators in AssignmentGen (array shifts etc) diff --git a/examples/test.p8 b/examples/test.p8 index 68b85a961..2bcf93787 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,116 +1,42 @@ %import textio %import string %import compression +%import test_stack %zeropage basicsafe %option no_sysinit main { sub start() { - ubyte[] rle = [ - 5, '1', '2', '3', '4', '5', '6', - 3, 'a', 'b', 'c', 'd', - 0, 'z', - 3, '1', '2', '3','4', - -5, '=', - -127, '+', - 4, '!', '@', '#', '$', '%', - 128] + test_stack.test() + example(function1, 42) + example(function1, 99) + example(function2, 42) + example(function2, 99) + test_stack.test() + cx16.r0++ - str @shared result = "\x00"*200 - - uword ptr = &rle - - txt.print_uw(compression.decode_rle_srcfunc(callback, result, len(result))) - txt.nl() - txt.print_uw(compression.decode_rle(rle, result, len(result))) - txt.nl() - txt.print_uw(string.length(result)) - txt.nl() - txt.print(result) - txt.nl() - txt.print_uwhex(&result, true) - txt.nl() - - sub callback() -> ubyte { - ubyte x = @(ptr) - ptr++ - return x - } - - - ubyte[256] buffer - ubyte[256] buffer2 - uword bufptr = &buffer - uword outputsize=0 - - sub outputter(ubyte value) { - @(bufptr) = value - bufptr++ - outputsize++ - } - - str input = iso:"123456aaaaabcdzzz1234======++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++!@#$%" - ubyte[31] expected_rle = [$5, $31, $32, $33, $34, $35, $36, $fc, $61, $2, $62, $63, $64, $fe, $7a, $3, $31, $32, $33, $34, $fb, $3d, $81, $2b, $4, $21, $40, $23, $24, $25, $80] - txt.print(input) - txt.nl() - - compression.encode_rle_outfunc(input, len(input), outputter, true) - txt.print_uw(outputsize) - txt.nl() - if outputsize!=len(expected_rle) - txt.print("wrong rle size!\n") - - txt.print("\ncompare rle (encode using callback):\n") - for cx16.r9L in 0 to len(expected_rle)-1 { -; txt.print_ub0(cx16.r9L) -; txt.spc() -; txt.print_ubhex(expected_rle[cx16.r9L], false) -; txt.spc() -; txt.print_ubhex(buffer[cx16.r9L], false) - if buffer[cx16.r9L] != expected_rle[cx16.r9L] - txt.print(" wrong rle data!") -; txt.nl() - } - txt.nl() - - cx16.r0 = compression.decode_rle(buffer, buffer2, len(buffer2)) - if cx16.r0 != len(input) - txt.print("wrong decompress result!\n") - else { - txt.print("good result: ") - txt.print(buffer2) + sub function1(ubyte arg) { + txt.print("function 1 arg=") + txt.print_ub(arg) txt.nl() } - - buffer[0] = buffer[1] = buffer[2] = 128 - outputsize = compression.encode_rle(input, len(input), buffer, true) - txt.print_uw(outputsize) - txt.nl() - if outputsize!=len(expected_rle) - txt.print("wrong rle size!\n") - - txt.print("\ncompare rle (encode into buffer):\n") - for cx16.r9L in 0 to len(expected_rle)-1 { -; txt.print_ub0(cx16.r9L) -; txt.spc() -; txt.print_ubhex(expected_rle[cx16.r9L], false) -; txt.spc() -; txt.print_ubhex(buffer[cx16.r9L], false) - if buffer[cx16.r9L] != expected_rle[cx16.r9L] - txt.print(" wrong rle data!") -; txt.nl() - } - txt.nl() - - cx16.r0 = compression.decode_rle(buffer, buffer2, len(buffer2)) - if cx16.r0 != len(input) - txt.print("wrong decompress result!\n") - else { - txt.print("good result: ") - txt.print(buffer2) + sub function2(ubyte arg) { + txt.print("function 2 arg=") + txt.print_ub(arg) txt.nl() } + + sub example(uword function, ubyte value) { + + %asm {{ + lda p8v_value + }} + + call(function) + cx16.r1 = function+10 + call(cx16.r1-10) + } } }