From 67f7ffa52dcc012f9ea865ff34728d11f7de59c8 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 29 Sep 2025 02:32:56 +0200 Subject: [PATCH] multi value returns with longs --- .../src/prog8/codegen/cpu6502/AsmGen.kt | 3 +- .../cpu6502/assignment/AsmAssignment.kt | 28 ++----------------- .../codegen/intermediate/AssignmentGen.kt | 5 ++++ .../prog8/codegen/intermediate/IRCodeGen.kt | 7 ++++- docs/source/technical.rst | 4 +-- docs/source/todo.rst | 4 ++- examples/test.p8 | 13 +++++---- 7 files changed, 29 insertions(+), 35 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 0aaed0030..a5686ee11 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -806,6 +806,7 @@ class AsmGen6502Internal ( RegisterOrPair.AY, RegisterOrPair.XY -> assignmentAsmGen.assignRegisterpairWord(target, reg) in Cx16VirtualRegisters -> assignmentAsmGen.assignVirtualRegister(target, reg) + in combinedLongRegisters -> assignmentAsmGen.assignVirtualRegister(target, reg) RegisterOrPair.FAC1 -> assignmentAsmGen.assignFAC1float(target) RegisterOrPair.FAC2 -> assignmentAsmGen.assignFAC2float(target) else -> throw AssemblyError("invalid register") @@ -1306,7 +1307,7 @@ $repeatLabel""") assignExpressionTo(it.first as PtExpression, tgt) } assigns.first().also { - assignExpressionToRegister(it.first as PtExpression, it.second.first.registerOrPair!!) + assignExpressionToRegister(it.first as PtExpression, it.second.first.registerOrPair!!, (it.first as PtExpression).type.isSigned) } } out(" rts") diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt index 2818c7295..b40233ade 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AsmAssignment.kt @@ -114,39 +114,17 @@ internal class AsmAssignTarget(val kind: TargetStorageKind, RegisterOrPair.FAC2 -> { AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, scope, pos, register = registers) } - RegisterOrPair.R0, - RegisterOrPair.R1, - RegisterOrPair.R2, - RegisterOrPair.R3, - RegisterOrPair.R4, - RegisterOrPair.R5, - RegisterOrPair.R6, - RegisterOrPair.R7, - RegisterOrPair.R8, - RegisterOrPair.R9, - RegisterOrPair.R10, - RegisterOrPair.R11, - RegisterOrPair.R12, - RegisterOrPair.R13, - RegisterOrPair.R14, - RegisterOrPair.R15 -> { + in Cx16VirtualRegisters -> { val dt = if(signed) DataType.WORD else DataType.UWORD AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers) } - - RegisterOrPair.R0R1_32, - RegisterOrPair.R2R3_32, - RegisterOrPair.R4R5_32, - RegisterOrPair.R6R7_32, - RegisterOrPair.R8R9_32, - RegisterOrPair.R10R11_32, - RegisterOrPair.R12R13_32, - RegisterOrPair.R14R15_32 -> { + in combinedLongRegisters -> { val dt = if(signed) DataType.LONG else TODO("unsigned long") AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, dt, scope, pos, register = registers) } + else -> throw AssemblyError("weird register $registers") } } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt index 296e66688..c1a3ae578 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/AssignmentGen.kt @@ -76,6 +76,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express RegisterOrPair.AY -> addInstr(result, IRInstruction(Opcode.LOADHAY, IRDataType.WORD, reg1=regNum), null) RegisterOrPair.XY -> addInstr(result, IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum), null) in Cx16VirtualRegisters -> addInstr(result, IRInstruction(Opcode.LOADM, irType(returns.type), reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}"), null) + in combinedLongRegisters -> { + require(returns.type.isLong) + val startreg = returns.register.registerOrPair!!.name.take(2).lowercase() + addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.LONG, reg1=regNum, labelSymbol = "cx16.${startreg}"), null) + } RegisterOrPair.FAC1 -> addInstr(result, IRInstruction(Opcode.LOADHFACZERO, IRDataType.FLOAT, fpReg1 = regNum), null) RegisterOrPair.FAC2 -> addInstr(result, IRInstruction(Opcode.LOADHFACONE, IRDataType.FLOAT, fpReg1 = regNum), null) null -> if(returns.register.statusflag!=null) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 0e19c77c4..8bb8c74e9 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -1976,12 +1976,17 @@ class IRCodeGen( in Cx16VirtualRegisters -> { chunk += IRInstruction(Opcode.STOREM, paramDt, reg1=resultReg, labelSymbol = "cx16.${registerOrFlag.registerOrPair.toString().lowercase()}") } + in combinedLongRegisters -> { + require(paramDt==IRDataType.LONG) + val startreg = registerOrFlag.registerOrPair!!.name.take(2).lowercase() + chunk += IRInstruction(Opcode.STOREM, paramDt, reg1=resultReg, labelSymbol = "cx16.${startreg}") + } null -> when(registerOrFlag.statusflag) { // TODO: do the statusflag argument as last Statusflag.Pc -> chunk += IRInstruction(Opcode.LSR, paramDt, reg1=resultReg) else -> throw AssemblyError("unsupported statusflag as param") } - else -> throw AssemblyError("unsupported register arg") + else -> throw AssemblyError("unsupported register arg $registerOrFlag") } return chunk } diff --git a/docs/source/technical.rst b/docs/source/technical.rst index 466d70398..12dd24cb4 100644 --- a/docs/source/technical.rst +++ b/docs/source/technical.rst @@ -160,7 +160,7 @@ Regular subroutines **Single arguments will often be passed in registers:** -For *single* byte, word, long, and pointer arguments, the values are simply loaded in cpu registers by the caller before calling the subroutine. +For *single* byte, word, and pointer arguments (not long or float), the values are simply loaded in cpu registers by the caller before calling the subroutine. *The subroutine itself will take care of putting the values into the parameter variables.* This saves on code size because otherwise all callers would have to store the values in those variables themselves. Note that his convention is also still used for subroutines that specify parameters to be put into @@ -179,7 +179,7 @@ Single word parameter: ``sub foo(uword bar) { ... }`` Single pointer parameter: ``sub foo(^^ubyte bar) { ... }`` gets bar in the register pair A + Y (lsb in A, msb in Y), *subroutine* stores it into parameter variable -Floating point parameter: ``sub foo(float bar) { ... }`` +Long or Floating point parameter: ``sub foo(long bar) { ... }``, ``sub foo(float bar) { ... }`` value for bar gets stored into the parameter variable *by the caller* Other: ``sub foo(ubyte bar, ubyte baz, ubyte zoo) { ... }`` diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 29c7d01f3..a91bc53af 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,7 +3,6 @@ TODO LONG TYPE --------- -- call convention: NEVER put LONG parameter into R0:R1 just use parameter variable (also fix convention doc) - call convention for asmsubs: asmsubs don't have syntax for passing a long value so use explicit separate msw() and lsw() arguments... Or introduce new syntax for R0+R1 combo's? - make sure == and != work with longs against byte and words as well signed and unsigned - how hard is it to also implement the other comparison operators on longs? @@ -32,6 +31,9 @@ STRUCTS and TYPED POINTERS Future Things and Ideas ^^^^^^^^^^^^^^^^^^^^^^^ +- After long variable type is completed: make all constants long by default (remove type name altogether), reduce to target type implictly if the actual value fits. + This will break some existing programs that depend on value wrap arounds, but gives more intuitive constant number handling. + Can give descriptive error message for old syntax that still includes the type name? - improve ANTLR grammar with better error handling (as suggested by Qwen AI) - allow memory() to occur in array initializer - when a complete block is removed because unused, suppress all info messages about everything in the block being removed diff --git a/examples/test.p8 b/examples/test.p8 index a2c1c8e09..5e2433ee3 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,17 +4,20 @@ main { sub start() { long lv = 99887766 - lv = func(lv) + lv, cx16.r0, cx16.r2L = func(lv) txt.print_l(lv) + txt.spc() + txt.print_uw(cx16.r0) + txt.spc() + txt.print_ub(cx16.r2L) + txt.nl() } - sub func(long arg) -> long { + sub func(long arg) -> long, uword, ubyte { arg -= 1234567 txt.print("func: ") txt.print_l(arg) txt.nl() - return arg + return arg, 9999, 42 } - - ; TODO multi-value returns }