From eaa22a9d130ac670fd669da42a528b13a3f2cfb2 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 8 Oct 2024 21:25:37 +0200 Subject: [PATCH] added callfar2() builtin function that allows to set A,X,Y and Carry arguments. --- .../src/prog8/code/core/BuiltinFunctions.kt | 1 + .../codegen/cpu6502/BuiltinFunctionsAsmGen.kt | 52 ++++++++++++++++- .../codegen/intermediate/BuiltinFuncGen.kt | 24 ++++++++ docs/source/libraries.rst | 4 ++ docs/source/todo.rst | 1 - examples/test.p8 | 57 ++++++++++++++----- .../src/prog8/intermediate/IMSyscall.kt | 9 +-- syntax-files/IDEA/Prog8.xml | 2 +- syntax-files/NotepadPlusPlus/Prog8.xml | 2 +- syntax-files/Vim/prog8_builtins.vim | 2 +- .../src/prog8/vm/VmProgramLoader.kt | 1 + 11 files changed, 130 insertions(+), 25 deletions(-) diff --git a/codeCore/src/prog8/code/core/BuiltinFunctions.kt b/codeCore/src/prog8/code/core/BuiltinFunctions.kt index 5267efdd0..926b82046 100644 --- a/codeCore/src/prog8/code/core/BuiltinFunctions.kt +++ b/codeCore/src/prog8/code/core/BuiltinFunctions.kt @@ -128,6 +128,7 @@ val BuiltinFunctions: Map = mapOf( "rrestore" to FSignature(false, emptyList(), null), "memory" to FSignature(true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD)), FParam("alignment", arrayOf(DataType.UWORD))), DataType.UWORD), "callfar" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), DataType.UWORD), + "callfar2" to FSignature(false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("argA", arrayOf(DataType.UBYTE)), FParam("argX", arrayOf(DataType.UBYTE)), FParam("argY", arrayOf(DataType.UBYTE)), FParam("argC", arrayOf(DataType.BOOL))), DataType.UWORD), "call" to FSignature(false, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD), ) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt index 38a3e2bc3..a924993bd 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/BuiltinFunctionsAsmGen.kt @@ -64,6 +64,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, "rrestore" -> funcRrestore() "cmp" -> funcCmp(fcall) "callfar" -> funcCallFar(fcall, resultRegister) + "callfar2" -> funcCallFar2(fcall, resultRegister) "call" -> funcCall(fcall) "prog8_ifelse_bittest_set" -> throw AssemblyError("prog8_ifelse_bittest_set() should have been translated as part of an ifElse statement") "prog8_ifelse_bittest_notset" -> throw AssemblyError("prog8_ifelse_bittest_notset() should have been translated as part of an ifElse statement") @@ -298,8 +299,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } 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") @@ -329,6 +328,55 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram, } } + private fun funcCallFar2(fcall: PtBuiltinFunctionCall, resultRegister: RegisterOrPair?) { + if(asmgen.options.compTarget.name != "cx16") + throw AssemblyError("callfar2 only works on cx16 target at this time") + + fun assignArgs() { + fun assign(value: PtExpression, register: Char) { + when(value) { + is PtBool -> asmgen.out(" ld$register #${value.asInt()}") + is PtNumber -> asmgen.out(" ld$register #${value.number.toInt()}") + is PtIdentifier -> asmgen.out(" ld$register ${asmgen.asmVariableName(value)}") + else -> TODO("callfar2: support non-simple expressions for arguments") + } + } + assign(fcall.args[2], 'a') + assign(fcall.args[3], 'x') + assign(fcall.args[4], 'y') + val carry = fcall.args[5].asConstInteger() + if(carry!=null) + asmgen.out(if(carry==0) " clc" else " sec") + else + TODO("callfar2: support non-const argument values") + } + + val constBank = fcall.args[0].asConstInteger() + val constAddress = fcall.args[1].asConstInteger() + if(constBank!=null && constAddress!=null) { + assignArgs() + asmgen.out(""" + jsr cx16.JSRFAR + .word ${constAddress.toHex()} + .byte $constBank""") + } else { + asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // bank + asmgen.out(" sta (++)+0") + asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY) // jump address + asmgen.out(" sta (+)+0 | sty (+)+1") + assignArgs() + asmgen.out(""" + jsr cx16.JSRFAR ++ .word 0 ++ .byte 0""") + } + + // note that by convention the values in A+Y registers are now the return value of the call. + if(resultRegister!=null) { + assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister, false, fcall.position, null, asmgen), RegisterOrPair.AY) + } + } + private fun funcCmp(fcall: PtBuiltinFunctionCall) { val arg1 = fcall.args[0] val arg2 = fcall.args[1] diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index 119b4dc35..4b9c9fcc1 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -17,6 +17,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe "divmod__uword" -> funcDivmod(call, IRDataType.WORD) "rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore "callfar" -> funcCallfar(call) + "callfar2" -> funcCallfar2(call) "call" -> funcCall(call) "msb" -> funcMsb(call) "lsb" -> funcLsb(call) @@ -149,6 +150,29 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe return ExpressionCodeResult(result, IRDataType.WORD, argumentwordTr.resultReg, -1) } + private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult { + val result = mutableListOf() + addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null) + val bankTr = exprGen.translateExpression(call.args[0]) + val addressTr = exprGen.translateExpression(call.args[1]) + val argumentA = exprGen.translateExpression(call.args[2]) + val argumentX = exprGen.translateExpression(call.args[3]) + val argumentY = exprGen.translateExpression(call.args[4]) + val argumentCarry = exprGen.translateExpression(call.args[5]) + addToResult(result, bankTr, bankTr.resultReg, -1) + addToResult(result, addressTr, addressTr.resultReg, -1) + addToResult(result, argumentA, argumentA.resultReg, -1) + addToResult(result, argumentX, argumentX.resultReg, -1) + addToResult(result, argumentY, argumentY.resultReg, -1) + addToResult(result, argumentCarry, argumentCarry.resultReg, -1) + result += codeGen.makeSyscall(IMSyscall.CALLFAR2, listOf(IRDataType.BYTE to bankTr.resultReg, IRDataType.WORD to addressTr.resultReg, + IRDataType.BYTE to argumentA.resultReg, + IRDataType.BYTE to argumentX.resultReg, + IRDataType.BYTE to argumentY.resultReg, + IRDataType.BYTE to argumentCarry.resultReg), IRDataType.WORD to addressTr.resultReg) + return ExpressionCodeResult(result, IRDataType.WORD, addressTr.resultReg, -1) + } + private fun funcDivmod(call: PtBuiltinFunctionCall, type: IRDataType): ExpressionCodeResult { val result = mutableListOf() val number = call.args[0] diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index 60d84910f..5ab163062 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -200,6 +200,10 @@ callfar (bank, address, argumentword) -> uword ; NOTE: specific to cx16 targ NOTE: this routine is very inefficient, so don't use it to call often. Set the bank yourself or even write a custom tailored trampoline routine if you need to. Or use ``call`` if you can. +callfar2 (bank, address, argA, argX, argY, argCarry) -> uword ; NOTE: specific to cx16 target for now + Identical to ``callfar``, except here you can give arguments not only for AY, + but for each of the A, X and Y registers (each an ubyte) and the Carry status bit as well (a boolean). + syscall (callnr), syscall1 (callnr, arg), syscall2 (callnr, arg1, arg2), syscall3 (callnr, arg1, arg2, arg3) Functions for doing a system call on targets that support this. Currently no actual target uses this though except, possibly, the experimental code generation target! diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 6e6fba9eb..fd2117481 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -13,7 +13,6 @@ Future Things and Ideas ^^^^^^^^^^^^^^^^^^^^^^^ - Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions -- callfar() should allow setting an argument in the X register as well? - 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 36cd589af..afa348dc7 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,19 +1,46 @@ +%import textio +%zeropage basicsafe + main { - sub foo() { - cx16.r0++ - } - asmsub baz() { - %asm{{ - jsr p8s_foo - }} - } - asmsub bar() { - %asm{{ - inx - jmp p8s_foo - }} - } sub start() { - bar() + routine(11,22,33) + txt.nl() + cx16.r0 = callfar2(0, &routine, 11, 22, 33, true) + txt.nl() + txt.print_uwhex(cx16.r0, true) + txt.nl() + cx16.r0 = callfar(0, &routine, 11*256 + 22) + txt.nl() + txt.print_uwhex(cx16.r0, true) + txt.nl() + } + + asmsub routine(ubyte v1 @A, ubyte v2 @X, ubyte v3 @Y) -> uword @AY { + %asm {{ + sta cx16.r8L + stx cx16.r9L + sty cx16.r10L + lda #0 + rol a + sta cx16.r11L + + lda cx16.r8L + jsr txt.print_ub + lda #' ' + jsr txt.chrout + lda cx16.r9L + jsr txt.print_ub + lda #' ' + jsr txt.chrout + lda cx16.r10L + jsr txt.print_ub + lda #' ' + jsr txt.chrout + lda cx16.r11L + jsr txt.print_ub + lda #$31 + ldy #$ea + rts + }} } } diff --git a/intermediate/src/prog8/intermediate/IMSyscall.kt b/intermediate/src/prog8/intermediate/IMSyscall.kt index ab6f4417d..e3548e542 100644 --- a/intermediate/src/prog8/intermediate/IMSyscall.kt +++ b/intermediate/src/prog8/intermediate/IMSyscall.kt @@ -16,8 +16,9 @@ enum class IMSyscall(val number: Int) { CLAMP_WORD(0x1015), CLAMP_FLOAT(0x1016), CALLFAR(0x1017), - MEMCOPY(0x1018), - MEMCOPY_SMALL(0x1019), - ARRAYCOPY_SPLITW_TO_NORMAL(0x101a), - ARRAYCOPY_NORMAL_TO_SPLITW(0x101b), + CALLFAR2(0x1018), + MEMCOPY(0x1019), + MEMCOPY_SMALL(0x101a), + ARRAYCOPY_SPLITW_TO_NORMAL(0x101b), + ARRAYCOPY_NORMAL_TO_SPLITW(0x101c), } diff --git a/syntax-files/IDEA/Prog8.xml b/syntax-files/IDEA/Prog8.xml index b2459c545..8463a1c88 100644 --- a/syntax-files/IDEA/Prog8.xml +++ b/syntax-files/IDEA/Prog8.xml @@ -14,7 +14,7 @@ - + diff --git a/syntax-files/NotepadPlusPlus/Prog8.xml b/syntax-files/NotepadPlusPlus/Prog8.xml index bb0b86953..acb42df2d 100644 --- a/syntax-files/NotepadPlusPlus/Prog8.xml +++ b/syntax-files/NotepadPlusPlus/Prog8.xml @@ -27,7 +27,7 @@ void const str byte ubyte bool word uword float zp shared split requirezp nozp %address %asm %ir %asmbinary %asminclude %breakpoint %encoding %import %launcher %option %output %zeropage %zpreserved %zpallowed inline sub asmsub romsub clobbers asm if when else if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z for in step do while repeat unroll break continue return goto - abs call callfar clamp cmp divmod len lsb lsl lsr memory mkword min max msb peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw + abs call callfar callfar2 clamp cmp divmod len lsb lsl lsr memory mkword min max msb peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex rnd rndw rol rol2 ror ror2 setlsb setmsb sgn sizeof sqrtw true false not and or xor as to downto |> diff --git a/syntax-files/Vim/prog8_builtins.vim b/syntax-files/Vim/prog8_builtins.vim index 47a399927..844baee35 100644 --- a/syntax-files/Vim/prog8_builtins.vim +++ b/syntax-files/Vim/prog8_builtins.vim @@ -15,7 +15,7 @@ syn keyword prog8BuiltInFunc len " Miscellaneous functions syn keyword prog8BuiltInFunc cmp divmod lsb msb mkword min max peek peekw peekf poke pokew pokef rsave rsavex rrestore rrestorex syn keyword prog8BuiltInFunc rol rol2 ror ror2 sizeof setlsb setmsb -syn keyword prog8BuiltInFunc memory call callfar clamp +syn keyword prog8BuiltInFunc memory call callfar callfar2 clamp " c64/floats.p8 diff --git a/virtualmachine/src/prog8/vm/VmProgramLoader.kt b/virtualmachine/src/prog8/vm/VmProgramLoader.kt index b05b7637e..60d3363fc 100644 --- a/virtualmachine/src/prog8/vm/VmProgramLoader.kt +++ b/virtualmachine/src/prog8/vm/VmProgramLoader.kt @@ -114,6 +114,7 @@ class VmProgramLoader { IMSyscall.CLAMP_UWORD.number -> Syscall.CLAMP_UWORD IMSyscall.CLAMP_FLOAT.number -> Syscall.CLAMP_FLOAT IMSyscall.CALLFAR.number -> throw IRParseException("vm doesn't support the callfar() syscall") + IMSyscall.CALLFAR2.number -> throw IRParseException("vm doesn't support the callfar2() syscall") IMSyscall.MEMCOPY.number -> Syscall.MEMCOPY IMSyscall.MEMCOPY_SMALL.number -> Syscall.MEMCOPY_SMALL IMSyscall.ARRAYCOPY_SPLITW_TO_NORMAL.number -> Syscall.ARRAYCOPY_SPLITW_TO_NORMAL