mirror of
https://github.com/irmen/prog8.git
synced 2025-02-18 05:30:34 +00:00
fixed multi-return value assignment
This commit is contained in:
parent
747c9604dd
commit
1e045b6a62
@ -2283,31 +2283,6 @@ private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal fun registerSet(asmReturnvaluesRegisters: Iterable<RegisterOrStatusflag>): Set<Register> {
|
|
||||||
val resultRegisters = mutableSetOf<Register>()
|
|
||||||
for(x in asmReturnvaluesRegisters) {
|
|
||||||
when(x.registerOrPair) {
|
|
||||||
RegisterOrPair.A -> resultRegisters.add(Register.A)
|
|
||||||
RegisterOrPair.X -> resultRegisters.add(Register.X)
|
|
||||||
RegisterOrPair.Y -> resultRegisters.add(Register.Y)
|
|
||||||
RegisterOrPair.AX -> {
|
|
||||||
resultRegisters.add(Register.A)
|
|
||||||
resultRegisters.add(Register.X)
|
|
||||||
}
|
|
||||||
RegisterOrPair.AY -> {
|
|
||||||
resultRegisters.add(Register.A)
|
|
||||||
resultRegisters.add(Register.Y)
|
|
||||||
}
|
|
||||||
RegisterOrPair.XY -> {
|
|
||||||
resultRegisters.add(Register.X)
|
|
||||||
resultRegisters.add(Register.Y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resultRegisters
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal fun escape(str: String) = str.replace("\t", "\\t").replace("\n", "\\n").replace("\r", "\\r")
|
internal fun escape(str: String) = str.replace("\t", "\\t").replace("\n", "\\n").replace("\r", "\\r")
|
||||||
|
|
||||||
internal fun unescape(str: String, position: Position): String {
|
internal fun unescape(str: String, position: Position): String {
|
||||||
|
@ -360,12 +360,6 @@ private class AstChecker(private val namespace: INameScope,
|
|||||||
if (stmt.returntypes.size != assignment.targets.size)
|
if (stmt.returntypes.size != assignment.targets.size)
|
||||||
checkResult.add(ExpressionError("number of return values doesn't match number of assignment targets", assignment.value.position))
|
checkResult.add(ExpressionError("number of return values doesn't match number of assignment targets", assignment.value.position))
|
||||||
else {
|
else {
|
||||||
if (assignment.targets.all { it.register != null }) {
|
|
||||||
val returnRegisters = registerSet(stmt.asmReturnvaluesRegisters)
|
|
||||||
val targetRegisters = assignment.targets.filter { it.register != null }.map { it.register }.toSet()
|
|
||||||
if (returnRegisters != targetRegisters)
|
|
||||||
checkResult.add(ExpressionError("asmsub return registers $returnRegisters don't match assignment target registers", assignment.position))
|
|
||||||
}
|
|
||||||
for (thing in stmt.returntypes.zip(assignment.targets)) {
|
for (thing in stmt.returntypes.zip(assignment.targets)) {
|
||||||
if (thing.second.determineDatatype(namespace, heap, assignment) != thing.first)
|
if (thing.second.determineDatatype(namespace, heap, assignment) != thing.first)
|
||||||
checkResult.add(ExpressionError("return type mismatch for target ${thing.second.shortString()}", assignment.value.position))
|
checkResult.add(ExpressionError("return type mismatch for target ${thing.second.shortString()}", assignment.value.position))
|
||||||
|
@ -1010,14 +1010,20 @@ internal class Compiler(private val rootModule: Module,
|
|||||||
// the result values of the asm-subroutine that are returned in registers, have to be pushed on the stack
|
// the result values of the asm-subroutine that are returned in registers, have to be pushed on the stack
|
||||||
// (in reversed order) otherwise the asm-subroutine can't be used in expressions.
|
// (in reversed order) otherwise the asm-subroutine can't be used in expressions.
|
||||||
for(rv in subroutine.asmReturnvaluesRegisters.reversed()) {
|
for(rv in subroutine.asmReturnvaluesRegisters.reversed()) {
|
||||||
if(rv.statusflag!=null)
|
if(rv.statusflag!=null) {
|
||||||
TODO("not yet supported: return values in cpu status flag $rv ($subroutine)")
|
if (rv.statusflag == Statusflag.Pc) {
|
||||||
when(rv.registerOrPair) {
|
prog.instr(Opcode.CARRY_TO_A)
|
||||||
A,X,Y -> prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = rv.registerOrPair.name)
|
prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = Register.A.name)
|
||||||
AX -> prog.instr(Opcode.PUSH_REGAX_WORD)
|
}
|
||||||
AY -> prog.instr(Opcode.PUSH_REGAY_WORD)
|
else TODO("return value in cpu status flag only supports Carry, not $rv ($subroutine)")
|
||||||
XY -> prog.instr(Opcode.PUSH_REGXY_WORD)
|
} else {
|
||||||
null -> {}
|
when (rv.registerOrPair) {
|
||||||
|
A, X, Y -> prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = rv.registerOrPair.name)
|
||||||
|
AX -> prog.instr(Opcode.PUSH_REGAX_WORD)
|
||||||
|
AY -> prog.instr(Opcode.PUSH_REGAY_WORD)
|
||||||
|
XY -> prog.instr(Opcode.PUSH_REGXY_WORD)
|
||||||
|
null -> {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1584,74 +1590,17 @@ internal class Compiler(private val rootModule: Module,
|
|||||||
private fun translateMultiReturnAssignment(stmt: Assignment) {
|
private fun translateMultiReturnAssignment(stmt: Assignment) {
|
||||||
val targetStmt = (stmt.value as? FunctionCall)?.target?.targetStatement(namespace)
|
val targetStmt = (stmt.value as? FunctionCall)?.target?.targetStatement(namespace)
|
||||||
if(targetStmt is Subroutine && targetStmt.isAsmSubroutine) {
|
if(targetStmt is Subroutine && targetStmt.isAsmSubroutine) {
|
||||||
// TODO check correctness of multi-return values (they should be on the stack rather than directly assigned!)
|
// this is the only case where multiple assignment targets are allowed: a call to an asmsub with multiple return values
|
||||||
// we're dealing with the one case where multiple assignment targets are allowed: a call to an asmsub with multiple return values
|
// the return values are already on the stack (the subroutine call puts them there)
|
||||||
// for now, we only support multiple return values as long as they're returned in registers as well.
|
if(stmt.targets.size!=targetStmt.asmReturnvaluesRegisters.size)
|
||||||
if(targetStmt.asmReturnvaluesRegisters.isEmpty())
|
throw CompilerException("asmsub number of return values doesn't match number of assignment targets ${stmt.position}")
|
||||||
throw CompilerException("we only support multiple return values / assignment when the asmsub returns values in registers")
|
for(target in stmt.targets) {
|
||||||
// if the result registers are not assigned in the exact same registers, or in variables, we need some code
|
val dt = target.determineDatatype(namespace, heap, stmt)
|
||||||
if(stmt.targets.all{it.register!=null}) {
|
popValueIntoTarget(target, dt!!)
|
||||||
val resultRegisters = registerSet(targetStmt.asmReturnvaluesRegisters)
|
|
||||||
if(stmt.targets.size!=resultRegisters.size)
|
|
||||||
throw CompilerException("asmsub number of return values doesn't match number of assignment targets ${stmt.position}")
|
|
||||||
val targetRegs = stmt.targets.filter {it.register!=null}.map{it.register}.toSet()
|
|
||||||
if(resultRegisters!=targetRegs)
|
|
||||||
throw CompilerException("asmsub return registers don't match assignment target registers ${stmt.position}")
|
|
||||||
// output is in registers already, no need to emit any asm code
|
|
||||||
} else {
|
|
||||||
// output is in registers but has to be stored somewhere
|
|
||||||
for(result in targetStmt.asmReturnvaluesRegisters.zip(stmt.targets))
|
|
||||||
storeRegisterIntoTarget(result.first, result.second, stmt)
|
|
||||||
}
|
}
|
||||||
} else throw CompilerException("can only use multiple assignment targets on an asmsub call")
|
} else throw CompilerException("can only use multiple assignment targets on an asmsub call")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun storeRegisterIntoTarget(registerOrStatus: RegisterOrStatusflag, target: AssignTarget, parent: IStatement) {
|
|
||||||
if(registerOrStatus.statusflag!=null)
|
|
||||||
return
|
|
||||||
when(registerOrStatus.registerOrPair){
|
|
||||||
A -> {
|
|
||||||
val assignment = Assignment(listOf(target), null, RegisterExpr(Register.A, target.position), target.position)
|
|
||||||
assignment.linkParents(parent)
|
|
||||||
translate(assignment)
|
|
||||||
}
|
|
||||||
X -> {
|
|
||||||
val assignment = Assignment(listOf(target), null, RegisterExpr(Register.X, target.position), target.position)
|
|
||||||
assignment.linkParents(parent)
|
|
||||||
translate(assignment)
|
|
||||||
}
|
|
||||||
Y -> {
|
|
||||||
val assignment = Assignment(listOf(target), null, RegisterExpr(Register.Y, target.position), target.position)
|
|
||||||
assignment.linkParents(parent)
|
|
||||||
translate(assignment)
|
|
||||||
}
|
|
||||||
AX -> {
|
|
||||||
// deal with register pair AX: target = A + X*256
|
|
||||||
val targetDt = target.determineDatatype(namespace, heap, parent)
|
|
||||||
if(targetDt!=DataType.UWORD && targetDt!=DataType.WORD)
|
|
||||||
throw CompilerException("invalid target datatype for registerpair $targetDt")
|
|
||||||
prog.instr(Opcode.PUSH_REGAX_WORD)
|
|
||||||
popValueIntoTarget(target, targetDt)
|
|
||||||
}
|
|
||||||
AY -> {
|
|
||||||
// deal with register pair AY: target = A + Y*256
|
|
||||||
val targetDt = target.determineDatatype(namespace, heap, parent)
|
|
||||||
if(targetDt!=DataType.UWORD && targetDt!=DataType.WORD)
|
|
||||||
throw CompilerException("invalid target datatype for registerpair $targetDt")
|
|
||||||
prog.instr(Opcode.PUSH_REGAY_WORD)
|
|
||||||
popValueIntoTarget(target, targetDt)
|
|
||||||
}
|
|
||||||
XY -> {
|
|
||||||
// deal with register pair XY: target = X + Y*256
|
|
||||||
val targetDt = target.determineDatatype(namespace, heap, parent)
|
|
||||||
if(targetDt!=DataType.UWORD && targetDt!=DataType.WORD)
|
|
||||||
throw CompilerException("invalid target datatype for registerpair $targetDt")
|
|
||||||
prog.instr(Opcode.PUSH_REGXY_WORD)
|
|
||||||
popValueIntoTarget(target, targetDt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) {
|
private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) {
|
||||||
when {
|
when {
|
||||||
assignTarget.identifier != null -> {
|
assignTarget.identifier != null -> {
|
||||||
|
@ -256,6 +256,7 @@ enum class Opcode {
|
|||||||
CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
|
CLC, // clear carry status flag NOTE: is mostly fake, carry flag is not affected by any numeric operations
|
||||||
SEI, // set irq-disable status flag
|
SEI, // set irq-disable status flag
|
||||||
CLI, // clear irq-disable status flag
|
CLI, // clear irq-disable status flag
|
||||||
|
CARRY_TO_A, // load var/register A with carry status bit
|
||||||
RSAVE, // save all internal registers and status flags
|
RSAVE, // save all internal registers and status flags
|
||||||
RSAVEX, // save just X (the evaluation stack pointer)
|
RSAVEX, // save just X (the evaluation stack pointer)
|
||||||
RRESTORE, // restore all internal registers and status flags
|
RRESTORE, // restore all internal registers and status flags
|
||||||
|
@ -449,6 +449,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
|||||||
Opcode.CLC -> " clc"
|
Opcode.CLC -> " clc"
|
||||||
Opcode.SEI -> " sei"
|
Opcode.SEI -> " sei"
|
||||||
Opcode.CLI -> " cli"
|
Opcode.CLI -> " cli"
|
||||||
|
Opcode.CARRY_TO_A -> " lda #0 | adc #0"
|
||||||
Opcode.JUMP -> {
|
Opcode.JUMP -> {
|
||||||
if(ins.callLabel!=null)
|
if(ins.callLabel!=null)
|
||||||
" jmp ${ins.callLabel}"
|
" jmp ${ins.callLabel}"
|
||||||
|
@ -1860,6 +1860,7 @@ class StackVm(private var traceOutputFile: String?) {
|
|||||||
Opcode.CAST_B_TO_F -> typecast(DataType.BYTE, DataType.FLOAT)
|
Opcode.CAST_B_TO_F -> typecast(DataType.BYTE, DataType.FLOAT)
|
||||||
Opcode.CAST_UW_TO_F -> typecast(DataType.UWORD, DataType.FLOAT)
|
Opcode.CAST_UW_TO_F -> typecast(DataType.UWORD, DataType.FLOAT)
|
||||||
Opcode.CAST_W_TO_F -> typecast(DataType.WORD, DataType.FLOAT)
|
Opcode.CAST_W_TO_F -> typecast(DataType.WORD, DataType.FLOAT)
|
||||||
|
Opcode.CARRY_TO_A -> variables["A"] = if(P_carry) Value(DataType.UBYTE, 1) else Value(DataType.UBYTE, 0)
|
||||||
|
|
||||||
//else -> throw VmExecutionException("unimplemented opcode: ${ins.opcode}")
|
//else -> throw VmExecutionException("unimplemented opcode: ${ins.opcode}")
|
||||||
}
|
}
|
||||||
|
@ -6,26 +6,42 @@
|
|||||||
; @todo see problem in looplabelproblem.p8
|
; @todo see problem in looplabelproblem.p8
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
str text = "hello"
|
|
||||||
ubyte ub1
|
ubyte ub1
|
||||||
ubyte ub2
|
ubyte ub2
|
||||||
ubyte ub3
|
ubyte ub3
|
||||||
ubyte ub4
|
ubyte ub4
|
||||||
ubyte ub5
|
ubyte ub5
|
||||||
|
|
||||||
ub1, ub2, ub3, ub4, ub5 = test()
|
ub1, ub2 = test2()
|
||||||
|
c64scr.print_ub(ub1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_ub(ub2)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
ub1, ub2 = test3()
|
||||||
|
c64scr.print_ub(ub1)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64scr.print_ub(ub2)
|
||||||
|
c64.CHROUT('\n')
|
||||||
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
sub test1() -> ubyte {
|
asmsub test2() -> clobbers() -> (ubyte @Pc, ubyte @A) {
|
||||||
return 99
|
|
||||||
}
|
|
||||||
|
|
||||||
asmsub test() -> clobbers() -> (ubyte @Pc, ubyte @Pz, ubyte @Pn, ubyte @Pv, ubyte @A) {
|
|
||||||
%asm {{
|
%asm {{
|
||||||
lda #99
|
lda #100
|
||||||
|
ldy #100
|
||||||
sec
|
sec
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub test3() -> clobbers() -> (ubyte @Pc, ubyte @A) {
|
||||||
|
%asm {{
|
||||||
|
lda #101
|
||||||
|
ldy #101
|
||||||
|
clc
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user