Skip to content

Commit

Permalink
6502 codegen for multi-assigns
Browse files Browse the repository at this point in the history
  • Loading branch information
irmen committed Mar 25, 2024
1 parent 2e37f5d commit 9a27505
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 9 deletions.
5 changes: 4 additions & 1 deletion codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,10 @@ class AsmGen6502Internal (
is PtInlineAssembly -> translate(stmt)
is PtBuiltinFunctionCall -> builtinFunctionsAsmGen.translateFunctioncallStatement(stmt)
is PtFunctionCall -> functioncallAsmGen.translateFunctionCallStatement(stmt)
is PtAssignment -> assignmentAsmGen.translate(stmt)
is PtAssignment -> {
if(stmt.multiTarget) assignmentAsmGen.translateMultiAssign(stmt)
else assignmentAsmGen.translate(stmt)
}
is PtAugmentedAssign -> assignmentAsmGen.translate(stmt)
is PtJump -> {
val (asmLabel, indirect) = getJumpTarget(stmt)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package prog8.codegen.cpu6502.assignment

import prog8.code.StMemVar
import prog8.code.StRomSub
import prog8.code.StRomSubParameter
import prog8.code.StStaticVariable
import prog8.code.ast.*
import prog8.code.core.*
Expand Down Expand Up @@ -32,6 +34,77 @@ internal class AssignmentAsmGen(private val program: PtProgram,
augmentableAsmGen.translate(assign, augmentedAssign.definingISub())
}

fun translateMultiAssign(assignment: PtAssignment) {
// TODO("translate multi-value assignment ${assignment.position}")
val values = assignment.value as? PtFunctionCall
?: throw AssemblyError("only function calls can return multiple values in a multi-assign")

val sub = asmgen.symbolTable.lookup(values.name) as? StRomSub
?: throw AssemblyError("only asmsubs can return multiple values")

require(sub.returns.size>=2)
if(sub.returns.any { it.type==DataType.FLOAT })
TODO("deal with (multiple?) FP return registers")

asmgen.translate(values)

fun needsToSaveA(registersResults: List<Pair<StRomSubParameter, PtNode>>): Boolean =
if(registersResults.isEmpty())
false
else if(registersResults.all { (it.second as PtAssignTarget).identifier!=null})
false
else
true

fun assignCarryResult(target: PtAssignTarget, saveA: Boolean) {
if(saveA) asmgen.out(" pha")
asmgen.out(" lda #0 | rol a")
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
assignRegisterByte(tgt, CpuRegister.A, false, false)
if(saveA) asmgen.out(" pla")
}

fun assignRegisterResults(registersResults: List<Pair<StRomSubParameter, PtNode>>) {
registersResults.forEach { (returns, target) ->
val targetIdent = (target as PtAssignTarget).identifier
val targetMem = target.memory
if(targetIdent!=null || targetMem!=null) {
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
when(returns.type) {
in ByteDatatypesWithBoolean -> {
assignRegisterByte(tgt, returns.register.registerOrPair!!.asCpuRegister(), false, false)
}
in WordDatatypes -> {
assignRegisterpairWord(tgt, returns.register.registerOrPair!!)
}
else -> throw AssemblyError("weird dt")
}
}
else TODO("array target for multi-value assignment") // Not done yet due to result register clobbering complexity
}
}

// because we can only handle integer results right now we can just zip() it all up
val (statusFlagResult, registersResults) = sub.returns.zip(assignment.children).partition { it.first.register.statusflag!=null }
if(statusFlagResult.isNotEmpty()) {
val (returns, target) = statusFlagResult.single()
if(returns.register.statusflag!=Statusflag.Pc)
TODO("other status flag for return value")

target as PtAssignTarget
if(registersResults.all { (it.second as PtAssignTarget).identifier!=null}) {
// all other results are just stored into identifiers directly so first handle those
// (simple store instructions that don't modify the carry flag)
assignRegisterResults(registersResults)
assignCarryResult(target, false)
return
}
assignCarryResult(target, needsToSaveA(registersResults))
}
assignRegisterResults(registersResults)
}


fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
when(assign.source.kind) {
SourceStorageKind.LITERALBOOLEAN -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
val target = it.first.second as PtAssignTarget
result += assignCpuRegister(returns, regNumber, target)
}
result.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(assignment.position)
return result
} else {
if (assignment.target.children.single() is PtIrRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")

val chunks = translateRegularAssign(assignment)
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(assignment.position)
return chunks
return translateRegularAssign(assignment)
}
}

Expand Down
4 changes: 2 additions & 2 deletions docs/source/todo.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
TODO
====

6502 codegen: make multi return value asmsub calls work.

add unit tests for vm and 6502 multi-assigns.
add docs for multi-assigns.

...

Expand Down
12 changes: 10 additions & 2 deletions examples/test.p8
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ main {
bool @shared flag

cx16.r1=9999
; flag = test(42)
flag = test(42)
cx16.r0L, flag = test2(12345, 5566, flag, -42)

cx16.r0, flag = test3()
}

asmsub test(ubyte arg @A) -> bool @Pc {
Expand All @@ -27,4 +27,12 @@ main {
rts
}}
}

asmsub test3() -> uword @AY, bool @Pc {
%asm {{
lda #0
ldy #0
rts
}}
}
}

0 comments on commit 9a27505

Please sign in to comment.