mirror of
https://github.com/irmen/prog8.git
synced 2025-01-23 00:31:14 +00:00
support assigning multiple return flags from asmsub in 6502 codegen
This commit is contained in:
parent
64e66e732f
commit
4e98fb75d6
@ -48,6 +48,26 @@ internal class AssignmentAsmGen(
|
||||
|
||||
asmgen.translate(values)
|
||||
|
||||
val assignmentTargets = assignment.children.dropLast(1)
|
||||
if(sub.returns.size==assignmentTargets.size) {
|
||||
// because we can only handle integer results right now we can just zip() it all up
|
||||
val (statusFlagResults, registersResults) = sub.returns.zip(assignmentTargets).partition { it.first.register.statusflag!=null }
|
||||
if (statusFlagResults.isEmpty())
|
||||
assignRegisterResults(registersResults)
|
||||
else if(registersResults.isEmpty())
|
||||
assignOnlyTheStatusFlagsResults(false, statusFlagResults)
|
||||
else
|
||||
assignStatusFlagsAndRegistersResults(statusFlagResults, registersResults)
|
||||
} else {
|
||||
throw AssemblyError("number of values and targets don't match")
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignStatusFlagsAndRegistersResults(
|
||||
statusFlagResults: List<Pair<StRomSubParameter, PtNode>>,
|
||||
registersResults: List<Pair<StRomSubParameter, PtNode>>
|
||||
) {
|
||||
|
||||
fun needsToSaveA(registersResults: List<Pair<StRomSubParameter, PtNode>>): Boolean =
|
||||
if(registersResults.isEmpty())
|
||||
false
|
||||
@ -56,124 +76,123 @@ internal class AssignmentAsmGen(
|
||||
else
|
||||
true
|
||||
|
||||
fun assignCarryFlagResult(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")
|
||||
if(registersResults.all {
|
||||
val tgt = it.second as PtAssignTarget
|
||||
tgt.void || tgt.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)
|
||||
assignOnlyTheStatusFlagsResults(false, statusFlagResults)
|
||||
} else {
|
||||
val saveA = needsToSaveA(registersResults)
|
||||
assignOnlyTheStatusFlagsResults(saveA, statusFlagResults)
|
||||
assignRegisterResults(registersResults)
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignOnlyTheStatusFlagsResults(saveA: Boolean, statusFlagResults: List<Pair<StRomSubParameter, PtNode>>) {
|
||||
// assigning flags to their variables targets requires load-instructions that destroy flags
|
||||
// so if there's more than 1, we need to save and restore the flags
|
||||
val saveFlags = statusFlagResults.size>1
|
||||
|
||||
fun hasFlag(statusFlagResults: List<Pair<StRomSubParameter, PtNode>>, flag: Statusflag): PtAssignTarget? {
|
||||
for ((returns, target) in statusFlagResults) {
|
||||
if(returns.register.statusflag!! == flag)
|
||||
return target as PtAssignTarget
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun assignZeroFlagResult(target: PtAssignTarget, saveA: Boolean) {
|
||||
if(saveA) asmgen.out(" pha")
|
||||
asmgen.out("""
|
||||
val targetCarry = hasFlag(statusFlagResults, Statusflag.Pc)
|
||||
val targetZero = hasFlag(statusFlagResults, Statusflag.Pz)
|
||||
val targetNeg = hasFlag(statusFlagResults, Statusflag.Pn)
|
||||
val targetOverflow = hasFlag(statusFlagResults, Statusflag.Pv)
|
||||
|
||||
if(saveA) asmgen.out(" pha")
|
||||
if(targetZero!=null && !targetZero.void)
|
||||
assignZeroFlagResult(targetZero, saveFlags)
|
||||
if(targetNeg!=null && !targetNeg.void)
|
||||
assignNegativeFlagResult(targetNeg, saveFlags)
|
||||
if(targetCarry!=null && !targetCarry.void)
|
||||
assignCarryFlagResult(targetCarry)
|
||||
if(targetOverflow!=null && !targetOverflow.void)
|
||||
assignOverflowFlagResult(targetOverflow)
|
||||
if(saveA) asmgen.out(" pla")
|
||||
}
|
||||
|
||||
private fun assignRegisterResults(registersResults: List<Pair<StRomSubParameter, PtNode>>) {
|
||||
registersResults.forEach { (returns, target) ->
|
||||
target as PtAssignTarget
|
||||
if(!target.void) {
|
||||
val targetIdent = target.identifier
|
||||
val targetMem = target.memory
|
||||
if(targetIdent!=null || targetMem!=null) {
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
when(returns.type) {
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
if(returns.register.registerOrPair in Cx16VirtualRegisters) {
|
||||
assignVirtualRegister(tgt, returns.register.registerOrPair!!)
|
||||
} else {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignCarryFlagResult(target: PtAssignTarget) {
|
||||
// overflow is not clobbered so no need to save/restore it
|
||||
asmgen.out(" lda #0 | rol a")
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
}
|
||||
|
||||
private fun assignZeroFlagResult(target: PtAssignTarget, saveFlags: Boolean) {
|
||||
if(saveFlags) asmgen.out(" php")
|
||||
asmgen.out("""
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
if(saveA) asmgen.out(" pla")
|
||||
}
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
if(saveFlags) asmgen.out(" plp")
|
||||
}
|
||||
|
||||
fun assignNegativeFlagResult(target: PtAssignTarget, saveA: Boolean) {
|
||||
if(saveA) asmgen.out(" pha")
|
||||
asmgen.out("""
|
||||
private fun assignNegativeFlagResult(target: PtAssignTarget, saveFlags: Boolean) {
|
||||
if(saveFlags) asmgen.out(" php")
|
||||
asmgen.out("""
|
||||
bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
if(saveA) asmgen.out(" pla")
|
||||
}
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
if(saveFlags) asmgen.out(" plp")
|
||||
}
|
||||
|
||||
fun assignOverflowFlagResult(target: PtAssignTarget, saveA: Boolean) {
|
||||
if(saveA) asmgen.out(" pha")
|
||||
asmgen.out("""
|
||||
private fun assignOverflowFlagResult(target: PtAssignTarget) {
|
||||
// overflow is not clobbered so no need to save/restore it
|
||||
asmgen.out("""
|
||||
bvs +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
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) ->
|
||||
target as PtAssignTarget
|
||||
if(!target.void) {
|
||||
val targetIdent = target.identifier
|
||||
val targetMem = target.memory
|
||||
if(targetIdent!=null || targetMem!=null) {
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
when(returns.type) {
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
if(returns.register.registerOrPair in Cx16VirtualRegisters) {
|
||||
assignVirtualRegister(tgt, returns.register.registerOrPair!!)
|
||||
} else {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val assignmentTargets = assignment.children.dropLast(1)
|
||||
if(sub.returns.size==assignmentTargets.size) {
|
||||
// because we can only handle integer results right now we can just zip() it all up
|
||||
val (statusFlagResults, registersResults) = sub.returns.zip(assignmentTargets).partition { it.first.register.statusflag!=null }
|
||||
if(statusFlagResults.isNotEmpty()) {
|
||||
if(statusFlagResults.size>1)
|
||||
TODO("handle multiple status flag results")
|
||||
val (returns, target) = statusFlagResults.single()
|
||||
target as PtAssignTarget
|
||||
if(target.void) {
|
||||
// forget about the Carry status flag, only assign the normal return values
|
||||
assignRegisterResults(registersResults)
|
||||
return
|
||||
}
|
||||
if(registersResults.all {
|
||||
val tgt = it.second as PtAssignTarget
|
||||
tgt.void || tgt.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)
|
||||
return when(returns.register.statusflag!!) {
|
||||
Statusflag.Pc -> assignCarryFlagResult(target, false)
|
||||
Statusflag.Pz -> assignZeroFlagResult(target, false)
|
||||
Statusflag.Pv -> assignOverflowFlagResult(target, false)
|
||||
Statusflag.Pn -> assignNegativeFlagResult(target, false)
|
||||
}
|
||||
}
|
||||
|
||||
val saveA = needsToSaveA(registersResults)
|
||||
when(returns.register.statusflag!!) {
|
||||
Statusflag.Pc -> assignCarryFlagResult(target, saveA)
|
||||
Statusflag.Pz -> assignZeroFlagResult(target, saveA)
|
||||
Statusflag.Pv -> assignOverflowFlagResult(target, saveA)
|
||||
Statusflag.Pn -> assignNegativeFlagResult(target, saveA)
|
||||
}
|
||||
}
|
||||
assignRegisterResults(registersResults)
|
||||
} else {
|
||||
throw AssemblyError("number of values and targets don't match")
|
||||
}
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
}
|
||||
|
||||
|
||||
fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
|
||||
when(assign.source.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> {
|
||||
|
@ -57,10 +57,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
RegisterOrPair.XY -> IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum)
|
||||
in Cx16VirtualRegisters -> IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}")
|
||||
null -> {
|
||||
when(returns.register.statusflag) {
|
||||
Statusflag.Pc -> IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum)
|
||||
else -> throw AssemblyError("weird statusflag as returnvalue")
|
||||
}
|
||||
TODO("assign CPU status flag ${returns.register.statusflag!!}")
|
||||
}
|
||||
else -> throw AssemblyError("cannot load register")
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ import prog8.code.ast.PtAssignTarget
|
||||
import prog8.code.ast.PtAssignment
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.SourceCode
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.parser.Prog8Parser.parseModule
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
@ -136,7 +136,7 @@ main {
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
|
||||
compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = compileText(Cx16Target(), false, src, errors, true)!!
|
||||
errors.errors.size shouldBe 0
|
||||
@ -186,7 +186,7 @@ main {
|
||||
romsub ${'$'}8003 = test3() -> uword @R1, bool @Pc, ubyte @X
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
|
||||
compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = compileText(Cx16Target(), false, src, errors, true)!!
|
||||
errors.errors.size shouldBe 0
|
||||
|
@ -420,4 +420,24 @@ main {
|
||||
compileText(Cx16Target(), true, src, writeAssembly = true) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("multiple status flags return values from asmsub") {
|
||||
val src="""
|
||||
main {
|
||||
romsub 5000 = carryAndNegativeAndByteAndWord() -> bool @Pc, bool @Pn, ubyte @X, uword @AY
|
||||
|
||||
sub start() {
|
||||
ubyte @shared x
|
||||
uword @shared w
|
||||
bool @shared flag1
|
||||
bool @shared flag2
|
||||
|
||||
flag1, flag2, x, w = carryAndNegativeAndByteAndWord()
|
||||
flag1, void, void, w = carryAndNegativeAndByteAndWord()
|
||||
void, void, void, void = carryAndNegativeAndByteAndWord()
|
||||
void carryAndNegativeAndByteAndWord()
|
||||
}
|
||||
}"""
|
||||
compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
})
|
139
examples/test.p8
139
examples/test.p8
@ -2,15 +2,142 @@
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
byte @shared x
|
||||
word @shared w
|
||||
ubyte @shared x
|
||||
uword @shared w
|
||||
bool flag1, flag2
|
||||
|
||||
if x==0 or x==1 or x==2
|
||||
x++
|
||||
sys.clear_carry()
|
||||
flag1 = onlyCarry()
|
||||
if flag1
|
||||
txt.print("1: ok\n")
|
||||
else
|
||||
txt.print("1: fail\n")
|
||||
|
||||
if w==0 or w==1 or w==2
|
||||
x++
|
||||
x=1
|
||||
flag1 = onlyZero()
|
||||
if flag1
|
||||
txt.print("2: ok\n")
|
||||
else
|
||||
txt.print("2: fail\n")
|
||||
|
||||
sys.clear_carry()
|
||||
flag1, x = carryAndByte()
|
||||
if flag1 and x==42
|
||||
txt.print("3: ok\n")
|
||||
else
|
||||
txt.print("3: fail\n")
|
||||
|
||||
sys.clear_carry()
|
||||
flag1, w = carryAndWord()
|
||||
if flag1 and w==4242
|
||||
txt.print("4: ok\n")
|
||||
else
|
||||
txt.print("4: fail\n")
|
||||
|
||||
sys.clear_carry()
|
||||
flag1, x, w = carryAndValues()
|
||||
if flag1 and x==99 and w==9999
|
||||
txt.print("5: ok\n")
|
||||
else
|
||||
txt.print("5: fail\n")
|
||||
|
||||
x = 1
|
||||
sys.clear_carry()
|
||||
flag1, flag2 = onlyCarryAndZero()
|
||||
if flag1 and flag2
|
||||
txt.print("6: ok\n")
|
||||
else
|
||||
txt.print("6: fail\n")
|
||||
|
||||
x = 1
|
||||
sys.clear_carry()
|
||||
flag1, flag2, x = carryAndZeroAndByte()
|
||||
if flag1 and flag2 and x==33
|
||||
txt.print("7: ok\n")
|
||||
else
|
||||
txt.print("7: fail\n")
|
||||
|
||||
x = 1
|
||||
sys.clear_carry()
|
||||
flag1, flag2, x, w = carryAndNegativeAndByteAndWord()
|
||||
if flag1 and flag2 and x==55 and w==51400
|
||||
txt.print("8: ok\n")
|
||||
else
|
||||
txt.print("8: fail\n")
|
||||
}
|
||||
|
||||
|
||||
asmsub carryAndNegativeAndByteAndWord() -> bool @Pc, bool @Pn, ubyte @X, uword @AY {
|
||||
%asm {{
|
||||
ldx #55
|
||||
lda #200
|
||||
ldy #200
|
||||
sec
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub carryAndZeroAndByte() -> bool @Pc, bool @Pz, ubyte @Y {
|
||||
%asm {{
|
||||
ldy #33
|
||||
lda #0
|
||||
sec
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub onlyCarryAndZero() -> bool @Pc, bool @Pz {
|
||||
%asm {{
|
||||
lda #0
|
||||
sec
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub carryAndValues() -> bool @Pc, ubyte @X, uword @AY {
|
||||
%asm {{
|
||||
ldx #99
|
||||
lda #<9999
|
||||
ldy #>9999
|
||||
sec
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub carryAndWord() -> bool @Pc, uword @AY {
|
||||
%asm {{
|
||||
lda #<4242
|
||||
ldy #>4242
|
||||
sec
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub carryAndByte() -> bool @Pc, ubyte @A {
|
||||
%asm {{
|
||||
lda #42
|
||||
sec
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub onlyCarry() -> bool @Pc {
|
||||
%asm {{
|
||||
sec
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub onlyZero() -> bool @Pz {
|
||||
%asm {{
|
||||
lda #0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user