tweaking multiple assignment targets

This commit is contained in:
Irmen de Jong 2018-11-19 23:39:52 +01:00
parent fad74a6ae0
commit c90230d33a
7 changed files with 252 additions and 45 deletions

View File

@ -0,0 +1,31 @@
%import c64utils
%option enable_floats
~ main {
const uword width = 320
const uword height = 200
sub start() {
;vm_gfx_clearscr(0)
float t
ubyte color
while(1) {
float x = sin(t*1.01) + cos(t*1.1234)
float y = cos(t) + sin(t*0.03456)
;vm_gfx_pixel(screenx(x), screeny(y), color//16)
t += 0.01
color++
}
}
sub screenx(x: float) -> word {
return floor(x * flt(width)/4.1) + width // 2
}
sub screeny(y: float) -> word {
return floor(y * flt(height)/4.1) + height // 2
}
}

View File

@ -16,7 +16,12 @@ sub start() {
byte b1
;v1=foo() ; @todo fix return type value error see sub
;v1=foo()
address =c64.MEMBOT(1, 40000.w) ; ok!
address =c64.MEMBOT(1, address) ; ok!
address =c64.MEMBOT(1, memaddr) ; ok!i
;address =c64.MEMBOT(1, wordarray[1]) ; @todo nice error about loading X register from stack
A, Y =c64.GETADR() ; ok!
Y, A =c64.GETADR() ; ok!
@ -24,7 +29,6 @@ sub start() {
memaddr = c64flt.GETADRAY() ; ok!
wordarray[1] = c64flt.GETADRAY() ; ok!
v1, v2 =c64.GETADR() ; ok!
address =c64.MEMBOT(1, 0.w) ; ok !
address =c64.IOBASE() ; ok!
A = c64.CHRIN() ; ok !
X = c64.CHRIN() ; ok !
@ -34,8 +38,8 @@ sub start() {
}
sub foo() -> ubyte {
return 1 ; @todo not ubyte but byte (if sub returns byte)
sub foo() -> byte {
return 1 ; @todo fix error: '1' as byte literal (not ubyte)
}

View File

@ -304,25 +304,27 @@ class AstChecker(private val namespace: INameScope,
override fun process(assignment: Assignment): IStatement {
// assigning from a functioncall COULD return multiple values (from an asm subroutine)
val stmt = (assignment.value as FunctionCall).target.targetStatement(namespace)
if(stmt is Subroutine && stmt.returntypes.size>1) {
if(stmt.isAsmSubroutine) {
if(stmt.returntypes.size != assignment.targets.size)
checkResult.add(ExpressionError("number of return values doesn't match number of assignment targets", assignment.value.position))
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))
if(assignment.value is FunctionCall) {
val stmt = (assignment.value as FunctionCall).target.targetStatement(namespace)
if (stmt is Subroutine && stmt.returntypes.size > 1) {
if (stmt.isAsmSubroutine) {
if (stmt.returntypes.size != assignment.targets.size)
checkResult.add(ExpressionError("number of return values doesn't match number of assignment targets", assignment.value.position))
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)) {
if (thing.second.determineDatatype(namespace, heap, assignment) != thing.first)
checkResult.add(ExpressionError("return type mismatch for target ${thing.second.shortString()}", assignment.value.position))
}
}
for(thing in stmt.returntypes.zip(assignment.targets)) {
if(thing.second.determineDatatype(namespace, heap, assignment)!=thing.first)
checkResult.add(ExpressionError("return type mismatch for target ${thing.second.shortString()}", assignment.value.position))
}
}
} else
checkResult.add(ExpressionError("only asmsub subroutines can return multiple values", assignment.value.position))
} else
checkResult.add(ExpressionError("only asmsub subroutines can return multiple values", assignment.value.position))
}
}
var resultingAssignment = assignment

View File

@ -1,6 +1,8 @@
package prog8.compiler
import com.sun.org.apache.xalan.internal.xsltc.cmdline.Compile
import prog8.ast.*
import prog8.ast.RegisterOrPair.*
import prog8.compiler.intermediate.IntermediateProgram
import prog8.compiler.intermediate.Opcode
import prog8.compiler.intermediate.Value
@ -792,11 +794,109 @@ private class StatementTranslator(private val prog: IntermediateProgram,
private fun translateSubroutineCall(subroutine: Subroutine, arguments: List<IExpression>, callPosition: Position) {
// evaluate the arguments and assign them into the subroutine's argument variables.
prog.line(callPosition)
if(subroutine.asmParameterRegisters.isNotEmpty()) {
if(subroutine.parameters.size!=subroutine.asmParameterRegisters.size)
throw CompilerException("no support for mix of register and non-register subroutine arguments")
// only register arguments (or status-flag bits)
var carryParam: Boolean? = null
for(arg in arguments.zip(subroutine.asmParameterRegisters)) {
if(arg.second.statusflag!=null) {
if(arg.second.statusflag==Statusflag.Pc)
carryParam = arg.first.constValue(namespace, heap)!!.asBooleanValue
else
throw CompilerException("no support for status flag parameter: ${arg.second.statusflag}")
} else {
when (arg.second.registerOrPair!!) {
A -> {
val assign = Assignment(listOf(AssignTarget(Register.A, null, null, callPosition)), null, arg.first, callPosition)
assign.linkParents(subroutine.parent)
translate(assign)
}
X -> {
// TODO: save X on stack & restore after call
val assign = Assignment(listOf(AssignTarget(Register.X, null, null, callPosition)), null, arg.first, callPosition)
assign.linkParents(subroutine.parent)
translate(assign)
}
Y -> {
val assign = Assignment(listOf(AssignTarget(Register.Y, null, null, callPosition)), null, arg.first, callPosition)
assign.linkParents(subroutine.parent)
translate(assign)
}
AX -> {
// TODO: save X on stack & restore after call
val valueA: IExpression
val valueX: IExpression
val paramDt = arg.first.resultingDatatype(namespace, heap)
if(paramDt==DataType.UBYTE) {
valueA=arg.first
valueX=LiteralValue.optimalInteger(0, callPosition)
val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, callPosition)), null, valueA, callPosition)
val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, callPosition)), null, valueX, callPosition)
assignA.linkParents(subroutine.parent)
assignX.linkParents(subroutine.parent)
translate(assignA)
translate(assignX)
} else if(paramDt==DataType.UWORD) {
translate(arg.first)
prog.instr(Opcode.POP_REGAX_WORD)
} else
throw CompilerException("don't know how to pass register parameter of type $paramDt")
}
AY -> {
val valueA: IExpression
val valueY: IExpression
val paramDt = arg.first.resultingDatatype(namespace, heap)
if(paramDt==DataType.UBYTE) {
valueA=arg.first
valueY=LiteralValue.optimalInteger(0, callPosition)
val assignA = Assignment(listOf(AssignTarget(Register.A, null, null, callPosition)), null, valueA, callPosition)
val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, callPosition)), null, valueY, callPosition)
assignA.linkParents(subroutine.parent)
assignY.linkParents(subroutine.parent)
translate(assignA)
translate(assignY)
} else if(paramDt==DataType.UWORD) {
translate(arg.first)
prog.instr(Opcode.POP_REGAY_WORD)
} else
throw CompilerException("don't know how to pass register parameter of type $paramDt")
}
XY -> {
// TODO: save X on stack & restore after call
val valueX: IExpression
val valueY: IExpression
val paramDt = arg.first.resultingDatatype(namespace, heap)
if(paramDt==DataType.UBYTE) {
valueX=arg.first
valueY=LiteralValue.optimalInteger(0, callPosition)
val assignX = Assignment(listOf(AssignTarget(Register.X, null, null, callPosition)), null, valueX, callPosition)
val assignY = Assignment(listOf(AssignTarget(Register.Y, null, null, callPosition)), null, valueY, callPosition)
assignX.linkParents(subroutine.parent)
assignY.linkParents(subroutine.parent)
translate(assignX)
translate(assignY)
} else if(paramDt==DataType.UWORD) {
translate(arg.first)
prog.instr(Opcode.POP_REGXY_WORD)
} else
throw CompilerException("don't know how to pass register parameter of type $paramDt")
}
}
}
}
// carry is set last, to avoid clobbering it when loading the other parameters
when(carryParam) {
true -> prog.instr(Opcode.SEC)
false -> prog.instr(Opcode.CLC)
}
if(subroutine.asmReturnvaluesRegisters.isNotEmpty()) {
TODO("call asmsub by loading registers instead")
} else {
prog.line(callPosition)
// only regular (non-register) arguments
for (arg in arguments.zip(subroutine.parameters)) {
translate(arg.first)
val opcode = opcodePopvar(arg.second.type)
@ -1195,22 +1295,22 @@ private class StatementTranslator(private val prog: IntermediateProgram,
if(registerOrStatus.statusflag!=null)
return
when(registerOrStatus.registerOrPair){
RegisterOrPair.A -> {
A -> {
val assignment = Assignment(listOf(target), null, RegisterExpr(Register.A, target.position), target.position)
assignment.linkParents(parent)
translate(assignment)
}
RegisterOrPair.X -> {
X -> {
val assignment = Assignment(listOf(target), null, RegisterExpr(Register.X, target.position), target.position)
assignment.linkParents(parent)
translate(assignment)
}
RegisterOrPair.Y -> {
Y -> {
val assignment = Assignment(listOf(target), null, RegisterExpr(Register.Y, target.position), target.position)
assignment.linkParents(parent)
translate(assignment)
}
RegisterOrPair.AX -> {
AX -> {
// deal with register pair AX: target = A + X*256
val targetDt = target.determineDatatype(namespace, heap, parent)
if(targetDt!=DataType.UWORD)
@ -1218,7 +1318,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
prog.instr(Opcode.PUSH_REGAX_WORD)
popValueIntoTarget(target, targetDt)
}
RegisterOrPair.AY -> {
AY -> {
// deal with register pair AY: target = A + Y*256
val targetDt = target.determineDatatype(namespace, heap, parent)
if(targetDt!=DataType.UWORD)
@ -1226,7 +1326,7 @@ private class StatementTranslator(private val prog: IntermediateProgram,
prog.instr(Opcode.PUSH_REGAY_WORD)
popValueIntoTarget(target, targetDt)
}
RegisterOrPair.XY -> {
XY -> {
// deal with register pair XY: target = X + Y*256
val targetDt = target.determineDatatype(namespace, heap, parent)
if(targetDt!=DataType.UWORD)

View File

@ -28,6 +28,9 @@ enum class Opcode {
POP_VAR_BYTE, // pop (u)byte value into variable
POP_VAR_WORD, // pop (u)word value into variable
POP_VAR_FLOAT, // pop float value into variable
POP_REGAX_WORD, // pop uword from stack into A/X registers
POP_REGAY_WORD, // pop uword from stack into A/Y registers
POP_REGXY_WORD, // pop uword from stack into X/Y registers
// numeric arithmetic
ADD_UB,

View File

@ -462,6 +462,11 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
Opcode.PUSH_REGAY_WORD -> {
" sta ${ESTACK_LO.toHex()},x | tya | sta ${ESTACK_HI.toHex()},x | dex "
}
Opcode.POP_REGAX_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
Opcode.POP_REGXY_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
Opcode.POP_REGAY_WORD -> {
" inx | lda ${ESTACK_LO.toHex()},x | ldy ${ESTACK_HI.toHex()},x "
}
Opcode.READ_INDEXED_VAR_BYTE -> { // @todo is this correct?
"""
@ -2440,13 +2445,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
// ---------- some special operations ------------------
// var word = AY register pair
AsmPattern(listOf(Opcode.PUSH_REGAY_WORD, Opcode.POP_VAR_WORD)) { segment ->
"""
sta ${segment[1].callLabel}
sty ${segment[1].callLabel}+1
"""
},
// var word = AX register pair
AsmPattern(listOf(Opcode.PUSH_REGAX_WORD, Opcode.POP_VAR_WORD)) { segment ->
"""
@ -2454,6 +2452,13 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
stx ${segment[1].callLabel}+1
"""
},
// var word = AY register pair
AsmPattern(listOf(Opcode.PUSH_REGAY_WORD, Opcode.POP_VAR_WORD)) { segment ->
"""
sta ${segment[1].callLabel}
sty ${segment[1].callLabel}+1
"""
},
// var word = XY register pair
AsmPattern(listOf(Opcode.PUSH_REGXY_WORD, Opcode.POP_VAR_WORD)) { segment ->
"""
@ -2461,13 +2466,6 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
sty ${segment[1].callLabel}+1
"""
},
// mem word = AY register pair
AsmPattern(listOf(Opcode.PUSH_REGAY_WORD, Opcode.POP_MEM_WORD)) { segment ->
"""
sta ${hexVal(segment[1])}
sty ${hexValPlusOne(segment[1])}
"""
},
// mem word = AX register pair
AsmPattern(listOf(Opcode.PUSH_REGAX_WORD, Opcode.POP_MEM_WORD)) { segment ->
"""
@ -2475,16 +2473,70 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
stx ${hexValPlusOne(segment[1])}
"""
},
// mem word = AY register pair
AsmPattern(listOf(Opcode.PUSH_REGAY_WORD, Opcode.POP_MEM_WORD)) { segment ->
"""
sta ${hexVal(segment[1])}
sty ${hexValPlusOne(segment[1])}
"""
},
// mem word = XY register pair
AsmPattern(listOf(Opcode.PUSH_REGXY_WORD, Opcode.POP_MEM_WORD)) { segment ->
"""
stx ${hexVal(segment[1])}
sty ${hexValPlusOne(segment[1])}
"""
},
// AX register pair = word value
AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.POP_REGAX_WORD)) { segment ->
val value = hexVal(segment[0])
" lda #<$value | ldx #>$value"
},
// AY register pair = word value
AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.POP_REGAY_WORD)) { segment ->
val value = hexVal(segment[0])
" lda #<$value | ldy #>$value"
},
// XY register pair = word value
AsmPattern(listOf(Opcode.PUSH_WORD, Opcode.POP_REGXY_WORD)) { segment ->
val value = hexVal(segment[0])
" ldx #<$value | ldy #>$value"
},
// AX register pair = word var
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_REGAX_WORD)) { segment ->
" lda #<${segment[0].callLabel} | ldx #>${segment[0].callLabel}"
},
// AY register pair = word var
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_REGAY_WORD)) { segment ->
" lda #<${segment[0].callLabel} | ldy #>${segment[0].callLabel}"
},
// XY register pair = word var
AsmPattern(listOf(Opcode.PUSH_VAR_WORD, Opcode.POP_REGXY_WORD)) { segment ->
" ldx #<${segment[0].callLabel} | ldy #>${segment[0].callLabel}"
},
// AX register pair = mem word
AsmPattern(
listOf(Opcode.PUSH_MEM_UW, Opcode.POP_REGAX_WORD),
listOf(Opcode.PUSH_MEM_W, Opcode.POP_REGAX_WORD)) { segment ->
" lda ${hexVal(segment[0])} | ldx ${hexValPlusOne(segment[0])}"
},
// AY register pair = mem word
AsmPattern(
listOf(Opcode.PUSH_MEM_UW, Opcode.POP_REGAY_WORD),
listOf(Opcode.PUSH_MEM_W, Opcode.POP_REGAY_WORD)) { segment ->
" lda ${hexVal(segment[0])} | ldy ${hexValPlusOne(segment[0])}"
},
// XY register pair = mem word
AsmPattern(
listOf(Opcode.PUSH_MEM_UW, Opcode.POP_REGXY_WORD),
listOf(Opcode.PUSH_MEM_W, Opcode.POP_REGXY_WORD)) { segment ->
" ldx ${hexVal(segment[0])} | ldy ${hexValPlusOne(segment[0])}"
}
// // @todo assignment: floatarray[idxbyte] = float
// AsmPattern(listOf(Opcode.PUSH_FLOAT, Opcode.PUSH_BYTE, Opcode.WRITE_INDEXED_VAR_FLOAT)) { segment ->
// val floatConst = getFloatConst(segment[0].arg!!)

View File

@ -777,6 +777,21 @@ class StackVm(private var traceOutputFile: String?) {
val y=variables["Y"]!!.integerValue()
evalstack.push(Value(DataType.UWORD, y*256+x))
}
Opcode.POP_REGAX_WORD -> {
val value=evalstack.pop().integerValue()
variables["A"] = Value(DataType.UBYTE, value and 255)
variables["X"] = Value(DataType.UBYTE, value shr 8)
}
Opcode.POP_REGAY_WORD -> {
val value=evalstack.pop().integerValue()
variables["A"] = Value(DataType.UBYTE, value and 255)
variables["Y"] = Value(DataType.UBYTE, value shr 8)
}
Opcode.POP_REGXY_WORD -> {
val value=evalstack.pop().integerValue()
variables["X"] = Value(DataType.UBYTE, value and 255)
variables["Y"] = Value(DataType.UBYTE, value shr 8)
}
Opcode.POP_VAR_BYTE -> {
val value = evalstack.pop()
checkDt(value, DataType.UBYTE, DataType.BYTE)