mirror of
https://github.com/irmen/prog8.git
synced 2025-08-12 09:25:33 +00:00
allow multi-assign to skip any status register result
This commit is contained in:
@@ -84,24 +84,39 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// because we can only handle integer results right now we can just zip() it all up
|
val assignmentTargets = assignment.children.dropLast(1)
|
||||||
val (statusFlagResult, registersResults) = sub.returns.zip(assignment.children).partition { it.first.register.statusflag!=null }
|
if(sub.returns.size==assignmentTargets.size) {
|
||||||
if(statusFlagResult.isNotEmpty()) {
|
// because we can only handle integer results right now we can just zip() it all up
|
||||||
val (returns, target) = statusFlagResult.single()
|
val (statusFlagResult, registersResults) = sub.returns.zip(assignmentTargets).partition { it.first.register.statusflag!=null }
|
||||||
if(returns.register.statusflag!=Statusflag.Pc)
|
if(statusFlagResult.isNotEmpty()) {
|
||||||
TODO("other status flag for return value")
|
val (returns, target) = statusFlagResult.single()
|
||||||
|
if(returns.register.statusflag!=Statusflag.Pc)
|
||||||
|
TODO("other status flag for return value")
|
||||||
|
|
||||||
target as PtAssignTarget
|
target as PtAssignTarget
|
||||||
if(registersResults.all { (it.second as PtAssignTarget).identifier!=null}) {
|
if(registersResults.all { (it.second as PtAssignTarget).identifier!=null}) {
|
||||||
// all other results are just stored into identifiers directly so first handle those
|
// all other results are just stored into identifiers directly so first handle those
|
||||||
// (simple store instructions that don't modify the carry flag)
|
// (simple store instructions that don't modify the carry flag)
|
||||||
assignRegisterResults(registersResults)
|
assignRegisterResults(registersResults)
|
||||||
assignCarryResult(target, false)
|
assignCarryResult(target, false)
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
assignCarryResult(target, needsToSaveA(registersResults))
|
||||||
}
|
}
|
||||||
assignCarryResult(target, needsToSaveA(registersResults))
|
assignRegisterResults(registersResults)
|
||||||
|
} else if (sub.returns.size>assignmentTargets.size) {
|
||||||
|
// Targets and values don't match. Skip status flag results, assign only the normal value results.
|
||||||
|
val targets = assignmentTargets.iterator()
|
||||||
|
sub.returns.forEach {
|
||||||
|
if(it.register.registerOrPair!=null) {
|
||||||
|
val target = targets.next() as PtAssignTarget
|
||||||
|
assignRegisterResults(listOf(it to target))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require(!targets.hasNext())
|
||||||
|
} else {
|
||||||
|
throw AssemblyError("number of values and targets don't match")
|
||||||
}
|
}
|
||||||
assignRegisterResults(registersResults)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -22,14 +22,30 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
|||||||
require(funcCall.multipleResultRegs.size + funcCall.multipleResultFpRegs.size >= 2)
|
require(funcCall.multipleResultRegs.size + funcCall.multipleResultFpRegs.size >= 2)
|
||||||
if(funcCall.multipleResultFpRegs.isNotEmpty())
|
if(funcCall.multipleResultFpRegs.isNotEmpty())
|
||||||
TODO("deal with (multiple?) FP return registers")
|
TODO("deal with (multiple?) FP return registers")
|
||||||
|
val assignmentTargets = assignment.children.dropLast(1)
|
||||||
// because we can only handle integer results right now we can just zip() it all up
|
|
||||||
addToResult(result, funcCall, funcCall.resultReg, funcCall.resultFpReg)
|
addToResult(result, funcCall, funcCall.resultReg, funcCall.resultFpReg)
|
||||||
sub.returns.zip(assignment.children).zip(funcCall.multipleResultRegs).forEach {
|
if(sub.returns.size==assignmentTargets.size) {
|
||||||
val regNumber = it.second
|
// Targets and values match. Assign all the things.
|
||||||
val returns = it.first.first
|
sub.returns.zip(assignmentTargets).zip(funcCall.multipleResultRegs).forEach {
|
||||||
val target = it.first.second as PtAssignTarget
|
val regNumber = it.second
|
||||||
result += assignCpuRegister(returns, regNumber, target)
|
val returns = it.first.first
|
||||||
|
val target = it.first.second as PtAssignTarget
|
||||||
|
result += assignCpuRegister(returns, regNumber, target)
|
||||||
|
}
|
||||||
|
} else if (sub.returns.size>assignmentTargets.size) {
|
||||||
|
// Targets and values don't match. Skip status flag results, assign only the normal value results.
|
||||||
|
val targets = assignmentTargets.iterator()
|
||||||
|
sub.returns.zip(funcCall.multipleResultRegs).forEach {
|
||||||
|
val returns = it.first
|
||||||
|
if(returns.register.registerOrPair!=null) {
|
||||||
|
val target = targets.next() as PtAssignTarget
|
||||||
|
val regNumber = it.second
|
||||||
|
result += assignCpuRegister(returns, regNumber, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require(!targets.hasNext())
|
||||||
|
} else {
|
||||||
|
throw AssemblyError("number of values and targets don't match")
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
} else {
|
} else {
|
||||||
|
@@ -547,47 +547,67 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// multi-assign: check the number of assign targets vs. the number of return values of the subroutine
|
|
||||||
// also check the types of the variables vs the types of each return value
|
|
||||||
val fcall = assignment.value as? IFunctionCall
|
val fcall = assignment.value as? IFunctionCall
|
||||||
val fcallTarget = fcall?.target?.targetSubroutine(program)
|
val fcallTarget = fcall?.target?.targetSubroutine(program)
|
||||||
if(assignment.target.multi!=null) {
|
if(assignment.target.multi!=null) {
|
||||||
val multi = assignment.target.multi!!
|
checkMultiAssignment(assignment, fcall, fcallTarget)
|
||||||
if(fcall==null) {
|
|
||||||
errors.err("expected a function call with multiple return values", assignment.value.position)
|
|
||||||
} else {
|
|
||||||
if(fcallTarget==null) {
|
|
||||||
errors.err("expected a function call with multiple return values", assignment.value.position)
|
|
||||||
} else {
|
|
||||||
if(fcallTarget.returntypes.size!=multi.size) {
|
|
||||||
errors.err("expected ${multi.size} return values, have ${fcallTarget.returntypes.size}", fcall.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(errors.noErrors()) {
|
|
||||||
// check the types...
|
|
||||||
fcallTarget!!.returntypes.zip(multi).withIndex().forEach { (index, p) ->
|
|
||||||
val (returnType, target) = p
|
|
||||||
val targetDt = target.inferType(program).getOr(DataType.UNDEFINED)
|
|
||||||
if(!(returnType isAssignableTo targetDt))
|
|
||||||
errors.err("can't assign returnvalue #${index+1} to corresponding target; ${returnType} vs $targetDt", target.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if(fcallTarget!=null) {
|
} else if(fcallTarget!=null) {
|
||||||
if(fcallTarget.returntypes.size!=1) {
|
if(fcallTarget.returntypes.size!=1) {
|
||||||
// If there are 2 return values, one of them being a boolean in a status register, this is okay.
|
// If there are 2 return values, one of them being a boolean in a status register, this is okay.
|
||||||
// In that case the normal value is assigned and the status bit is dealth with separately for example with if_cs
|
// In that case the normal value is assigned and the status bit is dealth with separately for example with if_cs
|
||||||
val (returnRegisters, _) = fcallTarget.asmReturnvaluesRegisters.partition { rr -> rr.registerOrPair != null }
|
val (returnRegisters, _) = fcallTarget.asmReturnvaluesRegisters.partition { rr -> rr.registerOrPair != null }
|
||||||
if(returnRegisters.size>1)
|
if(returnRegisters.size>1) {
|
||||||
errors.err("expected 1 return value, have ${fcallTarget.returntypes.size}", fcall.position)
|
errors.err("multiple return values and too few assignment targets, need at least ${returnRegisters.size}", fcall.position)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super.visit(assignment)
|
super.visit(assignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkMultiAssignment(assignment: Assignment, fcall: IFunctionCall?, fcallTarget: Subroutine?) {
|
||||||
|
// multi-assign: check the number of assign targets vs. the number of return values of the subroutine
|
||||||
|
// also check the types of the variables vs the types of each return value
|
||||||
|
if(fcall==null || fcallTarget==null) {
|
||||||
|
errors.err("expected a function call with multiple return values", assignment.value.position)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val targets = assignment.target.multi!!
|
||||||
|
if(fcallTarget.returntypes.size<targets.size) {
|
||||||
|
errors.err("too many assignment targets, ${targets.size} targets for ${fcallTarget.returntypes.size} return values", fcall.position)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(fcallTarget.returntypes.size>targets.size) {
|
||||||
|
// You can have LESS assign targets than the number of result values,
|
||||||
|
// as long as the result values contain booleans that are returned in cpu status flags (like Carry).
|
||||||
|
// These may be ignored in the assignment - only "true" values NEED to have a target.
|
||||||
|
val numberOfNormalValues = fcallTarget.asmReturnvaluesRegisters.count { it.registerOrPair!=null }
|
||||||
|
if(numberOfNormalValues != targets.size) {
|
||||||
|
errors.err("multiple return values and too few assignment targets, need at least $numberOfNormalValues", fcall.position)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// check the types of the 'normal' values that are being assigned
|
||||||
|
val returnTypesAndRegisters = fcallTarget.returntypes.zip(fcallTarget.asmReturnvaluesRegisters)
|
||||||
|
returnTypesAndRegisters.zip(targets).withIndex().forEach { (index, p) ->
|
||||||
|
val (returnType, register) = p.first
|
||||||
|
if(register.registerOrPair!=null) {
|
||||||
|
val target = p.second
|
||||||
|
val targetDt = target.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
|
if (!(returnType isAssignableTo targetDt))
|
||||||
|
errors.err("can't assign returnvalue #${index + 1} to corresponding target; $returnType vs $targetDt", target.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// check all the assigment target types
|
||||||
|
fcallTarget.returntypes.zip(targets).withIndex().forEach { (index, p) ->
|
||||||
|
val (returnType, target) = p
|
||||||
|
val targetDt = target.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
|
if (!(returnType isAssignableTo targetDt))
|
||||||
|
errors.err("can't assign returnvalue #${index + 1} to corresponding target; $returnType vs $targetDt", target.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun visit(assignTarget: AssignTarget) {
|
override fun visit(assignTarget: AssignTarget) {
|
||||||
super.visit(assignTarget)
|
super.visit(assignTarget)
|
||||||
|
@@ -2,6 +2,7 @@ package prog8tests.ast
|
|||||||
|
|
||||||
import io.kotest.core.spec.style.FunSpec
|
import io.kotest.core.spec.style.FunSpec
|
||||||
import io.kotest.matchers.shouldBe
|
import io.kotest.matchers.shouldBe
|
||||||
|
import io.kotest.matchers.shouldNotBe
|
||||||
import io.kotest.matchers.string.shouldContain
|
import io.kotest.matchers.string.shouldContain
|
||||||
import prog8.ast.statements.Block
|
import prog8.ast.statements.Block
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
@@ -110,9 +111,11 @@ main {
|
|||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
bool @shared flag
|
bool @shared flag
|
||||||
|
ubyte @shared bytevar
|
||||||
|
|
||||||
cx16.r0L, flag = test2(12345, 5566, flag, -42)
|
cx16.r0L, flag = test2(12345, 5566, flag, -42)
|
||||||
cx16.r1, flag = test3()
|
cx16.r1, flag, bytevar = test3()
|
||||||
|
cx16.r1, bytevar = test3() ; omitting the status flag result should also work
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub test2(uword arg @AY, uword arg2 @R1, bool flag @Pc, byte value @X) -> ubyte @A, bool @Pc {
|
asmsub test2(uword arg @AY, uword arg2 @R1, bool flag @Pc, byte value @X) -> ubyte @A, bool @Pc {
|
||||||
@@ -123,41 +126,37 @@ main {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub test3() -> uword @R1, bool @Pc {
|
asmsub test3() -> uword @R1, bool @Pc, ubyte @X {
|
||||||
%asm {{
|
%asm {{
|
||||||
lda #0
|
lda #0
|
||||||
ldy #0
|
ldy #0
|
||||||
|
ldx #0
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
|
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
|
||||||
val errors = ErrorReporterForTests()
|
val errors = ErrorReporterForTests()
|
||||||
val result = compileText(Cx16Target(), false, src, errors, true)!!
|
val result = compileText(Cx16Target(), false, src, errors, true)!!
|
||||||
errors.errors.size shouldBe 0
|
errors.errors.size shouldBe 0
|
||||||
val start = result.codegenAst!!.entrypoint()!!
|
val start = result.codegenAst!!.entrypoint()!!
|
||||||
start.children.size shouldBe 5
|
start.children.size shouldBe 8
|
||||||
val a1_1 = start.children[2] as PtAssignment
|
val a1_1 = start.children[4] as PtAssignment
|
||||||
val a1_2 = start.children[3] as PtAssignment
|
val a1_2 = start.children[5] as PtAssignment
|
||||||
|
val a1_3 = start.children[6] as PtAssignment
|
||||||
a1_1.multiTarget shouldBe true
|
a1_1.multiTarget shouldBe true
|
||||||
a1_2.multiTarget shouldBe true
|
a1_2.multiTarget shouldBe true
|
||||||
|
a1_3.multiTarget shouldBe true
|
||||||
|
a1_1.children.size shouldBe 3
|
||||||
|
a1_2.children.size shouldBe 4
|
||||||
|
a1_3.children.size shouldBe 3
|
||||||
(a1_1.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r0L")
|
(a1_1.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r0L")
|
||||||
(a1_1.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag")
|
(a1_1.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag")
|
||||||
(a1_2.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r1")
|
(a1_2.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r1")
|
||||||
(a1_2.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag")
|
(a1_2.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag")
|
||||||
|
(a1_2.children[2] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_bytevar")
|
||||||
errors.clear()
|
(a1_3.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r1")
|
||||||
val result2=compileText(VMTarget(), false, src, errors, true)!!
|
(a1_3.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_bytevar")
|
||||||
errors.errors.size shouldBe 0
|
|
||||||
val start2 = result2.codegenAst!!.entrypoint()!!
|
|
||||||
start2.children.size shouldBe 5
|
|
||||||
val a2_1 = start2.children[2] as PtAssignment
|
|
||||||
val a2_2 = start2.children[3] as PtAssignment
|
|
||||||
a2_1.multiTarget shouldBe true
|
|
||||||
a2_2.multiTarget shouldBe true
|
|
||||||
(a2_1.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r0L")
|
|
||||||
(a2_1.children[1] as PtAssignTarget).identifier!!.name shouldBe("main.start.flag")
|
|
||||||
(a2_2.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r1")
|
|
||||||
(a2_2.children[1] as PtAssignTarget).identifier!!.name shouldBe("main.start.flag")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("multi-assign from romsub") {
|
test("multi-assign from romsub") {
|
||||||
@@ -165,43 +164,40 @@ main {
|
|||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
bool @shared flag
|
bool @shared flag
|
||||||
|
ubyte @shared bytevar
|
||||||
|
|
||||||
flag = test(42)
|
flag = test(42)
|
||||||
cx16.r0L, flag = test2(12345, 5566, flag, -42)
|
cx16.r0L, flag = test2(12345, 5566, flag, -42)
|
||||||
cx16.r1, flag = test3()
|
cx16.r1, flag, bytevar = test3()
|
||||||
|
cx16.r1, bytevar = test3() ; omitting the status flag result should also work
|
||||||
}
|
}
|
||||||
|
|
||||||
romsub ${'$'}8000 = test(ubyte arg @A) -> bool @Pc
|
romsub ${'$'}8000 = test(ubyte arg @A) -> bool @Pc
|
||||||
romsub ${'$'}8002 = test2(uword arg @AY, uword arg2 @R1, bool flag @Pc, byte value @X) -> ubyte @A, bool @Pc
|
romsub ${'$'}8002 = test2(uword arg @AY, uword arg2 @R1, bool flag @Pc, byte value @X) -> ubyte @A, bool @Pc
|
||||||
romsub ${'$'}8003 = test3() -> uword @R1, bool @Pc
|
romsub ${'$'}8003 = test3() -> uword @R1, bool @Pc, ubyte @X
|
||||||
}"""
|
}"""
|
||||||
|
|
||||||
|
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
|
||||||
val errors = ErrorReporterForTests()
|
val errors = ErrorReporterForTests()
|
||||||
val result = compileText(Cx16Target(), false, src, errors, true)!!
|
val result = compileText(Cx16Target(), false, src, errors, true)!!
|
||||||
errors.errors.size shouldBe 0
|
errors.errors.size shouldBe 0
|
||||||
val start = result.codegenAst!!.entrypoint()!!
|
val start = result.codegenAst!!.entrypoint()!!
|
||||||
start.children.size shouldBe 5
|
start.children.size shouldBe 9
|
||||||
val a1_1 = start.children[2] as PtAssignment
|
val a1_1 = start.children[5] as PtAssignment
|
||||||
val a1_2 = start.children[3] as PtAssignment
|
val a1_2 = start.children[6] as PtAssignment
|
||||||
|
val a1_3 = start.children[7] as PtAssignment
|
||||||
a1_1.multiTarget shouldBe true
|
a1_1.multiTarget shouldBe true
|
||||||
a1_2.multiTarget shouldBe true
|
a1_2.multiTarget shouldBe true
|
||||||
|
a1_3.multiTarget shouldBe true
|
||||||
|
a1_1.children.size shouldBe 3
|
||||||
|
a1_2.children.size shouldBe 4
|
||||||
|
a1_3.children.size shouldBe 3
|
||||||
(a1_1.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r0L")
|
(a1_1.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r0L")
|
||||||
(a1_1.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag")
|
(a1_1.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag")
|
||||||
(a1_2.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r1")
|
(a1_2.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r1")
|
||||||
(a1_2.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag")
|
(a1_2.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_flag")
|
||||||
|
(a1_2.children[2] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_bytevar")
|
||||||
errors.clear()
|
(a1_3.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r1")
|
||||||
val result2=compileText(VMTarget(), false, src, errors, true)!!
|
(a1_3.children[1] as PtAssignTarget).identifier!!.name shouldBe("p8b_main.p8s_start.p8v_bytevar")
|
||||||
errors.errors.size shouldBe 0
|
|
||||||
val start2 = result2.codegenAst!!.entrypoint()!!
|
|
||||||
start2.children.size shouldBe 5
|
|
||||||
val a2_1 = start2.children[2] as PtAssignment
|
|
||||||
val a2_2 = start2.children[3] as PtAssignment
|
|
||||||
a2_1.multiTarget shouldBe true
|
|
||||||
a2_2.multiTarget shouldBe true
|
|
||||||
(a2_1.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r0L")
|
|
||||||
(a2_1.children[1] as PtAssignTarget).identifier!!.name shouldBe("main.start.flag")
|
|
||||||
(a2_2.children[0] as PtAssignTarget).identifier!!.name shouldBe("cx16.r1")
|
|
||||||
(a2_2.children[1] as PtAssignTarget).identifier!!.name shouldBe("main.start.flag")
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -684,16 +684,13 @@ are all assigned to individual assignment targets. You simply write them as a co
|
|||||||
|
|
||||||
asmsub multisub() -> uword @AY, bool @Pc, ubyte @X { ... }
|
asmsub multisub() -> uword @AY, bool @Pc, ubyte @X { ... }
|
||||||
|
|
||||||
**There is also a special rule:** if there's just one return value in a register, and one or more others that are returned
|
**There is also a special rule:** you are allowed to omit assignments of the boolean values returned in status registers such as the carry flag.
|
||||||
as bits in the status register (such as the Carry bit), the compiler *also* allows you to call the subroutine and just assign a *single* return value.
|
So in the case of multisub() above, you could also write `wordvar, bytevar = multisub()` and leave out the carry flag.
|
||||||
It will then store the result value in a variable if required, and *try to keep the status register untouched
|
The compiler will try to assign the normal numeric values and leave the status flags untouched, which then allows you to
|
||||||
after the call* so you can often use a conditional branch statement for that. But the latter is tricky,
|
use a conditional branch such as `if_cs` to do something with it. This is always more efficient
|
||||||
make sure you check the generated assembly code.
|
than storing it in a variable and then adding an `if flag...` statement afterwards.
|
||||||
|
It can sometimes be tricky to keep the status flags that are returned from the subroutine intact though,
|
||||||
.. note::
|
if the assign targets are not simple variables. In such cases, make sure you check the generated assembly code to see if it all works out.
|
||||||
For asmsubs or romsubs that return a boolean status flag in a cpu status register such as the Carry flag,
|
|
||||||
it is always more efficient to use a conditional branch like `if_cs` to act on that value, than storing
|
|
||||||
it in a variable and then adding an `if flag...` statement afterwards.
|
|
||||||
|
|
||||||
|
|
||||||
Subroutine definitions
|
Subroutine definitions
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
||||||
make it possible to omit the Status Register values in multi-assigns regardless of the number of return values (is now 1 value)
|
try to replace the variable number of assignment targets by allowing placeholder '_' target
|
||||||
|
|
||||||
|
|
||||||
|
check souce code of examples and library, for void calls that could now be turned into multi-assign calls.
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@@ -1,41 +1,30 @@
|
|||||||
%import textio
|
%import textio
|
||||||
%import test_stack
|
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
%option no_sysinit
|
%option no_sysinit
|
||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
bool @shared flag
|
|
||||||
ubyte @shared bytevar
|
ubyte @shared bytevar
|
||||||
uword @shared wordvar
|
uword @shared wordvar
|
||||||
|
|
||||||
; cx16.r1=9999
|
wordvar, bytevar = test4()
|
||||||
; flag = test(42)
|
if_cs
|
||||||
; cx16.r0L, flag = test2(12345, 5566, flag, -42)
|
txt.print("true! ")
|
||||||
; cx16.r1, flag = test3()
|
else
|
||||||
|
txt.print("false! ")
|
||||||
wordvar, bytevar, flag = test4()
|
|
||||||
wordvar, bytevar, flag = test4()
|
|
||||||
|
|
||||||
txt.print_uwhex(wordvar, true)
|
txt.print_uwhex(wordvar, true)
|
||||||
txt.spc()
|
txt.spc()
|
||||||
txt.print_bool(flag)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_ub(bytevar)
|
txt.print_ub(bytevar)
|
||||||
txt.nl()
|
txt.nl()
|
||||||
}
|
}
|
||||||
|
|
||||||
romsub $8000 = test(ubyte arg @A) -> bool @Pc
|
|
||||||
romsub $8002 = test2(uword arg @AY, uword arg2 @R1, bool flag @Pc, byte value @X) -> ubyte @A, bool @Pc
|
|
||||||
romsub $8003 = test3() -> uword @R1, bool @Pc
|
|
||||||
|
|
||||||
|
|
||||||
asmsub test4() -> uword @AY, ubyte @X, bool @Pc {
|
asmsub test4() -> uword @AY, ubyte @X, bool @Pc {
|
||||||
%asm {{
|
%asm {{
|
||||||
lda #<$11ee
|
lda #<$11ee
|
||||||
ldy #>$11ee
|
ldy #>$11ee
|
||||||
ldx #42
|
ldx #42
|
||||||
sec
|
clc
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user