added callfar2() builtin function that allows to set A,X,Y and Carry arguments.

This commit is contained in:
Irmen de Jong 2024-10-08 21:25:37 +02:00
parent b2bdfe8482
commit eaa22a9d13
11 changed files with 130 additions and 25 deletions

View File

@ -128,6 +128,7 @@ val BuiltinFunctions: Map<String, FSignature> = 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),
)

View File

@ -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]

View File

@ -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<IRCodeChunkBase>()
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<IRCodeChunkBase>()
val number = call.args[0]

View File

@ -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!

View File

@ -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)

View File

@ -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
}}
}
}

View File

@ -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),
}

View File

@ -14,7 +14,7 @@
<keywords keywords="&amp;;-&gt;;@;and;as;asmsub;break;clobbers;continue;do;downto;else;false;for;goto;if;if_cc;if_cs;if_eq;if_mi;if_ne;if_neg;if_nz;if_pl;if_pos;if_vc;if_vs;if_z;in;inline;not;or;repeat;return;romsub;step;sub;to;true;unroll;until;when;while;xor;~" ignore_case="false" />
<keywords2 keywords="%address;%asm;%asmbinary;%asminclude;%breakpoint;%encoding;%import;%ir;%launcher;%option;%output;%zeropage;%zpallowed;%zpreserved;atascii:;cp437:;default:;iso16:;iso5:;iso:;kata:;petscii:;sc:" />
<keywords3 keywords="@nozp;@requirezp;@shared;@split;@zp;bool;byte;const;float;str;ubyte;uword;void;word" />
<keywords4 keywords="abs;call;callfar;clamp;cmp;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekf;peekw;poke;pokef;pokew;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt" />
<keywords4 keywords="abs;call;callfar;callfar2;clamp;cmp;divmod;len;lsb;max;memory;min;mkword;msb;peek;peekf;peekw;poke;pokef;pokew;rol;rol2;ror;ror2;rrestore;rrestorex;rsave;rsavex;setlsb;setmsb;sgn;sizeof;sqrt" />
</highlighting>
<extensionMap>
<mapping ext="p8" />

View File

@ -27,7 +27,7 @@
<Keywords name="Keywords1">void const&#x000D;&#x000A;str&#x000D;&#x000A;byte ubyte bool&#x000D;&#x000A;word uword&#x000D;&#x000A;float&#x000D;&#x000A;zp shared split requirezp nozp</Keywords>
<Keywords name="Keywords2">%address&#x000D;&#x000A;%asm&#x000D;&#x000A;%ir&#x000D;&#x000A;%asmbinary&#x000D;&#x000A;%asminclude&#x000D;&#x000A;%breakpoint&#x000D;&#x000A;%encoding&#x000D;&#x000A;%import&#x000D;&#x000A;%launcher&#x000D;&#x000A;%option&#x000D;&#x000A;%output&#x000D;&#x000A;%zeropage&#x000D;&#x000A;%zpreserved&#x000D;&#x000A;%zpallowed</Keywords>
<Keywords name="Keywords3">inline sub asmsub romsub&#x000D;&#x000A;clobbers&#x000D;&#x000A;asm&#x000D;&#x000A;if&#x000D;&#x000A;when else&#x000D;&#x000A;if_cc if_cs if_eq if_mi if_neg if_nz if_pl if_pos if_vc if_vs if_z&#x000D;&#x000A;for in step do while repeat unroll&#x000D;&#x000A;break continue return goto</Keywords>
<Keywords name="Keywords4">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</Keywords>
<Keywords name="Keywords4">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</Keywords>
<Keywords name="Keywords5">true false&#x000D;&#x000A;not and or xor&#x000D;&#x000A;as to downto |&gt;</Keywords>
<Keywords name="Keywords6"></Keywords>
<Keywords name="Keywords7"></Keywords>

View File

@ -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

View File

@ -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