mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +00:00
asmsubroutines now also return their value on the evalstack (this fixes their use in expressions)
This commit is contained in:
parent
8811d2f7c5
commit
95f7c9bad0
@ -790,7 +790,7 @@ data class AssignTarget(val register: Register?,
|
||||
|
||||
fun shortString(withTypePrefix: Boolean=false): String {
|
||||
if(register!=null)
|
||||
return (if(withTypePrefix) "0register::" else "") + register.toString()
|
||||
return (if(withTypePrefix) "0register::" else "") + register.name
|
||||
if(identifier!=null)
|
||||
return (if(withTypePrefix) "3identifier::" else "") + identifier.nameInSource.last()
|
||||
if(arrayindexed!=null)
|
||||
|
@ -1137,9 +1137,9 @@ private class AstChecker(private val namespace: INameScope,
|
||||
checkResult.add(ExpressionError("cannot assign word to byte, use msb() or lsb()?", position))
|
||||
}
|
||||
else if(sourceDatatype==DataType.FLOAT && targetDatatype in IntegerDatatypes)
|
||||
checkResult.add(ExpressionError("cannot assign float to ${targetDatatype.toString().toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position))
|
||||
checkResult.add(ExpressionError("cannot assign float to ${targetDatatype.name.toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position))
|
||||
else
|
||||
checkResult.add(ExpressionError("cannot assign ${sourceDatatype.toString().toLowerCase()} to ${targetDatatype.toString().toLowerCase()}", position))
|
||||
checkResult.add(ExpressionError("cannot assign ${sourceDatatype.name.toLowerCase()} to ${targetDatatype.name.toLowerCase()}", position))
|
||||
|
||||
return false
|
||||
}
|
||||
|
@ -615,7 +615,7 @@ internal class Compiler(private val rootModule: Module,
|
||||
private fun translate(expr: IExpression) {
|
||||
when(expr) {
|
||||
is RegisterExpr -> {
|
||||
prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = expr.register.toString())
|
||||
prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = expr.register.name)
|
||||
}
|
||||
is PrefixExpression -> {
|
||||
translate(expr.expression)
|
||||
@ -645,7 +645,7 @@ internal class Compiler(private val rootModule: Module,
|
||||
if(target is BuiltinFunctionStatementPlaceholder) {
|
||||
// call to a builtin function (some will just be an opcode!)
|
||||
val funcname = expr.target.nameInSource[0]
|
||||
translateFunctionCall(funcname, expr.arglist)
|
||||
translateBuiltinFunctionCall(funcname, expr.arglist)
|
||||
} else {
|
||||
when(target) {
|
||||
is Subroutine -> translateSubroutineCall(target, expr.arglist, expr.position)
|
||||
@ -761,7 +761,7 @@ internal class Compiler(private val rootModule: Module,
|
||||
val targetStmt = stmt.target.targetStatement(namespace)!!
|
||||
if(targetStmt is BuiltinFunctionStatementPlaceholder) {
|
||||
val funcname = stmt.target.nameInSource[0]
|
||||
translateFunctionCall(funcname, stmt.arglist)
|
||||
translateBuiltinFunctionCall(funcname, stmt.arglist)
|
||||
return
|
||||
}
|
||||
|
||||
@ -771,8 +771,6 @@ internal class Compiler(private val rootModule: Module,
|
||||
is Subroutine -> {
|
||||
translateSubroutineCall(targetStmt, stmt.arglist, stmt.position)
|
||||
// make sure we clean up the unused result values from the stack
|
||||
// only if they're non-register return values!
|
||||
if(targetStmt.asmReturnvaluesRegisters.isEmpty())
|
||||
for(rv in targetStmt.returntypes) {
|
||||
val opcode=opcodeDiscard(rv)
|
||||
prog.instr(opcode)
|
||||
@ -783,8 +781,8 @@ internal class Compiler(private val rootModule: Module,
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateFunctionCall(funcname: String, args: List<IExpression>) {
|
||||
// some functions are implemented as vm opcodes
|
||||
private fun translateBuiltinFunctionCall(funcname: String, args: List<IExpression>) {
|
||||
// some builtin functions are implemented directly as vm opcodes
|
||||
|
||||
if(funcname == "swap") {
|
||||
translateSwap(args)
|
||||
@ -1000,8 +998,41 @@ internal class Compiler(private val rootModule: Module,
|
||||
// We don't bother about saving A and Y. They're considered expendable.
|
||||
|
||||
if(subroutine.isAsmSubroutine) {
|
||||
restoreX = translateAsmSubCallArguments(subroutine, arguments, callPosition, restoreX)
|
||||
} else {
|
||||
// only regular (non-register) arguments
|
||||
// "assign" the arguments to the locally scoped parameter variables for this subroutine
|
||||
// (subroutine arguments are not passed via the stack!)
|
||||
for (arg in arguments.zip(subroutine.parameters)) {
|
||||
translate(arg.first)
|
||||
convertType(arg.first.resultingDatatype(namespace, heap)!!, arg.second.type) // convert types of arguments to required parameter type
|
||||
val opcode = opcodePopvar(arg.second.type)
|
||||
prog.instr(opcode, callLabel = subroutine.scopedname + "." + arg.second.name)
|
||||
}
|
||||
}
|
||||
prog.instr(Opcode.CALL, callLabel = subroutine.scopedname)
|
||||
if(restoreX)
|
||||
prog.instr(Opcode.RRESTOREX)
|
||||
|
||||
if(subroutine.isAsmSubroutine && subroutine.asmReturnvaluesRegisters.isNotEmpty()) {
|
||||
// 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.
|
||||
for(rv in subroutine.asmReturnvaluesRegisters.reversed()) {
|
||||
if(rv.statusflag!=null)
|
||||
TODO("not yet supported: return values in cpu status flag $rv $subroutine")
|
||||
when(rv.registerOrPair) {
|
||||
A,X,Y -> prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = rv.registerOrPair.name)
|
||||
AX, AY, XY -> prog.instr(Opcode.PUSH_VAR_WORD, callLabel = rv.registerOrPair.name)
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateAsmSubCallArguments(subroutine: Subroutine, arguments: List<IExpression>, callPosition: Position, restoreXinitial: Boolean): Boolean {
|
||||
var restoreX = restoreXinitial
|
||||
if (subroutine.parameters.size != subroutine.asmParameterRegisters.size)
|
||||
throw CompilerException("no support for mix of register and non-register subroutine arguments")
|
||||
TODO("no support yet for mix of register and non-register subroutine arguments")
|
||||
|
||||
// only register arguments (or status-flag bits)
|
||||
var carryParam: Boolean? = null
|
||||
@ -1151,20 +1182,7 @@ internal class Compiler(private val rootModule: Module,
|
||||
true -> prog.instr(Opcode.SEC)
|
||||
false -> prog.instr(Opcode.CLC)
|
||||
}
|
||||
} else {
|
||||
// only regular (non-register) arguments
|
||||
// "assign" the arguments to the locally scoped parameter variables for this subroutine
|
||||
// (subroutine arguments are not passed via the stack!)
|
||||
for (arg in arguments.zip(subroutine.parameters)) {
|
||||
translate(arg.first)
|
||||
convertType(arg.first.resultingDatatype(namespace, heap)!!, arg.second.type) // convert types of arguments to required parameter type
|
||||
val opcode = opcodePopvar(arg.second.type)
|
||||
prog.instr(opcode, callLabel = subroutine.scopedname + "." + arg.second.name)
|
||||
}
|
||||
}
|
||||
prog.instr(Opcode.CALL, callLabel = subroutine.scopedname)
|
||||
if(restoreX)
|
||||
prog.instr(Opcode.RRESTOREX)
|
||||
return restoreX
|
||||
}
|
||||
|
||||
private fun translateBinaryOperator(operator: String, dt: DataType) {
|
||||
@ -1442,8 +1460,8 @@ internal class Compiler(private val rootModule: Module,
|
||||
prog.line(stmt.position)
|
||||
when {
|
||||
stmt.target.register != null -> when(stmt.operator) {
|
||||
"++" -> prog.instr(Opcode.INC_VAR_UB, callLabel = stmt.target.register.toString())
|
||||
"--" -> prog.instr(Opcode.DEC_VAR_UB, callLabel = stmt.target.register.toString())
|
||||
"++" -> prog.instr(Opcode.INC_VAR_UB, callLabel = stmt.target.register!!.name)
|
||||
"--" -> prog.instr(Opcode.DEC_VAR_UB, callLabel = stmt.target.register!!.name)
|
||||
}
|
||||
stmt.target.identifier != null -> {
|
||||
val targetStatement = stmt.target.identifier!!.targetStatement(namespace) as VarDecl
|
||||
@ -1541,15 +1559,6 @@ internal class Compiler(private val rootModule: Module,
|
||||
if(stmt.aug_op!=null)
|
||||
throw CompilerException("augmented assignment should have been converted to regular assignment already")
|
||||
|
||||
if(stmt.value is FunctionCall) {
|
||||
val sub = (stmt.value as FunctionCall).target.targetStatement(namespace)
|
||||
if(sub is Subroutine && sub.asmReturnvaluesRegisters.isNotEmpty()) {
|
||||
// the subroutine call returns its values in registers
|
||||
storeRegisterIntoTarget(sub.asmReturnvaluesRegisters.single(), assignTarget, stmt)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// pop the result value back into the assignment target
|
||||
val datatype = assignTarget.determineDatatype(namespace, heap, stmt)!!
|
||||
popValueIntoTarget(assignTarget, datatype)
|
||||
@ -1581,6 +1590,7 @@ internal class Compiler(private val rootModule: Module,
|
||||
private fun translateMultiReturnAssignment(stmt: Assignment) {
|
||||
val targetStmt = (stmt.value as? FunctionCall)?.target?.targetStatement(namespace)
|
||||
if(targetStmt is Subroutine && targetStmt.isAsmSubroutine) {
|
||||
// TODO check correctness of multi-return values (they should be on the stack rather than directly assigned!)
|
||||
// we're dealing with the one case where multiple assignment targets are allowed: a call to an asmsub with multiple return values
|
||||
// for now, we only support multiple return values as long as they're returned in registers as well.
|
||||
if(targetStmt.asmReturnvaluesRegisters.isEmpty())
|
||||
@ -1667,7 +1677,7 @@ internal class Compiler(private val rootModule: Module,
|
||||
}
|
||||
} else throw CompilerException("invalid assignment target type ${target::class}")
|
||||
}
|
||||
assignTarget.register != null -> prog.instr(Opcode.POP_VAR_BYTE, callLabel = assignTarget.register.toString())
|
||||
assignTarget.register != null -> prog.instr(Opcode.POP_VAR_BYTE, callLabel = assignTarget.register.name)
|
||||
assignTarget.arrayindexed != null -> translate(assignTarget.arrayindexed, true) // write value to it
|
||||
assignTarget.memoryAddress != null -> {
|
||||
val address = assignTarget.memoryAddress?.addressExpression?.constValue(namespace, heap)?.asIntegerValue
|
||||
@ -1703,7 +1713,7 @@ internal class Compiler(private val rootModule: Module,
|
||||
|
||||
if(loop.loopRegister!=null) {
|
||||
val reg = loop.loopRegister
|
||||
loopVarName = reg.toString()
|
||||
loopVarName = reg.name
|
||||
loopVarDt = DataType.UBYTE
|
||||
} else {
|
||||
val loopvar = (loop.loopVar!!.targetStatement(namespace) as VarDecl)
|
||||
@ -2036,7 +2046,7 @@ internal class Compiler(private val rootModule: Module,
|
||||
postIncr.linkParents(body)
|
||||
translate(postIncr)
|
||||
if(lvTarget.register!=null)
|
||||
prog.instr(Opcode.PUSH_VAR_BYTE, callLabel =lvTarget.register.toString())
|
||||
prog.instr(Opcode.PUSH_VAR_BYTE, callLabel =lvTarget.register.name)
|
||||
else {
|
||||
val opcode = opcodePushvar(targetStatement!!.datatype)
|
||||
prog.instr(opcode, callLabel = targetStatement.scopedname)
|
||||
|
@ -23,10 +23,10 @@ open class Instruction(val opcode: Opcode,
|
||||
}
|
||||
opcode in opcodesWithVarArgument -> {
|
||||
// opcodes that manipulate a variable
|
||||
"${opcode.toString().toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd()
|
||||
"${opcode.name.toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd()
|
||||
}
|
||||
callLabel==null -> "${opcode.toString().toLowerCase()} $argStr"
|
||||
else -> "${opcode.toString().toLowerCase()} $callLabel $argStr"
|
||||
callLabel==null -> "${opcode.name.toLowerCase()} $argStr"
|
||||
else -> "${opcode.name.toLowerCase()} $callLabel $argStr"
|
||||
}
|
||||
.trimEnd()
|
||||
|
||||
|
@ -462,11 +462,11 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
heap.allEntries().forEach {
|
||||
when {
|
||||
it.value.str!=null ->
|
||||
out.println("${it.key} ${it.value.type.toString().toLowerCase()} \"${escape(it.value.str!!)}\"")
|
||||
out.println("${it.key} ${it.value.type.name.toLowerCase()} \"${escape(it.value.str!!)}\"")
|
||||
it.value.array!=null ->
|
||||
out.println("${it.key} ${it.value.type.toString().toLowerCase()} ${it.value.array!!.toList()}")
|
||||
out.println("${it.key} ${it.value.type.name.toLowerCase()} ${it.value.array!!.toList()}")
|
||||
it.value.doubleArray!=null ->
|
||||
out.println("${it.key} ${it.value.type.toString().toLowerCase()} ${it.value.doubleArray!!.toList()}")
|
||||
out.println("${it.key} ${it.value.type.name.toLowerCase()} ${it.value.doubleArray!!.toList()}")
|
||||
else -> throw CompilerException("invalid heap entry $it")
|
||||
}
|
||||
}
|
||||
@ -477,12 +477,12 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
out.println("%variables")
|
||||
for(variable in blk.variables) {
|
||||
val valuestr = variable.value.toString()
|
||||
out.println("${variable.key} ${variable.value.type.toString().toLowerCase()} $valuestr")
|
||||
out.println("${variable.key} ${variable.value.type.name.toLowerCase()} $valuestr")
|
||||
}
|
||||
out.println("%end_variables")
|
||||
out.println("%memorypointers")
|
||||
for(iconst in blk.memoryPointers) {
|
||||
out.println("${iconst.key} ${iconst.value.second.toString().toLowerCase()} uw:${iconst.value.first.toString(16)}")
|
||||
out.println("${iconst.key} ${iconst.value.second.name.toLowerCase()} uw:${iconst.value.first.toString(16)}")
|
||||
}
|
||||
out.println("%end_memorypointers")
|
||||
out.println("%instructions")
|
||||
|
@ -514,8 +514,9 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
|
||||
Syscall.FUNC_MAX_F,
|
||||
Syscall.FUNC_MIN_F,
|
||||
Syscall.FUNC_AVG_F,
|
||||
Syscall.FUNC_SUM_F -> " jsr c64flt.${call.toString().toLowerCase()}"
|
||||
else -> " jsr prog8_lib.${call.toString().toLowerCase()}"
|
||||
Syscall.FUNC_SUM_F -> " jsr c64flt.${call.name.toLowerCase()}"
|
||||
null -> ""
|
||||
else -> " jsr prog8_lib.${call.name.toLowerCase()}"
|
||||
}
|
||||
}
|
||||
Opcode.BREAKPOINT -> {
|
||||
|
@ -357,7 +357,6 @@ waitkey:
|
||||
ubyte yp2 = ypos+2
|
||||
ubyte yp3 = ypos+3
|
||||
|
||||
; @todo the boolean expression below currently doesn't work because the result of an asmsub call (getchr) is not put on the stack right now
|
||||
if(currentBlock[0] and c64scr.getchr(x, ypos)!=32)
|
||||
return false
|
||||
if(currentBlock[4] and c64scr.getchr(x, yp1)!=32)
|
||||
@ -414,7 +413,6 @@ waitkey:
|
||||
ubyte yp2 = ypos+2
|
||||
ubyte yp3 = ypos+3
|
||||
|
||||
; @todo the boolean expression below currently doesn't work because the result of an asmsub call (getchr) is not put on the stack right now
|
||||
if(currentBlock[3] and c64scr.getchr(x, ypos)!=32)
|
||||
return false
|
||||
if(currentBlock[7] and c64scr.getchr(x, yp1)!=32)
|
||||
|
@ -7,16 +7,46 @@
|
||||
|
||||
; @todo see looplabelproblem.p8
|
||||
|
||||
|
||||
;ubyte x = rnd82() % 6 ; @todo fix compiler crash + always 0???
|
||||
|
||||
; if(X and c64scr.getcc(1,1)!=32) ... @todo the result value of the asmsub getcc is not put on the eval stack when used in an expression
|
||||
|
||||
sub start() {
|
||||
ubyte xx
|
||||
|
||||
c64scr.print_ub(X)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
A=c64scr.getchr(20,1)
|
||||
c64scr.print_ub(A)
|
||||
c64.CHROUT('\n')
|
||||
xx=c64scr.getchr(20,1)
|
||||
c64scr.print_ub(xx)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_ub(X)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
A=1+c64scr.getchr(20,1)
|
||||
c64scr.print_ub(A)
|
||||
c64.CHROUT('\n')
|
||||
xx=1+c64scr.getchr(20,1)
|
||||
c64scr.print_ub(xx)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_ub(X)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
A=c64scr.getchr(20,1)+1
|
||||
c64scr.print_ub(A)
|
||||
c64.CHROUT('\n')
|
||||
xx=c64scr.getchr(20,1)+1
|
||||
c64scr.print_ub(xx)
|
||||
c64.CHROUT('\n')
|
||||
c64scr.print_ub(X)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
|
||||
}
|
||||
|
||||
asmsub asm_routine(ubyte arg1 @ A, ubyte arg2 @ Y) -> clobbers() -> (ubyte @ A) {
|
||||
return A+Y
|
||||
}
|
||||
|
||||
sub drawNext(ubyte x) {
|
||||
A=x
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user